mod value;
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::sync::Arc;
use edlc_core::prelude::{EdlVarId, MirError, MirPhase};
use edlc_core::prelude::index_map::IndexMap;
use edlc_core::prelude::mir_type::abi::AbiConfig;
use edlc_core::prelude::mir_type::MirTypeId;
use cranelift_codegen::ir::{StackSlot, Value};
use log::info;
use crate::codegen::{short_vec, CodeCtx, CompileValue, IntoValue, ShortVec};
use crate::layout::SSARepr;
pub use crate::codegen::variable::value::{DataValue, DataVariant, PtrValue, RuntimeOffset, SSAGenerator, SSAValue, StackValue, VariableSetResult, VariableValue};
use crate::compiler::JIT;
#[derive(Clone, Debug)]
pub struct AggregateValue(pub DataValue);
impl AggregateValue {
pub fn from_values<Runtime>(values: &[Value], ty: MirTypeId, ctx: &mut CodeCtx) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::abi_repr(ty, ctx.abi.clone(), &ctx.phase.types)?,
data: DataVariant::Value(SSAValue::new(values)),
}))
}
pub fn from_comp_value<Runtime>(values: CompileValue, ctx: &mut CodeCtx) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::abi_repr(values.1, ctx.abi.clone(), &ctx.phase.types)?,
data: DataVariant::Value(SSAValue(values.0)),
}))
}
pub fn from_ref<Runtime>(ptr: Value, ty: MirTypeId, ctx: &mut CodeCtx) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::abi_repr(ty, ctx.abi.clone(), &ctx.phase.types)?,
data: DataVariant::Ref(PtrValue(ptr, 0)),
}))
}
pub fn from_ptr<Runtime>(ptr: PtrValue, ty: MirTypeId, ctx: &mut CodeCtx) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::abi_repr(ty, ctx.abi.clone(), &ctx.phase.types)?,
data: DataVariant::Ref(ptr),
}))
}
pub fn from_slot<Runtime>(slot: StackSlot, ty: MirTypeId, ctx: &mut CodeCtx) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::abi_repr(ty, ctx.abi.clone(), &ctx.phase.types)?,
data: DataVariant::StackSlot(StackValue(slot, 0)),
}))
}
pub fn empty<Runtime>(phase: &MirPhase, abi: Arc<AbiConfig>) -> Result<Self, MirError<JIT<Runtime>>> {
Ok(AggregateValue(DataValue {
ty: SSARepr::empty::<Runtime>(phase, abi),
data: DataVariant::Value(SSAValue::default()),
}))
}
#[allow(dead_code)]
fn is_large_aggregate_type(&self, abi: &AbiConfig) -> bool {
self.0.ty.is_large_aggregated_type(abi)
}
#[allow(dead_code)]
fn is_mutable(&self) -> bool {
false
}
pub fn ty(&self) -> MirTypeId {
self.0.ty.id
}
pub fn into_parameter<Runtime>(
self,
ctx: &mut CodeCtx,
) -> Result<Self, MirError<JIT<Runtime>>> {
if self.0.is_large_aggregated_type(&ctx.abi) {
let ptr = self.0.as_ptr(ctx)?;
Ok(AggregateValue(DataValue {
ty: self.0.ty,
data: DataVariant::Ref(ptr),
}))
} else {
let values = self.0.as_values(0, self.0.ty.byte_size(), ctx)?;
Ok(AggregateValue(DataValue {
ty: self.0.ty,
data: DataVariant::Value(SSAValue(values)),
}))
}
}
pub fn strip(self) -> ShortVec<Value> {
match self.0.data {
DataVariant::Value(val) => val.0,
DataVariant::Ref(ptr) => short_vec![ptr.0],
DataVariant::StackSlot(..) => panic!("Stack slot data cannot be stripped"),
}
}
pub fn values(self) -> CompileValue {
match self.0.data {
DataVariant::Value(val) => val.0.into_value(self.0.ty.id),
_ => panic!("Only value-like data blobs can be converted to a value vector"),
}
}
pub fn store_to_ptr<Runtime>(
&self,
dst_ptr: Value,
dst_offset: i32,
ctx: &mut CodeCtx,
) -> Result<(), MirError<JIT<Runtime>>> {
let size = ctx.phase.types.byte_size(self.0.ty.id)
.ok_or(MirError::UnknownType(self.0.ty.id))?;
self.0.write_to_ptr(dst_ptr, dst_offset, size, 0, ctx)
}
pub fn raw_values<Runtime: 'static>(
&self,
ctx: &mut CodeCtx,
) -> Result<ShortVec<Value>, MirError<JIT<Runtime>>> {
let size = ctx.phase.types.byte_size(self.0.ty.id)
.ok_or(MirError::UnknownType(self.0.ty.id))?;
assert!(size <= std::mem::size_of::<usize>() * 2);
self.0.as_values(0, size, ctx)
}
pub fn store_to_stack<Runtime>(
&self,
slot: StackSlot,
dst_offset: i32,
ctx: &mut CodeCtx,
) -> Result<(), MirError<JIT<Runtime>>> {
let size = ctx.phase.types.byte_size(self.0.ty.id)
.ok_or(MirError::UnknownType(self.0.ty.id))?;
self.0.write_to_stack(slot, dst_offset, size, 0, ctx)
}
pub fn as_ptr<Runtime>(
&self,
ctx: &mut CodeCtx,
) -> Result<PtrValue, MirError<JIT<Runtime>>> {
self.0.as_ptr(ctx)
}
pub fn get<Runtime>(
&self,
offset: usize,
ty: MirTypeId,
ctx: &mut CodeCtx,
) -> Result<Self, MirError<JIT<Runtime>>> {
let value = self.0.get::<Runtime>(offset, ty, ctx);
if value.0.ty.id != ty {
return Err(MirError::TypeMismatch {
got: value.0.ty.id,
exp: ty,
});
}
Ok(self.0.get::<Runtime>(offset, ty, ctx))
}
}
pub struct VarChange {
pub var_id: EdlVarId,
pub old_value: DataVariant,
}
pub struct VarMarker {
start_set: HashSet<EdlVarId>,
change_list: Vec<VarChange>,
}
impl VarMarker {
fn track_change(&mut self, id: EdlVarId, data: DataVariant) {
if self.start_set.contains(&id) && self.get_change_for_var(&id).is_none() {
self.change_list.push(VarChange {
var_id: id,
old_value: data,
});
}
}
pub fn get_change_for_var(&self, id: &EdlVarId) -> Option<&VarChange> {
self.change_list
.iter()
.find(|item| item.var_id == *id)
}
}
impl VarMarker {
pub fn get_changes(&self) -> &[VarChange] {
&self.change_list
}
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Hash)]
pub struct RecordMarker(usize);
#[derive(Default)]
pub struct VarCache {
counter: u32,
layers: Vec<CacheLayer>,
markers: HashMap<RecordMarker, VarMarker>,
marker_counter: usize,
gen: SSAGenerator,
}
impl Display for VarCache {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Variable cache with layers:")?;
for (i, layer) in self.layers.iter().enumerate() {
write!(f, " [{i:>2}]: {:?}", layer)?;
}
write!(f, "")
}
}
impl VarCache {
pub fn mark(&mut self) -> RecordMarker {
let mut start_set = HashSet::new();
self.layers.iter()
.for_each(|iter| iter.collect_variables(&mut start_set));
let marker = VarMarker {
start_set,
change_list: Vec::new(),
};
let prev_marker = RecordMarker(self.marker_counter);
self.marker_counter += 1;
self.markers.insert(prev_marker, marker);
prev_marker
}
pub fn fence(&mut self, marker: &RecordMarker) -> Option<VarMarker> {
self.markers.remove(marker)
}
fn track_change(&mut self, id: EdlVarId, old_data: DataVariant) {
self.markers.values_mut()
.for_each(|marker| marker.track_change(id, old_data.clone()));
}
pub fn pop(&mut self) {
info!("popping variable cache layer...");
self.layers.pop();
}
pub fn push(&mut self) {
info!("pushing variable cache layer...");
self.layers.push(CacheLayer {
vars: Default::default(),
});
}
pub fn clear(&mut self) {
info!("clearing variable cache layers...");
self.counter = 0;
self.layers.clear();
}
pub fn assert_empty(&self) {
assert!(self.layers.is_empty());
}
pub fn def_var<Runtime: 'static>(
&mut self,
id: EdlVarId,
value: AggregateValue,
mutable: bool,
ctx: &mut CodeCtx,
) -> Result<(), MirError<JIT<Runtime>>> {
if let Some(last) = self.layers.last_mut() {
last.def_var::<Runtime>(id, value, mutable, ctx, &mut self.gen)
} else {
panic!("Tried to insert a variable into an empty variable stack");
}
}
pub fn use_var<Runtime: 'static>(
&self,
id: EdlVarId,
offset: usize,
ty: MirTypeId,
ctx: &mut CodeCtx,
) -> Option<AggregateValue> {
for layer in self.layers.iter().rev() {
if let Some(val) = layer
.use_var::<Runtime>(id, offset, ty, ctx) {
return Some(val);
}
}
None
}
pub fn set_var<Runtime: 'static>(
&mut self,
id: EdlVarId,
value: AggregateValue,
offset: usize,
ctx: &mut CodeCtx,
) -> Result<(), MirError<JIT<Runtime>>> {
for layer in self.layers.iter_mut().rev() {
match layer.set_var::<Runtime>(id, value.clone(), offset, ctx)? {
VariableSetResult::Ok => { return Ok(()); },
VariableSetResult::SSAChange(data) => {
self.track_change(id, data);
return Ok(());
},
VariableSetResult::Unknown => (),
}
}
panic!("Attempted to partially set variable {id:?} which is still uninitialized");
}
pub fn set_var_runtime_offset<Runtime: 'static>(
&mut self,
id: EdlVarId,
value: AggregateValue,
offset: RuntimeOffset,
ctx: &mut CodeCtx,
) -> Result<(), MirError<JIT<Runtime>>> {
for layer in self.layers.iter_mut().rev() {
match layer.set_runtime_offset::<Runtime>(id, value.clone(), offset, ctx)? {
VariableSetResult::Ok => { return Ok(()); },
VariableSetResult::SSAChange(data) => {
self.track_change(id, data);
return Ok(());
},
VariableSetResult::Unknown => (),
}
}
panic!("Attempted to partially set variable {id:?} which is still uninitialized");
}
pub fn var_as_ptr<Runtime: 'static>(
&self,
edl: EdlVarId,
ctx: &mut CodeCtx,
) -> Result<PtrValue, MirError<JIT<Runtime>>> {
let mut res = Err(MirError::UnknownVar(edl));
for layer in self.layers.iter().rev() {
res = layer.var_as_ptr(edl, ctx);
if res.is_ok() {
return res;
}
}
res
}
}
#[derive(Debug)]
struct CacheLayer {
vars: IndexMap<VariableValue>,
}
impl CacheLayer {
fn def_var<Runtime: 'static>(
&mut self,
edl: EdlVarId,
var: AggregateValue,
mutable: bool,
ctx: &mut CodeCtx,
gen: &mut SSAGenerator,
) -> Result<(), MirError<JIT<Runtime>>> {
info!("defining variable `{:?}` with initial value: {:?}", edl, var);
self.vars.view_mut(edl.0)
.set(VariableValue::def::<Runtime>(var.0, mutable, gen, ctx)?);
Ok(())
}
fn use_var<Runtime: 'static>(
&self,
edl: EdlVarId,
offset: usize,
ty: MirTypeId,
ctx: &mut CodeCtx,
) -> Option<AggregateValue> {
info!("Getting local variable `{:?}` from variable cache", edl);
self.vars.get(edl.0)
.map(|data| data.get::<Runtime>(offset, ty, ctx))
}
fn collect_variables(&self, collection: &mut HashSet<EdlVarId>) {
for (id, _) in self.vars.iter() {
collection.insert(EdlVarId(id));
}
}
fn set_var<Runtime: 'static>(
&mut self,
edl: EdlVarId,
value: AggregateValue,
offset: usize,
ctx: &mut CodeCtx,
) -> Result<VariableSetResult, MirError<JIT<Runtime>>> {
if let Some(data) = self.vars.get_mut(edl.0) {
data.set::<Runtime>(value.0, offset, ctx)
} else {
Ok(VariableSetResult::Unknown)
}
}
fn set_runtime_offset<Runtime: 'static>(
&mut self,
edl: EdlVarId,
value: AggregateValue,
offset: RuntimeOffset,
ctx: &mut CodeCtx,
) -> Result<VariableSetResult, MirError<JIT<Runtime>>> {
if let Some(data) = self.vars.get_mut(edl.0) {
data.set_runtime_offset(value.0, offset.1, offset.0, ctx)
} else {
Ok(VariableSetResult::Unknown)
}
}
fn var_as_ptr<Runtime: 'static>(
&self,
edl: EdlVarId,
ctx: &mut CodeCtx,
) -> Result<PtrValue, MirError<JIT<Runtime>>> {
if let Some(data) = self.vars.get(edl.0) {
data.as_ptr(ctx)
} else {
Err(MirError::UnknownVar(edl))
}
}
}