[clang] [clang] Allow -fbuiltin to override -ffreestanding (PR #222828)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 19:31:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-driver
Author: Chad Austin (chadaustin)
<details>
<summary>Changes</summary>
For parity with gcc, allow an explicit -fbuiltin in freestanding compilations while preserving -fno-builtin as the default.
This allows memcpy to be inlined, especially important when targeting platforms with support for unaligned loads and stores.
Preserve the override when generating cc1 arguments.
Fixes #<!-- -->20418
---
Full diff: https://github.com/llvm/llvm-project/pull/222828.diff
6 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+4)
- (modified) clang/docs/UsersManual.md (+2-1)
- (modified) clang/include/clang/Options/Options.td (+2-1)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+41-8)
- (modified) clang/lib/Frontend/CompilerInvocation.cpp (+20-3)
- (modified) clang/test/CodeGen/PR3589-freestanding-libcalls.c (+6)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 12b0da9b299a6..90016ec2577d7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -261,6 +261,10 @@ features cannot lower the translation-unit ABI level;
- All options of the `-fzero-call-used-regs` compiler flag are now allowed on RISC-V.
+- `-fbuiltin` can now explicitly enable builtin function optimizations in
+ freestanding compilations. `-ffreestanding` continues to disable builtin
+ functions by default. (#GH20418)
+
### Removed Compiler Flags
### Attribute Changes in Clang
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 03f6a07d1a02c..2f8cd60b196d1 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -1199,7 +1199,8 @@ Passing the `-ffreestanding` flag causes Clang to build for a freestanding
(rather than a hosted) environment. The flag has the following effects:
- the `__STDC_HOSTED__` predefined macro will expand to `0`,
-- builtin functions are disabled by default (`-fno-builtins`),
+- builtin functions are disabled by default (`-fno-builtin`), but can be
+ explicitly enabled with `-fbuiltin`,
- unwind tables are disabled by default
(`fno-asynchronous-unwind-tables -fno-unwind-tables`), and
- does not treat the global `main` function as a special function.
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index f7e22ee84a9f4..79e29be24faf3 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2177,7 +2177,8 @@ defm borland_extensions : BoolFOption<"borland-extensions",
"Accept non-standard constructs supported by the Borland compiler">,
NegFlag<SetFalse>>;
def fbuiltin : Flag<["-"], "fbuiltin">, Group<f_Group>,
- Visibility<[ClangOption, CLOption, DXCOption, FlangOption, FC1Option]>;
+ Visibility<[ClangOption, CC1Option, CLOption, DXCOption, FlangOption,
+ FC1Option]>;
def fbuiltin_module_map : Flag <["-"], "fbuiltin-module-map">, Group<f_Group>,
Flags<[]>, HelpText<"Load the clang builtins module map file.">;
defm caret_diagnostics : BoolFOption<"caret-diagnostics",
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index ab852bf0e0043..29dabeba26979 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -61,6 +61,7 @@
#include "llvm/TargetParser/RISCVTargetParser.h"
#include <cctype>
#include <iterator>
+#include <optional>
using namespace clang::driver;
using namespace clang::driver::tools;
@@ -4021,16 +4022,48 @@ static void RenderOpenACCOptions(const Driver &D, const ArgList &Args,
static void RenderBuiltinOptions(const ToolChain &TC, const llvm::Triple &T,
const ArgList &Args, ArgStringList &CmdArgs) {
- // -fbuiltin is default unless -mkernel is used.
- bool UseBuiltins =
- Args.hasFlag(options::OPT_fbuiltin, options::OPT_fno_builtin,
- !Args.hasArg(options::OPT_mkernel));
+ bool KernelOrKext = false;
+ bool Freestanding = false;
+ bool UseBuiltins = true;
+ std::optional<bool> ExplicitUseBuiltins;
+ for (const Arg *A : Args) {
+ switch (A->getOption().getID()) {
+ case options::OPT_fbuiltin:
+ A->claim();
+ ExplicitUseBuiltins = true;
+ UseBuiltins = true;
+ break;
+ case options::OPT_fno_builtin:
+ A->claim();
+ ExplicitUseBuiltins = false;
+ UseBuiltins = false;
+ break;
+ case options::OPT_ffreestanding:
+ A->claim();
+ Freestanding = true;
+ UseBuiltins = false;
+ break;
+ case options::OPT_fhosted:
+ A->claim();
+ Freestanding = KernelOrKext;
+ UseBuiltins = ExplicitUseBuiltins.value_or(!Freestanding);
+ break;
+ case options::OPT_mkernel:
+ case options::OPT_fapple_kext:
+ A->claim();
+ KernelOrKext = true;
+ Freestanding = true;
+ UseBuiltins = false;
+ break;
+ default:
+ break;
+ }
+ }
+
if (!UseBuiltins)
CmdArgs.push_back("-fno-builtin");
-
- // -ffreestanding implies -fno-builtin.
- if (Args.hasArg(options::OPT_ffreestanding))
- UseBuiltins = false;
+ else if (Freestanding)
+ CmdArgs.push_back("-fbuiltin");
// Process the -fno-builtin-* options.
for (const Arg *A : Args.filtered(options::OPT_fno_builtin_)) {
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 037647f76bd1d..ad396d8b9000b 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1615,8 +1615,8 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
else if (Opts.OptimizeSize == 2)
GenerateArg(Consumer, OPT_O, "z");
- // SimplifyLibCalls is set only in the absence of -fno-builtin and
- // -ffreestanding. We'll consider that when generating them.
+ // SimplifyLibCalls is derived from LangOptions::NoBuiltin. We'll consider
+ // that when generating -f[no-]builtin.
// NoBuiltinFuncs are generated by LangOptions.
@@ -3875,6 +3875,8 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
if (Opts.NoBuiltin && !Opts.Freestanding)
GenerateArg(Consumer, OPT_fno_builtin);
+ else if (!Opts.NoBuiltin && Opts.Freestanding)
+ GenerateArg(Consumer, OPT_fbuiltin);
if (!Opts.NoBuiltin)
for (const auto &Func : Opts.NoBuiltinFuncs)
@@ -4294,7 +4296,22 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
Args.hasFlag(OPT_fconvergent_functions, OPT_fno_convergent_functions,
HasConvergentOperations);
- Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding;
+ Opts.NoBuiltin = false;
+ for (const Arg *A : Args) {
+ switch (A->getOption().getID()) {
+ case OPT_fbuiltin:
+ A->claim();
+ Opts.NoBuiltin = false;
+ break;
+ case OPT_fno_builtin:
+ case OPT_ffreestanding:
+ A->claim();
+ Opts.NoBuiltin = true;
+ break;
+ default:
+ break;
+ }
+ }
if (!Opts.NoBuiltin)
getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs);
if (Arg *A = Args.getLastArg(options::OPT_LongDouble_Group)) {
diff --git a/clang/test/CodeGen/PR3589-freestanding-libcalls.c b/clang/test/CodeGen/PR3589-freestanding-libcalls.c
index d691d8f25e114..ca336d7f6bf57 100644
--- a/clang/test/CodeGen/PR3589-freestanding-libcalls.c
+++ b/clang/test/CodeGen/PR3589-freestanding-libcalls.c
@@ -1,6 +1,12 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | grep 'declare i32 @printf' | count 1
// RUN: %clang_cc1 -triple i386-unknown-unknown -O2 -emit-llvm %s -o - | grep 'declare noundef i32 @puts' | count 1
// RUN: %clang_cc1 -triple i386-unknown-unknown -ffreestanding -O2 -emit-llvm %s -o - | not grep 'declare noundef i32 @puts'
+// RUN: %clang_cc1 -round-trip-args -triple i386-unknown-unknown -ffreestanding -fbuiltin -O2 -emit-llvm %s -o - | grep 'declare noundef i32 @puts' | count 1
+// RUN: %clang_cc1 -round-trip-args -triple i386-unknown-unknown -fbuiltin -ffreestanding -O2 -emit-llvm %s -o - | not grep 'declare noundef i32 @puts'
+// RUN: %clang -target i386-unknown-unknown -ffreestanding -fbuiltin -O2 -emit-llvm -S %s -o - | grep 'declare noundef i32 @puts' | count 1
+// RUN: %clang -target i386-unknown-unknown -fbuiltin -ffreestanding -O2 -emit-llvm -S %s -o - | not grep 'declare noundef i32 @puts'
+// RUN: %clang -target i386-unknown-unknown -ffreestanding -fbuiltin -ffreestanding -O2 -emit-llvm -S %s -o - | not grep 'declare noundef i32 @puts'
+// RUN: %clang -target i386-unknown-unknown -ffreestanding -fbuiltin -fno-builtin -O2 -emit-llvm -S %s -o - | not grep 'declare noundef i32 @puts'
int printf(const char *, ...);
``````````
</details>
https://github.com/llvm/llvm-project/pull/222828
More information about the cfe-commits
mailing list