https://github.com/chadaustin created 
https://github.com/llvm/llvm-project/pull/222828

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

>From be4cad383de92186cf68da2b24b2690ef435d4a0 Mon Sep 17 00:00:00 2001
From: Chad Austin <[email protected]>
Date: Thu, 10 Sep 2026 17:48:54 -0700
Subject: [PATCH] [clang] Allow -fbuiltin to override -ffreestanding

Honor an explicit -fbuiltin in freestanding compilations while preserving 
-fno-builtin as the default.

Preserve the override when generating cc1 arguments.

Fixes #20418
---
 clang/docs/ReleaseNotes.md                    |  4 ++
 clang/docs/UsersManual.md                     |  3 +-
 clang/include/clang/Options/Options.td        |  3 +-
 clang/lib/Driver/ToolChains/Clang.cpp         | 49 ++++++++++++++++---
 clang/lib/Frontend/CompilerInvocation.cpp     | 23 +++++++--
 .../CodeGen/PR3589-freestanding-libcalls.c    |  6 +++
 6 files changed, 75 insertions(+), 13 deletions(-)

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 *, ...);
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to