mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Switch over to function_builder
This commit is contained in:
parent
bca9f31c58
commit
74e3239a1c
6 changed files with 204 additions and 291 deletions
|
@ -1,9 +1,6 @@
|
|||
use bumpalo::collections::Vec;
|
||||
use parity_wasm::builder;
|
||||
use parity_wasm::builder::{CodeLocation, FunctionDefinition, ModuleBuilder, SignatureBuilder};
|
||||
use parity_wasm::elements::{
|
||||
BlockType, Instruction, Instruction::*, Instructions, Local, ValueType,
|
||||
};
|
||||
use parity_wasm::builder::{CodeLocation, FunctionDefinition, ModuleBuilder};
|
||||
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::low_level::LowLevel;
|
||||
|
@ -11,12 +8,10 @@ use roc_module::symbol::Symbol;
|
|||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
|
||||
use crate::code_builder::CodeBuilder;
|
||||
use crate::function_builder::{BlockType, FunctionBuilder, ValueType};
|
||||
use crate::layout::WasmLayout;
|
||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||
use crate::{
|
||||
copy_memory, pop_stack_frame, push_stack_frame, CopyMemoryConfig, Env, LocalId, PTR_TYPE,
|
||||
};
|
||||
use crate::{copy_memory, CopyMemoryConfig, Env, LocalId, PTR_TYPE};
|
||||
|
||||
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
||||
// Follow Emscripten's example by using 1kB (4 bytes would probably do)
|
||||
|
@ -37,7 +32,7 @@ pub struct WasmBackend<'a> {
|
|||
proc_symbol_map: MutMap<Symbol, CodeLocation>,
|
||||
|
||||
// Function level
|
||||
code_builder: CodeBuilder<'a>,
|
||||
code_builder: FunctionBuilder<'a>,
|
||||
storage: Storage<'a>,
|
||||
|
||||
/// how many blocks deep are we (used for jumps)
|
||||
|
@ -61,7 +56,7 @@ impl<'a> WasmBackend<'a> {
|
|||
joinpoint_label_map: MutMap::default(),
|
||||
|
||||
// Functions
|
||||
code_builder: CodeBuilder::new(env.arena),
|
||||
code_builder: FunctionBuilder::new(env.arena),
|
||||
storage: Storage::new(env.arena),
|
||||
}
|
||||
}
|
||||
|
@ -82,21 +77,24 @@ impl<'a> WasmBackend<'a> {
|
|||
pub fn build_proc(&mut self, proc: Proc<'a>, sym: Symbol) -> Result<u32, String> {
|
||||
// println!("\ngenerating procedure {:?}\n", sym);
|
||||
|
||||
let signature_builder = self.start_proc(&proc);
|
||||
// Use parity-wasm to add the signature in "types" and "functions" sections
|
||||
// but no instructions, since we are building our own code section
|
||||
let empty_function_def = self.start_proc(&proc);
|
||||
let location = self.module_builder.push_function(empty_function_def);
|
||||
let function_index = location.body;
|
||||
self.proc_symbol_map.insert(sym, location);
|
||||
|
||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||
|
||||
let function_def = self.finalize_proc(signature_builder);
|
||||
let location = self.module_builder.push_function(function_def);
|
||||
let function_index = location.body;
|
||||
self.proc_symbol_map.insert(sym, location);
|
||||
self.finalize_proc();
|
||||
self.reset();
|
||||
|
||||
// println!("\nfinished generating {:?}\n", sym);
|
||||
|
||||
Ok(function_index)
|
||||
}
|
||||
|
||||
fn start_proc(&mut self, proc: &Proc<'a>) -> SignatureBuilder {
|
||||
fn start_proc(&mut self, proc: &Proc<'a>) -> FunctionDefinition {
|
||||
let ret_layout = WasmLayout::new(&proc.ret_layout);
|
||||
|
||||
let signature_builder = if let WasmLayout::StackMemory { .. } = ret_layout {
|
||||
|
@ -106,7 +104,7 @@ impl<'a> WasmBackend<'a> {
|
|||
} else {
|
||||
let ret_type = ret_layout.value_type();
|
||||
self.start_block(BlockType::Value(ret_type)); // block to ensure all paths pop stack memory (if any)
|
||||
builder::signature().with_result(ret_type)
|
||||
builder::signature().with_result(ret_type.to_parity_wasm())
|
||||
};
|
||||
|
||||
for (layout, symbol) in proc.args {
|
||||
|
@ -117,60 +115,24 @@ impl<'a> WasmBackend<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
signature_builder.with_params(self.storage.arg_types.clone())
|
||||
let parity_params = self.storage.arg_types.iter().map(|t| t.to_parity_wasm());
|
||||
|
||||
let signature = signature_builder.with_params(parity_params).build_sig();
|
||||
|
||||
// parity-wasm FunctionDefinition with no instructions
|
||||
builder::function().with_signature(signature).build()
|
||||
}
|
||||
|
||||
fn finalize_proc(&mut self, signature_builder: SignatureBuilder) -> FunctionDefinition {
|
||||
self.end_block(); // end the block from start_proc, to ensure all paths pop stack memory (if any)
|
||||
fn finalize_proc(&mut self) {
|
||||
// end the block from start_proc, to ensure all paths pop stack memory (if any)
|
||||
self.end_block();
|
||||
|
||||
const STACK_FRAME_INSTRUCTIONS_LEN: usize = 10;
|
||||
let mut final_instructions =
|
||||
std::vec::Vec::with_capacity(self.code_builder.len() + STACK_FRAME_INSTRUCTIONS_LEN);
|
||||
|
||||
if self.storage.stack_frame_size > 0 {
|
||||
push_stack_frame(
|
||||
&mut final_instructions,
|
||||
self.storage.stack_frame_size,
|
||||
self.storage.stack_frame_pointer.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
self.code_builder.finalize_into(&mut final_instructions);
|
||||
|
||||
if self.storage.stack_frame_size > 0 {
|
||||
pop_stack_frame(
|
||||
&mut final_instructions,
|
||||
self.storage.stack_frame_size,
|
||||
self.storage.stack_frame_pointer.unwrap(),
|
||||
);
|
||||
}
|
||||
final_instructions.push(End);
|
||||
|
||||
// Declare local variables (in batches of the same type)
|
||||
let num_locals = self.storage.local_types.len();
|
||||
let mut locals = Vec::with_capacity_in(num_locals, self.env.arena);
|
||||
if num_locals > 0 {
|
||||
let mut batch_type = self.storage.local_types[0];
|
||||
let mut batch_size = 0;
|
||||
for t in &self.storage.local_types {
|
||||
if *t == batch_type {
|
||||
batch_size += 1;
|
||||
} else {
|
||||
locals.push(Local::new(batch_size, batch_type));
|
||||
batch_type = *t;
|
||||
batch_size = 1;
|
||||
}
|
||||
}
|
||||
locals.push(Local::new(batch_size, batch_type));
|
||||
}
|
||||
|
||||
builder::function()
|
||||
.with_signature(signature_builder.build_sig())
|
||||
.body()
|
||||
.with_locals(locals)
|
||||
.with_instructions(Instructions::new(final_instructions))
|
||||
.build() // body
|
||||
.build() // function
|
||||
// Write local declarations and stack frame push/pop code
|
||||
self.code_builder.finalize(
|
||||
&self.storage.local_types,
|
||||
self.storage.stack_frame_size,
|
||||
self.storage.stack_frame_pointer,
|
||||
);
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
|
@ -182,17 +144,17 @@ impl<'a> WasmBackend<'a> {
|
|||
/// start a loop that leaves a value on the stack
|
||||
fn start_loop_with_return(&mut self, value_type: ValueType) {
|
||||
self.block_depth += 1;
|
||||
self.code_builder.push(Loop(BlockType::Value(value_type)));
|
||||
self.code_builder.loop_(BlockType::Value(value_type));
|
||||
}
|
||||
|
||||
fn start_block(&mut self, block_type: BlockType) {
|
||||
self.block_depth += 1;
|
||||
self.code_builder.push(Block(block_type));
|
||||
self.code_builder.block(block_type);
|
||||
}
|
||||
|
||||
fn end_block(&mut self) {
|
||||
self.block_depth -= 1;
|
||||
self.code_builder.push(End);
|
||||
self.code_builder.end();
|
||||
}
|
||||
|
||||
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
|
||||
|
@ -255,7 +217,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
_ => {
|
||||
self.storage.load_symbols(&mut self.code_builder, &[*sym]);
|
||||
self.code_builder.push(Br(self.block_depth)); // jump to end of function (for stack frame pop)
|
||||
self.code_builder.br(self.block_depth); // jump to end of function (for stack frame pop)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,13 +255,13 @@ impl<'a> WasmBackend<'a> {
|
|||
self.storage
|
||||
.load_symbols(&mut self.code_builder, &[*cond_symbol]);
|
||||
|
||||
self.code_builder.push(I32Const(*value as i32));
|
||||
self.code_builder.i32_const(*value as i32);
|
||||
|
||||
// compare the 2 topmost values
|
||||
self.code_builder.push(I32Eq);
|
||||
self.code_builder.i32_eq();
|
||||
|
||||
// "break" out of `i` surrounding blocks
|
||||
self.code_builder.push(BrIf(i as u32));
|
||||
self.code_builder.br_if(i as u32);
|
||||
}
|
||||
|
||||
// if we never jumped because a value matched, we're in the default case
|
||||
|
@ -375,7 +337,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
// jump
|
||||
let levels = self.block_depth - target;
|
||||
self.code_builder.push(Br(levels));
|
||||
self.code_builder.br(levels);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -427,11 +389,8 @@ impl<'a> WasmBackend<'a> {
|
|||
func_sym, sym
|
||||
))?;
|
||||
|
||||
self.code_builder.push_call(
|
||||
function_location.body,
|
||||
wasm_args.len(),
|
||||
has_return_val,
|
||||
);
|
||||
self.code_builder
|
||||
.call(function_location.body, wasm_args.len(), has_return_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -448,25 +407,25 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
fn load_literal(&mut self, lit: &Literal<'a>, layout: &Layout<'a>) -> Result<(), String> {
|
||||
let instruction = match lit {
|
||||
Literal::Bool(x) => I32Const(*x as i32),
|
||||
Literal::Byte(x) => I32Const(*x as i32),
|
||||
match lit {
|
||||
Literal::Bool(x) => self.code_builder.i32_const(*x as i32),
|
||||
Literal::Byte(x) => self.code_builder.i32_const(*x as i32),
|
||||
Literal::Int(x) => match layout {
|
||||
Layout::Builtin(Builtin::Int64) => I64Const(*x as i64),
|
||||
Layout::Builtin(Builtin::Int64) => self.code_builder.i64_const(*x as i64),
|
||||
Layout::Builtin(
|
||||
Builtin::Int32
|
||||
| Builtin::Int16
|
||||
| Builtin::Int8
|
||||
| Builtin::Int1
|
||||
| Builtin::Usize,
|
||||
) => I32Const(*x as i32),
|
||||
) => self.code_builder.i32_const(*x as i32),
|
||||
x => {
|
||||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
||||
}
|
||||
},
|
||||
Literal::Float(x) => match layout {
|
||||
Layout::Builtin(Builtin::Float64) => F64Const((*x as f64).to_bits()),
|
||||
Layout::Builtin(Builtin::Float32) => F32Const((*x as f32).to_bits()),
|
||||
Layout::Builtin(Builtin::Float64) => self.code_builder.f64_const(*x as f64),
|
||||
Layout::Builtin(Builtin::Float32) => self.code_builder.f32_const(*x as f32),
|
||||
x => {
|
||||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
||||
}
|
||||
|
@ -475,7 +434,6 @@ impl<'a> WasmBackend<'a> {
|
|||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
||||
}
|
||||
};
|
||||
self.code_builder.push(instruction);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -546,34 +504,33 @@ impl<'a> WasmBackend<'a> {
|
|||
// Some Roc low-level ops care about wrapping, clipping, sign-extending...
|
||||
// For those, we'll need to pre-process each argument before the main op,
|
||||
// so simple arrays of instructions won't work. But there are common patterns.
|
||||
let instructions: &[Instruction] = match lowlevel {
|
||||
match lowlevel {
|
||||
LowLevel::NumAdd => match return_value_type {
|
||||
ValueType::I32 => &[I32Add],
|
||||
ValueType::I64 => &[I64Add],
|
||||
ValueType::F32 => &[F32Add],
|
||||
ValueType::F64 => &[F64Add],
|
||||
ValueType::I32 => self.code_builder.i32_add(),
|
||||
ValueType::I64 => self.code_builder.i64_add(),
|
||||
ValueType::F32 => self.code_builder.f32_add(),
|
||||
ValueType::F64 => self.code_builder.f64_add(),
|
||||
},
|
||||
LowLevel::NumSub => match return_value_type {
|
||||
ValueType::I32 => &[I32Sub],
|
||||
ValueType::I64 => &[I64Sub],
|
||||
ValueType::F32 => &[F32Sub],
|
||||
ValueType::F64 => &[F64Sub],
|
||||
ValueType::I32 => self.code_builder.i32_sub(),
|
||||
ValueType::I64 => self.code_builder.i64_sub(),
|
||||
ValueType::F32 => self.code_builder.f32_sub(),
|
||||
ValueType::F64 => self.code_builder.f64_sub(),
|
||||
},
|
||||
LowLevel::NumMul => match return_value_type {
|
||||
ValueType::I32 => &[I32Mul],
|
||||
ValueType::I64 => &[I64Mul],
|
||||
ValueType::F32 => &[F32Mul],
|
||||
ValueType::F64 => &[F64Mul],
|
||||
ValueType::I32 => self.code_builder.i32_mul(),
|
||||
ValueType::I64 => self.code_builder.i64_mul(),
|
||||
ValueType::F32 => self.code_builder.f32_mul(),
|
||||
ValueType::F64 => self.code_builder.f64_mul(),
|
||||
},
|
||||
LowLevel::NumGt => {
|
||||
// needs layout of the argument to be implemented fully
|
||||
&[I32GtS]
|
||||
self.code_builder.i32_gt_s()
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("unsupported low-level op {:?}", lowlevel));
|
||||
}
|
||||
};
|
||||
self.code_builder.extend_from_slice(instructions);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue