artiq: Improve IR value naming and add documentation

This commit is contained in:
David Mak 2023-10-23 13:35:29 +08:00
parent 6d746626c2
commit be6bd9e604
2 changed files with 83 additions and 42 deletions

View File

@ -28,9 +28,17 @@ use std::{
pub struct ArtiqCodeGenerator<'a> { pub struct ArtiqCodeGenerator<'a> {
name: String, name: String,
/// The size of a `size_t` variable in bits.
size_t: u32, size_t: u32,
/// Monotonic counter for naming `start`/`stop` variables used by `with parallel` blocks.
name_counter: u32, name_counter: u32,
/// Variable for tracking the start of a `with parallel` block.
start: Option<Expr<Option<Type>>>, start: Option<Expr<Option<Type>>>,
/// 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),
} }
@ -102,6 +110,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if let StmtKind::With { items, body, .. } = &stmt.node { if let StmtKind::With { items, body, .. } = &stmt.node {
if items.len() == 1 && items[0].optional_vars.is_none() { if items.len() == 1 && items[0].optional_vars.is_none() {
let item = &items[0]; let item = &items[0];
// Behavior of parallel and sequential: // Behavior of parallel and sequential:
// Each function call (indirectly, can be inside a sequential block) within a parallel // Each function call (indirectly, can be inside a sequential block) within a parallel
// block will update the end variable to the maximum now_mu in the block. // block will update the end variable to the maximum now_mu in the block.
@ -119,11 +128,15 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if id == &"parallel".into() { if id == &"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 now = if let Some(old_start) = &old_start { let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())? self.gen_expr(ctx, old_start)?
.unwrap()
.to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else { } else {
self.timeline.emit_now_mu(ctx) self.timeline.emit_now_mu(ctx)
}; };
// Emulate variable allocation, as we need to use the CodeGenContext // Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation // HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic // Note: we should be able to store variables directly if generic
@ -157,8 +170,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;
gen_block(self, ctx, body.iter())?; gen_block(self, ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap(); let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator // if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator // we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the // TODO: This may be unsound if there are multiple exit paths in the
@ -172,6 +188,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
} else { } else {
false false
}; };
// set duration // set duration
let end_expr = self.end.take().unwrap(); let end_expr = self.end.take().unwrap();
let end_val = self let end_val = self
@ -183,6 +200,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if old_start.is_none() { if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val); self.timeline.emit_at_mu(ctx, end_val);
} }
// inside a parallel block, should update the outer max now_mu // inside a parallel block, should update the outer max now_mu
if let Some(old_end) = &old_end { if let Some(old_end) = &old_end {
let outer_end_val = self let outer_end_val = self
@ -207,20 +225,25 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let outer_end = self.gen_store_target(ctx, old_end, Some("outer_end.addr"))?; let outer_end = self.gen_store_target(ctx, old_end, Some("outer_end.addr"))?;
ctx.builder.build_store(outer_end, max); ctx.builder.build_store(outer_end, max);
} }
self.start = old_start; self.start = old_start;
self.end = old_end; self.end = old_end;
if reset_position { if reset_position {
ctx.builder.position_at_end(current); ctx.builder.position_at_end(current);
} }
return Ok(()); return Ok(());
} else if id == &"sequential".into() { } else if id == &"sequential".into() {
let start = self.start.take(); let start = self.start.take();
gen_block(self, ctx, body.iter())?; gen_block(self, ctx, body.iter())?;
self.start = start; self.start = start;
return Ok(()); return Ok(());
} }
} }
} }
// not parallel/sequential // not parallel/sequential
gen_with(self, ctx, stmt) gen_with(self, ctx, stmt)
} else { } else {

View File

@ -1,9 +1,16 @@
use inkwell::{values::BasicValueEnum, AddressSpace, AtomicOrdering}; use inkwell::{values::BasicValueEnum, AddressSpace, AtomicOrdering};
use nac3core::codegen::CodeGenContext; use nac3core::codegen::CodeGenContext;
/// Functions for manipulating the timeline.
pub trait TimeFns { pub trait TimeFns {
/// Emits LLVM IR for `now_mu`.
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>; fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>;
/// Emits LLVM IR for `at_mu`.
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>); fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>);
/// Emits LLVM IR for `delay_mu`.
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>); fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>);
} }
@ -20,23 +27,25 @@ impl TimeFns for NowPinningTimeFns64 {
.get_global("now") .get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now")); .unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now_hiptr"); ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr { if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe { let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep") ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}; };
if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = ( if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
ctx.builder.build_load(now_hiptr, "now_hi"), ctx.builder.build_load(now_hiptr, "now.hi"),
ctx.builder.build_load(now_loptr, "now_lo"), ctx.builder.build_load(now_loptr, "now.lo"),
) { ) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi"); let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
let shifted_hi = ctx.builder.build_left_shift( let shifted_hi = ctx.builder.build_left_shift(
zext_hi, zext_hi,
i64_type.const_int(32, false), i64_type.const_int(32, false),
"now_shifted_zext_hi", "",
); );
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo"); let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
ctx.builder.build_or(shifted_hi, zext_lo, "now_or").into() ctx.builder.build_or(shifted_hi, zext_lo, "now_mu").into()
} else { } else {
unreachable!(); unreachable!();
} }
@ -48,14 +57,15 @@ impl TimeFns for NowPinningTimeFns64 {
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) { fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
let i32_type = ctx.ctx.i32_type(); let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type(); let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false); let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t { if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate( let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"), ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
i32_type, i32_type,
"now_trunc", "",
); );
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc"); let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
let now = ctx let now = ctx
.module .module
.get_global("now") .get_global("now")
@ -63,11 +73,12 @@ impl TimeFns for NowPinningTimeFns64 {
let now_hiptr = ctx.builder.build_bitcast( let now_hiptr = ctx.builder.build_bitcast(
now, now,
i32_type.ptr_type(AddressSpace::default()), i32_type.ptr_type(AddressSpace::default()),
"now_bitcast", "now.hi.addr",
); );
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr { if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe { let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep") ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}; };
ctx.builder ctx.builder
.build_store(now_hiptr, time_hi) .build_store(now_hiptr, time_hi)
@ -97,41 +108,43 @@ impl TimeFns for NowPinningTimeFns64 {
.get_global("now") .get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now")); .unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now_hiptr"); ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr { if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe { let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_loptr") ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}; };
if let ( if let (
BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_hi),
BasicValueEnum::IntValue(now_lo), BasicValueEnum::IntValue(now_lo),
BasicValueEnum::IntValue(dt), BasicValueEnum::IntValue(dt),
) = ( ) = (
ctx.builder.build_load(now_hiptr, "now_hi"), ctx.builder.build_load(now_hiptr, "now.hi"),
ctx.builder.build_load(now_loptr, "now_lo"), ctx.builder.build_load(now_loptr, "now.lo"),
dt, dt,
) { ) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi"); let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
let shifted_hi = ctx.builder.build_left_shift( let shifted_hi = ctx.builder.build_left_shift(
zext_hi, zext_hi,
i64_type.const_int(32, false), i64_type.const_int(32, false),
"now_shifted_zext_hi", "",
); );
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo"); let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now_or"); let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now");
let time = ctx.builder.build_int_add(now_val, dt, "now_add"); let time = ctx.builder.build_int_add(now_val, dt, "time");
let time_hi = ctx.builder.build_int_truncate( let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift( ctx.builder.build_right_shift(
time, time,
i64_type.const_int(32, false), i64_type.const_int(32, false),
false, false,
"now_lshr", "",
), ),
i32_type, i32_type,
"now_trunc", "time.hi",
); );
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc"); let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
ctx.builder ctx.builder
.build_store(now_hiptr, time_hi) .build_store(now_hiptr, time_hi)
@ -162,11 +175,12 @@ impl TimeFns for NowPinningTimeFns {
.get_global("now") .get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now")); .unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now"); let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
if let BasicValueEnum::IntValue(now_raw) = now_raw { if let BasicValueEnum::IntValue(now_raw) = now_raw {
let i64_32 = i64_type.const_int(32, false); let i64_32 = i64_type.const_int(32, false);
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl"); let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr"); let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
ctx.builder.build_or(now_lo, now_hi, "now_or").into() ctx.builder.build_or(now_lo, now_hi, "now_mu").into()
} else { } else {
unreachable!(); unreachable!();
} }
@ -176,11 +190,12 @@ impl TimeFns for NowPinningTimeFns {
let i32_type = ctx.ctx.i32_type(); let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type(); let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false); let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t { if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate( let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"), ctx.builder.build_right_shift(time, i64_32, false, ""),
i32_type, i32_type,
"now_trunc", "time.hi",
); );
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc"); let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let now = ctx let now = ctx
@ -190,11 +205,12 @@ impl TimeFns for NowPinningTimeFns {
let now_hiptr = ctx.builder.build_bitcast( let now_hiptr = ctx.builder.build_bitcast(
now, now,
i32_type.ptr_type(AddressSpace::default()), i32_type.ptr_type(AddressSpace::default()),
"now_bitcast", "now.hi.addr",
); );
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr { if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe { let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep") ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
}; };
ctx.builder ctx.builder
.build_store(now_hiptr, time_hi) .build_store(now_hiptr, time_hi)
@ -224,26 +240,28 @@ impl TimeFns for NowPinningTimeFns {
.module .module
.get_global("now") .get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now")); .unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now"); let now_raw = ctx.builder.build_load(now.as_pointer_value(), "");
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) { if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) {
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl"); let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr"); let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_or"); let now_val = ctx.builder.build_or(now_lo, now_hi, "now_val");
let time = ctx.builder.build_int_add(now_val, dt, "now_add"); let time = ctx.builder.build_int_add(now_val, dt, "time");
let time_hi = ctx.builder.build_int_truncate( let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"), ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
i32_type, i32_type,
"now_trunc", "now_trunc",
); );
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc"); let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
let now_hiptr = ctx.builder.build_bitcast( let now_hiptr = ctx.builder.build_bitcast(
now, now,
i32_type.ptr_type(AddressSpace::default()), i32_type.ptr_type(AddressSpace::default()),
"now_bitcast", "now.hi.addr",
); );
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr { if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe { let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep") ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
}; };
ctx.builder ctx.builder
.build_store(now_hiptr, time_hi) .build_store(now_hiptr, time_hi)