mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Merge remote-tracking branch 'origin/trunk' into decimal-literals
This commit is contained in:
commit
5529841d68
404 changed files with 9919 additions and 6764 deletions
|
@ -2,12 +2,13 @@ use bumpalo::{self, collections::Vec};
|
|||
|
||||
use code_builder::Align;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||
use roc_mono::layout::{Layout, LayoutIds};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
use crate::layout::WasmLayout;
|
||||
use crate::low_level::{build_call_low_level, LowlevelBuildResult};
|
||||
use crate::low_level::{decode_low_level, LowlevelBuildResult};
|
||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||
use crate::wasm_module::linking::{
|
||||
DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK,
|
||||
|
@ -22,8 +23,8 @@ use crate::wasm_module::{
|
|||
LocalId, Signature, SymInfo, ValueType,
|
||||
};
|
||||
use crate::{
|
||||
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE,
|
||||
STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
|
||||
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_SIZE,
|
||||
PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
|
||||
};
|
||||
|
||||
/// The memory address where the constants data will be loaded during module instantiation.
|
||||
|
@ -294,16 +295,17 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
_ => {
|
||||
self.storage.load_symbols(&mut self.code_builder, &[*sym]);
|
||||
self.code_builder.br(self.block_depth); // jump to end of function (for stack frame pop)
|
||||
}
|
||||
}
|
||||
// jump to the "stack frame pop" code at the end of the function
|
||||
self.code_builder.br(self.block_depth - 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
cond_layout: _,
|
||||
cond_layout,
|
||||
branches,
|
||||
default_branch,
|
||||
ret_layout: _,
|
||||
|
@ -321,21 +323,45 @@ impl<'a> WasmBackend<'a> {
|
|||
cond_storage,
|
||||
);
|
||||
|
||||
// create (number_of_branches - 1) new blocks.
|
||||
// create a block for each branch except the default
|
||||
for _ in 0..branches.len() {
|
||||
self.start_block(BlockType::NoResult)
|
||||
}
|
||||
|
||||
let is_bool = matches!(cond_layout, Layout::Builtin(Builtin::Int1));
|
||||
let cond_type = WasmLayout::new(cond_layout).value_type();
|
||||
|
||||
// then, we jump whenever the value under scrutiny is equal to the value of a branch
|
||||
for (i, (value, _, _)) in branches.iter().enumerate() {
|
||||
// put the cond_symbol on the top of the stack
|
||||
self.storage
|
||||
.load_symbols(&mut self.code_builder, &[*cond_symbol]);
|
||||
|
||||
self.code_builder.i32_const(*value as i32);
|
||||
|
||||
// compare the 2 topmost values
|
||||
self.code_builder.i32_eq();
|
||||
if is_bool {
|
||||
// We already have a bool, don't need to compare against a const to get one
|
||||
if *value == 0 {
|
||||
self.code_builder.i32_eqz();
|
||||
}
|
||||
} else {
|
||||
match cond_type {
|
||||
ValueType::I32 => {
|
||||
self.code_builder.i32_const(*value as i32);
|
||||
self.code_builder.i32_eq();
|
||||
}
|
||||
ValueType::I64 => {
|
||||
self.code_builder.i64_const(*value as i64);
|
||||
self.code_builder.i64_eq();
|
||||
}
|
||||
ValueType::F32 => {
|
||||
self.code_builder.f32_const(f32::from_bits(*value as u32));
|
||||
self.code_builder.f32_eq();
|
||||
}
|
||||
ValueType::F64 => {
|
||||
self.code_builder.f64_const(f64::from_bits(*value as u64));
|
||||
self.code_builder.f64_eq();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "break" out of `i` surrounding blocks
|
||||
self.code_builder.br_if(i as u32);
|
||||
|
@ -418,6 +444,13 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Stmt::Refcounting(_modify, following) => {
|
||||
// TODO: actually deal with refcounting. For hello world, we just skipped it.
|
||||
self.build_stmt(following, ret_layout)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
x => Err(format!("statement not yet implemented: {:?}", x)),
|
||||
}
|
||||
}
|
||||
|
@ -444,6 +477,11 @@ impl<'a> WasmBackend<'a> {
|
|||
arguments,
|
||||
}) => match call_type {
|
||||
CallType::ByName { name: func_sym, .. } => {
|
||||
// If this function is just a lowlevel wrapper, then inline it
|
||||
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
||||
return self.build_low_level(lowlevel, arguments, *sym, wasm_layout);
|
||||
}
|
||||
|
||||
let mut wasm_args_tmp: Vec<Symbol>;
|
||||
let (wasm_args, has_return_val) = match wasm_layout {
|
||||
WasmLayout::StackMemory { .. } => {
|
||||
|
@ -486,39 +524,80 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
let return_layout = WasmLayout::new(layout);
|
||||
self.storage.load_symbols(&mut self.code_builder, arguments);
|
||||
|
||||
let build_result = build_call_low_level(
|
||||
&mut self.code_builder,
|
||||
&mut self.storage,
|
||||
lowlevel,
|
||||
arguments,
|
||||
&return_layout,
|
||||
);
|
||||
use LowlevelBuildResult::*;
|
||||
|
||||
match build_result {
|
||||
Done => Ok(()),
|
||||
BuiltinCall(name) => {
|
||||
self.call_imported_builtin(name, arguments, &return_layout);
|
||||
Ok(())
|
||||
}
|
||||
NotImplemented => Err(format!(
|
||||
"Low level operation {:?} is not yet implemented",
|
||||
lowlevel
|
||||
)),
|
||||
}
|
||||
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout)
|
||||
}
|
||||
|
||||
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
||||
},
|
||||
|
||||
Expr::Struct(fields) => self.create_struct(sym, layout, fields),
|
||||
|
||||
Expr::StructAtIndex {
|
||||
index,
|
||||
field_layouts,
|
||||
structure,
|
||||
} => {
|
||||
if let StoredValue::StackMemory { location, .. } = self.storage.get(structure) {
|
||||
let (local_id, mut offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
for field in field_layouts.iter().take(*index as usize) {
|
||||
offset += field.stack_size(PTR_SIZE);
|
||||
}
|
||||
self.storage.copy_value_from_memory(
|
||||
&mut self.code_builder,
|
||||
*sym,
|
||||
local_id,
|
||||
offset,
|
||||
);
|
||||
} else {
|
||||
unreachable!("Unexpected storage for {:?}", structure)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
x => Err(format!("Expression is not yet implemented {:?}", x)),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_low_level(
|
||||
&mut self,
|
||||
lowlevel: LowLevel,
|
||||
arguments: &'a [Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: WasmLayout,
|
||||
) -> Result<(), String> {
|
||||
// Load symbols using the "fast calling convention" that Zig uses instead of the C ABI we normally use.
|
||||
// It's only different from the C ABI for small structs, and we are using Zig for all of those cases.
|
||||
// This is a workaround for a bug in Zig. If later versions fix it, we can change to the C ABI.
|
||||
self.storage.load_symbols_fastcc(
|
||||
&mut self.code_builder,
|
||||
arguments,
|
||||
return_sym,
|
||||
&return_layout,
|
||||
);
|
||||
|
||||
let build_result = decode_low_level(
|
||||
&mut self.code_builder,
|
||||
&mut self.storage,
|
||||
lowlevel,
|
||||
arguments,
|
||||
&return_layout,
|
||||
);
|
||||
use LowlevelBuildResult::*;
|
||||
|
||||
match build_result {
|
||||
Done => Ok(()),
|
||||
BuiltinCall(name) => {
|
||||
self.call_zig_builtin(name, arguments, &return_layout);
|
||||
Ok(())
|
||||
}
|
||||
NotImplemented => Err(format!(
|
||||
"Low level operation {:?} is not yet implemented",
|
||||
lowlevel
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_literal(
|
||||
&mut self,
|
||||
lit: &Literal<'a>,
|
||||
|
@ -578,8 +657,8 @@ impl<'a> WasmBackend<'a> {
|
|||
self.lookup_string_constant(string, sym, layout);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.insert_memory_relocation(linker_sym_index);
|
||||
self.code_builder.i32_const(elements_addr as i32);
|
||||
self.code_builder
|
||||
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||
|
||||
self.code_builder.get_local(local_id);
|
||||
|
@ -701,12 +780,10 @@ impl<'a> WasmBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn call_imported_builtin(
|
||||
&mut self,
|
||||
name: &'a str,
|
||||
arguments: &[Symbol],
|
||||
ret_layout: &WasmLayout,
|
||||
) {
|
||||
/// Generate a call instruction to a Zig builtin function.
|
||||
/// And if we haven't seen it before, add an Import and linker data for it.
|
||||
/// Zig calls use LLVM's "fast" calling convention rather than our usual C ABI.
|
||||
fn call_zig_builtin(&mut self, name: &'a str, arguments: &[Symbol], ret_layout: &WasmLayout) {
|
||||
let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) {
|
||||
Some(sym_idx) => match &self.linker_symbols[*sym_idx] {
|
||||
SymInfo::Function(WasmObjectSymbol::Imported { index, .. }) => {
|
||||
|
@ -716,11 +793,29 @@ impl<'a> WasmBackend<'a> {
|
|||
},
|
||||
|
||||
None => {
|
||||
let mut param_types = Vec::with_capacity_in(arguments.len(), self.env.arena);
|
||||
param_types.extend(arguments.iter().map(|a| self.storage.get(a).value_type()));
|
||||
let mut param_types = Vec::with_capacity_in(1 + arguments.len(), self.env.arena);
|
||||
|
||||
let ret_type = if ret_layout.is_stack_memory() {
|
||||
param_types.push(ValueType::I32);
|
||||
None
|
||||
} else {
|
||||
Some(ret_layout.value_type())
|
||||
};
|
||||
|
||||
// Zig's "fast calling convention" packs structs into CPU registers (stack machine slots) if possible.
|
||||
// If they're small enough they can go into an I32 or I64. If they're big, they're pointers (I32).
|
||||
for arg in arguments {
|
||||
param_types.push(match self.storage.get(arg) {
|
||||
StoredValue::StackMemory { size, .. } if *size > 4 && *size <= 8 => {
|
||||
ValueType::I64
|
||||
}
|
||||
stored => stored.value_type(),
|
||||
});
|
||||
}
|
||||
|
||||
let signature_index = self.module.types.insert(Signature {
|
||||
param_types,
|
||||
ret_type: Some(ret_layout.value_type()), // TODO: handle builtins with no return value
|
||||
ret_type,
|
||||
});
|
||||
|
||||
let import_index = self.module.import.entries.len() as u32;
|
||||
|
@ -731,14 +826,15 @@ impl<'a> WasmBackend<'a> {
|
|||
};
|
||||
self.module.import.entries.push(import);
|
||||
|
||||
let sym_idx = self.linker_symbols.len() as u32;
|
||||
let sym_idx = self.linker_symbols.len();
|
||||
let sym_info = SymInfo::Function(WasmObjectSymbol::Imported {
|
||||
flags: WASM_SYM_UNDEFINED,
|
||||
index: import_index,
|
||||
});
|
||||
self.linker_symbols.push(sym_info);
|
||||
self.builtin_sym_index_map.insert(name, sym_idx);
|
||||
|
||||
(import_index, sym_idx)
|
||||
(import_index, sym_idx as u32)
|
||||
}
|
||||
};
|
||||
self.code_builder.call(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue