[Mlir-commits] [mlir] [mlir][OpenACC] Preserve explicit device numbers in wait lowering (PR #222604)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Sep 10 03:57:12 PDT 2026
https://github.com/leovancekang created https://github.com/llvm/llvm-project/pull/222604
WaitOpLowering currently ignores wait_devnum and always passes zero as the device number to __tgt_acc_wait.
Forward the converted operand when present, adapting it to the runtime's i32 parameter. Zero-extend narrow unsigned integers, sign-extend narrow signed or signless integers, and truncate wider operands. Determine signedness from the original operand type, since LLVM type conversion makes integer types signless.
Keep the default device number unchanged when wait_devnum is absent.
Add regression coverage for explicit device numbers, integer width and signedness conversions, index operands, and waits combining if and async.
>From 9a389ae70dcc1e8aacc72236fadb8f4c63960a05 Mon Sep 17 00:00:00 2001
From: leovancekang <leovancekang at outlook.com>
Date: Tue, 8 Sep 2026 17:26:24 +0800
Subject: [PATCH] [mlir][OpenACC] Preserve explicit device numbers in wait
lowering
WaitOpLowering currently ignores wait_devnum and always passes zero as
the device number to __tgt_acc_wait.
Forward the converted operand when present, adapting it to the runtime's
i32 parameter. Zero-extend narrow unsigned integers, sign-extend narrow
signed or signless integers, and truncate wider operands. Determine
signedness from the original operand type, since LLVM type conversion
makes integer types signless.
Keep the default device number unchanged when wait_devnum is absent.
Add regression coverage for explicit device numbers, integer width and
signedness conversions, index operands, and waits combining if and async.
---
.../ACCExecutableDirectivePatterns.cpp | 34 +++--
mlir/test/Conversion/OpenACCToLLVM/wait.mlir | 129 +++++++++++++++++-
2 files changed, 153 insertions(+), 10 deletions(-)
diff --git a/mlir/lib/Conversion/OpenACCToLLVM/ACCExecutableDirectivePatterns.cpp b/mlir/lib/Conversion/OpenACCToLLVM/ACCExecutableDirectivePatterns.cpp
index ae4090d00bd26..a928a558cf8b8 100644
--- a/mlir/lib/Conversion/OpenACCToLLVM/ACCExecutableDirectivePatterns.cpp
+++ b/mlir/lib/Conversion/OpenACCToLLVM/ACCExecutableDirectivePatterns.cpp
@@ -29,17 +29,25 @@ using namespace mlir;
using namespace mlir::acc;
namespace {
-static Value castToI64(Location loc, Value value,
- ConversionPatternRewriter &rewriter) {
- Type i64Ty = IntegerType::get(rewriter.getContext(), 64);
+static Value castToInt(Location loc, Value value, IntegerType targetType,
+ bool isUnsigned, ConversionPatternRewriter &rewriter) {
unsigned bitwidth = value.getType().getIntOrFloatBitWidth();
- if (bitwidth > 64)
- return arith::TruncIOp::create(rewriter, loc, i64Ty, value);
- if (bitwidth < 64)
- return arith::ExtSIOp::create(rewriter, loc, i64Ty, value);
+ if (bitwidth > targetType.getWidth())
+ return arith::TruncIOp::create(rewriter, loc, targetType, value);
+ if (bitwidth < targetType.getWidth()) {
+ if (isUnsigned)
+ return arith::ExtUIOp::create(rewriter, loc, targetType, value);
+ return arith::ExtSIOp::create(rewriter, loc, targetType, value);
+ }
return value;
}
+static Value castToI64(Location loc, Value value,
+ ConversionPatternRewriter &rewriter) {
+ return castToInt(loc, value, rewriter.getI64Type(), /*isUnsigned=*/false,
+ rewriter);
+}
+
static Value getAsyncQueue(WaitOp op, ConversionPatternRewriter &rewriter,
const ACCRuntimeCallConfig &config) {
Location loc = op->getLoc();
@@ -99,7 +107,7 @@ struct WaitOpLowering : public ACCExecutableDirectivePattern<WaitOp> {
using ACCExecutableDirectivePattern<WaitOp>::ACCExecutableDirectivePattern;
LogicalResult
- matchAndRewrite(WaitOp op, WaitOp::Adaptor,
+ matchAndRewrite(WaitOp op, WaitOp::Adaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Location loc = op->getLoc();
ModuleOp module = op->getParentOfType<ModuleOp>();
@@ -138,7 +146,15 @@ struct WaitOpLowering : public ACCExecutableDirectivePattern<WaitOp> {
Value deviceType = LLVM::ConstantOp::create(
rewriter, loc, i64Ty,
config.getDeviceTypeRuntimeValue(DeviceType::None));
- Value deviceNum = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
+ Value deviceNum = adaptor.getWaitDevnum();
+ if (deviceNum) {
+ // The converted operand is signless, so inspect the original type.
+ bool isUnsigned = op.getWaitDevnum().getType().isUnsignedInteger();
+ deviceNum = castToInt(loc, deviceNum, rewriter.getI32Type(), isUnsigned,
+ rewriter);
+ } else {
+ deviceNum = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
+ }
return createRuntimeCall(
loc, rewriter, module, RuntimeFunction::ACCRTL_tgt_acc_wait, config,
diff --git a/mlir/test/Conversion/OpenACCToLLVM/wait.mlir b/mlir/test/Conversion/OpenACCToLLVM/wait.mlir
index 87aae3fb27bb7..74da7f70e19ac 100644
--- a/mlir/test/Conversion/OpenACCToLLVM/wait.mlir
+++ b/mlir/test/Conversion/OpenACCToLLVM/wait.mlir
@@ -5,7 +5,8 @@
// CHECK: llvm.mlir.constant(-1 : i64)
// CHECK: llvm.mlir.constant(0 : i32)
// CHECK: llvm.mlir.zero : !llvm.ptr
-// CHECK: llvm.call @__tgt_acc_wait
+// CHECK: %[[DEFAULT_DEVICE:.*]] = llvm.mlir.constant(0 : i32)
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEFAULT_DEVICE]],
module {
func.func @test_wait_empty() {
@@ -16,6 +17,132 @@ module {
// -----
+// An explicit device number is passed as the fourth runtime argument.
+// CHECK-LABEL: llvm.func @test_wait_devnum_i32
+// CHECK-SAME: (%[[DEVICE:.*]]: i32, %[[QUEUE:.*]]: i64)
+// CHECK: llvm.store %[[QUEUE]], %{{.*}} : i64, !llvm.ptr
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE]],
+
+module {
+ func.func @test_wait_devnum_i32(%device: i32, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : i32)
+ return
+ }
+}
+
+// -----
+
+// Narrow signless device numbers are sign-extended to the runtime's i32 parameter.
+// CHECK-LABEL: llvm.func @test_wait_devnum_i16
+// CHECK-SAME: (%[[DEVICE:.*]]: i16,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.sext %[[DEVICE]] : i16 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_i16(%device: i16, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : i16)
+ return
+ }
+}
+
+// -----
+
+// Unsigned device numbers must be zero-extended, even though the converted
+// operands are signless. In particular, ui1's value 1 must not become -1.
+// CHECK-LABEL: llvm.func @test_wait_devnum_ui1
+// CHECK-SAME: (%[[DEVICE:.*]]: i1,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.zext %[[DEVICE]] : i1 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_ui1(%device: ui1, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : ui1)
+ return
+ }
+}
+
+// -----
+
+// CHECK-LABEL: llvm.func @test_wait_devnum_ui8
+// CHECK-SAME: (%[[DEVICE:.*]]: i8,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.zext %[[DEVICE]] : i8 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_ui8(%device: ui8, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : ui8)
+ return
+ }
+}
+
+// -----
+
+// Explicitly signed device numbers still use sign extension.
+// CHECK-LABEL: llvm.func @test_wait_devnum_si8
+// CHECK-SAME: (%[[DEVICE:.*]]: i8,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.sext %[[DEVICE]] : i8 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_si8(%device: si8, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : si8)
+ return
+ }
+}
+
+// -----
+
+// Wide device numbers are truncated to the runtime's i32 parameter.
+// CHECK-LABEL: llvm.func @test_wait_devnum_i64
+// CHECK-SAME: (%[[DEVICE:.*]]: i64,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.trunc %[[DEVICE]] : i64 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_i64(%device: i64, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : i64)
+ return
+ }
+}
+
+// -----
+
+// The converted index operand is also narrowed to i32.
+// CHECK-LABEL: llvm.func @test_wait_devnum_index
+// CHECK-SAME: (%[[DEVICE:.*]]: i64,
+// CHECK: %[[DEVICE_I32:.*]] = llvm.trunc %[[DEVICE]] : i64 to i32
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE_I32]],
+
+module {
+ func.func @test_wait_devnum_index(%device: index, %queue: i64) {
+ acc.wait(%queue : i64) wait_devnum(%device : index)
+ return
+ }
+}
+
+// -----
+
+// The explicit device number and async queue are preserved in the guarded call.
+// CHECK-LABEL: llvm.func @test_wait_devnum_if_async
+// CHECK-SAME: (%[[COND:.*]]: i1, %[[DEVICE:.*]]: i32, %[[QUEUE:.*]]: i64, %[[ASYNC:.*]]: i32)
+// CHECK: llvm.cond_br %[[COND]], ^[[THEN:bb[0-9]+]], ^[[CONT:bb[0-9]+]]
+// CHECK: ^[[THEN]]:
+// CHECK: %[[ASYNC_I64:.*]] = llvm.sext %[[ASYNC]] : i32 to i64
+// CHECK: llvm.store %[[QUEUE]], %{{.*}} : i64, !llvm.ptr
+// CHECK: llvm.call @__tgt_acc_wait(%{{[^,]+}}, %{{[^,]+}}, %{{[^,]+}}, %[[DEVICE]], %{{[^,]+}}, %{{[^,]+}}, %[[ASYNC_I64]])
+// CHECK: llvm.br ^[[CONT]]
+// CHECK: ^[[CONT]]:
+
+module {
+ func.func @test_wait_devnum_if_async(%cond: i1, %device: i32,
+ %queue: i64, %async: i32) {
+ acc.wait(%queue : i64) async(%async : i32) wait_devnum(%device : i32) if(%cond)
+ return
+ }
+}
+
+// -----
+
// Wait operands: waitNum matches the list length, values are stored into an
// alloca'd wait list, and the async queue remains sync (-1).
// CHECK-LABEL: llvm.func @test_wait_operands
More information about the Mlir-commits
mailing list