diff options
| -rw-r--r-- | llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp | 52 | ||||
| -rw-r--r-- | llvm/test/Transforms/DeadArgElim/aggregates.ll | 71 |
2 files changed, 97 insertions, 26 deletions
diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index 4045c09aaa2..365555b78c5 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -541,7 +541,6 @@ void DAE::SurveyFunction(const Function &F) { // Keep track of the number of live retvals, so we can skip checks once all // of them turn out to be live. unsigned NumLiveRetVals = 0; - Type *STy = dyn_cast<StructType>(F.getReturnType()); // Loop all uses of the function. for (const Use &U : F.uses()) { // If the function is PASSED IN as an argument, its address has been @@ -563,34 +562,35 @@ void DAE::SurveyFunction(const Function &F) { // Now, check how our return value(s) is/are used in this caller. Don't // bother checking return values if all of them are live already. - if (NumLiveRetVals != RetCount) { - if (STy) { - // Check all uses of the return value. - for (const User *U : TheCall->users()) { - const ExtractValueInst *Ext = dyn_cast<ExtractValueInst>(U); - if (Ext && Ext->hasIndices()) { - // This use uses a part of our return value, survey the uses of - // that part and store the results for this index only. - unsigned Idx = *Ext->idx_begin(); - if (RetValLiveness[Idx] != Live) { - RetValLiveness[Idx] = SurveyUses(Ext, MaybeLiveRetUses[Idx]); - if (RetValLiveness[Idx] == Live) - NumLiveRetVals++; - } - } else { - // Used by something else than extractvalue. Mark all return - // values as live. - for (unsigned i = 0; i != RetCount; ++i ) - RetValLiveness[i] = Live; - NumLiveRetVals = RetCount; - break; - } + if (NumLiveRetVals == RetCount) + continue; + + // Check all uses of the return value. + for (const Use &U : TheCall->uses()) { + if (ExtractValueInst *Ext = dyn_cast<ExtractValueInst>(U.getUser())) { + // This use uses a part of our return value, survey the uses of + // that part and store the results for this index only. + unsigned Idx = *Ext->idx_begin(); + if (RetValLiveness[Idx] != Live) { + RetValLiveness[Idx] = SurveyUses(Ext, MaybeLiveRetUses[Idx]); + if (RetValLiveness[Idx] == Live) + NumLiveRetVals++; } } else { - // Single return value - RetValLiveness[0] = SurveyUses(TheCall, MaybeLiveRetUses[0]); - if (RetValLiveness[0] == Live) + // Used by something else than extractvalue. Survey, but assume that the + // result applies to all sub-values. + UseVector MaybeLiveAggregateUses; + if (SurveyUse(&U, MaybeLiveAggregateUses) == Live) { NumLiveRetVals = RetCount; + RetValLiveness.assign(RetCount, Live); + break; + } else { + for (unsigned i = 0; i != RetCount; ++i) { + if (RetValLiveness[i] != Live) + MaybeLiveRetUses[i].append(MaybeLiveAggregateUses.begin(), + MaybeLiveAggregateUses.end()); + } + } } } } diff --git a/llvm/test/Transforms/DeadArgElim/aggregates.ll b/llvm/test/Transforms/DeadArgElim/aggregates.ll new file mode 100644 index 00000000000..ce1d7ebca66 --- /dev/null +++ b/llvm/test/Transforms/DeadArgElim/aggregates.ll @@ -0,0 +1,71 @@ +; RUN: opt -S -deadargelim %s | FileCheck %s + +; Case 0: the basic example: an entire aggregate use is returned, but it's +; actually only used in ways we can eliminate. We gain benefit from analysing +; the "use" and applying its results to all sub-values. + +; CHECK-LABEL: define internal void @agguse_dead() + +define internal { i32, i32 } @agguse_dead() { + ret { i32, i32 } { i32 0, i32 1 } +} + +define internal { i32, i32 } @test_agguse_dead() { + %val = call { i32, i32 } @agguse_dead() + ret { i32, i32 } %val +} + + + +; Case 1: an opaque use of the aggregate exists (in this case dead). Otherwise +; only one value is used, so function can be simplified. + +; CHECK-LABEL: define internal i32 @rets_independent_if_agguse_dead() +; CHECK: [[RET:%.*]] = extractvalue { i32, i32 } { i32 0, i32 1 }, 1 +; CHECK: ret i32 [[RET]] + +define internal { i32, i32 } @rets_independent_if_agguse_dead() { + ret { i32, i32 } { i32 0, i32 1 } +} + +define internal { i32, i32 } @test_rets_independent_if_agguse_dead(i1 %tst) { + %val = call { i32, i32 } @rets_independent_if_agguse_dead() + br i1 %tst, label %use_1, label %use_aggregate + +use_1: + ; This use can be classified as applying only to ret 1. + %val0 = extractvalue { i32, i32 } %val, 1 + call void @callee(i32 %val0) + ret { i32, i32 } undef + +use_aggregate: + ; This use is assumed to apply to both 0 and 1. + ret { i32, i32 } %val +} + +; Case 2: an opaque use of the aggregate exists (in this case *live*). Other +; uses shouldn't matter. + +; CHECK-LABEL: define internal { i32, i32 } @rets_live_agguse() +; CHECK: ret { i32, i32 } { i32 0, i32 1 } + +define internal { i32, i32 } @rets_live_agguse() { + ret { i32, i32} { i32 0, i32 1 } +} + +define { i32, i32 } @test_rets_live_aggues(i1 %tst) { + %val = call { i32, i32 } @rets_live_agguse() + br i1 %tst, label %use_1, label %use_aggregate + +use_1: + ; This use can be classified as applying only to ret 1. + %val0 = extractvalue { i32, i32 } %val, 1 + call void @callee(i32 %val0) + ret { i32, i32 } undef + +use_aggregate: + ; This use is assumed to apply to both 0 and 1. + ret { i32, i32 } %val +} + +declare void @callee(i32)
\ No newline at end of file |

