[clang] [clang][ARM64X] Support compiling both native and EC objects with -marm64x (PR #207612)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 5 14:21:55 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Jacek Caban (cjacek)
<details>
<summary>Changes</summary>
When -marm64x is used during the assembly phase, construct jobs for both native and EC targets and merge their outputs using llvm-objcopy.
Allow passing ArchName to computeTargetTriple on non-Darwin targets to enable BindArchAction on other platforms. Additionally, allow passing multiple inputs to ObjcopyJobAction and use this capability to construct ARM64X merge jobs.
---
Full diff: https://github.com/llvm/llvm-project/pull/207612.diff
11 Files Affected:
- (modified) clang/include/clang/Driver/Action.h (+1-1)
- (modified) clang/include/clang/Options/Options.td (+1-1)
- (modified) clang/lib/Driver/Action.cpp (+2-2)
- (modified) clang/lib/Driver/Driver.cpp (+28-9)
- (modified) clang/lib/Driver/ToolChain.cpp (+3-1)
- (modified) clang/lib/Driver/ToolChains/MSVC.cpp (+36)
- (modified) clang/lib/Driver/ToolChains/MSVC.h (+16)
- (modified) clang/lib/Driver/ToolChains/MinGW.cpp (+4)
- (modified) clang/lib/Driver/ToolChains/MinGW.h (+2)
- (added) clang/test/Driver/arm64x.c (+6)
- (modified) clang/test/Driver/msvc-link.c (+2-2)
``````````diff
diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h
index bbd6f03dd30da..306db1be15910 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -695,7 +695,7 @@ class ObjcopyJobAction : public JobAction {
void anchor() override;
public:
- ObjcopyJobAction(Action *Input, types::ID Type);
+ ObjcopyJobAction(ActionList &Inputs, types::ID Type);
static bool classof(const Action *A) {
return A->getKind() == ObjcopyJobClass;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 35b0390bd2fc0..d577c09a65f51 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5467,7 +5467,7 @@ def municode : Joined<["-"], "municode">, Group<m_Group>;
def mthreads : Joined<["-"], "mthreads">, Group<m_Group>;
def marm64x : Joined<["-"], "marm64x">, Group<m_Group>,
Visibility<[ClangOption, CLOption]>,
- HelpText<"Link as a hybrid ARM64X image">;
+ HelpText<"Build as a hybrid ARM64X image">;
def mguard_EQ : Joined<["-"], "mguard=">, Group<m_Group>,
HelpText<"Enable or disable Control Flow Guard checks and guard tables emission">,
Values<"none,cf,cf-nochecks">;
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index c0b84ffcc95be..b74ae925e922c 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -470,5 +470,5 @@ BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input,
void ObjcopyJobAction::anchor() {}
-ObjcopyJobAction::ObjcopyJobAction(Action *Input, types::ID Type)
- : JobAction(ObjcopyJobClass, Input, Type) {}
+ObjcopyJobAction::ObjcopyJobAction(ActionList &Inputs, types::ID Type)
+ : JobAction(ObjcopyJobClass, Inputs, Type) {}
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 1a91b353a27e4..7adc60ed9f97f 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -616,10 +616,9 @@ static void setZosTargetVersion(const Driver &D, llvm::Triple &Target,
///
/// This routine provides the logic to compute a target triple from various
/// args passed to the driver and the default triple string.
-static llvm::Triple computeTargetTriple(const Driver &D,
- StringRef TargetTriple,
+static llvm::Triple computeTargetTriple(const Driver &D, StringRef TargetTriple,
const ArgList &Args,
- StringRef DarwinArchName = "") {
+ StringRef ArchName = "") {
// FIXME: Already done in Compilation *Driver::BuildCompilation
if (const Arg *A = Args.getLastArg(options::OPT_target))
TargetTriple = A->getValue();
@@ -635,9 +634,8 @@ static llvm::Triple computeTargetTriple(const Driver &D,
// Handle Apple-specific options available here.
if (Target.isOSBinFormatMachO()) {
// If an explicit Darwin arch name is given, that trumps all.
- if (!DarwinArchName.empty()) {
- tools::darwin::setTripleTypeForMachOArchName(Target, DarwinArchName,
- Args);
+ if (!ArchName.empty()) {
+ tools::darwin::setTripleTypeForMachOArchName(Target, ArchName, Args);
return llvm::Triple(Target.normalize());
}
@@ -646,6 +644,9 @@ static llvm::Triple computeTargetTriple(const Driver &D,
StringRef ArchName = A->getValue();
tools::darwin::setTripleTypeForMachOArchName(Target, ArchName, Args);
}
+ } else if (!ArchName.empty()) {
+ Target.setArchName(ArchName);
+ return Target;
}
// Handle pseudo-target flags '-mlittle-endian'/'-EL' and
@@ -703,6 +704,11 @@ static llvm::Triple computeTargetTriple(const Driver &D,
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << Target.str();
+ // The `-marm64x` flag is only valid for Windows targets.
+ if (Args.hasArgNoClaim(options::OPT_marm64x) && !Target.isOSWindows())
+ D.Diag(diag::err_drv_unsupported_opt_for_target)
+ << "-marm64x" << Target.str();
+
// Handle pseudo-target flags '-m64', '-mx32', '-m32' and '-m16'.
Arg *A = Args.getLastArg(options::OPT_m64, options::OPT_mx32,
options::OPT_m32, options::OPT_m16,
@@ -4807,9 +4813,11 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
if (TC.requiresObjcopy(Args)) {
Action *LastAction = Actions.back();
// llvm-objcopy expects an unvalidated DXIL container (TY_OBJECT).
- if (LastAction->getType() == types::TY_Object)
+ if (LastAction->getType() == types::TY_Object) {
+ ActionList ObjcopyActions({LastAction});
Actions.push_back(
- C.MakeAction<ObjcopyJobAction>(LastAction, types::TY_Object));
+ C.MakeAction<ObjcopyJobAction>(ObjcopyActions, types::TY_Object));
+ }
}
// Call validator when -Vd not in Args.
@@ -5401,6 +5409,16 @@ Action *Driver::ConstructPhaseAction(
return C.MakeAction<BackendJobAction>(Input, types::TY_PP_Asm);
}
case phases::Assemble:
+ // When -marm64x is used, construct jobs for the EC and native targets and
+ // merge them into an archive with llvm-objcopy.
+ if (Args.hasArg(options::OPT_marm64x)) {
+ Action *Act =
+ C.MakeAction<AssembleJobAction>(std::move(Input), types::TY_Object);
+ ActionList Inputs;
+ Inputs.push_back(C.MakeAction<BindArchAction>(Act, BoundArch("arm64ec")));
+ Inputs.push_back(C.MakeAction<BindArchAction>(Act, BoundArch("aarch64")));
+ return C.MakeAction<ObjcopyJobAction>(Inputs, types::TY_Object);
+ }
return C.MakeAction<AssembleJobAction>(std::move(Input), types::TY_Object);
}
@@ -5495,7 +5513,8 @@ void Driver::BuildJobs(Compilation &C) const {
BuildJobsForAction(C, A, &C.getDefaultToolChain(),
/*BA=*/{},
/*AtTopLevel*/ true,
- /*MultipleArchs*/ ArchNames.size() > 1,
+ /*MultipleArchs*/ ArchNames.size() > 1 ||
+ C.getArgs().hasArgNoClaim(options::OPT_marm64x),
/*LinkingOutput*/ LinkingOutput, CachedResults,
/*TargetDeviceOffloadKind*/ Action::OFK_None);
}
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 21d2fbef59fc6..9230e4dae548f 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -801,9 +801,11 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
case Action::VerifyDebugInfoJobClass:
case Action::BinaryAnalyzeJobClass:
case Action::BinaryTranslatorJobClass:
- case Action::ObjcopyJobClass:
llvm_unreachable("Invalid tool kind.");
+ case Action::ObjcopyJobClass:
+ return nullptr;
+
case Action::CompileJobClass:
case Action::PrecompileJobClass:
case Action::PreprocessJobClass:
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index eb81f1b4e142c..d826b81f0dd3b 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -498,6 +498,31 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
C.addCommand(std::move(LinkCmd));
}
+void tools::ARM64XObjcopy::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ // Assume llvm-objcopy is only used for hybrid ARM64X object files.
+ if (Inputs.size() != 2)
+ return;
+
+ std::string ObjcopyPath = getToolChain().GetProgramPath("llvm-objcopy");
+ const char *Exec = Args.MakeArgString(ObjcopyPath);
+
+ // Embed the hybrid object in the .obj.arm64ec section.
+ ArgStringList CmdArgs;
+ CmdArgs.push_back(Args.MakeArgString("--add-section=.obj.arm64ec=" +
+ Twine(Inputs[0].getFilename())));
+ // Mark the .obj.arm64ec section as discardable.
+ CmdArgs.push_back("--set-section-flags=.obj.arm64ec=debug");
+ CmdArgs.push_back(Inputs[1].getFilename());
+ CmdArgs.push_back(Output.getFilename());
+
+ C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+ Exec, CmdArgs, Inputs, Output));
+}
+
MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
const ArgList &Args)
: ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args),
@@ -531,6 +556,17 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
loadMultilibsFromYAML(Args, D);
}
+Tool *MSVCToolChain::getTool(Action::ActionClass AC) const {
+ switch (AC) {
+ case Action::ObjcopyJobClass:
+ if (!Objcopy)
+ Objcopy.reset(new tools::ARM64XObjcopy(*this));
+ return Objcopy.get();
+ default:
+ return ToolChain::getTool(AC);
+ }
+}
+
Tool *MSVCToolChain::buildLinker() const {
return new tools::visualstudio::Linker(*this);
}
diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index 0ec73fb59b921..6878d6a195d78 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -39,6 +39,19 @@ class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
};
} // end namespace visualstudio
+class LLVM_LIBRARY_VISIBILITY ARM64XObjcopy : public Tool {
+public:
+ ARM64XObjcopy(const ToolChain &TC)
+ : Tool("ARM64XObjcopy", "llvm-objcopy", TC) {}
+
+ bool hasIntegratedCPP() const override { return false; }
+
+ void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &TCArgs,
+ const char *LinkingOutput) const override;
+};
+
} // end namespace tools
namespace toolchains {
@@ -137,8 +150,10 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
const Twine &subfolder2 = "",
const Twine &subfolder3 = "") const;
+ Tool *getTool(Action::ActionClass AC) const override;
Tool *buildLinker() const override;
Tool *buildAssembler() const override;
+
private:
std::optional<llvm::StringRef> WinSdkDir, WinSdkVersion, WinSysRoot;
std::string VCToolChainPath;
@@ -146,6 +161,7 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
LazyDetector<CudaInstallationDetector> CudaInstallation;
LazyDetector<RocmInstallationDetector> RocmInstallation;
LazyDetector<SYCLInstallationDetector> SYCLInstallation;
+ mutable std::unique_ptr<tools::ARM64XObjcopy> Objcopy;
};
} // end namespace toolchains
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp
index 4237a6906be3b..7fe5be92012ee 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -575,6 +575,10 @@ Tool *toolchains::MinGW::getTool(Action::ActionClass AC) const {
if (!Compiler)
Compiler.reset(new tools::gcc::Compiler(*this));
return Compiler.get();
+ case Action::ObjcopyJobClass:
+ if (!Objcopy)
+ Objcopy.reset(new tools::ARM64XObjcopy(*this));
+ return Objcopy.get();
default:
return ToolChain::getTool(AC);
}
diff --git a/clang/lib/Driver/ToolChains/MinGW.h b/clang/lib/Driver/ToolChains/MinGW.h
index 36ab7b6c6e295..c8b7a73af0a2d 100644
--- a/clang/lib/Driver/ToolChains/MinGW.h
+++ b/clang/lib/Driver/ToolChains/MinGW.h
@@ -11,6 +11,7 @@
#include "Cuda.h"
#include "Gnu.h"
+#include "MSVC.h"
#include "clang/Driver/CudaInstallationDetector.h"
#include "clang/Driver/LazyDetector.h"
#include "clang/Driver/RocmInstallationDetector.h"
@@ -117,6 +118,7 @@ class LLVM_LIBRARY_VISIBILITY MinGW : public ToolChain {
std::string TripleDirName;
mutable std::unique_ptr<tools::gcc::Preprocessor> Preprocessor;
mutable std::unique_ptr<tools::gcc::Compiler> Compiler;
+ mutable std::unique_ptr<tools::ARM64XObjcopy> Objcopy;
void findGccLibDir(const llvm::Triple &LiteralTriple);
bool NativeLLVMSupport;
diff --git a/clang/test/Driver/arm64x.c b/clang/test/Driver/arm64x.c
new file mode 100644
index 0000000000000..34b50d9133d14
--- /dev/null
+++ b/clang/test/Driver/arm64x.c
@@ -0,0 +1,6 @@
+// RUN: %clang -c -marm64x --target=arm64ec-pc-windows-msvc -### %s 2>&1 | FileCheck %s
+// RUN: %clang -c -marm64x --target=arm64ec-pc-windows-gnu -### %s 2>&1 | FileCheck %s
+
+// CHECK: "-cc1" "-triple" "arm64ec-pc-windows-{{.*}}" "-emit-obj"
+// CHECK-NEXT: "-cc1" "-triple" "aarch64-pc-windows-{{.*}}" "-emit-obj"
+// CHECK-NEXT: llvm-objcopy" "--add-section=.obj.arm64ec={{.*}}arm64x-arm64ec-{{.*}}.o" "--set-section-flags=.obj.arm64ec=debug" "{{.*}}arm64x-aarch64-{{.*}}.o" "arm64x.o"
diff --git a/clang/test/Driver/msvc-link.c b/clang/test/Driver/msvc-link.c
index 5cb1653bf4db9..19027ff3e80ff 100644
--- a/clang/test/Driver/msvc-link.c
+++ b/clang/test/Driver/msvc-link.c
@@ -46,9 +46,9 @@
// ARM64X: "-machine:arm64x"
// RUN: not %clang --target=x86_64-linux-gnu -marm64x -### %s 2>&1 | FileCheck --check-prefix=HYBRID-ERR %s
-// HYBRID-ERR: error: unsupported option '-marm64x' for target 'x86_64-linux-gnu'
+// HYBRID-ERR: error: unsupported option '-marm64x' for target 'x86_64-unknown-linux-gnu'
-// RUN: %clang -c -marm64x --target=arm64ec-pc-windows-msvc -fuse-ld=link -### %s 2>&1 | \
+// RUN: %clang -S -marm64x --target=arm64ec-pc-windows-msvc -fuse-ld=link -### %s 2>&1 | \
// RUN: FileCheck --check-prefix=HYBRID-WARN %s
// HYBRID-WARN: warning: argument unused during compilation: '-marm64x' [-Wunused-command-line-argument]
``````````
</details>
https://github.com/llvm/llvm-project/pull/207612
More information about the cfe-commits
mailing list