Skip to content

Commit 605fe78

Browse files
committed
AIE: dynamic objectFifo lock lowering (SSA lock values)
Make aie.use_lock carry its lock count as an i32 SSA operand instead of an optional integer attribute, and emit runtime-computed lock counts in the dynamic (loop-preserving) objectFifo lowering via a per-(fifo,port) "held" counter. Passes that need a compile-time constant read it back through UseLockOp::getConstantValue(). The static loop-unrolling path (dynamic-objFifos=false) is left in place and still emits compile-time constant lock counts, so static and dynamic lock lowering coexist on this branch. Compiler code only; the corresponding test updates follow in the next commit.
1 parent 9d30275 commit 605fe78

11 files changed

Lines changed: 359 additions & 605 deletions

File tree

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,34 +1378,49 @@ def AIE_UseLockOp: AIE_Op<"use_lock", []> {
13781378
Then, the value of the lock is decremented by `value`.
13791379
- Release: In AIE1, set the lock to `value`.
13801380
In AIE2, increment the lock by `value`.
1381+
1382+
The lock value is always given as an `i32` SSA `value` operand. Passes that
1383+
need a compile-time constant obtain it via `getConstantValue()`, which
1384+
expects the operand to be defined by an `arith.constant` and fails
1385+
otherwise.
13811386
}];
13821387

13831388
let arguments = (
13841389
ins Index:$lock,
13851390
LockAction:$action,
1386-
OptionalAttr<AIEI32Attr>:$value,
1391+
I32:$value,
13871392
OptionalAttr<LockBlocking>:$blocking,
13881393
DefaultValuedOptionalAttr<BoolAttr, "true">:$acq_en
13891394
);
13901395

13911396
let assemblyFormat = [{
1392-
`(` $lock `,` $action (`,` $value^)? (`,` $blocking^)? `)` attr-dict
1397+
`(` $lock `,` custom<UseLockValue>($action, $value, $blocking) `)` attr-dict
13931398
}];
13941399

13951400
let hasVerifier = 1;
13961401
let builders = [
13971402
OpBuilder<(ins "mlir::Value":$lock,
13981403
"xilinx::AIE::LockAction":$action,
13991404
"int32_t":$value), [{
1400-
build($_builder, $_state, lock, action, $_builder.getI32IntegerAttr(value), nullptr);
1405+
auto constant = mlir::arith::ConstantOp::create(
1406+
$_builder, $_state.location, $_builder.getI32Type(),
1407+
$_builder.getI32IntegerAttr(value));
1408+
build($_builder, $_state, lock, action, constant, /*blocking=*/nullptr);
1409+
}]>,
1410+
OpBuilder<(ins "mlir::Value":$lock,
1411+
"xilinx::AIE::LockAction":$action,
1412+
"mlir::Value":$value), [{
1413+
build($_builder, $_state, lock, action, value, /*blocking=*/nullptr);
14011414
}]>
14021415
];
14031416

14041417
let extraClassDeclaration = [{
14051418
bool acquire() { return (getAction() == LockAction::Acquire); }
14061419
bool acquireGE() { return (getAction() == LockAction::AcquireGreaterEqual); }
14071420
bool release() { return (getAction() == LockAction::Release); }
1408-
int getLockValue() { return getValue().value_or(1); }
1421+
// Returns the compile-time constant lock value when `value` is defined by
1422+
// an arith.constant; otherwise emits an op error and returns failure.
1423+
mlir::FailureOr<int32_t> getConstantValue();
14091424
int getTimeout() {
14101425
// LockBlocking is an EnumAttr.
14111426
if (auto val = getBlocking())

lib/Dialect/AIE/IR/AIEDialect.cpp

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
#include "aie/Dialect/AIE/IR/AIEDialect.h"
1010

11+
#include "mlir/Dialect/Arith/IR/Arith.h"
1112
#include "mlir/Dialect/Func/IR/FuncOps.h"
1213
#include "mlir/Dialect/MemRef/IR/MemRef.h"
1314
#include "mlir/IR/DialectImplementation.h"
15+
#include "mlir/IR/Matchers.h"
1416
#include "mlir/IR/OpDefinition.h"
1517
#include "mlir/IR/SymbolTable.h"
1618
#include "mlir/Interfaces/FoldInterfaces.h"
@@ -73,7 +75,15 @@ struct AIEDialectFoldInterface : DialectFoldInterface {
7375
// them with a core's identical constants, which would leave the core
7476
// referencing a device-level value that is erased when the core is
7577
// outlined.
76-
return isa<CoreOp, RuntimeSequenceOp>(region->getParentOp());
78+
// - aie.mem/aie.memtile_dma/aie.shim_dma bodies carry constant lock values
79+
// (aie.use_lock operands). If these hoisted to the device, CSE would
80+
// merge them with the identical constant kept local to a core, and since
81+
// the device-level constant dominates the core, the core's use would be
82+
// rewritten to reference it -- reintroducing the cross-region reference
83+
// that breaks core outlining. Keeping them local avoids the dominating
84+
// device-level duplicate entirely.
85+
return isa<CoreOp, RuntimeSequenceOp, MemOp, MemTileDMAOp, ShimDMAOp>(
86+
region->getParentOp());
7787
}
7888
};
7989

@@ -2679,6 +2689,17 @@ LogicalResult LockOp::verify() {
26792689
return success();
26802690
}
26812691

2692+
// Centralized lookup for the compile-time constant lock value: returns the
2693+
// integer if `op`'s value operand is defined by an arith.constant, otherwise
2694+
// std::nullopt. This is the single place that knows how a static lock value is
2695+
// recovered from the SSA operand.
2696+
static std::optional<int32_t> getConstantLockValue(UseLockOp op) {
2697+
if (auto constant = op.getValue().getDefiningOp<arith::ConstantOp>())
2698+
if (auto intAttr = llvm::dyn_cast<IntegerAttr>(constant.getValue()))
2699+
return (int32_t)intAttr.getInt();
2700+
return std::nullopt;
2701+
}
2702+
26822703
struct UsesOneLockInDMABlock {
26832704
static LogicalResult verifyTrait(Operation *op) {
26842705
auto *block = op->getBlock();
@@ -2700,16 +2721,21 @@ struct AcquireReleaseOneStateInDMABlock {
27002721
auto *block = op->getBlock();
27012722
int acqValue = -1, relValue = -1;
27022723
for (auto op : block->getOps<UseLockOp>()) {
2724+
// Non-constant lock values cannot be compared here; the passes that
2725+
// require a constant enforce that separately via getConstantValue().
2726+
auto value = getConstantLockValue(op);
2727+
if (!value)
2728+
continue;
27032729
if (op.acquire() || op.acquireGE()) {
2704-
if (acqValue != -1 && acqValue != op.getLockValue()) {
2730+
if (acqValue != -1 && acqValue != *value) {
27052731
return failure();
27062732
}
2707-
acqValue = op.getLockValue();
2733+
acqValue = *value;
27082734
} else if (op.release()) {
2709-
if (relValue != -1 && relValue != op.getLockValue()) {
2735+
if (relValue != -1 && relValue != *value) {
27102736
return failure();
27112737
}
2712-
relValue = op.getLockValue();
2738+
relValue = *value;
27132739
}
27142740
}
27152741
return success();
@@ -2737,6 +2763,15 @@ LogicalResult UseLockOp::verify() {
27372763
return (*this)->emitOpError(
27382764
"AcquireGreaterEqual is not supported in AIE1.");
27392765

2766+
// Locks used inside a DMA/BD block are configured via MMIO and therefore
2767+
// require a compile-time constant value (an arith.constant operand).
2768+
if (HasSomeParent<MemOp, MemTileDMAOp, ShimDMAOp>::verifyTrait(*this)
2769+
.succeeded() &&
2770+
!getConstantLockValue(*this))
2771+
return (*this)->emitOpError(
2772+
"lock value in a DMA/BD block must be a compile-time constant "
2773+
"(defined by an arith.constant).");
2774+
27402775
// Otherwise, AIE.useLock should be inside MemOp, MemTileDMAOp, or
27412776
// ShimDMAOp,
27422777
if (HasSomeParent<MemOp, MemTileDMAOp, ShimDMAOp>::verifyTrait(*this)
@@ -2821,9 +2856,80 @@ static void printTraceRegValue(OpAsmPrinter &printer, Operation *op,
28212856
xilinx::AIE::printTraceEventEnum(printer, value);
28222857
}
28232858

2859+
// Helper to parse a LockBlocking enum keyword.
2860+
static ParseResult parseLockBlockingKeyword(OpAsmParser &parser,
2861+
xilinx::AIE::LockBlockingAttr &blocking) {
2862+
StringRef keyword;
2863+
if (parser.parseKeyword(&keyword))
2864+
return failure();
2865+
auto symbol = xilinx::AIE::symbolizeLockBlocking(keyword);
2866+
if (!symbol)
2867+
return parser.emitError(parser.getCurrentLocation())
2868+
<< "invalid lock blocking value: " << keyword;
2869+
blocking =
2870+
xilinx::AIE::LockBlockingAttr::get(parser.getContext(), *symbol);
2871+
return success();
2872+
}
2873+
2874+
// Custom parser for the value slot of aie.use_lock. The lock value is a
2875+
// required `i32` SSA operand, optionally followed by a `blocking` keyword. The
2876+
// action keyword is parsed here as well so that the printed form has no stray
2877+
// space before the comma.
2878+
static ParseResult
2879+
parseUseLockValue(OpAsmParser &parser, xilinx::AIE::LockActionAttr &action,
2880+
OpAsmParser::UnresolvedOperand &value,
2881+
xilinx::AIE::LockBlockingAttr &blocking) {
2882+
// The action is accepted either as a bare keyword (`Acquire`) or, for
2883+
// backward compatibility with older textual IR, as a quoted string
2884+
// (`"Acquire"`).
2885+
StringRef actionKeyword;
2886+
std::string actionString;
2887+
if (succeeded(parser.parseOptionalKeyword(&actionKeyword))) {
2888+
// Parsed a bare keyword.
2889+
} else if (succeeded(parser.parseOptionalString(&actionString))) {
2890+
actionKeyword = actionString;
2891+
} else {
2892+
return parser.emitError(parser.getCurrentLocation())
2893+
<< "expected lock action keyword";
2894+
}
2895+
auto actionSymbol = xilinx::AIE::symbolizeLockAction(actionKeyword);
2896+
if (!actionSymbol)
2897+
return parser.emitError(parser.getCurrentLocation())
2898+
<< "invalid lock action: " << actionKeyword;
2899+
action = xilinx::AIE::LockActionAttr::get(parser.getContext(), *actionSymbol);
2900+
2901+
// The lock value SSA operand.
2902+
if (parser.parseComma() || parser.parseOperand(value))
2903+
return failure();
2904+
2905+
// Optionally parse a trailing blocking keyword.
2906+
if (!parser.parseOptionalComma())
2907+
return parseLockBlockingKeyword(parser, blocking);
2908+
return success();
2909+
}
2910+
2911+
// Custom printer for the value slot of aie.use_lock.
2912+
static void printUseLockValue(OpAsmPrinter &printer, Operation *op,
2913+
xilinx::AIE::LockActionAttr action, Value value,
2914+
xilinx::AIE::LockBlockingAttr blocking) {
2915+
printer << xilinx::AIE::stringifyLockAction(action.getValue());
2916+
printer << ", ";
2917+
printer.printOperand(value);
2918+
if (blocking)
2919+
printer << ", "
2920+
<< xilinx::AIE::stringifyLockBlocking(blocking.getValue());
2921+
}
2922+
28242923
#define GET_OP_CLASSES
28252924
#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
28262925

2926+
FailureOr<int32_t> UseLockOp::getConstantValue() {
2927+
if (auto value = getConstantLockValue(*this))
2928+
return *value;
2929+
return emitOpError("expected the lock value to be a compile-time constant "
2930+
"(defined by an arith.constant).");
2931+
}
2932+
28272933
TileOp SwitchboxOp::getTileOp() {
28282934
return cast<TileElement>(this->getOperation()).getTileOp();
28292935
}

lib/Dialect/AIE/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_mlir_dialect_library(AIE
2121

2222
LINK_LIBS PUBLIC
2323
MLIRAIEUtil
24+
MLIRArithDialect
2425
MLIRIR
2526
MLIRSupport
2627
)

lib/Dialect/AIE/Transforms/AIECoreToStandard.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -443,19 +443,19 @@ struct AIEUseLockToStdLowering : OpConversionPattern<UseLockOp> {
443443
return useLock.emitOpError("Could not find the intrinsic function!");
444444

445445
SmallVector<Value, 2> args;
446-
auto lockValue = useLock.getLockValue();
446+
auto i32Ty = IntegerType::get(rewriter.getContext(), 32);
447+
args.push_back(arith::IndexCastOp::create(rewriter, useLock.getLoc(),
448+
i32Ty, useLock.getLock()));
447449

448-
// AIE2 acquire greater equal is encoded as a negative value.
450+
Value value = adaptor.getValue();
451+
// AIE2 acquire-greater-equal is encoded as a negative value, so negate
452+
// it at runtime.
449453
if (useLock.acquireGE()) {
450-
lockValue = -lockValue;
454+
Value zero = arith::ConstantOp::create(
455+
rewriter, useLock.getLoc(), i32Ty, rewriter.getI32IntegerAttr(0));
456+
value = arith::SubIOp::create(rewriter, useLock.getLoc(), zero, value);
451457
}
452-
args.push_back(arith::IndexCastOp::create(
453-
rewriter, useLock.getLoc(),
454-
IntegerType::get(rewriter.getContext(), 32), useLock.getLock()));
455-
args.push_back(
456-
arith::ConstantOp::create(rewriter, useLock.getLoc(),
457-
IntegerType::get(rewriter.getContext(), 32),
458-
rewriter.getI32IntegerAttr(lockValue)));
458+
args.push_back(value);
459459

460460
func::CallOp::create(rewriter, useLock.getLoc(), useLockFunc, args);
461461
}

0 commit comments

Comments
 (0)