mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Make all Wasm blocks NoResult and always put return value in a local var.
This commit is contained in:
parent
51119c8142
commit
17dc6668ec
6 changed files with 48 additions and 34 deletions
|
@ -269,7 +269,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
// Create a block so we can exit the function without skipping stack frame "pop" code.
|
||||
// We never use the `return` instruction. Instead, we break from this block.
|
||||
self.start_block(BlockType::from(ret_type));
|
||||
self.start_block();
|
||||
|
||||
for (layout, symbol) in proc.args {
|
||||
let arg_layout = WasmLayout::new(layout);
|
||||
|
@ -277,6 +277,11 @@ impl<'a> WasmBackend<'a> {
|
|||
.allocate(&arg_layout, *symbol, StoredValueKind::Parameter);
|
||||
}
|
||||
|
||||
if let Some(ty) = ret_type {
|
||||
let ret_var = self.storage.create_anonymous_local(ty);
|
||||
self.storage.return_var = Some(ret_var);
|
||||
}
|
||||
|
||||
self.module.add_function_signature(Signature {
|
||||
param_types: self.storage.arg_types.clone(),
|
||||
ret_type,
|
||||
|
@ -287,8 +292,12 @@ impl<'a> WasmBackend<'a> {
|
|||
// end the block from start_proc, to ensure all paths pop stack memory (if any)
|
||||
self.end_block();
|
||||
|
||||
if let Some(ret_var) = self.storage.return_var {
|
||||
self.code_builder.get_local(ret_var);
|
||||
}
|
||||
|
||||
// Write local declarations and stack frame push/pop code
|
||||
self.code_builder.build_fn_header(
|
||||
self.code_builder.build_fn_header_and_footer(
|
||||
&self.storage.local_types,
|
||||
self.storage.stack_frame_size,
|
||||
self.storage.stack_frame_pointer,
|
||||
|
@ -301,14 +310,18 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
***********************************************************/
|
||||
|
||||
fn start_loop(&mut self, block_type: BlockType) {
|
||||
fn start_block(&mut self) {
|
||||
// Wasm blocks can have result types, but we don't use them.
|
||||
// You need the right type on the stack when you jump from an inner block to an outer one.
|
||||
// The validation rules can be confusing, and implementing them adds complexity.
|
||||
// Instead we use local variables to move a value from an inner block to an outer one.
|
||||
self.block_depth += 1;
|
||||
self.code_builder.loop_(block_type);
|
||||
self.code_builder.block(BlockType::NoResult);
|
||||
}
|
||||
|
||||
fn start_block(&mut self, block_type: BlockType) {
|
||||
fn start_loop(&mut self) {
|
||||
self.block_depth += 1;
|
||||
self.code_builder.block(block_type);
|
||||
self.code_builder.loop_(BlockType::NoResult);
|
||||
}
|
||||
|
||||
fn end_block(&mut self) {
|
||||
|
@ -389,6 +402,12 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
_ => {
|
||||
self.storage.load_symbols(&mut self.code_builder, &[*sym]);
|
||||
|
||||
// If we have a return value, store it to the return variable
|
||||
// This avoids complications with block result types when returning from nested blocks
|
||||
if let Some(ret_var) = self.storage.return_var {
|
||||
self.code_builder.set_local(ret_var);
|
||||
}
|
||||
}
|
||||
}
|
||||
// jump to the "stack frame pop" code at the end of the function
|
||||
|
@ -417,7 +436,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
// create a block for each branch except the default
|
||||
for _ in 0..branches.len() {
|
||||
self.start_block(BlockType::NoResult)
|
||||
self.start_block()
|
||||
}
|
||||
|
||||
let is_bool = matches!(cond_layout, Layout::Builtin(Builtin::Bool));
|
||||
|
@ -494,7 +513,7 @@ impl<'a> WasmBackend<'a> {
|
|||
jp_param_storages.push(param_storage);
|
||||
}
|
||||
|
||||
self.start_block(BlockType::NoResult);
|
||||
self.start_block();
|
||||
|
||||
self.joinpoint_label_map
|
||||
.insert(*id, (self.block_depth, jp_param_storages));
|
||||
|
@ -502,15 +521,7 @@ impl<'a> WasmBackend<'a> {
|
|||
self.build_stmt(remainder, ret_layout);
|
||||
|
||||
self.end_block();
|
||||
|
||||
// A loop (or any block) needs to declare the type of the value it leaves on the stack on exit.
|
||||
// The runtime needs this to statically validate the program before running it.
|
||||
let loop_block_type = match WasmLayout::new(ret_layout).return_method() {
|
||||
ReturnMethod::Primitive(ty) => BlockType::Value(ty),
|
||||
ReturnMethod::WriteToPointerArg => BlockType::NoResult,
|
||||
ReturnMethod::NoReturnValue => BlockType::NoResult,
|
||||
};
|
||||
self.start_loop(loop_block_type);
|
||||
self.start_loop();
|
||||
|
||||
self.build_stmt(body, ret_layout);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue