Index: include/llvm/Target/TargetMachine.h =================================================================== --- include/llvm/Target/TargetMachine.h +++ include/llvm/Target/TargetMachine.h @@ -201,6 +201,9 @@ bool getO0WantsFastISel() { return O0WantsFastISel; } void setO0WantsFastISel(bool Enable) { O0WantsFastISel = Enable; } void setGlobalISel(bool Enable) { Options.EnableGlobalISel = Enable; } + void setGlobalISelAbort(GlobalISelAbortMode Mode) { + Options.GlobalISelAbort = Mode; + } void setMachineOutliner(bool Enable) { Options.EnableMachineOutliner = Enable; } Index: include/llvm/Target/TargetOptions.h =================================================================== --- include/llvm/Target/TargetOptions.h +++ include/llvm/Target/TargetOptions.h @@ -96,6 +96,14 @@ SCE // Tune debug info for SCE targets (e.g. PS4). }; + /// Enable abort calls when global instruction selection fails to lower/select + /// an instruction. + enum class GlobalISelAbortMode { + Disable, // Disable the abort. + Enable, // Enable the abort. + DisableWithDiag // Disable the abort but emit a diagnostic on failure. + }; + class TargetOptions { public: TargetOptions() @@ -192,6 +200,10 @@ /// EnableGlobalISel - This flag enables global instruction selection. unsigned EnableGlobalISel : 1; + // EnableGlobalISelAbort - Control abort behaviour when global instruction + // selection fails to lower/select an instruction. + GlobalISelAbortMode GlobalISelAbort = GlobalISelAbortMode::Enable; + /// UseInitArray - Use .init_array instead of .ctors for static /// constructors. unsigned UseInitArray : 1; Index: lib/CodeGen/StackProtector.cpp =================================================================== --- lib/CodeGen/StackProtector.cpp +++ lib/CodeGen/StackProtector.cpp @@ -199,6 +199,18 @@ return false; } +/// Search for the first call to the llvm.stackprotector intrinsic and return it +/// if present. +static const CallInst *findStackProtectorIntrinsic(Function &F) { + for (const BasicBlock &BB : F) + for (const Instruction &I : BB) + if (const CallInst *CI = dyn_cast(&I)) + if (CI->getCalledFunction() == + Intrinsic::getDeclaration(F.getParent(), Intrinsic::stackprotector)) + return CI; + return nullptr; +} + /// Check whether or not this function needs a stack protector based /// upon the stack protector level. /// @@ -215,13 +227,7 @@ bool StackProtector::RequiresStackProtector() { bool Strong = false; bool NeedsProtector = false; - for (const BasicBlock &BB : *F) - for (const Instruction &I : BB) - if (const CallInst *CI = dyn_cast(&I)) - if (CI->getCalledFunction() == - Intrinsic::getDeclaration(F->getParent(), - Intrinsic::stackprotector)) - HasPrologue = true; + HasPrologue = findStackProtectorIntrinsic(*F); if (F->hasFnAttribute(Attribute::SafeStack)) return false; @@ -379,7 +385,8 @@ // protection in SDAG. bool SupportsSelectionDAGSP = TLI->useStackGuardXorFP() || - (EnableSelectionDAGSP && !TM->Options.EnableFastISel); + (EnableSelectionDAGSP && !TM->Options.EnableFastISel && + !TM->Options.EnableGlobalISel); AllocaInst *AI = nullptr; // Place on stack that stores the stack guard. for (Function::iterator I = F->begin(), E = F->end(); I != E;) { @@ -399,6 +406,14 @@ if (SupportsSelectionDAGSP) break; + // Find the stack guard slot if the prologue was not created by this pass + // itself via a previous call to CreatePrologue(). + if (!AI) { + const CallInst *SPCall = findStackProtectorIntrinsic(*F); + assert(SPCall && "Call to llvm.stackprotector is missing"); + AI = cast(SPCall->getArgOperand(1)); + } + // Set HasIRCheck to true, so that SelectionDAG will not generate its own // version. SelectionDAG called 'shouldEmitSDCheck' to check whether // instrumentation has already been generated. Index: lib/CodeGen/TargetPassConfig.cpp =================================================================== --- lib/CodeGen/TargetPassConfig.cpp +++ lib/CodeGen/TargetPassConfig.cpp @@ -137,13 +137,15 @@ "print-machineinstrs", cl::ValueOptional, cl::desc("Print machine instrs"), cl::value_desc("pass-name"), cl::init("option-unspecified"), cl::Hidden); -static cl::opt EnableGlobalISelAbort( +static cl::opt EnableGlobalISelAbort( "global-isel-abort", cl::Hidden, cl::desc("Enable abort calls when \"global\" instruction selection " - "fails to lower/select an instruction: 0 disable the abort, " - "1 enable the abort, and " - "2 disable the abort but emit a diagnostic on failure"), - cl::init(1)); + "fails to lower/select an instruction"), + cl::values( + clEnumValN(GlobalISelAbortMode::Disable, "0", "Disable the abort"), + clEnumValN(GlobalISelAbortMode::Enable, "1", "Enable the abort"), + clEnumValN(GlobalISelAbortMode::DisableWithDiag, "2", + "Disable the abort but emit a diagnostic on failure"))); // Temporary option to allow experimenting with MachineScheduler as a post-RA // scheduler. Targets can "properly" enable this with @@ -384,6 +386,9 @@ if (TM.Options.EnableIPRA) setRequiresCodeGenSCCOrder(); + if (EnableGlobalISelAbort.getNumOccurrences()) + TM.Options.GlobalISelAbort = EnableGlobalISelAbort; + setStartStopPasses(); } @@ -721,8 +726,11 @@ // Enable FastISel with -fast-isel, but allow that to be overridden. TM->setO0WantsFastISel(EnableFastISelOption != cl::BOU_FALSE); if (EnableFastISelOption == cl::BOU_TRUE || - (TM->getOptLevel() == CodeGenOpt::None && TM->getO0WantsFastISel())) + (TM->getOptLevel() == CodeGenOpt::None && TM->getO0WantsFastISel() && + !TM->Options.EnableGlobalISel)) { TM->setFastISel(true); + TM->setGlobalISel(false); + } // Ask the target for an instruction selector. // Explicitly enabling fast-isel should override implicitly enabled @@ -730,6 +738,7 @@ if (EnableGlobalISelOption == cl::BOU_TRUE || (EnableGlobalISelOption == cl::BOU_UNSET && TM->Options.EnableGlobalISel && EnableFastISelOption != cl::BOU_TRUE)) { + TM->setGlobalISel(true); TM->setFastISel(false); SaveAndRestore SavedAddingMachinePasses(AddingMachinePasses, true); @@ -1165,14 +1174,9 @@ /// GlobalISel Configuration //===---------------------------------------------------------------------===// bool TargetPassConfig::isGlobalISelAbortEnabled() const { - if (EnableGlobalISelAbort.getNumOccurrences() > 0) - return EnableGlobalISelAbort == 1; - - // When no abort behaviour is specified, we don't abort if the target says - // that GISel is enabled. - return !TM->Options.EnableGlobalISel; + return TM->Options.GlobalISelAbort == GlobalISelAbortMode::Enable; } bool TargetPassConfig::reportDiagnosticWhenGlobalISelFallback() const { - return EnableGlobalISelAbort == 2; + return TM->Options.GlobalISelAbort == GlobalISelAbortMode::DisableWithDiag; } Index: lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- lib/Target/AArch64/AArch64TargetMachine.cpp +++ lib/Target/AArch64/AArch64TargetMachine.cpp @@ -275,8 +275,10 @@ } // Enable GlobalISel at or below EnableGlobalISelAt0. - if (getOptLevel() <= EnableGlobalISelAtO) + if (getOptLevel() <= EnableGlobalISelAtO) { setGlobalISel(true); + setGlobalISelAbort(GlobalISelAbortMode::Disable); + } // AArch64 supports the MachineOutliner. setMachineOutliner(true); Index: test/CodeGen/AArch64/GlobalISel/irtranslator-stackprotect-check.ll =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/irtranslator-stackprotect-check.ll @@ -0,0 +1,50 @@ +; RUN: llc -stop-before=irtranslator -global-isel %s -o - | FileCheck %s +; RUN: llc -stop-after=irtranslator -verify-machineinstrs -global-isel %s -o - | FileCheck --check-prefixes CHECK,CHECK-MIR %s + +; Check that when using GlobalISel, the StackProtector pass currently inserts +; both prologue and epilogue instrumentation because GlobalISel does not have +; the same epilogue insertion/optimization as SelectionDAG. + +target triple = "armv8-arm-none-eabi" + +define void @foo() ssp { +; CHECK-LABEL: entry: +; CHECK-NEXT: %StackGuardSlot = alloca i8* +; CHECK-NEXT: %0 = call i8* @llvm.stackguard() +; CHECK-NEXT: call void @llvm.stackprotector(i8* %0, i8** %StackGuardSlot) +; CHECK-NEXT: %buf = alloca [8 x i8], align 1 +; CHECK-NEXT: %1 = call i8* @llvm.stackguard() +; CHECK-NEXT: %2 = load volatile i8*, i8** %StackGuardSlot +; CHECK-NEXT: %3 = icmp eq i8* %1, %2 +; CHECK-NEXT: br i1 %3, label %SP_return, label %CallStackCheckFailBlk, !prof !0 +; +; CHECK: SP_return: +; CHECK-NEXT: ret void +; +; CHECK: CallStackCheckFailBlk: +; CHECK-NEXT: call void @__stack_chk_fail() +; CHECK-NEXT: unreachable + +; CHECK-MIR: bb.1.entry: +; CHECK-MIR: %0:_(p0) = G_FRAME_INDEX %stack.0.StackGuardSlot +; CHECK-MIR-NEXT: %1:gpr(p0) = LOAD_STACK_GUARD :: (dereferenceable invariant load 4 from @__stack_chk_guard) +; CHECK-MIR-NEXT: %2:gpr(p0) = LOAD_STACK_GUARD :: (dereferenceable invariant load 4 from @__stack_chk_guard) +; CHECK-MIR-NEXT: G_STORE %2(p0), %0(p0) :: (volatile store 4 into %stack.0.StackGuardSlot, align 8) +; CHECK-MIR-NEXT: %3:_(p0) = G_FRAME_INDEX %stack.1.buf +; CHECK-MIR-NEXT: %4:gpr(p0) = LOAD_STACK_GUARD :: (dereferenceable invariant load 4 from @__stack_chk_guard) +; CHECK-MIR-NEXT: %5:_(p0) = G_LOAD %0(p0) :: (volatile load 4 from %ir.StackGuardSlot) +; CHECK-MIR-NEXT: %6:_(s1) = G_ICMP intpred(eq), %4(p0), %5 +; CHECK-MIR-NEXT: G_BRCOND %6(s1), %bb.2 +; CHECK-MIR-NEXT: G_BR %bb.3 +; +; CHECK-MIR: bb.2.SP_return: +; CHECK-MIR-NEXT: BX_RET 14, $noreg +; +; CHECK-MIR: bb.3.CallStackCheckFailBlk: +; CHECK-MIR-NEXT: ADJCALLSTACKDOWN 0, 0, 14, $noreg, implicit-def $sp, implicit $sp +; CHECK-MIR-NEXT: BL @__stack_chk_fail, csr_aapcs, implicit-def $lr, implicit $sp +; CHECK-MIR-NEXT: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp +entry: + %buf = alloca [8 x i8], align 1 + ret void +}