diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -4568,6 +4568,9 @@ statement, and it can only produce worse code, since it bypasses many optimization passes. I would recommend not using it.) +Call arguments for indirect constraints must have pointer type and must specify +the :ref:`elementtype ` attribute to indicate the pointer +element type. Clobber constraints """"""""""""""""""" diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -551,11 +551,12 @@ void checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr, const Value *V); void verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, - const Value *V, bool IsIntrinsic); + const Value *V, bool IsIntrinsic, bool IsInlineAsm); void verifyFunctionMetadata(ArrayRef> MDs); void visitConstantExprsRecursively(const Constant *EntryC); void visitConstantExpr(const ConstantExpr *CE); + void verifyInlineAsmCall(const CallBase &Call); void verifyStatepoint(const CallBase &Call); void verifyFrameRecoverIndices(); void verifySiblingFuncletUnwinds(); @@ -1870,7 +1871,8 @@ // Check parameter attributes against a function type. // The value V is printed in error messages. void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, - const Value *V, bool IsIntrinsic) { + const Value *V, bool IsIntrinsic, + bool IsInlineAsm) { if (Attrs.isEmpty()) return; @@ -1913,8 +1915,10 @@ if (!IsIntrinsic) { Assert(!ArgAttrs.hasAttribute(Attribute::ImmArg), "immarg attribute only applies to intrinsics",V); - Assert(!ArgAttrs.hasAttribute(Attribute::ElementType), - "Attribute 'elementtype' can only be applied to intrinsics.", V); + if (!IsInlineAsm) + Assert(!ArgAttrs.hasAttribute(Attribute::ElementType), + "Attribute 'elementtype' can only be applied to intrinsics" + " and inline asm.", V); } verifyParameterAttrs(ArgAttrs, Ty, V); @@ -2141,6 +2145,33 @@ return Attrs.getNumAttrSets() <= Params + 2; } +void Verifier::verifyInlineAsmCall(const CallBase &Call) { + const InlineAsm *IA = cast(Call.getCalledOperand()); + unsigned ArgNo = 0; + for (const InlineAsm::ConstraintInfo &CI : IA->ParseConstraints()) { + // Only deal with constraints that correspond to call arguments. + bool HasArg = CI.Type == InlineAsm::isInput || + (CI.Type == InlineAsm::isOutput && CI.isIndirect); + if (!HasArg) + continue; + + if (CI.isIndirect) { + const Value *Arg = Call.getArgOperand(ArgNo); + Assert(Arg->getType()->isPointerTy(), + "Operand for indirect constraint must have pointer type", + &Call); + + // TODO: Require elementtype attribute here. + } else { + Assert(!Call.paramHasAttr(ArgNo, Attribute::ElementType), + "Elementtype attribute can only be applied for indirect " + "constraints", &Call); + } + + ArgNo++; + } +} + /// Verify that statepoint intrinsic is well formed. void Verifier::verifyStatepoint(const CallBase &Call) { assert(Call.getCalledFunction() && @@ -2364,7 +2395,7 @@ bool IsIntrinsic = F.isIntrinsic(); // Check function attributes. - verifyFunctionAttrs(FT, Attrs, &F, IsIntrinsic); + verifyFunctionAttrs(FT, Attrs, &F, IsIntrinsic, /* IsInlineAsm */ false); // On function declarations/definitions, we do not support the builtin // attribute. We do not check this in VerifyFunctionAttrs since that is @@ -2779,6 +2810,7 @@ Assert(ArgBBs.count(BB), "Indirect label missing from arglist.", &CBI); } + verifyInlineAsmCall(CBI); visitTerminator(CBI); } @@ -3123,7 +3155,7 @@ } // Verify call attributes. - verifyFunctionAttrs(FTy, Attrs, &Call, IsIntrinsic); + verifyFunctionAttrs(FTy, Attrs, &Call, IsIntrinsic, Call.isInlineAsm()); // Conservatively check the inalloca argument. // We have a bug if we can find that there is an underlying alloca without @@ -3316,6 +3348,9 @@ "debug info must have a !dbg location", Call); + if (Call.isInlineAsm()) + verifyInlineAsmCall(Call); + visitInstruction(Call); } diff --git a/llvm/test/Verifier/elementtype.ll b/llvm/test/Verifier/elementtype.ll --- a/llvm/test/Verifier/elementtype.ll +++ b/llvm/test/Verifier/elementtype.ll @@ -14,7 +14,7 @@ ret void } -; CHECK: Attribute 'elementtype' can only be applied to intrinsics. +; CHECK: Attribute 'elementtype' can only be applied to intrinsics and inline asm. define void @not_intrinsic() { call void @some_function(i32* elementtype(i32) null) ret void diff --git a/llvm/test/Verifier/inline-asm-indirect-operand.ll b/llvm/test/Verifier/inline-asm-indirect-operand.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/inline-asm-indirect-operand.ll @@ -0,0 +1,52 @@ +; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s + +define void @okay(i32* %p, i32 %x) { + call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i32) %p, i32 %x) + ret void +} + +; CHECK: Attribute 'elementtype' type does not match parameter! +; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i64) %p, i32 %x) +define void @wrong_element_type(i32* %p, i32 %x) { + call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i64) %p, i32 %x) + ret void +} + +; CHECK: Operand for indirect constraint must have pointer type +; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) +define void @not_pointer_arg(i32 %p, i32 %x) { + call void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) + ret void +} + +; CHECK: Elementtype attribute can only be applied for indirect constraints +; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32* %p, i32* elementtype(i32) %x) +define void @not_indirect(i32* %p, i32* %x) { + call void asm "addl $1, $0", "=*rm,r"(i32* %p, i32* elementtype(i32) %x) + ret void +} + +; CHECK: Operand for indirect constraint must have pointer type +; CHECK-NEXT: invoke void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) +define void @not_pointer_arg_invoke(i32 %p, i32 %x) personality i8* null { + invoke void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) + to label %cont unwind label %lpad + +lpad: + %lp = landingpad i32 + cleanup + ret void + +cont: + ret void +} + +; CHECK: Operand for indirect constraint must have pointer type +; CHECK-NEXT: callbr void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) +define void @not_pointer_arg_callbr(i32 %p, i32 %x) { + callbr void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x) + to label %cont [] + +cont: + ret void +}