Merge pull request #5539 from roc-lang/dev-gen-primitives

dev backend: de-duplicate function names based on the lambda set
This commit is contained in:
Folkert de Vries 2023-06-13 22:13:34 +02:00 committed by GitHub
commit b3c598fcf6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 433 additions and 126 deletions

View file

@ -965,33 +965,32 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
}
#[inline(always)]
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
if size == 8 {
Self::mov_reg64_base32(buf, dst, offset);
} else if size == 4 {
todo!("sign extending 4 byte values");
} else if size == 2 {
todo!("sign extending 2 byte values");
} else if size == 1 {
todo!("sign extending 1 byte values");
} else {
internal_error!("Invalid size for sign extension: {}", size);
fn movsx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: AArch64GeneralReg,
offset: i32,
) {
match register_width {
RegisterWidth::W8 => todo!("sign extend 1 byte values"),
RegisterWidth::W16 => todo!("sign extend 2 byte values"),
RegisterWidth::W32 => todo!("sign extend 4 byte values"),
RegisterWidth::W64 => Self::mov_reg64_base32(buf, dst, offset),
}
}
#[inline(always)]
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
if size == 8 {
Self::mov_reg64_base32(buf, dst, offset);
} else if size == 4 {
todo!("zero extending 4 byte values");
} else if size == 2 {
todo!("zero extending 2 byte values");
} else if size == 1 {
todo!("zero extending 1 byte values");
} else {
internal_error!("Invalid size for zero extension: {}", size);
fn movzx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: AArch64GeneralReg,
offset: i32,
) {
match register_width {
RegisterWidth::W8 => todo!("zero extend 1 byte values"),
RegisterWidth::W16 => todo!("zero extend 2 byte values"),
RegisterWidth::W32 => todo!("zero extend 4 byte values"),
RegisterWidth::W64 => Self::mov_reg64_base32(buf, dst, offset),
}
}

View file

@ -13,7 +13,7 @@ use roc_mono::ir::{
SelfRecursive, Stmt,
};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
Builtin, InLayout, LambdaName, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
TagIdIntType, UnionLayout,
};
use roc_mono::low_level::HigherOrder;
@ -391,10 +391,19 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
/// Sign extends the data at `offset` with `size` as it copies it to `dst`
/// size must be less than or equal to 8.
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, size: u8);
/// Zero extends the data at `offset` with `size` as it copies it to `dst`
/// size must be less than or equal to 8.
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, size: u8);
fn movsx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: GeneralReg,
offset: i32,
);
fn movzx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: GeneralReg,
offset: i32,
);
fn mov_freg64_stack32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
fn mov_reg64_stack32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
@ -1590,8 +1599,8 @@ impl<
HelperOp::Eq,
);
let fn_name = self.function_symbol_to_string(
eq_symbol,
let fn_name = self.lambda_name_to_string(
LambdaName::no_niche(eq_symbol),
[*arg_layout, *arg_layout].into_iter(),
None,
Layout::U8,
@ -1815,6 +1824,34 @@ impl<
);
}
fn build_num_cmp(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
arg_layout: &InLayout<'a>,
) {
// This implements the expression:
// (x != y) as u8 + (x < y) as u8
// For x==y: (false as u8) + (false as u8) = 0 = RocOrder::Eq
// For x>y: (true as u8) + (false as u8) = 1 = RocOrder::Gt
// For x<y: (true as u8) + (true as u8) = 2 = RocOrder::Lt
// u8 is represented in the stack machine as i32, but written to memory as 1 byte
let not_equal = self.debug_symbol("not_equal");
self.build_neq(&not_equal, src1, src2, arg_layout);
self.build_num_lt(dst, src1, src2, arg_layout);
let neq_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, &not_equal);
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
ASM::add_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, neq_reg);
self.free_symbol(&not_equal);
}
fn build_num_lt(
&mut self,
dst: &Symbol,
@ -1947,8 +1984,8 @@ impl<
self.load_layout_stack_size(old_element_layout, old_element_width);
self.load_layout_stack_size(new_element_layout, new_element_width);
let caller_string = self.function_symbol_to_string(
caller_proc.proc_symbol,
let caller_string = self.lambda_name_to_string(
LambdaName::no_niche(caller_proc.proc_symbol),
std::iter::empty(),
None,
Layout::UNIT,
@ -4047,10 +4084,12 @@ impl<
}
IntWidth::I16 | IntWidth::U16 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::xor_reg64_reg64_reg64(buf, dst_reg, dst_reg, dst_reg);
ASM::mov_reg16_mem16_offset32(buf, dst_reg, ptr_reg, offset);
}
IntWidth::I8 | IntWidth::U8 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::xor_reg64_reg64_reg64(buf, dst_reg, dst_reg, dst_reg);
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, offset);
}
},
@ -4065,6 +4104,8 @@ impl<
Builtin::Bool => {
// the same as an 8-bit integer
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::xor_reg64_reg64_reg64(buf, dst_reg, dst_reg, dst_reg);
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, offset);
}
Builtin::Decimal => {

View file

@ -23,6 +23,8 @@ use RegStorage::*;
use StackStorage::*;
use Storage::*;
use super::RegisterWidth;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RegStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
General(GeneralReg),
@ -343,10 +345,19 @@ impl<
sign_extend,
}) => {
let reg = self.get_general_reg(buf);
let register_width = match size {
8 => RegisterWidth::W64,
4 => RegisterWidth::W32,
2 => RegisterWidth::W16,
1 => RegisterWidth::W8,
_ => internal_error!("Invalid size: {size}"),
};
if sign_extend {
ASM::movsx_reg64_base32(buf, reg, base_offset, size as u8);
ASM::movsx_reg_base32(buf, register_width, reg, base_offset);
} else {
ASM::movzx_reg64_base32(buf, reg, base_offset, size as u8);
ASM::movzx_reg_base32(buf, register_width, reg, base_offset);
}
self.general_used_regs.push((reg, *sym));
self.symbol_storage_map.insert(*sym, Reg(General(reg)));
@ -469,10 +480,18 @@ impl<
}) => {
debug_assert!(*size <= 8);
let register_width = match size {
8 => RegisterWidth::W64,
4 => RegisterWidth::W32,
2 => RegisterWidth::W16,
1 => RegisterWidth::W8,
_ => internal_error!("Invalid size: {size}"),
};
if *sign_extend {
ASM::movsx_reg64_base32(buf, reg, *base_offset, *size as u8)
ASM::movsx_reg_base32(buf, register_width, reg, *base_offset)
} else {
ASM::movzx_reg64_base32(buf, reg, *base_offset, *size as u8)
ASM::movzx_reg_base32(buf, register_width, reg, *base_offset)
}
}
Stack(Complex { size, .. }) => {

View file

@ -1687,29 +1687,39 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
}
#[inline(always)]
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
match size {
8 => Self::mov_reg64_base32(buf, dst, offset),
4 => movsx_reg64_base32_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
2 => movsx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
1 => movsx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
_ => internal_error!("Invalid size for sign extension: {size}"),
fn movsx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: X86_64GeneralReg,
offset: i32,
) {
use RegisterWidth::*;
match register_width {
W64 => Self::mov_reg64_base32(buf, dst, offset),
W32 => movsx_reg64_base32_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
W16 => movsx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
W8 => movsx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
}
}
#[inline(always)]
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
match size {
8 => Self::mov_reg64_base32(buf, dst, offset),
4 => {
fn movzx_reg_base32(
buf: &mut Vec<'_, u8>,
register_width: RegisterWidth,
dst: X86_64GeneralReg,
offset: i32,
) {
use RegisterWidth::*;
match register_width {
W64 => Self::mov_reg64_base32(buf, dst, offset),
W32 => {
// The Intel documentation (3.4.1.1 General-Purpose Registers in 64-Bit Mode in manual Basic Architecture))
// 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
Self::mov_reg64_base32(buf, dst, offset)
}
2 => movzx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
1 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
_ => internal_error!("Invalid size for zero extension: {size}"),
W16 => movzx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
W8 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
}
}

View file

@ -20,7 +20,7 @@ use roc_mono::ir::{
Literal, Param, Proc, ProcLayout, SelfRecursive, Stmt,
};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
Builtin, InLayout, LambdaName, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
TagIdIntType, UnionLayout,
};
use roc_mono::list_element_layout;
@ -317,9 +317,9 @@ trait Backend<'a> {
&mut Vec<'a, CallerProc<'a>>,
);
fn function_symbol_to_string<'b, I>(
fn lambda_name_to_string<'b, I>(
&self,
symbol: Symbol,
name: LambdaName,
arguments: I,
_lambda_set: Option<InLayout>,
result: InLayout,
@ -327,18 +327,27 @@ trait Backend<'a> {
where
I: Iterator<Item = InLayout<'b>>,
{
use std::fmt::Write;
use std::hash::{BuildHasher, Hash, Hasher};
// NOTE: due to randomness, this will not be consistent between runs
let mut state = roc_collections::all::BuildHasher::default().build_hasher();
let symbol = name.name();
let mut buf = String::with_capacity(1024);
for a in arguments {
a.hash(&mut state);
write!(buf, "{:?}", self.interner().dbg_stable(a)).expect("capacity");
}
// lambda set should not matter; it should already be added as an argument
// lambda_set.hash(&mut state);
// but the niche of the lambda name may be the only thing differentiating two different
// implementations of a function with the same symbol
write!(buf, "{:?}", name.niche().dbg_stable(self.interner())).expect("capacity");
result.hash(&mut state);
write!(buf, "{:?}", self.interner().dbg_stable(result)).expect("capacity");
// NOTE: due to randomness, this will not be consistent between runs
let mut state = roc_collections::all::BuildHasher::default().build_hasher();
buf.hash(&mut state);
let interns = self.interns();
let ident_string = symbol.as_str(interns);
@ -389,8 +398,8 @@ trait Backend<'a> {
let element_increment = self.debug_symbol("element_increment");
let element_increment_symbol = self.build_indirect_inc(layout);
let element_increment_string = self.function_symbol_to_string(
element_increment_symbol,
let element_increment_string = self.lambda_name_to_string(
LambdaName::no_niche(element_increment_symbol),
[box_layout].into_iter(),
None,
Layout::UNIT,
@ -409,8 +418,8 @@ trait Backend<'a> {
let element_decrement = self.debug_symbol("element_decrement");
let element_decrement_symbol = self.build_indirect_dec(layout);
let element_decrement_string = self.function_symbol_to_string(
element_decrement_symbol,
let element_decrement_string = self.lambda_name_to_string(
LambdaName::no_niche(element_decrement_symbol),
[box_layout].into_iter(),
None,
Layout::UNIT,
@ -452,8 +461,8 @@ trait Backend<'a> {
proc: Proc<'a>,
layout_ids: &mut LayoutIds<'a>,
) -> (Vec<u8>, Vec<Relocation>, Vec<'a, (Symbol, String)>) {
let proc_name = self.function_symbol_to_string(
proc.name.name(),
let proc_name = self.lambda_name_to_string(
proc.name,
proc.args.iter().map(|t| t.0),
proc.closure_data_layout,
proc.ret_layout,
@ -683,15 +692,15 @@ trait Backend<'a> {
// implementation in `build_builtin` inlines some of the symbols.
return self.build_builtin(
sym,
func_sym.name(),
*func_sym,
arguments,
arg_layouts,
ret_layout,
);
}
let fn_name = self.function_symbol_to_string(
func_sym.name(),
let fn_name = self.lambda_name_to_string(
*func_sym,
arg_layouts.iter().copied(),
None,
*ret_layout,
@ -1807,6 +1816,10 @@ trait Backend<'a> {
);
}
LowLevel::NumCompare => {
self.build_num_cmp(sym, &args[0], &args[1], &arg_layouts[0]);
}
x => todo!("low level, {:?}", x),
}
}
@ -1816,12 +1829,12 @@ trait Backend<'a> {
fn build_builtin(
&mut self,
sym: &Symbol,
func_sym: Symbol,
func_name: LambdaName,
args: &'a [Symbol],
arg_layouts: &[InLayout<'a>],
ret_layout: &InLayout<'a>,
) {
match func_sym {
match func_name.name() {
Symbol::NUM_IS_ZERO => {
debug_assert_eq!(
1,
@ -1844,8 +1857,8 @@ trait Backend<'a> {
}
Symbol::LIST_GET | Symbol::LIST_SET | Symbol::LIST_REPLACE | Symbol::LIST_APPEND => {
// TODO: This is probably simple enough to be worth inlining.
let fn_name = self.function_symbol_to_string(
func_sym,
let fn_name = self.lambda_name_to_string(
func_name,
arg_layouts.iter().copied(),
None,
*ret_layout,
@ -1876,8 +1889,8 @@ trait Backend<'a> {
}
Symbol::STR_IS_VALID_SCALAR => {
// just call the function
let fn_name = self.function_symbol_to_string(
func_sym,
let fn_name = self.lambda_name_to_string(
func_name,
arg_layouts.iter().copied(),
None,
*ret_layout,
@ -1888,8 +1901,8 @@ trait Backend<'a> {
}
_other => {
// just call the function
let fn_name = self.function_symbol_to_string(
func_sym,
let fn_name = self.lambda_name_to_string(
func_name,
arg_layouts.iter().copied(),
None,
*ret_layout,
@ -2057,6 +2070,14 @@ trait Backend<'a> {
/// build_not stores the result of `!src` into dst.
fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>);
fn build_num_cmp(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
arg_layout: &InLayout<'a>,
);
/// build_num_lt stores the result of `src1 < src2` into dst.
fn build_num_lt(
&mut self,

View file

@ -343,8 +343,8 @@ fn build_object<'a, B: Backend<'a>>(
for ((sym, layout), proc) in helper_symbols_and_layouts.into_iter().zip(helper_procs) {
debug_assert_eq!(sym, proc.name.name());
let fn_name = backend.function_symbol_to_string(
sym,
let fn_name = backend.lambda_name_to_string(
LambdaName::no_niche(sym),
layout.arguments.iter().copied(),
None,
layout.result,
@ -559,8 +559,8 @@ fn build_proc_symbol<'a, B: Backend<'a>>(
Exposed::Exposed => layout_ids
.get_toplevel(sym, &layout)
.to_exposed_symbol_string(sym, backend.interns()),
Exposed::NotExposed => backend.function_symbol_to_string(
sym,
Exposed::NotExposed => backend.lambda_name_to_string(
proc.name,
layout.arguments.iter().copied(),
None,
layout.result,

View file

@ -1304,10 +1304,18 @@ impl<'a> Niche<'a> {
pub fn dbg_deep<'r, I: LayoutInterner<'a>>(
&'r self,
interner: &'r I,
) -> crate::layout::intern::dbg::DbgFields<'a, 'r, I> {
) -> crate::layout::intern::dbg_deep::DbgFields<'a, 'r, I> {
let NichePriv::Captures(caps) = &self.0;
interner.dbg_deep_iter(caps)
}
pub fn dbg_stable<'r, I: LayoutInterner<'a>>(
&'r self,
interner: &'r I,
) -> crate::layout::intern::dbg_stable::DbgFields<'a, 'r, I> {
let NichePriv::Captures(caps) = &self.0;
interner.dbg_stable_iter(caps)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]

View file

@ -405,12 +405,28 @@ pub trait LayoutInterner<'a>: Sized {
/// return false;
/// }
/// ```
fn dbg_deep<'r>(&'r self, layout: InLayout<'a>) -> dbg::Dbg<'a, 'r, Self> {
dbg::Dbg(self, layout)
fn dbg_deep<'r>(&'r self, layout: InLayout<'a>) -> dbg_deep::Dbg<'a, 'r, Self> {
dbg_deep::Dbg(self, layout)
}
fn dbg_deep_iter<'r>(&'r self, layouts: &'a [InLayout<'a>]) -> dbg::DbgFields<'a, 'r, Self> {
dbg::DbgFields(self, layouts)
fn dbg_deep_iter<'r>(
&'r self,
layouts: &'a [InLayout<'a>],
) -> dbg_deep::DbgFields<'a, 'r, Self> {
dbg_deep::DbgFields(self, layouts)
}
/// Similar to `Self::dbg_deep`, but does not display the interned name of symbols. This keeps
/// the output consistent in a multi-threaded (test) run
fn dbg_stable<'r>(&'r self, layout: InLayout<'a>) -> dbg_stable::Dbg<'a, 'r, Self> {
dbg_stable::Dbg(self, layout)
}
fn dbg_stable_iter<'r>(
&'r self,
layouts: &'a [InLayout<'a>],
) -> dbg_stable::DbgFields<'a, 'r, Self> {
dbg_stable::DbgFields(self, layouts)
}
}
@ -1372,7 +1388,7 @@ mod equiv {
}
}
pub mod dbg {
pub mod dbg_deep {
use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, LayoutRepr, UnionLayout};
@ -1531,6 +1547,179 @@ pub mod dbg {
}
}
/// Provides a stable debug output
///
/// The debug output defined in `dbg_deep` uses the `Symbol` `std::fmt::Debug` instance, which uses
/// interned string names to make the output easier to interpret. That is useful for manual
/// debugging, but the interned strings are not stable in a multi-threaded context (e.g. when
/// running `cargo test`). The output of this module is always stable.
pub mod dbg_stable {
use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, LayoutRepr, SemanticRepr, UnionLayout};
use super::{InLayout, LayoutInterner};
pub struct Dbg<'a, 'r, I: LayoutInterner<'a>>(pub &'r I, pub InLayout<'a>);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for Dbg<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let repr = self.0.get_repr(self.1);
let semantic = self.0.get_semantic(self.1);
struct ConsistentSemanticRepr<'a>(SemanticRepr<'a>);
impl std::fmt::Debug for ConsistentSemanticRepr<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt_consistent(f)
}
}
f.debug_struct("Layout")
.field("repr", &DbgRepr(self.0, &repr))
.field("semantic", &ConsistentSemanticRepr(semantic))
.finish()
}
}
pub struct DbgFields<'a, 'r, I: LayoutInterner<'a>>(pub &'r I, pub &'a [InLayout<'a>]);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgFields<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.1.iter().map(|l| Dbg(self.0, *l)))
.finish()
}
}
struct DbgRepr<'a, 'r, I: LayoutInterner<'a>>(&'r I, &'r LayoutRepr<'a>);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgRepr<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.1 {
LayoutRepr::Builtin(b) => f
.debug_tuple("Builtin")
.field(&DbgBuiltin(self.0, *b))
.finish(),
LayoutRepr::Struct(field_layouts) => f
.debug_struct("Struct")
.field("fields", &DbgFields(self.0, field_layouts))
.finish(),
LayoutRepr::Boxed(b) => f.debug_tuple("Boxed").field(&Dbg(self.0, *b)).finish(),
LayoutRepr::Union(un) => f
.debug_tuple("Union")
.field(&DbgUnion(self.0, *un))
.finish(),
LayoutRepr::LambdaSet(ls) => f
.debug_tuple("LambdaSet")
.field(&DbgLambdaSet(self.0, *ls))
.finish(),
LayoutRepr::RecursivePointer(rp) => {
f.debug_tuple("RecursivePointer").field(&rp.0).finish()
}
}
}
}
struct DbgTags<'a, 'r, I: LayoutInterner<'a>>(&'r I, &'a [&'a [InLayout<'a>]]);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgTags<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.1.iter().map(|l| DbgFields(self.0, l)))
.finish()
}
}
struct DbgBuiltin<'a, 'r, I: LayoutInterner<'a>>(&'r I, Builtin<'a>);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgBuiltin<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.1 {
Builtin::Int(w) => f.debug_tuple("Int").field(&w).finish(),
Builtin::Float(w) => f.debug_tuple("Float").field(&w).finish(),
Builtin::Bool => f.debug_tuple("Bool").finish(),
Builtin::Decimal => f.debug_tuple("Decimal").finish(),
Builtin::Str => f.debug_tuple("Str").finish(),
Builtin::List(e) => f.debug_tuple("List").field(&Dbg(self.0, e)).finish(),
}
}
}
struct DbgUnion<'a, 'r, I: LayoutInterner<'a>>(&'r I, UnionLayout<'a>);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgUnion<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.1 {
UnionLayout::NonRecursive(payloads) => f
.debug_tuple("NonRecursive")
.field(&DbgTags(self.0, payloads))
.finish(),
UnionLayout::Recursive(payloads) => f
.debug_tuple("Recursive")
.field(&DbgTags(self.0, payloads))
.finish(),
UnionLayout::NonNullableUnwrapped(fields) => f
.debug_tuple("NonNullableUnwrapped")
.field(&DbgFields(self.0, fields))
.finish(),
UnionLayout::NullableWrapped {
nullable_id,
other_tags,
} => f
.debug_struct("NullableWrapped")
.field("nullable_id", &nullable_id)
.field("other_tags", &DbgTags(self.0, other_tags))
.finish(),
UnionLayout::NullableUnwrapped {
nullable_id,
other_fields,
} => f
.debug_struct("NullableUnwrapped")
.field("nullable_id", &nullable_id)
.field("other_tags", &DbgFields(self.0, other_fields))
.finish(),
}
}
}
struct DbgLambdaSet<'a, 'r, I: LayoutInterner<'a>>(&'r I, LambdaSet<'a>);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgLambdaSet<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let LambdaSet {
args,
ret,
set,
representation,
full_layout,
} = self.1;
f.debug_struct("LambdaSet")
.field("args", &DbgFields(self.0, args))
.field("ret", &Dbg(self.0, ret))
.field("set", &DbgCapturesSet(self.0, set))
.field("representation", &Dbg(self.0, representation))
.field("full_layout", &full_layout)
.finish()
}
}
struct DbgCapturesSet<'a, 'r, I: LayoutInterner<'a>>(&'r I, &'a [(Symbol, &'a [InLayout<'a>])]);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgCapturesSet<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(
self.1
.iter()
.map(|(sym, captures)| (sym.as_u64(), DbgFields(self.0, captures))),
)
.finish()
}
}
}
#[cfg(test)]
mod insert_lambda_set {
use bumpalo::Bump;

View file

@ -15,6 +15,12 @@ impl<'a> std::fmt::Debug for SemanticRepr<'a> {
}
}
impl<'a> SemanticRepr<'a> {
pub fn fmt_consistent(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt_consistent(f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Inner<'a> {
None,
@ -24,6 +30,19 @@ enum Inner<'a> {
Lambdas(SemaLambdas<'a>),
}
impl<'a> Inner<'a> {
fn fmt_consistent(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// don't print the interned string name of a symbol to get consistent output
if let Self::Lambdas(sema_lambdas) = self {
f.debug_struct("SemaLambdas")
.field("lambda_count", &sema_lambdas.lambdas.len())
.finish()
} else {
std::fmt::Debug::fmt(&self, f)
}
}
}
impl<'a> SemanticRepr<'a> {
pub(super) const NONE: Self = Self(Inner::None);
pub(super) const EMPTY_RECORD: Self = Self::record(&[]);

View file

@ -304,7 +304,7 @@ fn apply_unnamed_identity() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_unnamed_fn() {
assert_evals_to!(
indoc!(
@ -325,7 +325,7 @@ fn return_unnamed_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_when_fn() {
assert_evals_to!(
indoc!(
@ -345,7 +345,7 @@ fn gen_when_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_basic_def() {
assert_evals_to!(
indoc!(
@ -373,7 +373,7 @@ fn gen_basic_def() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_multiple_defs() {
assert_evals_to!(
indoc!(
@ -492,7 +492,7 @@ fn gen_multiple_defs() {
// }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn factorial() {
assert_evals_to!(
indoc!(
@ -514,7 +514,7 @@ fn factorial() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn peano1() {
assert_evals_to!(
indoc!(
@ -535,7 +535,7 @@ fn peano1() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn peano2() {
assert_evals_to!(
indoc!(
@ -557,7 +557,7 @@ fn peano2() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn top_level_constant() {
assert_evals_to!(
indoc!(
@ -577,7 +577,7 @@ fn top_level_constant() {
#[test]
#[ignore]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn top_level_destructure() {
assert_evals_to!(
indoc!(
@ -829,7 +829,7 @@ fn linked_list_map() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn when_nested_maybe() {
assert_evals_to!(
indoc!(
@ -886,7 +886,7 @@ fn when_nested_maybe() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn when_peano() {
assert_evals_to!(
indoc!(
@ -1011,7 +1011,7 @@ fn annotation_without_body() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn simple_closure() {
assert_evals_to!(
indoc!(
@ -1033,7 +1033,7 @@ fn simple_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn nested_closure() {
assert_evals_to!(
indoc!(
@ -1057,7 +1057,7 @@ fn nested_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn closure_in_list() {
use roc_std::RocList;
@ -1085,7 +1085,7 @@ fn closure_in_list() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn specialize_closure() {
use roc_std::RocList;
@ -1117,7 +1117,7 @@ fn specialize_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn io_poc_effect() {
assert_evals_to!(
indoc!(
@ -1148,7 +1148,7 @@ fn io_poc_effect() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn io_poc_desugared() {
assert_evals_to!(
indoc!(
@ -1176,7 +1176,7 @@ fn io_poc_desugared() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_function_a() {
assert_evals_to!(
indoc!(
@ -1198,7 +1198,7 @@ fn return_wrapped_function_a() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_function_b() {
assert_evals_to!(
indoc!(
@ -1219,7 +1219,7 @@ fn return_wrapped_function_b() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn return_wrapped_closure() {
assert_evals_to!(
indoc!(
@ -1367,7 +1367,7 @@ fn linked_list_singleton() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn recursive_function_with_rigid() {
assert_evals_to!(
indoc!(
@ -1394,7 +1394,7 @@ fn recursive_function_with_rigid() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn rbtree_insert() {
assert_evals_to!(
indoc!(
@ -1606,7 +1606,7 @@ fn rbtree_balance_mono_problem() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn rbtree_balance_full() {
assert_evals_to!(
indoc!(
@ -1658,7 +1658,7 @@ fn rbtree_balance_full() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn nested_pattern_match_two_ways() {
// exposed an issue in the ordering of pattern match checks when ran with `--release` mode
assert_evals_to!(
@ -1771,6 +1771,7 @@ fn linked_list_double_pattern_match() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// dev backend: this test somehow corrupts the errors vector ?!
fn binary_tree_double_pattern_match() {
assert_evals_to!(
indoc!(
@ -1796,7 +1797,7 @@ fn binary_tree_double_pattern_match() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn unified_empty_closure_bool() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
@ -1821,7 +1822,7 @@ fn unified_empty_closure_bool() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn unified_empty_closure_byte() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
@ -1847,7 +1848,7 @@ fn unified_empty_closure_byte() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn task_always_twice() {
assert_evals_to!(
indoc!(
@ -1892,7 +1893,7 @@ fn task_always_twice() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn wildcard_rigid() {
assert_evals_to!(
indoc!(
@ -1921,7 +1922,7 @@ fn wildcard_rigid() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn alias_of_alias_with_type_arguments() {
assert_evals_to!(
indoc!(
@ -1998,7 +1999,7 @@ fn todo_bad_error_message() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn hof_conditional() {
// exposed issue with the if condition being just a symbol
assert_evals_to!(
@ -2095,7 +2096,7 @@ fn fingertree_basic() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn case_or_pattern() {
// the `0` branch body should only be generated once in the future
// it is currently duplicated
@ -3576,7 +3577,7 @@ fn polymorphic_lambda_captures_polymorphic_value() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niche_u64_vs_u8_capture() {
assert_evals_to!(
indoc!(
@ -3604,7 +3605,7 @@ fn lambda_capture_niche_u64_vs_u8_capture() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_with_other_lambda_capture() {
assert_evals_to!(
indoc!(
@ -3637,7 +3638,7 @@ fn lambda_capture_niches_with_other_lambda_capture() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_with_non_capturing_function() {
assert_evals_to!(
indoc!(
@ -3670,7 +3671,7 @@ fn lambda_capture_niches_with_non_capturing_function() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn lambda_capture_niches_have_captured_function_in_closure() {
assert_evals_to!(
indoc!(
@ -3862,7 +3863,7 @@ fn recursive_lambda_set_issue_3444_inferred() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn compose_recursive_lambda_set_productive_toplevel() {
assert_evals_to!(
indoc!(
@ -3930,7 +3931,7 @@ fn compose_recursive_lambda_set_productive_inferred() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn compose_recursive_lambda_set_productive_nullable_wrapped() {
assert_evals_to!(
indoc!(
@ -4216,7 +4217,7 @@ fn issue_4349() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn issue_4712() {
assert_evals_to!(
indoc!(