[clang] 37e2c3b - [CIR] Serialize lowering-relevant LangOptions as #cir.lowering_lang_options (#224757)

via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 21 12:59:27 PDT 2026


Author: Konstantinos Parasyris
Date: 2026-09-21T12:59:20-07:00
New Revision: 37e2c3bc9610b7952405ddd7c3557c93e933e554

URL: https://github.com/llvm/llvm-project/commit/37e2c3bc9610b7952405ddd7c3557c93e933e554
DIFF: https://github.com/llvm/llvm-project/commit/37e2c3bc9610b7952405ddd7c3557c93e933e554.diff

LOG: [CIR] Serialize lowering-relevant LangOptions as #cir.lowering_lang_options  (#224757)

Post-CIRGen lowering (LoweringPrepare, CallConvLowering) 
reads a handful of LangOptions flags that have no 
triple/module representation. A reloaded `.cir`
module has no live `clang::LangOptions`, so those flags must be
serialized for the file to be self-describing and lower the
same way it was compiled.

Add a module-level aggregate attribute `#cir.lowering_lang_options`
holding the LangOpts facts lowering consumes: "exceptions, 
threadsafe_statics, cuda, cuda_is_device, hip, gpu_rdc, openmp, 
openmp_is_target_device, and clang_abi_compat". Target-derived
facts (size_t width, CXXABI, comdat, char/pointer/atomic width,
SDK version, fp formats) are **NOT** stored: they are
rebuilt from `cir.triple`

Co-authored-by: Claude Opus 4.8 (1M context) <noreply at anthropic.com>

Added: 
    clang/test/CIR/CodeGen/lowering-lang-options.cpp
    clang/test/CIR/IR/lowering-lang-options.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/Dialect/IR/CIRDialect.td
    clang/lib/CIR/CodeGen/CIRGenModule.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index c5f19592d4592..717ef254b1965 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -2247,6 +2247,59 @@ def CIR_StaticLocalInfoAttr
   let canHaveIllegalCXXABIType = 0;
 }
 
+//===----------------------------------------------------------------------===//
+// LoweringLangOptionsAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_LoweringLangOptionsAttr
+    : CIR_Attr<"LoweringLangOptions", "lowering_lang_options"> {
+  let summary = "LangOptions facts that post-CIRGen lowering consumes";
+  let description = [{
+    Serializes the subset of `clang::LangOptions` that post-CIRGen lowering
+    reads, as plain data, so a reloaded `.cir` module lowers the same way it
+    was compiled without a live `clang::LangOptions`. Only LangOpts flags with
+    no triple/module representation live here; target-derived facts (size_t
+    width, CXXABI, fp formats, etc.) are rebuilt from `cir.triple` instead.
+
+    Each bool field mirrors the like-named `LangOptions` member: `exceptions`,
+    `threadsafe_statics`, `cuda`, `cuda_is_device`, `hip`, `gpu_rdc`,
+    `openmp` (`LangOpts.OpenMP != 0`; only its on/off state matters), and
+    `openmp_is_target_device`. `clang_abi_compat` is an `i32` mirroring
+    `LangOptions::ClangABI`; it is an `i32` rather than a CIR enum because
+    `ClangABI` gains an enumerator every Clang release (see
+    `clang/Basic/ABIVersions.def`), and the ordinal round-trips fine within a
+    single toolchain.
+
+    Example:
+    ```
+    module attributes {cir.lowering_lang_options = #cir.lowering_lang_options<
+      exceptions = false, threadsafe_statics = true, cuda = false,
+      cuda_is_device = false, hip = false, gpu_rdc = false, openmp = false,
+      openmp_is_target_device = false, clang_abi_compat = 17>} { }
+    ```
+  }];
+
+  let parameters = (ins
+    "bool":$exceptions,
+    "bool":$threadsafe_statics,
+    "bool":$cuda,
+    "bool":$cuda_is_device,
+    "bool":$hip,
+    "bool":$gpu_rdc,
+    "bool":$openmp,
+    "bool":$openmp_is_target_device,
+    "int32_t":$clang_abi_compat
+  );
+
+  let assemblyFormat = [{
+    `<` struct($exceptions, $threadsafe_statics, $cuda, $cuda_is_device, $hip,
+               $gpu_rdc, $openmp, $openmp_is_target_device,
+               $clang_abi_compat) `>`
+  }];
+
+  let canHaveIllegalCXXABIType = 0;
+}
+
 //===----------------------------------------------------------------------===//
 // AnnotationAttr
 //===----------------------------------------------------------------------===//

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index f07cd7c0dbeb3..423bffb06543a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -96,6 +96,10 @@ def CIR_Dialect : Dialect {
     // Mangled symbol name of the C++20 named-module initializer function,
     // precomputed by CIRGen so later passes don't need a live ASTContext.
     static llvm::StringRef getCXXModuleInitFnNameAttrName() { return "cir.cxx_module_init_fn_name"; }
+    // LangOptions facts that post-CIRGen lowering consumes, serialized by
+    // CIRGen so a reloaded .cir module is self-describing without a live
+    // clang::LangOptions. See #cir.lowering_lang_options.
+    static llvm::StringRef getLoweringLangOptionsAttrName() { return "cir.lowering_lang_options"; }
     static llvm::StringRef getMustTailAttrName() { return "musttail"; }
     static llvm::StringRef getCatchCopyThunkAttrName() { return "cir.eh.catch_copy_thunk"; }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 1fc4dc134028b..2f032fe39e66e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3973,6 +3973,24 @@ void CIRGenModule::release() {
     }
   }
 
+  // Serialize the lowering-relevant LangOptions onto the ModuleOp,
+  // unconditionally, so a reloaded .cir module is self-describing. See
+  // #cir.lowering_lang_options.
+  theModule->setAttr(
+      cir::CIRDialect::getLoweringLangOptionsAttrName(),
+      cir::LoweringLangOptionsAttr::get(
+          &getMLIRContext(),
+          /*exceptions=*/langOpts.Exceptions,
+          /*threadsafe_statics=*/langOpts.ThreadsafeStatics,
+          /*cuda=*/langOpts.CUDA,
+          /*cuda_is_device=*/langOpts.CUDAIsDevice,
+          /*hip=*/langOpts.HIP,
+          /*gpu_rdc=*/langOpts.GPURelocatableDeviceCode,
+          /*openmp=*/langOpts.OpenMP != 0,
+          /*openmp_is_target_device=*/langOpts.OpenMPIsTargetDevice,
+          /*clang_abi_compat=*/
+          static_cast<int32_t>(langOpts.getClangABICompat())));
+
   // Classic codegen calls `checkAliases` here to validate any alias
   // definitions emitted during codegen.
   assert(!cir::MissingFeatures::checkAliases());

diff  --git a/clang/test/CIR/CodeGen/lowering-lang-options.cpp b/clang/test/CIR/CodeGen/lowering-lang-options.cpp
new file mode 100644
index 0000000000000..196577428208a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/lowering-lang-options.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir \
+// RUN:   -emit-cir %s -o - | FileCheck %s --check-prefix=PLAIN
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir \
+// RUN:   -fexceptions -fcxx-exceptions -emit-cir %s -o - \
+// RUN:   | FileCheck %s --check-prefix=EXC
+
+// CIRGen serializes the LangOptions facts that post-CIRGen lowering consumes
+// onto the ModuleOp as a #cir.lowering_lang_options attribute, unconditionally, so
+// a reloaded .cir module lowers the same way it was compiled without a live
+// clang::LangOptions.
+
+int x;
+
+// Plain C++: exceptions off, thread-safe statics on, no CUDA/HIP/OpenMP. The
+// clang_abi_compat ordinal mirrors LangOptions::ClangABI and is left as a
+// regex since its default (Latest) shifts with each Clang release.
+// PLAIN: cir.lowering_lang_options = #cir.lowering_lang_options<
+// PLAIN-SAME: exceptions = false
+// PLAIN-SAME: threadsafe_statics = true
+// PLAIN-SAME: cuda = false
+// PLAIN-SAME: cuda_is_device = false
+// PLAIN-SAME: hip = false
+// PLAIN-SAME: gpu_rdc = false
+// PLAIN-SAME: openmp = false
+// PLAIN-SAME: openmp_is_target_device = false
+// PLAIN-SAME: clang_abi_compat = {{[0-9]+}}>
+
+// -fexceptions -fcxx-exceptions flips exceptions. (OpenMP is exercised in the
+// #cir.lowering_lang_options round-trip test, since -fopenmp on a global here hits
+// an unrelated CIRGen NYI for OpenMP globals.)
+// EXC: cir.lowering_lang_options = #cir.lowering_lang_options<
+// EXC-SAME: exceptions = true
+// EXC-SAME: threadsafe_statics = true
+// EXC-SAME: openmp = false
+// EXC-SAME: openmp_is_target_device = false
+// EXC-SAME: clang_abi_compat = {{[0-9]+}}>

diff  --git a/clang/test/CIR/IR/lowering-lang-options.cir b/clang/test/CIR/IR/lowering-lang-options.cir
new file mode 100644
index 0000000000000..2ef90bd94ed4e
--- /dev/null
+++ b/clang/test/CIR/IR/lowering-lang-options.cir
@@ -0,0 +1,16 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+// #cir.lowering_lang_options holds plain data, so it parses and prints without a
+// live clang::LangOptions. Check that every field round-trips, including a
+// non-default i32 clang_abi_compat and non-default booleans.
+
+module attributes {cir.lowering_lang_options = #cir.lowering_lang_options<
+  exceptions = true, threadsafe_statics = false, cuda = true,
+  cuda_is_device = true, hip = true, gpu_rdc = true, openmp = true,
+  openmp_is_target_device = true, clang_abi_compat = 9>} {
+}
+
+// CHECK: cir.lowering_lang_options = #cir.lowering_lang_options<
+// CHECK-SAME: exceptions = true, threadsafe_statics = false, cuda = true,
+// CHECK-SAME: cuda_is_device = true, hip = true, gpu_rdc = true, openmp = true,
+// CHECK-SAME: openmp_is_target_device = true, clang_abi_compat = 9>


        


More information about the cfe-commits mailing list