diff --git a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp --- a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp +++ b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp @@ -3069,6 +3069,21 @@ MI.eraseFromParent(); return true; } + case ARM::t2CALL_BTI: { + MachineFunction &MF = *MI.getMF(); + MachineInstrBuilder MIB = BuildMI(MF, MI.getDebugLoc(), TII->get(ARM::tBL)); + MIB.cloneMemRefs(MI); + for (unsigned i = 0; i < MI.getNumOperands(); ++i) + MIB.add(MI.getOperand(i)); + if (MI.isCandidateForCallSiteEntry()) + MF.moveCallSiteInfo(&MI, MIB.getInstr()); + MIBundleBuilder Bundler(MBB, MI); + Bundler.append(MIB); + Bundler.append(BuildMI(MF, MI.getDebugLoc(), TII->get(ARM::t2BTI))); + finalizeBundle(MBB, Bundler.begin(), Bundler.end()); + MI.eraseFromParent(); + return true; + } case ARM::LOADDUAL: case ARM::STOREDUAL: { Register PairReg = MI.getOperand(0).getReg(); diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h --- a/llvm/lib/Target/ARM/ARMISelLowering.h +++ b/llvm/lib/Target/ARM/ARMISelLowering.h @@ -69,6 +69,7 @@ CALL_PRED, // Function call that's predicable. CALL_NOLINK, // Function call with branch not branch-and-link. tSECALL, // CMSE non-secure function call. + t2CALL_BTI, // Thumb function call followed by BTI instruction. BRCOND, // Conditional branch. BR_JT, // Jumptable branch. BR2_JT, // Jumptable branch (2 level - jumptable entry is a jump). diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1643,6 +1643,7 @@ MAKE_CASE(ARMISD::CALL_PRED) MAKE_CASE(ARMISD::CALL_NOLINK) MAKE_CASE(ARMISD::tSECALL) + MAKE_CASE(ARMISD::t2CALL_BTI) MAKE_CASE(ARMISD::BRCOND) MAKE_CASE(ARMISD::BR_JT) MAKE_CASE(ARMISD::BR2_JT) @@ -2304,6 +2305,11 @@ bool isCmseNSCall = false; bool isSibCall = false; bool PreferIndirect = false; + bool GuardWithBTI = false; + + // Lower 'returns_twice' calls to a pseudo-instruction. + if (CLI.CB && CLI.CB->getAttributes().hasFnAttr(Attribute::ReturnsTwice)) + GuardWithBTI = AFI->branchTargetEnforcement(); // Determine whether this is a non-secure function call. if (CLI.CB && CLI.CB->getAttributes().hasFnAttr("cmse_nonsecure_call")) @@ -2709,7 +2715,9 @@ // FIXME: handle tail calls differently. unsigned CallOpc; if (Subtarget->isThumb()) { - if (isCmseNSCall) + if (GuardWithBTI) + CallOpc = ARMISD::t2CALL_BTI; + else if (isCmseNSCall) CallOpc = ARMISD::tSECALL; else if ((!isDirect || isARMFunc) && !Subtarget->hasV5TOps()) CallOpc = ARMISD::CALL_NOLINK; diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td --- a/llvm/lib/Target/ARM/ARMInstrThumb2.td +++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td @@ -5704,3 +5704,10 @@ def t2PACBTI : PACBTIHintSpaceDefInst<"pacbti", 0b00001101>; def t2AUT : PACBTIHintSpaceUseInst<"aut", 0b00101101>; def t2BTI : PACBTIHintSpaceNoOpsInst<"bti", 0b00001111>; + +def ARMt2CallBTI : SDNode<"ARMISD::t2CALL_BTI", SDT_ARMcall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, SDNPVariadic]>; + +def t2CALL_BTI : PseudoInst<(outs), (ins pred:$p, thumb_bl_target:$func), + IIC_Br, [(ARMt2CallBTI tglobaladdr:$func)]>, + Requires<[IsThumb2]>, Sched<[WriteBrL]>; diff --git a/llvm/test/CodeGen/ARM/setjmp-bti-basic.ll b/llvm/test/CodeGen/ARM/setjmp-bti-basic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/setjmp-bti-basic.ll @@ -0,0 +1,44 @@ +; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi < %s | FileCheck %s + +; C source +; -------- +; jmp_buf buf; +; +; extern void bar(int x); +; +; int foo(int x) { +; if (setjmp(buf)) +; x = 0; +; else +; bar(x); +; return x; +; } + +@buf = global [20 x i64] zeroinitializer, align 8 + +define i32 @foo(i32 %x) { +; CHECK-LABEL: foo: +; CHECK: bl setjmp +; CHECK-NEXT: bti +entry: + %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0 + %tobool.not = icmp eq i32 %call, 0 + br i1 %tobool.not, label %if.else, label %if.end + +if.else: ; preds = %entry + call void @bar(i32 %x) + br label %if.end + +if.end: ; preds = %entry, %if.else + %x.addr.0 = phi i32 [ %x, %if.else ], [ 0, %entry ] + ret i32 %x.addr.0 +} + +declare void @bar(i32) +declare i32 @setjmp(i64*) #0 + +attributes #0 = { returns_twice } + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"branch-target-enforcement", i32 1} diff --git a/llvm/test/CodeGen/ARM/setjmp-bti-outliner.ll b/llvm/test/CodeGen/ARM/setjmp-bti-outliner.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/setjmp-bti-outliner.ll @@ -0,0 +1,84 @@ +; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi -enable-machine-outliner < %s | FileCheck %s + +; C source +; -------- +; jmp_buf buf; +; +; extern void h(int a, int b, int *c); +; +; int f(int a, int b, int c, int d) { +; if (setjmp(buf) != 0) +; return -1; +; h(a, b, &a); +; return 2 + a * (a + b) / (c + d); +; } +; +; int g(int a, int b, int c, int d) { +; if (setjmp(buf) != 0) +; return -1; +; h(a, b, &a); +; return 1 + a * (a + b) / (c + d); +; } + +@buf = global [20 x i64] zeroinitializer, align 8 + +define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) { +; CHECK-LABEL: f: +; CHECK: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: bti +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0 + %cmp.not = icmp eq i32 %call, 0 + br i1 %cmp.not, label %if.end, label %return + +if.end: ; preds = %entry + call void @h(i32 %a, i32 %b, i32* nonnull %a.addr) + %0 = load i32, i32* %a.addr, align 4 + %add = add nsw i32 %0, %b + %mul = mul nsw i32 %add, %0 + %add1 = add nsw i32 %d, %c + %div = sdiv i32 %mul, %add1 + %add2 = add nsw i32 %div, 2 + br label %return + +return: ; preds = %entry, %if.end + %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ] + ret i32 %retval.0 +} + +define i32 @g(i32 %a, i32 %b, i32 %c, i32 %d) { +; CHECK-LABEL: g: +; CHECK: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: bti +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0 + %cmp.not = icmp eq i32 %call, 0 + br i1 %cmp.not, label %if.end, label %return + +if.end: ; preds = %entry + call void @h(i32 %a, i32 %b, i32* nonnull %a.addr) + %0 = load i32, i32* %a.addr, align 4 + %add = add nsw i32 %0, %b + %mul = mul nsw i32 %add, %0 + %add1 = add nsw i32 %d, %c + %div = sdiv i32 %mul, %add1 + %add2 = add nsw i32 %div, 1 + br label %return + +return: ; preds = %entry, %if.end + %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ] + ret i32 %retval.0 +} + +declare void @h(i32, i32, i32*) +declare i32 @setjmp(i64*) #0 + +attributes #0 = { returns_twice } + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"branch-target-enforcement", i32 1}