diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index b1cb553799e..a815339d913 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,3 +15,4 @@ either = "1.6" Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" +fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 8b137891791..5c7dbbd649c 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1 +1,4 @@ - +pub(crate) const NUM_FREE_REGISTERS: u8 = 48; +pub(crate) const TWENTY_FOUR_BITS: u64 = 0b111_111_111_111_111_111_111_111; +pub(crate) const EIGHTEEN_BITS: u64 = 0b111_111_111_111_111_111; +pub(crate) const TWELVE_BITS: u64 = 0b111_111_111_111; diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index a5f8ad857f2..da6998c69e1 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -1,18 +1,25 @@ -use crate::asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer}; -use crate::asm_lang::{ConstantRegister, Op, Opcode, RegisterId}; +use crate::asm_generation::{ + compiler_constants::*, convert_expression_to_asm, AsmNamespace, RegisterSequencer, +}; +use crate::asm_lang::{ + virtual_ops::{ + ConstantRegister, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, + VirtualRegister, + }, + Op, +}; use crate::error::*; use crate::semantic_analysis::ast_node::TypedEnumDeclaration; use crate::semantic_analysis::TypedExpression; use crate::Literal; use crate::{CompileResult, Ident}; -use std::convert::TryFrom; pub(crate) fn convert_enum_instantiation_to_asm<'sc>( decl: &TypedEnumDeclaration<'sc>, _variant_name: &Ident<'sc>, tag: usize, contents: &Option>>, - return_register: &RegisterId, + return_register: &VirtualRegister, namespace: &mut AsmNamespace<'sc>, register_sequencer: &mut RegisterSequencer, ) -> CompileResult<'sc, Vec>> { @@ -35,25 +42,35 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( // copy stack pointer into pointer register asm_buf.push(Op::unowned_register_move_comment( pointer_register.clone(), - RegisterId::Constant(ConstantRegister::StackPointer), + VirtualRegister::Constant(ConstantRegister::StackPointer), "load $sp for enum pointer", )); let size_of_enum = 1 /* tag */ + decl.as_type().stack_size_of(); - let size_of_enum: u32 = match u32::try_from(size_of_enum) { - Ok(o) if o < 16777216 /* 2^24 */ => o, - _ => { - errors.push(CompileError::Unimplemented( - "Stack variables which exceed 2^24 (16777216) words in size are not supported yet.", - decl.clone().span, - )); - return err(warnings, errors); - } - }; + if size_of_enum > EIGHTEEN_BITS { + errors.push(CompileError::Unimplemented( + "Stack variables which exceed 2^18 words in size are not supported yet.", + decl.clone().span, + )); + return err(warnings, errors); + } - asm_buf.push(Op::unowned_stack_allocate_memory(size_of_enum)); + asm_buf.push(Op::unowned_stack_allocate_memory( + VirtualImmediate24::new_unchecked( + size_of_enum, + "this size is manually checked to be lower than 2^24", + ), + )); // initialize all the memory to 0 + // there are only 18 bits of immediate in MCLI so we need to do this in multiple passes, + // This is not yet implemented, so instead we just limit enum size to 2^18 words asm_buf.push(Op::new( - Opcode::MemClearImmediate(pointer_register.clone(), size_of_enum), + VirtualOp::MCLI( + pointer_register.clone(), + VirtualImmediate18::new_unchecked( + size_of_enum, + "the enum was manually checked to be under 2^18 words in size", + ), + ), decl.clone().span, )); // write the tag @@ -61,7 +78,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( asm_buf.push(Op::write_register_to_memory( pointer_register.clone(), tag_register.clone(), - 0, + VirtualImmediate12::new_unchecked(0, "constant num; infallible"), decl.clone().span, )); @@ -86,7 +103,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( asm_buf.push(Op::write_register_to_memory_comment( pointer_register.clone(), return_register.clone(), - 1, /* offset by 1 because the tag was already written */ + VirtualImmediate12::new_unchecked(1, "this is the constant 1; infallible"), // offset by 1 because the tag was already written instantiation.span.clone(), format!("{} enum contents", decl.name.primary_name), )); diff --git a/core_lang/src/asm_generation/expression/if_exp.rs b/core_lang/src/asm_generation/expression/if_exp.rs index 3d14658e3a4..fdea8f22dcd 100644 --- a/core_lang/src/asm_generation/expression/if_exp.rs +++ b/core_lang/src/asm_generation/expression/if_exp.rs @@ -1,5 +1,8 @@ use crate::asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer}; -use crate::asm_lang::{ConstantRegister, Op, RegisterId}; +use crate::asm_lang::{ + virtual_ops::{ConstantRegister, VirtualRegister}, + Op, +}; use crate::error::*; use crate::semantic_analysis::TypedExpression; @@ -10,7 +13,7 @@ pub(crate) fn convert_if_exp_to_asm<'sc>( condition: &TypedExpression<'sc>, then: &TypedExpression<'sc>, r#else: &Option>>, - return_register: &RegisterId, + return_register: &VirtualRegister, namespace: &mut AsmNamespace<'sc>, register_sequencer: &mut RegisterSequencer, ) -> CompileResult<'sc, Vec>> { @@ -50,7 +53,7 @@ pub(crate) fn convert_if_exp_to_asm<'sc>( // if the condition is not true, jump to the else branch (if there is one). asm_buf.push(Op::jump_if_not_equal( condition_result.clone(), - RegisterId::Constant(ConstantRegister::One), + VirtualRegister::Constant(ConstantRegister::One), if r#else.is_some() { else_label.clone() } else { diff --git a/core_lang/src/asm_generation/expression/mod.rs b/core_lang/src/asm_generation/expression/mod.rs index ab478e63d14..36df4cc8ffa 100644 --- a/core_lang/src/asm_generation/expression/mod.rs +++ b/core_lang/src/asm_generation/expression/mod.rs @@ -1,5 +1,8 @@ use super::*; -use crate::{asm_lang::*, parse_tree::CallPath}; +use crate::{ + asm_lang::{virtual_ops::VirtualRegister, *}, + parse_tree::CallPath, +}; use crate::{ parse_tree::Literal, semantic_analysis::{ @@ -23,7 +26,7 @@ use subfield::convert_subfield_expression_to_asm; pub(crate) fn convert_expression_to_asm<'sc>( exp: &TypedExpression<'sc>, namespace: &mut AsmNamespace<'sc>, - return_register: &RegisterId, + return_register: &VirtualRegister, register_sequencer: &mut RegisterSequencer, ) -> CompileResult<'sc, Vec>> { let mut warnings = vec![]; @@ -83,7 +86,7 @@ pub(crate) fn convert_expression_to_asm<'sc>( let mut errors = vec![]; // Keep track of the mapping from the declared names of the registers to the actual // registers from the sequencer for replacement - let mut mapping_of_real_registers_to_declared_names: HashMap<&str, RegisterId> = + let mut mapping_of_real_registers_to_declared_names: HashMap<&str, VirtualRegister> = Default::default(); for TypedAsmRegisterDeclaration { name, @@ -149,11 +152,16 @@ pub(crate) fn convert_expression_to_asm<'sc>( } Ok(o) => Some(o), }) - .collect::>(); + .collect::>(); // parse the actual op and registers let opcode = type_check!( - Op::parse_opcode(&op.op_name, replaced_registers.as_slice(), op.immediate), + Op::parse_opcode( + &op.op_name, + replaced_registers.as_slice(), + &op.immediate, + op.span.clone() + ), continue, warnings, errors @@ -259,12 +267,12 @@ pub(crate) fn convert_expression_to_asm<'sc>( /// or finds nothing and returns `None`. fn realize_register( register_name: &str, - mapping_of_real_registers_to_declared_names: &HashMap<&str, RegisterId>, -) -> Option { + mapping_of_real_registers_to_declared_names: &HashMap<&str, VirtualRegister>, +) -> Option { match mapping_of_real_registers_to_declared_names.get(register_name) { Some(x) => Some(x.clone()), None => match ConstantRegister::parse_register_name(register_name) { - Some(x) => Some(RegisterId::Constant(x)), + Some(x) => Some(VirtualRegister::Constant(x)), None => None, }, } @@ -275,7 +283,7 @@ pub(crate) fn convert_code_block_to_asm<'sc>( namespace: &mut AsmNamespace<'sc>, register_sequencer: &mut RegisterSequencer, // Where to put the return value of this code block, if there was any. - return_register: Option<&RegisterId>, + return_register: Option<&VirtualRegister>, ) -> CompileResult<'sc, Vec>> { let mut asm_buf: Vec = vec![]; let mut warnings = vec![]; @@ -305,11 +313,11 @@ pub(crate) fn convert_code_block_to_asm<'sc>( ok(asm_buf, warnings, errors) } -/// Initializes [Literal] `lit` into [RegisterId] `return_register`. +/// Initializes [Literal] `lit` into [VirtualRegister] `return_register`. fn convert_literal_to_asm<'sc>( lit: &Literal<'sc>, namespace: &mut AsmNamespace<'sc>, - return_register: &RegisterId, + return_register: &VirtualRegister, _register_sequencer: &mut RegisterSequencer, span: Span<'sc>, ) -> Vec> { @@ -329,7 +337,7 @@ fn convert_fn_app_to_asm<'sc>( arguments: &[(Ident<'sc>, TypedExpression<'sc>)], function_body: &TypedCodeBlock<'sc>, parent_namespace: &mut AsmNamespace<'sc>, - return_register: &RegisterId, + return_register: &VirtualRegister, register_sequencer: &mut RegisterSequencer, ) -> CompileResult<'sc, Vec>> { let mut warnings = vec![]; @@ -338,7 +346,7 @@ fn convert_fn_app_to_asm<'sc>( // Make a local namespace so that the namespace of this function does not pollute the outer // scope let mut namespace = parent_namespace.clone(); - let mut args_and_registers: HashMap, RegisterId> = Default::default(); + let mut args_and_registers: HashMap, VirtualRegister> = Default::default(); // evaluate every expression being passed into the function for (name, arg) in arguments { let return_register = register_sequencer.next(); diff --git a/core_lang/src/asm_generation/expression/structs.rs b/core_lang/src/asm_generation/expression/structs.rs index 3d23f36e8ac..3b45fa85b23 100644 --- a/core_lang/src/asm_generation/expression/structs.rs +++ b/core_lang/src/asm_generation/expression/structs.rs @@ -1,13 +1,15 @@ //! This module contains the logic for struct layout in memory and instantiation. use crate::{ asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer}, - asm_lang::{ConstantRegister, Op, RegisterId}, + asm_lang::{ + virtual_ops::{ConstantRegister, VirtualImmediate12, VirtualImmediate24, VirtualRegister}, + Op, + }, error::*, semantic_analysis::ast_node::TypedStructExpressionField, types::{IntegerBits, MaybeResolvedType, PartiallyResolvedType, ResolvedType}, CompileResult, Ident, }; -use std::convert::TryInto; pub(crate) fn convert_struct_expression_to_asm<'sc>( struct_name: &Ident<'sc>, @@ -62,38 +64,31 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( let struct_beginning_pointer = register_sequencer.next(); asm_buf.push(Op::unowned_register_move( struct_beginning_pointer.clone(), - RegisterId::Constant(ConstantRegister::StackPointer), + VirtualRegister::Constant(ConstantRegister::StackPointer), )); // step 2 // decide how many call frame extensions are needed based on the size of the struct // and how many bits can be put in a single cfei op - let twenty_four_bits = 0b111111111111111111111111; - let number_of_allocations_necessary = (total_size / twenty_four_bits) + 1; + // limit struct size to 12 bits for now, for simplicity + let twelve_bits = super::compiler_constants::TWELVE_BITS; + let number_of_allocations_necessary = (total_size / twelve_bits) + 1; // construct the allocation ops for allocation_index in 0..number_of_allocations_necessary { - let left_to_allocate = total_size - (allocation_index * twenty_four_bits); - let this_allocation = if left_to_allocate > twenty_four_bits { - twenty_four_bits + let left_to_allocate = total_size - (allocation_index * twelve_bits); + let this_allocation = if left_to_allocate > twelve_bits { + twelve_bits } else { left_to_allocate }; - // since the size of `this_allocation` is bound by the size of 2^24, we know that - // downcasting to a u32 is safe. - // However, since we may change the twenty four bits to something else, we want to check - // anyway - let val_as_u32: u32 = match this_allocation.try_into() { - Ok(o) => o, - Err(_) => { - errors.push(CompileError::Unimplemented( - "This struct is too large, and would not fit in one call frame extension.", - struct_name.span.clone(), - )); - return err(warnings, errors); - } - }; - asm_buf.push(Op::unowned_stack_allocate_memory(val_as_u32)); + // we call `new_unchecked` here because we have validated the size is okay above + asm_buf.push(Op::unowned_stack_allocate_memory( + VirtualImmediate24::new_unchecked( + this_allocation, + "struct size was checked manually to be within 12 bits", + ), + )); } // step 3 @@ -124,7 +119,7 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( asm_buf.push(Op::write_register_to_memory( struct_beginning_pointer.clone(), return_register, - offset, + VirtualImmediate12::new_unchecked(offset, "the whole struct is less than 12 bits so every individual field should be as well."), name.span.clone(), )); // TODO: if the struct needs multiple allocations, this offset could exceed the size of the @@ -134,7 +129,7 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( // from john about the above: As a TODO, maybe let's just restrict the maximum size of // something (I don't know exactly what) at the consensus level so this case is guaranteed // to never be hit. - offset += value_stack_size as u32; + offset += value_stack_size; } ok(asm_buf, warnings, errors) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index dce89f3f76d..51d6baa6c18 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -1,7 +1,14 @@ use std::{collections::HashMap, fmt}; use crate::{ - asm_lang::{Label, Op, OrganizationalOp, RegisterId}, + asm_lang::{ + allocated_ops::{AllocatedOp, AllocatedRegister}, + virtual_ops::{ + ConstantRegister, Label, VirtualImmediate12, VirtualImmediate24, VirtualOp, + VirtualRegister, + }, + Op, OrganizationalOp, RealizedOp, + }, error::*, parse_tree::Literal, semantic_analysis::{TypedAstNode, TypedAstNodeContent, TypedParseTree}, @@ -9,7 +16,7 @@ use crate::{ }; use either::Either; -mod compiler_constants; +pub(crate) mod compiler_constants; mod declaration; mod expression; mod register_sequencer; @@ -68,12 +75,62 @@ pub enum HllAsmSet<'sc> { Library, } -/// The [AbstractInstructionSet] is the list of register namespaces and operations existing -/// within those namespaces in order. +/// An [AbstractInstructionSet] is a set of instructions that use entirely virtual registers +/// and excessive moves, with the intention of later optimizing it. +#[derive(Clone)] pub struct AbstractInstructionSet<'sc> { ops: Vec>, } +/// "Realized" here refers to labels -- there are no more organizational +/// ops or labels. In this struct, they are all "realized" to offsets. +pub struct RealizedAbstractInstructionSet<'sc> { + ops: Vec>, +} + +impl<'sc> RealizedAbstractInstructionSet<'sc> { + fn allocate_registers(self) -> InstructionSet<'sc> { + // Eventually, we will use a cool graph-coloring algorithm. + // For now, just keep a pool of registers and return + // registers when they are not read anymore + + // construct a mapping from every op to the registers it uses + let op_register_mapping = self + .ops + .into_iter() + .map(|op| { + ( + op.clone(), + op.opcode + .registers() + .into_iter() + .map(|x| x.clone()) + .collect(), + ) + }) + .collect::>(); + + // get registers from the pool. + 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 } + } +} + +/// An [InstructionSet] is produced by allocating registers on an [AbstractInstructionSet]. +pub struct InstructionSet<'sc> { + ops: Vec>, +} + type Data<'sc> = Literal<'sc>; impl<'sc> AbstractInstructionSet<'sc> { /// Removes any jumps that jump to the subsequent line @@ -118,6 +175,149 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } + + /// Runs two passes -- one to get the instruction offsets of the labels + /// and one to replace the labels in the organizational ops + fn realize_labels(self, data_section: &DataSection) -> 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); + } + // these ops will end up being exactly one op, so the counter goes up one + Either::Right(OrganizationalOp::Ld(..)) => counter += 2, + Either::Right(OrganizationalOp::Jump(..)) + | Either::Right(OrganizationalOp::JumpIfNotEq(..)) + | Either::Left(_) => { + counter += 1; + } + Either::Right(OrganizationalOp::Comment) => (), + } + } + + 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::Ld(reg, data_lab) => { + let data = data_section.value_pairs[data_lab.0 as usize].clone(); + // TODO force_to_imm() is very very bad. see it for details + realized_ops.push(RealizedOp { + opcode: VirtualOp::ORI( + reg, + VirtualRegister::Constant(ConstantRegister::Zero), + data.force_to_imm(), + ), + owning_span, + comment, + }); + } + 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::Comment => continue, + OrganizationalOp::Label(..) => continue, + }, + }; + } + RealizedAbstractInstructionSet { ops: realized_ops } + } +} + +struct RegisterAllocationStatus { + reg: AllocatedRegister, + in_use: Option, +} +pub(crate) struct RegisterPool { + registers: Vec, +} + +impl RegisterPool { + fn init() -> Self { + let register_pool: Vec = (0 + ..compiler_constants::NUM_FREE_REGISTERS) + .rev() + .map(|x| RegisterAllocationStatus { + reg: AllocatedRegister::Allocated(x), + in_use: None, + }) + .collect(); + Self { + registers: register_pool, + } + } + + /// Checks if any currently used registers are no longer in use, updates the pool, + /// and grabs an available register. + pub(crate) fn get_register( + &mut self, + virtual_register: &VirtualRegister, + op_register_mapping: &[(RealizedOp, std::collections::HashSet)], + ) -> Option { + // scan to see if any of the old ones are no longer in use + 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; + } + } + // find the next unused register, return it, flip assign it + 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)], +) -> bool { + !ops.iter().any(|(_, regs)| regs.contains(reg)) } /// helper function to check if a label is used in a given buffer of ops @@ -169,11 +369,11 @@ impl fmt::Display for HllAsmSet<'_> { HllAsmSet::ScriptMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), HllAsmSet::PredicateMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), HllAsmSet::ContractAbi { .. } => write!(f, "TODO contract ABI asm is unimplemented"), // Libraries do not directly generate any asm. HllAsmSet::Library => write!(f, ""), @@ -187,11 +387,11 @@ impl fmt::Display for JumpOptimizedAsmSet<'_> { JumpOptimizedAsmSet::ScriptMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), JumpOptimizedAsmSet::PredicateMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), JumpOptimizedAsmSet::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } @@ -201,17 +401,15 @@ impl fmt::Display for JumpOptimizedAsmSet<'_> { } } -impl fmt::Display for RegisterAllocatedAsmSet<'_> { +impl<'sc> fmt::Display for RegisterAllocatedAsmSet<'sc> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RegisterAllocatedAsmSet::ScriptMain { - data_section, - program_section, - } => write!(f, "{}\n{}", data_section, program_section), - RegisterAllocatedAsmSet::PredicateMain { - data_section, - program_section, - } => write!(f, "{}\n{}", data_section, program_section), + RegisterAllocatedAsmSet::ScriptMain { program_section } => { + write!(f, "{}", program_section) + } + RegisterAllocatedAsmSet::PredicateMain { program_section } => { + write!(f, "{}", program_section) + } RegisterAllocatedAsmSet::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } @@ -221,17 +419,11 @@ impl fmt::Display for RegisterAllocatedAsmSet<'_> { } } -impl fmt::Display for FinalizedAsm<'_> { +impl<'sc> fmt::Display for FinalizedAsm<'sc> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FinalizedAsm::ScriptMain { - data_section, - program_section, - } => write!(f, "{}\n{}", data_section, program_section), - FinalizedAsm::PredicateMain { - data_section, - program_section, - } => write!(f, "{}\n{}", data_section, program_section), + FinalizedAsm::ScriptMain { program_section } => write!(f, "{}", program_section,), + FinalizedAsm::PredicateMain { program_section } => write!(f, "{}", program_section,), FinalizedAsm::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } @@ -255,10 +447,24 @@ impl fmt::Display for AbstractInstructionSet<'_> { } } +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::>() + .join("\n") + ) + } +} + #[derive(Default, Clone)] pub(crate) struct AsmNamespace<'sc> { data_section: DataSection<'sc>, - variables: HashMap, RegisterId>, + variables: HashMap, VirtualRegister>, } /// An address which refers to a value in the data section of the asm. @@ -272,7 +478,11 @@ impl fmt::Display for DataId { } impl<'sc> AsmNamespace<'sc> { - pub(crate) fn insert_variable(&mut self, var_name: Ident<'sc>, register_location: RegisterId) { + 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 { @@ -292,7 +502,7 @@ impl<'sc> AsmNamespace<'sc> { pub(crate) fn look_up_variable( &self, var_name: &Ident<'sc>, - ) -> CompileResult<'sc, &RegisterId> { + ) -> CompileResult<'sc, &VirtualRegister> { match self.variables.get(&var_name) { Some(o) => ok(o, vec![], vec![]), None => err( @@ -309,7 +519,7 @@ impl<'sc> AsmNamespace<'sc> { pub(crate) fn compile_ast_to_asm<'sc>( ast: TypedParseTree<'sc>, -) -> CompileResult<'sc, FinalizedAsm<'sc>> { +) -> CompileResult<'sc, FinalizedAsm> { let mut register_sequencer = RegisterSequencer::new(); let mut warnings = vec![]; let mut errors = vec![]; @@ -405,15 +615,18 @@ impl<'sc> JumpOptimizedAsmSet<'sc> { data_section, program_section, } => RegisterAllocatedAsmSet::ScriptMain { - data_section, - program_section, + program_section: program_section + .clone() + .realize_labels(&data_section) + .allocate_registers(), }, JumpOptimizedAsmSet::PredicateMain { data_section, program_section, } => RegisterAllocatedAsmSet::PredicateMain { - data_section, - program_section, + program_section: program_section + .realize_labels(&data_section) + .allocate_registers(), }, JumpOptimizedAsmSet::ContractAbi => RegisterAllocatedAsmSet::ContractAbi, } @@ -438,12 +651,10 @@ pub enum JumpOptimizedAsmSet<'sc> { pub enum RegisterAllocatedAsmSet<'sc> { ContractAbi, ScriptMain { - data_section: DataSection<'sc>, - program_section: AbstractInstructionSet<'sc>, + program_section: InstructionSet<'sc>, }, PredicateMain { - data_section: DataSection<'sc>, - program_section: AbstractInstructionSet<'sc>, + program_section: InstructionSet<'sc>, }, // Libraries do not generate any asm. Library, @@ -454,20 +665,12 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { // TODO implement this -- noop for now match self { RegisterAllocatedAsmSet::Library => FinalizedAsm::Library, - RegisterAllocatedAsmSet::ScriptMain { - data_section, - program_section, - } => FinalizedAsm::ScriptMain { - data_section, - program_section, - }, - RegisterAllocatedAsmSet::PredicateMain { - data_section, - program_section, - } => FinalizedAsm::PredicateMain { - data_section, - program_section, - }, + RegisterAllocatedAsmSet::ScriptMain { program_section } => { + FinalizedAsm::ScriptMain { program_section } + } + RegisterAllocatedAsmSet::PredicateMain { program_section } => { + FinalizedAsm::PredicateMain { program_section } + } RegisterAllocatedAsmSet::ContractAbi => FinalizedAsm::ContractAbi, } } @@ -478,12 +681,10 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { pub enum FinalizedAsm<'sc> { ContractAbi, ScriptMain { - data_section: DataSection<'sc>, - program_section: AbstractInstructionSet<'sc>, + program_section: InstructionSet<'sc>, }, PredicateMain { - data_section: DataSection<'sc>, - program_section: AbstractInstructionSet<'sc>, + program_section: InstructionSet<'sc>, }, // Libraries do not generate any asm. Library, @@ -499,7 +700,7 @@ fn convert_node_to_asm<'sc>( namespace: &mut AsmNamespace<'sc>, register_sequencer: &mut RegisterSequencer, // Where to put the return value of this node, if it is needed. - return_register: Option<&RegisterId>, + return_register: Option<&VirtualRegister>, ) -> CompileResult<'sc, NodeAsmResult<'sc>> { let mut warnings = vec![]; let mut errors = vec![]; diff --git a/core_lang/src/asm_generation/register_sequencer.rs b/core_lang/src/asm_generation/register_sequencer.rs index 588a1dde975..e1556c11b0d 100644 --- a/core_lang/src/asm_generation/register_sequencer.rs +++ b/core_lang/src/asm_generation/register_sequencer.rs @@ -1,4 +1,4 @@ -use crate::asm_lang::{Label, RegisterId}; +use crate::asm_lang::virtual_ops::{Label, VirtualRegister}; /// The [RegisterSequencer] is basically an iterator over integers -- it distributes unique ids in /// the form of integers while ASM is being generated to ensure a monotonically increasing unique /// register Id for every virtual register that is used. @@ -14,10 +14,10 @@ impl RegisterSequencer { } /// Choosing to not use the iterator trait, because this iterator goes on forever and thusly /// does not need to return an `Option`. - pub(crate) fn next(&mut self) -> RegisterId { + pub(crate) fn next(&mut self) -> VirtualRegister { let next_val = self.next_register.clone(); self.next_register += 1; - RegisterId::Virtual(next_val.to_string()) + VirtualRegister::Virtual(next_val.to_string()) } pub(crate) fn get_label(&mut self) -> Label { let next_val = self.next_jump_label.clone(); diff --git a/core_lang/src/asm_generation/while_loop.rs b/core_lang/src/asm_generation/while_loop.rs index c979fac6368..486aade1624 100644 --- a/core_lang/src/asm_generation/while_loop.rs +++ b/core_lang/src/asm_generation/while_loop.rs @@ -1,5 +1,5 @@ use super::*; -use crate::asm_lang::{ConstantRegister, Op}; +use crate::asm_lang::virtual_ops::{ConstantRegister, VirtualRegister}; use crate::semantic_analysis::ast_node::TypedWhileLoop; pub(super) fn convert_while_loop_to_asm<'sc>( r#loop: &TypedWhileLoop<'sc>, @@ -47,7 +47,7 @@ pub(super) fn convert_while_loop_to_asm<'sc>( // if it is FALSE, then jump to the end of the block. buf.push(Op::jump_if_not_equal( condition_result_register.into(), - RegisterId::Constant(ConstantRegister::One), + VirtualRegister::Constant(ConstantRegister::One), exit_label.clone(), )); diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs new file mode 100644 index 00000000000..4f243806f1e --- /dev/null +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -0,0 +1,235 @@ +//! This module contains abstracted versions of bytecode primitives that the compiler uses to +//! ensure correctness and safety. +//! +//! These ops are different from [VirtualOp]s in that they contain allocated registers, i.e. at +//! most 48 free registers plus reserved registers. These ops can be safely directly converted to +//! bytecode. +//! +//! +//! It is unfortunate that there are copies of our opcodes in multiple places, but this ensures the +//! best type safety. It can be macro'd someday. + +use super::virtual_ops::*; +use pest::Span; +use std::fmt; + +const COMMENT_START_COLUMN: usize = 30; + +/// Represents registers that have gone through register allocation. The value in the [Allocated] +/// variant is guaranteed to be between 0 and [compiler_constants::NUM_FREE_REGISTERS]. +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub enum AllocatedRegister { + Allocated(u8), + Constant(super::virtual_ops::ConstantRegister), +} + +impl fmt::Display for AllocatedRegister { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AllocatedRegister::Allocated(name) => write!(f, "$r{}", name), + AllocatedRegister::Constant(name) => { + write!(f, "{}", name) + } + } + } +} + +/// This enum is unfortunately a redundancy of the [fuel_asm::Opcode] and [crate::VirtualOp] enums. This variant, however, +/// allows me to use the compiler's internal [AllocatedRegister] types and maintain type safety +/// between virtual ops and those which have gone through register allocation. +/// A bit of copy/paste seemed worth it for that safety, +/// so here it is. +#[derive(Clone)] +pub(crate) enum AllocatedOpcode { + ADD(AllocatedRegister, AllocatedRegister, AllocatedRegister), + ADDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + AND(AllocatedRegister, AllocatedRegister, AllocatedRegister), + ANDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + DIV(AllocatedRegister, AllocatedRegister, AllocatedRegister), + DIVI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + EQ(AllocatedRegister, AllocatedRegister, AllocatedRegister), + EXP(AllocatedRegister, AllocatedRegister, AllocatedRegister), + EXPI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + GT(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MLOG(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MROO(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MOD(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MODI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + MOVE(AllocatedRegister, AllocatedRegister), + MUL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MULI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + NOT(AllocatedRegister, AllocatedRegister), + OR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + ORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + SLL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + SLLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + SRL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + SRLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + SUB(AllocatedRegister, AllocatedRegister, AllocatedRegister), + SUBI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + XOR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + XORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + CIMV(AllocatedRegister, AllocatedRegister, AllocatedRegister), + CTMV(AllocatedRegister, AllocatedRegister), + JI(VirtualImmediate24), + JNEI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + RET(AllocatedRegister), + CFEI(VirtualImmediate24), + CFSI(VirtualImmediate24), + LB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + LW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + ALOC(AllocatedRegister), + MCL(AllocatedRegister, AllocatedRegister), + MCLI(AllocatedRegister, VirtualImmediate18), + MCP(AllocatedRegister, AllocatedRegister, AllocatedRegister), + MEQ( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + SB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + SW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + BHSH(AllocatedRegister, AllocatedRegister), + BHEI(AllocatedRegister), + BURN(AllocatedRegister), + CALL( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + CCP( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + CROO(AllocatedRegister, AllocatedRegister), + CSIZ(AllocatedRegister, AllocatedRegister), + CB(AllocatedRegister), + LDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), + LOG( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + MINT(AllocatedRegister), + RVRT(AllocatedRegister), + SLDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), + SRW(AllocatedRegister, AllocatedRegister), + SRWQ(AllocatedRegister, AllocatedRegister), + SWW(AllocatedRegister, AllocatedRegister), + SWWQ(AllocatedRegister, AllocatedRegister), + TR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + TRO( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + ECR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + K256(AllocatedRegister, AllocatedRegister, AllocatedRegister), + S256(AllocatedRegister, AllocatedRegister, AllocatedRegister), + NOOP, + FLAG(AllocatedRegister), + Undefined, +} + +#[derive(Clone)] +pub(crate) struct AllocatedOp<'sc> { + pub(crate) opcode: AllocatedOpcode, + /// A descriptive comment for ASM readability + pub(crate) comment: String, + pub(crate) owning_span: Option>, +} + +impl<'sc> fmt::Display for AllocatedOp<'sc> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use AllocatedOpcode::*; + #[rustfmt::skip] + let string = match &self.opcode { + ADD(a, b, c) => format!("add {} {} {}", a, b, c), + ADDI(a, b, c) => format!("addi {} {} {}", a, b, c), + AND(a, b, c) => format!("and {} {} {}", a, b, c), + ANDI(a, b, c) => format!("andi {} {} {}", a, b, c), + DIV(a, b, c) => format!("div {} {} {}", a, b, c), + DIVI(a, b, c) => format!("divi {} {} {}", a, b, c), + EQ(a, b, c) => format!("eq {} {} {}", a, b, c), + EXP(a, b, c) => format!("exp {} {} {}", a, b, c), + EXPI(a, b, c) => format!("expi {} {} {}", a, b, c), + GT(a, b, c) => format!("gt {} {} {}", a, b, c), + MLOG(a, b, c) => format!("mlog {} {} {}", a, b, c), + MROO(a, b, c) => format!("mroo {} {} {}", a, b, c), + MOD(a, b, c) => format!("mod {} {} {}", a, b, c), + MODI(a, b, c) => format!("modi {} {} {}", a, b, c), + MOVE(a, b) => format!("move {} {}", a, b), + MUL(a, b, c) => format!("mul {} {} {}", a, b, c), + MULI(a, b, c) => format!("muli {} {} {}", a, b, c), + NOT(a, b) => format!("not {} {}", a, b), + OR(a, b, c) => format!("or {} {} {}", a, b, c), + ORI(a, b, c) => format!("ori {} {} {}", a, b, c), + SLL(a, b, c) => format!("sll {} {} {}", a, b, c), + SLLI(a, b, c) => format!("slli {} {} {}", a, b, c), + SRL(a, b, c) => format!("srl {} {} {}", a, b, c), + SRLI(a, b, c) => format!("srli {} {} {}", a, b, c), + SUB(a, b, c) => format!("sub {} {} {}", a, b, c), + SUBI(a, b, c) => format!("subi {} {} {}", a, b, c), + XOR(a, b, c) => format!("xor {} {} {}", a, b, c), + XORI(a, b, c) => format!("xori {} {} {}", a, b, c), + CIMV(a, b, c) => format!("cimv {} {} {}", a, b, c), + CTMV(a, b) => format!("ctmv {} {}", a, b), + JI(a) => format!("ji {}", a), + JNEI(a, b, c) => format!("jnei {} {} {}", a, b, c), + RET(a) => format!("ret {}", a), + CFEI(a) => format!("cfei {}", a), + CFSI(a) => format!("cfsi {}", a), + LB(a, b, c) => format!("lb {} {} {}", a, b, c), + LW(a, b, c) => format!("lw {} {} {}", a, b, c), + ALOC(a) => format!("aloc {}", a), + MCL(a, b) => format!("mcl {} {}", a, b), + MCLI(a, b) => format!("mcli {} {}", a, b), + MCP(a, b, c) => format!("mcp {} {} {}", a, b, c), + MEQ(a, b, c, d) => format!("meq {} {} {} {}", a, b, c, d), + SB(a, b, c) => format!("sb {} {} {}", a, b, c), + SW(a, b, c) => format!("sw {} {} {}", a, b, c), + BHSH(a, b) => format!("bhsh {} {}", a, b), + BHEI(a) => format!("bhei {}", a), + BURN(a) => format!("burn {}", a), + CALL(a, b, c, d)=> format!("call {} {} {} {}", a, b, c, d), + CCP(a, b, c, d) => format!("ccp {} {} {} {}", a, b, c, d), + CROO(a, b) => format!("croo {} {}", a, b), + CSIZ(a, b) => format!("csiz {} {}", a, b), + CB(a) => format!("cb {}", a), + LDC(a, b, c) => format!("ldc {} {} {}", a, b, c), + LOG(a, b, c, d) => format!("log {} {} {} {}", a, b, c, d), + MINT(a) => format!("mint {}", a), + RVRT(a) => format!("rvrt {}", a), + SLDC(a, b, c) => format!("sldc {} {} {}", a, b, c), + SRW(a, b) => format!("srw {} {}", a, b), + SRWQ(a, b) => format!("srwq {} {}", a, b), + SWW(a, b) => format!("sww {} {}", a, b), + SWWQ(a, b) => format!("swwq {} {}", a, b), + TR(a, b, c) => format!("tr {} {} {}", a, b, c), + TRO(a, b, c, d) => format!("tro {} {} {} {}", a, b, c, d), + ECR(a, b, c) => format!("ecr {} {} {}", a, b, c), + K256(a, b, c) => format!("k256 {} {} {}", a, b, c), + S256(a, b, c) => format!("s256 {} {} {}", a, b, c), + NOOP => "noop".to_string(), + FLAG(a) => format!("flag {}", a), + Undefined => format!("undefined op"), + }; + // we want the comment to always be COMMENT_START_COLUMN characters offset to the right + // to not interfere with the ASM but to be aligned + let mut op_and_comment = string; + if self.comment.len() > 0 { + while op_and_comment.len() < COMMENT_START_COLUMN { + op_and_comment.push_str(" "); + } + op_and_comment.push_str(&format!("; {}", self.comment)) + } + + write!(f, "{}", op_and_comment) + } +} diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 57841a654a6..dd2388f150b 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -9,93 +9,87 @@ use crate::{asm_generation::DataId, error::*, parse_tree::AsmRegister, Ident}; use either::Either; use pest::Span; use std::{collections::HashSet, fmt}; +use virtual_ops::{ + ConstantRegister, Label, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, + VirtualRegister, +}; + +pub(crate) mod allocated_ops; +pub(crate) mod virtual_ops; /// The column where the ; for comments starts const COMMENT_START_COLUMN: usize = 40; -#[macro_export] -macro_rules! opcodes { - ( - $( - $op:ident ( $($inits:ident),* ) = $val:expr - ),+ - ) => { - #[derive(Clone, PartialEq, Debug)] - pub enum Opcode { - $( - #[warn(unused_must_use)] - $op( $($inits),* ), - )+ - } - - $( - #[allow(non_upper_case_globals)] - const $op:u32 = $val; - )+ - - } -} - -impl From<&AsmRegister> for RegisterId { +impl From<&AsmRegister> for VirtualRegister { fn from(o: &AsmRegister) -> Self { - RegisterId::Virtual(o.name.clone()) + VirtualRegister::Virtual(o.name.clone()) } } #[derive(Clone)] pub(crate) struct Op<'sc> { - pub(crate) opcode: Either, - /// A descriptive comment for debugging + pub(crate) opcode: Either, + /// A descriptive comment for ASM readability + pub(crate) comment: String, + pub(crate) owning_span: Option>, +} + +#[derive(Clone)] +pub(crate) struct RealizedOp<'sc> { + pub(crate) opcode: VirtualOp, + /// A descriptive comment for ASM readability pub(crate) comment: String, pub(crate) owning_span: Option>, } impl<'sc> Op<'sc> { - /// Write value in given [RegisterId] `value_to_write` to given memory address that is held - /// within the [RegisterId] `destination_address` + /// Write value in given [VirtualRegister] `value_to_write` to given memory address that is held within the + /// [VirtualRegister] `destination_address` pub(crate) fn write_register_to_memory( - destination_address: RegisterId, - value_to_write: RegisterId, - offset: ImmediateValue, + destination_address: VirtualRegister, + value_to_write: VirtualRegister, + offset: VirtualImmediate12, span: Span<'sc>, ) -> Self { Op { - opcode: Either::Left(Opcode::Sw(destination_address, value_to_write, offset)), + opcode: Either::Left(VirtualOp::SW(destination_address, value_to_write, offset)), comment: String::new(), owning_span: Some(span), } } - /// Write value in given [RegisterId] `value_to_write` to given memory address that is held - /// within the [RegisterId] `destination_address`, with the provided comment. + /// Write value in given [VirtualRegister] `value_to_write` to given memory address that is held within the + /// [VirtualRegister] `destination_address`, with the provided comment. pub(crate) fn write_register_to_memory_comment( - destination_address: RegisterId, - value_to_write: RegisterId, - offset: ImmediateValue, + destination_address: VirtualRegister, + value_to_write: VirtualRegister, + offset: VirtualImmediate12, span: Span<'sc>, comment: impl Into, ) -> Self { Op { - opcode: Either::Left(Opcode::Sw(destination_address, value_to_write, offset)), + opcode: Either::Left(VirtualOp::SW(destination_address, value_to_write, offset)), comment: comment.into(), owning_span: Some(span), } } /// Moves the stack pointer by the given amount (i.e. allocates stack memory) - pub(crate) fn unowned_stack_allocate_memory(size_to_allocate_in_words: u32) -> Self { + pub(crate) fn unowned_stack_allocate_memory( + size_to_allocate_in_words: VirtualImmediate24, + ) -> Self { Op { - opcode: Either::Left(Opcode::Cfei(size_to_allocate_in_words)), + opcode: Either::Left(VirtualOp::CFEI(size_to_allocate_in_words)), comment: String::new(), owning_span: None, } } - pub(crate) fn unowned_new_with_comment(opcode: Opcode, comment: impl Into) -> Self { + pub(crate) fn unowned_new_with_comment(opcode: VirtualOp, comment: impl Into) -> Self { Op { opcode: Either::Left(opcode), comment: comment.into(), owning_span: None, } } - pub(crate) fn new(opcode: Opcode, owning_span: Span<'sc>) -> Self { + pub(crate) fn new(opcode: VirtualOp, owning_span: Span<'sc>) -> Self { Op { opcode: Either::Left(opcode), comment: String::new(), @@ -103,7 +97,7 @@ impl<'sc> Op<'sc> { } } pub(crate) fn new_with_comment( - opcode: Opcode, + opcode: VirtualOp, owning_span: Span<'sc>, comment: impl Into, ) -> Self { @@ -123,9 +117,9 @@ impl<'sc> Op<'sc> { owning_span: Some(owning_span), } } - /// Loads the data from [DataId] `data` into [RegisterId] `reg`. + /// Loads the data from [DataId] `data` into [VirtualRegister] `reg`. pub(crate) fn unowned_load_data_comment( - reg: RegisterId, + reg: VirtualRegister, data: DataId, comment: impl Into, ) -> Self { @@ -170,30 +164,34 @@ impl<'sc> Op<'sc> { } /// Moves the register in the second argument into the register in the first argument - pub(crate) fn register_move(r1: RegisterId, r2: RegisterId, owning_span: Span<'sc>) -> Self { + pub(crate) fn register_move( + r1: VirtualRegister, + r2: VirtualRegister, + owning_span: Span<'sc>, + ) -> Self { Op { - opcode: Either::Right(OrganizationalOp::RMove(r1, r2)), + opcode: Either::Left(VirtualOp::MOVE(r1, r2)), comment: String::new(), owning_span: Some(owning_span), } } /// Moves the register in the second argument into the register in the first argument - pub(crate) fn unowned_register_move(r1: RegisterId, r2: RegisterId) -> Self { + pub(crate) fn unowned_register_move(r1: VirtualRegister, r2: VirtualRegister) -> Self { Op { - opcode: Either::Right(OrganizationalOp::RMove(r1, r2)), + opcode: Either::Left(VirtualOp::MOVE(r1, r2)), comment: String::new(), owning_span: None, } } pub(crate) fn register_move_comment( - r1: RegisterId, - r2: RegisterId, + r1: VirtualRegister, + r2: VirtualRegister, owning_span: Span<'sc>, comment: impl Into, ) -> Self { Op { - opcode: Either::Right(OrganizationalOp::RMove(r1, r2)), + opcode: Either::Left(VirtualOp::MOVE(r1, r2)), comment: comment.into(), owning_span: Some(owning_span), } @@ -201,12 +199,12 @@ impl<'sc> Op<'sc> { /// Moves the register in the second argument into the register in the first argument pub(crate) fn unowned_register_move_comment( - r1: RegisterId, - r2: RegisterId, + r1: VirtualRegister, + r2: VirtualRegister, comment: impl Into, ) -> Self { Op { - opcode: Either::Right(OrganizationalOp::RMove(r1, r2)), + opcode: Either::Left(VirtualOp::MOVE(r1, r2)), comment: comment.into(), owning_span: None, } @@ -236,8 +234,12 @@ impl<'sc> Op<'sc> { } } - /// Jumps to [Label] `label` if the given [RegisterId] `reg1` is not equal to `reg0`. - pub(crate) fn jump_if_not_equal(reg0: RegisterId, reg1: RegisterId, label: Label) -> Self { + /// Jumps to [Label] `label` if the given [VirtualRegister] `reg1` is not equal to `reg0`. + pub(crate) fn jump_if_not_equal( + reg0: VirtualRegister, + reg1: VirtualRegister, + label: Label, + ) -> Self { Op { opcode: Either::Right(OrganizationalOp::JumpIfNotEq(reg0, reg1, label)), comment: String::new(), @@ -247,1167 +249,1041 @@ impl<'sc> Op<'sc> { pub(crate) fn parse_opcode( name: &Ident<'sc>, - args: &[RegisterId], - immediate: Option, - ) -> CompileResult<'sc, Opcode> { - Opcode::parse(name, args.iter().collect::>().as_slice(), immediate) - } -} - -impl Into for &RegisterId { - fn into(self) -> RegisterId { - self.clone() - } -} - -impl Opcode { - /// If this name matches an opcode and there are the correct number and - /// type of arguments, parse the given inputs into an opcode. - pub(crate) fn parse<'sc>( - name: &Ident<'sc>, - args: &[&RegisterId], - immediate: Option, - ) -> CompileResult<'sc, Opcode> { - use Opcode::*; - let op = match name.primary_name { - "add" => { - if args.len() == 3 { - Add(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "addi" => { - if args.len() == 3 { - Addi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "and" => { - if args.len() == 3 { - And(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "andi" => { - if args.len() == 3 { - Andi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "div" => { - if args.len() == 3 { - Div(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "divi" => { - if args.len() == 3 { - Divi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "mod" => { - if args.len() == 3 { - Mod(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "modi" => { - if args.len() == 3 { - Modi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "eq" => { - if args.len() == 3 { - Eq(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "gt" => { - if args.len() == 3 { - Gt(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "mult" => { - if args.len() == 3 { - Mult(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "multi" => { - if args.len() == 3 { - Multi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "noop" => { - if args.len() == 0 { - Noop() - } else { - todo!("ArgMismatchError") - } - } - "not" => { - if args.len() == 2 { - Not(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "or" => { - if args.len() == 3 { - Or(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "ori" => { - if args.len() == 3 { - Ori( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "sll" => { - if args.len() == 3 { - Sll( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "sllv" => { - if args.len() == 3 { - Sllv(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "sltiu" => { - if args.len() == 3 { - Sltiu( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "sltu" => { - if args.len() == 3 { - Sltu(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "sra" => { - if args.len() == 3 { - Sra( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "srl" => { - if args.len() == 3 { - Srl( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "srlv" => { - if args.len() == 3 { - Srlv(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "srav" => { - if args.len() == 3 { - Srav(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "sub" => { - if args.len() == 3 { - Sub(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "subi" => { - if args.len() == 3 { - Subi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "xor" => { - if args.len() == 3 { - Xor(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "xori" => { - if args.len() == 3 { - Xori( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "exp" => { - if args.len() == 3 { - Exp(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, + ) -> CompileResult<'sc, VirtualOp> { + let mut warnings = vec![]; + let mut errors = vec![]; + ok( + match name.primary_name { + "add" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ADD(r1, r2, r3) + } + "addi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ADDI(r1, r2, imm) + } + "and" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::AND(r1, r2, r3) + } + "andi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ANDI(r1, r2, imm) + } + "div" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::DIV(r1, r2, r3) + } + "divi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::DIVI(r1, r2, imm) + } + "eq" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EQ(r1, r2, r3) + } + "exp" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EXP(r1, r2, r3) + } + "expi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EXPI(r1, r2, imm) + } + "gt" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::GT(r1, r2, r3) + } + "mlog" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MLOG(r1, r2, r3) + } + "mroo" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MROO(r1, r2, r3) + } + "mod" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MOD(r1, r2, r3) + } + "modi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MODI(r1, r2, imm) + } + "move" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MOVE(r1, r2) + } + "mul" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MUL(r1, r2, r3) + } + "muli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MULI(r1, r2, imm) + } + "not" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::NOT(r1, r2) + } + "or" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::OR(r1, r2, r3) + } + "ori" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ORI(r1, r2, imm) + } + "sll" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLL(r1, r2, r3) + } + "slli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLLI(r1, r2, imm) + } + "srl" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRL(r1, r2, r3) + } + "srli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRLI(r1, r2, imm) + } + "sub" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SUB(r1, r2, r3) + } + "subi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SUBI(r1, r2, imm) + } + "xor" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::XOR(r1, r2, r3) + } + "xori" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::XORI(r1, r2, imm) + } + "cimv" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CIMV(r1, r2, r3) + } + "ctmv" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CTMV(r1, r2) + } + "ji" => { + errors.push(CompileError::DisallowedJi { + span: name.span.clone(), + }); + return err(warnings, errors); + } + "jnei" => { + errors.push(CompileError::DisallowedJnei { + span: name.span.clone(), + }); + return err(warnings, errors); + } + "ret" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::RET(r1) + } + "cfei" => { + let imm = type_check!( + single_imm_24(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CFEI(imm) + } + "cfsi" => { + let imm = type_check!( + single_imm_24(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CFSI(imm) + } + "lb" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LB(r1, r2, imm) + } + "lw" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LW(r1, r2, imm) + } + "aloc" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ALOC(r1) + } + "mcl" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCL(r1, r2) + } + "mcli" => { + let (r1, imm) = type_check!( + single_reg_imm_18(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCLI(r1, imm) + } + "mcp" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCP(r1, r2, r3) + } + "meq" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MEQ(r1, r2, r3, r4) + } + "sb" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SB(r1, r2, imm) + } + "sw" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SW(r1, r2, imm) + } + "bhsh" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BHSH(r1, r2) + } + "bhei" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BHEI(r1) + } + "burn" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BURN(r1) + } + "call" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CALL(r1, r2, r3, r4) + } + "ccp" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CCP(r1, r2, r3, r4) + } + "croo" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CROO(r1, r2) + } + "csiz" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CSIZ(r1, r2) + } + "cb" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CB(r1) + } + "ldc" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LDC(r1, r2, r3) + } + "log" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LOG(r1, r2, r3, r4) + } + "mint" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MINT(r1) + } + "rvrt" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::RVRT(r1) + } + "sldc" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLDC(r1, r2, r3) + } + "srw" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRW(r1, r2) + } + "srwq" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRWQ(r1, r2) + } + "sww" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SWW(r1, r2) + } + "swwq" => { + let (r1, r2) = type_check!( + two_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SWWQ(r1, r2) + } + "tr" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::TR(r1, r2, r3) + } + "tro" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::TRO(r1, r2, r3, r4) + } + "ecr" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ECR(r1, r2, r3) + } + "k256" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::K256(r1, r2, r3) + } + "s256" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::S256(r1, r2, r3) + } + "noop" => VirtualOp::NOOP, + "flag" => { + let r1 = type_check!( + single_reg(args, immediate, whole_op_span), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::FLAG(r1) } - } - "expi" => { - if args.len() == 3 { - Expi( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "cimv" => { - if args.len() == 3 { - CIMV( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "ctmv" => { - if args.len() == 2 { - CTMV(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "ji" => { - if args.len() == 1 { - Ji(match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }) - } else { - todo!("ArgMismatchError") - } - } - "jnzi" => { - if args.len() == 2 { - Jnzi( - args[0].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "ret" => { - if args.len() == 1 { - Ret(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "cfei" => { - if args.len() == 1 { - Cfei(match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }) - } else { - todo!("ArgMismatchError") - } - } - "cfs" => { - if args.len() == 1 { - Cfs(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "lb" => { - if args.len() == 3 { - Lb( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "lw" => { - if args.len() == 3 { - Lw( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "malloc" => { - if args.len() == 1 { - Malloc(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "memcleari" => { - if args.len() == 2 { - MemClearImmediate( - args[0].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "memcp" => { - if args.len() == 2 { - MemCp(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "memeq" => { - if args.len() == 4 { - MemEq( - args[0].into(), - args[1].into(), - args[2].into(), - args[3].into(), - ) - } else { - todo!("ArgMismatchError") - } - } - "sb" => { - if args.len() == 3 { - Sb( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "sw" => { - if args.len() == 3 { - Sw( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "blockhash" => { - if args.len() == 2 { - BlockHash(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "blockheight" => { - if args.len() == 1 { - BlockHeight(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "call" => { - if args.len() == 4 { - Call( - args[0].into(), - args[1].into(), - args[2].into(), - args[3].into(), - ) - } else { - todo!("ArgMismatchError") - } - } - "codecopy" => { - if args.len() == 3 { - CodeCopy( - args[0].into(), - args[1].into(), - match immediate { - Some(i) => i, - None => { - return err( - vec![], - vec![CompileError::MissingImmediate { - span: name.span.clone(), - }], - ) - } - }, - ) - } else { - todo!("ArgMismatchError") - } - } - "coderoot" => { - if args.len() == 2 { - CodeRoot(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "codesize" => { - if args.len() == 2 { - Codesize(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "coinbase" => { - if args.len() == 1 { - Coinbase(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "loadcode" => { - if args.len() == 3 { - LoadCode(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "sloadcode" => { - if args.len() == 3 { - SLoadCode(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "log" => { - if args.len() == 4 { - Log( - args[0].into(), - args[1].into(), - args[2].into(), - args[3].into(), - ) - } else { - todo!("ArgMismatchError") - } - } - "revert" => { - if args.len() == 1 { - Revert(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - "srw" => { - if args.len() == 2 { - Srw(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "srwx" => { - if args.len() == 2 { - Srwx(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "sww" => { - if args.len() == 2 { - Sww(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "swwx" => { - if args.len() == 2 { - Swwx(args[0].into(), args[1].into()) - } else { - todo!("ArgMismatchError") - } - } - "transfer" => { - if args.len() == 3 { - Transfer(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "transferout" => { - if args.len() == 4 { - TransferOut( - args[0].into(), - args[1].into(), - args[2].into(), - args[3].into(), - ) - } else { - todo!("ArgMismatchError") - } - } - "ecrecover" => { - if args.len() == 3 { - Ecrecover(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "keccak256" => { - if args.len() == 3 { - Keccak256(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "sha256" => { - if args.len() == 3 { - Sha256(args[0].into(), args[1].into(), args[2].into()) - } else { - todo!("ArgMismatchError") - } - } - "flag" => { - if args.len() == 1 { - Flag(args[0].into()) - } else { - todo!("ArgMismatchError") - } - } - other => { - return err( - vec![], - vec![CompileError::UnrecognizedOp { + + other => { + errors.push(CompileError::UnrecognizedOp { op_name: other, - span: name.clone().span, - }], - ) - } - }; - ok(op, vec![], vec![]) + span: name.span.clone(), + }); + return err(warnings, errors); + } + }, + warnings, + errors, + ) } - pub(crate) fn get_register_names(&self) -> HashSet<&RegisterId> { - use Opcode::*; - let regs: Vec<&RegisterId> = match self { - Add(r1, r2, r3) => vec![r1, r2, r3], - Addi(r1, r2, _imm) => vec![r1, r2], - And(r1, r2, r3) => vec![r1, r2, r3], - Andi(r1, r2, _imm) => vec![r1, r2], - Div(r1, r2, r3) => vec![r1, r2, r3], - Divi(r1, r2, _imm) => vec![r1, r2], - Mod(r1, r2, r3) => vec![r1, r2, r3], - Modi(r1, r2, _imm) => vec![r1, r2], - Eq(r1, r2, r3) => vec![r1, r2, r3], - Gt(r1, r2, r3) => vec![r1, r2, r3], - Mult(r1, r2, r3) => vec![r1, r2, r3], - Multi(r1, r2, _imm) => vec![r1, r2], - Noop() => vec![], - Not(r1, r2) => vec![r1, r2], - Or(r1, r2, r3) => vec![r1, r2, r3], - Ori(r1, r2, _imm) => vec![r1, r2], - Sll(r1, r2, _imm) => vec![r1, r2], - Sllv(r1, r2, r3) => vec![r1, r2, r3], - Sltiu(r1, r2, _imm) => vec![r1, r2], - Sltu(r1, r2, r3) => vec![r1, r2, r3], - Sra(r1, r2, _imm) => vec![r1, r2], - Srl(r1, r2, _imm) => vec![r1, r2], - Srlv(r1, r2, r3) => vec![r1, r2, r3], - Srav(r1, r2, r3) => vec![r1, r2, r3], - Sub(r1, r2, r3) => vec![r1, r2, r3], - Subi(r1, r2, _imm) => vec![r1, r2], - Xor(r1, r2, r3) => vec![r1, r2, r3], - Xori(r1, r2, _imm) => vec![r1, r2], - Exp(r1, r2, r3) => vec![r1, r2, r3], - Expi(r1, r2, _imm) => vec![r1, r2], - CIMV(r1, r2, _imm) => vec![r1, r2], - CTMV(r1, r2) => vec![r1, r2], - Ji(_imm) => vec![], - Jnzi(r1, _imm) => vec![r1], - Ret(r1) => vec![r1], - Cfei(_imm) => vec![], - Cfs(r1) => vec![r1], - Lb(r1, r2, _imm) => vec![r1, r2], - Lw(r1, r2, _imm) => vec![r1, r2], - Malloc(r1) => vec![r1], - MemClearImmediate(r1, _imm) => vec![r1], - MemCp(r1, r2, r3) => vec![r1, r2, r3], - MemEq(r1, r2, r3, r4) => vec![r1, r2, r3, r4], - Sb(r1, r2, _imm) => vec![r1, r2], - Sw(r1, r2, _imm) => vec![r1, r2], - BlockHash(r1, r2) => vec![r1, r2], - BlockHeight(r1) => vec![r1], - Call(r1, r2, r3, r4) => vec![r1, r2, r3, r4], - CodeCopy(r1, r2, _imm) => vec![r1, r2], - CodeRoot(r1, r2) => vec![r1, r2], - Codesize(r1, r2) => vec![r1, r2], - Coinbase(r1) => vec![r1], - LoadCode(r1, r2, r3) => vec![r1, r2, r3], - SLoadCode(r1, r2, r3) => vec![r1, r2, r3], - Log(r1, r2, r3, r4) => vec![r1, r2, r3, r4], - Revert(r1) => vec![r1], - Srw(r1, r2) => vec![r1, r2], - Srwx(r1, r2) => vec![r1, r2], - Sww(r1, r2) => vec![r1, r2], - Swwx(r1, r2) => vec![r1, r2], - Transfer(r1, r2, r3) => vec![r1, r2, r3], - TransferOut(r1, r2, r3, r4) => vec![r1, r2, r3, r4], - Ecrecover(r1, r2, r3) => vec![r1, r2, r3], - Keccak256(r1, r2, r3) => vec![r1, r2, r3], - Sha256(r1, r2, r3) => vec![r1, r2, r3], - Flag(r1) => vec![r1], - }; +} - regs.into_iter().collect() +fn single_reg<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, VirtualRegister> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 1 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + expected: 1, + received: args.len(), + span: whole_op_span.clone(), + }); } -} -// internal representation for register ids -// simpler to represent as usize since it avoids casts -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub enum RegisterId { - Virtual(String), - Constant(ConstantRegister), -} + let reg = match args.get(0) { + Some(reg) => reg, + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + return err(warnings, errors); + } + }; + match immediate { + None => (), + Some(i) => { + errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }); + } + }; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -/// These are the special registers defined in the spec -pub enum ConstantRegister { - Zero, - One, - Overflow, - ProgramCounter, - StackStartPointer, - StackPointer, - FramePointer, - HeapPointer, - Error, - GlobalGas, - ContextGas, - Balance, - InstructionStart, - Flags, + ok(reg.clone(), warnings, errors) } -impl fmt::Display for ConstantRegister { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ConstantRegister::*; - let text = match self { - Zero => "$zero", - One => "$one", - Overflow => "$of", - ProgramCounter => "$pc", - StackStartPointer => "$ssp", - StackPointer => "$sp", - FramePointer => "$fp", - HeapPointer => "$hp", - Error => "$err", - GlobalGas => "$ggas", - ContextGas => "$cgas", - Balance => "$bal", - InstructionStart => "$is", - Flags => "$flag", - }; - write!(f, "{}", text) +fn two_regs<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister)> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 2 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 2, + received: args.len(), + }); } + + let (reg, reg2) = match (args.get(0), args.get(1)) { + (Some(reg), Some(reg2)) => (reg, reg2), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 2, + received: args.len(), + }); + return err(warnings, errors); + } + }; + match immediate { + None => (), + Some(i) => errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }), + }; + + ok((reg.clone(), reg2.clone()), warnings, errors) } -impl ConstantRegister { - pub(crate) fn parse_register_name(raw: &str) -> Option { - use ConstantRegister::*; - Some(match raw { - "zero" => Zero, - "one" => One, - "of" => Overflow, - "pc" => ProgramCounter, - "ssp" => StackStartPointer, - "sp" => StackPointer, - "fp" => FramePointer, - "hp" => HeapPointer, - "err" => Error, - "ggas" => GlobalGas, - "cgas" => ContextGas, - "bal" => Balance, - "is" => InstructionStart, - "flag" => Flags, - _ => return None, - }) +fn four_regs<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult< + 'sc, + ( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), +> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 4 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 4, + received: args.len(), + }); } -} -// Immediate Value. -pub type ImmediateValue = u32; + let (reg, reg2, reg3, reg4) = match (args.get(0), args.get(1), args.get(2), args.get(3)) { + (Some(reg), Some(reg2), Some(reg3), Some(reg4)) => (reg, reg2, reg3, reg4), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + return err(warnings, errors); + } + }; + match immediate { + None => (), + Some(i) => { + errors.push(CompileError::MissingImmediate { + span: i.span.clone(), + }); + } + }; -opcodes! { - // Arithmetic and Logic. - Add(RegisterId, RegisterId, RegisterId) = 0, - Addi(RegisterId, RegisterId, ImmediateValue) = 1, - And(RegisterId, RegisterId, RegisterId) = 2, - Andi(RegisterId, RegisterId, ImmediateValue) = 3, - Div(RegisterId, RegisterId, RegisterId) = 4, - Divi(RegisterId, RegisterId, ImmediateValue) = 5, - Mod(RegisterId, RegisterId, RegisterId) = 6, - Modi(RegisterId, RegisterId, ImmediateValue) = 7, - Eq(RegisterId, RegisterId, RegisterId) = 8, - Gt(RegisterId, RegisterId, RegisterId) = 9, - Mult(RegisterId, RegisterId, RegisterId) = 10, - Multi(RegisterId, RegisterId, ImmediateValue) = 11, - Noop() = 12, - Not(RegisterId, RegisterId) = 13, - Or(RegisterId, RegisterId, RegisterId) = 14, - Ori(RegisterId, RegisterId, ImmediateValue) = 15, - Sll(RegisterId, RegisterId, ImmediateValue) = 16, - Sllv(RegisterId, RegisterId, RegisterId) = 17, - Sltiu(RegisterId, RegisterId, ImmediateValue) = 18, - Sltu(RegisterId, RegisterId, RegisterId) = 19, - Sra(RegisterId, RegisterId, ImmediateValue) = 20, - Srl(RegisterId, RegisterId, ImmediateValue) = 21, - Srlv(RegisterId, RegisterId, RegisterId) = 22, - Srav(RegisterId, RegisterId, RegisterId) = 23, - Sub(RegisterId, RegisterId, RegisterId) = 24, - Subi(RegisterId, RegisterId, ImmediateValue) = 25, - Xor(RegisterId, RegisterId, RegisterId) = 26, - Xori(RegisterId, RegisterId, ImmediateValue) = 27, - Exp(RegisterId, RegisterId, RegisterId) = 28, - Expi(RegisterId, RegisterId, ImmediateValue) = 29, + impl ConstantRegister { + pub(crate) fn parse_register_name(raw: &str) -> Option { + use ConstantRegister::*; + Some(match raw { + "zero" => Zero, + "one" => One, + "of" => Overflow, + "pc" => ProgramCounter, + "ssp" => StackStartPointer, + "sp" => StackPointer, + "fp" => FramePointer, + "hp" => HeapPointer, + "err" => Error, + "ggas" => GlobalGas, + "cgas" => ContextGas, + "bal" => Balance, + "is" => InstructionStart, + "flag" => Flags, + _ => return None, + }) + } + } - // Control Flow Opcodes. - CIMV(RegisterId, RegisterId, ImmediateValue) = 50, - CTMV(RegisterId, RegisterId) = 51, - Ji(ImmediateValue) = 52, - Jnzi(RegisterId, ImmediateValue) = 53, - Ret(RegisterId) = 54, + // Immediate Value. + pub type ImmediateValue = u32; - // Memory opcodes. - Cfei(ImmediateValue) = 60, - Cfs(RegisterId) = 61, - Lb(RegisterId, RegisterId, ImmediateValue) = 62, - Lw(RegisterId, RegisterId, ImmediateValue) = 63, - Malloc(RegisterId) = 64, - MemClearImmediate(RegisterId, ImmediateValue) = 65, - MemCp(RegisterId, RegisterId, RegisterId) = 66, - MemEq(RegisterId, RegisterId, RegisterId, RegisterId) = 67, - Sb(RegisterId, RegisterId, ImmediateValue) = 68, - Sw(RegisterId, RegisterId, ImmediateValue) = 69, + ok( + (reg.clone(), reg2.clone(), reg3.clone(), reg4.clone()), + warnings, + errors, + ) +} - // Contract Opcodes. - BlockHash(RegisterId, RegisterId) = 80, - BlockHeight(RegisterId) = 81, - Call(RegisterId, RegisterId, RegisterId, RegisterId) = 82, - CodeCopy(RegisterId, RegisterId, ImmediateValue) = 83, - CodeRoot(RegisterId, RegisterId) = 84, - Codesize(RegisterId, RegisterId) = 85, - Coinbase(RegisterId) = 86, - LoadCode(RegisterId, RegisterId, RegisterId) = 87, - SLoadCode(RegisterId, RegisterId, RegisterId) = 88, - Log(RegisterId, RegisterId, RegisterId, RegisterId) = 89, - Revert(RegisterId) = 90, - Srw(RegisterId, RegisterId) = 91, - Srwx(RegisterId, RegisterId) = 92, - Sww(RegisterId, RegisterId) = 93, - Swwx(RegisterId, RegisterId) = 94, - Transfer(RegisterId, RegisterId, RegisterId) = 95, - TransferOut(RegisterId, RegisterId, RegisterId, RegisterId) = 96, +fn three_regs<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 3 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 3, + received: args.len(), + }); + } - // Cryptographic Opcodes. - Ecrecover(RegisterId, RegisterId, RegisterId) = 110, - Keccak256(RegisterId, RegisterId, RegisterId) = 111, - Sha256(RegisterId, RegisterId, RegisterId) = 112, + let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { + (Some(reg), Some(reg2), Some(reg3)) => (reg, reg2, reg3), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 3, + received: args.len(), + }); + return err(warnings, errors); + } + }; + match immediate { + None => (), + Some(i) => { + errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }); + } + }; - // Additional Opcodes. - Flag(RegisterId) = 130 + ok((reg.clone(), reg2.clone(), reg3.clone()), warnings, errors) } -impl fmt::Display for RegisterId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RegisterId::Virtual(name) => write!(f, "$r{}", name), - RegisterId::Constant(name) => { - write!(f, "{}", name) - } - } +fn single_imm_24<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, VirtualImmediate24> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 0 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 0, + received: args.len(), + }); } + let (imm, imm_span): (u64, _) = match immediate { + None => { + errors.push(CompileError::MissingImmediate { + span: whole_op_span.clone(), + }); + return err(warnings, errors); + } + Some(i) => match i.primary_name.parse() { + Ok(o) => (o, i.span.clone()), + Err(_) => { + errors.push(CompileError::InvalidImmediateValue { + span: i.span.clone(), + }); + return err(warnings, errors); + } + }, + }; + + let imm = match VirtualImmediate24::new(imm, imm_span) { + Ok(o) => o, + Err(e) => { + errors.push(e); + return err(warnings, errors); + } + }; + + ok(imm, warnings, errors) } +fn single_reg_imm_18<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, (VirtualRegister, VirtualImmediate18)> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 1 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + } + let reg = match args.get(0) { + Some(reg) => reg, + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + return err(warnings, errors); + } + }; + let (imm, imm_span): (u64, _) = match immediate { + None => { + errors.push(CompileError::MissingImmediate { + span: whole_op_span.clone(), + }); + return err(warnings, errors); + } + Some(i) => match i.primary_name.parse() { + Ok(o) => (o, i.span.clone()), + Err(_) => { + errors.push(CompileError::InvalidImmediateValue { + span: i.span.clone(), + }); + return err(warnings, errors); + } + }, + }; -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, ".{}", self.0) + let imm = match VirtualImmediate18::new(imm, imm_span) { + Ok(o) => o, + Err(e) => { + errors.push(e); + return err(warnings, errors); + } + }; + + ok((reg.clone(), imm), warnings, errors) +} +fn two_regs_imm_12<'sc>( + args: &[VirtualRegister], + immediate: &Option>, + whole_op_span: Span<'sc>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { + let warnings = vec![]; + let mut errors = vec![]; + if args.len() > 2 { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 2, + received: args.len(), + }); } + let (reg, reg2) = match (args.get(0), args.get(1)) { + (Some(reg), Some(reg2)) => (reg, reg2), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 2, + received: args.len(), + }); + return err(warnings, errors); + } + }; + let (imm, imm_span): (u64, _) = match immediate { + None => { + errors.push(CompileError::MissingImmediate { + span: whole_op_span.clone(), + }); + return err(warnings, errors); + } + Some(i) => match i.primary_name.parse() { + Ok(o) => (o, i.span.clone()), + Err(_) => { + errors.push(CompileError::InvalidImmediateValue { + span: i.span.clone(), + }); + return err(warnings, errors); + } + }, + }; + + let imm = match VirtualImmediate12::new(imm, imm_span) { + Ok(o) => o, + Err(e) => { + errors.push(e); + return err(warnings, errors); + } + }; + + ok((reg.clone(), reg2.clone(), imm), warnings, errors) } impl fmt::Display for Op<'_> { - // very clunky but lets us tweak assembly language most easily - // below code was constructed with vim macros -- easier to regenerate rather than rewrite. - // @alex if you want to change the format and save yourself the pain. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use Opcode::*; use OrganizationalOp::*; + use VirtualOp::*; let op_str = match &self.opcode { Either::Left(opcode) => match opcode { - Add(a, b, c) => format!("add {} {} {}", a, b, c), - Addi(a, b, c) => format!("addi {} {} {}", a, b, c), - And(a, b, c) => format!("and {} {} {}", a, b, c), - Andi(a, b, c) => format!("andi {} {} {}", a, b, c), - Div(a, b, c) => format!("div {} {} {}", a, b, c), - Divi(a, b, c) => format!("divi {} {} {}", a, b, c), - Mod(a, b, c) => format!("mod {} {} {}", a, b, c), - Modi(a, b, c) => format!("modi {} {} {}", a, b, c), - Eq(a, b, c) => format!("eq {} {} {}", a, b, c), - Gt(a, b, c) => format!("gt {} {} {}", a, b, c), - Mult(a, b, c) => format!("mult {} {} {}", a, b, c), - Multi(a, b, c) => format!("multi {} {} {}", a, b, c), - Noop() => "noop".to_string(), - Not(a, b) => format!("not {} {}", a, b), - Or(a, b, c) => format!("or {} {} {}", a, b, c), - Ori(a, b, c) => format!("ori {} {} {}", a, b, c), - Sll(a, b, c) => format!("sll {} {} {}", a, b, c), - Sllv(a, b, c) => format!("sllv {} {} {}", a, b, c), - Sltiu(a, b, c) => format!("sltiu {} {} {}", a, b, c), - - Sltu(a, b, c) => format!("sltu {} {} {}", a, b, c), - Sra(a, b, c) => format!("sra {} {} {}", a, b, c), - Srl(a, b, c) => format!("srl {} {} {}", a, b, c), - Srlv(a, b, c) => format!("srlv {} {} {}", a, b, c), - Srav(a, b, c) => format!("srav {} {} {}", a, b, c), - Sub(a, b, c) => format!("sub {} {} {}", a, b, c), - Subi(a, b, c) => format!("subi {} {} {}", a, b, c), - Xor(a, b, c) => format!("xor {} {} {}", a, b, c), - Xori(a, b, c) => format!("xori {} {} {}", a, b, c), - Exp(a, b, c) => format!("exp {} {} {}", a, b, c), - Expi(a, b, c) => format!("expi {} {} {}", a, b, c), - + ADD(a, b, c) => format!("add {} {} {}", a, b, c), + ADDI(a, b, c) => format!("addi {} {} {}", a, b, c), + AND(a, b, c) => format!("and {} {} {}", a, b, c), + ANDI(a, b, c) => format!("andi {} {} {}", a, b, c), + DIV(a, b, c) => format!("div {} {} {}", a, b, c), + DIVI(a, b, c) => format!("divi {} {} {}", a, b, c), + EQ(a, b, c) => format!("eq {} {} {}", a, b, c), + EXP(a, b, c) => format!("exp {} {} {}", a, b, c), + EXPI(a, b, c) => format!("expi {} {} {}", a, b, c), + GT(a, b, c) => format!("gt {} {} {}", a, b, c), + MLOG(a, b, c) => format!("mlog {} {} {}", a, b, c), + MROO(a, b, c) => format!("mroo {} {} {}", a, b, c), + MOD(a, b, c) => format!("mod {} {} {}", a, b, c), + MODI(a, b, c) => format!("modi {} {} {}", a, b, c), + MOVE(a, b) => format!("move {} {}", a, b), + MUL(a, b, c) => format!("mul {} {} {}", a, b, c), + MULI(a, b, c) => format!("muli {} {} {}", a, b, c), + NOT(a, b) => format!("not {} {}", a, b), + OR(a, b, c) => format!("or {} {} {}", a, b, c), + ORI(a, b, c) => format!("ori {} {} {}", a, b, c), + SLL(a, b, c) => format!("sll {} {} {}", a, b, c), + SLLI(a, b, c) => format!("slli {} {} {}", a, b, c), + SRL(a, b, c) => format!("srl {} {} {}", a, b, c), + SRLI(a, b, c) => format!("srli {} {} {}", a, b, c), + SUB(a, b, c) => format!("sub {} {} {}", a, b, c), + SUBI(a, b, c) => format!("subi {} {} {}", a, b, c), + XOR(a, b, c) => format!("xor {} {} {}", a, b, c), + XORI(a, b, c) => format!("xori {} {} {}", a, b, c), CIMV(a, b, c) => format!("cimv {} {} {}", a, b, c), CTMV(a, b) => format!("ctmv {} {}", a, b), - Ji(a) => format!("ji {}", a), - Jnzi(a, b) => format!("jnzi {} {}", a, b), - Ret(a) => format!("ret {}", a), - - Cfei(a) => format!("cfei {}", a), - Cfs(a) => format!("cfs {}", a), - Lb(a, b, c) => format!("lb {} {} {}", a, b, c), - Lw(a, b, c) => format!("lw {} {} {}", a, b, c), - Malloc(a) => format!("malloc {}", a), - MemClearImmediate(a, b) => format!("memcleari {} {}", a, b), - MemCp(a, b, c) => format!("memcp {} {} {}", a, b, c), - MemEq(a, b, c, d) => format!("memeq {} {} {} {}", a, b, c, d), - Sb(a, b, c) => format!("sb {} {} {}", a, b, c), - Sw(a, b, c) => format!("sw {} {} {}", a, b, c), - - BlockHash(a, b) => format!("blockhash {} {}", a, b), - BlockHeight(a) => format!("blockheight {}", a), - Call(a, b, c, d) => format!("call {} {} {} {}", a, b, c, d), - CodeCopy(a, b, c) => format!("codecopy {} {} {}", a, b, c), - CodeRoot(a, b) => format!("coderoot {} {}", a, b), - Codesize(a, b) => format!("codesize {} {}", a, b), - Coinbase(a) => format!("coinbase {}", a), - LoadCode(a, b, c) => format!("loadcode {} {} {}", a, b, c), - SLoadCode(a, b, c) => format!("sloadcode {} {} {}", a, b, c), - Log(a, b, c, d) => format!("log {} {} {} {}", a, b, c, d), - Revert(a) => format!("revert {}", a), - Srw(a, b) => format!("srw {} {}", a, b), - Srwx(a, b) => format!("srwx {} {}", a, b), - Sww(a, b) => format!("sww {} {}", a, b), - Swwx(a, b) => format!("swwx {} {}", a, b), - Transfer(a, b, c) => format!("transfer {} {} {}", a, b, c), - TransferOut(a, b, c, d) => format!("transferout {} {} {} {}", a, b, c, d), - - Ecrecover(a, b, c) => format!("ecrecover {} {} {}", a, b, c), - Keccak256(a, b, c) => format!("keccak256 {} {} {}", a, b, c), - Sha256(a, b, c) => format!("sha256 {} {} {}", a, b, c), - - Flag(a) => format!("flag {}", a), + JI(a) => format!("ji {}", a), + JNEI(a, b, c) => format!("jnei {} {} {}", a, b, c), + RET(a) => format!("ret {}", a), + CFEI(a) => format!("cfei {}", a), + CFSI(a) => format!("cfsi {}", a), + LB(a, b, c) => format!("lb {} {} {}", a, b, c), + LW(a, b, c) => format!("lw {} {} {}", a, b, c), + ALOC(a) => format!("aloc {}", a), + MCL(a, b) => format!("mcl {} {}", a, b), + MCLI(a, b) => format!("mcli {} {}", a, b), + MCP(a, b, c) => format!("mcp {} {} {}", a, b, c), + MEQ(a, b, c, d) => format!("meq {} {} {} {}", a, b, c, d), + SB(a, b, c) => format!("sb {} {} {}", a, b, c), + SW(a, b, c) => format!("sw {} {} {}", a, b, c), + BHSH(a, b) => format!("bhsh {} {}", a, b), + BHEI(a) => format!("bhei {}", a), + BURN(a) => format!("burn {}", a), + CALL(a, b, c, d) => format!("call {} {} {} {}", a, b, c, d), + CCP(a, b, c, d) => format!("ccp {} {} {} {}", a, b, c, d), + CROO(a, b) => format!("croo {} {}", a, b), + CSIZ(a, b) => format!("csiz {} {}", a, b), + CB(a) => format!("cb {}", a), + LDC(a, b, c) => format!("ldc {} {} {}", a, b, c), + LOG(a, b, c, d) => format!("log {} {} {} {}", a, b, c, d), + MINT(a) => format!("mint {}", a), + RVRT(a) => format!("rvrt {}", a), + SLDC(a, b, c) => format!("sldc {} {} {}", a, b, c), + SRW(a, b) => format!("srw {} {}", a, b), + SRWQ(a, b) => format!("srwq {} {}", a, b), + SWW(a, b) => format!("sww {} {}", a, b), + SWWQ(a, b) => format!("swwq {} {}", a, b), + TR(a, b, c) => format!("tr {} {} {}", a, b, c), + TRO(a, b, c, d) => format!("tro {} {} {} {}", a, b, c, d), + ECR(a, b, c) => format!("ecr {} {} {}", a, b, c), + K256(a, b, c) => format!("k256 {} {} {}", a, b, c), + S256(a, b, c) => format!("s256 {} {} {}", a, b, c), + NOOP => "noop".to_string(), + FLAG(a) => format!("flag {}", a), + Undefined => format!("undefined op"), }, Either::Right(opcode) => match opcode { Label(l) => format!("{}", l), - RMove(r1, r2) => format!("move {} {}", r1, r2), Comment => "".into(), Jump(label) => format!("jump {}", label), Ld(register, data_id) => format!("ld {} {}", register, data_id), @@ -1428,24 +1304,32 @@ impl fmt::Display for Op<'_> { } } -#[derive(Clone, Eq, PartialEq)] -pub(crate) struct Label(pub(crate) usize); - // Convenience opcodes for the compiler -- will be optimized out or removed // these do not reflect actual ops in the VM and will be compiled to bytecode #[derive(Clone)] pub(crate) enum OrganizationalOp { - // copies the second register into the first register - RMove(RegisterId, RegisterId), // Labels the code for jumps, will later be interpreted into offsets Label(Label), // Just a comment that will be inserted into the asm without an op Comment, // Jumps to a label Jump(Label), + // Jumps to a label + JumpIfNotEq(VirtualRegister, VirtualRegister, Label), // Loads from the data section into a register // "load data" - Ld(RegisterId, DataId), - // - JumpIfNotEq(RegisterId, RegisterId, Label), + Ld(VirtualRegister, DataId), +} + +impl OrganizationalOp { + pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { + use OrganizationalOp::*; + (match self { + Label(_) | Comment | Jump(_) => vec![], + Ld(r1, _) => vec![r1], + JumpIfNotEq(r1, r2, _) => vec![r1, r2], + }) + .into_iter() + .collect() + } } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs new file mode 100644 index 00000000000..90bd4633150 --- /dev/null +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -0,0 +1,721 @@ +//! This module contains abstracted versions of bytecode primitives that the compiler uses to +//! ensure correctness and safety. +//! +//! The immediate types are used to safely construct numbers that are within their bounds, and the +//! ops are clones of the actual opcodes, but with the safe primitives as arguments. + +use super::{ + allocated_ops::{AllocatedOpcode, AllocatedRegister}, + RealizedOp, +}; +use crate::asm_generation::RegisterPool; +use crate::error::*; +use pest::Span; +use std::collections::{HashMap, HashSet}; +use std::convert::TryInto; +use std::fmt; + +/// Represents virtual registers that have yet to be allocated. +/// Note that only the Virtual variant will be allocated, and the Constant variant refers to +/// reserved registers. +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub enum VirtualRegister { + Virtual(String), + Constant(ConstantRegister), +} + +impl Into for &VirtualRegister { + fn into(self) -> VirtualRegister { + self.clone() + } +} + +impl fmt::Display for VirtualRegister { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VirtualRegister::Virtual(name) => write!(f, "$r{}", name), + VirtualRegister::Constant(name) => { + write!(f, "{}", name) + } + } + } +} + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +/// These are the special registers defined in the spec +pub enum ConstantRegister { + Zero, + One, + Overflow, + ProgramCounter, + StackStartPointer, + StackPointer, + FramePointer, + HeapPointer, + Error, + GlobalGas, + ContextGas, + Balance, + InstructionStart, + Flags, +} + +impl fmt::Display for ConstantRegister { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ConstantRegister::*; + let text = match self { + Zero => "$zero", + One => "$one", + Overflow => "$of", + ProgramCounter => "$pc", + StackStartPointer => "$ssp", + StackPointer => "$sp", + FramePointer => "$fp", + HeapPointer => "$hp", + Error => "$err", + GlobalGas => "$ggas", + ContextGas => "$cgas", + Balance => "$bal", + InstructionStart => "$is", + Flags => "$flag", + }; + write!(f, "{}", text) + } +} + +/// 6-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate06 { + value: u8, +} + +impl VirtualImmediate06 { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > 0b111_111 { + return Err(CompileError::Immediate06TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } +} +impl fmt::Display for VirtualImmediate06 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i{}", self.value) + } +} + +/// 12-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate12 { + value: u16, +} + +impl VirtualImmediate12 { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > 0b111_111_111_111 { + return Err(CompileError::Immediate12TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } + /// This method should only be used if the size of the raw value has already been manually + /// checked. + /// This is valuable when you don't necessarily have exact [Span] info and want to handle the + /// error at a higher level, probably via an internal compiler error or similar. + /// A panic message is still required, just in case the programmer has made an error. + pub(crate) fn new_unchecked(raw: u64, msg: impl Into) -> Self { + Self { + value: raw.try_into().expect(&(msg.into())), + } + } +} +impl fmt::Display for VirtualImmediate12 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i{}", self.value) + } +} + +/// 18-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate18 { + value: u32, +} +impl VirtualImmediate18 { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > crate::asm_generation::compiler_constants::EIGHTEEN_BITS { + return Err(CompileError::Immediate18TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } + /// This method should only be used if the size of the raw value has already been manually + /// checked. + /// This is valuable when you don't necessarily have exact [Span] info and want to handle the + /// error at a higher level, probably via an internal compiler error or similar. + /// A panic message is still required, just in case the programmer has made an error. + pub(crate) fn new_unchecked(raw: u64, msg: impl Into) -> Self { + Self { + value: raw.try_into().expect(&(msg.into())), + } + } +} +impl fmt::Display for VirtualImmediate18 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i{}", self.value) + } +} + +/// 24-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate24 { + value: u32, +} +impl VirtualImmediate24 { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > crate::asm_generation::compiler_constants::TWENTY_FOUR_BITS { + return Err(CompileError::Immediate24TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } + /// This method should only be used if the size of the raw value has already been manually + /// checked. + /// This is valuable when you don't necessarily have exact [Span] info and want to handle the + /// error at a higher level, probably via an internal compiler error or similar. + /// A panic message is still required, just in case the programmer has made an error. + pub(crate) fn new_unchecked(raw: u64, msg: impl Into) -> Self { + Self { + value: raw.try_into().expect(&(msg.into())), + } + } +} +impl fmt::Display for VirtualImmediate24 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i{}", self.value) + } +} + +/// This enum is unfortunately a redundancy of the [fuel_asm::Opcode] enum. This variant, however, +/// allows me to use the compiler's internal [VirtualRegister] types and maintain type safety +/// between virtual ops and the real opcodes. A bit of copy/paste seemed worth it for that safety, +/// so here it is. +#[derive(Clone)] +pub(crate) enum VirtualOp { + ADD(VirtualRegister, VirtualRegister, VirtualRegister), + ADDI(VirtualRegister, VirtualRegister, VirtualImmediate12), + AND(VirtualRegister, VirtualRegister, VirtualRegister), + ANDI(VirtualRegister, VirtualRegister, VirtualImmediate12), + DIV(VirtualRegister, VirtualRegister, VirtualRegister), + DIVI(VirtualRegister, VirtualRegister, VirtualImmediate12), + EQ(VirtualRegister, VirtualRegister, VirtualRegister), + EXP(VirtualRegister, VirtualRegister, VirtualRegister), + EXPI(VirtualRegister, VirtualRegister, VirtualImmediate12), + GT(VirtualRegister, VirtualRegister, VirtualRegister), + MLOG(VirtualRegister, VirtualRegister, VirtualRegister), + MROO(VirtualRegister, VirtualRegister, VirtualRegister), + MOD(VirtualRegister, VirtualRegister, VirtualRegister), + MODI(VirtualRegister, VirtualRegister, VirtualImmediate12), + MOVE(VirtualRegister, VirtualRegister), + MUL(VirtualRegister, VirtualRegister, VirtualRegister), + MULI(VirtualRegister, VirtualRegister, VirtualImmediate12), + NOT(VirtualRegister, VirtualRegister), + OR(VirtualRegister, VirtualRegister, VirtualRegister), + ORI(VirtualRegister, VirtualRegister, VirtualImmediate12), + SLL(VirtualRegister, VirtualRegister, VirtualRegister), + SLLI(VirtualRegister, VirtualRegister, VirtualImmediate12), + SRL(VirtualRegister, VirtualRegister, VirtualRegister), + SRLI(VirtualRegister, VirtualRegister, VirtualImmediate12), + SUB(VirtualRegister, VirtualRegister, VirtualRegister), + SUBI(VirtualRegister, VirtualRegister, VirtualImmediate12), + XOR(VirtualRegister, VirtualRegister, VirtualRegister), + XORI(VirtualRegister, VirtualRegister, VirtualImmediate12), + CIMV(VirtualRegister, VirtualRegister, VirtualRegister), + CTMV(VirtualRegister, VirtualRegister), + JI(VirtualImmediate24), + JNEI(VirtualRegister, VirtualRegister, VirtualImmediate12), + RET(VirtualRegister), + CFEI(VirtualImmediate24), + CFSI(VirtualImmediate24), + LB(VirtualRegister, VirtualRegister, VirtualImmediate12), + LW(VirtualRegister, VirtualRegister, VirtualImmediate12), + ALOC(VirtualRegister), + MCL(VirtualRegister, VirtualRegister), + MCLI(VirtualRegister, VirtualImmediate18), + MCP(VirtualRegister, VirtualRegister, VirtualRegister), + MEQ( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + SB(VirtualRegister, VirtualRegister, VirtualImmediate12), + SW(VirtualRegister, VirtualRegister, VirtualImmediate12), + BHSH(VirtualRegister, VirtualRegister), + BHEI(VirtualRegister), + BURN(VirtualRegister), + CALL( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + CCP( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + CROO(VirtualRegister, VirtualRegister), + CSIZ(VirtualRegister, VirtualRegister), + CB(VirtualRegister), + LDC(VirtualRegister, VirtualRegister, VirtualRegister), + LOG( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + MINT(VirtualRegister), + RVRT(VirtualRegister), + SLDC(VirtualRegister, VirtualRegister, VirtualRegister), + SRW(VirtualRegister, VirtualRegister), + SRWQ(VirtualRegister, VirtualRegister), + SWW(VirtualRegister, VirtualRegister), + SWWQ(VirtualRegister, VirtualRegister), + TR(VirtualRegister, VirtualRegister, VirtualRegister), + TRO( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + ECR(VirtualRegister, VirtualRegister, VirtualRegister), + K256(VirtualRegister, VirtualRegister, VirtualRegister), + S256(VirtualRegister, VirtualRegister, VirtualRegister), + NOOP, + FLAG(VirtualRegister), + Undefined, +} + +impl VirtualOp { + pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { + use VirtualOp::*; + (match self { + ADD(r1, r2, r3) => vec![r1, r2, r3], + ADDI(r1, r2, _i) => vec![r1, r2], + AND(r1, r2, r3) => vec![r1, r2, r3], + ANDI(r1, r2, _i) => vec![r1, r2], + DIV(r1, r2, r3) => vec![r1, r2, r3], + DIVI(r1, r2, _i) => vec![r1, r2], + EQ(r1, r2, r3) => vec![r1, r2, r3], + EXP(r1, r2, r3) => vec![r1, r2, r3], + EXPI(r1, r2, _i) => vec![r1, r2], + GT(r1, r2, r3) => vec![r1, r2, r3], + MLOG(r1, r2, r3) => vec![r1, r2, r3], + MROO(r1, r2, r3) => vec![r1, r2, r3], + MOD(r1, r2, r3) => vec![r1, r2, r3], + MODI(r1, r2, _i) => vec![r1, r2], + MOVE(r1, r2) => vec![r1, r2], + MUL(r1, r2, r3) => vec![r1, r2, r3], + MULI(r1, r2, _i) => vec![r1, r2], + NOT(r1, r2) => vec![r1, r2], + OR(r1, r2, r3) => vec![r1, r2, r3], + ORI(r1, r2, _i) => vec![r1, r2], + SLL(r1, r2, r3) => vec![r1, r2, r3], + SLLI(r1, r2, _i) => vec![r1, r2], + SRL(r1, r2, r3) => vec![r1, r2, r3], + SRLI(r1, r2, _i) => vec![r1, r2], + SUB(r1, r2, r3) => vec![r1, r2, r3], + SUBI(r1, r2, _i) => vec![r1, r2], + XOR(r1, r2, r3) => vec![r1, r2, r3], + XORI(r1, r2, _i) => vec![r1, r2], + CIMV(r1, r2, r3) => vec![r1, r2, r3], + CTMV(r1, r2) => vec![r1, r2], + JI(_im) => vec![], + JNEI(r1, r2, _i) => vec![r1, r2], + RET(r1) => vec![r1], + CFEI(_imm) => vec![], + CFSI(_imm) => vec![], + LB(r1, r2, _i) => vec![r1, r2], + LW(r1, r2, _i) => vec![r1, r2], + ALOC(_imm) => vec![], + MCL(r1, r2) => vec![r1, r2], + MCLI(r1, _imm) => vec![r1], + MCP(r1, r2, r3) => vec![r1, r2, r3], + MEQ(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + SB(r1, r2, _i) => vec![r1, r2], + SW(r1, r2, _i) => vec![r1, r2], + BHSH(r1, r2) => vec![r1, r2], + BHEI(r1) => vec![r1], + BURN(r1) => vec![r1], + CALL(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + CCP(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + CROO(r1, r2) => vec![r1, r2], + CSIZ(r1, r2) => vec![r1, r2], + CB(r1) => vec![r1], + LDC(r1, r2, r3) => vec![r1, r2, r3], + LOG(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + MINT(r1) => vec![r1], + RVRT(r1) => vec![r1], + SLDC(r1, r2, r3) => vec![r1, r2, r3], + SRW(r1, r2) => vec![r1, r2], + SRWQ(r1, r2) => vec![r1, r2], + SWW(r1, r2) => vec![r1, r2], + SWWQ(r1, r2) => vec![r1, r2], + TR(r1, r2, r3) => vec![r1, r2, r3], + TRO(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + ECR(r1, r2, r3) => vec![r1, r2, r3], + K256(r1, r2, r3) => vec![r1, r2, r3], + S256(r1, r2, r3) => vec![r1, r2, r3], + NOOP => vec![], + FLAG(r1) => vec![r1], + Undefined => vec![], + }) + .into_iter() + .collect() + } + + pub(crate) fn allocate_registers( + &self, + pool: &mut RegisterPool, + op_register_mapping: &[(RealizedOp, HashSet)], + ix: usize, + ) -> AllocatedOpcode { + let virtual_registers = self.registers(); + let register_allocation_result = virtual_registers + .clone() + .into_iter() + .map(|x| (x, pool.get_register(x, &op_register_mapping[ix + 1..]))) + .map(|(x, res)| match res { + Some(res) => Some((x, res)), + None => None, + }) + .collect::>>(); + + // Maps virtual registers to their allocated equivalent + let mut mapping = HashMap::default(); + match register_allocation_result { + Some(o) => { + for (key, val) in o { + mapping.insert(key, val); + } + } + None => { + unimplemented!("The allocator cannot resolve a register mapping for this program. This is a temporary artifact of the extremely early stage version of this language. Try to lower the number of variables you use.") + } + }; + + use VirtualOp::*; + match self { + ADD(reg1, reg2, reg3) => AllocatedOpcode::ADD( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + ADDI(reg1, reg2, imm) => AllocatedOpcode::ADDI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + AND(reg1, reg2, reg3) => AllocatedOpcode::AND( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + ANDI(reg1, reg2, imm) => AllocatedOpcode::ANDI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + DIV(reg1, reg2, reg3) => AllocatedOpcode::DIV( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + DIVI(reg1, reg2, imm) => AllocatedOpcode::DIVI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + EQ(reg1, reg2, reg3) => AllocatedOpcode::EQ( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + EXP(reg1, reg2, reg3) => AllocatedOpcode::EXP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + EXPI(reg1, reg2, imm) => AllocatedOpcode::EXPI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + GT(reg1, reg2, reg3) => AllocatedOpcode::GT( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MLOG(reg1, reg2, reg3) => AllocatedOpcode::MLOG( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MROO(reg1, reg2, reg3) => AllocatedOpcode::MROO( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MOD(reg1, reg2, reg3) => AllocatedOpcode::MOD( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MODI(reg1, reg2, imm) => AllocatedOpcode::MODI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + MOVE(reg1, reg2) => { + AllocatedOpcode::MOVE(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + MUL(reg1, reg2, reg3) => AllocatedOpcode::MUL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MULI(reg1, reg2, imm) => AllocatedOpcode::MULI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + NOT(reg1, reg2) => { + AllocatedOpcode::NOT(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + OR(reg1, reg2, reg3) => AllocatedOpcode::OR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + ORI(reg1, reg2, imm) => AllocatedOpcode::ORI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SLL(reg1, reg2, reg3) => AllocatedOpcode::SLL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SLLI(reg1, reg2, imm) => AllocatedOpcode::SLLI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SRL(reg1, reg2, reg3) => AllocatedOpcode::SRL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SRLI(reg1, reg2, imm) => AllocatedOpcode::SRLI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SUB(reg1, reg2, reg3) => AllocatedOpcode::SUB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SUBI(reg1, reg2, imm) => AllocatedOpcode::SUBI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + XOR(reg1, reg2, reg3) => AllocatedOpcode::XOR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + XORI(reg1, reg2, imm) => AllocatedOpcode::XORI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + CIMV(reg1, reg2, reg3) => AllocatedOpcode::CIMV( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + CTMV(reg1, reg2) => { + AllocatedOpcode::CTMV(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + JI(imm) => AllocatedOpcode::JI(imm.clone()), + JNEI(reg1, reg2, imm) => AllocatedOpcode::JNEI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + RET(reg) => AllocatedOpcode::RET(map_reg(&mapping, reg)), + CFEI(imm) => AllocatedOpcode::CFEI(imm.clone()), + CFSI(imm) => AllocatedOpcode::CFSI(imm.clone()), + LB(reg1, reg2, imm) => AllocatedOpcode::LB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + LW(reg1, reg2, imm) => AllocatedOpcode::LW( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + ALOC(reg) => AllocatedOpcode::ALOC(map_reg(&mapping, reg)), + MCL(reg1, reg2) => { + AllocatedOpcode::MCL(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + MCLI(reg1, imm) => AllocatedOpcode::MCLI(map_reg(&mapping, reg1), imm.clone()), + MCP(reg1, reg2, reg3) => AllocatedOpcode::MCP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MEQ(reg1, reg2, reg3, reg4) => AllocatedOpcode::MEQ( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + SB(reg1, reg2, imm) => AllocatedOpcode::SB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SW(reg1, reg2, imm) => AllocatedOpcode::SW( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + BHSH(reg1, reg2) => { + AllocatedOpcode::BHSH(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + BHEI(reg1) => AllocatedOpcode::BHEI(map_reg(&mapping, reg1)), + BURN(reg1) => AllocatedOpcode::BURN(map_reg(&mapping, reg1)), + CALL(reg1, reg2, reg3, reg4) => AllocatedOpcode::CALL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + CCP(reg1, reg2, reg3, reg4) => AllocatedOpcode::CCP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + CROO(reg1, reg2) => { + AllocatedOpcode::CROO(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + CSIZ(reg1, reg2) => { + AllocatedOpcode::CSIZ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + CB(reg1) => AllocatedOpcode::CB(map_reg(&mapping, reg1)), + LDC(reg1, reg2, reg3) => AllocatedOpcode::LDC( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + LOG(reg1, reg2, reg3, reg4) => AllocatedOpcode::LOG( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + MINT(reg1) => AllocatedOpcode::MINT(map_reg(&mapping, reg1)), + RVRT(reg1) => AllocatedOpcode::RVRT(map_reg(&mapping, reg1)), + SLDC(reg1, reg2, reg3) => AllocatedOpcode::SLDC( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SRW(reg1, reg2) => { + AllocatedOpcode::SRW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + SRWQ(reg1, reg2) => { + AllocatedOpcode::SRWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + SWW(reg1, reg2) => { + AllocatedOpcode::SWW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + SWWQ(reg1, reg2) => { + AllocatedOpcode::SWWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) + } + TR(reg1, reg2, reg3) => AllocatedOpcode::TR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + TRO(reg1, reg2, reg3, reg4) => AllocatedOpcode::TRO( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + ECR(reg1, reg2, reg3) => AllocatedOpcode::ECR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + K256(reg1, reg2, reg3) => AllocatedOpcode::K256( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + S256(reg1, reg2, reg3) => AllocatedOpcode::S256( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + NOOP => AllocatedOpcode::NOOP, + FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), + Undefined => AllocatedOpcode::Undefined, + } + } +} + +/// An unchecked function which serves as a convenience for looking up register mappings +fn map_reg( + mapping: &HashMap<&VirtualRegister, AllocatedRegister>, + reg: &VirtualRegister, +) -> AllocatedRegister { + mapping.get(reg).unwrap().clone() +} + +#[derive(Clone, Eq, PartialEq, Hash)] +/// A label for a spot in the bytecode, to be later compiled to an offset. +pub(crate) struct Label(pub(crate) usize); +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, ".{}", self.0) + } +} diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index abc6a1a70fc..b559e6a95ff 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -591,6 +591,32 @@ pub enum CompileError<'sc> { point. Try providing an annotation or using a concrete type." )] TypeMustBeKnown { ty: String, span: Span<'sc> }, + #[error("The value \"{val}\" is too large to fit in this 6-bit immediate spot.")] + Immediate06TooLarge { val: u64, span: Span<'sc> }, + #[error("The value \"{val}\" is too large to fit in this 12-bit immediate spot.")] + Immediate12TooLarge { val: u64, span: Span<'sc> }, + #[error("The value \"{val}\" is too large to fit in this 18-bit immediate spot.")] + Immediate18TooLarge { val: u64, span: Span<'sc> }, + #[error("The value \"{val}\" is too large to fit in this 24-bit immediate spot.")] + Immediate24TooLarge { val: u64, span: Span<'sc> }, + #[error("The opcode \"jnei\" is not valid in inline assembly. Use an enclosing if expression instead.")] + DisallowedJnei { span: Span<'sc> }, + #[error( + "The opcode \"ji\" is not valid in inline assembly. Try using function calls instead." + )] + DisallowedJi { span: Span<'sc> }, + #[error( + "This op expects {expected} registers as arguments, but you provided {received} registers." + )] + IncorrectNumberOfAsmRegisters { + span: Span<'sc>, + expected: usize, + received: usize, + }, + #[error("This op does not take an immediate value.")] + UnnecessaryImmediate { span: Span<'sc> }, + #[error("This reference is ambiguous, and could refer to either a module or an enum of the same name. Try qualifying the name with a path.")] + AmbiguousPath { span: Span<'sc> }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -731,6 +757,15 @@ impl<'sc> CompileError<'sc> { UnknownEnumVariant { span, .. } => span, UnrecognizedOp { span, .. } => span, TypeMustBeKnown { span, .. } => span, + Immediate06TooLarge { span, .. } => span, + Immediate12TooLarge { span, .. } => span, + Immediate18TooLarge { span, .. } => span, + Immediate24TooLarge { span, .. } => span, + DisallowedJnei { span, .. } => span, + DisallowedJi { span, .. } => span, + IncorrectNumberOfAsmRegisters { span, .. } => span, + UnnecessaryImmediate { span, .. } => span, + AmbiguousPath { span, .. } => span, } } diff --git a/core_lang/src/parse_tree/expression/asm.rs b/core_lang/src/parse_tree/expression/asm.rs index 0e7d29f8c55..16ee89d43d2 100644 --- a/core_lang/src/parse_tree/expression/asm.rs +++ b/core_lang/src/parse_tree/expression/asm.rs @@ -1,4 +1,3 @@ -use crate::asm_lang::ImmediateValue; use crate::error::*; use crate::parser::Rule; use crate::{Ident, TypeInfo}; @@ -86,7 +85,7 @@ pub(crate) struct AsmOp<'sc> { pub(crate) op_name: Ident<'sc>, pub(crate) op_args: Vec>, pub(crate) span: Span<'sc>, - pub(crate) immediate: Option, + pub(crate) immediate: Option>, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -136,16 +135,10 @@ impl<'sc> AsmOp<'sc> { }); } Rule::asm_immediate => { - let imm: ImmediateValue = match pair.as_str().parse() { - Ok(o) => o, - Err(_e) => { - errors.push(CompileError::InvalidImmediateValue { - span: pair.as_span(), - }); - return err(warnings, errors); - } - }; - immediate_value = Some(imm); + immediate_value = Some(Ident { + primary_name: pair.as_str().trim(), + span: pair.as_span(), + }); } _ => unreachable!(), } diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index 10f161baebb..a601cd4b754 100644 --- a/core_lang/src/parse_tree/literal.rs +++ b/core_lang/src/parse_tree/literal.rs @@ -1,3 +1,4 @@ +use crate::asm_lang::virtual_ops::VirtualImmediate12; use crate::error::*; use crate::parser::Rule; use crate::CompileError; @@ -18,6 +19,24 @@ pub enum Literal<'sc> { } impl<'sc> Literal<'sc> { + // This function is very bad. Because I don't know how to do data sections right now, I just OR + // data against 0. + pub(crate) fn force_to_imm(&self) -> VirtualImmediate12 { + // please make sure this function dies quickly + use Literal::*; + match self { + U8(num) => VirtualImmediate12::new_unchecked(*num as u64, "the bad force_to_imm func"), + U16(num) => VirtualImmediate12::new_unchecked(*num as u64, "the bad force_to_imm func"), + U32(num) => VirtualImmediate12::new_unchecked(*num as u64, "the bad force_to_imm func"), + U64(num) => VirtualImmediate12::new_unchecked(*num, "the bad force_to_imm func"), + String(..) => panic!("Strings can't go in an immediate"), + Boolean(b) => VirtualImmediate12::new_unchecked(*b as u64, "the bad force_to_imm func"), + Byte(num) => { + VirtualImmediate12::new_unchecked(*num as u64, "the bad force_to_imm func") + } + Byte32(..) => panic!("byte32 can't fit in an immediate"), + } + } pub(crate) fn parse_from_pair(lit: Pair<'sc, Rule>) -> CompileResult<'sc, (Self, Span<'sc>)> { let lit_inner = lit.into_inner().next().unwrap(); let (parsed, span): (Result, _) = match lit_inner.as_rule() { diff --git a/core_lang/src/semantic_analysis/ast_node/expression/typed_expression.rs b/core_lang/src/semantic_analysis/ast_node/expression/typed_expression.rs index 0f9663b45ce..eb1872a8783 100644 --- a/core_lang/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/core_lang/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -684,11 +684,20 @@ impl<'sc> TypedExpression<'sc> { // enum instantiation let this_thing: Either = match (module_result, enum_module_combined_result) { - (Some(_module), Some(_enum_res)) => todo!("Ambiguous reference error"), + (Some(_module), Some(_enum_res)) => { + errors.push(CompileError::AmbiguousPath { span: span.clone() }); + return err(warnings, errors); + } (Some(module), None) => { match module.get_symbol(&call_path.suffix).cloned() { Some(decl) => Either::Left(decl), - None => todo!("symbol not found in module error"), + None => { + errors.push(CompileError::SymbolNotFound { + name: call_path.suffix.primary_name, + span: call_path.suffix.span.clone(), + }); + return err(warnings, errors); + } } } (None, Some(enum_decl)) => Either::Right(type_check!( diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.fm b/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.fm index 058684db365..e711c92db77 100644 --- a/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.fm +++ b/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.fm @@ -67,4 +67,11 @@ fn main() { let test = first_color == PrimaryColor::Green; // Specifically, when we call methods in the below way, `self` is undefined let rgb: Rgb = first_color.rgb(); + // now, going to test the register pool by using over 48 registers + let second_color = PrimaryColor::Blue; + let second_rgb = second_color.rgb(); + let second_color = PrimaryColor::Blue; + let second_rgb = second_color.rgb(); + let second_color = PrimaryColor::Blue; + let second_rgb = second_color.rgb(); } diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.fm b/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.fm index e23633aa9c9..72f2c2e900b 100644 --- a/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.fm +++ b/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.fm @@ -4,7 +4,7 @@ script; fn blockheight() -> u64 { asm(r1) { - blockheight r1; + bhei r1; r1: u64 } } @@ -16,7 +16,7 @@ struct GasCounts { fn get_gas() -> GasCounts { let x = asm(cgas: 6u64) { - blockheight cgas; + bhei cgas; cgas }; GasCounts {