| 1 | //===-- ExpandVariadicsPass.cpp --------------------------------*- C++ -*-=// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This is an optimization pass for variadic functions. If called from codegen, |
| 10 | // it can serve as the implementation of variadic functions for a given target. |
| 11 | // |
| 12 | // The strategy is to turn the ... part of a variadic function into a va_list |
| 13 | // and fix up the call sites. The majority of the pass is target independent. |
| 14 | // The exceptions are the va_list type itself and the rules for where to store |
| 15 | // variables in memory such that va_arg can iterate over them given a va_list. |
| 16 | // |
| 17 | // The majority of the plumbing is splitting the variadic function into a |
| 18 | // single basic block that packs the variadic arguments into a va_list and |
| 19 | // a second function that does the work of the original. That packing is |
| 20 | // exactly what is done by va_start. Further, the transform from ... to va_list |
| 21 | // replaced va_start with an operation to copy a va_list from the new argument, |
| 22 | // which is exactly a va_copy. This is useful for reducing target-dependence. |
| 23 | // |
| 24 | // A va_list instance is a forward iterator, where the primary operation va_arg |
| 25 | // is dereference-then-increment. This interface forces significant convergent |
| 26 | // evolution between target specific implementations. The variation in runtime |
| 27 | // data layout is limited to that representable by the iterator, parameterised |
| 28 | // by the type passed to the va_arg instruction. |
| 29 | // |
| 30 | // Therefore the majority of the target specific subtlety is packing arguments |
| 31 | // into a stack allocated buffer such that a va_list can be initialised with it |
| 32 | // and the va_arg expansion for the target will find the arguments at runtime. |
| 33 | // |
| 34 | // The aggregate effect is to unblock other transforms, most critically the |
| 35 | // general purpose inliner. Known calls to variadic functions become zero cost. |
| 36 | // |
| 37 | // Consistency with clang is primarily tested by emitting va_arg using clang |
| 38 | // then expanding the variadic functions using this pass, followed by trying |
| 39 | // to constant fold the functions to no-ops. |
| 40 | // |
| 41 | // Target specific behaviour is tested in IR - mainly checking that values are |
| 42 | // put into positions in call frames that make sense for that particular target. |
| 43 | // |
| 44 | // There is one "clever" invariant in use. va_start intrinsics that are not |
| 45 | // within a varidic functions are an error in the IR verifier. When this |
| 46 | // transform moves blocks from a variadic function into a fixed arity one, it |
| 47 | // moves va_start intrinsics along with everything else. That means that the |
| 48 | // va_start intrinsics that need to be rewritten to use the trailing argument |
| 49 | // are exactly those that are in non-variadic functions so no further state |
| 50 | // is needed to distinguish those that need to be rewritten. |
| 51 | // |
| 52 | //===----------------------------------------------------------------------===// |
| 53 | |
| 54 | #include "llvm/Transforms/IPO/ExpandVariadics.h" |
| 55 | #include "llvm/ADT/Sequence.h" |
| 56 | #include "llvm/ADT/SmallVector.h" |
| 57 | #include "llvm/Demangle/Demangle.h" |
| 58 | #include "llvm/IR/IRBuilder.h" |
| 59 | #include "llvm/IR/IntrinsicInst.h" |
| 60 | #include "llvm/IR/Module.h" |
| 61 | #include "llvm/IR/PassManager.h" |
| 62 | #include "llvm/InitializePasses.h" |
| 63 | #include "llvm/Pass.h" |
| 64 | #include "llvm/Support/CommandLine.h" |
| 65 | #include "llvm/Support/NVPTXAddrSpace.h" |
| 66 | #include "llvm/TargetParser/Triple.h" |
| 67 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
| 68 | |
| 69 | #define DEBUG_TYPE "expand-variadics" |
| 70 | |
| 71 | using namespace llvm; |
| 72 | |
| 73 | namespace { |
| 74 | |
| 75 | cl::opt<ExpandVariadicsMode> ExpandVariadicsModeOption( |
| 76 | DEBUG_TYPE "-override" , cl::desc("Override the behaviour of " DEBUG_TYPE), |
| 77 | cl::init(Val: ExpandVariadicsMode::Unspecified), |
| 78 | cl::values(clEnumValN(ExpandVariadicsMode::Unspecified, "unspecified" , |
| 79 | "Use the implementation defaults" ), |
| 80 | clEnumValN(ExpandVariadicsMode::Disable, "disable" , |
| 81 | "Disable the pass entirely" ), |
| 82 | clEnumValN(ExpandVariadicsMode::Optimize, "optimize" , |
| 83 | "Optimise without changing ABI" ), |
| 84 | clEnumValN(ExpandVariadicsMode::Lowering, "lowering" , |
| 85 | "Change variadic calling convention" ))); |
| 86 | |
| 87 | bool commandLineOverride() { |
| 88 | return ExpandVariadicsModeOption != ExpandVariadicsMode::Unspecified; |
| 89 | } |
| 90 | |
| 91 | // Instances of this class encapsulate the target-dependant behaviour as a |
| 92 | // function of triple. Implementing a new ABI is adding a case to the switch |
| 93 | // in create(llvm::Triple) at the end of this file. |
| 94 | // This class may end up instantiated in TargetMachine instances, keeping it |
| 95 | // here for now until enough targets are implemented for the API to evolve. |
| 96 | class VariadicABIInfo { |
| 97 | protected: |
| 98 | VariadicABIInfo() = default; |
| 99 | |
| 100 | public: |
| 101 | static std::unique_ptr<VariadicABIInfo> create(const Triple &T); |
| 102 | |
| 103 | // Allow overriding whether the pass runs on a per-target basis |
| 104 | virtual bool enableForTarget() = 0; |
| 105 | |
| 106 | // Whether a valist instance is passed by value or by address |
| 107 | // I.e. does it need to be alloca'ed and stored into, or can |
| 108 | // it be passed directly in a SSA register |
| 109 | virtual bool vaListPassedInSSARegister() = 0; |
| 110 | |
| 111 | // The type of a va_list iterator object |
| 112 | virtual Type *vaListType(LLVMContext &Ctx) = 0; |
| 113 | |
| 114 | // The type of a va_list as a function argument as lowered by C |
| 115 | virtual Type *vaListParameterType(Module &M) = 0; |
| 116 | |
| 117 | // Initialize an allocated va_list object to point to an already |
| 118 | // initialized contiguous memory region. |
| 119 | // Return the value to pass as the va_list argument |
| 120 | virtual Value *initializeVaList(Module &M, LLVMContext &Ctx, |
| 121 | IRBuilder<> &Builder, AllocaInst *VaList, |
| 122 | Value *Buffer) = 0; |
| 123 | |
| 124 | struct VAArgSlotInfo { |
| 125 | Align DataAlign; // With respect to the call frame |
| 126 | bool Indirect; // Passed via a pointer |
| 127 | }; |
| 128 | virtual VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) = 0; |
| 129 | |
| 130 | // Targets implemented so far all have the same trivial lowering for these |
| 131 | bool vaEndIsNop() { return true; } |
| 132 | bool vaCopyIsMemcpy() { return true; } |
| 133 | |
| 134 | // Per-target overrides of special symbols. |
| 135 | virtual bool ignoreFunction(const Function *F) { return false; } |
| 136 | |
| 137 | // Any additional address spaces used in va intrinsics that should be |
| 138 | // expanded. |
| 139 | virtual SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const { |
| 140 | return {}; |
| 141 | } |
| 142 | |
| 143 | virtual ~VariadicABIInfo() = default; |
| 144 | }; |
| 145 | |
| 146 | class ExpandVariadics : public ModulePass { |
| 147 | |
| 148 | // The pass construction sets the default to optimize when called from middle |
| 149 | // end and lowering when called from the backend. The command line variable |
| 150 | // overrides that. This is useful for testing and debugging. It also allows |
| 151 | // building an applications with variadic functions wholly removed if one |
| 152 | // has sufficient control over the dependencies, e.g. a statically linked |
| 153 | // clang that has no variadic function calls remaining in the binary. |
| 154 | |
| 155 | public: |
| 156 | static char ID; |
| 157 | const ExpandVariadicsMode Mode; |
| 158 | std::unique_ptr<VariadicABIInfo> ABI; |
| 159 | |
| 160 | ExpandVariadics(ExpandVariadicsMode Mode) |
| 161 | : ModulePass(ID), |
| 162 | Mode(commandLineOverride() ? ExpandVariadicsModeOption : Mode) {} |
| 163 | |
| 164 | StringRef getPassName() const override { return "Expand variadic functions" ; } |
| 165 | |
| 166 | bool rewriteABI() { return Mode == ExpandVariadicsMode::Lowering; } |
| 167 | |
| 168 | template <typename T> bool isValidCallingConv(T *F) { |
| 169 | return F->getCallingConv() == CallingConv::C || |
| 170 | F->getCallingConv() == CallingConv::SPIR_FUNC; |
| 171 | } |
| 172 | |
| 173 | bool runOnModule(Module &M) override; |
| 174 | |
| 175 | bool runOnFunction(Module &M, IRBuilder<> &Builder, Function *F); |
| 176 | |
| 177 | Function *replaceAllUsesWithNewDeclaration(Module &M, |
| 178 | Function *OriginalFunction); |
| 179 | |
| 180 | Function *deriveFixedArityReplacement(Module &M, IRBuilder<> &Builder, |
| 181 | Function *OriginalFunction); |
| 182 | |
| 183 | Function *defineVariadicWrapper(Module &M, IRBuilder<> &Builder, |
| 184 | Function *VariadicWrapper, |
| 185 | Function *FixedArityReplacement); |
| 186 | |
| 187 | bool expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB, FunctionType *, |
| 188 | Function *NF); |
| 189 | |
| 190 | // The intrinsic functions va_copy and va_end are removed unconditionally. |
| 191 | // They correspond to a memcpy and a no-op on all implemented targets. |
| 192 | // The va_start intrinsic is removed from basic blocks that were not created |
| 193 | // by this pass, some may remain if needed to maintain the external ABI. |
| 194 | |
| 195 | template <Intrinsic::ID ID, typename InstructionType> |
| 196 | bool expandIntrinsicUsers(Module &M, IRBuilder<> &Builder, |
| 197 | PointerType *IntrinsicArgType) { |
| 198 | bool Changed = false; |
| 199 | const DataLayout &DL = M.getDataLayout(); |
| 200 | if (Function *Intrinsic = |
| 201 | Intrinsic::getDeclarationIfExists(M: &M, id: ID, OverloadTys: {IntrinsicArgType})) { |
| 202 | for (User *U : make_early_inc_range(Range: Intrinsic->users())) |
| 203 | if (auto *I = dyn_cast<InstructionType>(U)) |
| 204 | Changed |= expandVAIntrinsicCall(Builder, DL, I); |
| 205 | |
| 206 | if (Intrinsic->use_empty()) |
| 207 | Intrinsic->eraseFromParent(); |
| 208 | } |
| 209 | return Changed; |
| 210 | } |
| 211 | |
| 212 | bool expandVAIntrinsicUsersWithAddrspace(Module &M, IRBuilder<> &Builder, |
| 213 | unsigned Addrspace) { |
| 214 | auto &Ctx = M.getContext(); |
| 215 | PointerType *IntrinsicArgType = PointerType::get(C&: Ctx, AddressSpace: Addrspace); |
| 216 | bool Changed = false; |
| 217 | |
| 218 | // expand vastart before vacopy as vastart may introduce a vacopy |
| 219 | Changed |= expandIntrinsicUsers<Intrinsic::vastart, VAStartInst>( |
| 220 | M, Builder, IntrinsicArgType); |
| 221 | Changed |= expandIntrinsicUsers<Intrinsic::vaend, VAEndInst>( |
| 222 | M, Builder, IntrinsicArgType); |
| 223 | Changed |= expandIntrinsicUsers<Intrinsic::vacopy, VACopyInst>( |
| 224 | M, Builder, IntrinsicArgType); |
| 225 | return Changed; |
| 226 | } |
| 227 | |
| 228 | bool expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL, |
| 229 | VAStartInst *Inst); |
| 230 | |
| 231 | bool expandVAIntrinsicCall(IRBuilder<> &, const DataLayout &, |
| 232 | VAEndInst *Inst); |
| 233 | |
| 234 | bool expandVAIntrinsicCall(IRBuilder<> &Builder, const DataLayout &DL, |
| 235 | VACopyInst *Inst); |
| 236 | |
| 237 | bool expandVAArgInst(IRBuilder<> &Builder, const DataLayout &DL, |
| 238 | VAArgInst *Inst); |
| 239 | |
| 240 | FunctionType *inlinableVariadicFunctionType(Module &M, FunctionType *FTy, |
| 241 | Type *ReturnType) { |
| 242 | // The type of "FTy" with the ... removed and a va_list appended |
| 243 | SmallVector<Type *> ArgTypes(FTy->params()); |
| 244 | ArgTypes.push_back(Elt: ABI->vaListParameterType(M)); |
| 245 | return FunctionType::get(Result: ReturnType, Params: ArgTypes, /*IsVarArgs=*/isVarArg: false); |
| 246 | } |
| 247 | |
| 248 | FunctionType *inlinableVariadicFunctionType(Module &M, FunctionType *FTy) { |
| 249 | return inlinableVariadicFunctionType(M, FTy, ReturnType: FTy->getReturnType()); |
| 250 | } |
| 251 | |
| 252 | bool expansionApplicableToFunction(Module &M, Function *F) { |
| 253 | if (F->isIntrinsic() || !F->isVarArg() || |
| 254 | F->hasFnAttribute(Kind: Attribute::Naked)) |
| 255 | return false; |
| 256 | |
| 257 | if (ABI->ignoreFunction(F)) |
| 258 | return false; |
| 259 | |
| 260 | if (!isValidCallingConv(F)) |
| 261 | return false; |
| 262 | |
| 263 | if (rewriteABI()) |
| 264 | return true; |
| 265 | |
| 266 | if (!F->hasExactDefinition()) |
| 267 | return false; |
| 268 | |
| 269 | return true; |
| 270 | } |
| 271 | |
| 272 | bool expansionApplicableToFunctionCall(CallBase *CB) { |
| 273 | if (CallInst *CI = dyn_cast<CallInst>(Val: CB)) { |
| 274 | if (CI->isMustTailCall()) { |
| 275 | // Cannot expand musttail calls |
| 276 | return false; |
| 277 | } |
| 278 | |
| 279 | if (!isValidCallingConv(F: CI)) |
| 280 | return false; |
| 281 | |
| 282 | return true; |
| 283 | } |
| 284 | |
| 285 | if (isa<InvokeInst>(Val: CB)) { |
| 286 | // Invoke not implemented in initial implementation of pass |
| 287 | return false; |
| 288 | } |
| 289 | |
| 290 | // Other unimplemented derivative of CallBase |
| 291 | return false; |
| 292 | } |
| 293 | |
| 294 | class ExpandedCallFrame { |
| 295 | // Helper for constructing an alloca instance containing the arguments bound |
| 296 | // to the variadic ... parameter, rearranged to allow indexing through a |
| 297 | // va_list iterator |
| 298 | enum { N = 4 }; |
| 299 | SmallVector<Type *, N> FieldTypes; |
| 300 | enum Tag { Store, Memcpy, Padding }; |
| 301 | SmallVector<std::tuple<Value *, uint64_t, Tag>, N> Source; |
| 302 | |
| 303 | template <Tag tag> void append(Type *FieldType, Value *V, uint64_t Bytes) { |
| 304 | FieldTypes.push_back(Elt: FieldType); |
| 305 | Source.push_back(Elt: {V, Bytes, tag}); |
| 306 | } |
| 307 | |
| 308 | public: |
| 309 | void store(LLVMContext &Ctx, Type *T, Value *V) { append<Store>(FieldType: T, V, Bytes: 0); } |
| 310 | |
| 311 | void memcpy(LLVMContext &Ctx, Type *T, Value *V, uint64_t Bytes) { |
| 312 | append<Memcpy>(FieldType: T, V, Bytes); |
| 313 | } |
| 314 | |
| 315 | void padding(LLVMContext &Ctx, uint64_t By) { |
| 316 | append<Padding>(FieldType: ArrayType::get(ElementType: Type::getInt8Ty(C&: Ctx), NumElements: By), V: nullptr, Bytes: 0); |
| 317 | } |
| 318 | |
| 319 | size_t size() const { return FieldTypes.size(); } |
| 320 | bool empty() const { return FieldTypes.empty(); } |
| 321 | |
| 322 | StructType *asStruct(LLVMContext &Ctx, StringRef Name) { |
| 323 | const bool IsPacked = true; |
| 324 | return StructType::create(Context&: Ctx, Elements: FieldTypes, |
| 325 | Name: (Twine(Name) + ".vararg" ).str(), isPacked: IsPacked); |
| 326 | } |
| 327 | |
| 328 | void initializeStructAlloca(const DataLayout &DL, IRBuilder<> &Builder, |
| 329 | AllocaInst *Alloced, StructType *VarargsTy) { |
| 330 | |
| 331 | for (size_t I = 0; I < size(); I++) { |
| 332 | |
| 333 | auto [V, bytes, tag] = Source[I]; |
| 334 | |
| 335 | if (tag == Padding) { |
| 336 | assert(V == nullptr); |
| 337 | continue; |
| 338 | } |
| 339 | |
| 340 | auto Dst = Builder.CreateStructGEP(Ty: VarargsTy, Ptr: Alloced, Idx: I); |
| 341 | |
| 342 | assert(V != nullptr); |
| 343 | |
| 344 | if (tag == Store) |
| 345 | Builder.CreateStore(Val: V, Ptr: Dst); |
| 346 | |
| 347 | if (tag == Memcpy) |
| 348 | Builder.CreateMemCpy(Dst, DstAlign: {}, Src: V, SrcAlign: {}, Size: bytes); |
| 349 | } |
| 350 | } |
| 351 | }; |
| 352 | }; |
| 353 | |
| 354 | bool ExpandVariadics::runOnModule(Module &M) { |
| 355 | bool Changed = false; |
| 356 | if (Mode == ExpandVariadicsMode::Disable) |
| 357 | return Changed; |
| 358 | |
| 359 | Triple TT(M.getTargetTriple()); |
| 360 | ABI = VariadicABIInfo::create(T: TT); |
| 361 | if (!ABI) |
| 362 | return Changed; |
| 363 | |
| 364 | if (!ABI->enableForTarget()) |
| 365 | return Changed; |
| 366 | |
| 367 | auto &Ctx = M.getContext(); |
| 368 | const DataLayout &DL = M.getDataLayout(); |
| 369 | IRBuilder<> Builder(Ctx); |
| 370 | |
| 371 | // Lowering needs to run on all functions exactly once. |
| 372 | // Optimize could run on functions containing va_start exactly once. |
| 373 | for (Function &F : make_early_inc_range(Range&: M)) |
| 374 | Changed |= runOnFunction(M, Builder, F: &F); |
| 375 | |
| 376 | // After runOnFunction, all known calls to known variadic functions have been |
| 377 | // replaced. va_start intrinsics are presently (and invalidly!) only present |
| 378 | // in functions that used to be variadic and have now been replaced to take a |
| 379 | // va_list instead. If lowering as opposed to optimising, calls to unknown |
| 380 | // variadic functions have also been replaced. |
| 381 | |
| 382 | { |
| 383 | unsigned Addrspace = 0; |
| 384 | Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace); |
| 385 | |
| 386 | Addrspace = DL.getAllocaAddrSpace(); |
| 387 | if (Addrspace != 0) |
| 388 | Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace); |
| 389 | |
| 390 | // Process any addrspaces targets declare to be important. |
| 391 | const SmallVector<unsigned> &TargetASVec = |
| 392 | ABI->getTargetSpecificVaIntrinAddrSpaces(); |
| 393 | for (unsigned TargetAS : TargetASVec) { |
| 394 | if (TargetAS == 0 || TargetAS == DL.getAllocaAddrSpace()) |
| 395 | continue; |
| 396 | Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace: TargetAS); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | if (Mode != ExpandVariadicsMode::Lowering) |
| 401 | return Changed; |
| 402 | |
| 403 | for (Function &F : make_early_inc_range(Range&: M)) { |
| 404 | if (F.isDeclaration()) |
| 405 | continue; |
| 406 | |
| 407 | // Now need to track down indirect calls and va_arg instructions. Can't find |
| 408 | // those by walking uses of variadic functions, need to crawl the |
| 409 | // instruction stream. Fortunately this is only necessary for the ABI |
| 410 | // rewrite case. |
| 411 | for (BasicBlock &BB : F) { |
| 412 | for (Instruction &I : make_early_inc_range(Range&: BB)) { |
| 413 | if (auto *VA = dyn_cast<VAArgInst>(Val: &I)) { |
| 414 | Changed |= expandVAArgInst(Builder, DL, Inst: VA); |
| 415 | } else if (CallBase *CB = dyn_cast<CallBase>(Val: &I)) { |
| 416 | if (CB->isIndirectCall()) { |
| 417 | FunctionType *FTy = CB->getFunctionType(); |
| 418 | if (FTy->isVarArg()) |
| 419 | Changed |= expandCall(M, Builder, CB, FTy, /*NF=*/nullptr); |
| 420 | } |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | return Changed; |
| 427 | } |
| 428 | |
| 429 | bool ExpandVariadics::runOnFunction(Module &M, IRBuilder<> &Builder, |
| 430 | Function *OriginalFunction) { |
| 431 | bool Changed = false; |
| 432 | |
| 433 | if (!expansionApplicableToFunction(M, F: OriginalFunction)) |
| 434 | return Changed; |
| 435 | |
| 436 | [[maybe_unused]] const bool OriginalFunctionIsDeclaration = |
| 437 | OriginalFunction->isDeclaration(); |
| 438 | assert(rewriteABI() || !OriginalFunctionIsDeclaration); |
| 439 | |
| 440 | // Declare a new function and redirect every use to that new function |
| 441 | Function *VariadicWrapper = |
| 442 | replaceAllUsesWithNewDeclaration(M, OriginalFunction); |
| 443 | assert(VariadicWrapper->isDeclaration()); |
| 444 | assert(OriginalFunction->use_empty()); |
| 445 | |
| 446 | // Create a new function taking va_list containing the implementation of the |
| 447 | // original |
| 448 | Function *FixedArityReplacement = |
| 449 | deriveFixedArityReplacement(M, Builder, OriginalFunction); |
| 450 | assert(OriginalFunction->isDeclaration()); |
| 451 | assert(FixedArityReplacement->isDeclaration() == |
| 452 | OriginalFunctionIsDeclaration); |
| 453 | assert(VariadicWrapper->isDeclaration()); |
| 454 | |
| 455 | // Create a single block forwarding wrapper that turns a ... into a va_list |
| 456 | [[maybe_unused]] Function *VariadicWrapperDefine = |
| 457 | defineVariadicWrapper(M, Builder, VariadicWrapper, FixedArityReplacement); |
| 458 | assert(VariadicWrapperDefine == VariadicWrapper); |
| 459 | assert(!VariadicWrapper->isDeclaration()); |
| 460 | |
| 461 | // Add the prof metadata from the original function to the wrapper. Because |
| 462 | // FixedArityReplacement is the owner of original function's prof metadata |
| 463 | // after the splice, we need to transfer it to VariadicWrapper. |
| 464 | VariadicWrapper->setMetadata( |
| 465 | KindID: LLVMContext::MD_prof, |
| 466 | Node: FixedArityReplacement->getMetadata(KindID: LLVMContext::MD_prof)); |
| 467 | |
| 468 | // We now have: |
| 469 | // 1. the original function, now as a declaration with no uses |
| 470 | // 2. a variadic function that unconditionally calls a fixed arity replacement |
| 471 | // 3. a fixed arity function equivalent to the original function |
| 472 | |
| 473 | // Replace known calls to the variadic with calls to the va_list equivalent |
| 474 | for (User *U : make_early_inc_range(Range: VariadicWrapper->users())) { |
| 475 | if (CallBase *CB = dyn_cast<CallBase>(Val: U)) { |
| 476 | Value *CalledOperand = CB->getCalledOperand(); |
| 477 | if (VariadicWrapper == CalledOperand) |
| 478 | Changed |= |
| 479 | expandCall(M, Builder, CB, VariadicWrapper->getFunctionType(), |
| 480 | NF: FixedArityReplacement); |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | // The original function will be erased. |
| 485 | // One of the two new functions will become a replacement for the original. |
| 486 | // When preserving the ABI, the other is an internal implementation detail. |
| 487 | // When rewriting the ABI, RAUW then the variadic one. |
| 488 | Function *const ExternallyAccessible = |
| 489 | rewriteABI() ? FixedArityReplacement : VariadicWrapper; |
| 490 | Function *const InternalOnly = |
| 491 | rewriteABI() ? VariadicWrapper : FixedArityReplacement; |
| 492 | |
| 493 | // The external function is the replacement for the original |
| 494 | ExternallyAccessible->setLinkage(OriginalFunction->getLinkage()); |
| 495 | ExternallyAccessible->setVisibility(OriginalFunction->getVisibility()); |
| 496 | ExternallyAccessible->setComdat(OriginalFunction->getComdat()); |
| 497 | ExternallyAccessible->takeName(V: OriginalFunction); |
| 498 | |
| 499 | // Annotate the internal one as internal |
| 500 | InternalOnly->setVisibility(GlobalValue::DefaultVisibility); |
| 501 | InternalOnly->setLinkage(GlobalValue::InternalLinkage); |
| 502 | |
| 503 | // The original is unused and obsolete |
| 504 | OriginalFunction->eraseFromParent(); |
| 505 | |
| 506 | InternalOnly->removeDeadConstantUsers(); |
| 507 | |
| 508 | if (rewriteABI()) { |
| 509 | // All known calls to the function have been removed by expandCall |
| 510 | // Resolve everything else by replaceAllUsesWith |
| 511 | VariadicWrapper->replaceAllUsesWith(V: FixedArityReplacement); |
| 512 | VariadicWrapper->eraseFromParent(); |
| 513 | } |
| 514 | |
| 515 | return Changed; |
| 516 | } |
| 517 | |
| 518 | Function * |
| 519 | ExpandVariadics::replaceAllUsesWithNewDeclaration(Module &M, |
| 520 | Function *OriginalFunction) { |
| 521 | auto &Ctx = M.getContext(); |
| 522 | Function &F = *OriginalFunction; |
| 523 | FunctionType *FTy = F.getFunctionType(); |
| 524 | Function *NF = Function::Create(Ty: FTy, Linkage: F.getLinkage(), AddrSpace: F.getAddressSpace()); |
| 525 | |
| 526 | NF->setName(F.getName() + ".varargs" ); |
| 527 | |
| 528 | F.getParent()->getFunctionList().insert(where: F.getIterator(), New: NF); |
| 529 | |
| 530 | AttrBuilder ParamAttrs(Ctx); |
| 531 | AttributeList Attrs = NF->getAttributes(); |
| 532 | Attrs = Attrs.addParamAttributes(C&: Ctx, ArgNo: FTy->getNumParams(), B: ParamAttrs); |
| 533 | NF->setAttributes(Attrs); |
| 534 | |
| 535 | OriginalFunction->replaceAllUsesWith(V: NF); |
| 536 | return NF; |
| 537 | } |
| 538 | |
| 539 | Function * |
| 540 | ExpandVariadics::deriveFixedArityReplacement(Module &M, IRBuilder<> &Builder, |
| 541 | Function *OriginalFunction) { |
| 542 | Function &F = *OriginalFunction; |
| 543 | // The purpose here is split the variadic function F into two functions |
| 544 | // One is a variadic function that bundles the passed argument into a va_list |
| 545 | // and passes it to the second function. The second function does whatever |
| 546 | // the original F does, except that it takes a va_list instead of the ... |
| 547 | |
| 548 | assert(expansionApplicableToFunction(M, &F)); |
| 549 | |
| 550 | auto &Ctx = M.getContext(); |
| 551 | |
| 552 | // Returned value isDeclaration() is equal to F.isDeclaration() |
| 553 | // but that property is not invariant throughout this function |
| 554 | const bool FunctionIsDefinition = !F.isDeclaration(); |
| 555 | |
| 556 | FunctionType *FTy = F.getFunctionType(); |
| 557 | SmallVector<Type *> ArgTypes(FTy->params()); |
| 558 | ArgTypes.push_back(Elt: ABI->vaListParameterType(M)); |
| 559 | |
| 560 | FunctionType *NFTy = inlinableVariadicFunctionType(M, FTy); |
| 561 | Function *NF = Function::Create(Ty: NFTy, Linkage: F.getLinkage(), AddrSpace: F.getAddressSpace()); |
| 562 | |
| 563 | // Note - same attribute handling as DeadArgumentElimination |
| 564 | NF->copyAttributesFrom(Src: &F); |
| 565 | NF->setComdat(F.getComdat()); |
| 566 | F.getParent()->getFunctionList().insert(where: F.getIterator(), New: NF); |
| 567 | NF->setName(F.getName() + ".valist" ); |
| 568 | |
| 569 | AttrBuilder ParamAttrs(Ctx); |
| 570 | |
| 571 | AttributeList Attrs = NF->getAttributes(); |
| 572 | Attrs = Attrs.addParamAttributes(C&: Ctx, ArgNo: NFTy->getNumParams() - 1, B: ParamAttrs); |
| 573 | NF->setAttributes(Attrs); |
| 574 | |
| 575 | // Splice the implementation into the new function with minimal changes |
| 576 | if (FunctionIsDefinition) { |
| 577 | NF->splice(ToIt: NF->begin(), FromF: &F); |
| 578 | |
| 579 | auto NewArg = NF->arg_begin(); |
| 580 | for (Argument &Arg : F.args()) { |
| 581 | Arg.replaceAllUsesWith(V: NewArg); |
| 582 | NewArg->setName(Arg.getName()); // takeName without killing the old one |
| 583 | ++NewArg; |
| 584 | } |
| 585 | NewArg->setName("varargs" ); |
| 586 | } |
| 587 | |
| 588 | SmallVector<std::pair<unsigned, MDNode *>, 1> MDs; |
| 589 | F.getAllMetadata(MDs); |
| 590 | for (auto [KindID, Node] : MDs) |
| 591 | NF->addMetadata(KindID, MD&: *Node); |
| 592 | F.clearMetadata(); |
| 593 | |
| 594 | return NF; |
| 595 | } |
| 596 | |
| 597 | Function * |
| 598 | ExpandVariadics::defineVariadicWrapper(Module &M, IRBuilder<> &Builder, |
| 599 | Function *VariadicWrapper, |
| 600 | Function *FixedArityReplacement) { |
| 601 | auto &Ctx = Builder.getContext(); |
| 602 | const DataLayout &DL = M.getDataLayout(); |
| 603 | assert(VariadicWrapper->isDeclaration()); |
| 604 | Function &F = *VariadicWrapper; |
| 605 | |
| 606 | assert(F.isDeclaration()); |
| 607 | Type *VaListTy = ABI->vaListType(Ctx); |
| 608 | |
| 609 | auto *BB = BasicBlock::Create(Context&: Ctx, Name: "entry" , Parent: &F); |
| 610 | Builder.SetInsertPoint(BB); |
| 611 | |
| 612 | AllocaInst *VaListInstance = |
| 613 | Builder.CreateAlloca(Ty: VaListTy, ArraySize: nullptr, Name: "va_start" ); |
| 614 | |
| 615 | Builder.CreateLifetimeStart(Ptr: VaListInstance); |
| 616 | |
| 617 | Builder.CreateIntrinsic(ID: Intrinsic::vastart, OverloadTypes: {DL.getAllocaPtrType(Ctx)}, |
| 618 | Args: {VaListInstance}); |
| 619 | |
| 620 | SmallVector<Value *> Args(llvm::make_pointer_range(Range: F.args())); |
| 621 | |
| 622 | Value *VaListValue = VaListInstance; |
| 623 | if (ABI->vaListPassedInSSARegister()) |
| 624 | VaListValue = Builder.CreateLoad(Ty: VaListTy, Ptr: VaListInstance); |
| 625 | |
| 626 | Type *ParameterType = ABI->vaListParameterType(M); |
| 627 | Args.push_back(Elt: Builder.CreateAddrSpaceCast(V: VaListValue, DestTy: ParameterType)); |
| 628 | |
| 629 | CallInst *Result = Builder.CreateCall(Callee: FixedArityReplacement, Args); |
| 630 | |
| 631 | Builder.CreateIntrinsic(ID: Intrinsic::vaend, OverloadTypes: {DL.getAllocaPtrType(Ctx)}, |
| 632 | Args: {VaListInstance}); |
| 633 | Builder.CreateLifetimeEnd(Ptr: VaListInstance); |
| 634 | |
| 635 | if (Result->getType()->isVoidTy()) |
| 636 | Builder.CreateRetVoid(); |
| 637 | else |
| 638 | Builder.CreateRet(V: Result); |
| 639 | |
| 640 | return VariadicWrapper; |
| 641 | } |
| 642 | |
| 643 | bool ExpandVariadics::expandCall(Module &M, IRBuilder<> &Builder, CallBase *CB, |
| 644 | FunctionType *VarargFunctionType, |
| 645 | Function *NF) { |
| 646 | bool Changed = false; |
| 647 | const DataLayout &DL = M.getDataLayout(); |
| 648 | |
| 649 | if (ABI->ignoreFunction(F: CB->getCalledFunction())) |
| 650 | return Changed; |
| 651 | |
| 652 | if (!expansionApplicableToFunctionCall(CB)) { |
| 653 | if (rewriteABI()) |
| 654 | report_fatal_error(reason: "Cannot lower callbase instruction" ); |
| 655 | return Changed; |
| 656 | } |
| 657 | |
| 658 | // This is tricky. The call instruction's function type might not match |
| 659 | // the type of the caller. When optimising, can leave it unchanged. |
| 660 | // Webassembly detects that inconsistency and repairs it. |
| 661 | if (CB->getFunctionType() != VarargFunctionType) |
| 662 | if (!rewriteABI()) |
| 663 | return Changed; |
| 664 | |
| 665 | auto &Ctx = CB->getContext(); |
| 666 | |
| 667 | Align MaxFieldAlign(1); |
| 668 | |
| 669 | // The strategy is to allocate a call frame containing the variadic |
| 670 | // arguments laid out such that a target specific va_list can be initialized |
| 671 | // with it, such that target specific va_arg instructions will correctly |
| 672 | // iterate over it. This means getting the alignment right and sometimes |
| 673 | // embedding a pointer to the value instead of embedding the value itself. |
| 674 | |
| 675 | Function *CBF = CB->getParent()->getParent(); |
| 676 | |
| 677 | ExpandedCallFrame Frame; |
| 678 | |
| 679 | uint64_t CurrentOffset = 0; |
| 680 | |
| 681 | for (unsigned I : seq(Begin: VarargFunctionType->getNumParams(), End: CB->arg_size())) { |
| 682 | Value *ArgVal = CB->getArgOperand(i: I); |
| 683 | const bool IsByVal = CB->paramHasAttr(ArgNo: I, Kind: Attribute::ByVal); |
| 684 | const bool IsByRef = CB->paramHasAttr(ArgNo: I, Kind: Attribute::ByRef); |
| 685 | |
| 686 | // The type of the value being passed, decoded from byval/byref metadata if |
| 687 | // required |
| 688 | Type *const UnderlyingType = IsByVal ? CB->getParamByValType(ArgNo: I) |
| 689 | : IsByRef ? CB->getParamByRefType(ArgNo: I) |
| 690 | : ArgVal->getType(); |
| 691 | const uint64_t UnderlyingSize = |
| 692 | DL.getTypeAllocSize(Ty: UnderlyingType).getFixedValue(); |
| 693 | |
| 694 | // The type to be written into the call frame |
| 695 | Type *FrameFieldType = UnderlyingType; |
| 696 | |
| 697 | // The value to copy from when initialising the frame alloca |
| 698 | Value *SourceValue = ArgVal; |
| 699 | |
| 700 | VariadicABIInfo::VAArgSlotInfo SlotInfo = ABI->slotInfo(DL, Parameter: UnderlyingType); |
| 701 | |
| 702 | if (SlotInfo.Indirect) { |
| 703 | // The va_arg lowering loads through a pointer. Set up an alloca to aim |
| 704 | // that pointer at. |
| 705 | Builder.SetInsertPointPastAllocas(CBF); |
| 706 | Builder.SetCurrentDebugLocation(CB->getStableDebugLoc()); |
| 707 | Value *CallerCopy = |
| 708 | Builder.CreateAlloca(Ty: UnderlyingType, ArraySize: nullptr, Name: "IndirectAlloca" ); |
| 709 | |
| 710 | Builder.SetInsertPoint(CB); |
| 711 | if (IsByVal) |
| 712 | Builder.CreateMemCpy(Dst: CallerCopy, DstAlign: {}, Src: ArgVal, SrcAlign: {}, Size: UnderlyingSize); |
| 713 | else |
| 714 | Builder.CreateStore(Val: ArgVal, Ptr: CallerCopy); |
| 715 | |
| 716 | // Indirection now handled, pass the alloca ptr by value |
| 717 | FrameFieldType = DL.getAllocaPtrType(Ctx); |
| 718 | SourceValue = CallerCopy; |
| 719 | } |
| 720 | |
| 721 | // Alignment of the value within the frame |
| 722 | // This probably needs to be controllable as a function of type |
| 723 | Align DataAlign = SlotInfo.DataAlign; |
| 724 | |
| 725 | MaxFieldAlign = std::max(a: MaxFieldAlign, b: DataAlign); |
| 726 | |
| 727 | uint64_t DataAlignV = DataAlign.value(); |
| 728 | if (uint64_t Rem = CurrentOffset % DataAlignV) { |
| 729 | // Inject explicit padding to deal with alignment requirements |
| 730 | uint64_t Padding = DataAlignV - Rem; |
| 731 | Frame.padding(Ctx, By: Padding); |
| 732 | CurrentOffset += Padding; |
| 733 | } |
| 734 | |
| 735 | if (SlotInfo.Indirect) { |
| 736 | Frame.store(Ctx, T: FrameFieldType, V: SourceValue); |
| 737 | } else { |
| 738 | if (IsByVal) |
| 739 | Frame.memcpy(Ctx, T: FrameFieldType, V: SourceValue, Bytes: UnderlyingSize); |
| 740 | else |
| 741 | Frame.store(Ctx, T: FrameFieldType, V: SourceValue); |
| 742 | } |
| 743 | |
| 744 | CurrentOffset += DL.getTypeAllocSize(Ty: FrameFieldType).getFixedValue(); |
| 745 | } |
| 746 | |
| 747 | if (Frame.empty()) { |
| 748 | // Not passing any arguments, hopefully va_arg won't try to read any |
| 749 | // Creating a single byte frame containing nothing to point the va_list |
| 750 | // instance as that is less special-casey in the compiler and probably |
| 751 | // easier to interpret in a debugger. |
| 752 | Frame.padding(Ctx, By: 1); |
| 753 | } |
| 754 | |
| 755 | StructType *VarargsTy = Frame.asStruct(Ctx, Name: CBF->getName()); |
| 756 | |
| 757 | // The struct instance needs to be at least MaxFieldAlign for the alignment of |
| 758 | // the fields to be correct at runtime. Use the native stack alignment instead |
| 759 | // if that's greater as that tends to give better codegen. |
| 760 | // This is an awkward way to guess whether there is a known stack alignment |
| 761 | // without hitting an assert in DL.getStackAlignment, 1024 is an arbitrary |
| 762 | // number likely to be greater than the natural stack alignment. |
| 763 | Align AllocaAlign = MaxFieldAlign; |
| 764 | if (MaybeAlign StackAlign = DL.getStackAlignment(); |
| 765 | StackAlign && *StackAlign > AllocaAlign) |
| 766 | AllocaAlign = *StackAlign; |
| 767 | |
| 768 | // Put the alloca to hold the variadic args in the entry basic block. |
| 769 | Builder.SetInsertPointPastAllocas(CBF); |
| 770 | |
| 771 | // SetCurrentDebugLocation when the builder SetInsertPoint method does not |
| 772 | Builder.SetCurrentDebugLocation(CB->getStableDebugLoc()); |
| 773 | |
| 774 | // The awkward construction here is to set the alignment on the instance |
| 775 | AllocaInst *Alloced = Builder.Insert( |
| 776 | I: new AllocaInst(VarargsTy, DL.getAllocaAddrSpace(), nullptr, AllocaAlign), |
| 777 | Name: "vararg_buffer" ); |
| 778 | Changed = true; |
| 779 | assert(Alloced->getAllocatedType() == VarargsTy); |
| 780 | |
| 781 | // Initialize the fields in the struct |
| 782 | Builder.SetInsertPoint(CB); |
| 783 | Builder.CreateLifetimeStart(Ptr: Alloced); |
| 784 | Frame.initializeStructAlloca(DL, Builder, Alloced, VarargsTy); |
| 785 | |
| 786 | const unsigned NumArgs = VarargFunctionType->getNumParams(); |
| 787 | SmallVector<Value *> Args(CB->arg_begin(), CB->arg_begin() + NumArgs); |
| 788 | |
| 789 | // Initialize a va_list pointing to that struct and pass it as the last |
| 790 | // argument |
| 791 | AllocaInst *VaList = nullptr; |
| 792 | { |
| 793 | if (!ABI->vaListPassedInSSARegister()) { |
| 794 | Type *VaListTy = ABI->vaListType(Ctx); |
| 795 | Builder.SetInsertPointPastAllocas(CBF); |
| 796 | Builder.SetCurrentDebugLocation(CB->getStableDebugLoc()); |
| 797 | VaList = Builder.CreateAlloca(Ty: VaListTy, ArraySize: nullptr, Name: "va_argument" ); |
| 798 | Builder.SetInsertPoint(CB); |
| 799 | Builder.CreateLifetimeStart(Ptr: VaList); |
| 800 | } |
| 801 | Builder.SetInsertPoint(CB); |
| 802 | Args.push_back(Elt: ABI->initializeVaList(M, Ctx, Builder, VaList, Buffer: Alloced)); |
| 803 | } |
| 804 | |
| 805 | // Attributes excluding any on the vararg arguments |
| 806 | AttributeList PAL = CB->getAttributes(); |
| 807 | if (!PAL.isEmpty()) { |
| 808 | SmallVector<AttributeSet, 8> ArgAttrs; |
| 809 | for (unsigned ArgNo = 0; ArgNo < NumArgs; ArgNo++) |
| 810 | ArgAttrs.push_back(Elt: PAL.getParamAttrs(ArgNo)); |
| 811 | PAL = |
| 812 | AttributeList::get(C&: Ctx, FnAttrs: PAL.getFnAttrs(), RetAttrs: PAL.getRetAttrs(), ArgAttrs); |
| 813 | } |
| 814 | |
| 815 | SmallVector<OperandBundleDef, 1> OpBundles; |
| 816 | CB->getOperandBundlesAsDefs(Defs&: OpBundles); |
| 817 | |
| 818 | CallBase *NewCB = nullptr; |
| 819 | |
| 820 | if (CallInst *CI = dyn_cast<CallInst>(Val: CB)) { |
| 821 | Value *Dst = NF ? NF : CI->getCalledOperand(); |
| 822 | // Use the type of the call site rather than the function type to ensure |
| 823 | // RAUW succeeds in the case of a mismatching return type. |
| 824 | FunctionType *NFTy = |
| 825 | inlinableVariadicFunctionType(M, FTy: VarargFunctionType, ReturnType: CB->getType()); |
| 826 | |
| 827 | NewCB = CallInst::Create(Ty: NFTy, Func: Dst, Args, Bundles: OpBundles, NameStr: "" , InsertBefore: CI->getIterator()); |
| 828 | |
| 829 | CallInst::TailCallKind TCK = CI->getTailCallKind(); |
| 830 | assert(TCK != CallInst::TCK_MustTail); |
| 831 | |
| 832 | // Can't tail call a function that is being passed a pointer to an alloca |
| 833 | if (TCK == CallInst::TCK_Tail) |
| 834 | TCK = CallInst::TCK_None; |
| 835 | CI->setTailCallKind(TCK); |
| 836 | |
| 837 | } else { |
| 838 | llvm_unreachable("Unreachable when !expansionApplicableToFunctionCall()" ); |
| 839 | } |
| 840 | |
| 841 | if (VaList) |
| 842 | Builder.CreateLifetimeEnd(Ptr: VaList); |
| 843 | |
| 844 | Builder.CreateLifetimeEnd(Ptr: Alloced); |
| 845 | |
| 846 | NewCB->setAttributes(PAL); |
| 847 | NewCB->takeName(V: CB); |
| 848 | NewCB->setCallingConv(CB->getCallingConv()); |
| 849 | NewCB->setDebugLoc(DebugLoc()); |
| 850 | |
| 851 | // DeadArgElim and ArgPromotion copy exactly this metadata |
| 852 | NewCB->copyMetadata(SrcInst: *CB, WL: {LLVMContext::MD_prof, LLVMContext::MD_dbg}); |
| 853 | |
| 854 | CB->replaceAllUsesWith(V: NewCB); |
| 855 | CB->eraseFromParent(); |
| 856 | return Changed; |
| 857 | } |
| 858 | |
| 859 | bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder, |
| 860 | const DataLayout &DL, |
| 861 | VAStartInst *Inst) { |
| 862 | // Only removing va_start instructions that are not in variadic functions. |
| 863 | // Those would be rejected by the IR verifier before this pass. |
| 864 | // After splicing basic blocks from a variadic function into a fixed arity |
| 865 | // one the va_start that used to refer to the ... parameter still exist. |
| 866 | // There are also variadic functions that this pass did not change and |
| 867 | // va_start instances in the created single block wrapper functions. |
| 868 | // Replace exactly the instances in non-variadic functions as those are |
| 869 | // the ones to be fixed up to use the va_list passed as the final argument. |
| 870 | |
| 871 | Function *ContainingFunction = Inst->getFunction(); |
| 872 | if (ContainingFunction->isVarArg()) { |
| 873 | return false; |
| 874 | } |
| 875 | |
| 876 | // The last argument is a vaListParameterType, either a va_list |
| 877 | // or a pointer to one depending on the target. |
| 878 | bool PassedByValue = ABI->vaListPassedInSSARegister(); |
| 879 | Argument *PassedVaList = |
| 880 | ContainingFunction->getArg(i: ContainingFunction->arg_size() - 1); |
| 881 | |
| 882 | // va_start takes a pointer to a va_list, e.g. one on the stack |
| 883 | Value *VaStartArg = Inst->getArgList(); |
| 884 | |
| 885 | Builder.SetInsertPoint(Inst); |
| 886 | |
| 887 | if (PassedByValue) { |
| 888 | // The general thing to do is create an alloca, store the va_list argument |
| 889 | // to it, then create a va_copy. When vaCopyIsMemcpy(), this optimises to a |
| 890 | // store to the VaStartArg. |
| 891 | assert(ABI->vaCopyIsMemcpy()); |
| 892 | // The va_list parameter may be passed in a different address space than |
| 893 | // the va_list object iterates in (e.g. NVPTX passes a local pointer but |
| 894 | // stores a generic cursor). Cast it to the type va_arg expects to load. |
| 895 | Value *Cursor = PassedVaList; |
| 896 | if (Cursor->getType() != VaStartArg->getType()) |
| 897 | Cursor = Builder.CreateAddrSpaceCast(V: Cursor, DestTy: VaStartArg->getType()); |
| 898 | Builder.CreateStore(Val: Cursor, Ptr: VaStartArg); |
| 899 | } else { |
| 900 | |
| 901 | // Otherwise emit a vacopy to pick up target-specific handling if any |
| 902 | auto &Ctx = Builder.getContext(); |
| 903 | |
| 904 | Builder.CreateIntrinsic(ID: Intrinsic::vacopy, OverloadTypes: {DL.getAllocaPtrType(Ctx)}, |
| 905 | Args: {VaStartArg, PassedVaList}); |
| 906 | } |
| 907 | |
| 908 | Inst->eraseFromParent(); |
| 909 | return true; |
| 910 | } |
| 911 | |
| 912 | bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &, const DataLayout &, |
| 913 | VAEndInst *Inst) { |
| 914 | assert(ABI->vaEndIsNop()); |
| 915 | Inst->eraseFromParent(); |
| 916 | return true; |
| 917 | } |
| 918 | |
| 919 | bool ExpandVariadics::expandVAIntrinsicCall(IRBuilder<> &Builder, |
| 920 | const DataLayout &DL, |
| 921 | VACopyInst *Inst) { |
| 922 | assert(ABI->vaCopyIsMemcpy()); |
| 923 | Builder.SetInsertPoint(Inst); |
| 924 | |
| 925 | auto &Ctx = Builder.getContext(); |
| 926 | Type *VaListTy = ABI->vaListType(Ctx); |
| 927 | uint64_t Size = DL.getTypeAllocSize(Ty: VaListTy).getFixedValue(); |
| 928 | |
| 929 | Builder.CreateMemCpy(Dst: Inst->getDest(), DstAlign: {}, Src: Inst->getSrc(), SrcAlign: {}, |
| 930 | Size: Builder.getInt32(C: Size)); |
| 931 | |
| 932 | Inst->eraseFromParent(); |
| 933 | return true; |
| 934 | } |
| 935 | |
| 936 | bool ExpandVariadics::expandVAArgInst(IRBuilder<> &Builder, |
| 937 | const DataLayout &DL, VAArgInst *Inst) { |
| 938 | Builder.SetInsertPoint(Inst); |
| 939 | |
| 940 | auto &Ctx = Builder.getContext(); |
| 941 | Type *ValTy = Inst->getType(); |
| 942 | Value *VaListPtr = Inst->getPointerOperand(); |
| 943 | |
| 944 | const VariadicABIInfo::VAArgSlotInfo SlotInfo = ABI->slotInfo(DL, Parameter: ValTy); |
| 945 | Type *FrameFieldType = SlotInfo.Indirect ? DL.getAllocaPtrType(Ctx) : ValTy; |
| 946 | const uint64_t SlotSize = DL.getTypeAllocSize(Ty: FrameFieldType).getFixedValue(); |
| 947 | const Align SlotAlign = SlotInfo.DataAlign; |
| 948 | |
| 949 | Type *PtrTy = VaListPtr->getType(); |
| 950 | Type *IdxTy = DL.getIndexType(PtrTy); |
| 951 | |
| 952 | Value *Cur = Builder.CreateLoad(Ty: PtrTy, Ptr: VaListPtr); |
| 953 | |
| 954 | // Round the cursor up to the slot alignment used by the caller. |
| 955 | Value *Aligned = Cur; |
| 956 | if (SlotAlign > Align(1)) { |
| 957 | Value *RoundUp = Builder.CreateInBoundsPtrAdd( |
| 958 | Ptr: Cur, Offset: ConstantInt::get(Ty: IdxTy, V: SlotAlign.value() - 1)); |
| 959 | Aligned = Builder.CreateIntrinsic( |
| 960 | ID: Intrinsic::ptrmask, OverloadTypes: {PtrTy, IdxTy}, |
| 961 | Args: {RoundUp, ConstantInt::getSigned(Ty: IdxTy, V: -(int64_t)SlotAlign.value())}); |
| 962 | } |
| 963 | |
| 964 | // Advance past the slot and write the iterator back. |
| 965 | Value *Next = |
| 966 | Builder.CreateInBoundsPtrAdd(Ptr: Aligned, Offset: ConstantInt::get(Ty: IdxTy, V: SlotSize)); |
| 967 | Builder.CreateStore(Val: Next, Ptr: VaListPtr); |
| 968 | |
| 969 | // Load the slot contents: the value itself for direct arguments, or a |
| 970 | // pointer to the value for indirect ones. |
| 971 | Value *Result = Builder.CreateAlignedLoad(Ty: FrameFieldType, Ptr: Aligned, Align: SlotAlign); |
| 972 | |
| 973 | if (SlotInfo.Indirect) |
| 974 | Result = Builder.CreateLoad(Ty: ValTy, Ptr: Result); |
| 975 | |
| 976 | Result->takeName(V: Inst); |
| 977 | Inst->replaceAllUsesWith(V: Result); |
| 978 | Inst->eraseFromParent(); |
| 979 | return true; |
| 980 | } |
| 981 | |
| 982 | struct Amdgpu final : public VariadicABIInfo { |
| 983 | |
| 984 | bool enableForTarget() override { return true; } |
| 985 | |
| 986 | bool vaListPassedInSSARegister() override { return true; } |
| 987 | |
| 988 | Type *vaListType(LLVMContext &Ctx) override { |
| 989 | return PointerType::getUnqual(C&: Ctx); |
| 990 | } |
| 991 | |
| 992 | Type *vaListParameterType(Module &M) override { |
| 993 | return PointerType::getUnqual(C&: M.getContext()); |
| 994 | } |
| 995 | |
| 996 | Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder, |
| 997 | AllocaInst * /*va_list*/, Value *Buffer) override { |
| 998 | // Given Buffer, which is an AllocInst of vararg_buffer |
| 999 | // need to return something usable as parameter type |
| 1000 | return Builder.CreateAddrSpaceCast(V: Buffer, DestTy: vaListParameterType(M)); |
| 1001 | } |
| 1002 | |
| 1003 | VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override { |
| 1004 | return {.DataAlign: Align(4), .Indirect: false}; |
| 1005 | } |
| 1006 | }; |
| 1007 | |
| 1008 | struct NVPTX final : public VariadicABIInfo { |
| 1009 | |
| 1010 | bool enableForTarget() override { return true; } |
| 1011 | |
| 1012 | bool vaListPassedInSSARegister() override { return true; } |
| 1013 | |
| 1014 | Type *vaListType(LLVMContext &Ctx) override { |
| 1015 | return PointerType::getUnqual(C&: Ctx); |
| 1016 | } |
| 1017 | |
| 1018 | Type *vaListParameterType(Module &M) override { |
| 1019 | return PointerType::get(C&: M.getContext(), AddressSpace: NVPTXAS::ADDRESS_SPACE_LOCAL); |
| 1020 | } |
| 1021 | |
| 1022 | Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder, |
| 1023 | AllocaInst *, Value *Buffer) override { |
| 1024 | return Builder.CreateAddrSpaceCast(V: Buffer, DestTy: vaListParameterType(M)); |
| 1025 | } |
| 1026 | |
| 1027 | VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override { |
| 1028 | // NVPTX expects natural alignment in all cases. The variadic call ABI will |
| 1029 | // handle promoting types to their appropriate size and alignment. |
| 1030 | Align A = DL.getABITypeAlign(Ty: Parameter); |
| 1031 | return {.DataAlign: A, .Indirect: false}; |
| 1032 | } |
| 1033 | }; |
| 1034 | |
| 1035 | struct SPIRV final : public VariadicABIInfo { |
| 1036 | |
| 1037 | bool enableForTarget() override { return true; } |
| 1038 | |
| 1039 | bool vaListPassedInSSARegister() override { return true; } |
| 1040 | |
| 1041 | Type *vaListType(LLVMContext &Ctx) override { |
| 1042 | return PointerType::getUnqual(C&: Ctx); |
| 1043 | } |
| 1044 | |
| 1045 | Type *vaListParameterType(Module &M) override { |
| 1046 | return PointerType::getUnqual(C&: M.getContext()); |
| 1047 | } |
| 1048 | |
| 1049 | Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder, |
| 1050 | AllocaInst *, Value *Buffer) override { |
| 1051 | return Builder.CreateAddrSpaceCast(V: Buffer, DestTy: vaListParameterType(M)); |
| 1052 | } |
| 1053 | |
| 1054 | VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override { |
| 1055 | // Expects natural alignment in all cases. The variadic call ABI will handle |
| 1056 | // promoting types to their appropriate size and alignment. |
| 1057 | Align A = DL.getABITypeAlign(Ty: Parameter); |
| 1058 | return {.DataAlign: A, .Indirect: false}; |
| 1059 | } |
| 1060 | |
| 1061 | // The SPIR-V backend has special handling for builtins. |
| 1062 | bool ignoreFunction(const Function *F) override { |
| 1063 | if (!F->isDeclaration()) |
| 1064 | return false; |
| 1065 | |
| 1066 | std::string Demangled = llvm::demangle(MangledName: F->getName()); |
| 1067 | StringRef DemangledName(Demangled); |
| 1068 | |
| 1069 | // Skip any SPIR-V builtins. |
| 1070 | if (DemangledName.starts_with(Prefix: "__spirv_" ) || |
| 1071 | DemangledName.starts_with(Prefix: "printf(" )) |
| 1072 | return true; |
| 1073 | |
| 1074 | return false; |
| 1075 | } |
| 1076 | |
| 1077 | // We will likely see va intrinsics in the generic addrspace (4). |
| 1078 | SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override { |
| 1079 | return {4}; |
| 1080 | } |
| 1081 | }; |
| 1082 | |
| 1083 | struct Wasm final : public VariadicABIInfo { |
| 1084 | |
| 1085 | bool enableForTarget() override { |
| 1086 | // Currently wasm is only used for testing. |
| 1087 | return commandLineOverride(); |
| 1088 | } |
| 1089 | |
| 1090 | bool vaListPassedInSSARegister() override { return true; } |
| 1091 | |
| 1092 | Type *vaListType(LLVMContext &Ctx) override { |
| 1093 | return PointerType::getUnqual(C&: Ctx); |
| 1094 | } |
| 1095 | |
| 1096 | Type *vaListParameterType(Module &M) override { |
| 1097 | return PointerType::getUnqual(C&: M.getContext()); |
| 1098 | } |
| 1099 | |
| 1100 | Value *initializeVaList(Module &M, LLVMContext &Ctx, IRBuilder<> &Builder, |
| 1101 | AllocaInst * /*va_list*/, Value *Buffer) override { |
| 1102 | return Buffer; |
| 1103 | } |
| 1104 | |
| 1105 | VAArgSlotInfo slotInfo(const DataLayout &DL, Type *Parameter) override { |
| 1106 | LLVMContext &Ctx = Parameter->getContext(); |
| 1107 | const unsigned MinAlign = 4; |
| 1108 | Align A = DL.getABITypeAlign(Ty: Parameter); |
| 1109 | if (A < MinAlign) |
| 1110 | A = Align(MinAlign); |
| 1111 | |
| 1112 | if (auto *S = dyn_cast<StructType>(Val: Parameter)) { |
| 1113 | if (S->getNumElements() > 1) { |
| 1114 | return {.DataAlign: DL.getABITypeAlign(Ty: PointerType::getUnqual(C&: Ctx)), .Indirect: true}; |
| 1115 | } |
| 1116 | } |
| 1117 | |
| 1118 | return {.DataAlign: A, .Indirect: false}; |
| 1119 | } |
| 1120 | }; |
| 1121 | |
| 1122 | std::unique_ptr<VariadicABIInfo> VariadicABIInfo::create(const Triple &T) { |
| 1123 | switch (T.getArch()) { |
| 1124 | case Triple::r600: |
| 1125 | case Triple::amdgcn: { |
| 1126 | return std::make_unique<Amdgpu>(); |
| 1127 | } |
| 1128 | |
| 1129 | case Triple::wasm32: { |
| 1130 | return std::make_unique<Wasm>(); |
| 1131 | } |
| 1132 | |
| 1133 | case Triple::nvptx: |
| 1134 | case Triple::nvptx64: { |
| 1135 | return std::make_unique<NVPTX>(); |
| 1136 | } |
| 1137 | |
| 1138 | case Triple::spirv: |
| 1139 | case Triple::spirv32: |
| 1140 | case Triple::spirv64: { |
| 1141 | return std::make_unique<SPIRV>(); |
| 1142 | } |
| 1143 | |
| 1144 | default: |
| 1145 | return {}; |
| 1146 | } |
| 1147 | } |
| 1148 | |
| 1149 | } // namespace |
| 1150 | |
| 1151 | char ExpandVariadics::ID = 0; |
| 1152 | |
| 1153 | INITIALIZE_PASS(ExpandVariadics, DEBUG_TYPE, "Expand variadic functions" , false, |
| 1154 | false) |
| 1155 | |
| 1156 | ModulePass *llvm::createExpandVariadicsPass(ExpandVariadicsMode M) { |
| 1157 | return new ExpandVariadics(M); |
| 1158 | } |
| 1159 | |
| 1160 | PreservedAnalyses ExpandVariadicsPass::run(Module &M, ModuleAnalysisManager &) { |
| 1161 | return ExpandVariadics(Mode).runOnModule(M) ? PreservedAnalyses::none() |
| 1162 | : PreservedAnalyses::all(); |
| 1163 | } |
| 1164 | |
| 1165 | ExpandVariadicsPass::ExpandVariadicsPass(ExpandVariadicsMode M) : Mode(M) {} |
| 1166 | |