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

Register allocation and virtualized opcodes #50

Merged
merged 33 commits into from
May 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 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
a2a3ee7
code review feedback
May 23, 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 change: 1 addition & 0 deletions core_lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ either = "1.6"
Inflector = "0.11"
petgraph = "0.5"
uuid-b64 = "0.1"
fuel-asm = { git = "ssh://git@github.com/FuelLabs/fuel-asm.git" }
5 changes: 4 additions & 1 deletion core_lang/src/asm_generation/compiler_constants.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@

pub(crate) const NUM_FREE_REGISTERS: u8 = 48;
pub(crate) const TWENTY_FOUR_BITS: u64 = 0b111_111_111_111_111_111_111_111;
pub(crate) const EIGHTEEN_BITS: u64 = 0b111_111_111_111_111_111;
pub(crate) const TWELVE_BITS: u64 = 0b111_111_111_111;
55 changes: 36 additions & 19 deletions core_lang/src/asm_generation/expression/enum_instantiation.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
use crate::asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer};
use crate::asm_lang::{ConstantRegister, Op, Opcode, RegisterId};
use crate::asm_generation::{
compiler_constants::*, convert_expression_to_asm, AsmNamespace, RegisterSequencer,
};
use crate::asm_lang::{
virtual_ops::{
ConstantRegister, VirtualImmediate12, VirtualImmediate18, VirtualImmediate24, VirtualOp,
VirtualRegister,
},
Op,
};
use crate::error::*;
use crate::semantic_analysis::ast_node::TypedEnumDeclaration;
use crate::semantic_analysis::TypedExpression;
use crate::Literal;
use crate::{CompileResult, Ident};
use std::convert::TryFrom;

pub(crate) fn convert_enum_instantiation_to_asm<'sc>(
decl: &TypedEnumDeclaration<'sc>,
_variant_name: &Ident<'sc>,
tag: usize,
contents: &Option<Box<TypedExpression<'sc>>>,
return_register: &RegisterId,
return_register: &VirtualRegister,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
Expand All @@ -35,33 +42,43 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>(
// copy stack pointer into pointer register
asm_buf.push(Op::unowned_register_move_comment(
pointer_register.clone(),
RegisterId::Constant(ConstantRegister::StackPointer),
VirtualRegister::Constant(ConstantRegister::StackPointer),
"load $sp for enum pointer",
));
let size_of_enum = 1 /* tag */ + decl.as_type().stack_size_of();
let size_of_enum: u32 = match u32::try_from(size_of_enum) {
Ok(o) if o < 16777216 /* 2^24 */ => o,
_ => {
errors.push(CompileError::Unimplemented(
"Stack variables which exceed 2^24 (16777216) words in size are not supported yet.",
decl.clone().span,
));
return err(warnings, errors);
}
};
if size_of_enum > EIGHTEEN_BITS {
errors.push(CompileError::Unimplemented(
"Stack variables which exceed 2^18 words in size are not supported yet.",
decl.clone().span,
));
return err(warnings, errors);
}

asm_buf.push(Op::unowned_stack_allocate_memory(size_of_enum));
asm_buf.push(Op::unowned_stack_allocate_memory(
VirtualImmediate24::new_unchecked(
size_of_enum,
"this size is manually checked to be lower than 2^24",
),
));
// initialize all the memory to 0
// there are only 18 bits of immediate in MCLI so we need to do this in multiple passes,
// This is not yet implemented, so instead we just limit enum size to 2^18 words
asm_buf.push(Op::new(
Opcode::MemClearImmediate(pointer_register.clone(), size_of_enum),
VirtualOp::MCLI(
pointer_register.clone(),
VirtualImmediate18::new_unchecked(
size_of_enum,
"the enum was manually checked to be under 2^18 words in size",
),
),
decl.clone().span,
));
// write the tag
// step 2
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,
));

Expand All @@ -86,7 +103,7 @@ pub(crate) fn convert_enum_instantiation_to_asm<'sc>(
asm_buf.push(Op::write_register_to_memory_comment(
pointer_register.clone(),
return_register.clone(),
1, /* offset by 1 because the tag was already written */
VirtualImmediate12::new_unchecked(1, "this is the constant 1; infallible"), // offset by 1 because the tag was already written
instantiation.span.clone(),
format!("{} enum contents", decl.name.primary_name),
));
Expand Down
9 changes: 6 additions & 3 deletions core_lang/src/asm_generation/expression/if_exp.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,7 +13,7 @@ pub(crate) fn convert_if_exp_to_asm<'sc>(
condition: &TypedExpression<'sc>,
then: &TypedExpression<'sc>,
r#else: &Option<Box<TypedExpression<'sc>>>,
return_register: &RegisterId,
return_register: &VirtualRegister,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
Expand Down Expand Up @@ -50,7 +53,7 @@ pub(crate) fn convert_if_exp_to_asm<'sc>(
// if the condition is not true, jump to the else branch (if there is one).
asm_buf.push(Op::jump_if_not_equal(
condition_result.clone(),
RegisterId::Constant(ConstantRegister::One),
VirtualRegister::Constant(ConstantRegister::One),
if r#else.is_some() {
else_label.clone()
} else {
Expand Down
34 changes: 21 additions & 13 deletions core_lang/src/asm_generation/expression/mod.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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<Op<'sc>>> {
let mut warnings = vec![];
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -149,11 +152,16 @@ pub(crate) fn convert_expression_to_asm<'sc>(
}
Ok(o) => Some(o),
})
.collect::<Vec<RegisterId>>();
.collect::<Vec<VirtualRegister>>();

// 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
Expand Down Expand Up @@ -259,12 +267,12 @@ pub(crate) fn convert_expression_to_asm<'sc>(
/// or finds nothing and returns `None`.
fn realize_register(
register_name: &str,
mapping_of_real_registers_to_declared_names: &HashMap<&str, RegisterId>,
) -> Option<RegisterId> {
mapping_of_real_registers_to_declared_names: &HashMap<&str, VirtualRegister>,
) -> Option<VirtualRegister> {
match mapping_of_real_registers_to_declared_names.get(register_name) {
Some(x) => Some(x.clone()),
None => match ConstantRegister::parse_register_name(register_name) {
Some(x) => Some(RegisterId::Constant(x)),
Some(x) => Some(VirtualRegister::Constant(x)),
None => None,
},
}
Expand All @@ -275,7 +283,7 @@ pub(crate) fn convert_code_block_to_asm<'sc>(
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
// Where to put the return value of this code block, if there was any.
return_register: Option<&RegisterId>,
return_register: Option<&VirtualRegister>,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let mut asm_buf: Vec<Op> = vec![];
let mut warnings = vec![];
Expand Down Expand Up @@ -305,11 +313,11 @@ pub(crate) fn convert_code_block_to_asm<'sc>(
ok(asm_buf, warnings, errors)
}

/// Initializes [Literal] `lit` into [RegisterId] `return_register`.
/// Initializes [Literal] `lit` into [VirtualRegister] `return_register`.
fn convert_literal_to_asm<'sc>(
lit: &Literal<'sc>,
namespace: &mut AsmNamespace<'sc>,
return_register: &RegisterId,
return_register: &VirtualRegister,
_register_sequencer: &mut RegisterSequencer,
span: Span<'sc>,
) -> Vec<Op<'sc>> {
Expand All @@ -329,7 +337,7 @@ fn convert_fn_app_to_asm<'sc>(
arguments: &[(Ident<'sc>, TypedExpression<'sc>)],
function_body: &TypedCodeBlock<'sc>,
parent_namespace: &mut AsmNamespace<'sc>,
return_register: &RegisterId,
return_register: &VirtualRegister,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let mut warnings = vec![];
Expand All @@ -338,7 +346,7 @@ fn convert_fn_app_to_asm<'sc>(
// Make a local namespace so that the namespace of this function does not pollute the outer
// scope
let mut namespace = parent_namespace.clone();
let mut args_and_registers: HashMap<Ident<'sc>, RegisterId> = Default::default();
let mut args_and_registers: HashMap<Ident<'sc>, VirtualRegister> = Default::default();
// evaluate every expression being passed into the function
for (name, arg) in arguments {
let return_register = register_sequencer.next();
Expand Down
45 changes: 20 additions & 25 deletions core_lang/src/asm_generation/expression/structs.rs
Original file line number Diff line number Diff line change
@@ -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>,
Expand Down Expand Up @@ -62,38 +64,31 @@ pub(crate) fn convert_struct_expression_to_asm<'sc>(
let struct_beginning_pointer = register_sequencer.next();
asm_buf.push(Op::unowned_register_move(
struct_beginning_pointer.clone(),
RegisterId::Constant(ConstantRegister::StackPointer),
VirtualRegister::Constant(ConstantRegister::StackPointer),
));

// step 2
// decide how many call frame extensions are needed based on the size of the struct
// and how many bits can be put in a single cfei op
let twenty_four_bits = 0b111111111111111111111111;
let number_of_allocations_necessary = (total_size / twenty_four_bits) + 1;
// limit struct size to 12 bits for now, for simplicity
let twelve_bits = super::compiler_constants::TWELVE_BITS;
let number_of_allocations_necessary = (total_size / twelve_bits) + 1;

// construct the allocation ops
for allocation_index in 0..number_of_allocations_necessary {
let left_to_allocate = total_size - (allocation_index * twenty_four_bits);
let this_allocation = if left_to_allocate > twenty_four_bits {
twenty_four_bits
let left_to_allocate = total_size - (allocation_index * twelve_bits);
let this_allocation = if left_to_allocate > twelve_bits {
twelve_bits
} else {
left_to_allocate
};
// since the size of `this_allocation` is bound by the size of 2^24, we know that
// downcasting to a u32 is safe.
// However, since we may change the twenty four bits to something else, we want to check
// anyway
let val_as_u32: u32 = match this_allocation.try_into() {
Ok(o) => o,
Err(_) => {
errors.push(CompileError::Unimplemented(
"This struct is too large, and would not fit in one call frame extension.",
struct_name.span.clone(),
));
return err(warnings, errors);
}
};
asm_buf.push(Op::unowned_stack_allocate_memory(val_as_u32));
// we call `new_unchecked` here because we have validated the size is okay above
asm_buf.push(Op::unowned_stack_allocate_memory(
VirtualImmediate24::new_unchecked(
this_allocation,
"struct size was checked manually to be within 12 bits",
),
));
}

// step 3
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
Loading