https://github.com/RiverDave updated 
https://github.com/llvm/llvm-project/pull/206576

>From 9d0f66f6fe743d75f3cae14b5e7412e646b6116a Mon Sep 17 00:00:00 2001
From: David Rivera <[email protected]>
Date: Mon, 29 Jun 2026 15:46:11 -0400
Subject: [PATCH 1/2] [CIR] Add offload container operation

Introduce cir.offload.container, a CIR dialect operation that groups one host 
CIR module with one or more device CIR modules in a single IR unit.

Nested modules are tagged with the new cir.offload.kind enum attribute using 
#cir.offload_kind<host> and #cir.offload_kind<device>. The verifier enforces 
the structural contract expected by follow-up offload merge/split pipeline 
patches: host module first, device modules after it, only nested builtin.module 
ops, and at least one device module.

This patch only adds the IR representation and verifier tests; the passes that 
create, consume, or split the container are left to later patches.
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |  1 +
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 59 ++++++++++++++++++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 61 +++++++++++++++++++
 .../test/CIR/IR/invalid-offload-container.cir | 54 ++++++++++++++++
 clang/test/CIR/IR/offload-container.cir       | 32 ++++++++++
 5 files changed, 207 insertions(+)
 create mode 100644 clang/test/CIR/IR/invalid-offload-container.cir
 create mode 100644 clang/test/CIR/IR/offload-container.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td 
b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index c20af04f97a1a..7d189361eee48 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -50,6 +50,7 @@ def CIR_Dialect : Dialect {
     static llvm::StringRef getModuleLevelAsmAttrName() { return 
"cir.module_asm"; }
     static llvm::StringRef getGlobalCtorsAttrName() { return 
"cir.global_ctors"; }
     static llvm::StringRef getGlobalDtorsAttrName() { return 
"cir.global_dtors"; }
+    static llvm::StringRef getOffloadKindAttrName() { return 
"cir.offload.kind"; }
     static llvm::StringRef getOperandSegmentSizesAttrName() { return 
"operandSegmentSizes"; }
     static llvm::StringRef getNoCallerSavedRegsAttrName() { return 
"no_caller_saved_registers"; }
     static llvm::StringRef getNoCallbackAttrName() { return "nocallback"; }
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7d1c48b994b27..0c18aac7f4eb4 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5672,6 +5672,65 @@ def CIR_VecSplatOp : CIR_Op<"vec.splat", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// OffloadKind
+//===----------------------------------------------------------------------===//
+
+def CIR_OffloadKind : CIR_I32EnumAttr<"OffloadKind", "offload kind", [
+  I32EnumAttrCase<"Host", 0, "host">,
+  I32EnumAttrCase<"Device", 1, "device">
+]> {
+  let genSpecializedAttr = 0;
+}
+
+def CIR_OffloadKindAttr : CIR_EnumAttr<CIR_OffloadKind, "offload_kind"> {
+  let summary = "Offload kind (host or device)";
+}
+
+//===----------------------------------------------------------------------===//
+// OffloadContainerOp
+//===----------------------------------------------------------------------===//
+
+def CIR_OffloadContainerOp : CIR_Op<"offload.container", [
+    NoRegionArguments, NoTerminator, SingleBlock, SymbolTable]> {
+  let summary = "Container for host and device CIR modules";
+  let description = [{
+    `cir.offload.container` groups one host CIR module with one or more device
+    CIR modules for offload-aware analysis and transformation.
+
+    The body holds nested `builtin.module` operations. The first nested
+    module is the host module and must carry
+    `cir.offload.kind = #cir.offload_kind<host>`. All remaining nested
+    modules are device modules and must carry
+    `cir.offload.kind = #cir.offload_kind<device>`. There must be at least
+    one device module.
+
+    Example:
+
+    ```mlir
+    cir.offload.container {
+      builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+      }
+      builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+      }
+    }
+    ```
+  }];
+
+  let regions = (region SizedRegion<1>:$body);
+
+  let assemblyFormat = "$body attr-dict";
+
+  let hasVerifier = 1;
+  let hasLLVMLowering = false;
+
+  let extraClassDeclaration = [{
+    mlir::ModuleOp getHostModule();
+    llvm::iterator_range<mlir::Block::op_iterator<mlir::ModuleOp>>
+    getDeviceModules();
+  }];
+}
+
 
//===----------------------------------------------------------------------===//
 // BaseClassAddrOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 67cc5e09f26d0..96501d5808168 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -18,6 +18,7 @@
 
 #include "mlir/IR/Attributes.h"
 #include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/DialectImplementation.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/IR/Value.h"
@@ -2334,6 +2335,66 @@ LogicalResult cir::VTTAddrPointOp::verify() {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// OffloadContainerOp
+//===----------------------------------------------------------------------===//
+
+mlir::ModuleOp cir::OffloadContainerOp::getHostModule() {
+  return mlir::cast<mlir::ModuleOp>(getBody().front().front());
+}
+
+llvm::iterator_range<mlir::Block::op_iterator<mlir::ModuleOp>>
+cir::OffloadContainerOp::getDeviceModules() {
+  mlir::Block &body = getBody().front();
+  auto begin = body.op_begin<mlir::ModuleOp>();
+  auto end = body.op_end<mlir::ModuleOp>();
+  if (begin != end)
+    ++begin;
+  return {begin, end};
+}
+
+static LogicalResult checkOffloadKind(mlir::ModuleOp module,
+                                      cir::OffloadKind expected) {
+  auto attr = module->getAttrOfType<cir::OffloadKindAttr>(
+      cir::CIRDialect::getOffloadKindAttrName());
+  if (!attr)
+    return module.emitOpError()
+           << "expects '" << cir::CIRDialect::getOffloadKindAttrName()
+           << "' offload kind attribute";
+  if (attr.getValue() != expected)
+    return module.emitOpError()
+           << "expects '" << cir::CIRDialect::getOffloadKindAttrName()
+           << "' value '" << cir::stringifyOffloadKind(expected) << "'";
+  return success();
+}
+
+LogicalResult cir::OffloadContainerOp::verify() {
+  mlir::Block &body = getBody().front();
+  if (body.empty())
+    return emitOpError() << "expects host module as the first nested op";
+
+  auto host = mlir::dyn_cast<mlir::ModuleOp>(body.front());
+  if (!host)
+    return emitOpError() << "expects host module as the first nested op";
+  if (failed(checkOffloadKind(host, cir::OffloadKind::Host)))
+    return failure();
+
+  unsigned numDevices = 0;
+  auto it = body.begin();
+  for (++it; it != body.end(); ++it) {
+    auto module = mlir::dyn_cast<mlir::ModuleOp>(*it);
+    if (!module)
+      return emitOpError() << "expects only nested builtin.module ops";
+    if (failed(checkOffloadKind(module, cir::OffloadKind::Device)))
+      return failure();
+    ++numDevices;
+  }
+
+  if (numDevices == 0)
+    return emitOpError() << "expects at least one device module";
+  return success();
+}
+
 
//===----------------------------------------------------------------------===//
 // FuncOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/IR/invalid-offload-container.cir 
b/clang/test/CIR/IR/invalid-offload-container.cir
new file mode 100644
index 0000000000000..219442f39674a
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-offload-container.cir
@@ -0,0 +1,54 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+module {
+  cir.offload.container {
+    builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} { // expected-error {{expects 'cir.offload.kind' 
value 'host'}}
+    }
+  }
+}
+
+// -----
+
+module {
+  cir.offload.container {
+    builtin.module @host_0 attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+    }
+    builtin.module @host_1 attributes {cir.offload.kind = 
#cir.offload_kind<host>} { // expected-error {{expects 'cir.offload.kind' value 
'device'}}
+    }
+  }
+}
+
+// -----
+
+module {
+  cir.offload.container {
+    builtin.module @host { // expected-error {{expects 'cir.offload.kind' 
offload kind attribute}}
+    }
+  }
+}
+
+// -----
+
+module {
+  cir.offload.container { // expected-error {{expects only nested 
builtin.module ops}}
+    builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+    }
+    cir.const #cir.int<0> : !cir.int<s, 32>
+  }
+}
+
+// -----
+
+module {
+  cir.offload.container { // expected-error {{expects at least one device 
module}}
+    builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+    }
+  }
+}
+
+// -----
+
+module {
+  cir.offload.container { // expected-error {{expects host module as the first 
nested op}}
+  }
+}
diff --git a/clang/test/CIR/IR/offload-container.cir 
b/clang/test/CIR/IR/offload-container.cir
new file mode 100644
index 0000000000000..b7b53aa973ce5
--- /dev/null
+++ b/clang/test/CIR/IR/offload-container.cir
@@ -0,0 +1,32 @@
+// RUN: cir-opt %s -split-input-file --verify-roundtrip | FileCheck %s
+
+module {
+  cir.offload.container {
+    builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+    }
+    builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+    }
+  }
+}
+
+// CHECK: cir.offload.container {
+// CHECK:   builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+// CHECK:   builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+
+// -----
+
+module {
+  cir.offload.container {
+    builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+    }
+    builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+    }
+    builtin.module @device_1 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+    }
+  }
+}
+
+// CHECK: cir.offload.container {
+// CHECK:   builtin.module @host attributes {cir.offload.kind = 
#cir.offload_kind<host>} {
+// CHECK:   builtin.module @device_0 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {
+// CHECK:   builtin.module @device_1 attributes {cir.offload.kind = 
#cir.offload_kind<device>} {

>From 999b3bb1a5f14302ee224dc073863035bec829be Mon Sep 17 00:00:00 2001
From: David Rivera <[email protected]>
Date: Mon, 29 Jun 2026 16:00:27 -0400
Subject: [PATCH 2/2] fix fmt

---
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 96501d5808168..1c1924099db7d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -17,8 +17,8 @@
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 
 #include "mlir/IR/Attributes.h"
-#include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/DialectImplementation.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/IR/Value.h"

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

Reply via email to