From 655d991a2e71f54ea53c2df53b116e56d0cf53ef Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Thu, 13 May 2021 19:37:16 -0700 Subject: [PATCH 01/62] begin register allocator --- core_lang/src/asm_generation/mod.rs | 41 +++++++++++++++++++++++------ core_lang/src/asm_lang/mod.rs | 2 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 579a96da6bc..9b987e94af7 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -68,11 +68,16 @@ 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>, } +/// 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> { @@ -118,6 +123,10 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } + + fn allocate_registers(self) -> InstructionSet<'sc> { + todo!() + } } /// helper function to check if a label is used in a given buffer of ops @@ -255,6 +264,22 @@ impl fmt::Display for AbstractInstructionSet<'_> { } } + +impl fmt::Display for InstructionSet<'_> { + 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>, @@ -400,14 +425,14 @@ impl<'sc> JumpOptimizedAsmSet<'sc> { program_section, } => RegisterAllocatedAsmSet::ScriptMain { data_section, - program_section, + program_section: program_section.clone().allocate_registers(), }, JumpOptimizedAsmSet::PredicateMain { data_section, program_section, } => RegisterAllocatedAsmSet::PredicateMain { data_section, - program_section, + program_section: program_section.allocate_registers(), }, JumpOptimizedAsmSet::ContractAbi => RegisterAllocatedAsmSet::ContractAbi, } @@ -433,11 +458,11 @@ 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, @@ -473,11 +498,11 @@ 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, diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 9aef4354102..f071c79c847 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1076,7 +1076,7 @@ impl Opcode { }; ok(op, vec![], vec![]) } - pub(crate) fn get_register_names(&self) -> HashSet<&RegisterId> { + pub(crate) fn next_names(&self) -> HashSet<&RegisterId> { use Opcode::*; let regs: Vec<&RegisterId> = match self { Add(r1, r2, r3) => vec![r1, r2, r3], From 3e56e38c5cd76f5034489d1944fbe2db00813636 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Sat, 15 May 2021 06:44:06 -0700 Subject: [PATCH 02/62] begin reg alloc --- .../src/asm_generation/compiler_constants.rs | 1 + core_lang/src/asm_generation/mod.rs | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 8b137891791..960c8e52687 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1 +1,2 @@ +pub(crate) const NUM_FREE_REGISTERS: usize = 48; \ No newline at end of file diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 9b987e94af7..9ec4cf6039a 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -125,10 +125,36 @@ impl<'sc> AbstractInstructionSet<'sc> { } fn allocate_registers(self) -> InstructionSet<'sc> { + // Eventually, we will use a cool graph-coloring algorithm. + // For now, just keep a pool of registers todo!() } } +struct RegisterPool { + available_registers: Vec, +} + +struct Register { val: u8 } + +impl RegisterPool { + fn init() -> Self { + let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0).map(|x| Register(x)).collect(); + Self { + available_registers: register_pool + } + } + + fn get_register(&mut self) -> Option { + self.available_registers.pop() + } + + fn return_register_to_pool(&mut self, item_to_return: Register) { + self.available_registers.push(item_to_return); + } + +} + /// helper function to check if a label is used in a given buffer of ops fn label_is_used<'sc>(buf: &[Op<'sc>], label: &Label) -> bool { buf.iter().any(|Op { ref opcode, .. }| match opcode { From a7f469e291ca56b795c6a813f90552c4f653f868 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Sun, 16 May 2021 07:38:47 -0700 Subject: [PATCH 03/62] mutable virtual registers; basic allocation algorithm skeleton --- .../src/asm_generation/compiler_constants.rs | 3 +- core_lang/src/asm_generation/mod.rs | 58 ++++++-- core_lang/src/asm_lang/mod.rs | 130 +++++++++--------- 3 files changed, 116 insertions(+), 75 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 960c8e52687..ebf5cface64 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1,2 +1 @@ - -pub(crate) const NUM_FREE_REGISTERS: usize = 48; \ No newline at end of file +pub(crate) const NUM_FREE_REGISTERS: u8 = 48; diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 9ec4cf6039a..d514469d8bc 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -124,24 +124,69 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } - fn allocate_registers(self) -> InstructionSet<'sc> { + fn allocate_registers(mut self) -> InstructionSet<'sc> { // Eventually, we will use a cool graph-coloring algorithm. - // For now, just keep a pool of registers + // 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 mut op_register_mapping = self + .ops + .iter_mut() + .map(|op| { + ( + op.clone(), + match op.opcode { + Either::Left(mut opc) => opc.registers(), + Either::Right(mut orgop) => orgop.registers(), + }, + ) + }) + .collect::>(); + + // get registers from the pool. + // if the registers are never read again, return them to the pool. + let mut pool = RegisterPool::init(); + for (op, registers) in op_register_mapping { + let new_registers: Option> = registers + .into_iter() + .map(|reg| (reg, pool.get_register())) + .collect(); + let new_registers = match new_registers { + a @ (_, Some(_)) => a, + _ => todo!("Return out of registers error"), + }; + // if the virtual register is never read again, then we can + // return this virtual register back into the pool + + // TODO: + // properly parse reserved registers and handle them in asm expressions + // do not pull from the pool for reserved registers + // + } todo!() } } +fn register_is_never_read_again(reg: &Register, ops: &[(Op, Vec)]) -> bool { + todo!() +} struct RegisterPool { available_registers: Vec, } -struct Register { val: u8 } +enum Register { + Free(u8), + Reserved(u8), +} impl RegisterPool { fn init() -> Self { - let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0).map(|x| Register(x)).collect(); + let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0) + .map(|x| Register::Free(x)) + .collect(); Self { - available_registers: register_pool + available_registers: register_pool, } } @@ -152,7 +197,6 @@ impl RegisterPool { fn return_register_to_pool(&mut self, item_to_return: Register) { self.available_registers.push(item_to_return); } - } /// helper function to check if a label is used in a given buffer of ops @@ -290,7 +334,6 @@ impl fmt::Display for AbstractInstructionSet<'_> { } } - impl fmt::Display for InstructionSet<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -305,7 +348,6 @@ impl fmt::Display for InstructionSet<'_> { } } - #[derive(Default, Clone)] pub(crate) struct AsmNamespace<'sc> { data_section: DataSection<'sc>, diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index f071c79c847..26f8d8d9e4c 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1076,75 +1076,75 @@ impl Opcode { }; ok(op, vec![], vec![]) } - pub(crate) fn next_names(&self) -> HashSet<&RegisterId> { + pub(crate) fn registers(&mut self) -> HashSet<&mut 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], + let regs: Vec<&mut RegisterId> = match self { + Add(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Addi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + And(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Andi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Div(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Divi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Mod(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Modi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Eq(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Gt(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Mult(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Multi(ref mut r1, ref mut 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], + Not(ref mut r1, ref mut r2) => vec![r1, r2], + Or(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Ori(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Sll(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Sllv(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Sltiu(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Sltu(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Sra(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Srl(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Srlv(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Srav(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Sub(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Subi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Xor(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Xori(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Exp(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Expi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + CIMV(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + CTMV(ref mut r1, ref mut r2) => vec![r1, r2], Ji(_imm) => vec![], - Jnzi(r1, _imm) => vec![r1], - Ret(r1) => vec![r1], + Jnzi(ref mut r1, _imm) => vec![r1], + Ret(ref mut 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], + Cfs(ref mut r1) => vec![r1], + Lb(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Lw(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Malloc(ref mut r1) => vec![r1], + MemClearImmediate(ref mut r1, _imm) => vec![r1], + MemCp(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + MemEq(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], + Sb(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + Sw(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + BlockHash(ref mut r1, ref mut r2) => vec![r1, r2], + BlockHeight(ref mut r1) => vec![r1], + Call(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], + CodeCopy(ref mut r1, ref mut r2, _imm) => vec![r1, r2], + CodeRoot(ref mut r1, ref mut r2) => vec![r1, r2], + Codesize(ref mut r1, ref mut r2) => vec![r1, r2], + Coinbase(ref mut r1) => vec![r1], + LoadCode(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + SLoadCode(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Log(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], + Revert(ref mut r1) => vec![r1], + Srw(ref mut r1, ref mut r2) => vec![r1, r2], + Srwx(ref mut r1, ref mut r2) => vec![r1, r2], + Sww(ref mut r1, ref mut r2) => vec![r1, r2], + Swwx(ref mut r1, ref mut r2) => vec![r1, r2], + Transfer(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + TransferOut(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], + Ecrecover(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Keccak256(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Sha256(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], + Flag(ref mut r1) => vec![r1], }; regs.into_iter().collect() From 5c500721ebe08deb4ce04c9603aebba92eafb6bb Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Sun, 16 May 2021 20:30:12 -0700 Subject: [PATCH 04/62] mutable registers in allocation --- core_lang/src/asm_generation/mod.rs | 17 +++++++++++------ core_lang/src/asm_lang/mod.rs | 13 +++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index d514469d8bc..39e37d79220 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -149,26 +149,31 @@ impl<'sc> AbstractInstructionSet<'sc> { let mut pool = RegisterPool::init(); for (op, registers) in op_register_mapping { let new_registers: Option> = registers - .into_iter() - .map(|reg| (reg, pool.get_register())) + .iter() + .map(|reg| pool.get_register()) .collect(); let new_registers = match new_registers { - a @ (_, Some(_)) => a, + Some(a) => registers.into_iter().zip(a.into_iter()).collect::>(), _ => todo!("Return out of registers error"), }; // if the virtual register is never read again, then we can // return this virtual register back into the pool - + new_registers.iter().for_each(|(virtual_reg, real_reg)| { + if virtual_register_is_never_accessed_again(&virtual_reg, op_register_mapping.as_slice()) { + pool.return_register_to_pool(*real_reg); + } + }); + // TODO: // properly parse reserved registers and handle them in asm expressions // do not pull from the pool for reserved registers - // + // Rename RegisterId to VirtualRegister } todo!() } } -fn register_is_never_read_again(reg: &Register, ops: &[(Op, Vec)]) -> bool { +fn virtual_register_is_never_accessed_again(reg: &RegisterId, ops: &[(Op, std::collections::HashSet<&mut RegisterId>)]) -> bool { todo!() } struct RegisterPool { diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 26f8d8d9e4c..02ad92e72e8 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1426,3 +1426,16 @@ pub(crate) enum OrganizationalOp { // JumpIfNotEq(RegisterId, RegisterId, Label), } + + +impl OrganizationalOp { + pub(crate) fn registers(&mut self) -> HashSet<&mut RegisterId> { + use OrganizationalOp::*; + (match self { + RMove(ref mut r1, ref mut r2) => vec![r1, r2], + Label(_) | Comment | Jump(_) => vec![], + Ld(r1, _) => vec![r1], + JumpIfNotEq(r1, r2, _l) => vec![r1, r2] + }).into_iter().collect() + } +} \ No newline at end of file From e40775798082db4e999fa639ec129eb4be9ac59d Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Tue, 18 May 2021 07:33:17 -0700 Subject: [PATCH 05/62] pull in fuel-asm official ops --- core_lang/Cargo.toml | 1 + core_lang/src/asm_lang/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index b1cb553799e..9a7f33b66c4 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://github.com/FuelLabs/fuel-asm.git" } diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 02ad92e72e8..d7e160097b8 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -9,6 +9,7 @@ use crate::{asm_generation::DataId, error::*, parse_tree::AsmRegister, Ident}; use either::Either; use pest::Span; use std::{collections::HashSet, fmt}; +use fuel_asm::Opcode; /// The column where the ; for comments starts const COMMENT_START_COLUMN: usize = 40; From f99b1270c0310a505fc73cf639a991c13ed1b679 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 19 May 2021 07:07:46 -0700 Subject: [PATCH 06/62] switching laptops --- core_lang/Cargo.toml | 3 +- .../expression/enum_instantiation.rs | 5 +- core_lang/src/asm_generation/mod.rs | 23 +- core_lang/src/asm_lang/mod.rs | 951 +----------------- 4 files changed, 52 insertions(+), 930 deletions(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index 9a7f33b66c4..37e0d34dd7f 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,4 +15,5 @@ either = "1.6" Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" -fuel-asm = { git = "ssh://github.com/FuelLabs/fuel-asm.git" } +fuel-asm = { path = "../../fuel-asm" } + diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index a5f8ad857f2..3e3ccfe573f 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -1,10 +1,11 @@ use crate::asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer}; -use crate::asm_lang::{ConstantRegister, Op, Opcode, RegisterId}; +use crate::asm_lang::{ConstantRegister, Op, RegisterId}; use crate::error::*; use crate::semantic_analysis::ast_node::TypedEnumDeclaration; use crate::semantic_analysis::TypedExpression; use crate::Literal; use crate::{CompileResult, Ident}; +use fuel_asm::Opcode; use std::convert::TryFrom; pub(crate) fn convert_enum_instantiation_to_asm<'sc>( @@ -53,7 +54,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( asm_buf.push(Op::unowned_stack_allocate_memory(size_of_enum)); // initialize all the memory to 0 asm_buf.push(Op::new( - Opcode::MemClearImmediate(pointer_register.clone(), size_of_enum), + Opcode::MCLI(pointer_register.clone(), size_of_enum), decl.clone().span, )); // write the tag diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 39e37d79220..ccfa6e3fa19 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -138,7 +138,7 @@ impl<'sc> AbstractInstructionSet<'sc> { op.clone(), match op.opcode { Either::Left(mut opc) => opc.registers(), - Either::Right(mut orgop) => orgop.registers(), + Either::Right(mut orgop) => todo!(), }, ) }) @@ -148,10 +148,8 @@ impl<'sc> AbstractInstructionSet<'sc> { // if the registers are never read again, return them to the pool. let mut pool = RegisterPool::init(); for (op, registers) in op_register_mapping { - let new_registers: Option> = registers - .iter() - .map(|reg| pool.get_register()) - .collect(); + let new_registers: Option> = + registers.iter().map(|reg| pool.get_register()).collect(); let new_registers = match new_registers { Some(a) => registers.into_iter().zip(a.into_iter()).collect::>(), _ => todo!("Return out of registers error"), @@ -159,11 +157,17 @@ impl<'sc> AbstractInstructionSet<'sc> { // if the virtual register is never read again, then we can // return this virtual register back into the pool new_registers.iter().for_each(|(virtual_reg, real_reg)| { - if virtual_register_is_never_accessed_again(&virtual_reg, op_register_mapping.as_slice()) { + todo!() + /* + if virtual_register_is_never_accessed_again( + &virtual_reg, + op_register_mapping.as_slice(), + ) { pool.return_register_to_pool(*real_reg); } + */ }); - + // TODO: // properly parse reserved registers and handle them in asm expressions // do not pull from the pool for reserved registers @@ -173,7 +177,10 @@ impl<'sc> AbstractInstructionSet<'sc> { } } -fn virtual_register_is_never_accessed_again(reg: &RegisterId, ops: &[(Op, std::collections::HashSet<&mut RegisterId>)]) -> bool { +fn virtual_register_is_never_accessed_again( + reg: &RegisterId, + ops: &[(Op, std::collections::HashSet<&mut RegisterId>)], +) -> bool { todo!() } struct RegisterPool { diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index d7e160097b8..4815b390507 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -7,36 +7,14 @@ use crate::{asm_generation::DataId, error::*, parse_tree::AsmRegister, Ident}; use either::Either; +use fuel_asm::Opcode; use pest::Span; +use std::str::FromStr; use std::{collections::HashSet, fmt}; -use fuel_asm::Opcode; /// 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 { fn from(o: &AsmRegister) -> Self { RegisterId::Virtual(o.name.clone()) @@ -61,7 +39,7 @@ impl<'sc> Op<'sc> { span: Span<'sc>, ) -> Self { Op { - opcode: Either::Left(Opcode::Sw(destination_address, value_to_write, offset)), + opcode: Either::Left(Opcode::SW(destination_address, value_to_write, offset)), comment: String::new(), owning_span: Some(span), } @@ -76,7 +54,7 @@ impl<'sc> Op<'sc> { comment: impl Into, ) -> Self { Op { - opcode: Either::Left(Opcode::Sw(destination_address, value_to_write, offset)), + opcode: Either::Left(Opcode::SW(destination_address, value_to_write, offset)), comment: comment.into(), owning_span: Some(span), } @@ -84,7 +62,7 @@ impl<'sc> Op<'sc> { /// 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 { Op { - opcode: Either::Left(Opcode::Cfei(size_to_allocate_in_words)), + opcode: Either::Left(Opcode::CFEI(size_to_allocate_in_words)), comment: String::new(), owning_span: None, } @@ -261,824 +239,33 @@ impl Into for &RegisterId { } } -impl Opcode { +trait Parsable { + fn parse<'sc>( + name: &Ident<'sc>, + args: &[&RegisterId], + immediate: Option, + ) -> CompileResult<'sc, Opcode>; + fn registers(&mut self) -> HashSet<&mut RegisterId>; +} + +impl Parsable for 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>( + 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") - } - } - "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 { - op_name: other, - span: name.clone().span, - }], - ) - } + let name = name.primary_name.to_uppercase(); + let op = match Opcode::from_str(&name) { + Ok(o) => o, + Err(e) => todo!("Error parsing op"), }; ok(op, vec![], vec![]) } - pub(crate) fn registers(&mut self) -> HashSet<&mut RegisterId> { - use Opcode::*; + fn registers(&mut self) -> HashSet<&mut RegisterId> { + todo!() + /* let regs: Vec<&mut RegisterId> = match self { Add(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], Addi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], @@ -1149,6 +336,7 @@ impl Opcode { }; regs.into_iter().collect() + */ } } @@ -1205,85 +393,6 @@ impl fmt::Display for ConstantRegister { // Immediate Value. pub type ImmediateValue = u32; -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, - - // Control Flow Opcodes. - CIMV(RegisterId, RegisterId, ImmediateValue) = 50, - CTMV(RegisterId, RegisterId) = 51, - Ji(ImmediateValue) = 52, - Jnzi(RegisterId, ImmediateValue) = 53, - Ret(RegisterId) = 54, - - // 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, - - // 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, - - // Cryptographic Opcodes. - Ecrecover(RegisterId, RegisterId, RegisterId) = 110, - Keccak256(RegisterId, RegisterId, RegisterId) = 111, - Sha256(RegisterId, RegisterId, RegisterId) = 112, - - // Additional Opcodes. - Flag(RegisterId) = 130 -} impl fmt::Display for RegisterId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -1306,6 +415,8 @@ impl fmt::Display for Op<'_> { // 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 { + todo!() + /* use Opcode::*; use OrganizationalOp::*; let op_str = match &self.opcode { @@ -1403,6 +514,7 @@ impl fmt::Display for Op<'_> { } write!(f, "{}", op_and_comment) + */ } } @@ -1428,7 +540,6 @@ pub(crate) enum OrganizationalOp { JumpIfNotEq(RegisterId, RegisterId, Label), } - impl OrganizationalOp { pub(crate) fn registers(&mut self) -> HashSet<&mut RegisterId> { use OrganizationalOp::*; @@ -1436,7 +547,9 @@ impl OrganizationalOp { RMove(ref mut r1, ref mut r2) => vec![r1, r2], Label(_) | Comment | Jump(_) => vec![], Ld(r1, _) => vec![r1], - JumpIfNotEq(r1, r2, _l) => vec![r1, r2] - }).into_iter().collect() + JumpIfNotEq(r1, r2, _l) => vec![r1, r2], + }) + .into_iter() + .collect() } -} \ No newline at end of file +} From af1c1123f4005cab0c1e924f740184f7c9728dc6 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 19 May 2021 15:09:31 -0700 Subject: [PATCH 07/62] begin work on virtual registers and ops --- core_lang/Cargo.toml | 1 - core_lang/src/asm_lang/mod.rs | 269 +----- core_lang/src/asm_lang/virtual_ops.rs | 1276 +++++++++++++++++++++++++ core_lang/src/error.rs | 12 + 4 files changed, 1340 insertions(+), 218 deletions(-) create mode 100644 core_lang/src/asm_lang/virtual_ops.rs diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index 37e0d34dd7f..09e121d494f 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -16,4 +16,3 @@ Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" fuel-asm = { path = "../../fuel-asm" } - diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 4815b390507..83cebeb1841 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -7,54 +7,58 @@ use crate::{asm_generation::DataId, error::*, parse_tree::AsmRegister, Ident}; use either::Either; -use fuel_asm::Opcode; use pest::Span; use std::str::FromStr; use std::{collections::HashSet, fmt}; +mod virtual_ops; +use virtual_ops::{ + VirtualImmediate06, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, + VirtualRegister, +}; /// The column where the ; for comments starts const COMMENT_START_COLUMN: usize = 40; -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>, } 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: VirtualImmediateValue, 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), } @@ -62,19 +66,19 @@ impl<'sc> Op<'sc> { /// 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 { 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(), @@ -82,7 +86,7 @@ impl<'sc> Op<'sc> { } } pub(crate) fn new_with_comment( - opcode: Opcode, + opcode: VirtualOp, owning_span: Span<'sc>, comment: impl Into, ) -> Self { @@ -102,9 +106,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 { @@ -149,16 +153,20 @@ 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::RMove(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)), comment: String::new(), @@ -166,8 +174,8 @@ impl<'sc> Op<'sc> { } } pub(crate) fn register_move_comment( - r1: RegisterId, - r2: RegisterId, + r1: VirtualRegister, + r2: VirtualRegister, owning_span: Span<'sc>, comment: impl Into, ) -> Self { @@ -180,8 +188,8 @@ 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 { @@ -215,8 +223,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(), @@ -226,181 +238,10 @@ impl<'sc> Op<'sc> { pub(crate) fn parse_opcode( name: &Ident<'sc>, - args: &[&RegisterId], - immediate: Option, - ) -> CompileResult<'sc, Opcode> { - Opcode::parse(name, args, immediate) - } -} - -impl Into for &RegisterId { - fn into(self) -> RegisterId { - self.clone() - } -} - -trait Parsable { - fn parse<'sc>( - name: &Ident<'sc>, - args: &[&RegisterId], - immediate: Option, - ) -> CompileResult<'sc, Opcode>; - fn registers(&mut self) -> HashSet<&mut RegisterId>; -} - -impl Parsable for Opcode { - /// If this name matches an opcode and there are the correct number and - /// type of arguments, parse the given inputs into an opcode. - fn parse<'sc>( - name: &Ident<'sc>, - args: &[&RegisterId], - immediate: Option, - ) -> CompileResult<'sc, Opcode> { - let name = name.primary_name.to_uppercase(); - let op = match Opcode::from_str(&name) { - Ok(o) => o, - Err(e) => todo!("Error parsing op"), - }; - ok(op, vec![], vec![]) - } - fn registers(&mut self) -> HashSet<&mut RegisterId> { - todo!() - /* - let regs: Vec<&mut RegisterId> = match self { - Add(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Addi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - And(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Andi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Div(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Divi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Mod(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Modi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Eq(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Gt(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Mult(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Multi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Noop() => vec![], - Not(ref mut r1, ref mut r2) => vec![r1, r2], - Or(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Ori(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Sll(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Sllv(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Sltiu(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Sltu(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Sra(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Srl(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Srlv(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Srav(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Sub(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Subi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Xor(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Xori(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Exp(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Expi(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - CIMV(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - CTMV(ref mut r1, ref mut r2) => vec![r1, r2], - Ji(_imm) => vec![], - Jnzi(ref mut r1, _imm) => vec![r1], - Ret(ref mut r1) => vec![r1], - Cfei(_imm) => vec![], - Cfs(ref mut r1) => vec![r1], - Lb(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Lw(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Malloc(ref mut r1) => vec![r1], - MemClearImmediate(ref mut r1, _imm) => vec![r1], - MemCp(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - MemEq(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], - Sb(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - Sw(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - BlockHash(ref mut r1, ref mut r2) => vec![r1, r2], - BlockHeight(ref mut r1) => vec![r1], - Call(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], - CodeCopy(ref mut r1, ref mut r2, _imm) => vec![r1, r2], - CodeRoot(ref mut r1, ref mut r2) => vec![r1, r2], - Codesize(ref mut r1, ref mut r2) => vec![r1, r2], - Coinbase(ref mut r1) => vec![r1], - LoadCode(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - SLoadCode(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Log(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], - Revert(ref mut r1) => vec![r1], - Srw(ref mut r1, ref mut r2) => vec![r1, r2], - Srwx(ref mut r1, ref mut r2) => vec![r1, r2], - Sww(ref mut r1, ref mut r2) => vec![r1, r2], - Swwx(ref mut r1, ref mut r2) => vec![r1, r2], - Transfer(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - TransferOut(ref mut r1, ref mut r2, ref mut r3, ref mut r4) => vec![r1, r2, r3, r4], - Ecrecover(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Keccak256(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Sha256(ref mut r1, ref mut r2, ref mut r3) => vec![r1, r2, r3], - Flag(ref mut r1) => vec![r1], - }; - - regs.into_iter().collect() - */ - } -} - -// 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), -} - -#[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) - } -} - -// Immediate Value. -pub type ImmediateValue = u32; - -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) - } - } + args: &[&VirtualRegister], + immediate: Option, + ) -> CompileResult<'sc, VirtualOp> { + VirtualOp::parse(name, args, immediate) } } @@ -417,7 +258,7 @@ impl fmt::Display for Op<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!() /* - use Opcode::*; + use VirtualOp::*; use OrganizationalOp::*; let op_str = match &self.opcode { Either::Left(opcode) => match opcode { @@ -464,7 +305,7 @@ impl fmt::Display for Op<'_> { 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), + MemClearVirtualImmediate(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), @@ -525,8 +366,6 @@ pub(crate) struct Label(pub(crate) usize); // 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 @@ -535,19 +374,15 @@ pub(crate) enum OrganizationalOp { Jump(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(&mut self) -> HashSet<&mut RegisterId> { + pub(crate) fn registers(&mut self) -> HashSet<&mut VirtualRegister> { use OrganizationalOp::*; (match self { - RMove(ref mut r1, ref mut r2) => vec![r1, r2], Label(_) | Comment | Jump(_) => vec![], Ld(r1, _) => vec![r1], - JumpIfNotEq(r1, r2, _l) => 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..494553f4f37 --- /dev/null +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -0,0 +1,1276 @@ +//! This module contains abstracted versions of bytecode primitives that the compiler uses to +//! ensure correctness and safety. + +use crate::{error::*, Ident}; +use pest::Span; +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 { + 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(), + }) + } + } +} + +/// 12-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate12 { + value: u16, +} +impl VirtualImmediate12 { + 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(), + }) + } + } +} + +/// 18-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate18 { + value: u32, +} +impl VirtualImmediate18 { + fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > 0b111_111_111_111_111_111 { + return Err(CompileError::Immediate18TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } +} + +/// 24-bits immediate value type +#[derive(Clone)] +pub struct VirtualImmediate24 { + value: u32, +} +impl VirtualImmediate24 { + fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + if raw > 0b111_111_111_111_111_111_111_111 { + return Err(CompileError::Immediate24TooLarge { + val: raw, + span: err_msg_span, + }); + } else { + Ok(Self { + value: raw.try_into().unwrap(), + }) + } + } +} + +/// 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 { + /// Adds two registers. + /// + /// | Operation | ```$rA = $rB + $rC;``` | + /// | Syntax | `add $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// `$err` is cleared. + ADD(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Adds a register and an immediate value. + /// + /// | Operation | ```$rA = $rB + imm;``` | + /// | Syntax | `addi $rA, $rB, immediate` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// `$err` is cleared. + ADDI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Bitwise ANDs two registers. + /// + /// | Operation | ```$rA = $rB & $rC;``` | + /// | Syntax | `and $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` and `$err` are cleared. + AND(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Bitwise ANDs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB & imm;``` | + /// | Syntax | `andi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. + /// `$of` and `$err` are cleared. + ANDI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Divides two registers. + /// + /// | Operation | ```$rA = $rB // $rC;``` | + /// | Syntax | `div $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// If `$rC == 0`, `$rA` is cleared and `$err` is set to `true`. + /// Otherwise, `$err` is cleared. + /// `$of` is cleared. + DIV(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Divides a register and an immediate value. + /// + /// | Operation | ```$rA = $rB // imm;``` | + /// | Syntax | `divi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// If `imm == 0`, `$rA` is cleared and `$err` is set to `true`. + /// Otherwise, `$err` is cleared. + /// `$of` is cleared. + DIVI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Compares two registers for equality. + /// + /// | Operation | ```$rA = $rB == $rC;``` | + /// | Syntax | `eq $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register, + /// + /// #### Execution + /// `$of` and `$err` are cleared. + EQ(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Raises one register to the power of another. + /// + /// | Operation | ```$rA = $rB ** $rC;``` | + /// | Syntax | `exp $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise + /// `$of` is cleared. + /// `$err` is cleared. + EXP(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Raises one register to the power of an immediate value. + /// + /// | Operation | ```$rA = $rB ** imm;``` | + /// | Syntax | `expi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise + /// `$of` is cleared. + /// `$err` is cleared. + EXPI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Compares two registers for greater-than. + /// + /// | Operation | ```$rA = $rB > $rC;``` | + /// | Syntax | `gt $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + GT(VirtualRegister, VirtualRegister, VirtualRegister), + + /// The (integer) logarithm base `$rC` of `$rB`. + /// + /// | Operation | ```$rA = math.floor(math.log($rB, $rC));``` | + /// | Syntax | `mlog $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rB == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// If `$rC <= 1`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MLOG(VirtualRegister, VirtualRegister, VirtualRegister), + + /// The (integer) `$rC`th root of `$rB`. + /// + /// | Operation | ```$rA = math.floor(math.root($rB, $rC));``` | + /// | Syntax | `mroo $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MROO(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Modulo remainder of two registers. + /// + /// | Operation | ```$rA = $rB % $rC;``` | + /// | Syntax | `mod $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MOD(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Modulo remainder of a register and an immediate value. + /// + /// | Operation | ```$rA = $rB % imm;``` | + /// | Syntax | `modi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `imm == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MODI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Copy from one register to another. + /// + /// | Operation | ```$rA = $rB;``` | + /// | Syntax | `move $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + MOVE(VirtualRegister, VirtualRegister), + + /// Multiplies two registers. + /// + /// | Operation | ```$rA = $rB * $rC;``` | + /// | Syntax | `mul $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + MUL(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Multiplies a register and an immediate value. + /// + /// | Operation | ```$rA = $rB * imm;``` | + /// | Syntax | `mul $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + MULI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Bitwise NOT a register. + /// + /// | Operation | ```$rA = ~$rB;``` | + /// | Syntax | `not $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + NOT(VirtualRegister, VirtualRegister), + + /// Bitwise ORs two registers. + /// + /// | Operation | ```$rA = $rB \| $rC;``` | + /// | Syntax | `or $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + OR(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Bitwise ORs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB \| imm;``` | + /// | Syntax | `ori $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. + /// + /// `$of` and `$err` are cleared. + ORI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Left shifts a register by a register. + /// + /// | Operation | ```$rA = $rB << $rC;``` | + /// | Syntax | `sll $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + SLL(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Left shifts a register by an immediate value. + /// + /// | Operation | ```$rA = $rB << imm;``` | + /// | Syntax | `slli $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + SLLI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Right shifts a register by a register. + /// + /// | Operation | ```$rA = $rB >> $rC;``` | + /// | Syntax | `srl $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SRL(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Right shifts a register by an immediate value. + /// + /// | Operation | ```$rA = $rB >> imm;``` | + /// | Syntax | `srli $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SRLI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Subtracts two registers. + /// + /// | Operation | ```$rA = $rB - $rC;``` | + /// | Syntax | `sub $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | `$of` is assigned the overflow of the operation. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SUB(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Subtracts a register and an immediate value. + /// + /// | Operation | ```$rA = $rB - imm;``` | + /// | Syntax | `subi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | `$of` is assigned the overflow of the operation. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SUBI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Bitwise XORs two registers. + /// + /// | Operation | ```$rA = $rB ^ $rC;``` | + /// | Syntax | `xor $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + XOR(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Bitwise XORs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB ^ imm;``` | + /// | Syntax | `xori $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + XORI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Set `$rA` to `true` if the `$rC <= tx.input[$rB].maturity`. + /// + /// | Operation | ```$rA = checkinputmaturityverify($rB, $rC);``` | + /// | Syntax | `cimv $rA $rB $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rC > tx.input[$rB].maturity` + /// - the input `$rB` is not of type + /// [`InputType.Coin`](../protocol/tx_format.md) + /// - `$rB > tx.inputsCount` + /// + /// #### Execution + /// Otherwise, advance the program counter `$pc` by `4`. + /// + /// See also: [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and [CLTV](#cltv-check-lock-time-verify). + CIMV(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Set `$rA` to `true` if `$rB <= tx.maturity`. + /// + /// | Operation | ```$rA = checktransactionmaturityverify($rB);``` | + /// | Syntax | `ctmv $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB > tx.maturity` + /// + /// #### Execution + /// Otherwise, advance the program counter `$pc` by `4`. + /// + /// See also: [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and [Bitcoin's Time Locks](https://prestwi.ch/bitcoin-time-locks). + CTMV(VirtualRegister, VirtualRegister), + + /// Jumps to the code instruction offset by `imm`. + /// + /// | Operation | ```$pc = $is + imm * 4;``` | + /// | Syntax | `ji imm` | + /// | Encoding | `0x00 i i i i` | + /// + /// #### Panics + /// - `$is + imm * 4 > VM_MAX_RAM - 1` + JI(VirtualImmediate24), + + /// Jump to the code instruction offset by `imm` if `$rA` is not equal to + /// `$rB`. + /// + /// | Operation | ```if $rA != $rB:```
```$pc = $is + imm * + /// 4;```
```else:```
```$pc += 4;``` | Syntax | `jnei $rA + /// $rB imm` | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$is + imm * 4 > VM_MAX_RAM - 1` + JNEI(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Returns from [context](./main.md#contexts) with value `$rA`. + /// + /// | Operation | ```return($rA);``` + /// | Syntax | `ret $rA` + /// | Encoding | `0x00 rA - - -` + /// + /// If current context is external, cease VM execution and return `$rA`. + /// + /// Returns from contract call, popping the call frame. Before popping: + /// + /// 1. Return the unused forwarded gas to the caller: + /// - `$cgas = $cgas + $fp->$cgas` (add remaining context gas from + /// previous context to current remaining context gas) + /// + /// Then pop the call frame and restoring registers _except_ `$ggas` and + /// `$cgas`. Afterwards, set the following registers: + /// + /// 1. `$pc = $pc + 4` (advance program counter from where we called) + RET(VirtualRegister), + + /// Extend the current call frame's stack by an immediate value. + /// + /// | Operation | ```$sp = $sp + imm``` + /// | Syntax | `cfei imm` + /// | Encoding | `0x00 i i i i` + /// | Notes | Does not initialize memory. + /// + /// #### Panics + /// - `$sp + imm` overflows + /// - `$sp + imm > $hp` + CFEI(VirtualImmediate24), + + /// Shrink the current call frame's stack by an immediate value. + /// + /// | Operation | ```$sp = $sp - imm``` + /// | Syntax | `cfsi imm` + /// | Encoding | `0x00 i i i i` + /// | Notes | Does not clear memory. + /// + /// #### Panics + /// - `$sp - imm` underflows + /// - `$sp - imm < $ssp` + CFSI(VirtualImmediate24), + + /// A byte is loaded from the specified address offset by `imm`. + /// + /// | Operation | ```$rA = MEM[$rB + imm, 1];``` + /// | Syntax | `lb $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + imm + 1` overflows + /// - `$rB + imm + 1 > VM_MAX_RAM` + LB(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// A word is loaded from the specified address offset by `imm`. + /// | Operation | ```$rA = MEM[$rB + imm, 8];``` + /// | Syntax | `lw $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + imm + 8` overflows + /// - `$rB + imm + 8 > VM_MAX_RAM` + LW(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Allocate a number of bytes from the heap. + /// + /// | Operation | ```$hp = $hp - $rA;``` | + /// | Syntax | `aloc $rA` | + /// | Encoding | `0x00 rA - - -` | + /// | Notes | Does not initialize memory. | + /// + /// #### Panics + /// - `$hp - $rA` underflows + /// - `$hp - $rA < $sp` + ALOC(VirtualRegister), + + /// Clear bytes in memory. + /// + /// | Operation | ```MEM[$rA, $rB] = 0;``` | + /// | Syntax | `mcl $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + $rB` overflows + /// - `$rA + $rB > VM_MAX_RAM` + /// - `$rB > MEM_MAX_ACCESS_SIZE` + /// - The memory range `MEM[$rA, $rB]` does not pass [ownership + /// check](./main.md#ownership) + MCL(VirtualRegister, VirtualRegister), + + /// Clear bytes in memory. + /// + /// | Operation | ```MEM[$rA, imm] = 0;``` | + /// | Syntax | `mcli $rA, imm` | + /// | Encoding | `0x00 rA i i i` | + /// + /// #### Panics + /// - `$rA + imm` overflows + /// - `$rA + imm > VM_MAX_RAM` + /// - `imm > MEM_MAX_ACCESS_SIZE` + /// - The memory range `MEM[$rA, imm]` does not pass [ownership + /// check](./main.md#ownership) + MCLI(VirtualRegister, VirtualImmediate18), + + /// Copy bytes in memory. + /// + /// | Operation | ```MEM[$rA, $rC] = MEM[$rB, $rC];``` | + /// | Syntax | `mcp $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA + $rC` overflows + /// - `$rB + $rC` overflows + /// - `$rA + $rC > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - The memory ranges `MEM[$rA, $rC]` and `MEM[$rB, $rC]` overlap + /// - The memory range `MEM[$rA, $rC]` does not pass [ownership + /// check](./main.md#ownership) + MCP(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Compare bytes in memory. + /// + /// | Operation | ```$rA = MEM[$rB, $rD] == MEM[$rC, $rD];``` | + /// | Syntax | `meq $rA, $rB, $rC, $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + $rD` overflows + /// - `$rC + $rD` overflows + /// - `$rB + $rD > VM_MAX_RAM` + /// - `$rC + $rD > VM_MAX_RAM` + /// - `$rD > MEM_MAX_ACCESS_SIZE` + MEQ( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + + /// The least significant byte of `$rB` is stored at the address `$rA` + /// offset by `imm`. + /// + /// | Operation | ```MEM[$rA + imm, 1] = $rB[7, 1];``` | + /// | Syntax | `sb $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA + imm + 1` overflows + /// - `$rA + imm + 1 > VM_MAX_RAM` + /// - The memory range `MEM[$rA + imm, 1]` does not pass [ownership + /// check](./main.md#ownership) + SB(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// The value of `$rB` is stored at the address `$rA` offset by `imm`. + /// + /// | Operation | ```MEM[$rA + imm, 8] = $rB;``` + /// | Syntax | `sw $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA + imm + 8` overflows + /// - `$rA + imm + 8 > VM_MAX_RAM` + /// - The memory range `MEM[$rA + imm, 8]` does not pass [ownership + /// check](./main.md#ownership) + SW(VirtualRegister, VirtualRegister, VirtualImmediate12), + + /// Get block header hash. + /// + /// | Operation | ```MEM[$rA, 32] = blockhash($rB);``` | + /// | Syntax | `bhsh $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// + /// Block header hashes for blocks with height greater than or equal to + /// current block height are zero (`0x00**32`). + BHSH(VirtualRegister, VirtualRegister), + + /// Get Fuel block height. + /// + /// | Operation | ```$rA = blockheight();``` | + /// | Syntax | `bhei $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + BHEI(VirtualRegister), + + /// Burn `$rA` coins of the current contract's color. + /// + /// | Operation | ```burn($rA);``` | + /// | Syntax | `burn $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panic + /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, + /// 32]` minus `$rA` underflows + /// - `$fp == 0` (in the script context) + /// + /// For output with contract ID `MEM[$fp, 32]`, decrease balance of color + /// `MEM[$fp, 32]` by `$rA`. + /// + /// This modifies the `balanceRoot` field of the appropriate output. + BURN(VirtualRegister), + + /// Call contract. + /// + /// | Syntax | `call $rA $rB $rC $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rC + 32` overflows + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// - Reading past `MEM[VM_MAX_RAM - 1]` + /// - Any output range does not pass [ownership check](./main.md#ownership) + /// - In an external context, if `$rB > MEM[balanceOfStart(MEM[$rC, 32]), + /// 8]` + /// - In an internal context, if `$rB` is greater than the balance of color + /// `MEM[$rC, 32]` of output with contract ID `MEM[$rA, 32]` + /// + /// Register `$rA` is a memory address from which the following fields are + /// set (word-aligned). + /// + /// `$rD` is the amount of gas to forward. If it is set to an amount greater + /// than the available gas, all available gas is forwarded. + /// + /// For output with contract ID `MEM[$rA, 32]`, increase balance of color + /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease + /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, + /// decrease color `MEM[$rC, 32]` balance of output with contract ID + /// `MEM[$fp, 32]` by `$rB`. + /// + /// A [call frame](./main.md#call-frames) is pushed at `$sp`. In addition to + /// filling in the values of the call frame, the following registers are + /// set: + /// + /// 1. `$fp = $sp` (on top of the previous call frame is the beginning of + /// this call frame) 1. Set `$ssp` and `$sp` to the start of the + /// writable stack area of the call frame. 1. Set `$pc` and `$is` to the + /// starting address of the code. 1. `$bal = $rD` (forward coins) + /// 1. `$cgas = $rD` or all available gas (forward gas) + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + CALL( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + + /// Copy `$rD` bytes of code starting at `$rC` for contract with ID equal to + /// the 32 bytes in memory starting at `$rB` into memory starting at `$rA`. + /// + /// | Operation | ```MEM[$rA, $rD] = code($rB, $rC, $rD);``` + /// | Syntax | `ccp $rA, $rB, $rC, $rD` + /// | Encoding | `0x00 rA rB rC rD` + /// | Notes | If `$rD` is greater than the code size, zero bytes are + /// filled in. + /// + /// #### Panics + /// - `$rA + $rD` overflows + /// - `$rB + 32` overflows + /// - `$rA + $rD > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, $rD]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rD > MEM_MAX_ACCESS_SIZE` + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + CCP( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + + /// Set the 32 bytes in memory starting at `$rA` to the code root for + /// contract with ID equal to the 32 bytes in memory starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = coderoot(MEM[$rB, 32]);``` + /// | Syntax | `croo $rA, $rB` + /// | Encoding | `0x00 rA rB - -` + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + /// + /// Code root compuration is defined + /// [here](../protocol/identifiers.md#contract-id). + CROO(VirtualRegister, VirtualRegister), + + /// Set `$rA` to the size of the code for contract with ID equal to the 32 + /// bytes in memory starting at `$rB`. + /// + /// | Operation | ```$rA = codesize(MEM[$rB, 32]);``` + /// | Syntax | `csiz $rA, $rB` + /// | Encoding | `0x00 rA rB - -` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + 32` overflows + /// - `$rB + 32 > VM_MAX_RAM` + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + CSIZ(VirtualRegister, VirtualRegister), + + /// Get block proposer address. + /// + /// | Operation | ```MEM[$rA, 32] = coinbase();``` | + /// | Syntax | `cb $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + CB(VirtualRegister), + + /// Copy `$rC` bytes of code starting at `$rB` for contract with ID equal to + /// the 32 bytes in memory starting at `$rA` into memory starting at `$ssp`. + /// + /// | Operation | ```MEM[$ssp, $rC] = code($rA, $rB, $rC);``` + /// | Syntax | `ldc $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// | Notes | If `$rC` is greater than the code size, zero bytes are + /// filled in. + /// + /// #### Panics + /// - `$ssp + $rC` overflows + /// - `$rA + 32` overflows + /// - `$ssp + $rC > VM_MAX_RAM` + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$ssp != $sp` + /// - `$ssp + $rC > $hp` + /// - `$rC > CONTRACT_MAX_SIZE` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// + /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word + /// alignment. + /// + /// This opcode can be used to concatenate the code of multiple contracts + /// together. It can only be used when the stack area of the call frame is + /// unused (i.e. prior to being used). + LDC(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Log an event. This is a no-op. + /// + /// | Operation | ```log($rA, $rB, $rC, $rD);``` | + /// | Syntax | `log $rA, $rB, $rC, $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + LOG( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + + /// Mint `$rA` coins of the current contract's color. + /// + /// | Operation | ```mint($rA);``` | + /// | Syntax | `mint $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, + /// 32]` plus `$rA` overflows + /// - `$fp == 0` (in the script context) + /// + /// For output with contract ID `MEM[$fp, 32]`, increase balance of color + /// `MEM[$fp, 32]` by `$rA`. + /// + /// This modifies the `balanceRoot` field of the appropriate output. + MINT(VirtualRegister), + + /// Halt execution, reverting state changes and returning value in `$rA`. + /// + /// | Operation | ```revert($rA);``` + /// | Syntax | `rvrt $rA` + /// | Encoding | `0x00 rA - - -` + /// + /// After a revert: + /// + /// 1. All [OutputContract](../protocol/tx_format.md#outputcontract) outputs + /// will have the same `amount` and `stateRoot` as on initialization. 1. + /// All [OutputVariable](../protocol/tx_format.md outputs#outputvariable) + /// outputs will have `to` and `amount` of zero. + /// 1. All [OutputContractConditional](../protocol/tx_format.md# + /// outputcontractconditional) outputs will have `contractID`, `amount`, and + /// `stateRoot` of zero. + RVRT(VirtualRegister), + + /// Copy `$rC` bytes of code starting at `$rB` for contract with static + /// index `$rA` into memory starting at `$ssp`. + /// + /// | Operation | ```MEM[$ssp, $rC] = scode($rA, $rB, $rC);``` + /// | Syntax | `sloadcode $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// | Notes | If `$rC` is greater than the code size, zero bytes + /// are filled in. | + /// + /// #### Panics + /// - `$ssp + $rC` overflows + /// - `$ssp + $rC > VM_MAX_RAM` + /// - `$rA >= MAX_STATIC_CONTRACTS` + /// - `$rA` is greater than or equal to `staticContractsCount` for the + /// contract with ID `MEM[$fp, 32]` + /// - `$ssp != $sp` + /// - `$ssp + $rC > $hp` + /// - `$rC > CONTRACT_MAX_SIZE` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - `$fp == 0` (in the script context) + /// + /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word + /// alignment. + /// + /// This opcode can be used to concatenate the code of multiple contracts + /// together. It can only be used when the stack area of the call frame is + /// unused (i.e. prior to being used). + SLDC(VirtualRegister, VirtualRegister, VirtualRegister), + + /// A word is read from the current contract's state. + /// + /// | Operation | ```$rA = STATE[MEM[$rB, 32]][0, 8];``` | + /// | Syntax | `srw $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | Returns zero if the state element does not exist. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + 32` overflows + /// - `$rB + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SRW(VirtualRegister, VirtualRegister), + + /// 32 bytes is read from the current contract's state. + /// + /// | Operation | ```MEM[$rA, 32] = STATE[MEM[$rB, 32]];``` | + /// | Syntax | `srwx $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | Returns zero if the state element does not exist. | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$fp == 0` (in the script context) + SRWQ(VirtualRegister, VirtualRegister), + + /// A word is written to the current contract's state. + /// + /// | Operation | ```STATE[MEM[$rA, 32]][0, 8] = $rB;``` | + /// | Syntax | `sww $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SWW(VirtualRegister, VirtualRegister), + + /// 32 bytes is written to the current contract's state. + /// + /// | Operation | ```STATE[MEM[$rA, 32]] = MEM[$rB, 32];``` | + /// | Syntax | `swwx $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SWWQ(VirtualRegister, VirtualRegister), + + /// Transfer `$rB` coins with color at `$rC` to contract with ID at `$rA`. + /// + /// | Operation | ```transfer(MEM[$rA, 32], $rB, MEM[$rC, 32]);``` + /// | Syntax | `tr $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns + /// the memory address of `color` balance, or `0` if `color` has no balance. + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rC + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rC + 32 > VM_MAX_RAM` + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// - In an external context, if `$rB > MEM[balanceOf(MEM[$rC, 32]), 8]` + /// - In an internal context, if `$rB` is greater than the balance of color + /// `MEM[$rC, 32]` of output with contract ID `MEM[$fp, 32]` + /// - `$rB == 0` + /// + /// For output with contract ID `MEM[$rA, 32]`, increase balance of color + /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease + /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, + /// decrease color `MEM[$rC, 32]` balance of output with contract ID + /// `MEM[$fp, 32]` by `$rB`. + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + TR(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Transfer `$rC` coins with color at `$rD` to address at `$rA`, with + /// output `$rB`. | Operation | ```transferout(MEM[$rA, 32], $rB, $rC, + /// MEM[$rD, 32]);``` | Syntax | `tro $rA, $rB, $rC, $rD` + /// | Encoding | `0x00 rA rB rC rD` + /// + /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns + /// the memory address of `color` balance, or `0` if `color` has no balance. + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rD + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rD + 32 > VM_MAX_RAM` + /// - `$rB > tx.outputsCount` + /// - In an external context, if `$rC > MEM[balanceOf(MEM[$rD, 32]), 8]` + /// - In an internal context, if `$rC` is greater than the balance of color + /// `MEM[$rD, 32]` of output with contract ID `MEM[$fp, 32]` + /// - `$rC == 0` + /// - `tx.outputs[$rB].type != OutputType.Variable` + /// - `tx.outputs[$rB].amount != 0` + /// + /// In an external context, decrease `MEM[balanceOfStart(MEM[$rD, 32]), 8]` + /// by `$rC`. In an internal context, decrease color `MEM[$rD, 32]` balance + /// of output with contract ID `MEM[$fp, 32]` by `$rC`. Then set: + /// + /// - `tx.outputs[$rB].to = MEM[$rA, 32]` + /// - `tx.outputs[$rB].amount = $rC` + /// - `tx.outputs[$rB].color = MEM[$rD, 32]` + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + TRO( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), + + /// The 64-byte public key (x, y) recovered from 64-byte + /// signature starting at `$rB` on 32-byte message hash starting at `$rC`. | + /// + /// | Operation | ```MEM[$rA, 64] = ecrecover(MEM[$rB, 64], MEM[$rC, + /// 32]);``` | Syntax | `ecr $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// #### Panics + /// - `$rA + 64` overflows + /// - `$rB + 64` overflows + /// - `$rC + 32` overflows + /// - `$rA + 64 > VM_MAX_RAM` + /// - `$rB + 64 > VM_MAX_RAM` + /// - `$rC + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 64]` does not pass [ownership + /// check](./main.md#ownership) + /// + /// To get the address, hash the public key with + /// [SHA-2-256](#sha256-sha-2-256). + ECR(VirtualRegister, VirtualRegister, VirtualRegister), + + /// The keccak-256 hash of `$rC` bytes starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = keccak256(MEM[$rB, $rC]);``` + /// | Syntax | `k256 $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + $rC` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rC > MEM_MAX_ACCESS_SIZE` + K256(VirtualRegister, VirtualRegister, VirtualRegister), + + /// The SHA-2-256 hash of `$rC` bytes starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = sha256(MEM[$rB, $rC]);``` | + /// | Syntax | `s256 $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + $rC` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rC > MEM_MAX_ACCESS_SIZE` + S256(VirtualRegister, VirtualRegister, VirtualRegister), + + /// Performs no operation. + /// + /// | Operation | | + /// | Syntax | `noop` | + /// | Encoding | `0x00 - - - -` | + /// + /// `$of` and `$err` are cleared. + NOOP, + + /// Set `$flag` to `$rA`. + /// + /// | Operation | ```$flag = $rA;``` | + /// | Syntax | `flag $rA` | + /// | Encoding | `0x00 rA - - -` | + FLAG(VirtualRegister), + + /// Undefined opcode, potentially from inconsistent serialization + Undefined, +} + +impl VirtualOp { + pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[VirtualRegister], immediate: Option) { + todo!() + } +} diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index 2ebe3e21cea..5d8e22f264b 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -451,6 +451,14 @@ pub enum CompileError<'sc> { UnrecognizedOp { op_name: &'sc str, span: Span<'sc> }, #[error("Unable to infer concrete type for partial type \"{ty}\". Type must be known at this 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> }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -588,6 +596,10 @@ 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, } } From 8d6f0884bca516065e7329174050fa98a9144130 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 19 May 2021 18:39:39 -0700 Subject: [PATCH 08/62] daily checkpoint --- .../expression/enum_instantiation.rs | 15 ++++--- .../src/asm_generation/expression/structs.rs | 45 +++++++++---------- core_lang/src/asm_lang/mod.rs | 37 +++++++-------- core_lang/src/asm_lang/virtual_ops.rs | 39 ++++++++++++++-- 4 files changed, 80 insertions(+), 56 deletions(-) diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index 3e3ccfe573f..d6d6d0c4c7f 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.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, VirtualImmediate12, VirtualOp, VirtualRegister}, + Op, +}; use crate::error::*; use crate::semantic_analysis::ast_node::TypedEnumDeclaration; use crate::semantic_analysis::TypedExpression; @@ -13,7 +16,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'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>> { @@ -36,7 +39,7 @@ 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(); @@ -54,7 +57,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( asm_buf.push(Op::unowned_stack_allocate_memory(size_of_enum)); // initialize all the memory to 0 asm_buf.push(Op::new( - Opcode::MCLI(pointer_register.clone(), size_of_enum), + VirtualOp::MCLI(pointer_register.clone(), size_of_enum), decl.clone().span, )); // write the tag @@ -62,7 +65,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, )); @@ -87,7 +90,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/structs.rs b/core_lang/src/asm_generation/expression/structs.rs index 1e941ca5376..b8490870802 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 = 0b111_111_111_111; + 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_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 83cebeb1841..3395513fcfb 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -10,12 +10,13 @@ use either::Either; use pest::Span; use std::str::FromStr; use std::{collections::HashSet, fmt}; -mod virtual_ops; use virtual_ops::{ - VirtualImmediate06, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, - VirtualRegister, + Label, VirtualImmediate06, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, + VirtualOp, VirtualRegister, }; +pub(crate) mod virtual_ops; + /// The column where the ; for comments starts const COMMENT_START_COLUMN: usize = 40; @@ -53,7 +54,7 @@ impl<'sc> Op<'sc> { pub(crate) fn write_register_to_memory_comment( destination_address: VirtualRegister, value_to_write: VirtualRegister, - offset: VirtualImmediateValue, + offset: VirtualImmediate12, span: Span<'sc>, comment: impl Into, ) -> Self { @@ -64,7 +65,9 @@ impl<'sc> Op<'sc> { } } /// 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(VirtualOp::CFEI(size_to_allocate_in_words)), comment: String::new(), @@ -159,7 +162,7 @@ impl<'sc> Op<'sc> { owning_span: Span<'sc>, ) -> Self { Op { - opcode: Either::Left(VirtualOp::RMove(r1, r2)), + opcode: Either::Left(VirtualOp::MOVE(r1, r2)), comment: String::new(), owning_span: Some(owning_span), } @@ -168,7 +171,7 @@ impl<'sc> Op<'sc> { /// Moves the register in the second argument into the register in the first argument 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, } @@ -180,7 +183,7 @@ impl<'sc> Op<'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), } @@ -193,7 +196,7 @@ impl<'sc> Op<'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: None, } @@ -230,7 +233,7 @@ impl<'sc> Op<'sc> { label: Label, ) -> Self { Op { - opcode: Either::Right(OrganizationalOp::JumpIfNotEq(reg0, reg1, label)), + opcode: Either::Left(VirtualOp::JNEI(reg0, reg1, label)), comment: String::new(), owning_span: None, } @@ -238,16 +241,9 @@ impl<'sc> Op<'sc> { pub(crate) fn parse_opcode( name: &Ident<'sc>, - args: &[&VirtualRegister], - immediate: Option, + args: &[&Ident<'sc>], ) -> CompileResult<'sc, VirtualOp> { - VirtualOp::parse(name, args, immediate) - } -} - -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, ".{}", self.0) + todo!() } } @@ -359,9 +355,6 @@ 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)] diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 494553f4f37..67a0ceeca33 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -1,5 +1,8 @@ //! 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 crate::{error::*, Ident}; use pest::Span; @@ -100,8 +103,9 @@ impl VirtualImmediate06 { pub struct VirtualImmediate12 { value: u16, } + impl VirtualImmediate12 { - fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + 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, @@ -113,6 +117,16 @@ impl VirtualImmediate12 { }) } } + /// 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())), + } + } } /// 18-bits immediate value type @@ -153,6 +167,16 @@ impl VirtualImmediate24 { }) } } + /// 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())), + } + } } /// This enum is unfortunately a redundancy of the [fuel_asm::Opcode] enum. This variant, however, @@ -636,7 +660,7 @@ pub(crate) enum VirtualOp { /// /// #### Panics /// - `$is + imm * 4 > VM_MAX_RAM - 1` - JNEI(VirtualRegister, VirtualRegister, VirtualImmediate12), + JNEI(VirtualRegister, VirtualRegister, Label), /// Returns from [context](./main.md#contexts) with value `$rA`. /// @@ -1270,7 +1294,16 @@ pub(crate) enum VirtualOp { } impl VirtualOp { - pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[VirtualRegister], immediate: Option) { + pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[&str]) -> CompileResult<'sc, Self> { todo!() } } + +#[derive(Clone, Eq, PartialEq)] +/// 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) + } +} From f44b7fd2f95fb8a5cc22c7d4992c972cdded9c71 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 May 2021 11:28:39 -0700 Subject: [PATCH 09/62] add AllocatedOp abstraction --- .../src/asm_generation/compiler_constants.rs | 2 + .../expression/enum_instantiation.rs | 43 +- .../src/asm_generation/expression/if_exp.rs | 9 +- .../src/asm_generation/expression/mod.rs | 23 +- core_lang/src/asm_generation/mod.rs | 122 +- .../src/asm_generation/register_sequencer.rs | 6 +- core_lang/src/asm_generation/while_loop.rs | 4 +- core_lang/src/asm_lang/allocated_ops.rs | 1148 +++++++++++++++++ core_lang/src/asm_lang/mod.rs | 4 +- core_lang/src/asm_lang/virtual_ops.rs | 14 + core_lang/src/parse_tree/expression/asm.rs | 17 +- 11 files changed, 1287 insertions(+), 105 deletions(-) create mode 100644 core_lang/src/asm_lang/allocated_ops.rs diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index ebf5cface64..de79d83332a 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1 +1,3 @@ pub(crate) const NUM_FREE_REGISTERS: u8 = 48; +pub(crate) const TWENTY_FOUR_BITS: u64 = 0b11111_11111_11111_11111_1111; +pub(crate) const EIGHTEEN_BITS: u64 = 0b11111_11111_11111_111; diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index d6d6d0c4c7f..57970a0495e 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -1,6 +1,11 @@ -use crate::asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer}; +use crate::asm_generation::{ + compiler_constants::*, convert_expression_to_asm, AsmNamespace, RegisterSequencer, +}; use crate::asm_lang::{ - virtual_ops::{ConstantRegister, VirtualImmediate12, VirtualOp, VirtualRegister}, + virtual_ops::{ + ConstantRegister, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, + VirtualRegister, + }, Op, }; use crate::error::*; @@ -43,21 +48,31 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( "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( - VirtualOp::MCLI(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 diff --git a/core_lang/src/asm_generation/expression/if_exp.rs b/core_lang/src/asm_generation/expression/if_exp.rs index e7d5ea21a27..4a0b33df011 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>> { @@ -48,7 +51,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 ecca4fdb9cd..2adbfed6109 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, initializer } in registers { let register = register_sequencer.next(); @@ -134,11 +137,11 @@ 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), continue, warnings, errors @@ -244,7 +247,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![]; @@ -274,11 +277,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> { @@ -298,7 +301,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![]; @@ -307,7 +310,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/mod.rs b/core_lang/src/asm_generation/mod.rs index ccfa6e3fa19..7740c3ccef0 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -1,7 +1,11 @@ use std::{collections::HashMap, fmt}; use crate::{ - asm_lang::{Label, Op, OrganizationalOp, RegisterId}, + asm_lang::{ + allocated_ops::AllocatedRegister, + virtual_ops::{Label, VirtualOp, VirtualRegister}, + Op, OrganizationalOp, + }, error::*, parse_tree::Literal, semantic_analysis::{TypedAstNode, TypedAstNodeContent, TypedParseTree}, @@ -130,83 +134,77 @@ impl<'sc> AbstractInstructionSet<'sc> { // registers when they are not read anymore // construct a mapping from every op to the registers it uses - let mut op_register_mapping = self - .ops - .iter_mut() - .map(|op| { - ( - op.clone(), - match op.opcode { - Either::Left(mut opc) => opc.registers(), - Either::Right(mut orgop) => todo!(), - }, - ) - }) - .collect::>(); - - // get registers from the pool. - // if the registers are never read again, return them to the pool. - let mut pool = RegisterPool::init(); - for (op, registers) in op_register_mapping { - let new_registers: Option> = - registers.iter().map(|reg| pool.get_register()).collect(); - let new_registers = match new_registers { - Some(a) => registers.into_iter().zip(a.into_iter()).collect::>(), - _ => todo!("Return out of registers error"), - }; - // if the virtual register is never read again, then we can - // return this virtual register back into the pool - new_registers.iter().for_each(|(virtual_reg, real_reg)| { - todo!() - /* - if virtual_register_is_never_accessed_again( - &virtual_reg, - op_register_mapping.as_slice(), - ) { - pool.return_register_to_pool(*real_reg); + /* + let mut op_register_mapping = self + .ops + .iter_mut() + .map(|op| { + ( + op.clone(), + match op.opcode { + Either::Left(mut opc) => opc.registers(), + Either::Right(mut orgop) => todo!(), + }, + ) + }) + .collect::>(); + + // get registers from the pool. + let mut pool = RegisterPool::init(); + for (op, registers) in op_register_mapping { + let new_registers: Option> = + registers.iter().map(|reg| pool.get_register()).collect(); + let new_registers = match new_registers { + Some(a) => registers.into_iter().zip(a.into_iter()).collect::>(), + _ => todo!("Return out of registers error"), + }; + // if the virtual register is never read again, then we can + // return this virtual register back into the pool + new_registers.iter().for_each(|(virtual_reg, real_reg)| { + if virtual_register_is_never_accessed_again( + &virtual_reg, + op_register_mapping.as_slice(), + ) { + pool.return_register_to_pool(*real_reg); + } + }); + + // TODO: + // properly parse reserved registers and handle them in asm expressions + // do not pull from the pool for reserved registers + // Rename VirtualRegister to VirtualRegister } - */ - }); - - // TODO: - // properly parse reserved registers and handle them in asm expressions - // do not pull from the pool for reserved registers - // Rename RegisterId to VirtualRegister - } + */ todo!() } } fn virtual_register_is_never_accessed_again( - reg: &RegisterId, - ops: &[(Op, std::collections::HashSet<&mut RegisterId>)], + reg: &VirtualRegister, + ops: &[(Op, std::collections::HashSet<&mut VirtualRegister>)], ) -> bool { todo!() } -struct RegisterPool { - available_registers: Vec, -} -enum Register { - Free(u8), - Reserved(u8), +struct RegisterPool { + available_registers: Vec, } impl RegisterPool { fn init() -> Self { - let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0) - .map(|x| Register::Free(x)) + let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0) + .map(|x| AllocatedRegister::Allocated(x)) .collect(); Self { available_registers: register_pool, } } - fn get_register(&mut self) -> Option { + fn get_register(&mut self) -> Option { self.available_registers.pop() } - fn return_register_to_pool(&mut self, item_to_return: Register) { + fn return_register_to_pool(&mut self, item_to_return: AllocatedRegister) { self.available_registers.push(item_to_return); } } @@ -215,7 +213,7 @@ impl RegisterPool { fn label_is_used<'sc>(buf: &[Op<'sc>], label: &Label) -> bool { buf.iter().any(|Op { ref opcode, .. }| match opcode { Either::Right(OrganizationalOp::Jump(ref l)) if label == l => true, - Either::Right(OrganizationalOp::JumpIfNotEq(_reg0, _reg1, ref l)) if label == l => true, + Either::Left(VirtualOp::JNEI(_reg0, _reg1, ref l)) if label == l => true, _ => false, }) } @@ -363,7 +361,7 @@ impl fmt::Display for InstructionSet<'_> { #[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. @@ -377,7 +375,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 { @@ -397,7 +399,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(vec![], vec![CompileError::Internal ("Unknown variable in assembly generation. This should have been an error during type checking.", var_name.span.clone() )]) @@ -598,7 +600,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..67d078c7da6 --- /dev/null +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -0,0 +1,1148 @@ +//! 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 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 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 AllocatedOp { + /// Adds two registers. + /// + /// | Operation | ```$rA = $rB + $rC;``` | + /// | Syntax | `add $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// `$err` is cleared. + ADD(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Adds a register and an immediate value. + /// + /// | Operation | ```$rA = $rB + imm;``` | + /// | Syntax | `addi $rA, $rB, immediate` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// `$err` is cleared. + ADDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Bitwise ANDs two registers. + /// + /// | Operation | ```$rA = $rB & $rC;``` | + /// | Syntax | `and $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `$of` and `$err` are cleared. + AND(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Bitwise ANDs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB & imm;``` | + /// | Syntax | `andi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. + /// `$of` and `$err` are cleared. + ANDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Divides two registers. + /// + /// | Operation | ```$rA = $rB // $rC;``` | + /// | Syntax | `div $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// If `$rC == 0`, `$rA` is cleared and `$err` is set to `true`. + /// Otherwise, `$err` is cleared. + /// `$of` is cleared. + DIV(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Divides a register and an immediate value. + /// + /// | Operation | ```$rA = $rB // imm;``` | + /// | Syntax | `divi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a reserved register. + /// + /// #### Execution + /// If `imm == 0`, `$rA` is cleared and `$err` is set to `true`. + /// Otherwise, `$err` is cleared. + /// `$of` is cleared. + DIVI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Compares two registers for equality. + /// + /// | Operation | ```$rA = $rB == $rC;``` | + /// | Syntax | `eq $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a reserved register, + /// + /// #### Execution + /// `$of` and `$err` are cleared. + EQ(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Raises one register to the power of another. + /// + /// | Operation | ```$rA = $rB ** $rC;``` | + /// | Syntax | `exp $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise + /// `$of` is cleared. + /// `$err` is cleared. + EXP(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Raises one register to the power of an immediate value. + /// + /// | Operation | ```$rA = $rB ** imm;``` | + /// | Syntax | `expi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise + /// `$of` is cleared. + /// `$err` is cleared. + EXPI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Compares two registers for greater-than. + /// + /// | Operation | ```$rA = $rB > $rC;``` | + /// | Syntax | `gt $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + GT(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// The (integer) logarithm base `$rC` of `$rB`. + /// + /// | Operation | ```$rA = math.floor(math.log($rB, $rC));``` | + /// | Syntax | `mlog $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rB == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// If `$rC <= 1`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MLOG(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// The (integer) `$rC`th root of `$rB`. + /// + /// | Operation | ```$rA = math.floor(math.root($rB, $rC));``` | + /// | Syntax | `mroo $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MROO(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Modulo remainder of two registers. + /// + /// | Operation | ```$rA = $rB % $rC;``` | + /// | Syntax | `mod $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MOD(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Modulo remainder of a register and an immediate value. + /// + /// | Operation | ```$rA = $rB % imm;``` | + /// | Syntax | `modi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// If `imm == 0`, both `$rA` and `$of` are cleared and `$err` is set to + /// `true`. + /// + /// Otherwise, `$of` and `$err` are cleared. + MODI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Copy from one register to another. + /// + /// | Operation | ```$rA = $rB;``` | + /// | Syntax | `move $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + MOVE(AllocatedRegister, AllocatedRegister), + + /// Multiplies two registers. + /// + /// | Operation | ```$rA = $rB * $rC;``` | + /// | Syntax | `mul $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + MUL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Multiplies a register and an immediate value. + /// + /// | Operation | ```$rA = $rB * imm;``` | + /// | Syntax | `mul $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + MULI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Bitwise NOT a register. + /// + /// | Operation | ```$rA = ~$rB;``` | + /// | Syntax | `not $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + NOT(AllocatedRegister, AllocatedRegister), + + /// Bitwise ORs two registers. + /// + /// | Operation | ```$rA = $rB \| $rC;``` | + /// | Syntax | `or $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + OR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Bitwise ORs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB \| imm;``` | + /// | Syntax | `ori $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. + /// + /// `$of` and `$err` are cleared. + ORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Left shifts a register by a register. + /// + /// | Operation | ```$rA = $rB << $rC;``` | + /// | Syntax | `sll $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + SLL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Left shifts a register by an immediate value. + /// + /// | Operation | ```$rA = $rB << imm;``` | + /// | Syntax | `slli $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the overflow of the operation. + /// + /// `$err` is cleared. + SLLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Right shifts a register by a register. + /// + /// | Operation | ```$rA = $rB >> $rC;``` | + /// | Syntax | `srl $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SRL(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Right shifts a register by an immediate value. + /// + /// | Operation | ```$rA = $rB >> imm;``` | + /// | Syntax | `srli $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | Zeroes are shifted in. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SRLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Subtracts two registers. + /// + /// | Operation | ```$rA = $rB - $rC;``` | + /// | Syntax | `sub $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | `$of` is assigned the overflow of the operation. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SUB(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Subtracts a register and an immediate value. + /// + /// | Operation | ```$rA = $rB - imm;``` | + /// | Syntax | `subi $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | `$of` is assigned the overflow of the operation. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` is assigned the underflow of the operation, as though `$of` is the + /// high byte of a 128-bit register. + /// + /// `$err` is cleared. + SUBI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Bitwise XORs two registers. + /// + /// | Operation | ```$rA = $rB ^ $rC;``` | + /// | Syntax | `xor $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + XOR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Bitwise XORs a register and an immediate value. + /// + /// | Operation | ```$rA = $rB ^ imm;``` | + /// | Syntax | `xori $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// | Notes | | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// + /// #### Execution + /// `$of` and `$err` are cleared. + XORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Set `$rA` to `true` if the `$rC <= tx.input[$rB].maturity`. + /// + /// | Operation | ```$rA = checkinputmaturityverify($rB, $rC);``` | + /// | Syntax | `cimv $rA $rB $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rC > tx.input[$rB].maturity` + /// - the input `$rB` is not of type + /// [`InputType.Coin`](../protocol/tx_format.md) + /// - `$rB > tx.inputsCount` + /// + /// #### Execution + /// Otherwise, advance the program counter `$pc` by `4`. + /// + /// See also: [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and [CLTV](#cltv-check-lock-time-verify). + CIMV(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Set `$rA` to `true` if `$rB <= tx.maturity`. + /// + /// | Operation | ```$rA = checktransactionmaturityverify($rB);``` | + /// | Syntax | `ctmv $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB > tx.maturity` + /// + /// #### Execution + /// Otherwise, advance the program counter `$pc` by `4`. + /// + /// See also: [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and [Bitcoin's Time Locks](https://prestwi.ch/bitcoin-time-locks). + CTMV(AllocatedRegister, AllocatedRegister), + + /// Jumps to the code instruction offset by `imm`. + /// + /// | Operation | ```$pc = $is + imm * 4;``` | + /// | Syntax | `ji imm` | + /// | Encoding | `0x00 i i i i` | + /// + /// #### Panics + /// - `$is + imm * 4 > VM_MAX_RAM - 1` + JI(VirtualImmediate24), + + /// Jump to the code instruction offset by `imm` if `$rA` is not equal to + /// `$rB`. + /// + /// | Operation | ```if $rA != $rB:```
```$pc = $is + imm * + /// 4;```
```else:```
```$pc += 4;``` | Syntax | `jnei $rA + /// $rB imm` | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$is + imm * 4 > VM_MAX_RAM - 1` + JNEI(AllocatedRegister, AllocatedRegister, Label), + + /// Returns from [context](./main.md#contexts) with value `$rA`. + /// + /// | Operation | ```return($rA);``` + /// | Syntax | `ret $rA` + /// | Encoding | `0x00 rA - - -` + /// + /// If current context is external, cease VM execution and return `$rA`. + /// + /// Returns from contract call, popping the call frame. Before popping: + /// + /// 1. Return the unused forwarded gas to the caller: + /// - `$cgas = $cgas + $fp->$cgas` (add remaining context gas from + /// previous context to current remaining context gas) + /// + /// Then pop the call frame and restoring registers _except_ `$ggas` and + /// `$cgas`. Afterwards, set the following registers: + /// + /// 1. `$pc = $pc + 4` (advance program counter from where we called) + RET(AllocatedRegister), + + /// Extend the current call frame's stack by an immediate value. + /// + /// | Operation | ```$sp = $sp + imm``` + /// | Syntax | `cfei imm` + /// | Encoding | `0x00 i i i i` + /// | Notes | Does not initialize memory. + /// + /// #### Panics + /// - `$sp + imm` overflows + /// - `$sp + imm > $hp` + CFEI(VirtualImmediate24), + + /// Shrink the current call frame's stack by an immediate value. + /// + /// | Operation | ```$sp = $sp - imm``` + /// | Syntax | `cfsi imm` + /// | Encoding | `0x00 i i i i` + /// | Notes | Does not clear memory. + /// + /// #### Panics + /// - `$sp - imm` underflows + /// - `$sp - imm < $ssp` + CFSI(VirtualImmediate24), + + /// A byte is loaded from the specified address offset by `imm`. + /// + /// | Operation | ```$rA = MEM[$rB + imm, 1];``` + /// | Syntax | `lb $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + imm + 1` overflows + /// - `$rB + imm + 1 > VM_MAX_RAM` + LB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// A word is loaded from the specified address offset by `imm`. + /// | Operation | ```$rA = MEM[$rB + imm, 8];``` + /// | Syntax | `lw $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + imm + 8` overflows + /// - `$rB + imm + 8 > VM_MAX_RAM` + LW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Allocate a number of bytes from the heap. + /// + /// | Operation | ```$hp = $hp - $rA;``` | + /// | Syntax | `aloc $rA` | + /// | Encoding | `0x00 rA - - -` | + /// | Notes | Does not initialize memory. | + /// + /// #### Panics + /// - `$hp - $rA` underflows + /// - `$hp - $rA < $sp` + ALOC(AllocatedRegister), + + /// Clear bytes in memory. + /// + /// | Operation | ```MEM[$rA, $rB] = 0;``` | + /// | Syntax | `mcl $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + $rB` overflows + /// - `$rA + $rB > VM_MAX_RAM` + /// - `$rB > MEM_MAX_ACCESS_SIZE` + /// - The memory range `MEM[$rA, $rB]` does not pass [ownership + /// check](./main.md#ownership) + MCL(AllocatedRegister, AllocatedRegister), + + /// Clear bytes in memory. + /// + /// | Operation | ```MEM[$rA, imm] = 0;``` | + /// | Syntax | `mcli $rA, imm` | + /// | Encoding | `0x00 rA i i i` | + /// + /// #### Panics + /// - `$rA + imm` overflows + /// - `$rA + imm > VM_MAX_RAM` + /// - `imm > MEM_MAX_ACCESS_SIZE` + /// - The memory range `MEM[$rA, imm]` does not pass [ownership + /// check](./main.md#ownership) + MCLI(AllocatedRegister, VirtualImmediate18), + + /// Copy bytes in memory. + /// + /// | Operation | ```MEM[$rA, $rC] = MEM[$rB, $rC];``` | + /// | Syntax | `mcp $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA + $rC` overflows + /// - `$rB + $rC` overflows + /// - `$rA + $rC > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - The memory ranges `MEM[$rA, $rC]` and `MEM[$rB, $rC]` overlap + /// - The memory range `MEM[$rA, $rC]` does not pass [ownership + /// check](./main.md#ownership) + MCP(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Compare bytes in memory. + /// + /// | Operation | ```$rA = MEM[$rB, $rD] == MEM[$rC, $rD];``` | + /// | Syntax | `meq $rA, $rB, $rC, $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + $rD` overflows + /// - `$rC + $rD` overflows + /// - `$rB + $rD > VM_MAX_RAM` + /// - `$rC + $rD > VM_MAX_RAM` + /// - `$rD > MEM_MAX_ACCESS_SIZE` + MEQ( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + + /// The least significant byte of `$rB` is stored at the address `$rA` + /// offset by `imm`. + /// + /// | Operation | ```MEM[$rA + imm, 1] = $rB[7, 1];``` | + /// | Syntax | `sb $rA, $rB, imm` | + /// | Encoding | `0x00 rA rB i i` | + /// + /// #### Panics + /// - `$rA + imm + 1` overflows + /// - `$rA + imm + 1 > VM_MAX_RAM` + /// - The memory range `MEM[$rA + imm, 1]` does not pass [ownership + /// check](./main.md#ownership) + SB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// The value of `$rB` is stored at the address `$rA` offset by `imm`. + /// + /// | Operation | ```MEM[$rA + imm, 8] = $rB;``` + /// | Syntax | `sw $rA, $rB, imm` + /// | Encoding | `0x00 rA rB i i` + /// + /// #### Panics + /// - `$rA + imm + 8` overflows + /// - `$rA + imm + 8 > VM_MAX_RAM` + /// - The memory range `MEM[$rA + imm, 8]` does not pass [ownership + /// check](./main.md#ownership) + SW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + + /// Get block header hash. + /// + /// | Operation | ```MEM[$rA, 32] = blockhash($rB);``` | + /// | Syntax | `bhsh $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// + /// Block header hashes for blocks with height greater than or equal to + /// current block height are zero (`0x00**32`). + BHSH(AllocatedRegister, AllocatedRegister), + + /// Get Fuel block height. + /// + /// | Operation | ```$rA = blockheight();``` | + /// | Syntax | `bhei $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + BHEI(AllocatedRegister), + + /// Burn `$rA` coins of the current contract's color. + /// + /// | Operation | ```burn($rA);``` | + /// | Syntax | `burn $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panic + /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, + /// 32]` minus `$rA` underflows + /// - `$fp == 0` (in the script context) + /// + /// For output with contract ID `MEM[$fp, 32]`, decrease balance of color + /// `MEM[$fp, 32]` by `$rA`. + /// + /// This modifies the `balanceRoot` field of the appropriate output. + BURN(AllocatedRegister), + + /// Call contract. + /// + /// | Syntax | `call $rA $rB $rC $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rC + 32` overflows + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// - Reading past `MEM[VM_MAX_RAM - 1]` + /// - Any output range does not pass [ownership check](./main.md#ownership) + /// - In an external context, if `$rB > MEM[balanceOfStart(MEM[$rC, 32]), + /// 8]` + /// - In an internal context, if `$rB` is greater than the balance of color + /// `MEM[$rC, 32]` of output with contract ID `MEM[$rA, 32]` + /// + /// Register `$rA` is a memory address from which the following fields are + /// set (word-aligned). + /// + /// `$rD` is the amount of gas to forward. If it is set to an amount greater + /// than the available gas, all available gas is forwarded. + /// + /// For output with contract ID `MEM[$rA, 32]`, increase balance of color + /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease + /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, + /// decrease color `MEM[$rC, 32]` balance of output with contract ID + /// `MEM[$fp, 32]` by `$rB`. + /// + /// A [call frame](./main.md#call-frames) is pushed at `$sp`. In addition to + /// filling in the values of the call frame, the following registers are + /// set: + /// + /// 1. `$fp = $sp` (on top of the previous call frame is the beginning of + /// this call frame) 1. Set `$ssp` and `$sp` to the start of the + /// writable stack area of the call frame. 1. Set `$pc` and `$is` to the + /// starting address of the code. 1. `$bal = $rD` (forward coins) + /// 1. `$cgas = $rD` or all available gas (forward gas) + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + CALL( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + + /// Copy `$rD` bytes of code starting at `$rC` for contract with ID equal to + /// the 32 bytes in memory starting at `$rB` into memory starting at `$rA`. + /// + /// | Operation | ```MEM[$rA, $rD] = code($rB, $rC, $rD);``` + /// | Syntax | `ccp $rA, $rB, $rC, $rD` + /// | Encoding | `0x00 rA rB rC rD` + /// | Notes | If `$rD` is greater than the code size, zero bytes are + /// filled in. + /// + /// #### Panics + /// - `$rA + $rD` overflows + /// - `$rB + 32` overflows + /// - `$rA + $rD > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, $rD]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rD > MEM_MAX_ACCESS_SIZE` + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + CCP( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + + /// Set the 32 bytes in memory starting at `$rA` to the code root for + /// contract with ID equal to the 32 bytes in memory starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = coderoot(MEM[$rB, 32]);``` + /// | Syntax | `croo $rA, $rB` + /// | Encoding | `0x00 rA rB - -` + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + /// + /// Code root compuration is defined + /// [here](../protocol/identifiers.md#contract-id). + CROO(AllocatedRegister, AllocatedRegister), + + /// Set `$rA` to the size of the code for contract with ID equal to the 32 + /// bytes in memory starting at `$rB`. + /// + /// | Operation | ```$rA = codesize(MEM[$rB, 32]);``` + /// | Syntax | `csiz $rA, $rB` + /// | Encoding | `0x00 rA rB - -` + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + 32` overflows + /// - `$rB + 32 > VM_MAX_RAM` + /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` + CSIZ(AllocatedRegister, AllocatedRegister), + + /// Get block proposer address. + /// + /// | Operation | ```MEM[$rA, 32] = coinbase();``` | + /// | Syntax | `cb $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + CB(AllocatedRegister), + + /// Copy `$rC` bytes of code starting at `$rB` for contract with ID equal to + /// the 32 bytes in memory starting at `$rA` into memory starting at `$ssp`. + /// + /// | Operation | ```MEM[$ssp, $rC] = code($rA, $rB, $rC);``` + /// | Syntax | `ldc $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// | Notes | If `$rC` is greater than the code size, zero bytes are + /// filled in. + /// + /// #### Panics + /// - `$ssp + $rC` overflows + /// - `$rA + 32` overflows + /// - `$ssp + $rC > VM_MAX_RAM` + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$ssp != $sp` + /// - `$ssp + $rC > $hp` + /// - `$rC > CONTRACT_MAX_SIZE` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// + /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word + /// alignment. + /// + /// This opcode can be used to concatenate the code of multiple contracts + /// together. It can only be used when the stack area of the call frame is + /// unused (i.e. prior to being used). + LDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Log an event. This is a no-op. + /// + /// | Operation | ```log($rA, $rB, $rC, $rD);``` | + /// | Syntax | `log $rA, $rB, $rC, $rD` | + /// | Encoding | `0x00 rA rB rC rD` | + LOG( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + + /// Mint `$rA` coins of the current contract's color. + /// + /// | Operation | ```mint($rA);``` | + /// | Syntax | `mint $rA` | + /// | Encoding | `0x00 rA - - -` | + /// + /// #### Panics + /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, + /// 32]` plus `$rA` overflows + /// - `$fp == 0` (in the script context) + /// + /// For output with contract ID `MEM[$fp, 32]`, increase balance of color + /// `MEM[$fp, 32]` by `$rA`. + /// + /// This modifies the `balanceRoot` field of the appropriate output. + MINT(AllocatedRegister), + + /// Halt execution, reverting state changes and returning value in `$rA`. + /// + /// | Operation | ```revert($rA);``` + /// | Syntax | `rvrt $rA` + /// | Encoding | `0x00 rA - - -` + /// + /// After a revert: + /// + /// 1. All [OutputContract](../protocol/tx_format.md#outputcontract) outputs + /// will have the same `amount` and `stateRoot` as on initialization. 1. + /// All [OutputVariable](../protocol/tx_format.md outputs#outputvariable) + /// outputs will have `to` and `amount` of zero. + /// 1. All [OutputContractConditional](../protocol/tx_format.md# + /// outputcontractconditional) outputs will have `contractID`, `amount`, and + /// `stateRoot` of zero. + RVRT(AllocatedRegister), + + /// Copy `$rC` bytes of code starting at `$rB` for contract with static + /// index `$rA` into memory starting at `$ssp`. + /// + /// | Operation | ```MEM[$ssp, $rC] = scode($rA, $rB, $rC);``` + /// | Syntax | `sloadcode $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// | Notes | If `$rC` is greater than the code size, zero bytes + /// are filled in. | + /// + /// #### Panics + /// - `$ssp + $rC` overflows + /// - `$ssp + $rC > VM_MAX_RAM` + /// - `$rA >= MAX_STATIC_CONTRACTS` + /// - `$rA` is greater than or equal to `staticContractsCount` for the + /// contract with ID `MEM[$fp, 32]` + /// - `$ssp != $sp` + /// - `$ssp + $rC > $hp` + /// - `$rC > CONTRACT_MAX_SIZE` + /// - `$rC > MEM_MAX_ACCESS_SIZE` + /// - `$fp == 0` (in the script context) + /// + /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word + /// alignment. + /// + /// This opcode can be used to concatenate the code of multiple contracts + /// together. It can only be used when the stack area of the call frame is + /// unused (i.e. prior to being used). + SLDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// A word is read from the current contract's state. + /// + /// | Operation | ```$rA = STATE[MEM[$rB, 32]][0, 8];``` | + /// | Syntax | `srw $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | Returns zero if the state element does not exist. | + /// + /// #### Panics + /// - `$rA` is a [reserved register](./main.md#semantics) + /// - `$rB + 32` overflows + /// - `$rB + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SRW(AllocatedRegister, AllocatedRegister), + + /// 32 bytes is read from the current contract's state. + /// + /// | Operation | ```MEM[$rA, 32] = STATE[MEM[$rB, 32]];``` | + /// | Syntax | `srwx $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// | Notes | Returns zero if the state element does not exist. | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$fp == 0` (in the script context) + SRWQ(AllocatedRegister, AllocatedRegister), + + /// A word is written to the current contract's state. + /// + /// | Operation | ```STATE[MEM[$rA, 32]][0, 8] = $rB;``` | + /// | Syntax | `sww $rA $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SWW(AllocatedRegister, AllocatedRegister), + + /// 32 bytes is written to the current contract's state. + /// + /// | Operation | ```STATE[MEM[$rA, 32]] = MEM[$rB, 32];``` | + /// | Syntax | `swwx $rA, $rB` | + /// | Encoding | `0x00 rA rB - -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + 32 > VM_MAX_RAM` + /// - `$fp == 0` (in the script context) + SWWQ(AllocatedRegister, AllocatedRegister), + + /// Transfer `$rB` coins with color at `$rC` to contract with ID at `$rA`. + /// + /// | Operation | ```transfer(MEM[$rA, 32], $rB, MEM[$rC, 32]);``` + /// | Syntax | `tr $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns + /// the memory address of `color` balance, or `0` if `color` has no balance. + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rC + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rC + 32 > VM_MAX_RAM` + /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` + /// - In an external context, if `$rB > MEM[balanceOf(MEM[$rC, 32]), 8]` + /// - In an internal context, if `$rB` is greater than the balance of color + /// `MEM[$rC, 32]` of output with contract ID `MEM[$fp, 32]` + /// - `$rB == 0` + /// + /// For output with contract ID `MEM[$rA, 32]`, increase balance of color + /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease + /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, + /// decrease color `MEM[$rC, 32]` balance of output with contract ID + /// `MEM[$fp, 32]` by `$rB`. + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + TR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Transfer `$rC` coins with color at `$rD` to address at `$rA`, with + /// output `$rB`. | Operation | ```transferout(MEM[$rA, 32], $rB, $rC, + /// MEM[$rD, 32]);``` | Syntax | `tro $rA, $rB, $rC, $rD` + /// | Encoding | `0x00 rA rB rC rD` + /// + /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns + /// the memory address of `color` balance, or `0` if `color` has no balance. + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rD + 32` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rD + 32 > VM_MAX_RAM` + /// - `$rB > tx.outputsCount` + /// - In an external context, if `$rC > MEM[balanceOf(MEM[$rD, 32]), 8]` + /// - In an internal context, if `$rC` is greater than the balance of color + /// `MEM[$rD, 32]` of output with contract ID `MEM[$fp, 32]` + /// - `$rC == 0` + /// - `tx.outputs[$rB].type != OutputType.Variable` + /// - `tx.outputs[$rB].amount != 0` + /// + /// In an external context, decrease `MEM[balanceOfStart(MEM[$rD, 32]), 8]` + /// by `$rC`. In an internal context, decrease color `MEM[$rD, 32]` balance + /// of output with contract ID `MEM[$fp, 32]` by `$rC`. Then set: + /// + /// - `tx.outputs[$rB].to = MEM[$rA, 32]` + /// - `tx.outputs[$rB].amount = $rC` + /// - `tx.outputs[$rB].color = MEM[$rD, 32]` + /// + /// This modifies the `balanceRoot` field of the appropriate output(s). + TRO( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), + + /// The 64-byte public key (x, y) recovered from 64-byte + /// signature starting at `$rB` on 32-byte message hash starting at `$rC`. | + /// + /// | Operation | ```MEM[$rA, 64] = ecrecover(MEM[$rB, 64], MEM[$rC, + /// 32]);``` | Syntax | `ecr $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// #### Panics + /// - `$rA + 64` overflows + /// - `$rB + 64` overflows + /// - `$rC + 32` overflows + /// - `$rA + 64 > VM_MAX_RAM` + /// - `$rB + 64 > VM_MAX_RAM` + /// - `$rC + 32 > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 64]` does not pass [ownership + /// check](./main.md#ownership) + /// + /// To get the address, hash the public key with + /// [SHA-2-256](#sha256-sha-2-256). + ECR(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// The keccak-256 hash of `$rC` bytes starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = keccak256(MEM[$rB, $rC]);``` + /// | Syntax | `k256 $rA, $rB, $rC` + /// | Encoding | `0x00 rA rB rC -` + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + $rC` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rC > MEM_MAX_ACCESS_SIZE` + K256(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// The SHA-2-256 hash of `$rC` bytes starting at `$rB`. + /// + /// | Operation | ```MEM[$rA, 32] = sha256(MEM[$rB, $rC]);``` | + /// | Syntax | `s256 $rA, $rB, $rC` | + /// | Encoding | `0x00 rA rB rC -` | + /// + /// #### Panics + /// - `$rA + 32` overflows + /// - `$rB + $rC` overflows + /// - `$rA + 32 > VM_MAX_RAM` + /// - `$rB + $rC > VM_MAX_RAM` + /// - The memory range `MEM[$rA, 32]` does not pass [ownership + /// check](./main.md#ownership) + /// - `$rC > MEM_MAX_ACCESS_SIZE` + S256(AllocatedRegister, AllocatedRegister, AllocatedRegister), + + /// Performs no operation. + /// + /// | Operation | | + /// | Syntax | `noop` | + /// | Encoding | `0x00 - - - -` | + /// + /// `$of` and `$err` are cleared. + NOOP, + + /// Set `$flag` to `$rA`. + /// + /// | Operation | ```$flag = $rA;``` | + /// | Syntax | `flag $rA` | + /// | Encoding | `0x00 rA - - -` | + FLAG(AllocatedRegister), + + /// Undefined opcode, potentially from inconsistent serialization + Undefined, +} diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 3395513fcfb..4490a6be263 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -15,6 +15,7 @@ use virtual_ops::{ VirtualOp, VirtualRegister, }; +pub(crate) mod allocated_ops; pub(crate) mod virtual_ops; /// The column where the ; for comments starts @@ -241,7 +242,8 @@ impl<'sc> Op<'sc> { pub(crate) fn parse_opcode( name: &Ident<'sc>, - args: &[&Ident<'sc>], + args: &[&VirtualRegister], + immediate: &Option>, ) -> CompileResult<'sc, VirtualOp> { todo!() } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 67a0ceeca33..31f9d032494 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -147,6 +147,16 @@ impl VirtualImmediate18 { }) } } + /// 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())), + } + } } /// 24-bits immediate value type @@ -1294,9 +1304,13 @@ pub(crate) enum VirtualOp { } impl VirtualOp { + /// Given an op name and a list of arguments, parse them into a [VirtualOp] pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[&str]) -> CompileResult<'sc, Self> { todo!() } + pub(crate) fn registers(&self) -> Vec { + todo!() + } } #[derive(Clone, Eq, PartialEq)] diff --git a/core_lang/src/parse_tree/expression/asm.rs b/core_lang/src/parse_tree/expression/asm.rs index 18bfb0b08f4..11c1abf4a80 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!(), } From 61705b9cf716e1d775e6a081826d93405bb7ea49 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 May 2021 12:46:28 -0700 Subject: [PATCH 10/62] template for parsing ops --- core_lang/src/asm_lang/mod.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 4490a6be263..614445395f4 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -8,7 +8,6 @@ use crate::{asm_generation::DataId, error::*, parse_tree::AsmRegister, Ident}; use either::Either; use pest::Span; -use std::str::FromStr; use std::{collections::HashSet, fmt}; use virtual_ops::{ Label, VirtualImmediate06, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, @@ -245,7 +244,25 @@ impl<'sc> Op<'sc> { args: &[&VirtualRegister], immediate: &Option>, ) -> CompileResult<'sc, VirtualOp> { - todo!() + match name.primary_name.to_lowercase().as_str() { + "add" => { + let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { + (Some(reg), Some(reg2), Some(reg3)) => (*reg, *reg2, *reg3), + _ => todo!("Not enough registers error"), + }; + match immediate { + None => (), + Some(_) => todo!("Err unnecessary immediate"), + }; + + ok( + VirtualOp::ADD(reg.clone(), reg2.clone(), reg3.clone()), + vec![], + vec![], + ) + } + _ => todo!(), + } } } From 8d21ff507a874a7de6e081c23e9fe34cae7c8806 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 May 2021 13:21:17 -0700 Subject: [PATCH 11/62] allocation algorithm progress --- core_lang/src/asm_generation/mod.rs | 78 ++++++++++----------------- core_lang/src/asm_lang/virtual_ops.rs | 66 ++++++++++++++++++++++- 2 files changed, 92 insertions(+), 52 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 7740c3ccef0..51edad86698 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -128,65 +128,41 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } - fn allocate_registers(mut self) -> InstructionSet<'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 mut op_register_mapping = self - .ops - .iter_mut() - .map(|op| { - ( - op.clone(), - match op.opcode { - Either::Left(mut opc) => opc.registers(), - Either::Right(mut orgop) => todo!(), - }, - ) - }) - .collect::>(); - - // get registers from the pool. - let mut pool = RegisterPool::init(); - for (op, registers) in op_register_mapping { - let new_registers: Option> = - registers.iter().map(|reg| pool.get_register()).collect(); - let new_registers = match new_registers { - Some(a) => registers.into_iter().zip(a.into_iter()).collect::>(), - _ => todo!("Return out of registers error"), - }; - // if the virtual register is never read again, then we can - // return this virtual register back into the pool - new_registers.iter().for_each(|(virtual_reg, real_reg)| { - if virtual_register_is_never_accessed_again( - &virtual_reg, - op_register_mapping.as_slice(), - ) { - pool.return_register_to_pool(*real_reg); + let mut op_register_mapping = self + .ops + .into_iter() + .map(|op| { + ( + op.clone(), + match op.opcode { + Either::Left(opc) => { + opc.registers().into_iter().map(|x| x.clone()).collect() } - }); - - // TODO: - // properly parse reserved registers and handle them in asm expressions - // do not pull from the pool for reserved registers - // Rename VirtualRegister to VirtualRegister - } - */ + Either::Right(_orgop) => todo!(), + }, + ) + }) + .collect::>(); + + // get registers from the pool. + let mut pool = RegisterPool::init(); + for (op, registers) in &op_register_mapping { + let allocated_op = match op.opcode { + Either::Left(ref op) => op.allocate_registers(&mut pool, &op_register_mapping), + Either::Right(ref _org_op) => todo!("convert org op to real op"), + }; + } todo!() } } -fn virtual_register_is_never_accessed_again( - reg: &VirtualRegister, - ops: &[(Op, std::collections::HashSet<&mut VirtualRegister>)], -) -> bool { - todo!() -} - -struct RegisterPool { +pub(crate) struct RegisterPool { available_registers: Vec, } @@ -200,11 +176,11 @@ impl RegisterPool { } } - fn get_register(&mut self) -> Option { + pub(crate) fn get_register(&mut self) -> Option { self.available_registers.pop() } - fn return_register_to_pool(&mut self, item_to_return: AllocatedRegister) { + pub(crate) fn return_register_to_pool(&mut self, item_to_return: AllocatedRegister) { self.available_registers.push(item_to_return); } } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 31f9d032494..5bc0b53775f 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -4,8 +4,14 @@ //! 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::{AllocatedOp, AllocatedRegister}, + Op, +}; +use crate::asm_generation::RegisterPool; use crate::{error::*, Ident}; use pest::Span; +use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::fmt; @@ -1308,9 +1314,61 @@ impl VirtualOp { pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[&str]) -> CompileResult<'sc, Self> { todo!() } - pub(crate) fn registers(&self) -> Vec { + pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { todo!() } + + pub(crate) fn allocate_registers( + &self, + pool: &mut RegisterPool, + op_register_mapping: &[(Op, HashSet)], + ) -> AllocatedOp { + let virtual_registers = self.registers(); + let register_allocation_result = virtual_registers + .clone() + .into_iter() + .map(|x| (x, pool.get_register())) + .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 => todo!("Out of registers error"), + }; + + for reg in virtual_registers { + if virtual_register_is_never_accessed_again(reg, &op_register_mapping) { + pool.return_register_to_pool(mapping.get(reg).unwrap().clone()); + } + } + + use VirtualOp::*; + match self { + ADD(reg1, reg2, reg3) => AllocatedOp::ADD( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + _ => todo!(), + } + } +} + +/// 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)] @@ -1321,3 +1379,9 @@ impl fmt::Display for Label { write!(f, ".{}", self.0) } } +fn virtual_register_is_never_accessed_again( + reg: &VirtualRegister, + ops: &[(Op, std::collections::HashSet)], +) -> bool { + todo!() +} From cfa3e4a0f51d4f7fb7fc41eb6423cdf0fd0afa9c Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Fri, 21 May 2021 07:27:51 -0700 Subject: [PATCH 12/62] change op parsing logic --- core_lang/src/asm_generation/mod.rs | 4 +- core_lang/src/asm_lang/mod.rs | 74 +++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 51edad86698..4794a06765a 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -144,7 +144,9 @@ impl<'sc> AbstractInstructionSet<'sc> { Either::Left(opc) => { opc.registers().into_iter().map(|x| x.clone()).collect() } - Either::Right(_orgop) => todo!(), + Either::Right(orgop) => { + orgop.registers().into_iter().map(|x| x.clone()).collect() + } }, ) }) diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 614445395f4..7f17b5ed31e 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -244,8 +244,34 @@ impl<'sc> Op<'sc> { args: &[&VirtualRegister], immediate: &Option>, ) -> CompileResult<'sc, VirtualOp> { - match name.primary_name.to_lowercase().as_str() { + let mut warnings = vec![]; + let mut errors = vec![]; + ok(match name.primary_name.to_lowercase().as_str() { "add" => { + let (r1, r2, r3) = type_check!(three_regs(args, immediate), return err(warnings, errors), warnings, errors); + VirtualOp::ADD(r1, r2, r3) + } + "addi" => { + let (r1, r2, imm) = type_check!(two_regs_imm_12(args, immediate), return err(warnings, errors), warnings, errors); + VirtualOp::ADDI(r1, r2, imm) + } + "and" => { + let (r1, r2, r3) = type_check!(three_regs(args, immediate), return err(warnings, errors), warnings, errors); + VirtualOp::AND(r1, r2, r3) + } + "andi" => { + let (r1, r2, imm) = type_check!(two_regs_imm_12(args, immediate), return err(warnings, errors), warnings, errors); + VirtualOp::ANDI(r1, r2, imm) + } + _ => todo!(), + }, warnings, errors) + } +} + +fn three_regs<'sc>(args: &[&VirtualRegister], immediate: &Option>) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { +let mut warnings = vec![]; +let mut errors = vec![]; + let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { (Some(reg), Some(reg2), Some(reg3)) => (*reg, *reg2, *reg3), _ => todo!("Not enough registers error"), @@ -256,14 +282,44 @@ impl<'sc> Op<'sc> { }; ok( - VirtualOp::ADD(reg.clone(), reg2.clone(), reg3.clone()), - vec![], - vec![], + (reg.clone(), reg2.clone(), reg3.clone()), +warnings, +errors + ) +} +fn two_regs_imm_12<'sc>(args: &[&VirtualRegister], immediate: &Option>) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { +let mut warnings = vec![]; +let mut errors = vec![]; + let (reg, reg2) = match (args.get(0), args.get(1)) { + (Some(reg), Some(reg2)) => (*reg, *reg2), + _ => todo!("Not enough registers error"), + }; + let (imm, imm_span): (u64, _) = match immediate { + None => todo!("Err missing immediate"), + 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 ) - } - _ => todo!(), - } - } } impl fmt::Display for Op<'_> { @@ -390,7 +446,7 @@ pub(crate) enum OrganizationalOp { } impl OrganizationalOp { - pub(crate) fn registers(&mut self) -> HashSet<&mut VirtualRegister> { + pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { use OrganizationalOp::*; (match self { Label(_) | Comment | Jump(_) => vec![], From 483c46dfcdc8efbc42619fe577904a23effeff5a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 09:01:27 -0700 Subject: [PATCH 13/62] WIP parsing inline asm to new ops --- core_lang/src/asm_lang/mod.rs | 761 +++++++++++++++++++++++--- core_lang/src/asm_lang/virtual_ops.rs | 6 +- core_lang/src/error.rs | 3 + 3 files changed, 697 insertions(+), 73 deletions(-) diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 7f17b5ed31e..ee37ed295ae 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -246,80 +246,701 @@ impl<'sc> Op<'sc> { ) -> CompileResult<'sc, VirtualOp> { let mut warnings = vec![]; let mut errors = vec![]; - ok(match name.primary_name.to_lowercase().as_str() { - "add" => { - let (r1, r2, r3) = type_check!(three_regs(args, immediate), return err(warnings, errors), warnings, errors); - VirtualOp::ADD(r1, r2, r3) - } - "addi" => { - let (r1, r2, imm) = type_check!(two_regs_imm_12(args, immediate), return err(warnings, errors), warnings, errors); - VirtualOp::ADDI(r1, r2, imm) - } - "and" => { - let (r1, r2, r3) = type_check!(three_regs(args, immediate), return err(warnings, errors), warnings, errors); - VirtualOp::AND(r1, r2, r3) - } - "andi" => { - let (r1, r2, imm) = type_check!(two_regs_imm_12(args, immediate), return err(warnings, errors), warnings, errors); - VirtualOp::ANDI(r1, r2, imm) - } - _ => todo!(), - }, warnings, errors) + ok( + match name.primary_name.to_lowercase().as_str() { + "add" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ADD(r1, r2, r3) + } + "addi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ADDI(r1, r2, imm) + } + "and" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::AND(r1, r2, r3) + } + "andi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ANDI(r1, r2, imm) + } + "div" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::DIV(r1, r2, r3) + } + "divi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::DIVI(r1, r2, imm) + } + "eq" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EQ(r1, r2, r3) + } + "exp" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EXP(r1, r2, r3) + } + "expi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::EXPI(r1, r2, imm) + } + "gt" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::GT(r1, r2, r3) + } + "mlog" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MLOG(r1, r2, r3) + } + "mroo" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MROO(r1, r2, r3) + } + "mod" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MOD(r1, r2, r3) + } + "modi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MODI(r1, r2, imm) + } + "move" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MOVE(r1, r2) + } + "mul" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MUL(r1, r2, r3) + } + "muli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MULI(r1, r2, imm) + } + "not" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::NOT(r1, r2) + } + "or" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::OR(r1, r2, r3) + } + "ori" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ORI(r1, r2, imm) + } + "sll" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLL(r1, r2, r3) + } + "slli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLLI(r1, r2, imm) + } + "srl" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRL(r1, r2, r3) + } + "srli" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRLI(r1, r2, imm) + } + "sub" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SUB(r1, r2, r3) + } + "subi" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SUBI(r1, r2, imm) + } + "xor" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::XOR(r1, r2, r3) + } + "xori" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::XORI(r1, r2, imm) + } + "cimv" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CIMV(r1, r2, r3) + } + "ctmv" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CTMV(r1, r2) + } + "ji" => { + let imm = type_check!( + single_imm_24(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::JI(imm) + } + "jnei" => { + errors.push(CompileError::DisallowedJnei { + span: name.span.clone(), + }); + return err(warnings, errors); + } + "ret" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::RET(r1) + } + "cfei" => { + let imm = type_check!( + single_imm_24(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CFEI(imm) + } + "cfsi" => { + let imm = type_check!( + single_imm_24(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CFSI(imm) + } + "lb" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LB(r1, r2, imm) + } + "lw" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LW(r1, r2, imm) + } + "aloc" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::ALOC(r1) + } + "mcl" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCL(r1, r2) + } + "mcli" => { + let (r1, imm) = type_check!( + single_reg_imm_18(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCLI(r1, imm) + } + "mcp" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MCP(r1, r2, r3) + } + "meq" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate), + 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), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SB(r1, r2, imm) + } + "sw" => { + let (r1, r2, imm) = type_check!( + two_regs_imm_12(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SW(r1, r2, imm) + } + "bhsh" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BHSH(r1, r2) + } + "bhei" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BHEI(r1) + } + "burn" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::BURN(r1) + } + "call" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CALL(r1, r2, r3, r4) + } + "ccp" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CCP(r1, r2, r3, r4) + } + "croo" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CROO(r1, r2) + } + "csiz" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CSIZ(r1, r2) + } + "cb" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::CB(r1) + } + "ldc" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LDC(r1, r2, r3) + } + "log" => { + let (r1, r2, r3, r4) = type_check!( + four_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::LOG(r1, r2, r3, r4) + } + _ => todo!(), + }, + warnings, + errors, + ) + } +} + +fn single_reg<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult<'sc, VirtualRegister> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 1 { + todo!("Unnecessary registers err") + } + + let reg = match args.get(0) { + Some(reg) => *reg, + _ => todo!("Not enough registers error"), + }; + match immediate { + None => (), + Some(_) => todo!("Err unnecessary immediate"), + }; + + ok(reg.clone(), warnings, errors) +} + +fn two_regs<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister)> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 2 { + todo!("Unnecessary registers err") + } + + let (reg, reg2) = match (args.get(0), args.get(1)) { + (Some(reg), Some(reg2)) => (*reg, *reg2), + _ => todo!("Not enough registers error"), + }; + match immediate { + None => (), + Some(_) => todo!("Err unnecessary immediate"), + }; + + ok((reg.clone(), reg2.clone()), warnings, errors) +} + +fn four_regs<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult< + 'sc, + ( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), +> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 4 { + todo!("Unnecessary registers err"); + } + + 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), + _ => todo!("Not enough registers error"), + }; + match immediate { + None => (), + Some(_) => todo!("Err unnecessary immediate"), + }; + + ok( + (reg.clone(), reg2.clone(), reg3.clone(), reg4.clone()), + warnings, + errors, + ) +} + +fn three_regs<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 3 { + todo!("Unnecessary registers err"); + } + + let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { + (Some(reg), Some(reg2), Some(reg3)) => (*reg, *reg2, *reg3), + _ => todo!("Not enough registers error"), + }; + match immediate { + None => (), + Some(_) => todo!("Err unnecessary immediate"), + }; + + ok((reg.clone(), reg2.clone(), reg3.clone()), warnings, errors) +} +fn single_imm_24<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult<'sc, VirtualImmediate24> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 0 { + todo!("Unnecessary registers err"); } + let (imm, imm_span): (u64, _) = match immediate { + None => todo!("Err missing immediate"), + 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>, +) -> CompileResult<'sc, (VirtualRegister, VirtualImmediate18)> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 1 { + todo!("Unnecessary registers err"); + } + let reg = match args.get(0) { + Some(reg) => *reg, + _ => todo!("Not enough registers error"), + }; + let (imm, imm_span): (u64, _) = match immediate { + None => todo!("Err missing immediate"), + 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 VirtualImmediate18::new(imm, imm_span) { + Ok(o) => o, + Err(e) => { + errors.push(e); + return err(warnings, errors); + } + }; -fn three_regs<'sc>(args: &[&VirtualRegister], immediate: &Option>) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { -let mut warnings = vec![]; -let mut errors = vec![]; - - let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { - (Some(reg), Some(reg2), Some(reg3)) => (*reg, *reg2, *reg3), - _ => todo!("Not enough registers error"), - }; - match immediate { - None => (), - Some(_) => todo!("Err unnecessary immediate"), - }; - - ok( - (reg.clone(), reg2.clone(), reg3.clone()), -warnings, -errors - ) + ok((reg.clone(), imm), warnings, errors) } -fn two_regs_imm_12<'sc>(args: &[&VirtualRegister], immediate: &Option>) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { -let mut warnings = vec![]; -let mut errors = vec![]; - let (reg, reg2) = match (args.get(0), args.get(1)) { - (Some(reg), Some(reg2)) => (*reg, *reg2), - _ => todo!("Not enough registers error"), - }; - let (imm, imm_span): (u64, _) = match immediate { - None => todo!("Err missing immediate"), - 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 - ) +fn two_regs_imm_12<'sc>( + args: &[&VirtualRegister], + immediate: &Option>, +) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { + let mut warnings = vec![]; + let mut errors = vec![]; + if args.len() > 2 { + todo!("Unnecessary registers err"); + } + let (reg, reg2) = match (args.get(0), args.get(1)) { + (Some(reg), Some(reg2)) => (*reg, *reg2), + _ => todo!("Not enough registers error"), + }; + let (imm, imm_span): (u64, _) = match immediate { + None => todo!("Err missing immediate"), + 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<'_> { diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 5bc0b53775f..d1f29b83d14 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -90,7 +90,7 @@ pub struct VirtualImmediate06 { } impl VirtualImmediate06 { - fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { if raw > 0b111_111 { return Err(CompileError::Immediate06TooLarge { val: raw, @@ -141,7 +141,7 @@ pub struct VirtualImmediate18 { value: u32, } impl VirtualImmediate18 { - fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { if raw > 0b111_111_111_111_111_111 { return Err(CompileError::Immediate18TooLarge { val: raw, @@ -171,7 +171,7 @@ pub struct VirtualImmediate24 { value: u32, } impl VirtualImmediate24 { - fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { + pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { if raw > 0b111_111_111_111_111_111_111_111 { return Err(CompileError::Immediate24TooLarge { val: raw, diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index 5d8e22f264b..19bcfb6305b 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -459,6 +459,8 @@ pub enum CompileError<'sc> { 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> }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -600,6 +602,7 @@ impl<'sc> CompileError<'sc> { Immediate12TooLarge { span, .. } => span, Immediate18TooLarge { span, .. } => span, Immediate24TooLarge { span, .. } => span, + DisallowedJnei { span, .. } => span, } } From fb5d61a3525c47eadf338d07b80230270cc10ff9 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 09:09:46 -0700 Subject: [PATCH 14/62] more op parsing --- core_lang/src/asm_lang/mod.rs | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index ee37ed295ae..5c1cf5d07b8 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -731,6 +731,69 @@ impl<'sc> Op<'sc> { ); VirtualOp::LOG(r1, r2, r3, r4) } + "mint" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::MINT(r1) + } + "rvrt" => { + let r1 = type_check!( + single_reg(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::RVRT(r1) + } + "sldc" => { + let (r1, r2, r3) = type_check!( + three_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SLDC(r1, r2, r3) + } + "srw" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRW(r1, r2) + } + "srwq" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SRWQ(r1, r2) + } + "sww" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SWW(r1, r2) + } + "swwq" => { + let (r1, r2) = type_check!( + two_regs(args, immediate), + return err(warnings, errors), + warnings, + errors + ); + VirtualOp::SWWQ(r1, r2) + } _ => todo!(), }, warnings, From 1723aca2239230ba09fedad8787997aed79aed4b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 10:45:57 -0700 Subject: [PATCH 15/62] finish parsing virtual ops from asm --- .../src/asm_generation/expression/mod.rs | 7 +- core_lang/src/asm_lang/mod.rs | 200 ++++++++++++------ core_lang/src/error.rs | 9 + 3 files changed, 152 insertions(+), 64 deletions(-) diff --git a/core_lang/src/asm_generation/expression/mod.rs b/core_lang/src/asm_generation/expression/mod.rs index 2adbfed6109..85b25e4335a 100644 --- a/core_lang/src/asm_generation/expression/mod.rs +++ b/core_lang/src/asm_generation/expression/mod.rs @@ -141,7 +141,12 @@ pub(crate) fn convert_expression_to_asm<'sc>( // 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 diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 5c1cf5d07b8..ced047aa47c 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -243,14 +243,15 @@ impl<'sc> Op<'sc> { name: &Ident<'sc>, 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.to_lowercase().as_str() { + match name.primary_name { "add" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -259,7 +260,7 @@ impl<'sc> Op<'sc> { } "addi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -268,7 +269,7 @@ impl<'sc> Op<'sc> { } "and" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -277,7 +278,7 @@ impl<'sc> Op<'sc> { } "andi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -286,7 +287,7 @@ impl<'sc> Op<'sc> { } "div" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -295,7 +296,7 @@ impl<'sc> Op<'sc> { } "divi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -304,7 +305,7 @@ impl<'sc> Op<'sc> { } "eq" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -313,7 +314,7 @@ impl<'sc> Op<'sc> { } "exp" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -322,7 +323,7 @@ impl<'sc> Op<'sc> { } "expi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -331,7 +332,7 @@ impl<'sc> Op<'sc> { } "gt" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -340,7 +341,7 @@ impl<'sc> Op<'sc> { } "mlog" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -349,7 +350,7 @@ impl<'sc> Op<'sc> { } "mroo" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -358,7 +359,7 @@ impl<'sc> Op<'sc> { } "mod" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -367,7 +368,7 @@ impl<'sc> Op<'sc> { } "modi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -376,7 +377,7 @@ impl<'sc> Op<'sc> { } "move" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -385,7 +386,7 @@ impl<'sc> Op<'sc> { } "mul" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -394,7 +395,7 @@ impl<'sc> Op<'sc> { } "muli" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -403,7 +404,7 @@ impl<'sc> Op<'sc> { } "not" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -412,7 +413,7 @@ impl<'sc> Op<'sc> { } "or" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -421,7 +422,7 @@ impl<'sc> Op<'sc> { } "ori" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -430,7 +431,7 @@ impl<'sc> Op<'sc> { } "sll" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -439,7 +440,7 @@ impl<'sc> Op<'sc> { } "slli" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -448,7 +449,7 @@ impl<'sc> Op<'sc> { } "srl" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -457,7 +458,7 @@ impl<'sc> Op<'sc> { } "srli" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -466,7 +467,7 @@ impl<'sc> Op<'sc> { } "sub" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -475,7 +476,7 @@ impl<'sc> Op<'sc> { } "subi" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -484,7 +485,7 @@ impl<'sc> Op<'sc> { } "xor" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -493,7 +494,7 @@ impl<'sc> Op<'sc> { } "xori" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -502,7 +503,7 @@ impl<'sc> Op<'sc> { } "cimv" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -511,7 +512,7 @@ impl<'sc> Op<'sc> { } "ctmv" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -520,7 +521,7 @@ impl<'sc> Op<'sc> { } "ji" => { let imm = type_check!( - single_imm_24(args, immediate), + single_imm_24(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -535,7 +536,7 @@ impl<'sc> Op<'sc> { } "ret" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -544,7 +545,7 @@ impl<'sc> Op<'sc> { } "cfei" => { let imm = type_check!( - single_imm_24(args, immediate), + single_imm_24(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -553,7 +554,7 @@ impl<'sc> Op<'sc> { } "cfsi" => { let imm = type_check!( - single_imm_24(args, immediate), + single_imm_24(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -562,7 +563,7 @@ impl<'sc> Op<'sc> { } "lb" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -571,7 +572,7 @@ impl<'sc> Op<'sc> { } "lw" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -580,7 +581,7 @@ impl<'sc> Op<'sc> { } "aloc" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -589,7 +590,7 @@ impl<'sc> Op<'sc> { } "mcl" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -598,7 +599,7 @@ impl<'sc> Op<'sc> { } "mcli" => { let (r1, imm) = type_check!( - single_reg_imm_18(args, immediate), + single_reg_imm_18(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -607,7 +608,7 @@ impl<'sc> Op<'sc> { } "mcp" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -616,7 +617,7 @@ impl<'sc> Op<'sc> { } "meq" => { let (r1, r2, r3, r4) = type_check!( - four_regs(args, immediate), + four_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -625,7 +626,7 @@ impl<'sc> Op<'sc> { } "sb" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -634,7 +635,7 @@ impl<'sc> Op<'sc> { } "sw" => { let (r1, r2, imm) = type_check!( - two_regs_imm_12(args, immediate), + two_regs_imm_12(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -643,7 +644,7 @@ impl<'sc> Op<'sc> { } "bhsh" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -652,7 +653,7 @@ impl<'sc> Op<'sc> { } "bhei" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -661,7 +662,7 @@ impl<'sc> Op<'sc> { } "burn" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -670,7 +671,7 @@ impl<'sc> Op<'sc> { } "call" => { let (r1, r2, r3, r4) = type_check!( - four_regs(args, immediate), + four_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -679,7 +680,7 @@ impl<'sc> Op<'sc> { } "ccp" => { let (r1, r2, r3, r4) = type_check!( - four_regs(args, immediate), + four_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -688,7 +689,7 @@ impl<'sc> Op<'sc> { } "croo" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -697,7 +698,7 @@ impl<'sc> Op<'sc> { } "csiz" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -706,7 +707,7 @@ impl<'sc> Op<'sc> { } "cb" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -715,7 +716,7 @@ impl<'sc> Op<'sc> { } "ldc" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -724,7 +725,7 @@ impl<'sc> Op<'sc> { } "log" => { let (r1, r2, r3, r4) = type_check!( - four_regs(args, immediate), + four_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -733,7 +734,7 @@ impl<'sc> Op<'sc> { } "mint" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -742,7 +743,7 @@ impl<'sc> Op<'sc> { } "rvrt" => { let r1 = type_check!( - single_reg(args, immediate), + single_reg(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -751,7 +752,7 @@ impl<'sc> Op<'sc> { } "sldc" => { let (r1, r2, r3) = type_check!( - three_regs(args, immediate), + three_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -760,7 +761,7 @@ impl<'sc> Op<'sc> { } "srw" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -769,7 +770,7 @@ impl<'sc> Op<'sc> { } "srwq" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -778,7 +779,7 @@ impl<'sc> Op<'sc> { } "sww" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors @@ -787,14 +788,76 @@ impl<'sc> Op<'sc> { } "swwq" => { let (r1, r2) = type_check!( - two_regs(args, immediate), + two_regs(args, immediate, whole_op_span), return err(warnings, errors), warnings, errors ); VirtualOp::SWWQ(r1, r2) } - _ => todo!(), + "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) + } + + other => { + errors.push(CompileError::UnrecognizedOp { + op_name: other, + span: name.span.clone(), + }); + return err(warnings, errors); + } }, warnings, errors, @@ -805,11 +868,16 @@ impl<'sc> Op<'sc> { fn single_reg<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, VirtualRegister> { let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 1 { - todo!("Unnecessary registers err") + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + expected: 1, + received: args.len(), + span: whole_op_span, + }); } let reg = match args.get(0) { @@ -827,6 +895,7 @@ fn single_reg<'sc>( fn two_regs<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister)> { let mut warnings = vec![]; let mut errors = vec![]; @@ -849,6 +918,7 @@ fn two_regs<'sc>( fn four_regs<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult< 'sc, ( @@ -883,6 +953,7 @@ fn four_regs<'sc>( fn three_regs<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { let mut warnings = vec![]; let mut errors = vec![]; @@ -904,6 +975,7 @@ fn three_regs<'sc>( fn single_imm_24<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, VirtualImmediate24> { let mut warnings = vec![]; let mut errors = vec![]; @@ -936,6 +1008,7 @@ fn single_imm_24<'sc>( fn single_reg_imm_18<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualImmediate18)> { let mut warnings = vec![]; let mut errors = vec![]; @@ -972,6 +1045,7 @@ fn single_reg_imm_18<'sc>( fn two_regs_imm_12<'sc>( args: &[&VirtualRegister], immediate: &Option>, + whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { let mut warnings = vec![]; let mut errors = vec![]; diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index 19bcfb6305b..5a18cae92a3 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -461,6 +461,14 @@ pub enum CompileError<'sc> { 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( + "This op expects {expected} registers as arguments, but you provided {received} registers." + )] + IncorrectNumberOfAsmRegisters { + span: Span<'sc>, + expected: usize, + received: usize, + }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -603,6 +611,7 @@ impl<'sc> CompileError<'sc> { Immediate18TooLarge { span, .. } => span, Immediate24TooLarge { span, .. } => span, DisallowedJnei { span, .. } => span, + IncorrectNumberOfAsmRegisters { span, .. } => span, } } From e961af75a324d305a127e4c3c7a3d8a5238de407 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 10:50:16 -0700 Subject: [PATCH 16/62] start registers method --- core_lang/src/asm_lang/virtual_ops.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index d1f29b83d14..4b0915f9eac 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -1310,12 +1310,21 @@ pub(crate) enum VirtualOp { } impl VirtualOp { - /// Given an op name and a list of arguments, parse them into a [VirtualOp] - pub(crate) fn parse<'sc>(name: &Ident<'sc>, args: &[&str]) -> CompileResult<'sc, Self> { - todo!() - } pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { - todo!() + 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], + + _ => todo!(), + }) + .into_iter() + .collect() } pub(crate) fn allocate_registers( From f61845bf7535ffc3332baab2eaaca301d6b8f4db Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 12:13:57 -0700 Subject: [PATCH 17/62] register allocation method --- core_lang/src/asm_generation/mod.rs | 2 +- core_lang/src/asm_lang/virtual_ops.rs | 66 +++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 4794a06765a..c754b94f623 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -170,7 +170,7 @@ pub(crate) struct RegisterPool { impl RegisterPool { fn init() -> Self { - let mut register_pool: Vec = (compiler_constants::NUM_FREE_REGISTERS..0) + let mut register_pool: Vec = (0..compiler_constants::NUM_FREE_REGISTERS) .map(|x| AllocatedRegister::Allocated(x)) .collect(); Self { diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 4b0915f9eac..6e4462525d7 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -1320,8 +1320,68 @@ impl VirtualOp { DIV(r1, r2, r3) => vec![r1, r2, r3], DIVI(r1, r2, _i) => vec![r1, r2], EQ(r1, r2, r3) => vec![r1, r2, r3], - - _ => todo!(), + 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() @@ -1392,5 +1452,5 @@ fn virtual_register_is_never_accessed_again( reg: &VirtualRegister, ops: &[(Op, std::collections::HashSet)], ) -> bool { - todo!() + !ops.iter().any(|(_, regs)| regs.contains(reg)) } From c36dbb57895a77bbeaa24f7d623369d5e8e8ddbc Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 12:49:59 -0700 Subject: [PATCH 18/62] convert virtual registers to allocated ones --- core_lang/src/asm_lang/virtual_ops.rs | 246 +++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 1 deletion(-) diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 6e4462525d7..4b5f1bec1c2 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -1427,7 +1427,251 @@ impl VirtualOp { map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - _ => todo!(), + ADDI(reg1, reg2, imm) => AllocatedOp::ADDI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + AND(reg1, reg2, reg3) => AllocatedOp::AND( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + ANDI(reg1, reg2, imm) => AllocatedOp::ANDI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + DIV(reg1, reg2, reg3) => AllocatedOp::DIV( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + DIVI(reg1, reg2, imm) => AllocatedOp::DIVI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + EQ(reg1, reg2, reg3) => AllocatedOp::EQ( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + EXP(reg1, reg2, reg3) => AllocatedOp::EXP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + EXPI(reg1, reg2, imm) => AllocatedOp::EXPI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + GT(reg1, reg2, reg3) => AllocatedOp::GT( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MLOG(reg1, reg2, reg3) => AllocatedOp::MLOG( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MROO(reg1, reg2, reg3) => AllocatedOp::MROO( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MOD(reg1, reg2, reg3) => AllocatedOp::MOD( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MODI(reg1, reg2, imm) => AllocatedOp::MODI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + MOVE(reg1, reg2) => AllocatedOp::MOVE(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + MUL(reg1, reg2, reg3) => AllocatedOp::MUL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MULI(reg1, reg2, imm) => AllocatedOp::MULI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + NOT(reg1, reg2) => AllocatedOp::NOT(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + OR(reg1, reg2, reg3) => AllocatedOp::OR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + ORI(reg1, reg2, imm) => AllocatedOp::ORI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SLL(reg1, reg2, reg3) => AllocatedOp::SLL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SLLI(reg1, reg2, imm) => AllocatedOp::SLLI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SRL(reg1, reg2, reg3) => AllocatedOp::SRL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SRLI(reg1, reg2, imm) => AllocatedOp::SRLI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SUB(reg1, reg2, reg3) => AllocatedOp::SUB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SUBI(reg1, reg2, imm) => AllocatedOp::SUBI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + XOR(reg1, reg2, reg3) => AllocatedOp::XOR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + XORI(reg1, reg2, imm) => AllocatedOp::XORI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + CIMV(reg1, reg2, reg3) => AllocatedOp::CIMV( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + CTMV(reg1, reg2) => AllocatedOp::CTMV(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + JI(imm) => AllocatedOp::JI(imm.clone()), + JNEI(reg1, reg2, imm) => AllocatedOp::JNEI( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + RET(reg) => AllocatedOp::RET(map_reg(&mapping, reg)), + CFEI(imm) => AllocatedOp::CFEI(imm.clone()), + CFSI(imm) => AllocatedOp::CFSI(imm.clone()), + LB(reg1, reg2, imm) => AllocatedOp::LB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + LW(reg1, reg2, imm) => AllocatedOp::LW( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + ALOC(reg) => AllocatedOp::ALOC(map_reg(&mapping, reg)), + MCL(reg1, reg2) => AllocatedOp::MCL(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + MCLI(reg1, imm) => AllocatedOp::MCLI(map_reg(&mapping, reg1), imm.clone()), + MCP(reg1, reg2, reg3) => AllocatedOp::MCP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + MEQ(reg1, reg2, reg3, reg4) => AllocatedOp::MEQ( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + SB(reg1, reg2, imm) => AllocatedOp::SB( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + SW(reg1, reg2, imm) => AllocatedOp::SW( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + imm.clone(), + ), + BHSH(reg1, reg2) => AllocatedOp::BHSH(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + BHEI(reg1) => AllocatedOp::BHEI(map_reg(&mapping, reg1)), + BURN(reg1) => AllocatedOp::BURN(map_reg(&mapping, reg1)), + CALL(reg1, reg2, reg3, reg4) => AllocatedOp::CALL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + CCP(reg1, reg2, reg3, reg4) => AllocatedOp::CCP( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + CROO(reg1, reg2) => AllocatedOp::CROO(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + CSIZ(reg1, reg2) => AllocatedOp::CSIZ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + CB(reg1) => AllocatedOp::CB(map_reg(&mapping, reg1)), + LDC(reg1, reg2, reg3) => AllocatedOp::LDC( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + LOG(reg1, reg2, reg3, reg4) => AllocatedOp::LOG( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + MINT(reg1) => AllocatedOp::MINT(map_reg(&mapping, reg1)), + RVRT(reg1) => AllocatedOp::RVRT(map_reg(&mapping, reg1)), + SLDC(reg1, reg2, reg3) => AllocatedOp::SLDC( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + SRW(reg1, reg2) => AllocatedOp::SRW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + SRWQ(reg1, reg2) => AllocatedOp::SRWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + SWW(reg1, reg2) => AllocatedOp::SWW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + SWWQ(reg1, reg2) => AllocatedOp::SWWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), + TR(reg1, reg2, reg3) => AllocatedOp::TR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + TRO(reg1, reg2, reg3, reg4) => AllocatedOp::TRO( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), + ECR(reg1, reg2, reg3) => AllocatedOp::ECR( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + K256(reg1, reg2, reg3) => AllocatedOp::K256( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + S256(reg1, reg2, reg3) => AllocatedOp::S256( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + ), + NOOP => AllocatedOp::NOOP, + FLAG(reg) => AllocatedOp::FLAG(map_reg(&mapping, reg)), + Undefined => AllocatedOp::Undefined, } } } From d35f6bf0d477931cd55adaf80660b27621204b6f Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 13:02:28 -0700 Subject: [PATCH 19/62] switch back to organizational labels for jumps --- core_lang/src/asm_generation/mod.rs | 18 +++++++++--------- core_lang/src/asm_lang/allocated_ops.rs | 2 +- core_lang/src/asm_lang/mod.rs | 16 ++++++++-------- core_lang/src/asm_lang/virtual_ops.rs | 2 +- core_lang/src/error.rs | 5 +++++ 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index c754b94f623..b9849723cad 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -191,7 +191,7 @@ impl RegisterPool { fn label_is_used<'sc>(buf: &[Op<'sc>], label: &Label) -> bool { buf.iter().any(|Op { ref opcode, .. }| match opcode { Either::Right(OrganizationalOp::Jump(ref l)) if label == l => true, - Either::Left(VirtualOp::JNEI(_reg0, _reg1, ref l)) if label == l => true, + Either::Right(OrganizationalOp::JumpIfNotEq(_reg0, _reg1, ref l)) if label == l => true, _ => false, }) } @@ -236,11 +236,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, ""), @@ -254,11 +254,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") } @@ -274,11 +274,11 @@ impl fmt::Display for RegisterAllocatedAsmSet<'_> { RegisterAllocatedAsmSet::ScriptMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), RegisterAllocatedAsmSet::PredicateMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), RegisterAllocatedAsmSet::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } @@ -294,11 +294,11 @@ impl fmt::Display for FinalizedAsm<'_> { FinalizedAsm::ScriptMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), FinalizedAsm::PredicateMain { data_section, program_section, - } => write!(f, "{}\n{}", data_section, program_section), + } => write!(f, "{}\n{}", program_section, data_section), FinalizedAsm::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 67d078c7da6..0f9b922a2ef 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -514,7 +514,7 @@ pub(crate) enum AllocatedOp { /// /// #### Panics /// - `$is + imm * 4 > VM_MAX_RAM - 1` - JNEI(AllocatedRegister, AllocatedRegister, Label), + JNEI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), /// Returns from [context](./main.md#contexts) with value `$rA`. /// diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index ced047aa47c..ba3c82eb54e 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -233,7 +233,7 @@ impl<'sc> Op<'sc> { label: Label, ) -> Self { Op { - opcode: Either::Left(VirtualOp::JNEI(reg0, reg1, label)), + opcode: Either::Right(OrganizationalOp::JumpIfNotEq(reg0, reg1, label)), comment: String::new(), owning_span: None, } @@ -520,13 +520,10 @@ impl<'sc> Op<'sc> { VirtualOp::CTMV(r1, r2) } "ji" => { - let imm = type_check!( - single_imm_24(args, immediate, whole_op_span), - return err(warnings, errors), - warnings, - errors - ); - VirtualOp::JI(imm) + errors.push(CompileError::DisallowedJi { + span: name.span.clone(), + }); + return err(warnings, errors); } "jnei" => { errors.push(CompileError::DisallowedJnei { @@ -1198,6 +1195,8 @@ pub(crate) enum OrganizationalOp { 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(VirtualRegister, DataId), @@ -1209,6 +1208,7 @@ impl 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 index 4b5f1bec1c2..84d15b39a5c 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -676,7 +676,7 @@ pub(crate) enum VirtualOp { /// /// #### Panics /// - `$is + imm * 4 > VM_MAX_RAM - 1` - JNEI(VirtualRegister, VirtualRegister, Label), + JNEI(VirtualRegister, VirtualRegister, VirtualImmediate12), /// Returns from [context](./main.md#contexts) with value `$rA`. /// diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index 5a18cae92a3..340f061c2fe 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -461,6 +461,10 @@ pub enum CompileError<'sc> { 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." )] @@ -611,6 +615,7 @@ impl<'sc> CompileError<'sc> { Immediate18TooLarge { span, .. } => span, Immediate24TooLarge { span, .. } => span, DisallowedJnei { span, .. } => span, + DisallowedJi { span, .. } => span, IncorrectNumberOfAsmRegisters { span, .. } => span, } } From 3a973af46fa90aba0daafb5789ecf128ee02e9c9 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Fri, 21 May 2021 17:24:14 -0700 Subject: [PATCH 20/62] realized ops --- core_lang/src/asm_generation/mod.rs | 8 ++++++++ core_lang/src/asm_lang/mod.rs | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index b9849723cad..f2427bcd6f1 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -78,6 +78,12 @@ pub enum HllAsmSet<'sc> { 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>, +} /// An [InstructionSet] is produced by allocating registers on an [AbstractInstructionSet]. pub struct InstructionSet<'sc> { ops: Vec>, @@ -128,6 +134,8 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } + fn realize_labels + 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 diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index ba3c82eb54e..de6131cd449 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -34,6 +34,16 @@ pub(crate) struct Op<'sc> { pub(crate) owning_span: Option>, } + +#[derive(Clone)] +pub(crate) struct RealizedOp<'sc> { + pub(crate) opcode: VirtualOp, OrganizationalOp, + /// A descriptive comment for ASM readability + pub(crate) comment: String, + pub(crate) owning_span: Option>, +} + + impl<'sc> Op<'sc> { /// Write value in given [VirtualRegister] `value_to_write` to given memory address that is held within the /// [VirtualRegister] `destination_address` From d8c0b83991ea8745609cf649926c7977b2ecb8c8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 May 2021 22:58:02 -0700 Subject: [PATCH 21/62] fully allocate registers and resolve labels --- .../expression/enum_instantiation.rs | 2 +- core_lang/src/asm_generation/mod.rs | 256 +++++++++++------- core_lang/src/asm_lang/mod.rs | 4 +- core_lang/src/asm_lang/virtual_ops.rs | 11 +- core_lang/src/lib.rs | 6 +- core_lang/src/parse_tree/literal.rs | 19 ++ forc/src/cli/build.rs | 2 +- 7 files changed, 188 insertions(+), 112 deletions(-) diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index 57970a0495e..75dd15a4e5c 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -48,7 +48,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( "load $sp for enum pointer", )); let size_of_enum = 1 /* tag */ + decl.as_type().stack_size_of(); - if size_of_enum < EIGHTEEN_BITS { + 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, diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index f2427bcd6f1..5326892d375 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -2,9 +2,12 @@ use std::{collections::HashMap, fmt}; use crate::{ asm_lang::{ - allocated_ops::AllocatedRegister, - virtual_ops::{Label, VirtualOp, VirtualRegister}, - Op, OrganizationalOp, + allocated_ops::{AllocatedOp, AllocatedRegister}, + virtual_ops::{ + ConstantRegister, Label, VirtualImmediate12, VirtualImmediate24, VirtualOp, + VirtualRegister, + }, + Op, OrganizationalOp, RealizedOp, }, error::*, parse_tree::Literal, @@ -79,14 +82,50 @@ pub struct AbstractInstructionSet<'sc> { ops: Vec>, } -/// "Realized" here refers to labels -- there are no more organizational +/// "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 { + // 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 mut 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( + op.opcode + .allocate_registers(&mut pool, &op_register_mapping, ix), + ) + } + InstructionSet { ops: buf } + } +} + /// An [InstructionSet] is produced by allocating registers on an [AbstractInstructionSet]. -pub struct InstructionSet<'sc> { - ops: Vec>, +pub struct InstructionSet { + ops: Vec, } type Data<'sc> = Literal<'sc>; @@ -134,41 +173,84 @@ impl<'sc> AbstractInstructionSet<'sc> { AbstractInstructionSet { ops: buf2 } } - fn realize_labels - - 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 mut op_register_mapping = self - .ops - .into_iter() - .map(|op| { - ( - op.clone(), - match op.opcode { - Either::Left(opc) => { - opc.registers().into_iter().map(|x| x.clone()).collect() - } - Either::Right(orgop) => { - orgop.registers().into_iter().map(|x| x.clone()).collect() - } - }, - ) - }) - .collect::>(); + /// 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) => (), + } + } - // get registers from the pool. - let mut pool = RegisterPool::init(); - for (op, registers) in &op_register_mapping { - let allocated_op = match op.opcode { - Either::Left(ref op) => op.allocate_registers(&mut pool, &op_register_mapping), - Either::Right(ref _org_op) => todo!("convert org op to real op"), + 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, + }, }; } - todo!() + RealizedAbstractInstructionSet { ops: realized_ops } } } @@ -178,7 +260,8 @@ pub(crate) struct RegisterPool { impl RegisterPool { fn init() -> Self { - let mut register_pool: Vec = (0..compiler_constants::NUM_FREE_REGISTERS) + let register_pool: Vec = (0..compiler_constants::NUM_FREE_REGISTERS) + .rev() .map(|x| AllocatedRegister::Allocated(x)) .collect(); Self { @@ -276,17 +359,15 @@ impl fmt::Display for JumpOptimizedAsmSet<'_> { } } -impl fmt::Display for RegisterAllocatedAsmSet<'_> { +impl fmt::Display for RegisterAllocatedAsmSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RegisterAllocatedAsmSet::ScriptMain { - data_section, - program_section, - } => write!(f, "{}\n{}", program_section, data_section), - RegisterAllocatedAsmSet::PredicateMain { - data_section, - program_section, - } => write!(f, "{}\n{}", program_section, data_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") } @@ -296,17 +377,11 @@ impl fmt::Display for RegisterAllocatedAsmSet<'_> { } } -impl fmt::Display for FinalizedAsm<'_> { +impl fmt::Display for FinalizedAsm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FinalizedAsm::ScriptMain { - data_section, - program_section, - } => write!(f, "{}\n{}", program_section, data_section), - FinalizedAsm::PredicateMain { - data_section, - program_section, - } => write!(f, "{}\n{}", program_section, data_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") } @@ -330,14 +405,14 @@ impl fmt::Display for AbstractInstructionSet<'_> { } } -impl fmt::Display for InstructionSet<'_> { +impl fmt::Display for InstructionSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, ".program:\n{}", self.ops .iter() - .map(|x| format!("{}", x)) + .map(|x| format!("{}", todo!())) .collect::>() .join("\n") ) @@ -396,7 +471,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![]; @@ -484,7 +559,7 @@ impl<'sc> HllAsmSet<'sc> { } impl<'sc> JumpOptimizedAsmSet<'sc> { - fn allocate_registers(self) -> RegisterAllocatedAsmSet<'sc> { + fn allocate_registers(self) -> RegisterAllocatedAsmSet { // TODO implement this -- noop for now match self { JumpOptimizedAsmSet::Library => RegisterAllocatedAsmSet::Library, @@ -492,15 +567,18 @@ impl<'sc> JumpOptimizedAsmSet<'sc> { data_section, program_section, } => RegisterAllocatedAsmSet::ScriptMain { - data_section, - program_section: program_section.clone().allocate_registers(), + 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.allocate_registers(), + program_section: program_section + .realize_labels(&data_section) + .allocate_registers(), }, JumpOptimizedAsmSet::ContractAbi => RegisterAllocatedAsmSet::ContractAbi, } @@ -522,39 +600,25 @@ pub enum JumpOptimizedAsmSet<'sc> { Library, } /// Represents an ASM set which has had registers allocated -pub enum RegisterAllocatedAsmSet<'sc> { +pub enum RegisterAllocatedAsmSet { ContractAbi, - ScriptMain { - data_section: DataSection<'sc>, - program_section: InstructionSet<'sc>, - }, - PredicateMain { - data_section: DataSection<'sc>, - program_section: InstructionSet<'sc>, - }, + ScriptMain { program_section: InstructionSet }, + PredicateMain { program_section: InstructionSet }, // Libraries do not generate any asm. Library, } -impl<'sc> RegisterAllocatedAsmSet<'sc> { - fn optimize(self) -> FinalizedAsm<'sc> { +impl<'sc> RegisterAllocatedAsmSet { + fn optimize(self) -> FinalizedAsm { // 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, } } @@ -562,16 +626,10 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { /// Represents an ASM set which has had register allocation, jump elimination, and optimization /// applied to it -pub enum FinalizedAsm<'sc> { +pub enum FinalizedAsm { ContractAbi, - ScriptMain { - data_section: DataSection<'sc>, - program_section: InstructionSet<'sc>, - }, - PredicateMain { - data_section: DataSection<'sc>, - program_section: InstructionSet<'sc>, - }, + ScriptMain { program_section: InstructionSet }, + PredicateMain { program_section: InstructionSet }, // Libraries do not generate any asm. Library, } diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index de6131cd449..af97f51ff81 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -34,16 +34,14 @@ pub(crate) struct Op<'sc> { pub(crate) owning_span: Option>, } - #[derive(Clone)] pub(crate) struct RealizedOp<'sc> { - pub(crate) opcode: VirtualOp, OrganizationalOp, + 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 [VirtualRegister] `value_to_write` to given memory address that is held within the /// [VirtualRegister] `destination_address` diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 84d15b39a5c..f245dd76721 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -6,7 +6,7 @@ use super::{ allocated_ops::{AllocatedOp, AllocatedRegister}, - Op, + Op, RealizedOp, }; use crate::asm_generation::RegisterPool; use crate::{error::*, Ident}; @@ -1390,7 +1390,8 @@ impl VirtualOp { pub(crate) fn allocate_registers( &self, pool: &mut RegisterPool, - op_register_mapping: &[(Op, HashSet)], + op_register_mapping: &[(RealizedOp, HashSet)], + ix: usize, ) -> AllocatedOp { let virtual_registers = self.registers(); let register_allocation_result = virtual_registers @@ -1415,7 +1416,7 @@ impl VirtualOp { }; for reg in virtual_registers { - if virtual_register_is_never_accessed_again(reg, &op_register_mapping) { + if virtual_register_is_never_accessed_again(reg, &op_register_mapping[ix + 1..]) { pool.return_register_to_pool(mapping.get(reg).unwrap().clone()); } } @@ -1684,7 +1685,7 @@ fn map_reg( mapping.get(reg).unwrap().clone() } -#[derive(Clone, Eq, PartialEq)] +#[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 { @@ -1694,7 +1695,7 @@ impl fmt::Display for Label { } fn virtual_register_is_never_accessed_again( reg: &VirtualRegister, - ops: &[(Op, std::collections::HashSet)], + ops: &[(RealizedOp, std::collections::HashSet)], ) -> bool { !ops.iter().any(|(_, regs)| regs.contains(reg)) } diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index 7bf8a710d0d..971d7f442e2 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -121,15 +121,15 @@ pub fn parse<'sc>(input: &'sc str) -> CompileResult<'sc, HllParseTree<'sc>> { pub enum CompilationResult<'sc> { ContractAbi { - abi: HashMap>, + abi: HashMap, warnings: Vec>, }, ScriptAsm { - asm: FinalizedAsm<'sc>, + asm: FinalizedAsm, warnings: Vec>, }, PredicateAsm { - asm: FinalizedAsm<'sc>, + asm: FinalizedAsm, warnings: Vec>, }, Library { diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index 87c6b8ff24a..aeabcf29075 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(crate) 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/forc/src/cli/build.rs b/forc/src/cli/build.rs index b6b4f1f74b5..63e1957bc96 100644 --- a/forc/src/cli/build.rs +++ b/forc/src/cli/build.rs @@ -196,7 +196,7 @@ fn compile<'source, 'manifest>( source: &'source str, proj_name: &str, namespace: &Namespace<'source>, -) -> Result, String> { +) -> Result { let res = core_lang::compile(&source, namespace); match res { CompilationResult::ScriptAsm { asm, warnings } => { From ea91252411afc27e8ed6a041f7cba19c1323823a Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 10:07:21 -0700 Subject: [PATCH 22/62] print allocated registers --- core_lang/src/asm_generation/mod.rs | 49 ++++--- core_lang/src/asm_lang/allocated_ops.rs | 102 ++++++++++++- core_lang/src/asm_lang/mod.rs | 146 +++++++++---------- core_lang/src/asm_lang/virtual_ops.rs | 184 +++++++++++++++--------- core_lang/src/lib.rs | 6 +- forc/src/cli/build.rs | 2 +- 6 files changed, 316 insertions(+), 173 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 5326892d375..7b07f869cf9 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -89,7 +89,7 @@ pub struct RealizedAbstractInstructionSet<'sc> { } impl<'sc> RealizedAbstractInstructionSet<'sc> { - fn allocate_registers(self) -> InstructionSet { + 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 @@ -114,18 +114,21 @@ impl<'sc> RealizedAbstractInstructionSet<'sc> { let mut pool = RegisterPool::init(); let mut buf = vec![]; for (ix, (op, _)) in op_register_mapping.iter().enumerate() { - buf.push( - op.opcode + 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 { - ops: Vec, +pub struct InstructionSet<'sc> { + ops: Vec>, } type Data<'sc> = Literal<'sc>; @@ -359,7 +362,7 @@ 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 { program_section } => { @@ -377,7 +380,7 @@ 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 { program_section } => write!(f, "{}", program_section,), @@ -405,14 +408,14 @@ impl fmt::Display for AbstractInstructionSet<'_> { } } -impl fmt::Display for InstructionSet { +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!("{}", todo!())) + .map(|x| format!("{}", x)) .collect::>() .join("\n") ) @@ -559,7 +562,7 @@ impl<'sc> HllAsmSet<'sc> { } impl<'sc> JumpOptimizedAsmSet<'sc> { - fn allocate_registers(self) -> RegisterAllocatedAsmSet { + fn allocate_registers(self) -> RegisterAllocatedAsmSet<'sc> { // TODO implement this -- noop for now match self { JumpOptimizedAsmSet::Library => RegisterAllocatedAsmSet::Library, @@ -600,16 +603,20 @@ pub enum JumpOptimizedAsmSet<'sc> { Library, } /// Represents an ASM set which has had registers allocated -pub enum RegisterAllocatedAsmSet { +pub enum RegisterAllocatedAsmSet<'sc> { ContractAbi, - ScriptMain { program_section: InstructionSet }, - PredicateMain { program_section: InstructionSet }, + ScriptMain { + program_section: InstructionSet<'sc>, + }, + PredicateMain { + program_section: InstructionSet<'sc>, + }, // Libraries do not generate any asm. Library, } -impl<'sc> RegisterAllocatedAsmSet { - fn optimize(self) -> FinalizedAsm { +impl<'sc> RegisterAllocatedAsmSet<'sc> { + fn optimize(self) -> FinalizedAsm<'sc> { // TODO implement this -- noop for now match self { RegisterAllocatedAsmSet::Library => FinalizedAsm::Library, @@ -626,10 +633,14 @@ impl<'sc> RegisterAllocatedAsmSet { /// Represents an ASM set which has had register allocation, jump elimination, and optimization /// applied to it -pub enum FinalizedAsm { +pub enum FinalizedAsm<'sc> { ContractAbi, - ScriptMain { program_section: InstructionSet }, - PredicateMain { program_section: InstructionSet }, + ScriptMain { + program_section: InstructionSet<'sc>, + }, + PredicateMain { + program_section: InstructionSet<'sc>, + }, // Libraries do not generate any asm. Library, } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 0f9b922a2ef..4b0b094a6ec 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -10,8 +10,11 @@ //! 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 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. @@ -38,7 +41,7 @@ impl fmt::Display for AllocatedRegister { /// A bit of copy/paste seemed worth it for that safety, /// so here it is. #[derive(Clone)] -pub(crate) enum AllocatedOp { +pub(crate) enum AllocatedOpcode { /// Adds two registers. /// /// | Operation | ```$rA = $rB + $rC;``` | @@ -1146,3 +1149,100 @@ pub(crate) enum AllocatedOp { /// Undefined opcode, potentially from inconsistent serialization 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 40 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 af97f51ff81..97a99c17366 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1086,92 +1086,83 @@ fn two_regs_imm_12<'sc>( } 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 { - todo!() - /* - use VirtualOp::*; 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), - MemClearVirtualImmediate(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), @@ -1189,7 +1180,6 @@ impl fmt::Display for Op<'_> { } write!(f, "{}", op_and_comment) - */ } } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index f245dd76721..147b0de77e3 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -5,7 +5,7 @@ //! ops are clones of the actual opcodes, but with the safe primitives as arguments. use super::{ - allocated_ops::{AllocatedOp, AllocatedRegister}, + allocated_ops::{AllocatedOp, AllocatedOpcode, AllocatedRegister}, Op, RealizedOp, }; use crate::asm_generation::RegisterPool; @@ -103,6 +103,11 @@ impl VirtualImmediate06 { } } } +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)] @@ -134,6 +139,11 @@ impl VirtualImmediate12 { } } } +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)] @@ -164,6 +174,11 @@ impl VirtualImmediate18 { } } } +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)] @@ -194,6 +209,11 @@ impl VirtualImmediate24 { } } } +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 @@ -1392,7 +1412,7 @@ impl VirtualOp { pool: &mut RegisterPool, op_register_mapping: &[(RealizedOp, HashSet)], ix: usize, - ) -> AllocatedOp { + ) -> AllocatedOpcode { let virtual_registers = self.registers(); let register_allocation_result = virtual_registers .clone() @@ -1423,256 +1443,278 @@ impl VirtualOp { use VirtualOp::*; match self { - ADD(reg1, reg2, reg3) => AllocatedOp::ADD( + ADD(reg1, reg2, reg3) => AllocatedOpcode::ADD( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - ADDI(reg1, reg2, imm) => AllocatedOp::ADDI( + ADDI(reg1, reg2, imm) => AllocatedOpcode::ADDI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - AND(reg1, reg2, reg3) => AllocatedOp::AND( + AND(reg1, reg2, reg3) => AllocatedOpcode::AND( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - ANDI(reg1, reg2, imm) => AllocatedOp::ANDI( + ANDI(reg1, reg2, imm) => AllocatedOpcode::ANDI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - DIV(reg1, reg2, reg3) => AllocatedOp::DIV( + DIV(reg1, reg2, reg3) => AllocatedOpcode::DIV( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - DIVI(reg1, reg2, imm) => AllocatedOp::DIVI( + DIVI(reg1, reg2, imm) => AllocatedOpcode::DIVI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - EQ(reg1, reg2, reg3) => AllocatedOp::EQ( + EQ(reg1, reg2, reg3) => AllocatedOpcode::EQ( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - EXP(reg1, reg2, reg3) => AllocatedOp::EXP( + EXP(reg1, reg2, reg3) => AllocatedOpcode::EXP( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - EXPI(reg1, reg2, imm) => AllocatedOp::EXPI( + EXPI(reg1, reg2, imm) => AllocatedOpcode::EXPI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - GT(reg1, reg2, reg3) => AllocatedOp::GT( + GT(reg1, reg2, reg3) => AllocatedOpcode::GT( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - MLOG(reg1, reg2, reg3) => AllocatedOp::MLOG( + MLOG(reg1, reg2, reg3) => AllocatedOpcode::MLOG( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - MROO(reg1, reg2, reg3) => AllocatedOp::MROO( + MROO(reg1, reg2, reg3) => AllocatedOpcode::MROO( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - MOD(reg1, reg2, reg3) => AllocatedOp::MOD( + MOD(reg1, reg2, reg3) => AllocatedOpcode::MOD( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - MODI(reg1, reg2, imm) => AllocatedOp::MODI( + MODI(reg1, reg2, imm) => AllocatedOpcode::MODI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - MOVE(reg1, reg2) => AllocatedOp::MOVE(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - MUL(reg1, reg2, reg3) => AllocatedOp::MUL( + 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) => AllocatedOp::MULI( + MULI(reg1, reg2, imm) => AllocatedOpcode::MULI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - NOT(reg1, reg2) => AllocatedOp::NOT(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - OR(reg1, reg2, reg3) => AllocatedOp::OR( + 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) => AllocatedOp::ORI( + ORI(reg1, reg2, imm) => AllocatedOpcode::ORI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - SLL(reg1, reg2, reg3) => AllocatedOp::SLL( + SLL(reg1, reg2, reg3) => AllocatedOpcode::SLL( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - SLLI(reg1, reg2, imm) => AllocatedOp::SLLI( + SLLI(reg1, reg2, imm) => AllocatedOpcode::SLLI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - SRL(reg1, reg2, reg3) => AllocatedOp::SRL( + SRL(reg1, reg2, reg3) => AllocatedOpcode::SRL( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - SRLI(reg1, reg2, imm) => AllocatedOp::SRLI( + SRLI(reg1, reg2, imm) => AllocatedOpcode::SRLI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - SUB(reg1, reg2, reg3) => AllocatedOp::SUB( + SUB(reg1, reg2, reg3) => AllocatedOpcode::SUB( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - SUBI(reg1, reg2, imm) => AllocatedOp::SUBI( + SUBI(reg1, reg2, imm) => AllocatedOpcode::SUBI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - XOR(reg1, reg2, reg3) => AllocatedOp::XOR( + XOR(reg1, reg2, reg3) => AllocatedOpcode::XOR( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - XORI(reg1, reg2, imm) => AllocatedOp::XORI( + XORI(reg1, reg2, imm) => AllocatedOpcode::XORI( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - CIMV(reg1, reg2, reg3) => AllocatedOp::CIMV( + CIMV(reg1, reg2, reg3) => AllocatedOpcode::CIMV( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - CTMV(reg1, reg2) => AllocatedOp::CTMV(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - JI(imm) => AllocatedOp::JI(imm.clone()), - JNEI(reg1, reg2, imm) => AllocatedOp::JNEI( + 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) => AllocatedOp::RET(map_reg(&mapping, reg)), - CFEI(imm) => AllocatedOp::CFEI(imm.clone()), - CFSI(imm) => AllocatedOp::CFSI(imm.clone()), - LB(reg1, reg2, imm) => AllocatedOp::LB( + 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) => AllocatedOp::LW( + LW(reg1, reg2, imm) => AllocatedOpcode::LW( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - ALOC(reg) => AllocatedOp::ALOC(map_reg(&mapping, reg)), - MCL(reg1, reg2) => AllocatedOp::MCL(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - MCLI(reg1, imm) => AllocatedOp::MCLI(map_reg(&mapping, reg1), imm.clone()), - MCP(reg1, reg2, reg3) => AllocatedOp::MCP( + 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) => AllocatedOp::MEQ( + 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) => AllocatedOp::SB( + SB(reg1, reg2, imm) => AllocatedOpcode::SB( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - SW(reg1, reg2, imm) => AllocatedOp::SW( + SW(reg1, reg2, imm) => AllocatedOpcode::SW( map_reg(&mapping, reg1), map_reg(&mapping, reg2), imm.clone(), ), - BHSH(reg1, reg2) => AllocatedOp::BHSH(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - BHEI(reg1) => AllocatedOp::BHEI(map_reg(&mapping, reg1)), - BURN(reg1) => AllocatedOp::BURN(map_reg(&mapping, reg1)), - CALL(reg1, reg2, reg3, reg4) => AllocatedOp::CALL( + 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) => AllocatedOp::CCP( + 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) => AllocatedOp::CROO(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - CSIZ(reg1, reg2) => AllocatedOp::CSIZ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - CB(reg1) => AllocatedOp::CB(map_reg(&mapping, reg1)), - LDC(reg1, reg2, reg3) => AllocatedOp::LDC( + 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) => AllocatedOp::LOG( + 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) => AllocatedOp::MINT(map_reg(&mapping, reg1)), - RVRT(reg1) => AllocatedOp::RVRT(map_reg(&mapping, reg1)), - SLDC(reg1, reg2, reg3) => AllocatedOp::SLDC( + 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) => AllocatedOp::SRW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - SRWQ(reg1, reg2) => AllocatedOp::SRWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - SWW(reg1, reg2) => AllocatedOp::SWW(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - SWWQ(reg1, reg2) => AllocatedOp::SWWQ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)), - TR(reg1, reg2, reg3) => AllocatedOp::TR( + 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) => AllocatedOp::TRO( + 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) => AllocatedOp::ECR( + ECR(reg1, reg2, reg3) => AllocatedOpcode::ECR( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - K256(reg1, reg2, reg3) => AllocatedOp::K256( + K256(reg1, reg2, reg3) => AllocatedOpcode::K256( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - S256(reg1, reg2, reg3) => AllocatedOp::S256( + S256(reg1, reg2, reg3) => AllocatedOpcode::S256( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - NOOP => AllocatedOp::NOOP, - FLAG(reg) => AllocatedOp::FLAG(map_reg(&mapping, reg)), - Undefined => AllocatedOp::Undefined, + NOOP => AllocatedOpcode::NOOP, + FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), + Undefined => AllocatedOpcode::Undefined, } } } diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index 971d7f442e2..7bf8a710d0d 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -121,15 +121,15 @@ pub fn parse<'sc>(input: &'sc str) -> CompileResult<'sc, HllParseTree<'sc>> { pub enum CompilationResult<'sc> { ContractAbi { - abi: HashMap, + abi: HashMap>, warnings: Vec>, }, ScriptAsm { - asm: FinalizedAsm, + asm: FinalizedAsm<'sc>, warnings: Vec>, }, PredicateAsm { - asm: FinalizedAsm, + asm: FinalizedAsm<'sc>, warnings: Vec>, }, Library { diff --git a/forc/src/cli/build.rs b/forc/src/cli/build.rs index 63e1957bc96..b6b4f1f74b5 100644 --- a/forc/src/cli/build.rs +++ b/forc/src/cli/build.rs @@ -196,7 +196,7 @@ fn compile<'source, 'manifest>( source: &'source str, proj_name: &str, namespace: &Namespace<'source>, -) -> Result { +) -> Result, String> { let res = core_lang::compile(&source, namespace); match res { CompilationResult::ScriptAsm { asm, warnings } => { From 8347dc32f664c7241d684ec4fe37802f941c4a62 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:01:58 -0700 Subject: [PATCH 23/62] fill in todo!() errors in asm parsing --- core_lang/src/asm_lang/mod.rs | 135 +++++++++++++++++++++++++++++----- core_lang/src/error.rs | 3 + 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 888f26761f6..1210dec7e17 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -881,17 +881,28 @@ fn single_reg<'sc>( errors.push(CompileError::IncorrectNumberOfAsmRegisters { expected: 1, received: args.len(), - span: whole_op_span, + span: whole_op_span.clone(), }); } let reg = match args.get(0) { Some(reg) => reg, - _ => todo!("Not enough registers error"), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + return err(warnings, errors); + } }; match immediate { None => (), - Some(_) => todo!("Err unnecessary immediate"), + Some(i) => { + errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }); + } }; ok(reg.clone(), warnings, errors) @@ -905,16 +916,29 @@ fn two_regs<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 2 { - todo!("Unnecessary registers err") + 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), - _ => todo!("Not enough registers error"), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 2, + received: args.len(), + }); + return err(warnings, errors); + } }; match immediate { None => (), - Some(_) => todo!("Err unnecessary immediate"), + Some(i) => errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }), }; ok((reg.clone(), reg2.clone()), warnings, errors) @@ -936,16 +960,31 @@ fn four_regs<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 4 { - todo!("Unnecessary registers err"); + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 4, + received: args.len(), + }); } 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), - _ => todo!("Not enough registers error"), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); + return err(warnings, errors); + } }; match immediate { None => (), - Some(_) => todo!("Err unnecessary immediate"), + Some(i) => { + errors.push(CompileError::MissingImmediate { + span: i.span.clone(), + }); + } }; impl ConstantRegister { @@ -989,16 +1028,31 @@ fn three_regs<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 3 { - todo!("Unnecessary registers err"); + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 3, + received: args.len(), + }); } let (reg, reg2, reg3) = match (args.get(0), args.get(1), args.get(2)) { (Some(reg), Some(reg2), Some(reg3)) => (reg, reg2, reg3), - _ => todo!("Not enough registers error"), + _ => { + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 3, + received: args.len(), + }); + return err(warnings, errors); + } }; match immediate { None => (), - Some(_) => todo!("Err unnecessary immediate"), + Some(i) => { + errors.push(CompileError::UnnecessaryImmediate { + span: i.span.clone(), + }); + } }; ok((reg.clone(), reg2.clone(), reg3.clone()), warnings, errors) @@ -1011,10 +1065,19 @@ fn single_imm_24<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 0 { - todo!("Unnecessary registers err"); + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 0, + received: args.len(), + }); } let (imm, imm_span): (u64, _) = match immediate { - None => todo!("Err missing 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(_) => { @@ -1044,14 +1107,30 @@ fn single_reg_imm_18<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 1 { - todo!("Unnecessary registers err"); + errors.push(CompileError::IncorrectNumberOfAsmRegisters { + span: whole_op_span.clone(), + expected: 1, + received: args.len(), + }); } let reg = match args.get(0) { Some(reg) => reg, - _ => todo!("Not enough registers error"), + _ => { + 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 => todo!("Err missing 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(_) => { @@ -1081,14 +1160,30 @@ fn two_regs_imm_12<'sc>( let mut warnings = vec![]; let mut errors = vec![]; if args.len() > 2 { - todo!("Unnecessary registers err"); + 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), - _ => todo!("Not enough registers error"), + _ => { + 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 => todo!("Err missing 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(_) => { diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index e66927e5a41..c5615e30506 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -613,6 +613,8 @@ pub enum CompileError<'sc> { expected: usize, received: usize, }, + #[error("This op does not take an immediate value.")] + UnnecessaryImmediate { span: Span<'sc> }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -760,6 +762,7 @@ impl<'sc> CompileError<'sc> { DisallowedJnei { span, .. } => span, DisallowedJi { span, .. } => span, IncorrectNumberOfAsmRegisters { span, .. } => span, + UnnecessaryImmediate { span, .. } => span, } } From d2b6b37c0e070ba25028ab383d60a5374244bb4d Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:11:49 -0700 Subject: [PATCH 24/62] resolve all todosudo apt-get install vlc in core_lang --- core_lang/src/asm_lang/virtual_ops.rs | 4 +++- core_lang/src/error.rs | 3 +++ .../ast_node/expression/typed_expression.rs | 13 +++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index fbeb43a65d4..c0f8e64c871 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -1432,7 +1432,9 @@ impl VirtualOp { mapping.insert(key, val); } } - None => todo!("Out of registers error"), + 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::*; diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index c5615e30506..b559e6a95ff 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -615,6 +615,8 @@ pub enum CompileError<'sc> { }, #[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> { @@ -763,6 +765,7 @@ impl<'sc> CompileError<'sc> { DisallowedJi { span, .. } => span, IncorrectNumberOfAsmRegisters { span, .. } => span, UnnecessaryImmediate { span, .. } => span, + AmbiguousPath { span, .. } => span, } } 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!( From 60c0b1cf16dd9529f86f93069ebb6b0d418eb5c9 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:16:17 -0700 Subject: [PATCH 25/62] switch to ssh for fuel-asm --- core_lang/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index 09e121d494f..fc91685503e 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,4 +15,4 @@ either = "1.6" Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" -fuel-asm = { path = "../../fuel-asm" } +fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm" } From 2156b67ef4c1f72dc268ec6b4dc760c7f7eb9d17 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:20:36 -0700 Subject: [PATCH 26/62] resolve warnings --- .../src/asm_generation/compiler_constants.rs | 1 - .../expression/enum_instantiation.rs | 4 ++-- core_lang/src/asm_generation/mod.rs | 2 +- core_lang/src/asm_lang/mod.rs | 16 ++++++++-------- core_lang/src/asm_lang/virtual_ops.rs | 6 +++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index de79d83332a..7bcc6040cb4 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1,3 +1,2 @@ pub(crate) const NUM_FREE_REGISTERS: u8 = 48; -pub(crate) const TWENTY_FOUR_BITS: u64 = 0b11111_11111_11111_11111_1111; pub(crate) const EIGHTEEN_BITS: u64 = 0b11111_11111_11111_111; diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index 75dd15a4e5c..0d57f9df5bb 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -13,8 +13,8 @@ use crate::semantic_analysis::ast_node::TypedEnumDeclaration; use crate::semantic_analysis::TypedExpression; use crate::Literal; use crate::{CompileResult, Ident}; -use fuel_asm::Opcode; -use std::convert::TryFrom; + + pub(crate) fn convert_enum_instantiation_to_asm<'sc>( decl: &TypedEnumDeclaration<'sc>, diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 94a30ae4997..36b81596533 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -95,7 +95,7 @@ impl<'sc> RealizedAbstractInstructionSet<'sc> { // registers when they are not read anymore // construct a mapping from every op to the registers it uses - let mut op_register_mapping = self + let op_register_mapping = self .ops .into_iter() .map(|op| { diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 1210dec7e17..4195fde139d 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -10,7 +10,7 @@ use either::Either; use pest::Span; use std::{collections::HashSet, fmt}; use virtual_ops::{ - ConstantRegister, Label, VirtualImmediate06, VirtualImmediate12, VirtualImmediate18, + ConstantRegister, Label, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, VirtualRegister, }; @@ -875,7 +875,7 @@ fn single_reg<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, VirtualRegister> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 1 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -913,7 +913,7 @@ fn two_regs<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister)> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 2 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -957,7 +957,7 @@ fn four_regs<'sc>( VirtualRegister, ), > { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 4 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -1025,7 +1025,7 @@ fn three_regs<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualRegister)> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 3 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -1062,7 +1062,7 @@ fn single_imm_24<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, VirtualImmediate24> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 0 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -1104,7 +1104,7 @@ fn single_reg_imm_18<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualImmediate18)> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 1 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { @@ -1157,7 +1157,7 @@ fn two_regs_imm_12<'sc>( immediate: &Option>, whole_op_span: Span<'sc>, ) -> CompileResult<'sc, (VirtualRegister, VirtualRegister, VirtualImmediate12)> { - let mut warnings = vec![]; + let warnings = vec![]; let mut errors = vec![]; if args.len() > 2 { errors.push(CompileError::IncorrectNumberOfAsmRegisters { diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index c0f8e64c871..95d90273f47 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -5,11 +5,11 @@ //! ops are clones of the actual opcodes, but with the safe primitives as arguments. use super::{ - allocated_ops::{AllocatedOp, AllocatedOpcode, AllocatedRegister}, - Op, RealizedOp, + allocated_ops::{AllocatedOpcode, AllocatedRegister}, + RealizedOp, }; use crate::asm_generation::RegisterPool; -use crate::{error::*, Ident}; +use crate::error::*; use pest::Span; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; From 2c126bf2b63770d30d5f39f2e47ac6bcb95bcaab Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:22:03 -0700 Subject: [PATCH 27/62] fix git url --- core_lang/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index fc91685503e..a815339d913 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,4 +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" } +fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } From 3598dd8642fd3165498f30b7ff701238848b2260 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:22:34 -0700 Subject: [PATCH 28/62] rustfmt --- core_lang/src/asm_generation/expression/enum_instantiation.rs | 2 -- core_lang/src/asm_lang/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index 0d57f9df5bb..da6998c69e1 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -14,8 +14,6 @@ use crate::semantic_analysis::TypedExpression; use crate::Literal; use crate::{CompileResult, Ident}; - - pub(crate) fn convert_enum_instantiation_to_asm<'sc>( decl: &TypedEnumDeclaration<'sc>, _variant_name: &Ident<'sc>, diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 4195fde139d..dd2388f150b 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -10,8 +10,8 @@ use either::Either; use pest::Span; use std::{collections::HashSet, fmt}; use virtual_ops::{ - ConstantRegister, Label, VirtualImmediate12, VirtualImmediate18, - VirtualImmediate24, VirtualOp, VirtualRegister, + ConstantRegister, Label, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp, + VirtualRegister, }; pub(crate) mod allocated_ops; From 8344df4c06bd4b0c8e6a19d54c47a327f6556bce Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:51:52 -0700 Subject: [PATCH 29/62] small self-code-review --- core_lang/src/asm_generation/compiler_constants.rs | 2 ++ core_lang/src/asm_generation/expression/structs.rs | 2 +- core_lang/src/asm_generation/mod.rs | 2 ++ core_lang/src/asm_lang/virtual_ops.rs | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 7bcc6040cb4..17d792bf229 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1,2 +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 = 0b11111_11111_11111_111; +pub(crate) const TWELVE_BITS: u64 = 0b111_111_111_111; diff --git a/core_lang/src/asm_generation/expression/structs.rs b/core_lang/src/asm_generation/expression/structs.rs index b8490870802..91b1468f7a1 100644 --- a/core_lang/src/asm_generation/expression/structs.rs +++ b/core_lang/src/asm_generation/expression/structs.rs @@ -71,7 +71,7 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( // 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 // limit struct size to 12 bits for now, for simplicity - let twelve_bits = 0b111_111_111_111; + let twelve_bits = compiler_constants::TWELVE_BITS; let number_of_allocations_necessary = (total_size / twelve_bits) + 1; // construct the allocation ops diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 36b81596533..ecfef8c329d 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -280,6 +280,8 @@ impl RegisterPool { } } + /// 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, diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 95d90273f47..4369908fd12 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -152,7 +152,7 @@ pub struct VirtualImmediate18 { } impl VirtualImmediate18 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { - if raw > 0b111_111_111_111_111_111 { + if raw > compiler_constants::EIGHTEEN_BITS { return Err(CompileError::Immediate18TooLarge { val: raw, span: err_msg_span, @@ -187,7 +187,7 @@ pub struct VirtualImmediate24 { } impl VirtualImmediate24 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { - if raw > 0b111_111_111_111_111_111_111_111 { + if raw > compiler_constants::TWENTY_FOUR_BITS { return Err(CompileError::Immediate24TooLarge { val: raw, span: err_msg_span, From cd6dbb58b9c9587d1abd7828e9835e4a85734093 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 22 May 2021 12:57:35 -0700 Subject: [PATCH 30/62] resolve module --- core_lang/src/asm_generation/expression/structs.rs | 2 +- core_lang/src/asm_generation/mod.rs | 2 +- core_lang/src/asm_lang/virtual_ops.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core_lang/src/asm_generation/expression/structs.rs b/core_lang/src/asm_generation/expression/structs.rs index 91b1468f7a1..3b45fa85b23 100644 --- a/core_lang/src/asm_generation/expression/structs.rs +++ b/core_lang/src/asm_generation/expression/structs.rs @@ -71,7 +71,7 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( // 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 // limit struct size to 12 bits for now, for simplicity - let twelve_bits = compiler_constants::TWELVE_BITS; + let twelve_bits = super::compiler_constants::TWELVE_BITS; let number_of_allocations_necessary = (total_size / twelve_bits) + 1; // construct the allocation ops diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index ecfef8c329d..51d6baa6c18 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -16,7 +16,7 @@ use crate::{ }; use either::Either; -mod compiler_constants; +pub(crate) mod compiler_constants; mod declaration; mod expression; mod register_sequencer; diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 4369908fd12..2a5a54ce329 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -152,7 +152,7 @@ pub struct VirtualImmediate18 { } impl VirtualImmediate18 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { - if raw > compiler_constants::EIGHTEEN_BITS { + if raw > crate::asm_generation::compiler_constants::EIGHTEEN_BITS { return Err(CompileError::Immediate18TooLarge { val: raw, span: err_msg_span, @@ -187,7 +187,7 @@ pub struct VirtualImmediate24 { } impl VirtualImmediate24 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { - if raw > compiler_constants::TWENTY_FOUR_BITS { + if raw > crate::asm_generation::compiler_constants::TWENTY_FOUR_BITS { return Err(CompileError::Immediate24TooLarge { val: raw, span: err_msg_span, From 4315bc288a9e09f779025a7bb6080062960c2b3c Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 23 May 2021 08:53:56 -0700 Subject: [PATCH 31/62] map the virtual opcodes to fuel_asm ops --- core_lang/src/asm_lang/allocated_ops.rs | 89 +++++++++++++++++++++++++ core_lang/src/asm_lang/virtual_ops.rs | 30 +++++++-- 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 4b0b094a6ec..9f419930f5f 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -35,6 +35,15 @@ impl fmt::Display for AllocatedRegister { } } +impl AllocatedRegister { + fn to_register_id(&self) -> fuel_asm::RegisterId { + match self { + AllocatedRegister::Allocated(a) => (a + 16) as fuel_asm::RegisterId, + AllocatedRegister::Constant(constant) => constant.to_register_id(), + } + } +} + /// 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. @@ -1246,3 +1255,83 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { write!(f, "{}", op_and_comment) } } + +impl<'sc> AllocatedOp<'sc> { + fn to_fuel_asm(&self) -> fuel_asm::Opcode { + use fuel_asm::Opcode as VmOp; + use AllocatedOpcode::*; + #[rustfmt::skip] + let fuel_op = match &self.opcode { + ADD (a, b, c) => VmOp::ADD (a.to_register_id(), b.to_register_id(), c.to_register_id()), + ADDI(a, b, c) => VmOp::ADDI(a.to_register_id(), b.to_register_id(), c.value), + AND (a, b, c) => VmOp::AND (a.to_register_id(), b.to_register_id(), c.to_register_id()), + ANDI(a, b, c) => VmOp::ANDI(a.to_register_id(), b.to_register_id(), c.value), + DIV (a, b, c) => VmOp::DIV (a.to_register_id(), b.to_register_id(), c.to_register_id()), + DIVI(a, b, c) => VmOp::DIVI(a.to_register_id(), b.to_register_id(), c.value), + EQ (a, b, c) => VmOp::EQ (a.to_register_id(), b.to_register_id(), c.to_register_id()), + EXP (a, b, c) => VmOp::EXP (a.to_register_id(), b.to_register_id(), c.to_register_id()), + EXPI(a, b, c) => VmOp::EXPI(a.to_register_id(), b.to_register_id(), c.value), + GT (a, b, c) => VmOp::GT (a.to_register_id(), b.to_register_id(), c.to_register_id()), + MLOG(a, b, c) => VmOp::MLOG(a.to_register_id(), b.to_register_id(), c.to_register_id()), + MROO(a, b, c) => VmOp::MROO(a.to_register_id(), b.to_register_id(), c.to_register_id()), + MOD (a, b, c) => VmOp::MOD (a.to_register_id(), b.to_register_id(), c.to_register_id()), + MODI(a, b, c) => VmOp::MODI(a.to_register_id(), b.to_register_id(), c.value), + MOVE(a, b) => VmOp::MOVE(a.to_register_id(), b.to_register_id()), + MUL (a, b, c) => VmOp::MUL (a.to_register_id(), b.to_register_id(), c.to_register_id()), + MULI(a, b, c) => VmOp::MULI(a.to_register_id(), b.to_register_id(), c.value), + NOT (a, b) => VmOp::NOT (a.to_register_id(), b.to_register_id()), + OR (a, b, c) => VmOp::OR (a.to_register_id(), b.to_register_id(), c.to_register_id()), + ORI (a, b, c) => VmOp::ORI (a.to_register_id(), b.to_register_id(), c.value), + SLL (a, b, c) => VmOp::SLL (a.to_register_id(), b.to_register_id(), c.to_register_id()), + SLLI(a, b, c) => VmOp::SLLI(a.to_register_id(), b.to_register_id(), c.value), + SRL (a, b, c) => VmOp::SRL (a.to_register_id(), b.to_register_id(), c.to_register_id()), + SRLI(a, b, c) => VmOp::SRLI(a.to_register_id(), b.to_register_id(), c.value), + SUB (a, b, c) => VmOp::SUB (a.to_register_id(), b.to_register_id(), c.to_register_id()), + SUBI(a, b, c) => VmOp::SUBI(a.to_register_id(), b.to_register_id(), c.value), + XOR (a, b, c) => VmOp::XOR (a.to_register_id(), b.to_register_id(), c.to_register_id()), + XORI(a, b, c) => VmOp::XORI(a.to_register_id(), b.to_register_id(), c.value), + CIMV(a, b, c) => VmOp::CIMV(a.to_register_id(), b.to_register_id(), c.to_register_id()), + CTMV(a, b) => VmOp::CTMV(a.to_register_id(), b.to_register_id()), + JI (a) => VmOp::JI (a.value), + JNEI(a, b, c) => VmOp::JNEI(a.to_register_id(), b.to_register_id(), c.value), + RET (a) => VmOp::RET (a.to_register_id()), + CFEI(a) => VmOp::CFEI(a.value), + CFSI(a) => VmOp::CFSI(a.value), + LB (a, b, c) => VmOp::LB (a.to_register_id(), b.to_register_id(), c.value), + LW (a, b, c) => VmOp::LW (a.to_register_id(), b.to_register_id(), c.value), + ALOC(a) => VmOp::ALOC(a.to_register_id()), + MCL (a, b) => VmOp::MCL (a.to_register_id(), b.to_register_id()), + MCLI(a, b) => VmOp::MCLI(a.to_register_id(), b.value), + MCP (a, b, c) => VmOp::MCP (a.to_register_id(), b.to_register_id(), c.to_register_id()), + MEQ (a, b, c, d)=> VmOp::MEQ (a.to_register_id(), b.to_register_id(), c.to_register_id(), d.to_register_id()), + SB (a, b, c) => VmOp::SB (a.to_register_id(), b.to_register_id(), c.value), + SW (a, b, c) => VmOp::SW (a.to_register_id(), b.to_register_id(), c.value), + BHSH(a, b) => VmOp::BHSH(a.to_register_id(), b.to_register_id()), + BHEI(a) => VmOp::BHEI(a.to_register_id()), + BURN(a) => VmOp::BURN(a.to_register_id()), + CALL(a, b, c, d)=> VmOp::CALL(a.to_register_id(), b.to_register_id(), c.to_register_id(), d.to_register_id()), + CCP (a, b, c, d)=> VmOp::CCP (a.to_register_id(), b.to_register_id(), c.to_register_id(), d.to_register_id()), + CROO(a, b) => VmOp::CROO(a.to_register_id(), b.to_register_id()), + CSIZ(a, b) => VmOp::CSIZ(a.to_register_id(), b.to_register_id()), + CB (a) => VmOp::CB (a.to_register_id()), + LDC (a, b, c) => VmOp::LDC (a.to_register_id(), b.to_register_id(), c.to_register_id()), + LOG (a, b, c, d)=> VmOp::LOG (a.to_register_id(), b.to_register_id(), c.to_register_id(), d.to_register_id()), + MINT(a) => VmOp::MINT(a.to_register_id()), + RVRT(a) => VmOp::RVRT(a.to_register_id()), + SLDC(a, b, c) => VmOp::SLDC(a.to_register_id(), b.to_register_id(), c.to_register_id()), + SRW (a, b) => VmOp::SRW (a.to_register_id(), b.to_register_id()), + SRWQ(a, b) => VmOp::SRWQ(a.to_register_id(), b.to_register_id()), + SWW (a, b) => VmOp::SWW (a.to_register_id(), b.to_register_id()), + SWWQ(a, b) => VmOp::SWWQ(a.to_register_id(), b.to_register_id()), + TR (a, b, c) => VmOp::TR (a.to_register_id(), b.to_register_id(), c.to_register_id()), + TRO (a, b, c, d)=> VmOp::TRO (a.to_register_id(), b.to_register_id(), c.to_register_id(), d.to_register_id()), + ECR (a, b, c) => VmOp::ECR (a.to_register_id(), b.to_register_id(), c.to_register_id()), + K256(a, b, c) => VmOp::K256(a.to_register_id(), b.to_register_id(), c.to_register_id()), + S256(a, b, c) => VmOp::S256(a.to_register_id(), b.to_register_id(), c.to_register_id()), + NOOP => VmOp::NOOP, + FLAG(a) => VmOp::FLAG(a.to_register_id()), + Undefined => VmOp::Undefined, + }; + fuel_op + } +} diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 2a5a54ce329..595b5b3093c 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -60,6 +60,28 @@ pub enum ConstantRegister { Flags, } +impl ConstantRegister { + pub(crate) fn to_register_id(&self) -> fuel_asm::RegisterId { + use ConstantRegister::*; + match self { + Zero => 0, + One => 1, + Overflow => 2, + ProgramCounter => 3, + StackStartPointer => 4, + StackPointer => 5, + FramePointer => 6, + HeapPointer => 7, + Error => 8, + GlobalGas => 9, + ContextGas => 10, + Balance => 11, + InstructionStart => 12, + Flags => 13, + } + } +} + impl fmt::Display for ConstantRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ConstantRegister::*; @@ -86,7 +108,7 @@ impl fmt::Display for ConstantRegister { /// 6-bits immediate value type #[derive(Clone)] pub struct VirtualImmediate06 { - value: u8, + pub(crate) value: u8, } impl VirtualImmediate06 { @@ -112,7 +134,7 @@ impl fmt::Display for VirtualImmediate06 { /// 12-bits immediate value type #[derive(Clone)] pub struct VirtualImmediate12 { - value: u16, + pub(crate) value: u16, } impl VirtualImmediate12 { @@ -148,7 +170,7 @@ impl fmt::Display for VirtualImmediate12 { /// 18-bits immediate value type #[derive(Clone)] pub struct VirtualImmediate18 { - value: u32, + pub(crate) value: u32, } impl VirtualImmediate18 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { @@ -183,7 +205,7 @@ impl fmt::Display for VirtualImmediate18 { /// 24-bits immediate value type #[derive(Clone)] pub struct VirtualImmediate24 { - value: u32, + pub(crate) value: u32, } impl VirtualImmediate24 { pub(crate) fn new<'sc>(raw: u64, err_msg_span: Span<'sc>) -> Result> { From a2a3ee71b3143a426dbcdfa6608054b0e8fbefd4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 23 May 2021 09:01:29 -0700 Subject: [PATCH 32/62] code review feedback --- .../src/asm_generation/compiler_constants.rs | 2 +- core_lang/src/asm_lang/allocated_ops.rs | 1019 +---------------- core_lang/src/asm_lang/virtual_ops.rs | 1012 ---------------- 3 files changed, 4 insertions(+), 2029 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 17d792bf229..5c7dbbd649c 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1,4 +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 = 0b11111_11111_11111_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_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 4b0b094a6ec..4f243806f1e 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -15,9 +15,8 @@ use std::fmt; const COMMENT_START_COLUMN: usize = 30; -/// 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. +/// 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), @@ -42,1111 +41,99 @@ impl fmt::Display for AllocatedRegister { /// so here it is. #[derive(Clone)] pub(crate) enum AllocatedOpcode { - /// Adds two registers. - /// - /// | Operation | ```$rA = $rB + $rC;``` | - /// | Syntax | `add $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// `$err` is cleared. ADD(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Adds a register and an immediate value. - /// - /// | Operation | ```$rA = $rB + imm;``` | - /// | Syntax | `addi $rA, $rB, immediate` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// `$err` is cleared. ADDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Bitwise ANDs two registers. - /// - /// | Operation | ```$rA = $rB & $rC;``` | - /// | Syntax | `and $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` and `$err` are cleared. AND(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Bitwise ANDs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB & imm;``` | - /// | Syntax | `andi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. - /// `$of` and `$err` are cleared. ANDI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Divides two registers. - /// - /// | Operation | ```$rA = $rB // $rC;``` | - /// | Syntax | `div $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// If `$rC == 0`, `$rA` is cleared and `$err` is set to `true`. - /// Otherwise, `$err` is cleared. - /// `$of` is cleared. DIV(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Divides a register and an immediate value. - /// - /// | Operation | ```$rA = $rB // imm;``` | - /// | Syntax | `divi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// If `imm == 0`, `$rA` is cleared and `$err` is set to `true`. - /// Otherwise, `$err` is cleared. - /// `$of` is cleared. DIVI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Compares two registers for equality. - /// - /// | Operation | ```$rA = $rB == $rC;``` | - /// | Syntax | `eq $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register, - /// - /// #### Execution - /// `$of` and `$err` are cleared. EQ(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Raises one register to the power of another. - /// - /// | Operation | ```$rA = $rB ** $rC;``` | - /// | Syntax | `exp $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise - /// `$of` is cleared. - /// `$err` is cleared. EXP(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Raises one register to the power of an immediate value. - /// - /// | Operation | ```$rA = $rB ** imm;``` | - /// | Syntax | `expi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise - /// `$of` is cleared. - /// `$err` is cleared. EXPI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Compares two registers for greater-than. - /// - /// | Operation | ```$rA = $rB > $rC;``` | - /// | Syntax | `gt $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. GT(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// The (integer) logarithm base `$rC` of `$rB`. - /// - /// | Operation | ```$rA = math.floor(math.log($rB, $rC));``` | - /// | Syntax | `mlog $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rB == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// If `$rC <= 1`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MLOG(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// The (integer) `$rC`th root of `$rB`. - /// - /// | Operation | ```$rA = math.floor(math.root($rB, $rC));``` | - /// | Syntax | `mroo $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MROO(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Modulo remainder of two registers. - /// - /// | Operation | ```$rA = $rB % $rC;``` | - /// | Syntax | `mod $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MOD(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Modulo remainder of a register and an immediate value. - /// - /// | Operation | ```$rA = $rB % imm;``` | - /// | Syntax | `modi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `imm == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MODI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Copy from one register to another. - /// - /// | Operation | ```$rA = $rB;``` | - /// | Syntax | `move $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. MOVE(AllocatedRegister, AllocatedRegister), - - /// Multiplies two registers. - /// - /// | Operation | ```$rA = $rB * $rC;``` | - /// | Syntax | `mul $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. MUL(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Multiplies a register and an immediate value. - /// - /// | Operation | ```$rA = $rB * imm;``` | - /// | Syntax | `mul $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. MULI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Bitwise NOT a register. - /// - /// | Operation | ```$rA = ~$rB;``` | - /// | Syntax | `not $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. NOT(AllocatedRegister, AllocatedRegister), - - /// Bitwise ORs two registers. - /// - /// | Operation | ```$rA = $rB \| $rC;``` | - /// | Syntax | `or $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. OR(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Bitwise ORs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB \| imm;``` | - /// | Syntax | `ori $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. - /// - /// `$of` and `$err` are cleared. ORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Left shifts a register by a register. - /// - /// | Operation | ```$rA = $rB << $rC;``` | - /// | Syntax | `sll $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. SLL(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Left shifts a register by an immediate value. - /// - /// | Operation | ```$rA = $rB << imm;``` | - /// | Syntax | `slli $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. SLLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Right shifts a register by a register. - /// - /// | Operation | ```$rA = $rB >> $rC;``` | - /// | Syntax | `srl $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SRL(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Right shifts a register by an immediate value. - /// - /// | Operation | ```$rA = $rB >> imm;``` | - /// | Syntax | `srli $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SRLI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Subtracts two registers. - /// - /// | Operation | ```$rA = $rB - $rC;``` | - /// | Syntax | `sub $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | `$of` is assigned the overflow of the operation. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SUB(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Subtracts a register and an immediate value. - /// - /// | Operation | ```$rA = $rB - imm;``` | - /// | Syntax | `subi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | `$of` is assigned the overflow of the operation. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SUBI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Bitwise XORs two registers. - /// - /// | Operation | ```$rA = $rB ^ $rC;``` | - /// | Syntax | `xor $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. XOR(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Bitwise XORs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB ^ imm;``` | - /// | Syntax | `xori $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. XORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Set `$rA` to `true` if the `$rC <= tx.input[$rB].maturity`. - /// - /// | Operation | ```$rA = checkinputmaturityverify($rB, $rC);``` | - /// | Syntax | `cimv $rA $rB $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rC > tx.input[$rB].maturity` - /// - the input `$rB` is not of type - /// [`InputType.Coin`](../protocol/tx_format.md) - /// - `$rB > tx.inputsCount` - /// - /// #### Execution - /// Otherwise, advance the program counter `$pc` by `4`. - /// - /// See also: [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and [CLTV](#cltv-check-lock-time-verify). CIMV(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Set `$rA` to `true` if `$rB <= tx.maturity`. - /// - /// | Operation | ```$rA = checktransactionmaturityverify($rB);``` | - /// | Syntax | `ctmv $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB > tx.maturity` - /// - /// #### Execution - /// Otherwise, advance the program counter `$pc` by `4`. - /// - /// See also: [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and [Bitcoin's Time Locks](https://prestwi.ch/bitcoin-time-locks). CTMV(AllocatedRegister, AllocatedRegister), - - /// Jumps to the code instruction offset by `imm`. - /// - /// | Operation | ```$pc = $is + imm * 4;``` | - /// | Syntax | `ji imm` | - /// | Encoding | `0x00 i i i i` | - /// - /// #### Panics - /// - `$is + imm * 4 > VM_MAX_RAM - 1` JI(VirtualImmediate24), - - /// Jump to the code instruction offset by `imm` if `$rA` is not equal to - /// `$rB`. - /// - /// | Operation | ```if $rA != $rB:```
```$pc = $is + imm * - /// 4;```
```else:```
```$pc += 4;``` | Syntax | `jnei $rA - /// $rB imm` | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$is + imm * 4 > VM_MAX_RAM - 1` JNEI(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Returns from [context](./main.md#contexts) with value `$rA`. - /// - /// | Operation | ```return($rA);``` - /// | Syntax | `ret $rA` - /// | Encoding | `0x00 rA - - -` - /// - /// If current context is external, cease VM execution and return `$rA`. - /// - /// Returns from contract call, popping the call frame. Before popping: - /// - /// 1. Return the unused forwarded gas to the caller: - /// - `$cgas = $cgas + $fp->$cgas` (add remaining context gas from - /// previous context to current remaining context gas) - /// - /// Then pop the call frame and restoring registers _except_ `$ggas` and - /// `$cgas`. Afterwards, set the following registers: - /// - /// 1. `$pc = $pc + 4` (advance program counter from where we called) RET(AllocatedRegister), - - /// Extend the current call frame's stack by an immediate value. - /// - /// | Operation | ```$sp = $sp + imm``` - /// | Syntax | `cfei imm` - /// | Encoding | `0x00 i i i i` - /// | Notes | Does not initialize memory. - /// - /// #### Panics - /// - `$sp + imm` overflows - /// - `$sp + imm > $hp` CFEI(VirtualImmediate24), - - /// Shrink the current call frame's stack by an immediate value. - /// - /// | Operation | ```$sp = $sp - imm``` - /// | Syntax | `cfsi imm` - /// | Encoding | `0x00 i i i i` - /// | Notes | Does not clear memory. - /// - /// #### Panics - /// - `$sp - imm` underflows - /// - `$sp - imm < $ssp` CFSI(VirtualImmediate24), - - /// A byte is loaded from the specified address offset by `imm`. - /// - /// | Operation | ```$rA = MEM[$rB + imm, 1];``` - /// | Syntax | `lb $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + imm + 1` overflows - /// - `$rB + imm + 1 > VM_MAX_RAM` LB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// A word is loaded from the specified address offset by `imm`. - /// | Operation | ```$rA = MEM[$rB + imm, 8];``` - /// | Syntax | `lw $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + imm + 8` overflows - /// - `$rB + imm + 8 > VM_MAX_RAM` LW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Allocate a number of bytes from the heap. - /// - /// | Operation | ```$hp = $hp - $rA;``` | - /// | Syntax | `aloc $rA` | - /// | Encoding | `0x00 rA - - -` | - /// | Notes | Does not initialize memory. | - /// - /// #### Panics - /// - `$hp - $rA` underflows - /// - `$hp - $rA < $sp` ALOC(AllocatedRegister), - - /// Clear bytes in memory. - /// - /// | Operation | ```MEM[$rA, $rB] = 0;``` | - /// | Syntax | `mcl $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + $rB` overflows - /// - `$rA + $rB > VM_MAX_RAM` - /// - `$rB > MEM_MAX_ACCESS_SIZE` - /// - The memory range `MEM[$rA, $rB]` does not pass [ownership - /// check](./main.md#ownership) MCL(AllocatedRegister, AllocatedRegister), - - /// Clear bytes in memory. - /// - /// | Operation | ```MEM[$rA, imm] = 0;``` | - /// | Syntax | `mcli $rA, imm` | - /// | Encoding | `0x00 rA i i i` | - /// - /// #### Panics - /// - `$rA + imm` overflows - /// - `$rA + imm > VM_MAX_RAM` - /// - `imm > MEM_MAX_ACCESS_SIZE` - /// - The memory range `MEM[$rA, imm]` does not pass [ownership - /// check](./main.md#ownership) MCLI(AllocatedRegister, VirtualImmediate18), - - /// Copy bytes in memory. - /// - /// | Operation | ```MEM[$rA, $rC] = MEM[$rB, $rC];``` | - /// | Syntax | `mcp $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA + $rC` overflows - /// - `$rB + $rC` overflows - /// - `$rA + $rC > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - The memory ranges `MEM[$rA, $rC]` and `MEM[$rB, $rC]` overlap - /// - The memory range `MEM[$rA, $rC]` does not pass [ownership - /// check](./main.md#ownership) MCP(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Compare bytes in memory. - /// - /// | Operation | ```$rA = MEM[$rB, $rD] == MEM[$rC, $rD];``` | - /// | Syntax | `meq $rA, $rB, $rC, $rD` | - /// | Encoding | `0x00 rA rB rC rD` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + $rD` overflows - /// - `$rC + $rD` overflows - /// - `$rB + $rD > VM_MAX_RAM` - /// - `$rC + $rD > VM_MAX_RAM` - /// - `$rD > MEM_MAX_ACCESS_SIZE` MEQ( AllocatedRegister, AllocatedRegister, AllocatedRegister, AllocatedRegister, ), - - /// The least significant byte of `$rB` is stored at the address `$rA` - /// offset by `imm`. - /// - /// | Operation | ```MEM[$rA + imm, 1] = $rB[7, 1];``` | - /// | Syntax | `sb $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA + imm + 1` overflows - /// - `$rA + imm + 1 > VM_MAX_RAM` - /// - The memory range `MEM[$rA + imm, 1]` does not pass [ownership - /// check](./main.md#ownership) SB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// The value of `$rB` is stored at the address `$rA` offset by `imm`. - /// - /// | Operation | ```MEM[$rA + imm, 8] = $rB;``` - /// | Syntax | `sw $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA + imm + 8` overflows - /// - `$rA + imm + 8 > VM_MAX_RAM` - /// - The memory range `MEM[$rA + imm, 8]` does not pass [ownership - /// check](./main.md#ownership) SW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - - /// Get block header hash. - /// - /// | Operation | ```MEM[$rA, 32] = blockhash($rB);``` | - /// | Syntax | `bhsh $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - /// Block header hashes for blocks with height greater than or equal to - /// current block height are zero (`0x00**32`). BHSH(AllocatedRegister, AllocatedRegister), - - /// Get Fuel block height. - /// - /// | Operation | ```$rA = blockheight();``` | - /// | Syntax | `bhei $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) BHEI(AllocatedRegister), - - /// Burn `$rA` coins of the current contract's color. - /// - /// | Operation | ```burn($rA);``` | - /// | Syntax | `burn $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panic - /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, - /// 32]` minus `$rA` underflows - /// - `$fp == 0` (in the script context) - /// - /// For output with contract ID `MEM[$fp, 32]`, decrease balance of color - /// `MEM[$fp, 32]` by `$rA`. - /// - /// This modifies the `balanceRoot` field of the appropriate output. BURN(AllocatedRegister), - - /// Call contract. - /// - /// | Syntax | `call $rA $rB $rC $rD` | - /// | Encoding | `0x00 rA rB rC rD` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rC + 32` overflows - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - Reading past `MEM[VM_MAX_RAM - 1]` - /// - Any output range does not pass [ownership check](./main.md#ownership) - /// - In an external context, if `$rB > MEM[balanceOfStart(MEM[$rC, 32]), - /// 8]` - /// - In an internal context, if `$rB` is greater than the balance of color - /// `MEM[$rC, 32]` of output with contract ID `MEM[$rA, 32]` - /// - /// Register `$rA` is a memory address from which the following fields are - /// set (word-aligned). - /// - /// `$rD` is the amount of gas to forward. If it is set to an amount greater - /// than the available gas, all available gas is forwarded. - /// - /// For output with contract ID `MEM[$rA, 32]`, increase balance of color - /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease - /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, - /// decrease color `MEM[$rC, 32]` balance of output with contract ID - /// `MEM[$fp, 32]` by `$rB`. - /// - /// A [call frame](./main.md#call-frames) is pushed at `$sp`. In addition to - /// filling in the values of the call frame, the following registers are - /// set: - /// - /// 1. `$fp = $sp` (on top of the previous call frame is the beginning of - /// this call frame) 1. Set `$ssp` and `$sp` to the start of the - /// writable stack area of the call frame. 1. Set `$pc` and `$is` to the - /// starting address of the code. 1. `$bal = $rD` (forward coins) - /// 1. `$cgas = $rD` or all available gas (forward gas) - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). CALL( AllocatedRegister, AllocatedRegister, AllocatedRegister, AllocatedRegister, ), - - /// Copy `$rD` bytes of code starting at `$rC` for contract with ID equal to - /// the 32 bytes in memory starting at `$rB` into memory starting at `$rA`. - /// - /// | Operation | ```MEM[$rA, $rD] = code($rB, $rC, $rD);``` - /// | Syntax | `ccp $rA, $rB, $rC, $rD` - /// | Encoding | `0x00 rA rB rC rD` - /// | Notes | If `$rD` is greater than the code size, zero bytes are - /// filled in. - /// - /// #### Panics - /// - `$rA + $rD` overflows - /// - `$rB + 32` overflows - /// - `$rA + $rD > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, $rD]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rD > MEM_MAX_ACCESS_SIZE` - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` CCP( AllocatedRegister, AllocatedRegister, AllocatedRegister, AllocatedRegister, ), - - /// Set the 32 bytes in memory starting at `$rA` to the code root for - /// contract with ID equal to the 32 bytes in memory starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = coderoot(MEM[$rB, 32]);``` - /// | Syntax | `croo $rA, $rB` - /// | Encoding | `0x00 rA rB - -` - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` - /// - /// Code root compuration is defined - /// [here](../protocol/identifiers.md#contract-id). CROO(AllocatedRegister, AllocatedRegister), - - /// Set `$rA` to the size of the code for contract with ID equal to the 32 - /// bytes in memory starting at `$rB`. - /// - /// | Operation | ```$rA = codesize(MEM[$rB, 32]);``` - /// | Syntax | `csiz $rA, $rB` - /// | Encoding | `0x00 rA rB - -` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + 32` overflows - /// - `$rB + 32 > VM_MAX_RAM` - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` CSIZ(AllocatedRegister, AllocatedRegister), - - /// Get block proposer address. - /// - /// | Operation | ```MEM[$rA, 32] = coinbase();``` | - /// | Syntax | `cb $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) CB(AllocatedRegister), - - /// Copy `$rC` bytes of code starting at `$rB` for contract with ID equal to - /// the 32 bytes in memory starting at `$rA` into memory starting at `$ssp`. - /// - /// | Operation | ```MEM[$ssp, $rC] = code($rA, $rB, $rC);``` - /// | Syntax | `ldc $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// | Notes | If `$rC` is greater than the code size, zero bytes are - /// filled in. - /// - /// #### Panics - /// - `$ssp + $rC` overflows - /// - `$rA + 32` overflows - /// - `$ssp + $rC > VM_MAX_RAM` - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$ssp != $sp` - /// - `$ssp + $rC > $hp` - /// - `$rC > CONTRACT_MAX_SIZE` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word - /// alignment. - /// - /// This opcode can be used to concatenate the code of multiple contracts - /// together. It can only be used when the stack area of the call frame is - /// unused (i.e. prior to being used). LDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Log an event. This is a no-op. - /// - /// | Operation | ```log($rA, $rB, $rC, $rD);``` | - /// | Syntax | `log $rA, $rB, $rC, $rD` | - /// | Encoding | `0x00 rA rB rC rD` | LOG( AllocatedRegister, AllocatedRegister, AllocatedRegister, AllocatedRegister, ), - - /// Mint `$rA` coins of the current contract's color. - /// - /// | Operation | ```mint($rA);``` | - /// | Syntax | `mint $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, - /// 32]` plus `$rA` overflows - /// - `$fp == 0` (in the script context) - /// - /// For output with contract ID `MEM[$fp, 32]`, increase balance of color - /// `MEM[$fp, 32]` by `$rA`. - /// - /// This modifies the `balanceRoot` field of the appropriate output. MINT(AllocatedRegister), - - /// Halt execution, reverting state changes and returning value in `$rA`. - /// - /// | Operation | ```revert($rA);``` - /// | Syntax | `rvrt $rA` - /// | Encoding | `0x00 rA - - -` - /// - /// After a revert: - /// - /// 1. All [OutputContract](../protocol/tx_format.md#outputcontract) outputs - /// will have the same `amount` and `stateRoot` as on initialization. 1. - /// All [OutputVariable](../protocol/tx_format.md outputs#outputvariable) - /// outputs will have `to` and `amount` of zero. - /// 1. All [OutputContractConditional](../protocol/tx_format.md# - /// outputcontractconditional) outputs will have `contractID`, `amount`, and - /// `stateRoot` of zero. RVRT(AllocatedRegister), - - /// Copy `$rC` bytes of code starting at `$rB` for contract with static - /// index `$rA` into memory starting at `$ssp`. - /// - /// | Operation | ```MEM[$ssp, $rC] = scode($rA, $rB, $rC);``` - /// | Syntax | `sloadcode $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// | Notes | If `$rC` is greater than the code size, zero bytes - /// are filled in. | - /// - /// #### Panics - /// - `$ssp + $rC` overflows - /// - `$ssp + $rC > VM_MAX_RAM` - /// - `$rA >= MAX_STATIC_CONTRACTS` - /// - `$rA` is greater than or equal to `staticContractsCount` for the - /// contract with ID `MEM[$fp, 32]` - /// - `$ssp != $sp` - /// - `$ssp + $rC > $hp` - /// - `$rC > CONTRACT_MAX_SIZE` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - `$fp == 0` (in the script context) - /// - /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word - /// alignment. - /// - /// This opcode can be used to concatenate the code of multiple contracts - /// together. It can only be used when the stack area of the call frame is - /// unused (i.e. prior to being used). SLDC(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// A word is read from the current contract's state. - /// - /// | Operation | ```$rA = STATE[MEM[$rB, 32]][0, 8];``` | - /// | Syntax | `srw $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | Returns zero if the state element does not exist. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + 32` overflows - /// - `$rB + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SRW(AllocatedRegister, AllocatedRegister), - - /// 32 bytes is read from the current contract's state. - /// - /// | Operation | ```MEM[$rA, 32] = STATE[MEM[$rB, 32]];``` | - /// | Syntax | `srwx $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | Returns zero if the state element does not exist. | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$fp == 0` (in the script context) SRWQ(AllocatedRegister, AllocatedRegister), - - /// A word is written to the current contract's state. - /// - /// | Operation | ```STATE[MEM[$rA, 32]][0, 8] = $rB;``` | - /// | Syntax | `sww $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SWW(AllocatedRegister, AllocatedRegister), - - /// 32 bytes is written to the current contract's state. - /// - /// | Operation | ```STATE[MEM[$rA, 32]] = MEM[$rB, 32];``` | - /// | Syntax | `swwx $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SWWQ(AllocatedRegister, AllocatedRegister), - - /// Transfer `$rB` coins with color at `$rC` to contract with ID at `$rA`. - /// - /// | Operation | ```transfer(MEM[$rA, 32], $rB, MEM[$rC, 32]);``` - /// | Syntax | `tr $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns - /// the memory address of `color` balance, or `0` if `color` has no balance. - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rC + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rC + 32 > VM_MAX_RAM` - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - In an external context, if `$rB > MEM[balanceOf(MEM[$rC, 32]), 8]` - /// - In an internal context, if `$rB` is greater than the balance of color - /// `MEM[$rC, 32]` of output with contract ID `MEM[$fp, 32]` - /// - `$rB == 0` - /// - /// For output with contract ID `MEM[$rA, 32]`, increase balance of color - /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease - /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, - /// decrease color `MEM[$rC, 32]` balance of output with contract ID - /// `MEM[$fp, 32]` by `$rB`. - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). TR(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Transfer `$rC` coins with color at `$rD` to address at `$rA`, with - /// output `$rB`. | Operation | ```transferout(MEM[$rA, 32], $rB, $rC, - /// MEM[$rD, 32]);``` | Syntax | `tro $rA, $rB, $rC, $rD` - /// | Encoding | `0x00 rA rB rC rD` - /// - /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns - /// the memory address of `color` balance, or `0` if `color` has no balance. - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rD + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rD + 32 > VM_MAX_RAM` - /// - `$rB > tx.outputsCount` - /// - In an external context, if `$rC > MEM[balanceOf(MEM[$rD, 32]), 8]` - /// - In an internal context, if `$rC` is greater than the balance of color - /// `MEM[$rD, 32]` of output with contract ID `MEM[$fp, 32]` - /// - `$rC == 0` - /// - `tx.outputs[$rB].type != OutputType.Variable` - /// - `tx.outputs[$rB].amount != 0` - /// - /// In an external context, decrease `MEM[balanceOfStart(MEM[$rD, 32]), 8]` - /// by `$rC`. In an internal context, decrease color `MEM[$rD, 32]` balance - /// of output with contract ID `MEM[$fp, 32]` by `$rC`. Then set: - /// - /// - `tx.outputs[$rB].to = MEM[$rA, 32]` - /// - `tx.outputs[$rB].amount = $rC` - /// - `tx.outputs[$rB].color = MEM[$rD, 32]` - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). TRO( AllocatedRegister, AllocatedRegister, AllocatedRegister, AllocatedRegister, ), - - /// The 64-byte public key (x, y) recovered from 64-byte - /// signature starting at `$rB` on 32-byte message hash starting at `$rC`. | - /// - /// | Operation | ```MEM[$rA, 64] = ecrecover(MEM[$rB, 64], MEM[$rC, - /// 32]);``` | Syntax | `ecr $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// #### Panics - /// - `$rA + 64` overflows - /// - `$rB + 64` overflows - /// - `$rC + 32` overflows - /// - `$rA + 64 > VM_MAX_RAM` - /// - `$rB + 64 > VM_MAX_RAM` - /// - `$rC + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 64]` does not pass [ownership - /// check](./main.md#ownership) - /// - /// To get the address, hash the public key with - /// [SHA-2-256](#sha256-sha-2-256). ECR(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// The keccak-256 hash of `$rC` bytes starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = keccak256(MEM[$rB, $rC]);``` - /// | Syntax | `k256 $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + $rC` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rC > MEM_MAX_ACCESS_SIZE` K256(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// The SHA-2-256 hash of `$rC` bytes starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = sha256(MEM[$rB, $rC]);``` | - /// | Syntax | `s256 $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + $rC` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rC > MEM_MAX_ACCESS_SIZE` S256(AllocatedRegister, AllocatedRegister, AllocatedRegister), - - /// Performs no operation. - /// - /// | Operation | | - /// | Syntax | `noop` | - /// | Encoding | `0x00 - - - -` | - /// - /// `$of` and `$err` are cleared. NOOP, - - /// Set `$flag` to `$rA`. - /// - /// | Operation | ```$flag = $rA;``` | - /// | Syntax | `flag $rA` | - /// | Encoding | `0x00 rA - - -` | FLAG(AllocatedRegister), - - /// Undefined opcode, potentially from inconsistent serialization Undefined, } @@ -1233,7 +220,7 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { FLAG(a) => format!("flag {}", a), Undefined => format!("undefined op"), }; - // we want the comment to always be 40 characters offset to the right + // 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 { diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 2a5a54ce329..90bd4633150 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -221,1111 +221,99 @@ impl fmt::Display for VirtualImmediate24 { /// so here it is. #[derive(Clone)] pub(crate) enum VirtualOp { - /// Adds two registers. - /// - /// | Operation | ```$rA = $rB + $rC;``` | - /// | Syntax | `add $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// `$err` is cleared. ADD(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Adds a register and an immediate value. - /// - /// | Operation | ```$rA = $rB + imm;``` | - /// | Syntax | `addi $rA, $rB, immediate` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// `$err` is cleared. ADDI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Bitwise ANDs two registers. - /// - /// | Operation | ```$rA = $rB & $rC;``` | - /// | Syntax | `and $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `$of` and `$err` are cleared. AND(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Bitwise ANDs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB & imm;``` | - /// | Syntax | `andi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. - /// `$of` and `$err` are cleared. ANDI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Divides two registers. - /// - /// | Operation | ```$rA = $rB // $rC;``` | - /// | Syntax | `div $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// If `$rC == 0`, `$rA` is cleared and `$err` is set to `true`. - /// Otherwise, `$err` is cleared. - /// `$of` is cleared. DIV(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Divides a register and an immediate value. - /// - /// | Operation | ```$rA = $rB // imm;``` | - /// | Syntax | `divi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a reserved register. - /// - /// #### Execution - /// If `imm == 0`, `$rA` is cleared and `$err` is set to `true`. - /// Otherwise, `$err` is cleared. - /// `$of` is cleared. DIVI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Compares two registers for equality. - /// - /// | Operation | ```$rA = $rB == $rC;``` | - /// | Syntax | `eq $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a reserved register, - /// - /// #### Execution - /// `$of` and `$err` are cleared. EQ(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Raises one register to the power of another. - /// - /// | Operation | ```$rA = $rB ** $rC;``` | - /// | Syntax | `exp $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise - /// `$of` is cleared. - /// `$err` is cleared. EXP(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Raises one register to the power of an immediate value. - /// - /// | Operation | ```$rA = $rB ** imm;``` | - /// | Syntax | `expi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If the result cannot fit in 8 bytes, `$of` is set to `1`, otherwise - /// `$of` is cleared. - /// `$err` is cleared. EXPI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Compares two registers for greater-than. - /// - /// | Operation | ```$rA = $rB > $rC;``` | - /// | Syntax | `gt $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. GT(VirtualRegister, VirtualRegister, VirtualRegister), - - /// The (integer) logarithm base `$rC` of `$rB`. - /// - /// | Operation | ```$rA = math.floor(math.log($rB, $rC));``` | - /// | Syntax | `mlog $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rB == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// If `$rC <= 1`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MLOG(VirtualRegister, VirtualRegister, VirtualRegister), - - /// The (integer) `$rC`th root of `$rB`. - /// - /// | Operation | ```$rA = math.floor(math.root($rB, $rC));``` | - /// | Syntax | `mroo $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MROO(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Modulo remainder of two registers. - /// - /// | Operation | ```$rA = $rB % $rC;``` | - /// | Syntax | `mod $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `$rC == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MOD(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Modulo remainder of a register and an immediate value. - /// - /// | Operation | ```$rA = $rB % imm;``` | - /// | Syntax | `modi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// If `imm == 0`, both `$rA` and `$of` are cleared and `$err` is set to - /// `true`. - /// - /// Otherwise, `$of` and `$err` are cleared. MODI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Copy from one register to another. - /// - /// | Operation | ```$rA = $rB;``` | - /// | Syntax | `move $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. MOVE(VirtualRegister, VirtualRegister), - - /// Multiplies two registers. - /// - /// | Operation | ```$rA = $rB * $rC;``` | - /// | Syntax | `mul $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. MUL(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Multiplies a register and an immediate value. - /// - /// | Operation | ```$rA = $rB * imm;``` | - /// | Syntax | `mul $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. MULI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Bitwise NOT a register. - /// - /// | Operation | ```$rA = ~$rB;``` | - /// | Syntax | `not $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. NOT(VirtualRegister, VirtualRegister), - - /// Bitwise ORs two registers. - /// - /// | Operation | ```$rA = $rB \| $rC;``` | - /// | Syntax | `or $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. OR(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Bitwise ORs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB \| imm;``` | - /// | Syntax | `ori $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `imm` is extended to 64 bits, with the high 52 bits set to `0`. - /// - /// `$of` and `$err` are cleared. ORI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Left shifts a register by a register. - /// - /// | Operation | ```$rA = $rB << $rC;``` | - /// | Syntax | `sll $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. SLL(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Left shifts a register by an immediate value. - /// - /// | Operation | ```$rA = $rB << imm;``` | - /// | Syntax | `slli $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the overflow of the operation. - /// - /// `$err` is cleared. SLLI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Right shifts a register by a register. - /// - /// | Operation | ```$rA = $rB >> $rC;``` | - /// | Syntax | `srl $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SRL(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Right shifts a register by an immediate value. - /// - /// | Operation | ```$rA = $rB >> imm;``` | - /// | Syntax | `srli $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | Zeroes are shifted in. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SRLI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Subtracts two registers. - /// - /// | Operation | ```$rA = $rB - $rC;``` | - /// | Syntax | `sub $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | `$of` is assigned the overflow of the operation. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SUB(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Subtracts a register and an immediate value. - /// - /// | Operation | ```$rA = $rB - imm;``` | - /// | Syntax | `subi $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | `$of` is assigned the overflow of the operation. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` is assigned the underflow of the operation, as though `$of` is the - /// high byte of a 128-bit register. - /// - /// `$err` is cleared. SUBI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Bitwise XORs two registers. - /// - /// | Operation | ```$rA = $rB ^ $rC;``` | - /// | Syntax | `xor $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. XOR(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Bitwise XORs a register and an immediate value. - /// - /// | Operation | ```$rA = $rB ^ imm;``` | - /// | Syntax | `xori $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// | Notes | | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - /// #### Execution - /// `$of` and `$err` are cleared. XORI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Set `$rA` to `true` if the `$rC <= tx.input[$rB].maturity`. - /// - /// | Operation | ```$rA = checkinputmaturityverify($rB, $rC);``` | - /// | Syntax | `cimv $rA $rB $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rC > tx.input[$rB].maturity` - /// - the input `$rB` is not of type - /// [`InputType.Coin`](../protocol/tx_format.md) - /// - `$rB > tx.inputsCount` - /// - /// #### Execution - /// Otherwise, advance the program counter `$pc` by `4`. - /// - /// See also: [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and [CLTV](#cltv-check-lock-time-verify). CIMV(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Set `$rA` to `true` if `$rB <= tx.maturity`. - /// - /// | Operation | ```$rA = checktransactionmaturityverify($rB);``` | - /// | Syntax | `ctmv $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB > tx.maturity` - /// - /// #### Execution - /// Otherwise, advance the program counter `$pc` by `4`. - /// - /// See also: [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) and [Bitcoin's Time Locks](https://prestwi.ch/bitcoin-time-locks). CTMV(VirtualRegister, VirtualRegister), - - /// Jumps to the code instruction offset by `imm`. - /// - /// | Operation | ```$pc = $is + imm * 4;``` | - /// | Syntax | `ji imm` | - /// | Encoding | `0x00 i i i i` | - /// - /// #### Panics - /// - `$is + imm * 4 > VM_MAX_RAM - 1` JI(VirtualImmediate24), - - /// Jump to the code instruction offset by `imm` if `$rA` is not equal to - /// `$rB`. - /// - /// | Operation | ```if $rA != $rB:```
```$pc = $is + imm * - /// 4;```
```else:```
```$pc += 4;``` | Syntax | `jnei $rA - /// $rB imm` | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$is + imm * 4 > VM_MAX_RAM - 1` JNEI(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Returns from [context](./main.md#contexts) with value `$rA`. - /// - /// | Operation | ```return($rA);``` - /// | Syntax | `ret $rA` - /// | Encoding | `0x00 rA - - -` - /// - /// If current context is external, cease VM execution and return `$rA`. - /// - /// Returns from contract call, popping the call frame. Before popping: - /// - /// 1. Return the unused forwarded gas to the caller: - /// - `$cgas = $cgas + $fp->$cgas` (add remaining context gas from - /// previous context to current remaining context gas) - /// - /// Then pop the call frame and restoring registers _except_ `$ggas` and - /// `$cgas`. Afterwards, set the following registers: - /// - /// 1. `$pc = $pc + 4` (advance program counter from where we called) RET(VirtualRegister), - - /// Extend the current call frame's stack by an immediate value. - /// - /// | Operation | ```$sp = $sp + imm``` - /// | Syntax | `cfei imm` - /// | Encoding | `0x00 i i i i` - /// | Notes | Does not initialize memory. - /// - /// #### Panics - /// - `$sp + imm` overflows - /// - `$sp + imm > $hp` CFEI(VirtualImmediate24), - - /// Shrink the current call frame's stack by an immediate value. - /// - /// | Operation | ```$sp = $sp - imm``` - /// | Syntax | `cfsi imm` - /// | Encoding | `0x00 i i i i` - /// | Notes | Does not clear memory. - /// - /// #### Panics - /// - `$sp - imm` underflows - /// - `$sp - imm < $ssp` CFSI(VirtualImmediate24), - - /// A byte is loaded from the specified address offset by `imm`. - /// - /// | Operation | ```$rA = MEM[$rB + imm, 1];``` - /// | Syntax | `lb $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + imm + 1` overflows - /// - `$rB + imm + 1 > VM_MAX_RAM` LB(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// A word is loaded from the specified address offset by `imm`. - /// | Operation | ```$rA = MEM[$rB + imm, 8];``` - /// | Syntax | `lw $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + imm + 8` overflows - /// - `$rB + imm + 8 > VM_MAX_RAM` LW(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Allocate a number of bytes from the heap. - /// - /// | Operation | ```$hp = $hp - $rA;``` | - /// | Syntax | `aloc $rA` | - /// | Encoding | `0x00 rA - - -` | - /// | Notes | Does not initialize memory. | - /// - /// #### Panics - /// - `$hp - $rA` underflows - /// - `$hp - $rA < $sp` ALOC(VirtualRegister), - - /// Clear bytes in memory. - /// - /// | Operation | ```MEM[$rA, $rB] = 0;``` | - /// | Syntax | `mcl $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + $rB` overflows - /// - `$rA + $rB > VM_MAX_RAM` - /// - `$rB > MEM_MAX_ACCESS_SIZE` - /// - The memory range `MEM[$rA, $rB]` does not pass [ownership - /// check](./main.md#ownership) MCL(VirtualRegister, VirtualRegister), - - /// Clear bytes in memory. - /// - /// | Operation | ```MEM[$rA, imm] = 0;``` | - /// | Syntax | `mcli $rA, imm` | - /// | Encoding | `0x00 rA i i i` | - /// - /// #### Panics - /// - `$rA + imm` overflows - /// - `$rA + imm > VM_MAX_RAM` - /// - `imm > MEM_MAX_ACCESS_SIZE` - /// - The memory range `MEM[$rA, imm]` does not pass [ownership - /// check](./main.md#ownership) MCLI(VirtualRegister, VirtualImmediate18), - - /// Copy bytes in memory. - /// - /// | Operation | ```MEM[$rA, $rC] = MEM[$rB, $rC];``` | - /// | Syntax | `mcp $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA + $rC` overflows - /// - `$rB + $rC` overflows - /// - `$rA + $rC > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - The memory ranges `MEM[$rA, $rC]` and `MEM[$rB, $rC]` overlap - /// - The memory range `MEM[$rA, $rC]` does not pass [ownership - /// check](./main.md#ownership) MCP(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Compare bytes in memory. - /// - /// | Operation | ```$rA = MEM[$rB, $rD] == MEM[$rC, $rD];``` | - /// | Syntax | `meq $rA, $rB, $rC, $rD` | - /// | Encoding | `0x00 rA rB rC rD` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + $rD` overflows - /// - `$rC + $rD` overflows - /// - `$rB + $rD > VM_MAX_RAM` - /// - `$rC + $rD > VM_MAX_RAM` - /// - `$rD > MEM_MAX_ACCESS_SIZE` MEQ( VirtualRegister, VirtualRegister, VirtualRegister, VirtualRegister, ), - - /// The least significant byte of `$rB` is stored at the address `$rA` - /// offset by `imm`. - /// - /// | Operation | ```MEM[$rA + imm, 1] = $rB[7, 1];``` | - /// | Syntax | `sb $rA, $rB, imm` | - /// | Encoding | `0x00 rA rB i i` | - /// - /// #### Panics - /// - `$rA + imm + 1` overflows - /// - `$rA + imm + 1 > VM_MAX_RAM` - /// - The memory range `MEM[$rA + imm, 1]` does not pass [ownership - /// check](./main.md#ownership) SB(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// The value of `$rB` is stored at the address `$rA` offset by `imm`. - /// - /// | Operation | ```MEM[$rA + imm, 8] = $rB;``` - /// | Syntax | `sw $rA, $rB, imm` - /// | Encoding | `0x00 rA rB i i` - /// - /// #### Panics - /// - `$rA + imm + 8` overflows - /// - `$rA + imm + 8 > VM_MAX_RAM` - /// - The memory range `MEM[$rA + imm, 8]` does not pass [ownership - /// check](./main.md#ownership) SW(VirtualRegister, VirtualRegister, VirtualImmediate12), - - /// Get block header hash. - /// - /// | Operation | ```MEM[$rA, 32] = blockhash($rB);``` | - /// | Syntax | `bhsh $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - /// Block header hashes for blocks with height greater than or equal to - /// current block height are zero (`0x00**32`). BHSH(VirtualRegister, VirtualRegister), - - /// Get Fuel block height. - /// - /// | Operation | ```$rA = blockheight();``` | - /// | Syntax | `bhei $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) BHEI(VirtualRegister), - - /// Burn `$rA` coins of the current contract's color. - /// - /// | Operation | ```burn($rA);``` | - /// | Syntax | `burn $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panic - /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, - /// 32]` minus `$rA` underflows - /// - `$fp == 0` (in the script context) - /// - /// For output with contract ID `MEM[$fp, 32]`, decrease balance of color - /// `MEM[$fp, 32]` by `$rA`. - /// - /// This modifies the `balanceRoot` field of the appropriate output. BURN(VirtualRegister), - - /// Call contract. - /// - /// | Syntax | `call $rA $rB $rC $rD` | - /// | Encoding | `0x00 rA rB rC rD` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rC + 32` overflows - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - Reading past `MEM[VM_MAX_RAM - 1]` - /// - Any output range does not pass [ownership check](./main.md#ownership) - /// - In an external context, if `$rB > MEM[balanceOfStart(MEM[$rC, 32]), - /// 8]` - /// - In an internal context, if `$rB` is greater than the balance of color - /// `MEM[$rC, 32]` of output with contract ID `MEM[$rA, 32]` - /// - /// Register `$rA` is a memory address from which the following fields are - /// set (word-aligned). - /// - /// `$rD` is the amount of gas to forward. If it is set to an amount greater - /// than the available gas, all available gas is forwarded. - /// - /// For output with contract ID `MEM[$rA, 32]`, increase balance of color - /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease - /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, - /// decrease color `MEM[$rC, 32]` balance of output with contract ID - /// `MEM[$fp, 32]` by `$rB`. - /// - /// A [call frame](./main.md#call-frames) is pushed at `$sp`. In addition to - /// filling in the values of the call frame, the following registers are - /// set: - /// - /// 1. `$fp = $sp` (on top of the previous call frame is the beginning of - /// this call frame) 1. Set `$ssp` and `$sp` to the start of the - /// writable stack area of the call frame. 1. Set `$pc` and `$is` to the - /// starting address of the code. 1. `$bal = $rD` (forward coins) - /// 1. `$cgas = $rD` or all available gas (forward gas) - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). CALL( VirtualRegister, VirtualRegister, VirtualRegister, VirtualRegister, ), - - /// Copy `$rD` bytes of code starting at `$rC` for contract with ID equal to - /// the 32 bytes in memory starting at `$rB` into memory starting at `$rA`. - /// - /// | Operation | ```MEM[$rA, $rD] = code($rB, $rC, $rD);``` - /// | Syntax | `ccp $rA, $rB, $rC, $rD` - /// | Encoding | `0x00 rA rB rC rD` - /// | Notes | If `$rD` is greater than the code size, zero bytes are - /// filled in. - /// - /// #### Panics - /// - `$rA + $rD` overflows - /// - `$rB + 32` overflows - /// - `$rA + $rD > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, $rD]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rD > MEM_MAX_ACCESS_SIZE` - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` CCP( VirtualRegister, VirtualRegister, VirtualRegister, VirtualRegister, ), - - /// Set the 32 bytes in memory starting at `$rA` to the code root for - /// contract with ID equal to the 32 bytes in memory starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = coderoot(MEM[$rB, 32]);``` - /// | Syntax | `croo $rA, $rB` - /// | Encoding | `0x00 rA rB - -` - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` - /// - /// Code root compuration is defined - /// [here](../protocol/identifiers.md#contract-id). CROO(VirtualRegister, VirtualRegister), - - /// Set `$rA` to the size of the code for contract with ID equal to the 32 - /// bytes in memory starting at `$rB`. - /// - /// | Operation | ```$rA = codesize(MEM[$rB, 32]);``` - /// | Syntax | `csiz $rA, $rB` - /// | Encoding | `0x00 rA rB - -` - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + 32` overflows - /// - `$rB + 32 > VM_MAX_RAM` - /// - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` CSIZ(VirtualRegister, VirtualRegister), - - /// Get block proposer address. - /// - /// | Operation | ```MEM[$rA, 32] = coinbase();``` | - /// | Syntax | `cb $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) CB(VirtualRegister), - - /// Copy `$rC` bytes of code starting at `$rB` for contract with ID equal to - /// the 32 bytes in memory starting at `$rA` into memory starting at `$ssp`. - /// - /// | Operation | ```MEM[$ssp, $rC] = code($rA, $rB, $rC);``` - /// | Syntax | `ldc $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// | Notes | If `$rC` is greater than the code size, zero bytes are - /// filled in. - /// - /// #### Panics - /// - `$ssp + $rC` overflows - /// - `$rA + 32` overflows - /// - `$ssp + $rC > VM_MAX_RAM` - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$ssp != $sp` - /// - `$ssp + $rC > $hp` - /// - `$rC > CONTRACT_MAX_SIZE` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word - /// alignment. - /// - /// This opcode can be used to concatenate the code of multiple contracts - /// together. It can only be used when the stack area of the call frame is - /// unused (i.e. prior to being used). LDC(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Log an event. This is a no-op. - /// - /// | Operation | ```log($rA, $rB, $rC, $rD);``` | - /// | Syntax | `log $rA, $rB, $rC, $rD` | - /// | Encoding | `0x00 rA rB rC rD` | LOG( VirtualRegister, VirtualRegister, VirtualRegister, VirtualRegister, ), - - /// Mint `$rA` coins of the current contract's color. - /// - /// | Operation | ```mint($rA);``` | - /// | Syntax | `mint $rA` | - /// | Encoding | `0x00 rA - - -` | - /// - /// #### Panics - /// - Balance of color `MEM[$fp, 32]` of output with contract ID `MEM[$fp, - /// 32]` plus `$rA` overflows - /// - `$fp == 0` (in the script context) - /// - /// For output with contract ID `MEM[$fp, 32]`, increase balance of color - /// `MEM[$fp, 32]` by `$rA`. - /// - /// This modifies the `balanceRoot` field of the appropriate output. MINT(VirtualRegister), - - /// Halt execution, reverting state changes and returning value in `$rA`. - /// - /// | Operation | ```revert($rA);``` - /// | Syntax | `rvrt $rA` - /// | Encoding | `0x00 rA - - -` - /// - /// After a revert: - /// - /// 1. All [OutputContract](../protocol/tx_format.md#outputcontract) outputs - /// will have the same `amount` and `stateRoot` as on initialization. 1. - /// All [OutputVariable](../protocol/tx_format.md outputs#outputvariable) - /// outputs will have `to` and `amount` of zero. - /// 1. All [OutputContractConditional](../protocol/tx_format.md# - /// outputcontractconditional) outputs will have `contractID`, `amount`, and - /// `stateRoot` of zero. RVRT(VirtualRegister), - - /// Copy `$rC` bytes of code starting at `$rB` for contract with static - /// index `$rA` into memory starting at `$ssp`. - /// - /// | Operation | ```MEM[$ssp, $rC] = scode($rA, $rB, $rC);``` - /// | Syntax | `sloadcode $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// | Notes | If `$rC` is greater than the code size, zero bytes - /// are filled in. | - /// - /// #### Panics - /// - `$ssp + $rC` overflows - /// - `$ssp + $rC > VM_MAX_RAM` - /// - `$rA >= MAX_STATIC_CONTRACTS` - /// - `$rA` is greater than or equal to `staticContractsCount` for the - /// contract with ID `MEM[$fp, 32]` - /// - `$ssp != $sp` - /// - `$ssp + $rC > $hp` - /// - `$rC > CONTRACT_MAX_SIZE` - /// - `$rC > MEM_MAX_ACCESS_SIZE` - /// - `$fp == 0` (in the script context) - /// - /// Increment `$hp->codesize`, `$ssp`, and `$sp` by `$rC` padded to word - /// alignment. - /// - /// This opcode can be used to concatenate the code of multiple contracts - /// together. It can only be used when the stack area of the call frame is - /// unused (i.e. prior to being used). SLDC(VirtualRegister, VirtualRegister, VirtualRegister), - - /// A word is read from the current contract's state. - /// - /// | Operation | ```$rA = STATE[MEM[$rB, 32]][0, 8];``` | - /// | Syntax | `srw $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | Returns zero if the state element does not exist. | - /// - /// #### Panics - /// - `$rA` is a [reserved register](./main.md#semantics) - /// - `$rB + 32` overflows - /// - `$rB + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SRW(VirtualRegister, VirtualRegister), - - /// 32 bytes is read from the current contract's state. - /// - /// | Operation | ```MEM[$rA, 32] = STATE[MEM[$rB, 32]];``` | - /// | Syntax | `srwx $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// | Notes | Returns zero if the state element does not exist. | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$fp == 0` (in the script context) SRWQ(VirtualRegister, VirtualRegister), - - /// A word is written to the current contract's state. - /// - /// | Operation | ```STATE[MEM[$rA, 32]][0, 8] = $rB;``` | - /// | Syntax | `sww $rA $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SWW(VirtualRegister, VirtualRegister), - - /// 32 bytes is written to the current contract's state. - /// - /// | Operation | ```STATE[MEM[$rA, 32]] = MEM[$rB, 32];``` | - /// | Syntax | `swwx $rA, $rB` | - /// | Encoding | `0x00 rA rB - -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + 32 > VM_MAX_RAM` - /// - `$fp == 0` (in the script context) SWWQ(VirtualRegister, VirtualRegister), - - /// Transfer `$rB` coins with color at `$rC` to contract with ID at `$rA`. - /// - /// | Operation | ```transfer(MEM[$rA, 32], $rB, MEM[$rC, 32]);``` - /// | Syntax | `tr $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns - /// the memory address of `color` balance, or `0` if `color` has no balance. - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rC + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rC + 32 > VM_MAX_RAM` - /// - Contract with ID `MEM[$rA, 32]` is not in `tx.inputs` - /// - In an external context, if `$rB > MEM[balanceOf(MEM[$rC, 32]), 8]` - /// - In an internal context, if `$rB` is greater than the balance of color - /// `MEM[$rC, 32]` of output with contract ID `MEM[$fp, 32]` - /// - `$rB == 0` - /// - /// For output with contract ID `MEM[$rA, 32]`, increase balance of color - /// `MEM[$rC, 32]` by `$rB`. In an external context, decrease - /// `MEM[balanceOfStart(MEM[$rC, 32]), 8]` by `$rB`. In an internal context, - /// decrease color `MEM[$rC, 32]` balance of output with contract ID - /// `MEM[$fp, 32]` by `$rB`. - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). TR(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Transfer `$rC` coins with color at `$rD` to address at `$rA`, with - /// output `$rB`. | Operation | ```transferout(MEM[$rA, 32], $rB, $rC, - /// MEM[$rD, 32]);``` | Syntax | `tro $rA, $rB, $rC, $rD` - /// | Encoding | `0x00 rA rB rC rD` - /// - /// Given helper `balanceOfStart(color: byte[32]) -> uint32` which returns - /// the memory address of `color` balance, or `0` if `color` has no balance. - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rD + 32` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rD + 32 > VM_MAX_RAM` - /// - `$rB > tx.outputsCount` - /// - In an external context, if `$rC > MEM[balanceOf(MEM[$rD, 32]), 8]` - /// - In an internal context, if `$rC` is greater than the balance of color - /// `MEM[$rD, 32]` of output with contract ID `MEM[$fp, 32]` - /// - `$rC == 0` - /// - `tx.outputs[$rB].type != OutputType.Variable` - /// - `tx.outputs[$rB].amount != 0` - /// - /// In an external context, decrease `MEM[balanceOfStart(MEM[$rD, 32]), 8]` - /// by `$rC`. In an internal context, decrease color `MEM[$rD, 32]` balance - /// of output with contract ID `MEM[$fp, 32]` by `$rC`. Then set: - /// - /// - `tx.outputs[$rB].to = MEM[$rA, 32]` - /// - `tx.outputs[$rB].amount = $rC` - /// - `tx.outputs[$rB].color = MEM[$rD, 32]` - /// - /// This modifies the `balanceRoot` field of the appropriate output(s). TRO( VirtualRegister, VirtualRegister, VirtualRegister, VirtualRegister, ), - - /// The 64-byte public key (x, y) recovered from 64-byte - /// signature starting at `$rB` on 32-byte message hash starting at `$rC`. | - /// - /// | Operation | ```MEM[$rA, 64] = ecrecover(MEM[$rB, 64], MEM[$rC, - /// 32]);``` | Syntax | `ecr $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// #### Panics - /// - `$rA + 64` overflows - /// - `$rB + 64` overflows - /// - `$rC + 32` overflows - /// - `$rA + 64 > VM_MAX_RAM` - /// - `$rB + 64 > VM_MAX_RAM` - /// - `$rC + 32 > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 64]` does not pass [ownership - /// check](./main.md#ownership) - /// - /// To get the address, hash the public key with - /// [SHA-2-256](#sha256-sha-2-256). ECR(VirtualRegister, VirtualRegister, VirtualRegister), - - /// The keccak-256 hash of `$rC` bytes starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = keccak256(MEM[$rB, $rC]);``` - /// | Syntax | `k256 $rA, $rB, $rC` - /// | Encoding | `0x00 rA rB rC -` - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + $rC` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rC > MEM_MAX_ACCESS_SIZE` K256(VirtualRegister, VirtualRegister, VirtualRegister), - - /// The SHA-2-256 hash of `$rC` bytes starting at `$rB`. - /// - /// | Operation | ```MEM[$rA, 32] = sha256(MEM[$rB, $rC]);``` | - /// | Syntax | `s256 $rA, $rB, $rC` | - /// | Encoding | `0x00 rA rB rC -` | - /// - /// #### Panics - /// - `$rA + 32` overflows - /// - `$rB + $rC` overflows - /// - `$rA + 32 > VM_MAX_RAM` - /// - `$rB + $rC > VM_MAX_RAM` - /// - The memory range `MEM[$rA, 32]` does not pass [ownership - /// check](./main.md#ownership) - /// - `$rC > MEM_MAX_ACCESS_SIZE` S256(VirtualRegister, VirtualRegister, VirtualRegister), - - /// Performs no operation. - /// - /// | Operation | | - /// | Syntax | `noop` | - /// | Encoding | `0x00 - - - -` | - /// - /// `$of` and `$err` are cleared. NOOP, - - /// Set `$flag` to `$rA`. - /// - /// | Operation | ```$flag = $rA;``` | - /// | Syntax | `flag $rA` | - /// | Encoding | `0x00 rA - - -` | FLAG(VirtualRegister), - - /// Undefined opcode, potentially from inconsistent serialization Undefined, } From 7b87ee05e6c549c8b43f38b13a67f7ebd9daccb2 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 23 May 2021 10:58:46 -0700 Subject: [PATCH 33/62] factor finalized asm out into its own file --- core_lang/src/asm_generation/finalized_asm.rs | 20 ++++++++++++++++ core_lang/src/asm_generation/mod.rs | 15 ++---------- core_lang/src/lib.rs | 24 +++++++------------ 3 files changed, 30 insertions(+), 29 deletions(-) create mode 100644 core_lang/src/asm_generation/finalized_asm.rs diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs new file mode 100644 index 00000000000..7da31305b5b --- /dev/null +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -0,0 +1,20 @@ +use super::InstructionSet; +/// Represents an ASM set which has had register allocation, jump elimination, and optimization +/// applied to it +pub enum FinalizedAsm<'sc> { + ContractAbi, + ScriptMain { + program_section: InstructionSet<'sc>, + }, + PredicateMain { + program_section: InstructionSet<'sc>, + }, + // Libraries do not generate any asm. + Library, +} + +impl<'sc> FinalizedAsm<'sc> { + fn to_bytecode(&self) -> Vec { + todo!() + } +} diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 51d6baa6c18..71715f6034c 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -19,11 +19,13 @@ use either::Either; pub(crate) mod compiler_constants; mod declaration; mod expression; +mod finalized_asm; mod register_sequencer; mod while_loop; pub(crate) use declaration::*; pub(crate) use expression::*; +pub use finalized_asm::FinalizedAsm; pub(crate) use register_sequencer::*; use while_loop::convert_while_loop_to_asm; @@ -676,19 +678,6 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { } } -/// Represents an ASM set which has had register allocation, jump elimination, and optimization -/// applied to it -pub enum FinalizedAsm<'sc> { - ContractAbi, - ScriptMain { - program_section: InstructionSet<'sc>, - }, - PredicateMain { - program_section: InstructionSet<'sc>, - }, - // Libraries do not generate any asm. - Library, -} pub(crate) enum NodeAsmResult<'sc> { JustAsm(Vec>), ReturnStatement { asm: Vec> }, diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index a89d01c32bb..e17ac892730 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -19,7 +19,6 @@ use control_flow_analysis::ControlFlowGraph; use pest::iterators::Pair; use pest::Parser; use semantic_analysis::{TreeType, TypedParseTree}; -use std::collections::HashMap; pub(crate) mod types; pub(crate) mod utils; pub use crate::parse_tree::{Declaration, Expression, UseStatement, WhileLoop}; @@ -119,15 +118,7 @@ pub fn parse<'sc>(input: &'sc str) -> CompileResult<'sc, HllParseTree<'sc>> { } pub enum CompilationResult<'sc> { - ContractAbi { - abi: HashMap>, - warnings: Vec>, - }, - ScriptAsm { - asm: FinalizedAsm<'sc>, - warnings: Vec>, - }, - PredicateAsm { + Success { asm: FinalizedAsm<'sc>, warnings: Vec>, }, @@ -355,17 +346,18 @@ pub fn compile<'sc, 'manifest>( if errors.is_empty() { // TODO move this check earlier and don't compile all of them if there is only one match (predicate_asm, contract_asm, script_asm, library_exports) { - (Some(pred), None, None, o) if o.trees.is_empty() => CompilationResult::PredicateAsm { + (Some(pred), None, None, o) if o.trees.is_empty() => CompilationResult::Success { asm: pred, warnings, }, (None, Some(_contract), None, o) if o.trees.is_empty() => { - CompilationResult::ContractAbi { - abi: Default::default(), - warnings, - } + errors.push(CompileError::Unimplemented( + "Contracts are not implemented yet. ", + Span::new(input, 0, input.len() - 1).unwrap(), + )); + return CompilationResult::Failure { errors, warnings }; } - (None, None, Some(script), o) if o.trees.is_empty() => CompilationResult::ScriptAsm { + (None, None, Some(script), o) if o.trees.is_empty() => CompilationResult::Success { asm: script, warnings, }, From 31e9f29fc92e64e7b475816cf01f37dccc3607e3 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 25 May 2021 15:02:39 -0700 Subject: [PATCH 34/62] realize data section and instructions to bits --- core_lang/Cargo.toml | 2 +- .../src/asm_generation/expression/mod.rs | 2 +- core_lang/src/asm_generation/finalized_asm.rs | 58 +++++++++- core_lang/src/asm_generation/mod.rs | 101 ++++++++++++------ core_lang/src/asm_lang/allocated_ops.rs | 23 +++- core_lang/src/asm_lang/mod.rs | 20 ++-- core_lang/src/asm_lang/virtual_ops.rs | 12 +-- core_lang/src/error.rs | 14 +++ core_lang/src/lib.rs | 57 +++++++++- core_lang/src/parse_tree/literal.rs | 36 ++++--- .../ast_node/expression/typed_expression.rs | 4 +- core_lang/src/types/resolved_type.rs | 9 +- core_lang/src/types/type_info.rs | 62 ++++++++++- forc/src/ops/forc_build.rs | 63 ++--------- 14 files changed, 322 insertions(+), 141 deletions(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index a815339d913..09e121d494f 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,4 +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" } +fuel-asm = { path = "../../fuel-asm" } diff --git a/core_lang/src/asm_generation/expression/mod.rs b/core_lang/src/asm_generation/expression/mod.rs index 36df4cc8ffa..d42791eacd4 100644 --- a/core_lang/src/asm_generation/expression/mod.rs +++ b/core_lang/src/asm_generation/expression/mod.rs @@ -325,7 +325,7 @@ fn convert_literal_to_asm<'sc>( let data_id = namespace.insert_data_value(lit); // then get that literal id and use it to make a load word op vec![Op { - opcode: either::Either::Right(OrganizationalOp::Ld(return_register.clone(), data_id)), + opcode: either::Either::Left(VirtualOp::LW(return_register.clone(), data_id)), comment: "literal instantiation".into(), owning_span: Some(span), }] diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index 7da31305b5b..9bce8fea5d5 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -1,12 +1,17 @@ -use super::InstructionSet; +use super::{DataSection, InstructionSet}; +use crate::asm_lang::virtual_ops::VirtualImmediate12; +use crate::error::*; +use std::io::Write; /// Represents an ASM set which has had register allocation, jump elimination, and optimization /// applied to it pub enum FinalizedAsm<'sc> { ContractAbi, ScriptMain { + data_section: DataSection<'sc>, program_section: InstructionSet<'sc>, }, PredicateMain { + data_section: DataSection<'sc>, program_section: InstructionSet<'sc>, }, // Libraries do not generate any asm. @@ -14,7 +19,54 @@ pub enum FinalizedAsm<'sc> { } impl<'sc> FinalizedAsm<'sc> { - fn to_bytecode(&self) -> Vec { - todo!() + pub(crate) fn to_bytecode(&self) -> CompileResult<'sc, Vec> { + use FinalizedAsm::*; + match self { + ContractAbi | Library => ok(vec![], vec![], vec![]), + ScriptMain { + program_section, + data_section, + } => to_bytecode(program_section, data_section), + PredicateMain { + program_section, + data_section, + } => to_bytecode(program_section, data_section), + } } } + +fn to_bytecode<'sc>( + program_section: &InstructionSet<'sc>, + data_section: &DataSection<'sc>, +) -> CompileResult<'sc, Vec> { + let offset_to_data_section = program_section.ops.len() as u64; + // TODO anything else besides this -- we should not have a 2^12 limit on instructions + if offset_to_data_section > crate::asm_generation::compiler_constants::TWELVE_BITS { + return err( + vec![], + vec![CompileError::TooManyInstructions { + span: pest::Span::new(" ", 0, 0).unwrap(), + }], + ); + } + + let offset_to_data_section = VirtualImmediate12::new_unchecked( + offset_to_data_section, + "this was manually checked with [CompileError::TooManyInstructions] above. ", + ); + + // each op is four bytes, so the length of the buf is then number of ops times four. + let mut buf = vec![0; program_section.ops.len() * 4]; + + for (ix, op) in program_section.ops.iter().enumerate() { + let mut op = op.to_fuel_asm(&offset_to_data_section, data_section); + op.write(&buf[ix * 4..]) + .expect("Failed to write to in-memory buffer."); + } + + let mut data_section = data_section.serialize_to_bytes(); + + buf.append(&mut data_section); + + ok(buf, vec![], vec![]) +} diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 71715f6034c..48c9eb7378d 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -180,7 +180,7 @@ impl<'sc> AbstractInstructionSet<'sc> { /// 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> { + fn realize_labels(self) -> RealizedAbstractInstructionSet<'sc> { let mut label_namespace: HashMap<&Label, u64> = Default::default(); let mut counter = 0; for op in &self.ops { @@ -189,7 +189,6 @@ impl<'sc> AbstractInstructionSet<'sc> { 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(_) => { @@ -213,19 +212,6 @@ impl<'sc> AbstractInstructionSet<'sc> { 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( @@ -271,7 +257,6 @@ 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, @@ -337,6 +322,27 @@ pub struct DataSection<'sc> { value_pairs: Vec>, } +impl<'sc> DataSection<'sc> { + /// Given a [DataId], calculate the offset _from the beginning of the data section_ to the data + /// in words. + pub(crate) fn offset_to_id(&self, id: &DataId) -> usize { + self.value_pairs + .iter() + .take(id.0 as usize) + .map(|x| x.as_type().stack_size_of()) + .sum::() as usize + } + + pub(crate) fn serialize_to_bytes(&self) -> Vec { + // not the exact right capacity but serves as a lower bound + let mut buf = Vec::with_capacity(self.value_pairs.len()); + for val in &self.value_pairs { + buf.append(&mut val.to_bytes()); + } + buf + } +} + impl fmt::Display for DataSection<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut data_buf = String::new(); @@ -406,11 +412,17 @@ impl fmt::Display for JumpOptimizedAsmSet<'_> { impl<'sc> fmt::Display for RegisterAllocatedAsmSet<'sc> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RegisterAllocatedAsmSet::ScriptMain { program_section } => { - write!(f, "{}", program_section) + RegisterAllocatedAsmSet::ScriptMain { + program_section, + data_section, + } => { + write!(f, "{}\n{}", program_section, data_section) } - RegisterAllocatedAsmSet::PredicateMain { program_section } => { - write!(f, "{}", program_section) + RegisterAllocatedAsmSet::PredicateMain { + program_section, + data_section, + } => { + write!(f, "{}\n{}", program_section, data_section) } RegisterAllocatedAsmSet::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") @@ -424,8 +436,14 @@ impl<'sc> fmt::Display for RegisterAllocatedAsmSet<'sc> { impl<'sc> fmt::Display for FinalizedAsm<'sc> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FinalizedAsm::ScriptMain { program_section } => write!(f, "{}", program_section,), - FinalizedAsm::PredicateMain { program_section } => write!(f, "{}", program_section,), + FinalizedAsm::ScriptMain { + program_section, + data_section, + } => write!(f, "{}\n{}", program_section, data_section), + FinalizedAsm::PredicateMain { + program_section, + data_section, + } => write!(f, "{}\n{}", program_section, data_section), FinalizedAsm::ContractAbi { .. } => { write!(f, "TODO contract ABI asm is unimplemented") } @@ -530,18 +548,25 @@ pub(crate) fn compile_ast_to_asm<'sc>( let mut namespace: AsmNamespace = Default::default(); let mut asm_buf = vec![]; // start generating from the main function + let return_register = register_sequencer.next(); let mut body = type_check!( convert_code_block_to_asm( &main_function.body, &mut namespace, &mut register_sequencer, - None, + // TODO validate that this isn't just implicit returns? + Some(&return_register), ), vec![], warnings, errors ); asm_buf.append(&mut body); + asm_buf.push(Op { + owning_span: None, + opcode: Either::Left(VirtualOp::RET(return_register)), + comment: "main fn return value".into(), + }); HllAsmSet::ScriptMain { program_section: AbstractInstructionSet { ops: asm_buf }, @@ -617,18 +642,18 @@ impl<'sc> JumpOptimizedAsmSet<'sc> { data_section, program_section, } => RegisterAllocatedAsmSet::ScriptMain { + data_section, program_section: program_section .clone() - .realize_labels(&data_section) + .realize_labels() .allocate_registers(), }, JumpOptimizedAsmSet::PredicateMain { data_section, program_section, } => RegisterAllocatedAsmSet::PredicateMain { - program_section: program_section - .realize_labels(&data_section) - .allocate_registers(), + data_section, + program_section: program_section.realize_labels().allocate_registers(), }, JumpOptimizedAsmSet::ContractAbi => RegisterAllocatedAsmSet::ContractAbi, } @@ -653,9 +678,11 @@ pub enum JumpOptimizedAsmSet<'sc> { pub enum RegisterAllocatedAsmSet<'sc> { ContractAbi, ScriptMain { + data_section: DataSection<'sc>, program_section: InstructionSet<'sc>, }, PredicateMain { + data_section: DataSection<'sc>, program_section: InstructionSet<'sc>, }, // Libraries do not generate any asm. @@ -667,12 +694,20 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { // TODO implement this -- noop for now match self { RegisterAllocatedAsmSet::Library => FinalizedAsm::Library, - RegisterAllocatedAsmSet::ScriptMain { program_section } => { - FinalizedAsm::ScriptMain { program_section } - } - RegisterAllocatedAsmSet::PredicateMain { program_section } => { - FinalizedAsm::PredicateMain { program_section } - } + RegisterAllocatedAsmSet::ScriptMain { + program_section, + data_section, + } => FinalizedAsm::ScriptMain { + program_section, + data_section, + }, + RegisterAllocatedAsmSet::PredicateMain { + program_section, + data_section, + } => FinalizedAsm::PredicateMain { + program_section, + data_section, + }, RegisterAllocatedAsmSet::ContractAbi => FinalizedAsm::ContractAbi, } } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 9456cf7fbef..4d64db78b85 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -10,6 +10,9 @@ //! best type safety. It can be macro'd someday. use super::virtual_ops::*; +use super::DataId; +use crate::asm_generation::DataSection; +use fuel_asm::Opcode as VmOp; use pest::Span; use std::fmt; @@ -86,7 +89,7 @@ pub(crate) enum AllocatedOpcode { CFEI(VirtualImmediate24), CFSI(VirtualImmediate24), LB(AllocatedRegister, AllocatedRegister, VirtualImmediate12), - LW(AllocatedRegister, AllocatedRegister, VirtualImmediate12), + LW(AllocatedRegister, DataId), ALOC(AllocatedRegister), MCL(AllocatedRegister, AllocatedRegister), MCLI(AllocatedRegister, VirtualImmediate18), @@ -195,7 +198,7 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { 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), + LW(a, b) => format!("lw {} {}", a, b), ALOC(a) => format!("aloc {}", a), MCL(a, b) => format!("mcl {} {}", a, b), MCLI(a, b) => format!("mcli {} {}", a, b), @@ -244,8 +247,11 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { } impl<'sc> AllocatedOp<'sc> { - fn to_fuel_asm(&self) -> fuel_asm::Opcode { - use fuel_asm::Opcode as VmOp; + pub(crate) fn to_fuel_asm( + &self, + offset_to_data_section: &VirtualImmediate12, + data_section: &DataSection, + ) -> fuel_asm::Opcode { use AllocatedOpcode::*; #[rustfmt::skip] let fuel_op = match &self.opcode { @@ -285,7 +291,7 @@ impl<'sc> AllocatedOp<'sc> { CFEI(a) => VmOp::CFEI(a.value), CFSI(a) => VmOp::CFSI(a.value), LB (a, b, c) => VmOp::LB (a.to_register_id(), b.to_register_id(), c.value), - LW (a, b, c) => VmOp::LW (a.to_register_id(), b.to_register_id(), c.value), + LW (a, b) => realize_lw(a, b, offset_to_data_section, data_section), ALOC(a) => VmOp::ALOC(a.to_register_id()), MCL (a, b) => VmOp::MCL (a.to_register_id(), b.to_register_id()), MCLI(a, b) => VmOp::MCLI(a.to_register_id(), b.value), @@ -322,3 +328,10 @@ impl<'sc> AllocatedOp<'sc> { fuel_op } } + +fn realize_lw(dest: &AllocatedRegister, data_id: &DataId, offset_to_data_section: &VirtualImmediate12, data_section: &DataSection) -> VmOp { + let dest = dest.to_register_id(); + let offset = data_section.offset_to_id(data_id); + // Calculate how many words of data there are in the dta section before this item + VmOp::LW (dest, offset, offset_to_data_section.value) +} diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index dd2388f150b..e9cec348774 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -124,7 +124,7 @@ impl<'sc> Op<'sc> { comment: impl Into, ) -> Self { Op { - opcode: Either::Right(OrganizationalOp::Ld(reg, data)), + opcode: Either::Left(VirtualOp::LW(reg, data)), comment: comment.into(), owning_span: None, } @@ -576,13 +576,10 @@ impl<'sc> Op<'sc> { 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) + errors.push(CompileError::DisallowedLw { + span: name.span.clone(), + }); + return err(warnings, errors); } "aloc" => { let r1 = type_check!( @@ -1248,7 +1245,7 @@ impl fmt::Display for Op<'_> { 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), + LW(a, b) => format!("lw {} {}", a, b), ALOC(a) => format!("aloc {}", a), MCL(a, b) => format!("mcl {} {}", a, b), MCLI(a, b) => format!("mcli {} {}", a, b), @@ -1286,7 +1283,6 @@ impl fmt::Display for Op<'_> { Label(l) => format!("{}", l), Comment => "".into(), Jump(label) => format!("jump {}", label), - Ld(register, data_id) => format!("ld {} {}", register, data_id), JumpIfNotEq(reg0, reg1, label) => format!("jnei {} {} {}", reg0, reg1, label), }, }; @@ -1316,9 +1312,6 @@ pub(crate) enum OrganizationalOp { Jump(Label), // Jumps to a label JumpIfNotEq(VirtualRegister, VirtualRegister, Label), - // Loads from the data section into a register - // "load data" - Ld(VirtualRegister, DataId), } impl OrganizationalOp { @@ -1326,7 +1319,6 @@ impl OrganizationalOp { use OrganizationalOp::*; (match self { Label(_) | Comment | Jump(_) => vec![], - Ld(r1, _) => vec![r1], JumpIfNotEq(r1, r2, _) => vec![r1, r2], }) .into_iter() diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index bfa04f25991..e919a8e5fcb 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -6,7 +6,7 @@ use super::{ allocated_ops::{AllocatedOpcode, AllocatedRegister}, - RealizedOp, + DataId, RealizedOp, }; use crate::asm_generation::RegisterPool; use crate::error::*; @@ -279,7 +279,7 @@ pub(crate) enum VirtualOp { CFEI(VirtualImmediate24), CFSI(VirtualImmediate24), LB(VirtualRegister, VirtualRegister, VirtualImmediate12), - LW(VirtualRegister, VirtualRegister, VirtualImmediate12), + LW(VirtualRegister, DataId), ALOC(VirtualRegister), MCL(VirtualRegister, VirtualRegister), MCLI(VirtualRegister, VirtualImmediate18), @@ -379,7 +379,7 @@ impl VirtualOp { CFEI(_imm) => vec![], CFSI(_imm) => vec![], LB(r1, r2, _i) => vec![r1, r2], - LW(r1, r2, _i) => vec![r1, r2], + LW(r1, _i) => vec![r1], ALOC(_imm) => vec![], MCL(r1, r2) => vec![r1, r2], MCLI(r1, _imm) => vec![r1], @@ -607,11 +607,7 @@ impl VirtualOp { map_reg(&mapping, reg2), imm.clone(), ), - LW(reg1, reg2, imm) => AllocatedOpcode::LW( - map_reg(&mapping, reg1), - map_reg(&mapping, reg2), - imm.clone(), - ), + LW(reg1, imm) => AllocatedOpcode::LW(map_reg(&mapping, reg1), imm.clone()), ALOC(reg) => AllocatedOpcode::ALOC(map_reg(&mapping, reg)), MCL(reg1, reg2) => { AllocatedOpcode::MCL(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) diff --git a/core_lang/src/error.rs b/core_lang/src/error.rs index b559e6a95ff..46ed64f3fa6 100644 --- a/core_lang/src/error.rs +++ b/core_lang/src/error.rs @@ -605,6 +605,10 @@ pub enum CompileError<'sc> { "The opcode \"ji\" is not valid in inline assembly. Try using function calls instead." )] DisallowedJi { span: Span<'sc> }, + #[error( + "The opcode \"lw\" is not valid in inline assembly. Try assigning a static value to a variable instead." + )] + DisallowedLw { span: Span<'sc> }, #[error( "This op expects {expected} registers as arguments, but you provided {received} registers." )] @@ -617,6 +621,12 @@ pub enum CompileError<'sc> { 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> }, + #[error("This value is not valid within a \"str\" type.")] + InvalidStrType { raw: &'sc str, span: Span<'sc> }, + #[error("Unknown type name.")] + UnknownType { span: Span<'sc> }, + #[error("Bytecode can only support programs with up to 2^12 words worth of opcodes. Try refactoring into contract calls? This is a temporary error and will be implemented in the future.")] + TooManyInstructions { span: Span<'sc> }, } impl<'sc> std::convert::From> for CompileError<'sc> { @@ -763,9 +773,13 @@ impl<'sc> CompileError<'sc> { Immediate24TooLarge { span, .. } => span, DisallowedJnei { span, .. } => span, DisallowedJi { span, .. } => span, + DisallowedLw { span, .. } => span, IncorrectNumberOfAsmRegisters { span, .. } => span, UnnecessaryImmediate { span, .. } => span, AmbiguousPath { span, .. } => span, + UnknownType { span, .. } => span, + InvalidStrType { span, .. } => span, + TooManyInstructions { span, .. } => span, } } diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index e17ac892730..c0bac1e2ac9 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -131,6 +131,19 @@ pub enum CompilationResult<'sc> { errors: Vec>, }, } +pub enum BytecodeCompilationResult<'sc> { + Success { + bytes: Vec, + warnings: Vec>, + }, + Library { + warnings: Vec>, + }, + Failure { + warnings: Vec>, + errors: Vec>, + }, +} fn get_start(err: &pest::error::Error) -> usize { match err.location { @@ -146,7 +159,7 @@ fn get_end(err: &pest::error::Error) -> usize { } } -pub fn compile<'sc, 'manifest>( +pub fn compile_to_asm<'sc, 'manifest>( input: &'sc str, initial_namespace: &Namespace<'sc>, ) -> CompilationResult<'sc> { @@ -375,6 +388,48 @@ pub fn compile<'sc, 'manifest>( CompilationResult::Failure { errors, warnings } } } +pub fn compile_to_bytecode<'sc, 'manifest>( + input: &'sc str, + initial_namespace: &Namespace<'sc>, +) -> BytecodeCompilationResult<'sc> { + match compile_to_asm(input, initial_namespace) { + CompilationResult::Success { asm, mut warnings } => { + let bytes = match asm.to_bytecode() { + CompileResult::Ok { + value, + warnings: mut l_w, + errors, + } if errors.is_empty() => { + warnings.append(&mut l_w); + value + } + CompileResult::Ok { + value, + warnings: mut l_w, + errors, + } => { + warnings.append(&mut l_w); + return BytecodeCompilationResult::Failure { warnings, errors }; + } + CompileResult::Err { + warnings: mut l_w, + errors, + } => { + warnings.append(&mut l_w); + return BytecodeCompilationResult::Failure { warnings, errors }; + } + }; + BytecodeCompilationResult::Success { bytes, warnings } + } + CompilationResult::Failure { warnings, errors } => { + BytecodeCompilationResult::Failure { warnings, errors } + } + CompilationResult::Library { + warnings, + exports: _exports, + } => BytecodeCompilationResult::Library { warnings }, + } +} fn perform_control_flow_analysis<'sc>( tree: &Option>, diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index a601cd4b754..5b09124bd1b 100644 --- a/core_lang/src/parse_tree/literal.rs +++ b/core_lang/src/parse_tree/literal.rs @@ -1,6 +1,7 @@ use crate::asm_lang::virtual_ops::VirtualImmediate12; use crate::error::*; use crate::parser::Rule; +use crate::types::{IntegerBits, ResolvedType}; use crate::CompileError; use pest::iterators::Pair; use pest::Span; @@ -19,22 +20,17 @@ 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 + pub(crate) fn as_type(&self) -> ResolvedType<'sc> { 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"), + U8(_) => ResolvedType::UnsignedInteger(IntegerBits::Eight), + U16(_) => ResolvedType::UnsignedInteger(IntegerBits::Sixteen), + U32(_) => ResolvedType::UnsignedInteger(IntegerBits::ThirtyTwo), + U64(_) => ResolvedType::UnsignedInteger(IntegerBits::SixtyFour), + String(inner) => ResolvedType::Str(inner.len() as u64), + Boolean(_) => ResolvedType::Boolean, + Byte(_) => ResolvedType::Byte, + Byte32(_) => ResolvedType::Byte32, } } pub(crate) fn parse_from_pair(lit: Pair<'sc, Rule>) -> CompileResult<'sc, (Self, Span<'sc>)> { @@ -149,6 +145,18 @@ impl<'sc> Literal<'sc> { Err(compile_err) => err(Vec::new(), vec![compile_err]), } } + pub(crate) fn to_bytes(&self) -> Vec { + use Literal::*; + match self { + // TODO are we big endian? + U8(val) => val.to_be_bytes().to_vec(), + U16(val) => val.to_be_bytes().to_vec(), + U32(val) => val.to_be_bytes().to_vec(), + U64(val) => val.to_be_bytes().to_vec(), + Boolean(b) => (if *b { 1u64 } else { 0u64 }).to_be_bytes().to_vec(), + a => todo!("{:?}", a), + } + } } fn parse_hex_from_pair<'sc>(pair: Pair<'sc, Rule>) -> Result, CompileError<'sc>> { 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 eb1872a8783..079607fa4c9 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 @@ -36,7 +36,9 @@ impl<'sc> TypedExpression<'sc> { let mut typed_expression = match other { Expression::Literal { value: lit, span } => { let return_type = match lit { - Literal::String(_) => MaybeResolvedType::Resolved(ResolvedType::String), + Literal::String(s) => { + MaybeResolvedType::Resolved(ResolvedType::Str(s.len() as u64)) + } Literal::U8(_) => MaybeResolvedType::Resolved(ResolvedType::UnsignedInteger( IntegerBits::Eight, )), diff --git a/core_lang/src/types/resolved_type.rs b/core_lang/src/types/resolved_type.rs index 8a237d759da..f09697f9509 100644 --- a/core_lang/src/types/resolved_type.rs +++ b/core_lang/src/types/resolved_type.rs @@ -15,7 +15,8 @@ pub enum MaybeResolvedType<'sc> { } #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum ResolvedType<'sc> { - String, + /// The number in a `Str` represents its size, which must be known at compile time + Str(u64), UnsignedInteger(IntegerBits), Boolean, Unit, @@ -185,7 +186,7 @@ impl<'sc> ResolvedType<'sc> { pub(crate) fn friendly_type_str(&self) -> String { use ResolvedType::*; match self { - String => "String".into(), + Str(len) => format!("str[{}]", len), UnsignedInteger(bits) => { use IntegerBits::*; match bits { @@ -218,8 +219,8 @@ impl<'sc> ResolvedType<'sc> { /// This is _in words_! pub(crate) fn stack_size_of(&self) -> u64 { match self { - // the pointer to the beginning of the string is 64 bits - ResolvedType::String => 1, + // Each char is a word, so the size is the num of characters + ResolvedType::Str(len) => *len, // Since things are unpacked, all unsigned integers are 64 bits.....for now ResolvedType::UnsignedInteger(_) => 1, ResolvedType::Boolean => 1, diff --git a/core_lang/src/types/type_info.rs b/core_lang/src/types/type_info.rs index edc02c142a7..7363f53adad 100644 --- a/core_lang/src/types/type_info.rs +++ b/core_lang/src/types/type_info.rs @@ -3,11 +3,12 @@ use crate::error::*; use crate::types::PartiallyResolvedType; use crate::{Ident, Rule}; use pest::iterators::Pair; +use std::iter::FromIterator; /// Type information without an associated value, used for type inferencing and definition. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TypeInfo<'sc> { - String, + Str(u64), UnsignedInteger(IntegerBits), Boolean, /// A custom type could be a struct or similar if the name is in scope, @@ -47,7 +48,7 @@ impl<'sc> TypeInfo<'sc> { more details." ), TypeInfo::Boolean => MaybeResolvedType::Resolved(ResolvedType::Boolean), - TypeInfo::String => MaybeResolvedType::Resolved(ResolvedType::String), + TypeInfo::Str(len) => MaybeResolvedType::Resolved(ResolvedType::Str(*len)), TypeInfo::UnsignedInteger(bits) => { MaybeResolvedType::Resolved(ResolvedType::UnsignedInteger(*bits)) } @@ -72,12 +73,17 @@ impl<'sc> TypeInfo<'sc> { "u32" => TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo), "u64" => TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), "bool" => TypeInfo::Boolean, - "string" => TypeInfo::String, "unit" => TypeInfo::Unit, "byte" => TypeInfo::Byte, "byte32" => TypeInfo::Byte32, "Self" | "self" => TypeInfo::SelfType, "()" => TypeInfo::Unit, + a if a.contains("str[") => type_check!( + parse_str_type(a, input.as_span()), + return err(warnings, errors), + warnings, + errors + ), _other => TypeInfo::Custom { name: eval!( Ident::parse_from_pair, @@ -93,3 +99,53 @@ impl<'sc> TypeInfo<'sc> { ) } } + +fn parse_str_type<'sc>(raw: &'sc str, span: pest::Span<'sc>) -> CompileResult<'sc, TypeInfo<'sc>> { + if raw.starts_with("str[") { + let mut rest = raw.split_at("str[".len()).1.chars().collect::>(); + if let Some(']') = rest.pop() { + if let Ok(num) = String::from_iter(rest).parse() { + return ok(TypeInfo::Str(num), vec![], vec![]); + } + } + return err(vec![], vec![CompileError::InvalidStrType { raw, span }]); + } + return err(vec![], vec![CompileError::UnknownType { span }]); +} + +#[test] +fn test_str_parse() { + match parse_str_type("str[20]", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Ok { value, .. } if value == TypeInfo::Str(20) => (), + _ => panic!("failed test"), + } + match parse_str_type("str[]", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + match parse_str_type("str[ab]", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + match parse_str_type("str [ab]", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + + match parse_str_type("not even a str[ type", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + match parse_str_type("", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + match parse_str_type("20", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } + match parse_str_type("[20]", pest::Span::new("", 0, 0).unwrap()) { + CompileResult::Err { .. } => (), + _ => panic!("failed test"), + } +} diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 23734d5b778..7f329786c55 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -7,7 +7,7 @@ use std::io::{self, Write}; use termcolor::{BufferWriter, Color as TermColor, ColorChoice, ColorSpec, WriteColor}; use crate::utils::manifest::{Dependency, DependencyDetails, Manifest}; -use core_lang::{CompilationResult, LibraryExports, Namespace}; +use core_lang::{BytecodeCompilationResult, CompilationResult, LibraryExports, Namespace}; use std::{fs, path::PathBuf}; pub fn build(path: Option) -> Result<(), String> { @@ -44,7 +44,7 @@ pub fn build(path: Option) -> Result<(), String> { let main_file = get_main_file(&manifest, &manifest_dir)?; let main = compile(main_file, &manifest.project.name, &namespace)?; - println!("Generated assembly was: \n{}", main); + println!("Bytecode size is {} bytes.", main.len() / 4); Ok(()) } @@ -150,7 +150,7 @@ fn compile_library<'source, 'manifest>( proj_name: &str, namespace: &Namespace<'source>, ) -> Result, String> { - let res = core_lang::compile(&source, namespace); + let res = core_lang::compile_to_asm(&source, namespace); match res { CompilationResult::Library { exports, warnings } => { for ref warning in warnings.iter() { @@ -202,10 +202,10 @@ fn compile<'source, 'manifest>( source: &'source str, proj_name: &str, namespace: &Namespace<'source>, -) -> Result, String> { - let res = core_lang::compile(&source, namespace); +) -> Result, String> { + let res = core_lang::compile_to_bytecode(&source, namespace); match res { - CompilationResult::ScriptAsm { asm, warnings } => { + BytecodeCompilationResult::Success { bytes, warnings } => { for ref warning in warnings.iter() { format_warning(&source, warning); } @@ -223,52 +223,9 @@ fn compile<'source, 'manifest>( } )); } - Ok(asm) + Ok(bytes) } - CompilationResult::PredicateAsm { asm, warnings } => { - for ref warning in warnings.iter() { - format_warning(&source, warning); - } - if warnings.is_empty() { - let _ = write_green(&format!("Compiled predicate {:?}.", proj_name)); - } else { - let _ = write_yellow(&format!( - "Compiled predicate {:?} with {} {}.", - proj_name, - warnings.len(), - if warnings.len() > 1 { - "warnings" - } else { - "warning" - } - )); - } - Ok(asm) - } - CompilationResult::ContractAbi { abi: _, warnings } => { - for ref warning in warnings.iter() { - format_warning(&source, warning); - } - if warnings.is_empty() { - let _ = write_green(&format!("Compiled contract {:?}.", proj_name)); - } else { - let _ = write_yellow(&format!( - "Compiled contract {:?} with {} {}.", - proj_name, - warnings.len(), - if warnings.len() > 1 { - "warnings" - } else { - "warning" - } - )); - } - Ok(core_lang::FinalizedAsm::ContractAbi) - } - CompilationResult::Library { - exports: _, - warnings, - } => { + BytecodeCompilationResult::Library { warnings } => { for ref warning in warnings.iter() { format_warning(&source, warning); } @@ -286,9 +243,9 @@ fn compile<'source, 'manifest>( } )); } - Ok(core_lang::FinalizedAsm::Library) + Ok(vec![]) } - CompilationResult::Failure { errors, warnings } => { + BytecodeCompilationResult::Failure { errors, warnings } => { let e_len = errors.len(); for ref warning in warnings.iter() { From c39141a28ee0a0514d68f4e6a122a1b75eeb0122 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 25 May 2021 20:12:58 -0700 Subject: [PATCH 35/62] data section offset label --- core_lang/src/asm_generation/mod.rs | 93 +++++++++++++++++++++---- core_lang/src/asm_lang/allocated_ops.rs | 3 + core_lang/src/asm_lang/mod.rs | 8 ++- core_lang/src/asm_lang/virtual_ops.rs | 4 +- 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 48c9eb7378d..0dea665c830 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -1,4 +1,7 @@ -use std::{collections::HashMap, fmt}; +use std::{ + collections::{HashMap, VecDeque}, + fmt, +}; use crate::{ asm_lang::{ @@ -195,6 +198,11 @@ impl<'sc> AbstractInstructionSet<'sc> { counter += 1; } Either::Right(OrganizationalOp::Comment) => (), + Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder) => { + // If the placeholder is 32 bits, this is 1. if 64, this should be 2. We use LW + // to load the data, which loads a whole word, so for now this is 2. + counter += 2 + } } } @@ -236,6 +244,13 @@ impl<'sc> AbstractInstructionSet<'sc> { comment, }); } + OrganizationalOp::DataSectionOffsetPlaceholder => { + realized_ops.push(RealizedOp { + opcode: VirtualOp::DataSectionOffsetPlaceholder, + owning_span: None, + comment: String::new(), + }); + } OrganizationalOp::Comment => continue, OrganizationalOp::Label(..) => continue, }, @@ -256,7 +271,8 @@ pub(crate) struct RegisterPool { impl RegisterPool { fn init() -> Self { let register_pool: Vec = (0 - ..compiler_constants::NUM_FREE_REGISTERS) + // - 1 because we reserve the final register for the data_section begin + ..compiler_constants::NUM_FREE_REGISTERS - 1) .map(|x| RegisterAllocationStatus { reg: AllocatedRegister::Allocated(x), in_use: None, @@ -546,7 +562,7 @@ pub(crate) fn compile_ast_to_asm<'sc>( let asm = match ast { TypedParseTree::Script { main_function, .. } => { let mut namespace: AsmNamespace = Default::default(); - let mut asm_buf = vec![]; + let mut asm_buf = build_preamble(&mut register_sequencer); // start generating from the main function let return_register = register_sequencer.next(); let mut body = type_check!( @@ -695,19 +711,41 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { match self { RegisterAllocatedAsmSet::Library => FinalizedAsm::Library, RegisterAllocatedAsmSet::ScriptMain { - program_section, - data_section, - } => FinalizedAsm::ScriptMain { - program_section, + mut program_section, data_section, - }, + } => { + // ensure there's an even number of ops so the + // data section offset is valid + if program_section.ops.len() % 2 != 0 { + program_section.ops.push(AllocatedOp { + opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, + comment: "word-alignment of data section".into(), + owning_span: None, + }); + } + FinalizedAsm::ScriptMain { + program_section, + data_section, + } + } RegisterAllocatedAsmSet::PredicateMain { - program_section, + mut program_section, data_section, - } => FinalizedAsm::PredicateMain { - program_section, - data_section, - }, + } => { + // ensure there's an even number of ops so the + // data section offset is valid + if program_section.ops.len() % 2 != 0 { + program_section.ops.push(AllocatedOp { + opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, + comment: "word-alignment of data section".into(), + owning_span: None, + }); + } + FinalizedAsm::PredicateMain { + program_section, + data_section, + } + } RegisterAllocatedAsmSet::ContractAbi => FinalizedAsm::ContractAbi, } } @@ -776,3 +814,32 @@ fn convert_node_to_asm<'sc>( } } } + +/// Builds the asm preamble, which includes metadata and a jump past the metadata. +/// Right now, it looks like this: +/// +/// WORD OP +/// 1 JI program_start +/// - NOOP +/// 2 DATA_START (0-32) +/// - DATA_START (32-64) +/// .program_start: +fn build_preamble(register_sequencer: &mut RegisterSequencer) -> Vec> { + let mut buf = Vec::new(); + let label = register_sequencer.get_label(); + buf.push(Op::jump_to_label(label.clone())); + buf.push(Op { + opcode: Either::Left(VirtualOp::NOOP), + comment: "".into(), + owning_span: None, + }); + + buf.push(Op { + opcode: Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder), + comment: "data section offset".into(), + owning_span: None, + }); + + buf.push(Op::unowned_jump_label_comment(label, "end of metadata")); + buf +} diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 4d64db78b85..e914201a706 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -147,6 +147,7 @@ pub(crate) enum AllocatedOpcode { NOOP, FLAG(AllocatedRegister), Undefined, + DataSectionOffsetPlaceholder, } #[derive(Clone)] @@ -231,6 +232,7 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { NOOP => "noop".to_string(), FLAG(a) => format!("flag {}", a), Undefined => format!("undefined op"), + DataSectionOffsetPlaceholder => "data section offset placeholder".into() }; // 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 @@ -324,6 +326,7 @@ impl<'sc> AllocatedOp<'sc> { NOOP => VmOp::NOOP, FLAG(a) => VmOp::FLAG(a.to_register_id()), Undefined => VmOp::Undefined, + DataSectionOffsetPlaceholder => todo!() }; fuel_op } diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index e9cec348774..3ee2de40d96 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1278,12 +1278,16 @@ impl fmt::Display for Op<'_> { NOOP => "noop".to_string(), FLAG(a) => format!("flag {}", a), Undefined => format!("undefined op"), + VirtualOp::DataSectionOffsetPlaceholder => "data section offset placeholder".into(), }, Either::Right(opcode) => match opcode { Label(l) => format!("{}", l), Comment => "".into(), Jump(label) => format!("jump {}", label), JumpIfNotEq(reg0, reg1, label) => format!("jnei {} {} {}", reg0, reg1, label), + OrganizationalOp::DataSectionOffsetPlaceholder => { + format!("data section offset placeholder") + } }, }; // we want the comment to always be 40 characters offset to the right @@ -1312,13 +1316,15 @@ pub(crate) enum OrganizationalOp { Jump(Label), // Jumps to a label JumpIfNotEq(VirtualRegister, VirtualRegister, Label), + // placeholder for the DataSection offset to go + DataSectionOffsetPlaceholder, } impl OrganizationalOp { pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { use OrganizationalOp::*; (match self { - Label(_) | Comment | Jump(_) => vec![], + Label(_) | Comment | Jump(_) | DataSectionOffsetPlaceholder => vec![], JumpIfNotEq(r1, r2, _) => vec![r1, r2], }) .into_iter() diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index e919a8e5fcb..15ecadae03d 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -337,6 +337,7 @@ pub(crate) enum VirtualOp { NOOP, FLAG(VirtualRegister), Undefined, + DataSectionOffsetPlaceholder, } impl VirtualOp { @@ -411,7 +412,7 @@ impl VirtualOp { S256(r1, r2, r3) => vec![r1, r2, r3], NOOP => vec![], FLAG(r1) => vec![r1], - Undefined => vec![], + Undefined | DataSectionOffsetPlaceholder => vec![], }) .into_iter() .collect() @@ -717,6 +718,7 @@ impl VirtualOp { NOOP => AllocatedOpcode::NOOP, FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), Undefined => AllocatedOpcode::Undefined, + DataSectionOffsetPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder, } } } From 5c212594d136e19401b8e9ec5bba692485f7377a Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 13:44:13 -0700 Subject: [PATCH 36/62] initial bytecode generation --- .../src/asm_generation/compiler_constants.rs | 5 +++ core_lang/src/asm_generation/finalized_asm.rs | 35 +++++++++---------- core_lang/src/asm_generation/mod.rs | 28 +++++++++++++-- core_lang/src/asm_lang/allocated_ops.rs | 34 +++++++++++++----- core_lang/src/asm_lang/mod.rs | 4 +++ core_lang/src/asm_lang/virtual_ops.rs | 18 ++++++++++ 6 files changed, 94 insertions(+), 30 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 5c7dbbd649c..8428d39b886 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -2,3 +2,8 @@ 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; + +#[allow(non_snake_case)] +pub(crate) const fn DATA_SECTION_REGISTER() -> u8 { + NUM_FREE_REGISTERS - 2 +} diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index 9bce8fea5d5..024f0f99f70 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -1,6 +1,7 @@ use super::{DataSection, InstructionSet}; use crate::asm_lang::virtual_ops::VirtualImmediate12; use crate::error::*; +use either::Either; use std::io::Write; /// Represents an ASM set which has had register allocation, jump elimination, and optimization /// applied to it @@ -39,29 +40,27 @@ fn to_bytecode<'sc>( program_section: &InstructionSet<'sc>, data_section: &DataSection<'sc>, ) -> CompileResult<'sc, Vec> { - let offset_to_data_section = program_section.ops.len() as u64; - // TODO anything else besides this -- we should not have a 2^12 limit on instructions - if offset_to_data_section > crate::asm_generation::compiler_constants::TWELVE_BITS { - return err( - vec![], - vec![CompileError::TooManyInstructions { - span: pest::Span::new(" ", 0, 0).unwrap(), - }], - ); - } - - let offset_to_data_section = VirtualImmediate12::new_unchecked( - offset_to_data_section, - "this was manually checked with [CompileError::TooManyInstructions] above. ", - ); + // The below invariant is introduced to word-align the data section. + // A noop is inserted in ASM generation if there is an odd number of ops. + assert_eq!(program_section.ops.len() % 2, 0); + let offset_to_data_section = (program_section.ops.len() * 4) as u64; // each op is four bytes, so the length of the buf is then number of ops times four. let mut buf = vec![0; program_section.ops.len() * 4]; for (ix, op) in program_section.ops.iter().enumerate() { - let mut op = op.to_fuel_asm(&offset_to_data_section, data_section); - op.write(&buf[ix * 4..]) - .expect("Failed to write to in-memory buffer."); + let op = op.to_fuel_asm(offset_to_data_section, data_section); + match op { + Either::Right(data) => { + for i in 0..data.len() { + buf[ix + i] = data[i]; + } + } + Either::Left(mut op) => { + op.write(&buf[ix * 4..]) + .expect("Failed to write to in-memory buffer."); + } + } } let mut data_section = data_section.serialize_to_bytes(); diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 0dea665c830..b665482689c 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -821,25 +821,47 @@ fn convert_node_to_asm<'sc>( /// WORD OP /// 1 JI program_start /// - NOOP -/// 2 DATA_START (0-32) +/// 2 DATA_START (0-32) (in bytes, offset from $is) /// - DATA_START (32-64) -/// .program_start: +/// - (absolute address) (offset in words) +/// 3 LW $ds $is 1 +/// - ADD $ds $ds $is +/// 4 .program_start: fn build_preamble(register_sequencer: &mut RegisterSequencer) -> Vec> { let mut buf = Vec::new(); let label = register_sequencer.get_label(); + // word 1 buf.push(Op::jump_to_label(label.clone())); + // word 1.5 buf.push(Op { opcode: Either::Left(VirtualOp::NOOP), comment: "".into(), owning_span: None, }); - + // word 2 -- full word u64 placeholder buf.push(Op { opcode: Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder), comment: "data section offset".into(), owning_span: None, }); + // word 3 -- load the data offset into $ds + buf.push(Op { + opcode: Either::Left(VirtualOp::DataSectionRegisterLoadPlaceholder), + comment: "".into(), + owning_span: None, + }); + // word 3.5 -- add $ds $ds $is + buf.push(Op { + opcode: Either::Left(VirtualOp::ADD( + VirtualRegister::Constant(ConstantRegister::DataSectionStart), + VirtualRegister::Constant(ConstantRegister::DataSectionStart), + VirtualRegister::Constant(ConstantRegister::InstructionStart), + )), + comment: "".into(), + owning_span: None, + }); + // word 3 buf.push(Op::unowned_jump_label_comment(label, "end of metadata")); buf } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index e914201a706..e53ac3c4d14 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -12,6 +12,8 @@ use super::virtual_ops::*; use super::DataId; use crate::asm_generation::DataSection; +use crate::error::*; +use either::Either; use fuel_asm::Opcode as VmOp; use pest::Span; use std::fmt; @@ -248,15 +250,17 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { } } +type DoubleWideData = [u8; 8]; + impl<'sc> AllocatedOp<'sc> { pub(crate) fn to_fuel_asm( &self, - offset_to_data_section: &VirtualImmediate12, + offset_to_data_section: u64, data_section: &DataSection, - ) -> fuel_asm::Opcode { + ) -> Either { use AllocatedOpcode::*; #[rustfmt::skip] - let fuel_op = match &self.opcode { + let fuel_op = Either::Left(match &self.opcode { ADD (a, b, c) => VmOp::ADD (a.to_register_id(), b.to_register_id(), c.to_register_id()), ADDI(a, b, c) => VmOp::ADDI(a.to_register_id(), b.to_register_id(), c.value), AND (a, b, c) => VmOp::AND (a.to_register_id(), b.to_register_id(), c.to_register_id()), @@ -326,15 +330,27 @@ impl<'sc> AllocatedOp<'sc> { NOOP => VmOp::NOOP, FLAG(a) => VmOp::FLAG(a.to_register_id()), Undefined => VmOp::Undefined, - DataSectionOffsetPlaceholder => todo!() - }; + DataSectionOffsetPlaceholder => return Either::Right(offset_to_data_section.to_be_bytes()) + }); fuel_op } } -fn realize_lw(dest: &AllocatedRegister, data_id: &DataId, offset_to_data_section: &VirtualImmediate12, data_section: &DataSection) -> VmOp { +fn realize_lw( + dest: &AllocatedRegister, + data_id: &DataId, + offset_to_data_section: u64, + data_section: &DataSection, +) -> VmOp { let dest = dest.to_register_id(); - let offset = data_section.offset_to_id(data_id); - // Calculate how many words of data there are in the dta section before this item - VmOp::LW (dest, offset, offset_to_data_section.value) + let offset = data_section.offset_to_id(data_id) as u64; + let offset = match VirtualImmediate12::new(offset, Span::new(" ", 0, 0).unwrap()) { + Ok ( value ) => value, + Err (_) => panic!("Unable to offset into the data section more than 2^12 bits. Unsupported data section length.") + }; + VmOp::LW( + dest, + crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER() as usize, + offset.value, + ) } diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index 3ee2de40d96..d91279a5d4b 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1002,6 +1002,7 @@ fn four_regs<'sc>( "bal" => Balance, "is" => InstructionStart, "flag" => Flags, + "ds" => DataSectionStart, _ => return None, }) } @@ -1279,6 +1280,9 @@ impl fmt::Display for Op<'_> { FLAG(a) => format!("flag {}", a), Undefined => format!("undefined op"), VirtualOp::DataSectionOffsetPlaceholder => "data section offset placeholder".into(), + DataSectionRegisterLoadPlaceholder => { + "data section register load placeholder".into() + } }, Either::Right(opcode) => match opcode { Label(l) => format!("{}", l), diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 15ecadae03d..832a152c9f0 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -44,6 +44,7 @@ impl fmt::Display for VirtualRegister { #[derive(Hash, PartialEq, Eq, Debug, Clone)] /// These are the special registers defined in the spec pub enum ConstantRegister { + // Below are VM-reserved registers Zero, One, Overflow, @@ -58,6 +59,8 @@ pub enum ConstantRegister { Balance, InstructionStart, Flags, + // Below are compiler-reserved registers + DataSectionStart, } impl ConstantRegister { @@ -78,6 +81,10 @@ impl ConstantRegister { Balance => 11, InstructionStart => 12, Flags => 13, + DataSectionStart => { + (crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER()) + as fuel_asm::RegisterId + } } } } @@ -100,6 +107,7 @@ impl fmt::Display for ConstantRegister { Balance => "$bal", InstructionStart => "$is", Flags => "$flag", + DataSectionStart => "$ds", }; write!(f, "{}", text) } @@ -279,6 +287,10 @@ pub(crate) enum VirtualOp { CFEI(VirtualImmediate24), CFSI(VirtualImmediate24), LB(VirtualRegister, VirtualRegister, VirtualImmediate12), + // LW takes a virtual register and a DataId, which points to a labeled piece + // of data in the data section. Note that the ASM op corresponding to a LW is + // subtly complex: $rB is in bytes and points to some mem address. The immediate + // third argument is a _word_ offset from that byte address. LW(VirtualRegister, DataId), ALOC(VirtualRegister), MCL(VirtualRegister, VirtualRegister), @@ -338,6 +350,7 @@ pub(crate) enum VirtualOp { FLAG(VirtualRegister), Undefined, DataSectionOffsetPlaceholder, + DataSectionRegisterLoadPlaceholder, } impl VirtualOp { @@ -413,6 +426,10 @@ impl VirtualOp { NOOP => vec![], FLAG(r1) => vec![r1], Undefined | DataSectionOffsetPlaceholder => vec![], + DataSectionRegisterLoadPlaceholder => vec![ + &VirtualRegister::Constant(ConstantRegister::DataSectionStart), + &VirtualRegister::Constant(ConstantRegister::InstructionStart), + ], }) .into_iter() .collect() @@ -719,6 +736,7 @@ impl VirtualOp { FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), Undefined => AllocatedOpcode::Undefined, DataSectionOffsetPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder, + DataSectionRegisterLoadPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder, } } } From 11e6a17a3f9790ed32d51d82ab227bbfa12fe28e Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 14:05:16 -0700 Subject: [PATCH 37/62] add forc --asm command --- .../src/semantic_analysis/syntax_tree.rs | 13 ++- example_project/fuel_project/src/main.fm | 17 ++- forc/src/cli/commands/build.rs | 3 + forc/src/ops/forc_build.rs | 109 +++++++++++++++++- 4 files changed, 136 insertions(+), 6 deletions(-) diff --git a/core_lang/src/semantic_analysis/syntax_tree.rs b/core_lang/src/semantic_analysis/syntax_tree.rs index ee6e80df97b..1c3cc4061c3 100644 --- a/core_lang/src/semantic_analysis/syntax_tree.rs +++ b/core_lang/src/semantic_analysis/syntax_tree.rs @@ -73,6 +73,7 @@ impl<'sc> TypedParseTree<'sc> { let mut next_pass_nodes: VecDeque<_> = parsed.root_nodes.into_iter().collect(); let mut num_failed_nodes = next_pass_nodes.len(); let mut warnings = Vec::new(); + let mut is_first_pass = true; let mut errors = Vec::new(); while num_failed_nodes > 0 { let nodes = next_pass_nodes @@ -105,10 +106,17 @@ impl<'sc> TypedParseTree<'sc> { } // If we did not solve any issues, i.e. the same number of nodes failed, // then this is a genuine error and so we break. - if next_pass_nodes.len() == num_failed_nodes { + if next_pass_nodes.len() == num_failed_nodes && !is_first_pass { for (_, failed_node_res) in nodes { match failed_node_res { - CompileResult::Ok { .. } => unreachable!(), + CompileResult::Ok { + errors: mut l_e, + warnings: mut l_w, + .. + } => { + errors.append(&mut l_e); + warnings.append(&mut l_w); + } CompileResult::Err { errors: mut l_e, warnings: mut l_w, @@ -120,6 +128,7 @@ impl<'sc> TypedParseTree<'sc> { } break; } + is_first_pass = false; assert!( next_pass_nodes.len() < num_failed_nodes, "This collection should be strictly monotonically decreasing in size." diff --git a/example_project/fuel_project/src/main.fm b/example_project/fuel_project/src/main.fm index 21f3c43e977..95607ae29fb 100644 --- a/example_project/fuel_project/src/main.fm +++ b/example_project/fuel_project/src/main.fm @@ -5,9 +5,6 @@ struct Rgb { blue: u64, } -trait Color { - fn rgb(self) -> Rgb; -} enum PrimaryColor { Red : (), @@ -15,6 +12,15 @@ enum PrimaryColor { Blue : () } +impl std::ops::Eq for PrimaryColor { + fn equals(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + eq r3 r1 r2; + r3: bool + } + } +} + impl Color for PrimaryColor { // TODO: when we support match statements, change this to a match statement fn rgb(self) -> Rgb { @@ -53,3 +59,8 @@ fn main() { let first_color = PrimaryColor::Green; let rgb: Rgb = first_color.rgb(); } + +trait Color { + fn rgb(self) -> Rgb; +} + diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index 2c74d4e3e17..8710c368466 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -5,6 +5,9 @@ use crate::ops::forc_build; pub(crate) struct Command { #[structopt(short = "p")] pub path: Option, + /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + #[structopt(long = "asm")] + pub asm: bool, } pub(crate) fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 7f329786c55..5d4dc85e8ba 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -7,9 +7,50 @@ use std::io::{self, Write}; use termcolor::{BufferWriter, Color as TermColor, ColorChoice, ColorSpec, WriteColor}; use crate::utils::manifest::{Dependency, DependencyDetails, Manifest}; -use core_lang::{BytecodeCompilationResult, CompilationResult, LibraryExports, Namespace}; +use core_lang::{ + BytecodeCompilationResult, CompilationResult, FinalizedAsm, LibraryExports, Namespace, +}; use std::{fs, path::PathBuf}; +pub fn print_asm(path: Option) -> Result<(), String> { + // find manifest directory, even if in subdirectory + let this_dir = if let Some(path) = path { + PathBuf::from(path) + } else { + std::env::current_dir().unwrap() + }; + let manifest_dir = match find_manifest_dir(&this_dir) { + Some(dir) => dir, + None => { + return Err(format!( + "No manifest file found in this directory or any parent directories of it: {:?}", + this_dir + )) + } + }; + let manifest = read_manifest(&manifest_dir)?; + + let mut namespace: Namespace = Default::default(); + if let Some(ref deps) = manifest.dependencies { + for (dependency_name, dependency_details) in deps.iter() { + compile_dependency_lib( + &this_dir, + &dependency_name, + &dependency_details, + &mut namespace, + )?; + } + } + + // now, compile this program with all of its dependencies + let main_file = get_main_file(&manifest, &manifest_dir)?; + let main = compile_to_asm(main_file, &manifest.project.name, &namespace)?; + + println!("{}", main); + + Ok(()) +} + pub fn build(path: Option) -> Result<(), String> { // find manifest directory, even if in subdirectory let this_dir = if let Some(path) = path { @@ -370,3 +411,69 @@ fn get_main_file( let main_file: &'static mut String = Box::leak(main_file); return Ok(main_file); } +fn compile_to_asm<'source, 'manifest>( + source: &'source str, + proj_name: &str, + namespace: &Namespace<'source>, +) -> Result, String> { + let res = core_lang::compile_to_asm(&source, namespace); + match res { + CompilationResult::Success { asm, warnings } => { + for ref warning in warnings.iter() { + format_warning(&source, warning); + } + if warnings.is_empty() { + let _ = write_green(&format!("Compiled script {:?}.", proj_name)); + } else { + let _ = write_yellow(&format!( + "Compiled script {:?} with {} {}.", + proj_name, + warnings.len(), + if warnings.len() > 1 { + "warnings" + } else { + "warning" + } + )); + } + Ok(asm) + } + CompilationResult::Library { warnings, .. } => { + for ref warning in warnings.iter() { + format_warning(&source, warning); + } + if warnings.is_empty() { + let _ = write_green(&format!("Compiled library {:?}.", proj_name)); + } else { + let _ = write_yellow(&format!( + "Compiled library {:?} with {} {}.", + proj_name, + warnings.len(), + if warnings.len() > 1 { + "warnings" + } else { + "warning" + } + )); + } + Ok(FinalizedAsm::Library) + } + CompilationResult::Failure { errors, warnings } => { + let e_len = errors.len(); + + for ref warning in warnings.iter() { + format_warning(&source, warning); + } + + errors.into_iter().for_each(|e| format_err(&source, e)); + + write_red(format!( + "Aborting due to {} {}.", + e_len, + if e_len > 1 { "errors" } else { "error" } + )) + .unwrap(); + Err(format!("Failed to compile {}", proj_name)) + } + } +} From e3c1d1a714f69127baa9bc74bf4d3c5880a5f52f Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 14:13:58 -0700 Subject: [PATCH 38/62] print out the loading of the data section op --- core_lang/src/asm_lang/allocated_ops.rs | 7 +++++-- core_lang/src/asm_lang/virtual_ops.rs | 4 +++- forc/src/cli/commands/build.rs | 6 +++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index e53ac3c4d14..62c040cc463 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -150,6 +150,7 @@ pub(crate) enum AllocatedOpcode { FLAG(AllocatedRegister), Undefined, DataSectionOffsetPlaceholder, + DataSectionRegisterLoadPlaceholder } #[derive(Clone)] @@ -234,7 +235,8 @@ impl<'sc> fmt::Display for AllocatedOp<'sc> { NOOP => "noop".to_string(), FLAG(a) => format!("flag {}", a), Undefined => format!("undefined op"), - DataSectionOffsetPlaceholder => "data section offset placeholder".into() + DataSectionOffsetPlaceholder => "DATA_SECTION_OFFSET[0..32]\nDATA_SECTION_OFFSET[32..64]".into(), + DataSectionRegisterLoadPlaceholder => "lw $ds $is 1".into() }; // 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 @@ -330,7 +332,8 @@ impl<'sc> AllocatedOp<'sc> { NOOP => VmOp::NOOP, FLAG(a) => VmOp::FLAG(a.to_register_id()), Undefined => VmOp::Undefined, - DataSectionOffsetPlaceholder => return Either::Right(offset_to_data_section.to_be_bytes()) + DataSectionOffsetPlaceholder => return Either::Right(offset_to_data_section.to_be_bytes()), + DataSectionRegisterLoadPlaceholder => VmOp::LW(crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER() as fuel_asm::RegisterId, ConstantRegister::InstructionStart.to_register_id(), 1), }); fuel_op } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 832a152c9f0..9b2b1ef5307 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -736,7 +736,9 @@ impl VirtualOp { FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), Undefined => AllocatedOpcode::Undefined, DataSectionOffsetPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder, - DataSectionRegisterLoadPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder, + DataSectionRegisterLoadPlaceholder => { + AllocatedOpcode::DataSectionRegisterLoadPlaceholder + } } } } diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index 8710c368466..efd36d1f659 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -11,5 +11,9 @@ pub(crate) struct Command { } pub(crate) fn exec(command: Command) -> Result<(), String> { - forc_build::build(command.path) + if command.asm { + forc_build::print_asm(command.path) + } else { + forc_build::build(command.path) + } } From db009fb2e8674b539930e55588343dddec69d7eb Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 16:57:07 -0700 Subject: [PATCH 39/62] resolve warnings --- core_lang/src/asm_generation/finalized_asm.rs | 1 - core_lang/src/asm_generation/mod.rs | 5 +---- core_lang/src/asm_lang/allocated_ops.rs | 4 +--- core_lang/src/lib.rs | 2 +- core_lang/src/parse_tree/literal.rs | 1 - 5 files changed, 3 insertions(+), 10 deletions(-) diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index 024f0f99f70..e17641fc236 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -1,5 +1,4 @@ use super::{DataSection, InstructionSet}; -use crate::asm_lang::virtual_ops::VirtualImmediate12; use crate::error::*; use either::Either; use std::io::Write; diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index b665482689c..246fa9e2b4a 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{HashMap, VecDeque}, - fmt, -}; +use std::{collections::HashMap, fmt}; use crate::{ asm_lang::{ diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 62c040cc463..558da080558 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -12,7 +12,6 @@ use super::virtual_ops::*; use super::DataId; use crate::asm_generation::DataSection; -use crate::error::*; use either::Either; use fuel_asm::Opcode as VmOp; use pest::Span; @@ -299,7 +298,7 @@ impl<'sc> AllocatedOp<'sc> { CFEI(a) => VmOp::CFEI(a.value), CFSI(a) => VmOp::CFSI(a.value), LB (a, b, c) => VmOp::LB (a.to_register_id(), b.to_register_id(), c.value), - LW (a, b) => realize_lw(a, b, offset_to_data_section, data_section), + LW (a, b) => realize_lw(a, b, data_section), ALOC(a) => VmOp::ALOC(a.to_register_id()), MCL (a, b) => VmOp::MCL (a.to_register_id(), b.to_register_id()), MCLI(a, b) => VmOp::MCLI(a.to_register_id(), b.value), @@ -342,7 +341,6 @@ impl<'sc> AllocatedOp<'sc> { fn realize_lw( dest: &AllocatedRegister, data_id: &DataId, - offset_to_data_section: u64, data_section: &DataSection, ) -> VmOp { let dest = dest.to_register_id(); diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index c0bac1e2ac9..0716bb46027 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -404,9 +404,9 @@ pub fn compile_to_bytecode<'sc, 'manifest>( value } CompileResult::Ok { - value, warnings: mut l_w, errors, + .. } => { warnings.append(&mut l_w); return BytecodeCompilationResult::Failure { warnings, errors }; diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index 5b09124bd1b..c80b8caff50 100644 --- a/core_lang/src/parse_tree/literal.rs +++ b/core_lang/src/parse_tree/literal.rs @@ -1,4 +1,3 @@ -use crate::asm_lang::virtual_ops::VirtualImmediate12; use crate::error::*; use crate::parser::Rule; use crate::types::{IntegerBits, ResolvedType}; From 1e27b4c00f85199a4f485dc7fd8b1fcb8b85e404 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 17:15:47 -0700 Subject: [PATCH 40/62] begin VM tests --- forc/src/cli/commands/build.rs | 3 +- forc/src/ops/forc_build.rs | 4 +- test_suite/Cargo.toml | 2 + test_suite/src/e2e_vm_tests/harness.rs | 42 ++++++++++ test_suite/src/e2e_vm_tests/mod.rs | 14 ++++ .../test_programs/script_1/Forc.toml | 11 +++ .../test_programs/script_1/src/main.fm | 77 +++++++++++++++++++ .../test_programs/script_2/Forc.toml | 11 +++ .../test_programs/script_2/src/main.fm | 35 +++++++++ .../test_programs/script_3/Forc.toml | 11 +++ .../test_programs/script_3/src/main.fm | 38 +++++++++ test_suite/src/main.rs | 2 + 12 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 test_suite/src/e2e_vm_tests/harness.rs create mode 100644 test_suite/src/e2e_vm_tests/mod.rs create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml create mode 100644 test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index efd36d1f659..0111bf7f90f 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -14,6 +14,7 @@ pub(crate) fn exec(command: Command) -> Result<(), String> { if command.asm { forc_build::print_asm(command.path) } else { - forc_build::build(command.path) + forc_build::build(command.path)?; + Ok(()) } } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 5d4dc85e8ba..a35b61b883d 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -51,7 +51,7 @@ pub fn print_asm(path: Option) -> Result<(), String> { Ok(()) } -pub fn build(path: Option) -> Result<(), String> { +pub fn build(path: Option) -> Result, String> { // find manifest directory, even if in subdirectory let this_dir = if let Some(path) = path { PathBuf::from(path) @@ -87,7 +87,7 @@ pub fn build(path: Option) -> Result<(), String> { println!("Bytecode size is {} bytes.", main.len() / 4); - Ok(()) + Ok(main) } /// Continually go up in the file tree until a manifest (Forc.toml) is found. diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index 3d411a321f9..07d04b4774f 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -8,3 +8,5 @@ edition = "2018" [dependencies] forc = { path = "../forc" } +fuel-tx = { path = "../../fuel-tx" } +fuel-vm-rust = { path = "../../fuel-core" } diff --git a/test_suite/src/e2e_vm_tests/harness.rs b/test_suite/src/e2e_vm_tests/harness.rs new file mode 100644 index 00000000000..d2c1db9f49f --- /dev/null +++ b/test_suite/src/e2e_vm_tests/harness.rs @@ -0,0 +1,42 @@ +use forc; + +use fuel_tx::Transaction; +use fuel_vm_rust::interpreter::Interpreter; + +/// Very basic check that code does indeed run in the VM. +/// `true` if it does, `false` if not. +pub(crate) fn runs_in_vm(file_name: &str) -> bool { + let bytes = compile_to_bytes(file_name); + let transaction = Transaction::Script { + gas_price: 0, + gas_limit: u64::MAX, + maturity: 0, + script: bytes, + script_data: vec![], + inputs: vec![], + outputs: vec![], + witnesses: vec![], + }; + + Interpreter::execute_tx(transaction).is_ok() +} + +/// Returns `true` if a file compiled without any errors or warnings, +/// and `false` if it did not. +pub(crate) fn compile_to_bytes(file_name: &str) -> Vec { + println!("Compiling {}", file_name); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let res = forc::ops::forc_build::build(Some(format!( + "{}/src/e2e_vm_tests/test_programs/{}", + manifest_dir, file_name + ))); + match res { + Ok(bytes) => bytes, + Err(_) => { + panic!( + "TEST FAILURE: Project \"{}\" failed to compile. ", + file_name + ); + } + } +} diff --git a/test_suite/src/e2e_vm_tests/mod.rs b/test_suite/src/e2e_vm_tests/mod.rs new file mode 100644 index 00000000000..6786645a4b8 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/mod.rs @@ -0,0 +1,14 @@ +mod harness; + +pub fn run() { + let project_names = vec!["script_1", "script_2", "script_3"]; + assert!(project_names.into_iter().all(|name| { + let result = crate::e2e_vm_tests::harness::runs_in_vm(name); + if !result { + println!("Failure: {} should have compiled.", name); + false + } else { + true + } + })); +} diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml new file mode 100644 index 00000000000..e2d079acf68 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml @@ -0,0 +1,11 @@ +[project] +author = "Alexander Hansen " +license = "MIT" +name = "script_1" +entry = "main.fm" + + +[dependencies] +std = { path = "../../../../../stdlib" } + + diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm new file mode 100644 index 00000000000..e711c92db77 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm @@ -0,0 +1,77 @@ +script; +use std::*; +use std::ops::Eq; + +struct Rgb { + red: u64, + green: u64, + blue: u64, +} + +trait Color { + fn rgb(self) -> Rgb; +} + +enum PrimaryColor { + Red : (), + Green : (), + Blue : () +} + +impl std::ops::Eq for PrimaryColor { + fn equals(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + eq r3 r1 r2; + r3: bool + } + } +} + +impl Color for PrimaryColor { + // TODO: when we support match statements, change this to a match statement + fn rgb(self) -> Rgb { + if self == PrimaryColor::Red { + Rgb { + red: 255, + blue: 0, + green: 0, + } + } + else if self == PrimaryColor::Green { + Rgb { + red: 0, + blue: 0, + green: 255, + } + } + else if self == PrimaryColor::Blue { + Rgb { + red: 0, + blue: 255, + green: 0, + } + } + // TODO remove this else when exhaustive ifs are checked for + else { + Rgb { + red: 0, + green: 0, + blue: 0, + } + } + } +} + +fn main() { + let first_color: PrimaryColor = PrimaryColor::Green; + 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/e2e_vm_tests/test_programs/script_2/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml new file mode 100644 index 00000000000..e2d079acf68 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml @@ -0,0 +1,11 @@ +[project] +author = "Alexander Hansen " +license = "MIT" +name = "script_1" +entry = "main.fm" + + +[dependencies] +std = { path = "../../../../../stdlib" } + + diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm new file mode 100644 index 00000000000..72f2c2e900b --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm @@ -0,0 +1,35 @@ +script; + +// This file tests different kinds of ASM generation and parsing. + +fn blockheight() -> u64 { + asm(r1) { + bhei r1; + r1: u64 + } +} + +struct GasCounts { + global_gas: u64, + context_gas: u64 +} + +fn get_gas() -> GasCounts { + let x = asm(cgas: 6u64) { + bhei cgas; + cgas + }; + GasCounts { + global_gas: asm() { + ggas + }, + context_gas: asm() { + cgas + } + } +} + +fn main() { + let block_height = blockheight(); + let remaining_gas = get_gas(); +} diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml new file mode 100644 index 00000000000..e2d079acf68 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml @@ -0,0 +1,11 @@ +[project] +author = "Alexander Hansen " +license = "MIT" +name = "script_1" +entry = "main.fm" + + +[dependencies] +std = { path = "../../../../../stdlib" } + + diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm new file mode 100644 index 00000000000..a222cce5d42 --- /dev/null +++ b/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm @@ -0,0 +1,38 @@ +script; +// This test tests two-pass compilation and allowing usages before declarations. + +fn main() { + // fn before decl + let x = the_number_five(); + // enum before decl + let z = AnEnum::Variant; + // struct before decl + let y = FuelStruct { + a: true, + b: false + }; +} + +struct FuelStruct { + a: bool, + b: bool +} + +fn the_number_five() -> u64 { + 5 +} + +enum AnEnum { + Variant: (), +} + +// trait before decl +impl FuelTrait for u64 { + fn foo() -> bool { + true + } +} + +trait FuelTrait { + fn foo() -> bool; +} diff --git a/test_suite/src/main.rs b/test_suite/src/main.rs index 3ecb21cd749..7b55dc86e59 100644 --- a/test_suite/src/main.rs +++ b/test_suite/src/main.rs @@ -1,5 +1,7 @@ mod basic_compilation_tests; +mod e2e_vm_tests; fn main() { basic_compilation_tests::run(); + e2e_vm_tests::run(); } From f23350deeb653e0eed5e7e8e28883e28b720ac35 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 17:52:48 -0700 Subject: [PATCH 41/62] fix register allocater bug --- core_lang/src/asm_generation/expression/mod.rs | 7 +++++-- core_lang/src/asm_generation/mod.rs | 11 ++++++++++- core_lang/src/asm_lang/allocated_ops.rs | 10 +++------- core_lang/src/asm_lang/mod.rs | 17 +++++++++++++++++ core_lang/src/asm_lang/virtual_ops.rs | 7 ++++++- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/core_lang/src/asm_generation/expression/mod.rs b/core_lang/src/asm_generation/expression/mod.rs index d42791eacd4..cfa9ff6d09f 100644 --- a/core_lang/src/asm_generation/expression/mod.rs +++ b/core_lang/src/asm_generation/expression/mod.rs @@ -333,7 +333,7 @@ fn convert_literal_to_asm<'sc>( /// For now, all functions are handled by inlining at the time of application. fn convert_fn_app_to_asm<'sc>( - _name: &CallPath<'sc>, + name: &CallPath<'sc>, arguments: &[(Ident<'sc>, TypedExpression<'sc>)], function_body: &TypedCodeBlock<'sc>, parent_namespace: &mut AsmNamespace<'sc>, @@ -342,7 +342,10 @@ fn convert_fn_app_to_asm<'sc>( ) -> CompileResult<'sc, Vec>> { let mut warnings = vec![]; let mut errors = vec![]; - let mut asm_buf = vec![]; + let mut asm_buf = vec![Op::new_comment(format!( + "{} fn call", + name.suffix.primary_name + ))]; // Make a local namespace so that the namespace of this function does not pollute the outer // scope let mut namespace = parent_namespace.clone(); diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 246fa9e2b4a..568a3a03109 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -287,6 +287,15 @@ impl RegisterPool { virtual_register: &VirtualRegister, op_register_mapping: &[(RealizedOp, std::collections::HashSet)], ) -> Option { + // check if this register has already been allocated for + if let a @ Some(_) = self.registers.iter().find_map( + |RegisterAllocationStatus { reg, in_use }| match in_use { + Some(x) if x == virtual_register => Some(reg), + _ => None, + }, + ) { + return a.cloned(); + } // 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()) @@ -298,7 +307,7 @@ impl RegisterPool { *in_use = None; } } - // find the next unused register, return it, flip assign it + // find the next unused register, return it, assign it let next_available = self .registers .iter_mut() diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 558da080558..3a3cf60849b 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -149,7 +149,7 @@ pub(crate) enum AllocatedOpcode { FLAG(AllocatedRegister), Undefined, DataSectionOffsetPlaceholder, - DataSectionRegisterLoadPlaceholder + DataSectionRegisterLoadPlaceholder, } #[derive(Clone)] @@ -298,7 +298,7 @@ impl<'sc> AllocatedOp<'sc> { CFEI(a) => VmOp::CFEI(a.value), CFSI(a) => VmOp::CFSI(a.value), LB (a, b, c) => VmOp::LB (a.to_register_id(), b.to_register_id(), c.value), - LW (a, b) => realize_lw(a, b, data_section), + LW (a, b) => realize_lw(a, b, data_section), ALOC(a) => VmOp::ALOC(a.to_register_id()), MCL (a, b) => VmOp::MCL (a.to_register_id(), b.to_register_id()), MCLI(a, b) => VmOp::MCLI(a.to_register_id(), b.value), @@ -338,11 +338,7 @@ impl<'sc> AllocatedOp<'sc> { } } -fn realize_lw( - dest: &AllocatedRegister, - data_id: &DataId, - data_section: &DataSection, -) -> VmOp { +fn realize_lw(dest: &AllocatedRegister, data_id: &DataId, data_section: &DataSection) -> VmOp { let dest = dest.to_register_id(); let offset = data_section.offset_to_id(data_id) as u64; let offset = match VirtualImmediate12::new(offset, Span::new(" ", 0, 0).unwrap()) { diff --git a/core_lang/src/asm_lang/mod.rs b/core_lang/src/asm_lang/mod.rs index d91279a5d4b..581d3a01a48 100644 --- a/core_lang/src/asm_lang/mod.rs +++ b/core_lang/src/asm_lang/mod.rs @@ -1323,6 +1323,23 @@ pub(crate) enum OrganizationalOp { // placeholder for the DataSection offset to go DataSectionOffsetPlaceholder, } +impl fmt::Display for OrganizationalOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use OrganizationalOp::*; + write!( + f, + "{}", + match self { + Label(lab) => format!("{}", lab), + Jump(lab) => format!("ji {}", lab), + Comment => "".into(), + JumpIfNotEq(r1, r2, lab) => format!("jnei {} {} {}", r1, r2, lab), + DataSectionOffsetPlaceholder => + "DATA SECTION OFFSET[0..32]\nDATA SECTION OFFSET[32..64]".into(), + } + ) + } +} impl OrganizationalOp { pub(crate) fn registers(&self) -> HashSet<&VirtualRegister> { diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 9b2b1ef5307..803984622fb 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -445,7 +445,12 @@ impl VirtualOp { let register_allocation_result = virtual_registers .clone() .into_iter() - .map(|x| (x, pool.get_register(x, &op_register_mapping[ix + 1..]))) + .map(|x| match x { + VirtualRegister::Constant(c) => (x, Some(AllocatedRegister::Constant(c.clone()))), + VirtualRegister::Virtual(_) => { + (x, pool.get_register(x, &op_register_mapping[ix + 1..])) + } + }) .map(|(x, res)| match res { Some(res) => Some((x, res)), None => None, From 1b746640ca5b418d191a263fc097a978c8573715 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 26 May 2021 18:42:50 -0700 Subject: [PATCH 42/62] cleanup --- core_lang/Cargo.toml | 2 +- .../src/asm_generation/compiler_constants.rs | 15 ++++++++++++++- core_lang/src/asm_generation/mod.rs | 2 +- core_lang/src/asm_lang/allocated_ops.rs | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index 09e121d494f..a815339d913 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -15,4 +15,4 @@ either = "1.6" Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" -fuel-asm = { path = "../../fuel-asm" } +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 8428d39b886..78eaca6c6da 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -1,9 +1,22 @@ -pub(crate) const NUM_FREE_REGISTERS: u8 = 48; +/// The number of registers available for the compiler to use. Registers reserved by the +/// compiler are contained within these. +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; +/// This is the number of registers reserved by the compiler. Adjust this number if a new +/// reservation must be made. +/// So far, the compiler-reserved registers are: +/// 1. DATA_SECTION_BEGIN +const NUM_COMPILER_RESERVED_REGISTERS: u8 = 1; + #[allow(non_snake_case)] pub(crate) const fn DATA_SECTION_REGISTER() -> u8 { NUM_FREE_REGISTERS - 2 } + +#[allow(non_snake_case)] +pub(crate) const fn NUM_ALLOCATABLE_REGISTERS() -> u8 { + NUM_FREE_REGISTERS - NUM_COMPILER_RESERVED_REGISTERS +} diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 568a3a03109..cebc8945117 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -269,7 +269,7 @@ impl RegisterPool { fn init() -> Self { let register_pool: Vec = (0 // - 1 because we reserve the final register for the data_section begin - ..compiler_constants::NUM_FREE_REGISTERS - 1) + ..compiler_constants::NUM_ALLOCATABLE_REGISTERS()) .map(|x| RegisterAllocationStatus { reg: AllocatedRegister::Allocated(x), in_use: None, diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 3a3cf60849b..5baf39eaefe 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -20,7 +20,7 @@ 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]. +/// variant is guaranteed to be between 0 and [compiler_constants::NUM_ALLOCATABLE_REGISTERS]. #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub enum AllocatedRegister { Allocated(u8), From 980e9c71aff942f35e88119197d436b69af37209 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Thu, 27 May 2021 08:56:15 -0700 Subject: [PATCH 43/62] fix bad error message --- .../ast_node/expression/typed_expression.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 079607fa4c9..b6ff323f5a6 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 @@ -572,11 +572,15 @@ impl<'sc> TypedExpression<'sc> { ) { Some(o) => o, None => { - errors.push(CompileError::MethodNotFound { - span: method_name.suffix.clone().span, - method_name: method_name.suffix.clone().primary_name, - type_name: parent_expr.return_type.friendly_type_str(), - }); + if parent_expr.return_type + != MaybeResolvedType::Resolved(ResolvedType::ErrorRecovery) + { + errors.push(CompileError::MethodNotFound { + span: method_name.suffix.clone().span, + method_name: method_name.suffix.clone().primary_name, + type_name: parent_expr.return_type.friendly_type_str(), + }); + } return err(warnings, errors); } } From 13507d575f196ca0355531851b3ea395eb808d29 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Thu, 27 May 2021 17:44:25 -0700 Subject: [PATCH 44/62] some tiny tweaks --- stdlib/src/main.fm | 8 ++++++ test_suite/src/e2e_vm_tests/harness.rs | 40 +++++++++++++++++--------- test_suite/src/e2e_vm_tests/mod.rs | 2 +- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/stdlib/src/main.fm b/stdlib/src/main.fm index 56a039a43c4..91d58f75c03 100644 --- a/stdlib/src/main.fm +++ b/stdlib/src/main.fm @@ -7,6 +7,14 @@ pub trait Subtract { fn subtract(self, other: Self) -> Self; } +pub trait Multiply { + fn multiply(self, other: Self) -> Self; +} + +pub trait Divide { + fn divide(self, other: Self) -> Self; +} + impl Subtract for u64 { fn subtract(self, other: Self) -> Self { // TODO write asm diff --git a/test_suite/src/e2e_vm_tests/harness.rs b/test_suite/src/e2e_vm_tests/harness.rs index d2c1db9f49f..4489bc491a0 100644 --- a/test_suite/src/e2e_vm_tests/harness.rs +++ b/test_suite/src/e2e_vm_tests/harness.rs @@ -6,19 +6,33 @@ use fuel_vm_rust::interpreter::Interpreter; /// Very basic check that code does indeed run in the VM. /// `true` if it does, `false` if not. pub(crate) fn runs_in_vm(file_name: &str) -> bool { - let bytes = compile_to_bytes(file_name); - let transaction = Transaction::Script { - gas_price: 0, - gas_limit: u64::MAX, - maturity: 0, - script: bytes, - script_data: vec![], - inputs: vec![], - outputs: vec![], - witnesses: vec![], - }; - - Interpreter::execute_tx(transaction).is_ok() + let script = compile_to_bytes(file_name); + let gas_price = 10; + let gas_limit = 10000; + let maturity = 100; + let script_data = vec![]; + let inputs = vec![]; + let outputs = vec![]; + let witness = vec![]; + let tx = Transaction::script( + gas_price, + gas_limit, + maturity, + script, + script_data, + inputs, + outputs, + witness, + ); + let block_height = (u32::MAX >> 1) as u64; + tx.validate(block_height).unwrap(); + match Interpreter::execute_tx(tx) { + Ok(_) => true, + Err(e) => { + println!("Failed: {}", e); + false + } + } } /// Returns `true` if a file compiled without any errors or warnings, diff --git a/test_suite/src/e2e_vm_tests/mod.rs b/test_suite/src/e2e_vm_tests/mod.rs index 6786645a4b8..441f6c68234 100644 --- a/test_suite/src/e2e_vm_tests/mod.rs +++ b/test_suite/src/e2e_vm_tests/mod.rs @@ -5,7 +5,7 @@ pub fn run() { assert!(project_names.into_iter().all(|name| { let result = crate::e2e_vm_tests::harness::runs_in_vm(name); if !result { - println!("Failure: {} should have compiled.", name); + println!("E2E Failure: {} should have run in the VM.", name); false } else { true From 05f347cca72386055debd28530234a517699f308 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Thu, 27 May 2021 17:53:27 -0700 Subject: [PATCH 45/62] fix opcode encoding bug --- core_lang/src/asm_generation/finalized_asm.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index e17641fc236..e19920d8705 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -1,7 +1,7 @@ use super::{DataSection, InstructionSet}; use crate::error::*; use either::Either; -use std::io::Write; +use std::io::Read; /// Represents an ASM set which has had register allocation, jump elimination, and optimization /// applied to it pub enum FinalizedAsm<'sc> { @@ -17,7 +17,6 @@ pub enum FinalizedAsm<'sc> { // Libraries do not generate any asm. Library, } - impl<'sc> FinalizedAsm<'sc> { pub(crate) fn to_bytecode(&self) -> CompileResult<'sc, Vec> { use FinalizedAsm::*; @@ -45,19 +44,22 @@ fn to_bytecode<'sc>( let offset_to_data_section = (program_section.ops.len() * 4) as u64; // each op is four bytes, so the length of the buf is then number of ops times four. - let mut buf = vec![0; program_section.ops.len() * 4]; + let mut buf = vec![0; (program_section.ops.len() * 4) + 4]; - for (ix, op) in program_section.ops.iter().enumerate() { + let mut half_word_ix = 0; + for op in program_section.ops.iter() { let op = op.to_fuel_asm(offset_to_data_section, data_section); match op { Either::Right(data) => { for i in 0..data.len() { - buf[ix + i] = data[i]; + buf[(half_word_ix * 4) + i] = data[i]; } + half_word_ix += 2; } Either::Left(mut op) => { - op.write(&buf[ix * 4..]) + op.read(&mut buf[half_word_ix * 4..]) .expect("Failed to write to in-memory buffer."); + half_word_ix += 1; } } } From 54fae9717bf9800841ab82a5afe239a7dd9a1def Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:08:09 -0700 Subject: [PATCH 46/62] fix off-by-one error --- core_lang/src/asm_generation/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index cebc8945117..6831ab90c6f 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -158,6 +158,8 @@ impl<'sc> AbstractInstructionSet<'sc> { } buf.push(self.ops[i].clone()); } + // the last item cannot sequentially jump by definition so we add it in here + self.ops.last().map(|x| buf.push(x.clone())); // scan through the jumps and remove any labels that are unused // this could of course be N instead of 2N if i did this in the above for loop. @@ -657,7 +659,6 @@ impl<'sc> HllAsmSet<'sc> { impl<'sc> JumpOptimizedAsmSet<'sc> { fn allocate_registers(self) -> RegisterAllocatedAsmSet<'sc> { - // TODO implement this -- noop for now match self { JumpOptimizedAsmSet::Library => RegisterAllocatedAsmSet::Library, JumpOptimizedAsmSet::ScriptMain { From 3c42de9e0288882936fb6ffac17c5437b81ee748 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:17:30 -0700 Subject: [PATCH 47/62] change all tests to run in the vm --- .../src/basic_compilation_tests/harness.rs | 19 ----- test_suite/src/basic_compilation_tests/mod.rs | 7 -- .../src/basic_compilation_tests/test_cases.rs | 12 --- .../test_programs/script_1/Forc.toml | 11 --- .../test_programs/script_1/src/main.sw | 77 ------------------- .../test_programs/script_2/Forc.toml | 11 --- .../test_programs/script_2/src/main.sw | 35 --------- .../test_programs/script_3/Forc.toml | 11 --- .../test_programs/script_3/src/main.sw | 38 --------- test_suite/src/main.rs | 2 - 10 files changed, 223 deletions(-) delete mode 100644 test_suite/src/basic_compilation_tests/harness.rs delete mode 100644 test_suite/src/basic_compilation_tests/mod.rs delete mode 100644 test_suite/src/basic_compilation_tests/test_cases.rs delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_1/Forc.toml delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.sw delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_2/Forc.toml delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.sw delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_3/Forc.toml delete mode 100644 test_suite/src/basic_compilation_tests/test_programs/script_3/src/main.sw diff --git a/test_suite/src/basic_compilation_tests/harness.rs b/test_suite/src/basic_compilation_tests/harness.rs deleted file mode 100644 index a55384e43ec..00000000000 --- a/test_suite/src/basic_compilation_tests/harness.rs +++ /dev/null @@ -1,19 +0,0 @@ -use forc; - -/// Returns `true` if a file compiled without any errors or warnings, -/// and `false` if it did not. -pub(crate) fn should_compile(file_name: &str) -> bool { - println!("Compiling {}", file_name); - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let res = forc::ops::forc_build::build(Some(format!( - "{}/src/basic_compilation_tests/test_programs/{}", - manifest_dir, file_name - ))); - match res { - Ok(_) => true, - Err(_) => { - println!("Project \"{}\" failed to compile. ", file_name); - return false; - } - } -} diff --git a/test_suite/src/basic_compilation_tests/mod.rs b/test_suite/src/basic_compilation_tests/mod.rs deleted file mode 100644 index 23c5e65fbf5..00000000000 --- a/test_suite/src/basic_compilation_tests/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! This module contains a harness and test code that verifies -//! a set of known-good programs compile. - -mod harness; -mod test_cases; - -pub use test_cases::run; diff --git a/test_suite/src/basic_compilation_tests/test_cases.rs b/test_suite/src/basic_compilation_tests/test_cases.rs deleted file mode 100644 index 118bc11033e..00000000000 --- a/test_suite/src/basic_compilation_tests/test_cases.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub fn run() { - let project_names = vec!["script_1", "script_2", "script_3"]; - assert!(project_names.into_iter().all(|name| { - let result = crate::basic_compilation_tests::harness::should_compile(name); - if !result { - println!("Failure: {} should have compiled.", name); - false - } else { - true - } - })); -} diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_1/Forc.toml b/test_suite/src/basic_compilation_tests/test_programs/script_1/Forc.toml deleted file mode 100644 index d9c16b43ac1..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_1/Forc.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -author = "Alexander Hansen " -license = "MIT" -name = "script_1" -entry = "main.sw" - - -[dependencies] -std = { path = "../../../../../stdlib" } - - diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.sw b/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.sw deleted file mode 100644 index e711c92db77..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_1/src/main.sw +++ /dev/null @@ -1,77 +0,0 @@ -script; -use std::*; -use std::ops::Eq; - -struct Rgb { - red: u64, - green: u64, - blue: u64, -} - -trait Color { - fn rgb(self) -> Rgb; -} - -enum PrimaryColor { - Red : (), - Green : (), - Blue : () -} - -impl std::ops::Eq for PrimaryColor { - fn equals(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } - } -} - -impl Color for PrimaryColor { - // TODO: when we support match statements, change this to a match statement - fn rgb(self) -> Rgb { - if self == PrimaryColor::Red { - Rgb { - red: 255, - blue: 0, - green: 0, - } - } - else if self == PrimaryColor::Green { - Rgb { - red: 0, - blue: 0, - green: 255, - } - } - else if self == PrimaryColor::Blue { - Rgb { - red: 0, - blue: 255, - green: 0, - } - } - // TODO remove this else when exhaustive ifs are checked for - else { - Rgb { - red: 0, - green: 0, - blue: 0, - } - } - } -} - -fn main() { - let first_color: PrimaryColor = PrimaryColor::Green; - 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/Forc.toml b/test_suite/src/basic_compilation_tests/test_programs/script_2/Forc.toml deleted file mode 100644 index d9c16b43ac1..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_2/Forc.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -author = "Alexander Hansen " -license = "MIT" -name = "script_1" -entry = "main.sw" - - -[dependencies] -std = { path = "../../../../../stdlib" } - - diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.sw b/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.sw deleted file mode 100644 index 72f2c2e900b..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_2/src/main.sw +++ /dev/null @@ -1,35 +0,0 @@ -script; - -// This file tests different kinds of ASM generation and parsing. - -fn blockheight() -> u64 { - asm(r1) { - bhei r1; - r1: u64 - } -} - -struct GasCounts { - global_gas: u64, - context_gas: u64 -} - -fn get_gas() -> GasCounts { - let x = asm(cgas: 6u64) { - bhei cgas; - cgas - }; - GasCounts { - global_gas: asm() { - ggas - }, - context_gas: asm() { - cgas - } - } -} - -fn main() { - let block_height = blockheight(); - let remaining_gas = get_gas(); -} diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_3/Forc.toml b/test_suite/src/basic_compilation_tests/test_programs/script_3/Forc.toml deleted file mode 100644 index d9c16b43ac1..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_3/Forc.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -author = "Alexander Hansen " -license = "MIT" -name = "script_1" -entry = "main.sw" - - -[dependencies] -std = { path = "../../../../../stdlib" } - - diff --git a/test_suite/src/basic_compilation_tests/test_programs/script_3/src/main.sw b/test_suite/src/basic_compilation_tests/test_programs/script_3/src/main.sw deleted file mode 100644 index a222cce5d42..00000000000 --- a/test_suite/src/basic_compilation_tests/test_programs/script_3/src/main.sw +++ /dev/null @@ -1,38 +0,0 @@ -script; -// This test tests two-pass compilation and allowing usages before declarations. - -fn main() { - // fn before decl - let x = the_number_five(); - // enum before decl - let z = AnEnum::Variant; - // struct before decl - let y = FuelStruct { - a: true, - b: false - }; -} - -struct FuelStruct { - a: bool, - b: bool -} - -fn the_number_five() -> u64 { - 5 -} - -enum AnEnum { - Variant: (), -} - -// trait before decl -impl FuelTrait for u64 { - fn foo() -> bool { - true - } -} - -trait FuelTrait { - fn foo() -> bool; -} diff --git a/test_suite/src/main.rs b/test_suite/src/main.rs index 7b55dc86e59..3fd8915bac1 100644 --- a/test_suite/src/main.rs +++ b/test_suite/src/main.rs @@ -1,7 +1,5 @@ -mod basic_compilation_tests; mod e2e_vm_tests; fn main() { - basic_compilation_tests::run(); e2e_vm_tests::run(); } From 2f45da492e85a67c199d54020a0bbbc2a357542a Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:17:50 -0700 Subject: [PATCH 48/62] proper allocation size for structs --- core_lang/src/asm_generation/expression/structs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core_lang/src/asm_generation/expression/structs.rs b/core_lang/src/asm_generation/expression/structs.rs index 3b45fa85b23..4a7e0ee1415 100644 --- a/core_lang/src/asm_generation/expression/structs.rs +++ b/core_lang/src/asm_generation/expression/structs.rs @@ -85,7 +85,7 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>( // 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, + this_allocation * 8, // this_allocation is words but this op takes bytes "struct size was checked manually to be within 12 bits", ), )); From 88217bd404e9fee778e2e6a06385e92e6cb64e7a Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:39:55 -0700 Subject: [PATCH 49/62] code review feedback --- .../src/asm_generation/compiler_constants.rs | 13 +-- core_lang/src/asm_generation/finalized_asm.rs | 12 ++- core_lang/src/asm_generation/mod.rs | 79 +++++++++---------- core_lang/src/asm_lang/allocated_ops.rs | 4 +- core_lang/src/asm_lang/virtual_ops.rs | 2 +- forc/src/ops/forc_build.rs | 13 +-- 6 files changed, 59 insertions(+), 64 deletions(-) diff --git a/core_lang/src/asm_generation/compiler_constants.rs b/core_lang/src/asm_generation/compiler_constants.rs index 78eaca6c6da..42aef9b218e 100644 --- a/core_lang/src/asm_generation/compiler_constants.rs +++ b/core_lang/src/asm_generation/compiler_constants.rs @@ -10,13 +10,6 @@ pub(crate) const TWELVE_BITS: u64 = 0b111_111_111_111; /// So far, the compiler-reserved registers are: /// 1. DATA_SECTION_BEGIN const NUM_COMPILER_RESERVED_REGISTERS: u8 = 1; - -#[allow(non_snake_case)] -pub(crate) const fn DATA_SECTION_REGISTER() -> u8 { - NUM_FREE_REGISTERS - 2 -} - -#[allow(non_snake_case)] -pub(crate) const fn NUM_ALLOCATABLE_REGISTERS() -> u8 { - NUM_FREE_REGISTERS - NUM_COMPILER_RESERVED_REGISTERS -} +pub(crate) const DATA_SECTION_REGISTER: u8 = NUM_FREE_REGISTERS - 2; +pub(crate) const NUM_ALLOCATABLE_REGISTERS: u8 = + NUM_FREE_REGISTERS - NUM_COMPILER_RESERVED_REGISTERS; diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index e17641fc236..dcc830d0215 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -39,9 +39,17 @@ fn to_bytecode<'sc>( program_section: &InstructionSet<'sc>, data_section: &DataSection<'sc>, ) -> CompileResult<'sc, Vec> { + let mut errors = vec![]; + if program_section.ops.len() & 1 != 0 { + errors.push(CompileError::Internal( + "Non-word-aligned (odd-number) ops generated. This is an invariant violation.", + pest::Span::new(" ", 0, 0).unwrap(), + )); + return err(vec![], errors); + } // The below invariant is introduced to word-align the data section. // A noop is inserted in ASM generation if there is an odd number of ops. - assert_eq!(program_section.ops.len() % 2, 0); + assert_eq!(program_section.ops.len() & 1, 0); let offset_to_data_section = (program_section.ops.len() * 4) as u64; // each op is four bytes, so the length of the buf is then number of ops times four. @@ -66,5 +74,5 @@ fn to_bytecode<'sc>( buf.append(&mut data_section); - ok(buf, vec![], vec![]) + ok(buf, vec![], errors) } diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index cebc8945117..180573a4ebc 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -269,7 +269,7 @@ impl RegisterPool { fn init() -> Self { let register_pool: Vec = (0 // - 1 because we reserve the final register for the data_section begin - ..compiler_constants::NUM_ALLOCATABLE_REGISTERS()) + ..compiler_constants::NUM_ALLOCATABLE_REGISTERS) .map(|x| RegisterAllocationStatus { reg: AllocatedRegister::Allocated(x), in_use: None, @@ -568,7 +568,7 @@ pub(crate) fn compile_ast_to_asm<'sc>( let asm = match ast { TypedParseTree::Script { main_function, .. } => { let mut namespace: AsmNamespace = Default::default(); - let mut asm_buf = build_preamble(&mut register_sequencer); + let mut asm_buf = build_preamble(&mut register_sequencer).to_vec(); // start generating from the main function let return_register = register_sequencer.next(); let mut body = type_check!( @@ -722,7 +722,7 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { } => { // ensure there's an even number of ops so the // data section offset is valid - if program_section.ops.len() % 2 != 0 { + if program_section.ops.len() & 1 == 0 { program_section.ops.push(AllocatedOp { opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, comment: "word-alignment of data section".into(), @@ -740,7 +740,7 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { } => { // ensure there's an even number of ops so the // data section offset is valid - if program_section.ops.len() % 2 != 0 { + if program_section.ops.len() & 1 == 0 { program_section.ops.push(AllocatedOp { opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, comment: "word-alignment of data section".into(), @@ -833,41 +833,40 @@ fn convert_node_to_asm<'sc>( /// 3 LW $ds $is 1 /// - ADD $ds $ds $is /// 4 .program_start: -fn build_preamble(register_sequencer: &mut RegisterSequencer) -> Vec> { - let mut buf = Vec::new(); +fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op<'static>; 6] { let label = register_sequencer.get_label(); - // word 1 - buf.push(Op::jump_to_label(label.clone())); - // word 1.5 - buf.push(Op { - opcode: Either::Left(VirtualOp::NOOP), - comment: "".into(), - owning_span: None, - }); - // word 2 -- full word u64 placeholder - buf.push(Op { - opcode: Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder), - comment: "data section offset".into(), - owning_span: None, - }); - // word 3 -- load the data offset into $ds - buf.push(Op { - opcode: Either::Left(VirtualOp::DataSectionRegisterLoadPlaceholder), - comment: "".into(), - owning_span: None, - }); - // word 3.5 -- add $ds $ds $is - buf.push(Op { - opcode: Either::Left(VirtualOp::ADD( - VirtualRegister::Constant(ConstantRegister::DataSectionStart), - VirtualRegister::Constant(ConstantRegister::DataSectionStart), - VirtualRegister::Constant(ConstantRegister::InstructionStart), - )), - comment: "".into(), - owning_span: None, - }); - - // word 3 - buf.push(Op::unowned_jump_label_comment(label, "end of metadata")); - buf + [ + // word 1 + Op::jump_to_label(label.clone()), + // word 1.5 + Op { + opcode: Either::Left(VirtualOp::NOOP), + comment: "".into(), + owning_span: None, + }, + // word 2 -- full word u64 placeholder + Op { + opcode: Either::Right(OrganizationalOp::DataSectionOffsetPlaceholder), + comment: "data section offset".into(), + owning_span: None, + }, + // word 3 -- load the data offset into $ds + Op { + opcode: Either::Left(VirtualOp::DataSectionRegisterLoadPlaceholder), + comment: "".into(), + owning_span: None, + }, + // word 3.5 -- add $ds $ds $is + Op { + opcode: Either::Left(VirtualOp::ADD( + VirtualRegister::Constant(ConstantRegister::DataSectionStart), + VirtualRegister::Constant(ConstantRegister::DataSectionStart), + VirtualRegister::Constant(ConstantRegister::InstructionStart), + )), + comment: "".into(), + owning_span: None, + }, + // word 3 + Op::unowned_jump_label_comment(label, "end of metadata"), + ] } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 5baf39eaefe..5752e9e27bd 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -332,7 +332,7 @@ impl<'sc> AllocatedOp<'sc> { FLAG(a) => VmOp::FLAG(a.to_register_id()), Undefined => VmOp::Undefined, DataSectionOffsetPlaceholder => return Either::Right(offset_to_data_section.to_be_bytes()), - DataSectionRegisterLoadPlaceholder => VmOp::LW(crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER() as fuel_asm::RegisterId, ConstantRegister::InstructionStart.to_register_id(), 1), + DataSectionRegisterLoadPlaceholder => VmOp::LW(crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER as fuel_asm::RegisterId, ConstantRegister::InstructionStart.to_register_id(), 1), }); fuel_op } @@ -347,7 +347,7 @@ fn realize_lw(dest: &AllocatedRegister, data_id: &DataId, data_section: &DataSec }; VmOp::LW( dest, - crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER() as usize, + crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER as usize, offset.value, ) } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 803984622fb..70a2053e397 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -82,7 +82,7 @@ impl ConstantRegister { InstructionStart => 12, Flags => 13, DataSectionStart => { - (crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER()) + (crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER) as fuel_asm::RegisterId } } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 5d4dc85e8ba..dab1ff6323b 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -19,15 +19,10 @@ pub fn print_asm(path: Option) -> Result<(), String> { } else { std::env::current_dir().unwrap() }; - let manifest_dir = match find_manifest_dir(&this_dir) { - Some(dir) => dir, - None => { - return Err(format!( - "No manifest file found in this directory or any parent directories of it: {:?}", - this_dir - )) - } - }; + let manifest_dir = find_manifest_dir(&this_dir).ok_or(format!( + "No manifest file found in this directory or any parent directories of it: {:?}", + this_dir + ))?; let manifest = read_manifest(&manifest_dir)?; let mut namespace: Namespace = Default::default(); From 22237bc286819dfb55d22d37d9b80c6c951be967 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:44:56 -0700 Subject: [PATCH 50/62] use ssh for deps --- test_suite/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index 07d04b4774f..62277ba82d3 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -8,5 +8,6 @@ edition = "2018" [dependencies] forc = { path = "../forc" } -fuel-tx = { path = "../../fuel-tx" } -fuel-vm-rust = { path = "../../fuel-core" } +fuel-tx = { git = "ssh://git@github.com/FuelLabs/fuel-tx.git" } +fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } +fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } From a7c4d4a099cb67d5714d3ac15e3e52d94aca4de6 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 08:46:35 -0700 Subject: [PATCH 51/62] undo rename --- test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml | 2 +- .../test_programs/script_1/src/{main.fm => main.sw} | 0 test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml | 2 +- .../test_programs/script_2/src/{main.fm => main.sw} | 0 test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml | 2 +- .../test_programs/script_3/src/{main.fm => main.sw} | 0 6 files changed, 3 insertions(+), 3 deletions(-) rename test_suite/src/e2e_vm_tests/test_programs/script_1/src/{main.fm => main.sw} (100%) rename test_suite/src/e2e_vm_tests/test_programs/script_2/src/{main.fm => main.sw} (100%) rename test_suite/src/e2e_vm_tests/test_programs/script_3/src/{main.fm => main.sw} (100%) diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml index e2d079acf68..d9c16b43ac1 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml +++ b/test_suite/src/e2e_vm_tests/test_programs/script_1/Forc.toml @@ -2,7 +2,7 @@ author = "Alexander Hansen " license = "MIT" name = "script_1" -entry = "main.fm" +entry = "main.sw" [dependencies] diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.sw similarity index 100% rename from test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.fm rename to test_suite/src/e2e_vm_tests/test_programs/script_1/src/main.sw diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml index e2d079acf68..d9c16b43ac1 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml +++ b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml @@ -2,7 +2,7 @@ author = "Alexander Hansen " license = "MIT" name = "script_1" -entry = "main.fm" +entry = "main.sw" [dependencies] diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.sw similarity index 100% rename from test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.fm rename to test_suite/src/e2e_vm_tests/test_programs/script_2/src/main.sw diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml index e2d079acf68..d9c16b43ac1 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml +++ b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml @@ -2,7 +2,7 @@ author = "Alexander Hansen " license = "MIT" name = "script_1" -entry = "main.fm" +entry = "main.sw" [dependencies] diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm b/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw similarity index 100% rename from test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.fm rename to test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw From 0babc0bb56725c9c7c970029c67beb6da42fd003 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Fri, 28 May 2021 09:04:41 -0700 Subject: [PATCH 52/62] fix doctest --- core_lang/src/asm_generation/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 180573a4ebc..d67e5a48c60 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -829,8 +829,7 @@ fn convert_node_to_asm<'sc>( /// - NOOP /// 2 DATA_START (0-32) (in bytes, offset from $is) /// - DATA_START (32-64) -/// - (absolute address) (offset in words) -/// 3 LW $ds $is 1 +/// 3 LW $ds $is 1 (where 1 is in words and $is is a byte address to base off of) /// - ADD $ds $ds $is /// 4 .program_start: fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op<'static>; 6] { From 01af3d08008c8b6d3dd5f160b271e9f6e863335f Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 May 2021 11:06:30 -0700 Subject: [PATCH 53/62] fix typo --- core_lang/src/asm_generation/finalized_asm.rs | 1 + core_lang/src/asm_generation/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index dcc830d0215..d92f6226aa7 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -41,6 +41,7 @@ fn to_bytecode<'sc>( ) -> CompileResult<'sc, Vec> { let mut errors = vec![]; if program_section.ops.len() & 1 != 0 { + println!("ops len: {}", program_section.ops.len()); errors.push(CompileError::Internal( "Non-word-aligned (odd-number) ops generated. This is an invariant violation.", pest::Span::new(" ", 0, 0).unwrap(), diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index d67e5a48c60..a23efab0f93 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -722,7 +722,7 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { } => { // ensure there's an even number of ops so the // data section offset is valid - if program_section.ops.len() & 1 == 0 { + if program_section.ops.len() & 1 != 0 { program_section.ops.push(AllocatedOp { opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, comment: "word-alignment of data section".into(), @@ -740,7 +740,7 @@ impl<'sc> RegisterAllocatedAsmSet<'sc> { } => { // ensure there's an even number of ops so the // data section offset is valid - if program_section.ops.len() & 1 == 0 { + if program_section.ops.len() & 1 != 0 { program_section.ops.push(AllocatedOp { opcode: crate::asm_lang::allocated_ops::AllocatedOpcode::NOOP, comment: "word-alignment of data section".into(), From 3cea222c1b2a8c50ee5748eabc77d33b69a219d8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 May 2021 14:21:13 -0700 Subject: [PATCH 54/62] reference fuel_core for register constants --- core_lang/Cargo.toml | 1 + core_lang/src/asm_lang/virtual_ops.rs | 29 ++++++++++++++------------- forc/src/ops/forc_build.rs | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core_lang/Cargo.toml b/core_lang/Cargo.toml index a815339d913..23224886946 100644 --- a/core_lang/Cargo.toml +++ b/core_lang/Cargo.toml @@ -16,3 +16,4 @@ Inflector = "0.11" petgraph = "0.5" uuid-b64 = "0.1" fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } +fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } diff --git a/core_lang/src/asm_lang/virtual_ops.rs b/core_lang/src/asm_lang/virtual_ops.rs index 70a2053e397..0cdc53892ee 100644 --- a/core_lang/src/asm_lang/virtual_ops.rs +++ b/core_lang/src/asm_lang/virtual_ops.rs @@ -65,22 +65,23 @@ pub enum ConstantRegister { impl ConstantRegister { pub(crate) fn to_register_id(&self) -> fuel_asm::RegisterId { + use fuel_vm_rust::consts::*; use ConstantRegister::*; match self { - Zero => 0, - One => 1, - Overflow => 2, - ProgramCounter => 3, - StackStartPointer => 4, - StackPointer => 5, - FramePointer => 6, - HeapPointer => 7, - Error => 8, - GlobalGas => 9, - ContextGas => 10, - Balance => 11, - InstructionStart => 12, - Flags => 13, + Zero => REG_ZERO, + One => REG_ONE, + Overflow => REG_OF, + ProgramCounter => REG_PC, + StackStartPointer => REG_SSP, + StackPointer => REG_SP, + FramePointer => REG_FP, + HeapPointer => REG_HP, + Error => REG_ERR, + GlobalGas => REG_GGAS, + ContextGas => REG_CGAS, + Balance => REG_BAL, + InstructionStart => REG_IS, + Flags => REG_FLAG, DataSectionStart => { (crate::asm_generation::compiler_constants::DATA_SECTION_REGISTER) as fuel_asm::RegisterId diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index dab1ff6323b..bb4271aee3f 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -80,7 +80,7 @@ pub fn build(path: Option) -> Result<(), String> { let main_file = get_main_file(&manifest, &manifest_dir)?; let main = compile(main_file, &manifest.project.name, &namespace)?; - println!("Bytecode size is {} bytes.", main.len() / 4); + println!("Bytecode size is {} bytes.", main.len()); Ok(()) } From e0fe2fc49d71fee1170c398f146d92e25d24036b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 May 2021 15:26:01 -0700 Subject: [PATCH 55/62] new ssh key --- .github/workflows/cargo_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cargo_test.yml b/.github/workflows/cargo_test.yml index 688bc1919c4..7c75be40b50 100644 --- a/.github/workflows/cargo_test.yml +++ b/.github/workflows/cargo_test.yml @@ -24,6 +24,7 @@ jobs: uses: webfactory/ssh-agent@v0.5.2 with: ssh-private-key: | + ${{ secrets.PRIVATE_KEY_FUEL_CORE }} ${{ secrets.PRIVATE_KEY_FUEL_ASM }} ${{ secrets.PRIVATE_KEY_FUEL_TX }} From 6f0127d4386536f71807eae5f0f6b606f6c63f43 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 May 2021 15:29:57 -0700 Subject: [PATCH 56/62] git change rust version --- .github/workflows/cargo_test.yml | 2 +- core_lang/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cargo_test.yml b/.github/workflows/cargo_test.yml index 7c75be40b50..394a47053ea 100644 --- a/.github/workflows/cargo_test.yml +++ b/.github/workflows/cargo_test.yml @@ -32,7 +32,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.50.0 + toolchain: 1.53.0 override: true - name: Install rustfmt diff --git a/core_lang/README.md b/core_lang/README.md index b678d07dfd4..76a99946941 100644 --- a/core_lang/README.md +++ b/core_lang/README.md @@ -8,4 +8,4 @@ It is recommended to run this compiler from the `forc` executable, which can be ## Minimum supported Rust version -As of now, this code was developed on and is guaranteed to run on Rust 1.50 stable. +As of now, this code was developed on and is guaranteed to run on Rust 1.53 stable. From 72e49a49c99308e1e07b220fc428a0ba6e989dc3 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 May 2021 15:34:25 -0700 Subject: [PATCH 57/62] wrong rust version --- .github/workflows/cargo_test.yml | 2 +- core_lang/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cargo_test.yml b/.github/workflows/cargo_test.yml index 394a47053ea..5cd3e64237c 100644 --- a/.github/workflows/cargo_test.yml +++ b/.github/workflows/cargo_test.yml @@ -32,7 +32,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.53.0 + toolchain: 1.52.0 override: true - name: Install rustfmt diff --git a/core_lang/README.md b/core_lang/README.md index 76a99946941..e1187506bdd 100644 --- a/core_lang/README.md +++ b/core_lang/README.md @@ -8,4 +8,4 @@ It is recommended to run this compiler from the `forc` executable, which can be ## Minimum supported Rust version -As of now, this code was developed on and is guaranteed to run on Rust 1.53 stable. +As of now, this code was developed on and is guaranteed to run on Rust 1.52 stable. From e6f5cf8f5d2669fa955b4055ac51a75cad17fd86 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Mon, 31 May 2021 05:53:11 -0700 Subject: [PATCH 58/62] update lockfile --- Cargo.lock | 1504 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1504 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..9b22ec478fb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1504 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static 1.4.0", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + +[[package]] +name = "async-trait" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "auto_impl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cbf586c80ada5e5ccdecae80d3ef0854f224e2dd74435f8d87e6831b8d0a38" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c4a342b450b268e1be8036311e2c613d7f8a7ed31214dff1cc3b60852a3168d" +dependencies = [ + "byteorder", + "safemem", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding 0.2.1", + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "core_lang" +version = "0.1.0" +dependencies = [ + "Inflector", + "either", + "fuel-asm", + "fuel-vm-rust", + "pest", + "pest_derive", + "petgraph", + "thiserror", + "uuid-b64", +] + +[[package]] +name = "cpufeatures" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" +dependencies = [ + "libc", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if 1.0.0", + "num_cpus", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "error-chain" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +dependencies = [ + "backtrace", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "forc" +version = "0.1.0" +dependencies = [ + "core_lang", + "line-col", + "pest", + "serde", + "source-span", + "structopt", + "termcolor", + "toml", + "whoami", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuel-asm" +version = "0.1.0" +source = "git+ssh://git@github.com/FuelLabs/fuel-asm.git#2a0c2f8482434f438bea47fb7f0b08408a29112c" + +[[package]] +name = "fuel-tx" +version = "0.1.0" +source = "git+ssh://git@github.com/FuelLabs/fuel-tx.git#47c98bc74ac0e3a753fcb3a73850db1095de0fab" +dependencies = [ + "fuel-asm", + "itertools", + "sha2", +] + +[[package]] +name = "fuel-vm-rust" +version = "0.1.0" +source = "git+ssh://git@github.com/FuelLabs/fuel-core.git#34d7066aaf448d5e22ef0eec56df5f1dd0d578f7" +dependencies = [ + "fuel-asm", + "fuel-tx", + "itertools", + "secp256k1", + "sha3", + "tracing", +] + +[[package]] +name = "futures" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" + +[[package]] +name = "futures-io" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" + +[[package]] +name = "futures-macro" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" + +[[package]] +name = "futures-task" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" + +[[package]] +name = "futures-util" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +dependencies = [ + "autocfg", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "httparse" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inlinable_string" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77" +dependencies = [ + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" + +[[package]] +name = "line-col" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e69cdf6b85b5c8dce514f694089a2cf8b1a702f6cd28607bcb3cf296c9778db" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "lsp-types" +version = "0.89.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48b8a871b0a450bcec0e26d74a59583c8173cb9fb7d7f98889e18abb84838e0f" +dependencies = [ + "bitflags", + "serde", + "serde_json", + "serde_repr", + "url", +] + +[[package]] +name = "lspower" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3167a7555dbacd9ddf3c42eefcc2d4d5e46bb79cd1702bb25d4152bdf537d4b4" +dependencies = [ + "anyhow", + "async-trait", + "auto_impl", + "bytes", + "dashmap", + "futures", + "httparse", + "log", + "lsp-types", + "lspower-macros", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "twoway", +] + +[[package]] +name = "lspower-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1d48da0e4a6100b4afd52fae99f36d47964a209624021280ad9ffdd410e83d" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "object" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "ropey" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f3ef16589fdbb3e8fbce3dca944c08e61f39c7f16064b21a257d68ea911a83" +dependencies = [ + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safemem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" + +[[package]] +name = "secp256k1" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee5070fdc6f26ca5be6dcfc3d07c76fdb974a63a8b246b459854274145f5a258" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e4b6455ee49f5901c8985b88f98fb0a0e1d90a6661f5a03f4888bd987dad29" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.0", +] + +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "source-span" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403a235b99be82b668acac73519cc19f3cf5be5ed0be388eda888261bb75536b" +dependencies = [ + "termion", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static 1.4.0", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sway-server" +version = "0.1.0" +dependencies = [ + "core_lang", + "dashmap", + "lspower", + "pest", + "ropey", + "serde_json", + "tokio", +] + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termion" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] + +[[package]] +name = "test_suite" +version = "0.1.0" +dependencies = [ + "forc", + "fuel-asm", + "fuel-tx", + "fuel-vm-rust", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a38d31d7831c6ed7aad00aa4c12d9375fd225a6dd77da1d25b707346319a975" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +dependencies = [ + "lazy_static 1.4.0", +] + +[[package]] +name = "twoway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47" +dependencies = [ + "memchr", + "unchecked-index", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unchecked-index" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33717dca7ac877f497014e10d73f3acf948c342bee31b5ca7892faf94ccc6b49" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + +[[package]] +name = "uuid" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" +dependencies = [ + "cfg-if 0.1.10", + "rand", +] + +[[package]] +name = "uuid-b64" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b9cb172a0b1f4e0bf63b807d67ff9ff0d9fba2bcc5f30d3aacb7240e02fc1b" +dependencies = [ + "base64", + "error-chain", + "inlinable_string", + "lazy_static 0.2.11", + "uuid", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static 1.4.0", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abacf325c958dfeaf1046931d37f2a901b6dfe0968ee965a29e94c6766b2af6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From 490bcd9f87b18b86e0facd4d95ef5dfb13a30b15 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Mon, 31 May 2021 07:57:40 -0700 Subject: [PATCH 59/62] fix jump and enum instantiation bugs --- Cargo.lock | 16 +++++++-- .../expression/enum_instantiation.rs | 2 +- core_lang/src/asm_generation/finalized_asm.rs | 1 + core_lang/src/asm_generation/mod.rs | 36 +++++++++++++++---- core_lang/src/asm_lang/allocated_ops.rs | 3 +- core_lang/src/parse_tree/literal.rs | 23 ++++++++---- example_project/fuel_project/src/main.sw | 2 +- test_suite/Cargo.toml | 3 +- .../test_programs/script_2/Forc.toml | 2 +- .../test_programs/script_3/Forc.toml | 2 +- .../test_programs/script_3/src/main.sw | 3 +- 11 files changed, 71 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b22ec478fb..752aa9a53d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ "Inflector", "either", "fuel-asm", - "fuel-vm-rust", + "fuel-vm-rust 0.1.0 (git+ssh://git@github.com/FuelLabs/fuel-core.git)", "pest", "pest_derive", "petgraph", @@ -341,6 +341,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "fuel-vm-rust" +version = "0.1.0" +dependencies = [ + "fuel-asm", + "fuel-tx", + "itertools", + "secp256k1", + "sha3", + "tracing", +] + [[package]] name = "fuel-vm-rust" version = "0.1.0" @@ -1151,7 +1163,7 @@ dependencies = [ "forc", "fuel-asm", "fuel-tx", - "fuel-vm-rust", + "fuel-vm-rust 0.1.0", ] [[package]] diff --git a/core_lang/src/asm_generation/expression/enum_instantiation.rs b/core_lang/src/asm_generation/expression/enum_instantiation.rs index da6998c69e1..9988d897524 100644 --- a/core_lang/src/asm_generation/expression/enum_instantiation.rs +++ b/core_lang/src/asm_generation/expression/enum_instantiation.rs @@ -56,7 +56,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>( asm_buf.push(Op::unowned_stack_allocate_memory( VirtualImmediate24::new_unchecked( - size_of_enum, + size_of_enum * 8, "this size is manually checked to be lower than 2^24", ), )); diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index b36057cfa33..7b33d19a14f 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -66,6 +66,7 @@ fn to_bytecode<'sc>( half_word_ix += 2; } Either::Left(mut op) => { + println!("Serializing {:?}", op); op.read(&mut buf[half_word_ix * 4..]) .expect("Failed to write to in-memory buffer."); half_word_ix += 1; diff --git a/core_lang/src/asm_generation/mod.rs b/core_lang/src/asm_generation/mod.rs index 5e3688e7199..71227e48adf 100644 --- a/core_lang/src/asm_generation/mod.rs +++ b/core_lang/src/asm_generation/mod.rs @@ -348,20 +348,20 @@ pub struct DataSection<'sc> { impl<'sc> DataSection<'sc> { /// Given a [DataId], calculate the offset _from the beginning of the data section_ to the data - /// in words. + /// in bytes. pub(crate) fn offset_to_id(&self, id: &DataId) -> usize { self.value_pairs .iter() .take(id.0 as usize) - .map(|x| x.as_type().stack_size_of()) - .sum::() as usize + .map(|x| x.to_bytes().len()) + .sum() } pub(crate) fn serialize_to_bytes(&self) -> Vec { // not the exact right capacity but serves as a lower bound let mut buf = Vec::with_capacity(self.value_pairs.len()); for val in &self.value_pairs { - buf.append(&mut val.to_bytes()); + buf.append(&mut val.to_bytes().to_vec()); } buf } @@ -812,6 +812,31 @@ fn convert_node_to_asm<'sc>( errors, ) } + TypedAstNodeContent::ReturnStatement(exp) => { + // if a return register was specified, we use it. If not, we generate a register but + // it is going to get thrown away later (in coalescing) as it is never read + let return_register = if let Some(return_register) = return_register { + return_register.clone() + } else { + register_sequencer.next() + }; + let ops = type_check!( + convert_expression_to_asm( + &exp.expr, + namespace, + &return_register, + register_sequencer + ), + return err(warnings, errors), + warnings, + errors + ); + ok( + NodeAsmResult::ReturnStatement { asm: ops }, + warnings, + errors, + ) + } _ => { errors.push(CompileError::Unimplemented( "The ASM for this construct has not been written yet.", @@ -850,6 +875,7 @@ fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op<'static>; 6 comment: "data section offset".into(), owning_span: None, }, + Op::unowned_jump_label_comment(label, "end of metadata"), // word 3 -- load the data offset into $ds Op { opcode: Either::Left(VirtualOp::DataSectionRegisterLoadPlaceholder), @@ -866,7 +892,5 @@ fn build_preamble(register_sequencer: &mut RegisterSequencer) -> [Op<'static>; 6 comment: "".into(), owning_span: None, }, - // word 3 - Op::unowned_jump_label_comment(label, "end of metadata"), ] } diff --git a/core_lang/src/asm_lang/allocated_ops.rs b/core_lang/src/asm_lang/allocated_ops.rs index 5752e9e27bd..3e4484b541b 100644 --- a/core_lang/src/asm_lang/allocated_ops.rs +++ b/core_lang/src/asm_lang/allocated_ops.rs @@ -340,7 +340,8 @@ impl<'sc> AllocatedOp<'sc> { fn realize_lw(dest: &AllocatedRegister, data_id: &DataId, data_section: &DataSection) -> VmOp { let dest = dest.to_register_id(); - let offset = data_section.offset_to_id(data_id) as u64; + // all data is word-aligned right now, and `offset_to_id` returns the offset in bytes + let offset = (data_section.offset_to_id(data_id) / 8) as u64; let offset = match VirtualImmediate12::new(offset, Span::new(" ", 0, 0).unwrap()) { Ok ( value ) => value, Err (_) => panic!("Unable to offset into the data section more than 2^12 bits. Unsupported data section length.") diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index c80b8caff50..3470ff68691 100644 --- a/core_lang/src/parse_tree/literal.rs +++ b/core_lang/src/parse_tree/literal.rs @@ -144,15 +144,24 @@ impl<'sc> Literal<'sc> { Err(compile_err) => err(Vec::new(), vec![compile_err]), } } - pub(crate) fn to_bytes(&self) -> Vec { + /// Converts a literal to a big-endian representation. This is padded to words. + pub(crate) fn to_bytes(&self) -> [u8; 8] { use Literal::*; match self { - // TODO are we big endian? - U8(val) => val.to_be_bytes().to_vec(), - U16(val) => val.to_be_bytes().to_vec(), - U32(val) => val.to_be_bytes().to_vec(), - U64(val) => val.to_be_bytes().to_vec(), - Boolean(b) => (if *b { 1u64 } else { 0u64 }).to_be_bytes().to_vec(), + U8(val) => [0, 0, 0, 0, 0, 0, 0, val.to_be_bytes()[0]], + U16(val) => { + let bytes = val.to_be_bytes(); + [0, 0, 0, 0, 0, 0, bytes[0], bytes[1]] + } + U32(val) => { + let bytes = val.to_be_bytes(); + [0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3]] + } + U64(val) => val.to_be_bytes(), + Boolean(b) => { + let bytes = (if *b { 1u64 } else { 0u64 }).to_be_bytes(); + [0, 0, 0, 0, 0, 0, 0, bytes[0]] + } a => todo!("{:?}", a), } } diff --git a/example_project/fuel_project/src/main.sw b/example_project/fuel_project/src/main.sw index 95607ae29fb..84a7820e3ab 100644 --- a/example_project/fuel_project/src/main.sw +++ b/example_project/fuel_project/src/main.sw @@ -1,4 +1,4 @@ -script; +predicate; struct Rgb { red: u64, green: u64, diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index 62277ba82d3..e9da974abce 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -9,5 +9,6 @@ edition = "2018" [dependencies] forc = { path = "../forc" } fuel-tx = { git = "ssh://git@github.com/FuelLabs/fuel-tx.git" } -fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } +# fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } +fuel-vm-rust = { path = "../../fuel-core" } fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml index d9c16b43ac1..75bf79bae15 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml +++ b/test_suite/src/e2e_vm_tests/test_programs/script_2/Forc.toml @@ -1,7 +1,7 @@ [project] author = "Alexander Hansen " license = "MIT" -name = "script_1" +name = "script_2" entry = "main.sw" diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml index d9c16b43ac1..f4dbab55663 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml +++ b/test_suite/src/e2e_vm_tests/test_programs/script_3/Forc.toml @@ -1,7 +1,7 @@ [project] author = "Alexander Hansen " license = "MIT" -name = "script_1" +name = "script_3" entry = "main.sw" diff --git a/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw b/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw index a222cce5d42..66b9f49e96d 100644 --- a/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw +++ b/test_suite/src/e2e_vm_tests/test_programs/script_3/src/main.sw @@ -1,7 +1,7 @@ script; // This test tests two-pass compilation and allowing usages before declarations. -fn main() { +fn main() -> bool { // fn before decl let x = the_number_five(); // enum before decl @@ -11,6 +11,7 @@ fn main() { a: true, b: false }; + return true; } struct FuelStruct { From da1d1f4925121648b473c82b76feb725a473ccfc Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Mon, 31 May 2021 17:50:19 -0700 Subject: [PATCH 60/62] fix toml --- test_suite/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index e9da974abce..62277ba82d3 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -9,6 +9,5 @@ edition = "2018" [dependencies] forc = { path = "../forc" } fuel-tx = { git = "ssh://git@github.com/FuelLabs/fuel-tx.git" } -# fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } -fuel-vm-rust = { path = "../../fuel-core" } +fuel-vm-rust = { git = "ssh://git@github.com/FuelLabs/fuel-core.git" } fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" } From b59f3ebeed989dafccaead29aca1e07cb3b06358 Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Mon, 31 May 2021 17:50:31 -0700 Subject: [PATCH 61/62] lockfile --- Cargo.lock | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 752aa9a53d1..9b22ec478fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ "Inflector", "either", "fuel-asm", - "fuel-vm-rust 0.1.0 (git+ssh://git@github.com/FuelLabs/fuel-core.git)", + "fuel-vm-rust", "pest", "pest_derive", "petgraph", @@ -341,18 +341,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "fuel-vm-rust" -version = "0.1.0" -dependencies = [ - "fuel-asm", - "fuel-tx", - "itertools", - "secp256k1", - "sha3", - "tracing", -] - [[package]] name = "fuel-vm-rust" version = "0.1.0" @@ -1163,7 +1151,7 @@ dependencies = [ "forc", "fuel-asm", "fuel-tx", - "fuel-vm-rust 0.1.0", + "fuel-vm-rust", ] [[package]] From ab0327dd362b2799bdcb18d9aefc03e12f11536d Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Tue, 1 Jun 2021 07:42:33 -0700 Subject: [PATCH 62/62] code review feedback --- core_lang/src/asm_generation/finalized_asm.rs | 1 - core_lang/src/parse_tree/literal.rs | 1 + test_suite/src/e2e_vm_tests/harness.rs | 10 ++-------- test_suite/src/e2e_vm_tests/mod.rs | 12 +++--------- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/core_lang/src/asm_generation/finalized_asm.rs b/core_lang/src/asm_generation/finalized_asm.rs index 7b33d19a14f..b36057cfa33 100644 --- a/core_lang/src/asm_generation/finalized_asm.rs +++ b/core_lang/src/asm_generation/finalized_asm.rs @@ -66,7 +66,6 @@ fn to_bytecode<'sc>( half_word_ix += 2; } Either::Left(mut op) => { - println!("Serializing {:?}", op); op.read(&mut buf[half_word_ix * 4..]) .expect("Failed to write to in-memory buffer."); half_word_ix += 1; diff --git a/core_lang/src/parse_tree/literal.rs b/core_lang/src/parse_tree/literal.rs index 3470ff68691..ffc75beb50a 100644 --- a/core_lang/src/parse_tree/literal.rs +++ b/core_lang/src/parse_tree/literal.rs @@ -19,6 +19,7 @@ pub enum Literal<'sc> { } impl<'sc> Literal<'sc> { + #[allow(dead_code)] pub(crate) fn as_type(&self) -> ResolvedType<'sc> { use Literal::*; match self { diff --git a/test_suite/src/e2e_vm_tests/harness.rs b/test_suite/src/e2e_vm_tests/harness.rs index 4489bc491a0..a54765067ba 100644 --- a/test_suite/src/e2e_vm_tests/harness.rs +++ b/test_suite/src/e2e_vm_tests/harness.rs @@ -5,7 +5,7 @@ use fuel_vm_rust::interpreter::Interpreter; /// Very basic check that code does indeed run in the VM. /// `true` if it does, `false` if not. -pub(crate) fn runs_in_vm(file_name: &str) -> bool { +pub(crate) fn runs_in_vm(file_name: &str) { let script = compile_to_bytes(file_name); let gas_price = 10; let gas_limit = 10000; @@ -26,13 +26,7 @@ pub(crate) fn runs_in_vm(file_name: &str) -> bool { ); let block_height = (u32::MAX >> 1) as u64; tx.validate(block_height).unwrap(); - match Interpreter::execute_tx(tx) { - Ok(_) => true, - Err(e) => { - println!("Failed: {}", e); - false - } - } + Interpreter::execute_tx(tx).unwrap(); } /// Returns `true` if a file compiled without any errors or warnings, diff --git a/test_suite/src/e2e_vm_tests/mod.rs b/test_suite/src/e2e_vm_tests/mod.rs index 441f6c68234..1651f9a1394 100644 --- a/test_suite/src/e2e_vm_tests/mod.rs +++ b/test_suite/src/e2e_vm_tests/mod.rs @@ -2,13 +2,7 @@ mod harness; pub fn run() { let project_names = vec!["script_1", "script_2", "script_3"]; - assert!(project_names.into_iter().all(|name| { - let result = crate::e2e_vm_tests::harness::runs_in_vm(name); - if !result { - println!("E2E Failure: {} should have run in the VM.", name); - false - } else { - true - } - })); + project_names.into_iter().for_each(|name| { + crate::e2e_vm_tests::harness::runs_in_vm(name); + }); }