mirror of
https://github.com/RPCS3/llvm.git
synced 2024-12-01 07:30:33 +00:00
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
This commit is contained in:
parent
8b4cc5e616
commit
9f7c861416
@ -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:
|
||||
""""""""""
|
||||
|
||||
|
@ -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(); }
|
||||
|
@ -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],
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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 : 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;
|
||||
// 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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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: }
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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(...)
|
||||
|
@ -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: }
|
||||
|
@ -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
|
||||
|
@ -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
|
63
test/CodeGen/X86/frameescape.ll
Normal file
63
test/CodeGen/X86/frameescape.ll
Normal file
@ -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
|
||||
}
|
@ -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
|
69
test/Verifier/frameescape.ll
Normal file
69
test/Verifier/frameescape.ll
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user