use crate::{
asm_lang::{
allocated_ops::AllocatedRegister, virtual_register::*, Label, Op, OrganizationalOp,
VirtualImmediate12, VirtualOp,
},
parse_tree::Literal,
};
use std::{collections::BTreeSet, fmt};
use either::Either;
mod abstract_instruction_set;
pub(crate) mod checks;
pub(crate) mod compiler_constants;
mod data_section;
mod finalized_asm;
pub mod from_ir;
mod instruction_set;
mod jump_optimized_asm_set;
mod register_allocated_asm_set;
pub(crate) mod register_allocator;
mod register_sequencer;
pub use finalized_asm::FinalizedAsm;
use abstract_instruction_set::*;
pub(crate) use data_section::*;
use instruction_set::*;
use jump_optimized_asm_set::*;
use register_allocated_asm_set::*;
use register_sequencer::*;
pub enum SwayAsmSet {
ContractAbi {
data_section: DataSection,
program_section: AbstractInstructionSet,
},
ScriptMain {
data_section: DataSection,
program_section: AbstractInstructionSet,
},
#[allow(dead_code)]
PredicateMain {
data_section: DataSection,
program_section: AbstractInstructionSet,
},
#[allow(dead_code)]
Library,
}
#[derive(Debug)]
struct RegisterAllocationStatus {
reg: AllocatedRegister,
used_by: BTreeSet<VirtualRegister>,
}
#[derive(Debug)]
pub(crate) struct RegisterPool {
registers: Vec<RegisterAllocationStatus>,
}
impl RegisterPool {
fn init() -> Self {
let reg_pool: Vec<RegisterAllocationStatus> = (0
..compiler_constants::NUM_ALLOCATABLE_REGISTERS)
.map(|x| RegisterAllocationStatus {
reg: AllocatedRegister::Allocated(x),
used_by: BTreeSet::new(),
})
.collect();
Self {
registers: reg_pool,
}
}
pub(crate) fn get_register(
&self,
virtual_register: &VirtualRegister,
) -> Option<AllocatedRegister> {
let allocated_reg =
self.registers
.iter()
.find(|RegisterAllocationStatus { reg: _, used_by }| {
used_by.contains(virtual_register)
});
allocated_reg.map(|RegisterAllocationStatus { reg, used_by: _ }| reg.clone())
}
}
impl fmt::Display for SwayAsmSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SwayAsmSet::ScriptMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
SwayAsmSet::PredicateMain {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
SwayAsmSet::ContractAbi {
data_section,
program_section,
} => write!(f, "{}\n{}", program_section, data_section),
SwayAsmSet::Library => write!(f, ""),
}
}
}
impl SwayAsmSet {
pub(crate) fn remove_unnecessary_jumps(self) -> JumpOptimizedAsmSet {
match self {
SwayAsmSet::ScriptMain {
data_section,
program_section,
} => JumpOptimizedAsmSet::ScriptMain {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
SwayAsmSet::PredicateMain {
data_section,
program_section,
} => JumpOptimizedAsmSet::PredicateMain {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
SwayAsmSet::Library {} => JumpOptimizedAsmSet::Library,
SwayAsmSet::ContractAbi {
data_section,
program_section,
} => JumpOptimizedAsmSet::ContractAbi {
data_section,
program_section: program_section.remove_sequential_jumps(),
},
}
}
}
fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op; 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(
register_sequencer: &mut RegisterSequencer,
data_section: &mut DataSection,
selectors_and_labels: Vec<([u8; 4], Label)>,
) -> Vec<Op> {
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 =
data_section.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::JumpIfNotZero(
comparison_result_register,
label,
)),
comment: "jump to selected function".into(),
owning_span: None,
});
}
asm_buf.push(Op {
opcode: Either::Left(VirtualOp::RVRT(VirtualRegister::Constant(
ConstantRegister::Zero,
))),
comment: "revert if no selectors matched".into(),
owning_span: None,
});
asm_buf
}