//===-- ExpandVariadicsPass.cpp --------------------------------*- C++ -*-=// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This is an optimization pass for variadic functions. If called from codegen, // it can serve as the implementation of variadic functions for a given target. // // The strategy is to turn the ... part of a variadic function into a va_list // and fix up the call sites. The majority of the pass is target independent. // The exceptions are the va_list type itself and the rules for where to store // variables in memory such that va_arg can iterate over them given a va_list. // // The majority of the plumbing is splitting the variadic function into a // single basic block that packs the variadic arguments into a va_list and // a second function that does the work of the original. That packing is // exactly what is done by va_start. Further, the transform from ... to va_list // replaced va_start with an operation to copy a va_list from the new argument, // which is exactly a va_copy. This is useful for reducing target-dependence. // // A va_list instance is a forward iterator, where the primary operation va_arg // is dereference-then-increment. This interface forces significant convergent // evolution between target specific implementations. The variation in runtime // data layout is limited to that representable by the iterator, parameterised // by the type passed to the va_arg instruction. // // Therefore the majority of the target specific subtlety is packing arguments // into a stack allocated buffer such that a va_list can be initialised with it // and the va_arg expansion for the target will find the arguments at runtime. // // The aggregate effect is to unblock other transforms, most critically the // general purpose inliner. Known calls to variadic functions become zero cost. // // Consistency with clang is primarily tested by emitting va_arg using clang // then expanding the variadic functions using this pass, followed by trying // to constant fold the functions to no-ops. // // Target specific behaviour is tested in IR - mainly checking that values are // put into positions in call frames that make sense for that particular target. // // There is one "clever" invariant in use. va_start intrinsics that are not // within a varidic functions are an error in the IR verifier. When this // transform moves blocks from a variadic function into a fixed arity one, it // moves va_start intrinsics along with everything else. That means that the // va_start intrinsics that need to be rewritten to use the trailing argument // are exactly those that are in non-variadic functions so no further state // is needed to distinguish those that need to be rewritten. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/IPO/ExpandVariadics.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #define DEBUG_TYPE … usingnamespacellvm; namespace { cl::opt<ExpandVariadicsMode> ExpandVariadicsModeOption( DEBUG_TYPE "-override", cl::desc("Override the behaviour of " DEBUG_TYPE), cl::init(ExpandVariadicsMode::Unspecified), cl::values(clEnumValN(ExpandVariadicsMode::Unspecified, "unspecified", "Use the implementation defaults"), clEnumValN(ExpandVariadicsMode::Disable, "disable", "Disable the pass entirely"), clEnumValN(ExpandVariadicsMode::Optimize, "optimize", "Optimise without changing ABI"), clEnumValN(ExpandVariadicsMode::Lowering, "lowering", "Change variadic calling convention"))); bool commandLineOverride() { … } // Instances of this class encapsulate the target-dependant behaviour as a // function of triple. Implementing a new ABI is adding a case to the switch // in create(llvm::Triple) at the end of this file. // This class may end up instantiated in TargetMachine instances, keeping it // here for now until enough targets are implemented for the API to evolve. class VariadicABIInfo { … }; // Module implements getFunction() which returns nullptr on missing declaration // and getOrInsertFunction which creates one when absent. Intrinsics.h only // implements getDeclaration which creates one when missing. Checking whether // an intrinsic exists thus inserts it in the module and it then needs to be // deleted again to clean up. // The right name for the two functions on intrinsics would match Module::, // but doing that in a single change would introduce nullptr dereferences // where currently there are none. The minimal collateral damage approach // would split the change over a release to help downstream branches. As it // is unclear what approach will be preferred, implementing the trivial // function here in the meantime to decouple from that discussion. Function *getPreexistingDeclaration(Module *M, Intrinsic::ID Id, ArrayRef<Type *> Tys = { … } class ExpandVariadics : public ModulePass { … }; bool ExpandVariadics::runOnModule(Module &M) { … } bool ExpandVariadics::runOnFunction(Module &M, IRBuilder<> &Builder, Function *OriginalFunction) { … } Function * ExpandVariadics::replaceAllUsesWithNewDeclaration(Module &M, Function *OriginalFunction) { … } Function * ExpandVariadics::deriveFixedArityReplacement(Module &M, IRBuilder<> &Builder, Function *OriginalFunction) { … } Function * ExpandVariadics::defineVariadicWrapper(Module &M, IRBuilder<> &Builder, Function *VariadicWrapper, Function *FixedArityReplacement) { … } bool ExpandVariadics::expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB, FunctionType *VarargFunctionType, Function *NF) { … } bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL, VAStartInst *Inst) { … } bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &, const DataLayout &, VAEndInst *Inst) { … } bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL, VACopyInst *Inst) { … } struct Amdgpu final : public VariadicABIInfo { … }; struct NVPTX final : public VariadicABIInfo { … }; struct Wasm final : public VariadicABIInfo { … }; std::unique_ptr<VariadicABIInfo> VariadicABIInfo::create(const Triple &T) { … } } // namespace char ExpandVariadics::ID = …; INITIALIZE_PASS(…) ModulePass *llvm::createExpandVariadicsPass(ExpandVariadicsMode M) { … } PreservedAnalyses ExpandVariadicsPass::run(Module &M, ModuleAnalysisManager &) { … } ExpandVariadicsPass::ExpandVariadicsPass(ExpandVariadicsMode M) : … { … }