mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Merge pull request #5009 from roc-lang/dev-box-box
implement Box.box and Box.unbox in the dev backend
This commit is contained in:
commit
301bf0f367
@ -715,14 +715,23 @@ impl<
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
ASM::mov_freg64_freg64(&mut self.buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
|
||||
}
|
||||
_ => {
|
||||
CC::load_returned_complex_symbol(
|
||||
&mut self.buf,
|
||||
&mut self.storage_manager,
|
||||
self.layout_interner,
|
||||
dst,
|
||||
ret_layout,
|
||||
);
|
||||
other => {
|
||||
//
|
||||
match self.layout_interner.get(other) {
|
||||
Layout::Boxed(_) => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
|
||||
}
|
||||
_ => {
|
||||
CC::load_returned_complex_symbol(
|
||||
&mut self.buf,
|
||||
&mut self.storage_manager,
|
||||
self.layout_interner,
|
||||
dst,
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1920,6 +1929,85 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_box(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) {
|
||||
let element_width_symbol = Symbol::DEV_TMP;
|
||||
self.load_layout_stack_size(element_layout, element_width_symbol);
|
||||
|
||||
// Load allocation alignment (u32)
|
||||
let element_alignment_symbol = Symbol::DEV_TMP2;
|
||||
self.load_layout_alignment(Layout::U32, element_alignment_symbol);
|
||||
|
||||
self.allocate_with_refcount(
|
||||
Symbol::DEV_TMP3,
|
||||
element_width_symbol,
|
||||
element_alignment_symbol,
|
||||
);
|
||||
|
||||
self.free_symbol(&element_width_symbol);
|
||||
self.free_symbol(&element_alignment_symbol);
|
||||
|
||||
// Fill pointer with the value
|
||||
let ptr_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, &Symbol::DEV_TMP3);
|
||||
|
||||
let element_width = self.layout_interner.stack_size(element_layout) as u64;
|
||||
let element_offset = 0;
|
||||
|
||||
// TODO: Expand to all types.
|
||||
match self.layout_interner.get(element_layout) {
|
||||
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
||||
let sym_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, &value);
|
||||
ASM::mov_mem64_offset32_reg64(&mut self.buf, ptr_reg, element_offset, sym_reg);
|
||||
}
|
||||
_ if element_width == 0 => {}
|
||||
_ if element_width > 8 => {
|
||||
let (from_offset, size) = self.storage_manager.stack_offset_and_size(&value);
|
||||
debug_assert!(from_offset % 8 == 0);
|
||||
debug_assert!(size % 8 == 0);
|
||||
debug_assert_eq!(size as u64, element_width);
|
||||
self.storage_manager.with_tmp_general_reg(
|
||||
&mut self.buf,
|
||||
|_storage_manager, buf, tmp_reg| {
|
||||
for i in (0..size as i32).step_by(8) {
|
||||
ASM::mov_reg64_base32(buf, tmp_reg, from_offset + i);
|
||||
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, element_offset, tmp_reg);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
x => todo!("copying data to list with layout, {:?}", x),
|
||||
}
|
||||
|
||||
if value == Symbol::DEV_TMP {
|
||||
self.free_symbol(&value);
|
||||
}
|
||||
|
||||
// box is just a pointer on the stack
|
||||
let base_offset = self.storage_manager.claim_stack_area(&sym, 8);
|
||||
ASM::mov_base32_reg64(&mut self.buf, base_offset, ptr_reg);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
}
|
||||
|
||||
fn expr_unbox(&mut self, dst: Symbol, ptr: Symbol, element_layout: InLayout<'a>) {
|
||||
let ptr_reg = self
|
||||
.storage_manager
|
||||
.load_to_general_reg(&mut self.buf, &ptr);
|
||||
|
||||
let ret_stack_size = self.layout_interner.stack_size(element_layout);
|
||||
|
||||
match element_layout {
|
||||
single_register_integers!() if ret_stack_size == 8 => {
|
||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, &dst);
|
||||
ASM::mov_reg64_mem64_offset32(&mut self.buf, dst_reg, ptr_reg, 0);
|
||||
}
|
||||
x => internal_error!("Loading list element with layout: {:?}", x),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) {
|
||||
self.storage_manager.load_union_tag_id(
|
||||
self.layout_interner,
|
||||
@ -2036,9 +2124,19 @@ impl<
|
||||
CC::FLOAT_RETURN_REGS[0],
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
internal_error!("All primitive valuse should fit in a single register");
|
||||
}
|
||||
other => match self.layout_interner.get(other) {
|
||||
Layout::Boxed(_) => {
|
||||
// treat like a 64-bit integer
|
||||
self.storage_manager.load_to_specified_general_reg(
|
||||
&mut self.buf,
|
||||
sym,
|
||||
CC::GENERAL_RETURN_REGS[0],
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
internal_error!("All primitive values should fit in a single register");
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
CC::return_complex_symbol(
|
||||
|
@ -928,7 +928,7 @@ impl<
|
||||
) => (*base_offset, *size),
|
||||
storage => {
|
||||
internal_error!(
|
||||
"Data not on the stack for sym ({}) with storage ({:?})",
|
||||
"Data not on the stack for sym {:?} with storage {:?}",
|
||||
sym,
|
||||
storage
|
||||
)
|
||||
|
@ -268,9 +268,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||
storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]);
|
||||
general_i += 1;
|
||||
}
|
||||
for (layout, sym) in args.iter() {
|
||||
let stack_size = layout_interner.stack_size(*layout);
|
||||
match *layout {
|
||||
for (in_layout, sym) in args.iter() {
|
||||
let stack_size = layout_interner.stack_size(*in_layout);
|
||||
match *in_layout {
|
||||
single_register_integers!() => {
|
||||
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||
storage_manager.general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]);
|
||||
@ -297,9 +297,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||
storage_manager.complex_stack_arg(sym, arg_offset, stack_size);
|
||||
arg_offset += stack_size as i32;
|
||||
}
|
||||
x => {
|
||||
todo!("Loading args with layout {:?}", x);
|
||||
}
|
||||
other => match layout_interner.get(other) {
|
||||
Layout::Boxed(_) => {
|
||||
// boxed layouts are pointers, which we treat as 64-bit integers
|
||||
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||
storage_manager
|
||||
.general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]);
|
||||
general_i += 1;
|
||||
} else {
|
||||
storage_manager.primitive_stack_arg(sym, arg_offset);
|
||||
arg_offset += 8;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!("Loading args with layout {:?}", layout_interner.dbg(other));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -338,6 +351,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||
base_offset,
|
||||
);
|
||||
}
|
||||
|
||||
for (sym, layout) in args.iter().zip(arg_layouts.iter()) {
|
||||
match *layout {
|
||||
single_register_integers!() => {
|
||||
@ -407,8 +421,37 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||
}
|
||||
tmp_stack_offset += size as i32;
|
||||
}
|
||||
x => {
|
||||
todo!("calling with arg type, {:?}", x);
|
||||
other => {
|
||||
// look at the layout in more detail
|
||||
match layout_interner.get(other) {
|
||||
Layout::Boxed(_) => {
|
||||
// treat boxed like a 64-bit integer
|
||||
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||
storage_manager.load_to_specified_general_reg(
|
||||
buf,
|
||||
sym,
|
||||
Self::GENERAL_PARAM_REGS[general_i],
|
||||
);
|
||||
general_i += 1;
|
||||
} else {
|
||||
// Copy to stack using return reg as buffer.
|
||||
storage_manager.load_to_specified_general_reg(
|
||||
buf,
|
||||
sym,
|
||||
Self::GENERAL_RETURN_REGS[0],
|
||||
);
|
||||
X86_64Assembler::mov_stack32_reg64(
|
||||
buf,
|
||||
tmp_stack_offset,
|
||||
Self::GENERAL_RETURN_REGS[0],
|
||||
);
|
||||
tmp_stack_offset += 8;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!("calling with arg type, {:?}", layout_interner.dbg(other));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -384,6 +384,21 @@ trait Backend<'a> {
|
||||
self.load_literal_symbols(arguments);
|
||||
self.tag(sym, arguments, tag_layout, *tag_id);
|
||||
}
|
||||
Expr::ExprBox { symbol: value } => {
|
||||
let element_layout = match self.interner().get(*layout) {
|
||||
Layout::Boxed(boxed) => boxed,
|
||||
_ => unreachable!("{:?}", self.interner().dbg(*layout)),
|
||||
};
|
||||
|
||||
self.load_literal_symbols([*value].as_slice());
|
||||
self.expr_box(*sym, *value, element_layout)
|
||||
}
|
||||
Expr::ExprUnbox { symbol: ptr } => {
|
||||
let element_layout = *layout;
|
||||
|
||||
self.load_literal_symbols([*ptr].as_slice());
|
||||
self.expr_unbox(*sym, *ptr, element_layout)
|
||||
}
|
||||
x => todo!("the expression, {:?}", x),
|
||||
}
|
||||
}
|
||||
@ -1147,6 +1162,12 @@ trait Backend<'a> {
|
||||
tag_id: TagIdIntType,
|
||||
);
|
||||
|
||||
/// load a value from a pointer
|
||||
fn expr_unbox(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>);
|
||||
|
||||
/// store a refcounted value on the heap
|
||||
fn expr_box(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>);
|
||||
|
||||
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
|
||||
fn return_symbol(&mut self, sym: &Symbol, layout: &InLayout<'a>);
|
||||
|
||||
|
@ -8,7 +8,7 @@ use bumpalo::{collections::Vec, Bump};
|
||||
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{Builtin, InLayout, Layout, LayoutInterner, UnionLayout};
|
||||
use roc_std::{RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_wasm_module::{
|
||||
linking::SymInfo, linking::WasmObjectSymbol, Align, Export, ExportType, LocalId, Signature,
|
||||
ValueType, WasmModule,
|
||||
@ -203,6 +203,13 @@ impl<T: Wasm32Result> Wasm32Result for RocList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Wasm32Result> Wasm32Result for RocBox<T> {
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
// treat box as if it's just a isize value
|
||||
<i32 as Wasm32Result>::build_wrapper_body(code_builder, main_function_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized, E: Wasm32Sized> Wasm32Result for RocResult<T, E> {
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
|
@ -1,4 +1,4 @@
|
||||
use roc_std::{RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
|
||||
pub trait Wasm32Sized: Sized {
|
||||
const SIZE_OF_WASM: usize;
|
||||
@ -45,6 +45,11 @@ impl<T: Wasm32Sized> Wasm32Sized for RocList<T> {
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized> Wasm32Sized for RocBox<T> {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized, E: Wasm32Sized> Wasm32Sized for RocResult<T, E> {
|
||||
const ALIGN_OF_WASM: usize = max(&[T::ALIGN_OF_WASM, E::ALIGN_OF_WASM]);
|
||||
const SIZE_OF_WASM: usize = max(&[T::ACTUAL_WIDTH, E::ACTUAL_WIDTH]) + 1;
|
||||
|
@ -9,9 +9,7 @@ use crate::helpers::wasm::assert_evals_to;
|
||||
|
||||
use indoc::indoc;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::RocList;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::RocStr;
|
||||
use roc_std::{RocBox, RocList, RocStr};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
@ -3275,17 +3273,15 @@ fn box_and_unbox_string() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn box_num() {
|
||||
assert_evals_to!("Box.box 123u64", RocBox::new(123), RocBox<u64>)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn box_and_unbox_num() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Box.unbox (Box.box (123u8))
|
||||
"#
|
||||
),
|
||||
123,
|
||||
u8
|
||||
)
|
||||
assert_evals_to!("Box.unbox (Box.box (123u64))", 123, u64)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_gen_wasm::wasm32_sized::Wasm32Sized;
|
||||
use roc_mono::layout::Builtin;
|
||||
use roc_std::{RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_wasm_module::round_up_to_alignment;
|
||||
use std::convert::TryInto;
|
||||
|
||||
@ -104,6 +104,17 @@ impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocBox<T> {
|
||||
fn decode(memory: &[u8], offset: u32) -> Self {
|
||||
let ptr = <u32 as FromWasm32Memory>::decode(memory, offset + 4 * Builtin::WRAPPER_PTR);
|
||||
debug_assert_ne!(ptr, 0);
|
||||
|
||||
let value = <T as FromWasm32Memory>::decode(memory, ptr);
|
||||
|
||||
RocBox::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FromWasm32Memory for RocResult<T, E>
|
||||
where
|
||||
T: FromWasm32Memory + Wasm32Sized,
|
||||
|
Loading…
Reference in New Issue
Block a user