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
