Skip to content

Commit 8e88711

Browse files
committed
[Attributor] Pack out arguments into a struct
1 parent dd5735f commit 8e88711

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

llvm/lib/Transforms/IPO/Attributor.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,132 @@ bool Attributor::shouldSeedAttribute(AbstractAttribute &AA) {
29632963
return Result;
29642964
}
29652965

2966+
// For now: argument can be put in the struct if it's write only and
2967+
// has no aliases.
2968+
static bool canBeComapctedInAStruct(const Argument &Arg, Attributor &A,
2969+
const AbstractAttribute &QueryingAA) {
2970+
IRPosition ArgPosition = IRPosition::argument(Arg);
2971+
// Check if Arg has no alias.
2972+
auto *AAliasInfo =
2973+
A.getAAFor<AANoAlias>(QueryingAA, ArgPosition, DepClassTy::NONE);
2974+
if (!AAliasInfo || !AAliasInfo->isKnownNoAlias())
2975+
return false;
2976+
2977+
// Check if Arg is write-only.
2978+
const auto *MemBehaviorAA =
2979+
A.getAAFor<AAMemoryBehavior>(QueryingAA, ArgPosition, DepClassTy::NONE);
2980+
if (!MemBehaviorAA || !MemBehaviorAA->isKnownWriteOnly())
2981+
return false;
2982+
2983+
return true;
2984+
}
2985+
2986+
static void replaceArgRetWithStructRetCalls(Function &OldFunction,
2987+
Function &NewFunction) {
2988+
for (auto UseItr = OldFunction.use_begin(); UseItr != OldFunction.use_end();
2989+
++UseItr) {
2990+
CallBase *Call = dyn_cast<CallBase>(UseItr->getUser());
2991+
if (!Call)
2992+
continue;
2993+
2994+
IRBuilder<> Builder(Call);
2995+
SmallVector<Value *, 8> NewArgs;
2996+
for (unsigned ArgIdx = 0; ArgIdx < Call->arg_size(); ++ArgIdx)
2997+
if (std::find_if(OldFunction.arg_begin(), OldFunction.arg_end(),
2998+
[&](Argument &Arg) {
2999+
return &Arg == Call->getArgOperand(ArgIdx);
3000+
}) == OldFunction.arg_end())
3001+
NewArgs.push_back(Call->getArgOperand(ArgIdx));
3002+
3003+
CallInst *NewCall = Builder.CreateCall(&NewFunction, NewArgs);
3004+
Call->replaceAllUsesWith(NewCall);
3005+
Call->eraseFromParent();
3006+
}
3007+
}
3008+
3009+
static bool convertOutArgsToRetStruct(Function &F, Attributor &A,
3010+
AbstractAttribute &QueryingAA) {
3011+
// Get valid ptr args.
3012+
DenseMap<Argument *, Type *> PtrToType;
3013+
for (unsigned ArgIdx = 0; ArgIdx < F.arg_size(); ++ArgIdx) {
3014+
Argument *Arg = F.getArg(ArgIdx);
3015+
if (Arg->getType()->isPointerTy() &&
3016+
canBeComapctedInAStruct(*Arg, A, QueryingAA)) {
3017+
// Get the the type of the pointer through its users
3018+
for (auto UseItr = Arg->use_begin(); UseItr != Arg->use_end(); ++UseItr) {
3019+
auto *Store = dyn_cast<StoreInst>(UseItr->getUser());
3020+
if (Store)
3021+
PtrToType[Arg] = Store->getValueOperand()->getType();
3022+
}
3023+
}
3024+
}
3025+
3026+
// If there is no valid candidates then return false.
3027+
if (PtrToType.empty())
3028+
return false;
3029+
3030+
// Create the new struct return type.
3031+
SmallVector<Type *, 4> OutStructElements;
3032+
if (auto *OriginalFuncTy = F.getReturnType(); !OriginalFuncTy->isVoidTy())
3033+
OutStructElements.push_back(OriginalFuncTy);
3034+
3035+
for (const auto &[Arg, Type] : PtrToType)
3036+
OutStructElements.push_back(Type);
3037+
3038+
auto *ReturnStructType = StructType::create(F.getContext(), OutStructElements,
3039+
(F.getName() + "Out").str());
3040+
3041+
// Get the new Args.
3042+
SmallVector<Type *, 4> NewParamTypes;
3043+
for (unsigned ArgIdx = 0; ArgIdx < F.arg_size(); ++ArgIdx)
3044+
if (!PtrToType.count(F.getArg(ArgIdx)))
3045+
NewParamTypes.push_back(F.getArg(ArgIdx)->getType());
3046+
3047+
auto *NewFunctionType =
3048+
FunctionType::get(ReturnStructType, NewParamTypes, F.isVarArg());
3049+
auto *NewFunction = Function::Create(NewFunctionType, F.getLinkage(),
3050+
F.getAddressSpace(), F.getName());
3051+
3052+
// Map old args to new args.
3053+
ValueToValueMapTy VMap;
3054+
auto *NewArgIt = NewFunction->arg_begin();
3055+
for (Argument &OldArg : F.args())
3056+
if (!PtrToType.count(F.getArg(OldArg.getArgNo())))
3057+
VMap[&OldArg] = &(*NewArgIt++);
3058+
3059+
// Clone the old function into the new one.
3060+
SmallVector<ReturnInst *, 8> Returns;
3061+
CloneFunctionInto(NewFunction, &F, VMap,
3062+
CloneFunctionChangeType::LocalChangesOnly, Returns);
3063+
3064+
// Update the return values (make it struct).
3065+
for (ReturnInst *Ret : Returns) {
3066+
IRBuilder<> Builder(Ret);
3067+
SmallVector<Value *, 4> StructValues;
3068+
// Include original return type, if any
3069+
if (auto *OriginalFuncTy = F.getReturnType(); !OriginalFuncTy->isVoidTy())
3070+
StructValues.push_back(Ret->getReturnValue());
3071+
3072+
// Create a load instruction to fill the struct element.
3073+
for (const auto &[Arg, Ty] : PtrToType) {
3074+
Value *OutVal = Builder.CreateLoad(Ty, VMap[Arg]);
3075+
StructValues.push_back(OutVal);
3076+
}
3077+
3078+
// Build the return struct incrementally.
3079+
Value *StructRetVal = UndefValue::get(ReturnStructType);
3080+
for (unsigned i = 0; i < StructValues.size(); ++i)
3081+
StructRetVal =
3082+
Builder.CreateInsertValue(StructRetVal, StructValues[i], i);
3083+
3084+
Builder.CreateRet(StructRetVal);
3085+
Ret->eraseFromParent();
3086+
}
3087+
3088+
replaceArgRetWithStructRetCalls(F, *NewFunction);
3089+
F.eraseFromParent();
3090+
}
3091+
29663092
ChangeStatus Attributor::rewriteFunctionSignatures(
29673093
SmallSetVector<Function *, 8> &ModifiedFns) {
29683094
ChangeStatus Changed = ChangeStatus::UNCHANGED;

0 commit comments

Comments
 (0)