diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -3233,21 +3233,21 @@ void visitCallSite(CallSite CS) { Instruction &I = *CS.getInstruction(); assert(!I.getMetadata("nosanitize")); - assert((CS.isCall() || CS.isInvoke()) && "Unknown type of CallSite"); + assert((CS.isCall() || CS.isInvoke() || CS.isCallBr()) && + "Unknown type of CallSite"); + if (CS.isCallBr() || (CS.isCall() && cast(&I)->isInlineAsm())) { + // For inline asm (either a call to asm function, or callbr instruction), + // do the usual thing: check argument shadow and mark all outputs as + // clean. Note that any side effects of the inline asm that are not + // immediately visible in its constraints are not handled. + if (ClHandleAsmConservative && MS.CompileKernel) + visitAsmInstruction(I); + else + visitInstruction(I); + return; + } if (CS.isCall()) { CallInst *Call = cast(&I); - - // For inline asm, do the usual thing: check argument shadow and mark all - // outputs as clean. Note that any side effects of the inline asm that are - // not immediately visible in its constraints are not handled. - if (Call->isInlineAsm()) { - if (ClHandleAsmConservative && MS.CompileKernel) - visitAsmInstruction(I); - else - visitInstruction(I); - return; - } - assert(!isa(&I) && "intrinsics are handled elsewhere"); // We are going to insert code that relies on the fact that the callee @@ -3624,10 +3624,10 @@ } /// Get the number of output arguments returned by pointers. - int getNumOutputArgs(InlineAsm *IA, CallInst *CI) { + int getNumOutputArgs(InlineAsm *IA, CallBase *CB) { int NumRetOutputs = 0; int NumOutputs = 0; - Type *RetTy = dyn_cast(CI)->getType(); + Type *RetTy = dyn_cast(CB)->getType(); if (!RetTy->isVoidTy()) { // Register outputs are returned via the CallInst return value. StructType *ST = dyn_cast_or_null(RetTy); @@ -3667,24 +3667,24 @@ // corresponding CallInst has nO+nI+1 operands (the last operand is the // function to be called). const DataLayout &DL = F.getParent()->getDataLayout(); - CallInst *CI = dyn_cast(&I); + CallBase *CB = dyn_cast(&I); IRBuilder<> IRB(&I); - InlineAsm *IA = cast(CI->getCalledValue()); - int OutputArgs = getNumOutputArgs(IA, CI); + InlineAsm *IA = cast(CB->getCalledValue()); + int OutputArgs = getNumOutputArgs(IA, CB); // The last operand of a CallInst is the function itself. - int NumOperands = CI->getNumOperands() - 1; + int NumOperands = CB->getNumOperands() - 1; // Check input arguments. Doing so before unpoisoning output arguments, so // that we won't overwrite uninit values before checking them. for (int i = OutputArgs; i < NumOperands; i++) { - Value *Operand = CI->getOperand(i); + Value *Operand = CB->getOperand(i); instrumentAsmArgument(Operand, I, IRB, DL, /*isOutput*/ false); } // Unpoison output arguments. This must happen before the actual InlineAsm // call, so that the shadow for memory published in the asm() statement // remains valid. for (int i = 0; i < OutputArgs; i++) { - Value *Operand = CI->getOperand(i); + Value *Operand = CB->getOperand(i); instrumentAsmArgument(Operand, I, IRB, DL, /*isOutput*/ true); } diff --git a/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll b/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll --- a/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/msan_asm_conservative.ll @@ -264,3 +264,34 @@ ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1) ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8) ; CHECK: call void asm "", "=*m,=*m,=*m,*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(%struct.pair* @pair2, i8* @c2, i8* (i8*, i8*, i32)** @memcpy_d1, %struct.pair* @pair1, i8* @c1, i8* (i8*, i8*, i32)** @memcpy_s1) + + +; A simple asm goto construct to check that callbr is handled correctly: +; int asm_goto(int n) { +; int v = 1; +; asm goto("cmp %0, %1; jnz %l2;" :: "r"(n), "r"(v)::skip_label); +; return 0; +; skip_label: +; return 1; +; } +; asm goto statements can't have outputs, so just make sure we check the input +; and the compiler doesn't crash. +define dso_local i32 @asm_goto(i32 %n) sanitize_memory { +entry: + callbr void asm sideeffect "cmp $0, $1; jnz ${2:l}", "r,r,X,~{dirflag},~{fpsr},~{flags}"(i32 %n, i32 1, i8* blockaddress(@asm_goto, %skip_label)) + to label %cleanup [label %skip_label] + +skip_label: ; preds = %entry + br label %cleanup + +cleanup: ; preds = %entry, %skip_label + %retval.0 = phi i32 [ 2, %skip_label ], [ 1, %entry ] + ret i32 %retval.0 +} + +; CHECK-LABEL: @asm_goto +; CHECK: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg +; CHECK: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0 +; CHECK: br {{.*}} [[CMP]], label %[[LABEL:.*]], label +; CHECK: [[LABEL]]: +; CHECK-NEXT: call void @__msan_warning