artiq: Implement `with legacy_parallel` block

This commit is contained in:
David Mak 2023-10-25 16:16:20 +08:00
parent 139c9c123e
commit 86017be4d0
1 changed files with 74 additions and 6 deletions

View File

@ -1,7 +1,7 @@
use nac3core::{ use nac3core::{
codegen::{ codegen::{
expr::gen_call, expr::gen_call,
stmt::gen_with, stmt::{gen_block, gen_with},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}, },
symbol_resolver::ValueEnum, symbol_resolver::ValueEnum,
@ -26,6 +26,24 @@ use std::{
sync::Arc, sync::Arc,
}; };
/// The parallelism mode within a block.
#[derive(Copy, Clone, Eq, PartialEq)]
enum ParallelMode {
/// No parallelism is currently registered for this context.
None,
/// Legacy (or shallow) parallelism. Default before ARTIQ-8.
///
/// Each statement within the `with` block is treated as statements to be executed in parallel.
Legacy,
/// Deep parallelism. Default since ARTIQ-8.
///
/// Each function call within the `with` block (except those within a nested `sequential` block)
/// are treated to be executed in parallel.
Deep
}
pub struct ArtiqCodeGenerator<'a> { pub struct ArtiqCodeGenerator<'a> {
name: String, name: String,
@ -41,6 +59,12 @@ pub struct ArtiqCodeGenerator<'a> {
/// Variable for tracking the end of a `with parallel` block. /// Variable for tracking the end of a `with parallel` block.
end: Option<Expr<Option<Type>>>, end: Option<Expr<Option<Type>>>,
timeline: &'a (dyn TimeFns + Sync), timeline: &'a (dyn TimeFns + Sync),
/// The [ParallelMode] of the current parallel context.
///
/// The current parallel context refers to the nearest `with parallel` or `with legacy_parallel`
/// statement, which is used to determine when and how the timeline should be updated.
parallel_mode: ParallelMode,
} }
impl<'a> ArtiqCodeGenerator<'a> { impl<'a> ArtiqCodeGenerator<'a> {
@ -57,6 +81,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
start: None, start: None,
end: None, end: None,
timeline, timeline,
parallel_mode: ParallelMode::None,
} }
} }
@ -141,6 +166,31 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
} }
} }
fn gen_block<'ctx, 'a, 'c, I: Iterator<Item=&'c Stmt<Option<Type>>>>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmts: I
) -> Result<(), String> where Self: Sized {
// Legacy parallel emits timeline end-update/timeline-reset after each top-level statement
// in the parallel block
if self.parallel_mode == ParallelMode::Legacy {
for stmt in stmts {
self.gen_stmt(ctx, stmt)?;
if ctx.is_terminated() {
break;
}
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
self.timeline_reset_start(ctx)?;
}
Ok(())
} else {
gen_block(self, ctx, stmts)
}
}
fn gen_call<'ctx, 'a>( fn gen_call<'ctx, 'a>(
&mut self, &mut self,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
@ -150,8 +200,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
) -> Result<Option<BasicValueEnum<'ctx>>, String> { ) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?; let result = gen_call(self, ctx, obj, fun, params)?;
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?; // Deep parallel emits timeline end-update/timeline-reset after each function call
self.timeline_reset_start(ctx)?; if self.parallel_mode == ParallelMode::Deep {
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
self.timeline_reset_start(ctx)?;
}
Ok(result) Ok(result)
} }
@ -179,9 +232,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
// - If there is a end variable, it indicates that we are (indirectly) inside a // - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value. // parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node { if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
if id == &"parallel".into() { if id == &"parallel".into() || id == &"legacy_parallel".into() {
let old_start = self.start.take(); let old_start = self.start.take();
let old_end = self.end.take(); let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
let now = if let Some(old_start) = &old_start { let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)? self.gen_expr(ctx, old_start)?
@ -224,6 +278,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
ctx.builder.build_store(end, now); ctx.builder.build_store(end, now);
self.end = Some(end_expr); self.end = Some(end_expr);
self.name_counter += 1; self.name_counter += 1;
self.parallel_mode = match id.to_string().as_str() {
"parallel" => ParallelMode::Deep,
"legacy_parallel" => ParallelMode::Legacy,
_ => unreachable!(),
};
self.gen_block(ctx, body.iter())?; self.gen_block(ctx, body.iter())?;
@ -258,8 +317,9 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
// inside a parallel block, should update the outer max now_mu // inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?; self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.start = old_start; self.parallel_mode = old_parallel_mode;
self.end = old_end; self.end = old_end;
self.start = old_start;
if reset_position { if reset_position {
ctx.builder.position_at_end(current); ctx.builder.position_at_end(current);
@ -267,12 +327,20 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
return Ok(()); return Ok(());
} else if id == &"sequential".into() { } else if id == &"sequential".into() {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take(); let start = self.start.take();
self.gen_block(ctx, body.iter())?; self.gen_block(ctx, body.iter())?;
self.start = start; self.start = start;
// Reset the timeline when we are exiting the sequential block // Reset the timeline when we are exiting the sequential block
self.timeline_reset_start(ctx)?; // Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(()); return Ok(());
} }