diff options
author | Reid Kleckner <reid@kleckner.net> | 2015-03-05 18:26:34 +0000 |
---|---|---|
committer | Reid Kleckner <reid@kleckner.net> | 2015-03-05 18:26:34 +0000 |
commit | 9f7c86141659e7637fb099bc41b1e015bbb42781 (patch) | |
tree | 6b5ca47d9a6d14e195785454fedc749c7146f35b | |
parent | 8b4cc5e61610d58ca356beeff224efbe6d458bee (diff) |
Replace llvm.frameallocate with llvm.frameescape
Turns out it's pretty straightforward and simplifies the implementation.
Reviewers: andrew.w.kaylor
Differential Revision: http://reviews.llvm.org/D8051
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@231386 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/LangRef.rst | 24 | ||||
-rw-r--r-- | include/llvm/IR/CallSite.h | 7 | ||||
-rw-r--r-- | include/llvm/IR/Intrinsics.td | 6 | ||||
-rw-r--r-- | include/llvm/MC/MCContext.h | 2 | ||||
-rw-r--r-- | lib/CodeGen/PrologEpilogInserter.cpp | 11 | ||||
-rw-r--r-- | lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 46 | ||||
-rw-r--r-- | lib/CodeGen/WinEHPrepare.cpp | 213 | ||||
-rw-r--r-- | lib/IR/Verifier.cpp | 55 | ||||
-rw-r--r-- | lib/MC/MCContext.cpp | 7 | ||||
-rw-r--r-- | lib/Target/X86/X86RegisterInfo.cpp | 19 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-catch-all.ll | 2 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-catch-scalar.ll | 16 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-frame-vars.ll | 42 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-inalloca.ll | 29 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-min-unwind.ll | 17 | ||||
-rw-r--r-- | test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll | 62 | ||||
-rw-r--r-- | test/CodeGen/X86/frameallocate.ll | 43 | ||||
-rw-r--r-- | test/CodeGen/X86/frameescape.ll | 63 | ||||
-rw-r--r-- | test/Verifier/frameallocate.ll | 48 | ||||
-rw-r--r-- | test/Verifier/frameescape.ll | 69 |
20 files changed, 379 insertions, 402 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst index cf6cc7e1dcd..218be344b6b 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -7614,7 +7614,7 @@ Note that calling this intrinsic does not prevent function inlining or other aggressive transformations, so the value returned may not be that of the obvious source-language caller. -'``llvm.frameallocate``' and '``llvm.framerecover``' Intrinsics +'``llvm.frameescape``' and '``llvm.framerecover``' Intrinsics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Syntax: @@ -7622,24 +7622,23 @@ Syntax: :: - declare i8* @llvm.frameallocate(i32 %size) - declare i8* @llvm.framerecover(i8* %func, i8* %fp) + declare void @llvm.frameescape(...) + declare i8* @llvm.framerecover(i8* %func, i8* %fp, i32 %idx) Overview: """"""""" -The '``llvm.frameallocate``' intrinsic allocates stack memory at some fixed -offset from the frame pointer, and the '``llvm.framerecover``' -intrinsic applies that offset to a live frame pointer to recover the address of -the allocation. The offset is computed during frame layout of the caller of -``llvm.frameallocate``. +The '``llvm.frameescape``' intrinsic escapes offsets of a collection of static +allocas, and the '``llvm.framerecover``' intrinsic applies those offsets to a +live frame pointer to recover the address of the allocation. The offset is +computed during frame layout of the caller of ``llvm.frameescape``. Arguments: """""""""" -The ``size`` argument to '``llvm.frameallocate``' must be a constant integer -indicating the amount of stack memory to allocate. As with allocas, allocating -zero bytes is legal, but the result is undefined. +All arguments to '``llvm.frameescape``' must be pointers to static allocas or +casts of static allocas. Each function can only call '``llvm.frameescape``' +once, and it can only do so from the entry block. The ``func`` argument to '``llvm.framerecover``' must be a constant bitcasted pointer to a function defined in the current module. The code @@ -7651,6 +7650,9 @@ pointer of a call frame that is currently live. The return value of '``llvm.frameaddress``' is one way to produce such a value, but most platforms also expose the frame pointer through stack unwinding mechanisms. +The ``idx`` argument to '``llvm.framerecover``' indicates which alloca passed to +'``llvm.frameescape``' to recover. It is zero-indexed. + Semantics: """""""""" diff --git a/include/llvm/IR/CallSite.h b/include/llvm/IR/CallSite.h index a4ea243ecdc..16a9e5780fc 100644 --- a/include/llvm/IR/CallSite.h +++ b/include/llvm/IR/CallSite.h @@ -151,7 +151,12 @@ public: IterTy arg_end() const { return (*this)->op_end() - getArgumentEndOffset(); } bool arg_empty() const { return arg_end() == arg_begin(); } unsigned arg_size() const { return unsigned(arg_end() - arg_begin()); } - + + /// arg_operands - iteration adapter for range-for loops. + iterator_range<IterTy> arg_operands() const { + return iterator_range<User::const_op_iterator>(arg_begin(), arg_eng()); + } + /// getType - Return the type of the instruction that generated this call site /// Type *getType() const { return (*this)->getType(); } diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index af65b1a6a4a..bd7f42e6bd9 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -261,10 +261,10 @@ def int_gcwrite : Intrinsic<[], // def int_returnaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>; def int_frameaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>; -def int_frameallocate : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty]>; +def int_frameescape : Intrinsic<[], [llvm_vararg_ty]>; def int_framerecover : Intrinsic<[llvm_ptr_ty], - [llvm_ptr_ty, llvm_ptr_ty], - [IntrNoMem]>; + [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty], + [IntrNoMem]>; def int_read_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty], [IntrNoMem], "llvm.read_register">; def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty], diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h index f9792ecf5f7..630db53ec02 100644 --- a/include/llvm/MC/MCContext.h +++ b/include/llvm/MC/MCContext.h @@ -238,7 +238,7 @@ namespace llvm { MCSymbol *getOrCreateSectionSymbol(const MCSectionELF &Section); - MCSymbol *getOrCreateFrameAllocSymbol(StringRef FuncName); + MCSymbol *getOrCreateFrameAllocSymbol(StringRef FuncName, unsigned Idx); /// LookupSymbol - Get the symbol for \p Name, or null. MCSymbol *LookupSymbol(StringRef Name) const; diff --git a/lib/CodeGen/PrologEpilogInserter.cpp b/lib/CodeGen/PrologEpilogInserter.cpp index 6d29b98f3c9..e75cb038abb 100644 --- a/lib/CodeGen/PrologEpilogInserter.cpp +++ b/lib/CodeGen/PrologEpilogInserter.cpp @@ -810,17 +810,6 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &Fn, continue; } - // Frame allocations are target independent. Simply swap the index with - // the offset. - if (MI->getOpcode() == TargetOpcode::FRAME_ALLOC) { - assert(TFI->hasFP(Fn) && "frame alloc requires FP"); - MachineOperand &FI = MI->getOperand(i); - unsigned Reg; - int FrameOffset = TFI->getFrameIndexReference(Fn, FI.getIndex(), Reg); - FI.ChangeToImmediate(FrameOffset); - continue; - } - // Some instructions (e.g. inline asm instructions) can have // multiple frame indices and/or cause eliminateFrameIndex // to insert more than one instruction. We need the register diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 097b618252e..c5bd2ab19ca 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5618,45 +5618,47 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { case Intrinsic::instrprof_increment: llvm_unreachable("instrprof failed to lower an increment"); - case Intrinsic::frameallocate: { + case Intrinsic::frameescape: { MachineFunction &MF = DAG.getMachineFunction(); const TargetInstrInfo *TII = DAG.getSubtarget().getInstrInfo(); - // Do the allocation and map it as a normal value. - // FIXME: Maybe we should add this to the alloca map so that we don't have - // to register allocate it? - uint64_t Size = cast<ConstantInt>(I.getArgOperand(0))->getZExtValue(); - int Alloc = MF.getFrameInfo()->CreateFrameAllocation(Size); - MVT PtrVT = TLI.getPointerTy(0); - SDValue FIVal = DAG.getFrameIndex(Alloc, PtrVT); - setValue(&I, FIVal); - - // Directly emit a FRAME_ALLOC machine instr. Label assignment emission is - // the same on all targets. - MCSymbol *FrameAllocSym = - MF.getMMI().getContext().getOrCreateFrameAllocSymbol(MF.getName()); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, dl, - TII->get(TargetOpcode::FRAME_ALLOC)) - .addSym(FrameAllocSym) - .addFrameIndex(Alloc); + // Directly emit some FRAME_ALLOC machine instrs. Label assignment emission + // is the same on all targets. + for (unsigned Idx = 0, E = I.getNumArgOperands(); Idx < E; ++Idx) { + AllocaInst *Slot = + cast<AllocaInst>(I.getArgOperand(Idx)->stripPointerCasts()); + assert(FuncInfo.StaticAllocaMap.count(Slot) && + "can only escape static allocas"); + int FI = FuncInfo.StaticAllocaMap[Slot]; + MCSymbol *FrameAllocSym = + MF.getMMI().getContext().getOrCreateFrameAllocSymbol(MF.getName(), + Idx); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, dl, + TII->get(TargetOpcode::FRAME_ALLOC)) + .addSym(FrameAllocSym) + .addFrameIndex(FI); + } return nullptr; } case Intrinsic::framerecover: { - // i8* @llvm.framerecover(i8* %fn, i8* %fp) + // i8* @llvm.framerecover(i8* %fn, i8* %fp, i32 %idx) MachineFunction &MF = DAG.getMachineFunction(); MVT PtrVT = TLI.getPointerTy(0); // Get the symbol that defines the frame offset. - Function *Fn = cast<Function>(I.getArgOperand(0)->stripPointerCasts()); + auto *Fn = cast<Function>(I.getArgOperand(0)->stripPointerCasts()); + auto *Idx = cast<ConstantInt>(I.getArgOperand(2)); + unsigned IdxVal = unsigned(Idx->getLimitedValue(INT_MAX)); MCSymbol *FrameAllocSym = - MF.getMMI().getContext().getOrCreateFrameAllocSymbol(Fn->getName()); + MF.getMMI().getContext().getOrCreateFrameAllocSymbol(Fn->getName(), + IdxVal); // Create a TargetExternalSymbol for the label to avoid any target lowering // that would make this PC relative. StringRef Name = FrameAllocSym->getName(); - assert(Name.size() == strlen(Name.data()) && "not null terminated"); + assert(Name.data()[Name.size()] == '\0' && "not null terminated"); SDValue OffsetSym = DAG.getTargetExternalSymbol(Name.data(), PtrVT); SDValue OffsetVal = DAG.getNode(ISD::FRAME_ALLOC_RECOVER, sdl, PtrVT, OffsetSym); diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 5a13e556d54..eefb278f0a7 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -36,17 +36,12 @@ using namespace llvm::PatternMatch; namespace { -struct HandlerAllocas { - TinyPtrVector<AllocaInst *> Allocas; - int ParentFrameAllocationIndex; -}; - // This map is used to model frame variable usage during outlining, to // construct a structure type to hold the frame variables in a frame // allocation block, and to remap the frame variable allocas (including // spill locations as needed) to GEPs that get the variable from the // frame allocation structure. -typedef MapVector<Value *, HandlerAllocas> FrameVarInfoMap; +typedef MapVector<Value *, TinyPtrVector<AllocaInst *>> FrameVarInfoMap; class WinEHPrepare : public FunctionPass { std::unique_ptr<FunctionPass> DwarfPrepare; @@ -73,7 +68,7 @@ private: SmallVectorImpl<LandingPadInst *> &LPads); bool outlineHandler(HandlerType CatchOrCleanup, Function *SrcFn, Constant *SelectorType, LandingPadInst *LPad, - CallInst *&EHAlloc, FrameVarInfoMap &VarInfo); + FrameVarInfoMap &VarInfo); }; class WinEHFrameVariableMaterializer : public ValueMaterializer { @@ -236,7 +231,6 @@ bool WinEHPrepare::prepareCPPEHHandlers( // outlined catch and cleanup handlers. They will be populated as the // handlers are outlined. FrameVarInfoMap FrameVarInfo; - SmallVector<CallInst *, 4> HandlerAllocs; bool HandlersOutlined = false; @@ -263,15 +257,10 @@ bool WinEHPrepare::prepareCPPEHHandlers( if (LPad->isCatch(Idx)) { // Create a new instance of the handler data structure in the // HandlerData vector. - CallInst *EHAlloc = nullptr; bool Outlined = outlineHandler(Catch, &F, LPad->getClause(Idx), LPad, - EHAlloc, FrameVarInfo); + FrameVarInfo); if (Outlined) { HandlersOutlined = true; - // These values must be resolved after all handlers have been - // outlined. - if (EHAlloc) - HandlerAllocs.push_back(EHAlloc); } } // End if (isCatch) } // End for each clause @@ -283,14 +272,10 @@ bool WinEHPrepare::prepareCPPEHHandlers( // multiple landing pads. Those cases will be supported later // when landing pad block analysis is added. if (LPad->isCleanup()) { - CallInst *EHAlloc = nullptr; bool Outlined = - outlineHandler(Cleanup, &F, nullptr, LPad, EHAlloc, FrameVarInfo); + outlineHandler(Cleanup, &F, nullptr, LPad, FrameVarInfo); if (Outlined) { HandlersOutlined = true; - // This value must be resolved after all handlers have been outlined. - if (EHAlloc) - HandlerAllocs.push_back(EHAlloc); } } } // End for each landingpad @@ -306,103 +291,27 @@ bool WinEHPrepare::prepareCPPEHHandlers( // that looks for allocas with no uses in the parent function. // That will only happen after the pruning is implemented. - // Remap the frame variables. - SmallVector<Type *, 2> StructTys; - StructTys.push_back(Type::getInt32Ty(F.getContext())); // EH state - StructTys.push_back(Type::getInt8PtrTy(F.getContext())); // EH object - - // Start the index at two since we always have the above fields at 0 and 1. - int Idx = 2; - - // FIXME: Sort the FrameVarInfo vector by the ParentAlloca size and alignment - // and add padding as necessary to provide the proper alignment. - - // Map the alloca instructions to the corresponding index in the - // frame allocation structure. If any alloca is used only in a single - // handler and is not used in the parent frame after outlining, it will - // be assigned an index of -1, meaning the handler can keep its - // "temporary" alloca and the original alloca can be erased from the - // parent function. If we later encounter this alloca in a second - // handler, we will assign it a place in the frame allocation structure - // at that time. Since the instruction replacement doesn't happen until - // all the entries in the HandlerData have been processed this isn't a - // problem. - for (auto &VarInfoEntry : FrameVarInfo) { - Value *ParentVal = VarInfoEntry.first; - HandlerAllocas &AllocaInfo = VarInfoEntry.second; - - if (auto *ParentAlloca = dyn_cast<AllocaInst>(ParentVal)) { - // If the instruction still has uses in the parent function or if it is - // referenced by more than one handler, add it to the frame allocation - // structure. - if (ParentAlloca->getNumUses() != 0 || AllocaInfo.Allocas.size() > 1) { - Type *VarTy = ParentAlloca->getAllocatedType(); - StructTys.push_back(VarTy); - AllocaInfo.ParentFrameAllocationIndex = Idx++; - } else { - // If the variable is not used in the parent frame and it is only used - // in one handler, the alloca can be removed from the parent frame - // and the handler will keep its "temporary" alloca to define the value. - // An element index of -1 is used to indicate this condition. - AllocaInfo.ParentFrameAllocationIndex = -1; - } - } else { - // FIXME: Sink non-alloca values into the handler if they have no other - // uses in the parent function after outlining and are only used in - // one handler. - Type *VarTy = ParentVal->getType(); - StructTys.push_back(VarTy); - AllocaInfo.ParentFrameAllocationIndex = Idx++; - } - } - - // Having filled the StructTys vector and assigned an index to each element, - // we can now create the structure. - StructType *EHDataStructTy = StructType::create( - F.getContext(), StructTys, "struct." + F.getName().str() + ".ehdata"); - IRBuilder<> Builder(F.getParent()->getContext()); - - // Create a frame allocation. Module *M = F.getParent(); LLVMContext &Context = M->getContext(); BasicBlock *Entry = &F.getEntryBlock(); + IRBuilder<> Builder(F.getParent()->getContext()); Builder.SetInsertPoint(Entry->getFirstInsertionPt()); - Function *FrameAllocFn = - Intrinsic::getDeclaration(M, Intrinsic::frameallocate); - uint64_t EHAllocSize = M->getDataLayout().getTypeAllocSize(EHDataStructTy); - Value *FrameAllocArgs[] = { - ConstantInt::get(Type::getInt32Ty(Context), EHAllocSize)}; - CallInst *FrameAlloc = - Builder.CreateCall(FrameAllocFn, FrameAllocArgs, "frame.alloc"); - - Value *FrameEHData = Builder.CreateBitCast( - FrameAlloc, EHDataStructTy->getPointerTo(), "eh.data"); - - // Now visit each handler that is using the structure and bitcast its EHAlloc - // value to be a pointer to the frame alloc structure. - DenseMap<Function *, Value *> EHDataMap; - for (CallInst *EHAlloc : HandlerAllocs) { - // The EHAlloc has no uses at this time, so we need to just insert the - // cast before the next instruction. There is always a next instruction. - BasicBlock::iterator II = EHAlloc; - ++II; - Builder.SetInsertPoint(cast<Instruction>(II)); - Value *EHData = Builder.CreateBitCast( - EHAlloc, EHDataStructTy->getPointerTo(), "eh.data"); - EHDataMap[EHAlloc->getParent()->getParent()] = EHData; - } + + Function *FrameEscapeFn = + Intrinsic::getDeclaration(M, Intrinsic::frameescape); + Function *RecoverFrameFn = + Intrinsic::getDeclaration(M, Intrinsic::framerecover); + Type *Int8PtrType = Type::getInt8PtrTy(Context); + Type *Int32Type = Type::getInt32Ty(Context); // Finally, replace all of the temporary allocas for frame variables used in - // the outlined handlers and the original frame allocas with GEP instructions - // that get the equivalent pointer from the frame allocation struct. - Instruction *FrameEHDataInst = cast<Instruction>(FrameEHData); - BasicBlock::iterator II = FrameEHDataInst; - ++II; + // the outlined handlers with calls to llvm.framerecover. + BasicBlock::iterator II = Entry->getFirstInsertionPt(); Instruction *AllocaInsertPt = II; + SmallVector<Value *, 8> AllocasToEscape; for (auto &VarInfoEntry : FrameVarInfo) { Value *ParentVal = VarInfoEntry.first; - HandlerAllocas &AllocaInfo = VarInfoEntry.second; - int Idx = AllocaInfo.ParentFrameAllocationIndex; + TinyPtrVector<AllocaInst *> &Allocas = VarInfoEntry.second; // If the mapped value isn't already an alloca, we need to spill it if it // is a computed value or copy it if it is an argument. @@ -430,48 +339,51 @@ bool WinEHPrepare::prepareCPPEHHandlers( } } - // If we have an index of -1 for this instruction, it means it isn't used - // outside of this handler. In that case, we just keep the "temporary" - // alloca in the handler and erase the original alloca from the parent. - if (Idx == -1) { + // If the parent alloca is no longer used and only one of the handlers used + // it, erase the parent and leave the copy in the outlined handler. + if (ParentAlloca->getNumUses() == 0 && Allocas.size() == 1) { ParentAlloca->eraseFromParent(); - } else { - // Otherwise, we replace the parent alloca and all outlined allocas - // which map to it with GEP instructions. - - // First replace the original alloca. - Builder.SetInsertPoint(ParentAlloca); - Builder.SetCurrentDebugLocation(ParentAlloca->getDebugLoc()); - Value *ElementPtr = - Builder.CreateConstInBoundsGEP2_32(FrameEHData, 0, Idx); - ParentAlloca->replaceAllUsesWith(ElementPtr); - ParentAlloca->removeFromParent(); - ElementPtr->takeName(ParentAlloca); - if (ParentAlloca == AllocaInsertPt) - AllocaInsertPt = dyn_cast<Instruction>(ElementPtr); - delete ParentAlloca; - - // Next replace all outlined allocas that are mapped to it. - for (AllocaInst *TempAlloca : AllocaInfo.Allocas) { - Value *EHData = EHDataMap[TempAlloca->getParent()->getParent()]; - // FIXME: Sink this GEP into the blocks where it is used. - Builder.SetInsertPoint(TempAlloca); - Builder.SetCurrentDebugLocation(TempAlloca->getDebugLoc()); - ElementPtr = Builder.CreateConstInBoundsGEP2_32(EHData, 0, Idx); - TempAlloca->replaceAllUsesWith(ElementPtr); - TempAlloca->removeFromParent(); - ElementPtr->takeName(TempAlloca); - delete TempAlloca; + continue; + } + + // Add this alloca to the list of things to escape. + AllocasToEscape.push_back(ParentAlloca); + + // Next replace all outlined allocas that are mapped to it. + for (AllocaInst *TempAlloca : Allocas) { + Function *HandlerFn = TempAlloca->getParent()->getParent(); + // FIXME: Sink this GEP into the blocks where it is used. + Builder.SetInsertPoint(TempAlloca); + Builder.SetCurrentDebugLocation(TempAlloca->getDebugLoc()); + Value *RecoverArgs[] = { + Builder.CreateBitCast(&F, Int8PtrType, ""), + &(HandlerFn->getArgumentList().back()), + llvm::ConstantInt::get(Int32Type, AllocasToEscape.size() - 1)}; + Value *RecoveredAlloca = + Builder.CreateCall(RecoverFrameFn, RecoverArgs); + // Add a pointer bitcast if the alloca wasn't an i8. + if (RecoveredAlloca->getType() != TempAlloca->getType()) { + RecoveredAlloca->setName(Twine(TempAlloca->getName()) + ".i8"); + RecoveredAlloca = + Builder.CreateBitCast(RecoveredAlloca, TempAlloca->getType()); } - } // end else of if (Idx == -1) + TempAlloca->replaceAllUsesWith(RecoveredAlloca); + TempAlloca->removeFromParent(); + RecoveredAlloca->takeName(TempAlloca); + delete TempAlloca; + } } // End for each FrameVarInfo entry. + // Insert 'call void (...)* @llvm.frameescape(...)' at the end of the entry + // block. + Builder.SetInsertPoint(&F.getEntryBlock().back()); + Builder.CreateCall(FrameEscapeFn, AllocasToEscape); + return HandlersOutlined; } bool WinEHPrepare::outlineHandler(HandlerType CatchOrCleanup, Function *SrcFn, Constant *SelectorType, LandingPadInst *LPad, - CallInst *&EHAlloc, FrameVarInfoMap &VarInfo) { Module *M = SrcFn->getParent(); LLVMContext &Context = M->getContext(); @@ -500,23 +412,6 @@ bool WinEHPrepare::outlineHandler(HandlerType CatchOrCleanup, Function *SrcFn, Builder.SetInsertPoint(Entry); Builder.SetCurrentDebugLocation(LPad->getDebugLoc()); - // The outlined handler will be called with the parent's frame pointer as - // its second argument. To enable the handler to access variables from - // the parent frame, we use that pointer to get locate a special block - // of memory that was allocated using llvm.eh.allocateframe for this - // purpose. During the outlining process we will determine which frame - // variables are used in handlers and create a structure that maps these - // variables into the frame allocation block. - // - // The frame allocation block also contains an exception state variable - // used by the runtime and a pointer to the exception object pointer - // which will be filled in by the runtime for use in the handler. - Function *RecoverFrameFn = - Intrinsic::getDeclaration(M, Intrinsic::framerecover); - Value *RecoverArgs[] = {Builder.CreateBitCast(SrcFn, Int8PtrType, ""), - &(Handler->getArgumentList().back())}; - EHAlloc = Builder.CreateCall(RecoverFrameFn, RecoverArgs, "eh.alloc"); - std::unique_ptr<WinEHCloningDirectorBase> Director; if (CatchOrCleanup == Catch) { @@ -765,7 +660,7 @@ Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { if (auto *AV = dyn_cast<AllocaInst>(V)) { AllocaInst *NewAlloca = dyn_cast<AllocaInst>(AV->clone()); Builder.Insert(NewAlloca, AV->getName()); - FrameVarInfo[AV].Allocas.push_back(NewAlloca); + FrameVarInfo[AV].push_back(NewAlloca); return NewAlloca; } @@ -776,7 +671,7 @@ Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { if (isa<Instruction>(V) || isa<Argument>(V)) { AllocaInst *NewAlloca = Builder.CreateAlloca(V->getType(), nullptr, "eh.temp.alloca"); - FrameVarInfo[V].Allocas.push_back(NewAlloca); + FrameVarInfo[V].push_back(NewAlloca); LoadInst *NewLoad = Builder.CreateLoad(NewAlloca, V->getName() + ".reload"); return NewLoad; } diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index fbdc9880c5c..71e53f4b15c 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -198,14 +198,18 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport { /// personality function. const Value *PersonalityFn; - /// \brief Whether we've seen a call to @llvm.frameallocate in this function + /// \brief Whether we've seen a call to @llvm.frameescape in this function /// already. - bool SawFrameAllocate; + bool SawFrameEscape; + + /// Stores the count of how many objects were passed to llvm.frameescape for a + /// given function and the largest index passed to llvm.framerecover. + DenseMap<Function *, std::pair<unsigned, unsigned>> FrameEscapeInfo; public: explicit Verifier(raw_ostream &OS = dbgs()) : VerifierSupport(OS), Context(nullptr), PersonalityFn(nullptr), - SawFrameAllocate(false) {} + SawFrameEscape(false) {} bool verify(const Function &F) { M = F.getParent(); @@ -240,7 +244,7 @@ public: visit(const_cast<Function &>(F)); InstsInThisBlock.clear(); PersonalityFn = nullptr; - SawFrameAllocate = false; + SawFrameEscape = false; return !Broken; } @@ -259,6 +263,10 @@ public: visitFunction(*I); } + // Now that we've visited every function, verify that we never asked to + // recover a frame index that wasn't escaped. + verifyFrameRecoverIndices(); + for (Module::const_global_iterator I = M.global_begin(), E = M.global_end(); I != E; ++I) visitGlobalVariable(*I); @@ -373,6 +381,7 @@ private: void VerifyConstantExprBitcastType(const ConstantExpr *CE); void VerifyStatepoint(ImmutableCallSite CS); + void verifyFrameRecoverIndices(); }; class DebugInfoVerifier : public VerifierSupport { public: @@ -1266,6 +1275,20 @@ void Verifier::VerifyStatepoint(ImmutableCallSite CS) { // about. See example statepoint.ll in the verifier subdirectory } +void Verifier::verifyFrameRecoverIndices() { + llvm::errs() << "verifyFrameRecoverIndices\n"; + for (auto &Counts : FrameEscapeInfo) { + Function *F = Counts.first; + unsigned EscapedObjectCount = Counts.second.first; + unsigned MaxRecoveredIndex = Counts.second.second; + Assert1(MaxRecoveredIndex <= EscapedObjectCount, + "all indices passed to llvm.framerecover must be less than the " + "number of arguments passed ot llvm.frameescape in the parent " + "function", + F); + } +} + // visitFunction - Verify that a function is ok. // void Verifier::visitFunction(const Function &F) { @@ -2859,15 +2882,19 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) { "llvm.invariant.end parameter #2 must be a constant integer", &CI); break; - case Intrinsic::frameallocate: { + case Intrinsic::frameescape: { BasicBlock *BB = CI.getParent(); Assert1(BB == &BB->getParent()->front(), - "llvm.frameallocate used outside of entry block", &CI); - Assert1(!SawFrameAllocate, - "multiple calls to llvm.frameallocate in one function", &CI); - SawFrameAllocate = true; - Assert1(isa<ConstantInt>(CI.getArgOperand(0)), - "llvm.frameallocate argument must be constant integer size", &CI); + "llvm.frameescape used outside of entry block", &CI); + Assert1(!SawFrameEscape, + "multiple calls to llvm.frameescape in one function", &CI); + for (Value *Arg : CI.arg_operands()) { + auto *AI = dyn_cast<AllocaInst>(Arg->stripPointerCasts()); + Assert1(AI && AI->isStaticAlloca(), + "llvm.frameescape only accepts static allocas", &CI); + } + FrameEscapeInfo[BB->getParent()].first = CI.getNumArgOperands(); + SawFrameEscape = true; break; } case Intrinsic::framerecover: { @@ -2875,6 +2902,12 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) { Function *Fn = dyn_cast<Function>(FnArg); Assert1(Fn && !Fn->isDeclaration(), "llvm.framerecover first " "argument must be function defined in this module", &CI); + auto *IdxArg = dyn_cast<ConstantInt>(CI.getArgOperand(2)); + Assert1(IdxArg, "idx argument of llvm.framerecover must be a constant int", + &CI); + auto &Entry = FrameEscapeInfo[Fn]; + Entry.second = unsigned( + std::max(uint64_t(Entry.second), IdxArg->getLimitedValue(~0U) + 1)); break; } diff --git a/lib/MC/MCContext.cpp b/lib/MC/MCContext.cpp index 27b0728403a..ae082bb6074 100644 --- a/lib/MC/MCContext.cpp +++ b/lib/MC/MCContext.cpp @@ -131,9 +131,10 @@ MCSymbol *MCContext::getOrCreateSectionSymbol(const MCSectionELF &Section) { return Sym; } -MCSymbol *MCContext::getOrCreateFrameAllocSymbol(StringRef FuncName) { - return GetOrCreateSymbol(Twine(MAI->getPrivateGlobalPrefix()) + - "frameallocation_" + FuncName); +MCSymbol *MCContext::getOrCreateFrameAllocSymbol(StringRef FuncName, + unsigned Idx) { + return GetOrCreateSymbol(Twine(MAI->getPrivateGlobalPrefix()) + FuncName + + "$frame_escape_" + Twine(Idx)); } MCSymbol *MCContext::CreateSymbol(StringRef Name) { diff --git a/lib/Target/X86/X86RegisterInfo.cpp b/lib/Target/X86/X86RegisterInfo.cpp index f1fd5470c88..dc3de1c01b0 100644 --- a/lib/Target/X86/X86RegisterInfo.cpp +++ b/lib/Target/X86/X86RegisterInfo.cpp @@ -496,6 +496,25 @@ X86RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, else BasePtr = (TFI->hasFP(MF) ? FramePtr : StackPtr); + // FRAME_ALLOC uses a single offset, with no register. It only works in the + // simple FP case, and doesn't work with stack realignment. On 32-bit, the + // offset is from the traditional base pointer location. On 64-bit, the + // offset is from the SP at the end of the prologue, not the FP location. This + // matches the behavior of llvm.frameaddress. + if (Opc == TargetOpcode::FRAME_ALLOC) { + assert(TFI->hasFP(MF) && "frame alloc requires FP"); + MachineOperand &FI = MI.getOperand(FIOperandNum); + const MachineFrameInfo *MFI = MF.getFrameInfo(); + int Offset = MFI->getObjectOffset(FrameIndex) - TFI->getOffsetOfLocalArea(); + bool IsWinEH = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); + if (IsWinEH) + Offset += MFI->getStackSize(); + else + Offset += SlotSize; + FI.ChangeToImmediate(Offset); + return; + } + // For LEA64_32r when BasePtr is 32-bits (X32) we can use full-size 64-bit // register as source operand, semantic is the same and destination is // 32-bits. It saves one byte per lea in code since 0x67 prefix is avoided. diff --git a/test/CodeGen/WinEH/cppeh-catch-all.ll b/test/CodeGen/WinEH/cppeh-catch-all.ll index 0958d745250..d1141e38638 100644 --- a/test/CodeGen/WinEH/cppeh-catch-all.ll +++ b/test/CodeGen/WinEH/cppeh-catch-all.ll @@ -54,8 +54,6 @@ try.cont: ; preds = %invoke.cont2, %invo ; CHECK: define internal i8* @_Z4testv.catch(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* ; CHECK: call void @_Z16handle_exceptionv() ; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) ; CHECK: } diff --git a/test/CodeGen/WinEH/cppeh-catch-scalar.ll b/test/CodeGen/WinEH/cppeh-catch-scalar.ll index 7ed846eaa43..0b1d1772388 100644 --- a/test/CodeGen/WinEH/cppeh-catch-scalar.ll +++ b/test/CodeGen/WinEH/cppeh-catch-scalar.ll @@ -18,20 +18,17 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" -; This is the structure that will get created for the frame allocation. -; CHECK: %struct._Z4testv.ehdata = type { i32, i8*, i32 } - @_ZTIi = external constant i8* ; The function entry will be rewritten like this. ; CHECK: define void @_Z4testv() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 24) -; CHECK: %eh.data = bitcast i8* %frame.alloc to %struct._Z4testv.ehdata* ; CHECK: %exn.slot = alloca i8* ; CHECK: %ehselector.slot = alloca i32 -; CHECK-NOT: %i = alloca i32, align 4 -; CHECK: %i = getelementptr inbounds %struct._Z4testv.ehdata, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: %i = alloca i32, align 4 +; CHECK: call void (...)* @llvm.frameescape(i32* %i) +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont unwind label %lpad ; Function Attrs: uwtable define void @_Z4testv() #0 { @@ -85,9 +82,8 @@ eh.resume: ; preds = %catch.dispatch ; CHECK: define internal i8* @_Z4testv.catch(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* -; CHECK: %i = getelementptr inbounds %struct._Z4testv.ehdata, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: %i.i81 = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 0) +; CHECK: %i = bitcast i8* %i.i81 to i32* ; CHECK: %tmp7 = load i32, i32* %i, align 4 ; CHECK: call void @_Z10handle_inti(i32 %tmp7) ; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) diff --git a/test/CodeGen/WinEH/cppeh-frame-vars.ll b/test/CodeGen/WinEH/cppeh-frame-vars.ll index 4aafad0a9e8..3d3f5f67330 100644 --- a/test/CodeGen/WinEH/cppeh-frame-vars.ll +++ b/test/CodeGen/WinEH/cppeh-frame-vars.ll @@ -41,9 +41,6 @@ target triple = "x86_64-pc-windows-msvc" %rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } %struct.SomeData = type { i32, i32 } -; This structure should be declared for the frame allocation block. -; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, i32, [10 x i32], i32, %struct.SomeData } - $"\01??_R0H@8" = comdat any @"\01??_7type_info@@6B@" = external constant i8* @@ -52,19 +49,19 @@ $"\01??_R0H@8" = comdat any ; The function entry should be rewritten like this. ; CHECK: define void @"\01?test@@YAXXZ"() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 80) -; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAXXZ.ehdata"* -; CHECK-NOT: %NumExceptions = alloca i32, align 4 -; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 -; CHECK-NOT: %ExceptionVal = alloca [10 x i32], align 16 -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 -; CHECK-NOT: %Data = alloca %struct.SomeData, align 4 -; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 -; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %NumExceptions = alloca i32, align 4 +; CHECK: %ExceptionVal = alloca [10 x i32], align 16 +; CHECK: %Data = alloca %struct.SomeData, align 4 +; CHECK: %i = alloca i32, align 4 ; CHECK: %exn.slot = alloca i8* ; CHECK: %ehselector.slot = alloca i32 -; CHECK-NOT: %e = alloca i32, align 4 -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 +; CHECK: %e = alloca i32, align 4 +; CHECK: store i32 0, i32* %NumExceptions, align 4 +; CHECK: %tmp = bitcast %struct.SomeData* %Data to i8* +; CHECK: call void @llvm.memset(i8* %tmp, i8 0, i64 8, i32 4, i1 false) +; CHECK: store i32 0, i32* %i, align 4 +; CHECK: call void (...)* @llvm.frameescape(i32* %e, i32* %NumExceptions, [10 x i32]* %ExceptionVal, i32* %i, %struct.SomeData* %Data) +; CHECK: br label %for.cond ; Function Attrs: uwtable define void @"\01?test@@YAXXZ"() #0 { @@ -179,13 +176,16 @@ eh.resume: ; preds = %catch.dispatch ; The following catch handler should be outlined. ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAXXZ.ehdata"* -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 -; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 -; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 -; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 +; CHECK: %e.i81 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: %e = bitcast i8* %e.i81 to i32* +; CHECK: %NumExceptions.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: %NumExceptions = bitcast i8* %NumExceptions.i8 to i32* +; CHECK: %ExceptionVal.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: %ExceptionVal = bitcast i8* %ExceptionVal.i8 to [10 x i32]* +; CHECK: %i.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) +; CHECK: %i = bitcast i8* %i.i8 to i32* +; CHECK: %Data.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) +; CHECK: %Data = bitcast i8* %Data.i8 to %struct.SomeData* ; CHECK: %tmp11 = load i32, i32* %e, align 4 ; CHECK: %tmp12 = load i32, i32* %NumExceptions, align 4 ; CHECK: %idxprom = sext i32 %tmp12 to i64 diff --git a/test/CodeGen/WinEH/cppeh-inalloca.ll b/test/CodeGen/WinEH/cppeh-inalloca.ll index 13f3e6c9660..5595b0c82b6 100644 --- a/test/CodeGen/WinEH/cppeh-inalloca.ll +++ b/test/CodeGen/WinEH/cppeh-inalloca.ll @@ -38,18 +38,15 @@ $"\01??_R0H@8" = comdat any ; The function entry should be rewritten like this. ; CHECK: define i32 @"\01?test@@YAHUA@@@Z"(<{ %struct.A }>* inalloca) #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 24) -; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAHUA@@@Z.ehdata"* -; CHECK: %.tmp.reg2mem = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %.tmp.reg2mem = alloca <{ %struct.A }>* ; CHECK: %.tmp = select i1 true, <{ %struct.A }>* %0, <{ %struct.A }>* undef ; CHECK: store <{ %struct.A }>* %.tmp, <{ %struct.A }>** %.tmp.reg2mem -; CHECK-NOT: %retval = alloca i32, align 4 -; CHECK: %retval = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 4 +; CHECK: %retval = alloca i32, align 4 ; CHECK: %exn.slot = alloca i8* ; CHECK: %ehselector.slot = alloca i32 -; CHECK-NOT: %e = alloca i32, align 4 -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 2 -; CHECK: %cleanup.dest.slot = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %e = alloca i32, align 4 +; CHECK: %cleanup.dest.slot = alloca i32 +; CHECK: call void (...)* @llvm.frameescape(i32* %e, <{ %struct.A }>** %.tmp.reg2mem, i32* %retval, i32* %cleanup.dest.slot) ; CHECK: invoke void @"\01?may_throw@@YAXXZ"() ; CHECK: to label %invoke.cont unwind label %lpad @@ -132,13 +129,16 @@ eh.resume: ; preds = %ehcleanup ; The following catch handler should be outlined. ; CHECK: define internal i8* @"\01?test@@YAHUA@@@Z.catch"(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAHUA@@@Z.ehdata"* -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 2 -; CHECK: %eh.temp.alloca = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %e.i81 = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 0) +; CHECK: %e = bitcast i8* %e.i81 to i32* +; CHECK: %eh.temp.alloca.i8 = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 1) +; CHECK: %eh.temp.alloca = bitcast i8* %eh.temp.alloca.i8 to <{ %struct.A }>** ; CHECK: %.reload = load <{ %struct.A }>*, <{ %struct.A }>** %eh.temp.alloca -; CHECK: %retval = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 4 -; CHECK: %cleanup.dest.slot = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %retval.i8 = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 2) +; CHECK: %retval = bitcast i8* %retval.i8 to i32* +; CHECK: %cleanup.dest.slot.i8 = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 3) +; CHECK: %cleanup.dest.slot = bitcast i8* %cleanup.dest.slot.i8 to i32* +; CHECK: %e.i8 = bitcast i32* %e to i8* ; CHECK: %a = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %.reload, i32 0, i32 0 ; CHECK: %a1 = getelementptr inbounds %struct.A, %struct.A* %a, i32 0, i32 0 ; CHECK: %tmp8 = load i32, i32* %a1, align 4 @@ -149,7 +149,6 @@ eh.resume: ; preds = %ehcleanup ; CHECK: ret i8* blockaddress(@"\01?test@@YAHUA@@@Z", %cleanup) ; CHECK: } - declare void @"\01?may_throw@@YAXXZ"() #0 declare i32 @__CxxFrameHandler3(...) diff --git a/test/CodeGen/WinEH/cppeh-min-unwind.ll b/test/CodeGen/WinEH/cppeh-min-unwind.ll index a7b97385e90..abdbc3826c5 100644 --- a/test/CodeGen/WinEH/cppeh-min-unwind.ll +++ b/test/CodeGen/WinEH/cppeh-min-unwind.ll @@ -18,18 +18,16 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" -; This structure should be created for the frame allocation. -; CHECK: %struct._Z4testv.ehdata = type { i32, i8*, %class.SomeClass } - %class.SomeClass = type { [28 x i32] } ; The function entry should be rewritten like this. ; CHECK: define void @_Z4testv() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 128) -; CHECK: %eh.data = bitcast i8* %frame.alloc to %struct._Z4testv.ehdata* -; CHECK-NOT: %obj = alloca %class.SomeClass, align 4 -; CHECK: %obj = getelementptr inbounds %struct._Z4testv.ehdata, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: %obj = alloca %class.SomeClass, align 4 +; CHECK: call void @_ZN9SomeClassC1Ev(%class.SomeClass* %obj) +; CHECK: call void (...)* @llvm.frameescape(%class.SomeClass* %obj) +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont unwind label %lpad ; Function Attrs: uwtable define void @_Z4testv() #0 { @@ -66,9 +64,8 @@ eh.resume: ; preds = %lpad ; This cleanup handler should be outlined. ; CHECK: define internal void @_Z4testv.cleanup(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* -; CHECK: %obj = getelementptr inbounds %struct._Z4testv.ehdata, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: %obj.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 0) +; CHECK: %obj = bitcast i8* %obj.i8 to %class.SomeClass* ; CHECK: call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) ; CHECK: ret void ; CHECK: } diff --git a/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll b/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll index 354f4095486..0fa0fd0f6da 100644 --- a/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll +++ b/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll @@ -50,32 +50,28 @@ $"\01??_R0H@8" = comdat any @"\01??_7type_info@@6B@" = external constant i8* @"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat -; This structure should be declared for the frame allocation block. -; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, i32, [10 x i32], i32, i32*, i32* } - ; The function entry should be rewritten like this. ; CHECK: define void @"\01?test@@YAXXZ"() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 88) -; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAXXZ.ehdata"* -; CHECK-NOT: %ExceptionVal = alloca [10 x i32], align 16 -; CHECK: %NumExceptions.020.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 -; CHECK: %i.019.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 -; CHECK: %Data = alloca i64, align 8 -; CHECK: %tmpcast = bitcast i64* %Data to %struct.SomeData* -; CHECK: %0 = bitcast [10 x i32]* %ExceptionVal to i8* -; CHECK: call void @llvm.lifetime.start(i64 40, i8* %0) #1 -; CHECK: store i64 0, i64* %Data, align 8 -; CHECK: %a.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 -; CHECK: %a = bitcast i64* %Data to i32* -; CHECK: store i32* %a, i32** %a.reg2mem -; CHECK: %b.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 7 -; CHECK: %b = getelementptr inbounds %struct.SomeData, %struct.SomeData* %tmpcast, i64 0, i32 1 -; CHECK: store i32* %b, i32** %b.reg2mem -; CHECK: store i32 0, i32* %NumExceptions.020.reg2mem -; CHECK: store i32 0, i32* %i.019.reg2mem -; CHECK: br label %for.body +; CHECK: %NumExceptions.020.reg2mem = alloca i32 +; CHECK: %i.019.reg2mem = alloca i32 +; CHECK: %e = alloca i32, align 4 +; CHECK: %ExceptionVal = alloca [10 x i32], align 16 +; CHECK: %Data = alloca i64, align 8 +; CHECK: %tmpcast = bitcast i64* %Data to %struct.SomeData* +; CHECK: %0 = bitcast [10 x i32]* %ExceptionVal to i8* +; CHECK: call void @llvm.lifetime.start(i64 40, i8* %0) #1 +; CHECK: store i64 0, i64* %Data, align 8 +; CHECK: %a.reg2mem = alloca i32* +; CHECK: %a = bitcast i64* %Data to i32* +; CHECK: store i32* %a, i32** %a.reg2mem +; CHECK: %b.reg2mem = alloca i32* +; CHECK: %b = getelementptr inbounds %struct.SomeData, %struct.SomeData* %tmpcast, i64 0, i32 1 +; CHECK: store i32* %b, i32** %b.reg2mem +; CHECK: store i32 0, i32* %NumExceptions.020.reg2mem +; CHECK: store i32 0, i32* %i.019.reg2mem +; CHECK: call void (...)* @llvm.frameescape(i32* %e, i32* %NumExceptions.020.reg2mem, [10 x i32]* %ExceptionVal, i32* %i.019.reg2mem, i32** %a.reg2mem, i32** %b.reg2mem) +; CHECK: br label %for.body ; Function Attrs: uwtable define void @"\01?test@@YAXXZ"() #0 { @@ -187,17 +183,21 @@ eh.resume: ; preds = %lpad ; The following catch handler should be outlined. ; CHECK: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { ; CHECK: entry: -; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1) -; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAXXZ.ehdata"* -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 -; CHECK: %eh.temp.alloca = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %e.i84 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: %e = bitcast i8* %e.i84 to i32* +; CHECK: %eh.temp.alloca.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: %eh.temp.alloca = bitcast i8* %eh.temp.alloca.i8 to i32* ; CHECK: %NumExceptions.020.reload = load i32, i32* %eh.temp.alloca -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 -; CHECK: %eh.temp.alloca1 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %ExceptionVal.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: %ExceptionVal = bitcast i8* %ExceptionVal.i8 to [10 x i32]* +; CHECK: %eh.temp.alloca1.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) +; CHECK: %eh.temp.alloca1 = bitcast i8* %eh.temp.alloca1.i8 to i32* ; CHECK: %i.019.reload = load i32, i32* %eh.temp.alloca1 -; CHECK: %eh.temp.alloca2 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 +; CHECK: %eh.temp.alloca2.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) +; CHECK: %eh.temp.alloca2 = bitcast i8* %eh.temp.alloca2.i8 to i32** ; CHECK: %a.reload = load i32*, i32** %eh.temp.alloca2 -; CHECK: %eh.temp.alloca3 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 7 +; CHECK: %eh.temp.alloca3.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 5) +; CHECK: %eh.temp.alloca3 = bitcast i8* %eh.temp.alloca3.i8 to i32** ; CHECK: %b.reload = load i32*, i32** %eh.temp.alloca3 ; CHECK: %e.i8 = bitcast i32* %e to i8* ; CHECK: %tmp8 = load i32, i32* %e, align 4, !tbaa !7 diff --git a/test/CodeGen/X86/frameallocate.ll b/test/CodeGen/X86/frameallocate.ll deleted file mode 100644 index 2172ac02420..00000000000 --- a/test/CodeGen/X86/frameallocate.ll +++ /dev/null @@ -1,43 +0,0 @@ -; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s - -declare i8* @llvm.frameallocate(i32) -declare i8* @llvm.frameaddress(i32) -declare i8* @llvm.framerecover(i8*, i8*) -declare i32 @printf(i8*, ...) - -@str = internal constant [10 x i8] c"asdf: %d\0A\00" - -define void @print_framealloc_from_fp(i8* %fp) { - %alloc = call i8* @llvm.framerecover(i8* bitcast (void(i32*, i32*)* @alloc_func to i8*), i8* %fp) - %alloc_i32 = bitcast i8* %alloc to i32* - %r = load i32, i32* %alloc_i32 - call i32 (i8*, ...)* @printf(i8* getelementptr ([10 x i8]* @str, i32 0, i32 0), i32 %r) - ret void -} - -; CHECK-LABEL: print_framealloc_from_fp: -; CHECK: movabsq $.Lframeallocation_alloc_func, %[[offs:[a-z]+]] -; CHECK: movl (%rcx,%[[offs]]), %edx -; CHECK: leaq {{.*}}(%rip), %rcx -; CHECK: callq printf -; CHECK: retq - -define void @alloc_func(i32* %s, i32* %d) { - %alloc = call i8* @llvm.frameallocate(i32 16) - %alloc_i32 = bitcast i8* %alloc to i32* - store i32 42, i32* %alloc_i32 - %fp = call i8* @llvm.frameaddress(i32 0) - call void @print_framealloc_from_fp(i8* %fp) - ret void -} - -; CHECK-LABEL: alloc_func: -; CHECK: subq $48, %rsp -; CHECK: .seh_stackalloc 48 -; CHECK: leaq 48(%rsp), %rbp -; CHECK: .seh_setframe 5, 48 -; CHECK: .Lframeallocation_alloc_func = -[[offs:[0-9]+]] -; CHECK: movl $42, -[[offs]](%rbp) -; CHECK: leaq -48(%rbp), %rcx -; CHECK: callq print_framealloc_from_fp -; CHECK: retq diff --git a/test/CodeGen/X86/frameescape.ll b/test/CodeGen/X86/frameescape.ll new file mode 100644 index 00000000000..621dbe2d276 --- /dev/null +++ b/test/CodeGen/X86/frameescape.ll @@ -0,0 +1,63 @@ +; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s + +declare void @llvm.frameescape(...) +declare i8* @llvm.frameaddress(i32) +declare i8* @llvm.framerecover(i8*, i8*, i32) +declare i32 @printf(i8*, ...) + +@str = internal constant [10 x i8] c"asdf: %d\0A\00" + +define void @print_framealloc_from_fp(i8* %fp) { + %a.i8 = call i8* @llvm.framerecover(i8* bitcast (void()* @alloc_func to i8*), i8* %fp, i32 0) + %a = bitcast i8* %a.i8 to i32* + %a.val = load i32, i32* %a + call i32 (i8*, ...)* @printf(i8* getelementptr ([10 x i8]* @str, i32 0, i32 0), i32 %a.val) + %b.i8 = call i8* @llvm.framerecover(i8* bitcast (void()* @alloc_func to i8*), i8* %fp, i32 1) + %b = bitcast i8* %b.i8 to i32* + %b.val = load i32, i32* %b + call i32 (i8*, ...)* @printf(i8* getelementptr ([10 x i8]* @str, i32 0, i32 0), i32 %b.val) + ret void +} + +; CHECK-LABEL: print_framealloc_from_fp: +; CHECK: movq %rcx, %[[parent_fp:[a-z]+]] +; CHECK: movabsq $.Lalloc_func$frame_escape_0, %[[offs:[a-z]+]] +; CHECK: movl (%[[parent_fp]],%[[offs]]), %edx +; CHECK: leaq {{.*}}(%rip), %[[str:[a-z]+]] +; CHECK: movq %[[str]], %rcx +; CHECK: callq printf +; CHECK: movabsq $.Lalloc_func$frame_escape_1, %[[offs:[a-z]+]] +; CHECK: movl (%[[parent_fp]],%[[offs]]), %edx +; CHECK: movq %[[str]], %rcx +; CHECK: callq printf +; CHECK: retq + +define void @alloc_func() { + %a = alloca i32 + %b = alloca i32 + call void (...)* @llvm.frameescape(i32* %a, i32* %b) + store i32 42, i32* %a + store i32 13, i32* %b + %fp = call i8* @llvm.frameaddress(i32 0) + call void @print_framealloc_from_fp(i8* %fp) + ret void +} + +; CHECK-LABEL: alloc_func: +; CHECK: subq $48, %rsp +; CHECK: .seh_stackalloc 48 +; CHECK: leaq 48(%rsp), %rbp +; CHECK: .seh_setframe 5, 48 +; CHECK: .Lalloc_func$frame_escape_0 = 44 +; CHECK: .Lalloc_func$frame_escape_1 = 40 +; CHECK: movl $42, -4(%rbp) +; CHECK: movl $13, -8(%rbp) +; CHECK: leaq -48(%rbp), %rcx +; CHECK: callq print_framealloc_from_fp +; CHECK: retq + +; Helper to make this a complete program so it can be compiled and tested. +define i32 @main() { + call void @alloc_func() + ret i32 0 +} diff --git a/test/Verifier/frameallocate.ll b/test/Verifier/frameallocate.ll deleted file mode 100644 index e3018db1527..00000000000 --- a/test/Verifier/frameallocate.ll +++ /dev/null @@ -1,48 +0,0 @@ -; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s - -declare i8* @llvm.frameallocate(i32) -declare i8* @llvm.framerecover(i8*, i8*) - -define internal void @f() { - call i8* @llvm.frameallocate(i32 4) - call i8* @llvm.frameallocate(i32 4) - ret void -} -; CHECK: multiple calls to llvm.frameallocate in one function - -define internal void @f_a(i32 %n) { - call i8* @llvm.frameallocate(i32 %n) - ret void -} -; CHECK: llvm.frameallocate argument must be constant integer size - -define internal void @g() { -entry: - br label %not_entry -not_entry: - call i8* @llvm.frameallocate(i32 4) - ret void -} -; CHECK: llvm.frameallocate used outside of entry block - -define internal void @h() { - call i8* @llvm.framerecover(i8* null, i8* null) - ret void -} -; CHECK: llvm.framerecover first argument must be function defined in this module - -@global = constant i8 0 - -declare void @declaration() - -define internal void @i() { - call i8* @llvm.framerecover(i8* @global, i8* null) - ret void -} -; CHECK: llvm.framerecover first argument must be function defined in this module - -define internal void @j() { - call i8* @llvm.framerecover(i8* bitcast(void()* @declaration to i8*), i8* null) - ret void -} -; CHECK: llvm.framerecover first argument must be function defined in this module diff --git a/test/Verifier/frameescape.ll b/test/Verifier/frameescape.ll new file mode 100644 index 00000000000..54e0db4c400 --- /dev/null +++ b/test/Verifier/frameescape.ll @@ -0,0 +1,69 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @llvm.frameescape(...) +declare i8* @llvm.framerecover(i8*, i8*, i32) + +define internal void @f() { + %a = alloca i8 + call void (...)* @llvm.frameescape(i8* %a) + call void (...)* @llvm.frameescape(i8* %a) + ret void +} +; CHECK: multiple calls to llvm.frameescape in one function + +define internal void @g() { +entry: + %a = alloca i8 + br label %not_entry +not_entry: + call void (...)* @llvm.frameescape(i8* %a) + ret void +} +; CHECK: llvm.frameescape used outside of entry block + +define internal void @h() { + call i8* @llvm.framerecover(i8* null, i8* null, i32 0) + ret void +} +; CHECK: llvm.framerecover first argument must be function defined in this module + +@global = constant i8 0 + +declare void @declaration() + +define internal void @i() { + call i8* @llvm.framerecover(i8* @global, i8* null, i32 0) + ret void +} +; CHECK: llvm.framerecover first argument must be function defined in this module + +define internal void @j() { + call i8* @llvm.framerecover(i8* bitcast(void()* @declaration to i8*), i8* null, i32 0) + ret void +} +; CHECK: llvm.framerecover first argument must be function defined in this module + +define internal void @k(i32 %n) { + call i8* @llvm.framerecover(i8* bitcast(void()* @f to i8*), i8* null, i32 %n) + ret void +} +; CHECK: idx argument of llvm.framerecover must be a constant int + +define internal void @l(i8* %b) { + %a = alloca i8 + call void (...)* @llvm.frameescape(i8* %a, i8* %b) + ret void +} +; CHECK: llvm.frameescape only accepts static allocas + +define internal void @m() { + %a = alloca i8 + call void (...)* @llvm.frameescape(i8* %a) + ret void +} + +define internal void @n(i8* %fp) { + call i8* @llvm.framerecover(i8* bitcast(void ()* @m to i8*), i8* %fp, i32 1) + ret void +} +; CHECK: all indices passed to llvm.framerecover must be less than the number of arguments passed ot llvm.frameescape in the parent function |