class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
bool UseInitArray;
- mutable unsigned NextUniqueID = 0;
+ mutable unsigned NextUniqueID = 1; // ID 0 is reserved for execute-only sections
protected:
MCSymbolRefExpr::VariantKind PLTRelativeVariantKind =
/// Text - Text section, used for functions and other executable code.
Text,
+ /// ExecuteOnly, Text section that is not readable.
+ ExecuteOnly,
+
/// ReadOnly - Data that is never written to at program runtime by the
/// program or the dynamic linker. Things in the top-level readonly
/// SectionKind are not mergeable.
public:
bool isMetadata() const { return K == Metadata; }
- bool isText() const { return K == Text; }
+
+ bool isText() const { return K == Text || K == ExecuteOnly; }
+
+ bool isExecuteOnly() const { return K == ExecuteOnly; }
bool isReadOnly() const {
return K == ReadOnly || isMergeableCString() ||
static SectionKind getMetadata() { return get(Metadata); }
static SectionKind getText() { return get(Text); }
+ static SectionKind getExecuteOnly() { return get(ExecuteOnly); }
static SectionKind getReadOnly() { return get(ReadOnly); }
static SectionKind getMergeable1ByteCString() {
return get(Mergeable1ByteCString);
// Section data is string data by default.
SHF_MIPS_STRING = 0x80000000,
+ // Make code section unreadable when in execute-only mode
+ SHF_ARM_PURECODE = 0x20000000,
+
SHF_AMDGPU_HSA_GLOBAL = 0x00100000,
SHF_AMDGPU_HSA_READONLY = 0x00200000,
SHF_AMDGPU_HSA_CODE = 0x00400000,
if (K.isText())
Flags |= ELF::SHF_EXECINSTR;
+ if (K.isExecuteOnly())
+ Flags |= ELF::SHF_ARM_PURECODE;
+
if (K.isWriteable())
Flags |= ELF::SHF_WRITE;
UniqueID = *NextUniqueID;
(*NextUniqueID)++;
}
+ // Use 0 as the unique ID for execute-only text
+ if (Kind.isExecuteOnly())
+ UniqueID = 0;
return Ctx.getELFSection(Name, getELFSectionType(Name, Kind), Flags,
EntrySize, Group, UniqueID);
}
StringRef CachedName = Entry.first.SectionName;
SectionKind Kind;
- if (Flags & ELF::SHF_EXECINSTR)
+ if (Flags & ELF::SHF_ARM_PURECODE)
+ Kind = SectionKind::getExecuteOnly();
+ else if (Flags & ELF::SHF_EXECINSTR)
Kind = SectionKind::getText();
else
Kind = SectionKind::getReadOnly();
case 'd':
flags |= ELF::XCORE_SHF_DP_SECTION;
break;
+ case 'y':
+ flags |= ELF::SHF_ARM_PURECODE;
+ break;
case 'G':
flags |= ELF::SHF_GROUP;
break;
OS << 'c';
if (Flags & ELF::XCORE_SHF_DP_SECTION)
OS << 'd';
+ if (Flags & ELF::SHF_ARM_PURECODE)
+ OS << 'y';
OS << '"';
BCase(SHF_GROUP)
BCase(SHF_TLS)
switch(Object->Header.Machine) {
+ case ELF::EM_ARM:
+ BCase(SHF_ARM_PURECODE)
+ break;
case ELF::EM_AMDGPU:
BCase(SHF_AMDGPU_HSA_GLOBAL)
BCase(SHF_AMDGPU_HSA_READONLY)
break;
}
case MachineOperand::MO_ConstantPoolIndex:
+ if (Subtarget->genExecuteOnly())
+ llvm_unreachable("execute-only should not generate constant pools");
GetCPISymbol(MO.getIndex())->print(O, MAI);
break;
}
MadeChange |= optimizeThumb2Branches();
// Optimize jump tables using TBB / TBH.
- if (GenerateTBB)
+ if (GenerateTBB && !STI->genExecuteOnly())
MadeChange |= optimizeThumb2JumpTables();
// After a while, this might be made debug-only, but it is not expensive.
(isa<GlobalVariable>(GV) && cast<GlobalVariable>(GV)->isConstant()) ||
isa<Function>(GV);
- if (TM.shouldAssumeDSOLocal(*GV->getParent(), GV))
+ // promoteToConstantPool only if not generating XO text section
+ if (TM.shouldAssumeDSOLocal(*GV->getParent(), GV) && !Subtarget->genExecuteOnly())
if (SDValue V = promoteToConstantPool(GV, DAG, PtrVT, dl))
return V;
Table = DAG.getNode(ARMISD::WrapperJT, dl, MVT::i32, JTI);
Index = DAG.getNode(ISD::MUL, dl, PTy, Index, DAG.getConstant(4, dl, PTy));
SDValue Addr = DAG.getNode(ISD::ADD, dl, PTy, Index, Table);
- if (Subtarget->isThumb2()) {
- // Thumb2 uses a two-level jump. That is, it jumps into the jump table
+ if (Subtarget->isThumb2() || (Subtarget->hasV8MBaselineOps() && Subtarget->isThumb())) {
+ // Thumb2 and ARMv8-M use a two-level jump. That is, it jumps into the jump table
// which does another jump to the destination. This also makes it easier
- // to translate it to TBB / TBH later.
+ // to translate it to TBB / TBH later (Thumb2 only).
// FIXME: This might not work if the function is extremely large.
return DAG.getNode(ARMISD::BR2_JT, dl, MVT::Other, Chain,
Addr, Op.getOperand(2), JTI);
SDValue ARMTargetLowering::LowerConstantFP(SDValue Op, SelectionDAG &DAG,
const ARMSubtarget *ST) const {
- if (!ST->hasVFP3())
- return SDValue();
-
bool IsDouble = Op.getValueType() == MVT::f64;
ConstantFPSDNode *CFP = cast<ConstantFPSDNode>(Op);
+ const APFloat &FPVal = CFP->getValueAPF();
+
+ // Prevent floating-point constants from using literal loads
+ // when execute-only is enabled.
+ if (ST->genExecuteOnly()) {
+ APInt INTVal = FPVal.bitcastToAPInt();
+ SDLoc DL(CFP);
+ if (IsDouble) {
+ SDValue Lo = DAG.getConstant(INTVal.trunc(32), DL, MVT::i32);
+ SDValue Hi = DAG.getConstant(INTVal.lshr(32).trunc(32), DL, MVT::i32);
+ if (!ST->isLittle())
+ std::swap(Lo, Hi);
+ return DAG.getNode(ARMISD::VMOVDRR, DL, MVT::f64, Lo, Hi);
+ } else {
+ return DAG.getConstant(INTVal, DL, MVT::i32);
+ }
+ }
+
+ if (!ST->hasVFP3())
+ return SDValue();
// Use the default (constant pool) lowering for double constants when we have
// an SP-only FPU
return SDValue();
// Try splatting with a VMOV.f32...
- const APFloat &FPVal = CFP->getValueAPF();
int ImmVal = IsDouble ? ARM_AM::getFP64Imm(FPVal) : ARM_AM::getFP32Imm(FPVal);
if (ImmVal != -1) {
switch (Op.getOpcode()) {
default: llvm_unreachable("Don't know how to custom lower this!");
case ISD::WRITE_REGISTER: return LowerWRITE_REGISTER(Op, DAG);
- case ISD::ConstantPool: return LowerConstantPool(Op, DAG);
+ case ISD::ConstantPool:
+ if (Subtarget->genExecuteOnly())
+ llvm_unreachable("execute-only should not generate constant pools");
+ return LowerConstantPool(Op, DAG);
case ISD::BlockAddress: return LowerBlockAddress(Op, DAG);
case ISD::GlobalAddress:
switch (Subtarget->getTargetTriple().getObjectFormat()) {
list<Predicate> Predicates = [IsThumb];
}
+// PseudoInst that's in ARMv8-M baseline (Somewhere between Thumb and Thumb2)
+class t2basePseudoInst<dag oops, dag iops, int sz, InstrItinClass itin,
+ list<dag> pattern>
+ : PseudoInst<oops, iops, itin, pattern> {
+ let Size = sz;
+ list<Predicate> Predicates = [IsThumb,HasV8MBaseline];
+}
+
// PseudoInst that's Thumb2-mode only.
class t2PseudoInst<dag oops, dag iops, int sz, InstrItinClass itin,
list<dag> pattern>
def IsLE : Predicate<"MF->getDataLayout().isLittleEndian()">;
def IsBE : Predicate<"MF->getDataLayout().isBigEndian()">;
+def GenExecuteOnly : Predicate<"Subtarget->genExecuteOnly()">;
+
//===----------------------------------------------------------------------===//
// ARM Flag Definitions.
}
let Size = 4, isNotDuplicable = 1, isIndirectBranch = 1 in {
-def t2BR_JT : t2PseudoInst<(outs),
+
+// available in both v8-M.Baseline and Thumb2 targets
+def t2BR_JT : t2basePseudoInst<(outs),
(ins GPR:$target, GPR:$index, i32imm:$jt),
0, IIC_Br,
[(ARMbr2jt GPR:$target, GPR:$index, tjumptable:$jt)]>,
MCOp = GetSymbolRef(MO, GetJTISymbol(MO.getIndex()));
break;
case MachineOperand::MO_ConstantPoolIndex:
+ if (Subtarget->genExecuteOnly())
+ llvm_unreachable("execute-only should not generate constant pools");
MCOp = GetSymbolRef(MO, GetCPISymbol(MO.getIndex()));
break;
case MachineOperand::MO_BlockAddress:
return *this;
}
+/// EnableExecuteOnly - Enables the generation of execute-only code on supported
+/// targets
+static cl::opt<bool>
+EnableExecuteOnly("arm-execute-only");
+
ARMFrameLowering *ARMSubtarget::initializeFrameLowering(StringRef CPU,
StringRef FS) {
ARMSubtarget &STI = initializeSubtargetDependencies(CPU, FS);
const ARMBaseTargetMachine &TM, bool IsLittle)
: ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps),
CPUString(CPU), IsLittle(IsLittle), TargetTriple(TT), Options(TM.Options),
- TM(TM), FrameLowering(initializeFrameLowering(CPU, FS)),
+ TM(TM), GenExecuteOnly(EnableExecuteOnly),
+ FrameLowering(initializeFrameLowering(CPU, FS)),
// At this point initializeSubtargetDependencies has been called so
// we can query directly.
InstrInfo(isThumb1Only()
// Assert this for now to make the change obvious.
assert(hasV6T2Ops() || !hasThumb2());
+ // Execute only support requires movt support
+ if (genExecuteOnly())
+ assert(hasV8MBaselineOps() && !NoMovt && "Cannot generate execute-only code for this target");
+
// Keep a pointer to static instruction cost data for the specified CPU.
SchedModel = getSchedModelForCPU(CPUString);
// immediates as it is inherently position independent, and may be out of
// range otherwise.
return !NoMovt && hasV8MBaselineOps() &&
- (isTargetWindows() || !MF.getFunction()->optForMinSize());
+ (isTargetWindows() || !MF.getFunction()->optForMinSize() || genExecuteOnly());
}
bool ARMSubtarget::useFastISel() const {
/// Generate calls via indirect call instructions.
bool GenLongCalls = false;
+ /// Generate code that does not contain data access to code sections.
+ bool GenExecuteOnly = false;
+
/// Target machine allowed unsafe FP math (such as use of NEON fp)
bool UnsafeFPMath = false;
bool useNaClTrap() const { return UseNaClTrap; }
bool useSjLjEH() const { return UseSjLjEH; }
bool genLongCalls() const { return GenLongCalls; }
+ bool genExecuteOnly() const { return GenExecuteOnly; }
bool hasFP16() const { return HasFP16; }
bool hasD16() const { return HasD16; }
void ARMElfTargetObjectFile::Initialize(MCContext &Ctx,
const TargetMachine &TM) {
- bool isAAPCS_ABI = static_cast<const ARMTargetMachine &>(TM).TargetABI ==
- ARMTargetMachine::ARMABI::ARM_ABI_AAPCS;
+ const ARMTargetMachine &ARM_TM = static_cast<const ARMTargetMachine &>(TM);
+ bool isAAPCS_ABI = ARM_TM.TargetABI == ARMTargetMachine::ARMABI::ARM_ABI_AAPCS;
+ genExecuteOnly = ARM_TM.getSubtargetImpl()->genExecuteOnly();
+
TargetLoweringObjectFileELF::Initialize(Ctx, TM);
InitializeELF(isAAPCS_ABI);
AttributesSection =
getContext().getELFSection(".ARM.attributes", ELF::SHT_ARM_ATTRIBUTES, 0);
+
+ // Make code section unreadable when in execute-only mode
+ if (genExecuteOnly) {
+ unsigned Type = ELF::SHT_PROGBITS;
+ unsigned Flags = ELF::SHF_EXECINSTR | ELF::SHF_ALLOC | ELF::SHF_ARM_PURECODE;
+ // Since we cannot modify flags for an existing section, we create a new
+ // section with the right flags, and use 0 as the unique ID for
+ // execute-only text
+ TextSection = Ctx.getELFSection(".text", Type, Flags, 0, "", 0U);
+ }
}
const MCExpr *ARMElfTargetObjectFile::getTTypeGlobalReference(
return MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_ARM_TLSLDO,
getContext());
}
+
+MCSection *
+ARMElfTargetObjectFile::getExplicitSectionGlobal(const GlobalObject *GO,
+ SectionKind SK, const TargetMachine &TM) const {
+ // Set execute-only access for the explicit section
+ if (genExecuteOnly && SK.isText())
+ SK = SectionKind::getExecuteOnly();
+
+ return TargetLoweringObjectFileELF::getExplicitSectionGlobal(GO, SK, TM);
+}
+
+MCSection *
+ARMElfTargetObjectFile::SelectSectionForGlobal(const GlobalObject *GO,
+ SectionKind SK, const TargetMachine &TM) const {
+ // Place the global in the execute-only text section
+ if (genExecuteOnly && SK.isText())
+ SK = SectionKind::getExecuteOnly();
+
+ return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, SK, TM);
+}
class TargetMachine;
class ARMElfTargetObjectFile : public TargetLoweringObjectFileELF {
+ mutable bool genExecuteOnly = false;
protected:
const MCSection *AttributesSection;
public:
/// \brief Describe a TLS variable address within debug info.
const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
+
+ MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
+ const TargetMachine &TM) const override;
+
+ MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
+ const TargetMachine &TM) const override;
};
} // end namespace llvm
if (getParser().parseExpression(SubExprVal))
return true;
E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1);
+
+ // execute-only: we assume that assembly programmers know what they are
+ // doing and allow literal pool creation here
Operands.push_back(ARMOperand::CreateConstantPoolImm(SubExprVal, S, E));
return false;
}
bool CanChangeCC, const TargetInstrInfo &TII,
const ARMBaseRegisterInfo &MRI, unsigned MIFlags = MachineInstr::NoFlags) {
MachineFunction &MF = *MBB.getParent();
+ const ARMSubtarget &ST = MF.getSubtarget<ARMSubtarget>();
bool isHigh = !isARMLowRegister(DestReg) ||
(BaseReg != 0 && !isARMLowRegister(BaseReg));
bool isSub = false;
AddDefaultT1CC(BuildMI(MBB, MBBI, dl, TII.get(ARM::tRSB), LdReg))
.addReg(LdReg, RegState::Kill)
.setMIFlags(MIFlags);
+ } else if (ST.genExecuteOnly()) {
+ BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm), LdReg)
+ .addImm(NumBytes).setMIFlags(MIFlags);
} else
MRI.emitLoadConstPool(MBB, MBBI, dl, LdReg, 0, NumBytes, ARMCC::AL, 0,
MIFlags);
unsigned TmpReg = MI.getOperand(0).getReg();
bool UseRR = false;
if (Opcode == ARM::tLDRspi) {
- if (FrameReg == ARM::SP)
+ if (FrameReg == ARM::SP || STI.genExecuteOnly())
emitThumbRegPlusImmInReg(MBB, II, dl, TmpReg, FrameReg,
Offset, false, TII, *this);
else {
bool UseRR = false;
if (Opcode == ARM::tSTRspi) {
- if (FrameReg == ARM::SP)
+ if (FrameReg == ARM::SP || STI.genExecuteOnly())
emitThumbRegPlusImmInReg(MBB, II, dl, VReg, FrameReg,
Offset, false, TII, *this);
else {
; RUN: llc -mtriple=armv7 -mattr=+neon -mcpu=cortex-a8 %s -o - | FileCheck --check-prefix=CHECK-NONEONFP %s
; RUN: llc -mtriple=armv7 -mattr=-neon -mcpu=cortex-a8 %s -o - | FileCheck --check-prefix=CHECK-NONEON %s
+; RUN: llc -mtriple=thumbv7m -mcpu=cortex-m4 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-NO-XO %s
+
+; RUN: llc -mtriple=thumbv7m -arm-execute-only -mcpu=cortex-m4 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-XO-FLOAT --check-prefix=CHECK-XO-DOUBLE %s
+
+; RUN: llc -mtriple=thumbv7meb -arm-execute-only -mcpu=cortex-m4 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-XO-FLOAT --check-prefix=CHECK-XO-DOUBLE-BE %s
+
+; RUN: llc -mtriple=thumbv8m.main -mattr=fp-armv8 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-NO-XO %s
+
+; RUN: llc -mtriple=thumbv8m.main -arm-execute-only -mattr=fp-armv8 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-XO-FLOAT --check-prefix=CHECK-XO-DOUBLE %s
+
+; RUN: llc -mtriple=thumbv8m.maineb -arm-execute-only -mattr=fp-armv8 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-XO-FLOAT --check-prefix=CHECK-XO-DOUBLE-BE %s
+
+
define arm_aapcs_vfpcc float @test_vmov_f32() {
; CHECK-LABEL: test_vmov_f32:
; CHECK: vmov.f32 d0, #1.0
; CHECK-NONEON-LABEL: test_vmov_imm:
; CHECK-NONEON: vldr s0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-NO-XO-LABEL: test_vmov_imm:
+; CHECK-NO-XO: vldr s0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-FLOAT-LABEL: test_vmov_imm:
+; CHECK-XO-FLOAT: movs [[REG:r[0-9]+]], #0
+; CHECK-XO-FLOAT: vmov {{s[0-9]+}}, [[REG]]
+; CHECK-XO-FLOAT-NOT: vldr
ret float 0.0
}
; CHECK-NONEON-LABEL: test_vmvn_imm:
; CHECK-NONEON: vldr s0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-NO-XO-LABEL: test_vmvn_imm:
+; CHECK-NO-XO: vldr s0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-FLOAT-LABEL: test_vmvn_imm:
+; CHECK-XO-FLOAT: mvn [[REG:r[0-9]+]], #-1342177280
+; CHECK-XO-FLOAT: vmov {{s[0-9]+}}, [[REG]]
+; CHECK-XO-FLOAT-NOT: vldr
ret float 8589934080.0
}
; CHECK-NONEON-LABEL: test_vmov_double_imm:
; CHECK-NONEON: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-NO-XO-LABEL: test_vmov_double_imm:
+; CHECK-NO-XO: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-DOUBLE-LABEL: test_vmov_double_imm:
+; CHECK-XO-DOUBLE: movs [[REG:r[0-9]+]], #0
+; CHECK-XO-DOUBLE: vmov {{d[0-9]+}}, [[REG]], [[REG]]
+; CHECK-XO-DOUBLE-NOT: vldr
+
+; CHECK-XO-DOUBLE-BE-LABEL: test_vmov_double_imm:
+; CHECK-XO-DOUBLE-BE: movs [[REG:r[0-9]+]], #0
+; CHECK-XO-DOUBLE-BE: vmov {{d[0-9]+}}, [[REG]], [[REG]]
+; CHECK-XO-DOUBLE-NOT: vldr
ret double 0.0
}
; CHECK-NONEON-LABEL: test_vmvn_double_imm:
; CHECK-NONEON: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-NO-XO-LABEL: test_vmvn_double_imm:
+; CHECK-NO-XO: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-DOUBLE-LABEL: test_vmvn_double_imm:
+; CHECK-XO-DOUBLE: mvn [[REG:r[0-9]+]], #-1342177280
+; CHECK-XO-DOUBLE: vmov {{d[0-9]+}}, [[REG]], [[REG]]
+; CHECK-XO-DOUBLE-NOT: vldr
+
+; CHECK-XO-DOUBLE-BE-LABEL: test_vmvn_double_imm:
+; CHECK-XO-DOUBLE-BE: mvn [[REG:r[0-9]+]], #-1342177280
+; CHECK-XO-DOUBLE-BE: vmov {{d[0-9]+}}, [[REG]], [[REG]]
+; CHECK-XO-DOUBLE-BE-NOT: vldr
ret double 0x4fffffff4fffffff
}
; CHECK-NONEON-LABEL: test_notvmvn_double_imm:
; CHECK-NONEON: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-NO-XO-LABEL: test_notvmvn_double_imm:
+; CHECK-NO-XO: vldr d0, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-DOUBLE-LABEL: test_notvmvn_double_imm:
+; CHECK-XO-DOUBLE: mvn [[REG1:r[0-9]+]], #-1342177280
+; CHECK-XO-DOUBLE: mov.w [[REG2:r[0-9]+]], #-1
+; CHECK-XO-DOUBLE: vmov {{d[0-9]+}}, [[REG2]], [[REG1]]
+; CHECK-XO-DOUBLE-NOT: vldr
+
+; CHECK-XO-DOUBLE-BE-LABEL: test_notvmvn_double_imm:
+; CHECK-XO-DOUBLE-BE: mov.w [[REG1:r[0-9]+]], #-1
+; CHECK-XO-DOUBLE-BE: mvn [[REG2:r[0-9]+]], #-1342177280
+; CHECK-XO-DOUBLE-BE: vmov {{d[0-9]+}}, [[REG2]], [[REG1]]
+; CHECK-XO-DOUBLE-BE-NOT: vldr
ret double 0x4fffffffffffffff
}
+
+define arm_aapcs_vfpcc float @lower_const_f32_xo() {
+; CHECK-NO-XO-LABEL: lower_const_f32_xo
+; CHECK-NO-XO: vldr {{s[0-9]+}}, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-FLOAT-LABEL: lower_const_f32_xo
+; CHECK-XO-FLOAT: movw [[REG:r[0-9]+]], #29884
+; CHECK-XO-FLOAT: movt [[REG]], #16083
+; CHECK-XO-FLOAT: vmov {{s[0-9]+}}, [[REG]]
+; CHECK-XO-FLOAT-NOT: vldr
+ ret float 0x3FDA6E9780000000
+}
+
+define arm_aapcs_vfpcc double @lower_const_f64_xo() {
+; CHECK-NO-XO-LABEL: lower_const_f64_xo
+; CHECK-NO-XO: vldr {{d[0-9]+}}, {{.?LCPI[0-9]+_[0-9]+}}
+
+; CHECK-XO-DOUBLE-LABEL: lower_const_f64_xo
+; CHECK-XO-DOUBLE: movw [[REG1:r[0-9]+]], #6291
+; CHECK-XO-DOUBLE: movw [[REG2:r[0-9]+]], #27263
+; CHECK-XO-DOUBLE: movt [[REG1]], #16340
+; CHECK-XO-DOUBLE: movt [[REG2]], #29884
+; CHECK-XO-DOUBLE: vmov {{d[0-9]+}}, [[REG2]], [[REG1]]
+; CHECK-XO-DOUBLE-NOT: vldr
+
+; CHECK-XO-DOUBLE-BE-LABEL: lower_const_f64_xo
+; CHECK-XO-DOUBLE-BE: movw [[REG1:r[0-9]+]], #27263
+; CHECK-XO-DOUBLE-BE: movw [[REG2:r[0-9]+]], #6291
+; CHECK-XO-DOUBLE-BE: movt [[REG1]], #29884
+; CHECK-XO-DOUBLE-BE: movt [[REG2]], #16340
+; CHECK-XO-DOUBLE-BE: vmov {{d[0-9]+}}, [[REG2]], [[REG1]]
+; CHECK-XO-DOUBLE-BE-NOT: vldr
+ ret double 3.140000e-01
+}
--- /dev/null
+; RUN: llc < %s -mtriple=thumbv7m -arm-execute-only -O0 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-SUBW-ADDW %s
+; RUN: llc < %s -mtriple=thumbv8m.base -arm-execute-only -O0 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-MOVW-MOVT-ADD %s
+; RUN: llc < %s -mtriple=thumbv8m.main -arm-execute-only -O0 %s -o - \
+; RUN: | FileCheck --check-prefix=CHECK-SUBW-ADDW %s
+
+define i8 @test_big_stack_frame() {
+; CHECK-SUBW-ADDW-LABEL: test_big_stack_frame:
+; CHECK-SUBW-ADDW-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-SUBW-ADDW: sub.w sp, sp, #65536
+; CHECK-SUBW-ADDW-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-SUBW-ADDW: add.w [[REG1:r[0-9]+]], sp, #255
+; CHECK-SUBW-ADDW: add.w {{r[0-9]+}}, [[REG1]], #65280
+; CHECK-SUBW-ADDW-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-SUBW-ADDW: add.w lr, sp, #61440
+; CHECK-SUBW-ADDW-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-SUBW-ADDW: add.w sp, sp, #65536
+
+; CHECK-MOVW-MOVT-ADD-LABEL: test_big_stack_frame:
+; CHECK-MOVW-MOVT-ADD-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-MOVW-MOVT-ADD: movw [[REG1:r[0-9]+]], #0
+; CHECK-MOVW-MOVT-ADD: movt [[REG1]], #65535
+; CHECK-MOVW-MOVT-ADD: add sp, [[REG1]]
+; CHECK-MOVW-MOVT-ADD-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-MOVW-MOVT-ADD: movw [[REG2:r[0-9]+]], #65532
+; CHECK-MOVW-MOVT-ADD: movt [[REG2]], #0
+; CHECK-MOVW-MOVT-ADD: add [[REG2]], sp
+; CHECK-MOVW-MOVT-ADD-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-MOVW-MOVT-ADD: movw [[REG3:r[0-9]+]], #65532
+; CHECK-MOVW-MOVT-ADD: movt [[REG3]], #0
+; CHECK-MOVW-MOVT-ADD: add [[REG3]], sp
+; CHECK-MOVW-MOVT-ADD-NOT: ldr {{r[0-9]+}}, .{{.*}}
+; CHECK-MOVW-MOVT-ADD: movw [[REG4:r[0-9]+]], #0
+; CHECK-MOVW-MOVT-ADD: movt [[REG4]], #1
+; CHECK-MOVW-MOVT-ADD: add sp, [[REG4]]
+
+entry:
+ %s1 = alloca i8
+ %buffer = alloca [65528 x i8], align 1
+ call void @foo(i8* %s1)
+ %load = load i8, i8* %s1
+ ret i8 %load
+}
+
+declare void @foo(i8*)
--- /dev/null
+; RUN: llc < %s -mtriple=thumbv7m -arm-execute-only %s -o - | FileCheck %s
+; RUN: llc < %s -mtriple=thumbv8m.base -arm-execute-only %s -o - | FileCheck %s
+; RUN: llc < %s -mtriple=thumbv8m.main -arm-execute-only %s -o - | FileCheck %s
+
+; CHECK: .section .text,"axy",%progbits,unique,0
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK: .globl test_SectionForGlobal
+; CHECK: .type test_SectionForGlobal,%function
+define void @test_SectionForGlobal() {
+entry:
+ ret void
+}
+
+; CHECK: .section .test,"axy",%progbits
+; CHECK-NOT: .section
+; CHECK-NOT: .text
+; CHECK: .globl test_ExplicitSectionForGlobal
+; CHECK: .type test_ExplicitSectionForGlobal,%function
+define void @test_ExplicitSectionForGlobal() section ".test" {
+entry:
+ ret void
+}
--- /dev/null
+; RUN: llc -mtriple=thumbv8m.base-eabi -arm-execute-only %s -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-T2BASE %s
+; RUN: llc -mtriple=thumbv7m-eabi -arm-execute-only %s -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-T2 %s
+; RUN: llc -mtriple=thumbv8m.main-eabi -arm-execute-only %s -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-T2 %s
+
+@var = global i32 0
+
+define i32 @global() minsize {
+; CHECK-LABEL: global:
+; CHECK: movw [[GLOBDEST:r[0-9]+]], :lower16:var
+; CHECK: movt [[GLOBDEST]], :upper16:var
+
+ %val = load i32, i32* @var
+ ret i32 %val
+}
+
+define i32 @jump_table(i32 %c, i32 %a, i32 %b) #0 {
+; CHECK-LABEL: jump_table:
+; CHECK-T2: adr.w [[REG_JT:r[0-9]+]], .LJTI1_0
+; CHECK-T2: add.w [[REG_ENTRY:r[0-9]+]], [[REG_JT]], {{r[0-9]+}}, lsl #2
+; CHECK-T2: mov pc, [[REG_ENTRY]]
+
+; CHECK-T2BASE: lsls [[REG_OFFSET:r[0-9]+]], {{r[0-9]+}}, #2
+; CHECK-T2BASE: adr [[REG_JT:r[0-9]+]], .LJTI1_0
+; CHECK-T2BASE: adds [[REG_ENTRY:r[0-9]+]], [[REG_OFFSET]], [[REG_JT]]
+; CHECK-T2BASE: mov pc, [[REG_ENTRY]]
+
+; CHECK-LABEL: .LJTI1_0:
+; CHECK-NEXT: b.w
+; CHECK-NEXT: b.w
+; CHECK-NEXT: b.w
+; CHECK-NEXT: b.w
+; CHECK-NEXT: b.w
+; CHECK-NEXT: b.w
+
+entry:
+ switch i32 %c, label %return [
+ i32 1, label %sw.bb
+ i32 2, label %sw.bb1
+ i32 3, label %sw.bb3
+ i32 4, label %sw.bb4
+ i32 5, label %sw.bb6
+ i32 6, label %sw.bb8
+ ]
+
+sw.bb: ; preds = %entry
+ %add = add nsw i32 %a, 6
+ br label %return
+
+sw.bb1: ; preds = %entry
+ %add2 = add nsw i32 %a, 4
+ br label %return
+
+sw.bb3: ; preds = %entry
+ %sub = add nsw i32 %a, -3
+ br label %return
+
+sw.bb4: ; preds = %entry
+ %add5 = add nsw i32 %b, 5
+ br label %return
+
+sw.bb6: ; preds = %entry
+ %add7 = add nsw i32 %a, 1
+ br label %return
+
+sw.bb8: ; preds = %entry
+ %add9 = add nsw i32 %a, 2
+ br label %return
+
+return: ; preds = %entry, %sw.bb8, %sw.bb6, %sw.bb4, %sw.bb3, %sw.bb1, %sw.bb
+ %retval.0 = phi i32 [ %add9, %sw.bb8 ], [ %add7, %sw.bb6 ], [ %add5, %sw.bb4 ], [ %sub, %sw.bb3 ], [ %add2, %sw.bb1 ], [ %add, %sw.bb ], [ 0, %entry ]
+ ret i32 %retval.0
+}
+
+@.str = private unnamed_addr constant [4 x i8] c"FOO\00", align 1
+
+define hidden i8* @string_literal() {
+entry:
+; CHECK-LABEL: string_literal:
+; CHECK-NOT: .asciz
+; CHECK: .fnend
+ ret i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0)
+}
--- /dev/null
+// RUN: llvm-mc -filetype=obj -triple thumbv7m-arm-linux-gnu %s -o - \
+// RUN: | llvm-readobj -s -t | FileCheck %s
+
+ .section .text,"axy",%progbits,unique,0
+ .globl foo
+ .align 2
+ .type foo,%function
+ .code 16
+ .thumb_func
+foo:
+ .fnstart
+ bx lr
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .fnend
+
+ .section ".note.GNU-stack","",%progbits
+
+
+// CHECK: Section {
+// CHECK: Name: .text (16)
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [ (0x6)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_EXECINSTR (0x4)
+// CHECK-NEXT: ]
+// CHECK: Size: 0
+// CHECK: }
+
+// CHECK: Section {
+// CHECK: Name: .text (16)
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [ (0x20000006)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_ARM_PURECODE (0x20000000)
+// CHECK-NEXT: SHF_EXECINSTR (0x4)
+// CHECK-NEXT: ]
+// CHECK: Size: 2
+// CHECK: }
+
+// CHECK: Symbol {
+// CHECK: Name: foo (22)
+// CHECK: Section: .text (0x3)
+// CHECK: }
LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_AGENT)
};
+static const EnumEntry<unsigned> ElfARMSectionFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_ARM_PURECODE)
+};
+
static const EnumEntry<unsigned> ElfHexagonSectionFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, SHF_HEX_GPREL)
};
SectionFlags.insert(SectionFlags.end(), std::begin(ElfAMDGPUSectionFlags),
std::end(ElfAMDGPUSectionFlags));
break;
+ case EM_ARM:
+ SectionFlags.insert(SectionFlags.end(), std::begin(ElfARMSectionFlags),
+ std::end(ElfARMSectionFlags));
+ break;
case EM_HEXAGON:
SectionFlags.insert(SectionFlags.end(),
std::begin(ElfHexagonSectionFlags),