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+
26822703struct 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+
28272933TileOp SwitchboxOp::getTileOp () {
28282934 return cast<TileElement>(this ->getOperation ()).getTileOp ();
28292935}
0 commit comments