Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/NeuraDialect/NeuraOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def Neura_FDivOp : Op<NeuraDialect, "fdiv"> {
Example:
%result = neura.fdiv %a, %b : f32
}];
let arguments = (ins AnyType:$lhs, AnyType:$rhs);
let arguments = (ins AnyType:$lhs, Optional<AnyType>:$rhs);
let results = (outs AnyType:$result);
let traits = [SameOperandsAndResultElementType];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ bool HeuristicMapping::mapWithBacktrack(
<< sorted_ops_with_levels.size() - materialized_ops.size()
<< " non-materialized operations, " << materialized_ops.size()
<< " operations require physical mapping." << "\n";

llvm::outs() << "[HeuristicMapping] Materialized operations list:\n";
for (size_t i = 0; i < materialized_ops.size(); ++i) {
llvm::outs() << i << " " << *materialized_ops[i].first
<< " (level: " << materialized_ops[i].second << ")\n";
}

// Stores the mapping state snapshots for backtracking.
std::vector<MappingStateSnapshot> snapshots;
Expand Down
144 changes: 135 additions & 9 deletions lib/NeuraDialect/Mapping/MappingState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
using namespace mlir;
using namespace mlir::neura;

// Constants for table formatting in dumpOpToLocs.
// Total column width including separators.
constexpr int kKeyMaxLen = 36;
// Actual cell content width (35).
constexpr int kCellWidth = kKeyMaxLen - 1;
// Threshold to distinguish single-digit from double-digit numbers.
constexpr int kTwoDigitThreshold = 10;
// Threshold to distinguish double-digit from triple-digit numbers.
constexpr int kThreeDigitThreshold = 100;
// Length of time slot header prefix "t%N=" for single-digit II (e.g., "t%3=").
constexpr int kHeaderPrefixLenSingleDigit = 5;
// Length of time slot header prefix "t%N=" for double-digit II (e.g., "t%10=").
constexpr int kHeaderPrefixLenDoubleDigit = 6;
// Number of digits for single-digit slot numbers.
constexpr int kSingleDigitLen = 1;
// Number of digits for double-digit slot numbers.
constexpr int kDoubleDigitLen = 2;

MappingState::MappingState(const Architecture &arch, int II,
bool is_spatial_only)
: II(II), is_spatial_only(is_spatial_only) {}
Expand Down Expand Up @@ -200,21 +218,129 @@ void MappingState::releaseRoute(Operation *op) {
}

void MappingState::dumpOpToLocs(llvm::raw_ostream &os) const {
os << "=== MappingState: op_to_locs ===\n";
os << "=== MappingState: Resource Allocation Table ===\n";

// Collects all tiles and time steps (modulo II).
std::set<int> tile_ids;
// Time slots range from 0 to II-1.
std::set<int> time_slots;
// Maps (tile_id, time_slot) to list of (operation, actual_time_step).
std::map<std::pair<int, int>, std::vector<std::pair<Operation*, int>>> tile_slot_to_ops;

for (const auto &[op, locs] : op_to_locs) {
os << " - " << op->getName();
if (auto name_attr = op->getAttrOfType<StringAttr>("sym_name")) {
os << " @" << name_attr;
}
os << "\n";

for (const MappingLoc &loc : locs) {
auto *res = loc.resource;
os << " -> " << res->getType() << "#" << res->getId()
<< " @t=" << loc.time_step << "\n";
// Only shows tiles in the table.
if (res->getType() == "tile") {
tile_ids.insert(res->getId());
// Computes modulo II.
int time_slot = loc.time_step % II;
time_slots.insert(time_slot);
tile_slot_to_ops[{res->getId(), time_slot}].push_back({op, loc.time_step});
}
}
}

if (tile_ids.empty() || time_slots.empty()) {
os << "No tile operations mapped.\n";
os << "=== End ===\n";
return;
}

os << "II = " << II << "\n";

// Prints header - time slots (0 to II-1) as columns.
os << "\nTile | ";
for (int slot : time_slots) {
os << "t%" << II << "=" << slot;
int padding = kKeyMaxLen - (II < kTwoDigitThreshold ? kHeaderPrefixLenSingleDigit : kHeaderPrefixLenDoubleDigit)
- (slot < kTwoDigitThreshold ? kSingleDigitLen : kDoubleDigitLen);
for (int i = 0; i < padding; ++i) os << " ";
os << " | ";
}
os << "\n";

// Prints separator line.
os << "---------+";
for (size_t i = 0; i < time_slots.size(); ++i) {
for (int j = 0; j < kKeyMaxLen + 1; ++j) os << "-";
os << "+";
}
os << "\n";

// Prints each tile as a row.
for (int tile_id : tile_ids) {
os << "Tile#" << tile_id;
if (tile_id < kTwoDigitThreshold) os << " ";
else if (tile_id < kThreeDigitThreshold) os << " ";
os << " | ";

for (int slot : time_slots) {
auto it = tile_slot_to_ops.find({tile_id, slot});
if (it != tile_slot_to_ops.end() && !it->second.empty()) {
// Multiple operations may exist in the same slot (from different iterations).
// Shows the first one.
Operation *op = it->second[0].first;
int actual_time = it->second[0].second;

// Builds operation string: %result = op_name(%operand1, %operand2, ...).
std::string op_str;
llvm::raw_string_ostream op_stream(op_str);
mlir::OpPrintingFlags flags;

// Prints result (if exists).
if (op->getNumResults() > 0) {
op->getResult(0).printAsOperand(op_stream, flags);
op_stream << " = ";
}

// Prints operation name (removes "neura." prefix).
std::string op_name = op->getName().getStringRef().str();
if (op_name.rfind("neura.", 0) == 0) {
op_name = op_name.substr(6);
}
op_stream << op_name;

// Prints operands.
if (op->getNumOperands() > 0) {
op_stream << "(";
for (unsigned i = 0; i < op->getNumOperands(); ++i) {
if (i > 0) op_stream << ", ";
op->getOperand(i).printAsOperand(op_stream, flags);
}
op_stream << ")";
}

// Adds time annotation if not in [0, II).
if (actual_time >= II) {
op_stream << " (t=" << actual_time << ")";
}

op_stream.flush();

// Truncates string if too long to fit in the cell.
if (op_str.length() > kCellWidth) {
op_str = op_str.substr(0, kCellWidth - 3) + "...";
}

// Pads to fixed width (kCellWidth chars).
os << op_str;
int padding = kCellWidth - op_str.length();
for (int i = 0; i < padding; ++i) os << " ";
} else {
// Renders empty cell.
for (int i = 0; i < kCellWidth; ++i) os << " ";
}
os << " | ";
}
os << "\n";
}

os << "\n=== Legend ===\n";
os << "- Table shows operations mapped to tiles (modulo II scheduling)\n";
os << "- Column headers: t%II=X means time slot X (t=X, X+II, X+2*II, ...)\n";
os << "- Operations with (t=Y) annotation are scheduled at actual time step Y\n";
os << "- Operations without annotation are scheduled at t=0 to t=" << (II-1) << "\n";
os << "=== End ===\n";
}

Expand Down
10 changes: 9 additions & 1 deletion lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,11 @@ struct MapToAcceleratorPass
"customized=max_loc,max_depth (default "
"max_loc=5, max_depth=3)"),
llvm::cl::init("customized")};
Option<bool> dumpMappingTable{
*this, "dump-mapping-table",
llvm::cl::desc(
"Dump the resource allocation table after mapping (default: true)"),
llvm::cl::init(true)};

void runOnOperation() override {
ModuleOp module = getOperation();
Expand Down Expand Up @@ -609,7 +614,10 @@ struct MapToAcceleratorPass
if (mapping_strategy->map(sorted_ops_with_alap_levels, critical_ops,
architecture, mapping_state)) {
// success
mapping_state.dumpOpToLocs(); // logs to stderr
if (dumpMappingTable) {
// logs to stderr
mapping_state.dumpOpToLocs();
}
mapping_state.encodeMappingState();

// Sets the mapping_info attribute on the function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ DEFINE_BINARY_OP_PATTERN(FAdd, FAddOp)
DEFINE_BINARY_OP_PATTERN(FSub, FSubOp)
// Generates: FuseFMulConstantPattern.
DEFINE_BINARY_OP_PATTERN(FMul, FMulOp)
// Generates: FuseFDivConstantPattern.
DEFINE_BINARY_OP_PATTERN(FDiv, FDivOp)
// Generates: FuseICmpConstantPattern.
// Note: ICmpOp has a cmp_type attribute that is automatically preserved.
DEFINE_BINARY_OP_PATTERN(ICmp, ICmpOp)
Expand Down Expand Up @@ -587,6 +589,7 @@ struct FoldConstantPass
patterns.add<FuseFAddConstantPattern>(&getContext());
patterns.add<FuseFSubConstantPattern>(&getContext());
patterns.add<FuseFMulConstantPattern>(&getContext());
patterns.add<FuseFDivConstantPattern>(&getContext());
patterns.add<FuseFMaxConstantPattern>(&getContext());
patterns.add<FuseFMinConstantPattern>(&getContext());

Expand Down
11 changes: 10 additions & 1 deletion test/c2llvm2mlir/simple_loop/test.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
// RUN: --insert-data-mov %t-kernel.mlir -o %t-kernel-neura.mlir
// RUN: FileCheck %s --check-prefix=CHECK-LLVM2NEURA < %t-kernel-neura.mlir

// Test with mapping table dump enabled
// RUN: mlir-neura-opt --assign-accelerator \
// RUN: --lower-llvm-to-neura \
// RUN: --promote-func-arg-to-const \
Expand All @@ -42,9 +43,17 @@
// RUN: --view-op-graph \
// RUN: --architecture-spec=../../arch_spec/architecture.yaml \
// RUN: --insert-data-mov \
// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized=5,3" %t-kernel.mlir -o %t-kernel-mapped.mlir
// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized=5,3 dump-mapping-table=true" %t-kernel.mlir -o %t-kernel-mapped.mlir 2>&1 | tee %t-kernel-mapping-output.txt
// RUN: FileCheck %s --check-prefix=CHECK-MAPPING-TABLE < %t-kernel-mapping-output.txt
// RUN: FileCheck %s --check-prefix=CHECK-LLVM2NEURA-MAP < %t-kernel-mapped.mlir

// Checks the resource allocation table output
// CHECK-MAPPING-TABLE: === MappingState: Resource Allocation Table ===
// CHECK-MAPPING-TABLE: II = 5
// CHECK-MAPPING-TABLE: Tile | t%{{[0-9]+}}={{[0-9]+}}
// CHECK-MAPPING-TABLE: ---------+
// CHECK-MAPPING-TABLE: Tile#{{[0-9]+}} |

// CHECK-LLVM2NEURA: accelerator = "neura"
// CHECK-LLVM2NEURA: dataflow_mode = "predicate"
// CHECK-LLVM2NEURA: neura.phi
Expand Down
Loading