diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index e3a1ec57..83ae58d5 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -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. + /// + /// 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>>, 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>>>( + &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>, 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(()); } diff --git a/nac3core/src/codegen/generator.rs b/nac3core/src/codegen/generator.rs index 431f3455..b8b2729e 100644 --- a/nac3core/src/codegen/generator.rs +++ b/nac3core/src/codegen/generator.rs @@ -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>>>( + &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, diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index ddce7bcf..ae12b347 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -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()) }) } diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 03edd1cc..65b3dbbc 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -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);