Skip to content

Commit 7804cf1

Browse files
committed
Annotate intrinsic calls with inlining decisions.
When using intrinsics, such as `memcpy`, in LLVM IR, the compiler may decide to inline/optimise them during codegen, or leave them as is. This makes mapping a PT trace back to LLVM IR difficult, since we don't know whether to expect a hole in the trace (from the call to the intrinsic) or not. There's two ways to solve this: 1) Remove all optimisations that inline intrinsic 2) annotate intrinsics with metadata so we can identify inlined intrinsics during trace construction (in `JITModBuilder`). The problem with the first approach is that these optimisations are not in a single place and spread out through different optimisation levels and even architectures. This makes them easy to miss, which we will only notice when traces behave in unexpected ways (if we're lucky the trace compiler crashes). We can solve this problem with the second approach. By annotating an intrinsic, we can check during trace construction if it was inlined and behave accordingly. And by annotating an intrisinc in both inlined and not inlined cases, we can check if we've missed an intrinsic (i.e. it has no annotation) and crash the trace compiler. This second solution sounds much better, but comes with a small caveat. It requires a nasty cast from a constant to a non-constant in the codegen part of LLVM. I can picture the horror written in @ltratt's face upon reading this, but here's my reasoning: 1) casting from const to non-const is only UB if the variable is a real `const`, which LLVM IR is not 2) I believe the reason LLVM makes instructions `const` is so they don't accidentally alter the IR during codegen. Adding metadata doesn't semantically change the IR and so has no effect on the codegen. I thus believe the second solution to be the better option, which I have implemented here, starting with the `memcpy` intrinsic.
1 parent 7667f19 commit 7804cf1

File tree

1 file changed

+13
-0
lines changed

1 file changed

+13
-0
lines changed

llvm/lib/Target/X86/X86FastISel.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,17 @@ bool X86FastISel::TryEmitSmallMemcpy(X86AddressMode DestAM,
25882588
return true;
25892589
}
25902590

2591+
// Add an annotation to an intrinsic instruction, specifying whether the
2592+
// intrinsic has been inlined or not.
2593+
void annotateIntrinsic(const IntrinsicInst *II, bool Inlined) {
2594+
IntrinsicInst *CI = const_cast<IntrinsicInst *>(II);
2595+
LLVMContext& C = CI->getContext();
2596+
ConstantInt *CInt;
2597+
CInt = ConstantInt::get(C, APInt(1, Inlined ? 1: 0));
2598+
MDNode* N = MDNode::get(C, ConstantAsMetadata::get(CInt));
2599+
CI->setMetadata("yk.intrinsic.inlined", N);
2600+
}
2601+
25912602
bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) {
25922603
// FIXME: Handle more intrinsics.
25932604
switch (II->getIntrinsicID()) {
@@ -2725,6 +2736,7 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) {
27252736
// without a call if possible.
27262737
uint64_t Len = cast<ConstantInt>(MCI->getLength())->getZExtValue();
27272738
if (IsMemcpySmall(Len)) {
2739+
annotateIntrinsic(II, true);
27282740
X86AddressMode DestAM, SrcAM;
27292741
if (!X86SelectAddress(MCI->getRawDest(), DestAM) ||
27302742
!X86SelectAddress(MCI->getRawSource(), SrcAM))
@@ -2741,6 +2753,7 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) {
27412753
if (MCI->getSourceAddressSpace() > 255 || MCI->getDestAddressSpace() > 255)
27422754
return false;
27432755

2756+
annotateIntrinsic(II, false);
27442757
return lowerCallTo(II, "memcpy", II->getNumArgOperands() - 1);
27452758
}
27462759
case Intrinsic::memset: {

0 commit comments

Comments
 (0)