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<std::pair<unsigned, MDNode *>> 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();
// 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;
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);
return Attrs.getNumAttrSets() <= Params + 2;
}
+void Verifier::verifyInlineAsmCall(const CallBase &Call) {
+ const InlineAsm *IA = cast<InlineAsm>(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() &&
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
Assert(ArgBBs.count(BB), "Indirect label missing from arglist.", &CBI);
}
+ verifyInlineAsmCall(CBI);
visitTerminator(CBI);
}
}
// 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
"debug info must have a !dbg location",
Call);
+ if (Call.isInlineAsm())
+ verifyInlineAsmCall(Call);
+
visitInstruction(Call);
}
--- /dev/null
+; 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
+}