I find that there are some intrinsic functions in LLVM such as llvm.memcpy
, llvm.va_start
.
However I haven't got any idea why they exist and why others don't. For example, as memcpy
's prototype is inside string.h
, why other functions, like strcpy
, are not treated as intrinsic?
I noticed that the frontend may generate special intrinsic function call in some cases. For a simple case:
#include<string.h>
int foo(void){
char str[10] = "str";
return 0;
}
The llvm IR for foo
generated by clang is:
define i32 @foo() #0 {
entry:
%str = alloca [10 x i8], align 1
%0 = bitcast [10 x i8]* %str to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* getelementptr inbounds ([10 x i8]* @foo.str, i32 0, i32 0), i64 10, i32 1, i1 false)
ret i32 0
}
llvm.memcpy
is called in IR but is not in source code. But can the frontend generate LLVM IR without this intrinsic?
I also came across a document about a much earlier version of llvm language reference and found that some special functions like malloc
, free
were included in LLVM instructions(obviously they no longer exist).
So what's the insight that the llvm instruction is designed so?
Of course, you can do what your example shows without memcpy - just a bit harder (well, maybe not with only 4 bytes, which could be done in four single byte moves, not much harder than memcpy - on the other hand, if your string that you initialize str with is 128 bytes [and str
suitably long enough to hold it], a sequence of 128 single byte moves would be rather awkward, and generating a loop is also a bit clumsy).
However, the main point of intrinsic functions is to allow the compiler (backend) to "understand what goes on", because it will be able to determine [for constants at least] what size the copy is, and, for example, generate two 32-bit moves to store the "str"
value into your str
variable. Or, if the amount is large, call real memcpy
, or make a loop for intermediate sizes.
In the end, the simple answer is "because the compiler can generate better code than the alternative solutions".
The reason for NOT having strcpy
, I'm guessing, is that strcpy
can be replaced (more efficiently) with memcpy
for constant strings, and if the string is not constant, strcpy
is a bit more complicated than memcpy
anyway, so not as beneficial to make inline optimisations for.
In theory all kinds of functionality could be made intrinsic, but it's a "cost/benefit" analysis that has to be done - how much do you gain, and how long does it take to write the code to do that.
[Of course, I'm only deducing this from my experience using LLVM, I don't know this from someone who implemented intrinsic functions in LLVM]
Having intrinsic functions makes it easier to extend LLVM to take advantage of capabilities of hardware to perform specialized operations that would otherwise have to be coded in software.
Some operations, such as copying data from one place to another, can be performed completely by hardware in some CPU types, but in others have to be coded as a normal function.
Using these intrinsic functions allows LLVM to output a call to an intrinsic, which then later gets converted (by the coder) into the most efficient form for the target processor - either specialized machine instructions, or calls to actual functions.
In theory you could have separate special IR instructions covering all these cases, however that would not be very extensible. The number of instructions that would have to be created would expand significantly over time.
In the LLVM docs, it says
Almost all extensions to LLVM should start as an intrinsic function and then be turned into an instruction if warranted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With