Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2E Tests using the VM #59

Merged
merged 71 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
655d991
begin register allocator
May 14, 2021
3e56e38
begin reg alloc
May 15, 2021
a7f469e
mutable virtual registers; basic allocation algorithm skeleton
May 16, 2021
5c50072
mutable registers in allocation
May 17, 2021
e407757
pull in fuel-asm official ops
May 18, 2021
f99b127
switching laptops
May 19, 2021
af1c112
begin work on virtual registers and ops
May 19, 2021
8d6f088
daily checkpoint
May 20, 2021
f44b7fd
add AllocatedOp abstraction
May 20, 2021
61705b9
template for parsing ops
May 20, 2021
8d21ff5
allocation algorithm progress
May 20, 2021
cfa3e4a
change op parsing logic
May 21, 2021
483c46d
WIP parsing inline asm to new ops
May 21, 2021
fb5d61a
more op parsing
May 21, 2021
1723aca
finish parsing virtual ops from asm
May 21, 2021
e961af7
start registers method
May 21, 2021
f61845b
register allocation method
May 21, 2021
c36dbb5
convert virtual registers to allocated ones
May 21, 2021
d35f6bf
switch back to organizational labels for jumps
May 21, 2021
3a973af
realized ops
May 22, 2021
d8c0b83
fully allocate registers and resolve labels
May 22, 2021
ea91252
print allocated registers
May 22, 2021
b822630
merge from master; fix reg alloc algo
May 22, 2021
8347dc3
fill in todo!() errors in asm parsing
May 22, 2021
d2b6b37
resolve all todosudo apt-get install vlc in core_lang
May 22, 2021
60c0b1c
switch to ssh for fuel-asm
May 22, 2021
2156b67
resolve warnings
May 22, 2021
2c126bf
fix git url
May 22, 2021
3598dd8
rustfmt
May 22, 2021
f3ea12e
Merge branch 'master' of github.com:FuelLabs/fuel-vm-hll into allocation
May 22, 2021
8344df4
small self-code-review
May 22, 2021
cd6dbb5
resolve module
May 22, 2021
4315bc2
map the virtual opcodes to fuel_asm ops
May 23, 2021
a2a3ee7
code review feedback
May 23, 2021
a2551fb
Merge branch 'allocation' of github.com:FuelLabs/fuel-vm-hll into act…
May 23, 2021
7b87ee0
factor finalized asm out into its own file
May 23, 2021
f3dd962
merge from master
May 23, 2021
31e9f29
realize data section and instructions to bits
May 25, 2021
c39141a
data section offset label
May 26, 2021
5c21259
initial bytecode generation
May 26, 2021
11e6a17
add forc --asm command
May 26, 2021
e3c1d1a
print out the loading of the data section op
May 26, 2021
db009fb
resolve warnings
May 26, 2021
1e27b4c
begin VM tests
May 27, 2021
f23350d
fix register allocater bug
May 27, 2021
1b74664
cleanup
May 27, 2021
980e9c7
fix bad error message
May 27, 2021
6d27fb3
Merge branch 'actual_bytecode' of github.com:fuellabs/fuel-vm-hll int…
May 27, 2021
13507d5
some tiny tweaks
May 28, 2021
05f347c
fix opcode encoding bug
May 28, 2021
54fae97
fix off-by-one error
May 28, 2021
86cb504
Merge branch 'master' of github.com:fuellabs/fuel-vm-hll into vm_tests
May 28, 2021
3c42de9
change all tests to run in the vm
May 28, 2021
2f45da4
proper allocation size for structs
May 28, 2021
88217bd
code review feedback
May 28, 2021
fafb111
Merge branch 'master' of github.com:fuellabs/fuel-vm-hll into actual_…
May 28, 2021
22237bc
use ssh for deps
May 28, 2021
a7c4d4a
undo rename
May 28, 2021
0babc0b
fix doctest
May 28, 2021
01af3d0
fix typo
May 28, 2021
3cea222
reference fuel_core for register constants
May 28, 2021
e0fe2fc
new ssh key
May 28, 2021
6f0127d
git change rust version
May 28, 2021
72e49a4
wrong rust version
May 28, 2021
90e72c0
Merge branch 'actual_bytecode' of github.com:fuellabs/fuel-vm-hll int…
May 29, 2021
e6f5cf8
update lockfile
May 31, 2021
490bcd9
fix jump and enum instantiation bugs
sezna May 31, 2021
901fc57
merge from master
sezna May 31, 2021
da1d1f4
fix toml
sezna Jun 1, 2021
b59f3eb
lockfile
sezna Jun 1, 2021
ab0327d
code review feedback
sezna Jun 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,504 changes: 1,504 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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",
),
));
Expand Down
2 changes: 1 addition & 1 deletion core_lang/src/asm_generation/expression/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
),
));
Expand Down
14 changes: 8 additions & 6 deletions core_lang/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand All @@ -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<u8>> {
use FinalizedAsm::*;
Expand Down Expand Up @@ -54,19 +53,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;
}
}
}
Expand Down
39 changes: 32 additions & 7 deletions core_lang/src/asm_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -346,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::<u64>() as usize
.map(|x| x.to_bytes().len())
.sum()
}

pub(crate) fn serialize_to_bytes(&self) -> Vec<u8> {
// 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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -811,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.",
Expand Down Expand Up @@ -849,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),
Expand All @@ -865,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"),
]
}
3 changes: 2 additions & 1 deletion core_lang/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
24 changes: 17 additions & 7 deletions core_lang/src/parse_tree/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -144,15 +145,24 @@ impl<'sc> Literal<'sc> {
Err(compile_err) => err(Vec::new(), vec![compile_err]),
}
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
/// 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),
}
}
Expand Down
2 changes: 1 addition & 1 deletion example_project/fuel_project/src/main.sw
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
script;
predicate;
struct Rgb {
red: u64,
green: u64,
Expand Down
3 changes: 2 additions & 1 deletion forc/src/cli/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}
4 changes: 2 additions & 2 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub fn print_asm(path: Option<String>) -> Result<(), String> {
Ok(())
}

pub fn build(path: Option<String>) -> Result<(), String> {
pub fn build(path: Option<String>) -> Result<Vec<u8>, String> {
// find manifest directory, even if in subdirectory
let this_dir = if let Some(path) = path {
PathBuf::from(path)
Expand Down Expand Up @@ -82,7 +82,7 @@ pub fn build(path: Option<String>) -> Result<(), String> {

println!("Bytecode size is {} bytes.", main.len());

Ok(())
Ok(main)
}

/// Continually go up in the file tree until a manifest (Forc.toml) is found.
Expand Down
8 changes: 8 additions & 0 deletions stdlib/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions test_suite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,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-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" }
19 changes: 0 additions & 19 deletions test_suite/src/basic_compilation_tests/harness.rs

This file was deleted.

7 changes: 0 additions & 7 deletions test_suite/src/basic_compilation_tests/mod.rs

This file was deleted.

12 changes: 0 additions & 12 deletions test_suite/src/basic_compilation_tests/test_cases.rs

This file was deleted.

50 changes: 50 additions & 0 deletions test_suite/src/e2e_vm_tests/harness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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) {
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();
sezna marked this conversation as resolved.
Show resolved Hide resolved
Interpreter::execute_tx(tx).unwrap();
}

/// 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<u8> {
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 {
sezna marked this conversation as resolved.
Show resolved Hide resolved
Ok(bytes) => bytes,
Err(_) => {
panic!(
"TEST FAILURE: Project \"{}\" failed to compile. ",
file_name
);
}
}
}
8 changes: 8 additions & 0 deletions test_suite/src/e2e_vm_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod harness;

pub fn run() {
let project_names = vec!["script_1", "script_2", "script_3"];
project_names.into_iter().for_each(|name| {
crate::e2e_vm_tests::harness::runs_in_vm(name);
});
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
author = "Alexander Hansen <alex.hansen@fuel.sh>"
license = "MIT"
name = "script_1"
name = "script_2"
entry = "main.sw"


Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
author = "Alexander Hansen <alex.hansen@fuel.sh>"
license = "MIT"
name = "script_1"
name = "script_3"
entry = "main.sw"


Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -11,6 +11,7 @@ fn main() {
a: true,
b: false
};
return true;
}

struct FuelStruct {
Expand Down
Loading