Index: docs/BitCodeFormat.rst =================================================================== --- docs/BitCodeFormat.rst +++ docs/BitCodeFormat.rst @@ -1039,6 +1039,7 @@ * code 50: ``inaccessiblememonly_or_argmemonly`` * code 51: ``allocsize([, ])`` * code 52: ``writeonly`` +* code 55: ``pagerando`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1552,6 +1552,10 @@ arguments. Note that ``argmemonly`` can be used together with ``readonly`` attribute in order to specify that function reads only from its arguments. +``pagerando`` + This attribute indicates that the function is compatible with pagerando and + will be located in a section which the dynamic loader can place at an + independent offset from any other section in the binary. ``returns_twice`` This attribute indicates that this function can return twice. The C ``setjmp`` is an example of such a function. The compiler disables Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -560,6 +560,7 @@ ATTR_KIND_WRITEONLY = 52, ATTR_KIND_SPECULATABLE = 53, ATTR_KIND_STRICT_FP = 54, + ATTR_KIND_PAGERANDO = 55, }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -179,6 +179,9 @@ /// Zero extended before/after call. def ZExt : EnumAttr<"zeroext">; +/// Function is placed into a Pagerando bin. +def Pagerando : EnumAttr<"pagerando">; + /// Target-independent string attributes. def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">; def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">; Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -513,6 +513,12 @@ AttributeSets.hasParamAttribute(1, Attribute::StructRet); } + /// @brief True if the function is compatible with Pagerando and should be + /// placed into a randomized bin. + bool isPagerando() const { + return hasFnAttribute(Attribute::Pagerando); + } + /// @brief Determine if the parameter or return value is marked with NoAlias /// attribute. bool returnDoesNotAlias() const { Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -272,6 +272,7 @@ void initializeOptimizationRemarkEmitterWrapperPassPass(PassRegistry&); void initializeOptimizePHIsPass(PassRegistry&); void initializePAEvalPass(PassRegistry&); +void initializePagerandoWrappersPass(PassRegistry&); void initializePEIPass(PassRegistry&); void initializePGOIndirectCallPromotionLegacyPassPass(PassRegistry&); void initializePGOInstrumentationGenLegacyPassPass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -138,6 +138,7 @@ (void) llvm::createObjCARCContractPass(); (void) llvm::createObjCARCOptPass(); (void) llvm::createPAEvalPass(); + (void) llvm::createPagerandoWrappersPass(); (void) llvm::createPromoteMemoryToRegisterPass(); (void) llvm::createDemoteRegisterToMemoryPass(); (void) llvm::createPruneEHPass(); Index: include/llvm/Transforms/IPO.h =================================================================== --- include/llvm/Transforms/IPO.h +++ include/llvm/Transforms/IPO.h @@ -267,6 +267,10 @@ ModulePass *createWriteThinLTOBitcodePass(raw_ostream &Str, raw_ostream *ThinLinkOS = nullptr); +/// \brief This pass creates Pagerando entry wrappers that properly set up the +/// POT base register. +ModulePass *createPagerandoWrappersPass(); + } // End llvm namespace #endif Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -666,6 +666,7 @@ KEYWORD(uwtable); KEYWORD(writeonly); KEYWORD(zeroext); + KEYWORD(pagerando); KEYWORD(type); KEYWORD(opaque); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1133,6 +1133,8 @@ case lltok::kw_strictfp: B.addAttribute(Attribute::StrictFP); break; case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break; + case lltok::kw_pagerando: + B.addAttribute(Attribute::Pagerando); break; // Error handling. case lltok::kw_inreg: Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -215,6 +215,7 @@ kw_uwtable, kw_writeonly, kw_zeroext, + kw_pagerando, kw_type, kw_opaque, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1144,6 +1144,7 @@ case Attribute::WriteOnly: return 1ULL << 53; case Attribute::Speculatable: return 1ULL << 54; case Attribute::StrictFP: return 1ULL << 55; + case Attribute::Pagerando: return 1ULL << 56; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1370,6 +1371,8 @@ return Attribute::WriteOnly; case bitc::ATTR_KIND_Z_EXT: return Attribute::ZExt; + case bitc::ATTR_KIND_PAGERANDO: + return Attribute::Pagerando; } } Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -677,6 +677,8 @@ return bitc::ATTR_KIND_WRITEONLY; case Attribute::ZExt: return bitc::ATTR_KIND_Z_EXT; + case Attribute::Pagerando: + return bitc::ATTR_KIND_PAGERANDO; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -341,6 +341,8 @@ return "zeroext"; if (hasAttribute(Attribute::Cold)) return "cold"; + if (hasAttribute(Attribute::Pagerando)) + return "pagerando"; // FIXME: These should be output like this: // Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1390,6 +1390,7 @@ case Attribute::AllocSize: case Attribute::Speculatable: case Attribute::StrictFP: + case Attribute::Pagerando: return true; default: break; Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -256,6 +256,8 @@ PMB.populateThinLTOPassManager(passes); else PMB.populateLTOPassManager(passes); + if (TM->isPagerando()) + passes.add(createPagerandoWrappersPass()); passes.run(Mod); } Index: lib/Transforms/IPO/CMakeLists.txt =================================================================== --- lib/Transforms/IPO/CMakeLists.txt +++ lib/Transforms/IPO/CMakeLists.txt @@ -24,6 +24,7 @@ MergeFunctions.cpp PartialInlining.cpp PassManagerBuilder.cpp + PagerandoWrappers.cpp PruneEH.cpp SampleProfile.cpp StripDeadPrototypes.cpp Index: lib/Transforms/IPO/IPO.cpp =================================================================== --- lib/Transforms/IPO/IPO.cpp +++ lib/Transforms/IPO/IPO.cpp @@ -43,6 +43,7 @@ initializeSingleLoopExtractorPass(Registry); initializeLowerTypeTestsPass(Registry); initializeMergeFunctionsPass(Registry); + initializePagerandoWrappersPass(Registry); initializePartialInlinerLegacyPassPass(Registry); initializePostOrderFunctionAttrsLegacyPassPass(Registry); initializeReversePostOrderFunctionAttrsLegacyPassPass(Registry); Index: lib/Transforms/IPO/PagerandoWrappers.cpp =================================================================== --- /dev/null +++ lib/Transforms/IPO/PagerandoWrappers.cpp @@ -0,0 +1,333 @@ +//===-- PagerandoWrappers.cpp - Pagerando entry wrappers ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass creates wrappers for pagerando-enabled functions. A function needs +// a wrapper if it has non-local linkage or its address taken, i.e., if it can +// be used from outside the module. (As an optimization we could use pointer +// escape analysis for address-taken functions instead of creating wrappers for +// all of them.) +// Vararg functions require special treatment: their variable arguments on the +// stack need to be preserved even when indirecting through the POT. We replace +// the original function with a new function that takes an explicit 'va_list' +// parameter: foo(int, ...) -> foo$$origva(int, *va_list). The wrapper captures +// its variable arguments and explicitly passes it to the adapted function to +// preserve the variable arguments passed by the caller. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +#define DEBUG_TYPE "pagerando" + +namespace { +class PagerandoWrappers : public ModulePass { +public: + static char ID; + explicit PagerandoWrappers() : ModulePass(ID) { + initializePagerandoWrappersPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + // Requires nothing, preserves nothing + ModulePass::getAnalysisUsage(AU); + } + +private: + static constexpr const char *OrigSuffix = "$$orig"; + static constexpr const char *OrigVASuffix = "$$origva"; + + void processFunction(Function *F); + Function *rewriteVarargs(Function &F, Type *&VAListTy); + Function *createWrapper(Function &F, const SmallVectorImpl &AddressUses); + void createWrapperBody(Function *Wrapper, Function *Callee, Type *VAListTy); +}; +} // end anonymous namespace + +char PagerandoWrappers::ID = 0; +INITIALIZE_PASS(PagerandoWrappers, "pagerando-wrappers", + "Pagerando entry wrappers", false, false) + +ModulePass *llvm::createPagerandoWrappersPass() { + return new PagerandoWrappers(); +} + +// We can safely skip functions consisting of only debug, trap, and unreachable +// instructions. Such functions are created for abstract, non-base +// destructors. We do not need to randomize these functions since they are +// trivial and not useful for an attacker to reuse. +static bool isTrivialFunction(const Function &F) { + for (auto &I : F.getEntryBlock()) { + if (isa(&I)) + continue; + if (auto *Int = dyn_cast(&I)) + if (Int->getIntrinsicID() == Intrinsic::trap) + continue; + if (isa(&I)) + continue; + + // We found an instruction that is not debug, trap, or unreachable. + return false; + } + + // We only found debug, trap, or unreachable instructions. + return true; +} + +// We skip functions that are only declarations, comdat, trivial trap functions, +// and naked functions. Skipping naked functions is important so that CFI jump +// tables are not placed in pagerando sections. CFI jump tables are marked as +// naked in LowerTypeTests::createJumpTable. If this ever changes, this function +// will also need to be updated. +// +// TODO: Support COMDAT +static bool skipFunction(const Function &F) { + return F.isDeclaration() + || F.hasAvailableExternallyLinkage() + || F.hasComdat() + || isTrivialFunction(F) + || F.hasFnAttribute(Attribute::Naked); +} + +bool PagerandoWrappers::runOnModule(Module &M) { + std::vector Worklist; + for (auto &F : M) { + if (!skipFunction(F)) + Worklist.push_back(&F); + } + + for (auto F : Worklist) + processFunction(F); + + return !Worklist.empty(); +} + +static bool skipFunctionUse(const Use &U); +static bool IsDirectCallOfBitcast(User *Usr) { + auto CE = dyn_cast(Usr); + return CE && CE->getOpcode() == Instruction::BitCast + && std::all_of(CE->use_begin(), CE->use_end(), skipFunctionUse); +} + +static bool skipFunctionUse(const Use &U) { + auto User = U.getUser(); + auto UserFn = dyn_cast(User); + ImmutableCallSite CS(User); + + return (CS && CS.isCallee(&U)) // Used as the callee + || isa(User) // No need to indirect + || isa(User) // Handled in AsmPrinter::EmitBasicBlockStart + || (UserFn && UserFn->getPersonalityFn() == U.get()) // Skip pers. fn uses + || IsDirectCallOfBitcast(User); // Calls to bitcasted functions end up as direct calls +} + +void PagerandoWrappers::processFunction(Function *F) { + SmallVector AddressUses; + for (Use &U : F->uses()) { + if (!skipFunctionUse(U)) + AddressUses.push_back(&U); + } + + if (!F->hasLocalLinkage() || !AddressUses.empty()) { + auto Wrapper = createWrapper(*F, AddressUses); + Type *VAListTy = nullptr; + if (F->isVarArg()) { + // Reassign F, it might have been deleted + F = rewriteVarargs(*F, /* out */ VAListTy); + } + createWrapperBody(Wrapper, F, VAListTy); + } + + F->setSection(""); + F->addFnAttr(Attribute::Pagerando); +} + +static void replaceAddressTakenUse(Use *U, Function *F, Function *Wrapper, + SmallSet &Constants) { + // Already replaced this use? + if (!U->get()) return; + + if (auto GV = dyn_cast(U->getUser())) { + assert(GV->getInitializer() == F); + GV->setInitializer(Wrapper); + } else if (auto C = dyn_cast(U->getUser())) { + // Constant::handleOperandChange must not be called more than once per user + if (Constants.insert(C).second) + C->handleOperandChange(F, Wrapper); + } else { + U->set(Wrapper); + } +} + +Function *PagerandoWrappers::createWrapper(Function &F, + const SmallVectorImpl &AddressUses) { + std::string OriginalName = F.getName(); + F.setName(Twine(OriginalName, F.isVarArg() ? OrigVASuffix : OrigSuffix)); + + auto Wrapper = Function::Create(F.getFunctionType(), F.getLinkage(), OriginalName, + F.getParent()); + Wrapper->copyAttributesFrom(&F); + Wrapper->setComdat(F.getComdat()); + + Wrapper->addFnAttr(Attribute::NoInline); + Wrapper->addFnAttr(Attribute::OptimizeForSize); + + // +) Calls to a non-local function must go through the wrapper since they + // could be redirected by the dynamic linker (i.e, LD_PRELOAD). + // +) Calls to vararg functions must go through the wrapper to ensure that we + // preserve the arguments on the stack when we indirect through the POT. + // -) Address-taken uses of local functions might escape, hence we must also + // replace them. + if (!F.hasLocalLinkage() || F.isVarArg()) { + F.replaceAllUsesWith(Wrapper); + if (!F.hasLocalLinkage()) + F.setVisibility(GlobalValue::HiddenVisibility); + } else { + assert(!AddressUses.empty()); + SmallSet Constants; + for (auto U : AddressUses) + replaceAddressTakenUse(U, &F, Wrapper, Constants); + } + + return Wrapper; +} + +static SmallVector findVAStarts(Function &F) { + SmallVector Insts; + for (auto &I : instructions(F)) { + if (isa(&I)) + Insts.push_back(cast(&I)); + } + return Insts; +} + +static AllocaInst *findAlloca(VAStartInst *VAStart) { + Instruction *Inst = VAStart; + while (Inst && !isa(Inst)) + Inst = dyn_cast(Inst->op_begin()); + + assert(Inst && "Could not find va_list alloca in var args function"); + return cast(Inst); +} + +static AllocaInst *createVAList(Module *M, IRBuilder<> &Builder, Type *VAListTy) { + auto VAListAlloca = Builder.CreateAlloca(VAListTy); + Builder.CreateCall( // @llvm.va_start(i8* ) + Intrinsic::getDeclaration(M, Intrinsic::vastart), + { Builder.CreateBitCast(VAListAlloca, Builder.getInt8PtrTy()) }); + + return VAListAlloca; +} + +static void createVAEndCall(IRBuilder<> &Builder, AllocaInst *VAListAlloca) { + Builder.CreateCall( // @llvm.va_end(i8* ) + Intrinsic::getDeclaration(VAListAlloca->getModule(), Intrinsic::vaend), + { Builder.CreateBitCast(VAListAlloca, Builder.getInt8PtrTy()) }); +} + +static void replaceWithVACopyCall(IRBuilder<> &Builder, VAStartInst *VAStart, + Argument *VAListArg) { + Builder.SetInsertPoint(VAStart); + Builder.CreateCall( // @llvm.va_copy(i8* , i8* ) + Intrinsic::getDeclaration(VAStart->getModule(), Intrinsic::vacopy), + { VAStart->getArgOperand(0), + Builder.CreateBitCast(VAListArg, Builder.getInt8PtrTy()) }); + VAStart->eraseFromParent(); +} + +void PagerandoWrappers::createWrapperBody(Function *Wrapper, Function *Callee, + Type *VAListTy) { + auto BB = BasicBlock::Create(Wrapper->getContext(), "", Wrapper); + IRBuilder<> Builder(BB); + + // Arguments + SmallVector Args; + for (auto &A : Wrapper->args()) + Args.push_back(&A); + + // va_list alloca and va_start + AllocaInst* VAListAlloca; + if (VAListTy) { + VAListAlloca = createVAList(Wrapper->getParent(), Builder, VAListTy); + Args.push_back(VAListAlloca); + } + + // Call + auto Call = Builder.CreateCall(Callee, Args); + Call->setCallingConv(Callee->getCallingConv()); + + // va_end + if (VAListTy) + createVAEndCall(Builder, VAListAlloca); + + // Return + if (Wrapper->getReturnType()->isVoidTy()) { + Builder.CreateRetVoid(); + } else { + Builder.CreateRet(Call); + } +} + +// Replaces the original function with a new function that takes a va_list +// parameter but is not varargs: foo(int, ...) -> foo$$origva(int, *va_list) +Function *PagerandoWrappers::rewriteVarargs(Function &F, Type *&VAListTy) { + auto VAStarts = findVAStarts(F); + if (VAStarts.empty()) return &F; + + // Determine va_list type + auto VAListAlloca = findAlloca(VAStarts[0]); + VAListTy = VAListAlloca->getAllocatedType(); + + // Adapt function type + auto FTy = F.getFunctionType(); + SmallVector Params(FTy->param_begin(), FTy->param_end()); + Params.push_back(VAListTy->getPointerTo()); + auto NonVAFty = FunctionType::get(FTy->getReturnType(), Params, false); + + // Create new function definition + auto NF = Function::Create(NonVAFty, F.getLinkage(), "", F.getParent()); + NF->takeName(&F); + NF->copyAttributesFrom(&F); + NF->setComdat(F.getComdat()); + NF->setSubprogram(F.getSubprogram()); + + // Move basic blocks into new function; F is now dysfunctional + NF->getBasicBlockList().splice(NF->begin(), F.getBasicBlockList()); + + // Adapt arguments (FN's additional 'va_list' arg does not need adaption) + auto DestArg = NF->arg_begin(); + for (auto &A : F.args()) { + A.replaceAllUsesWith(DestArg); + DestArg->takeName(&A); + ++DestArg; + } + + // Replace va_start with va_copy + IRBuilder<> Builder(NF->getContext()); + auto VAListArg = NF->arg_end() - 1; + for (auto VAStart : VAStarts) + replaceWithVACopyCall(Builder, VAStart, VAListArg); + + // Delete original function + F.eraseFromParent(); + + return NF; +} Index: test/Transforms/PagerandoWrappers/address-taken.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/address-taken.ll @@ -0,0 +1,16 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: @fn_ptr1 = global void ()* @global +; CHECK-LABEL: @fn_ptr2 = global void ()* @internal + +@fn_ptr1 = global void ()* @global +@fn_ptr2 = global void ()* @internal + +; CHECK-LABEL: define hidden void @"global$$orig"() #0 +; CHECK-LABEL: define internal void @"internal$$orig"() #0 + +define void @global() { ret void } +define internal void @internal() { ret void } + +; CHECK-LABEL: define void @global() #1 +; CHECK-LABEL: define internal void @internal() #1 Index: test/Transforms/PagerandoWrappers/calls.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/calls.ll @@ -0,0 +1,22 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: define hidden void @"global$$orig"() #0 { +; CHECK-LABEL: define internal void @internal() #0 { + +define void @global() { ret void } +define internal void @internal() { ret void } + +; CHECK-LABEL: define internal void @user() #0 { +define internal void @user() { +; CHECK-NEXT: call void @global() +; CHECK-NEXT: call void @internal() +; CHECK-NEXT: ret void + call void @global() + call void @internal() + ret void +} + +; CHECK-LABEL: define void @global() #1 { + +; CHECK-LABEL: attributes #0 = { pagerando } +; CHECK-LABEL: attributes #1 = { noinline optsize } Index: test/Transforms/PagerandoWrappers/varargs.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/varargs.ll @@ -0,0 +1,114 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +declare void @llvm.va_start(i8*) +declare void @llvm.va_end(i8*) +declare void @llvm.va_copy(i8*, i8*) + +; CHECK-LABEL: define internal void @user() +; CHECK-NEXT: call i32 (...) @varags(i32 13, i32 37) + +define internal void @user() { + call i32 (...) @varags(i32 13, i32 37) + ret void +} + +; CHECK-LABEL: define i32 @varags(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: %2 = call i32 @"varags$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret i32 %2 + +; CHECK-LABEL: define hidden i32 @"varags$$origva"(i8*) +; CHECK-NEXT: %va = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va, i8* %0) +; CHECK-NEXT: %ret = va_arg i8* %va, i32 +; CHECK-NEXT: call void @llvm.va_end(i8* %va) +; CHECK-NEXT: ret i32 %ret + +define i32 @varags(...) { + %va = alloca i8 + call void @llvm.va_start(i8* %va) + %ret = va_arg i8* %va, i32 + call void @llvm.va_end(i8* %va) + ret i32 %ret +} + +; CHECK-LABEL: define void @multiple_starts(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: call void @"multiple_starts$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define hidden void @"multiple_starts$$origva"(i8*) +; CHECK-NEXT: %va1 = alloca i8 +; CHECK-NEXT: %va2 = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va1, i8* %0) +; CHECK-NEXT: call void @llvm.va_copy(i8* %va2, i8* %0) +; CHECK-NEXT: call void @llvm.va_end(i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va2) +; CHECK-NEXT: ret void + +define void @multiple_starts(...) { + %va1 = alloca i8 + %va2 = alloca i8 + call void @llvm.va_start(i8* %va1) + call void @llvm.va_start(i8* %va2) + call void @llvm.va_end(i8* %va1) + call void @llvm.va_end(i8* %va2) + ret void +} + +; CHECK-LABEL: define void @copy(...) +; CHECK-NEXT: %1 = alloca i8 +; CHECK-NEXT: call void @llvm.va_start(i8* %1) +; CHECK-NEXT: call void @"copy$$origva"(i8* %1) +; CHECK-NEXT: call void @llvm.va_end(i8* %1) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define hidden void @"copy$$origva"(i8*) +; CHECK-NEXT: %va1 = alloca i8 +; CHECK-NEXT: %va2 = alloca i8 +; CHECK-NEXT: call void @llvm.va_copy(i8* %va1, i8* %0) +; CHECK-NEXT: call void @llvm.va_copy(i8* %va2, i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va1) +; CHECK-NEXT: call void @llvm.va_end(i8* %va2) +; CHECK-NEXT: ret void + +define void @copy(...) { + %va1 = alloca i8 + %va2 = alloca i8 + call void @llvm.va_start(i8* %va1) + call void @llvm.va_copy(i8* %va2, i8* %va1) + call void @llvm.va_end(i8* %va1) + call void @llvm.va_end(i8* %va2) + ret void +} + +; CHECK-LABEL: define void @real_va_type(...) +; CHECK-NEXT: %1 = alloca %struct.va_list +; CHECK-NEXT: %2 = bitcast %struct.va_list* %1 to i8* +; CHECK-NEXT: call void @llvm.va_start(i8* %2) +; CHECK-NEXT: call void @"real_va_type$$origva"(%struct.va_list* %1) +; CHECK-NEXT: %3 = bitcast %struct.va_list* %1 to i8* +; CHECK-NEXT: call void @llvm.va_end(i8* %3) +; CHECK-NEXT: ret void + +; CHECK-LABEL: define hidden void @"real_va_type$$origva"(%struct.va_list*) +; CHECK-NEXT: %va = alloca %struct.va_list +; CHECK-NEXT: %va_ptr = bitcast %struct.va_list* %va to i8* +; CHECK-NEXT: %2 = bitcast %struct.va_list* %0 to i8* +; CHECK-NEXT: call void @llvm.va_copy(i8* %va_ptr, i8* %2) +; CHECK-NEXT: call void @llvm.va_end(i8* %va_ptr) +; CHECK-NEXT: ret void + +%struct.va_list = type { i8*, i8*, i8*, i32, i32 } + +define void @real_va_type(...) { + %va = alloca %struct.va_list + %va_ptr = bitcast %struct.va_list* %va to i8* + call void @llvm.va_start(i8* %va_ptr) + call void @llvm.va_end(i8* %va_ptr) + ret void +} Index: test/Transforms/PagerandoWrappers/wrapper-body.ll =================================================================== --- /dev/null +++ test/Transforms/PagerandoWrappers/wrapper-body.ll @@ -0,0 +1,15 @@ +; RUN: opt < %s -pagerando-wrappers -S | FileCheck %s + +; CHECK-LABEL: define hidden i32 @"foo$$orig"(i32 %arg) #0 +; CHECK-NEXT: ret i32 %arg + +define i32 @foo(i32 %arg) { + ret i32 %arg +} + +; CHECK-LABEL: define i32 @foo(i32) #1 +; CHECK-NEXT: %2 = call i32 @"foo$$orig"(i32 %0) +; CHECK-NEXT: ret i32 %2 + +; CHECK-LABEL: attributes #0 = { pagerando } +; CHECK-LABEL: attributes #1 = { noinline optsize }