Skip to content
Open
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
112 changes: 65 additions & 47 deletions lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ struct MapToAcceleratorPass
MapToAcceleratorPass() = default;
MapToAcceleratorPass(const MapToAcceleratorPass &pass)
: PassWrapper<MapToAcceleratorPass, OperationPass<ModuleOp>>(pass) {}
Option<std::string> sortStrategy{
*this, "sort-strategy",
llvm::cl::desc("Strategy for sorting operations before mapping. "
"Options: topological, mixed (default)."),
llvm::cl::init("mixed")};
Option<std::string> mappingStrategy{
*this, "mapping-strategy",
llvm::cl::desc("Mapping strategy to use for mapping operations to the "
Expand All @@ -63,46 +68,47 @@ struct MapToAcceleratorPass
void runOnOperation() override {
ModuleOp module = getOperation();
std::unique_ptr<Mapping> mapping_strategy;
StringRef mapping_strategy_stringRef(mappingStrategy.getValue());
StringRef backtrack_config_stringRef(backtrackConfig.getValue());
StringRef mapping_mode_stringRef(mappingMode.getValue());
bool is_spatial_only = (mapping_mode_stringRef == "spatial-only");
if (is_spatial_only || mapping_mode_stringRef == "spatial-temporal" ||
mapping_mode_stringRef.empty()) {
if (mapping_mode_stringRef.empty()) {
mapping_mode_stringRef = "spatial-temporal";
StringRef mapping_strategy_string_ref(mappingStrategy.getValue());
StringRef backtrack_config_string_ref(backtrackConfig.getValue());
StringRef mapping_mode_string_ref(mappingMode.getValue());
StringRef sort_strategy_string_ref(sortStrategy.getValue());
bool is_spatial_only = (mapping_mode_string_ref == "spatial-only");
if (is_spatial_only || mapping_mode_string_ref == "spatial-temporal" ||
mapping_mode_string_ref.empty()) {
if (mapping_mode_string_ref.empty()) {
mapping_mode_string_ref = "spatial-temporal";
}
llvm::errs() << "[MapToAcceleratorPass] Using Mapping Mode: "
<< mapping_mode_stringRef << "\n";
<< mapping_mode_string_ref << "\n";
} else {
llvm::errs() << "[MapToAcceleratorPass] Unsupported mapping mode: "
<< mapping_mode_stringRef << "\n";
<< mapping_mode_string_ref << "\n";
return;
}

if (mapping_strategy_stringRef == "heuristic" ||
mapping_strategy_stringRef.empty()) {
mapping_strategy_stringRef = "heuristic";
if (mapping_strategy_string_ref == "heuristic" ||
mapping_strategy_string_ref.empty()) {
mapping_strategy_string_ref = "heuristic";

if (backtrack_config_stringRef == "simple") {
if (backtrack_config_string_ref == "simple") {
mapping_strategy = std::make_unique<HeuristicMapping>(1, 1);
} else if (backtrack_config_stringRef == "greedy") {
} else if (backtrack_config_string_ref == "greedy") {
mapping_strategy = std::make_unique<HeuristicMapping>(INT_MAX, 1);
} else if (backtrack_config_stringRef == "exhaustive") {
} else if (backtrack_config_string_ref == "exhaustive") {
mapping_strategy = std::make_unique<HeuristicMapping>(INT_MAX, INT_MAX);
} else if (backtrack_config_stringRef == "customized") {
} else if (backtrack_config_string_ref == "customized") {
mapping_strategy = std::make_unique<HeuristicMapping>(5, 3);
} else if (backtrack_config_stringRef.starts_with("customized=")) {
} else if (backtrack_config_string_ref.starts_with("customized=")) {
// Used for custom backtrack parameters.
// Example: "customized=5,3" means max_loc=5, max_depth=3
// Extracts the parameters after "customized=".
StringRef paramsRef =
backtrack_config_stringRef.substr(strlen("customized="));
size_t comma_pos = paramsRef.find(',');
StringRef params_ref =
backtrack_config_string_ref.substr(strlen("customized="));
size_t comma_pos = params_ref.find(',');

if (comma_pos != StringRef::npos) {
StringRef max_loc_str = paramsRef.substr(0, comma_pos);
StringRef max_depth_str = paramsRef.substr(comma_pos + 1);
StringRef max_loc_str = params_ref.substr(0, comma_pos);
StringRef max_depth_str = params_ref.substr(comma_pos + 1);

int max_loc, max_depth;
if (!max_loc_str.getAsInteger(10, max_loc) &&
Expand All @@ -116,19 +122,19 @@ struct MapToAcceleratorPass
} else {
llvm::errs() << "[MapToAcceleratorPass] Illegal customized "
"parameters format: "
<< backtrack_config_stringRef << "\n";
<< backtrack_config_string_ref << "\n";
return;
}
} else {
llvm::errs()
<< "[MapToAcceleratorPass] Illegal customized parameters format: "
<< backtrack_config_stringRef << "\n";
<< backtrack_config_string_ref << "\n";
return;
}
}
} else {
llvm::errs() << "[MapToAcceleratorPass] Unsupported mapping strategy: "
<< mapping_strategy_stringRef << "\n";
<< mapping_strategy_string_ref << "\n";
return;
}

Expand All @@ -147,10 +153,10 @@ struct MapToAcceleratorPass

// If steering mode, enforce spatial-only mapping.
if (is_steering_mode) {
if (mapping_mode_stringRef != "spatial-only") {
if (mapping_mode_string_ref != "spatial-only") {
func.emitError() << "Steering IR mode requires spatial-only mapping, "
<< "but got mapping mode: "
<< mapping_mode_stringRef;
<< mapping_mode_string_ref;
signalPassFailure();
return;
}
Expand Down Expand Up @@ -206,30 +212,41 @@ struct MapToAcceleratorPass
llvm::outs() << "[MapToAcceleratorPass] Topologically sorted op: "
<< *op << "\n";
}
std::vector<std::vector<Operation *>> level_buckets =
getOpsInAlapLevels(topologically_sorted_ops, critical_ops);
for (int level = 0; level < static_cast<int>(level_buckets.size());
++level) {
llvm::outs() << "[MapToAcceleratorPass] ALAP Bucket Level " << level
<< ": " << level_buckets[level].size() << " ops\n";
for (Operation *op : level_buckets[level]) {
llvm::outs() << " " << *op << "\n";

// Two sorting strategies: pure topological order, or mixed ALAP + topo.
std::vector<std::pair<Operation *, int>> sorted_ops_with_levels;
if (sort_strategy_string_ref == "topological") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please remind me what's wrong with "mixed"? You plan to fix "mixed" if no "due to time limit"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this case:

module {
  func.func @simple_add_loop() -> i64 attributes {accelerator = "neura", dataflow_mode = "steering"} {
    %0 = neura.reserve : i64
    %1 = neura.reserve : i64
    %2 = neura.reserve : i1
    %3 = "neura.constant"() <{value = 16 : i64}> : () -> i64
    %4 = "neura.constant"() <{value = 1 : i64}> : () -> i64
    %5 = "neura.constant"() <{value = 1 : i64}> : () -> i64
    %6 = "neura.constant"() <{value = 0 : i64}> : () -> i64
    %7 = neura.invariant %4, %2 : i64, i1 -> i64
    %8 = neura.invariant %3, %2 : i64, i1 -> i64
    %9 = neura.carry %5, %2, %0 : i64, i1, i64 -> i64
    %10 = neura.carry %6, %2, %1 : i64, i1, i64 -> i64
    %11 = "neura.icmp"(%10, %8) <{cmpType = "slt"}> : (i64, i64) -> i1
    neura.ctrl_mov %11 -> %2 : i1 i1
    %12 = neura.false_steer %9, %11 : i64, i1 -> i64
    %13 = "neura.add"(%9, %9) : (i64, i64) -> i64
    neura.ctrl_mov %13 -> %0 : i64 i64
    %14 = "neura.add"(%10, %7) : (i64, i64) -> i64
    neura.ctrl_mov %14 -> %1 : i64 i64
    "neura.return"(%12) : (i64) -> ()
  }
}

Its topological order is:

[MapToAcceleratorPass] Topologically sorted op: %0 = neura.reserve : i64
[MapToAcceleratorPass] Topologically sorted op: %1 = neura.reserve : i64
[MapToAcceleratorPass] Topologically sorted op: %2 = neura.reserve : i1
[MapToAcceleratorPass] Topologically sorted op: %3 = "neura.constant"() <{value = 16 : i64}> : () -> i64
[MapToAcceleratorPass] Topologically sorted op: %4 = "neura.constant"() <{value = 1 : i64}> : () -> i64
[MapToAcceleratorPass] Topologically sorted op: %5 = "neura.constant"() <{value = 1 : i64}> : () -> i64
[MapToAcceleratorPass] Topologically sorted op: %6 = "neura.constant"() <{value = 0 : i64}> : () -> i64
[MapToAcceleratorPass] Topologically sorted op: %9 = "neura.data_mov"(%3) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %7 = "neura.data_mov"(%4) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %11 = "neura.data_mov"(%5) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %13 = "neura.data_mov"(%6) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %10 = neura.invariant %9, %2 : i64, i1 -> i64
[MapToAcceleratorPass] Topologically sorted op: %8 = neura.invariant %7, %2 : i64, i1 -> i64
[MapToAcceleratorPass] Topologically sorted op: %12 = neura.carry %11, %2, %0 : i64, i1, i64 -> i64
[MapToAcceleratorPass] Topologically sorted op: %14 = neura.carry %13, %2, %1 : i64, i1, i64 -> i64
[MapToAcceleratorPass] Topologically sorted op: %16 = "neura.data_mov"(%10) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %25 = "neura.data_mov"(%8) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %21 = "neura.data_mov"(%12) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %22 = "neura.data_mov"(%12) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %18 = "neura.data_mov"(%12) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %24 = "neura.data_mov"(%14) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %15 = "neura.data_mov"(%14) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %23 = "neura.add"(%21, %22) : (i64, i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %26 = "neura.add"(%24, %25) : (i64, i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: %17 = "neura.icmp"(%15, %16) <{cmpType = "slt"}> : (i64, i64) -> i1
[MapToAcceleratorPass] Topologically sorted op: neura.ctrl_mov %23 -> %0 : i64 i64
[MapToAcceleratorPass] Topologically sorted op: neura.ctrl_mov %26 -> %1 : i64 i64
[MapToAcceleratorPass] Topologically sorted op: neura.ctrl_mov %17 -> %2 : i1 i1
[MapToAcceleratorPass] Topologically sorted op: %19 = "neura.data_mov"(%17) : (i1) -> i1
[MapToAcceleratorPass] Topologically sorted op: %20 = neura.false_steer %18, %19 : i64, i1 -> i64
[MapToAcceleratorPass] Topologically sorted op: %27 = "neura.data_mov"(%20) : (i64) -> i64
[MapToAcceleratorPass] Topologically sorted op: "neura.return"(%27) : (i64) -> ()

It's mixed sorted order is:

[MapToAcceleratorPass] ALAP Bucket Level 0: 6 ops
  %1 = neura.reserve : i64
  %2 = neura.reserve : i1
  %3 = "neura.constant"() <{value = 16 : i64}> : () -> i64
  %6 = "neura.constant"() <{value = 0 : i64}> : () -> i64
  %9 = "neura.data_mov"(%3) : (i64) -> i64
  %13 = "neura.data_mov"(%6) : (i64) -> i64
[MapToAcceleratorPass] ALAP Bucket Level 1: 8 ops
  %0 = neura.reserve : i64
  %5 = "neura.constant"() <{value = 1 : i64}> : () -> i64
  %11 = "neura.data_mov"(%5) : (i64) -> i64
  %10 = neura.invariant %9, %2 : i64, i1 -> i64
  %14 = neura.carry %13, %2, %1 : i64, i1, i64 -> i64
  %16 = "neura.data_mov"(%10) : (i64) -> i64
  %24 = "neura.data_mov"(%14) : (i64) -> i64
  %15 = "neura.data_mov"(%14) : (i64) -> i64
[MapToAcceleratorPass] ALAP Bucket Level 2: 9 ops
  %4 = "neura.constant"() <{value = 1 : i64}> : () -> i64
  %7 = "neura.data_mov"(%4) : (i64) -> i64
  %12 = neura.carry %11, %2, %0 : i64, i1, i64 -> i64
  %21 = "neura.data_mov"(%12) : (i64) -> i64
  %22 = "neura.data_mov"(%12) : (i64) -> i64
  %18 = "neura.data_mov"(%12) : (i64) -> i64
  %17 = "neura.icmp"(%15, %16) <{cmpType = "slt"}> : (i64, i64) -> i1
  neura.ctrl_mov %17 -> %2 : i1 i1
  %19 = "neura.data_mov"(%17) : (i1) -> i1
[MapToAcceleratorPass] ALAP Bucket Level 3: 6 ops
  %8 = neura.invariant %7, %2 : i64, i1 -> i64
  %25 = "neura.data_mov"(%8) : (i64) -> i64
  %23 = "neura.add"(%21, %22) : (i64, i64) -> i64
  neura.ctrl_mov %23 -> %0 : i64 i64
  %20 = neura.false_steer %18, %19 : i64, i1 -> i64
  %27 = "neura.data_mov"(%20) : (i64) -> i64
[MapToAcceleratorPass] ALAP Bucket Level 4: 3 ops
  %26 = "neura.add"(%24, %25) : (i64, i64) -> i64
  neura.ctrl_mov %26 -> %1 : i64 i64
  "neura.return"(%27) : (i64) -> ()

Here %8 = neura.invariant %7, %2 : i64, i1 -> i64 is a backward user of %17 = "neura.icmp"(%15, %16) <{cmpType = "slt"}> : (i64, i64) -> i1, but it's ALAP level is higher than %17. Making it unable to satisfy the producer-consumer dependency check in

assert(!user_locs.empty() && "No locations found for backward user");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is not correctly recognized as a critical op?

// Step 3: Overwrites critical ops with ASAP schedule: shortest path from
// source.
for (Operation *op : sorted_ops) {
if (!critical_ops.count(op)) {
continue;
}

File an issue and resolve it later?

for (Operation *op : topologically_sorted_ops) {
sorted_ops_with_levels.push_back({op, 0}); // Level 0 for all ops
}
} else if (sort_strategy_string_ref == "mixed") {
std::vector<std::vector<Operation *>> level_buckets =
getOpsInAlapLevels(topologically_sorted_ops, critical_ops);
for (int level = 0; level < static_cast<int>(level_buckets.size());
++level) {
llvm::outs() << "[MapToAcceleratorPass] ALAP Bucket Level " << level
<< ": " << level_buckets[level].size() << " ops\n";
for (Operation *op : level_buckets[level]) {
llvm::outs() << " " << *op << "\n";
}
}
sorted_ops_with_levels = flatten_level_buckets(level_buckets);
for (const auto &[op, level] : sorted_ops_with_levels) {
llvm::outs() << "[MapToAcceleratorPass] ALAP sorted op: " << *op
<< " (ALAP level: " << level << ")\n";
}
} else {
llvm::errs() << "[MapToAcceleratorPass] Unsupported sort strategy: "
<< sort_strategy_string_ref << "\n";
return;
}
std::vector<std::pair<Operation *, int>> sorted_ops_with_alap_levels =
flatten_level_buckets(level_buckets);
for (const auto &[op, level] : sorted_ops_with_alap_levels) {
llvm::outs() << "[MapToAcceleratorPass] ALAP sorted op: " << *op
<< " (ALAP level: " << level << ")\n";
}
// assert(false);
for (int ii = possibleMinII; ii <= maxII; ++ii) {
llvm::errs()
<< "[MapToAcceleratorPass] Start mapping with target II of " << ii
<< "\n";
// Creates a mapping state for the current II.
MappingState mapping_state(architecture, ii, is_spatial_only);
if (mapping_strategy->map(sorted_ops_with_alap_levels, critical_ops,
if (mapping_strategy->map(sorted_ops_with_levels, critical_ops,
architecture, mapping_state)) {
// success
llvm::errs() << "[MapToAcceleratorPass] Successfully mapped function "
Expand All @@ -247,10 +264,11 @@ struct MapToAcceleratorPass
NamedAttribute(StringAttr::get(ctx, "y_tiles"),
IntegerAttr::get(IntegerType::get(ctx, 32),
architecture.getHeight())),
NamedAttribute(StringAttr::get(ctx, "mapping_strategy"),
StringAttr::get(ctx, mapping_strategy_stringRef)),
NamedAttribute(
StringAttr::get(ctx, "mapping_strategy"),
StringAttr::get(ctx, mapping_strategy_string_ref)),
NamedAttribute(StringAttr::get(ctx, "mapping_mode"),
StringAttr::get(ctx, mapping_mode_stringRef)),
StringAttr::get(ctx, mapping_mode_string_ref)),
NamedAttribute(StringAttr::get(ctx, "compiled_ii"),
IntegerAttr::get(IntegerType::get(ctx, 32), ii)),
NamedAttribute(
Expand Down
Loading