From 80d0666040065a8f4092cbc11890ae65fb1894f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 24 Mar 2016 20:20:07 +0000 Subject: [PATCH] [Hexagon] Add support for run-time stack overflow checking Patch by Sundeep Kushwaha. llvm-svn: 264328 --- lib/Target/Hexagon/HexagonFrameLowering.cpp | 56 +++++++++++++++++---- lib/Target/Hexagon/HexagonFrameLowering.h | 4 +- lib/Target/Hexagon/HexagonInstrInfoV3.td | 3 ++ lib/Target/Hexagon/HexagonInstrInfoV4.td | 14 ++++++ test/CodeGen/Hexagon/runtime-stkchk.ll | 44 ++++++++++++++++ 5 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 test/CodeGen/Hexagon/runtime-stkchk.ll diff --git a/lib/Target/Hexagon/HexagonFrameLowering.cpp b/lib/Target/Hexagon/HexagonFrameLowering.cpp index 6ad82e8c21f..6ee2e8bef61 100644 --- a/lib/Target/Hexagon/HexagonFrameLowering.cpp +++ b/lib/Target/Hexagon/HexagonFrameLowering.cpp @@ -137,6 +137,10 @@ static cl::opt SpillFuncThresholdOs("spill-func-threshold-Os", cl::Hidden, cl::desc("Specify Os spill func threshold"), cl::init(1), cl::ZeroOrMore); +static cl::opt EnableStackOVFSanitizer("enable-stackovf-sanitizer", + cl::Hidden, cl::desc("Enable runtime checks for stack overflow."), + cl::init(false), cl::ZeroOrMore); + static cl::opt EnableShrinkWrapping("hexagon-shrink-frame", cl::init(true), cl::Hidden, cl::ZeroOrMore, cl::desc("Enable stack frame shrink wrapping")); @@ -387,6 +391,7 @@ void HexagonFrameLowering::findShrunkPrologEpilog(MachineFunction &MF, EpilogB = PDomB; } + /// Perform most of the PEI work here: /// - saving/restoring of the callee-saved registers, /// - stack frame creation and destruction. @@ -404,8 +409,9 @@ void HexagonFrameLowering::emitPrologue(MachineFunction &MF, if (EnableShrinkWrapping) findShrunkPrologEpilog(MF, PrologB, EpilogB); - insertCSRSpillsInBlock(*PrologB, CSI, HRI); - insertPrologueInBlock(*PrologB); + bool PrologueStubs = false; + insertCSRSpillsInBlock(*PrologB, CSI, HRI, PrologueStubs); + insertPrologueInBlock(*PrologB, PrologueStubs); if (EpilogB) { insertCSRRestoresInBlock(*EpilogB, CSI, HRI); @@ -422,7 +428,8 @@ void HexagonFrameLowering::emitPrologue(MachineFunction &MF, } -void HexagonFrameLowering::insertPrologueInBlock(MachineBasicBlock &MBB) const { +void HexagonFrameLowering::insertPrologueInBlock(MachineBasicBlock &MBB, + bool PrologueStubs) const { MachineFunction &MF = *MBB.getParent(); MachineFrameInfo *MFI = MF.getFrameInfo(); auto &HST = MF.getSubtarget(); @@ -497,6 +504,13 @@ void HexagonFrameLowering::insertPrologueInBlock(MachineBasicBlock &MBB) const { .addReg(SP) .addImm(-int64_t(MaxAlign)); } + + // If the stack-checking is enabled, and we spilled the callee-saved + // registers inline (i.e. did not use a spill function), then call + // the stack checker directly. + if (EnableStackOVFSanitizer && !PrologueStubs) + BuildMI(MBB, InsertPt, dl, HII.get(Hexagon::CALLstk)) + .addExternalSymbol("__runtime_stack_check"); } void HexagonFrameLowering::insertEpilogueInBlock(MachineBasicBlock &MBB) const { @@ -736,7 +750,7 @@ bool HexagonFrameLowering::hasFP(const MachineFunction &MF) const { return true; if (MFI.getStackSize() > 0) { - if (UseAllocframe) + if (EnableStackOVFSanitizer || UseAllocframe) return true; } @@ -754,8 +768,8 @@ enum SpillKind { SK_FromMemTailcall }; -static const char * -getSpillFunctionFor(unsigned MaxReg, SpillKind SpillType) { +static const char *getSpillFunctionFor(unsigned MaxReg, SpillKind SpillType, + bool Stkchk = false) { const char * V4SpillToMemoryFunctions[] = { "__save_r16_through_r17", "__save_r16_through_r19", @@ -764,6 +778,14 @@ getSpillFunctionFor(unsigned MaxReg, SpillKind SpillType) { "__save_r16_through_r25", "__save_r16_through_r27" }; + const char * V4SpillToMemoryStkchkFunctions[] = { + "__save_r16_through_r17_stkchk", + "__save_r16_through_r19_stkchk", + "__save_r16_through_r21_stkchk", + "__save_r16_through_r23_stkchk", + "__save_r16_through_r25_stkchk", + "__save_r16_through_r27_stkchk" }; + const char * V4SpillFromMemoryFunctions[] = { "__restore_r16_through_r17_and_deallocframe", "__restore_r16_through_r19_and_deallocframe", @@ -785,7 +807,8 @@ getSpillFunctionFor(unsigned MaxReg, SpillKind SpillType) { switch(SpillType) { case SK_ToMem: - SpillFunc = V4SpillToMemoryFunctions; + SpillFunc = Stkchk ? V4SpillToMemoryStkchkFunctions + : V4SpillToMemoryFunctions; break; case SK_FromMem: SpillFunc = V4SpillFromMemoryFunctions; @@ -913,24 +936,34 @@ int HexagonFrameLowering::getFrameIndexReference(const MachineFunction &MF, bool HexagonFrameLowering::insertCSRSpillsInBlock(MachineBasicBlock &MBB, - const CSIVect &CSI, const HexagonRegisterInfo &HRI) const { + const CSIVect &CSI, const HexagonRegisterInfo &HRI, + bool &PrologueStubs) const { if (CSI.empty()) return true; MachineBasicBlock::iterator MI = MBB.begin(); + PrologueStubs = false; MachineFunction &MF = *MBB.getParent(); auto &HII = *MF.getSubtarget().getInstrInfo(); if (useSpillFunction(MF, CSI)) { + PrologueStubs = true; unsigned MaxReg = getMaxCalleeSavedReg(CSI, HRI); - const char *SpillFun = getSpillFunctionFor(MaxReg, SK_ToMem); + bool StkOvrFlowEnabled = EnableStackOVFSanitizer; + const char *SpillFun = getSpillFunctionFor(MaxReg, SK_ToMem, + StkOvrFlowEnabled); auto &HTM = static_cast(MF.getTarget()); bool IsPIC = HTM.getRelocationModel() == Reloc::PIC_; // Call spill function. DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc(); - unsigned SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4_PIC - : Hexagon::SAVE_REGISTERS_CALL_V4; + unsigned SpillOpc; + if (StkOvrFlowEnabled) + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4STK_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4STK; + else + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4; MachineInstr *SaveRegsCall = BuildMI(MBB, MI, DL, HII.get(SpillOpc)) @@ -1007,6 +1040,7 @@ bool HexagonFrameLowering::insertCSRRestoresInBlock(MachineBasicBlock &MBB, int FI = CSI[i].getFrameIdx(); HII.loadRegFromStackSlot(MBB, MI, Reg, FI, RC, &HRI); } + return true; } diff --git a/lib/Target/Hexagon/HexagonFrameLowering.h b/lib/Target/Hexagon/HexagonFrameLowering.h index c9cae04cb30..dc6b0515f0a 100644 --- a/lib/Target/Hexagon/HexagonFrameLowering.h +++ b/lib/Target/Hexagon/HexagonFrameLowering.h @@ -84,10 +84,10 @@ private: void expandAlloca(MachineInstr *AI, const HexagonInstrInfo &TII, unsigned SP, unsigned CF) const; - void insertPrologueInBlock(MachineBasicBlock &MBB) const; + void insertPrologueInBlock(MachineBasicBlock &MBB, bool PrologueStubs) const; void insertEpilogueInBlock(MachineBasicBlock &MBB) const; bool insertCSRSpillsInBlock(MachineBasicBlock &MBB, const CSIVect &CSI, - const HexagonRegisterInfo &HRI) const; + const HexagonRegisterInfo &HRI, bool &PrologueStubs) const; bool insertCSRRestoresInBlock(MachineBasicBlock &MBB, const CSIVect &CSI, const HexagonRegisterInfo &HRI) const; void insertCFIInstructionsAt(MachineBasicBlock &MBB, diff --git a/lib/Target/Hexagon/HexagonInstrInfoV3.td b/lib/Target/Hexagon/HexagonInstrInfoV3.td index 84d035da451..ead3b41c891 100644 --- a/lib/Target/Hexagon/HexagonInstrInfoV3.td +++ b/lib/Target/Hexagon/HexagonInstrInfoV3.td @@ -67,6 +67,9 @@ defm J2_call: T_Calls<"">, PredRel; let isCodeGenOnly = 1, isCall = 1, hasSideEffects = 1, Defs = VolatileV3.Regs in def CALLv3nr : T_Call<"">, PredRel; +let isCodeGenOnly = 1, isCall = 1, hasSideEffects = 1, Defs = [PC, R31, R6, R7, P0] in +def CALLstk : T_Call<"">, PredRel; + //===----------------------------------------------------------------------===// // J - //===----------------------------------------------------------------------===// diff --git a/lib/Target/Hexagon/HexagonInstrInfoV4.td b/lib/Target/Hexagon/HexagonInstrInfoV4.td index f79837ad1c8..4bfc7e21f5b 100644 --- a/lib/Target/Hexagon/HexagonInstrInfoV4.td +++ b/lib/Target/Hexagon/HexagonInstrInfoV4.td @@ -3295,6 +3295,7 @@ let isCall = 1, isBarrier = 1, isReturn = 1, isTerminator = 1, // Restore registers and dealloc frame before a tail call. let isCall = 1, Defs = [R29, R30, R31, PC], isAsmParserOnly = 1 in { def RESTORE_DEALLOC_BEFORE_TAILCALL_V4 : T_Call<"">, PredRel; + let isExtended = 1, opExtendable = 0 in def RESTORE_DEALLOC_BEFORE_TAILCALL_V4_EXT : T_Call<"">, PredRel; @@ -3309,14 +3310,27 @@ let isCall = 1, Defs = [R29, R30, R31, PC], isAsmParserOnly = 1 in { // Save registers function call. let isCall = 1, Uses = [R29, R31], isAsmParserOnly = 1 in { def SAVE_REGISTERS_CALL_V4 : T_Call<"">, PredRel; + let isExtended = 1, opExtendable = 0 in def SAVE_REGISTERS_CALL_V4_EXT : T_Call<"">, PredRel; + let Defs = [P0] in + def SAVE_REGISTERS_CALL_V4STK : T_Call<"">, PredRel; + + let Defs = [P0], isExtended = 1, opExtendable = 0 in + def SAVE_REGISTERS_CALL_V4STK_EXT : T_Call<"">, PredRel; + let Defs = [R14, R15, R28] in def SAVE_REGISTERS_CALL_V4_PIC : T_Call<"">, PredRel; let Defs = [R14, R15, R28], isExtended = 1, opExtendable = 0 in def SAVE_REGISTERS_CALL_V4_EXT_PIC : T_Call<"">, PredRel; + + let Defs = [R14, R15, R28, P0] in + def SAVE_REGISTERS_CALL_V4STK_PIC : T_Call<"">, PredRel; + + let Defs = [R14, R15, R28, P0], isExtended = 1, opExtendable = 0 in + def SAVE_REGISTERS_CALL_V4STK_EXT_PIC : T_Call<"">, PredRel; } //===----------------------------------------------------------------------===// diff --git a/test/CodeGen/Hexagon/runtime-stkchk.ll b/test/CodeGen/Hexagon/runtime-stkchk.ll new file mode 100644 index 00000000000..a4e8f117679 --- /dev/null +++ b/test/CodeGen/Hexagon/runtime-stkchk.ll @@ -0,0 +1,44 @@ +; RUN: llc -march=hexagon -mcpu=hexagonv55 -enable-stackovf-sanitizer < %s | FileCheck %s + +; CHECK-LABEL: foo_1 +; CHECK: __runtime_stack_check +define i32 @foo_1(i32 %n) #0 { +entry: + %local = alloca [1024 x i32], align 8 + %0 = bitcast [1024 x i32]* %local to i8* + call void @llvm.lifetime.start(i64 4096, i8* %0) #1 + %arraydecay = getelementptr inbounds [1024 x i32], [1024 x i32]* %local, i32 0, i32 0 + call void @baz_1(i32* %arraydecay) #3 + %arrayidx = getelementptr inbounds [1024 x i32], [1024 x i32]* %local, i32 0, i32 %n + %1 = load i32, i32* %arrayidx, align 4 + call void @llvm.lifetime.end(i64 4096, i8* %0) #1 + ret i32 %1 +} + +; CHECK-LABEL: foo_2 +; CHECK: __save_r16_through_r19_stkchk +define i32 @foo_2(i32 %n, i32* %y) #0 { +entry: + %local = alloca [2048 x i32], align 8 + %0 = bitcast [2048 x i32]* %local to i8* + call void @llvm.lifetime.start(i64 8192, i8* %0) #1 + %arraydecay = getelementptr inbounds [2048 x i32], [2048 x i32]* %local, i32 0, i32 0 + call void @baz_2(i32* %y, i32* %arraydecay) #3 + %1 = load i32, i32* %y, align 4 + %add = add nsw i32 %n, %1 + %arrayidx = getelementptr inbounds [2048 x i32], [2048 x i32]* %local, i32 0, i32 %add + %2 = load i32, i32* %arrayidx, align 4 + call void @llvm.lifetime.end(i64 8192, i8* %0) #1 + ret i32 %2 +} + +declare void @baz_1(i32*) #2 +declare void @baz_2(i32*, i32*) #2 +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +attributes #0 = { nounwind optsize "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { optsize "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { optsize } +