Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
35269 views
//===-- ExpandVariadicsPass.cpp --------------------------------*- C++ -*-=//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This is an optimization pass for variadic functions. If called from codegen,9// it can serve as the implementation of variadic functions for a given target.10//11// The strategy is to turn the ... part of a variadic function into a va_list12// and fix up the call sites. The majority of the pass is target independent.13// The exceptions are the va_list type itself and the rules for where to store14// variables in memory such that va_arg can iterate over them given a va_list.15//16// The majority of the plumbing is splitting the variadic function into a17// single basic block that packs the variadic arguments into a va_list and18// a second function that does the work of the original. That packing is19// exactly what is done by va_start. Further, the transform from ... to va_list20// replaced va_start with an operation to copy a va_list from the new argument,21// which is exactly a va_copy. This is useful for reducing target-dependence.22//23// A va_list instance is a forward iterator, where the primary operation va_arg24// is dereference-then-increment. This interface forces significant convergent25// evolution between target specific implementations. The variation in runtime26// data layout is limited to that representable by the iterator, parameterised27// by the type passed to the va_arg instruction.28//29// Therefore the majority of the target specific subtlety is packing arguments30// into a stack allocated buffer such that a va_list can be initialised with it31// and the va_arg expansion for the target will find the arguments at runtime.32//33// The aggregate effect is to unblock other transforms, most critically the34// general purpose inliner. Known calls to variadic functions become zero cost.35//36// Consistency with clang is primarily tested by emitting va_arg using clang37// then expanding the variadic functions using this pass, followed by trying38// to constant fold the functions to no-ops.39//40// Target specific behaviour is tested in IR - mainly checking that values are41// put into positions in call frames that make sense for that particular target.42//43// There is one "clever" invariant in use. va_start intrinsics that are not44// within a varidic functions are an error in the IR verifier. When this45// transform moves blocks from a variadic function into a fixed arity one, it46// moves va_start intrinsics along with everything else. That means that the47// va_start intrinsics that need to be rewritten to use the trailing argument48// are exactly those that are in non-variadic functions so no further state49// is needed to distinguish those that need to be rewritten.50//51//===----------------------------------------------------------------------===//5253#include "llvm/Transforms/IPO/ExpandVariadics.h"54#include "llvm/ADT/SmallVector.h"55#include "llvm/IR/Constants.h"56#include "llvm/IR/IRBuilder.h"57#include "llvm/IR/IntrinsicInst.h"58#include "llvm/IR/Module.h"59#include "llvm/IR/PassManager.h"60#include "llvm/InitializePasses.h"61#include "llvm/Pass.h"62#include "llvm/Support/CommandLine.h"63#include "llvm/TargetParser/Triple.h"64#include "llvm/Transforms/Utils/ModuleUtils.h"6566#define DEBUG_TYPE "expand-variadics"6768using namespace llvm;6970namespace {7172cl::opt<ExpandVariadicsMode> ExpandVariadicsModeOption(73DEBUG_TYPE "-override", cl::desc("Override the behaviour of " DEBUG_TYPE),74cl::init(ExpandVariadicsMode::Unspecified),75cl::values(clEnumValN(ExpandVariadicsMode::Unspecified, "unspecified",76"Use the implementation defaults"),77clEnumValN(ExpandVariadicsMode::Disable, "disable",78"Disable the pass entirely"),79clEnumValN(ExpandVariadicsMode::Optimize, "optimize",80"Optimise without changing ABI"),81clEnumValN(ExpandVariadicsMode::Lowering, "lowering",82"Change variadic calling convention")));8384bool commandLineOverride() {85return ExpandVariadicsModeOption != ExpandVariadicsMode::Unspecified;86}8788// Instances of this class encapsulate the target-dependant behaviour as a89// function of triple. Implementing a new ABI is adding a case to the switch90// in create(llvm::Triple) at the end of this file.91// This class may end up instantiated in TargetMachine instances, keeping it92// here for now until enough targets are implemented for the API to evolve.93class VariadicABIInfo {94protected:95VariadicABIInfo() = default;9697public:98static std::unique_ptr<VariadicABIInfo> create(const Triple &T);99100// Allow overriding whether the pass runs on a per-target basis101virtual bool enableForTarget() = 0;102103// Whether a valist instance is passed by value or by address104// I.e. does it need to be alloca'ed and stored into, or can105// it be passed directly in a SSA register106virtual bool vaListPassedInSSARegister() = 0;107108// The type of a va_list iterator object109virtual Type *vaListType(LLVMContext &Ctx) = 0;110111// The type of a va_list as a function argument as lowered by C112virtual Type *vaListParameterType(Module &M) = 0;113114// Initialize an allocated va_list object to point to an already115// initialized contiguous memory region.116// Return the value to pass as the va_list argument117virtual Value *initializeVaList(Module &M, LLVMContext &Ctx,118IRBuilder<> &Builder, AllocaInst *VaList,119Value *Buffer) = 0;120121struct VAArgSlotInfo {122Align DataAlign; // With respect to the call frame123bool Indirect; // Passed via a pointer124};125virtual VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) = 0;126127// Targets implemented so far all have the same trivial lowering for these128bool vaEndIsNop() { return true; }129bool vaCopyIsMemcpy() { return true; }130131virtual ~VariadicABIInfo() = default;132};133134// Module implements getFunction() which returns nullptr on missing declaration135// and getOrInsertFunction which creates one when absent. Intrinsics.h only136// implements getDeclaration which creates one when missing. Checking whether137// an intrinsic exists thus inserts it in the module and it then needs to be138// deleted again to clean up.139// The right name for the two functions on intrinsics would match Module::,140// but doing that in a single change would introduce nullptr dereferences141// where currently there are none. The minimal collateral damage approach142// would split the change over a release to help downstream branches. As it143// is unclear what approach will be preferred, implementing the trivial144// function here in the meantime to decouple from that discussion.145Function *getPreexistingDeclaration(Module *M, Intrinsic::ID Id,146ArrayRef<Type *> Tys = {}) {147auto *FT = Intrinsic::getType(M->getContext(), Id, Tys);148return M->getFunction(Tys.empty() ? Intrinsic::getName(Id)149: Intrinsic::getName(Id, Tys, M, FT));150}151152class ExpandVariadics : public ModulePass {153154// The pass construction sets the default to optimize when called from middle155// end and lowering when called from the backend. The command line variable156// overrides that. This is useful for testing and debugging. It also allows157// building an applications with variadic functions wholly removed if one158// has sufficient control over the dependencies, e.g. a statically linked159// clang that has no variadic function calls remaining in the binary.160161public:162static char ID;163const ExpandVariadicsMode Mode;164std::unique_ptr<VariadicABIInfo> ABI;165166ExpandVariadics(ExpandVariadicsMode Mode)167: ModulePass(ID),168Mode(commandLineOverride() ? ExpandVariadicsModeOption : Mode) {}169170StringRef getPassName() const override { return "Expand variadic functions"; }171172bool rewriteABI() { return Mode == ExpandVariadicsMode::Lowering; }173174bool runOnModule(Module &M) override;175176bool runOnFunction(Module &M, IRBuilder<> &Builder, Function *F);177178Function *replaceAllUsesWithNewDeclaration(Module &M,179Function *OriginalFunction);180181Function *deriveFixedArityReplacement(Module &M, IRBuilder<> &Builder,182Function *OriginalFunction);183184Function *defineVariadicWrapper(Module &M, IRBuilder<> &Builder,185Function *VariadicWrapper,186Function *FixedArityReplacement);187188bool expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB, FunctionType *,189Function *NF);190191// The intrinsic functions va_copy and va_end are removed unconditionally.192// They correspond to a memcpy and a no-op on all implemented targets.193// The va_start intrinsic is removed from basic blocks that were not created194// by this pass, some may remain if needed to maintain the external ABI.195196template <Intrinsic::ID ID, typename InstructionType>197bool expandIntrinsicUsers(Module &M, IRBuilder<> &Builder,198PointerType *IntrinsicArgType) {199bool Changed = false;200const DataLayout &DL = M.getDataLayout();201if (Function *Intrinsic =202getPreexistingDeclaration(&M, ID, {IntrinsicArgType})) {203for (User *U : make_early_inc_range(Intrinsic->users()))204if (auto *I = dyn_cast<InstructionType>(U))205Changed |= expandVAIntrinsicCall(Builder, DL, I);206207if (Intrinsic->use_empty())208Intrinsic->eraseFromParent();209}210return Changed;211}212213bool expandVAIntrinsicUsersWithAddrspace(Module &M, IRBuilder<> &Builder,214unsigned Addrspace) {215auto &Ctx = M.getContext();216PointerType *IntrinsicArgType = PointerType::get(Ctx, Addrspace);217bool Changed = false;218219// expand vastart before vacopy as vastart may introduce a vacopy220Changed |= expandIntrinsicUsers<Intrinsic::vastart, VAStartInst>(221M, Builder, IntrinsicArgType);222Changed |= expandIntrinsicUsers<Intrinsic::vaend, VAEndInst>(223M, Builder, IntrinsicArgType);224Changed |= expandIntrinsicUsers<Intrinsic::vacopy, VACopyInst>(225M, Builder, IntrinsicArgType);226return Changed;227}228229bool expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL,230VAStartInst *Inst);231232bool expandVAIntrinsicCall(IRBuilder<> &, const DataLayout &,233VAEndInst *Inst);234235bool expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL,236VACopyInst *Inst);237238FunctionType *inlinableVariadicFunctionType(Module &M, FunctionType *FTy) {239// The type of "FTy" with the ... removed and a va_list appended240SmallVector<Type *> ArgTypes(FTy->param_begin(), FTy->param_end());241ArgTypes.push_back(ABI->vaListParameterType(M));242return FunctionType::get(FTy->getReturnType(), ArgTypes,243/*IsVarArgs=*/false);244}245246static ConstantInt *sizeOfAlloca(LLVMContext &Ctx, const DataLayout &DL,247AllocaInst *Alloced) {248std::optional<TypeSize> AllocaTypeSize = Alloced->getAllocationSize(DL);249uint64_t AsInt = AllocaTypeSize ? AllocaTypeSize->getFixedValue() : 0;250return ConstantInt::get(Type::getInt64Ty(Ctx), AsInt);251}252253bool expansionApplicableToFunction(Module &M, Function *F) {254if (F->isIntrinsic() || !F->isVarArg() ||255F->hasFnAttribute(Attribute::Naked))256return false;257258if (F->getCallingConv() != CallingConv::C)259return false;260261if (rewriteABI())262return true;263264if (!F->hasExactDefinition())265return false;266267return true;268}269270bool expansionApplicableToFunctionCall(CallBase *CB) {271if (CallInst *CI = dyn_cast<CallInst>(CB)) {272if (CI->isMustTailCall()) {273// Cannot expand musttail calls274return false;275}276277if (CI->getCallingConv() != CallingConv::C)278return false;279280return true;281}282283if (isa<InvokeInst>(CB)) {284// Invoke not implemented in initial implementation of pass285return false;286}287288// Other unimplemented derivative of CallBase289return false;290}291292class ExpandedCallFrame {293// Helper for constructing an alloca instance containing the arguments bound294// to the variadic ... parameter, rearranged to allow indexing through a295// va_list iterator296enum { N = 4 };297SmallVector<Type *, N> FieldTypes;298enum Tag { Store, Memcpy, Padding };299SmallVector<std::tuple<Value *, uint64_t, Tag>, N> Source;300301template <Tag tag> void append(Type *FieldType, Value *V, uint64_t Bytes) {302FieldTypes.push_back(FieldType);303Source.push_back({V, Bytes, tag});304}305306public:307void store(LLVMContext &Ctx, Type *T, Value *V) { append<Store>(T, V, 0); }308309void memcpy(LLVMContext &Ctx, Type *T, Value *V, uint64_t Bytes) {310append<Memcpy>(T, V, Bytes);311}312313void padding(LLVMContext &Ctx, uint64_t By) {314append<Padding>(ArrayType::get(Type::getInt8Ty(Ctx), By), nullptr, 0);315}316317size_t size() const { return FieldTypes.size(); }318bool empty() const { return FieldTypes.empty(); }319320StructType *asStruct(LLVMContext &Ctx, StringRef Name) {321const bool IsPacked = true;322return StructType::create(Ctx, FieldTypes,323(Twine(Name) + ".vararg").str(), IsPacked);324}325326void initializeStructAlloca(const DataLayout &DL, IRBuilder<> &Builder,327AllocaInst *Alloced) {328329StructType *VarargsTy = cast<StructType>(Alloced->getAllocatedType());330331for (size_t I = 0; I < size(); I++) {332333auto [V, bytes, tag] = Source[I];334335if (tag == Padding) {336assert(V == nullptr);337continue;338}339340auto Dst = Builder.CreateStructGEP(VarargsTy, Alloced, I);341342assert(V != nullptr);343344if (tag == Store)345Builder.CreateStore(V, Dst);346347if (tag == Memcpy)348Builder.CreateMemCpy(Dst, {}, V, {}, bytes);349}350}351};352};353354bool ExpandVariadics::runOnModule(Module &M) {355bool Changed = false;356if (Mode == ExpandVariadicsMode::Disable)357return Changed;358359Triple TT(M.getTargetTriple());360ABI = VariadicABIInfo::create(TT);361if (!ABI)362return Changed;363364if (!ABI->enableForTarget())365return Changed;366367auto &Ctx = M.getContext();368const DataLayout &DL = M.getDataLayout();369IRBuilder<> Builder(Ctx);370371// Lowering needs to run on all functions exactly once.372// Optimize could run on functions containing va_start exactly once.373for (Function &F : make_early_inc_range(M))374Changed |= runOnFunction(M, Builder, &F);375376// After runOnFunction, all known calls to known variadic functions have been377// replaced. va_start intrinsics are presently (and invalidly!) only present378// in functions that used to be variadic and have now been replaced to take a379// va_list instead. If lowering as opposed to optimising, calls to unknown380// variadic functions have also been replaced.381382{383// 0 and AllocaAddrSpace are sufficient for the targets implemented so far384unsigned Addrspace = 0;385Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);386387Addrspace = DL.getAllocaAddrSpace();388if (Addrspace != 0)389Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);390}391392if (Mode != ExpandVariadicsMode::Lowering)393return Changed;394395for (Function &F : make_early_inc_range(M)) {396if (F.isDeclaration())397continue;398399// Now need to track down indirect calls. Can't find those400// by walking uses of variadic functions, need to crawl the instruction401// stream. Fortunately this is only necessary for the ABI rewrite case.402for (BasicBlock &BB : F) {403for (Instruction &I : make_early_inc_range(BB)) {404if (CallBase *CB = dyn_cast<CallBase>(&I)) {405if (CB->isIndirectCall()) {406FunctionType *FTy = CB->getFunctionType();407if (FTy->isVarArg())408Changed |= expandCall(M, Builder, CB, FTy, 0);409}410}411}412}413}414415return Changed;416}417418bool ExpandVariadics::runOnFunction(Module &M, IRBuilder<> &Builder,419Function *OriginalFunction) {420bool Changed = false;421422if (!expansionApplicableToFunction(M, OriginalFunction))423return Changed;424425[[maybe_unused]] const bool OriginalFunctionIsDeclaration =426OriginalFunction->isDeclaration();427assert(rewriteABI() || !OriginalFunctionIsDeclaration);428429// Declare a new function and redirect every use to that new function430Function *VariadicWrapper =431replaceAllUsesWithNewDeclaration(M, OriginalFunction);432assert(VariadicWrapper->isDeclaration());433assert(OriginalFunction->use_empty());434435// Create a new function taking va_list containing the implementation of the436// original437Function *FixedArityReplacement =438deriveFixedArityReplacement(M, Builder, OriginalFunction);439assert(OriginalFunction->isDeclaration());440assert(FixedArityReplacement->isDeclaration() ==441OriginalFunctionIsDeclaration);442assert(VariadicWrapper->isDeclaration());443444// Create a single block forwarding wrapper that turns a ... into a va_list445[[maybe_unused]] Function *VariadicWrapperDefine =446defineVariadicWrapper(M, Builder, VariadicWrapper, FixedArityReplacement);447assert(VariadicWrapperDefine == VariadicWrapper);448assert(!VariadicWrapper->isDeclaration());449450// We now have:451// 1. the original function, now as a declaration with no uses452// 2. a variadic function that unconditionally calls a fixed arity replacement453// 3. a fixed arity function equivalent to the original function454455// Replace known calls to the variadic with calls to the va_list equivalent456for (User *U : make_early_inc_range(VariadicWrapper->users())) {457if (CallBase *CB = dyn_cast<CallBase>(U)) {458Value *CalledOperand = CB->getCalledOperand();459if (VariadicWrapper == CalledOperand)460Changed |=461expandCall(M, Builder, CB, VariadicWrapper->getFunctionType(),462FixedArityReplacement);463}464}465466// The original function will be erased.467// One of the two new functions will become a replacement for the original.468// When preserving the ABI, the other is an internal implementation detail.469// When rewriting the ABI, RAUW then the variadic one.470Function *const ExternallyAccessible =471rewriteABI() ? FixedArityReplacement : VariadicWrapper;472Function *const InternalOnly =473rewriteABI() ? VariadicWrapper : FixedArityReplacement;474475// The external function is the replacement for the original476ExternallyAccessible->setLinkage(OriginalFunction->getLinkage());477ExternallyAccessible->setVisibility(OriginalFunction->getVisibility());478ExternallyAccessible->setComdat(OriginalFunction->getComdat());479ExternallyAccessible->takeName(OriginalFunction);480481// Annotate the internal one as internal482InternalOnly->setVisibility(GlobalValue::DefaultVisibility);483InternalOnly->setLinkage(GlobalValue::InternalLinkage);484485// The original is unused and obsolete486OriginalFunction->eraseFromParent();487488InternalOnly->removeDeadConstantUsers();489490if (rewriteABI()) {491// All known calls to the function have been removed by expandCall492// Resolve everything else by replaceAllUsesWith493VariadicWrapper->replaceAllUsesWith(FixedArityReplacement);494VariadicWrapper->eraseFromParent();495}496497return Changed;498}499500Function *501ExpandVariadics::replaceAllUsesWithNewDeclaration(Module &M,502Function *OriginalFunction) {503auto &Ctx = M.getContext();504Function &F = *OriginalFunction;505FunctionType *FTy = F.getFunctionType();506Function *NF = Function::Create(FTy, F.getLinkage(), F.getAddressSpace());507508NF->setName(F.getName() + ".varargs");509NF->IsNewDbgInfoFormat = F.IsNewDbgInfoFormat;510511F.getParent()->getFunctionList().insert(F.getIterator(), NF);512513AttrBuilder ParamAttrs(Ctx);514AttributeList Attrs = NF->getAttributes();515Attrs = Attrs.addParamAttributes(Ctx, FTy->getNumParams(), ParamAttrs);516NF->setAttributes(Attrs);517518OriginalFunction->replaceAllUsesWith(NF);519return NF;520}521522Function *523ExpandVariadics::deriveFixedArityReplacement(Module &M, IRBuilder<> &Builder,524Function *OriginalFunction) {525Function &F = *OriginalFunction;526// The purpose here is split the variadic function F into two functions527// One is a variadic function that bundles the passed argument into a va_list528// and passes it to the second function. The second function does whatever529// the original F does, except that it takes a va_list instead of the ...530531assert(expansionApplicableToFunction(M, &F));532533auto &Ctx = M.getContext();534535// Returned value isDeclaration() is equal to F.isDeclaration()536// but that property is not invariant throughout this function537const bool FunctionIsDefinition = !F.isDeclaration();538539FunctionType *FTy = F.getFunctionType();540SmallVector<Type *> ArgTypes(FTy->param_begin(), FTy->param_end());541ArgTypes.push_back(ABI->vaListParameterType(M));542543FunctionType *NFTy = inlinableVariadicFunctionType(M, FTy);544Function *NF = Function::Create(NFTy, F.getLinkage(), F.getAddressSpace());545546// Note - same attribute handling as DeadArgumentElimination547NF->copyAttributesFrom(&F);548NF->setComdat(F.getComdat());549F.getParent()->getFunctionList().insert(F.getIterator(), NF);550NF->setName(F.getName() + ".valist");551NF->IsNewDbgInfoFormat = F.IsNewDbgInfoFormat;552553AttrBuilder ParamAttrs(Ctx);554555AttributeList Attrs = NF->getAttributes();556Attrs = Attrs.addParamAttributes(Ctx, NFTy->getNumParams() - 1, ParamAttrs);557NF->setAttributes(Attrs);558559// Splice the implementation into the new function with minimal changes560if (FunctionIsDefinition) {561NF->splice(NF->begin(), &F);562563auto NewArg = NF->arg_begin();564for (Argument &Arg : F.args()) {565Arg.replaceAllUsesWith(NewArg);566NewArg->setName(Arg.getName()); // takeName without killing the old one567++NewArg;568}569NewArg->setName("varargs");570}571572SmallVector<std::pair<unsigned, MDNode *>, 1> MDs;573F.getAllMetadata(MDs);574for (auto [KindID, Node] : MDs)575NF->addMetadata(KindID, *Node);576F.clearMetadata();577578return NF;579}580581Function *582ExpandVariadics::defineVariadicWrapper(Module &M, IRBuilder<> &Builder,583Function *VariadicWrapper,584Function *FixedArityReplacement) {585auto &Ctx = Builder.getContext();586const DataLayout &DL = M.getDataLayout();587assert(VariadicWrapper->isDeclaration());588Function &F = *VariadicWrapper;589590assert(F.isDeclaration());591Type *VaListTy = ABI->vaListType(Ctx);592593auto *BB = BasicBlock::Create(Ctx, "entry", &F);594Builder.SetInsertPoint(BB);595596AllocaInst *VaListInstance =597Builder.CreateAlloca(VaListTy, nullptr, "va_start");598599Builder.CreateLifetimeStart(VaListInstance,600sizeOfAlloca(Ctx, DL, VaListInstance));601602Builder.CreateIntrinsic(Intrinsic::vastart, {DL.getAllocaPtrType(Ctx)},603{VaListInstance});604605SmallVector<Value *> Args;606for (Argument &A : F.args())607Args.push_back(&A);608609Type *ParameterType = ABI->vaListParameterType(M);610if (ABI->vaListPassedInSSARegister())611Args.push_back(Builder.CreateLoad(ParameterType, VaListInstance));612else613Args.push_back(Builder.CreateAddrSpaceCast(VaListInstance, ParameterType));614615CallInst *Result = Builder.CreateCall(FixedArityReplacement, Args);616617Builder.CreateIntrinsic(Intrinsic::vaend, {DL.getAllocaPtrType(Ctx)},618{VaListInstance});619Builder.CreateLifetimeEnd(VaListInstance,620sizeOfAlloca(Ctx, DL, VaListInstance));621622if (Result->getType()->isVoidTy())623Builder.CreateRetVoid();624else625Builder.CreateRet(Result);626627return VariadicWrapper;628}629630bool ExpandVariadics::expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB,631FunctionType *VarargFunctionType,632Function *NF) {633bool Changed = false;634const DataLayout &DL = M.getDataLayout();635636if (!expansionApplicableToFunctionCall(CB)) {637if (rewriteABI())638report_fatal_error("Cannot lower callbase instruction");639return Changed;640}641642// This is tricky. The call instruction's function type might not match643// the type of the caller. When optimising, can leave it unchanged.644// Webassembly detects that inconsistency and repairs it.645FunctionType *FuncType = CB->getFunctionType();646if (FuncType != VarargFunctionType) {647if (!rewriteABI())648return Changed;649FuncType = VarargFunctionType;650}651652auto &Ctx = CB->getContext();653654Align MaxFieldAlign(1);655656// The strategy is to allocate a call frame containing the variadic657// arguments laid out such that a target specific va_list can be initialized658// with it, such that target specific va_arg instructions will correctly659// iterate over it. This means getting the alignment right and sometimes660// embedding a pointer to the value instead of embedding the value itself.661662Function *CBF = CB->getParent()->getParent();663664ExpandedCallFrame Frame;665666uint64_t CurrentOffset = 0;667668for (unsigned I = FuncType->getNumParams(), E = CB->arg_size(); I < E; ++I) {669Value *ArgVal = CB->getArgOperand(I);670const bool IsByVal = CB->paramHasAttr(I, Attribute::ByVal);671const bool IsByRef = CB->paramHasAttr(I, Attribute::ByRef);672673// The type of the value being passed, decoded from byval/byref metadata if674// required675Type *const UnderlyingType = IsByVal ? CB->getParamByValType(I)676: IsByRef ? CB->getParamByRefType(I)677: ArgVal->getType();678const uint64_t UnderlyingSize =679DL.getTypeAllocSize(UnderlyingType).getFixedValue();680681// The type to be written into the call frame682Type *FrameFieldType = UnderlyingType;683684// The value to copy from when initialising the frame alloca685Value *SourceValue = ArgVal;686687VariadicABIInfo::VAArgSlotInfo SlotInfo = ABI->slotInfo(DL, UnderlyingType);688689if (SlotInfo.Indirect) {690// The va_arg lowering loads through a pointer. Set up an alloca to aim691// that pointer at.692Builder.SetInsertPointPastAllocas(CBF);693Builder.SetCurrentDebugLocation(CB->getStableDebugLoc());694Value *CallerCopy =695Builder.CreateAlloca(UnderlyingType, nullptr, "IndirectAlloca");696697Builder.SetInsertPoint(CB);698if (IsByVal)699Builder.CreateMemCpy(CallerCopy, {}, ArgVal, {}, UnderlyingSize);700else701Builder.CreateStore(ArgVal, CallerCopy);702703// Indirection now handled, pass the alloca ptr by value704FrameFieldType = DL.getAllocaPtrType(Ctx);705SourceValue = CallerCopy;706}707708// Alignment of the value within the frame709// This probably needs to be controllable as a function of type710Align DataAlign = SlotInfo.DataAlign;711712MaxFieldAlign = std::max(MaxFieldAlign, DataAlign);713714uint64_t DataAlignV = DataAlign.value();715if (uint64_t Rem = CurrentOffset % DataAlignV) {716// Inject explicit padding to deal with alignment requirements717uint64_t Padding = DataAlignV - Rem;718Frame.padding(Ctx, Padding);719CurrentOffset += Padding;720}721722if (SlotInfo.Indirect) {723Frame.store(Ctx, FrameFieldType, SourceValue);724} else {725if (IsByVal)726Frame.memcpy(Ctx, FrameFieldType, SourceValue, UnderlyingSize);727else728Frame.store(Ctx, FrameFieldType, SourceValue);729}730731CurrentOffset += DL.getTypeAllocSize(FrameFieldType).getFixedValue();732}733734if (Frame.empty()) {735// Not passing any arguments, hopefully va_arg won't try to read any736// Creating a single byte frame containing nothing to point the va_list737// instance as that is less special-casey in the compiler and probably738// easier to interpret in a debugger.739Frame.padding(Ctx, 1);740}741742StructType *VarargsTy = Frame.asStruct(Ctx, CBF->getName());743744// The struct instance needs to be at least MaxFieldAlign for the alignment of745// the fields to be correct at runtime. Use the native stack alignment instead746// if that's greater as that tends to give better codegen.747// This is an awkward way to guess whether there is a known stack alignment748// without hitting an assert in DL.getStackAlignment, 1024 is an arbitrary749// number likely to be greater than the natural stack alignment.750// TODO: DL.getStackAlignment could return a MaybeAlign instead of assert751Align AllocaAlign = MaxFieldAlign;752if (DL.exceedsNaturalStackAlignment(Align(1024)))753AllocaAlign = std::max(AllocaAlign, DL.getStackAlignment());754755// Put the alloca to hold the variadic args in the entry basic block.756Builder.SetInsertPointPastAllocas(CBF);757758// SetCurrentDebugLocation when the builder SetInsertPoint method does not759Builder.SetCurrentDebugLocation(CB->getStableDebugLoc());760761// The awkward construction here is to set the alignment on the instance762AllocaInst *Alloced = Builder.Insert(763new AllocaInst(VarargsTy, DL.getAllocaAddrSpace(), nullptr, AllocaAlign),764"vararg_buffer");765Changed = true;766assert(Alloced->getAllocatedType() == VarargsTy);767768// Initialize the fields in the struct769Builder.SetInsertPoint(CB);770Builder.CreateLifetimeStart(Alloced, sizeOfAlloca(Ctx, DL, Alloced));771Frame.initializeStructAlloca(DL, Builder, Alloced);772773const unsigned NumArgs = FuncType->getNumParams();774SmallVector<Value *> Args(CB->arg_begin(), CB->arg_begin() + NumArgs);775776// Initialize a va_list pointing to that struct and pass it as the last777// argument778AllocaInst *VaList = nullptr;779{780if (!ABI->vaListPassedInSSARegister()) {781Type *VaListTy = ABI->vaListType(Ctx);782Builder.SetInsertPointPastAllocas(CBF);783Builder.SetCurrentDebugLocation(CB->getStableDebugLoc());784VaList = Builder.CreateAlloca(VaListTy, nullptr, "va_argument");785Builder.SetInsertPoint(CB);786Builder.CreateLifetimeStart(VaList, sizeOfAlloca(Ctx, DL, VaList));787}788Builder.SetInsertPoint(CB);789Args.push_back(ABI->initializeVaList(M, Ctx, Builder, VaList, Alloced));790}791792// Attributes excluding any on the vararg arguments793AttributeList PAL = CB->getAttributes();794if (!PAL.isEmpty()) {795SmallVector<AttributeSet, 8> ArgAttrs;796for (unsigned ArgNo = 0; ArgNo < NumArgs; ArgNo++)797ArgAttrs.push_back(PAL.getParamAttrs(ArgNo));798PAL =799AttributeList::get(Ctx, PAL.getFnAttrs(), PAL.getRetAttrs(), ArgAttrs);800}801802SmallVector<OperandBundleDef, 1> OpBundles;803CB->getOperandBundlesAsDefs(OpBundles);804805CallBase *NewCB = nullptr;806807if (CallInst *CI = dyn_cast<CallInst>(CB)) {808Value *Dst = NF ? NF : CI->getCalledOperand();809FunctionType *NFTy = inlinableVariadicFunctionType(M, VarargFunctionType);810811NewCB = CallInst::Create(NFTy, Dst, Args, OpBundles, "", CI);812813CallInst::TailCallKind TCK = CI->getTailCallKind();814assert(TCK != CallInst::TCK_MustTail);815816// Can't tail call a function that is being passed a pointer to an alloca817if (TCK == CallInst::TCK_Tail)818TCK = CallInst::TCK_None;819CI->setTailCallKind(TCK);820821} else {822llvm_unreachable("Unreachable when !expansionApplicableToFunctionCall()");823}824825if (VaList)826Builder.CreateLifetimeEnd(VaList, sizeOfAlloca(Ctx, DL, VaList));827828Builder.CreateLifetimeEnd(Alloced, sizeOfAlloca(Ctx, DL, Alloced));829830NewCB->setAttributes(PAL);831NewCB->takeName(CB);832NewCB->setCallingConv(CB->getCallingConv());833NewCB->setDebugLoc(DebugLoc());834835// DeadArgElim and ArgPromotion copy exactly this metadata836NewCB->copyMetadata(*CB, {LLVMContext::MD_prof, LLVMContext::MD_dbg});837838CB->replaceAllUsesWith(NewCB);839CB->eraseFromParent();840return Changed;841}842843bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder,844const DataLayout &DL,845VAStartInst *Inst) {846// Only removing va_start instructions that are not in variadic functions.847// Those would be rejected by the IR verifier before this pass.848// After splicing basic blocks from a variadic function into a fixed arity849// one the va_start that used to refer to the ... parameter still exist.850// There are also variadic functions that this pass did not change and851// va_start instances in the created single block wrapper functions.852// Replace exactly the instances in non-variadic functions as those are853// the ones to be fixed up to use the va_list passed as the final argument.854855Function *ContainingFunction = Inst->getFunction();856if (ContainingFunction->isVarArg()) {857return false;858}859860// The last argument is a vaListParameterType, either a va_list861// or a pointer to one depending on the target.862bool PassedByValue = ABI->vaListPassedInSSARegister();863Argument *PassedVaList =864ContainingFunction->getArg(ContainingFunction->arg_size() - 1);865866// va_start takes a pointer to a va_list, e.g. one on the stack867Value *VaStartArg = Inst->getArgList();868869Builder.SetInsertPoint(Inst);870871if (PassedByValue) {872// The general thing to do is create an alloca, store the va_list argument873// to it, then create a va_copy. When vaCopyIsMemcpy(), this optimises to a874// store to the VaStartArg.875assert(ABI->vaCopyIsMemcpy());876Builder.CreateStore(PassedVaList, VaStartArg);877} else {878879// Otherwise emit a vacopy to pick up target-specific handling if any880auto &Ctx = Builder.getContext();881882Builder.CreateIntrinsic(Intrinsic::vacopy, {DL.getAllocaPtrType(Ctx)},883{VaStartArg, PassedVaList});884}885886Inst->eraseFromParent();887return true;888}889890bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &, const DataLayout &,891VAEndInst *Inst) {892assert(ABI->vaEndIsNop());893Inst->eraseFromParent();894return true;895}896897bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder,898const DataLayout &DL,899VACopyInst *Inst) {900assert(ABI->vaCopyIsMemcpy());901Builder.SetInsertPoint(Inst);902903auto &Ctx = Builder.getContext();904Type *VaListTy = ABI->vaListType(Ctx);905uint64_t Size = DL.getTypeAllocSize(VaListTy).getFixedValue();906907Builder.CreateMemCpy(Inst->getDest(), {}, Inst->getSrc(), {},908Builder.getInt32(Size));909910Inst->eraseFromParent();911return true;912}913914struct Amdgpu final : public VariadicABIInfo {915916bool enableForTarget() override { return true; }917918bool vaListPassedInSSARegister() override { return true; }919920Type *vaListType(LLVMContext &Ctx) override {921return PointerType::getUnqual(Ctx);922}923924Type *vaListParameterType(Module &M) override {925return PointerType::getUnqual(M.getContext());926}927928Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder,929AllocaInst * /*va_list*/, Value *Buffer) override {930// Given Buffer, which is an AllocInst of vararg_buffer931// need to return something usable as parameter type932return Builder.CreateAddrSpaceCast(Buffer, vaListParameterType(M));933}934935VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override {936return {Align(4), false};937}938};939940struct NVPTX final : public VariadicABIInfo {941942bool enableForTarget() override { return true; }943944bool vaListPassedInSSARegister() override { return true; }945946Type *vaListType(LLVMContext &Ctx) override {947return PointerType::getUnqual(Ctx);948}949950Type *vaListParameterType(Module &M) override {951return PointerType::getUnqual(M.getContext());952}953954Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder,955AllocaInst *, Value *Buffer) override {956return Builder.CreateAddrSpaceCast(Buffer, vaListParameterType(M));957}958959VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override {960// NVPTX expects natural alignment in all cases. The variadic call ABI will961// handle promoting types to their appropriate size and alignment.962Align A = DL.getABITypeAlign(Parameter);963return {A, false};964}965};966967struct Wasm final : public VariadicABIInfo {968969bool enableForTarget() override {970// Currently wasm is only used for testing.971return commandLineOverride();972}973974bool vaListPassedInSSARegister() override { return true; }975976Type *vaListType(LLVMContext &Ctx) override {977return PointerType::getUnqual(Ctx);978}979980Type *vaListParameterType(Module &M) override {981return PointerType::getUnqual(M.getContext());982}983984Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder,985AllocaInst * /*va_list*/, Value *Buffer) override {986return Buffer;987}988989VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override {990LLVMContext &Ctx = Parameter->getContext();991const unsigned MinAlign = 4;992Align A = DL.getABITypeAlign(Parameter);993if (A < MinAlign)994A = Align(MinAlign);995996if (auto *S = dyn_cast<StructType>(Parameter)) {997if (S->getNumElements() > 1) {998return {DL.getABITypeAlign(PointerType::getUnqual(Ctx)), true};999}1000}10011002return {A, false};1003}1004};10051006std::unique_ptr<VariadicABIInfo> VariadicABIInfo::create(const Triple &T) {1007switch (T.getArch()) {1008case Triple::r600:1009case Triple::amdgcn: {1010return std::make_unique<Amdgpu>();1011}10121013case Triple::wasm32: {1014return std::make_unique<Wasm>();1015}10161017case Triple::nvptx:1018case Triple::nvptx64: {1019return std::make_unique<NVPTX>();1020}10211022default:1023return {};1024}1025}10261027} // namespace10281029char ExpandVariadics::ID = 0;10301031INITIALIZE_PASS(ExpandVariadics, DEBUG_TYPE, "Expand variadic functions", false,1032false)10331034ModulePass *llvm::createExpandVariadicsPass(ExpandVariadicsMode M) {1035return new ExpandVariadics(M);1036}10371038PreservedAnalyses ExpandVariadicsPass::run(Module &M, ModuleAnalysisManager &) {1039return ExpandVariadics(Mode).runOnModule(M) ? PreservedAnalyses::none()1040: PreservedAnalyses::all();1041}10421043ExpandVariadicsPass::ExpandVariadicsPass(ExpandVariadicsMode M) : Mode(M) {}104410451046