From fb78495376017111d8b5a1f43745d3c17b0d8978 Mon Sep 17 00:00:00 2001 From: Prabhuk Date: Fri, 20 Sep 2024 11:16:36 -0700 Subject: [PATCH] Reland "[Driver] Add toolchain for X86_64 UEFI target" (#109364) Reverts llvm/llvm-project#109340 Addressing the failed MAC Clang Driver test as part of this reland. --- clang/lib/Basic/Targets.cpp | 3 + clang/lib/Basic/Targets/OSTargets.h | 15 ++++ clang/lib/Basic/Targets/X86.h | 37 ++++++++++ clang/lib/Driver/CMakeLists.txt | 1 + clang/lib/Driver/Driver.cpp | 4 ++ clang/lib/Driver/ToolChains/UEFI.cpp | 88 +++++++++++++++++++++++ clang/lib/Driver/ToolChains/UEFI.h | 59 +++++++++++++++ clang/test/CodeGen/X86/uefi-data-layout.c | 3 + clang/test/Driver/uefi-constructed-args.c | 12 ++++ clang/unittests/Driver/ToolChainTest.cpp | 21 ++++++ 10 files changed, 243 insertions(+) create mode 100644 clang/lib/Driver/ToolChains/UEFI.cpp create mode 100644 clang/lib/Driver/ToolChains/UEFI.h create mode 100644 clang/test/CodeGen/X86/uefi-data-layout.c create mode 100644 clang/test/Driver/uefi-constructed-args.c diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 0b8e565345b6a4..4917ef015941be 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -613,6 +613,9 @@ std::unique_ptr AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::Solaris: return std::make_unique>(Triple, Opts); + case llvm::Triple::UEFI: + return std::make_unique(Triple, Opts); + case llvm::Triple::Win32: { switch (Triple.getEnvironment()) { case llvm::Triple::Cygnus: diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h index 0a4f06967fff5a..a83d6464e789d6 100644 --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/clang/lib/Basic/Targets/OSTargets.h @@ -778,6 +778,21 @@ class LLVM_LIBRARY_VISIBILITY ZOSTargetInfo : public OSTargetInfo { } }; +// UEFI target +template +class LLVM_LIBRARY_VISIBILITY UEFITargetInfo : public OSTargetInfo { +protected: + void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const override {} + +public: + UEFITargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) + : OSTargetInfo(Triple, Opts) { + this->WCharType = TargetInfo::UnsignedShort; + this->WIntType = TargetInfo::UnsignedShort; + } +}; + void addWindowsDefines(const llvm::Triple &Triple, const LangOptions &Opts, MacroBuilder &Builder); diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 79fd5867cf6673..a99ae62984c7d5 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -814,6 +814,43 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo { } }; +// x86-64 UEFI target +class LLVM_LIBRARY_VISIBILITY UEFIX86_64TargetInfo + : public UEFITargetInfo { +public: + UEFIX86_64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) + : UEFITargetInfo(Triple, Opts) { + this->TheCXXABI.set(TargetCXXABI::Microsoft); + this->MaxTLSAlign = 8192u * this->getCharWidth(); + this->resetDataLayout("e-m:w-p270:32:32-p271:32:32-p272:64:64-" + "i64:64-i128:128-f80:128-n8:16:32:64-S128"); + } + + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override { + getOSDefines(Opts, X86TargetInfo::getTriple(), Builder); + } + + BuiltinVaListKind getBuiltinVaListKind() const override { + return TargetInfo::CharPtrBuiltinVaList; + } + + CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { + switch (CC) { + case CC_C: + case CC_Win64: + return CCCR_OK; + default: + return CCCR_Warning; + } + } + + TargetInfo::CallingConvKind + getCallingConvKind(bool ClangABICompat4) const override { + return CCK_MicrosoftWin64; + } +}; + // x86-64 Windows target class LLVM_LIBRARY_VISIBILITY WindowsX86_64TargetInfo : public WindowsTargetInfo { diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index 32a4378ab499fa..4fd10bf671512f 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -78,6 +78,7 @@ add_clang_library(clangDriver ToolChains/Solaris.cpp ToolChains/SPIRV.cpp ToolChains/TCE.cpp + ToolChains/UEFI.cpp ToolChains/VEToolchain.cpp ToolChains/WebAssembly.cpp ToolChains/XCore.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 1c64ceabad1bf4..44548fa9d706fb 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -45,6 +45,7 @@ #include "ToolChains/SPIRV.h" #include "ToolChains/Solaris.h" #include "ToolChains/TCE.h" +#include "ToolChains/UEFI.h" #include "ToolChains/VEToolchain.h" #include "ToolChains/WebAssembly.h" #include "ToolChains/XCore.h" @@ -6422,6 +6423,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::Mesa3D: TC = std::make_unique(*this, Target, Args); break; + case llvm::Triple::UEFI: + TC = std::make_unique(*this, Target, Args); + break; case llvm::Triple::Win32: switch (Target.getEnvironment()) { default: diff --git a/clang/lib/Driver/ToolChains/UEFI.cpp b/clang/lib/Driver/ToolChains/UEFI.cpp new file mode 100644 index 00000000000000..66cbbec59246c0 --- /dev/null +++ b/clang/lib/Driver/ToolChains/UEFI.cpp @@ -0,0 +1,88 @@ +//===--- UEFI.cpp - UEFI ToolChain Implementations -----------------------===// +// +// 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 "UEFI.h" +#include "CommonArgs.h" +#include "Darwin.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/Host.h" + +using namespace clang::driver; +using namespace clang::driver::toolchains; +using namespace clang; +using namespace llvm::opt; + +UEFI::UEFI(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) + : ToolChain(D, Triple, Args) {} + +Tool *UEFI::buildLinker() const { return new tools::uefi::Linker(*this); } + +void tools::uefi::Linker::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + auto &TC = static_cast(getToolChain()); + + assert((Output.isFilename() || Output.isNothing()) && "invalid output"); + if (Output.isFilename()) + CmdArgs.push_back( + Args.MakeArgString(std::string("-out:") + Output.getFilename())); + + CmdArgs.push_back("-nologo"); + + // TODO: Other UEFI binary subsystems that are currently unsupported: + // efi_boot_service_driver, efi_rom, efi_runtime_driver. + CmdArgs.push_back("-subsystem:efi_application"); + + // Default entry function name according to the TianoCore reference + // implementation is EfiMain. + // TODO: Provide a flag to override the entry function name. + CmdArgs.push_back("-entry:EfiMain"); + + // "Terminal Service Aware" flag is not needed for UEFI applications. + CmdArgs.push_back("-tsaware:no"); + + // EFI_APPLICATION to be linked as DLL by default. + CmdArgs.push_back("-dll"); + + if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7)) + CmdArgs.push_back("-debug"); + + Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link); + + AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); + + // This should ideally be handled by ToolChain::GetLinkerPath but we need + // to special case some linker paths. In the case of lld, we need to + // translate 'lld' into 'lld-link'. + StringRef Linker = + Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER); + if (Linker.empty() || Linker == "lld") + Linker = "lld-link"; + + auto LinkerPath = TC.GetProgramPath(Linker.str().c_str()); + auto LinkCmd = std::make_unique( + JA, *this, ResponseFileSupport::AtFileUTF16(), + Args.MakeArgString(LinkerPath), CmdArgs, Inputs, Output); + C.addCommand(std::move(LinkCmd)); +} diff --git a/clang/lib/Driver/ToolChains/UEFI.h b/clang/lib/Driver/ToolChains/UEFI.h new file mode 100644 index 00000000000000..a126ac32db6c6c --- /dev/null +++ b/clang/lib/Driver/ToolChains/UEFI.h @@ -0,0 +1,59 @@ +//===--- UEFI.h - UEFI 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_UEFI_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_UEFI_H + +#include "clang/Driver/Tool.h" +#include "clang/Driver/ToolChain.h" + +namespace clang::driver { +namespace tools { +namespace uefi { +class LLVM_LIBRARY_VISIBILITY Linker : public Tool { +public: + Linker(const ToolChain &TC) : Tool("uefi::Linker", "lld-link", 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 uefi +} // end namespace tools + +namespace toolchains { + +class LLVM_LIBRARY_VISIBILITY UEFI : public ToolChain { +public: + UEFI(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + +protected: + Tool *buildLinker() const override; + +public: + bool HasNativeLLVMSupport() const override { return true; } + UnwindTableLevel + getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override { + return UnwindTableLevel::Asynchronous; + } + bool isPICDefault() const override { return true; } + bool isPIEDefault(const llvm::opt::ArgList &Args) const override { + return false; + } + bool isPICDefaultForced() const override { return true; } +}; + +} // namespace toolchains +} // namespace clang::driver + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_UEFI_H diff --git a/clang/test/CodeGen/X86/uefi-data-layout.c b/clang/test/CodeGen/X86/uefi-data-layout.c new file mode 100644 index 00000000000000..3e6908dd038b65 --- /dev/null +++ b/clang/test/CodeGen/X86/uefi-data-layout.c @@ -0,0 +1,3 @@ +// RUN: %clang -target x86_64-unknown-uefi -S -emit-llvm -o - %s | \ +// RUN: FileCheck --check-prefix=X86_64_UEFI %s +// X86_64_UEFI: target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" diff --git a/clang/test/Driver/uefi-constructed-args.c b/clang/test/Driver/uefi-constructed-args.c new file mode 100644 index 00000000000000..e90857bb6fb5b8 --- /dev/null +++ b/clang/test/Driver/uefi-constructed-args.c @@ -0,0 +1,12 @@ +// RUN: %clang_cl -### --target=x86_64-unknown-uefi -g -- %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK %s +// CHECK: "-cc1" +// CHECK-SAME: "-triple" "x86_64-unknown-uefi" +// CHECK-SAME: "-mrelocation-model" "pic" "-pic-level" "2" +// CHECK-SAME: "-mframe-pointer=all" +// CHECK-NEXT: "-nologo" +// CHECK-SAME: "-subsystem:efi_application" +// CHECK-SAME: "-entry:EfiMain" +// CHECK-SAME: "-tsaware:no" +// CHECK-SAME: "-dll" +// CHECK-SAME: "-debug" diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp index a9b5f3c700315c..8542a168f93c27 100644 --- a/clang/unittests/Driver/ToolChainTest.cpp +++ b/clang/unittests/Driver/ToolChainTest.cpp @@ -14,6 +14,7 @@ #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" @@ -574,6 +575,26 @@ TEST(CompilerInvocation, SplitSwarfSingleCrash) { EXPECT_TRUE(CI); // no-crash } +TEST(ToolChainTest, UEFICallingConventionTest) { + clang::CompilerInstance compiler; + compiler.createDiagnostics(); + + std::string TrStr = "x86_64-unknown-uefi"; + llvm::Triple Tr(TrStr); + Tr.setOS(llvm::Triple::OSType::UEFI); + Tr.setVendor(llvm::Triple::VendorType::UnknownVendor); + Tr.setEnvironment(llvm::Triple::EnvironmentType::UnknownEnvironment); + Tr.setArch(llvm::Triple::ArchType::x86_64); + + compiler.getTargetOpts().Triple = Tr.getTriple(); + compiler.setTarget(clang::TargetInfo::CreateTargetInfo( + compiler.getDiagnostics(), + std::make_shared(compiler.getTargetOpts()))); + + EXPECT_EQ(compiler.getTarget().getCallingConvKind(true), + TargetInfo::CallingConvKind::CCK_MicrosoftWin64); +} + TEST(GetDriverMode, PrefersLastDriverMode) { static constexpr const char *Args[] = {"clang-cl", "--driver-mode=foo", "--driver-mode=bar", "foo.cpp"};