2019-02-09 04:48:56 +08:00
|
|
|
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
Allow "callbr" to return non-void values
Summary:
Terminators in LLVM aren't prohibited from returning values. This means that
the "callbr" instruction, which is used for "asm goto", can support "asm goto
with outputs."
This patch removes all restrictions against "callbr" returning values. The
heavy lifting is done by the code generator. The "INLINEASM_BR" instruction's
a terminator, and the code generator doesn't allow non-terminator instructions
after a terminator. In order to correctly model the feature, we need to copy
outputs from "INLINEASM_BR" into virtual registers. Of course, those copies
aren't terminators.
To get around this issue, we split the block containing the "INLINEASM_BR"
right before the "COPY" instructions. This results in two cheats:
- Any physical registers defined by "INLINEASM_BR" need to be marked as
live-in into the block with the "COPY" instructions. This violates an
assumption that physical registers aren't marked as "live-in" until after
register allocation. But it seems as if the live-in information only
needs to be correct after register allocation. So we're able to get away
with this.
- The indirect branches from the "INLINEASM_BR" are moved to the "COPY"
block. This is to satisfy PHI nodes.
I've been told that MLIR can support this handily, but until we're able to
use it, we'll have to stick with the above.
Reviewers: jyknight, nickdesaulniers, hfinkel, MaskRay, lattner
Reviewed By: nickdesaulniers, MaskRay, lattner
Subscribers: rriddle, qcolombet, jdoerfert, MatzeB, echristo, MaskRay, xbolva00, aaron.ballman, cfe-commits, JonChesterfield, hiraditya, llvm-commits, rnk, craig.topper
Tags: #llvm, #clang
Differential Revision: https://reviews.llvm.org/D69868
2020-02-25 10:28:32 +08:00
|
|
|
; RUN: llc < %s -mtriple=i686-- -O3 -verify-machineinstrs | FileCheck %s
|
2019-02-09 04:48:56 +08:00
|
|
|
|
|
|
|
; Tests for using callbr as an asm-goto wrapper
|
|
|
|
|
|
|
|
; Test 1 - fallthrough label gets removed, but the fallthrough code that is
|
|
|
|
; unreachable due to asm ending on a jmp is still left in.
|
|
|
|
define i32 @test1(i32 %a) {
|
|
|
|
; CHECK-LABEL: test1:
|
|
|
|
; CHECK: # %bb.0: # %entry
|
|
|
|
; CHECK-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
|
|
; CHECK-NEXT: addl $4, %eax
|
|
|
|
; CHECK-NEXT: #APP
|
|
|
|
; CHECK-NEXT: xorl %eax, %eax
|
2019-08-10 04:16:31 +08:00
|
|
|
; CHECK-NEXT: jmp .Ltmp0
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: #NO_APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: # %bb.1: # %normal
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: xorl %eax, %eax
|
|
|
|
; CHECK-NEXT: retl
|
|
|
|
; CHECK-NEXT: .Ltmp0: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB0_2: # %fail
|
|
|
|
; CHECK-NEXT: movl $1, %eax
|
|
|
|
; CHECK-NEXT: retl
|
|
|
|
entry:
|
|
|
|
%0 = add i32 %a, 4
|
|
|
|
callbr void asm "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@test1, %fail)) to label %normal [label %fail]
|
|
|
|
|
|
|
|
normal:
|
|
|
|
ret i32 0
|
|
|
|
|
|
|
|
fail:
|
|
|
|
ret i32 1
|
|
|
|
}
|
|
|
|
|
|
|
|
; Test 2 - callbr terminates an unreachable block, function gets simplified
|
|
|
|
; to a trivial zero return.
|
|
|
|
define i32 @test2(i32 %a) {
|
|
|
|
; CHECK-LABEL: test2:
|
|
|
|
; CHECK: # %bb.0: # %entry
|
|
|
|
; CHECK-NEXT: xorl %eax, %eax
|
|
|
|
; CHECK-NEXT: retl
|
|
|
|
entry:
|
|
|
|
br label %normal
|
|
|
|
|
|
|
|
unreachableasm:
|
|
|
|
%0 = add i32 %a, 4
|
|
|
|
callbr void asm sideeffect "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@test2, %fail)) to label %normal [label %fail]
|
|
|
|
|
|
|
|
normal:
|
|
|
|
ret i32 0
|
|
|
|
|
|
|
|
fail:
|
|
|
|
ret i32 1
|
|
|
|
}
|
|
|
|
|
|
|
|
; Test 3 - asm-goto implements a loop. The loop gets recognized, but many loop
|
|
|
|
; transforms fail due to canonicalization having callbr exceptions. Trivial
|
|
|
|
; blocks at labels 1 and 3 also don't get simplified due to callbr.
|
2020-03-12 08:07:02 +08:00
|
|
|
define i32 @test3(i32 %a) {
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-LABEL: test3:
|
|
|
|
; CHECK: # %bb.0: # %entry
|
|
|
|
; CHECK-NEXT: .Ltmp1: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB2_1: # %label01
|
|
|
|
; CHECK-NEXT: # =>This Loop Header: Depth=1
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_2 Depth 2
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_3 Depth 3
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_4 Depth 4
|
|
|
|
; CHECK-NEXT: .Ltmp2: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB2_2: # %label02
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_1 Depth=1
|
|
|
|
; CHECK-NEXT: # => This Loop Header: Depth=2
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_3 Depth 3
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_4 Depth 4
|
|
|
|
; CHECK-NEXT: addl $4, {{[0-9]+}}(%esp)
|
|
|
|
; CHECK-NEXT: .Ltmp3: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB2_3: # %label03
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_1 Depth=1
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_2 Depth=2
|
|
|
|
; CHECK-NEXT: # => This Loop Header: Depth=3
|
|
|
|
; CHECK-NEXT: # Child Loop BB2_4 Depth 4
|
|
|
|
; CHECK-NEXT: .Ltmp4: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB2_4: # %label04
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_1 Depth=1
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_2 Depth=2
|
|
|
|
; CHECK-NEXT: # Parent Loop BB2_3 Depth=3
|
|
|
|
; CHECK-NEXT: # => This Inner Loop Header: Depth=4
|
|
|
|
; CHECK-NEXT: #APP
|
2019-08-10 04:16:31 +08:00
|
|
|
; CHECK-NEXT: jmp .Ltmp1
|
|
|
|
; CHECK-NEXT: jmp .Ltmp2
|
|
|
|
; CHECK-NEXT: jmp .Ltmp3
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: #NO_APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: # %bb.5: # %normal0
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: # in Loop: Header=BB2_4 Depth=4
|
|
|
|
; CHECK-NEXT: #APP
|
2019-08-10 04:16:31 +08:00
|
|
|
; CHECK-NEXT: jmp .Ltmp1
|
|
|
|
; CHECK-NEXT: jmp .Ltmp2
|
|
|
|
; CHECK-NEXT: jmp .Ltmp3
|
|
|
|
; CHECK-NEXT: jmp .Ltmp4
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: #NO_APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: # %bb.6: # %normal1
|
2019-02-09 04:48:56 +08:00
|
|
|
; CHECK-NEXT: movl {{[0-9]+}}(%esp), %eax
|
|
|
|
; CHECK-NEXT: retl
|
|
|
|
entry:
|
|
|
|
%a.addr = alloca i32, align 4
|
|
|
|
store i32 %a, i32* %a.addr, align 4
|
|
|
|
br label %label01
|
|
|
|
|
|
|
|
label01: ; preds = %normal0, %label04, %entry
|
|
|
|
br label %label02
|
|
|
|
|
|
|
|
label02: ; preds = %normal0, %label04, %label01
|
|
|
|
%0 = load i32, i32* %a.addr, align 4
|
|
|
|
%add = add nsw i32 %0, 4
|
|
|
|
store i32 %add, i32* %a.addr, align 4
|
|
|
|
br label %label03
|
|
|
|
|
|
|
|
label03: ; preds = %normal0, %label04, %label02
|
|
|
|
br label %label04
|
|
|
|
|
|
|
|
label04: ; preds = %normal0, %label03
|
|
|
|
callbr void asm sideeffect "jmp ${0:l}; jmp ${1:l}; jmp ${2:l}", "X,X,X,~{dirflag},~{fpsr},~{flags}"(i8* blockaddress(@test3, %label01), i8* blockaddress(@test3, %label02), i8* blockaddress(@test3, %label03))
|
|
|
|
to label %normal0 [label %label01, label %label02, label %label03]
|
|
|
|
|
|
|
|
normal0: ; preds = %label04
|
|
|
|
callbr void asm sideeffect "jmp ${0:l}; jmp ${1:l}; jmp ${2:l}; jmp ${3:l}", "X,X,X,X,~{dirflag},~{fpsr},~{flags}"(i8* blockaddress(@test3, %label01), i8* blockaddress(@test3, %label02), i8* blockaddress(@test3, %label03), i8* blockaddress(@test3, %label04))
|
|
|
|
to label %normal1 [label %label01, label %label02, label %label03, label %label04]
|
|
|
|
|
|
|
|
normal1: ; preds = %normal0
|
|
|
|
%1 = load i32, i32* %a.addr, align 4
|
|
|
|
ret i32 %1
|
|
|
|
}
|
2019-12-24 08:57:41 +08:00
|
|
|
|
|
|
|
; Test 4 - asm-goto referenced with the 'l' (ell) modifier and not.
|
|
|
|
define void @test4() {
|
|
|
|
; CHECK-LABEL: test4:
|
2020-03-12 08:07:02 +08:00
|
|
|
; CHECK: # %bb.0: # %entry
|
2019-12-24 08:57:41 +08:00
|
|
|
; CHECK-NEXT: #APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: ja .Ltmp5
|
2019-12-24 08:57:41 +08:00
|
|
|
; CHECK-NEXT: #NO_APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: # %bb.1: # %asm.fallthrough
|
2019-12-24 08:57:41 +08:00
|
|
|
; CHECK-NEXT: #APP
|
2020-05-16 11:43:30 +08:00
|
|
|
; CHECK-NEXT: ja .Ltmp5
|
2019-12-24 08:57:41 +08:00
|
|
|
; CHECK-NEXT: #NO_APP
|
2020-03-12 08:07:02 +08:00
|
|
|
; CHECK-NEXT: .Ltmp5: # Block address taken
|
|
|
|
; CHECK-NEXT: .LBB3_3: # %quux
|
|
|
|
; CHECK-NEXT: retl
|
2019-12-24 08:57:41 +08:00
|
|
|
entry:
|
|
|
|
callbr void asm sideeffect "ja $0", "X,~{dirflag},~{fpsr},~{flags}"(i8* blockaddress(@test4, %quux))
|
|
|
|
to label %asm.fallthrough [label %quux]
|
|
|
|
|
|
|
|
asm.fallthrough: ; preds = %entry
|
|
|
|
callbr void asm sideeffect "ja ${0:l}", "X,~{dirflag},~{fpsr},~{flags}"(i8* blockaddress(@test4, %quux))
|
|
|
|
to label %cleanup [label %quux]
|
|
|
|
|
|
|
|
quux: ; preds = %asm.fallthrough, %entry
|
|
|
|
br label %cleanup
|
|
|
|
|
|
|
|
cleanup: ; preds = %asm.fallthrough, %quux
|
|
|
|
ret void
|
|
|
|
}
|