Skip to content

Commit 614e8d8

Browse files
authored
Fix overflow in number of bytes for long DMA BD lengths (#3034)
1 parent 6032217 commit 614e8d8

9 files changed

Lines changed: 129 additions & 11 deletions

File tree

cmake/modulesXilinx

include/aie/Dialect/AIE/IR/AIEOps.td

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,15 +1025,20 @@ def AIE_DMABDOp: AIE_Op<"dma_bd", []> {
10251025
auto bufferType = llvm::cast<mlir::BaseMemRefType>(getBuffer().getType());
10261026
return dataLayout.getTypeSize(bufferType.getElementType());
10271027
}
1028-
uint32_t getLenInBytes() {
1029-
if (std::optional<uint32_t> len = getLen(); len.has_value())
1030-
return len.value() * getBufferElementTypeWidthInBytes();
1028+
uint64_t getLenInBytes() {
1029+
if (std::optional<int32_t> len = getLen(); len.has_value())
1030+
return static_cast<uint64_t>(static_cast<uint32_t>(len.value())) *
1031+
static_cast<uint64_t>(getBufferElementTypeWidthInBytes());
10311032
auto memrefType = llvm::dyn_cast<mlir::MemRefType>(getBuffer().getType());
10321033
if (!memrefType)
10331034
return 0; // Unranked memref without explicit len
1034-
return memrefType.getNumElements() * getBufferElementTypeWidthInBytes();
1035+
return static_cast<uint64_t>(memrefType.getNumElements()) *
1036+
static_cast<uint64_t>(getBufferElementTypeWidthInBytes());
1037+
}
1038+
int64_t getOffsetInBytes() {
1039+
return static_cast<int64_t>(getOffset()) *
1040+
static_cast<int64_t>(getBufferElementTypeWidthInBytes());
10351041
}
1036-
int32_t getOffsetInBytes() { return getOffset() * getBufferElementTypeWidthInBytes(); }
10371042
}];
10381043

10391044
let hasVerifier = 1;

include/aie/Targets/AIETargetShared.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::string packetStr(int id, int type);
3434
void generateXAieDmaSetMultiDimAddr(llvm::raw_ostream &output, int ndims,
3535
llvm::ArrayRef<BDDimLayoutAttr> dims,
3636
int col, int row, int bdNum, int baseAddrA,
37-
int offsetA, int lenA,
37+
int64_t offsetA, uint32_t lenA,
3838
int elementWidthInBytes,
3939
const char *errorRet);
4040

lib/Dialect/AIE/IR/AIEDialect.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2294,7 +2294,7 @@ LogicalResult DMABDOp::verify() {
22942294
if (!isUnrankedMemRef &&
22952295
(parentTile.isMemTile() || parentTile.isCoreTile())) {
22962296
if (auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
2297-
int offsetInBytes = *baseAddr + getOffsetInBytes();
2297+
int64_t offsetInBytes = *baseAddr + getOffsetInBytes();
22982298
if (offsetInBytes % 4)
22992299
return emitOpError("bd address must be 4 byte (32b) aligned; got "
23002300
"base+offset: ")

lib/Targets/AIETargetShared.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static std::string tileDMATensorStr(int col, int row, int bdNum) {
8686
void generateXAieDmaSetMultiDimAddr(raw_ostream &output, int ndims,
8787
ArrayRef<BDDimLayoutAttr> dims, int col,
8888
int row, int bdNum, int baseAddrA,
89-
int offsetA, int lenA,
89+
int64_t offsetA, uint32_t lenA,
9090
int elementWidthInBytes,
9191
const char *errorRetval) {
9292
// libxaie requires stride in multiples of 32b

lib/Targets/AIETargetXAIEV2.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "llvm/ADT/StringExtras.h"
2323
#include "llvm/IR/Module.h"
2424

25+
#include <limits>
26+
2527
using namespace mlir;
2628
using namespace xilinx;
2729
using namespace xilinx::AIE;
@@ -103,8 +105,8 @@ static mlir::LogicalResult generateDMAConfig(OpType memOp, raw_ostream &output,
103105
int packetType = 0;
104106
int packetID = 0;
105107
bool foundBd = false;
106-
int lenA = 0;
107-
int offsetA = 0;
108+
uint64_t lenA = 0;
109+
int64_t offsetA = 0;
108110
int BaseAddrA = 0;
109111
int elementWidthInBytes = 0;
110112
int ndims = 0;
@@ -142,6 +144,12 @@ static mlir::LogicalResult generateDMAConfig(OpType memOp, raw_ostream &output,
142144
return memOp.emitOpError("DMA contains at least one multi-dimensional "
143145
"buffer descriptor. This is currently only "
144146
"supported for AIE-ML and later devices.");
147+
// Ensure BD length in bytes fits in the u32 `Len` argument of the libxaie
148+
// DMA APIs (XAie_DmaSetAddrLen / XAie_DmaSetMultiDimAddr).
149+
if (foundBd && lenA > std::numeric_limits<uint32_t>::max()) {
150+
return memOp.emitOpError("buffer descriptor length in bytes (")
151+
<< lenA << ") does not fit in 32 bits";
152+
}
145153

146154
int acqValue = 0, relValue = 0;
147155
bool hasAcq = false, hasRel = false;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===- test_error_dma_len_overflow.mlir ------------------------*- MLIR -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
// (c) Copyright 2024 Advanced Micro Devices, Inc. or its affiliates
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
// RUN: aie-translate --aie-generate-xaie --verify-diagnostics %s
12+
13+
// `XAie_DmaSetAddrLen` / `XAie_DmaSetMultiDimAddr` take the byte length as a
14+
// `u32`, so any length that does not fit in 32 bits would be silently
15+
// truncated by the emitted C call. Verify the translator rejects it with an
16+
// explicit op error instead.
17+
//
18+
// memref<536870912xi64>: 536870912 * 8 = 4294967296 bytes = 0x100000000 which
19+
// does not fit in 32 bits.
20+
21+
module {
22+
aie.device(npu1) {
23+
%tile_0_0 = aie.tile(0, 0)
24+
// expected-error @+1 {{does not fit in 32 bits}}
25+
aie.shim_dma(%tile_0_0) {
26+
%srcDma = aie.dma_start(MM2S, 0, ^bd0, ^end)
27+
^bd0:
28+
%buf = aie.external_buffer { sym_name = "myBuffer_0_0_0" } : memref<536870912xi64>
29+
aie.dma_bd(%buf : memref<536870912xi64>, 0, 536870912)
30+
aie.next_bd ^end
31+
^end:
32+
aie.end
33+
}
34+
}
35+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// (c) Copyright 2024 AMD Inc.
7+
8+
// RUN: aie-opt --verify-diagnostics --aie-dma-tasks-to-npu %s
9+
10+
// This test verifies that when a large-length dma_bd has mismatched dimensions,
11+
// the error message correctly reports the byte count without integer overflow.
12+
//
13+
// memref<536870912xi64>: 536870912 elements * 8 bytes = 4294967296 bytes.
14+
// BD length in 32-bit words = 4294967296 / 4 = 1073741824.
15+
// The lowest three dims product: sizes[0] = 2*64/32 = 4 words, sizes[1] = sizes[2] = 1.
16+
// Total = 4 words = 16 bytes, which does not match 4294967296 bytes.
17+
// The error message must report "4294967296 bytes" (not a truncated 32-bit value
18+
// like "0 bytes" that the overflow bug would produce) and "16 bytes" for the dims.
19+
20+
module {
21+
aie.device(npu1) {
22+
%tile_0_0 = aie.tile(0, 0)
23+
24+
aie.runtime_sequence(%arg0: memref<536870912xi64>) {
25+
%t1 = aiex.dma_configure_task(%tile_0_0, MM2S, 0) {
26+
// expected-error@+2 {{Buffer descriptor length does not match length of transfer expressed by lowest three dimensions of data layout transformation strides/wraps. BD length is 4294967296 bytes. Lowest three dimensions of data layout transformation would result in transfer of 16 bytes.}}
27+
// expected-note@+1 {{}}
28+
aie.dma_bd(%arg0 : memref<536870912xi64>, 0, 536870912,
29+
[<size=1, stride=1>, <size=1, stride=1>, <size=2, stride=1>]) {bd_id = 0 : i32}
30+
aie.end
31+
}
32+
}
33+
}
34+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// (c) Copyright 2024 AMD Inc.
7+
8+
// RUN: aie-opt --aie-dma-tasks-to-npu %s | FileCheck %s
9+
10+
// This test verifies that a dma_bd with a transfer length that would overflow a
11+
// 32-bit integer when converted to bytes is handled correctly. Without the fix,
12+
// `len * element_size_bytes` would overflow to 0 or a wrong value, causing a
13+
// false "Buffer descriptor length does not match" error.
14+
//
15+
// memref<536870912xi64>: 536870912 elements * 8 bytes = 4294967296 bytes.
16+
// In 32-bit words: 4294967296 / 4 = 1073741824 (= 0x40000000).
17+
// Without overflow fix, the byte count truncates to 0, so len_addr_granularity
18+
// would be 0 instead of 1073741824, triggering a spurious validation error.
19+
20+
// CHECK-LABEL: aie.runtime_sequence
21+
// CHECK: aiex.npu.writebd
22+
// CHECK-SAME: buffer_length = 1073741824 : i32
23+
24+
module {
25+
aie.device(npu1) {
26+
%tile_0_0 = aie.tile(0, 0)
27+
28+
aie.runtime_sequence(%arg0: memref<536870912xi64>) {
29+
%t1 = aiex.dma_configure_task(%tile_0_0, MM2S, 0) {
30+
aie.dma_bd(%arg0 : memref<536870912xi64>, 0, 536870912,
31+
[<size=1, stride=1>, <size=1, stride=1>, <size=536870912, stride=1>]) {bd_id = 0 : i32}
32+
aie.end
33+
}
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)