mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
IRGen: Emit failure blocks individually
Reapply commit r24722. The original commit did not break the build. There appears to be an issue in CoreAutomation (an iOS test failed with empty output). Follow up builds with the original commit in succeeded. Also apply Dimitri's patch to make the test condfail.sil portable. Original message: This significantly improves debugging experience as failures such as overflow can be mapped back to a source line. Code size impacts I measured on PerfTestSuite and libswift*.dylib was neglectable. __text section size increase: 0.24% bin/PerfTest_O 0.01% bin/PerfTest_Onone 0.20% libswift*.dylib rdar://19118593 Swift SVN r24725
This commit is contained in:
@@ -265,9 +265,9 @@ public:
|
||||
llvm::SmallDenseMap<const VarDecl *, unsigned, 8> ArgNo;
|
||||
llvm::SmallBitVector DidEmitDebugInfoForArg;
|
||||
|
||||
// Shared destination basic block for condfail traps.
|
||||
llvm::BasicBlock *FailBB = nullptr;
|
||||
|
||||
// Destination basic blocks for condfail traps.
|
||||
llvm::SmallVector<llvm::BasicBlock *, 8> FailBBs;
|
||||
|
||||
SILFunction *CurSILFn;
|
||||
Address IndirectReturn;
|
||||
|
||||
@@ -500,29 +500,15 @@ public:
|
||||
Ty, DS, Name, DirectValue);
|
||||
}
|
||||
|
||||
/// Emit the shared trap block for condfail instructions, or reuse one we
|
||||
/// already emitted.
|
||||
llvm::BasicBlock *getFailBB() {
|
||||
if (FailBB)
|
||||
return FailBB;
|
||||
|
||||
FailBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
||||
return FailBB;
|
||||
}
|
||||
|
||||
void emitFailBB() {
|
||||
assert(FailBB && "no failure BB");
|
||||
// Any line number we would put on a unified trap block would be
|
||||
// misleading. Let's be blunt about this and use an artificial location.
|
||||
if (IGM.DebugInfo)
|
||||
IGM.DebugInfo->setArtificialTrapLocation(Builder,
|
||||
CurSILFn->getDebugScope());
|
||||
CurFn->getBasicBlockList().push_back(FailBB);
|
||||
Builder.SetInsertPoint(FailBB);
|
||||
llvm::Function *trapIntrinsic = llvm::Intrinsic::getDeclaration(&IGM.Module,
|
||||
llvm::Intrinsic::ID::trap);
|
||||
Builder.CreateCall(trapIntrinsic);
|
||||
Builder.CreateUnreachable();
|
||||
if (!FailBBs.empty()) {
|
||||
// Move the trap basic blocks to the end of the function.
|
||||
for (auto *FailBB : FailBBs) {
|
||||
auto &BlockList = CurFn->getBasicBlockList();
|
||||
BlockList.splice(BlockList.end(), BlockList, FailBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
@@ -751,7 +737,7 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM,
|
||||
IRGenSILFunction::~IRGenSILFunction() {
|
||||
assert(Builder.hasPostTerminatorIP() && "did not terminate BB?!");
|
||||
// Emit the fail BB if we have one.
|
||||
if (FailBB)
|
||||
if (!FailBBs.empty())
|
||||
emitFailBB();
|
||||
DEBUG(CurFn->print(llvm::dbgs()));
|
||||
}
|
||||
@@ -3135,8 +3121,17 @@ static void emitValueBitCast(IRGenSILFunction &IGF,
|
||||
// with mismatching fixed sizes.
|
||||
// Usually llvm can eliminate this code again because the user's safety
|
||||
// check should be constant foldable on llvm level.
|
||||
llvm::BasicBlock *failBB = IGF.getFailBB();
|
||||
llvm::BasicBlock *failBB =
|
||||
llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
|
||||
IGF.Builder.CreateBr(failBB);
|
||||
IGF.FailBBs.push_back(failBB);
|
||||
|
||||
IGF.Builder.emitBlock(failBB);
|
||||
llvm::Function *trapIntrinsic = llvm::Intrinsic::getDeclaration(
|
||||
&IGF.IGM.Module, llvm::Intrinsic::ID::trap);
|
||||
IGF.Builder.CreateCall(trapIntrinsic);
|
||||
IGF.Builder.CreateUnreachable();
|
||||
|
||||
llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
|
||||
IGF.Builder.emitBlock(contBB);
|
||||
in.claimAll();
|
||||
@@ -3845,11 +3840,19 @@ void IRGenSILFunction::visitDestroyAddrInst(swift::DestroyAddrInst *i) {
|
||||
void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) {
|
||||
Explosion e = getLoweredExplosion(i->getOperand());
|
||||
llvm::Value *cond = e.claimNext();
|
||||
llvm::BasicBlock *failBB = getFailBB();
|
||||
|
||||
// Emit individual fail blocks so that we can map the failure back to a source
|
||||
// line.
|
||||
llvm::BasicBlock *failBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
||||
llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
||||
|
||||
Builder.CreateCondBr(cond, failBB, contBB);
|
||||
Builder.emitBlock(failBB);
|
||||
llvm::Function *trapIntrinsic =
|
||||
llvm::Intrinsic::getDeclaration(&IGM.Module, llvm::Intrinsic::ID::trap);
|
||||
Builder.CreateCall(trapIntrinsic);
|
||||
Builder.CreateUnreachable();
|
||||
Builder.emitBlock(contBB);
|
||||
FailBBs.push_back(failBB);
|
||||
}
|
||||
|
||||
void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
// RUN: %target-swift-frontend -O -primary-file %s -emit-ir -g -o - | FileCheck %s
|
||||
|
||||
// CHECK: define{{.*}}2fn
|
||||
// CHECK-LABEL: define{{.*}}2fn
|
||||
func fn() {
|
||||
println("two")
|
||||
println(0 - UInt(Process.arguments.count))
|
||||
println(1 - UInt(Process.arguments.count))
|
||||
println("three")
|
||||
}
|
||||
// All traps should be coalesced at the end.
|
||||
|
||||
// CHECK: ret
|
||||
// CHECK-NOT: define
|
||||
// CHECK: call void @llvm.trap(), !dbg ![[LOC:.*]]
|
||||
// CHECK-NEXT: unreachable, !dbg ![[LOC]]
|
||||
// CHECK: ![[LOC]] = !MDLocation(line: 0, scope
|
||||
// CHECK: call void @llvm.trap(), !dbg ![[LOC2:.*]]
|
||||
// CHECK-NEXT: unreachable, !dbg ![[LOC2]]
|
||||
// CHECK: ![[LOC]] = !MDLocation(line: 6, column
|
||||
// CHECK: ![[LOC2]] = !MDLocation(line: 7, column
|
||||
|
||||
@@ -352,7 +352,7 @@ func testCondFail(b: Bool, c: Bool) {
|
||||
// CHECK: br i1 %0, label %[[FAIL:.*]], label %[[CONT:.*]]
|
||||
Builtin.condfail(b)
|
||||
// CHECK: <label>:[[CONT]]
|
||||
// CHECK: br i1 %1, label %[[FAIL]], label %[[CONT:.*]]
|
||||
// CHECK: br i1 %1, label %[[FAIL2:.*]], label %[[CONT:.*]]
|
||||
Builtin.condfail(c)
|
||||
// CHECK: <label>:[[CONT]]
|
||||
// CHECK: ret void
|
||||
@@ -360,6 +360,10 @@ func testCondFail(b: Bool, c: Bool) {
|
||||
// CHECK: <label>:[[FAIL]]
|
||||
// CHECK: call void @llvm.trap()
|
||||
// CHECK: unreachable
|
||||
|
||||
// CHECK: <label>:[[FAIL2]]
|
||||
// CHECK: call void @llvm.trap()
|
||||
// CHECK: unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define hidden void @_TF8builtins8testOnce{{.*}}(i8*, i8*, %swift.refcounted*) {
|
||||
|
||||
27
test/IRGen/condfail.sil
Normal file
27
test/IRGen/condfail.sil
Normal file
@@ -0,0 +1,27 @@
|
||||
// RUN: %target-swift-frontend -g -emit-ir %s | FileCheck %s
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
// Make sure we emit two traps.
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @test_cond_fail({{.*}}) {
|
||||
// CHECK: call void @llvm.trap()
|
||||
// CHECK: call void @llvm.trap()
|
||||
// CHECK: }
|
||||
|
||||
sil hidden @test_cond_fail : $@thin (Int32) -> Int32 {
|
||||
bb0(%0 : $Int32):
|
||||
%2 = integer_literal $Builtin.Int32, 1
|
||||
%3 = struct_extract %0 : $Int32, #Int32.value
|
||||
%4 = integer_literal $Builtin.Int1, -1
|
||||
%5 = builtin "sadd_with_overflow_Int32"(%3 : $Builtin.Int32, %2 : $Builtin.Int32, %4 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
||||
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
|
||||
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
|
||||
cond_fail %7 : $Builtin.Int1
|
||||
%8 = builtin "sadd_with_overflow_Int32"(%6 : $Builtin.Int32, %2 : $Builtin.Int32, %4 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
||||
%9 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
|
||||
cond_fail %9 : $Builtin.Int1
|
||||
%10 = struct $Int32 (%6 : $Builtin.Int32)
|
||||
return %10 : $Int32
|
||||
}
|
||||
Reference in New Issue
Block a user