@@ -200,6 +200,75 @@ pub struct InstructionBlock {
200
200
pub terminator : BlockTerminator ,
201
201
}
202
202
203
+ /// PreviousNodes is a structure which helps maintain ordering among instructions which operate on a given frame.
204
+ /// It works similarly to a multiple-reader-single-writer queue, where an instruction which "uses" a frame is like
205
+ /// a writer and an instruction which blocks that frame is like a reader. Multiple instructions may concurrently
206
+ /// block a frame, but an instruction may not use a frame while it is concurrently used or blocked.
207
+ ///
208
+ /// ## Examples
209
+ ///
210
+ /// Note that "depends on" is equivalent to "must execute after completion of".
211
+ ///
212
+ /// ```ignore
213
+ /// user --> user # a second user takes a dependency on the first
214
+ ///
215
+ /// user --> blocker # multiple blockers take a dependency on the most recent user
216
+ /// \-> blocker
217
+ /// \-> blocker
218
+ ///
219
+ /// blocker --> user --> blocker # users and blockers take dependencies on one another,
220
+ /// # but blockers do not depend on other blocking instructions
221
+ /// ```
222
+ struct PreviousNodes {
223
+ using : Option < ScheduledGraphNode > ,
224
+ blocking : HashSet < ScheduledGraphNode > ,
225
+ }
226
+
227
+ impl Default for PreviousNodes {
228
+ /// The default value for [PreviousNodes] is useful in that, if no previous nodes have been recorded
229
+ /// as using a frame, we should consider that the start of the instruction block "blocks" use of that frame
230
+ /// (in other words, this instruction cannot be scheduled prior to the start of the instruction block).
231
+ fn default ( ) -> Self {
232
+ Self {
233
+ using : None ,
234
+ blocking : vec ! [ ScheduledGraphNode :: BlockStart ] . into_iter ( ) . collect ( ) ,
235
+ }
236
+ }
237
+ }
238
+
239
+ impl PreviousNodes {
240
+ /// Register a node as using a frame, and return the instructions on which it should depend/wait for scheduling (if any).
241
+ ///
242
+ /// A node which uses a frame will block on any previous user or blocker of the frame, much like a writer in a read-write lock.
243
+ pub fn register_user ( & mut self , node : ScheduledGraphNode ) -> HashSet < ScheduledGraphNode > {
244
+ let mut result = std:: mem:: take ( & mut self . blocking ) ;
245
+ if let Some ( previous_user) = self . using . replace ( node) {
246
+ result. insert ( previous_user) ;
247
+ }
248
+
249
+ result
250
+ }
251
+
252
+ /// Register a node as blocking a frame, and return the instructions on which it should depend/wait for scheduling (if any).
253
+ ///
254
+ /// A node which blocks a frame will block on any previous user of the frame, but not concurrent blockers.
255
+ ///
256
+ /// If the frame is currently blocked by other nodes, it will add itself to the list of blockers,
257
+ /// much like a reader in a read-write lock.
258
+ pub fn register_blocker ( & mut self , node : ScheduledGraphNode ) -> Option < ScheduledGraphNode > {
259
+ self . blocking . insert ( node) ;
260
+ self . using
261
+ }
262
+
263
+ /// Consume the [PreviousNodes] and return all nodes within.
264
+ pub fn drain ( mut self ) -> HashSet < ScheduledGraphNode > {
265
+ if let Some ( using) = self . using {
266
+ self . blocking . insert ( using) ;
267
+ }
268
+ self . blocking
269
+ }
270
+ }
271
+
203
272
impl InstructionBlock {
204
273
pub fn build (
205
274
instructions : Vec < Instruction > ,
@@ -213,8 +282,7 @@ impl InstructionBlock {
213
282
let mut last_classical_instruction = ScheduledGraphNode :: BlockStart ;
214
283
215
284
// Store the instruction index of the last instruction to block that frame
216
- let mut last_instruction_by_frame: HashMap < FrameIdentifier , ScheduledGraphNode > =
217
- HashMap :: new ( ) ;
285
+ let mut last_instruction_by_frame: HashMap < FrameIdentifier , PreviousNodes > = HashMap :: new ( ) ;
218
286
219
287
// Store memory access reads and writes. Key is memory region name.
220
288
// NOTE: this may be refined to serialize by memory region offset rather than by entire region.
@@ -233,24 +301,47 @@ impl InstructionBlock {
233
301
Ok ( ( ) )
234
302
}
235
303
InstructionRole :: RFControl => {
236
- let used_frames = program
304
+ let used_frames: HashSet < & FrameIdentifier > = program
237
305
. get_frames_for_instruction ( instruction, false )
238
- . unwrap_or_default ( ) ;
239
- let blocked_frames = program
306
+ . unwrap_or_default ( )
307
+ . into_iter ( )
308
+ . collect ( ) ;
309
+ let blocked_frames: HashSet < & FrameIdentifier > = program
240
310
. get_frames_for_instruction ( instruction, true )
241
- . unwrap_or_default ( ) ;
311
+ . unwrap_or_default ( )
312
+ . into_iter ( )
313
+ . filter ( |f| !used_frames. contains ( f) )
314
+ . collect ( ) ;
242
315
243
- // Take a dependency on any previous instructions to _block_ a frame which this instruction _uses_.
244
316
for frame in used_frames {
245
- let previous_node_id = last_instruction_by_frame
246
- . get ( frame)
247
- . unwrap_or ( & ScheduledGraphNode :: BlockStart ) ;
248
- add_dependency ! ( graph, * previous_node_id => node, ExecutionDependency :: ReferenceFrame ) ;
317
+ let previous_node_ids = last_instruction_by_frame
318
+ . entry ( frame. clone ( ) )
319
+ . or_insert ( PreviousNodes {
320
+ using : None ,
321
+ blocking : vec ! [ ScheduledGraphNode :: BlockStart ]
322
+ . into_iter ( )
323
+ . collect ( ) ,
324
+ } )
325
+ . register_user ( node) ;
326
+
327
+ for previous_node_id in previous_node_ids {
328
+ add_dependency ! ( graph, previous_node_id => node, ExecutionDependency :: ReferenceFrame ) ;
329
+ }
249
330
}
250
331
251
- // We mark all "blocked" frames as such for later instructions to take a dependency on
252
332
for frame in blocked_frames {
253
- last_instruction_by_frame. insert ( frame. clone ( ) , node) ;
333
+ if let Some ( previous_node_id) = last_instruction_by_frame
334
+ . entry ( frame. clone ( ) )
335
+ . or_insert ( PreviousNodes {
336
+ using : None ,
337
+ blocking : vec ! [ ScheduledGraphNode :: BlockStart ]
338
+ . into_iter ( )
339
+ . collect ( ) ,
340
+ } )
341
+ . register_blocker ( node)
342
+ {
343
+ add_dependency ! ( graph, previous_node_id => node, ExecutionDependency :: ReferenceFrame ) ;
344
+ }
254
345
}
255
346
256
347
Ok ( ( ) )
@@ -295,8 +386,10 @@ impl InstructionBlock {
295
386
// does not terminate until these are complete
296
387
add_dependency ! ( graph, last_classical_instruction => ScheduledGraphNode :: BlockEnd , ExecutionDependency :: StableOrdering ) ;
297
388
298
- for ( _, last_instruction) in last_instruction_by_frame {
299
- add_dependency ! ( graph, last_instruction => ScheduledGraphNode :: BlockEnd , ExecutionDependency :: ReferenceFrame ) ;
389
+ for ( _, last_instructions) in last_instruction_by_frame {
390
+ for node in last_instructions. drain ( ) {
391
+ add_dependency ! ( graph, node => ScheduledGraphNode :: BlockEnd , ExecutionDependency :: ReferenceFrame ) ;
392
+ }
300
393
}
301
394
302
395
// Examine all "pending" memory operations for all regions
0 commit comments