[Verifier] add invariant check for callbr

Summary:
The list of indirect labels should ALWAYS have their blockaddresses as
argument operands to the callbr (but not necessarily the other way
around).  Add an invariant that checks this.

The verifier catches a bad test case that was added recently in r368478.
I think that was a simple mistake, and the test was made less strict in
regards to the precise addresses (as those weren't specifically the
point of the test).

This invariant will be used to find a reported bug.

Link: https://www.spinics.net/lists/arm-kernel/msg753473.html
Link: https://github.com/ClangBuiltLinux/linux/issues/649

Reviewers: craig.topper, void, chandlerc

Reviewed By: void

Subscribers: ychen, lebedev.ri, javed.absar, kristof.beyls, hiraditya, llvm-commits, srhines

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D67196

llvm-svn: 372923
This commit is contained in:
Nick Desaulniers 2019-09-25 22:28:27 +00:00
parent a773cb1281
commit 4e5aa08380
4 changed files with 77 additions and 17 deletions

View File

@ -7070,7 +7070,7 @@ Syntax:
::
<result> = callbr [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
[operand bundles] to label <normal label> or jump [other labels]
[operand bundles] to label <normal label> [other labels]
Overview:
"""""""""
@ -7114,7 +7114,8 @@ This instruction requires several arguments:
#. '``normal label``': the label reached when the called function
executes a '``ret``' instruction.
#. '``other labels``': the labels reached when a callee transfers control
to a location other than the normal '``normal label``'
to a location other than the normal '``normal label``'. The blockaddress
constant for these should also be in the list of '``function args``'.
#. The optional :ref:`function attributes <fnattrs>` list.
#. The optional :ref:`operand bundles <opbundles>` list.
@ -7136,7 +7137,7 @@ Example:
.. code-block:: text
callbr void asm "", "r,x"(i32 %x, i8 *blockaddress(@foo, %fail))
to label %normal or jump [label %fail]
to label %normal [label %fail]
.. _i_resume:

View File

@ -2504,6 +2504,15 @@ void Verifier::visitCallBrInst(CallBrInst &CBI) {
Assert(CBI.getOperand(i) != CBI.getOperand(j),
"Duplicate callbr destination!", &CBI);
}
{
SmallPtrSet<BasicBlock *, 4> ArgBBs;
for (Value *V : CBI.args())
if (auto *BA = dyn_cast<BlockAddress>(V))
ArgBBs.insert(BA->getBasicBlock());
for (BasicBlock *BB : CBI.getIndirectDests())
Assert(ArgBBs.find(BB) != ArgBBs.end(),
"Indirect label missing from arglist.", &CBI);
}
visitTerminator(CBI);
}

View File

@ -6,11 +6,11 @@
@l = common hidden local_unnamed_addr global i32 0, align 4
; CHECK-LABEL: 0000000000000000 test1:
; CHECK-LABEL: 0000000000000018 $d.1:
; CHECK-LABEL: 0000000000000020 $x.2:
; CHECK-LABEL: test1:
; CHECK-LABEL: $d.1:
; CHECK-LABEL: $x.2:
; CHECK-NEXT: b #16 <$x.4+0x4>
; CHECK-LABEL: 000000000000002c $x.4:
; CHECK-LABEL: $x.4:
; CHECK-NEXT: b #4 <$x.4+0x4>
; CHECK-NEXT: mov w0, wzr
; CHECK-NEXT: ldr x30, [sp], #16
@ -40,11 +40,11 @@ declare dso_local i32 @g(...) local_unnamed_addr
declare dso_local i32 @i(...) local_unnamed_addr
; CHECK-LABEL: 000000000000003c test2:
; CHECK: bl #0 <test2+0x18>
; CHECK-LABEL: 0000000000000064 $d.5:
; CHECK-LABEL: 000000000000006c $x.6:
; CHECK-NEXT: b #-24 <test2+0x18>
; CHECK-LABEL: test2:
; CHECK: bl #0 <test2+0x10>
; CHECK-LABEL: $d.5:
; CHECK-LABEL: $x.6:
; CHECK-NEXT: b #16 <$x.8+0x4>
define hidden i32 @test2() local_unnamed_addr {
%1 = load i32, i32* @l, align 4
%2 = icmp eq i32 %1, 0
@ -57,7 +57,7 @@ define hidden i32 @test2() local_unnamed_addr {
6: ; preds = %3
callbr void asm sideeffect "1: nop\0A\09.quad b\0A\09b ${1:l}\0A\09.quad ${0:c}", "i,X"(i32* null, i8* blockaddress(@test2, %7))
to label %10 [label %9]
to label %10 [label %7]
7: ; preds = %3
%8 = tail call i32 bitcast (i32 (...)* @i to i32 ()*)()
@ -70,11 +70,11 @@ define hidden i32 @test2() local_unnamed_addr {
ret i32 undef
}
; CHECK-LABEL: 0000000000000084 test3:
; CHECK-LABEL: 00000000000000a8 $d.9:
; CHECK-LABEL: 00000000000000b0 $x.10:
; CHECK-LABEL: test3:
; CHECK-LABEL: $d.9:
; CHECK-LABEL: $x.10:
; CHECK-NEXT: b #20 <$x.12+0x8>
; CHECK-LABEL: 00000000000000bc $x.12:
; CHECK-LABEL: $x.12:
; CHECK-NEXT: b #4 <$x.12+0x4>
; CHECK-NEXT: mov w0, wzr
; CHECK-NEXT: ldr x30, [sp], #16

50
test/Verifier/callbr.ll Normal file
View File

@ -0,0 +1,50 @@
; RUN: not opt -S %s -verify 2>&1 | FileCheck %s
; CHECK: Indirect label missing from arglist.
define void @foo() {
; The %4 in the indirect label list is not found in the blockaddresses in the
; arg list (bad).
callbr void asm sideeffect "${0:l} {1:l}", "X,X"(i8* blockaddress(@foo, %3), i8* blockaddress(@foo, %2))
to label %1 [label %4, label %2]
1:
ret void
2:
ret void
3:
ret void
4:
ret void
}
; CHECK-NOT: Indirect label missing from arglist.
define void @bar() {
; %4 and %2 are both in the indirect label list and the arg list (good).
callbr void asm sideeffect "${0:l} ${1:l}", "X,X"(i8* blockaddress(@bar, %4), i8* blockaddress(@bar, %2))
to label %1 [label %4, label %2]
1:
ret void
2:
ret void
3:
ret void
4:
ret void
}
; CHECK-NOT: Indirect label missing from arglist.
define void @baz() {
; note %2 blockaddress. Such a case is possible when passing the address of
; a label as an input to the inline asm (both address of label and asm goto
; use blockaddress constants; we're testing that the indirect label list from
; the asm goto is in the arg list to the asm).
callbr void asm sideeffect "${0:l} ${1:l} ${2:l}", "X,X,X"(i8* blockaddress(@baz, %4), i8* blockaddress(@baz, %2), i8* blockaddress(@baz, %3))
to label %1 [label %3, label %4]
1:
ret void
2:
ret void
3:
ret void
4:
ret void
}