12#include "mlir/IR/OpImplementation.h"
13#include "mlir/IR/SymbolTable.h"
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/ADT/StringRef.h"
25std::string TraceEventAttr::getEventName()
const {
26 if (
auto strAttr = llvm::dyn_cast<StringAttr>(getValue())) {
28 StringRef strValue = strAttr.getValue();
29 size_t pos = strValue.find(
"::");
30 if (pos != StringRef::npos) {
31 return strValue.substr(pos + 2).str();
33 return strAttr.getValue().str();
40bool TraceEventAttr::isStringAttr()
const {
41 return llvm::isa<StringAttr>(getValue());
44std::optional<int64_t> TraceEventAttr::getEnumValue()
const {
45 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(getValue())) {
46 return intAttr.getInt();
56static bool isValidEventForTile(TileLike tile, Attribute eventAttr,
59 bool isShimTile = tile.isShimTile();
60 bool isMemTile = tile.isMemTile();
61 bool isCoreTile = tile.isCoreTile();
65 Attribute innerAttr = eventAttr;
66 if (
auto traceEvent = llvm::dyn_cast<TraceEventAttr>(eventAttr)) {
67 innerAttr = traceEvent.getValue();
78 if (
auto strAttr = llvm::dyn_cast<StringAttr>(innerAttr)) {
79 StringRef val = strAttr.getValue();
80 size_t sep = val.find(
"::");
81 if (sep == StringRef::npos)
84 StringRef prefix = val.substr(0, sep);
90 return prefix ==
"CoreEventAIE" || prefix ==
"MemEventAIE";
92 return prefix ==
"CoreEventAIE2" || prefix ==
"MemEventAIE2";
94 return prefix ==
"CoreEventAIE2P" || prefix ==
"MemEventAIE2P";
102 return prefix ==
"MemTileEventAIE2";
104 return prefix ==
"MemTileEventAIE2P";
112 return prefix ==
"ShimTileEventAIE";
114 return prefix ==
"ShimTileEventAIE2";
116 return prefix ==
"ShimTileEventAIE2P";
131void TraceOp::getAsmResultNames(
132 function_ref<
void(Value, StringRef)> setNameFn) {
136LogicalResult TraceOp::verify() {
139 for (
auto &op : getBody().getOps()) {
140 if (isa<TraceEventOp>(op)) {
146 if (eventCount > 8) {
147 return emitOpError(
"trace unit supports maximum 8 events, got ")
152 if (!llvm::dyn_cast<TileLike>(getTile().getDefiningOp())) {
153 return emitOpError(
"tile operand must be a TileOp or LogicalTileOp");
157 llvm::DenseSet<uint32_t> comboSlots;
158 llvm::DenseSet<uint32_t> edgeSlots;
160 for (
auto &op : getBody().getOps()) {
161 if (
auto comboOp = dyn_cast<TraceComboEventOp>(op)) {
162 uint32_t slot = comboOp.getSlot();
163 if (!comboSlots.insert(slot).second) {
164 return comboOp.emitOpError(
"combo event slot ")
165 << slot <<
" already in use in this trace";
167 }
else if (
auto edgeOp = dyn_cast<TraceEdgeEventOp>(op)) {
168 uint32_t slot = edgeOp.getSlot();
169 if (!edgeSlots.insert(slot).second) {
170 return edgeOp.emitOpError(
"edge detection slot ")
171 << slot <<
" already in use in this trace";
185 MLIRContext *ctx = parser.getContext();
188 std::string strValue;
189 if (succeeded(parser.parseOptionalString(&strValue))) {
190 result = StringAttr::get(ctx, strValue);
195 StringRef enumTypeName;
196 llvm::SMLoc loc = parser.getCurrentLocation();
198 if (failed(parser.parseKeyword(&enumTypeName))) {
199 return parser.emitError(loc,
"expected string or enum event");
202 if (failed(parser.parseColon()) || failed(parser.parseColon())) {
203 return parser.emitError(loc,
"expected '::' after enum type name");
207 if (failed(parser.parseKeyword(&caseName))) {
208 return parser.emitError(parser.getCurrentLocation(),
209 "expected enum case name");
217 struct EnumValidator {
219 std::function<bool(StringRef)> symbolizer;
223 static const EnumValidator validators[] = {
226 [](StringRef s) {
return symbolizeCoreEventAIE2(s).has_value(); }},
229 auto result = symbolizeMemEventAIE2(s);
230 return result.has_value();
234 auto result = symbolizeMemTileEventAIE2(s);
235 return result.has_value();
237 {
"ShimTileEventAIE2",
239 auto result = symbolizeShimTileEventAIE2(s);
240 return result.has_value();
245 auto result = symbolizeCoreEventAIE(s);
246 return result.has_value();
250 auto result = symbolizeMemEventAIE(s);
251 return result.has_value();
255 auto result = symbolizeShimTileEventAIE(s);
256 return result.has_value();
261 auto result = symbolizeCoreEventAIE2P(s);
262 return result.has_value();
266 auto result = symbolizeMemEventAIE2P(s);
267 return result.has_value();
269 {
"MemTileEventAIE2P",
271 auto result = symbolizeMemTileEventAIE2P(s);
272 return result.has_value();
274 {
"ShimTileEventAIE2P",
276 auto result = symbolizeShimTileEventAIE2P(s);
277 return result.has_value();
282 for (
const auto &validator : validators) {
283 if (enumTypeName != validator.name)
285 if (!validator.symbolizer(caseName))
286 return parser.emitError(loc,
"unknown ")
287 << enumTypeName <<
" value: " << caseName;
288 result = StringAttr::get(ctx, enumTypeName +
"::" + caseName);
292 return parser.emitError(loc,
"unknown event enum type: ") << enumTypeName;
296 if (
auto traceAttr = llvm::dyn_cast<TraceEventAttr>(attr)) {
300 if (
auto strAttr = llvm::dyn_cast<StringAttr>(attr)) {
302 if (strAttr.getValue().contains(
"::")) {
303 printer << strAttr.getValue();
306 printer <<
"\"" << strAttr.getValue() <<
"\"";
308 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
309 printer << intAttr.getInt();
318LogicalResult TraceEventOp::verify() {
320 auto eventAttr = getEvent();
323 std::string eventName = eventAttr.getEventName();
324 if (eventName.empty()) {
325 return emitOpError(
"event name cannot be empty");
329 auto trace = (*this)->getParentOfType<TraceOp>();
331 return emitOpError(
"must be nested in aie.trace");
334 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
336 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
339 auto device = trace->getParentOfType<DeviceOp>();
340 const auto &targetModel = device.getTargetModel();
342 if (!isValidEventForTile(tileLike, eventAttr.getValue(), targetModel)) {
343 std::string tileTypeStr;
344 if (tileLike.isCoreTile())
345 tileTypeStr =
"core tile";
346 else if (tileLike.isMemTile())
347 tileTypeStr =
"mem tile";
349 tileTypeStr =
"shim tile";
352 std::string fullEventName;
353 if (
auto strAttr = llvm::dyn_cast<StringAttr>(eventAttr.getValue()))
354 fullEventName = strAttr.getValue().str();
356 fullEventName = eventName;
358 auto col = tileLike.tryGetCol();
359 auto row = tileLike.tryGetRow();
360 auto fmtCoord = [](std::optional<int> v) -> std::string {
361 return v ? std::to_string(*v) :
"?";
363 return emitOpError(
"event '")
364 << fullEventName <<
"' is not valid for " << tileTypeStr <<
" ("
365 << stringifyAIEArch(targetModel.getTargetArch()) <<
") at ("
366 << fmtCoord(col) <<
", " << fmtCoord(row) <<
")";
376LogicalResult TracePacketOp::verify() {
380 if (
auto id = getId()) {
381 if (*id < 1 || *id > 31)
382 return emitOpError(
"packet ID must be in range [1, 31], got ") << *id;
391LogicalResult TracePortOp::verify() {
393 auto trace = (*this)->getParentOfType<TraceOp>();
395 return emitOpError(
"must be nested in aie.trace");
398 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
400 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
404 auto device = trace->getParentOfType<DeviceOp>();
405 const auto &targetModel = device.getTargetModel();
408 bool isMaster = (getDirection() == DMAChannelDir::S2MM);
411 auto col = tileLike.tryGetCol();
412 auto row = tileLike.tryGetRow();
415 .getStreamSwitchPortIndex(*col, *row, getPort(), getChannel(),
418 return emitOpError(
"invalid stream switch port configuration for tile (")
419 << *col <<
", " << *row <<
")";
424 for (
auto &op : trace.getBody().getOps()) {
425 if (
auto otherPort = dyn_cast<TracePortOp>(op)) {
426 if (otherPort != *
this && otherPort.getSlot() == getSlot()) {
427 return emitOpError(
"duplicate port slot ")
428 << getSlot() <<
" in trace " << trace.getSymName();
440LogicalResult TraceStartEventOp::verify() {
442 bool hasBroadcast = getBroadcast().has_value();
443 bool hasEvent = getEvent().has_value();
445 if (!hasBroadcast && !hasEvent) {
446 return emitOpError(
"must specify either broadcast or event");
449 if (hasBroadcast && hasEvent) {
450 return emitOpError(
"cannot specify both broadcast and event");
460LogicalResult TraceStopEventOp::verify() {
462 bool hasBroadcast = getBroadcast().has_value();
463 bool hasEvent = getEvent().has_value();
465 if (!hasBroadcast && !hasEvent) {
466 return emitOpError(
"must specify either broadcast or event");
469 if (hasBroadcast && hasEvent) {
470 return emitOpError(
"cannot specify both broadcast and event");
480void TraceStartEventOp::print(OpAsmPrinter &p) {
481 if (
auto broadcast = getBroadcast()) {
482 p <<
" broadcast = " << broadcast.value();
484 if (
auto event = getEvent()) {
489 p.printOptionalAttrDict((*this)->getAttrs(),
490 {
"broadcast",
"event"});
493ParseResult TraceStartEventOp::parse(OpAsmParser &parser,
494 OperationState &result) {
496 if (succeeded(parser.parseOptionalKeyword(
"broadcast"))) {
497 IntegerAttr broadcast;
498 if (parser.parseEqual() ||
499 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
500 "broadcast", result.attributes))
505 if (succeeded(parser.parseOptionalKeyword(
"event"))) {
506 if (parser.parseEqual() || parser.parseLess())
509 Attribute innerValue;
513 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
514 result.attributes.set(
"event", traceEvent);
516 if (parser.parseGreater())
520 if (parser.parseOptionalAttrDict(result.attributes))
526void TraceStopEventOp::print(OpAsmPrinter &p) {
527 if (
auto broadcast = getBroadcast()) {
528 p <<
" broadcast = " << broadcast.value();
530 if (
auto event = getEvent()) {
535 p.printOptionalAttrDict((*this)->getAttrs(),
536 {
"broadcast",
"event"});
539ParseResult TraceStopEventOp::parse(OpAsmParser &parser,
540 OperationState &result) {
542 if (succeeded(parser.parseOptionalKeyword(
"broadcast"))) {
543 IntegerAttr broadcast;
544 if (parser.parseEqual() ||
545 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
546 "broadcast", result.attributes))
551 if (succeeded(parser.parseOptionalKeyword(
"event"))) {
552 if (parser.parseEqual() || parser.parseLess())
555 Attribute innerValue;
559 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
560 result.attributes.set(
"event", traceEvent);
562 if (parser.parseGreater())
566 if (parser.parseOptionalAttrDict(result.attributes))
576LogicalResult TraceComboEventOp::verify() {
577 uint32_t slot = getSlot();
581 return emitOpError(
"combo event slot must be 0, 1, or 2, got ") << slot;
585 auto trace = (*this)->getParentOfType<TraceOp>();
587 return emitOpError(
"must be nested in aie.trace");
590 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
592 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
596 auto device = trace->getParentOfType<DeviceOp>();
597 const auto &targetModel = device.getTargetModel();
599 if (!isValidEventForTile(tileLike, getEventA().getValue(), targetModel)) {
600 return emitOpError(
"eventA is not valid for this tile type");
602 if (!isValidEventForTile(tileLike, getEventB().getValue(), targetModel)) {
603 return emitOpError(
"eventB is not valid for this tile type");
607 std::string eventAName = getEventA().getEventName();
608 std::string eventBName = getEventB().getEventName();
612 if (eventAName.find(
"COMBO_EVENT") != std::string::npos ||
613 eventBName.find(
"COMBO_EVENT") != std::string::npos) {
614 return emitOpError(
"combo slot 0 should use regular events, not "
615 "COMBO_EVENT_* (uses eventA/B)");
617 }
else if (slot == 1) {
619 if (eventAName.find(
"COMBO_EVENT") != std::string::npos ||
620 eventBName.find(
"COMBO_EVENT") != std::string::npos) {
621 return emitOpError(
"combo slot 1 should use regular events, not "
622 "COMBO_EVENT_* (uses eventC/D)");
624 }
else if (slot == 2) {
626 if (eventAName !=
"COMBO_EVENT_0") {
627 return emitOpError(
"combo slot 2 first event must be COMBO_EVENT_0 "
628 "(hierarchical), got ")
631 if (eventBName !=
"COMBO_EVENT_1") {
632 return emitOpError(
"combo slot 2 second event must be COMBO_EVENT_1 "
633 "(hierarchical), got ")
645LogicalResult TraceEdgeEventOp::verify() {
646 uint32_t slot = getSlot();
650 return emitOpError(
"edge detection slot must be 0 or 1, got ") << slot;
654 auto trace = (*this)->getParentOfType<TraceOp>();
656 return emitOpError(
"must be nested in aie.trace");
659 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
661 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
665 auto device = trace->getParentOfType<DeviceOp>();
666 const auto &targetModel = device.getTargetModel();
668 if (!isValidEventForTile(tileLike, getEvent().getValue(), targetModel)) {
669 return emitOpError(
"event is not valid for this tile type");
673 std::string eventName = getEvent().getEventName();
674 if (eventName.find(
"EDGE_DETECTION_EVENT") != std::string::npos) {
675 return emitOpError(
"edge detection source should be a regular event, not "
676 "another EDGE_DETECTION_EVENT");
678 if (eventName.find(
"COMBO_EVENT") != std::string::npos) {
679 return emitOpError(
"edge detection source should be a regular event, not "
680 "a COMBO_EVENT (combo events can be used but may have "
681 "unexpected behavior)");
687void TraceEdgeEventOp::print(OpAsmPrinter &p) {
688 p <<
"<" << getSlot() <<
"> event = <";
690 p <<
"> trigger = " << getTrigger();
691 p.printOptionalAttrDict((*this)->getAttrs(),
692 {
"slot",
"event",
"trigger"});
695ParseResult TraceEdgeEventOp::parse(OpAsmParser &parser,
696 OperationState &result) {
698 Attribute eventValue;
699 EdgeTriggerAttr trigger;
701 if (parser.parseLess() ||
702 parser.parseAttribute(slot, parser.getBuilder().getI32Type(),
"slot",
703 result.attributes) ||
704 parser.parseGreater() || parser.parseKeyword(
"event") ||
705 parser.parseEqual() || parser.parseLess())
712 auto event = TraceEventAttr::get(parser.getContext(), eventValue);
713 result.attributes.set(
"event", event);
715 if (parser.parseGreater() || parser.parseKeyword(
"trigger") ||
720 StringRef triggerStr;
721 if (failed(parser.parseKeyword(&triggerStr)))
724 auto triggerEnum = symbolizeEdgeTrigger(triggerStr);
726 return parser.emitError(parser.getCurrentLocation(),
727 "unknown edge trigger: ")
730 trigger = EdgeTriggerAttr::get(parser.getContext(), *triggerEnum);
731 result.attributes.set(
"trigger", trigger);
733 if (parser.parseOptionalAttrDict(result.attributes))
743LogicalResult TraceHostConfigOp::verify() {
746 if (getReuseOutputBuffer()) {
747 if (getRouting() != TraceShimRouting::Single) {
748 return emitOpError(
"reuse_output_buffer (append trace after last tensor) "
749 "only works with single shim destination strategy "
755 if (getBufferSize() <= 0) {
756 return emitOpError(
"buffer_size must be positive");
760 if (getEgressShimCol() < 0) {
761 return emitOpError(
"egress_shim_col must be >= 0");
771LogicalResult TraceStartConfigOp::verify() {
774 auto symbolName = getTraceConfig();
775 if (symbolName.empty()) {
776 return emitOpError(
"trace config symbol name cannot be empty");
virtual AIEArch getTargetArch() const =0
Return the target architecture.
mlir::ParseResult parseTraceEvent(mlir::AsmParser &parser, mlir::Attribute &result)
void printTraceEventEnum(mlir::AsmPrinter &printer, mlir::Attribute attr)