Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
82c4a77
Enforce single-read-port constraint in mapping algorithm
guosran Mar 18, 2026
4afbfdd
Enforce register-file intra-index constraint for mov vs compute
guosran Mar 19, 2026
dda69d8
feat: Implement register file occupancy tracking in MappingState, add…
guosran Mar 19, 2026
d26c4cc
Replaced autos with explicit data types
guosran Mar 19, 2026
cc551ee
feat: Enhance register occupancy tracking and add CGRA placement util…
guosran Mar 21, 2026
d6ee173
Replace RegClusterOccupyStatus with Operation* for register occupancy…
guosran Mar 22, 2026
4f64662
Fix shell command error messages in MLIR fusion test cases
guosran Mar 23, 2026
02d974f
Split reg_file map into read/write; allow same-register sharing; upda…
guosran Mar 23, 2026
af2cc0a
Implement read/write separation for register occupancy tracking
guosran Mar 24, 2026
b18095e
Merge remote-tracking branch 'origin/main' into feature/register-bypa…
guosran Mar 24, 2026
cba04b5
Resolved conflicts and implemented feedbacks
guosran Mar 24, 2026
6fb1032
Update mapping utilities and state, refresh test files.
guosran Mar 24, 2026
dac5ca8
Updated tests
guosran Mar 24, 2026
55cd94b
Refactor code for improved readability and consistency in MappingStat…
guosran Mar 25, 2026
7c0d4ef
Refactor mapping_util.cpp for improved type clarity and consistency
guosran Mar 25, 2026
d288203
Refactor MLIR test cases and add script for updating CHECK lines
guosran Mar 25, 2026
228105e
Refactor MappingState and mapping_util for improved clarity and consi…
guosran Mar 25, 2026
82cf52f
Enhance function signatures in MappingState and mapping_util for impr…
guosran Mar 25, 2026
103f489
Delete update_test_checks.py
guosran Mar 25, 2026
411f39f
Remove accidentally committed = file
guosran Mar 25, 2026
d9f4461
Update Zeonica_Testbench submodule to main branch
guosran Mar 25, 2026
2ccd34e
Sync Zeonica_Testbench submodule to match main branch (1e98cf0)
guosran Mar 25, 2026
1f90f12
Merge remote-tracking branch 'origin/main' into feature/register-bypa…
guosran Mar 25, 2026
872e387
Resolved merge conflicts
guosran Mar 25, 2026
6676bab
Fix formatting in comments for clarity in MappingState.cpp and fir_ke…
guosran Mar 26, 2026
048e313
Update tests
guosran Mar 26, 2026
76182e4
Edit variable names
guosran Mar 26, 2026
8510547
Rename `op` parameter to `move_op`
guosran Mar 27, 2026
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
163 changes: 157 additions & 6 deletions lib/NeuraDialect/Mapping/MappingState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,35 +116,186 @@ void MappingState::unbindOp(Operation *op) {
}

bool MappingState::isAvailableAcrossTime(const MappingLoc &loc) const {
// For spatial mapping, checks if the location is available across all time.
// For spatial mapping, checks if the location stays available at all time steps.
if (this->is_spatial_only) {
for (int t = 0; t < II * kMaxSteps; ++t) {
MappingLoc check_loc = {loc.resource, t};
auto it = occupied_locs.find(check_loc);
if (it != occupied_locs.end()) {
// Check if all existing occupy statuses allow new single-cycle op
// Rejects the location if any non-shareable occupancy is present.
for (const auto &entry : it->second) {
if (entry.first != IN_PIPE_OCCUPY) {
return false;
}
}
}
}

// Enforces identical intra-index when a mov read and a compute read share a register file.
if (loc.resource->getKind() == ResourceKind::Register) {
Register *reg = static_cast<Register *>(loc.resource);
RegisterFile *reg_file = reg->getRegisterFile();
if (reg_file) {
auto isMovOp = [](Operation *op) {
// Identifies bypass routing ops by name to avoid relying on generated op classes.
if (!op) {
return false;
}
auto name = op->getName().getStringRef();
return name == "neura.data_mov" || name == "neura.ctrl_mov";
};

auto violatesClusterReadConstraintAt = [&](int t) -> bool {
// Tracks whether sibling registers in the file are used by mov or compute in this slot.
bool otherRegHasMov = false;
bool otherRegHasCompute = false;

for (const auto &[_, sibling] : reg_file->getRegisters()) {
if (sibling == reg) {
continue;
}
auto itOcc = occupied_locs.find({sibling, t});
if (itOcc == occupied_locs.end()) {
continue;
}

for (const auto &entry : itOcc->second) {
Operation *occOp = entry.second;
if (isMovOp(occOp)) {
otherRegHasMov = true;
} else {
otherRegHasCompute = true;
}

// Rejects the slot once mov and compute are found on different sibling registers.
if (otherRegHasMov && otherRegHasCompute) {
return true;
}
}
}

auto itThis = occupied_locs.find({reg, t});
bool thisRegHasMov = false;
bool thisRegHasCompute = false;
if (itThis != occupied_locs.end()) {
for (const auto &entry : itThis->second) {
Operation *occOp = entry.second;
if (isMovOp(occOp)) {
thisRegHasMov = true;
} else {
thisRegHasCompute = true;
}
}
}

// Rejects the slot when this register mixes with a sibling of the opposite kind.
if (otherRegHasCompute && thisRegHasMov) {
return true;
}
if (otherRegHasMov && thisRegHasCompute) {
return true;
}

return false;
};

for (int t = 0; t < II * kMaxSteps; ++t) {
if (violatesClusterReadConstraintAt(t)) {
return false;
}
}
}
}

return true;
} else {
// Checks the availability across time domain.
// Checks the availability across the modulo-II time domain.
for (int t = loc.time_step % II; t < II * kMaxSteps; t += II) {
MappingLoc check_loc = {loc.resource, t};
auto it = occupied_locs.find(check_loc);
if (it != occupied_locs.end()) {
// Check if all existing occupy statuses allow new single-cycle op
// Rejects the location if any non-shareable occupancy is present.
for (const auto &entry : it->second) {
if (entry.first != IN_PIPE_OCCUPY) {
return false;
}
}
}
}

// Enforces identical intra-index when a mov read and a compute read share a register file.
if (loc.resource->getKind() == ResourceKind::Register) {
Register *reg = static_cast<Register *>(loc.resource);
RegisterFile *reg_file = reg->getRegisterFile();
if (reg_file) {
auto isMovOp = [](Operation *op) {
// Identifies bypass routing ops by name to avoid relying on generated op classes.
if (!op) {
return false;
}
auto name = op->getName().getStringRef();
return name == "neura.data_mov" || name == "neura.ctrl_mov";
};

auto violatesClusterReadConstraintAt = [&](int t) -> bool {
// Tracks whether sibling registers in the file are used by mov or compute in this slot.
bool otherRegHasMov = false;
bool otherRegHasCompute = false;

for (const auto &[_, sibling] : reg_file->getRegisters()) {
if (sibling == reg) {
continue;
}
auto itOcc = occupied_locs.find({sibling, t});
if (itOcc == occupied_locs.end()) {
continue;
}

for (const auto &entry : itOcc->second) {
Operation *occOp = entry.second;
if (isMovOp(occOp)) {
otherRegHasMov = true;
} else {
otherRegHasCompute = true;
}
if (otherRegHasMov && otherRegHasCompute) {
return true;
}
}
}

auto itThis = occupied_locs.find({reg, t});
bool thisRegHasMov = false;
bool thisRegHasCompute = false;
if (itThis != occupied_locs.end()) {
for (const auto &entry : itThis->second) {
Operation *occOp = entry.second;
if (isMovOp(occOp)) {
thisRegHasMov = true;
} else {
thisRegHasCompute = true;
}
}
}

// Rejects the slot when this register mixes with a sibling of the opposite kind.
if (otherRegHasCompute && thisRegHasMov) {
return true;
}
if (otherRegHasMov && thisRegHasCompute) {
return true;
}
return false;
};

for (int t = loc.time_step % II; t < II * kMaxSteps; t += II) {
if (violatesClusterReadConstraintAt(t)) {
return false;
}
}
}
}

return true;
}
}
Expand All @@ -159,11 +310,11 @@ bool MappingState::isAvailableForOccupyStatus(const MappingLoc &loc,
return true;
}

// Check against all existing entries at this location
// Checks against all existing entries at this location
for (const auto &entry : it->second) {
int existing_status = entry.first;

// Implement the pipeline-aware availability rules:
// Implements the pipeline-aware availability rules:
// - SINGLE_OCCUPY (0): exclusive, no other op can share
// - START_PIPE_OCCUPY (1): cannot coexist with SINGLE or another START
// - END_PIPE_OCCUPY (2): cannot coexist with SINGLE or another END
Expand Down
10 changes: 4 additions & 6 deletions lib/NeuraDialect/Mapping/mapping_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,14 +551,12 @@ bool mlir::neura::tryRouteBackwardMove(Operation *mov_op, MappingLoc src_loc,
Register *mlir::neura::getAvailableRegister(const MappingState &state,
Tile *tile, int start_time,
int exclusive_end_time) {
// The single-read-port constraint (only one register per RegisterFile may be
// used at a time) is now enforced inside isAvailableAcrossTime(), so we only
// need to find the first register that is free across the requested range.
for (Register *reg : tile->getRegisters()) {
// FIXME: We may need constrain the register availability to the conflicting
// input channel (either the input channel or a register file on the same
// input direction could be active at one time).
if (state.isAvailableAcrossTimeInRange(reg, start_time,
exclusive_end_time)) {
if (state.isAvailableAcrossTimeInRange(reg, start_time, exclusive_end_time))
return reg;
}
}
return nullptr;
}
Expand Down
Loading