Implement with legacy_parallel
#348
@ -26,6 +26,24 @@ use std::{
|
||||
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 NAC3.
|
||||
derppening marked this conversation as resolved
Outdated
|
||||
///
|
||||
/// Each statement within the `with` block is treated as statements to be executed in parallel.
|
||||
Legacy,
|
||||
|
||||
/// Deep parallelism. Default since NAC3.
|
||||
///
|
||||
/// 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> {
|
||||
name: String,
|
||||
|
||||
@ -41,6 +59,12 @@ pub struct ArtiqCodeGenerator<'a> {
|
||||
/// Variable for tracking the end of a `with parallel` block.
|
||||
end: Option<Expr<Option<Type>>>,
|
||||
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> {
|
||||
@ -57,6 +81,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
start: None,
|
||||
end: None,
|
||||
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>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
@ -150,8 +200,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let result = gen_call(self, ctx, obj, fun, params)?;
|
||||
|
||||
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
|
||||
self.timeline_reset_start(ctx)?;
|
||||
// Deep parallel emits timeline end-update/timeline-reset after each function call
|
||||
if self.parallel_mode == ParallelMode::Deep {
|
||||
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
|
||||
self.timeline_reset_start(ctx)?;
|
||||
}
|
||||
|
||||
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
|
||||
// parallel block, and we should update the max end value.
|
||||
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_end = self.end.take();
|
||||
let old_parallel_mode = self.parallel_mode;
|
||||
|
||||
let now = if let Some(old_start) = &old_start {
|
||||
self.gen_expr(ctx, old_start)?
|
||||
@ -224,8 +278,13 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||
ctx.builder.build_store(end, now);
|
||||
self.end = Some(end_expr);
|
||||
self.name_counter += 1;
|
||||
self.parallel_mode = match id.to_string().as_str() {
|
||||
"parallel" => ParallelMode::Deep,
|
||||
"legacy_parallel" => ParallelMode::Legacy,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
gen_block(self, ctx, body.iter())?;
|
||||
self.gen_block(ctx, body.iter())?;
|
||||
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
|
||||
@ -258,8 +317,9 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||
// inside a parallel block, should update the outer max now_mu
|
||||
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.start = old_start;
|
||||
|
||||
if reset_position {
|
||||
ctx.builder.position_at_end(current);
|
||||
@ -267,12 +327,20 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
||||
|
||||
return Ok(());
|
||||
} 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();
|
||||
gen_block(self, ctx, body.iter())?;
|
||||
self.gen_block(ctx, body.iter())?;
|
||||
self.start = start;
|
||||
|
||||
// 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(());
|
||||
}
|
||||
|
@ -169,6 +169,7 @@ pub trait CodeGenerator {
|
||||
}
|
||||
|
||||
/// Generate code for a statement
|
||||
///
|
||||
/// Return true if the statement must early return
|
||||
fn gen_stmt<'ctx, 'a>(
|
||||
&mut self,
|
||||
@ -181,6 +182,18 @@ pub trait CodeGenerator {
|
||||
gen_stmt(self, ctx, stmt)
|
||||
}
|
||||
|
||||
/// Generates code for a block statement.
|
||||
fn gen_block<'ctx, 'a, 'b, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
|
||||
&mut self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmts: I,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
gen_block(self, ctx, stmts)
|
||||
}
|
||||
|
||||
/// See [bool_to_i1].
|
||||
fn bool_to_i1<'ctx, 'a>(
|
||||
&self,
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
codegen::stmt::gen_block,
|
||||
symbol_resolver::{StaticValue, SymbolResolver},
|
||||
toplevel::{TopLevelContext, TopLevelDef},
|
||||
typecheck::{
|
||||
@ -859,7 +858,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
|
||||
let body = task.body.clone();
|
||||
gen_func_impl(context, generator, registry, builder, module, task, |generator, ctx| {
|
||||
gen_block(generator, ctx, body.iter())
|
||||
generator.gen_block(ctx, body.iter())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -322,7 +322,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
ctx.builder.build_store(target_i, ctx.builder.build_load(i, "").into_int_value());
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
} else {
|
||||
let index_addr = generator.gen_var_alloc(ctx, size_t.into(), Some("for.index.addr"))?;
|
||||
ctx.builder.build_store(index_addr, size_t.const_zero());
|
||||
@ -353,7 +353,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||
let index = ctx.builder.build_load(index_addr, "for.index").into_int_value();
|
||||
let val = ctx.build_gep_and_load(arr_ptr, &[index], Some("val"));
|
||||
generator.gen_assign(ctx, target, val.into())?;
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
}
|
||||
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
@ -369,7 +369,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
||||
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
generator.gen_block(ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
@ -423,7 +423,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
@ -435,7 +435,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
generator.gen_block(ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
}
|
||||
@ -487,7 +487,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||
unreachable!()
|
||||
};
|
||||
ctx.builder.position_at_end(body_bb);
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
for (k, (_, _, counter)) in var_assignment.iter() {
|
||||
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
|
||||
if counter != counter2 {
|
||||
@ -503,7 +503,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
if !orelse.is_empty() {
|
||||
ctx.builder.position_at_end(orelse_bb);
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
generator.gen_block(ctx, orelse.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
if cont_bb.is_none() {
|
||||
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
|
||||
@ -792,9 +792,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
|
||||
let old_unwind = ctx.unwind_target.replace(landingpad);
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
|
||||
gen_block(generator, ctx, orelse.iter())?;
|
||||
generator.gen_block(ctx, orelse.iter())?;
|
||||
}
|
||||
let body = ctx.builder.get_insert_block().unwrap();
|
||||
// reset old_clauses and old_unwind
|
||||
@ -896,7 +896,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
ctx.var_assignment.insert(*name, (exn_store, None, 0));
|
||||
ctx.builder.build_store(exn_store, exn.as_basic_value());
|
||||
}
|
||||
gen_block(generator, ctx, body.iter())?;
|
||||
generator.gen_block(ctx, body.iter())?;
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
// only need to call end catch if not terminated
|
||||
// otherwise, we already handled in return/break/continue/raise
|
||||
@ -979,7 +979,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
// exception path
|
||||
let cleanup = cleanup.unwrap();
|
||||
ctx.builder.position_at_end(cleanup);
|
||||
gen_block(generator, ctx, finalbody.iter())?;
|
||||
generator.gen_block(ctx, finalbody.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
ctx.builder.build_unreachable();
|
||||
@ -991,7 +991,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
final_targets.push(tail);
|
||||
let finalizer = ctx.ctx.append_basic_block(current_fun, "try.finally");
|
||||
ctx.builder.position_at_end(finalizer);
|
||||
gen_block(generator, ctx, finalbody.iter())?;
|
||||
generator.gen_block(ctx, finalbody.iter())?;
|
||||
if !ctx.is_terminated() {
|
||||
let dest = ctx.builder.build_load(final_state, "final_dest");
|
||||
ctx.builder.build_indirect_branch(dest, &final_targets);
|
||||
|
Loading…
Reference in New Issue
Block a user
Just refer to "NAC3" since the ARTIQ-8 statement is actually incorrect and NAC3 is only tentatively planned for ARTIQ-9.