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:
Arnold Schwaighofer
2015-01-26 00:35:53 +00:00
parent 15b7a9347d
commit 2e3c0b69f7
4 changed files with 70 additions and 32 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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
View 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
}