@@ -116,35 +116,186 @@ void MappingState::unbindOp(Operation *op) {
116116}
117117
118118bool MappingState::isAvailableAcrossTime (const MappingLoc &loc) const {
119- // For spatial mapping, checks if the location is available across all time.
119+ // For spatial mapping, checks if the location stays available at all time steps .
120120 if (this ->is_spatial_only ) {
121121 for (int t = 0 ; t < II * kMaxSteps ; ++t) {
122122 MappingLoc check_loc = {loc.resource , t};
123123 auto it = occupied_locs.find (check_loc);
124124 if (it != occupied_locs.end ()) {
125- // Check if all existing occupy statuses allow new single-cycle op
125+ // Rejects the location if any non-shareable occupancy is present.
126126 for (const auto &entry : it->second ) {
127127 if (entry.first != IN_PIPE_OCCUPY) {
128128 return false ;
129129 }
130130 }
131131 }
132132 }
133+
134+ // Enforces identical intra-index when a mov read and a compute read share a register file.
135+ if (loc.resource ->getKind () == ResourceKind::Register) {
136+ Register *reg = static_cast <Register *>(loc.resource );
137+ RegisterFile *reg_file = reg->getRegisterFile ();
138+ if (reg_file) {
139+ auto isMovOp = [](Operation *op) {
140+ // Identifies bypass routing ops by name to avoid relying on generated op classes.
141+ if (!op) {
142+ return false ;
143+ }
144+ auto name = op->getName ().getStringRef ();
145+ return name == " neura.data_mov" || name == " neura.ctrl_mov" ;
146+ };
147+
148+ auto violatesClusterReadConstraintAt = [&](int t) -> bool {
149+ // Tracks whether sibling registers in the file are used by mov or compute in this slot.
150+ bool otherRegHasMov = false ;
151+ bool otherRegHasCompute = false ;
152+
153+ for (const auto &[_, sibling] : reg_file->getRegisters ()) {
154+ if (sibling == reg) {
155+ continue ;
156+ }
157+ auto itOcc = occupied_locs.find ({sibling, t});
158+ if (itOcc == occupied_locs.end ()) {
159+ continue ;
160+ }
161+
162+ for (const auto &entry : itOcc->second ) {
163+ Operation *occOp = entry.second ;
164+ if (isMovOp (occOp)) {
165+ otherRegHasMov = true ;
166+ } else {
167+ otherRegHasCompute = true ;
168+ }
169+
170+ // Rejects the slot once mov and compute are found on different sibling registers.
171+ if (otherRegHasMov && otherRegHasCompute) {
172+ return true ;
173+ }
174+ }
175+ }
176+
177+ auto itThis = occupied_locs.find ({reg, t});
178+ bool thisRegHasMov = false ;
179+ bool thisRegHasCompute = false ;
180+ if (itThis != occupied_locs.end ()) {
181+ for (const auto &entry : itThis->second ) {
182+ Operation *occOp = entry.second ;
183+ if (isMovOp (occOp)) {
184+ thisRegHasMov = true ;
185+ } else {
186+ thisRegHasCompute = true ;
187+ }
188+ }
189+ }
190+
191+ // Rejects the slot when this register mixes with a sibling of the opposite kind.
192+ if (otherRegHasCompute && thisRegHasMov) {
193+ return true ;
194+ }
195+ if (otherRegHasMov && thisRegHasCompute) {
196+ return true ;
197+ }
198+
199+ return false ;
200+ };
201+
202+ for (int t = 0 ; t < II * kMaxSteps ; ++t) {
203+ if (violatesClusterReadConstraintAt (t)) {
204+ return false ;
205+ }
206+ }
207+ }
208+ }
209+
133210 return true ;
134211 } else {
135- // Checks the availability across time domain.
212+ // Checks the availability across the modulo-II time domain.
136213 for (int t = loc.time_step % II; t < II * kMaxSteps ; t += II) {
137214 MappingLoc check_loc = {loc.resource , t};
138215 auto it = occupied_locs.find (check_loc);
139216 if (it != occupied_locs.end ()) {
140- // Check if all existing occupy statuses allow new single-cycle op
217+ // Rejects the location if any non-shareable occupancy is present.
141218 for (const auto &entry : it->second ) {
142219 if (entry.first != IN_PIPE_OCCUPY) {
143220 return false ;
144221 }
145222 }
146223 }
147224 }
225+
226+ // Enforces identical intra-index when a mov read and a compute read share a register file.
227+ if (loc.resource ->getKind () == ResourceKind::Register) {
228+ Register *reg = static_cast <Register *>(loc.resource );
229+ RegisterFile *reg_file = reg->getRegisterFile ();
230+ if (reg_file) {
231+ auto isMovOp = [](Operation *op) {
232+ // Identifies bypass routing ops by name to avoid relying on generated op classes.
233+ if (!op) {
234+ return false ;
235+ }
236+ auto name = op->getName ().getStringRef ();
237+ return name == " neura.data_mov" || name == " neura.ctrl_mov" ;
238+ };
239+
240+ auto violatesClusterReadConstraintAt = [&](int t) -> bool {
241+ // Tracks whether sibling registers in the file are used by mov or compute in this slot.
242+ bool otherRegHasMov = false ;
243+ bool otherRegHasCompute = false ;
244+
245+ for (const auto &[_, sibling] : reg_file->getRegisters ()) {
246+ if (sibling == reg) {
247+ continue ;
248+ }
249+ auto itOcc = occupied_locs.find ({sibling, t});
250+ if (itOcc == occupied_locs.end ()) {
251+ continue ;
252+ }
253+
254+ for (const auto &entry : itOcc->second ) {
255+ Operation *occOp = entry.second ;
256+ if (isMovOp (occOp)) {
257+ otherRegHasMov = true ;
258+ } else {
259+ otherRegHasCompute = true ;
260+ }
261+ if (otherRegHasMov && otherRegHasCompute) {
262+ return true ;
263+ }
264+ }
265+ }
266+
267+ auto itThis = occupied_locs.find ({reg, t});
268+ bool thisRegHasMov = false ;
269+ bool thisRegHasCompute = false ;
270+ if (itThis != occupied_locs.end ()) {
271+ for (const auto &entry : itThis->second ) {
272+ Operation *occOp = entry.second ;
273+ if (isMovOp (occOp)) {
274+ thisRegHasMov = true ;
275+ } else {
276+ thisRegHasCompute = true ;
277+ }
278+ }
279+ }
280+
281+ // Rejects the slot when this register mixes with a sibling of the opposite kind.
282+ if (otherRegHasCompute && thisRegHasMov) {
283+ return true ;
284+ }
285+ if (otherRegHasMov && thisRegHasCompute) {
286+ return true ;
287+ }
288+ return false ;
289+ };
290+
291+ for (int t = loc.time_step % II; t < II * kMaxSteps ; t += II) {
292+ if (violatesClusterReadConstraintAt (t)) {
293+ return false ;
294+ }
295+ }
296+ }
297+ }
298+
148299 return true ;
149300 }
150301}
@@ -159,11 +310,11 @@ bool MappingState::isAvailableForOccupyStatus(const MappingLoc &loc,
159310 return true ;
160311 }
161312
162- // Check against all existing entries at this location
313+ // Checks against all existing entries at this location
163314 for (const auto &entry : it->second ) {
164315 int existing_status = entry.first ;
165316
166- // Implement the pipeline-aware availability rules:
317+ // Implements the pipeline-aware availability rules:
167318 // - SINGLE_OCCUPY (0): exclusive, no other op can share
168319 // - START_PIPE_OCCUPY (1): cannot coexist with SINGLE or another START
169320 // - END_PIPE_OCCUPY (2): cannot coexist with SINGLE or another END
0 commit comments