[llvm-branch-commits] [clang] [CIR] Migrate seven operation enums off IntegerAttr (PR #220883)
Henrich Lauko via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Sep 3 04:31:08 PDT 2026
https://github.com/xlauko created https://github.com/llvm/llvm-project/pull/220883
CastKind, DynamicCastKind, CmpOpKind, ComplexRangeKind, InitCatchKind,
CaseOpKind and AwaitKind were I32EnumAttrs with the default
genSpecializedAttr, so each generated an IntegerAttr subclass. Operations
printed them symbolically because the format referenced the argument
directly, but the underlying attribute had no dialect spelling at all. In an
attribute dictionary or a generic-form dump it was a plain integer, so
`cir.cast bitcast` was stored as `kind = 1 : i32`.
Each enum now sets genSpecializedAttr = 0 and gets a CIR_EnumAttr wrapper,
which is the pattern EnumAttr.td documents, and the operations wrap the
argument in `enum()` to keep the bare keyword. The attributes gain a real
spelling, e.g. `#cir.cast<bitcast>`.
Operation syntax is unchanged, hence no test churn. enum-attrs.cir covers the
new standalone spellings, since nothing else in the test suite exercises
them. It also covers tls_model, whose spelling the earlier TLS commit
changed without leaving a standalone case behind.
Mnemonics drop the suffix the C++ enum class name carries, since `kind` adds
nothing to `#cir.cast<bitcast>`. DynamicCastKind spells out `dynamic_cast`
rather than abbreviating. `dyn_cast` is already taken by the operation and by
`#cir.dyn_cast_info`, and it reads as the LLVM cast function rather than the
C++ operator being modelled.
The def names are constrained, unlike the mnemonics. CIRLoweringEmitter turns
each EnumAttr record into an `isa<cir::...>` entry for CXXABILowering by
stripping the prefix up to the first underscore, so the def must be
CIR_<CppClassName>, and EnumAttr's cppClassName defaults to <EnumName>Attr.
CUDADeviceVarKind gets different treatment. It is only ever a raw parameter
of CIR_CUDAVarRegistrationInfoAttr, which has a custom assembly format, and
its generated CUDADeviceVarKindAttr was referenced nowhere, so it just sets
genSpecializedAttr = 0 with no wrapper. It also moves to CIR_I32EnumAttr
instead of spelling out cppNamespace.
---
Stacked on #220881. Based on `users/xlauko/cir-enum-04-drop-visibility-attr`, so the diff shown is this commit alone.
>From d86c70a39146c1c72815cdc5094a29ae325404ec Mon Sep 17 00:00:00 2001
From: Henrich Lauko <hlauko at nvidia.com>
Date: Thu, 3 Sep 2026 11:29:23 +0000
Subject: [PATCH] [CIR] Migrate seven operation enums off IntegerAttr
CastKind, DynamicCastKind, CmpOpKind, ComplexRangeKind, InitCatchKind,
CaseOpKind and AwaitKind were I32EnumAttrs with the default
genSpecializedAttr, so each generated an IntegerAttr subclass. Operations
printed them symbolically because the format referenced the argument
directly, but the underlying attribute had no dialect spelling at all. In an
attribute dictionary or a generic-form dump it was a plain integer, so
`cir.cast bitcast` was stored as `kind = 1 : i32`.
Each enum now sets genSpecializedAttr = 0 and gets a CIR_EnumAttr wrapper,
which is the pattern EnumAttr.td documents, and the operations wrap the
argument in `enum()` to keep the bare keyword. The attributes gain a real
spelling, e.g. `#cir.cast<bitcast>`.
Operation syntax is unchanged, hence no test churn. enum-attrs.cir covers the
new standalone spellings, since nothing else in the test suite exercises
them. It also covers tls_model, whose spelling the earlier TLS commit
changed without leaving a standalone case behind.
Mnemonics drop the suffix the C++ enum class name carries, since `kind` adds
nothing to `#cir.cast<bitcast>`. DynamicCastKind spells out `dynamic_cast`
rather than abbreviating. `dyn_cast` is already taken by the operation and by
`#cir.dyn_cast_info`, and it reads as the LLVM cast function rather than the
C++ operator being modelled.
The def names are constrained, unlike the mnemonics. CIRLoweringEmitter turns
each EnumAttr record into an `isa<cir::...>` entry for CXXABILowering by
stripping the prefix up to the first underscore, so the def must be
CIR_<CppClassName>, and EnumAttr's cppClassName defaults to <EnumName>Attr.
CUDADeviceVarKind gets different treatment. It is only ever a raw parameter
of CIR_CUDAVarRegistrationInfoAttr, which has a custom assembly format, and
its generated CUDADeviceVarKindAttr was referenced nowhere, so it just sets
genSpecializedAttr = 0 with no wrapper. It also moves to CIR_I32EnumAttr
instead of spelling out cppNamespace.
---
.../clang/CIR/Dialect/IR/CIRCUDAAttrs.td | 7 +-
clang/include/clang/CIR/Dialect/IR/CIROps.td | 82 ++++++++++++------
clang/test/CIR/IR/enum-attrs.cir | 86 +++++++++++++++++++
3 files changed, 147 insertions(+), 28 deletions(-)
create mode 100644 clang/test/CIR/IR/enum-attrs.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
index 73dd7baaa5caa..2d5f70ec9c3ad 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
@@ -68,14 +68,15 @@ def CIR_CUDABinaryHandleAttr : CIR_Attr<
let assemblyFormat = "`<` $name `>`";
}
-// Enum for device variable kinds
-def CIR_CUDADeviceVarKind : I32EnumAttr<"CUDADeviceVarKind",
+// No wrapper attribute: the kind is only ever printed by
+// CIR_CUDAVarRegistrationInfoAttr's own assembly format.
+def CIR_CUDADeviceVarKind : CIR_I32EnumAttr<"CUDADeviceVarKind",
"CUDA device variable kind", [
I32EnumAttrCase<"Variable", 0>,
I32EnumAttrCase<"Surface", 1>, // Future
I32EnumAttrCase<"Texture", 2>, // Future
]> {
- let cppNamespace = "::cir";
+ let genSpecializedAttr = 0;
}
// Attribute carrying device variable registration flags
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 8ccd086a146fb..4d807f88715c2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -242,7 +242,11 @@ def CIR_CastKind : CIR_I32EnumAttr<"CastKind", "cast kind", [
// Enums below are specific to CIR and don't have a correspondence to classic
// codegen:
I32EnumAttrCase<"bool_to_float", 1000>,
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CastKindAttr : CIR_EnumAttr<CIR_CastKind, "cast">;
def CIR_CastOp : CIR_Op<"cast", !listconcat([
DeclareOpInterfaceMethods<PromotableOpInterface>
@@ -302,12 +306,12 @@ def CIR_CastOp : CIR_Op<"cast", !listconcat([
```
}];
- let arguments = (ins CIR_CastKind:$kind, CIR_AnyType:$src,
+ let arguments = (ins CIR_CastKindAttr:$kind, CIR_AnyType:$src,
OptionalAttr<CIR_FenvAttr>:$fenv);
let results = (outs CIR_AnyType:$result);
let assemblyFormat = [{
- $kind $src `:` type($src) `->` type($result) attr-dict
+ enum($kind) $src `:` type($src) `->` type($result) attr-dict
}];
let builders = [
@@ -395,7 +399,12 @@ def CIR_DynamicCastKind : CIR_I32EnumAttr<
"DynamicCastKind", "dynamic cast kind", [
I32EnumAttrCase<"Ptr", 0, "ptr">,
I32EnumAttrCase<"Ref", 1, "ref">
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_DynamicCastKindAttr
+ : CIR_EnumAttr<CIR_DynamicCastKind, "dynamic_cast">;
def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
let summary = "Perform dynamic cast on record pointers";
@@ -450,7 +459,7 @@ def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
}];
let arguments = (ins
- CIR_DynamicCastKind:$kind,
+ CIR_DynamicCastKindAttr:$kind,
CIR_PtrToRecordType:$src,
OptionalAttr<CIR_DynamicCastInfoAttr>:$info,
UnitAttr:$relative_layout
@@ -461,7 +470,7 @@ def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
);
let assemblyFormat = [{
- $kind (`relative_layout` $relative_layout^)? $src
+ enum($kind) (`relative_layout` $relative_layout^)? $src
`:` qualified(type($src)) `->` qualified(type($result))
(qualified($info)^)? attr-dict
}];
@@ -1513,7 +1522,11 @@ def CIR_CaseOpKind : CIR_I32EnumAttr<"CaseOpKind", "case kind", [
I32EnumAttrCase<"Equal", 1, "equal">,
I32EnumAttrCase<"Anyof", 2, "anyof">,
I32EnumAttrCase<"Range", 3, "range">
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CaseOpKindAttr : CIR_EnumAttr<CIR_CaseOpKind, "case">;
def CIR_CaseOp : CIR_Op<"case", [
DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
@@ -1537,10 +1550,11 @@ def CIR_CaseOp : CIR_Op<"case", [
Each case region must be explicitly terminated.
}];
- let arguments = (ins ArrayAttr:$value, CIR_CaseOpKind:$kind);
+ let arguments = (ins ArrayAttr:$value, CIR_CaseOpKindAttr:$kind);
let regions = (region AnyRegion:$caseRegion);
- let assemblyFormat = "`(` $kind `,` $value `)` $caseRegion attr-dict";
+ let assemblyFormat =
+ "`(` enum($kind) `,` $value `)` $caseRegion attr-dict";
let skipDefaultBuilders = 1;
let builders = [
@@ -2522,7 +2536,11 @@ def CIR_CmpOpKind : CIR_I32EnumAttr<"CmpOpKind", "compare operation kind", [
I32EnumAttrCase<"ne", 5>,
I32EnumAttrCase<"one", 6>,
I32EnumAttrCase<"uno", 7>
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CmpOpKindAttr : CIR_EnumAttr<CIR_CmpOpKind, "cmp">;
def CIR_CmpOp : CIR_Op<"cmp",
!listconcat([SameTypeOperands], CIR_FenvOpTraits)> {
@@ -2561,7 +2579,7 @@ def CIR_CmpOp : CIR_Op<"cmp",
}];
let arguments = (ins
- CIR_CmpOpKind:$kind,
+ CIR_CmpOpKindAttr:$kind,
CIR_ComparableType:$lhs,
CIR_ComparableType:$rhs,
OptionalAttr<CIR_FenvAttr>:$fenv
@@ -2570,7 +2588,7 @@ def CIR_CmpOp : CIR_Op<"cmp",
let results = (outs CIR_BoolType:$result);
let assemblyFormat = [{
- $kind $lhs `,` $rhs `:` type($lhs) attr-dict
+ enum($kind) $lhs `,` $rhs `:` type($lhs) attr-dict
}];
let builders = [
@@ -4718,7 +4736,11 @@ def CIR_AwaitKind : CIR_I32EnumAttr<"AwaitKind", "await kind", [
I32EnumAttrCase<"User", 1, "user">,
I32EnumAttrCase<"Yield", 2, "yield">,
I32EnumAttrCase<"Final", 3, "final">
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_AwaitKindAttr : CIR_EnumAttr<CIR_AwaitKind, "await">;
def CIR_AwaitOp : CIR_Op<"await",[
DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
@@ -4774,12 +4796,12 @@ def CIR_AwaitOp : CIR_Op<"await",[
as part of the enclosing await scope.
}];
- let arguments = (ins CIR_AwaitKind:$kind);
+ let arguments = (ins CIR_AwaitKindAttr:$kind);
let regions = (region SizedRegion<1>:$ready,
SizedRegion<1>:$suspend,
SizedRegion<1>:$resume);
let assemblyFormat = [{
- `(` $kind `,`
+ `(` enum($kind) `,`
`ready` `:` $ready `,`
`suspend` `:` $suspend `,`
`resume` `:` $resume `,`
@@ -5919,7 +5941,7 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp",
}];
let arguments = (ins
- CIR_CmpOpKind:$kind,
+ CIR_CmpOpKindAttr:$kind,
CIR_VectorType:$lhs,
CIR_VectorType:$rhs,
OptionalAttr<CIR_FenvAttr>:$fenv
@@ -5928,7 +5950,7 @@ def CIR_VecCmpOp : CIR_Op<"vec.cmp",
let results = (outs CIR_VectorType:$result);
let assemblyFormat = [{
- `(` $kind `,` $lhs `,` $rhs `)` `:` qualified(type($lhs)) `,`
+ `(` enum($kind) `,` $lhs `,` $rhs `)` `:` qualified(type($lhs)) `,`
qualified(type($result)) attr-dict
}];
@@ -6502,20 +6524,26 @@ def CIR_ComplexRangeKind : CIR_I32EnumAttr<
I32EnumAttrCase<"Improved", 1, "improved">,
I32EnumAttrCase<"Promoted", 2, "promoted">,
I32EnumAttrCase<"Basic", 3, "basic">,
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_ComplexRangeKindAttr
+ : CIR_EnumAttr<CIR_ComplexRangeKind, "complex_range">;
class CIR_ComplexRangeBinOp<string mnemonic>
: CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]> {
let arguments = (ins
CIR_ComplexType:$lhs,
CIR_ComplexType:$rhs,
- CIR_ComplexRangeKind:$range
+ CIR_ComplexRangeKindAttr:$range
);
let results = (outs CIR_ComplexType:$result);
let assemblyFormat = [{
- $lhs `,` $rhs `range` `(` $range `)` `:` qualified(type($result)) attr-dict
+ $lhs `,` $rhs `range` `(` enum($range) `)` `:` qualified(type($result))
+ attr-dict
}];
let hasLLVMLowering = false;
@@ -8703,7 +8731,11 @@ def CIR_InitCatchKind : CIR_I32EnumAttr<
I32EnumAttrCase<"Objc", 3, "objc">,
I32EnumAttrCase<"TrivialCopy", 4, "trivial_copy">,
I32EnumAttrCase<"NonTrivialCopy", 5, "non_trivial_copy">,
-]>;
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_InitCatchKindAttr : CIR_EnumAttr<CIR_InitCatchKind, "init_catch">;
def CIR_InitCatchParamOp : CIR_Op<"init_catch_param"> {
let summary = "Initialize a catch parameter from the exception pointer";
@@ -8733,11 +8765,11 @@ def CIR_InitCatchParamOp : CIR_Op<"init_catch_param"> {
let arguments = (ins
CIR_PointerType:$exn_ptr,
CIR_PointerType:$param_addr,
- CIR_InitCatchKind:$kind
+ CIR_InitCatchKindAttr:$kind
);
let assemblyFormat = [{
- $kind $exn_ptr `to` $param_addr `:`
+ enum($kind) $exn_ptr `to` $param_addr `:`
qualified(type($exn_ptr)) `,` qualified(type($param_addr)) attr-dict
}];
@@ -8783,12 +8815,12 @@ def CIR_ConstructCatchParamOp : CIR_Op<"construct_catch_param", [
let arguments = (ins
CIR_EhTokenType:$eh_token,
CIR_PointerType:$param_addr,
- CIR_InitCatchKind:$kind,
+ CIR_InitCatchKindAttr:$kind,
OptionalAttr<FlatSymbolRefAttr>:$copy_fn
);
let assemblyFormat = [{
- $kind $eh_token `to` $param_addr `using` ($copy_fn^)? `:`
+ enum($kind) $eh_token `to` $param_addr `using` ($copy_fn^)? `:`
qualified(type($param_addr)) attr-dict
}];
diff --git a/clang/test/CIR/IR/enum-attrs.cir b/clang/test/CIR/IR/enum-attrs.cir
new file mode 100644
index 0000000000000..0e0238c690dbc
--- /dev/null
+++ b/clang/test/CIR/IR/enum-attrs.cir
@@ -0,0 +1,86 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+// Enum attributes that operations print as a bare keyword must still have a
+// readable standalone spelling for when they show up in an attribute
+// dictionary. These used to be IntegerAttr subclasses, which printed as
+// `2 : i32`.
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.func @cast_attr() {
+ cir.return {cir.test = [#cir.cast<bitcast>, #cir.cast<integral>,
+ #cir.cast<bool_to_float>]}
+}
+
+// CHECK: cir.func @cast_attr() {
+// CHECK: cir.return {cir.test = [#cir.cast<bitcast>, #cir.cast<integral>, #cir.cast<bool_to_float>]}
+
+cir.func @dynamic_cast_attr() {
+ cir.return {cir.test = [#cir.dynamic_cast<ptr>,
+ #cir.dynamic_cast<ref>]}
+}
+
+// CHECK: cir.func @dynamic_cast_attr() {
+// CHECK: cir.return {cir.test = [#cir.dynamic_cast<ptr>, #cir.dynamic_cast<ref>]}
+
+cir.func @cmp_attr() {
+ cir.return {cir.test = [#cir.cmp<lt>, #cir.cmp<eq>,
+ #cir.cmp<uno>]}
+}
+
+// CHECK: cir.func @cmp_attr() {
+// CHECK: cir.return {cir.test = [#cir.cmp<lt>, #cir.cmp<eq>, #cir.cmp<uno>]}
+
+cir.func @complex_range_attr() {
+ cir.return {cir.test = [#cir.complex_range<full>,
+ #cir.complex_range<basic>]}
+}
+
+// CHECK: cir.func @complex_range_attr() {
+// CHECK: cir.return {cir.test = [#cir.complex_range<full>, #cir.complex_range<basic>]}
+
+cir.func @init_catch_attr() {
+ cir.return {cir.test = [#cir.init_catch<reference>,
+ #cir.init_catch<non_trivial_copy>]}
+}
+
+// CHECK: cir.func @init_catch_attr() {
+// CHECK: cir.return {cir.test = [#cir.init_catch<reference>, #cir.init_catch<non_trivial_copy>]}
+
+cir.func @case_attr() {
+ cir.return {cir.test = [#cir.case<default>, #cir.case<anyof>,
+ #cir.case<range>]}
+}
+
+// CHECK: cir.func @case_attr() {
+// CHECK: cir.return {cir.test = [#cir.case<default>, #cir.case<anyof>, #cir.case<range>]}
+
+cir.func @await_attr() {
+ cir.return {cir.test = [#cir.await<init>, #cir.await<final>]}
+}
+
+// CHECK: cir.func @await_attr() {
+// CHECK: cir.return {cir.test = [#cir.await<init>, #cir.await<final>]}
+
+cir.func @tls_model_attr() {
+ cir.return {cir.test = [#cir.tls_model<tls_dyn>,
+ #cir.tls_model<tls_local_exec>]}
+}
+
+// CHECK: cir.func @tls_model_attr() {
+// CHECK: cir.return {cir.test = [#cir.tls_model<tls_dyn>, #cir.tls_model<tls_local_exec>]}
+
+// The operations themselves keep printing a bare keyword.
+cir.func @operations_print_bare_keywords(%arg0: !s32i, %arg1: !s32i) {
+ %0 = cir.cast bitcast %arg0 : !s32i -> !s32i
+ %1 = cir.cmp lt %arg0, %arg1 : !s32i
+ cir.return
+}
+
+// CHECK: cir.func @operations_print_bare_keywords(%arg0: !s32i, %arg1: !s32i) {
+// CHECK: %0 = cir.cast bitcast %arg0 : !s32i -> !s32i
+// CHECK: %1 = cir.cmp lt %arg0, %arg1 : !s32i
+
+}
More information about the llvm-branch-commits
mailing list