summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/docs/LangRef.rst50
-rw-r--r--llvm/include/llvm/CodeGen/AsmPrinter.h2
-rw-r--r--llvm/include/llvm/CodeGen/ISDOpcodes.h5
-rw-r--r--llvm/include/llvm/CodeGen/MachineFrameInfo.h4
-rw-r--r--llvm/include/llvm/IR/Intrinsics.td4
-rw-r--r--llvm/include/llvm/MC/MCContext.h2
-rw-r--r--llvm/include/llvm/Target/Target.td9
-rw-r--r--llvm/include/llvm/Target/TargetFrameLowering.h4
-rw-r--r--llvm/include/llvm/Target/TargetOpcodes.h7
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp14
-rw-r--r--llvm/lib/CodeGen/DeadMachineInstructionElim.cpp4
-rw-r--r--llvm/lib/CodeGen/MachineFunction.cpp8
-rw-r--r--llvm/lib/CodeGen/PrologEpilogInserter.cpp11
-rw-r--r--llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp53
-rw-r--r--llvm/lib/IR/Verifier.cpp29
-rw-r--r--llvm/lib/MC/MCContext.cpp5
-rw-r--r--llvm/lib/Target/X86/X86InstrCompiler.td3
-rw-r--r--llvm/lib/Target/X86/X86InstrInfo.td4
-rw-r--r--llvm/test/CodeGen/X86/frameallocate.ll39
-rw-r--r--llvm/test/Verifier/frameallocate.ll48
-rw-r--r--llvm/utils/TableGen/CodeGenTarget.cpp2
21 files changed, 303 insertions, 4 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index b3f97de25b4..1735a8920a5 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -7281,6 +7281,56 @@ 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.recoverframeallocation``' Intrinsics
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+ declare i8* @llvm.frameallocate(i32 %size)
+ declare i8* @llvm.recoverframeallocation(i8* %func, i8* %fp)
+
+Overview:
+"""""""""
+
+The '``llvm.frameallocate``' intrinsic allocates stack memory at some fixed
+offset from the frame pointer, and the '``llvm.recoverframeallocation``'
+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``.
+
+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.
+
+The ``func`` argument to '``llvm.recoverframeallocation``' must be a constant
+bitcasted pointer to a function defined in the current module. The code
+generator cannot determine the frame allocation offset of functions defined in
+other modules.
+
+The ``fp`` argument to '``llvm.recoverframeallocation``' must be a frame
+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.
+
+Semantics:
+""""""""""
+
+These intrinsics allow a group of functions to access one stack memory
+allocation in an ancestor stack frame. The memory returned from
+'``llvm.frameallocate``' may be allocated prior to stack realignment, so the
+memory is only aligned to the ABI-required stack alignment. Each function may
+only call '``llvm.frameallocate``' one or zero times from the function entry
+block. The frame allocation intrinsic inhibits inlining, as any frame
+allocations in the inlined function frame are likely to be at a different
+offset from the one used by '``llvm.recoverframeallocation``' called with the
+uninlined function.
+
.. _int_read_register:
.. _int_write_register:
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 4f2c9495a49..e3ce57ad185 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -205,6 +205,8 @@ public:
void emitCFIInstruction(const MachineInstr &MI);
+ void emitFrameAlloc(const MachineInstr &MI);
+
enum CFIMoveType { CFI_M_None, CFI_M_EH, CFI_M_Debug };
CFIMoveType needsCFIMoves();
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 4f55ccac16e..cbd1015975f 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -72,6 +72,11 @@ namespace ISD {
/// the parent's frame or return address, and so on.
FRAMEADDR, RETURNADDR,
+ /// RECOVER_FRAME_ALLOC - Represents the llvm.recoverframeallocation
+ /// intrinsic. Materializes the offset from the frame pointer of another
+ /// function to the result of llvm.frameallocate.
+ RECOVER_FRAME_ALLOC,
+
/// READ_REGISTER, WRITE_REGISTER - This node represents llvm.register on
/// the DAG, which implements the named register global variables extension.
READ_REGISTER,
diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index 1e7fee666fc..667736021f9 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -516,6 +516,10 @@ public:
/// on the stack. Returns an index with a negative value.
int CreateFixedSpillStackObject(uint64_t Size, int64_t SPOffset);
+ /// Allocates memory at a fixed, target-specific offset from the frame
+ /// pointer. Marks the function as having its frame address taken.
+ int CreateFrameAllocation(uint64_t Size);
+
/// isFixedObjectIndex - Returns true if the specified index corresponds to a
/// fixed stack object.
bool isFixedObjectIndex(int ObjectIdx) const {
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 1f8e163d81d..01f7cfeaefd 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -259,6 +259,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_recoverframeallocation : Intrinsic<[llvm_ptr_ty],
+ [llvm_ptr_ty, llvm_ptr_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/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index f209448d1bf..e3163a7946a 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -237,6 +237,8 @@ namespace llvm {
MCSymbol *getOrCreateSectionSymbol(const MCSectionELF &Section);
+ MCSymbol *getOrCreateFrameAllocSymbol(StringRef FuncName);
+
/// LookupSymbol - Get the symbol for \p Name, or null.
MCSymbol *LookupSymbol(StringRef Name) const;
MCSymbol *LookupSymbol(const Twine &Name) const;
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 7a5ae092778..6c970d0c19d 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -865,6 +865,15 @@ def LOAD_STACK_GUARD : Instruction {
let hasSideEffects = 0;
bit isPseudo = 1;
}
+def FRAME_ALLOC : Instruction {
+ // This instruction is really just a label. It has to be part of the chain so
+ // that it doesn't get dropped from the DAG, but it produces nothing and has
+ // no side effects.
+ let OutOperandList = (outs);
+ let InOperandList = (ins ptr_rc:$symbol, i32imm:$id);
+ let hasSideEffects = 0;
+ let hasCtrlDep = 1;
+}
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/Target/TargetFrameLowering.h b/llvm/include/llvm/Target/TargetFrameLowering.h
index 82c6093d8fb..277bd98d371 100644
--- a/llvm/include/llvm/Target/TargetFrameLowering.h
+++ b/llvm/include/llvm/Target/TargetFrameLowering.h
@@ -142,6 +142,10 @@ public:
/// the assembly prologue to explicitly handle the stack.
virtual void adjustForHiPEPrologue(MachineFunction &MF) const { }
+ /// Adjust the prologue to add an allocation at a fixed offset from the frame
+ /// pointer.
+ virtual void adjustForFrameAllocatePrologue(MachineFunction &MF) const { }
+
/// spillCalleeSavedRegisters - Issues instruction(s) to spill all callee
/// saved registers and returns true if it isn't possible / profitable to do
/// so by issuing a series of store instructions via
diff --git a/llvm/include/llvm/Target/TargetOpcodes.h b/llvm/include/llvm/Target/TargetOpcodes.h
index 7ab1bc3837f..afc22365eba 100644
--- a/llvm/include/llvm/Target/TargetOpcodes.h
+++ b/llvm/include/llvm/Target/TargetOpcodes.h
@@ -116,7 +116,12 @@ enum {
/// of live pointers for relocation by the garbage collector. It is
/// intended to support garbage collection with fully precise relocating
/// collectors and deoptimizations in either the callee or caller.
- STATEPOINT = 20
+ STATEPOINT = 20,
+
+ /// Instruction that records the offset of a function's frame allocation in a
+ /// label. Created by the llvm.frameallocate intrinsic. It has two arguments:
+ /// the symbol for the label and the frame index of the stack allocation.
+ FRAME_ALLOC = 21,
};
} // end namespace TargetOpcode
} // end namespace llvm
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 461b6d272da..bbed8081a14 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -748,6 +748,16 @@ void AsmPrinter::emitCFIInstruction(const MachineInstr &MI) {
emitCFIInstruction(CFI);
}
+void AsmPrinter::emitFrameAlloc(const MachineInstr &MI) {
+ // The operands are the MCSymbol and the frame offset of the allocation.
+ MCSymbol *FrameAllocSym = MI.getOperand(0).getMCSymbol();
+ int FrameOffset = MI.getOperand(1).getImm();
+
+ // Emit a symbol assignment.
+ OutStreamer.EmitAssignment(FrameAllocSym,
+ MCConstantExpr::Create(FrameOffset, OutContext));
+}
+
/// EmitFunctionBody - This method emits the body and trailer for a
/// function.
void AsmPrinter::EmitFunctionBody() {
@@ -786,6 +796,10 @@ void AsmPrinter::EmitFunctionBody() {
emitCFIInstruction(MI);
break;
+ case TargetOpcode::FRAME_ALLOC:
+ emitFrameAlloc(MI);
+ break;
+
case TargetOpcode::EH_LABEL:
case TargetOpcode::GC_LABEL:
OutStreamer.EmitLabel(MI.getOperand(0).getMCSymbol());
diff --git a/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp b/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp
index 48213c12e04..c17a35d1c73 100644
--- a/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp
+++ b/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp
@@ -59,6 +59,10 @@ bool DeadMachineInstructionElim::isDead(const MachineInstr *MI) const {
if (MI->isInlineAsm())
return false;
+ // Don't delete frame allocation labels.
+ if (MI->getOpcode() == TargetOpcode::FRAME_ALLOC)
+ return false;
+
// Don't delete instructions with side effects.
bool SawStore = false;
if (!MI->isSafeToMove(TII, nullptr, SawStore) && !MI->isPHI())
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index 8a2b610948b..6b4cba6c678 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -587,6 +587,14 @@ int MachineFrameInfo::CreateFixedSpillStackObject(uint64_t Size,
return -++NumFixedObjects;
}
+int MachineFrameInfo::CreateFrameAllocation(uint64_t Size) {
+ // Force the use of a frame pointer. The intention is that this intrinsic be
+ // used in conjunction with unwind mechanisms that leak the frame pointer.
+ setFrameAddressIsTaken(true);
+ Size = RoundUpToAlignment(Size, StackAlignment);
+ return CreateStackObject(Size, StackAlignment, false);
+}
+
BitVector
MachineFrameInfo::getPristineRegs(const MachineBasicBlock *MBB) const {
assert(MBB && "MBB must be valid");
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index f5f32abc0e4..385e5a35afb 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -817,6 +817,17 @@ 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/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index e1396649f34..8a762555577 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -48,6 +48,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Statepoint.h"
+#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -5581,6 +5582,58 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
}
case Intrinsic::instrprof_increment:
llvm_unreachable("instrprof failed to lower an increment");
+
+ case Intrinsic::frameallocate: {
+ 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);
+
+ return nullptr;
+ }
+
+ case Intrinsic::recoverframeallocation: {
+ // i8* @llvm.recoverframeallocation(i8* %fn, i8* %fp)
+ 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());
+ MCSymbol *FrameAllocSym =
+ MF.getMMI().getContext().getOrCreateFrameAllocSymbol(Fn->getName());
+
+ // 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");
+ SDValue OffsetSym = DAG.getTargetExternalSymbol(Name.data(), PtrVT);
+ SDValue OffsetVal =
+ DAG.getNode(ISD::RECOVER_FRAME_ALLOC, sdl, PtrVT, OffsetSym);
+
+ // Add the offset to the FP.
+ Value *FP = I.getArgOperand(1);
+ SDValue FPVal = getValue(FP);
+ SDValue Add = DAG.getNode(ISD::ADD, sdl, PtrVT, FPVal, OffsetVal);
+ setValue(&I, Add);
+
+ return nullptr;
+ }
}
}
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index ad7473b6611..4a98b546099 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -198,9 +198,14 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
/// personality function.
const Value *PersonalityFn;
+ /// \brief Whether we've seen a call to @llvm.frameallocate in this function
+ /// already.
+ bool SawFrameAllocate;
+
public:
explicit Verifier(raw_ostream &OS = dbgs())
- : VerifierSupport(OS), Context(nullptr), PersonalityFn(nullptr) {}
+ : VerifierSupport(OS), Context(nullptr), PersonalityFn(nullptr),
+ SawFrameAllocate(false) {}
bool verify(const Function &F) {
M = F.getParent();
@@ -235,6 +240,7 @@ public:
visit(const_cast<Function &>(F));
InstsInThisBlock.clear();
PersonalityFn = nullptr;
+ SawFrameAllocate = false;
return !Broken;
}
@@ -2599,7 +2605,26 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) {
Assert1(isa<ConstantInt>(CI.getArgOperand(1)),
"llvm.invariant.end parameter #2 must be a constant integer", &CI);
break;
-
+
+ case Intrinsic::frameallocate: {
+ 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);
+ break;
+ }
+ case Intrinsic::recoverframeallocation: {
+ Value *FnArg = CI.getArgOperand(0)->stripPointerCasts();
+ Function *Fn = dyn_cast<Function>(FnArg);
+ Assert1(Fn && !Fn->isDeclaration(), "llvm.recoverframeallocation first "
+ "argument must be function defined in this module", &CI);
+ break;
+ }
+
case Intrinsic::experimental_gc_statepoint: {
Assert1(!CI.doesNotAccessMemory() &&
!CI.onlyReadsMemory(),
diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp
index 8630b25a84e..b41e6d8201d 100644
--- a/llvm/lib/MC/MCContext.cpp
+++ b/llvm/lib/MC/MCContext.cpp
@@ -130,6 +130,11 @@ MCSymbol *MCContext::getOrCreateSectionSymbol(const MCSectionELF &Section) {
return Sym;
}
+MCSymbol *MCContext::getOrCreateFrameAllocSymbol(StringRef FuncName) {
+ return GetOrCreateSymbol(Twine(MAI->getPrivateGlobalPrefix()) +
+ "frameallocation_" + FuncName);
+}
+
MCSymbol *MCContext::CreateSymbol(StringRef Name) {
// Determine whether this is an assembler temporary or normal label, if used.
bool isTemporary = false;
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index ad79c9eedf2..ed0a6346929 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1010,6 +1010,9 @@ def : Pat<(store (i64 (X86Wrapper tblockaddress:$src)), addr:$dst),
(MOV64mi32 addr:$dst, tblockaddress:$src)>,
Requires<[NearData, IsStatic]>;
+def : Pat<(i32 (X86RecoverFrameAlloc texternalsym:$dst)), (MOV32ri texternalsym:$dst)>;
+def : Pat<(i64 (X86RecoverFrameAlloc texternalsym:$dst)), (MOV64ri texternalsym:$dst)>;
+
// Calls
// tls has some funny stuff here...
diff --git a/llvm/lib/Target/X86/X86InstrInfo.td b/llvm/lib/Target/X86/X86InstrInfo.td
index 4903a730955..59301f9837d 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.td
+++ b/llvm/lib/Target/X86/X86InstrInfo.td
@@ -194,6 +194,10 @@ def X86rdpmc : SDNode<"X86ISD::RDPMC_DAG", SDTX86Void,
def X86Wrapper : SDNode<"X86ISD::Wrapper", SDTX86Wrapper>;
def X86WrapperRIP : SDNode<"X86ISD::WrapperRIP", SDTX86Wrapper>;
+def X86RecoverFrameAlloc : SDNode<"ISD::RECOVER_FRAME_ALLOC",
+ SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
+ SDTCisInt<1>]>>;
+
def X86tlsaddr : SDNode<"X86ISD::TLSADDR", SDT_X86TLSADDR,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
diff --git a/llvm/test/CodeGen/X86/frameallocate.ll b/llvm/test/CodeGen/X86/frameallocate.ll
new file mode 100644
index 00000000000..4adcf688a63
--- /dev/null
+++ b/llvm/test/CodeGen/X86/frameallocate.ll
@@ -0,0 +1,39 @@
+; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s
+
+declare i8* @llvm.frameallocate(i32)
+declare i8* @llvm.frameaddress(i32)
+declare i8* @llvm.recoverframeallocation(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.recoverframeallocation(i8* bitcast (void(i32*, i32*)* @alloc_func to i8*), i8* %fp)
+ %alloc_i32 = bitcast i8* %alloc to i32*
+ %r = load 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: .Lframeallocation_alloc_func = -[[offs:[0-9]+]]
+; CHECK: movl $42, -[[offs]](%rbp)
+; CHECK: movq %rbp, %rcx
+; CHECK: callq print_framealloc_from_fp
+; CHECK: retq
diff --git a/llvm/test/Verifier/frameallocate.ll b/llvm/test/Verifier/frameallocate.ll
new file mode 100644
index 00000000000..3a4258007fd
--- /dev/null
+++ b/llvm/test/Verifier/frameallocate.ll
@@ -0,0 +1,48 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+declare i8* @llvm.frameallocate(i32)
+declare i8* @llvm.recoverframeallocation(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.recoverframeallocation(i8* null, i8* null)
+ ret void
+}
+; CHECK: llvm.recoverframeallocation first argument must be function defined in this module
+
+@global = constant i8 0
+
+declare void @declaration()
+
+define internal void @i() {
+ call i8* @llvm.recoverframeallocation(i8* @global, i8* null)
+ ret void
+}
+; CHECK: llvm.recoverframeallocation first argument must be function defined in this module
+
+define internal void @j() {
+ call i8* @llvm.recoverframeallocation(i8* bitcast(void()* @declaration to i8*), i8* null)
+ ret void
+}
+; CHECK: llvm.recoverframeallocation first argument must be function defined in this module
diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp
index c4d30e924b6..e727a0e3f7a 100644
--- a/llvm/utils/TableGen/CodeGenTarget.cpp
+++ b/llvm/utils/TableGen/CodeGenTarget.cpp
@@ -295,7 +295,7 @@ void CodeGenTarget::ComputeInstrsByEnum() const {
"IMPLICIT_DEF", "SUBREG_TO_REG", "COPY_TO_REGCLASS", "DBG_VALUE",
"REG_SEQUENCE", "COPY", "BUNDLE", "LIFETIME_START",
"LIFETIME_END", "STACKMAP", "PATCHPOINT", "LOAD_STACK_GUARD",
- "STATEPOINT",
+ "STATEPOINT", "FRAME_ALLOC",
nullptr};
const auto &Insts = getInstructions();
for (const char *const *p = FixedInstrs; *p; ++p) {
OpenPOWER on IntegriCloud