I'm trying to figure out exactly how the intrinsics/libc/libm work in llvm.
Intrinsics are basically "lesser" instructions that aren't guaranteed
to have first-class support (e.g., work on all targets). They are
specified in the LangRef.
Target intrinsics are similar to generic intrinsics, but are
specifically only available on one target. They are (intentionally
under-) specified in the various Intrinsics<Target>.td files.
Some functions (libc, libm, and a few others) are recognized as having
well-defined behavior for the target platform; this information is in
TargetLibraryInfo.
For example, this code return user defined function:
float acos(float x, float y)
{
return x+y;
}
float a;
void foo(float b, float c)
{
a = acos(b, c);
}
But this code returns llvm.intrinsic:
float acos(float, float);
float a;
void foo(float b, float c)
{
a = acos(b, c);
}
float acos(float x, float y)
{
return x+y;
}
What is the expected behavior here?
I don't see how they can behave differently. What IR are you seeing?
Also, there are certain "standard C library functions" included in LLVM that
I'd like to remove without modifying core code, is this possible?
If you're using clang: -fno-builtin=acos is what you're looking for.
If you're using llvm: when setting up your pass pipeline, you should
create an instance of TargetLibraryInfo (an example is in
tools/opt/opt.cpp), and either use:
- setUnavailable(LibFunc::acos): this marks acos as "unavailable",
preventing optimizations from making assumptions about its behavior.
Equivalent to clang -fno-builtin-acos
- disableAllFunctions(): this marks all known functions as
unavailable. Equivalent to clang -fno-builtin
I'm also curious how LLVM handles pure functions in regards to
optimizations. For example, libm functions such as acos.
Note that I don't think libm functions are pure on most platform,
because they can modify errno (-ffast-math/-fno-math-errno disables
that, though).
It appears that X86
doesn't have acos as an intrinsic and instead just calls a function "tail
call @acos...", is this still able to be optimized.
Yes, because TLI knows about the name 'acos'. However, the prototype
needs to be reasonably correct ('double @acos(double)'), but isn't in
your example. (Specifically, ConstantFolding checks that @acos only
has one operand, but yours has two.)
I see the hardcoded
'name' lookup for 'acos' in ConstantFolding.cpp. It doesn't appear that if
you slightly change the name from 'acos' to say 'XXX_acos' in your libm
it'll still be optimized?
Correct, it won't be.
It's possible to make that happen with a few patches, but there has
been no need for that yet:
- replace the various calls to TLI::has() or
TLI::getLibFunc(StringRef, Func&) with TLI::getLibFunc(Function&,
Func&). Any of the former are probably hiding bugs related to
incorrect prototypes
- teach TLI::getLibFunc to check function availability using the
custom name instead of always checking the standard name. Again, this
is (arguably) hiding bugs, where we recognize the standard name even
though it's not what the target uses
- fix canConstantFoldCallTo to pass it TLI or maybe remove it entirely
(it's always used before ConstantFoldCall, which does check TLI)
- tell TLI that 'acos' is available with name 'XXX_acos' for your target
-Ahmed