use super::compiler_constants::{TWELVE_BITS, TWENTY_FOUR_BITS};
use super::*;
pub(super) fn convert_array_instantiation_to_asm<'sc>(
contents: &[TypedExpression<'sc>],
namespace: &mut AsmNamespace<'sc>,
return_register: &VirtualRegister,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
if contents.is_empty() {
return ok(
vec![Op::new_comment("array size is zero.")],
Vec::new(),
Vec::new(),
);
}
let warnings = Vec::new();
let mut errors = Vec::new();
let mut bytecode = Vec::new();
let elem_type = check_std_result!(
resolve_type(contents[0].return_type, &contents[0].span),
warnings,
errors
);
let elem_size_in_words =
check_std_result!(elem_type.size_in_words(&contents[0].span), warnings, errors);
let mut array_size = elem_size_in_words * 8 * contents.len() as u64;
bytecode.push(Op::unowned_register_move(
return_register.clone(),
VirtualRegister::Constant(ConstantRegister::StackPointer),
));
while array_size != 0 {
let expansion_size = std::cmp::min(TWENTY_FOUR_BITS, array_size);
bytecode.push(Op::unowned_stack_allocate_memory(
VirtualImmediate24::new_unchecked(expansion_size, "guaranteed to be < than 2^24"),
));
array_size -= expansion_size;
}
if elem_size_in_words > 1 || (contents.len() as u64 - 1) > TWELVE_BITS {
initialize_large_array_instantiation(
contents,
elem_size_in_words,
return_register,
bytecode,
namespace,
register_sequencer,
warnings,
errors,
)
} else {
initialize_small_array_instantiation(
contents,
return_register,
bytecode,
namespace,
register_sequencer,
warnings,
errors,
)
}
}
fn initialize_small_array_instantiation<'sc>(
contents: &[TypedExpression<'sc>],
array_start_reg: &VirtualRegister,
mut bytecode: Vec<Op<'sc>>,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
mut warnings: Vec<CompileWarning<'sc>>,
mut errors: Vec<CompileError<'sc>>,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
assert!(contents.len() as u64 - 1 <= TWELVE_BITS);
let elem_init_reg = register_sequencer.next();
for (idx, elem) in contents.iter().enumerate() {
bytecode.append(&mut check!(
convert_expression_to_asm(elem, namespace, &elem_init_reg.clone(), register_sequencer),
Vec::new(),
warnings,
errors
));
bytecode.push(Op::write_register_to_memory(
array_start_reg.clone(),
elem_init_reg.clone(),
VirtualImmediate12::new_unchecked(idx as u64, "array is indexable with 12 bits"),
elem.span.clone(),
));
}
ok(bytecode, warnings, errors)
}
#[allow(clippy::too_many_arguments)]
fn initialize_large_array_instantiation<'sc>(
contents: &[TypedExpression<'sc>],
elem_size_in_words: u64,
array_offs_reg: &VirtualRegister,
mut bytecode: Vec<Op<'sc>>,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
mut warnings: Vec<CompileWarning<'sc>>,
mut errors: Vec<CompileError<'sc>>,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let elem_offs_reg = register_sequencer.next();
bytecode.push(Op::unowned_register_move(
elem_offs_reg.clone(),
array_offs_reg.clone(),
));
let elem_init_reg = register_sequencer.next();
for elem in contents {
bytecode.append(&mut check!(
convert_expression_to_asm(elem, namespace, &elem_init_reg.clone(), register_sequencer),
Vec::new(),
warnings,
errors
));
if elem_size_in_words == 1 {
bytecode.push(Op::write_register_to_memory(
elem_offs_reg.clone(),
elem_init_reg.clone(),
VirtualImmediate12 { value: 0 },
elem.span.clone(),
));
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::ADDI(
elem_offs_reg.clone(),
elem_offs_reg.clone(),
VirtualImmediate12 { value: 8 },
)),
owning_span: Some(elem.span.clone()),
comment: "increment to next element offset".into(),
});
} else {
let mut elem_size_in_bytes = elem_size_in_words * 8;
while elem_size_in_bytes != 0 {
let copy_size = std::cmp::min(TWELVE_BITS, elem_size_in_bytes);
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::MCPI(
elem_offs_reg.clone(),
elem_init_reg.clone(),
VirtualImmediate12::new_unchecked(
copy_size,
"guaranteed to be < than 2^12",
),
)),
owning_span: Some(elem.span.clone()),
comment: format!("cp array element size {}", copy_size),
});
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::ADDI(
elem_offs_reg.clone(),
elem_offs_reg.clone(),
VirtualImmediate12::new_unchecked(
copy_size,
"guaranteed to be < than 2^12",
),
)),
owning_span: Some(elem.span.clone()),
comment: "increment to next element offset".into(),
});
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::ADDI(
elem_init_reg.clone(),
elem_init_reg.clone(),
VirtualImmediate12::new_unchecked(
copy_size,
"guaranteed to be < than 2^12",
),
)),
owning_span: Some(elem.span.clone()),
comment: "increment to next init offset".into(),
});
elem_size_in_bytes -= copy_size;
}
}
}
ok(bytecode, warnings, errors)
}
pub(super) fn convert_array_index_to_asm<'sc>(
prefix: &TypedExpression<'sc>,
index: &TypedExpression<'sc>,
span: &Span<'sc>,
namespace: &mut AsmNamespace<'sc>,
return_register: &VirtualRegister,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let mut warnings = Vec::new();
let mut errors = Vec::new();
let mut bytecode = Vec::new();
let (elem_type, count) = match check_std_result!(
resolve_type(prefix.return_type, &prefix.span),
warnings,
errors
) {
TypeInfo::Array(elem_type_id, count) => (
check_std_result!(resolve_type(elem_type_id, &prefix.span), warnings, errors),
count as u64,
),
_otherwise => {
errors.push(CompileError::Internal(
"attempt to index a non-array",
span.clone(),
));
return err(warnings, errors);
}
};
if let TypedExpressionVariant::Literal(Literal::U64(index)) = index.expression {
if index >= count {
errors.push(CompileError::ArrayOutOfBounds {
index,
count,
span: span.clone(),
});
return err(warnings, errors);
}
}
let prefix_reg = register_sequencer.next();
bytecode.append(&mut check!(
convert_expression_to_asm(prefix, namespace, &prefix_reg, register_sequencer),
return err(warnings, errors),
warnings,
errors
));
let index_reg = register_sequencer.next();
bytecode.append(&mut check!(
convert_expression_to_asm(index, namespace, &index_reg.clone(), register_sequencer),
return err(warnings, errors),
warnings,
errors
));
let count_reg = register_sequencer.next();
set_large_register_value(count - 1, &count_reg, &mut bytecode, span);
compile_bounds_assertion(
&mut bytecode,
&count_reg,
&index_reg,
span,
register_sequencer,
);
let elem_size_in_words =
check_std_result!(elem_type.size_in_words(&prefix.span), warnings, errors);
let elem_size_reg = register_sequencer.next();
set_large_register_value(elem_size_in_words * 8, &elem_size_reg, &mut bytecode, span);
let elem_offs_reg = register_sequencer.next();
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::MUL(
elem_offs_reg.clone(),
index_reg.clone(),
elem_size_reg.clone(),
)),
owning_span: Some(span.clone()),
comment: "convert index into byte offset".into(),
});
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::ADD(
elem_offs_reg.clone(),
prefix_reg,
elem_offs_reg.clone(),
)),
owning_span: Some(span.clone()),
comment: "add element offset to array base offset".into(),
});
if elem_size_in_words == 1 {
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::LW(
return_register.clone(),
elem_offs_reg,
VirtualImmediate12 { value: 0 },
)),
owning_span: Some(span.clone()),
comment: "load array element".into(),
});
} else {
bytecode.push(Op::unowned_register_move(
return_register.clone(),
elem_offs_reg,
));
}
ok(bytecode, warnings, errors)
}
fn set_large_register_value<'sc, 'a>(
value: u64,
dst_reg: &'a VirtualRegister,
bytecode: &mut Vec<Op<'sc>>,
span: &Span<'sc>,
) -> &'a VirtualRegister {
if value == 0 {
return &VirtualRegister::Constant(ConstantRegister::Zero);
}
let src_reg = set_large_register_value(value >> 12, dst_reg, bytecode, span);
if value > TWELVE_BITS {
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::SLLI(
src_reg.clone(),
src_reg.clone(),
VirtualImmediate12 { value: 12 },
)),
owning_span: Some(span.clone()),
comment: "shift high bits of value".into(),
});
}
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::ORI(
dst_reg.clone(),
src_reg.clone(),
VirtualImmediate12::new_unchecked(value & TWELVE_BITS, "guaranteed to be < than 2^12"),
)),
owning_span: Some(span.clone()),
comment: "setting value bits".into(),
});
dst_reg
}
fn compile_bounds_assertion<'sc>(
bytecode: &mut Vec<Op<'sc>>,
count_reg: &VirtualRegister,
index_reg: &VirtualRegister,
span: &Span<'sc>,
register_sequencer: &mut RegisterSequencer,
) {
let gt_reg = register_sequencer.next();
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::GT(
gt_reg.clone(),
index_reg.clone(),
count_reg.clone(),
)),
owning_span: Some(span.clone()),
comment: "compare array index for out of bounds".into(),
});
let skip_label = register_sequencer.get_label();
bytecode.push(Op::jump_if_not_equal(
gt_reg,
VirtualRegister::Constant(ConstantRegister::One),
skip_label.clone(),
));
bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::RVRT(VirtualRegister::Constant(
ConstantRegister::One,
))),
owning_span: Some(span.clone()),
comment: "aborting due to out of bounds access".into(),
});
bytecode.push(Op::jump_label_comment(
skip_label,
span.clone(),
"after bounds check",
));
}