Skip to content

Commit

Permalink
[ARM] Support -mexecute-only with -mlong-calls.
Browse files Browse the repository at this point in the history
Instead of using constant pools, use movw movt pair.

Differential Revision: https://reviews.llvm.org/D136203
  • Loading branch information
ZhiyaoMa98 authored and efriedma-quic committed Oct 24, 2022
1 parent d30727f commit 7e8af2f
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 48 deletions.
12 changes: 3 additions & 9 deletions clang/lib/Driver/ToolChains/Arch/ARM.cpp
Expand Up @@ -776,21 +776,15 @@ void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple,
// This only makes sense for the compiler, not for the assembler.
if (!ForAS) {
// Supported only on ARMv6T2 and ARMv7 and above.
// Cannot be combined with -mno-movt or -mlong-calls
// Cannot be combined with -mno-movt.
if (Arg *A = Args.getLastArg(options::OPT_mexecute_only, options::OPT_mno_execute_only)) {
if (A->getOption().matches(options::OPT_mexecute_only)) {
if (getARMSubArchVersionNumber(Triple) < 7 &&
llvm::ARM::parseArch(Triple.getArchName()) != llvm::ARM::ArchKind::ARMV6T2)
D.Diag(diag::err_target_unsupported_execute_only) << Triple.getArchName();
else if (Arg *B = Args.getLastArg(options::OPT_mno_movt))
D.Diag(diag::err_opt_not_valid_with_opt) << A->getAsString(Args) << B->getAsString(Args);
// Long calls create constant pool entries and have not yet been fixed up
// to play nicely with execute-only. Hence, they cannot be used in
// execute-only code for now
else if (Arg *B = Args.getLastArg(options::OPT_mlong_calls, options::OPT_mno_long_calls)) {
if (B->getOption().matches(options::OPT_mlong_calls))
D.Diag(diag::err_opt_not_valid_with_opt) << A->getAsString(Args) << B->getAsString(Args);
}
D.Diag(diag::err_opt_not_valid_with_opt)
<< A->getAsString(Args) << B->getAsString(Args);
Features.push_back("+execute-only");
}
}
Expand Down
8 changes: 0 additions & 8 deletions clang/test/Driver/arm-execute-only.c
Expand Up @@ -6,10 +6,6 @@
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-NO-MOVT
// CHECK-EXECUTE-ONLY-NO-MOVT: error: option '-mexecute-only' cannot be specified with '-mno-movt'

// RUN: not %clang -target armv8m.main-eabi -mexecute-only -mlong-calls %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-LONG-CALLS
// CHECK-EXECUTE-ONLY-LONG-CALLS: error: option '-mexecute-only' cannot be specified with '-mlong-calls'

// RUN: %clang -target armv7m-eabi -x assembler -mexecute-only %s -c -### 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-EXECUTE-ONLY-ASM
// CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only'
Expand All @@ -21,7 +17,3 @@
// RUN: not %clang -target armv8m.main-eabi -mpure-code -mno-movt %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-PURE-CODE-NO-MOVT
// CHECK-PURE-CODE-NO-MOVT: error: option '-mpure-code' cannot be specified with '-mno-movt'

// RUN: not %clang -target armv8m.main-eabi -mpure-code -mlong-calls %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-PURE-CODE-LONG-CALLS
// CHECK-PURE-CODE-LONG-CALLS: error: option '-mpure-code' cannot be specified with '-mlong-calls'
84 changes: 53 additions & 31 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Expand Up @@ -2630,11 +2630,11 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,

const TargetMachine &TM = getTargetMachine();
const Module *Mod = MF.getFunction().getParent();
const GlobalValue *GV = nullptr;
const GlobalValue *GVal = nullptr;
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee))
GV = G->getGlobal();
GVal = G->getGlobal();
bool isStub =
!TM.shouldAssumeDSOLocal(*Mod, GV) && Subtarget->isTargetMachO();
!TM.shouldAssumeDSOLocal(*Mod, GVal) && Subtarget->isTargetMachO();

bool isARMFunc = !Subtarget->isThumb() || (isStub && !Subtarget->isMClass());
bool isLocalARMFunc = false;
Expand All @@ -2647,36 +2647,58 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// those, the target's already in a register, so we don't need to do
// anything extra.
if (isa<GlobalAddressSDNode>(Callee)) {
// Create a constant pool entry for the callee address
unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
ARMConstantPoolValue *CPV =
ARMConstantPoolConstant::Create(GV, ARMPCLabelIndex, ARMCP::CPValue, 0);

// Get the address of the callee into a register
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4));
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
Callee = DAG.getLoad(
PtrVt, dl, DAG.getEntryNode(), CPAddr,
MachinePointerInfo::getConstantPool(DAG.getMachineFunction()));
// When generating execute-only code we use movw movt pair.
// Currently execute-only is only available for architectures that
// support movw movt, so we are safe to assume that.
if (Subtarget->genExecuteOnly()) {
assert(Subtarget->useMovt() &&
"long-calls with execute-only requires movt and movw!");
++NumMovwMovt;
Callee = DAG.getNode(ARMISD::Wrapper, dl, PtrVt,
DAG.getTargetGlobalAddress(GVal, dl, PtrVt));
} else {
// Create a constant pool entry for the callee address
unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
ARMConstantPoolValue *CPV = ARMConstantPoolConstant::Create(
GVal, ARMPCLabelIndex, ARMCP::CPValue, 0);

// Get the address of the callee into a register
SDValue Addr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4));
Addr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Addr);
Callee = DAG.getLoad(
PtrVt, dl, DAG.getEntryNode(), Addr,
MachinePointerInfo::getConstantPool(DAG.getMachineFunction()));
}
} else if (ExternalSymbolSDNode *S=dyn_cast<ExternalSymbolSDNode>(Callee)) {
const char *Sym = S->getSymbol();

// Create a constant pool entry for the callee address
unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
ARMConstantPoolValue *CPV =
ARMConstantPoolSymbol::Create(*DAG.getContext(), Sym,
ARMPCLabelIndex, 0);
// Get the address of the callee into a register
SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4));
CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
Callee = DAG.getLoad(
PtrVt, dl, DAG.getEntryNode(), CPAddr,
MachinePointerInfo::getConstantPool(DAG.getMachineFunction()));
// When generating execute-only code we use movw movt pair.
// Currently execute-only is only available for architectures that
// support movw movt, so we are safe to assume that.
if (Subtarget->genExecuteOnly()) {
assert(Subtarget->useMovt() &&
"long-calls with execute-only requires movt and movw!");
++NumMovwMovt;
Callee = DAG.getNode(ARMISD::Wrapper, dl, PtrVt,
DAG.getTargetGlobalAddress(GVal, dl, PtrVt));
} else {
// Create a constant pool entry for the callee address
unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
ARMConstantPoolValue *CPV = ARMConstantPoolSymbol::Create(
*DAG.getContext(), Sym, ARMPCLabelIndex, 0);

// Get the address of the callee into a register
SDValue Addr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4));
Addr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, Addr);
Callee = DAG.getLoad(
PtrVt, dl, DAG.getEntryNode(), Addr,
MachinePointerInfo::getConstantPool(DAG.getMachineFunction()));
}
}
} else if (isa<GlobalAddressSDNode>(Callee)) {
if (!PreferIndirect) {
isDirect = true;
bool isDef = GV->isStrongDefinitionForLinker();
bool isDef = GVal->isStrongDefinitionForLinker();

// ARM call to a local ARM function is predicable.
isLocalARMFunc = !Subtarget->isThumb() && (isDef || !ARMInterworking);
Expand All @@ -2685,7 +2707,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
assert(Subtarget->isTargetMachO() && "WrapperPIC use on non-MachO?");
Callee = DAG.getNode(
ARMISD::WrapperPIC, dl, PtrVt,
DAG.getTargetGlobalAddress(GV, dl, PtrVt, 0, ARMII::MO_NONLAZY));
DAG.getTargetGlobalAddress(GVal, dl, PtrVt, 0, ARMII::MO_NONLAZY));
Callee = DAG.getLoad(
PtrVt, dl, DAG.getEntryNode(), Callee,
MachinePointerInfo::getGOT(DAG.getMachineFunction()), MaybeAlign(),
Expand All @@ -2695,19 +2717,19 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
assert(Subtarget->isTargetWindows() &&
"Windows is the only supported COFF target");
unsigned TargetFlags = ARMII::MO_NO_FLAG;
if (GV->hasDLLImportStorageClass())
if (GVal->hasDLLImportStorageClass())
TargetFlags = ARMII::MO_DLLIMPORT;
else if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV))
else if (!TM.shouldAssumeDSOLocal(*GVal->getParent(), GVal))
TargetFlags = ARMII::MO_COFFSTUB;
Callee = DAG.getTargetGlobalAddress(GV, dl, PtrVt, /*offset=*/0,
Callee = DAG.getTargetGlobalAddress(GVal, dl, PtrVt, /*offset=*/0,
TargetFlags);
if (TargetFlags & (ARMII::MO_DLLIMPORT | ARMII::MO_COFFSTUB))
Callee =
DAG.getLoad(PtrVt, dl, DAG.getEntryNode(),
DAG.getNode(ARMISD::Wrapper, dl, PtrVt, Callee),
MachinePointerInfo::getGOT(DAG.getMachineFunction()));
} else {
Callee = DAG.getTargetGlobalAddress(GV, dl, PtrVt, 0, 0);
Callee = DAG.getTargetGlobalAddress(GVal, dl, PtrVt, 0, 0);
}
}
} else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
Expand Down
35 changes: 35 additions & 0 deletions llvm/test/CodeGen/Thumb2/thumb2-execute-only-long-calls.ll
@@ -0,0 +1,35 @@
; RUN: llc < %s -mtriple=thumbv7em-arm-none-eabi -relocation-model=static | FileCheck %s -check-prefixes=CHECK,STATIC
; RUN: llc < %s -mtriple=thumbv7em-arm-none-eabi -relocation-model=rwpi | FileCheck %s -check-prefixes=CHECK,RWPI

define void @fn() #0 {
entry:
; CHECK-LABEL: fn:
; CHECK: ldr [[REG:r[0-9]+]], .LCPI0_0
; CHECK-NEXT: blx [[REG]]
; CHECK: .LCPI0_0:
; CHECK-NEXT: .long bar
call void @bar()
ret void
}

define void @execute_only_fn() #1 {
; STATIC-LABEL: execute_only_fn:
; STATIC: movw [[REG0:r[0-9]+]], :lower16:bar
; STATIC-NEXT: movt [[REG0]], :upper16:bar
; STATIC-NEXT: blx [[REG0]]
; STATIC-NOT: .LCPI0_0:

; RWPI-LABEL: execute_only_fn:
; RWPI: movw [[REG0:r[0-9]+]], :lower16:bar
; RWPI-NEXT: movt [[REG0]], :upper16:bar
; RWPI-NEXT: blx [[REG0]]
; RWPI-NOT: .LCPI0_0:
entry:
call void @bar()
ret void
}

attributes #0 = { noinline optnone "target-features"="+thumb-mode,+long-calls" }
attributes #1 = { noinline optnone "target-features"="+execute-only,+thumb-mode,+long-calls" }

declare dso_local void @bar()

0 comments on commit 7e8af2f

Please sign in to comment.