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
26 changes: 26 additions & 0 deletions include/NeuraDialect/mapping/mapping_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "mlir/IR/Operation.h"

namespace mlir {
namespace neura {

// Represents a recurrence cycle rooted at a reserve operation and closed by ctrl_mov.
struct RecurrenceCycle {
SmallVector<Operation *> operations; // Ordered list of operations in the cycle.
int length = 0; // Number of operations excluding reserve/ctrl_mov.
};

// Accelerator configuration struct.
struct AcceleratorConfig {
int num_tiles = 4; // Default to 4 tiles if unspecified.
};

// Collects recurrence cycles rooted at reserve and closed by ctrl_mov.
SmallVector<RecurrenceCycle, 4> collectRecurrenceCycles(Operation *func_op);

// Calculates ResMII: ceil(#ops / #tiles).
int calculateResMii(Operation *func_op, const AcceleratorConfig &config);

} // namespace neura
} // namespace mlir
1 change: 1 addition & 0 deletions lib/NeuraDialect/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_mlir_library(
TransformCtrlToDataFlowPass.cpp
LeveragePredicatedValuePass.cpp
MapToAcceleratorPass.cpp
mapping/mapping_util.cpp

DEPENDS
MLIRNeuraTransformsIncGen
Expand Down
89 changes: 11 additions & 78 deletions lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,20 @@
#include "NeuraDialect/NeuraOps.h"
#include "NeuraDialect/NeuraTypes.h"
#include "NeuraDialect/NeuraPasses.h"
#include "NeuraDialect/mapping/mapping_util.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"

using namespace mlir;
using namespace mlir::neura;

#define GEN_PASS_DEF_MapToAccelerator
#include "NeuraDialect/NeuraPasses.h.inc"

namespace {

/// Represents a recurrence cycle rooted at a reserve operation and ending at a ctrl_mov.
/// The cycle consists of a sequence of operations and its corresponding length.
struct RecurrenceCycle {
SmallVector<Operation *> operations; // Ordered list of operations in the cycle.
int length = 0; // Number of operations excluding ctrl_mov and reserve_op.
};

// Traverses (backward) the operation graph starting from the given operation
// towards reserve_value.
void traverseAlongPath(Operation *op, Value reserve_value,
std::deque<Operation *> &current_path,
DenseSet<Operation *> &visited_in_path,
SmallVector<RecurrenceCycle, 4> &collected_paths) {
if (!op || visited_in_path.contains(op))
return;

visited_in_path.insert(op);
current_path.push_front(op);

for (Value operand : op->getOperands()) {
if (operand == reserve_value) {
Operation *res_op = reserve_value.getDefiningOp();
if (res_op) current_path.push_front(res_op);

constexpr int kNumExcludedOps = 2;
collected_paths.push_back(RecurrenceCycle{
operations: SmallVector<Operation *>(current_path.begin(), current_path.end()),
length: static_cast<int>(current_path.size()) - kNumExcludedOps
});

if (res_op) current_path.pop_front(); // Remove reserve before backtracking
continue;
}

if (Operation *def_op = operand.getDefiningOp()) {
traverseAlongPath(def_op, reserve_value, current_path, visited_in_path, collected_paths);
}
}

current_path.pop_front(); // Backtrack
visited_in_path.erase(op); // Unmark from path
}

/// Collects all recurrence cycles rooted at reserve operations and closed by ctrl_mov.
/// Each cycle contains the operation sequence and its corresponding length.
SmallVector<RecurrenceCycle, 4> collectRecurrenceCycles(Operation *root_op) {
SmallVector<RecurrenceCycle, 4> recurrence_cycles;

root_op->walk([&](neura::CtrlMovOp ctrl_mov_op) {
Value target = ctrl_mov_op.getTarget();
auto reserve_op = target.getDefiningOp<neura::ReserveOp>();
if (!reserve_op)
return;

Value reserve_value = reserve_op.getResult();
Value ctrl_mov_from = ctrl_mov_op.getValue();

Operation *parent_op = ctrl_mov_from.getDefiningOp();
if (!parent_op)
return;

std::deque<Operation *> current_path;
SmallVector<RecurrenceCycle, 4> collected_paths;
DenseSet<Operation *> visited_in_path;
traverseAlongPath(parent_op, reserve_value, current_path, visited_in_path, collected_paths);

for (auto &cycle : collected_paths) {
cycle.operations.push_back(ctrl_mov_op);
++cycle.length;
recurrence_cycles.push_back(std::move(cycle));
}
});

return recurrence_cycles;
}

struct MapToAcceleratorPass
: public PassWrapper<MapToAcceleratorPass, OperationPass<ModuleOp>> {
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(MapToAcceleratorPass)
Expand Down Expand Up @@ -127,10 +53,17 @@ struct MapToAcceleratorPass
<< longest->length << "):\n";
for (Operation *op : longest->operations)
op->print(llvm::errs()), llvm::errs() << "\n";
IntegerAttr mii_attr = IntegerAttr::get(
IntegerAttr rec_mii_attr = IntegerAttr::get(
IntegerType::get(func.getContext(), 32), longest->length);
func->setAttr("RecMII", mii_attr);
func->setAttr("RecMII", rec_mii_attr);
}

AcceleratorConfig config{/*numTiles=*/8}; // Example
int res_mii = calculateResMii(func, config);
IntegerAttr res_mii_attr = IntegerAttr::get(
IntegerType::get(func.getContext(), 32), res_mii);
func->setAttr("ResMII", res_mii_attr);

});
}
};
Expand Down
104 changes: 104 additions & 0 deletions lib/NeuraDialect/Transforms/mapping/mapping_util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include <deque>

#include "NeuraDialect/mapping/mapping_util.h"
#include "NeuraDialect/NeuraOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/Operation.h"

using namespace mlir;
using namespace mlir::neura;

namespace {

// Traverses (backward) the operation graph starting from the given operation
// towards reserve_value.
void traverseAlongPath(Operation *op, Value reserve_value,
std::deque<Operation *> &current_path,
DenseSet<Operation *> &visited_in_path,
SmallVector<RecurrenceCycle, 4> &collected_paths) {
if (!op || visited_in_path.contains(op))
return;

visited_in_path.insert(op);
current_path.push_front(op);

for (Value operand : op->getOperands()) {
if (operand == reserve_value) {
Operation *res_op = reserve_value.getDefiningOp();
if (res_op) current_path.push_front(res_op);

constexpr int kNumExcludedOps = 2;
collected_paths.push_back(RecurrenceCycle{
operations: SmallVector<Operation *>(current_path.begin(), current_path.end()),
length: static_cast<int>(current_path.size()) - kNumExcludedOps
});

if (res_op) current_path.pop_front();
continue;
}

if (Operation *def_op = operand.getDefiningOp()) {
traverseAlongPath(def_op, reserve_value, current_path, visited_in_path, collected_paths);
}
}

current_path.pop_front();
visited_in_path.erase(op);
}

} // namespace

SmallVector<RecurrenceCycle, 4> mlir::neura::collectRecurrenceCycles(Operation *func_op) {
SmallVector<RecurrenceCycle, 4> recurrence_cycles;

func_op->walk([&](neura::CtrlMovOp ctrl_mov_op) {
Value target = ctrl_mov_op.getTarget();
auto reserve_op = target.getDefiningOp<neura::ReserveOp>();
if (!reserve_op)
return;

Value reserve_value = reserve_op.getResult();
Value ctrl_mov_from = ctrl_mov_op.getValue();

Operation *parent_op = ctrl_mov_from.getDefiningOp();
if (!parent_op)
return;

std::deque<Operation *> current_path;
SmallVector<RecurrenceCycle, 4> collected_paths;
DenseSet<Operation *> visited_in_path;
traverseAlongPath(parent_op, reserve_value, current_path, visited_in_path, collected_paths);

for (auto &cycle : collected_paths) {
cycle.operations.push_back(ctrl_mov_op);
++cycle.length;
recurrence_cycles.push_back(std::move(cycle));
}
});

return recurrence_cycles;
}

int mlir::neura::calculateResMii(Operation *func_op, const AcceleratorConfig &config) {
int num_ops = 0;

// Count all "compute" operations (non-terminators, non-block ops).
func_op->walk([&](Operation *op) {
// Skips non-materialized ops.
if (isa<func::FuncOp>(op) ||
isa<neura::ConstantOp,
neura::CtrlMovOp,
neura::ReserveOp,
neura::ReturnOp>(op)) {
return;
}
++num_ops;
});

llvm::errs() << "[calculateResMii] Total operations: " << num_ops << "\n";

// Avoid divide-by-zero
int tiles = std::max(1, config.num_tiles);

return llvm::divideCeil(num_ops, tiles);
}
4 changes: 2 additions & 2 deletions test/neura/ctrl/branch_for.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// RUN: --leverage-predicated-value \
// RUN: --transform-ctrl-to-data-flow \
// RUN: --map-to-accelerator \
// RUN: | FileCheck %s -check-prefix=RECMII
// RUN: | FileCheck %s -check-prefix=MII

func.func @loop_test() -> f32 {
%n = llvm.mlir.constant(10 : i64) : i64
Expand Down Expand Up @@ -81,4 +81,4 @@ func.func @loop_test() -> f32 {
// CTRL2DATA-NEXT: "neura.return"(%18) : (!neura.data<f32, i1>) -> ()
// CTRL2DATA-NEXT: }

// RECMII: func.func @loop_test() -> f32 attributes {RecMII = 4 : i32, accelerator = "neura"}
// MII: func.func @loop_test() -> f32 attributes {RecMII = 4 : i32, ResMII = 2 : i32, accelerator = "neura"}