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:
Folkert de Vries 2023-02-09 18:04:53 +01:00 committed by GitHub
commit 301bf0f367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 217 additions and 36 deletions

View File

@ -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(

View File

@ -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
)

View File

@ -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));
}
}
}
}
}

View File

@ -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>);

View File

@ -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(

View File

@ -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;

View File

@ -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]

View File

@ -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,