mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-15 04:30:12 +00:00
DeadArgElim: assess uses of entire return value aggregate.
Previously, a non-extractvalue use of an aggregate return value meant the entire return was considered live (the algorithm gave up entirely). This was correct, but conservative. It's better to actually look at that Use, making the analysis results apply to all sub-values under consideration. E.g. %val = call { i32, i32 } @whatever() [...] ret { i32, i32 } %val The return is using the entire aggregate (sub-values 0 and 1). We can still simplify @whatever if we can prove that this return is itself unused. Also unifies the logic slightly between aggregate and non-aggregate cases.. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228558 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
7d5ae5bc1f
commit
c4af8c9467
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
test/Transforms/DeadArgElim/aggregates.ll
Normal file
71
test/Transforms/DeadArgElim/aggregates.ll
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user