mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
first attempt at List.replace in the dev backend
This commit is contained in:
parent
a5ce124bd3
commit
069361a07e
6 changed files with 180 additions and 19 deletions
|
@ -282,7 +282,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
|
||||||
AArch64Call,
|
AArch64Call,
|
||||||
>,
|
>,
|
||||||
_dst: &Symbol,
|
_dst: &Symbol,
|
||||||
_args: &'a [Symbol],
|
_args: &[Symbol],
|
||||||
_arg_layouts: &[Layout<'a>],
|
_arg_layouts: &[Layout<'a>],
|
||||||
_ret_layout: &Layout<'a>,
|
_ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
Relocation,
|
Relocation,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
|
@ -78,7 +78,7 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
|
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
|
@ -495,7 +495,7 @@ impl<
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
fn_name: String,
|
fn_name: String,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -636,7 +636,7 @@ impl<
|
||||||
fn build_jump(
|
fn build_jump(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
_ret_layout: &Layout<'a>,
|
_ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -897,6 +897,7 @@ impl<
|
||||||
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol) {
|
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol) {
|
||||||
self.storage_manager.list_len(&mut self.buf, dst, list);
|
self.storage_manager.list_len(&mut self.buf, dst, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_list_get_unsafe(
|
fn build_list_get_unsafe(
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
|
@ -931,6 +932,129 @@ impl<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_list_replace_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
// We want to delegate to the zig builtin, but it takes some extra parameters.
|
||||||
|
// Firstly, it takes the alignment of the list.
|
||||||
|
// Secondly, it takes the stack size of an element.
|
||||||
|
// Thirdly, it takes a pointer that it will write the output element to.
|
||||||
|
let list = args[0];
|
||||||
|
let list_layout = arg_layouts[0];
|
||||||
|
let index = args[1];
|
||||||
|
let index_layout = arg_layouts[1];
|
||||||
|
let elem = args[2];
|
||||||
|
let elem_layout = arg_layouts[2];
|
||||||
|
|
||||||
|
let u32_layout = &Layout::Builtin(Builtin::Int(IntWidth::U32));
|
||||||
|
let list_alignment = list_layout.alignment_bytes(self.storage_manager.target_info());
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP,
|
||||||
|
&u32_layout,
|
||||||
|
&Literal::Int(list_alignment as i128),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Have to pass the input element by pointer, so put it on the stack and load it's address.
|
||||||
|
self.storage_manager
|
||||||
|
.ensure_symbol_on_stack(&mut self.buf, &elem);
|
||||||
|
let u64_layout = &Layout::Builtin(Builtin::Int(IntWidth::U64));
|
||||||
|
let (new_elem_offset, _) = self.storage_manager.stack_offset_and_size(&elem);
|
||||||
|
// Load address of output element into register.
|
||||||
|
let reg = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP2);
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, reg, CC::BASE_PTR_REG, new_elem_offset);
|
||||||
|
|
||||||
|
// Load the elements size.
|
||||||
|
let elem_stack_size = elem_layout.stack_size(self.storage_manager.target_info());
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP3,
|
||||||
|
&u64_layout,
|
||||||
|
&Literal::Int(elem_stack_size as i128),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the return location.
|
||||||
|
let base_offset = self.storage_manager.claim_stack_area(
|
||||||
|
&dst,
|
||||||
|
ret_layout.stack_size(self.storage_manager.target_info()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ret_fields = if let Layout::Struct { field_layouts, .. } = ret_layout {
|
||||||
|
field_layouts
|
||||||
|
} else {
|
||||||
|
internal_error!(
|
||||||
|
"Expected replace to return a struct instead found: {:?}",
|
||||||
|
ret_layout
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only return list and old element.
|
||||||
|
debug_assert_eq!(ret_fields.len(), 2);
|
||||||
|
|
||||||
|
let (out_list_offset, out_elem_offset) = if ret_fields[0] == elem_layout {
|
||||||
|
(
|
||||||
|
base_offset + ret_fields[0].stack_size(self.storage_manager.target_info()) as i32,
|
||||||
|
base_offset,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
base_offset,
|
||||||
|
base_offset + ret_fields[0].stack_size(self.storage_manager.target_info()) as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load address of output element into register.
|
||||||
|
let reg = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP4);
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, reg, CC::BASE_PTR_REG, out_elem_offset);
|
||||||
|
|
||||||
|
let lowlevel_args = bumpalo::vec![
|
||||||
|
in self.env.arena;
|
||||||
|
list,
|
||||||
|
Symbol::DEV_TMP,
|
||||||
|
index,
|
||||||
|
Symbol::DEV_TMP2,
|
||||||
|
Symbol::DEV_TMP3,
|
||||||
|
Symbol::DEV_TMP4,
|
||||||
|
];
|
||||||
|
let lowlevel_arg_layouts = bumpalo::vec![
|
||||||
|
in self.env.arena;
|
||||||
|
list_layout,
|
||||||
|
*u32_layout,
|
||||||
|
index_layout,
|
||||||
|
*u64_layout,
|
||||||
|
*u64_layout,
|
||||||
|
*u64_layout,
|
||||||
|
];
|
||||||
|
|
||||||
|
self.build_fn_call(
|
||||||
|
&Symbol::DEV_TMP5,
|
||||||
|
bitcode::LIST_REPLACE.to_string(),
|
||||||
|
&lowlevel_args,
|
||||||
|
&lowlevel_arg_layouts,
|
||||||
|
&list_layout,
|
||||||
|
);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP2);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP3);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP4);
|
||||||
|
|
||||||
|
// Copy from list to the output record.
|
||||||
|
self.storage_manager.copy_symbol_to_stack_offset(
|
||||||
|
&mut self.buf,
|
||||||
|
out_list_offset,
|
||||||
|
&Symbol::DEV_TMP5,
|
||||||
|
&list_layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP5);
|
||||||
|
}
|
||||||
|
|
||||||
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
|
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
|
||||||
// We may not strictly need an instruction here.
|
// We may not strictly need an instruction here.
|
||||||
// What's important is to load the value, and for src and dest to have different Layouts.
|
// What's important is to load the value, and for src and dest to have different Layouts.
|
||||||
|
|
|
@ -806,6 +806,28 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ensure_symbol_on_stack(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) {
|
||||||
|
match self.remove_storage_for_sym(sym) {
|
||||||
|
Reg(reg_storage) => {
|
||||||
|
let base_offset = self.claim_stack_size(8);
|
||||||
|
match reg_storage {
|
||||||
|
General(reg) => ASM::mov_base32_reg64(buf, base_offset, reg),
|
||||||
|
Float(reg) => ASM::mov_base32_freg64(buf, base_offset, reg),
|
||||||
|
}
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
Stack(Primitive {
|
||||||
|
base_offset,
|
||||||
|
reg: Some(reg_storage),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
self.symbol_storage_map.insert(*sym, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack.
|
/// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack.
|
||||||
/// Note, used and free regs are expected to be updated outside of this function.
|
/// Note, used and free regs are expected to be updated outside of this function.
|
||||||
fn free_to_stack(
|
fn free_to_stack(
|
||||||
|
@ -960,7 +982,7 @@ impl<
|
||||||
&mut self,
|
&mut self,
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
) {
|
) {
|
||||||
// TODO: remove was use here and for current_storage to deal with borrow checker.
|
// TODO: remove was use here and for current_storage to deal with borrow checker.
|
||||||
|
|
|
@ -267,7 +267,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
X86_64SystemV,
|
X86_64SystemV,
|
||||||
>,
|
>,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -693,7 +693,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
||||||
X86_64WindowsFastcall,
|
X86_64WindowsFastcall,
|
||||||
>,
|
>,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -233,7 +233,7 @@ trait Backend<'a> {
|
||||||
fn build_jump(
|
fn build_jump(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
@ -571,17 +571,18 @@ trait Backend<'a> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
2,
|
2,
|
||||||
args.len(),
|
args.len(),
|
||||||
"ListLen: expected to have exactly two arguments"
|
"ListGetUnsafe: expected to have exactly two arguments"
|
||||||
);
|
);
|
||||||
self.build_list_get_unsafe(sym, &args[0], &args[1], ret_layout)
|
self.build_list_get_unsafe(sym, &args[0], &args[1], ret_layout)
|
||||||
}
|
}
|
||||||
LowLevel::ListSet => self.build_fn_call(
|
LowLevel::ListReplaceUnsafe => {
|
||||||
sym,
|
debug_assert_eq!(
|
||||||
bitcode::LIST_SET.to_string(),
|
3,
|
||||||
args,
|
args.len(),
|
||||||
arg_layouts,
|
"ListReplaceUnsafe: expected to have exactly three arguments"
|
||||||
ret_layout,
|
);
|
||||||
),
|
self.build_list_replace_unsafe(sym, args, arg_layouts, ret_layout)
|
||||||
|
}
|
||||||
LowLevel::StrConcat => self.build_fn_call(
|
LowLevel::StrConcat => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::STR_CONCAT.to_string(),
|
bitcode::STR_CONCAT.to_string(),
|
||||||
|
@ -643,7 +644,7 @@ trait Backend<'a> {
|
||||||
self.build_eq(sym, &args[0], &Symbol::DEV_TMP, &arg_layouts[0]);
|
self.build_eq(sym, &args[0], &Symbol::DEV_TMP, &arg_layouts[0]);
|
||||||
self.free_symbol(&Symbol::DEV_TMP)
|
self.free_symbol(&Symbol::DEV_TMP)
|
||||||
}
|
}
|
||||||
Symbol::LIST_GET | Symbol::LIST_SET => {
|
Symbol::LIST_GET | Symbol::LIST_SET | Symbol::LIST_REPLACE => {
|
||||||
// TODO: This is probably simple enough to be worth inlining.
|
// TODO: This is probably simple enough to be worth inlining.
|
||||||
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
|
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
|
||||||
let fn_name = self.symbol_to_string(func_sym, layout_id);
|
let fn_name = self.symbol_to_string(func_sym, layout_id);
|
||||||
|
@ -661,7 +662,7 @@ trait Backend<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
fn_name: String,
|
fn_name: String,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
@ -728,6 +729,16 @@ trait Backend<'a> {
|
||||||
index: &Symbol,
|
index: &Symbol,
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// build_list_replace_unsafe returns the old element and new list with the list having the new element inserted.
|
||||||
|
fn build_list_replace_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
/// build_refcount_getptr loads the pointer to the reference count of src into dst.
|
/// build_refcount_getptr loads the pointer to the reference count of src into dst.
|
||||||
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol);
|
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol);
|
||||||
|
|
||||||
|
|
|
@ -883,6 +883,10 @@ define_builtins! {
|
||||||
|
|
||||||
// used in dev backend
|
// used in dev backend
|
||||||
26 DEV_TMP: "#dev_tmp"
|
26 DEV_TMP: "#dev_tmp"
|
||||||
|
27 DEV_TMP2: "#dev_tmp2"
|
||||||
|
28 DEV_TMP3: "#dev_tmp3"
|
||||||
|
29 DEV_TMP4: "#dev_tmp4"
|
||||||
|
30 DEV_TMP5: "#dev_tmp5"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue