use std::{collections::HashMap, fmt};
use crate::type_engine::resolve_type;
use crate::{
asm_generation::expression::convert_abi_fn_to_asm,
asm_lang::{
allocated_ops::{AllocatedOp, AllocatedRegister},
virtual_register::*,
Label, Op, OrganizationalOp, RealizedOp, VirtualImmediate12, VirtualImmediate24, VirtualOp,
},
error::*,
parse_tree::Literal,
semantic_analysis::{
Namespace, TypedAstNode, TypedAstNodeContent, TypedDeclaration, TypedFunctionDeclaration,
TypedParseTree,
},
types::ResolvedType,
BuildConfig, Ident, TypeInfo,
};
use either::Either;
pub(crate) mod checks;
pub(crate) mod compiler_constants;
mod declaration;
mod expression;
mod finalized_asm;
mod register_sequencer;
mod while_loop;
pub(crate) use declaration::*;
pub(crate) use expression::*;
pub use finalized_asm::FinalizedAsm;
pub(crate) use register_sequencer::*;
use while_loop::convert_while_loop_to_asm;
pub enum HllAsmSet<'sc> {
ContractAbi {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
ScriptMain {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
PredicateMain {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
Library,
}
#[derive(Clone)]
pub struct AbstractInstructionSet<'sc> {
ops: Vec<Op<'sc>>,
}
pub struct RealizedAbstractInstructionSet<'sc> {
ops: Vec<RealizedOp<'sc>>,
}
impl<'sc> RealizedAbstractInstructionSet<'sc> {
fn allocate_registers(self) -> InstructionSet<'sc> {
let op_register_mapping = self
.ops
.into_iter()
.map(|op| {
(
op.clone(),
op.opcode.registers().into_iter().cloned().collect(),
)
})
.collect::<Vec<_>>();
let mut pool = RegisterPool::init();
let mut buf = vec![];
for (ix, (op, _)) in op_register_mapping.iter().enumerate() {
buf.push(AllocatedOp {
opcode: op
.opcode
.allocate_registers(&mut pool, &op_register_mapping, ix),
comment: op.comment.clone(),
owning_span: op.owning_span.clone(),
})
}
InstructionSet { ops: buf }
}
}
#[derive(Clone)]
pub struct InstructionSet<'sc> {
ops: Vec<AllocatedOp<'sc>>,
}
type Data<'sc> = Literal<'sc>;
impl<'sc> AbstractInstructionSet<'sc> {
fn remove_sequential_jumps(&self) -> AbstractInstructionSet<'sc> {
let mut buf = vec![];
for i in 0..self.ops.len() - 1 {
if let Op {
opcode: Either::Right(OrganizationalOp::Jump(ref label)),
..
} = self.ops[i]
{
if let Op {
opcode: Either::Right(OrganizationalOp::Label(ref label2)),
..
} = self.ops[i + 1]
{
if label == label2 {
continue;
}
}
}
buf.push(self.ops[i].clone());
}
if let Some(x) = self.ops.last() {
buf.push(x.clone())
};
let mut buf2 = vec![];
for op in &buf {
match op.opcode {
Either::Right(OrganizationalOp::Label(ref label)) => {
if label_is_used(&buf, label) {
buf2.push(op.clone());
}
}
_ => buf2.push(op.clone()),
}
}
AbstractInstructionSet { ops: buf2 }
}
fn realize_labels(
self,
data_section: &DataSection<'sc>,
_namespace: &AsmNamespace,
) -> RealizedAbstractInstructionSet<'sc> {
let mut label_namespace: HashMap<&Label, u64> = Default::default();
let mut counter = 0;
for op in &self.ops {
match op.opcode {
Either::Right(OrganizationalOp::Label(ref lab)) => {
label_namespace.insert(lab, counter);
}
Either::Left(VirtualOp::LWDataId(_, ref data_id)) => {
let type_of_data = data_section.type_of_data(data_id).expect(
"Internal miscalculation in data section -- data id did not match up to any actual data",
);
counter += if type_of_data.stack_size_of() > 1 {
2
} else {
1
};
}
Either::Right(OrganizationalOp::Jump(..))
| Either::Right(OrganizationalOp::JumpIfNotEq(..))
| Either::Left(_) => {
counter += 1;
}
Either::Right(OrganizationalOp::Comment) => (),
Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder) => {
counter += 2
}
}
}
let mut realized_ops = vec![];
for Op {
opcode,
owning_span,
comment,
} in self.ops.clone().into_iter()
{
match opcode {
Either::Left(op) => realized_ops.push(RealizedOp {
opcode: op,
owning_span,
comment,
}),
Either::Right(org_op) => match org_op {
OrganizationalOp::Jump(ref lab) => {
let offset = label_namespace.get(lab).unwrap();
let imm = VirtualImmediate24::new_unchecked(
*offset,
"Programs with more than 2^24 labels are unsupported right now",
);
realized_ops.push(RealizedOp {
opcode: VirtualOp::JI(imm),
owning_span,
comment,
});
}
OrganizationalOp::JumpIfNotEq(r1, r2, ref lab) => {
let offset = label_namespace.get(lab).unwrap();
let imm = VirtualImmediate12::new_unchecked(
*offset,
"Programs with more than 2^12 labels are unsupported right now",
);
realized_ops.push(RealizedOp {
opcode: VirtualOp::JNEI(r1, r2, imm),
owning_span,
comment,
});
}
OrganizationalOp::DataSectionOffsetPlaceholder => {
realized_ops.push(RealizedOp {
opcode: VirtualOp::DataSectionOffsetPlaceholder,
owning_span: None,
comment: String::new(),
});
}
OrganizationalOp::Comment => continue,
OrganizationalOp::Label(..) => continue,
},
};
}
RealizedAbstractInstructionSet { ops: realized_ops }
}
}
#[derive(Debug)]
struct RegisterAllocationStatus {
reg: AllocatedRegister,
in_use: Option<VirtualRegister>,
}
#[derive(Debug)]
pub(crate) struct RegisterPool {
registers: Vec<RegisterAllocationStatus>,
}
impl RegisterPool {
fn init() -> Self {
let register_pool: Vec<RegisterAllocationStatus> = (0
..compiler_constants::NUM_ALLOCATABLE_REGISTERS)
.map(|x| RegisterAllocationStatus {
reg: AllocatedRegister::Allocated(x),
in_use: None,
})
.collect();
Self {
registers: register_pool,
}
}
pub(crate) fn get_register(
&mut self,
virtual_register: &VirtualRegister,
op_register_mapping: &[(RealizedOp, std::collections::HashSet<VirtualRegister>)],
) -> Option<AllocatedRegister> {
if let a @ Some(_) = self.registers.iter().find_map(
|RegisterAllocationStatus { reg, in_use }| match in_use {
Some(x) if x == virtual_register => Some(reg),
_ => None,
},
) {
return a.cloned();
}
for RegisterAllocationStatus { in_use, .. } in
self.registers.iter_mut().filter(|r| r.in_use.is_some())
{
if virtual_register_is_never_accessed_again(
in_use.as_ref().unwrap(),
op_register_mapping,
) {
*in_use = None;
}
}
let next_available = self
.registers
.iter_mut()
.find(|RegisterAllocationStatus { in_use, .. }| in_use.is_none());
match next_available {
Some(RegisterAllocationStatus { in_use, reg }) => {
*in_use = Some(virtual_register.clone());
Some(reg.clone())
}
None => None,
}
}
}
fn virtual_register_is_never_accessed_again(
reg: &VirtualRegister,
ops: &[(RealizedOp, std::collections::HashSet<VirtualRegister>)],
) -> bool {
!ops.iter().any(|(_, regs)| regs.contains(reg))
}
fn label_is_used<'sc>(buf: &[Op<'sc>], label: &Label) -> bool {
buf.iter().any(|Op { ref opcode, .. }| match opcode {
Either::Right(OrganizationalOp::Jump(ref l)) if label == l => true,
Either::Right(OrganizationalOp::JumpIfNotEq(_reg0, _reg1, ref l)) if label == l => true,
_ => false,
})
}
#[derive(Default, Clone, Debug)]
pub struct DataSection<'sc> {
pub value_pairs: Vec<Data<'sc>>,
}
impl<'sc> DataSection<'sc> {
pub(crate) fn offset_to_id(&self, id: &DataId) -> usize {
self.value_pairs
.iter()
.take(id.0 as usize)
.map(|x| x.to_bytes().len())
.sum()
}
pub(crate) fn serialize_to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.value_pairs.len());
for val in &self.value_pairs {
buf.append(&mut val.to_bytes().to_vec());
}
buf
}
pub(crate) fn type_of_data(&self, id: &DataId) -> Option<ResolvedType<'sc>> {
self.value_pairs.get(id.0 as usize).map(|x| x.as_type())
}
pub(crate) fn append_pointer(&mut self, pointer_value: u64) -> DataId {
let pointer_as_data = Literal::new_pointer_literal(pointer_value);
self.insert_data_value(&pointer_as_data)
}
pub(crate) fn insert_data_value(&mut self, data: &Literal<'sc>) -> DataId {
match self.value_pairs.iter().position(|x| x == data) {
Some(num) => DataId(num as u32),
None => {
self.value_pairs.push(data.clone());
DataId((self.value_pairs.len() - 1) as u32)
}
}
}
}
impl fmt::Display for DataSection<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data_buf = String::new();
for (ix, data) in self.value_pairs.iter().enumerate() {
let data_val = match data {
Literal::U8(num) => format!(".u8 {:#04x}", num),
Literal::U16(num) => format!(".u16 {:#04x}", num),
Literal::U32(num) => format!(".u32 {:#04x}", num),
Literal::U64(num) => format!(".u64 {:#04x}", num),
Literal::Boolean(b) => format!(".bool {}", if *b { "0x01" } else { "0x00" }),
Literal::String(st) => format!(".str \"{}\"", st),
Literal::Byte(b) => format!(".byte {:#08b}", b),
Literal::B256(b) => format!(
".b256 0x{}",
b.iter()
.map(|x| format!("{:02x}", x))
.collect::<Vec<_>>()
.join("")
),
};
let data_label = DataId(ix as u32);
data_buf.push_str(&format!("{} {}\n", data_label, data_val));
}
write!(f, ".data:\n{}", data_buf)
}
}
impl fmt::Display for HllAsmSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HllAsmSet::ScriptMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
HllAsmSet::PredicateMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
HllAsmSet::ContractAbi {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
HllAsmSet::Library => write!(f, ""),
}
}
}
impl fmt::Display for JumpOptimizedAsmSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JumpOptimizedAsmSet::ScriptMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
JumpOptimizedAsmSet::PredicateMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
JumpOptimizedAsmSet::ContractAbi {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
JumpOptimizedAsmSet::Library => write!(f, ""),
}
}
}
impl<'sc> fmt::Display for RegisterAllocatedAsmSet<'sc> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegisterAllocatedAsmSet::ScriptMain {
program_section,
data_section,
} => {
write!(f, "{}\n{}", program_section, data_section)
}
RegisterAllocatedAsmSet::PredicateMain {
program_section,
data_section,
} => {
write!(f, "{}\n{}", program_section, data_section)
}
RegisterAllocatedAsmSet::ContractAbi {
program_section,
data_section,
} => {
write!(f, "{}\n{}", program_section, data_section)
}
RegisterAllocatedAsmSet::Library => write!(f, ""),
}
}
}
impl<'sc> fmt::Display for FinalizedAsm<'sc> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FinalizedAsm::ScriptMain {
program_section,
data_section,
} => write!(f, "{}\n{}", program_section, data_section),
FinalizedAsm::PredicateMain {
program_section,
data_section,
} => write!(f, "{}\n{}", program_section, data_section),
FinalizedAsm::ContractAbi {
program_section,
data_section,
} => write!(f, "{}\n{}", program_section, data_section),
FinalizedAsm::Library => write!(f, ""),
}
}
}
impl fmt::Display for AbstractInstructionSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
".program:\n{}",
self.ops
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join("\n")
)
}
}
impl<'sc> fmt::Display for InstructionSet<'sc> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
".program:\n{}",
self.ops
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join("\n")
)
}
}
#[derive(Default, Clone, Debug)]
pub(crate) struct AsmNamespace<'sc> {
data_section: DataSection<'sc>,
variables: HashMap<Ident<'sc>, VirtualRegister>,
}
#[derive(Clone, Debug)]
pub(crate) struct DataId(pub(crate) u32);
impl fmt::Display for DataId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "data_{}", self.0)
}
}
impl<'sc> AsmNamespace<'sc> {
pub(crate) fn insert_variable(
&mut self,
var_name: Ident<'sc>,
register_location: VirtualRegister,
) {
self.variables.insert(var_name, register_location);
}
pub(crate) fn insert_data_value(&mut self, data: &Data<'sc>) -> DataId {
self.data_section.insert_data_value(data)
}
pub(crate) fn look_up_variable(
&self,
var_name: &Ident<'sc>,
) -> CompileResult<'sc, &VirtualRegister> {
match self.variables.get(var_name) {
Some(o) => ok(o, vec![], vec![]),
None => err(
vec![],
vec![CompileError::Internal(
"Unknown variable in assembly generation. This should have been an error \
during type checking.",
var_name.span.clone(),
)],
),
}
}
}
pub(crate) fn compile_ast_to_asm<'sc>(
ast: TypedParseTree<'sc>,
build_config: &BuildConfig,
) -> CompileResult<'sc, FinalizedAsm<'sc>> {
let mut register_sequencer = RegisterSequencer::new();
let mut warnings = vec![];
let mut errors = vec![];
let (asm, asm_namespace) = match ast {
TypedParseTree::Script {
main_function,
namespace: ast_namespace,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_all_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations,
&ast_namespace,
),
return err(warnings, errors),
warnings,
errors
);
let return_register = register_sequencer.next();
let mut body = check!(
convert_code_block_to_asm(
&main_function.body,
&mut namespace,
&mut register_sequencer,
Some(&return_register),
),
vec![],
warnings,
errors
);
asm_buf.append(&mut body);
asm_buf.append(&mut check!(
ret_or_retd_value(
&main_function,
return_register,
&mut register_sequencer,
&mut namespace
),
return err(warnings, errors),
warnings,
errors
));
(
HllAsmSet::ScriptMain {
program_section: AbstractInstructionSet { ops: asm_buf },
data_section: namespace.data_section.clone(),
},
namespace,
)
}
TypedParseTree::Predicate {
main_function,
namespace: ast_namespace,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_all_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations,
&ast_namespace,
),
return err(warnings, errors),
warnings,
errors
);
let mut body = check!(
convert_code_block_to_asm(
&main_function.body,
&mut namespace,
&mut register_sequencer,
None,
),
vec![],
warnings,
errors
);
asm_buf.append(&mut body);
(
HllAsmSet::PredicateMain {
program_section: AbstractInstructionSet { ops: asm_buf },
data_section: namespace.data_section.clone(),
},
namespace,
)
}
TypedParseTree::Contract {
abi_entries,
namespace: ast_namespace,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_all_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations,
&ast_namespace,
),
return err(warnings, errors),
warnings,
errors
);
let (selectors_and_labels, mut contract_asm) = check!(
compile_contract_to_selectors(abi_entries, &mut namespace, &mut register_sequencer),
return err(warnings, errors),
warnings,
errors
);
asm_buf.append(&mut build_contract_abi_switch(
&mut register_sequencer,
&mut namespace,
selectors_and_labels,
));
asm_buf.append(&mut contract_asm);
(
HllAsmSet::ContractAbi {
program_section: AbstractInstructionSet { ops: asm_buf },
data_section: namespace.data_section.clone(),
},
namespace,
)
}
TypedParseTree::Library { .. } => (HllAsmSet::Library, Default::default()),
};
if build_config.print_intermediate_asm {
println!("{}", asm);
}
let finalized_asm = asm
.remove_unnecessary_jumps()
.allocate_registers(&asm_namespace)
.optimize();
if build_config.print_finalized_asm {
println!("{}", finalized_asm);
}
check!(
super::check_invalid_opcodes(&finalized_asm),
return err(warnings, errors),
warnings,
errors
);
ok(finalized_asm, warnings, errors)
}
impl<'sc> HllAsmSet<'sc> {
pub(crate) fn remove_unnecessary_jumps(self) -> JumpOptimizedAsmSet<'sc> {
match self {
HllAsmSet::ScriptMain {
data_section,
program_section,
} => JumpOptimizedAsmSet::ScriptMain {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
HllAsmSet::PredicateMain {
data_section,
program_section,
} => JumpOptimizedAsmSet::PredicateMain {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
HllAsmSet::Library {} => JumpOptimizedAsmSet::Library,
HllAsmSet::ContractAbi {
data_section,
program_section,
} => JumpOptimizedAsmSet::ContractAbi {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
}
}
}
impl<'sc> JumpOptimizedAsmSet<'sc> {
fn allocate_registers(self, namespace: &AsmNamespace) -> RegisterAllocatedAsmSet<'sc> {
match self {
JumpOptimizedAsmSet::Library => RegisterAllocatedAsmSet::Library,
JumpOptimizedAsmSet::ScriptMain {
data_section,
program_section,
} => {
let program_section = program_section
.realize_labels(&data_section, namespace)
.allocate_registers();
RegisterAllocatedAsmSet::ScriptMain {
data_section,
program_section,
}
}
JumpOptimizedAsmSet::PredicateMain {
data_section,
program_section,
} => {
let program_section = program_section
.realize_labels(&data_section, namespace)
.allocate_registers();
RegisterAllocatedAsmSet::PredicateMain {
data_section,
program_section,
}
}
JumpOptimizedAsmSet::ContractAbi {
program_section,
data_section,
} => RegisterAllocatedAsmSet::ContractAbi {
program_section: program_section
.realize_labels(&data_section, namespace)
.allocate_registers(),
data_section,
},
}
}
}
pub enum JumpOptimizedAsmSet<'sc> {
ContractAbi {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
ScriptMain {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
PredicateMain {
data_section: DataSection<'sc>,
program_section: AbstractInstructionSet<'sc>,
},
Library,
}
pub enum RegisterAllocatedAsmSet<'sc> {
ContractAbi {
data_section: DataSection<'sc>,
program_section: InstructionSet<'sc>,
},
ScriptMain {
data_section: DataSection<'sc>,
program_section: InstructionSet<'sc>,
},
PredicateMain {
data_section: DataSection<'sc>,
program_section: InstructionSet<'sc>,
},
Library,
}
impl<'sc> RegisterAllocatedAsmSet<'sc> {
fn optimize(self) -> FinalizedAsm<'sc> {
match self {
RegisterAllocatedAsmSet::Library => FinalizedAsm::Library,
RegisterAllocatedAsmSet::ScriptMain {
mut program_section,
data_section,
} => {
if program_section.ops.len() & 1 != 0 {
program_section.ops.push(AllocatedOp {
opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP,
comment: "word-alignment of data section".into(),
owning_span: None,
});
}
FinalizedAsm::ScriptMain {
program_section,
data_section,
}
}
RegisterAllocatedAsmSet::PredicateMain {
mut program_section,
data_section,
} => {
if program_section.ops.len() & 1 != 0 {
program_section.ops.push(AllocatedOp {
opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP,
comment: "word-alignment of data section".into(),
owning_span: None,
});
}
FinalizedAsm::PredicateMain {
program_section,
data_section,
}
}
RegisterAllocatedAsmSet::ContractAbi {
mut program_section,
data_section,
} => {
if program_section.ops.len() & 1 != 0 {
program_section.ops.push(AllocatedOp {
opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP,
comment: "word-alignment of data section".into(),
owning_span: None,
});
}
FinalizedAsm::ContractAbi {
program_section,
data_section,
}
}
}
}
}
pub(crate) enum NodeAsmResult<'sc> {
JustAsm(Vec<Op<'sc>>),
ReturnStatement { asm: Vec<Op<'sc>> },
}
fn convert_node_to_asm<'sc>(
node: &TypedAstNode<'sc>,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
return_register: Option<&VirtualRegister>,
) -> CompileResult<'sc, NodeAsmResult<'sc>> {
let mut warnings = vec![];
let mut errors = vec![];
match &node.content {
TypedAstNodeContent::WhileLoop(r#loop) => {
let res = check!(
convert_while_loop_to_asm(r#loop, namespace, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
ok(NodeAsmResult::JustAsm(res), warnings, errors)
}
TypedAstNodeContent::Declaration(typed_decl) => {
let res = check!(
convert_decl_to_asm(typed_decl, namespace, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
ok(NodeAsmResult::JustAsm(res), warnings, errors)
}
TypedAstNodeContent::ImplicitReturnExpression(exp) => {
let return_register = if let Some(return_register) = return_register {
return_register.clone()
} else {
register_sequencer.next()
};
let ops = check!(
convert_expression_to_asm(exp, namespace, &return_register, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
ok(
NodeAsmResult::ReturnStatement { asm: ops },
warnings,
errors,
)
}
TypedAstNodeContent::ReturnStatement(exp) => {
let return_register = if let Some(return_register) = return_register {
return_register.clone()
} else {
register_sequencer.next()
};
let ops = check!(
convert_expression_to_asm(
&exp.expr,
namespace,
&return_register,
register_sequencer
),
return err(warnings, errors),
warnings,
errors
);
ok(
NodeAsmResult::ReturnStatement { asm: ops },
warnings,
errors,
)
}
TypedAstNodeContent::Expression(ref typed_expr) => {
let return_register = if let Some(return_register) = return_register {
return_register.clone()
} else {
register_sequencer.next()
};
let asm = check!(
convert_expression_to_asm(
typed_expr,
namespace,
&return_register,
register_sequencer
),
return err(warnings, errors),
warnings,
errors
);
ok(NodeAsmResult::JustAsm(asm), warnings, errors)
}
a => {
println!("Unimplemented: {:?}", a);
errors.push(CompileError::Unimplemented(
"The ASM for this construct has not been written yet.",
node.clone().span,
));
err(warnings, errors)
}
}
}
fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op<'static>; 6] {
let label = register_sequencer.get_label();
[
Op::jump_to_label(label.clone()),
Op {
opcode: Either::Left(VirtualOp::NOOP),
comment: "".into(),
owning_span: None,
},
Op {
opcode: Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder),
comment: "data section offset".into(),
owning_span: None,
},
Op::unowned_jump_label_comment(label, "end of metadata"),
Op {
opcode: Either::Left(VirtualOp::DataSectionRegisterLoadPlaceholder),
comment: "".into(),
owning_span: None,
},
Op {
opcode: Either::Left(VirtualOp::ADD(
VirtualRegister::Constant(ConstantRegister::DataSectionStart),
VirtualRegister::Constant(ConstantRegister::DataSectionStart),
VirtualRegister::Constant(ConstantRegister::InstructionStart),
)),
comment: "".into(),
owning_span: None,
},
]
}
fn build_contract_abi_switch<'sc>(
register_sequencer: &mut RegisterSequencer,
namespace: &mut AsmNamespace<'sc>,
selectors_and_labels: Vec<([u8; 4], Label)>,
) -> Vec<Op<'sc>> {
let input_selector_register = register_sequencer.next();
let mut asm_buf = vec![Op {
opcode: Either::Right(OrganizationalOp::Comment),
comment: "Begin contract ABI selector switch".into(),
owning_span: None,
}];
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::LW(
input_selector_register.clone(),
VirtualRegister::Constant(ConstantRegister::FramePointer),
VirtualImmediate12::new_unchecked(73, "constant infallible value"),
)),
comment: "load input function selector".into(),
owning_span: None,
});
for (selector, label) in selectors_and_labels {
let data_label = namespace.insert_data_value(&Literal::U32(u32::from_be_bytes(selector)));
let prog_selector_register = register_sequencer.next();
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::LWDataId(
prog_selector_register.clone(),
data_label,
)),
comment: "load fn selector for comparison".into(),
owning_span: None,
});
let comparison_result_register = register_sequencer.next();
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::EQ(
comparison_result_register.clone(),
input_selector_register.clone(),
prog_selector_register,
)),
comment: "function selector comparison".into(),
owning_span: None,
});
asm_buf.push(Op {
opcode: Either::Right(OrganizationalOp::JumpIfNotEq(
VirtualRegister::Constant(ConstantRegister::Zero),
comparison_result_register,
label,
)),
comment: "jump to selected function".into(),
owning_span: None,
});
}
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::RET(VirtualRegister::Constant(
ConstantRegister::Zero,
))),
comment: "return if no selectors matched".into(),
owning_span: None,
});
asm_buf
}
fn add_all_constant_decls<'sc>(
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
asm_buf: &mut Vec<Op<'sc>>,
declarations: &[TypedDeclaration<'sc>],
ast_namespace: &Namespace<'sc>,
) -> CompileResult<'sc, ()> {
let mut warnings = vec![];
let mut errors = vec![];
check!(
add_global_constant_decls(namespace, register_sequencer, asm_buf, declarations),
return err(warnings, errors),
warnings,
errors
);
check!(
add_module_constant_decls(namespace, register_sequencer, asm_buf, ast_namespace),
return err(warnings, errors),
warnings,
errors
);
ok((), warnings, errors)
}
fn add_global_constant_decls<'sc>(
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
asm_buf: &mut Vec<Op<'sc>>,
declarations: &[TypedDeclaration<'sc>],
) -> CompileResult<'sc, ()> {
let mut warnings = vec![];
let mut errors = vec![];
for declaration in declarations {
if let TypedDeclaration::ConstantDeclaration(decl) = declaration {
let mut ops = check!(
convert_constant_decl_to_asm(decl, namespace, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
asm_buf.append(&mut ops);
}
}
ok((), warnings, errors)
}
fn add_module_constant_decls<'sc>(
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
asm_buf: &mut Vec<Op<'sc>>,
ast_namespace: &Namespace<'sc>,
) -> CompileResult<'sc, ()> {
let mut warnings = vec![];
let mut errors = vec![];
for ns in ast_namespace.get_all_imported_modules() {
for decl in ns.get_all_declared_symbols() {
if let TypedDeclaration::ConstantDeclaration(decl) = decl {
let mut ops = check!(
convert_constant_decl_to_asm(decl, namespace, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
asm_buf.append(&mut ops);
}
}
check!(
add_module_constant_decls(namespace, register_sequencer, asm_buf, ns),
return err(warnings, errors),
warnings,
errors
);
}
ok((), warnings, errors)
}
type JumpDestination = Vec<([u8; 4], Label)>;
type AbiFunctionOpcodeBuffer<'sc> = Vec<Op<'sc>>;
type SerializedAbiFunction<'sc> = (JumpDestination, AbiFunctionOpcodeBuffer<'sc>);
fn compile_contract_to_selectors<'sc>(
abi_entries: Vec<TypedFunctionDeclaration<'sc>>,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, SerializedAbiFunction<'sc>> {
let mut warnings = vec![];
let mut errors = vec![];
let mut selectors_labels_buf = vec![];
let mut asm_buf = vec![];
for decl in abi_entries {
if decl.parameters.len() != 4 {
errors.push(CompileError::InvalidNumberOfAbiParams {
span: decl.parameters_span(),
});
continue;
}
let cgas_name = decl.parameters[0].name.clone();
let bal_name = decl.parameters[1].name.clone();
let coin_color_name = decl.parameters[2].name.clone();
let user_argument_name = decl.parameters[3].name.clone();
let selector = check!(decl.to_fn_selector_value(), [0u8; 4], warnings, errors);
let fn_label = register_sequencer.get_label();
asm_buf.push(Op::jump_label(fn_label.clone(), decl.span.clone()));
let user_argument_register = register_sequencer.next();
let cgas_register = register_sequencer.next();
let bal_register = register_sequencer.next();
let coin_color_register = register_sequencer.next();
asm_buf.push(load_user_argument(user_argument_register.clone()));
asm_buf.push(load_cgas(cgas_register.clone()));
asm_buf.push(load_bal(bal_register.clone()));
asm_buf.push(load_coin_color(coin_color_register.clone()));
asm_buf.append(&mut check!(
convert_abi_fn_to_asm(
&decl,
(user_argument_name, user_argument_register),
(cgas_name, cgas_register),
(bal_name, bal_register),
(coin_color_name, coin_color_register),
namespace,
register_sequencer
),
vec![],
warnings,
errors
));
selectors_labels_buf.push((selector, fn_label));
}
ok((selectors_labels_buf, asm_buf), warnings, errors)
}
fn load_user_argument<'sc>(return_register: VirtualRegister) -> Op<'sc> {
Op {
opcode: Either::Left(VirtualOp::LW(
return_register,
VirtualRegister::Constant(ConstantRegister::FramePointer),
VirtualImmediate12::new_unchecked(74, "infallible constant 74"),
)),
comment: "loading argument into abi function".into(),
owning_span: None,
}
}
fn load_cgas<'sc>(return_register: VirtualRegister) -> Op<'sc> {
Op {
opcode: Either::Left(VirtualOp::LW(
return_register,
VirtualRegister::Constant(ConstantRegister::ContextGas),
VirtualImmediate12::new_unchecked(0, "infallible constant 0"),
)),
comment: "loading cgas into abi function".into(),
owning_span: None,
}
}
fn load_bal<'sc>(return_register: VirtualRegister) -> Op<'sc> {
Op {
opcode: Either::Left(VirtualOp::LW(
return_register,
VirtualRegister::Constant(ConstantRegister::Balance),
VirtualImmediate12::new_unchecked(0, "infallible constant 0"),
)),
comment: "loading coin balance into abi function".into(),
owning_span: None,
}
}
fn load_coin_color<'sc>(return_register: VirtualRegister) -> Op<'sc> {
Op {
opcode: Either::Left(VirtualOp::LW(
return_register,
VirtualRegister::Constant(ConstantRegister::FramePointer),
VirtualImmediate12::new_unchecked(5, "infallible constant 5"),
)),
comment: "loading coin color into abi function".into(),
owning_span: None,
}
}
fn ret_or_retd_value<'sc>(
func: &TypedFunctionDeclaration<'sc>,
return_register: VirtualRegister,
register_sequencer: &mut RegisterSequencer,
namespace: &mut AsmNamespace<'sc>,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let mut errors = vec![];
let warnings = vec![];
let mut asm_buf = vec![];
let main_func_ret_ty: TypeInfo = match resolve_type(func.return_type, &func.return_type_span) {
Ok(o) => o,
Err(e) => {
errors.push(e.into());
return err(warnings, errors);
}
};
if main_func_ret_ty == TypeInfo::Unit {
return ok(
vec![Op {
opcode: Either::Left(VirtualOp::RET(VirtualRegister::Constant(
ConstantRegister::Zero,
))),
owning_span: Some(func.return_type_span.clone()),
comment: format!("fn {} returns unit", func.name.primary_name),
}],
warnings,
errors,
);
}
let span = crate::Span {
span: pest::Span::new("TODO(static span)", 0, 0).unwrap(),
path: None,
};
let size_of_main_func_return_bytes = main_func_ret_ty.size_in_words(&span).expect(
"TODO(static span): Internal error: Static spans will allow for a proper error here.",
) * 8;
if size_of_main_func_return_bytes <= 8 {
asm_buf.push(Op {
owning_span: None,
opcode: Either::Left(VirtualOp::RET(return_register)),
comment: format!("{} fn return value", func.name.primary_name),
});
} else {
let rb_register = register_sequencer.next();
let size_bytes = namespace.insert_data_value(&Literal::U64(size_of_main_func_return_bytes));
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::LWDataId(rb_register.clone(), size_bytes)),
owning_span: Some(func.return_type_span.clone()),
comment: "loading rB for RETD".into(),
});
asm_buf.push(Op {
owning_span: None,
opcode: Either::Left(VirtualOp::RETD(return_register, rb_register)),
comment: format!("{} fn return value", func.name.primary_name),
});
}
ok(asm_buf, warnings, errors)
}