blob: 8f76a934449ead8bb11039da13d6dbfd06e1b22b [file] [log] [blame]
commit 195f44278c4361a4a32377a98a1e3a15203d3647
Author: Kristof Beyls <kristof.beyls@arm.com>
Date: Wed Oct 28 21:04:11 2020 +0000
[ARM] Implement harden-sls-retbr for ARM mode
Some processors may speculatively execute the instructions immediately
following indirect control flow, such as returns, indirect jumps and
indirect function calls.
To avoid a potential miss-speculatively executed gadget after these
instructions leaking secrets through side channels, this pass places a
speculation barrier immediately after every indirect control flow where
control flow doesn't return to the next instruction, such as returns and
indirect jumps, but not indirect function calls.
Hardening of indirect function calls will be done in a later,
independent patch.
This patch is implementing the same functionality as the AArch64 counter
part implemented in https://reviews.llvm.org/D81400.
For AArch64, returns and indirect jumps only occur on RET and BR
instructions and hence the function attribute to control the hardening
is called "harden-sls-retbr" there. On AArch32, there is a much wider
variety of instructions that can trigger an indirect unconditional
control flow change. I've decided to stick with the name
"harden-sls-retbr" as introduced for the corresponding AArch64
mitigation.
This patch implements this for ARM mode. A future patch will extend this
to also support Thumb mode.
The inserted barriers are never on the correct, architectural execution
path, and therefore performance overhead of this is expected to be low.
To ensure these barriers are never on an architecturally executed path,
when the harden-sls-retbr function attribute is present, indirect
control flow is never conditionalized/predicated.
On targets that implement that Armv8.0-SB Speculation Barrier extension,
a single SB instruction is emitted that acts as a speculation barrier.
On other targets, a DSB SYS followed by a ISB is emitted to act as a
speculation barrier.
These speculation barriers are implemented as pseudo instructions to
avoid later passes to analyze them and potentially remove them.
The mitigation is off by default and can be enabled by the
harden-sls-retbr subtarget feature.
Differential Revision: https://reviews.llvm.org/D92395
diff --git a/llvm/lib/Target/ARM/ARM.h b/llvm/lib/Target/ARM/ARM.h
index 7398968bb24a..51dfaaa96892 100644
--- a/llvm/lib/Target/ARM/ARM.h
+++ b/llvm/lib/Target/ARM/ARM.h
@@ -55,6 +55,7 @@ InstructionSelector *
createARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget &STI,
const ARMRegisterBankInfo &RBI);
Pass *createMVEGatherScatterLoweringPass();
+FunctionPass *createARMSLSHardeningPass();
void LowerARMMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
ARMAsmPrinter &AP);
@@ -71,6 +72,7 @@ void initializeMVEVPTOptimisationsPass(PassRegistry &);
void initializeARMLowOverheadLoopsPass(PassRegistry &);
void initializeMVETailPredicationPass(PassRegistry &);
void initializeMVEGatherScatterLoweringPass(PassRegistry &);
+void initializeARMSLSHardeningPass(PassRegistry &);
} // end namespace llvm
diff --git a/llvm/lib/Target/ARM/ARM.td b/llvm/lib/Target/ARM/ARM.td
index 3fa65289744e..4d4ace51e13f 100644
--- a/llvm/lib/Target/ARM/ARM.td
+++ b/llvm/lib/Target/ARM/ARM.td
@@ -562,6 +562,16 @@ foreach i = {0-7} in
"Coprocessor "#i#" ISA is CDEv1",
[HasCDEOps]>;
+//===----------------------------------------------------------------------===//
+// Control codegen mitigation against Straight Line Speculation vulnerability.
+//===----------------------------------------------------------------------===//
+
+def FeatureHardenSlsRetBr : SubtargetFeature<"harden-sls-retbr",
+ "HardenSlsRetBr", "true",
+ "Harden against straight line speculation across RETurn and BranchRegister "
+ "instructions">;
+
+
//===----------------------------------------------------------------------===//
// ARM Processor subtarget features.
//
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index f2ee03981f24..4cc85ad82d51 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -2180,6 +2180,25 @@ void ARMAsmPrinter::emitInstruction(const MachineInstr *MI) {
case ARM::PATCHABLE_TAIL_CALL:
LowerPATCHABLE_TAIL_CALL(*MI);
return;
+ case ARM::SpeculationBarrierISBDSBEndBB: {
+ // Print DSB SYS + ISB
+ MCInst TmpInstDSB;
+ TmpInstDSB.setOpcode(ARM::DSB);
+ TmpInstDSB.addOperand(MCOperand::createImm(0xf));
+ EmitToStreamer(*OutStreamer, TmpInstDSB);
+ MCInst TmpInstISB;
+ TmpInstISB.setOpcode(ARM::ISB);
+ TmpInstISB.addOperand(MCOperand::createImm(0xf));
+ EmitToStreamer(*OutStreamer, TmpInstISB);
+ return;
+ }
+ case ARM::SpeculationBarrierSBEndBB: {
+ // Print SB
+ MCInst TmpInstSB;
+ TmpInstSB.setOpcode(ARM::SB);
+ EmitToStreamer(*OutStreamer, TmpInstSB);
+ return;
+ }
}
MCInst TmpInst;
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index 2d937930d89f..7068da5eb004 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -339,8 +339,10 @@ bool ARMBaseInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
// out.
bool CantAnalyze = false;
- // Skip over DEBUG values and predicated nonterminators.
- while (I->isDebugInstr() || !I->isTerminator()) {
+ // Skip over DEBUG values, predicated nonterminators and speculation
+ // barrier terminators.
+ while (I->isDebugInstr() || !I->isTerminator() ||
+ isSpeculationBarrierEndBBOpcode(I->getOpcode()) ){
if (I == MBB.instr_begin())
return false;
--I;
@@ -389,6 +391,9 @@ bool ARMBaseInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
while (DI != MBB.instr_end()) {
MachineInstr &InstToDelete = *DI;
++DI;
+ // Speculation barriers must not be deleted.
+ if (isSpeculationBarrierEndBBOpcode(InstToDelete.getOpcode()))
+ continue;
InstToDelete.eraseFromParent();
}
}
@@ -672,14 +677,21 @@ bool ARMBaseInstrInfo::isPredicable(const MachineInstr &MI) const {
if (!isEligibleForITBlock(&MI))
return false;
+ const MachineFunction *MF = MI.getParent()->getParent();
const ARMFunctionInfo *AFI =
- MI.getParent()->getParent()->getInfo<ARMFunctionInfo>();
+ MF->getInfo<ARMFunctionInfo>();
// Neon instructions in Thumb2 IT blocks are deprecated, see ARMARM.
// In their ARM encoding, they can't be encoded in a conditional form.
if ((MI.getDesc().TSFlags & ARMII::DomainMask) == ARMII::DomainNEON)
return false;
+ // Make indirect control flow changes unpredicable when SLS mitigation is
+ // enabled.
+ const ARMSubtarget &ST = MF->getSubtarget<ARMSubtarget>();
+ if (ST.hardenSlsRetBr() && isIndirectControlFlowNotComingBack(MI))
+ return false;
+
if (AFI->isThumb2Function()) {
if (getSubtarget().restrictIT())
return isV8EligibleForIT(&MI);
@@ -762,6 +774,12 @@ unsigned ARMBaseInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
Size = alignTo(Size, 4);
return Size;
}
+ case ARM::SpeculationBarrierISBDSBEndBB:
+ // This gets lowered to 2 4-byte instructions.
+ return 8;
+ case ARM::SpeculationBarrierSBEndBB:
+ // This gets lowered to 1 4-byte instructions.
+ return 4;
}
}
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
index df237dffe4fb..51a4b44eae1d 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
@@ -635,6 +635,17 @@ bool isIndirectBranchOpcode(int Opc) {
return Opc == ARM::BX || Opc == ARM::MOVPCRX || Opc == ARM::tBRIND;
}
+static inline bool isIndirectControlFlowNotComingBack(const MachineInstr &MI) {
+ int opc = MI.getOpcode();
+ return MI.isReturn() || isIndirectBranchOpcode(MI.getOpcode()) ||
+ isJumpTableBranchOpcode(opc);
+}
+
+static inline bool isSpeculationBarrierEndBBOpcode(int Opc) {
+ return Opc == ARM::SpeculationBarrierISBDSBEndBB ||
+ Opc == ARM::SpeculationBarrierSBEndBB;
+}
+
static inline bool isPopOpcode(int Opc) {
return Opc == ARM::tPOP_RET || Opc == ARM::LDMIA_RET ||
Opc == ARM::t2LDMIA_RET || Opc == ARM::tPOP || Opc == ARM::LDMIA_UPD ||
diff --git a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
index 86da5a24d340..77839710e03e 100644
--- a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
+++ b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
@@ -553,6 +553,12 @@ void ARMConstantIslands::doInitialJumpTablePlacement(
MachineBasicBlock *LastCorrectlyNumberedBB = nullptr;
for (MachineBasicBlock &MBB : *MF) {
auto MI = MBB.getLastNonDebugInstr();
+ // Look past potential SpeculationBarriers at end of BB.
+ while (MI != MBB.end() &&
+ (isSpeculationBarrierEndBBOpcode(MI->getOpcode()) ||
+ MI->isDebugInstr()))
+ --MI;
+
if (MI == MBB.end())
continue;
@@ -784,6 +790,7 @@ initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs) {
NegOk = true;
IsSoImm = true;
unsigned CPI = I.getOperand(op).getIndex();
+ assert(CPI < CPEMIs.size());
MachineInstr *CPEMI = CPEMIs[CPI];
const Align CPEAlign = getCPEAlign(CPEMI);
const unsigned LogCPEAlign = Log2(CPEAlign);
diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td
index c37734b716cc..840af9052376 100644
--- a/llvm/lib/Target/ARM/ARMInstrInfo.td
+++ b/llvm/lib/Target/ARM/ARMInstrInfo.td
@@ -6365,6 +6365,15 @@ def SPACE : PseudoInst<(outs GPR:$Rd), (ins i32imm:$size, GPR:$Rn),
NoItinerary,
[(set GPR:$Rd, (int_arm_space timm:$size, GPR:$Rn))]>;
+// SpeculationBarrierEndBB must only be used after an unconditional control
+// flow, i.e. after a terminator for which isBarrier is True.
+let hasSideEffects = 1, isCodeGenOnly = 1, isTerminator = 1, isBarrier = 1 in {
+ def SpeculationBarrierISBDSBEndBB
+ : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
+ def SpeculationBarrierSBEndBB
+ : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
+}
+
//===----------------------------------
// Atomic cmpxchg for -O0
//===----------------------------------
diff --git a/llvm/lib/Target/ARM/ARMSLSHardening.cpp b/llvm/lib/Target/ARM/ARMSLSHardening.cpp
new file mode 100644
index 000000000000..b3c697893ed9
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMSLSHardening.cpp
@@ -0,0 +1,117 @@
+//===- ARMSLSHardening.cpp - Harden Straight Line Missspeculation ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains a pass to insert code to mitigate against side channel
+// vulnerabilities that may happen under straight line miss-speculation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARM.h"
+#include "ARMInstrInfo.h"
+#include "ARMSubtarget.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/IR/DebugLoc.h"
+#include <cassert>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "arm-sls-hardening"
+
+#define ARM_SLS_HARDENING_NAME "ARM sls hardening pass"
+
+namespace {
+
+class ARMSLSHardening : public MachineFunctionPass {
+public:
+ const TargetInstrInfo *TII;
+ const ARMSubtarget *ST;
+
+ static char ID;
+
+ ARMSLSHardening() : MachineFunctionPass(ID) {
+ initializeARMSLSHardeningPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnMachineFunction(MachineFunction &Fn) override;
+
+ StringRef getPassName() const override { return ARM_SLS_HARDENING_NAME; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+
+private:
+ bool hardenReturnsAndBRs(MachineBasicBlock &MBB) const;
+};
+
+} // end anonymous namespace
+
+char ARMSLSHardening::ID = 0;
+
+INITIALIZE_PASS(ARMSLSHardening, "arm-sls-hardening",
+ ARM_SLS_HARDENING_NAME, false, false)
+
+static void insertSpeculationBarrier(const ARMSubtarget *ST,
+ MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ DebugLoc DL,
+ bool AlwaysUseISBDSB = false) {
+ assert(MBBI != MBB.begin() &&
+ "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
+ assert(std::prev(MBBI)->isBarrier() &&
+ "SpeculationBarrierEndBB must only follow unconditional control flow "
+ "instructions.");
+ assert(std::prev(MBBI)->isTerminator() &&
+ "SpeculationBarrierEndBB must only follow terminators.");
+ const TargetInstrInfo *TII = ST->getInstrInfo();
+ unsigned BarrierOpc = ST->hasSB() && !AlwaysUseISBDSB
+ ? ARM::SpeculationBarrierSBEndBB
+ : ARM::SpeculationBarrierISBDSBEndBB;
+ if (MBBI == MBB.end() || !isSpeculationBarrierEndBBOpcode(MBBI->getOpcode()))
+ BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc));
+}
+
+bool ARMSLSHardening::runOnMachineFunction(MachineFunction &MF) {
+ ST = &MF.getSubtarget<ARMSubtarget>();
+ TII = MF.getSubtarget().getInstrInfo();
+
+ bool Modified = false;
+ for (auto &MBB : MF)
+ Modified |= hardenReturnsAndBRs(MBB);
+
+ return Modified;
+}
+
+bool ARMSLSHardening::hardenReturnsAndBRs(MachineBasicBlock &MBB) const {
+ if (!ST->hardenSlsRetBr())
+ return false;
+ bool Modified = false;
+ MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end();
+ MachineBasicBlock::iterator NextMBBI;
+ for (; MBBI != E; MBBI = NextMBBI) {
+ MachineInstr &MI = *MBBI;
+ NextMBBI = std::next(MBBI);
+ if (isIndirectControlFlowNotComingBack(MI)) {
+ assert(MI.isTerminator());
+ assert(!TII->isPredicated(MI));
+ insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc());
+ Modified = true;
+ }
+ }
+ return Modified;
+}
+
+FunctionPass *llvm::createARMSLSHardeningPass() {
+ return new ARMSLSHardening();
+}
diff --git a/llvm/lib/Target/ARM/ARMSubtarget.h b/llvm/lib/Target/ARM/ARMSubtarget.h
index 695a53eec93e..bfde47290dcd 100644
--- a/llvm/lib/Target/ARM/ARMSubtarget.h
+++ b/llvm/lib/Target/ARM/ARMSubtarget.h
@@ -464,6 +464,10 @@ protected:
/// cannot be encoded. For example, ADD r0, r1, #FFFFFFFF -> SUB r0, r1, #1.
bool NegativeImmediates = true;
+ /// Harden against Straight Line Speculation for Returns and Indirect
+ /// Branches.
+ bool HardenSlsRetBr = false;
+
/// stackAlignment - The minimum alignment known to hold of the stack frame on
/// entry to the function and which must be maintained by every function.
Align stackAlignment = Align(4);
@@ -905,6 +909,8 @@ public:
bool ignoreCSRForAllocationOrder(const MachineFunction &MF,
unsigned PhysReg) const override;
unsigned getGPRAllocationOrder(const MachineFunction &MF) const;
+
+ bool hardenSlsRetBr() const { return HardenSlsRetBr; }
};
} // end namespace llvm
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index cf4115f77fec..2e20070833e6 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -100,6 +100,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeARMTarget() {
initializeMVETailPredicationPass(Registry);
initializeARMLowOverheadLoopsPass(Registry);
initializeMVEGatherScatterLoweringPass(Registry);
+ initializeARMSLSHardeningPass(Registry);
}
static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
@@ -538,6 +539,8 @@ void ARMPassConfig::addPreSched2() {
addPass(&PostMachineSchedulerID);
addPass(&PostRASchedulerID);
}
+
+ addPass(createARMSLSHardeningPass());
}
void ARMPassConfig::addPreEmitPass() {
diff --git a/llvm/lib/Target/ARM/CMakeLists.txt b/llvm/lib/Target/ARM/CMakeLists.txt
index 6a2ffdaef126..3894c0cfed68 100644
--- a/llvm/lib/Target/ARM/CMakeLists.txt
+++ b/llvm/lib/Target/ARM/CMakeLists.txt
@@ -48,6 +48,7 @@ add_llvm_target(ARMCodeGen
ARMOptimizeBarriersPass.cpp
ARMRegisterBankInfo.cpp
ARMSelectionDAGInfo.cpp
+ ARMSLSHardening.cpp
ARMSubtarget.cpp
ARMTargetMachine.cpp
ARMTargetObjectFile.cpp
diff --git a/llvm/test/CodeGen/ARM/O3-pipeline.ll b/llvm/test/CodeGen/ARM/O3-pipeline.ll
index bba384715bc6..6390e42570d7 100644
--- a/llvm/test/CodeGen/ARM/O3-pipeline.ll
+++ b/llvm/test/CodeGen/ARM/O3-pipeline.ll
@@ -159,6 +159,7 @@
; CHECK-NEXT: Machine Natural Loop Construction
; CHECK-NEXT: PostRA Machine Instruction Scheduler
; CHECK-NEXT: Post RA top-down list latency scheduler
+; CHECK-NEXT: ARM sls hardening pass
; CHECK-NEXT: Analyze Machine Code For Garbage Collection
; CHECK-NEXT: Machine Block Frequency Analysis
; CHECK-NEXT: MachinePostDominator Tree Construction
diff --git a/llvm/test/CodeGen/ARM/speculation-hardening-sls.ll b/llvm/test/CodeGen/ARM/speculation-hardening-sls.ll
new file mode 100644
index 000000000000..9a2335988790
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/speculation-hardening-sls.ll
@@ -0,0 +1,142 @@
+; RUN: llc -mattr=harden-sls-retbr -verify-machineinstrs -mtriple=armv8-linux-gnueabi < %s | FileCheck %s --check-prefixes=CHECK,ARM,HARDEN,ISBDSB,ISBDSBDAGISEL -dump-input-context=100
+; RUN: llc -mattr=harden-sls-retbr -mattr=+sb -verify-machineinstrs -mtriple=armv8-linux-gnueabi < %s | FileCheck %s --check-prefixes=CHECK,ARM,HARDEN,SB,SBDAGISEL -dump-input-context=100
+; RUN: llc -verify-machineinstrs -mtriple=armv8-linux-gnueabi < %s | FileCheck %s --check-prefixes=CHECK,ARM,NOHARDEN,NOHARDENARM -dump-input-context=100
+; RUN: llc -global-isel -global-isel-abort=0 -mattr=harden-sls-retbr -verify-machineinstrs -mtriple=armv8-linux-gnueabi < %s | FileCheck %s --check-prefixes=CHECK,ARM,HARDEN,ISBDSB
+; RUN: llc -global-isel -global-isel-abort=0 -mattr=harden-sls-retbr -mattr=+sb -verify-machineinstrs -mtriple=armv8-linux-gnueabi < %s | FileCheck %s --check-prefixes=CHECK,ARM,HARDEN,SB
+
+; Function Attrs: norecurse nounwind readnone
+define dso_local i32 @double_return(i32 %a, i32 %b) local_unnamed_addr {
+entry:
+ %cmp = icmp sgt i32 %a, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ ; Make a very easy, very likely to predicate return (BX LR), to test that
+ ; it will not get predicated when sls-hardening is enabled.
+ %mul = mul i32 %b, %a
+ ret i32 %mul
+; CHECK-LABEL: double_return:
+; HARDEN: {{bx lr$}}
+; NOHARDENARM: {{bxge lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+
+if.else: ; preds = %entry
+ %div3 = sdiv i32 %a, %b
+ %div2 = sdiv i32 %a, %div3
+ %div1 = sdiv i32 %a, %div2
+ ret i32 %div1
+
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+; CHECK-NEXT: .Lfunc_end
+}
+
+@__const.indirect_branch.ptr = private unnamed_addr constant [2 x i8*] [i8* blockaddress(@indirect_branch, %return), i8* blockaddress(@indirect_branch, %l2)], align 8
+
+; Function Attrs: norecurse nounwind readnone
+define dso_local i32 @indirect_branch(i32 %a, i32 %b, i32 %i) {
+; CHECK-LABEL: indirect_branch:
+entry:
+ %idxprom = sext i32 %i to i64
+ %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @__const.indirect_branch.ptr, i64 0, i64 %idxprom
+ %0 = load i8*, i8** %arrayidx, align 8
+ indirectbr i8* %0, [label %return, label %l2]
+; ARM: bx r0
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+
+l2: ; preds = %entry
+ br label %return
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+
+return: ; preds = %entry, %l2
+ %retval.0 = phi i32 [ 1, %l2 ], [ 0, %entry ]
+ ret i32 %retval.0
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+; CHECK-NEXT: .Lfunc_end
+}
+
+define i32 @asmgoto() {
+entry:
+; CHECK-LABEL: asmgoto:
+ callbr void asm sideeffect "B $0", "X"(i8* blockaddress(@asmgoto, %d))
+ to label %asm.fallthrough [label %d]
+ ; The asm goto above produces a direct branch:
+; CHECK: @APP
+; CHECK-NEXT: {{^[ \t]+b }}
+; CHECK-NEXT: @NO_APP
+ ; For direct branches, no mitigation is needed.
+; ISDDSB-NOT: dsb sy
+; SB-NOT: {{ sb$}}
+
+asm.fallthrough: ; preds = %entry
+ ret i32 0
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+
+d: ; preds = %asm.fallthrough, %entry
+ ret i32 1
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+; CHECK-NEXT: .Lfunc_end
+}
+
+; Check that indirect branches produced through switch jump tables are also
+; hardened:
+define dso_local i32 @jumptable(i32 %a, i32 %b) {
+; CHECK-LABEL: jumptable:
+entry:
+ switch i32 %b, label %sw.epilog [
+ i32 0, label %sw.bb
+ i32 1, label %sw.bb1
+ i32 3, label %sw.bb3
+ i32 4, label %sw.bb5
+ ]
+; ARM: ldr pc, [{{r[0-9]}}, {{r[0-9]}}, lsl #2]
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+
+
+sw.bb: ; preds = %entry
+ %add = shl nsw i32 %a, 1
+ br label %sw.bb1
+
+sw.bb1: ; preds = %entry, %sw.bb
+ %a.addr.0 = phi i32 [ %a, %entry ], [ %add, %sw.bb ]
+ %add2 = shl nsw i32 %a.addr.0, 1
+ br label %sw.bb3
+
+sw.bb3: ; preds = %entry, %sw.bb1
+ %a.addr.1 = phi i32 [ %a, %entry ], [ %add2, %sw.bb1 ]
+ %add4 = shl nsw i32 %a.addr.1, 1
+ br label %sw.bb5
+
+sw.bb5: ; preds = %entry, %sw.bb3
+ %a.addr.2 = phi i32 [ %a, %entry ], [ %add4, %sw.bb3 ]
+ %add6 = shl nsw i32 %a.addr.2, 1
+ br label %sw.epilog
+
+sw.epilog: ; preds = %sw.bb5, %entry
+ %a.addr.3 = phi i32 [ %a, %entry ], [ %add6, %sw.bb5 ]
+ ret i32 %a.addr.3
+; CHECK: {{bx lr$}}
+; ISBDSB-NEXT: dsb sy
+; ISBDSB-NEXT: isb
+; SB-NEXT: {{ sb$}}
+}