mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
DeadCodeElimination: remove dead overflow producing operations together with the cond_fail which handles the overflow.
This commit is contained in:
@@ -74,6 +74,10 @@ class DCE : public SILFunctionTransform {
|
|||||||
PostDominanceInfo *PDT;
|
PostDominanceInfo *PDT;
|
||||||
llvm::DenseMap<SILBasicBlock *, ControllingInfo> ControllingInfoMap;
|
llvm::DenseMap<SILBasicBlock *, ControllingInfo> ControllingInfoMap;
|
||||||
|
|
||||||
|
// Maps instructions which produce a failing condition (like overflow
|
||||||
|
// builtins) to the actual cond_fail instructions which handle the failure.
|
||||||
|
llvm::DenseMap<SILInstruction *, CondFailInst *> CondFailProducers;
|
||||||
|
|
||||||
/// Tracks if the pass changed branches.
|
/// Tracks if the pass changed branches.
|
||||||
bool BranchesChanged;
|
bool BranchesChanged;
|
||||||
/// Trackes if the pass changed ApplyInsts.
|
/// Trackes if the pass changed ApplyInsts.
|
||||||
@@ -173,15 +177,51 @@ void DCE::markValueLive(ValueBase *V) {
|
|||||||
propagateLiveBlockArgument(Arg);
|
propagateLiveBlockArgument(Arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the producing instruction of a cond_fail condition. Currently these
|
||||||
|
/// are overflow builtints but may be extended to other instructions in the
|
||||||
|
/// future.
|
||||||
|
static SILInstruction *getProducer(CondFailInst *CFI) {
|
||||||
|
// Check for the pattern:
|
||||||
|
// %1 = builtin "some_operation_with_overflow"
|
||||||
|
// %2 = tuple_extract %1
|
||||||
|
// %3 = cond_fail %2
|
||||||
|
SILValue FailCond = CFI->getOperand();
|
||||||
|
if (auto *TEI = dyn_cast<TupleExtractInst>(FailCond)) {
|
||||||
|
if (auto *BI = dyn_cast<BuiltinInst>(TEI->getOperand())) {
|
||||||
|
return BI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine which instructions from this function we need to keep.
|
// Determine which instructions from this function we need to keep.
|
||||||
void DCE::markLive(SILFunction &F) {
|
void DCE::markLive(SILFunction &F) {
|
||||||
|
|
||||||
|
llvm::SmallVector<CondFailInst *, 16> CondFailInsts;
|
||||||
|
|
||||||
// Find the initial set of instructions in this function that appear
|
// Find the initial set of instructions in this function that appear
|
||||||
// to be live in the sense that they are not trivially something we
|
// to be live in the sense that they are not trivially something we
|
||||||
// can delete by examining only that instruction.
|
// can delete by examining only that instruction.
|
||||||
for (auto &BB : F)
|
for (auto &BB : F) {
|
||||||
for (auto &I : BB)
|
for (auto &I : BB) {
|
||||||
|
if (auto *CFI = dyn_cast<CondFailInst>(&I)) {
|
||||||
|
// Special case cond_fail instructions. A cond_fail is only alive
|
||||||
|
// if its (identifyable) producer is alive. We handle this in
|
||||||
|
// propagateLiveness.
|
||||||
|
if (SILInstruction *Prod = getProducer(CFI)) {
|
||||||
|
CondFailInst *&MappedCFI = CondFailProducers[Prod];
|
||||||
|
if (!MappedCFI) {
|
||||||
|
// For simplicity we only handle a single cond_fail for each
|
||||||
|
// producer (which is the usual case).
|
||||||
|
MappedCFI = CFI;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (seemsUseful(&I))
|
if (seemsUseful(&I))
|
||||||
markValueLive(&I);
|
markValueLive(&I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now propagate liveness backwards from each instruction in our
|
// Now propagate liveness backwards from each instruction in our
|
||||||
// worklist, adding new instructions to the worklist as we discover
|
// worklist, adding new instructions to the worklist as we discover
|
||||||
@@ -281,6 +321,11 @@ void DCE::propagateLiveness(SILInstruction *I) {
|
|||||||
for (Operand *DU : getDebugUses(*I))
|
for (Operand *DU : getDebugUses(*I))
|
||||||
markValueLive(DU->getUser());
|
markValueLive(DU->getUser());
|
||||||
|
|
||||||
|
// The same situation for cond_fail instructions. Only if the producer of
|
||||||
|
// the cond_fail is alive, the cond_fail itself is alive.
|
||||||
|
if (CondFailInst *CFI = CondFailProducers.lookup(I)) {
|
||||||
|
markValueLive(CFI);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ bb5:
|
|||||||
%47 = struct $Int32 (%46 : $Builtin.Int32)
|
%47 = struct $Int32 (%46 : $Builtin.Int32)
|
||||||
br bb6(%47 : $Int32)
|
br bb6(%47 : $Int32)
|
||||||
|
|
||||||
// CHECK: bb6
|
// CHECK: bb6{{.*}}):
|
||||||
bb6(%49 : $Int32):
|
bb6(%49 : $Int32):
|
||||||
%50 = integer_literal $Builtin.Int32, 10
|
%50 = integer_literal $Builtin.Int32, 10
|
||||||
%52 = struct_extract %29 : $Int32, #Int32._value
|
%52 = struct_extract %29 : $Int32, #Int32._value
|
||||||
@@ -142,11 +142,13 @@ bb7:
|
|||||||
// CHECK: br bb8
|
// CHECK: br bb8
|
||||||
br bb8(%58 : $Int32)
|
br bb8(%58 : $Int32)
|
||||||
|
|
||||||
// CHECK: bb8
|
// CHECK: bb8(%{{[0-9]+}} : $Int32):
|
||||||
bb8(%60 : $Int32):
|
bb8(%60 : $Int32):
|
||||||
%61 = struct_extract %31 : $Int32, #Int32._value
|
%61 = struct_extract %31 : $Int32, #Int32._value
|
||||||
%62 = integer_literal $Builtin.Int32, 1
|
%62 = integer_literal $Builtin.Int32, 1
|
||||||
%64 = integer_literal $Builtin.Int1, -1
|
%64 = integer_literal $Builtin.Int1, -1
|
||||||
|
// CHECK-NOT: builtin "sadd_with_overflow_Int32"
|
||||||
|
// CHECK-NOT: cond_fail
|
||||||
%65 = builtin "sadd_with_overflow_Int32"(%61 : $Builtin.Int32, %62 : $Builtin.Int32, %64 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
%65 = builtin "sadd_with_overflow_Int32"(%61 : $Builtin.Int32, %62 : $Builtin.Int32, %64 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
||||||
%66 = tuple_extract %65 : $(Builtin.Int32, Builtin.Int1), 0
|
%66 = tuple_extract %65 : $(Builtin.Int32, Builtin.Int1), 0
|
||||||
%67 = tuple_extract %65 : $(Builtin.Int32, Builtin.Int1), 1
|
%67 = tuple_extract %65 : $(Builtin.Int32, Builtin.Int1), 1
|
||||||
@@ -155,11 +157,15 @@ bb8(%60 : $Int32):
|
|||||||
%70 = struct_extract %30 : $Int32, #Int32._value
|
%70 = struct_extract %30 : $Int32, #Int32._value
|
||||||
%71 = integer_literal $Builtin.Int32, 1
|
%71 = integer_literal $Builtin.Int32, 1
|
||||||
%73 = integer_literal $Builtin.Int1, -1
|
%73 = integer_literal $Builtin.Int1, -1
|
||||||
%74 = builtin "sadd_with_overflow_Int32"(%70 : $Builtin.Int32, %71 : $Builtin.Int32, %73 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
// CHECK: builtin "ssub_with_overflow_Int32"
|
||||||
|
%74 = builtin "ssub_with_overflow_Int32"(%70 : $Builtin.Int32, %71 : $Builtin.Int32, %73 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
||||||
%75 = tuple_extract %74 : $(Builtin.Int32, Builtin.Int1), 0
|
%75 = tuple_extract %74 : $(Builtin.Int32, Builtin.Int1), 0
|
||||||
%76 = tuple_extract %74 : $(Builtin.Int32, Builtin.Int1), 1
|
%76 = tuple_extract %74 : $(Builtin.Int32, Builtin.Int1), 1
|
||||||
|
// CHECK: cond_fail
|
||||||
cond_fail %76 : $Builtin.Int1
|
cond_fail %76 : $Builtin.Int1
|
||||||
%78 = struct $Int32 (%75 : $Builtin.Int32)
|
%78 = struct $Int32 (%75 : $Builtin.Int32)
|
||||||
|
%tif = function_ref @take_int32 : $@convention(thin) (Int32) -> ()
|
||||||
|
%atif = apply %tif(%78) : $@convention(thin) (Int32) -> ()
|
||||||
// CHECK: br bb3(undef : $Int32, undef : $Int32,
|
// CHECK: br bb3(undef : $Int32, undef : $Int32,
|
||||||
br bb3(%49 : $Int32, %60 : $Int32, %78 : $Int32, %69 : $Int32)
|
br bb3(%49 : $Int32, %60 : $Int32, %78 : $Int32, %69 : $Int32)
|
||||||
|
|
||||||
@@ -182,3 +188,5 @@ bb10:
|
|||||||
// CHECK: return
|
// CHECK: return
|
||||||
return %90 : $()
|
return %90 : $()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sil @take_int32 : $@convention(thin) (Int32) -> ()
|
||||||
|
|||||||
Reference in New Issue
Block a user