Skip to content

Commit 8484bab

Browse files
committed
[LangRef] Require elementtype attribute for indirect inline asm operands
Indirect inline asm operands may require the materialization of a memory access according to the pointer element type. As this will no longer be available with opaque pointers, we require it to be explicitly annotated using the elementtype attribute, for example: define void @test(i32* %p, i32 %x) { call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i32) %p, i32 %x) ret void } This patch only includes the LangRef change and Verifier updates to allow adding the elementtype attribute in this position. It does not yet enforce this, as this will require changes on the clang side (and test updates) first. Something I'm a bit unsure about is whether we really need the elementtype for all indirect constraints, rather than only indirect register constraints. I think indirect memory constraints might not strictly need it (though the backend code is written in a way that does require it). I think it's okay to just make this a general requirement though, as this means we don't need to carefully deal with multiple or alternative constraints. In addition, I believe that MemorySanitizer benefits from having the element type even in cases where it may not be strictly necessary for normal lowering (https://github.com/llvm/llvm-project/blob/cd2b050fa4995b75b9c36fae16c0d9f105b67585/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp#L4066). Differential Revision: https://reviews.llvm.org/D116531
1 parent 29e6e52 commit 8484bab

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

llvm/docs/LangRef.rst

+3
Original file line numberDiff line numberDiff line change
@@ -4568,6 +4568,9 @@ functionality provides, compared to writing the store explicitly after the asm
45684568
statement, and it can only produce worse code, since it bypasses many
45694569
optimization passes. I would recommend not using it.)
45704570

4571+
Call arguments for indirect constraints must have pointer type and must specify
4572+
the :ref:`elementtype <attr_elementtype>` attribute to indicate the pointer
4573+
element type.
45714574

45724575
Clobber constraints
45734576
"""""""""""""""""""

llvm/lib/IR/Verifier.cpp

+41-6
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,12 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
551551
void checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr,
552552
const Value *V);
553553
void verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
554-
const Value *V, bool IsIntrinsic);
554+
const Value *V, bool IsIntrinsic, bool IsInlineAsm);
555555
void verifyFunctionMetadata(ArrayRef<std::pair<unsigned, MDNode *>> MDs);
556556

557557
void visitConstantExprsRecursively(const Constant *EntryC);
558558
void visitConstantExpr(const ConstantExpr *CE);
559+
void verifyInlineAsmCall(const CallBase &Call);
559560
void verifyStatepoint(const CallBase &Call);
560561
void verifyFrameRecoverIndices();
561562
void verifySiblingFuncletUnwinds();
@@ -1870,7 +1871,8 @@ void Verifier::checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr,
18701871
// Check parameter attributes against a function type.
18711872
// The value V is printed in error messages.
18721873
void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
1873-
const Value *V, bool IsIntrinsic) {
1874+
const Value *V, bool IsIntrinsic,
1875+
bool IsInlineAsm) {
18741876
if (Attrs.isEmpty())
18751877
return;
18761878

@@ -1913,8 +1915,10 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
19131915
if (!IsIntrinsic) {
19141916
Assert(!ArgAttrs.hasAttribute(Attribute::ImmArg),
19151917
"immarg attribute only applies to intrinsics",V);
1916-
Assert(!ArgAttrs.hasAttribute(Attribute::ElementType),
1917-
"Attribute 'elementtype' can only be applied to intrinsics.", V);
1918+
if (!IsInlineAsm)
1919+
Assert(!ArgAttrs.hasAttribute(Attribute::ElementType),
1920+
"Attribute 'elementtype' can only be applied to intrinsics"
1921+
" and inline asm.", V);
19181922
}
19191923

19201924
verifyParameterAttrs(ArgAttrs, Ty, V);
@@ -2141,6 +2145,33 @@ bool Verifier::verifyAttributeCount(AttributeList Attrs, unsigned Params) {
21412145
return Attrs.getNumAttrSets() <= Params + 2;
21422146
}
21432147

2148+
void Verifier::verifyInlineAsmCall(const CallBase &Call) {
2149+
const InlineAsm *IA = cast<InlineAsm>(Call.getCalledOperand());
2150+
unsigned ArgNo = 0;
2151+
for (const InlineAsm::ConstraintInfo &CI : IA->ParseConstraints()) {
2152+
// Only deal with constraints that correspond to call arguments.
2153+
bool HasArg = CI.Type == InlineAsm::isInput ||
2154+
(CI.Type == InlineAsm::isOutput && CI.isIndirect);
2155+
if (!HasArg)
2156+
continue;
2157+
2158+
if (CI.isIndirect) {
2159+
const Value *Arg = Call.getArgOperand(ArgNo);
2160+
Assert(Arg->getType()->isPointerTy(),
2161+
"Operand for indirect constraint must have pointer type",
2162+
&Call);
2163+
2164+
// TODO: Require elementtype attribute here.
2165+
} else {
2166+
Assert(!Call.paramHasAttr(ArgNo, Attribute::ElementType),
2167+
"Elementtype attribute can only be applied for indirect "
2168+
"constraints", &Call);
2169+
}
2170+
2171+
ArgNo++;
2172+
}
2173+
}
2174+
21442175
/// Verify that statepoint intrinsic is well formed.
21452176
void Verifier::verifyStatepoint(const CallBase &Call) {
21462177
assert(Call.getCalledFunction() &&
@@ -2364,7 +2395,7 @@ void Verifier::visitFunction(const Function &F) {
23642395
bool IsIntrinsic = F.isIntrinsic();
23652396

23662397
// Check function attributes.
2367-
verifyFunctionAttrs(FT, Attrs, &F, IsIntrinsic);
2398+
verifyFunctionAttrs(FT, Attrs, &F, IsIntrinsic, /* IsInlineAsm */ false);
23682399

23692400
// On function declarations/definitions, we do not support the builtin
23702401
// attribute. We do not check this in VerifyFunctionAttrs since that is
@@ -2779,6 +2810,7 @@ void Verifier::visitCallBrInst(CallBrInst &CBI) {
27792810
Assert(ArgBBs.count(BB), "Indirect label missing from arglist.", &CBI);
27802811
}
27812812

2813+
verifyInlineAsmCall(CBI);
27822814
visitTerminator(CBI);
27832815
}
27842816

@@ -3123,7 +3155,7 @@ void Verifier::visitCallBase(CallBase &Call) {
31233155
}
31243156

31253157
// Verify call attributes.
3126-
verifyFunctionAttrs(FTy, Attrs, &Call, IsIntrinsic);
3158+
verifyFunctionAttrs(FTy, Attrs, &Call, IsIntrinsic, Call.isInlineAsm());
31273159

31283160
// Conservatively check the inalloca argument.
31293161
// We have a bug if we can find that there is an underlying alloca without
@@ -3316,6 +3348,9 @@ void Verifier::visitCallBase(CallBase &Call) {
33163348
"debug info must have a !dbg location",
33173349
Call);
33183350

3351+
if (Call.isInlineAsm())
3352+
verifyInlineAsmCall(Call);
3353+
33193354
visitInstruction(Call);
33203355
}
33213356

llvm/test/Verifier/elementtype.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ define void @type_mismatch2() {
1414
ret void
1515
}
1616

17-
; CHECK: Attribute 'elementtype' can only be applied to intrinsics.
17+
; CHECK: Attribute 'elementtype' can only be applied to intrinsics and inline asm.
1818
define void @not_intrinsic() {
1919
call void @some_function(i32* elementtype(i32) null)
2020
ret void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
2+
3+
define void @okay(i32* %p, i32 %x) {
4+
call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i32) %p, i32 %x)
5+
ret void
6+
}
7+
8+
; CHECK: Attribute 'elementtype' type does not match parameter!
9+
; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i64) %p, i32 %x)
10+
define void @wrong_element_type(i32* %p, i32 %x) {
11+
call void asm "addl $1, $0", "=*rm,r"(i32* elementtype(i64) %p, i32 %x)
12+
ret void
13+
}
14+
15+
; CHECK: Operand for indirect constraint must have pointer type
16+
; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
17+
define void @not_pointer_arg(i32 %p, i32 %x) {
18+
call void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
19+
ret void
20+
}
21+
22+
; CHECK: Elementtype attribute can only be applied for indirect constraints
23+
; CHECK-NEXT: call void asm "addl $1, $0", "=*rm,r"(i32* %p, i32* elementtype(i32) %x)
24+
define void @not_indirect(i32* %p, i32* %x) {
25+
call void asm "addl $1, $0", "=*rm,r"(i32* %p, i32* elementtype(i32) %x)
26+
ret void
27+
}
28+
29+
; CHECK: Operand for indirect constraint must have pointer type
30+
; CHECK-NEXT: invoke void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
31+
define void @not_pointer_arg_invoke(i32 %p, i32 %x) personality i8* null {
32+
invoke void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
33+
to label %cont unwind label %lpad
34+
35+
lpad:
36+
%lp = landingpad i32
37+
cleanup
38+
ret void
39+
40+
cont:
41+
ret void
42+
}
43+
44+
; CHECK: Operand for indirect constraint must have pointer type
45+
; CHECK-NEXT: callbr void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
46+
define void @not_pointer_arg_callbr(i32 %p, i32 %x) {
47+
callbr void asm "addl $1, $0", "=*rm,r"(i32 %p, i32 %x)
48+
to label %cont []
49+
50+
cont:
51+
ret void
52+
}

0 commit comments

Comments
 (0)