Why did "llvm.memcpy" changed when emitting an object file?

Dear LLVM-Mailing list,

I have a beginners question again - please don’t be mad at me!

I was playing around with llvm::Module instance and ended in iterating over all global values and functions. I then noticed the function “llvm.memcpy”. As I understood, this is an intrinsic coming from the LLVM.

So far so good! I then emitted an object file with the llvm::ExecutionEngine and noticed an undefined references to “memcpy”. Now I’m surprised! To me, it looks like that the intrinsic turned into a ‘normal’ function. I checked the iteration of the functions and were not able to locate any “memcpy” there, this is why I’m assuming the intrinsic changed.

I wonder if this is true - I always thought that intrinsics will be replaced by specific predefined assembly instructions, even the MSVC compiler provides a memcpy intrinsic, so I don’t understand why this function would appear, when it was an intrinsic before. Did I maybe configured the EngineBuilder wrong? Or the ExecutionEngine itself?

Kind greetings

Björn

So far so good! I then emitted an object file with the llvm::ExecutionEngine and noticed an undefined references to "memcpy". Now I'm surprised! To me, it looks like that the intrinsic turned into a 'normal' function.

That sounds very likely.

I always thought that intrinsics will be replaced by specific predefined assembly instructions, even the MSVC compiler provides a memcpy intrinsic, so I don't understand why this function would appear, when it was an intrinsic before.

LLVM assumes the runtime environment will provide basic string
functions (memcpy, and I think memset). Combine that with the fact
that @llvm.memcpy is more of a request to produce the best possible
memcpy rather than specifically to inline the sequence and we get the
behaviour you're seeing. LLVM will use an inline sequence if it thinks
that's going to be most efficient, otherwise it will call out to the
library one (typically when the number of bytes being copied is
unknown or large, and in -Oz mode).

Cheers.

Tim.

Hi Björn,

Your observation is correct, LLVM can "recreate" actual function calls
for intrinsics calls.

Generally, there is no single way LLVM intrinsics are lowered. It
depends on the intrinsic and the target (afaik). Some, especially target
specific ones, are lowered to assembly sequences. Others, are simply
dropped or replaced by one of their arguments (some annotations).
Memory operations, among others, can be "lowered" to calls to library
calls, e.g., in IntrinsicLowering::LowerIntrinsicCall. Though, memcpy
can also be lowered to a sequence of load/stores in
SelectionDAG::getMemcpy() (via getMemcpyLoadsAndStores). I guess the
fact that you use the JIT interface might be a reason you see the former
lowering but I don't know for sure.

I hope this general answer helps, otherwise you can probably get a more
detailed response from a backend person.

Cheers,
  Johannes

Hello Johannes and Tim,

Thank you for your answers! They really helped me.

Can I influence if intrinsics are lowered or not?
I set llvm::EngineBuilder::setOptLevel to "llvm::CodeGenOpt::Aggressive"

And I couldn't find any flag in llvm::TargetOptions that might effect this behavior.

Kind greetings
Björn

You can't guarantee memcpy won't be called, if that's what you mean.
If nothing else, most targets simply don't have the code to build a
physical loop to do the copy. You have to find a way to provide it in
your JIT.

Cheers.

Tim.

Sorry, I was not precise enough with my question.

I wanted to ask if I can give the JIT a hint how it should treat those intrinsic functions. Like "always create a function call" or "always use the intrinsic if available". I know that the Clang compiler has such flags but I don't know if the JIT interfaces has those too.

Kind greetings
Björn

Which flags are you talking about for Clang? The closest I'm aware of
is something like -msoft-float, which applies more to instructions
than intrinsics.

Cheers.

Tim.