blob: 42f95194980d4a112825747fbd0540803c0e7bc9 [file] [log] [blame] [edit]
; 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
}