[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