I'm a bit confused about isLandingPad on BasicBlocks in LLVM. I have the following code, where I create an empty BasicBlock and then call isLandingPad on it:
#include "llvm/IR/IRBuilder.h"
#include <assert.h>
using namespace llvm;
int main(void)
{
// Start with a LLVM context.
LLVMContext TheContext;
// Make a module.
Module *TheModule = new Module("mymod", TheContext);
// Make a function
std::vector<Type*> NoArgs = {};
Type *u32 = Type::getInt32Ty(TheContext);
FunctionType *FT = FunctionType::get(u32, NoArgs, false);
Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule);
// Make an empty block
IRBuilder<> Builder(TheContext);
BasicBlock *BB = BasicBlock::Create(TheContext, "entry", F);
Builder.SetInsertPoint(BB);
auto fnp = BB->getFirstNonPHI();
assert(fnp == nullptr);
// I think this should crash.
auto islp = BB->isLandingPad();
printf("isLP = %d\n", islp);
// If we inline the implementation of the above call, we have the following
// (which *does* crash).
auto islp2 = isa<LandingPadInst>(BB->getFirstNonPHI());
printf("isLP2 = %d\n", islp2);
return 0;
}
which outputs:
isLP = 0
codegen: /usr/lib/llvm-7/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<llvm::LandingPadInst, const llvm::Instruction *>::doit(const From *) [To = llvm::LandingPadInst, From = const llvm::Instruction *]: Assertion `Val && "isa<> used on a null pointer"' failed.
According to the LLVM source of isLandingPad (https://llvm.org/doxygen/BasicBlock_8cpp_source.html#l00470) this should segfault when the BasicBlock is empty (since we are calling isa on a nullptr). However, when I run this program the call to isLandingPad succeeds and returns false. Interestingly, when I inline the function definition of isLandingPad (as seen further below), it crashes as expected.
I'm clearly doing something wrong here, but I don't see in what way the BB->isLandingPad() call is different to the inlined version, and why isLandingPad doesn't crash, when it should according to the source.
LLVM itself is (at least on my system) compiled with assertions disabled, so the assertion doesn't trigger. When you inline it in your code, you are compiling with assertions enabled, so it does trigger.
Note that since isa<...> is a template, it will be compiled into the compilation unit it is instantiated as part of. In this case, there's at least two: one in LLVM and one that comprises your program. Strictly speaking they should both be identical (the "one definition rule") or you have UB anyway. The practical upshot in a case like this one is that calls to isa<...>() from either compilation unit might end up calling the version instantiated in the other one. However, it's likely that in the case of isa<...>() the calls are being inlined, i.e. you end up with a version of isa<...>() specific to each compilation unit that instantiates it.
If the code "should segfault", that seems to imply that the code is invoking undefined behavior (UB) at runtime. It is a possibility that the compiler is doing optimizations based on the false assumption that UB does not occur in your program and this false assumption leads to the false result isLP == false that you observe.
You should never invoke undefined behavior and restructure your code to never call functions with parameters that can call UB. (E.g., check the result of getFirstNonPHI before calling isa<LandingPadInst> or isLandingPad.
Specifically you should not assume that UB (such as dereferencing nullptr or an address near it) has a well-defined effect such as "it will segfault" because the compiler may reorganize your code (assuming UB never happens) in ways that will eliminate the effect you expect (e.g., it will generate code that doesn't attempt to load from nullptr).
Inlining and optimization levels have great effect on the generated code and this is why you see different results (invalid return value vs. segfault) in different cases.
More info on undefined behavior:
Undefined, unspecified and implementation-defined behavior
What Every C Programmer Should Know About Undefined Behavior
https://en.cppreference.com/w/cpp/language/ub (See the links at the bottom of the page for further references)
Related
I have to admit I appreciate the effort the Visual Studio static code analyzer does. It has found some issues in my C/C++ code that could, under certain circumstances, lead to some unusual bugs.
However, there are some other places where it finds issues that I have already prepared for.
Take for instance something like this: I have a C array, with a static size somewhere:
static const int DataSize = 100;
int Data[DataSize];
...and then I have a method or function that accesses this data:
void DoSomething(size_t index)
{
if (index >= DataSize)
Panic("Out of bounds");
Data[index] = DoSomethingElse();
}
This Panic() function is meant to notify me when the precondition is not met, and points to an error that can't be recovered from. In a debug build, it breaks into the debugger so I can analyze what happened, while in a release build, it will intentionally crash the program, leaving some useful debug information.
However, if I compile this, Visual Studio 2019 gives me the following warning:
Warning C6386: Buffer overrun while writing to 'Data': the writable size is '400' bytes, but 'index' bytes might be written.
The warning is indeed valid, if I were not performing the precondition check just before.
One way to suppress this warning is by changing the code to something like:
void DoSomething(size_t index)
{
if (index >= DataSize)
{
Panic("Out of bounds");
return; // This is never reached...
}
Data[index] = DoSomethingElse();
}
But, not only this is unnecessarily verbose, it also may confuse the static code analyzer even more, because it can make it think that returning is a valid code path, further confusing it.
Another example, this time unrelated to arrays, and applicable to C and C++. Consider a function or method like this:
int DoSomeOperation(int operationType, int value)
{
int foo;
switch (operationType)
{
case 0:
foo = 10;
break;
case 1:
foo = value + 1;
break;
case 2:
foo = value - 1;
break;
// ... many more cases
default:
Panic("Invalid operationType");
// We never reach this line...
}
DoSomeOtherOperation(foo);
}
This raises the following warning in the DoSomeOtherOperation() line:
Warning C6001: Using uninitialized memory 'foo'.
Once again, I can either explicitly initialize foo, initialize it to something in the default case, or return from the function after Panic(), but technically, neither is necessary, since for all semantically valid values of operationType, foo is indeed initialized.
In fact, I might prefer to leave foo uninitialized at the beginning, so the static analyzer -does- catch a case where I did indeed missed the initialization.
Now, is there a way to annotate the Panic function, so the static code analyzer knows that this function will validate the precondition, or alternatively, that it terminates the program in all cases, so it doesn't have to consider the case where the function returns and the program continues?
I am not aware of any way to tell the analyzer that a parameter is checked in the code but isn't this a job exactly for the In_range(low, hi) annotation?
Change the declaration to the following and the analyzer should be able to check the precondition (So long as it picks up the values of static like DataSize in this example, I am not sure whether it does or not).
void DoSomething(_In_range_(0, DataSize)
SAL is described at
https://learn.microsoft.com/en-us/cpp/code-quality/using-sal-annotations-to-reduce-c-cpp-code-defects?view=msvc-160
I have a program which nearly immediately finishes with -O0 on gcc, but hangs forever with gcc and -O3. It also exits immediately if I remove the [[gnu::pure]] function attribute, even though the function does not modify global state. The program is in three files:
thread.hpp
#include <atomic>
extern ::std::atomic<bool> stopthread;
extern void threadloop();
[[gnu::pure]] extern int get_value_plus(int x);
thread.cpp
#include <thread>
#include <atomic>
#include "thread.hpp"
namespace {
::std::atomic<int> val;
}
::std::atomic<bool> stopthread;
void threadloop()
{
while (!stopthread.load())
{
++val;
}
}
[[gnu::pure]] int get_value_plus(int x)
{
return val.load() + x;
}
main.cpp
#include <thread>
#include "thread.hpp"
int main()
{
stopthread.store(false);
::std::thread loop(threadloop);
while ((get_value_plus(5) + get_value_plus(5)) % 2 == 0)
;
stopthread.store(true);
loop.join();
return 0;
}
Is this a compiler bug? A lack of documentation for the proper caveats to using [[gnu::pure]]? A misreading of the documentation for [[gnu::pure]] such that I've coded a bug?
I have a program which nearly immediately finishes with -O0 on gcc, but hangs forever with gcc and -O3
Yes, because the program gets compiled down to an infinite loop when optimizations are enabled.
Is this a compiler bug? A lack of documentation for the proper caveats to using [[gnu::pure]]? A misreading of the documentation for [[gnu::pure]] such that I've coded a bug?
It isn't a compiler bug. get_value_plus is not a pure function:
[[gnu::pure]] int get_value_plus(int x)
{
return val.load() + x;
}
since the return value can change at any time (for the same x), because val is expected to be modified by the other thread.
The compiler, however, thinking that get_value_plus will always return the same value, will perform CSE and therefore will assume this:
while ((get_value_plus(5) + get_value_plus(5)) % 2 == 0);
can be written as:
int x = get_value_plus(5);
while ((x + x) % 2 == 0);
Which, indeed, it is an infinite loop regardless of the value of x:
while (true);
Please see the GCC documentation on pure for more details.
In general, avoid using optimization hints unless they are well understood!
In this case, the misunderstanding is that pure functions are allowed to read global memory, but not if that memory is changing from call to call by someone else than the caller:
However, functions declared with the pure attribute can safely read any non-volatile objects, and modify the value of objects in a way that does not affect their return value or the observable state of the program.
As it turns out, I misread the documentation. From the online documentation about the pure attribute in gcc:
The pure attribute prohibits a function from modifying the state of the program that is observable by means other than inspecting the function’s return value. However, functions declared with the pure attribute can safely read any non-volatile objects, and modify the value of objects in a way that does not affect their return value or the observable state of the program.
and a different paragraph:
Some common examples of pure functions are strlen or memcmp. Interesting non-pure functions are functions with infinite loops or those depending on volatile memory or other system resource, that may change between consecutive calls (such as the standard C feof function in a multithreading environment).
These two paragraphs make it clear that I've been lying to the compiler, and the function I wrote does not qualify as being 'pure' because it depends on a variable that might change at any time.
The reason I asked this question is because the answers to this question: __attribute__((const)) vs __attribute__((pure)) in GNU C didn't address this problem at all (at the time I asked my question anyway). And a recent C++ Weekly episode had a comment asking about threads and pure functions. So it's clear there's some confusion out there.
So the criteria for a function that qualifies for this marker is that it must not modify global state, though it is allowed to read it. But, if it does read global state, it is not allowed to read any global state that could be considered 'volatile', and this is best understood as state that might change between two immediately successive calls to the function, i.e. if the state it's reading can change in a situation like this:
f();
f();
So in doctest (my testing framework) the user can disable all tests by defining the DOCTEST_CONFIG_DISABLE identifier which makes the following code and macros:
TEST_CASE("name") {
int a = 5;
int b = 6;
CHECK(a == b);
}
turn into the following after the preprocessor:
template<typename T>
void some_anon_func_123() {
int a = 5;
int b = 6;
}
that means that the self-registering test case is turned into an uninstantiated template function and the CHECK() macro (which functions as an if statement checking the condition) into a no-op - like this:
#define CHECK(x) ((void)0) // if disabled
However if the user has factored such testing code in a separate function like this:
static int g() {
std::cout << "called!" << std::endl;
return 42;
}
static void f() {
int a = 5;
CHECK(a == g());
}
TEST_CASE("name") {
f();
}
then there will be warnings for unused functions and unused variables. doctest prides itself with producing 0 warnings even on the most aggressive levels so this is unacceptable.
I tried using the ((void) ...) trick by passing it the macro argument like this:
#define CHECK(x) ((void)(x))
and that indeed silenced the warnings (atleast for a and g()) but there is still code being generated for that statement - if I invoke the f() function from my main() I will see the called! string printed in the console. This is undesirable since I want the compilation to be as fast as possible when test cases and asserts are disabled from the build (by using the DOCTEST_CONFIG_DISABLE identifier). If a user has 100 000 asserts and builds with them disabled he wouldn't want all that unnecessary codegen and compile time overhead for macros that are supposed to be disabled (the CHECK() one).
__attribute__((unused)) has to be used at the point of declaration of a variable - I cannot stick it in the CHECK() macro (or can I? I don't know...).
Not sure if _Pragma() could help - and even if it could - it is known to have issues with GCC:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543
Is there a solution to my problem - like perhaps passing the expression to some template or whatever...? (C++98 solution needed)
I explained my problem in excruciating detail only because I often get accused of the XY problem...
EDIT:
A C++11 solution is OK too - some C++11 features have started to conditionally creep into the library anyway...
So, you want to "lie" to the compiler that you're using a function which you're not actually calling. So how to use a piece of code without executing it?
It seems that the only thing that works on all popular compilers is a C++11-only solution - a lambda which is never called:
#define CHECK(x) [&](){ ((void)(x)); }
If you absolutely need a c++98 solution, a sizeof will also work on many compilers, MSVC being a notable exception:
#define CHECK(x) sizeof(x)
MSVC will still warn for uncalled functions in the expression x.
I guess for maximum coverage you could employ a combination of the two.
msdn link
text here:
'function call' : recursive call has no side effects, deleting A
function contains a recursive call, but otherwise has no side effects.
A call to this function is being deleted. The correctness of the
program is not affected, but the behavior is. Whereas leaving the call
in could result in a runtime stack overflow exception, deleting the
call removes that possibility.
The code causing this warning is:
template<class Key, class Value>
void Map<Key, Value>::Clear(NodeType* pNode)
{
((Key*) (pNode->m_key))->~Key();
((Value*) (pNode->m_item))->~Value();
NodeType* pL = pNode->GetLeftChild();
NodeType* pR = pNode->GetRightChild();
if (pL != &m_dummy)
{
Clear(pL);
}
if (pR != &m_dummy)
{
Clear(pR);
}
}
and 1 more point: this warning only happens in release build (/Ox)
What is this warning? Thanks!
I'll bet it occurs when ~Key and ~Value are no-ops. The compiler will notice that there is literally nothing else that this function tries to do, so it entirely eliminates this function.
For starters, the warning only happens in a release build because it's the result of an optimization, and the optimizer only runs during release builds.
The optimizer is allowed to restructure or eliminate code, including recursive calls, if it can prove that that will not change the program behavior. There is probably some data-dependent path where one or both calls to Clear() for the left and right nodes would have no effect.
Edit: As #MSalters points out, it is more likely that the destructors for Key and Value are no-ops -- as they would be if Key and Value are both plain-old-data structures or simple types, since the destructors are the only side-effect possible from the function as written.
Edit 2: instead of using placement new, why not use the : initializer?
template struct Map<typename Key, typename Value> {
Key key;
Value value;
Map(Key k, Value v) : key(k), value(v) {}
}
Say I have the following function:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
// normal execution guarantees that the Thingy we're looking for exists
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// If we got this far, then something went horribly wrong and we can't recover.
// This function terminates the program.
fatalError("The sky is falling!");
// Execution will never reach this point.
}
Compilers will typically complain at this, saying that "not all control paths return a value". Which is technically true, but the control paths that don't return a value abort the program before the function ends, and are therefore semantically correct. Is there a way to tell the compiler (VS2010 in my case, but I'm curious about others as well) that a certain control path is to be ignored for the purposes of this check, without suppressing the warning completely or returning a nonsensical dummy value at the end of the function?
You can annotate the function fatalError (its declaration) to let the compiler know it will never return.
In C++11, this would be something like:
[[noreturn]] void fatalError(std::string const&);
Pre C++11, you have compiler specific attributes, such as GCC's:
void fatalError(std::string const&) __attribute__((noreturn));
or Visual Studio's:
__declspec(noreturn) void fatalError(std::string const&);
Why don't you throw an exception? That would solve the problem and it would force the calling method to deal with the exception.
If you did manage to haggle the warning out some way or other, you are still left with having to do something with the function that calls getThingy(). What happens when getThingy() fails? How will the caller know? What you have here is an exception (conceptually) and your design should reflect that.
You can use a run time assertion in lieu of your fatalError routine. This would just look like:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// Clean up and error condition reporting go here.
assert(false);
}