mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
commit
3e82d30e89
4 changed files with 167 additions and 10 deletions
|
@ -8,7 +8,9 @@ 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};
|
||||||
use roc_mono::code_gen_help::CodeGenHelp;
|
use roc_mono::code_gen_help::CodeGenHelp;
|
||||||
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
use roc_mono::ir::{
|
||||||
|
BranchInfo, JoinPointId, ListLiteralElement, Literal, Param, ProcLayout, SelfRecursive, Stmt,
|
||||||
|
};
|
||||||
use roc_mono::layout::{Builtin, Layout, TagIdIntType, UnionLayout};
|
use roc_mono::layout::{Builtin, Layout, TagIdIntType, UnionLayout};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -21,6 +23,7 @@ pub(crate) mod x86_64;
|
||||||
|
|
||||||
use storage::StorageManager;
|
use storage::StorageManager;
|
||||||
|
|
||||||
|
const REFCOUNT_ONE: u64 = i64::MIN as u64;
|
||||||
// TODO: on all number functions double check and deal with over/underflow.
|
// TODO: on all number functions double check and deal with over/underflow.
|
||||||
|
|
||||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
|
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
|
||||||
|
@ -1075,6 +1078,133 @@ impl<
|
||||||
ASM::add_reg64_reg64_imm32(&mut self.buf, dst_reg, CC::BASE_PTR_REG, offset);
|
ASM::add_reg64_reg64_imm32(&mut self.buf, dst_reg, CC::BASE_PTR_REG, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_empty_array(&mut self, sym: &Symbol) {
|
||||||
|
let base_offset = self.storage_manager.claim_stack_area(sym, 24);
|
||||||
|
self.storage_manager
|
||||||
|
.with_tmp_general_reg(&mut self.buf, |_storage_manager, buf, reg| {
|
||||||
|
ASM::mov_reg64_imm64(buf, reg, 0);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset, reg);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset + 8, reg);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset + 16, reg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_array(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
elem_layout: &Layout<'a>,
|
||||||
|
elems: &'a [ListLiteralElement<'a>],
|
||||||
|
) {
|
||||||
|
// Allocate
|
||||||
|
// This requires at least 8 for the refcount alignment.
|
||||||
|
let allocation_alignment = std::cmp::max(
|
||||||
|
8,
|
||||||
|
elem_layout.allocation_alignment_bytes(self.storage_manager.target_info()) as u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
let elem_size = elem_layout.stack_size(self.storage_manager.target_info()) as u64;
|
||||||
|
let allocation_size = elem_size * elems.len() as u64 + allocation_alignment /* add space for refcount */;
|
||||||
|
let u64_layout = Layout::Builtin(Builtin::Int(IntWidth::U64));
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP,
|
||||||
|
&u64_layout,
|
||||||
|
&Literal::Int((allocation_size as i128).to_ne_bytes()),
|
||||||
|
);
|
||||||
|
let u32_layout = Layout::Builtin(Builtin::Int(IntWidth::U32));
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP2,
|
||||||
|
&u32_layout,
|
||||||
|
&Literal::Int((allocation_alignment as i128).to_ne_bytes()),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.build_fn_call(
|
||||||
|
&Symbol::DEV_TMP3,
|
||||||
|
"roc_alloc".to_string(),
|
||||||
|
&[Symbol::DEV_TMP, Symbol::DEV_TMP2],
|
||||||
|
&[u64_layout, u32_layout],
|
||||||
|
&u64_layout,
|
||||||
|
);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP2);
|
||||||
|
|
||||||
|
// Fill pointer with elems
|
||||||
|
let ptr_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, &Symbol::DEV_TMP3);
|
||||||
|
// Point to first element of array.
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, ptr_reg, ptr_reg, allocation_alignment as i32);
|
||||||
|
|
||||||
|
// fill refcount at -8.
|
||||||
|
self.storage_manager.with_tmp_general_reg(
|
||||||
|
&mut self.buf,
|
||||||
|
|_storage_manager, buf, tmp_reg| {
|
||||||
|
ASM::mov_reg64_imm64(buf, tmp_reg, REFCOUNT_ONE as i64);
|
||||||
|
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, -8, tmp_reg);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy everything into output array.
|
||||||
|
let mut elem_offset = 0;
|
||||||
|
for elem in elems {
|
||||||
|
// TODO: this could be a lot faster when loading large lists
|
||||||
|
// if we move matching on the element layout to outside this loop.
|
||||||
|
// It also greatly bloats the code here.
|
||||||
|
// Refactor this and switch to one external match.
|
||||||
|
// We also could make loadining indivitual literals much faster
|
||||||
|
let elem_sym = match elem {
|
||||||
|
ListLiteralElement::Symbol(sym) => sym,
|
||||||
|
ListLiteralElement::Literal(lit) => {
|
||||||
|
self.load_literal(&Symbol::DEV_TMP, elem_layout, lit);
|
||||||
|
&Symbol::DEV_TMP
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// TODO: Expand to all types.
|
||||||
|
match elem_layout {
|
||||||
|
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
||||||
|
let sym_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, elem_sym);
|
||||||
|
ASM::mov_mem64_offset32_reg64(&mut self.buf, ptr_reg, elem_offset, sym_reg);
|
||||||
|
}
|
||||||
|
_ if elem_size == 0 => {}
|
||||||
|
_ if elem_size > 8 => {
|
||||||
|
let (from_offset, size) = self.storage_manager.stack_offset_and_size(elem_sym);
|
||||||
|
debug_assert!(from_offset % 8 == 0);
|
||||||
|
debug_assert!(size % 8 == 0);
|
||||||
|
debug_assert_eq!(size as u64, elem_size);
|
||||||
|
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, elem_offset, tmp_reg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x => todo!("copying data to list with layout, {:?}", x),
|
||||||
|
}
|
||||||
|
elem_offset += elem_size as i32;
|
||||||
|
if elem_sym == &Symbol::DEV_TMP {
|
||||||
|
self.free_symbol(elem_sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup list on stack.
|
||||||
|
self.storage_manager.with_tmp_general_reg(
|
||||||
|
&mut self.buf,
|
||||||
|
|storage_manager, buf, tmp_reg| {
|
||||||
|
let base_offset = storage_manager.claim_stack_area(sym, 24);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset, ptr_reg);
|
||||||
|
|
||||||
|
ASM::mov_reg64_imm64(buf, tmp_reg, elems.len() as i64);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset + 16, tmp_reg);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP3);
|
||||||
|
}
|
||||||
|
|
||||||
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]) {
|
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]) {
|
||||||
self.storage_manager
|
self.storage_manager
|
||||||
.create_struct(&mut self.buf, sym, layout, fields);
|
.create_struct(&mut self.buf, sym, layout, fields);
|
||||||
|
|
|
@ -310,6 +310,22 @@ trait Backend<'a> {
|
||||||
x => todo!("the call type, {:?}", x),
|
x => todo!("the call type, {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::EmptyArray => {
|
||||||
|
self.create_empty_array(sym);
|
||||||
|
}
|
||||||
|
Expr::Array { elem_layout, elems } => {
|
||||||
|
let mut syms = bumpalo::vec![in self.env().arena];
|
||||||
|
for sym in elems.iter().filter_map(|x| match x {
|
||||||
|
ListLiteralElement::Symbol(sym) => Some(sym),
|
||||||
|
_ => None,
|
||||||
|
}) {
|
||||||
|
syms.push(*sym);
|
||||||
|
}
|
||||||
|
// TODO: This could be a huge waste.
|
||||||
|
// We probably want to call this within create_array, one element at a time.
|
||||||
|
self.load_literal_symbols(syms.into_bump_slice());
|
||||||
|
self.create_array(sym, elem_layout, elems);
|
||||||
|
}
|
||||||
Expr::Struct(fields) => {
|
Expr::Struct(fields) => {
|
||||||
self.load_literal_symbols(fields);
|
self.load_literal_symbols(fields);
|
||||||
self.create_struct(sym, layout, fields);
|
self.create_struct(sym, layout, fields);
|
||||||
|
@ -772,6 +788,17 @@ trait Backend<'a> {
|
||||||
/// load_literal sets a symbol to be equal to a literal.
|
/// load_literal sets a symbol to be equal to a literal.
|
||||||
fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>);
|
fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>);
|
||||||
|
|
||||||
|
/// create_empty_array creates an empty array with nullptr, zero length, and zero capacity.
|
||||||
|
fn create_empty_array(&mut self, sym: &Symbol);
|
||||||
|
|
||||||
|
/// create_array creates an array filling it with the specified objects.
|
||||||
|
fn create_array(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
elem_layout: &Layout<'a>,
|
||||||
|
elems: &'a [ListLiteralElement<'a>],
|
||||||
|
);
|
||||||
|
|
||||||
/// create_struct creates a struct with the elements specified loaded into it as data.
|
/// create_struct creates a struct with the elements specified loaded into it as data.
|
||||||
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]);
|
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]);
|
||||||
|
|
||||||
|
|
|
@ -420,8 +420,8 @@ fn build_proc<'a, B: Backend<'a>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Relocation::LinkedFunction { offset, name } => {
|
Relocation::LinkedFunction { offset, name } => {
|
||||||
// If the symbol is an undefined zig builtin, we need to add it here.
|
// If the symbol is an undefined roc function, we need to add it here.
|
||||||
if output.symbol_id(name.as_bytes()) == None && name.starts_with("roc_builtins.") {
|
if output.symbol_id(name.as_bytes()) == None && name.starts_with("roc_") {
|
||||||
let builtin_symbol = Symbol {
|
let builtin_symbol = Symbol {
|
||||||
name: name.as_bytes().to_vec(),
|
name: name.as_bytes().to_vec(),
|
||||||
value: 0,
|
value: 0,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#[cfg(feature = "gen-llvm")]
|
#[cfg(feature = "gen-llvm")]
|
||||||
use crate::helpers::llvm::assert_evals_to;
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
// #[cfg(feature = "gen-dev")]
|
#[cfg(feature = "gen-dev")]
|
||||||
// use crate::helpers::dev::assert_evals_to;
|
use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
#[cfg(feature = "gen-wasm")]
|
#[cfg(feature = "gen-wasm")]
|
||||||
use crate::helpers::wasm::assert_evals_to;
|
use crate::helpers::wasm::assert_evals_to;
|
||||||
|
@ -25,25 +25,25 @@ fn roc_list_construction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn empty_list_literal() {
|
fn empty_list_literal() {
|
||||||
assert_evals_to!("[]", RocList::<i64>::from_slice(&[]), RocList<i64>);
|
assert_evals_to!("[]", RocList::<i64>::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn list_literal_empty_record() {
|
fn list_literal_empty_record() {
|
||||||
assert_evals_to!("[{}]", RocList::from_slice(&[()]), RocList<()>);
|
assert_evals_to!("[{}]", RocList::from_slice(&[()]), RocList<()>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn int_singleton_list_literal() {
|
fn int_singleton_list_literal() {
|
||||||
assert_evals_to!("[1, 2]", RocList::from_slice(&[1, 2]), RocList<i64>);
|
assert_evals_to!("[1, 2]", RocList::from_slice(&[1, 2]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn int_list_literal() {
|
fn int_list_literal() {
|
||||||
assert_evals_to!("[12, 9]", RocList::from_slice(&[12, 9]), RocList<i64>);
|
assert_evals_to!("[12, 9]", RocList::from_slice(&[12, 9]), RocList<i64>);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -133,7 +133,7 @@ fn bool_list_literal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn variously_sized_list_literals() {
|
fn variously_sized_list_literals() {
|
||||||
assert_evals_to!("[]", RocList::<i64>::from_slice(&[]), RocList<i64>);
|
assert_evals_to!("[]", RocList::<i64>::from_slice(&[]), RocList<i64>);
|
||||||
assert_evals_to!("[1]", RocList::from_slice(&[1]), RocList<i64>);
|
assert_evals_to!("[1]", RocList::from_slice(&[1]), RocList<i64>);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue