Index: include/llvm/CodeGen/TargetLoweringObjectFileImpl.h =================================================================== --- include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -33,7 +33,7 @@ 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 = Index: include/llvm/MC/SectionKind.h =================================================================== --- include/llvm/MC/SectionKind.h +++ include/llvm/MC/SectionKind.h @@ -28,6 +28,9 @@ /// 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. @@ -112,7 +115,10 @@ 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() || @@ -172,6 +178,7 @@ 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); Index: include/llvm/Support/ELF.h =================================================================== --- include/llvm/Support/ELF.h +++ include/llvm/Support/ELF.h @@ -804,6 +804,9 @@ // 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, Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -181,6 +181,9 @@ if (K.isText()) Flags |= ELF::SHF_EXECINSTR; + if (K.isExecuteOnly()) + Flags |= ELF::SHF_ARM_PURECODE; + if (K.isWriteable()) Flags |= ELF::SHF_WRITE; @@ -312,6 +315,9 @@ 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); } Index: lib/MC/MCContext.cpp =================================================================== --- lib/MC/MCContext.cpp +++ lib/MC/MCContext.cpp @@ -368,7 +368,9 @@ 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(); Index: lib/MC/MCParser/ELFAsmParser.cpp =================================================================== --- lib/MC/MCParser/ELFAsmParser.cpp +++ lib/MC/MCParser/ELFAsmParser.cpp @@ -293,6 +293,9 @@ case 'd': flags |= ELF::XCORE_SHF_DP_SECTION; break; + case 'y': + flags |= ELF::SHF_ARM_PURECODE; + break; case 'G': flags |= ELF::SHF_GROUP; break; Index: lib/MC/MCSectionELF.cpp =================================================================== --- lib/MC/MCSectionELF.cpp +++ lib/MC/MCSectionELF.cpp @@ -110,6 +110,8 @@ OS << 'c'; if (Flags & ELF::XCORE_SHF_DP_SECTION) OS << 'd'; + if (Flags & ELF::SHF_ARM_PURECODE) + OS << 'y'; OS << '"'; Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -423,6 +423,9 @@ 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) Index: lib/Target/ARM/ARMAsmPrinter.cpp =================================================================== --- lib/Target/ARM/ARMAsmPrinter.cpp +++ lib/Target/ARM/ARMAsmPrinter.cpp @@ -232,6 +232,8 @@ break; } case MachineOperand::MO_ConstantPoolIndex: + if (Subtarget->genExecuteOnly()) + llvm_unreachable("execute-only should not generate constant pools"); GetCPISymbol(MO.getIndex())->print(O, MAI); break; } Index: lib/Target/ARM/ARMConstantIslandPass.cpp =================================================================== --- lib/Target/ARM/ARMConstantIslandPass.cpp +++ lib/Target/ARM/ARMConstantIslandPass.cpp @@ -423,7 +423,7 @@ 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. Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -3150,7 +3150,8 @@ (isa(GV) && cast(GV)->isConstant()) || isa(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; @@ -4490,10 +4491,10 @@ 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); @@ -5569,11 +5570,28 @@ 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(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 @@ -5581,7 +5599,6 @@ 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) { @@ -7594,7 +7611,10 @@ 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()) { Index: lib/Target/ARM/ARMInstrFormats.td =================================================================== --- lib/Target/ARM/ARMInstrFormats.td +++ lib/Target/ARM/ARMInstrFormats.td @@ -398,6 +398,14 @@ list Predicates = [IsThumb]; } +// PseudoInst that's in ARMv8-M baseline (Somewhere between Thumb and Thumb2) +class t2basePseudoInst pattern> + : PseudoInst { + let Size = sz; + list Predicates = [IsThumb,HasV8MBaseline]; +} + // PseudoInst that's Thumb2-mode only. class t2PseudoInst pattern> Index: lib/Target/ARM/ARMInstrInfo.td =================================================================== --- lib/Target/ARM/ARMInstrInfo.td +++ lib/Target/ARM/ARMInstrInfo.td @@ -330,6 +330,8 @@ def IsLE : Predicate<"MF->getDataLayout().isLittleEndian()">; def IsBE : Predicate<"MF->getDataLayout().isBigEndian()">; +def GenExecuteOnly : Predicate<"Subtarget->genExecuteOnly()">; + //===----------------------------------------------------------------------===// // ARM Flag Definitions. Index: lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- lib/Target/ARM/ARMInstrThumb2.td +++ lib/Target/ARM/ARMInstrThumb2.td @@ -3381,7 +3381,9 @@ } 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)]>, Index: lib/Target/ARM/ARMMCInstLower.cpp =================================================================== --- lib/Target/ARM/ARMMCInstLower.cpp +++ lib/Target/ARM/ARMMCInstLower.cpp @@ -91,6 +91,8 @@ 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: Index: lib/Target/ARM/ARMSubtarget.h =================================================================== --- lib/Target/ARM/ARMSubtarget.h +++ lib/Target/ARM/ARMSubtarget.h @@ -301,6 +301,9 @@ /// 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; @@ -494,6 +497,7 @@ 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; } Index: lib/Target/ARM/ARMSubtarget.cpp =================================================================== --- lib/Target/ARM/ARMSubtarget.cpp +++ lib/Target/ARM/ARMSubtarget.cpp @@ -76,6 +76,11 @@ return *this; } +/// EnableExecuteOnly - Enables the generation of execute-only code on supported +/// targets +static cl::opt +EnableExecuteOnly("arm-execute-only"); + ARMFrameLowering *ARMSubtarget::initializeFrameLowering(StringRef CPU, StringRef FS) { ARMSubtarget &STI = initializeSubtargetDependencies(CPU, FS); @@ -90,7 +95,8 @@ 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() @@ -169,6 +175,10 @@ // 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); @@ -353,7 +363,7 @@ // 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 { Index: lib/Target/ARM/ARMTargetObjectFile.h =================================================================== --- lib/Target/ARM/ARMTargetObjectFile.h +++ lib/Target/ARM/ARMTargetObjectFile.h @@ -18,6 +18,7 @@ class TargetMachine; class ARMElfTargetObjectFile : public TargetLoweringObjectFileELF { + mutable bool genExecuteOnly = false; protected: const MCSection *AttributesSection; public: @@ -36,6 +37,12 @@ /// \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 Index: lib/Target/ARM/ARMTargetObjectFile.cpp =================================================================== --- lib/Target/ARM/ARMTargetObjectFile.cpp +++ lib/Target/ARM/ARMTargetObjectFile.cpp @@ -27,8 +27,10 @@ void ARMElfTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { - bool isAAPCS_ABI = static_cast(TM).TargetABI == - ARMTargetMachine::ARMABI::ARM_ABI_AAPCS; + const ARMTargetMachine &ARM_TM = static_cast(TM); + bool isAAPCS_ABI = ARM_TM.TargetABI == ARMTargetMachine::ARMABI::ARM_ABI_AAPCS; + genExecuteOnly = ARM_TM.getSubtargetImpl()->genExecuteOnly(); + TargetLoweringObjectFileELF::Initialize(Ctx, TM); InitializeELF(isAAPCS_ABI); @@ -38,6 +40,16 @@ 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( @@ -58,3 +70,23 @@ 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); +} Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -5453,6 +5453,9 @@ 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; } Index: lib/Target/ARM/ThumbRegisterInfo.cpp =================================================================== --- lib/Target/ARM/ThumbRegisterInfo.cpp +++ lib/Target/ARM/ThumbRegisterInfo.cpp @@ -126,6 +126,7 @@ bool CanChangeCC, const TargetInstrInfo &TII, const ARMBaseRegisterInfo &MRI, unsigned MIFlags = MachineInstr::NoFlags) { MachineFunction &MF = *MBB.getParent(); + const ARMSubtarget &ST = MF.getSubtarget(); bool isHigh = !isARMLowRegister(DestReg) || (BaseReg != 0 && !isARMLowRegister(BaseReg)); bool isSub = false; @@ -154,6 +155,9 @@ 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); @@ -570,7 +574,7 @@ 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 { @@ -594,7 +598,7 @@ 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 { Index: test/CodeGen/ARM/constantfp.ll =================================================================== --- test/CodeGen/ARM/constantfp.ll +++ test/CodeGen/ARM/constantfp.ll @@ -2,6 +2,25 @@ ; 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 @@ -16,6 +35,14 @@ ; 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 } @@ -25,6 +52,14 @@ ; 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 } @@ -44,6 +79,19 @@ ; 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 } @@ -53,6 +101,19 @@ ; 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 } @@ -64,5 +125,54 @@ ; 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 +} Index: test/CodeGen/ARM/execute-only-big-stack-frame.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/execute-only-big-stack-frame.ll @@ -0,0 +1,46 @@ +; 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*) Index: test/CodeGen/ARM/execute-only-section.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/execute-only-section.ll @@ -0,0 +1,23 @@ +; 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 +} Index: test/CodeGen/ARM/execute-only.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/execute-only.ll @@ -0,0 +1,82 @@ +; 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) +} Index: test/MC/ELF/ARM/execute-only-section.s =================================================================== --- /dev/null +++ test/MC/ELF/ARM/execute-only-section.s @@ -0,0 +1,44 @@ +// 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: } Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -1067,6 +1067,10 @@ LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_AGENT) }; +static const EnumEntry ElfARMSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_ARM_PURECODE) +}; + static const EnumEntry ElfHexagonSectionFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, SHF_HEX_GPREL) }; @@ -3564,6 +3568,10 @@ 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),