sim: implement memories, still needs testing
This commit is contained in:
parent
cd0dd7b7ee
commit
e4cf66adf8
|
@ -26,12 +26,12 @@ use crate::{
|
||||||
reset::{ResetType, ResetTypeDispatch},
|
reset::{ResetType, ResetTypeDispatch},
|
||||||
sim::{
|
sim::{
|
||||||
interpreter::{
|
interpreter::{
|
||||||
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
|
||||||
SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed,
|
InsnsBuildingDone, InsnsBuildingKind, Label, SlotDebugData, SmallUInt, State,
|
||||||
StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
|
StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, StatePartIndexRange,
|
||||||
StatePartKindMemories, StatePartKindSmallSlots, StatePartLayout, StatePartLen,
|
StatePartKind, StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots,
|
||||||
StatePartsValue, TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange,
|
StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes,
|
||||||
TypeLayout, TypeLen, TypeParts,
|
TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts,
|
||||||
},
|
},
|
||||||
time::{SimDuration, SimInstant},
|
time::{SimDuration, SimInstant},
|
||||||
},
|
},
|
||||||
|
@ -970,7 +970,7 @@ struct Assignment {
|
||||||
inputs: SlotSet,
|
inputs: SlotSet,
|
||||||
outputs: SlotSet,
|
outputs: SlotSet,
|
||||||
conditions: Interned<[Cond]>,
|
conditions: Interned<[Cond]>,
|
||||||
insns: Vec<Insn>,
|
insns: Vec<InsnOrLabel>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,12 +1116,16 @@ impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'
|
||||||
impl Assignment {
|
impl Assignment {
|
||||||
fn new(
|
fn new(
|
||||||
conditions: Interned<[Cond]>,
|
conditions: Interned<[Cond]>,
|
||||||
insns: Vec<Insn>,
|
insns: Vec<InsnOrLabel>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut inputs = SlotSet::default();
|
let mut inputs = SlotSet::default();
|
||||||
let mut outputs = SlotSet::default();
|
let mut outputs = SlotSet::default();
|
||||||
for insn in &insns {
|
for insn in &insns {
|
||||||
|
let insn = match insn {
|
||||||
|
InsnOrLabel::Insn(insn) => insn,
|
||||||
|
InsnOrLabel::Label(_) => continue,
|
||||||
|
};
|
||||||
for InsnField { ty, kind } in insn.fields() {
|
for InsnField { ty, kind } in insn.fields() {
|
||||||
match (kind, ty) {
|
match (kind, ty) {
|
||||||
(InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => {
|
(InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => {
|
||||||
|
@ -1220,11 +1224,29 @@ struct MemoryPort {
|
||||||
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
|
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
addr_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
addr_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
en_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
en_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
data: CompiledValue<CanonicalType>,
|
data_layout: CompiledTypeLayout<CanonicalType>,
|
||||||
read_data_delayed: Vec<TypeIndex>,
|
read_data_delayed: Vec<TypeIndexRange>,
|
||||||
write_data_delayed: Vec<TypeIndex>,
|
write_data_delayed: Vec<TypeIndexRange>,
|
||||||
write_mask_delayed: Vec<TypeIndex>,
|
write_mask_delayed: Vec<TypeIndexRange>,
|
||||||
write_mode_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
write_mode_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
write_insns: Vec<InsnOrLabel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemoryPortReadInsns<'a> {
|
||||||
|
addr: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
en: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
write_mode: Option<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
data: TypeIndexRange,
|
||||||
|
insns: &'a mut Vec<InsnOrLabel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemoryPortWriteInsns<'a> {
|
||||||
|
addr: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
en: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
write_mode: Option<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
data: TypeIndexRange,
|
||||||
|
mask: TypeIndexRange,
|
||||||
|
insns: &'a mut Vec<InsnOrLabel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1749,13 +1771,13 @@ impl Compiler {
|
||||||
self.compiled_exprs_to_values.insert(expr, retval);
|
self.compiled_exprs_to_values.insert(expr, retval);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
fn add_assignment(
|
fn add_assignment<T: Into<InsnOrLabel>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
conditions: Interned<[Cond]>,
|
conditions: Interned<[Cond]>,
|
||||||
insns: impl IntoIterator<Item = Insn>,
|
insns: impl IntoIterator<Item = T>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
) {
|
) {
|
||||||
let insns = Vec::from_iter(insns);
|
let insns = Vec::from_iter(insns.into_iter().map(Into::into));
|
||||||
self.assignments
|
self.assignments
|
||||||
.push(Assignment::new(conditions, insns, source_location));
|
.push(Assignment::new(conditions, insns, source_location));
|
||||||
}
|
}
|
||||||
|
@ -1828,12 +1850,14 @@ impl Compiler {
|
||||||
{
|
{
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
let mut ty = compiled_value.layout.ty;
|
||||||
|
ty.width = ty.width.min(SmallUInt::BITS as usize);
|
||||||
let retval = match compiled_value.range.len() {
|
let retval = match compiled_value.range.len() {
|
||||||
TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start,
|
TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start,
|
||||||
TypeLen::A_BIG_SLOT => {
|
TypeLen::A_BIG_SLOT => {
|
||||||
let debug_data = SlotDebugData {
|
let debug_data = SlotDebugData {
|
||||||
name: Interned::default(),
|
name: Interned::default(),
|
||||||
ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(),
|
ty: ty.canonical(),
|
||||||
};
|
};
|
||||||
let dest = self
|
let dest = self
|
||||||
.insns
|
.insns
|
||||||
|
@ -3324,6 +3348,443 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
self.make_trace_decl(*parent_module, target_base)
|
self.make_trace_decl(*parent_module, target_base)
|
||||||
}
|
}
|
||||||
|
fn allocate_delay_chain<T, BK: InsnsBuildingKind>(
|
||||||
|
&mut self,
|
||||||
|
len: usize,
|
||||||
|
layout: &TypeLayout<BK>,
|
||||||
|
first: Option<T>,
|
||||||
|
last: Option<T>,
|
||||||
|
mut from_allocation: impl FnMut(TypeIndexRange) -> T,
|
||||||
|
) -> Vec<T> {
|
||||||
|
match (len, first, last) {
|
||||||
|
(0, _, _) => Vec::new(),
|
||||||
|
(1, Some(v), _) | (1, None, Some(v)) => vec![v],
|
||||||
|
(2, Some(first), Some(last)) => vec![first, last],
|
||||||
|
(len, first, last) => {
|
||||||
|
let inner_len = len - first.is_some() as usize - last.is_some() as usize;
|
||||||
|
first
|
||||||
|
.into_iter()
|
||||||
|
.chain(
|
||||||
|
(0..inner_len)
|
||||||
|
.map(|_| from_allocation(self.insns.allocate_variable(layout))),
|
||||||
|
)
|
||||||
|
.chain(last)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn allocate_delay_chain_small(
|
||||||
|
&mut self,
|
||||||
|
len: usize,
|
||||||
|
ty: CanonicalType,
|
||||||
|
first: Option<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
last: Option<StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
) -> Vec<StatePartIndex<StatePartKindSmallSlots>> {
|
||||||
|
self.allocate_delay_chain(
|
||||||
|
len,
|
||||||
|
&TypeLayout {
|
||||||
|
small_slots: StatePartLayout::scalar(
|
||||||
|
SlotDebugData {
|
||||||
|
name: Interned::default(),
|
||||||
|
ty,
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
big_slots: StatePartLayout::empty(),
|
||||||
|
},
|
||||||
|
first,
|
||||||
|
last,
|
||||||
|
|range| range.small_slots.start,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn compile_memory_port_rw_helper(
|
||||||
|
&mut self,
|
||||||
|
memory: StatePartIndex<StatePartKindMemories>,
|
||||||
|
stride: usize,
|
||||||
|
mut start: usize,
|
||||||
|
data_layout: CompiledTypeLayout<CanonicalType>,
|
||||||
|
mask_layout: CompiledTypeLayout<CanonicalType>,
|
||||||
|
mut read: Option<MemoryPortReadInsns<'_>>,
|
||||||
|
mut write: Option<MemoryPortWriteInsns<'_>>,
|
||||||
|
) {
|
||||||
|
match data_layout.body {
|
||||||
|
CompiledTypeLayoutBody::Scalar => {
|
||||||
|
let CompiledTypeLayoutBody::Scalar = mask_layout.body else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let signed = match data_layout.ty {
|
||||||
|
CanonicalType::UInt(_) => false,
|
||||||
|
CanonicalType::SInt(_) => true,
|
||||||
|
CanonicalType::Bool(_) => false,
|
||||||
|
CanonicalType::Array(_) => unreachable!(),
|
||||||
|
CanonicalType::Enum(_) => false,
|
||||||
|
CanonicalType::Bundle(_) => unreachable!(),
|
||||||
|
CanonicalType::AsyncReset(_) => false,
|
||||||
|
CanonicalType::SyncReset(_) => false,
|
||||||
|
CanonicalType::Reset(_) => false,
|
||||||
|
CanonicalType::Clock(_) => false,
|
||||||
|
};
|
||||||
|
let width = data_layout.ty.bit_width();
|
||||||
|
if let Some(MemoryPortReadInsns {
|
||||||
|
addr,
|
||||||
|
en: _,
|
||||||
|
write_mode: _,
|
||||||
|
data,
|
||||||
|
insns,
|
||||||
|
}) = read
|
||||||
|
{
|
||||||
|
insns.push(
|
||||||
|
match data.len() {
|
||||||
|
TypeLen::A_BIG_SLOT => {
|
||||||
|
let dest = data.big_slots.start;
|
||||||
|
if signed {
|
||||||
|
Insn::MemoryReadSInt {
|
||||||
|
dest,
|
||||||
|
memory,
|
||||||
|
addr,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Insn::MemoryReadUInt {
|
||||||
|
dest,
|
||||||
|
memory,
|
||||||
|
addr,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeLen::A_SMALL_SLOT => {
|
||||||
|
let _dest = data.small_slots.start;
|
||||||
|
todo!("memory ports' data are always big for now");
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(MemoryPortWriteInsns {
|
||||||
|
addr,
|
||||||
|
en: _,
|
||||||
|
write_mode: _,
|
||||||
|
data,
|
||||||
|
mask,
|
||||||
|
insns,
|
||||||
|
}) = write
|
||||||
|
{
|
||||||
|
let end_label = self.insns.new_label();
|
||||||
|
insns.push(
|
||||||
|
match mask.len() {
|
||||||
|
TypeLen::A_BIG_SLOT => Insn::BranchIfZero {
|
||||||
|
target: end_label.0,
|
||||||
|
value: mask.big_slots.start,
|
||||||
|
},
|
||||||
|
TypeLen::A_SMALL_SLOT => Insn::BranchIfSmallZero {
|
||||||
|
target: end_label.0,
|
||||||
|
value: mask.small_slots.start,
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
insns.push(
|
||||||
|
match data.len() {
|
||||||
|
TypeLen::A_BIG_SLOT => {
|
||||||
|
let dest = data.big_slots.start;
|
||||||
|
if signed {
|
||||||
|
Insn::MemoryReadSInt {
|
||||||
|
dest,
|
||||||
|
memory,
|
||||||
|
addr,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Insn::MemoryReadUInt {
|
||||||
|
dest,
|
||||||
|
memory,
|
||||||
|
addr,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeLen::A_SMALL_SLOT => {
|
||||||
|
let _dest = data.small_slots.start;
|
||||||
|
todo!("memory ports' data are always big for now");
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
insns.push(end_label.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompiledTypeLayoutBody::Array { element } => {
|
||||||
|
let CompiledTypeLayoutBody::Array {
|
||||||
|
element: mask_element,
|
||||||
|
} = mask_layout.body
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let ty = <Array>::from_canonical(data_layout.ty);
|
||||||
|
let element_bit_width = ty.element().bit_width();
|
||||||
|
let element_size = element.layout.len();
|
||||||
|
let mask_element_size = mask_element.layout.len();
|
||||||
|
for element_index in 0..ty.len() {
|
||||||
|
self.compile_memory_port_rw_helper(
|
||||||
|
memory,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
*element,
|
||||||
|
*mask_element,
|
||||||
|
read.as_mut().map(
|
||||||
|
|MemoryPortReadInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
insns,
|
||||||
|
}| MemoryPortReadInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: data.index_array(element_size, element_index),
|
||||||
|
insns,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
write.as_mut().map(
|
||||||
|
|MemoryPortWriteInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
mask,
|
||||||
|
insns,
|
||||||
|
}| {
|
||||||
|
MemoryPortWriteInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: data.index_array(element_size, element_index),
|
||||||
|
mask: mask.index_array(mask_element_size, element_index),
|
||||||
|
insns,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
start += element_bit_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompiledTypeLayoutBody::Bundle { fields } => {
|
||||||
|
let CompiledTypeLayoutBody::Bundle {
|
||||||
|
fields: mask_fields,
|
||||||
|
} = mask_layout.body
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
assert_eq!(fields.len(), mask_fields.len());
|
||||||
|
for (field, mask_field) in fields.into_iter().zip(mask_fields) {
|
||||||
|
let field_index_range =
|
||||||
|
TypeIndexRange::new(field.offset, field.ty.layout.len());
|
||||||
|
let mask_field_index_range =
|
||||||
|
TypeIndexRange::new(mask_field.offset, mask_field.ty.layout.len());
|
||||||
|
self.compile_memory_port_rw_helper(
|
||||||
|
memory,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
field.ty,
|
||||||
|
mask_field.ty,
|
||||||
|
read.as_mut().map(
|
||||||
|
|MemoryPortReadInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
insns,
|
||||||
|
}| MemoryPortReadInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: data.slice(field_index_range),
|
||||||
|
insns,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
write.as_mut().map(
|
||||||
|
|MemoryPortWriteInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
mask,
|
||||||
|
insns,
|
||||||
|
}| {
|
||||||
|
MemoryPortWriteInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: data.slice(field_index_range),
|
||||||
|
mask: mask.slice(mask_field_index_range),
|
||||||
|
insns,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
start = start + field.ty.ty.bit_width();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn compile_memory_port_rw(
|
||||||
|
&mut self,
|
||||||
|
memory: StatePartIndex<StatePartKindMemories>,
|
||||||
|
data_layout: CompiledTypeLayout<CanonicalType>,
|
||||||
|
mask_layout: CompiledTypeLayout<CanonicalType>,
|
||||||
|
mut read: Option<MemoryPortReadInsns<'_>>,
|
||||||
|
mut write: Option<MemoryPortWriteInsns<'_>>,
|
||||||
|
) {
|
||||||
|
let read_else_label = read.as_mut().map(
|
||||||
|
|MemoryPortReadInsns {
|
||||||
|
addr: _,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data: _,
|
||||||
|
insns,
|
||||||
|
}| {
|
||||||
|
let else_label = self.insns.new_label();
|
||||||
|
insns.push(
|
||||||
|
Insn::BranchIfSmallZero {
|
||||||
|
target: else_label.0,
|
||||||
|
value: *en,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
if let Some(write_mode) = *write_mode {
|
||||||
|
insns.push(
|
||||||
|
Insn::BranchIfSmallNonZero {
|
||||||
|
target: else_label.0,
|
||||||
|
value: write_mode,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else_label
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let write_end_label = write.as_mut().map(
|
||||||
|
|MemoryPortWriteInsns {
|
||||||
|
addr: _,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data: _,
|
||||||
|
mask: _,
|
||||||
|
insns,
|
||||||
|
}| {
|
||||||
|
let end_label = self.insns.new_label();
|
||||||
|
insns.push(
|
||||||
|
Insn::BranchIfSmallZero {
|
||||||
|
target: end_label.0,
|
||||||
|
value: *en,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
if let Some(write_mode) = *write_mode {
|
||||||
|
insns.push(
|
||||||
|
Insn::BranchIfSmallZero {
|
||||||
|
target: end_label.0,
|
||||||
|
value: write_mode,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
end_label
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.compile_memory_port_rw_helper(
|
||||||
|
memory,
|
||||||
|
data_layout.ty.bit_width(),
|
||||||
|
0,
|
||||||
|
data_layout,
|
||||||
|
mask_layout,
|
||||||
|
read.as_mut().map(
|
||||||
|
|MemoryPortReadInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
insns,
|
||||||
|
}| MemoryPortReadInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: *data,
|
||||||
|
insns: *insns,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
write.as_mut().map(
|
||||||
|
|MemoryPortWriteInsns {
|
||||||
|
addr,
|
||||||
|
en,
|
||||||
|
write_mode,
|
||||||
|
data,
|
||||||
|
mask,
|
||||||
|
insns,
|
||||||
|
}| MemoryPortWriteInsns {
|
||||||
|
addr: *addr,
|
||||||
|
en: *en,
|
||||||
|
write_mode: *write_mode,
|
||||||
|
data: *data,
|
||||||
|
mask: *mask,
|
||||||
|
insns: *insns,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let (
|
||||||
|
Some(else_label),
|
||||||
|
Some(MemoryPortReadInsns {
|
||||||
|
addr: _,
|
||||||
|
en: _,
|
||||||
|
write_mode: _,
|
||||||
|
data,
|
||||||
|
insns,
|
||||||
|
}),
|
||||||
|
) = (read_else_label, read)
|
||||||
|
{
|
||||||
|
let end_label = self.insns.new_label();
|
||||||
|
insns.push(
|
||||||
|
Insn::Branch {
|
||||||
|
target: end_label.0,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
insns.push(else_label.into());
|
||||||
|
let TypeIndexRange {
|
||||||
|
small_slots,
|
||||||
|
big_slots,
|
||||||
|
} = data;
|
||||||
|
for dest in small_slots.iter() {
|
||||||
|
insns.push(Insn::ConstSmall { dest, value: 0 }.into());
|
||||||
|
}
|
||||||
|
for dest in big_slots.iter() {
|
||||||
|
insns.push(
|
||||||
|
Insn::Const {
|
||||||
|
dest,
|
||||||
|
value: BigInt::ZERO.intern_sized(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
insns.push(end_label.into());
|
||||||
|
}
|
||||||
|
if let (Some(end_label), Some(write)) = (write_end_label, write) {
|
||||||
|
write.insns.push(end_label.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
fn compile_memory(
|
fn compile_memory(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: Mem,
|
mem: Mem,
|
||||||
|
@ -3331,6 +3792,22 @@ impl Compiler {
|
||||||
conditions: Interned<[Cond]>,
|
conditions: Interned<[Cond]>,
|
||||||
trace_decls: &mut Vec<TraceDecl>,
|
trace_decls: &mut Vec<TraceDecl>,
|
||||||
) {
|
) {
|
||||||
|
let data_layout = CompiledTypeLayout::get(mem.array_type().element());
|
||||||
|
let mask_layout = CompiledTypeLayout::get(mem.array_type().element().mask_type());
|
||||||
|
let read_latency_plus_1 = mem
|
||||||
|
.read_latency()
|
||||||
|
.checked_add(1)
|
||||||
|
.expect("read latency too big");
|
||||||
|
let write_latency_plus_1 = mem
|
||||||
|
.write_latency()
|
||||||
|
.get()
|
||||||
|
.checked_add(1)
|
||||||
|
.expect("write latency too big");
|
||||||
|
let read_cycle = match mem.read_under_write() {
|
||||||
|
ReadUnderWrite::Old => 0,
|
||||||
|
ReadUnderWrite::New => mem.read_latency(),
|
||||||
|
ReadUnderWrite::Undefined => mem.read_latency() / 2, // something other than Old or New
|
||||||
|
};
|
||||||
let memory = self
|
let memory = self
|
||||||
.insns
|
.insns
|
||||||
.state_layout
|
.state_layout
|
||||||
|
@ -3356,42 +3833,188 @@ impl Compiler {
|
||||||
};
|
};
|
||||||
self.decl_conditions.insert(target, conditions);
|
self.decl_conditions.insert(target, conditions);
|
||||||
trace_decls.push(self.make_trace_decl(instantiated_module, target_base));
|
trace_decls.push(self.make_trace_decl(instantiated_module, target_base));
|
||||||
match port.port_kind() {
|
let clk = Expr::field(port.to_expr(), "clk");
|
||||||
PortKind::ReadOnly => MemoryPort {
|
let clk = self.compile_expr(instantiated_module, clk);
|
||||||
clk_triggered: todo!(),
|
let clk = self.compiled_expr_to_value(clk, mem.source_location());
|
||||||
addr_delayed: todo!(),
|
let clk_triggered = self
|
||||||
en_delayed: todo!(),
|
.compile_clock(clk.map_ty(Clock::from_canonical), mem.source_location())
|
||||||
data: todo!(),
|
.clk_triggered;
|
||||||
read_data_delayed: todo!(),
|
let en = Expr::field(port.to_expr(), "en");
|
||||||
write_data_delayed: todo!(),
|
let en = self.compile_expr(instantiated_module, en);
|
||||||
write_mask_delayed: todo!(),
|
let en = self.compiled_expr_to_value(en, mem.source_location());
|
||||||
write_mode_delayed: todo!(),
|
let en = self.compiled_value_bool_dest_is_small(en, mem.source_location());
|
||||||
|
let addr = Expr::field(port.to_expr(), "addr");
|
||||||
|
let addr = self.compile_expr(instantiated_module, addr);
|
||||||
|
let addr = self.compiled_expr_to_value(addr, mem.source_location());
|
||||||
|
let addr_ty = addr.layout.ty;
|
||||||
|
let addr = self.compiled_value_to_dyn_array_index(
|
||||||
|
addr.map_ty(UInt::from_canonical),
|
||||||
|
mem.source_location(),
|
||||||
|
);
|
||||||
|
let read_data = port.port_kind().rdata_name().map(|name| {
|
||||||
|
let read_data =
|
||||||
|
self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name));
|
||||||
|
let read_data = self.compiled_expr_to_value(read_data, mem.source_location());
|
||||||
|
read_data.range
|
||||||
|
});
|
||||||
|
let write_data = port.port_kind().wdata_name().map(|name| {
|
||||||
|
let write_data =
|
||||||
|
self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name));
|
||||||
|
let write_data = self.compiled_expr_to_value(write_data, mem.source_location());
|
||||||
|
write_data.range
|
||||||
|
});
|
||||||
|
let write_mask = port.port_kind().wmask_name().map(|name| {
|
||||||
|
let write_mask =
|
||||||
|
self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name));
|
||||||
|
let write_mask = self.compiled_expr_to_value(write_mask, mem.source_location());
|
||||||
|
write_mask.range
|
||||||
|
});
|
||||||
|
let write_mode = port.port_kind().wmode_name().map(|name| {
|
||||||
|
let write_mode =
|
||||||
|
self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name));
|
||||||
|
let write_mode = self.compiled_expr_to_value(write_mode, mem.source_location());
|
||||||
|
self.compiled_value_bool_dest_is_small(write_mode, mem.source_location())
|
||||||
|
});
|
||||||
|
struct PortParts {
|
||||||
|
en_delayed_len: usize,
|
||||||
|
addr_delayed_len: usize,
|
||||||
|
read_data_delayed_len: usize,
|
||||||
|
write_data_delayed_len: usize,
|
||||||
|
write_mask_delayed_len: usize,
|
||||||
|
write_mode_delayed_len: usize,
|
||||||
|
read_cycle: Option<usize>,
|
||||||
|
write_cycle: Option<usize>,
|
||||||
|
}
|
||||||
|
let PortParts {
|
||||||
|
en_delayed_len,
|
||||||
|
addr_delayed_len,
|
||||||
|
read_data_delayed_len,
|
||||||
|
write_data_delayed_len,
|
||||||
|
write_mask_delayed_len,
|
||||||
|
write_mode_delayed_len,
|
||||||
|
read_cycle,
|
||||||
|
write_cycle,
|
||||||
|
} = match port.port_kind() {
|
||||||
|
PortKind::ReadOnly => PortParts {
|
||||||
|
en_delayed_len: read_cycle + 1,
|
||||||
|
addr_delayed_len: read_cycle + 1,
|
||||||
|
read_data_delayed_len: read_latency_plus_1 - read_cycle,
|
||||||
|
write_data_delayed_len: 0,
|
||||||
|
write_mask_delayed_len: 0,
|
||||||
|
write_mode_delayed_len: 0,
|
||||||
|
read_cycle: Some(read_cycle),
|
||||||
|
write_cycle: None,
|
||||||
},
|
},
|
||||||
PortKind::WriteOnly => MemoryPort {
|
PortKind::WriteOnly => PortParts {
|
||||||
clk_triggered: todo!(),
|
en_delayed_len: write_latency_plus_1,
|
||||||
addr_delayed: todo!(),
|
addr_delayed_len: write_latency_plus_1,
|
||||||
en_delayed: todo!(),
|
read_data_delayed_len: 0,
|
||||||
data: todo!(),
|
write_data_delayed_len: write_latency_plus_1,
|
||||||
read_data_delayed: todo!(),
|
write_mask_delayed_len: write_latency_plus_1,
|
||||||
write_data_delayed: todo!(),
|
write_mode_delayed_len: 0,
|
||||||
write_mask_delayed: todo!(),
|
read_cycle: None,
|
||||||
write_mode_delayed: todo!(),
|
write_cycle: Some(mem.write_latency().get()),
|
||||||
},
|
|
||||||
PortKind::ReadWrite => MemoryPort {
|
|
||||||
clk_triggered: todo!(),
|
|
||||||
addr_delayed: todo!(),
|
|
||||||
en_delayed: todo!(),
|
|
||||||
data: todo!(),
|
|
||||||
read_data_delayed: todo!(),
|
|
||||||
write_data_delayed: todo!(),
|
|
||||||
write_mask_delayed: todo!(),
|
|
||||||
write_mode_delayed: todo!(),
|
|
||||||
},
|
},
|
||||||
|
PortKind::ReadWrite => {
|
||||||
|
let can_rw_at_end = match mem.read_under_write() {
|
||||||
|
ReadUnderWrite::Old => false,
|
||||||
|
ReadUnderWrite::New | ReadUnderWrite::Undefined => true,
|
||||||
|
};
|
||||||
|
let latency_plus_1 = read_latency_plus_1;
|
||||||
|
if latency_plus_1 != write_latency_plus_1 || !can_rw_at_end {
|
||||||
|
todo!(
|
||||||
|
"not sure what to do, issue: \
|
||||||
|
https://github.com/chipsalliance/firrtl-spec/issues/263"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PortParts {
|
||||||
|
en_delayed_len: latency_plus_1,
|
||||||
|
addr_delayed_len: latency_plus_1,
|
||||||
|
read_data_delayed_len: 1,
|
||||||
|
write_data_delayed_len: latency_plus_1,
|
||||||
|
write_mask_delayed_len: latency_plus_1,
|
||||||
|
write_mode_delayed_len: latency_plus_1,
|
||||||
|
read_cycle: Some(latency_plus_1 - 1),
|
||||||
|
write_cycle: Some(latency_plus_1 - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let addr_delayed = self.allocate_delay_chain_small(
|
||||||
|
addr_delayed_len,
|
||||||
|
addr_ty.canonical(),
|
||||||
|
Some(addr),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let en_delayed = self.allocate_delay_chain_small(
|
||||||
|
en_delayed_len,
|
||||||
|
Bool.canonical(),
|
||||||
|
Some(en),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let read_data_delayed = self.allocate_delay_chain(
|
||||||
|
read_data_delayed_len,
|
||||||
|
&data_layout.layout,
|
||||||
|
None,
|
||||||
|
read_data,
|
||||||
|
|v| v,
|
||||||
|
);
|
||||||
|
let write_data_delayed = self.allocate_delay_chain(
|
||||||
|
write_data_delayed_len,
|
||||||
|
&data_layout.layout,
|
||||||
|
write_data,
|
||||||
|
None,
|
||||||
|
|v| v,
|
||||||
|
);
|
||||||
|
let write_mask_delayed = self.allocate_delay_chain(
|
||||||
|
write_mask_delayed_len,
|
||||||
|
&mask_layout.layout,
|
||||||
|
write_mask,
|
||||||
|
None,
|
||||||
|
|v| v,
|
||||||
|
);
|
||||||
|
let write_mode_delayed = self.allocate_delay_chain_small(
|
||||||
|
write_mode_delayed_len,
|
||||||
|
Bool.canonical(),
|
||||||
|
write_mode,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let mut read_insns = Vec::new();
|
||||||
|
let mut write_insns = Vec::new();
|
||||||
|
self.compile_memory_port_rw(
|
||||||
|
memory,
|
||||||
|
data_layout,
|
||||||
|
mask_layout,
|
||||||
|
read_cycle.map(|read_cycle| MemoryPortReadInsns {
|
||||||
|
addr: addr_delayed[read_cycle],
|
||||||
|
en: en_delayed[read_cycle],
|
||||||
|
write_mode: write_mode_delayed.get(read_cycle).copied(),
|
||||||
|
data: read_data_delayed[0],
|
||||||
|
insns: &mut read_insns,
|
||||||
|
}),
|
||||||
|
write_cycle.map(|write_cycle| MemoryPortWriteInsns {
|
||||||
|
addr: addr_delayed[write_cycle],
|
||||||
|
en: en_delayed[write_cycle],
|
||||||
|
write_mode: write_mode_delayed.get(write_cycle).copied(),
|
||||||
|
data: write_data_delayed[write_cycle],
|
||||||
|
mask: write_mask_delayed[write_cycle],
|
||||||
|
insns: &mut write_insns,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
self.add_assignment(Interned::default(), read_insns, mem.source_location());
|
||||||
|
MemoryPort {
|
||||||
|
clk_triggered,
|
||||||
|
addr_delayed,
|
||||||
|
en_delayed,
|
||||||
|
data_layout,
|
||||||
|
read_data_delayed,
|
||||||
|
write_data_delayed,
|
||||||
|
write_mask_delayed,
|
||||||
|
write_mode_delayed,
|
||||||
|
write_insns,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.memories.push(Memory { mem, memory, ports });
|
self.memories.push(Memory { mem, memory, ports });
|
||||||
todo!("implement memory");
|
|
||||||
}
|
}
|
||||||
fn compile_block(
|
fn compile_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -3549,7 +4172,7 @@ impl Compiler {
|
||||||
};
|
};
|
||||||
struct CondStackEntry<'a> {
|
struct CondStackEntry<'a> {
|
||||||
cond: &'a Cond,
|
cond: &'a Cond,
|
||||||
end_label_index: usize,
|
end_label: Label,
|
||||||
}
|
}
|
||||||
let mut cond_stack = Vec::<CondStackEntry<'_>>::new();
|
let mut cond_stack = Vec::<CondStackEntry<'_>>::new();
|
||||||
for assignment_index in assignments_order {
|
for assignment_index in assignments_order {
|
||||||
|
@ -3568,35 +4191,33 @@ impl Compiler {
|
||||||
same_len = index + 1;
|
same_len = index + 1;
|
||||||
}
|
}
|
||||||
while cond_stack.len() > same_len {
|
while cond_stack.len() > same_len {
|
||||||
let CondStackEntry {
|
let CondStackEntry { cond: _, end_label } =
|
||||||
cond: _,
|
cond_stack.pop().expect("just checked len");
|
||||||
end_label_index,
|
self.insns.define_label_at_next_insn(end_label);
|
||||||
} = cond_stack.pop().expect("just checked len");
|
|
||||||
self.insns.define_label_at_next_insn(end_label_index);
|
|
||||||
}
|
}
|
||||||
for cond in &conditions[cond_stack.len()..] {
|
for cond in &conditions[cond_stack.len()..] {
|
||||||
let end_label_index = self.insns.new_label();
|
let end_label = self.insns.new_label();
|
||||||
match cond.body {
|
match cond.body {
|
||||||
CondBody::IfTrue { cond: cond_value }
|
CondBody::IfTrue { cond: cond_value }
|
||||||
| CondBody::IfFalse { cond: cond_value } => {
|
| CondBody::IfFalse { cond: cond_value } => {
|
||||||
let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() {
|
let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() {
|
||||||
TypeLen::A_SMALL_SLOT => (
|
TypeLen::A_SMALL_SLOT => (
|
||||||
Insn::BranchIfSmallZero {
|
Insn::BranchIfSmallZero {
|
||||||
target: end_label_index,
|
target: end_label.0,
|
||||||
value: cond_value.range.small_slots.start,
|
value: cond_value.range.small_slots.start,
|
||||||
},
|
},
|
||||||
Insn::BranchIfSmallNonZero {
|
Insn::BranchIfSmallNonZero {
|
||||||
target: end_label_index,
|
target: end_label.0,
|
||||||
value: cond_value.range.small_slots.start,
|
value: cond_value.range.small_slots.start,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TypeLen::A_BIG_SLOT => (
|
TypeLen::A_BIG_SLOT => (
|
||||||
Insn::BranchIfZero {
|
Insn::BranchIfZero {
|
||||||
target: end_label_index,
|
target: end_label.0,
|
||||||
value: cond_value.range.big_slots.start,
|
value: cond_value.range.big_slots.start,
|
||||||
},
|
},
|
||||||
Insn::BranchIfNonZero {
|
Insn::BranchIfNonZero {
|
||||||
target: end_label_index,
|
target: end_label.0,
|
||||||
value: cond_value.range.big_slots.start,
|
value: cond_value.range.big_slots.start,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -3617,7 +4238,7 @@ impl Compiler {
|
||||||
} => {
|
} => {
|
||||||
self.insns.push(
|
self.insns.push(
|
||||||
Insn::BranchIfSmallNeImmediate {
|
Insn::BranchIfSmallNeImmediate {
|
||||||
target: end_label_index,
|
target: end_label.0,
|
||||||
lhs: discriminant,
|
lhs: discriminant,
|
||||||
rhs: variant_index as _,
|
rhs: variant_index as _,
|
||||||
},
|
},
|
||||||
|
@ -3625,14 +4246,9 @@ impl Compiler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cond_stack.push(CondStackEntry {
|
cond_stack.push(CondStackEntry { cond, end_label });
|
||||||
cond,
|
|
||||||
end_label_index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for insn in insns {
|
|
||||||
self.insns.push(*insn, *source_location);
|
|
||||||
}
|
}
|
||||||
|
self.insns.extend(insns.iter().copied(), *source_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> {
|
fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> {
|
||||||
|
@ -3674,11 +4290,11 @@ impl Compiler {
|
||||||
let reg_end = self.insns.new_label();
|
let reg_end = self.insns.new_label();
|
||||||
let reg_reset = self.insns.new_label();
|
let reg_reset = self.insns.new_label();
|
||||||
let branch_if_reset = Insn::BranchIfSmallNonZero {
|
let branch_if_reset = Insn::BranchIfSmallNonZero {
|
||||||
target: reg_reset,
|
target: reg_reset.0,
|
||||||
value: rst,
|
value: rst,
|
||||||
};
|
};
|
||||||
let branch_if_not_triggered = Insn::BranchIfSmallZero {
|
let branch_if_not_triggered = Insn::BranchIfSmallZero {
|
||||||
target: reg_end,
|
target: reg_end.0,
|
||||||
value: clk_triggered,
|
value: clk_triggered,
|
||||||
};
|
};
|
||||||
if is_async {
|
if is_async {
|
||||||
|
@ -3693,7 +4309,7 @@ impl Compiler {
|
||||||
source_location,
|
source_location,
|
||||||
);
|
);
|
||||||
self.insns
|
self.insns
|
||||||
.push(Insn::Branch { target: reg_end }, source_location);
|
.push(Insn::Branch { target: reg_end.0 }, source_location);
|
||||||
self.insns.define_label_at_next_insn(reg_reset);
|
self.insns.define_label_at_next_insn(reg_reset);
|
||||||
self.insns
|
self.insns
|
||||||
.extend(value.range.insns_for_copy_from(init.range), source_location);
|
.extend(value.range.insns_for_copy_from(init.range), source_location);
|
||||||
|
@ -3703,7 +4319,7 @@ impl Compiler {
|
||||||
let reg_end = self.insns.new_label();
|
let reg_end = self.insns.new_label();
|
||||||
self.insns.push(
|
self.insns.push(
|
||||||
Insn::BranchIfSmallZero {
|
Insn::BranchIfSmallZero {
|
||||||
target: reg_end,
|
target: reg_end.0,
|
||||||
value: clk_triggered,
|
value: clk_triggered,
|
||||||
},
|
},
|
||||||
source_location,
|
source_location,
|
||||||
|
@ -3718,8 +4334,59 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn process_memories(&mut self) {
|
fn process_memories(&mut self) {
|
||||||
for memory in mem::take(&mut self.memories) {
|
for Memory {
|
||||||
todo!();
|
mem,
|
||||||
|
memory: _,
|
||||||
|
ports,
|
||||||
|
} in mem::take(&mut self.memories)
|
||||||
|
{
|
||||||
|
for MemoryPort {
|
||||||
|
clk_triggered,
|
||||||
|
addr_delayed,
|
||||||
|
en_delayed,
|
||||||
|
data_layout: _,
|
||||||
|
read_data_delayed,
|
||||||
|
write_data_delayed,
|
||||||
|
write_mask_delayed,
|
||||||
|
write_mode_delayed,
|
||||||
|
write_insns,
|
||||||
|
} in ports
|
||||||
|
{
|
||||||
|
let port_end = self.insns.new_label();
|
||||||
|
self.insns.push(
|
||||||
|
Insn::BranchIfSmallZero {
|
||||||
|
target: port_end.0,
|
||||||
|
value: clk_triggered,
|
||||||
|
},
|
||||||
|
mem.source_location(),
|
||||||
|
);
|
||||||
|
self.insns.extend(write_insns, mem.source_location());
|
||||||
|
let small_shift_reg =
|
||||||
|
|this: &mut Self, values: &[StatePartIndex<StatePartKindSmallSlots>]| {
|
||||||
|
for pair in values.windows(2).rev() {
|
||||||
|
this.insns.push(
|
||||||
|
Insn::CopySmall {
|
||||||
|
dest: pair[1],
|
||||||
|
src: pair[0],
|
||||||
|
},
|
||||||
|
mem.source_location(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let shift_reg = |this: &mut Self, values: &[TypeIndexRange]| {
|
||||||
|
for pair in values.windows(2).rev() {
|
||||||
|
this.insns
|
||||||
|
.extend(pair[0].insns_for_copy_to(pair[1]), mem.source_location());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
small_shift_reg(self, &addr_delayed);
|
||||||
|
small_shift_reg(self, &en_delayed);
|
||||||
|
shift_reg(self, &read_data_delayed);
|
||||||
|
shift_reg(self, &write_data_delayed);
|
||||||
|
shift_reg(self, &write_mask_delayed);
|
||||||
|
small_shift_reg(self, &write_mode_delayed);
|
||||||
|
self.insns.define_label_at_next_insn(port_end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn compile(mut self) -> Compiled<Bundle> {
|
pub fn compile(mut self) -> Compiled<Bundle> {
|
||||||
|
|
|
@ -477,14 +477,50 @@ impl InsnsBuildingKind for InsnsBuilding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Label {
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct Label(pub(crate) usize);
|
||||||
|
|
||||||
|
impl fmt::Debug for Label {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "L{}:", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) enum InsnOrLabel {
|
||||||
|
Insn(Insn),
|
||||||
|
Label(Label),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Insn> for InsnOrLabel {
|
||||||
|
fn from(value: Insn) -> Self {
|
||||||
|
Self::Insn(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Label> for InsnOrLabel {
|
||||||
|
fn from(value: Label) -> Self {
|
||||||
|
Self::Label(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for InsnOrLabel {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Insn(v) => v.fmt(f),
|
||||||
|
Self::Label(v) => v.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LabelData {
|
||||||
address: Option<usize>,
|
address: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Labels {
|
pub(crate) struct Labels {
|
||||||
labels: Vec<Label>,
|
labels: Vec<LabelData>,
|
||||||
address_to_label_indexes_map: HashMap<usize, Vec<usize>>,
|
address_to_label_map: HashMap<usize, Vec<Label>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -518,12 +554,12 @@ struct InsnDebug<'a, BK: InsnsBuildingKind> {
|
||||||
|
|
||||||
impl<BK: InsnsBuildingKind> fmt::Debug for InsnDebug<'_, BK> {
|
impl<BK: InsnsBuildingKind> fmt::Debug for InsnDebug<'_, BK> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(label_indexes) = self
|
if let Some(labels) = self
|
||||||
.labels
|
.labels
|
||||||
.and_then(|labels| labels.address_to_label_indexes_map.get(&self.address))
|
.and_then(|labels| labels.address_to_label_map.get(&self.address))
|
||||||
{
|
{
|
||||||
for &label_index in label_indexes {
|
for &label in labels {
|
||||||
writeln!(f, "L{label_index}:")?;
|
writeln!(f, "{label:?}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(source_location) = self.source_location {
|
if let Some(source_location) = self.source_location {
|
||||||
|
@ -2019,14 +2055,17 @@ impl Insns<InsnsBuilding> {
|
||||||
self.insns.push(insn);
|
self.insns.push(insn);
|
||||||
self.insn_source_locations.push(source_location);
|
self.insn_source_locations.push(source_location);
|
||||||
}
|
}
|
||||||
pub(crate) fn extend(
|
pub(crate) fn extend<T: Into<InsnOrLabel>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
insns: impl IntoIterator<Item = Insn>,
|
insns_or_labels: impl IntoIterator<Item = T>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
) {
|
) {
|
||||||
self.insns.extend(insns);
|
for insn_or_label in insns_or_labels {
|
||||||
self.insn_source_locations
|
match insn_or_label.into() {
|
||||||
.resize(self.insns.len(), source_location);
|
InsnOrLabel::Insn(insn) => self.push(insn, source_location),
|
||||||
|
InsnOrLabel::Label(label) => self.define_label_at_next_insn(label),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn allocate_variable<BK: InsnsBuildingKind>(
|
pub(crate) fn allocate_variable<BK: InsnsBuildingKind>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -2034,22 +2073,22 @@ impl Insns<InsnsBuilding> {
|
||||||
) -> TypeIndexRange {
|
) -> TypeIndexRange {
|
||||||
self.state_layout.ty.allocate(layout)
|
self.state_layout.ty.allocate(layout)
|
||||||
}
|
}
|
||||||
pub(crate) fn new_label(&mut self) -> usize {
|
pub(crate) fn new_label(&mut self) -> Label {
|
||||||
let retval = self.labels.labels.len();
|
let retval = Label(self.labels.labels.len());
|
||||||
self.labels.labels.push(Label { address: None });
|
self.labels.labels.push(LabelData { address: None });
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn define_label_at_next_insn(&mut self, label_index: usize) {
|
pub(crate) fn define_label_at_next_insn(&mut self, label: Label) {
|
||||||
let address = self.next_insn_pc();
|
let address = self.next_insn_pc();
|
||||||
let label = &mut self.labels.labels[label_index];
|
let label_data = &mut self.labels.labels[label.0];
|
||||||
assert!(label.address.is_none(), "label already defined");
|
assert!(label_data.address.is_none(), "label already defined");
|
||||||
label.address = Some(address);
|
label_data.address = Some(address);
|
||||||
self.labels
|
self.labels
|
||||||
.address_to_label_indexes_map
|
.address_to_label_map
|
||||||
.entry(address)
|
.entry(address)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(label_index);
|
.push(label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2727,6 +2766,15 @@ impl_insns! {
|
||||||
state.big_slots[dest].clone_from(&value);
|
state.big_slots[dest].clone_from(&value);
|
||||||
next!();
|
next!();
|
||||||
}
|
}
|
||||||
|
ConstSmall {
|
||||||
|
#[kind = Output]
|
||||||
|
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
#[kind = Immediate]
|
||||||
|
value: SmallUInt,
|
||||||
|
} => {
|
||||||
|
state.small_slots[dest] = value;
|
||||||
|
next!();
|
||||||
|
}
|
||||||
Branch {
|
Branch {
|
||||||
#[kind = BranchTarget]
|
#[kind = BranchTarget]
|
||||||
target: usize,
|
target: usize,
|
||||||
|
|
|
@ -294,3 +294,4 @@ fn test_shift_register() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add tests for enums
|
// TODO: add tests for enums
|
||||||
|
// TODO: add tests for memories
|
||||||
|
|
Loading…
Reference in a new issue