[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