[clang] [llvm] Add support for MorphOS (PR #191951)
Jonathan Schleifer via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 06:18:09 PDT 2026
https://github.com/Midar updated https://github.com/llvm/llvm-project/pull/191951
>From 14e0d9af04aef67ac8ba38dd936ccf6556add29d Mon Sep 17 00:00:00 2001
From: Jonathan Schleifer <js at nil.im>
Date: Tue, 14 Apr 2026 00:18:28 -0500
Subject: [PATCH] Add support for MorphOS
- Adds a MorphOS target
- Adds toolchain for MorphOS
- Adds -noixemul flag for MorphOS
---
clang/include/clang/Basic/LangOptions.def | 2 +
clang/include/clang/Options/Options.td | 7 +
clang/lib/Basic/Targets.cpp | 2 +
clang/lib/Basic/Targets/OSTargets.h | 32 ++++
clang/lib/Driver/CMakeLists.txt | 1 +
clang/lib/Driver/Driver.cpp | 4 +
clang/lib/Driver/ToolChains/Clang.cpp | 10 +
clang/lib/Driver/ToolChains/MorphOS.cpp | 214 ++++++++++++++++++++++
clang/lib/Driver/ToolChains/MorphOS.h | 83 +++++++++
llvm/include/llvm/TargetParser/Triple.h | 5 +
llvm/lib/TargetParser/Triple.cpp | 2 +
11 files changed, 362 insertions(+)
create mode 100644 clang/lib/Driver/ToolChains/MorphOS.cpp
create mode 100644 clang/lib/Driver/ToolChains/MorphOS.h
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 6bba142aaf428..a63eac0b5c0c4 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -530,6 +530,8 @@ LANGOPT(EnableLifetimeSafetyTUAnalysis, 1, 0, NotCompatible, "Lifetime safety at
LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type")
LANGOPT(Reflection , 1, 0, NotCompatible, "C++26 Reflection")
+LANGOPT(NoIxemul, 1, 0, NotCompatible, "Do not use ixemul on MorphOS")
+
#undef LANGOPT
#undef ENUM_LANGOPT
#undef VALUE_LANGOPT
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index bffb3dfb27485..507d696d39ad0 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -10096,3 +10096,10 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
Group<m_Group>,
HelpText<"Enable the wasm-opt optimizer (default)">,
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+
+// Unfortunate naming to keep compatibility with GCC.
+defm noixemul : BoolOption<"", "noixemul",
+ LangOpts<"NoIxemul">, DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption], "Do not use ixemul on MorphOS">,
+ NegFlag<SetFalse>,
+ BothFlags<[], [ClangOption, CC1Option]>>;
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index dc6ef6ed8f3f8..1eeafa71fdcd7 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -368,6 +368,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
return std::make_unique<LinuxTargetInfo<PPC32TargetInfo>>(Triple, Opts);
case llvm::Triple::FreeBSD:
return std::make_unique<FreeBSDTargetInfo<PPC32TargetInfo>>(Triple, Opts);
+ case llvm::Triple::MorphOS:
+ return std::make_unique<MorphOSTargetInfo<PPC32TargetInfo>>(Triple, Opts);
case llvm::Triple::NetBSD:
return std::make_unique<NetBSDTargetInfo<PPC32TargetInfo>>(Triple, Opts);
case llvm::Triple::OpenBSD:
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index 16337d56f82c3..aee7f0fca3526 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -447,6 +447,38 @@ class LLVM_LIBRARY_VISIBILITY ManagarmTargetInfo : public OSTargetInfo<Target> {
}
};
+// MorphOS Target
+template <typename Target>
+class LLVM_LIBRARY_VISIBILITY MorphOSTargetInfo : public OSTargetInfo<Target> {
+protected:
+ void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+ MacroBuilder &Builder) const override {
+ // MorphOS defines; list based off of gcc output
+ Builder.defineMacro("__MORPHOS__");
+ Builder.defineMacro("__morphos__");
+ Builder.defineMacro("__AMIGA__");
+ Builder.defineMacro("__AMIGA");
+ Builder.defineMacro("_AMIGA");
+ Builder.defineMacro("AMIGA");
+ Builder.defineMacro("__amigaos__");
+ Builder.defineMacro("__amigaos");
+ Builder.defineMacro("amigaos");
+ if (Opts.NoIxemul) {
+ Builder.defineMacro("__libnix__");
+ Builder.defineMacro("__libnix");
+ Builder.defineMacro("libnix");
+ } else {
+ Builder.defineMacro("__ixemul__");
+ Builder.defineMacro("__ixemul");
+ Builder.defineMacro("ixemul");
+ }
+ }
+
+public:
+ MorphOSTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
+ : OSTargetInfo<Target>(Triple, Opts) {}
+};
+
// NetBSD Target
template <typename Target>
class LLVM_LIBRARY_VISIBILITY NetBSDTargetInfo : public OSTargetInfo<Target> {
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index b8b10b351633f..cb09669d0a1a8 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -80,6 +80,7 @@ add_clang_library(clangDriver
ToolChains/Managarm.cpp
ToolChains/MipsLinux.cpp
ToolChains/MinGW.cpp
+ ToolChains/MorphOS.cpp
ToolChains/MSP430.cpp
ToolChains/MSVC.cpp
ToolChains/NetBSD.cpp
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index c41a8ded73b7b..a186d28cf48cd 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -37,6 +37,7 @@
#include "ToolChains/Managarm.h"
#include "ToolChains/MinGW.h"
#include "ToolChains/MipsLinux.h"
+#include "ToolChains/MorphOS.h"
#include "ToolChains/NetBSD.h"
#include "ToolChains/OHOS.h"
#include "ToolChains/OpenBSD.h"
@@ -7152,6 +7153,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
case llvm::Triple::ChipStar:
TC = std::make_unique<toolchains::HIPSPVToolChain>(*this, Target, Args);
break;
+ case llvm::Triple::MorphOS:
+ TC = std::make_unique<toolchains::MorphOS>(*this, Target, Args);
+ break;
default:
// Of these targets, Hexagon is the only one that might have
// an OS of Linux, in which case it got handled above already.
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f685abe9dad35..fd0376e651c4e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8071,6 +8071,16 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
addOpenMPHostOffloadingArgs(C, JA, Args, CmdArgs);
+ if (TC.getTriple().isOSMorphOS()) {
+ Args.AddLastArg(CmdArgs, options::OPT_noixemul);
+ } else {
+ // Reject MorphOS-specific link options on other targets.
+ for (const Arg *A : Args.filtered(options::OPT_noixemul)) {
+ D.Diag(diag::err_drv_unsupported_opt_for_target)
+ << A->getSpelling() << TripleStr;
+ }
+ }
+
if (Args.hasFlag(options::OPT_fdevirtualize_speculatively,
options::OPT_fno_devirtualize_speculatively,
/*Default value*/ false))
diff --git a/clang/lib/Driver/ToolChains/MorphOS.cpp b/clang/lib/Driver/ToolChains/MorphOS.cpp
new file mode 100644
index 0000000000000..21fd12fc33649
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/MorphOS.cpp
@@ -0,0 +1,214 @@
+//===--- MorphOS.cpp - MorphOS ToolChain Implementations --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MorphOS.h"
+#include "clang/Config/config.h"
+#include "clang/Driver/CommonArgs.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/SanitizerArgs.h"
+#include "clang/Options/Options.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+using namespace clang::driver;
+using namespace clang::driver::tools;
+using namespace clang::driver::toolchains;
+using namespace clang;
+using namespace llvm::opt;
+
+void morphos::Assembler::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ const auto &ToolChain = static_cast<const MorphOS &>(getToolChain());
+ ArgStringList CmdArgs;
+
+ claimNoWarnArgs(Args);
+
+ Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler);
+
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+
+ for (const auto &II : Inputs)
+ CmdArgs.push_back(II.getFilename());
+
+ const char *Exec = Args.MakeArgString((ToolChain.GetProgramPath("as")));
+ C.addCommand(std::make_unique<Command>(JA, *this,
+ ResponseFileSupport::AtFileCurCP(),
+ Exec, CmdArgs, Inputs, Output));
+}
+
+void morphos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ const auto &ToolChain = static_cast<const MorphOS &>(getToolChain());
+ const Driver &D = ToolChain.getDriver();
+ ArgStringList CmdArgs;
+ // FIXME: Discover GCC instead of hard-coding the version.
+ const std::string GCCLibPath = D.SysRoot + "/lib/gcc-lib/ppc-morphos/15.1.0";
+ const bool NoIxemul = Args.hasArg(options::OPT_noixemul);
+
+ if (!D.SysRoot.empty())
+ CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot));
+
+ CmdArgs.push_back("--defsym");
+ CmdArgs.push_back("__abox__=1");
+ CmdArgs.push_back("-Qy");
+
+ CmdArgs.push_back("-Bstatic");
+ if (NoIxemul) {
+ Args.ClaimAllArgs(options::OPT_noixemul);
+ CmdArgs.push_back("--flavor=libnix");
+ } else {
+ CmdArgs.push_back("--flavor=ixemul");
+ }
+
+ assert((Output.isFilename() || Output.isNothing()) && "Invalid output.");
+ if (Output.isFilename()) {
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+ }
+
+ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles,
+ options::OPT_r)) {
+ if (NoIxemul) {
+ CmdArgs.push_back(Args.MakeArgString(
+ GCCLibPath + "/../../../../ppc-morphos/lib/libnix/crt0i.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/libnix/ecrti.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/libnix/crtbegin.o"));
+ } else {
+ CmdArgs.push_back(Args.MakeArgString(
+ GCCLibPath + "/../../../../ppc-morphos/lib/crt0i.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/ecrti.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/crtbegin.o"));
+ }
+ }
+
+ CmdArgs.push_back(Args.MakeArgString("-L" + GCCLibPath));
+ CmdArgs.push_back(
+ Args.MakeArgString("-L" + GCCLibPath + "/../../../../ppc-morphos/lib"));
+ CmdArgs.push_back(Args.MakeArgString("-L" + D.SysRoot + "/lib"));
+
+ Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group,
+ options::OPT_s, options::OPT_t});
+ ToolChain.AddFilePathLibArgs(Args, CmdArgs);
+
+ AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
+
+ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs,
+ options::OPT_r)) {
+ if (D.CCCIsCXX()) {
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ CmdArgs.push_back("-lm");
+ }
+
+ // Silence warnings when linking C code with a C++ '-stdlib' argument.
+ Args.ClaimAllArgs(options::OPT_stdlib_EQ);
+
+ CmdArgs.push_back("--start-group");
+ CmdArgs.push_back("-lc");
+ CmdArgs.push_back("-lgcc");
+ CmdArgs.push_back("-labox");
+ CmdArgs.push_back("-laboxstubs");
+ CmdArgs.push_back("-lsavl");
+ CmdArgs.push_back("--end-group");
+ }
+
+ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles,
+ options::OPT_r)) {
+ if (NoIxemul) {
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/libnix/crtend.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/libnix/ecrtn.o"));
+ } else {
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/crtend.o"));
+ CmdArgs.push_back(Args.MakeArgString(GCCLibPath + "/ecrtn.o"));
+ }
+ }
+
+ const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath());
+ C.addCommand(std::make_unique<Command>(JA, *this,
+ ResponseFileSupport::AtFileCurCP(),
+ Exec, CmdArgs, Inputs, Output));
+}
+
+/// MorphOS - MorphOS tool chain which can call as(1) and ld(1) directly.
+
+MorphOS::MorphOS(const Driver &D, const llvm::Triple &Triple,
+ const ArgList &Args)
+ : Generic_ELF(D, Triple, Args) {
+ if (!Args.hasArg(options::OPT_nostdlib)) {
+ getFilePaths().push_back(concat(getDriver().SysRoot, "/ppc-morphos/lib"));
+ }
+}
+
+Tool *MorphOS::buildAssembler() const {
+ return new tools::morphos::Assembler(*this);
+}
+
+Tool *MorphOS::buildLinker() const { return new tools::morphos::Linker(*this); }
+
+ToolChain::CXXStdlibType MorphOS::GetDefaultCXXStdlibType() const {
+ return ToolChain::CST_Libstdcxx;
+}
+
+void MorphOS::AddClangSystemIncludeArgs(
+ const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const {
+ const Driver &D = getDriver();
+
+ if (DriverArgs.hasArg(options::OPT_nostdinc))
+ return;
+
+ if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
+ SmallString<128> Dir(D.ResourceDir);
+ llvm::sys::path::append(Dir, "include");
+ addSystemInclude(DriverArgs, CC1Args, Dir.str());
+ }
+
+ if (DriverArgs.hasArg(options::OPT_nostdlibinc))
+ return;
+
+ // Check for configure-time C include directories.
+ StringRef CIncludeDirs(C_INCLUDE_DIRS);
+ if (CIncludeDirs != "") {
+ SmallVector<StringRef, 5> dirs;
+ CIncludeDirs.split(dirs, ":");
+ for (StringRef dir : dirs) {
+ StringRef Prefix =
+ llvm::sys::path::is_absolute(dir) ? StringRef(D.SysRoot) : "";
+ addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir);
+ }
+ return;
+ }
+
+ if (DriverArgs.hasArg(options::OPT_noixemul)) {
+ addExternCSystemInclude(DriverArgs, CC1Args,
+ concat(D.SysRoot, "/includestd"));
+ }
+ addExternCSystemInclude(DriverArgs, CC1Args, concat(D.SysRoot, "/include"));
+ addExternCSystemInclude(DriverArgs, CC1Args,
+ concat(D.SysRoot, "/usr/include"));
+ addExternCSystemInclude(DriverArgs, CC1Args,
+ concat(D.SysRoot, "/os-include"));
+}
+
+void MorphOS::addLibStdCxxIncludePaths(
+ const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const {
+ // FIXME: Discover GCC instead of hard-coding the version.
+ addLibStdCXXIncludePaths(
+ concat(getDriver().SysRoot,
+ "/lib/gcc-lib/ppc-morphos/15.1.0/include/c++"),
+ "", "", DriverArgs, CC1Args);
+}
diff --git a/clang/lib/Driver/ToolChains/MorphOS.h b/clang/lib/Driver/ToolChains/MorphOS.h
new file mode 100644
index 0000000000000..c58488ac36104
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/MorphOS.h
@@ -0,0 +1,83 @@
+//===--- MorphOS.h - MorphOS ToolChain Implementations ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MORPHOS_H
+#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MORPHOS_H
+
+#include "Gnu.h"
+#include "clang/Driver/Tool.h"
+#include "clang/Driver/ToolChain.h"
+
+namespace clang {
+namespace driver {
+namespace tools {
+
+/// Directly call GNU Binutils assembler and linker
+namespace morphos {
+class LLVM_LIBRARY_VISIBILITY Assembler final : public Tool {
+public:
+ Assembler(const ToolChain &TC)
+ : Tool("morphos::Assembler", "assembler", 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;
+};
+
+class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
+public:
+ Linker(const ToolChain &TC) : Tool("morphos::Linker", "linker", TC) {}
+
+ bool hasIntegratedCPP() const override { return false; }
+ bool isLinkJob() const override { return true; }
+
+ 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 morphos
+} // end namespace tools
+
+namespace toolchains {
+
+class LLVM_LIBRARY_VISIBILITY MorphOS : public Generic_ELF {
+public:
+ MorphOS(const Driver &D, const llvm::Triple &Triple,
+ const llvm::opt::ArgList &Args);
+
+ bool IsMathErrnoDefault() const override { return false; }
+ bool IsObjCNonFragileABIDefault() const override { return true; }
+
+ CXXStdlibType GetDefaultCXXStdlibType() const override;
+
+ void
+ AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+ void
+ addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+
+ UnwindTableLevel
+ getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override {
+ return UnwindTableLevel::Asynchronous;
+ }
+
+protected:
+ Tool *buildAssembler() const override;
+ Tool *buildLinker() const override;
+};
+
+} // end namespace toolchains
+} // end namespace driver
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MORPHOS_H
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 7c5aa52a1bd04..538da08186b21 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -256,6 +256,7 @@ class Triple {
ChipStar,
Firmware,
QURT,
+ MorphOS,
LastOSType = QURT
};
enum EnvironmentType {
@@ -690,6 +691,10 @@ class Triple {
return getOS() == Triple::ELFIAMCU;
}
+ bool isOSMorphOS() const {
+ return getOS() == Triple::MorphOS;
+ }
+
bool isOSUnknown() const { return getOS() == Triple::UnknownOS; }
bool isGNUEnvironment() const {
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 7a907808c0f34..82cc71989768c 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -315,6 +315,7 @@ StringRef Triple::getOSTypeName(OSType Kind) {
case Managarm:
return "managarm";
case Mesa3D: return "mesa3d";
+ case MorphOS: return "morphos";
case NVCL: return "nvcl";
case NetBSD: return "netbsd";
case OpenBSD: return "openbsd";
@@ -724,6 +725,7 @@ static Triple::OSType parseOS(StringRef OSName) {
.StartsWith("lv2", Triple::Lv2)
.StartsWith("macos", Triple::MacOSX)
.StartsWith("managarm", Triple::Managarm)
+ .StartsWith("morphos", Triple::MorphOS)
.StartsWith("netbsd", Triple::NetBSD)
.StartsWith("openbsd", Triple::OpenBSD)
.StartsWith("solaris", Triple::Solaris)
More information about the cfe-commits
mailing list