Skip to content

[msan] Add handleIntrinsicByApplyingToShadow; support NEON tbl/tbx #114490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 1, 2024
Merged
58 changes: 58 additions & 0 deletions llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3944,6 +3944,42 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
}

/// Handle intrinsics by applying the intrinsic to the shadows. The trailing
/// arguments are passed verbatim e.g., for an intrinsic with one trailing
/// verbatim argument:
/// out = intrinsic(var1, var2, opType)
/// we compute:
/// shadow[out] = intrinsic(shadow[var1], shadow[var2], opType)
///
/// For example, this can be applied to the Arm NEON vector table intrinsics
/// (tbl{1,2,3,4}).
///
/// The origin is approximated using setOriginForNaryOp.
void handleIntrinsicByApplyingToShadow(IntrinsicInst &I, unsigned int trailingVerbatimArgs) {
IRBuilder<> IRB(&I);

assert (trailingVerbatimArgs < I.arg_size());

SmallVector<Value *, 8> ShadowArgs;
// Don't use getNumOperands() because it includes the callee
for (unsigned int i = 0; i < I.arg_size(); i++) {
if (i < I.arg_size() - trailingVerbatimArgs) {
Value *Shadow = getShadow(&I, i);
ShadowArgs.append(1, Shadow);
} else {
Value *Arg = I.getArgOperand(i);
insertShadowCheck(Arg, &I);
ShadowArgs.append(1, Arg);
}
}

CallInst *CI =
IRB.CreateIntrinsic(I.getType(), I.getIntrinsicID(), ShadowArgs);
setShadow(&I, CI);

setOriginForNaryOp(I);
}

void visitIntrinsicInst(IntrinsicInst &I) {
switch (I.getIntrinsicID()) {
case Intrinsic::uadd_with_overflow:
Expand Down Expand Up @@ -4319,6 +4355,28 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
break;
}

// Arm NEON vector table intrinsics have the source/table register(s) as
// arguments, followed by the index register. They return the output.
//
// 'TBL writes a zero if an index is out-of-range, while TBX leaves the
// original value unchanged in the destination register.'
// Conveniently, zero denotes a clean shadow, which means out-of-range
// indices for TBL will initialize the user data with zero and also clean
// the shadow. (For TBX, neither the user data nor the shadow will be
// updated, which is also correct.)
case Intrinsic::aarch64_neon_tbl1:
case Intrinsic::aarch64_neon_tbl2:
case Intrinsic::aarch64_neon_tbl3:
case Intrinsic::aarch64_neon_tbl4:
case Intrinsic::aarch64_neon_tbx1:
case Intrinsic::aarch64_neon_tbx2:
case Intrinsic::aarch64_neon_tbx3:
case Intrinsic::aarch64_neon_tbx4: {
// The last trailing argument (index register) should be handled verbatim
handleIntrinsicByApplyingToShadow(I, 1);
break;
}

default:
if (!handleUnknownIntrinsic(I))
visitInstruction(I);
Expand Down
Loading
Loading