mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-15 04:30:12 +00:00
DeadArgElim: fix mismatch in accounting of array return types.
Some parts of DeadArgElim were only considering the individual fields of StructTypes separately, but others (where insertvalue & extractvalue instructions occur) also looked into ArrayTypes. This one is an actual bug; the mismatch can lead to an argument being considered used by a return sub-value that isn't being tracked (and hence is dead by default). It then gets incorrectly eliminated. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228559 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
c4af8c9467
commit
968ed6a5f0
@ -387,14 +387,32 @@ bool DAE::RemoveDeadArgumentsFromCallers(Function &Fn)
|
||||
/// for void functions and 1 for functions not returning a struct. It returns
|
||||
/// the number of struct elements for functions returning a struct.
|
||||
static unsigned NumRetVals(const Function *F) {
|
||||
if (F->getReturnType()->isVoidTy())
|
||||
Type *RetTy = F->getReturnType();
|
||||
if (RetTy->isVoidTy())
|
||||
return 0;
|
||||
else if (StructType *STy = dyn_cast<StructType>(F->getReturnType()))
|
||||
else if (StructType *STy = dyn_cast<StructType>(RetTy))
|
||||
return STy->getNumElements();
|
||||
else if (ArrayType *ATy = dyn_cast<ArrayType>(RetTy))
|
||||
return ATy->getNumElements();
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Returns the sub-type a function will return at a given Idx. Should
|
||||
/// correspond to the result type of an ExtractValue instruction executed with
|
||||
/// just that one Idx (i.e. only top-level structure is considered).
|
||||
static Type *getRetComponentType(const Function *F, unsigned Idx) {
|
||||
Type *RetTy = F->getReturnType();
|
||||
assert(!RetTy->isVoidTy() && "void type has no subtype");
|
||||
|
||||
if (StructType *STy = dyn_cast<StructType>(RetTy))
|
||||
return STy->getElementType(Idx);
|
||||
else if (ArrayType *ATy = dyn_cast<ArrayType>(RetTy))
|
||||
return ATy->getElementType();
|
||||
else
|
||||
return RetTy;
|
||||
}
|
||||
|
||||
/// MarkIfNotLive - This checks Use for liveness in LiveValues. If Use is not
|
||||
/// live, it adds Use to the MaybeLiveUses argument. Returns the determined
|
||||
/// liveness of Use.
|
||||
@ -775,39 +793,29 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
|
||||
if (RetTy->isVoidTy() || HasLiveReturnedArg) {
|
||||
NRetTy = RetTy;
|
||||
} else {
|
||||
StructType *STy = dyn_cast<StructType>(RetTy);
|
||||
if (STy)
|
||||
// Look at each of the original return values individually.
|
||||
for (unsigned i = 0; i != RetCount; ++i) {
|
||||
RetOrArg Ret = CreateRet(F, i);
|
||||
if (LiveValues.erase(Ret)) {
|
||||
RetTypes.push_back(STy->getElementType(i));
|
||||
NewRetIdxs[i] = RetTypes.size() - 1;
|
||||
} else {
|
||||
++NumRetValsEliminated;
|
||||
DEBUG(dbgs() << "DAE - Removing return value " << i << " from "
|
||||
<< F->getName() << "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
// We used to return a single value.
|
||||
if (LiveValues.erase(CreateRet(F, 0))) {
|
||||
RetTypes.push_back(RetTy);
|
||||
NewRetIdxs[0] = 0;
|
||||
// Look at each of the original return values individually.
|
||||
for (unsigned i = 0; i != RetCount; ++i) {
|
||||
RetOrArg Ret = CreateRet(F, i);
|
||||
if (LiveValues.erase(Ret)) {
|
||||
RetTypes.push_back(getRetComponentType(F, i));
|
||||
NewRetIdxs[i] = RetTypes.size() - 1;
|
||||
} else {
|
||||
DEBUG(dbgs() << "DAE - Removing return value from " << F->getName()
|
||||
<< "\n");
|
||||
++NumRetValsEliminated;
|
||||
DEBUG(dbgs() << "DAE - Removing return value " << i << " from "
|
||||
<< F->getName() << "\n");
|
||||
}
|
||||
if (RetTypes.size() > 1)
|
||||
// More than one return type? Return a struct with them. Also, if we used
|
||||
// to return a struct and didn't change the number of return values,
|
||||
// return a struct again. This prevents changing {something} into
|
||||
// something and {} into void.
|
||||
// Make the new struct packed if we used to return a packed struct
|
||||
// already.
|
||||
NRetTy = StructType::get(STy->getContext(), RetTypes, STy->isPacked());
|
||||
else if (RetTypes.size() == 1)
|
||||
}
|
||||
if (RetTypes.size() > 1) {
|
||||
// More than one return type? Reduce it down to size.
|
||||
if (StructType *STy = dyn_cast<StructType>(RetTy)) {
|
||||
// Make the new struct packed if we used to return a packed struct
|
||||
// already.
|
||||
NRetTy = StructType::get(STy->getContext(), RetTypes, STy->isPacked());
|
||||
} else {
|
||||
assert(isa<ArrayType>(RetTy) && "unexpected multi-value return");
|
||||
NRetTy = ArrayType::get(RetTypes[0], RetTypes.size());
|
||||
}
|
||||
} else if (RetTypes.size() == 1)
|
||||
// One return type? Just a simple value then, but only if we didn't use to
|
||||
// return a struct with that simple value before.
|
||||
NRetTy = RetTypes.front();
|
||||
@ -959,9 +967,9 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
|
||||
if (!Call->getType()->isX86_MMXTy())
|
||||
Call->replaceAllUsesWith(Constant::getNullValue(Call->getType()));
|
||||
} else {
|
||||
assert(RetTy->isStructTy() &&
|
||||
assert((RetTy->isStructTy() || RetTy->isArrayTy()) &&
|
||||
"Return type changed, but not into a void. The old return type"
|
||||
" must have been a struct!");
|
||||
" must have been a struct or an array!");
|
||||
Instruction *InsertPt = Call;
|
||||
if (InvokeInst *II = dyn_cast<InvokeInst>(Call)) {
|
||||
BasicBlock::iterator IP = II->getNormalDest()->begin();
|
||||
@ -969,9 +977,9 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
|
||||
InsertPt = IP;
|
||||
}
|
||||
|
||||
// We used to return a struct. Instead of doing smart stuff with all the
|
||||
// uses of this struct, we will just rebuild it using
|
||||
// extract/insertvalue chaining and let instcombine clean that up.
|
||||
// We used to return a struct or array. Instead of doing smart stuff
|
||||
// with all the uses, we will just rebuild it using extract/insertvalue
|
||||
// chaining and let instcombine clean that up.
|
||||
//
|
||||
// Start out building up our return value from undef
|
||||
Value *RetVal = UndefValue::get(RetTy);
|
||||
@ -1034,8 +1042,8 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
|
||||
if (NFTy->getReturnType()->isVoidTy()) {
|
||||
RetVal = nullptr;
|
||||
} else {
|
||||
assert (RetTy->isStructTy());
|
||||
// The original return value was a struct, insert
|
||||
assert(RetTy->isStructTy() || RetTy->isArrayTy());
|
||||
// The original return value was a struct or array, insert
|
||||
// extractvalue/insertvalue chains to extract only the values we need
|
||||
// to return and insert them into our new result.
|
||||
// This does generate messy code, but we'll let it to instcombine to
|
||||
|
@ -68,4 +68,48 @@ use_aggregate:
|
||||
ret { i32, i32 } %val
|
||||
}
|
||||
|
||||
declare void @callee(i32)
|
||||
declare void @callee(i32)
|
||||
|
||||
; Case 3: the insertvalue meant %in was live if ret-slot-1 was, but we were only
|
||||
; tracking multiple ret-slots for struct types. So %in was eliminated
|
||||
; incorrectly.
|
||||
|
||||
; CHECK-LABEL: define internal [2 x i32] @array_rets_have_multiple_slots(i32 %in)
|
||||
|
||||
define internal [2 x i32] @array_rets_have_multiple_slots(i32 %in) {
|
||||
%ret = insertvalue [2 x i32] undef, i32 %in, 1
|
||||
ret [2 x i32] %ret
|
||||
}
|
||||
|
||||
define [2 x i32] @test_array_rets_have_multiple_slots() {
|
||||
%res = call [2 x i32] @array_rets_have_multiple_slots(i32 42)
|
||||
ret [2 x i32] %res
|
||||
}
|
||||
|
||||
; Case 4: we can remove some retvals from the array. It's nice to produce an
|
||||
; array again having done so (rather than converting it to a struct).
|
||||
|
||||
; CHECK-LABEL: define internal [2 x i32] @can_shrink_arrays()
|
||||
; CHECK: [[VAL0:%.*]] = extractvalue [3 x i32] [i32 42, i32 43, i32 44], 0
|
||||
; CHECK: [[RESTMP:%.*]] = insertvalue [2 x i32] undef, i32 [[VAL0]], 0
|
||||
; CHECK: [[VAL2:%.*]] = extractvalue [3 x i32] [i32 42, i32 43, i32 44], 2
|
||||
; CHECK: [[RES:%.*]] = insertvalue [2 x i32] [[RESTMP]], i32 [[VAL2]], 1
|
||||
; CHECK: ret [2 x i32] [[RES]]
|
||||
|
||||
; CHECK-LABEL: define void @test_can_shrink_arrays()
|
||||
|
||||
define internal [3 x i32] @can_shrink_arrays() {
|
||||
ret [3 x i32] [i32 42, i32 43, i32 44]
|
||||
}
|
||||
|
||||
define void @test_can_shrink_arrays() {
|
||||
%res = call [3 x i32] @can_shrink_arrays()
|
||||
|
||||
%res.0 = extractvalue [3 x i32] %res, 0
|
||||
call void @callee(i32 %res.0)
|
||||
|
||||
%res.2 = extractvalue [3 x i32] %res, 2
|
||||
call void @callee(i32 %res.2)
|
||||
|
||||
ret void
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user