[SimplifyCFG][swifterror] Don't sink calls with swifterror params (#139015)
We've encountered an LLVM verification failure when building Swift with
the SimplifyCFG pass enabled. I found that
https://reviews.llvm.org/D158083 fixed this pass by preventing sinking
loads or stores of swifterror values, but it did not implement the same
protection for call or invokes.
In `Verifier.cpp`
[here](https://github.com/ellishg/llvm-project/blob/c68535581135a1513c9c4c1c7672307d4b5e616e/llvm/lib/IR/Verifier.cpp#L4360-L4364)
and
[here](https://github.com/ellishg/llvm-project/blob/c68535581135a1513c9c4c1c7672307d4b5e616e/llvm/lib/IR/Verifier.cpp#L3661-L3662)
we can see that swifterror values must also be used directly by call
instructions.
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 1db01b2..3dbd605 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -4220,12 +4220,19 @@
}
bool llvm::canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx) {
+ const auto *Op = I->getOperand(OpIdx);
// We can't have a PHI with a metadata type.
- if (I->getOperand(OpIdx)->getType()->isMetadataTy())
+ if (Op->getType()->isMetadataTy())
+ return false;
+
+ // swifterror pointers can only be used by a load, store, or as a swifterror
+ // argument; swifterror pointers are not allowed to be used in select or phi
+ // instructions.
+ if (Op->isSwiftError())
return false;
// Early exit.
- if (!isa<Constant, InlineAsm>(I->getOperand(OpIdx)))
+ if (!isa<Constant, InlineAsm>(Op))
return true;
switch (I->getOpcode()) {
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 5cd244c..d94abea 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -2208,14 +2208,6 @@
if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs))
return false;
- // swifterror pointers can only be used by a load or store; sinking a load
- // or store would require introducing a select for the pointer operand,
- // which isn't allowed for swifterror pointers.
- if (isa<StoreInst>(I) && I->getOperand(1)->isSwiftError())
- return false;
- if (isa<LoadInst>(I) && I->getOperand(0)->isSwiftError())
- return false;
-
// Treat MMRAs conservatively. This pass can be quite aggressive and
// could drop a lot of MMRAs otherwise.
if (MMRAMetadata(*I) != I0MMRA)
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll b/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
index 0c13f57..5dff39c 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-sink-swifterror-store.ll
@@ -3,6 +3,8 @@
declare void @clobber1()
declare void @clobber2()
+declare swiftcc void @foo(ptr swifterror)
+declare swiftcc void @bar(ptr swifterror, ptr)
; Do not try to sink the stores to the exit block, as this requires introducing
; a select for the pointer operand. This is not allowed for swifterror pointers.
@@ -76,6 +78,22 @@
; introduces a select for the pointer operand. This is not allowed for
; swifterror pointers.
define swiftcc ptr @sink_load(ptr %arg, ptr swifterror %arg1, i1 %c) {
+; CHECK-LABEL: define swiftcc ptr @sink_load
+; CHECK-SAME: (ptr [[ARG:%.*]], ptr swifterror [[ARG1:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: bb:
+; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: call void @clobber1()
+; CHECK-NEXT: [[L1:%.*]] = load ptr, ptr [[ARG]], align 8
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: else:
+; CHECK-NEXT: call void @clobber2()
+; CHECK-NEXT: [[L2:%.*]] = load ptr, ptr [[ARG1]], align 8
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: [[P:%.*]] = phi ptr [ [[L1]], [[THEN]] ], [ [[L2]], [[ELSE]] ]
+; CHECK-NEXT: ret ptr [[P]]
+;
bb:
br i1 %c, label %then, label %else
@@ -127,3 +145,77 @@
%p = phi ptr [ %l1, %then ], [ %l2, %else ]
ret ptr %p
}
+
+
+define swiftcc void @sink_call(i1 %c) {
+; CHECK-LABEL: define swiftcc void @sink_call
+; CHECK-SAME: (i1 [[C:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = alloca swifterror ptr, align 8
+; CHECK-NEXT: [[TMP2:%.*]] = alloca swifterror ptr, align 8
+; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: call void @clobber1()
+; CHECK-NEXT: call swiftcc void @foo(ptr [[TMP2]])
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: else:
+; CHECK-NEXT: call void @clobber2()
+; CHECK-NEXT: call swiftcc void @foo(ptr [[TMP1]])
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+ %2 = alloca swifterror ptr, align 8
+ %3 = alloca swifterror ptr, align 8
+ br i1 %c, label %then, label %else
+
+then:
+ call void @clobber1()
+ call swiftcc void @foo(ptr %3)
+ br label %exit
+
+else:
+ call void @clobber2()
+ call swiftcc void @foo(ptr %2)
+ br label %exit
+
+exit:
+ ret void
+}
+
+
+define swiftcc void @safe_sink_call(i1 %c) {
+; CHECK-LABEL: define swiftcc void @safe_sink_call
+; CHECK-SAME: (i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ERR:%.*]] = alloca swifterror ptr, align 8
+; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: [[B:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: call void @clobber1()
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: else:
+; CHECK-NEXT: call void @clobber2()
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: [[B_SINK:%.*]] = phi ptr [ [[B]], [[ELSE]] ], [ [[A]], [[THEN]] ]
+; CHECK-NEXT: call swiftcc void @bar(ptr [[ERR]], ptr [[B_SINK]])
+; CHECK-NEXT: ret void
+;
+ %err = alloca swifterror ptr, align 8
+ %a = alloca ptr, align 8
+ %b = alloca ptr, align 8
+ br i1 %c, label %then, label %else
+
+then:
+ call void @clobber1()
+ call swiftcc void @bar(ptr %err, ptr %a)
+ br label %exit
+
+else:
+ call void @clobber2()
+ call swiftcc void @bar(ptr %err, ptr %b)
+ br label %exit
+
+exit:
+ ret void
+}