Skip to content

Commit

Permalink
[ARM] Implement execute-only support in CodeGen
Browse files Browse the repository at this point in the history
This implements execute-only support for ARM code generation, which
prevents the compiler from generating data accesses to code sections.
The following changes are involved:

* Add the CodeGen option "-arm-execute-only" to the ARM code generator.
* Add the clang flag "-mexecute-only" as well as the GCC-compatible
  alias "-mpure-code" to enable this option.
* When enabled, literal pools are replaced with MOVW/MOVT instructions,
  with VMOV used in addition for floating-point literals. As the MOVT
  instruction is required, execute-only support is only available in
  Thumb mode for targets supporting ARMv8-M baseline or Thumb2.
* Jump tables are placed in data sections when in execute-only mode.
* The execute-only text section is assigned section ID 0, and is
  marked as unreadable with the SHF_ARM_PURECODE flag with symbol 'y'.
  This also overrides selection of ELF sections for globals.

llvm-svn: 289784
  • Loading branch information
Prakhar Bahuguna committed Dec 15, 2016
1 parent 342beeb commit 52a7dd7
Show file tree
Hide file tree
Showing 27 changed files with 455 additions and 20 deletions.
2 changes: 1 addition & 1 deletion llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace llvm {

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 =
Expand Down
9 changes: 8 additions & 1 deletion llvm/include/llvm/MC/SectionKind.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class SectionKind {
/// 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.
Expand Down Expand Up @@ -112,7 +115,10 @@ class SectionKind {
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() ||
Expand Down Expand Up @@ -172,6 +178,7 @@ class SectionKind {

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);
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Support/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,9 @@ enum : unsigned {
// 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,
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ static unsigned getELFSectionFlags(SectionKind K) {
if (K.isText())
Flags |= ELF::SHF_EXECINSTR;

if (K.isExecuteOnly())
Flags |= ELF::SHF_ARM_PURECODE;

if (K.isWriteable())
Flags |= ELF::SHF_WRITE;

Expand Down Expand Up @@ -312,6 +315,9 @@ selectELFSectionForGlobal(MCContext &Ctx, const GlobalObject *GO,
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);
}
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/MC/MCContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,9 @@ MCSectionELF *MCContext::getELFSection(const Twine &Section, unsigned Type,
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();
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/MC/MCParser/ELFAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ static unsigned parseSectionFlags(StringRef flagsStr, bool *UseLastGroup) {
case 'd':
flags |= ELF::XCORE_SHF_DP_SECTION;
break;
case 'y':
flags |= ELF::SHF_ARM_PURECODE;
break;
case 'G':
flags |= ELF::SHF_GROUP;
break;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/MC/MCSectionELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ void MCSectionELF::PrintSwitchToSection(const MCAsmInfo &MAI,
OS << 'c';
if (Flags & ELF::XCORE_SHF_DP_SECTION)
OS << 'd';
if (Flags & ELF::SHF_ARM_PURECODE)
OS << 'y';

OS << '"';

Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/ObjectYAML/ELFYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ void ScalarBitSetTraits<ELFYAML::ELF_SHF>::bitset(IO &IO,
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)
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ void ARMAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
break;
}
case MachineOperand::MO_ConstantPoolIndex:
if (Subtarget->genExecuteOnly())
llvm_unreachable("execute-only should not generate constant pools");
GetCPISymbol(MO.getIndex())->print(O, MAI);
break;
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ bool ARMConstantIslands::runOnMachineFunction(MachineFunction &mf) {
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.
Expand Down
38 changes: 29 additions & 9 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3152,7 +3152,8 @@ SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op,
(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;

Expand Down Expand Up @@ -4492,10 +4493,10 @@ SDValue ARMTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) const {
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);
Expand Down Expand Up @@ -5571,19 +5572,35 @@ static SDValue isNEONModifiedImm(uint64_t SplatBits, uint64_t SplatUndef,

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
if (IsDouble && Subtarget->isFPOnlySP())
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) {
Expand Down Expand Up @@ -7617,7 +7634,10 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
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()) {
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/ARM/ARMInstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ class tPseudoInst<dag oops, dag iops, int sz, InstrItinClass itin,
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>
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ def DontUseVMOVSR : Predicate<"!Subtarget->preferVMOVSR() &&"
def IsLE : Predicate<"MF->getDataLayout().isLittleEndian()">;
def IsBE : Predicate<"MF->getDataLayout().isBigEndian()">;

def GenExecuteOnly : Predicate<"Subtarget->genExecuteOnly()">;

//===----------------------------------------------------------------------===//
// ARM Flag Definitions.

Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Target/ARM/ARMInstrThumb2.td
Original file line number Diff line number Diff line change
Expand Up @@ -3381,7 +3381,9 @@ def t2B : T2I<(outs), (ins thumb_br_target:$target), IIC_Br,
}

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)]>,
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMMCInstLower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ bool ARMAsmPrinter::lowerOperand(const MachineOperand &MO,
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:
Expand Down
14 changes: 12 additions & 2 deletions llvm/lib/Target/ARM/ARMSubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ ARMSubtarget &ARMSubtarget::initializeSubtargetDependencies(StringRef CPU,
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);
Expand All @@ -90,7 +95,8 @@ ARMSubtarget::ARMSubtarget(const Triple &TT, const std::string &CPU,
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()
Expand Down Expand Up @@ -169,6 +175,10 @@ void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {
// 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);

Expand Down Expand Up @@ -353,7 +363,7 @@ bool ARMSubtarget::useMovt(const MachineFunction &MF) const {
// 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 {
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/ARM/ARMSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
/// 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;

Expand Down Expand Up @@ -494,6 +497,7 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
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; }
Expand Down
36 changes: 34 additions & 2 deletions llvm/lib/Target/ARM/ARMTargetObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ using namespace dwarf;

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);

Expand All @@ -38,6 +40,16 @@ void ARMElfTargetObjectFile::Initialize(MCContext &Ctx,

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(
Expand All @@ -58,3 +70,23 @@ getDebugThreadLocalSymbol(const MCSymbol *Sym) const {
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);
}
7 changes: 7 additions & 0 deletions llvm/lib/Target/ARM/ARMTargetObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class MCContext;
class TargetMachine;

class ARMElfTargetObjectFile : public TargetLoweringObjectFileELF {
mutable bool genExecuteOnly = false;
protected:
const MCSection *AttributesSection;
public:
Expand All @@ -36,6 +37,12 @@ class ARMElfTargetObjectFile : public TargetLoweringObjectFileELF {

/// \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
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5453,6 +5453,9 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) {
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;
}
Expand Down
Loading

0 comments on commit 52a7dd7

Please sign in to comment.