236 lines
7.7 KiB
LLVM
236 lines
7.7 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
|
; RUN: opt -S -passes=structurizecfg %s -o - | FileCheck %s
|
|
|
|
; Structurize as usual, but don't tear callbr and its destination blocks apart.
|
|
;
|
|
; Note: currently, callbr blocks and their corresponding target blocks
|
|
; themselves are not handled by the structurizer.* If the CFG turns out to be
|
|
; unstructured at the end, the CFG lowering (si-annotate-control-flow) will
|
|
; detect this. For the currently intended use cases of callbr in the context of
|
|
; the AMDGPU backend, this is not a limitation (cf.
|
|
; https://discourse.llvm.org/t/rfc-add-callbr-intrinsic-support/86087).
|
|
;
|
|
; Note 2: while callbr and its targets remain untouched, everything else is
|
|
; handled as usual, even if it is nested in a callbr region.
|
|
;
|
|
; *FIXME: this will be fixed in the future. Callbr can be handled as follows:
|
|
; Input IR:
|
|
; ```
|
|
; define void @foo_callbr() {
|
|
; callbr void asm "", "!i"() to label %fallthrough [label %indirect, ...]
|
|
; fallthrough:
|
|
; br label %exit
|
|
; indirect:
|
|
; br label %exit
|
|
; ...
|
|
; exit:
|
|
; ret void
|
|
; }
|
|
; ```
|
|
;
|
|
; Output IR:
|
|
; ```
|
|
; define void @foo_callbr() {
|
|
; callbr void asm "", "!i"()
|
|
; to label %fallthrough [label %fake.indirect, label %fake.indirect1, label %fake.indirect2, ...]
|
|
; fake.indirect: ; preds = %0
|
|
; br label %Flow
|
|
; fake.indirect1: ; preds = %0
|
|
; br label %Flow
|
|
; fake.indirect2: ; preds = %0
|
|
; br label %Flow
|
|
; ...
|
|
; Flow: ; preds = %fallthrough, %fake.indirect[0-N]
|
|
; %1 = phi i1 [ false, %fallthrough ], [ true, %fake.indirect ], [ false, %fake.indirect[1-N] ]
|
|
; br i1 %1, label %indirect, label %Flow1
|
|
; Flow1: ; preds = %Flow, %indirect
|
|
; %2 = phi i1 [ false, %Flow], [ true, %fake.indirect1 ], [ false, %indirect ]
|
|
; br i1 %2, label %indirect1, label %Flow2
|
|
; Flow2: ; preds = %Flow, %indirect1
|
|
; %2 = phi i1 [ false, %Flow], [ true, %fake.indirect2 ], [ false, %indirect1 ]
|
|
; br i1 %2, label %indirect2, label %Flow3
|
|
; ...
|
|
; fallthrough: ; preds = %0
|
|
; br label %Flow
|
|
; indirect: ; preds = %Flow
|
|
; br label %Flow1
|
|
; indirect1: ; preds = %Flow1
|
|
; br label %Flow2
|
|
; indirect2: : preds = %Flow2
|
|
; br label %Flow3
|
|
; ...
|
|
; exit: ; preds = %indirectN, %FlowN
|
|
; ret void
|
|
; }
|
|
; ```
|
|
;
|
|
; Output IR as ASCII-art:
|
|
; %0
|
|
; ---------------------
|
|
; | | | |
|
|
; v v v v
|
|
; f f.i f.i1 f.i2
|
|
; | | | |
|
|
; v v v v
|
|
; ---------------------
|
|
; %Flow
|
|
; | \
|
|
; | %indirect
|
|
; | /
|
|
; %Flow1
|
|
; | \
|
|
; | %indirect1
|
|
; | /
|
|
; %Flow2
|
|
; | \
|
|
; | %indirect2
|
|
; | /
|
|
; %exit
|
|
;
|
|
|
|
; Only callbr, nothing to do.
|
|
define void @callbr_simple() {
|
|
; CHECK-LABEL: define void @callbr_simple() {
|
|
; CHECK-NEXT: [[CALLBR:.*:]]
|
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
|
; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
|
|
; CHECK: [[INDIRECT]]:
|
|
; CHECK-NEXT: br label %[[EXIT:.*]]
|
|
; CHECK: [[INDIRECT1:.*:]]
|
|
; CHECK-NEXT: br label %[[EXIT]]
|
|
; CHECK: [[EXIT]]:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
callbr:
|
|
callbr void asm "", "!i"() to label %fallthrough [label %indirect]
|
|
fallthrough:
|
|
br label %exit
|
|
indirect:
|
|
br label %exit
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Callbr nested in non-callbr: non-callbr is transformed
|
|
define void @callbr_in_non_callbr(i1 %c) {
|
|
; CHECK-LABEL: define void @callbr_in_non_callbr(
|
|
; CHECK-SAME: i1 [[C:%.*]]) {
|
|
; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
|
|
; CHECK-NEXT: br i1 [[C_INV]], label %[[NOCALLBR:.*]], label %[[FLOW:.*]]
|
|
; CHECK: [[FLOW]]:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[NOCALLBR]] ], [ true, [[TMP0:%.*]] ]
|
|
; CHECK-NEXT: br i1 [[TMP1]], label %[[CALLBR:.*]], label %[[EXIT:.*]]
|
|
; CHECK: [[CALLBR]]:
|
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
|
; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
|
|
; CHECK: [[INDIRECT]]:
|
|
; CHECK-NEXT: br label %[[EXIT]]
|
|
; CHECK: [[INDIRECT1:.*:]]
|
|
; CHECK-NEXT: br label %[[EXIT]]
|
|
; CHECK: [[NOCALLBR]]:
|
|
; CHECK-NEXT: br label %[[FLOW]]
|
|
; CHECK: [[EXIT]]:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
br i1 %c, label %callbr, label %nocallbr
|
|
callbr:
|
|
callbr void asm "", "!i"() to label %fallthrough [label %indirect]
|
|
fallthrough:
|
|
br label %exit
|
|
indirect:
|
|
br label %exit
|
|
nocallbr:
|
|
br label %exit
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Callbr parent of non-callbr: non-callbr is transformed
|
|
define void @non_callbr_in_callbr(i1 %c) {
|
|
; CHECK-LABEL: define void @non_callbr_in_callbr(
|
|
; CHECK-SAME: i1 [[C:%.*]]) {
|
|
; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
|
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
|
; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
|
|
; CHECK: [[INDIRECT]]:
|
|
; CHECK-NEXT: br i1 [[C_INV]], label %[[FALLTHROUGH2:.*]], label %[[FLOW:.*]]
|
|
; CHECK: [[FLOW]]:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[FALLTHROUGH2]] ], [ true, %[[INDIRECT]] ]
|
|
; CHECK-NEXT: br i1 [[TMP1]], label %[[FALLTHROUGH1:.*]], label %[[FLOW1:.*]]
|
|
; CHECK: [[FALLTHROUGH1]]:
|
|
; CHECK-NEXT: br label %[[FLOW1]]
|
|
; CHECK: [[FALLTHROUGH2]]:
|
|
; CHECK-NEXT: br label %[[FLOW]]
|
|
; CHECK: [[INDIRECT1:.*:]]
|
|
; CHECK-NEXT: br label %[[EXIT:.*]]
|
|
; CHECK: [[FLOW1]]:
|
|
; CHECK-NEXT: br label %[[EXIT]]
|
|
; CHECK: [[EXIT]]:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
callbr void asm "", "!i"() to label %fallthrough [label %indirect]
|
|
fallthrough:
|
|
br i1 %c, label %fallthrough1, label %fallthrough2
|
|
fallthrough1:
|
|
br label %exit
|
|
fallthrough2:
|
|
br label %exit
|
|
indirect:
|
|
br label %exit
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Callbr surrounded by non-callbr: all three regular branches are handled
|
|
; correctly
|
|
define void @callbr_nested_in_non_callbr(i1 %c, i1 %d, i1 %e, i1 %f) {
|
|
; CHECK-LABEL: define void @callbr_nested_in_non_callbr(
|
|
; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]], i1 [[E:%.*]], i1 [[F:%.*]]) {
|
|
; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
|
|
; CHECK-NEXT: br i1 [[C_INV]], label %[[NOCALLBR:.*]], label %[[FLOW3:.*]]
|
|
; CHECK: [[FLOW3]]:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[FLOW:.*]] ], [ true, [[TMP0:%.*]] ]
|
|
; CHECK-NEXT: br i1 [[TMP1]], label %[[CALLBR:.*]], label %[[RET:.*]]
|
|
; CHECK: [[CALLBR]]:
|
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
|
; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
|
|
; CHECK: [[INDIRECT]]:
|
|
; CHECK-NEXT: br i1 [[D]], label %[[FALLTHROUGH1:.*]], label %[[FLOW2:.*]]
|
|
; CHECK: [[FALLTHROUGH1]]:
|
|
; CHECK-NEXT: br label %[[FLOW2]]
|
|
; CHECK: [[INDIRECT2:.*:]]
|
|
; CHECK-NEXT: br i1 [[E]], label %[[INDIRECT1:.*]], label %[[FLOW1:.*]]
|
|
; CHECK: [[INDIRECT1]]:
|
|
; CHECK-NEXT: br label %[[FLOW1]]
|
|
; CHECK: [[NOCALLBR]]:
|
|
; CHECK-NEXT: br i1 [[F]], label %[[NOCALLBR1:.*]], label %[[FLOW]]
|
|
; CHECK: [[NOCALLBR1]]:
|
|
; CHECK-NEXT: br label %[[FLOW]]
|
|
; CHECK: [[FLOW]]:
|
|
; CHECK-NEXT: br label %[[FLOW3]]
|
|
; CHECK: [[FLOW1]]:
|
|
; CHECK-NEXT: br label %[[RET]]
|
|
; CHECK: [[FLOW2]]:
|
|
; CHECK-NEXT: br label %[[RET]]
|
|
; CHECK: [[RET]]:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
br i1 %c, label %callbr, label %nocallbr
|
|
callbr:
|
|
callbr void asm "", "!i"() to label %fallthrough [label %indirect]
|
|
fallthrough:
|
|
br i1 %d, label %fallthrough1, label %ret
|
|
fallthrough1:
|
|
br label %ret
|
|
indirect:
|
|
br i1 %e, label %indirect1, label %ret
|
|
indirect1:
|
|
br label %ret
|
|
nocallbr:
|
|
br i1 %f, label %nocallbr1, label %ret
|
|
nocallbr1:
|
|
br label %ret
|
|
ret:
|
|
ret void
|
|
}
|