diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp --- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -776,21 +776,15 @@ // 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"); } } diff --git a/clang/test/Driver/arm-execute-only.c b/clang/test/Driver/arm-execute-only.c --- a/clang/test/Driver/arm-execute-only.c +++ b/clang/test/Driver/arm-execute-only.c @@ -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' @@ -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' diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -2630,11 +2630,11 @@ const TargetMachine &TM = getTargetMachine(); const Module *Mod = MF.getFunction().getParent(); - const GlobalValue *GV = nullptr; + const GlobalValue *GVal = nullptr; if (GlobalAddressSDNode *G = dyn_cast(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; @@ -2647,36 +2647,58 @@ // those, the target's already in a register, so we don't need to do // anything extra. if (isa(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(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(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); @@ -2685,7 +2707,7 @@ 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(), @@ -2695,11 +2717,11 @@ 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 = @@ -2707,7 +2729,7 @@ 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(Callee)) { diff --git a/llvm/test/CodeGen/Thumb2/thumb2-execute-only-long-calls.ll b/llvm/test/CodeGen/Thumb2/thumb2-execute-only-long-calls.ll new file mode 100644 --- /dev/null +++ b/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()