15#include "mlir/IR/OpImplementation.h"
16#include "mlir/IR/SymbolTable.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringRef.h"
28std::string TraceEventAttr::getEventName()
const {
29 if (
auto strAttr = llvm::dyn_cast<StringAttr>(getValue())) {
31 StringRef strValue = strAttr.getValue();
32 size_t pos = strValue.find(
"::");
33 if (pos != StringRef::npos) {
34 return strValue.substr(pos + 2).str();
36 return strAttr.getValue().str();
43bool TraceEventAttr::isStringAttr()
const {
44 return llvm::isa<StringAttr>(getValue());
47std::optional<int64_t> TraceEventAttr::getEnumValue()
const {
48 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(getValue())) {
49 return intAttr.getInt();
59static bool isValidEventForTile(TileLike tile, Attribute eventAttr,
62 bool isShimTile = tile.isShimTile();
63 bool isMemTile = tile.isMemTile();
64 bool isCoreTile = tile.isCoreTile();
68 Attribute innerAttr = eventAttr;
69 if (
auto traceEvent = llvm::dyn_cast<TraceEventAttr>(eventAttr)) {
70 innerAttr = traceEvent.getValue();
81 if (
auto strAttr = llvm::dyn_cast<StringAttr>(innerAttr)) {
82 StringRef val = strAttr.getValue();
83 size_t sep = val.find(
"::");
84 if (sep == StringRef::npos)
87 StringRef prefix = val.substr(0, sep);
93 return prefix ==
"CoreEventAIE" || prefix ==
"MemEventAIE";
95 return prefix ==
"CoreEventAIE2" || prefix ==
"MemEventAIE2";
97 return prefix ==
"CoreEventAIE2P" || prefix ==
"MemEventAIE2P";
105 return prefix ==
"MemTileEventAIE2";
107 return prefix ==
"MemTileEventAIE2P";
115 return prefix ==
"ShimTileEventAIE";
117 return prefix ==
"ShimTileEventAIE2";
119 return prefix ==
"ShimTileEventAIE2P";
134void TraceOp::getAsmResultNames(
135 function_ref<
void(Value, StringRef)> setNameFn) {
139LogicalResult TraceOp::verify() {
142 for (
auto &op : getBody().getOps()) {
143 if (isa<TraceEventOp>(op)) {
149 if (eventCount > 8) {
150 return emitOpError(
"trace unit supports maximum 8 events, got ")
155 if (!llvm::dyn_cast<TileLike>(getTile().getDefiningOp())) {
156 return emitOpError(
"tile operand must be a TileOp or LogicalTileOp");
160 llvm::DenseSet<uint32_t> comboSlots;
161 llvm::DenseSet<uint32_t> edgeSlots;
163 for (
auto &op : getBody().getOps()) {
164 if (
auto comboOp = dyn_cast<TraceComboEventOp>(op)) {
165 uint32_t slot = comboOp.getSlot();
166 if (!comboSlots.insert(slot).second) {
167 return comboOp.emitOpError(
"combo event slot ")
168 << slot <<
" already in use in this trace";
170 }
else if (
auto edgeOp = dyn_cast<TraceEdgeEventOp>(op)) {
171 uint32_t slot = edgeOp.getSlot();
172 if (!edgeSlots.insert(slot).second) {
173 return edgeOp.emitOpError(
"edge detection slot ")
174 << slot <<
" already in use in this trace";
188 MLIRContext *ctx = parser.getContext();
191 std::string strValue;
192 if (succeeded(parser.parseOptionalString(&strValue))) {
193 result = StringAttr::get(ctx, strValue);
198 StringRef enumTypeName;
199 llvm::SMLoc loc = parser.getCurrentLocation();
201 if (failed(parser.parseKeyword(&enumTypeName))) {
202 return parser.emitError(loc,
"expected string or enum event");
205 if (failed(parser.parseColon()) || failed(parser.parseColon())) {
206 return parser.emitError(loc,
"expected '::' after enum type name");
210 if (failed(parser.parseKeyword(&caseName))) {
211 return parser.emitError(parser.getCurrentLocation(),
212 "expected enum case name");
220 struct EnumValidator {
222 std::function<
bool(StringRef)> symbolizer;
226 static const EnumValidator validators[] = {
229 [](StringRef s) {
return symbolizeCoreEventAIE2(s).has_value(); }},
232 auto result = symbolizeMemEventAIE2(s);
233 return result.has_value();
237 auto result = symbolizeMemTileEventAIE2(s);
238 return result.has_value();
240 {
"ShimTileEventAIE2",
242 auto result = symbolizeShimTileEventAIE2(s);
243 return result.has_value();
248 auto result = symbolizeCoreEventAIE(s);
249 return result.has_value();
253 auto result = symbolizeMemEventAIE(s);
254 return result.has_value();
258 auto result = symbolizeShimTileEventAIE(s);
259 return result.has_value();
264 auto result = symbolizeCoreEventAIE2P(s);
265 return result.has_value();
269 auto result = symbolizeMemEventAIE2P(s);
270 return result.has_value();
272 {
"MemTileEventAIE2P",
274 auto result = symbolizeMemTileEventAIE2P(s);
275 return result.has_value();
277 {
"ShimTileEventAIE2P",
279 auto result = symbolizeShimTileEventAIE2P(s);
280 return result.has_value();
285 for (
const auto &validator : validators) {
286 if (enumTypeName != validator.name)
288 if (!validator.symbolizer(caseName))
289 return parser.emitError(loc,
"unknown ")
290 << enumTypeName <<
" value: " << caseName;
291 result = StringAttr::get(ctx, enumTypeName +
"::" + caseName);
295 return parser.emitError(loc,
"unknown event enum type: ") << enumTypeName;
299 if (
auto traceAttr = llvm::dyn_cast<TraceEventAttr>(attr)) {
303 if (
auto strAttr = llvm::dyn_cast<StringAttr>(attr)) {
305 if (strAttr.getValue().contains(
"::")) {
306 printer << strAttr.getValue();
309 printer <<
"\"" << strAttr.getValue() <<
"\"";
311 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
312 printer << intAttr.getInt();
321LogicalResult TraceEventOp::verify() {
323 auto eventAttr = getEvent();
326 std::string eventName = eventAttr.getEventName();
327 if (eventName.empty()) {
328 return emitOpError(
"event name cannot be empty");
332 auto trace = (*this)->getParentOfType<TraceOp>();
334 return emitOpError(
"must be nested in aie.trace");
337 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
339 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
342 auto device = trace->getParentOfType<DeviceOp>();
343 const auto &targetModel = device.getTargetModel();
345 if (!isValidEventForTile(tileLike, eventAttr.getValue(), targetModel)) {
346 std::string tileTypeStr;
347 if (tileLike.isCoreTile())
348 tileTypeStr =
"core tile";
349 else if (tileLike.isMemTile())
350 tileTypeStr =
"mem tile";
352 tileTypeStr =
"shim tile";
355 std::string fullEventName;
356 if (
auto strAttr = llvm::dyn_cast<StringAttr>(eventAttr.getValue()))
357 fullEventName = strAttr.getValue().str();
359 fullEventName = eventName;
361 auto col = tileLike.tryGetCol();
362 auto row = tileLike.tryGetRow();
363 auto fmtCoord = [](std::optional<int> v) -> std::string {
364 return v ? std::to_string(*v) :
"?";
366 return emitOpError(
"event '")
367 << fullEventName <<
"' is not valid for " << tileTypeStr <<
" ("
368 << stringifyAIEArch(targetModel.getTargetArch()) <<
") at ("
369 << fmtCoord(
col) <<
", " << fmtCoord(
row) <<
")";
379LogicalResult TracePacketOp::verify() {
382 int32_t
id = getId();
383 if (id < 1 || id > 31) {
384 return emitOpError(
"packet ID must be in range [1, 31], got ") << id;
394LogicalResult TracePortOp::verify() {
396 auto trace = (*this)->getParentOfType<TraceOp>();
398 return emitOpError(
"must be nested in aie.trace");
401 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
403 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
407 auto device = trace->getParentOfType<DeviceOp>();
408 const auto &targetModel = device.getTargetModel();
411 bool isMaster = (getDirection() == DMAChannelDir::S2MM);
414 auto col = tileLike.tryGetCol();
415 auto row = tileLike.tryGetRow();
418 .getStreamSwitchPortIndex(*
col, *
row, getPort(), getChannel(),
421 return emitOpError(
"invalid stream switch port configuration for tile (")
422 << *
col <<
", " << *
row <<
")";
427 for (
auto &op : trace.getBody().getOps()) {
428 if (
auto otherPort = dyn_cast<TracePortOp>(op)) {
429 if (otherPort != *
this && otherPort.getSlot() == getSlot()) {
430 return emitOpError(
"duplicate port slot ")
431 << getSlot() <<
" in trace " << trace.getSymName();
443LogicalResult TraceStartEventOp::verify() {
445 bool hasBroadcast = getBroadcast().has_value();
446 bool hasEvent = getEvent().has_value();
448 if (!hasBroadcast && !hasEvent) {
449 return emitOpError(
"must specify either broadcast or event");
452 if (hasBroadcast && hasEvent) {
453 return emitOpError(
"cannot specify both broadcast and event");
463LogicalResult TraceStopEventOp::verify() {
465 bool hasBroadcast = getBroadcast().has_value();
466 bool hasEvent = getEvent().has_value();
468 if (!hasBroadcast && !hasEvent) {
469 return emitOpError(
"must specify either broadcast or event");
472 if (hasBroadcast && hasEvent) {
473 return emitOpError(
"cannot specify both broadcast and event");
483void TraceStartEventOp::print(OpAsmPrinter &p) {
484 if (
auto broadcast = getBroadcast()) {
485 p <<
" broadcast = " << broadcast.value();
487 if (
auto event = getEvent()) {
492 p.printOptionalAttrDict((*this)->getAttrs(),
493 {
"broadcast",
"event"});
496ParseResult TraceStartEventOp::parse(OpAsmParser &parser,
497 OperationState &result) {
499 if (succeeded(parser.parseOptionalKeyword(
"broadcast"))) {
500 IntegerAttr broadcast;
501 if (parser.parseEqual() ||
502 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
503 "broadcast", result.attributes))
508 if (succeeded(parser.parseOptionalKeyword(
"event"))) {
509 if (parser.parseEqual() || parser.parseLess())
512 Attribute innerValue;
516 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
517 result.attributes.set(
"event", traceEvent);
519 if (parser.parseGreater())
523 if (parser.parseOptionalAttrDict(result.attributes))
529void TraceStopEventOp::print(OpAsmPrinter &p) {
530 if (
auto broadcast = getBroadcast()) {
531 p <<
" broadcast = " << broadcast.value();
533 if (
auto event = getEvent()) {
538 p.printOptionalAttrDict((*this)->getAttrs(),
539 {
"broadcast",
"event"});
542ParseResult TraceStopEventOp::parse(OpAsmParser &parser,
543 OperationState &result) {
545 if (succeeded(parser.parseOptionalKeyword(
"broadcast"))) {
546 IntegerAttr broadcast;
547 if (parser.parseEqual() ||
548 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
549 "broadcast", result.attributes))
554 if (succeeded(parser.parseOptionalKeyword(
"event"))) {
555 if (parser.parseEqual() || parser.parseLess())
558 Attribute innerValue;
562 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
563 result.attributes.set(
"event", traceEvent);
565 if (parser.parseGreater())
569 if (parser.parseOptionalAttrDict(result.attributes))
579LogicalResult TraceComboEventOp::verify() {
580 uint32_t slot = getSlot();
584 return emitOpError(
"combo event slot must be 0, 1, or 2, got ") << slot;
588 auto trace = (*this)->getParentOfType<TraceOp>();
590 return emitOpError(
"must be nested in aie.trace");
593 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
595 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
599 auto device = trace->getParentOfType<DeviceOp>();
600 const auto &targetModel = device.getTargetModel();
602 if (!isValidEventForTile(tileLike, getEventA().getValue(), targetModel)) {
603 return emitOpError(
"eventA is not valid for this tile type");
605 if (!isValidEventForTile(tileLike, getEventB().getValue(), targetModel)) {
606 return emitOpError(
"eventB is not valid for this tile type");
610 std::string eventAName = getEventA().getEventName();
611 std::string eventBName = getEventB().getEventName();
615 if (eventAName.find(
"COMBO_EVENT") != std::string::npos ||
616 eventBName.find(
"COMBO_EVENT") != std::string::npos) {
617 return emitOpError(
"combo slot 0 should use regular events, not "
618 "COMBO_EVENT_* (uses eventA/B)");
620 }
else if (slot == 1) {
622 if (eventAName.find(
"COMBO_EVENT") != std::string::npos ||
623 eventBName.find(
"COMBO_EVENT") != std::string::npos) {
624 return emitOpError(
"combo slot 1 should use regular events, not "
625 "COMBO_EVENT_* (uses eventC/D)");
627 }
else if (slot == 2) {
629 if (eventAName !=
"COMBO_EVENT_0") {
630 return emitOpError(
"combo slot 2 first event must be COMBO_EVENT_0 "
631 "(hierarchical), got ")
634 if (eventBName !=
"COMBO_EVENT_1") {
635 return emitOpError(
"combo slot 2 second event must be COMBO_EVENT_1 "
636 "(hierarchical), got ")
648LogicalResult TraceEdgeEventOp::verify() {
649 uint32_t slot = getSlot();
653 return emitOpError(
"edge detection slot must be 0 or 1, got ") << slot;
657 auto trace = (*this)->getParentOfType<TraceOp>();
659 return emitOpError(
"must be nested in aie.trace");
662 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
664 return emitOpError(
"trace tile must be a TileOp or LogicalTileOp");
668 auto device = trace->getParentOfType<DeviceOp>();
669 const auto &targetModel = device.getTargetModel();
671 if (!isValidEventForTile(tileLike, getEvent().getValue(), targetModel)) {
672 return emitOpError(
"event is not valid for this tile type");
676 std::string eventName = getEvent().getEventName();
677 if (eventName.find(
"EDGE_DETECTION_EVENT") != std::string::npos) {
678 return emitOpError(
"edge detection source should be a regular event, not "
679 "another EDGE_DETECTION_EVENT");
681 if (eventName.find(
"COMBO_EVENT") != std::string::npos) {
682 return emitOpError(
"edge detection source should be a regular event, not "
683 "a COMBO_EVENT (combo events can be used but may have "
684 "unexpected behavior)");
690void TraceEdgeEventOp::print(OpAsmPrinter &p) {
691 p <<
"<" << getSlot() <<
"> event = <";
693 p <<
"> trigger = " << getTrigger();
694 p.printOptionalAttrDict((*this)->getAttrs(),
695 {
"slot",
"event",
"trigger"});
698ParseResult TraceEdgeEventOp::parse(OpAsmParser &parser,
699 OperationState &result) {
701 Attribute eventValue;
702 EdgeTriggerAttr trigger;
704 if (parser.parseLess() ||
705 parser.parseAttribute(slot, parser.getBuilder().getI32Type(),
"slot",
706 result.attributes) ||
707 parser.parseGreater() || parser.parseKeyword(
"event") ||
708 parser.parseEqual() || parser.parseLess())
715 auto event = TraceEventAttr::get(parser.getContext(), eventValue);
716 result.attributes.set(
"event", event);
718 if (parser.parseGreater() || parser.parseKeyword(
"trigger") ||
723 StringRef triggerStr;
724 if (failed(parser.parseKeyword(&triggerStr)))
727 auto triggerEnum = symbolizeEdgeTrigger(triggerStr);
729 return parser.emitError(parser.getCurrentLocation(),
730 "unknown edge trigger: ")
733 trigger = EdgeTriggerAttr::get(parser.getContext(), *triggerEnum);
734 result.attributes.set(
"trigger", trigger);
736 if (parser.parseOptionalAttrDict(result.attributes))
746void TraceHostConfigOp::print(OpAsmPrinter &p) {
747 p <<
" buffer_size = " << getBufferSize();
750 if (getArgIdx() != 4)
751 p <<
" arg_idx = " << getArgIdx();
753 if (getRouting() != TraceShimRouting::Single)
754 p <<
" routing = " << stringifyTraceShimRouting(getRouting());
756 p.printOptionalAttrDict(
758 {
"buffer_size",
"arg_idx",
"routing"});
761ParseResult TraceHostConfigOp::parse(OpAsmParser &parser,
762 OperationState &result) {
764 IntegerAttr bufferSize;
765 if (parser.parseKeyword(
"buffer_size") || parser.parseEqual() ||
766 parser.parseAttribute(bufferSize, parser.getBuilder().getI32Type(),
767 "buffer_size", result.attributes))
771 int32_t argIdxVal = 4;
772 if (succeeded(parser.parseOptionalKeyword(
"arg_idx"))) {
774 if (parser.parseEqual() ||
775 parser.parseAttribute(argIdx, parser.getBuilder().getI32Type(),
776 "arg_idx", result.attributes))
779 result.attributes.set(
"arg_idx",
780 parser.getBuilder().getI32IntegerAttr(argIdxVal));
784 TraceShimRouting routingVal = TraceShimRouting::Single;
785 if (succeeded(parser.parseOptionalKeyword(
"routing"))) {
786 if (parser.parseEqual())
788 StringRef routingStr;
789 if (failed(parser.parseKeyword(&routingStr)))
791 auto routing = symbolizeTraceShimRouting(routingStr);
793 return parser.emitError(parser.getCurrentLocation(),
794 "unknown routing strategy: ")
796 routingVal = *routing;
798 result.attributes.set(
799 "routing", TraceShimRoutingAttr::get(parser.getContext(), routingVal));
801 if (parser.parseOptionalAttrDict(result.attributes))
807LogicalResult TraceHostConfigOp::verify() {
809 if (getArgIdx() == -1) {
810 if (getRouting() != TraceShimRouting::Single) {
811 return emitOpError(
"arg_idx=-1 (append trace after last tensor) "
812 "only works with single shim destination strategy "
818 if (getBufferSize() <= 0) {
819 return emitOpError(
"buffer_size must be positive");
829LogicalResult TraceStartConfigOp::verify() {
832 auto symbolName = getTraceConfig();
833 if (symbolName.empty()) {
834 return emitOpError(
"trace config symbol name cannot be empty");
virtual AIEArch getTargetArch() const =0
Return the target architecture.
Include the generated interface declarations.
mlir::ParseResult parseTraceEvent(mlir::AsmParser &parser, mlir::Attribute &result)
void printTraceEventEnum(mlir::AsmPrinter &printer, mlir::Attribute attr)