MLIR-AIE 1.4.5.dev
AIETraceToConfig.cpp
Go to the documentation of this file.
1//===- AIETraceToConfig.cpp -------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2025 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7// Pass to lower aie.trace to aie.trace.config
8//===----------------------------------------------------------------------===//
9
12
13#include "mlir/IR/Attributes.h"
14#include "mlir/Pass/Pass.h"
15
16namespace xilinx::AIE {
17#define GEN_PASS_DEF_AIETRACETOCONFIG
18#define GEN_PASS_DEF_AIETRACEREGPACKWRITES
19#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
20} // namespace xilinx::AIE
21
22using namespace mlir;
23using namespace xilinx;
24using namespace xilinx::AIE;
25
26namespace {
27
28struct AIETraceToConfigPass
29 : xilinx::AIE::impl::AIETraceToConfigBase<AIETraceToConfigPass> {
30 void runOnOperation() override {
31 DeviceOp device = getOperation();
32 OpBuilder builder(device);
33 const auto &targetModel = device.getTargetModel();
34
35 // Collect all trace operations
36 SmallVector<TraceOp> traces;
37 device.walk([&](TraceOp trace) { traces.push_back(trace); });
38
39 for (auto trace : traces) {
40 // Create config symbol name
41 std::string configName = (trace.getSymName().str() + "_config");
42 auto tile = cast<TileOp>(trace.getTile().getDefiningOp());
43 TileID tileID = tile.getTileID();
44
45 // Find packet type (if any)
46 TracePacketType packetType = TracePacketType::Core; // default
47 for (auto &op : trace.getBody().getOps()) {
48 if (auto packetOp = dyn_cast<TracePacketOp>(op)) {
49 packetType = packetOp.getType();
50 break;
51 }
52 }
53
54 // Insert trace.config after trace declaration
55 builder.setInsertionPointAfter(trace);
56 auto configOp = TraceConfigOp::create(
57 builder, trace.getLoc(), trace.getTile(),
58 builder.getStringAttr(configName),
59 TracePacketTypeAttr::get(builder.getContext(), packetType));
60
61 // Build register writes inside config body
62 Block *configBody = new Block();
63 configOp.getBody().push_back(configBody);
64 OpBuilder configBuilder = OpBuilder::atBlockEnd(configBody);
65
66 bool isMem = (packetType == TracePacketType::Mem);
67
68 // Process combo/edge events FIRST (before other trace config)
69 // This ensures COMBO_EVENT_*/EDGE_DETECTION_EVENT_* are configured
70 // before they can be referenced in trace.event operations
71
72 // 0a. Emit combo event configurations
73 for (auto &op : trace.getBody().getOps()) {
74 if (auto comboOp = dyn_cast<TraceComboEventOp>(op)) {
75 uint32_t slot = comboOp.getSlot();
76
77 // Get input events - use getEventName() helper
78 std::string eventAName = comboOp.getEventA().getEventName();
79 std::string eventBName = comboOp.getEventB().getEventName();
80 ComboLogic logic = comboOp.getLogic();
81
82 // If enum, use enum value directly; otherwise lookup by name
83 std::optional<uint32_t> eventANum, eventBNum;
84
85 if (auto enumValA = comboOp.getEventA().getEnumValue()) {
86 eventANum = static_cast<uint32_t>(*enumValA);
87 } else {
88 eventANum = targetModel.lookupEvent(eventAName, tileID, isMem);
89 }
90
91 if (auto enumValB = comboOp.getEventB().getEnumValue()) {
92 eventBNum = static_cast<uint32_t>(*enumValB);
93 } else {
94 eventBNum = targetModel.lookupEvent(eventBName, tileID, isMem);
95 }
96
97 if (!eventANum) {
98 comboOp.emitError("unknown trace event '") << eventAName << "'";
99 return signalPassFailure();
100 }
101 if (!eventBNum) {
102 comboOp.emitError("unknown trace event '") << eventBName << "'";
103 return signalPassFailure();
104 }
105
106 // Map slot to input event fields
107 StringRef eventAField, eventBField, controlField;
108 if (slot == 0) {
109 eventAField = "eventA";
110 eventBField = "eventB";
111 controlField = "combo0";
112 } else if (slot == 1) {
113 eventAField = "eventC";
114 eventBField = "eventD";
115 controlField = "combo1";
116 } else if (slot == 2) {
117 // Combo2 is hierarchical - reuses eventA/B fields but represents
118 // combo0/combo1
119 eventAField = "eventA";
120 eventBField = "eventB";
121 controlField = "combo2";
122 }
123
124 // Emit Combo_event_inputs register fields
125 TraceRegOp::create(configBuilder, comboOp.getLoc(),
126 builder.getStringAttr("Combo_event_inputs"),
127 builder.getStringAttr(eventAField),
128 comboOp.getEventA(),
129 /*mask=*/nullptr,
130 builder.getStringAttr(
131 "combo" + std::to_string(slot) + " eventA"));
132
133 TraceRegOp::create(configBuilder, comboOp.getLoc(),
134 builder.getStringAttr("Combo_event_inputs"),
135 builder.getStringAttr(eventBField),
136 comboOp.getEventB(),
137 /*mask=*/nullptr,
138 builder.getStringAttr(
139 "combo" + std::to_string(slot) + " eventB"));
140
141 // Emit Combo_event_control register field
142 TraceRegOp::create(
143 configBuilder, comboOp.getLoc(),
144 builder.getStringAttr("Combo_event_control"),
145 builder.getStringAttr(controlField),
146 builder.getI32IntegerAttr(static_cast<uint32_t>(logic)),
147 /*mask=*/nullptr,
148 builder.getStringAttr("combo" + std::to_string(slot) + " logic"));
149 }
150 }
151
152 // 0b. Emit edge detection configurations
153 for (auto &op : trace.getBody().getOps()) {
154 if (auto edgeOp = dyn_cast<TraceEdgeEventOp>(op)) {
155 uint32_t slot = edgeOp.getSlot();
156 std::string eventName = edgeOp.getEvent().getEventName();
157 EdgeTrigger trigger = edgeOp.getTrigger();
158
159 // If enum, use enum value directly; otherwise lookup by name
160 std::optional<uint32_t> eventNum;
161
162 if (auto enumVal = edgeOp.getEvent().getEnumValue()) {
163 eventNum = static_cast<uint32_t>(*enumVal);
164 } else {
165 eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
166 }
167
168 if (!eventNum) {
169 edgeOp.emitError("unknown trace event '") << eventName << "'";
170 return signalPassFailure();
171 }
172
173 // Map slot to field names
174 StringRef eventField =
175 (slot == 0) ? "Edge_Detection_Event_0" : "Edge_Detection_Event_1";
176 StringRef risingField = (slot == 0)
177 ? "Edge_Detection_0_Trigger_Rising"
178 : "Edge_Detection_1_Trigger_Rising";
179 StringRef fallingField = (slot == 0)
180 ? "Edge_Detection_0_Trigger_Falling"
181 : "Edge_Detection_1_Trigger_Falling";
182
183 // Source event
184 TraceRegOp::create(
185 configBuilder, edgeOp.getLoc(),
186 builder.getStringAttr("Edge_Detection_event_control"),
187 builder.getStringAttr(eventField), edgeOp.getEvent(),
188 /*mask=*/nullptr,
189 builder.getStringAttr("edge" + std::to_string(slot) + " source"));
190
191 // Trigger mode
192 bool rising =
193 (trigger == EdgeTrigger::RISING || trigger == EdgeTrigger::BOTH);
194 bool falling =
195 (trigger == EdgeTrigger::FALLING || trigger == EdgeTrigger::BOTH);
196
197 TraceRegOp::create(
198 configBuilder, edgeOp.getLoc(),
199 builder.getStringAttr("Edge_Detection_event_control"),
200 builder.getStringAttr(risingField),
201 builder.getI32IntegerAttr(rising ? 1 : 0),
202 /*mask=*/nullptr,
203 builder.getStringAttr("edge" + std::to_string(slot) + " rising"));
204
205 TraceRegOp::create(
206 configBuilder, edgeOp.getLoc(),
207 builder.getStringAttr("Edge_Detection_event_control"),
208 builder.getStringAttr(fallingField),
209 builder.getI32IntegerAttr(falling ? 1 : 0),
210 /*mask=*/nullptr,
211 builder.getStringAttr("edge" + std::to_string(slot) +
212 " falling"));
213 }
214 }
215
216 // 1. Emit Trace_Control0 fields
217 // Check for start/stop events
218 for (auto &op : trace.getBody().getOps()) {
219 if (auto startOp = dyn_cast<TraceStartEventOp>(op)) {
220 uint32_t startEvent = 0;
221 if (auto broadcast = startOp.getBroadcast()) {
222 uint32_t broadcastNum = *broadcast;
223 // Resolve broadcast channel to hardware event ID
224 std::string eventName;
225 if (tile.isShimTile()) {
226 eventName = "BROADCAST_A_" + std::to_string(broadcastNum);
227 } else {
228 eventName = "BROADCAST_" + std::to_string(broadcastNum);
229 }
230 auto eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
231 if (eventNum) {
232 startEvent = *eventNum;
233 } else {
234 startOp.emitError("unknown broadcast event '")
235 << eventName << "'";
236 return signalPassFailure();
237 }
238 } else if (auto eventAttr = startOp.getEvent()) {
239 // Use getEventName() helper and check for enum
240 std::string eventName = eventAttr->getEventName();
241
242 std::optional<uint32_t> eventNum;
243 if (auto enumVal = eventAttr->getEnumValue()) {
244 eventNum = static_cast<uint32_t>(*enumVal);
245 } else {
246 eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
247 }
248
249 if (eventNum) {
250 startEvent = *eventNum;
251 } else {
252 startOp.emitError("unknown trace event '") << eventName << "'";
253 return signalPassFailure();
254 }
255 }
256
257 TraceRegOp::create(configBuilder, trace.getLoc(),
258 builder.getStringAttr("Trace_Control0"),
259 builder.getStringAttr("Trace_Start_Event"),
260 builder.getI32IntegerAttr(startEvent),
261 /*mask=*/nullptr,
262 builder.getStringAttr("start event"));
263 }
264
265 if (auto stopOp = dyn_cast<TraceStopEventOp>(op)) {
266 uint32_t stopEvent = 0;
267 if (auto broadcast = stopOp.getBroadcast()) {
268 uint32_t broadcastNum = *broadcast;
269 // Resolve broadcast channel to hardware event ID
270 std::string eventName;
271 if (tile.isShimTile()) {
272 eventName = "BROADCAST_A_" + std::to_string(broadcastNum);
273 } else {
274 eventName = "BROADCAST_" + std::to_string(broadcastNum);
275 }
276 auto eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
277 if (eventNum) {
278 stopEvent = *eventNum;
279 } else {
280 stopOp.emitError("unknown broadcast event '") << eventName << "'";
281 return signalPassFailure();
282 }
283 } else if (auto eventAttr = stopOp.getEvent()) {
284 // Use getEventName() helper and check for enum
285 std::string eventName = eventAttr->getEventName();
286
287 std::optional<uint32_t> eventNum;
288 if (auto enumVal = eventAttr->getEnumValue()) {
289 eventNum = static_cast<uint32_t>(*enumVal);
290 } else {
291 eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
292 }
293
294 if (eventNum) {
295 stopEvent = *eventNum;
296 } else {
297 stopOp.emitError("unknown trace event '") << eventName << "'";
298 return signalPassFailure();
299 }
300 }
301
302 TraceRegOp::create(configBuilder, trace.getLoc(),
303 builder.getStringAttr("Trace_Control0"),
304 builder.getStringAttr("Trace_Stop_Event"),
305 builder.getI32IntegerAttr(stopEvent),
306 /*mask=*/nullptr,
307 builder.getStringAttr("stop event"));
308 }
309
310 // Emit mode if present.
311 // Only core traces expose Trace_Control0.Mode in the register DB.
312 // Memory, memory_tile, and shim modules do not have the Mode field.
313 bool isCore = (packetType == TracePacketType::Core);
314 if (auto modeOp = dyn_cast<TraceModeOp>(op); modeOp && isCore) {
315 TraceRegOp::create(configBuilder, trace.getLoc(),
316 builder.getStringAttr("Trace_Control0"),
317 builder.getStringAttr("Mode"),
318 builder.getI32IntegerAttr(
319 static_cast<uint32_t>(modeOp.getMode())),
320 /*mask=*/nullptr,
321 builder.getStringAttr("trace mode"));
322 }
323
324 // Emit packet config if present. -aie-insert-trace-flows runs
325 // before this pass and materializes the id, so getId() is set
326 // here.
327 if (auto packetOp = dyn_cast<TracePacketOp>(op)) {
328 auto packetId = packetOp.getId();
329 assert(packetId.has_value() &&
330 "TracePacketOp id should be assigned by "
331 "-aie-insert-trace-flows");
332 TraceRegOp::create(
333 configBuilder, trace.getLoc(),
334 builder.getStringAttr("Trace_Control1"),
335 builder.getStringAttr("ID"), builder.getI32IntegerAttr(*packetId),
336 /*mask=*/nullptr, builder.getStringAttr("packet ID"));
337
338 TraceRegOp::create(configBuilder, trace.getLoc(),
339 builder.getStringAttr("Trace_Control1"),
340 builder.getStringAttr("Packet_Type"),
341 builder.getI32IntegerAttr(
342 static_cast<uint32_t>(packetOp.getType())),
343 /*mask=*/nullptr,
344 builder.getStringAttr("packet type"));
345 }
346 }
347
348 // 2. Emit port configurations (Stream_Switch_Event_Port_Selection_0/1)
349 for (auto &op : trace.getBody().getOps()) {
350 if (auto portOp = dyn_cast<TracePortOp>(op)) {
351 uint32_t slot = portOp.getSlot();
352
353 // Determine which register based on slot
354 StringRef registerName = (slot < 4)
355 ? "Stream_Switch_Event_Port_Selection_0"
356 : "Stream_Switch_Event_Port_Selection_1";
357
358 // Generate field names
359 std::string idFieldName = "Port_" + std::to_string(slot) + "_ID";
360 std::string masterSlaveFieldName =
361 "Port_" + std::to_string(slot) + "_Master_Slave";
362
363 // Generate port value string "PORT:CHANNEL"
364 std::string portValue = stringifyWireBundle(portOp.getPort()).str() +
365 ":" + std::to_string(portOp.getChannel());
366
367 // Convert DMAChannelDir to master flag: S2MM=master(1), MM2S=slave(0)
368 int masterSlaveValue =
369 (portOp.getDirection() == DMAChannelDir::S2MM) ? 1 : 0;
370
371 // Emit Port_N_ID field
372 TraceRegOp::create(
373 configBuilder, portOp.getLoc(),
374 builder.getStringAttr(registerName),
375 builder.getStringAttr(idFieldName),
376 builder.getStringAttr(portValue), // "NORTH:1" format
377 /*mask=*/nullptr,
378 builder.getStringAttr("port " + std::to_string(slot) + " ID"));
379
380 // Emit Port_N_Master_Slave field
381 TraceRegOp::create(configBuilder, portOp.getLoc(),
382 builder.getStringAttr(registerName),
383 builder.getStringAttr(masterSlaveFieldName),
384 builder.getI32IntegerAttr(masterSlaveValue),
385 /*mask=*/nullptr,
386 builder.getStringAttr("port " +
387 std::to_string(slot) +
388 " master/slave"));
389 }
390 }
391
392 // 3. Emit event slots (Trace_Event0 / Trace_Event1)
393 SmallVector<TraceEventOp> events;
394 for (auto &op : trace.getBody().getOps()) {
395 if (auto eventOp = dyn_cast<TraceEventOp>(op)) {
396 events.push_back(eventOp);
397 }
398 }
399
400 for (size_t i = 0; i < events.size() && i < 8; ++i) {
401 std::string eventName = events[i].getEvent().getEventName();
402
403 // If enum, use enum value directly; otherwise lookup by name
404 std::optional<uint32_t> eventNum;
405
406 if (auto enumVal = events[i].getEvent().getEnumValue()) {
407 eventNum = static_cast<uint32_t>(*enumVal);
408 } else {
409 eventNum = targetModel.lookupEvent(eventName, tileID, isMem);
410 }
411
412 if (!eventNum) {
413 events[i].emitError("unknown trace event '") << eventName << "'";
414 return signalPassFailure();
415 }
416
417 // Determine which register and field
418 StringRef registerName = (i < 4) ? "Trace_Event0" : "Trace_Event1";
419 std::string fieldName = "Trace_Event" + std::to_string(i);
420
421 // Emit register write with event number as integer
422 TraceRegOp::create(
423 configBuilder, trace.getLoc(), builder.getStringAttr(registerName),
424 builder.getStringAttr(fieldName),
425 events[i].getEvent(), // builder.getI32IntegerAttr(*eventNum),
426 /*mask=*/nullptr, builder.getStringAttr(eventName));
427 }
428
429 // Add terminator
430 EndOp::create(configBuilder, trace.getLoc());
431
432 // Update all trace.start_config references
433 device.walk([&](TraceStartConfigOp startConfig) {
434 if (startConfig.getTraceConfig() == trace.getSymName()) {
435 startConfig.setTraceConfigAttr(
436 SymbolRefAttr::get(builder.getContext(), configName));
437 }
438 });
439
440 // Remove original trace op
441 trace.erase();
442 }
443 }
444};
445
446} // namespace
447
448std::unique_ptr<OperationPass<DeviceOp>>
450 return std::make_unique<AIETraceToConfigPass>();
451}
452
453//===----------------------------------------------------------------------===//
454// AIETraceRegPackWritesPass - Pack multiple register field writes
455//===----------------------------------------------------------------------===//
456
457namespace {
458
459struct AIETraceRegPackWritesPass
460 : xilinx::AIE::impl::AIETraceRegPackWritesBase<AIETraceRegPackWritesPass> {
461 void runOnOperation() override {
462 DeviceOp device = getOperation();
463 const auto &targetModel = device.getTargetModel();
464
465 // Process each trace config
466 device.walk([&](TraceConfigOp configOp) {
467 // Determine module based on tile type and packet type
468 auto tile = cast<TileOp>(configOp.getTile().getDefiningOp());
469
470 // Get packet type to determine if this is memory or core trace
471 bool isMem = false;
472 if (auto packetType = configOp.getPacketType()) {
473 isMem = (*packetType == TracePacketType::Mem);
474 }
475
476 // Phase 1: Convert field+value to mask+shifted_value
477 SmallVector<TraceRegOp> regsToConvert;
478 for (auto &op : configOp.getBody().front()) {
479 if (auto regOp = dyn_cast<TraceRegOp>(op)) {
480 if (regOp.getField() && !regOp.getMask()) {
481 regsToConvert.push_back(regOp);
482 }
483 }
484 }
485
486 OpBuilder builder(&configOp.getBody().front(),
487 configOp.getBody().front().begin());
488
489 TileID tileID = tile.getTileID();
490 for (auto regOp : regsToConvert) {
491 // Look up register and field information
492 const RegisterInfo *regInfo =
493 targetModel.lookupRegister(regOp.getRegName(), tileID, isMem);
494
495 if (!regInfo) {
496 regOp.emitError("Register not found in database: ")
497 << regOp.getRegName();
498 return signalPassFailure();
499 }
500
501 const BitFieldInfo *fieldInfo = regInfo->getField(*regOp.getField());
502 if (!fieldInfo) {
503 regOp.emitError("Field not found in register: ")
504 << *regOp.getField() << " in " << regOp.getRegName();
505 return signalPassFailure();
506 }
507
508 // Get the value - handle both integers and port strings
509 uint32_t value = 0;
510 Attribute valAttr = regOp.getValue();
511 if (auto traceEventAttr = dyn_cast<TraceEventAttr>(valAttr)) {
512 if (auto enumVal = traceEventAttr.getEnumValue()) {
513 value = static_cast<uint32_t>(*enumVal);
514 valAttr = builder.getI32IntegerAttr(value);
515 } else {
516 std::string eventName = traceEventAttr.getEventName();
517 std::optional<uint32_t> eventNum =
518 targetModel.lookupEvent(eventName, tileID, isMem);
519 if (!eventNum) {
520 regOp.emitError("unknown trace event '") << eventName << "'";
521 return signalPassFailure();
522 }
523 value = *eventNum;
524 valAttr = builder.getI32IntegerAttr(value);
525 }
526 }
527 if (auto intAttr = dyn_cast<IntegerAttr>(valAttr)) {
528 // Integer value
529 value = intAttr.getInt();
530 } else if (auto strAttr = dyn_cast<StringAttr>(valAttr)) {
531 // String value - check if it's a port specification
532 StringRef valueStr = strAttr.getValue();
533
534 // Determine master/slave from field name
535 // If field name contains "Master_Slave", this is not a port ID field
536 // Port ID fields are named "Port_N_ID"
537 bool isMasterSlaveField =
538 fieldInfo->name.find("Master_Slave") != std::string::npos;
539
540 if (!isMasterSlaveField && valueStr.contains(':')) {
541 // This looks like "PORT:CHANNEL" format
542 // We need master/slave info - look for corresponding Master_Slave
543 // field
544 bool master = false; // Default to slave
545
546 // Derive the port slot prefix from the current field name, e.g.:
547 // "Port_1_ID" -> "Port_1"
548 StringRef fieldName(fieldInfo->name);
549 StringRef portSlotPrefix;
550 if (fieldName.starts_with("Port_")) {
551 size_t idSuffixPos = fieldName.find("_ID");
552 if (idSuffixPos != std::string::npos)
553 portSlotPrefix = fieldName.take_front(idSuffixPos);
554 }
555 // Search for companion Master_Slave field in same register and,
556 // when possible, for the same port slot.
557 for (auto &siblingOp : configOp.getBody().front()) {
558 if (auto siblingReg = dyn_cast<TraceRegOp>(siblingOp)) {
559 if (siblingReg.getRegName() != regOp.getRegName() ||
560 !siblingReg.getField())
561 continue;
562 StringRef siblingFieldName = *siblingReg.getField();
563 // If we could determine a slot prefix (e.g. "Port_1"), require
564 // the sibling to match that slot and contain "Master_Slave".
565 if (!portSlotPrefix.empty()) {
566 if (!siblingFieldName.starts_with(portSlotPrefix) ||
567 !siblingFieldName.contains("Master_Slave"))
568 continue;
569 } else {
570 // Fallback: match any Master_Slave field in this register.
571 if (!siblingFieldName.contains("Master_Slave"))
572 continue;
573 }
574 // Found companion field - extract master flag
575 if (auto siblingInt =
576 dyn_cast<IntegerAttr>(siblingReg.getValue())) {
577 master = (siblingInt.getInt() != 0);
578 }
579 break;
580 }
581 }
582
583 // Resolve port value
584 auto portIndex =
585 targetModel.resolvePortValue(valueStr, tileID, master);
586 if (!portIndex) {
587 regOp.emitError("Failed to resolve port value: ") << valueStr;
588 return signalPassFailure();
589 }
590 value = *portIndex;
591 } else {
592 regOp.emitError("Unsupported string value: ") << valueStr;
593 return signalPassFailure();
594 }
595 } else {
596 regOp.emitError("Unsupported value type in pack pass");
597 return signalPassFailure();
598 }
599
600 // Compute mask and shifted value
601 auto mask = targetModel.getFieldMask(*fieldInfo);
602 if (!mask) {
603 regOp.emitError("Invalid field bit range for register write: ")
604 << regOp.getRegName() << "." << fieldInfo->name << " ["
605 << fieldInfo->bit_start << ":" << fieldInfo->bit_end
606 << "], width=" << fieldInfo->getWidth();
607 return signalPassFailure();
608 }
609 auto shiftedValue = targetModel.encodeFieldValue(*fieldInfo, value);
610 if (!shiftedValue) {
611 regOp.emitError("Value ")
612 << value << " does not fit in field " << regOp.getRegName() << "."
613 << fieldInfo->name << " (width " << fieldInfo->getWidth() << ")";
614 return signalPassFailure();
615 }
616 // Create new operation with mask
617 builder.setInsertionPoint(regOp);
618 TraceRegOp::create(builder, regOp.getLoc(), regOp.getRegNameAttr(),
619 nullptr, // no field
620 builder.getI32IntegerAttr(*shiftedValue),
621 builder.getI32IntegerAttr(*mask),
622 regOp.getCommentAttr());
623
624 // Remove old operation
625 regOp.erase();
626 }
627
628 // Phase 2: Merge writes to same register with non-overlapping masks
629 bool changed = true;
630 while (changed) {
631 changed = false;
632
633 // Collect all register writes
634 SmallVector<TraceRegOp> regWrites;
635 for (TraceRegOp op : configOp.getBody().front().getOps<TraceRegOp>()) {
636 if (op.getMask()) {
637 regWrites.push_back(op);
638 }
639 }
640
641 // Try to merge pairs
642 for (size_t i = 0; i < regWrites.size() && !changed; ++i) {
643 for (size_t j = i + 1; j < regWrites.size() && !changed; ++j) {
644 TraceRegOp reg1 = regWrites[i];
645 TraceRegOp reg2 = regWrites[j];
646
647 // Must be same register
648 if (reg1.getRegName() != reg2.getRegName())
649 continue;
650
651 auto mask1Attr = reg1.getMask();
652 auto mask2Attr = reg2.getMask();
653 if (!mask1Attr || !mask2Attr)
654 continue;
655
656 uint32_t mask1 = *mask1Attr;
657 uint32_t mask2 = *mask2Attr;
658
659 // Check for overlap
660 if (mask1 & mask2) {
661 reg1.emitError("Overlapping masks for register ")
662 << reg1.getRegName() << ": mask1=" << mask1
663 << " mask2=" << mask2;
664 return signalPassFailure();
665 }
666
667 // Merge the two writes
668 auto value1Attr = dyn_cast<IntegerAttr>(reg1.getValue());
669 auto value2Attr = dyn_cast<IntegerAttr>(reg2.getValue());
670 if (!value1Attr || !value2Attr) {
671 reg1.emitError(
672 "Expected integer values for packed register writes");
673 return signalPassFailure();
674 }
675 uint32_t value1 = value1Attr.getInt();
676 uint32_t value2 = value2Attr.getInt();
677 uint32_t mergedValue = value1 | value2;
678 uint32_t mergedMask = mask1 | mask2;
679
680 // Create merged operation
681 builder.setInsertionPoint(reg1);
682 std::string comment;
683 if (reg1.getComment())
684 comment += reg1.getComment()->str();
685 if (reg2.getComment()) {
686 if (!comment.empty())
687 comment += " + ";
688 comment += reg2.getComment()->str();
689 }
690
691 TraceRegOp::create(
692 builder, reg1.getLoc(), reg1.getRegNameAttr(), nullptr,
693 builder.getI32IntegerAttr(mergedValue),
694 builder.getI32IntegerAttr(mergedMask),
695 comment.empty() ? nullptr : builder.getStringAttr(comment));
696
697 // Remove both old operations
698 reg1.erase();
699 reg2.erase();
700 changed = true;
701 }
702 }
703 }
704 });
705 }
706};
707
708} // namespace
709
710std::unique_ptr<OperationPass<DeviceOp>>
712 return std::make_unique<AIETraceRegPackWritesPass>();
713}
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIETraceRegPackWritesPass()
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIETraceToConfigPass()
const BitFieldInfo * getField(llvm::StringRef fieldName) const