From f96c78b3a5a50ed0f82322860915b170830c79d8 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 23 Jul 2023 15:05:24 +0200 Subject: [PATCH 01/25] setjmp/longjmp assembly gen --- .../compiler/gen_dev/src/generic64/aarch64.rs | 20 +- crates/compiler/gen_dev/src/generic64/mod.rs | 26 +++ .../compiler/gen_dev/src/generic64/x86_64.rs | 189 ++++++++++++++++++ crates/compiler/gen_dev/src/lib.rs | 4 + crates/compiler/gen_dev/src/object_builder.rs | 46 +++++ 5 files changed, 284 insertions(+), 1 deletion(-) diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index fb777b1f4b..79ad1276ab 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -455,6 +455,14 @@ impl CallConv for AArch64C ) { todo!("Loading returned complex symbols for AArch64"); } + + fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } + + fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } } impl Assembler for AArch64Assembler { @@ -529,7 +537,17 @@ impl Assembler for AArch64Assembler { _fn_name: String, _dst: AArch64GeneralReg, ) { - todo!("calling functions literal for AArch64"); + todo!("function pointer for AArch64"); + } + + #[inline(always)] + fn data_pointer( + _buf: &mut Vec<'_, u8>, + _relocs: &mut Vec<'_, Relocation>, + _fn_name: String, + _dst: AArch64GeneralReg, + ) { + todo!("data pointer for AArch64"); } #[inline(always)] diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 68b27356f3..e50a715a53 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -134,6 +134,9 @@ pub trait CallConv, ); + + fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); + fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); } pub enum CompareOperation { @@ -238,6 +241,13 @@ pub trait Assembler: Sized + Copy { dst: GeneralReg, ); + fn data_pointer( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + fn_name: String, + dst: GeneralReg, + ); + /// Jumps by an offset of offset bytes unconditionally. /// It should always generate the same number of bytes to enable replacement if offset changes. /// It returns the base offset to calculate the jump from (generally the instruction after the jump). @@ -887,6 +897,22 @@ impl< (out.into_bump_slice(), offset) } + fn build_roc_setjmp(&mut self) -> &'a [u8] { + let mut out = bumpalo::vec![in self.env.arena]; + + CC::setjmp(&mut out, &mut self.relocs); + + out.into_bump_slice() + } + + fn build_roc_longjmp(&mut self) -> &'a [u8] { + let mut out = bumpalo::vec![in self.env.arena]; + + CC::longjmp(&mut out, &mut self.relocs); + + out.into_bump_slice() + } + fn build_fn_pointer(&mut self, dst: &Symbol, fn_name: String) { let reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index f67f877194..e2ca290b1d 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -436,6 +436,90 @@ impl CallConv for X86_64Syste } } } + + fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + use X86_64GeneralReg::*; + type ASM = X86_64Assembler; + + // based on the musl libc setjmp implementation + // + // 000000000020237c <__setjmp>: + // 20237c: 48 89 1f mov QWORD PTR [rdi],rbx + // 20237f: 48 89 6f 08 mov QWORD PTR [rdi+0x8],rbp + // 202383: 4c 89 67 10 mov QWORD PTR [rdi+0x10],r12 + // 202387: 4c 89 6f 18 mov QWORD PTR [rdi+0x18],r13 + // 20238b: 4c 89 77 20 mov QWORD PTR [rdi+0x20],r14 + // 20238f: 4c 89 7f 28 mov QWORD PTR [rdi+0x28],r15 + // 202393: 48 8d 54 24 08 lea rdx,[rsp+0x8] + // 202398: 48 89 57 30 mov QWORD PTR [rdi+0x30],rdx + // 20239c: 48 8b 14 24 mov rdx,QWORD PTR [rsp] + // 2023a0: 48 89 57 38 mov QWORD PTR [rdi+0x38],rdx + // 2023a4: 31 c0 xor eax,eax + // 2023a6: c3 ret + + let mut offset = 0; + + // move the argument to rdi + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RBX); + offset += 8; + + // store the base pointer + ASM::mov_mem64_offset32_reg64(buf, RBP, offset, RBP); + offset += 8; + + // store other non-volatile registers + for register in [R12, R13, R14, R15] { + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, register); + offset += 8; + } + + // not 100% sure on what this does. It calculates the address that the longjmp will jump + // to, so this just be right after the call to setjmp. + lea_reg64_offset8(buf, RDX, RSP, 0x8); + + // store the location to later jump to + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RDX); + offset += 8; + + // store the current stack pointer + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RSP); + + // zero out eax, so we return 0i32 (we do a 64-bit xor for convenience) + ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); + + ASM::ret(buf) + } + + fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + use X86_64GeneralReg::*; + type ASM = X86_64Assembler; + + // 202358: 31 c0 xor eax,eax + // 20235a: 83 fe 01 cmp esi,0x1 + // 20235d: 11 f0 adc eax,esi + // 20235f: 48 8b 1f mov rbx,QWORD PTR [rdi] + // 202362: 48 8b 6f 08 mov rbp,QWORD PTR [rdi+0x8] + // 202366: 4c 8b 67 10 mov r12,QWORD PTR [rdi+0x10] + // 20236a: 4c 8b 6f 18 mov r13,QWORD PTR [rdi+0x18] + // 20236e: 4c 8b 77 20 mov r14,QWORD PTR [rdi+0x20] + // 202372: 4c 8b 7f 28 mov r15,QWORD PTR [rdi+0x28] + // 202376: 48 8b 67 30 mov rsp,QWORD PTR [rdi+0x30] + // 20237a: ff 67 38 jmp QWORD PTR [rdi+0x38] + + // make sure something nonzero is returned ?! + ASM::mov_reg64_imm64(buf, RAX, 0x1); + + // move the values back into the registers + let mut offset = 0; + let registers = [RBX, RBP, R12, R13, R14, R15, RSP]; + + for dst in registers { + ASM::mov_reg64_mem64_offset32(buf, dst, RDI, offset); + offset += 8; + } + + jmp_reg64_offset8(buf, RDI, offset as i8) + } } struct X64_64SystemVStoreArgs { @@ -1023,6 +1107,14 @@ impl CallConv for X86_64Windo ) { todo!("Loading returned complex symbols for X86_64"); } + + fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } + + fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } } impl X86_64WindowsFastcall { @@ -1239,6 +1331,21 @@ impl Assembler for X86_64Assembler { }); } + #[inline(always)] + fn data_pointer( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + fn_name: String, + dst: X86_64GeneralReg, + ) { + lea_reg64(buf, dst); + + relocs.push(Relocation::LinkedData { + offset: buf.len() as u64 - 4, + name: fn_name, + }); + } + #[inline(always)] fn imul_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, @@ -2610,6 +2717,21 @@ fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) { buf.extend(imm.to_le_bytes()); } +#[inline(always)] +fn jmp_reg64_offset8(buf: &mut Vec<'_, u8>, base: X86_64GeneralReg, offset: i8) { + let rex = add_rm_extension(base, REX_W); + + #[allow(clippy::unusual_byte_groupings)] + buf.extend([rex, 0xff, 0b01_100_000 | base as u8 % 8]); + + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + + buf.extend(offset.to_le_bytes()) +} + /// Jump near if not equal (ZF=0). #[inline(always)] fn jne_imm32(buf: &mut Vec<'_, u8>, imm: i32) { @@ -2662,6 +2784,32 @@ fn lea_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg) { ]) } +/// `LEA r64, m` -> Store effective address for m in register r64. +#[inline(always)] +fn lea_reg64_offset8( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, + offset: i8, +) { + let rex = add_rm_extension(src, REX_W); + let rex = add_reg_extension(dst, rex); + + let dst_mod = dst as u8 % 8; + let src_mod = src as u8 % 8; + + #[allow(clippy::unusual_byte_groupings)] + // the upper bits 0b01 of the mod_rm byte indicate 8-bit displacement + buf.extend([rex, 0x8d, 0b01_000_000 | (dst_mod << 3) | src_mod]); + + // Using RSP or R12 requires a secondary index byte. + if src == X86_64GeneralReg::RSP || src == X86_64GeneralReg::R12 { + buf.push(0x24); + } + + buf.push(offset as u8); +} + fn raw_mov_reg_reg( buf: &mut Vec<'_, u8>, register_width: RegisterWidth, @@ -2899,6 +3047,16 @@ fn mov_reg_base_offset32( buf.extend(offset.to_le_bytes()); } +#[inline(always)] +fn mov_reg64_offset32_reg64( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, +) { + todo!() +} + /// `MOV r64,r/m64` -> Move r/m64 to r64, where m64 references a base + offset. #[inline(always)] fn mov_reg64_base64_offset32( @@ -3832,6 +3990,20 @@ mod tests { ); } + #[test] + fn test_jmp_reg64_offset8() { + disassembler_test!( + jmp_reg64_offset8, + |base, offset| if offset < 0x10 { + format!("jmp qword ptr [{base} + {offset:x}]") + } else { + format!("jmp qword ptr [{base} + 0x{offset:x}]") + }, + ALL_GENERAL_REGS, + [0x8, 0x10] + ); + } + #[test] fn test_jne_imm32() { const INST_SIZE: i32 = 6; @@ -3877,6 +4049,23 @@ mod tests { ); } + #[test] + fn test_lea_reg64_offset32() { + disassembler_test!( + lea_reg64_offset8, + |dst, src, offset| { + if offset < 16 { + format!("lea {dst}, [{src} + {offset:x}]") + } else { + format!("lea {dst}, [{src} + 0x{offset:x}]") + } + }, + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [0x8i8, 0x10i8] + ); + } + #[test] fn test_mov_reg64_reg64() { disassembler_test!( diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 90b2d0cf9d..26e272610a 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -463,6 +463,10 @@ trait Backend<'a> { /// Used for generating wrappers for malloc/realloc/free fn build_wrapped_jmp(&mut self) -> (&'a [u8], u64); + // use for roc_panic + fn build_roc_setjmp(&mut self) -> &'a [u8]; + fn build_roc_longjmp(&mut self) -> &'a [u8]; + /// build_proc creates a procedure and outputs it to the wrapped object writer. /// Returns the procedure bytes, its relocations, and the names of the refcounting functions it references. fn build_proc( diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 504d1f9ea0..93b40cc2e8 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -30,6 +30,8 @@ pub fn build_module<'a, 'r>( target: &Triple, procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, ) -> Object<'a> { + dbg!(&target); + match target { Triple { architecture: TargetArch::X86_64, @@ -118,6 +120,46 @@ pub fn build_module<'a, 'r>( } } +fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( + backend: &mut B, + output: &mut Object, +) -> SymbolId { + let bss_section = output.section_id(StandardSection::UninitializedData); + + dbg!(bss_section); + + let symbol = Symbol { + name: b"setlongjmp_buffer".to_vec(), + value: 0, + size: 8 * core::mem::size_of::() as u64, + kind: SymbolKind::Data, + scope: SymbolScope::Linkage, + weak: false, + section: SymbolSection::Section(bss_section), + flags: SymbolFlags::None, + }; + + dbg!(output.add_symbol(symbol)) +} + +fn generate_roc_setjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { + let text_section = output.section_id(StandardSection::Text); + let proc_symbol = Symbol { + name: b"roc_setjmp".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(text_section), + flags: SymbolFlags::None, + }; + + let proc_id = output.add_symbol(proc_symbol); + let (proc_data, offset) = backend.build_wrapped_jmp(); + let proc_offset = output.add_symbol_data(proc_id, text_section, proc_data, 16); +} + fn generate_wrapper<'a, B: Backend<'a>>( backend: &mut B, output: &mut Object, @@ -178,6 +220,8 @@ fn build_object<'a, B: Backend<'a>>( ) -> Object<'a> { let data_section = output.section_id(StandardSection::Data); + dbg!("build object"); + let arena = backend.env().arena; /* @@ -190,6 +234,8 @@ fn build_object<'a, B: Backend<'a>>( ); */ + let setlongjmp_buffer = generate_setlongjmp_buffer(&mut backend, &mut output); + if backend.env().mode.generate_allocators() { generate_wrapper( &mut backend, From 10aa4f58c99a3191efb1cd9f74c21b4d2fe68fc5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 23 Jul 2023 19:02:17 +0200 Subject: [PATCH 02/25] WIP --- crates/compiler/gen_dev/src/generic64/mod.rs | 6 ++ crates/compiler/gen_dev/src/lib.rs | 18 ++++ crates/compiler/gen_dev/src/object_builder.rs | 76 ++++++++++++++- crates/compiler/mono/src/code_gen_help/mod.rs | 94 ++++++++++++++++++- crates/compiler/test_gen/src/helpers/dev.rs | 2 +- 5 files changed, 191 insertions(+), 5 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index e50a715a53..9e3aac1c00 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -919,6 +919,12 @@ impl< ASM::function_pointer(&mut self.buf, &mut self.relocs, fn_name, reg) } + fn build_data_pointer(&mut self, dst: &Symbol, data_name: String) { + let reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); + + ASM::data_pointer(&mut self.buf, &mut self.relocs, data_name, reg) + } + fn build_fn_call( &mut self, dst: &Symbol, diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 26e272610a..1eb1176498 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -1665,6 +1665,23 @@ trait Backend<'a> { arg_layouts, ret_layout, ), + LowLevel::SetJmp => self.build_fn_call( + sym, + String::from("roc_setjmp"), + args, + arg_layouts, + ret_layout, + ), + LowLevel::LongJmp => self.build_fn_call( + sym, + String::from("roc_longjmp"), + args, + arg_layouts, + ret_layout, + ), + LowLevel::SetLongJmpBuffer => { + self.build_data_pointer(sym, String::from("setlongjmp_buffer")) + } LowLevel::DictPseudoSeed => self.build_fn_call( sym, bitcode::UTILS_DICT_PSEUDO_SEED.to_string(), @@ -1967,6 +1984,7 @@ trait Backend<'a> { ); fn build_fn_pointer(&mut self, dst: &Symbol, fn_name: String); + fn build_data_pointer(&mut self, dst: &Symbol, data_name: String); /// Move a returned value into `dst` fn move_return_value(&mut self, dst: &Symbol, ret_layout: &InLayout<'a>); diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 93b40cc2e8..01d70f20a3 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -142,7 +142,7 @@ fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( dbg!(output.add_symbol(symbol)) } -fn generate_roc_setjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { +fn generate_roc_setlongjmp_buffer<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { let text_section = output.section_id(StandardSection::Text); let proc_symbol = Symbol { name: b"roc_setjmp".to_vec(), @@ -160,6 +160,42 @@ fn generate_roc_setjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) let proc_offset = output.add_symbol_data(proc_id, text_section, proc_data, 16); } +fn generate_setjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { + let text_section = output.section_id(StandardSection::Text); + let proc_symbol = Symbol { + name: b"roc_setjmp".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(text_section), + flags: SymbolFlags::None, + }; + let proc_id = output.add_symbol(proc_symbol); + let proc_data = backend.build_roc_setjmp(); + + output.add_symbol_data(proc_id, text_section, proc_data, 16); +} + +fn generate_longjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { + let text_section = output.section_id(StandardSection::Text); + let proc_symbol = Symbol { + name: b"roc_longjmp".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(text_section), + flags: SymbolFlags::None, + }; + let proc_id = output.add_symbol(proc_symbol); + let proc_data = backend.build_roc_longjmp(); + + output.add_symbol_data(proc_id, text_section, proc_data, 16); +} + fn generate_wrapper<'a, B: Backend<'a>>( backend: &mut B, output: &mut Object, @@ -234,7 +270,10 @@ fn build_object<'a, B: Backend<'a>>( ); */ - let setlongjmp_buffer = generate_setlongjmp_buffer(&mut backend, &mut output); + generate_setlongjmp_buffer(&mut backend, &mut output); + + generate_setjmp(&mut backend, &mut output); + generate_longjmp(&mut backend, &mut output); if backend.env().mode.generate_allocators() { generate_wrapper( @@ -576,6 +615,39 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<' } } +fn build_test_main<'a, B: Backend<'a>>( + output: &mut Object<'a>, + layout_ids: &mut LayoutIds<'a>, + procs: &mut Vec<'a, (String, SectionId, SymbolId, Proc<'a>)>, + backend: &mut B, + layout: ProcLayout<'a>, + proc: Proc<'a>, + exposed: Exposed, +) { + let sym = proc.name.name(); + + let section_id = output.add_section( + output.segment_name(StandardSegment::Text).to_vec(), + &b".text.test_main", + SectionKind::Text, + ); + + let fn_name = "test_main"; + + let proc_symbol = Symbol { + name: fn_name.as_bytes().to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }; + let proc_id = output.add_symbol(proc_symbol); + procs.push((fn_name, section_id, proc_id, proc)); +} + #[allow(clippy::enum_variant_names)] enum Exposed { ExposedGeneric, diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 861aaec471..c266787194 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -6,8 +6,8 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_target::TargetInfo; use crate::ir::{ - Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, PassedFunction, - Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, + Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, Literal, ModifyRc, + PassedFunction, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; use crate::layout::{ Builtin, InLayout, LambdaName, Layout, LayoutInterner, LayoutRepr, LayoutWrapper, Niche, @@ -848,3 +848,93 @@ fn layout_needs_helper_proc<'a>( LayoutRepr::Erased(_) => true, } } + +fn test_helper<'a>( + arena: &'a Bump, + layout_interner: &STLayoutInterner<'a>, + main_proc: &Proc<'a>, +) -> Proc<'a> { + Proc { + name: (), + args: (), + body: (), + closure_data_layout: (), + ret_layout: (), + is_self_recursive: (), + host_exposed_layouts: (), + is_erased: (), + } +} + +fn test_helper_body<'a>( + env: &CodeGenHelp, + ident_ids: &mut IdentIds, + layout_interner: &STLayoutInterner<'a>, + main_proc: &Proc<'a>, +) -> Stmt<'a> { + // let buffer = SetLongJmpBuffer + let buffer_symbol = env.create_symbol(ident_ids, "buffer"); + let buffer_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::SetLongJmpBuffer, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: &[], + }); + let buffer_stmt = |next| Stmt::Let(buffer_symbol, buffer_expr, Layout::U64, next); + + let is_longjmp_symbol = env.create_symbol(ident_ids, "is_longjmp"); + let is_longjmp_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::SetJmp, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: &[buffer_symbol], + }); + let is_longjmp_stmt = |next| Stmt::Let(is_longjmp_symbol, is_longjmp_expr, Layout::U64, next); + + // tag: u64, + // error_msg: *mut RocStr, + // value: MaybeUninit, + let repr = + LayoutRepr::Struct( + env.arena + .alloc([Layout::U64, Layout::U64, main_proc.ret_layout]), + ); + let return_layout = layout_interner.insert_direct_no_semantic(repr); + + // normal path, no panics + let else_branch_stmt = { + let result_symbol = Expr::Call(Call { + call_type: CallType::ByName { + name: (), + ret_layout: (), + arg_layouts: (), + specialization_id: (), + }, + arguments: proc_arguments, + }); + + let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); + let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let ok_tag = |next| Stmt::Let(ok_tag_symbol, ok_tag_expr, Layout::U64, next); + + let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); + let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); + }; + + // a longjmp/panic occured + let then_branch_stmt = { + // + todo!() + }; + + Stmt::if_then_else( + env.arena, + is_longjmp_symbol, + return_layout, + then_branch_stmt, + else_branch_stmt, + ) +} diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index fd94e6e87f..3cbb4324e2 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -212,7 +212,7 @@ pub fn helper( let builtins_host_tempfile = roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile"); - if false { + if true { std::fs::copy(&app_o_file, "/tmp/app.o").unwrap(); } From e7f9ff8b51d44d1cbbbfbfe5c50be03387ab1551 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 24 Jul 2023 18:59:49 +0200 Subject: [PATCH 03/25] WIP --- crates/compiler/mono/src/code_gen_help/mod.rs | 172 ++++++++++++++---- 1 file changed, 140 insertions(+), 32 deletions(-) diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index c266787194..3b6cb103b5 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -1,13 +1,14 @@ use bumpalo::collections::vec::Vec; use bumpalo::collections::CollectIn; use bumpalo::Bump; +use roc_can::abilities::SpecializationId; use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_target::TargetInfo; use crate::ir::{ - Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, Literal, ModifyRc, - PassedFunction, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, + BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, Literal, + ModifyRc, PassedFunction, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; use crate::layout::{ Builtin, InLayout, LambdaName, Layout, LayoutInterner, LayoutRepr, LayoutWrapper, Niche, @@ -850,27 +851,45 @@ fn layout_needs_helper_proc<'a>( } fn test_helper<'a>( - arena: &'a Bump, - layout_interner: &STLayoutInterner<'a>, + env: &CodeGenHelp<'a>, + ident_ids: &mut IdentIds, + layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, ) -> Proc<'a> { + let it = (0..main_proc.args.len()).map(|i| env.create_symbol(ident_ids, &format!("arg_{i}"))); + let arguments = Vec::from_iter_in(it, env.arena).into_bump_slice(); + + let it = arguments + .iter() + .zip(main_proc.args.iter()) + .map(|(s, (l, _))| (*l, *s)); + let args = Vec::from_iter_in(it, env.arena).into_bump_slice(); + + let body = test_helper_body(env, ident_ids, layout_interner, main_proc, arguments); + + let name = LambdaName::no_niche(env.create_symbol(ident_ids, "test_main")); + Proc { - name: (), - args: (), - body: (), - closure_data_layout: (), - ret_layout: (), - is_self_recursive: (), - host_exposed_layouts: (), - is_erased: (), + name, + args, + body, + closure_data_layout: None, + ret_layout: main_proc.ret_layout, + is_self_recursive: main_proc.is_self_recursive, + host_exposed_layouts: HostExposedLayouts::HostExposed { + rigids: Default::default(), + aliases: Default::default(), + }, + is_erased: false, } } fn test_helper_body<'a>( - env: &CodeGenHelp, + env: &CodeGenHelp<'a>, ident_ids: &mut IdentIds, - layout_interner: &STLayoutInterner<'a>, + layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, + arguments: &'a [Symbol], ) -> Stmt<'a> { // let buffer = SetLongJmpBuffer let buffer_symbol = env.create_symbol(ident_ids, "buffer"); @@ -896,24 +915,26 @@ fn test_helper_body<'a>( // tag: u64, // error_msg: *mut RocStr, // value: MaybeUninit, - let repr = - LayoutRepr::Struct( - env.arena - .alloc([Layout::U64, Layout::U64, main_proc.ret_layout]), - ); + let fields = [Layout::U64, Layout::U64, main_proc.ret_layout]; + let repr = LayoutRepr::Struct(env.arena.alloc(fields)); let return_layout = layout_interner.insert_direct_no_semantic(repr); // normal path, no panics - let else_branch_stmt = { - let result_symbol = Expr::Call(Call { + let if_zero_stmt = { + let it = main_proc.args.iter().map(|(a, _)| *a); + let arg_layouts = Vec::from_iter_in(it, env.arena).into_bump_slice(); + + let result_symbol = env.create_symbol(ident_ids, "result"); + let result_expr = Expr::Call(Call { call_type: CallType::ByName { - name: (), - ret_layout: (), - arg_layouts: (), - specialization_id: (), + name: main_proc.name, + ret_layout: main_proc.ret_layout, + arg_layouts, + specialization_id: CallSpecId::BACKEND_DUMMY, }, - arguments: proc_arguments, + arguments, }); + let result = |next| Stmt::Let(result_symbol, result_expr, main_proc.ret_layout, next); let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); @@ -922,19 +943,106 @@ fn test_helper_body<'a>( let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); + + // construct the record + let output_symbol = env.create_symbol(ident_ids, "output"); + let fields = [ok_tag_symbol, msg_ptr_symbol, result_symbol]; + let output_expr = Expr::Struct(env.arena.alloc(fields)); + let output = |next| Stmt::Let(output_symbol, output_expr, Layout::U64, next); + + let arena = env.arena; + result(arena.alloc( + // + ok_tag(arena.alloc( + // + msg_ptr(arena.alloc( + // + output(arena.alloc( + // + Stmt::Ret(output_symbol), + )), + )), + )), + )) }; // a longjmp/panic occured - let then_branch_stmt = { - // - todo!() + let if_nonzero_stmt = { + let alloca_symbol = env.create_symbol(ident_ids, "alloca"); + let alloca_expr = Expr::Alloca { + element_layout: main_proc.ret_layout, + initializer: None, + }; + let alloca = |next| Stmt::Let(alloca_symbol, alloca_expr, Layout::U64, next); + + let load_symbol = env.create_symbol(ident_ids, "load"); + let load_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::PtrLoad, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: &[alloca_symbol], + }); + let load = |next| Stmt::Let(load_symbol, load_expr, Layout::U64, next); + + // is_longjmp_symbol will the pointer to the error message + let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); + let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let ok_tag = |next| Stmt::Let(ok_tag_symbol, ok_tag_expr, Layout::U64, next); + + let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); + let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); + + // construct the record + let output_symbol = env.create_symbol(ident_ids, "output"); + let fields = [ok_tag_symbol, msg_ptr_symbol, load_symbol]; + let output_expr = Expr::Struct(env.arena.alloc(fields)); + let output = |next| Stmt::Let(output_symbol, output_expr, Layout::U64, next); + + let arena = env.arena; + arena.alloc(alloca(arena.alloc( + // + load(arena.alloc( + // + ok_tag(arena.alloc( + // + msg_ptr(arena.alloc( + // + output(arena.alloc( + // + Stmt::Ret(output_symbol), + )), + )), + )), + )), + ))) }; - Stmt::if_then_else( + switch_if_zero_else( env.arena, is_longjmp_symbol, return_layout, - then_branch_stmt, - else_branch_stmt, + if_zero_stmt, + if_nonzero_stmt, ) } + +fn switch_if_zero_else<'a>( + arena: &'a Bump, + condition_symbol: Symbol, + return_layout: InLayout<'a>, + then_branch_stmt: Stmt<'a>, + else_branch_stmt: &'a Stmt<'a>, +) -> Stmt<'a> { + let then_branch = (0u64, BranchInfo::None, then_branch_stmt); + let else_branch = (BranchInfo::None, else_branch_stmt); + + Stmt::Switch { + cond_symbol: condition_symbol, + cond_layout: Layout::U64, + branches: &*arena.alloc([then_branch]), + default_branch: else_branch, + ret_layout: return_layout, + } +} From 8c99cdf8179aa320ba8dc9411c0c0d29c2149faa Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 25 Jul 2023 14:43:37 +0200 Subject: [PATCH 04/25] working codegen --- .../compiler/gen_dev/src/generic64/storage.rs | 1 + .../compiler/gen_dev/src/generic64/x86_64.rs | 4 +- crates/compiler/gen_dev/src/object_builder.rs | 71 +++++++++---------- crates/compiler/mono/src/code_gen_help/mod.rs | 70 ++++++++++-------- crates/compiler/test_gen/src/helpers/dev.rs | 25 ++++++- 5 files changed, 103 insertions(+), 68 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 127e1c4d67..32152873d6 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -704,6 +704,7 @@ impl< } } else { // This is a single element struct. Just copy the single field to the stack. + dbg!(sym, layout); debug_assert_eq!(fields.len(), 1); self.copy_symbol_to_stack_offset( layout_interner, diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index e2ca290b1d..f550612a67 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -266,6 +266,7 @@ impl CallConv for X86_64Syste args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, ) { + dbg!(layout_interner.dbg(*ret_layout)); let returns_via_pointer = X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout); @@ -276,7 +277,7 @@ impl CallConv for X86_64Syste argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16, }; - if returns_via_pointer { + if dbg!(returns_via_pointer) { storage_manager.ret_pointer_arg(X86_64SystemV::GENERAL_PARAM_REGS[0]); } @@ -379,6 +380,7 @@ impl CallConv for X86_64Syste } _ => { // This is a large type returned via the arg pointer. + dbg!(sym); storage_manager.copy_symbol_to_arg_pointer(buf, sym, layout); // Also set the return reg to the arg pointer. storage_manager.load_to_specified_general_reg( diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 01d70f20a3..9ba72cd68e 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -13,7 +13,7 @@ use roc_module::symbol; use roc_module::symbol::Interns; use roc_mono::ir::{Call, CallSpecId, Expr, UpdateModeId}; use roc_mono::ir::{Proc, ProcLayout, Stmt}; -use roc_mono::layout::{LambdaName, Layout, LayoutIds, LayoutInterner, STLayoutInterner}; +use roc_mono::layout::{LambdaName, Layout, LayoutIds, LayoutInterner, Niche, STLayoutInterner}; use roc_target::TargetInfo; use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple}; @@ -30,8 +30,6 @@ pub fn build_module<'a, 'r>( target: &Triple, procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, ) -> Object<'a> { - dbg!(&target); - match target { Triple { architecture: TargetArch::X86_64, @@ -330,6 +328,24 @@ fn build_object<'a, B: Backend<'a>>( let exposed_proc = build_exposed_proc(&mut backend, &proc); let exposed_generic_proc = build_exposed_generic_proc(&mut backend, &proc); + // ModuleId, + // &mut STLayoutInterner<'a>, + // &mut Interns, + // &mut CodeGenHelp<'a>, + // &mut Vec<'a, CallerProc<'a>>, + + let (module_id, layout_interner, interns, code_gen_help, _) = + backend.module_interns_helpers_mut(); + + let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap(); + + let test_helper = roc_mono::code_gen_help::test_helper( + code_gen_help, + ident_ids, + layout_interner, + &proc, + ); + #[cfg(debug_assertions)] { let module_id = exposed_generic_proc.name.name().module_id(); @@ -341,6 +357,18 @@ fn build_object<'a, B: Backend<'a>>( module_id.register_debug_idents(ident_ids); } + println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); + + build_proc_symbol( + &mut output, + &mut layout_ids, + &mut procs, + &mut backend, + layout, + test_helper, + Exposed::TestMain, + ); + build_proc_symbol( &mut output, &mut layout_ids, @@ -615,44 +643,12 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<' } } -fn build_test_main<'a, B: Backend<'a>>( - output: &mut Object<'a>, - layout_ids: &mut LayoutIds<'a>, - procs: &mut Vec<'a, (String, SectionId, SymbolId, Proc<'a>)>, - backend: &mut B, - layout: ProcLayout<'a>, - proc: Proc<'a>, - exposed: Exposed, -) { - let sym = proc.name.name(); - - let section_id = output.add_section( - output.segment_name(StandardSegment::Text).to_vec(), - &b".text.test_main", - SectionKind::Text, - ); - - let fn_name = "test_main"; - - let proc_symbol = Symbol { - name: fn_name.as_bytes().to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Linkage, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }; - let proc_id = output.add_symbol(proc_symbol); - procs.push((fn_name, section_id, proc_id, proc)); -} - #[allow(clippy::enum_variant_names)] enum Exposed { ExposedGeneric, Exposed, NotExposed, + TestMain, } fn build_proc_symbol<'a, B: Backend<'a>>( @@ -685,6 +681,7 @@ fn build_proc_symbol<'a, B: Backend<'a>>( None, layout.result, ), + Exposed::TestMain => String::from("test_main"), }; let proc_symbol = Symbol { @@ -695,7 +692,7 @@ fn build_proc_symbol<'a, B: Backend<'a>>( // TODO: Depending on whether we are building a static or dynamic lib, this should change. // We should use Dynamic -> anyone, Linkage -> static link, Compilation -> this module only. scope: match exposed { - Exposed::ExposedGeneric | Exposed::Exposed => SymbolScope::Dynamic, + Exposed::ExposedGeneric | Exposed::Exposed | Exposed::TestMain => SymbolScope::Dynamic, Exposed::NotExposed => SymbolScope::Linkage, }, weak: false, diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 3b6cb103b5..4fc7b1f1aa 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -850,7 +850,7 @@ fn layout_needs_helper_proc<'a>( } } -fn test_helper<'a>( +pub fn test_helper<'a>( env: &CodeGenHelp<'a>, ident_ids: &mut IdentIds, layout_interner: &mut STLayoutInterner<'a>, @@ -865,7 +865,21 @@ fn test_helper<'a>( .map(|(s, (l, _))| (*l, *s)); let args = Vec::from_iter_in(it, env.arena).into_bump_slice(); - let body = test_helper_body(env, ident_ids, layout_interner, main_proc, arguments); + // tag: u64, + // error_msg: *mut RocStr, + // value: MaybeUninit, + let fields = [Layout::U64, Layout::U64, main_proc.ret_layout]; + let repr = LayoutRepr::Struct(env.arena.alloc(fields)); + let output_layout = layout_interner.insert_direct_no_semantic(repr); + + let body = test_helper_body( + env, + ident_ids, + layout_interner, + main_proc, + arguments, + output_layout, + ); let name = LambdaName::no_niche(env.create_symbol(ident_ids, "test_main")); @@ -874,7 +888,7 @@ fn test_helper<'a>( args, body, closure_data_layout: None, - ret_layout: main_proc.ret_layout, + ret_layout: output_layout, is_self_recursive: main_proc.is_self_recursive, host_exposed_layouts: HostExposedLayouts::HostExposed { rigids: Default::default(), @@ -890,6 +904,7 @@ fn test_helper_body<'a>( layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, arguments: &'a [Symbol], + output_layout: InLayout<'a>, ) -> Stmt<'a> { // let buffer = SetLongJmpBuffer let buffer_symbol = env.create_symbol(ident_ids, "buffer"); @@ -908,17 +923,10 @@ fn test_helper_body<'a>( op: LowLevel::SetJmp, update_mode: UpdateModeId::BACKEND_DUMMY, }, - arguments: &[buffer_symbol], + arguments: env.arena.alloc([buffer_symbol]), }); let is_longjmp_stmt = |next| Stmt::Let(is_longjmp_symbol, is_longjmp_expr, Layout::U64, next); - // tag: u64, - // error_msg: *mut RocStr, - // value: MaybeUninit, - let fields = [Layout::U64, Layout::U64, main_proc.ret_layout]; - let repr = LayoutRepr::Struct(env.arena.alloc(fields)); - let return_layout = layout_interner.insert_direct_no_semantic(repr); - // normal path, no panics let if_zero_stmt = { let it = main_proc.args.iter().map(|(a, _)| *a); @@ -945,10 +953,10 @@ fn test_helper_body<'a>( let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); // construct the record - let output_symbol = env.create_symbol(ident_ids, "output"); + let output_symbol = env.create_symbol(ident_ids, "output_ok"); let fields = [ok_tag_symbol, msg_ptr_symbol, result_symbol]; let output_expr = Expr::Struct(env.arena.alloc(fields)); - let output = |next| Stmt::Let(output_symbol, output_expr, Layout::U64, next); + let output = |next| Stmt::Let(output_symbol, output_expr, output_layout, next); let arena = env.arena; result(arena.alloc( @@ -981,31 +989,31 @@ fn test_helper_body<'a>( op: LowLevel::PtrLoad, update_mode: UpdateModeId::BACKEND_DUMMY, }, - arguments: &[alloca_symbol], + arguments: env.arena.alloc([alloca_symbol]), }); let load = |next| Stmt::Let(load_symbol, load_expr, Layout::U64, next); // is_longjmp_symbol will the pointer to the error message - let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); - let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); - let ok_tag = |next| Stmt::Let(ok_tag_symbol, ok_tag_expr, Layout::U64, next); + let err_tag_symbol = env.create_symbol(ident_ids, "err_tag"); + let err_tag_expr = Expr::Literal(Literal::Int((1 as i128).to_be_bytes())); + let err_tag = |next| Stmt::Let(err_tag_symbol, err_tag_expr, Layout::U64, next); let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); // construct the record - let output_symbol = env.create_symbol(ident_ids, "output"); - let fields = [ok_tag_symbol, msg_ptr_symbol, load_symbol]; + let output_symbol = env.create_symbol(ident_ids, "output_err"); + let fields = [err_tag_symbol, msg_ptr_symbol, load_symbol]; let output_expr = Expr::Struct(env.arena.alloc(fields)); - let output = |next| Stmt::Let(output_symbol, output_expr, Layout::U64, next); + let output = |next| Stmt::Let(output_symbol, output_expr, output_layout, next); let arena = env.arena; arena.alloc(alloca(arena.alloc( // load(arena.alloc( // - ok_tag(arena.alloc( + err_tag(arena.alloc( // msg_ptr(arena.alloc( // @@ -1019,13 +1027,19 @@ fn test_helper_body<'a>( ))) }; - switch_if_zero_else( - env.arena, - is_longjmp_symbol, - return_layout, - if_zero_stmt, - if_nonzero_stmt, - ) + buffer_stmt(env.arena.alloc( + // + is_longjmp_stmt(env.arena.alloc( + // + switch_if_zero_else( + env.arena, + is_longjmp_symbol, + output_layout, + if_zero_stmt, + if_nonzero_stmt, + ), + )), + )) } fn switch_if_zero_else<'a>( diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 3cbb4324e2..6432f42ae8 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -267,7 +267,6 @@ macro_rules! assert_evals_to { }; ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $lazy_literals:expr) => { use bumpalo::Bump; - use roc_gen_dev::run_jit_function_raw; let arena = Bump::new(); let (main_fn_name, errors, lib) = @@ -279,7 +278,29 @@ macro_rules! assert_evals_to { let given = $transform(success); assert_eq!(&given, &expected); }; - run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors) + + unsafe { + let main: libloading::Symbol $ty> = lib + .get(main_fn_name.as_bytes()) + .ok() + .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) + .expect("errored"); + + let result = main(); + + if !errors.is_empty() { + dbg!(&errors); + + assert_eq!( + errors, + std::vec::Vec::new(), + "Encountered errors: {:?}", + errors + ); + } + + transform(result) + } }; } From d3ac7d616d20e780754dd5ab3f2e0b27ed77ca6a Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 25 Jul 2023 18:42:14 +0200 Subject: [PATCH 05/25] WIP --- .../compiler/gen_dev/src/generic64/x86_64.rs | 2 +- crates/compiler/gen_dev/src/object_builder.rs | 9 +- crates/compiler/test_gen/src/helpers/dev.rs | 91 +++++++++++++++++-- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index f550612a67..14ef08016a 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -466,7 +466,7 @@ impl CallConv for X86_64Syste offset += 8; // store the base pointer - ASM::mov_mem64_offset32_reg64(buf, RBP, offset, RBP); + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RBP); offset += 8; // store other non-volatile registers diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 9ba72cd68e..81bfd1f24e 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -122,7 +122,7 @@ fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( backend: &mut B, output: &mut Object, ) -> SymbolId { - let bss_section = output.section_id(StandardSection::UninitializedData); + let bss_section = output.section_id(StandardSection::Data); dbg!(bss_section); @@ -131,13 +131,16 @@ fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( value: 0, size: 8 * core::mem::size_of::() as u64, kind: SymbolKind::Data, - scope: SymbolScope::Linkage, + scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Section(bss_section), flags: SymbolFlags::None, }; - dbg!(output.add_symbol(symbol)) + let symbol_id = output.add_symbol(symbol); + let section_offset = output.add_symbol_data(symbol_id, bss_section, &[0; 64], 8); + + symbol_id } fn generate_roc_setlongjmp_buffer<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 6432f42ae8..cc941f3385 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -2,10 +2,13 @@ use libloading::Library; use roc_build::link::{link, LinkType}; use roc_builtins::bitcode; use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading}; +use roc_mono::ir::CrashTag; use roc_mono::ir::SingleEntryPoint; use roc_packaging::cache::RocCacheDir; use roc_region::all::LineInfo; use roc_solve::FunctionKind; +use roc_std::RocStr; +use std::mem::MaybeUninit; use tempfile::tempdir; #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] @@ -245,6 +248,56 @@ pub fn helper( (main_fn_name, delayed_errors, lib) } +#[repr(C)] +pub struct RocCallResult { + pub tag: u64, + pub error_msg: *mut RocStr, + pub value: MaybeUninit, +} + +impl RocCallResult { + pub fn new(value: T) -> Self { + Self { + tag: 0, + error_msg: std::ptr::null_mut(), + value: MaybeUninit::new(value), + } + } +} + +impl Default for RocCallResult { + fn default() -> Self { + Self { + tag: 0, + error_msg: std::ptr::null_mut(), + value: MaybeUninit::new(Default::default()), + } + } +} + +impl RocCallResult { + pub fn into_result(self) -> Result { + match self.tag { + 0 => Ok(unsafe { self.value.assume_init() }), + n => Err({ + let msg: &RocStr = unsafe { &*self.error_msg }; + let tag = (n - 1) as u32; + let tag = tag + .try_into() + .unwrap_or_else(|_| panic!("received illegal tag: {tag}")); + + (msg.as_str().to_owned(), tag) + }), + } + } +} + +impl From> for Result { + fn from(call_result: RocCallResult) -> Self { + call_result.into_result() + } +} + #[allow(unused_macros)] macro_rules! assert_evals_to { ($src:expr, $expected:expr, $ty:ty) => {{ @@ -267,27 +320,41 @@ macro_rules! assert_evals_to { }; ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $lazy_literals:expr) => { use bumpalo::Bump; + use $crate::helpers::dev::RocCallResult; let arena = Bump::new(); - let (main_fn_name, errors, lib) = + let (_main_fn_name, errors, lib) = $crate::helpers::dev::helper(&arena, $src, $leak, $lazy_literals); - let transform = |success| { - let expected = $expected; - #[allow(clippy::redundant_closure_call)] - let given = $transform(success); - assert_eq!(&given, &expected); - }; + // let transform = |success| { + // let expected = $expected; + // #[allow(clippy::redundant_closure_call)] + // let given = $transform(success); + // assert_eq!(&given, &expected); + // }; + + let main_fn_name = "test_main"; + + // type Main = unsafe extern "C" fn(*mut RocCallResult<$ty>); + type Main = unsafe extern "C" fn(&mut [u8]); unsafe { - let main: libloading::Symbol $ty> = lib + let main: libloading::Symbol
= lib .get(main_fn_name.as_bytes()) .ok() .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) .expect("errored"); - let result = main(); + // let mut result = std::mem::MaybeUninit::uninit(); + // main(result.as_mut_ptr()); + // let result = result.assume_init(); + let mut memory = [0u8; 64]; + let result = main(&mut memory); + dbg!(memory); + panic!(); + + /* if !errors.is_empty() { dbg!(&errors); @@ -299,7 +366,11 @@ macro_rules! assert_evals_to { ); } - transform(result) + match result.into_result() { + Ok(value) => transform(value), + Err((msg, _tag)) => panic!("roc_panic: {msg}"), + } + */ } }; } From 644def72f106e9838d677de91cf18a4111ce79e9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jul 2023 17:37:06 +0200 Subject: [PATCH 06/25] working happy path --- .../compiler/gen_dev/src/generic64/aarch64.rs | 4 ++ crates/compiler/gen_dev/src/generic64/mod.rs | 17 ++++- .../compiler/gen_dev/src/generic64/x86_64.rs | 19 +++++- crates/compiler/gen_dev/src/lib.rs | 4 +- crates/compiler/gen_dev/src/object_builder.rs | 67 +++++++++---------- .../compiler/test_gen/src/gen_primitives.rs | 20 +++++- crates/compiler/test_gen/src/helpers/dev.rs | 31 ++++----- 7 files changed, 103 insertions(+), 59 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index 79ad1276ab..827866a0b7 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -463,6 +463,10 @@ impl CallConv for AArch64C fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { todo!() } + + fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } } impl Assembler for AArch64Assembler { diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 9e3aac1c00..8801227dc2 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -137,6 +137,7 @@ pub trait CallConv, relocs: &mut Vec<'_, Relocation>); fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); + fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); } pub enum CompareOperation { @@ -913,6 +914,16 @@ impl< out.into_bump_slice() } + fn build_roc_panic(&mut self) -> &'a [u8] { + let mut out = bumpalo::vec![in self.env.arena]; + + CC::roc_panic(&mut out, &mut self.relocs); + + dbg!(&self.relocs); + + out.into_bump_slice() + } + fn build_fn_pointer(&mut self, dst: &Symbol, fn_name: String) { let reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); @@ -922,7 +933,11 @@ impl< fn build_data_pointer(&mut self, dst: &Symbol, data_name: String) { let reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); - ASM::data_pointer(&mut self.buf, &mut self.relocs, data_name, reg) + // now, this gives a pointer to the value + ASM::data_pointer(&mut self.buf, &mut self.relocs, data_name, reg); + + // dereference + ASM::mov_reg64_mem64_offset32(&mut self.buf, reg, reg, 0); } fn build_fn_call( diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 14ef08016a..e88ac0fc57 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -266,7 +266,6 @@ impl CallConv for X86_64Syste args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, ) { - dbg!(layout_interner.dbg(*ret_layout)); let returns_via_pointer = X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout); @@ -277,7 +276,7 @@ impl CallConv for X86_64Syste argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16, }; - if dbg!(returns_via_pointer) { + if returns_via_pointer { storage_manager.ret_pointer_arg(X86_64SystemV::GENERAL_PARAM_REGS[0]); } @@ -380,7 +379,6 @@ impl CallConv for X86_64Syste } _ => { // This is a large type returned via the arg pointer. - dbg!(sym); storage_manager.copy_symbol_to_arg_pointer(buf, sym, layout); // Also set the return reg to the arg pointer. storage_manager.load_to_specified_general_reg( @@ -522,6 +520,17 @@ impl CallConv for X86_64Syste jmp_reg64_offset8(buf, RDI, offset as i8) } + + fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + use X86_64GeneralReg::*; + type ASM = X86_64Assembler; + + ASM::data_pointer(buf, relocs, String::from("setlongjmp_buffer"), RDI); + ASM::mov_reg64_imm64(buf, RSI, 42); + + // Call function and generate reloc. + ASM::call(buf, relocs, String::from("roc_longjmp")); + } } struct X64_64SystemVStoreArgs { @@ -1117,6 +1126,10 @@ impl CallConv for X86_64Windo fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { todo!() } + + fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + todo!() + } } impl X86_64WindowsFastcall { diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 1eb1176498..37eee79b23 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -466,6 +466,7 @@ trait Backend<'a> { // use for roc_panic fn build_roc_setjmp(&mut self) -> &'a [u8]; fn build_roc_longjmp(&mut self) -> &'a [u8]; + fn build_roc_panic(&mut self) -> &'a [u8]; /// build_proc creates a procedure and outputs it to the wrapped object writer. /// Returns the procedure bytes, its relocations, and the names of the refcounting functions it references. @@ -1680,7 +1681,8 @@ trait Backend<'a> { ret_layout, ), LowLevel::SetLongJmpBuffer => { - self.build_data_pointer(sym, String::from("setlongjmp_buffer")) + let ptr_to_the_thing = + self.build_data_pointer(sym, String::from("setlongjmp_buffer")); } LowLevel::DictPseudoSeed => self.build_fn_call( sym, diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 81bfd1f24e..07ca6f8dae 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -118,14 +118,9 @@ pub fn build_module<'a, 'r>( } } -fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( - backend: &mut B, - output: &mut Object, -) -> SymbolId { +fn define_setlongjmp_buffer(output: &mut Object) -> SymbolId { let bss_section = output.section_id(StandardSection::Data); - dbg!(bss_section); - let symbol = Symbol { name: b"setlongjmp_buffer".to_vec(), value: 0, @@ -138,29 +133,11 @@ fn generate_setlongjmp_buffer<'a, B: Backend<'a>>( }; let symbol_id = output.add_symbol(symbol); - let section_offset = output.add_symbol_data(symbol_id, bss_section, &[0; 64], 8); + output.add_symbol_data(symbol_id, bss_section, &[0xAA; 64], 8); symbol_id } -fn generate_roc_setlongjmp_buffer<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { - let text_section = output.section_id(StandardSection::Text); - let proc_symbol = Symbol { - name: b"roc_setjmp".to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Section(text_section), - flags: SymbolFlags::None, - }; - - let proc_id = output.add_symbol(proc_symbol); - let (proc_data, offset) = backend.build_wrapped_jmp(); - let proc_offset = output.add_symbol_data(proc_id, text_section, proc_data, 16); -} - fn generate_setjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { let text_section = output.section_id(StandardSection::Text); let proc_symbol = Symbol { @@ -197,6 +174,25 @@ fn generate_longjmp<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { output.add_symbol_data(proc_id, text_section, proc_data, 16); } +// a roc_panic to be used in tests; relies on setjmp/longjmp +fn generate_roc_panic<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) { + let text_section = output.section_id(StandardSection::Text); + let proc_symbol = Symbol { + name: b"roc_panic".to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(text_section), + flags: SymbolFlags::None, + }; + let proc_id = output.add_symbol(proc_symbol); + let proc_data = backend.build_roc_panic(); + + output.add_symbol_data(proc_id, text_section, proc_data, 16); +} + fn generate_wrapper<'a, B: Backend<'a>>( backend: &mut B, output: &mut Object, @@ -257,8 +253,6 @@ fn build_object<'a, B: Backend<'a>>( ) -> Object<'a> { let data_section = output.section_id(StandardSection::Data); - dbg!("build object"); - let arena = backend.env().arena; /* @@ -271,8 +265,9 @@ fn build_object<'a, B: Backend<'a>>( ); */ - generate_setlongjmp_buffer(&mut backend, &mut output); + define_setlongjmp_buffer(&mut output); + generate_roc_panic(&mut backend, &mut output); generate_setjmp(&mut backend, &mut output); generate_longjmp(&mut backend, &mut output); @@ -295,12 +290,14 @@ fn build_object<'a, B: Backend<'a>>( "roc_dealloc".into(), "free".into(), ); - generate_wrapper( - &mut backend, - &mut output, - "roc_panic".into(), - "roc_builtins.utils.test_panic".into(), - ); + + // generate_wrapper( + // &mut backend, + // &mut output, + // "roc_panic".into(), + // "roc_builtins.utils.test_panic".into(), + // ); + // Extra symbols only required on unix systems. if matches!(output.format(), BinaryFormat::Elf | BinaryFormat::MachO) { generate_wrapper( @@ -360,7 +357,7 @@ fn build_object<'a, B: Backend<'a>>( module_id.register_debug_idents(ident_ids); } - println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); + // println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); build_proc_symbol( &mut output, diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 7c51ad5c8f..6c901ed8b5 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -975,7 +975,7 @@ fn overflow_frees_list() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[should_panic(expected = "Roc failed with message: ")] fn undefined_variable() { assert_evals_to!( @@ -992,6 +992,24 @@ fn undefined_variable() { ); } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +#[should_panic(expected = "Roc failed with message: ")] +fn a_crash() { + assert_evals_to!( + indoc!( + r#" + if Bool.true then + crash "a crash" + else + 0u64 + "# + ), + 3, + i64 + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[should_panic(expected = "Roc failed with message: ")] diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index cc941f3385..8610eafd31 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -248,6 +248,7 @@ pub fn helper( (main_fn_name, delayed_errors, lib) } +#[derive(Debug)] #[repr(C)] pub struct RocCallResult { pub tag: u64, @@ -326,17 +327,16 @@ macro_rules! assert_evals_to { let (_main_fn_name, errors, lib) = $crate::helpers::dev::helper(&arena, $src, $leak, $lazy_literals); - // let transform = |success| { - // let expected = $expected; - // #[allow(clippy::redundant_closure_call)] - // let given = $transform(success); - // assert_eq!(&given, &expected); - // }; + let transform = |success| { + let expected = $expected; + #[allow(clippy::redundant_closure_call)] + let given = $transform(success); + assert_eq!(&given, &expected, "output is different"); + }; let main_fn_name = "test_main"; - // type Main = unsafe extern "C" fn(*mut RocCallResult<$ty>); - type Main = unsafe extern "C" fn(&mut [u8]); + type Main = unsafe extern "C" fn(*mut RocCallResult<$ty>); unsafe { let main: libloading::Symbol
= lib @@ -345,16 +345,10 @@ macro_rules! assert_evals_to { .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) .expect("errored"); - // let mut result = std::mem::MaybeUninit::uninit(); - // main(result.as_mut_ptr()); - // let result = result.assume_init(); - let mut memory = [0u8; 64]; - let result = main(&mut memory); + let mut result = std::mem::MaybeUninit::uninit(); + main(result.as_mut_ptr()); + let result = result.assume_init(); - dbg!(memory); - panic!(); - - /* if !errors.is_empty() { dbg!(&errors); @@ -366,11 +360,12 @@ macro_rules! assert_evals_to { ); } + dbg!(&result); + match result.into_result() { Ok(value) => transform(value), Err((msg, _tag)) => panic!("roc_panic: {msg}"), } - */ } }; } From f59eed6366cd8c80d54fc981cfb1a7e4d9728e62 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jul 2023 22:53:15 +0200 Subject: [PATCH 07/25] so close --- crates/compiler/gen_dev/src/generic64/mod.rs | 33 +++++++++++++++-- .../compiler/gen_dev/src/generic64/storage.rs | 9 ++++- .../compiler/gen_dev/src/generic64/x86_64.rs | 27 +++++++++++--- crates/compiler/gen_dev/src/lib.rs | 3 +- crates/compiler/gen_dev/src/object_builder.rs | 37 ++++++++++++++++--- crates/compiler/mono/src/code_gen_help/mod.rs | 25 +++++++------ .../compiler/test_gen/src/gen_primitives.rs | 2 +- crates/compiler/test_gen/src/helpers/dev.rs | 4 +- 8 files changed, 107 insertions(+), 33 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 8801227dc2..8dba9edb41 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -725,6 +725,9 @@ impl< fn interner(&self) -> &STLayoutInterner<'a> { self.layout_interner } + fn relocations_mut(&mut self) -> &mut Vec<'a, Relocation> { + &mut self.relocs + } fn module_interns_helpers_mut( &mut self, ) -> ( @@ -914,14 +917,16 @@ impl< out.into_bump_slice() } - fn build_roc_panic(&mut self) -> &'a [u8] { + fn build_roc_panic(&mut self) -> (&'a [u8], Vec<'a, Relocation>) { let mut out = bumpalo::vec![in self.env.arena]; + let before = self.relocs.len(); + CC::roc_panic(&mut out, &mut self.relocs); - dbg!(&self.relocs); + let relocs = self.relocs.split_off(before); - out.into_bump_slice() + (out.into_bump_slice(), relocs) } fn build_fn_pointer(&mut self, dst: &Symbol, fn_name: String) { @@ -4262,7 +4267,18 @@ impl< Builtin::Int(int_width) => match int_width { IntWidth::I128 | IntWidth::U128 => { // can we treat this as 2 u64's? - todo!() + storage_manager.with_tmp_general_reg( + buf, + |storage_manager, buf, tmp_reg| { + let base_offset = storage_manager.claim_stack_area(&dst, 16); + + ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset); + ASM::mov_base32_reg64(buf, base_offset, tmp_reg); + + ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset + 8); + ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg); + }, + ); } IntWidth::I64 | IntWidth::U64 => { let dst_reg = storage_manager.claim_general_reg(buf, &dst); @@ -4300,6 +4316,15 @@ impl< } Builtin::Decimal => { // same as 128-bit integer + storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| { + let base_offset = storage_manager.claim_stack_area(&dst, 16); + + ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset); + ASM::mov_base32_reg64(buf, base_offset, tmp_reg); + + ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset + 8); + ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg); + }); } Builtin::Str | Builtin::List(_) => { storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| { diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 32152873d6..62307bab66 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -808,8 +808,15 @@ impl< } } } - Builtin::Decimal => todo!(), + Builtin::Decimal => { + let (from_offset, size) = self.stack_offset_and_size(sym); + debug_assert_eq!(from_offset % 8, 0); + debug_assert_eq!(size % 8, 0); + debug_assert_eq!(size, layout_interner.stack_size(*layout)); + self.copy_to_stack_offset(buf, size, from_offset, to_offset) + } Builtin::Str | Builtin::List(_) => { + dbg!(sym); let (from_offset, size) = self.stack_offset_and_size(sym); debug_assert_eq!(size, layout_interner.stack_size(*layout)); self.copy_to_stack_offset(buf, size, from_offset, to_offset) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index e88ac0fc57..2aacdde351 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -482,7 +482,8 @@ impl CallConv for X86_64Syste offset += 8; // store the current stack pointer - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RSP); + ASM::mov_reg64_mem64_offset32(buf, RDX, RSP, 0); + ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RDX); // zero out eax, so we return 0i32 (we do a 64-bit xor for convenience) ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); @@ -507,7 +508,7 @@ impl CallConv for X86_64Syste // 20237a: ff 67 38 jmp QWORD PTR [rdi+0x38] // make sure something nonzero is returned ?! - ASM::mov_reg64_imm64(buf, RAX, 0x1); + ASM::mov_reg64_reg64(buf, RAX, RSI); // move the values back into the registers let mut offset = 0; @@ -525,11 +526,25 @@ impl CallConv for X86_64Syste use X86_64GeneralReg::*; type ASM = X86_64Assembler; - ASM::data_pointer(buf, relocs, String::from("setlongjmp_buffer"), RDI); - ASM::mov_reg64_imm64(buf, RSI, 42); + // move the first argument to roc_panic (a *RocStr) into r8 + ASM::mov_reg64_imm64(buf, R8, 8); + ASM::add_reg64_reg64_reg64(buf, R8, R8, RSP); - // Call function and generate reloc. - ASM::call(buf, relocs, String::from("roc_longjmp")); + // the setlongjmp_buffer + ASM::data_pointer(buf, relocs, String::from("setlongjmp_buffer"), RDI); + ASM::mov_reg64_mem64_offset32(buf, RDI, RDI, 0); + + // the value to return from the longjmp. It is a pointer to the last 3 words of the setlongjmp_buffer + // they represent the errore message. + ASM::mov_reg64_imm64(buf, RSI, 0x40); + ASM::add_reg64_reg64_reg64(buf, RSI, RSI, RDI); + + for offset in [0, 8, 16] { + ASM::mov_reg64_mem64_offset32(buf, R9, R8, offset); + ASM::mov_mem64_offset32_reg64(buf, RSI, offset, R9); + } + + Self::longjmp(buf, relocs) } } diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 37eee79b23..61de4f6f6c 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -291,6 +291,7 @@ trait Backend<'a> { fn interns(&self) -> &Interns; fn interns_mut(&mut self) -> &mut Interns; fn interner(&self) -> &STLayoutInterner<'a>; + fn relocations_mut(&mut self) -> &mut Vec<'a, Relocation>; fn interner_mut(&mut self) -> &mut STLayoutInterner<'a> { self.module_interns_helpers_mut().1 @@ -466,7 +467,7 @@ trait Backend<'a> { // use for roc_panic fn build_roc_setjmp(&mut self) -> &'a [u8]; fn build_roc_longjmp(&mut self) -> &'a [u8]; - fn build_roc_panic(&mut self) -> &'a [u8]; + fn build_roc_panic(&mut self) -> (&'a [u8], Vec<'a, Relocation>); /// build_proc creates a procedure and outputs it to the wrapped object writer. /// Returns the procedure bytes, its relocations, and the names of the refcounting functions it references. diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 07ca6f8dae..f50784100b 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -121,10 +121,13 @@ pub fn build_module<'a, 'r>( fn define_setlongjmp_buffer(output: &mut Object) -> SymbolId { let bss_section = output.section_id(StandardSection::Data); + // 8 registers + 3 words for a RocStr + const SIZE: usize = (8 + 3) * core::mem::size_of::(); + let symbol = Symbol { name: b"setlongjmp_buffer".to_vec(), value: 0, - size: 8 * core::mem::size_of::() as u64, + size: SIZE as u64, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, @@ -133,7 +136,7 @@ fn define_setlongjmp_buffer(output: &mut Object) -> SymbolId { }; let symbol_id = output.add_symbol(symbol); - output.add_symbol_data(symbol_id, bss_section, &[0xAA; 64], 8); + output.add_symbol_data(symbol_id, bss_section, &[0x00; SIZE], 8); symbol_id } @@ -188,9 +191,33 @@ fn generate_roc_panic<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) flags: SymbolFlags::None, }; let proc_id = output.add_symbol(proc_symbol); - let proc_data = backend.build_roc_panic(); + let (proc_data, relocs) = backend.build_roc_panic(); - output.add_symbol_data(proc_id, text_section, proc_data, 16); + let proc_offset = output.add_symbol_data(proc_id, text_section, proc_data, 16); + + for r in relocs { + let relocation = match r { + Relocation::LocalData { offset, .. } => unreachable!(), + Relocation::LinkedFunction { offset, .. } => unreachable!(), + Relocation::LinkedData { offset, name } => { + if let Some(sym_id) = output.symbol_id(name.as_bytes()) { + write::Relocation { + offset: offset + proc_offset, + size: 32, + kind: RelocationKind::GotRelative, + encoding: RelocationEncoding::Generic, + symbol: sym_id, + addend: -4, + } + } else { + internal_error!("failed to find data symbol for {:?}", name); + } + } + Relocation::JmpToReturn { offset, .. } => unreachable!(), + }; + + output.add_relocation(text_section, relocation).unwrap(); + } } fn generate_wrapper<'a, B: Backend<'a>>( @@ -357,7 +384,7 @@ fn build_object<'a, B: Backend<'a>>( module_id.register_debug_idents(ident_ids); } - // println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); + println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); build_proc_symbol( &mut output, diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 4fc7b1f1aa..943eb8a1a7 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -945,11 +945,11 @@ fn test_helper_body<'a>( let result = |next| Stmt::Let(result_symbol, result_expr, main_proc.ret_layout, next); let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); - let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); let ok_tag = |next| Stmt::Let(ok_tag_symbol, ok_tag_expr, Layout::U64, next); let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); - let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); + let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); // construct the record @@ -991,16 +991,17 @@ fn test_helper_body<'a>( }, arguments: env.arena.alloc([alloca_symbol]), }); - let load = |next| Stmt::Let(load_symbol, load_expr, Layout::U64, next); + let load = |next| Stmt::Let(load_symbol, load_expr, main_proc.ret_layout, next); // is_longjmp_symbol will the pointer to the error message let err_tag_symbol = env.create_symbol(ident_ids, "err_tag"); - let err_tag_expr = Expr::Literal(Literal::Int((1 as i128).to_be_bytes())); + let err_tag_expr = Expr::Literal(Literal::Int((1 as i128).to_ne_bytes())); let err_tag = |next| Stmt::Let(err_tag_symbol, err_tag_expr, Layout::U64, next); - let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); - let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_be_bytes())); - let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); + // let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); + // let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); + // let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); + let msg_ptr_symbol = is_longjmp_symbol; // construct the record let output_symbol = env.create_symbol(ident_ids, "output_err"); @@ -1015,13 +1016,13 @@ fn test_helper_body<'a>( // err_tag(arena.alloc( // - msg_ptr(arena.alloc( + // msg_ptr(arena.alloc( + // + output(arena.alloc( // - output(arena.alloc( - // - Stmt::Ret(output_symbol), - )), + Stmt::Ret(output_symbol), )), + // )), )), )), ))) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 6c901ed8b5..d2737b91b6 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -994,7 +994,7 @@ fn undefined_variable() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -#[should_panic(expected = "Roc failed with message: ")] +#[should_panic(expected = "Roc failed with message: a crash")] fn a_crash() { assert_evals_to!( indoc!( diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 8610eafd31..6357262c2a 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -360,11 +360,9 @@ macro_rules! assert_evals_to { ); } - dbg!(&result); - match result.into_result() { Ok(value) => transform(value), - Err((msg, _tag)) => panic!("roc_panic: {msg}"), + Err((msg, _tag)) => panic!("Roc failed with message: {msg}"), } } }; From bb97b9ff745aeec0ab586b1f4cdf86a5b9533acc Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jul 2023 23:15:47 +0200 Subject: [PATCH 08/25] cleanup --- .../compiler/gen_dev/src/generic64/aarch64.rs | 6 ++-- crates/compiler/gen_dev/src/generic64/mod.rs | 8 ++--- .../compiler/gen_dev/src/generic64/storage.rs | 2 -- .../compiler/gen_dev/src/generic64/x86_64.rs | 24 ++++--------- crates/compiler/gen_dev/src/lib.rs | 3 +- crates/compiler/gen_dev/src/object_builder.rs | 12 ++++--- crates/compiler/mono/src/code_gen_help/mod.rs | 34 +++++-------------- 7 files changed, 30 insertions(+), 59 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index 827866a0b7..f16044aaf5 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -456,15 +456,15 @@ impl CallConv for AArch64C todo!("Loading returned complex symbols for AArch64"); } - fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn setjmp(_buf: &mut Vec<'_, u8>) { todo!() } - fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn longjmp(_buf: &mut Vec<'_, u8>) { todo!() } - fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn roc_panic(_buf: &mut Vec<'_, u8>, _relocs: &mut Vec<'_, Relocation>) { todo!() } } diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 8dba9edb41..65538bee94 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -135,8 +135,8 @@ pub trait CallConv, ); - fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); - fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); + fn setjmp(buf: &mut Vec<'_, u8>); + fn longjmp(buf: &mut Vec<'_, u8>); fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>); } @@ -904,7 +904,7 @@ impl< fn build_roc_setjmp(&mut self) -> &'a [u8] { let mut out = bumpalo::vec![in self.env.arena]; - CC::setjmp(&mut out, &mut self.relocs); + CC::setjmp(&mut out); out.into_bump_slice() } @@ -912,7 +912,7 @@ impl< fn build_roc_longjmp(&mut self) -> &'a [u8] { let mut out = bumpalo::vec![in self.env.arena]; - CC::longjmp(&mut out, &mut self.relocs); + CC::longjmp(&mut out); out.into_bump_slice() } diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 62307bab66..d60338e4c2 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -704,7 +704,6 @@ impl< } } else { // This is a single element struct. Just copy the single field to the stack. - dbg!(sym, layout); debug_assert_eq!(fields.len(), 1); self.copy_symbol_to_stack_offset( layout_interner, @@ -816,7 +815,6 @@ impl< self.copy_to_stack_offset(buf, size, from_offset, to_offset) } Builtin::Str | Builtin::List(_) => { - dbg!(sym); let (from_offset, size) = self.stack_offset_and_size(sym); debug_assert_eq!(size, layout_interner.stack_size(*layout)); self.copy_to_stack_offset(buf, size, from_offset, to_offset) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 2aacdde351..da592e1baf 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -437,7 +437,7 @@ impl CallConv for X86_64Syste } } - fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn setjmp(buf: &mut Vec<'_, u8>) { use X86_64GeneralReg::*; type ASM = X86_64Assembler; @@ -491,7 +491,7 @@ impl CallConv for X86_64Syste ASM::ret(buf) } - fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn longjmp(buf: &mut Vec<'_, u8>) { use X86_64GeneralReg::*; type ASM = X86_64Assembler; @@ -544,7 +544,7 @@ impl CallConv for X86_64Syste ASM::mov_mem64_offset32_reg64(buf, RSI, offset, R9); } - Self::longjmp(buf, relocs) + Self::longjmp(buf) } } @@ -1134,15 +1134,15 @@ impl CallConv for X86_64Windo todo!("Loading returned complex symbols for X86_64"); } - fn setjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn setjmp(_buf: &mut Vec<'_, u8>) { todo!() } - fn longjmp(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn longjmp(_buf: &mut Vec<'_, u8>) { todo!() } - fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + fn roc_panic(_buf: &mut Vec<'_, u8>, _relocs: &mut Vec<'_, Relocation>) { todo!() } } @@ -2752,7 +2752,7 @@ fn jmp_reg64_offset8(buf: &mut Vec<'_, u8>, base: X86_64GeneralReg, offset: i8) let rex = add_rm_extension(base, REX_W); #[allow(clippy::unusual_byte_groupings)] - buf.extend([rex, 0xff, 0b01_100_000 | base as u8 % 8]); + buf.extend([rex, 0xff, 0b01_100_000 | (base as u8 % 8)]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { @@ -3077,16 +3077,6 @@ fn mov_reg_base_offset32( buf.extend(offset.to_le_bytes()); } -#[inline(always)] -fn mov_reg64_offset32_reg64( - buf: &mut Vec<'_, u8>, - dst: X86_64GeneralReg, - offset: i32, - src: X86_64GeneralReg, -) { - todo!() -} - /// `MOV r64,r/m64` -> Move r/m64 to r64, where m64 references a base + offset. #[inline(always)] fn mov_reg64_base64_offset32( diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 61de4f6f6c..87a341d230 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -1682,8 +1682,7 @@ trait Backend<'a> { ret_layout, ), LowLevel::SetLongJmpBuffer => { - let ptr_to_the_thing = - self.build_data_pointer(sym, String::from("setlongjmp_buffer")); + self.build_data_pointer(sym, String::from("setlongjmp_buffer")); } LowLevel::DictPseudoSeed => self.build_fn_call( sym, diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index f50784100b..4469efbc78 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -13,7 +13,7 @@ use roc_module::symbol; use roc_module::symbol::Interns; use roc_mono::ir::{Call, CallSpecId, Expr, UpdateModeId}; use roc_mono::ir::{Proc, ProcLayout, Stmt}; -use roc_mono::layout::{LambdaName, Layout, LayoutIds, LayoutInterner, Niche, STLayoutInterner}; +use roc_mono::layout::{LambdaName, Layout, LayoutIds, LayoutInterner, STLayoutInterner}; use roc_target::TargetInfo; use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple}; @@ -197,8 +197,6 @@ fn generate_roc_panic<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) for r in relocs { let relocation = match r { - Relocation::LocalData { offset, .. } => unreachable!(), - Relocation::LinkedFunction { offset, .. } => unreachable!(), Relocation::LinkedData { offset, name } => { if let Some(sym_id) = output.symbol_id(name.as_bytes()) { write::Relocation { @@ -213,7 +211,11 @@ fn generate_roc_panic<'a, B: Backend<'a>>(backend: &mut B, output: &mut Object) internal_error!("failed to find data symbol for {:?}", name); } } - Relocation::JmpToReturn { offset, .. } => unreachable!(), + Relocation::LocalData { .. } + | Relocation::LinkedFunction { .. } + | Relocation::JmpToReturn { .. } => { + unreachable!("not currently created by build_roc_panic") + } }; output.add_relocation(text_section, relocation).unwrap(); @@ -384,7 +386,7 @@ fn build_object<'a, B: Backend<'a>>( module_id.register_debug_idents(ident_ids); } - println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); + // println!("{}", test_helper.to_pretty(backend.interner(), 200, true)); build_proc_symbol( &mut output, diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 943eb8a1a7..b757e15984 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -1,7 +1,6 @@ use bumpalo::collections::vec::Vec; use bumpalo::collections::CollectIn; use bumpalo::Bump; -use roc_can::abilities::SpecializationId; use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_target::TargetInfo; @@ -856,6 +855,8 @@ pub fn test_helper<'a>( layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, ) -> Proc<'a> { + let name = LambdaName::no_niche(env.create_symbol(ident_ids, "test_main")); + let it = (0..main_proc.args.len()).map(|i| env.create_symbol(ident_ids, &format!("arg_{i}"))); let arguments = Vec::from_iter_in(it, env.arena).into_bump_slice(); @@ -871,17 +872,7 @@ pub fn test_helper<'a>( let fields = [Layout::U64, Layout::U64, main_proc.ret_layout]; let repr = LayoutRepr::Struct(env.arena.alloc(fields)); let output_layout = layout_interner.insert_direct_no_semantic(repr); - - let body = test_helper_body( - env, - ident_ids, - layout_interner, - main_proc, - arguments, - output_layout, - ); - - let name = LambdaName::no_niche(env.create_symbol(ident_ids, "test_main")); + let body = test_helper_body(env, ident_ids, main_proc, arguments, output_layout); Proc { name, @@ -901,7 +892,6 @@ pub fn test_helper<'a>( fn test_helper_body<'a>( env: &CodeGenHelp<'a>, ident_ids: &mut IdentIds, - layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, arguments: &'a [Symbol], output_layout: InLayout<'a>, @@ -945,11 +935,11 @@ fn test_helper_body<'a>( let result = |next| Stmt::Let(result_symbol, result_expr, main_proc.ret_layout, next); let ok_tag_symbol = env.create_symbol(ident_ids, "ok_tag"); - let ok_tag_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); + let ok_tag_expr = Expr::Literal(Literal::Int((0i128).to_ne_bytes())); let ok_tag = |next| Stmt::Let(ok_tag_symbol, ok_tag_expr, Layout::U64, next); let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); - let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); + let msg_ptr_expr = Expr::Literal(Literal::Int((0i128).to_ne_bytes())); let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); // construct the record @@ -993,19 +983,14 @@ fn test_helper_body<'a>( }); let load = |next| Stmt::Let(load_symbol, load_expr, main_proc.ret_layout, next); - // is_longjmp_symbol will the pointer to the error message let err_tag_symbol = env.create_symbol(ident_ids, "err_tag"); - let err_tag_expr = Expr::Literal(Literal::Int((1 as i128).to_ne_bytes())); + let err_tag_expr = Expr::Literal(Literal::Int((1i128).to_ne_bytes())); let err_tag = |next| Stmt::Let(err_tag_symbol, err_tag_expr, Layout::U64, next); - // let msg_ptr_symbol = env.create_symbol(ident_ids, "msg_ptr"); - // let msg_ptr_expr = Expr::Literal(Literal::Int((0 as i128).to_ne_bytes())); - // let msg_ptr = |next| Stmt::Let(msg_ptr_symbol, msg_ptr_expr, Layout::U64, next); - let msg_ptr_symbol = is_longjmp_symbol; - // construct the record let output_symbol = env.create_symbol(ident_ids, "output_err"); - let fields = [err_tag_symbol, msg_ptr_symbol, load_symbol]; + // is_longjmp_symbol is a pointer to the error message + let fields = [err_tag_symbol, is_longjmp_symbol, load_symbol]; let output_expr = Expr::Struct(env.arena.alloc(fields)); let output = |next| Stmt::Let(output_symbol, output_expr, output_layout, next); @@ -1015,14 +1000,11 @@ fn test_helper_body<'a>( load(arena.alloc( // err_tag(arena.alloc( - // - // msg_ptr(arena.alloc( // output(arena.alloc( // Stmt::Ret(output_symbol), )), - // )), )), )), ))) From f692c8dabf760ac410eb311a66d66ded0a357125 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Jul 2023 18:59:54 +0200 Subject: [PATCH 09/25] consistent printing of the error message --- crates/compiler/test_gen/src/helpers/dev.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 6357262c2a..5163fda67b 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -362,7 +362,14 @@ macro_rules! assert_evals_to { match result.into_result() { Ok(value) => transform(value), - Err((msg, _tag)) => panic!("Roc failed with message: {msg}"), + Err((msg, tag)) => { + use roc_mono::ir::CrashTag; + + match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), + } + } } } }; From 29df340a26e8cf38bc718bd690c3cfa1def495b0 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Jul 2023 19:28:08 +0200 Subject: [PATCH 10/25] pass the crash tag through --- .../compiler/gen_dev/src/generic64/x86_64.rs | 9 ++- crates/compiler/mono/src/code_gen_help/mod.rs | 68 +++++++++++++------ .../compiler/test_gen/src/gen_primitives.rs | 2 +- 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index da592e1baf..44e0764ac4 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -485,7 +485,7 @@ impl CallConv for X86_64Syste ASM::mov_reg64_mem64_offset32(buf, RDX, RSP, 0); ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RDX); - // zero out eax, so we return 0i32 (we do a 64-bit xor for convenience) + // zero out eax, so we return 0 (we do a 64-bit xor for convenience) ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); ASM::ret(buf) @@ -527,8 +527,11 @@ impl CallConv for X86_64Syste type ASM = X86_64Assembler; // move the first argument to roc_panic (a *RocStr) into r8 - ASM::mov_reg64_imm64(buf, R8, 8); - ASM::add_reg64_reg64_reg64(buf, R8, R8, RSP); + ASM::add_reg64_reg64_imm32(buf, R8, RSP, 8); + + // move the crash tag into the second return register. We add 1 to it because the 0 value + // is already used for "no crash occurred" + ASM::add_reg64_reg64_imm32(buf, RDX, RDI, 1); // the setlongjmp_buffer ASM::data_pointer(buf, relocs, String::from("setlongjmp_buffer"), RDI); diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index b757e15984..7d9b27c654 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -872,7 +872,14 @@ pub fn test_helper<'a>( let fields = [Layout::U64, Layout::U64, main_proc.ret_layout]; let repr = LayoutRepr::Struct(env.arena.alloc(fields)); let output_layout = layout_interner.insert_direct_no_semantic(repr); - let body = test_helper_body(env, ident_ids, main_proc, arguments, output_layout); + let body = test_helper_body( + env, + ident_ids, + layout_interner, + main_proc, + arguments, + output_layout, + ); Proc { name, @@ -892,6 +899,7 @@ pub fn test_helper<'a>( fn test_helper_body<'a>( env: &CodeGenHelp<'a>, ident_ids: &mut IdentIds, + layout_interner: &mut STLayoutInterner<'a>, main_proc: &Proc<'a>, arguments: &'a [Symbol], output_layout: InLayout<'a>, @@ -907,16 +915,35 @@ fn test_helper_body<'a>( }); let buffer_stmt = |next| Stmt::Let(buffer_symbol, buffer_expr, Layout::U64, next); - let is_longjmp_symbol = env.create_symbol(ident_ids, "is_longjmp"); - let is_longjmp_expr = Expr::Call(Call { + let field_layouts = env.arena.alloc([Layout::U64, Layout::U64]); + let ret_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Struct(field_layouts)); + + let setjmp_symbol = env.create_symbol(ident_ids, "setjmp"); + let setjmp_expr = Expr::Call(Call { call_type: CallType::LowLevel { op: LowLevel::SetJmp, update_mode: UpdateModeId::BACKEND_DUMMY, }, arguments: env.arena.alloc([buffer_symbol]), }); + let setjmp_stmt = |next| Stmt::Let(setjmp_symbol, setjmp_expr, ret_layout, next); + + let is_longjmp_symbol = env.create_symbol(ident_ids, "is_longjmp"); + let is_longjmp_expr = Expr::StructAtIndex { + index: 0, + field_layouts, + structure: setjmp_symbol, + }; let is_longjmp_stmt = |next| Stmt::Let(is_longjmp_symbol, is_longjmp_expr, Layout::U64, next); + let tag_symbol = env.create_symbol(ident_ids, "tag"); + let tag_expr = Expr::StructAtIndex { + index: 1, + field_layouts, + structure: setjmp_symbol, + }; + let tag_stmt = |next| Stmt::Let(tag_symbol, tag_expr, Layout::U64, next); + // normal path, no panics let if_zero_stmt = { let it = main_proc.args.iter().map(|(a, _)| *a); @@ -983,14 +1010,10 @@ fn test_helper_body<'a>( }); let load = |next| Stmt::Let(load_symbol, load_expr, main_proc.ret_layout, next); - let err_tag_symbol = env.create_symbol(ident_ids, "err_tag"); - let err_tag_expr = Expr::Literal(Literal::Int((1i128).to_ne_bytes())); - let err_tag = |next| Stmt::Let(err_tag_symbol, err_tag_expr, Layout::U64, next); - // construct the record let output_symbol = env.create_symbol(ident_ids, "output_err"); // is_longjmp_symbol is a pointer to the error message - let fields = [err_tag_symbol, is_longjmp_symbol, load_symbol]; + let fields = [tag_symbol, is_longjmp_symbol, load_symbol]; let output_expr = Expr::Struct(env.arena.alloc(fields)); let output = |next| Stmt::Let(output_symbol, output_expr, output_layout, next); @@ -999,12 +1022,9 @@ fn test_helper_body<'a>( // load(arena.alloc( // - err_tag(arena.alloc( + output(arena.alloc( // - output(arena.alloc( - // - Stmt::Ret(output_symbol), - )), + Stmt::Ret(output_symbol), )), )), ))) @@ -1012,15 +1032,21 @@ fn test_helper_body<'a>( buffer_stmt(env.arena.alloc( // - is_longjmp_stmt(env.arena.alloc( + setjmp_stmt(env.arena.alloc( // - switch_if_zero_else( - env.arena, - is_longjmp_symbol, - output_layout, - if_zero_stmt, - if_nonzero_stmt, - ), + is_longjmp_stmt(env.arena.alloc( + // + tag_stmt(env.arena.alloc( + // + switch_if_zero_else( + env.arena, + is_longjmp_symbol, + output_layout, + if_zero_stmt, + if_nonzero_stmt, + ), + )), + )), )), )) } diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index d2737b91b6..3b4cced298 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -994,7 +994,7 @@ fn undefined_variable() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -#[should_panic(expected = "Roc failed with message: a crash")] +#[should_panic(expected = "User crash with message: \"a crash\"")] fn a_crash() { assert_evals_to!( indoc!( From d5bf526d6671c9041f3471a67b1cbb1b5a8192f5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 28 Jul 2023 15:44:21 +0200 Subject: [PATCH 11/25] fix calling convention problems --- .../compiler/test_gen/src/gen_primitives.rs | 2 +- crates/compiler/test_gen/src/helpers/dev.rs | 82 ++++++++++--------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 3b4cced298..8afd1e9ddd 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -975,7 +975,7 @@ fn overflow_frees_list() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[should_panic(expected = "Roc failed with message: ")] fn undefined_variable() { assert_evals_to!( diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 5163fda67b..2f3f4ec5f7 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -285,7 +285,7 @@ impl RocCallResult { let tag = (n - 1) as u32; let tag = tag .try_into() - .unwrap_or_else(|_| panic!("received illegal tag: {tag}")); + .unwrap_or_else(|_| panic!("received illegal tag: {tag} {msg}")); (msg.as_str().to_owned(), tag) }), @@ -293,6 +293,27 @@ impl RocCallResult { } } +fn get_test_main_fn( + lib: &libloading::Library, +) -> libloading::Symbol RocCallResult> { + let main_fn_name = "test_main"; + + unsafe { + lib.get(main_fn_name.as_bytes()) + .ok() + .ok_or(format!("Unable to JIT compile `{main_fn_name}`")) + .expect("errored") + } +} + +pub(crate) fn run_test_main(lib: &libloading::Library) -> Result { + let main = get_test_main_fn::(lib); + + let result = unsafe { main() }; + + result.into_result() +} + impl From> for Result { fn from(call_result: RocCallResult) -> Self { call_result.into_result() @@ -321,54 +342,37 @@ macro_rules! assert_evals_to { }; ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $lazy_literals:expr) => { use bumpalo::Bump; - use $crate::helpers::dev::RocCallResult; let arena = Bump::new(); let (_main_fn_name, errors, lib) = $crate::helpers::dev::helper(&arena, $src, $leak, $lazy_literals); - let transform = |success| { - let expected = $expected; - #[allow(clippy::redundant_closure_call)] - let given = $transform(success); - assert_eq!(&given, &expected, "output is different"); - }; + let result = $crate::helpers::dev::run_test_main::<$ty>(&lib); - let main_fn_name = "test_main"; + if !errors.is_empty() { + dbg!(&errors); - type Main = unsafe extern "C" fn(*mut RocCallResult<$ty>); + assert_eq!( + errors, + std::vec::Vec::new(), + "Encountered errors: {:?}", + errors + ); + } - unsafe { - let main: libloading::Symbol
= lib - .get(main_fn_name.as_bytes()) - .ok() - .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) - .expect("errored"); - - let mut result = std::mem::MaybeUninit::uninit(); - main(result.as_mut_ptr()); - let result = result.assume_init(); - - if !errors.is_empty() { - dbg!(&errors); - - assert_eq!( - errors, - std::vec::Vec::new(), - "Encountered errors: {:?}", - errors - ); + match result { + Ok(value) => { + let expected = $expected; + #[allow(clippy::redundant_closure_call)] + let given = $transform(value); + assert_eq!(&given, &expected, "output is different"); } + Err((msg, tag)) => { + use roc_mono::ir::CrashTag; - match result.into_result() { - Ok(value) => transform(value), - Err((msg, tag)) => { - use roc_mono::ir::CrashTag; - - match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), - CrashTag::User => panic!(r#"User crash with message: "{msg}""#), - } + match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), } } } From a2b8274ade1da9d2c0c28ce386a336ab4aa68daf Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 28 Jul 2023 15:53:06 +0200 Subject: [PATCH 12/25] spelling --- crates/compiler/mono/src/code_gen_help/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 7d9b27c654..f0587fa70d 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -991,7 +991,7 @@ fn test_helper_body<'a>( )) }; - // a longjmp/panic occured + // a longjmp/panic occurred let if_nonzero_stmt = { let alloca_symbol = env.create_symbol(ident_ids, "alloca"); let alloca_expr = Expr::Alloca { From 863ba062697bdb1fc3e61a1c7dbee6a1975aa194 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 28 Jul 2023 16:23:43 +0200 Subject: [PATCH 13/25] disable development flag --- crates/compiler/test_gen/src/helpers/dev.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 2f3f4ec5f7..b9140c688d 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -215,7 +215,7 @@ pub fn helper( let builtins_host_tempfile = roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile"); - if true { + if false { std::fs::copy(&app_o_file, "/tmp/app.o").unwrap(); } From d42e987b12a7fa6f0e88ee721e17e617ad76dde5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 29 Jul 2023 15:59:37 +0200 Subject: [PATCH 14/25] a blind attempt on setjmp/longjmp for windows --- .../compiler/gen_dev/src/generic64/x86_64.rs | 184 ++++++++++++++---- 1 file changed, 150 insertions(+), 34 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 44e0764ac4..f793ee1c0c 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -457,33 +457,25 @@ impl CallConv for X86_64Syste // 2023a4: 31 c0 xor eax,eax // 2023a6: c3 ret - let mut offset = 0; + let env = RDI; - // move the argument to rdi - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RBX); - offset += 8; + // store caller-saved (i.e. non-volatile) registers + ASM::mov_mem64_offset32_reg64(buf, env, 0x00, RBX); + ASM::mov_mem64_offset32_reg64(buf, env, 0x08, RBP); + ASM::mov_mem64_offset32_reg64(buf, env, 0x10, R12); + ASM::mov_mem64_offset32_reg64(buf, env, 0x18, R13); + ASM::mov_mem64_offset32_reg64(buf, env, 0x20, R14); + ASM::mov_mem64_offset32_reg64(buf, env, 0x28, R15); - // store the base pointer - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RBP); - offset += 8; - - // store other non-volatile registers - for register in [R12, R13, R14, R15] { - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, register); - offset += 8; - } - - // not 100% sure on what this does. It calculates the address that the longjmp will jump - // to, so this just be right after the call to setjmp. + // go one value up (as if setjmp wasn't called) lea_reg64_offset8(buf, RDX, RSP, 0x8); - // store the location to later jump to - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RDX); - offset += 8; + // store the new stack pointer + ASM::mov_mem64_offset32_reg64(buf, env, 0x30, RDX); - // store the current stack pointer + // store the address we'll resume at ASM::mov_reg64_mem64_offset32(buf, RDX, RSP, 0); - ASM::mov_mem64_offset32_reg64(buf, RDI, offset, RDX); + ASM::mov_mem64_offset32_reg64(buf, env, 0x38, RDX); // zero out eax, so we return 0 (we do a 64-bit xor for convenience) ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); @@ -510,16 +502,19 @@ impl CallConv for X86_64Syste // make sure something nonzero is returned ?! ASM::mov_reg64_reg64(buf, RAX, RSI); - // move the values back into the registers - let mut offset = 0; - let registers = [RBX, RBP, R12, R13, R14, R15, RSP]; + // load the caller-saved registers + let env = RDI; + ASM::mov_reg64_mem64_offset32(buf, RBX, env, 0x00); + ASM::mov_reg64_mem64_offset32(buf, RBP, env, 0x08); + ASM::mov_reg64_mem64_offset32(buf, R12, env, 0x10); + ASM::mov_reg64_mem64_offset32(buf, R13, env, 0x18); + ASM::mov_reg64_mem64_offset32(buf, R14, env, 0x20); + ASM::mov_reg64_mem64_offset32(buf, R15, env, 0x28); - for dst in registers { - ASM::mov_reg64_mem64_offset32(buf, dst, RDI, offset); - offset += 8; - } + // value of rsp before the setjmp call + ASM::mov_reg64_mem64_offset32(buf, RSP, env, 0x30); - jmp_reg64_offset8(buf, RDI, offset as i8) + jmp_reg64_offset8(buf, env, 0x38) } fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { @@ -1137,16 +1132,137 @@ impl CallConv for X86_64Windo todo!("Loading returned complex symbols for X86_64"); } - fn setjmp(_buf: &mut Vec<'_, u8>) { - todo!() + fn setjmp(buf: &mut Vec<'_, u8>) { + use X86_64GeneralReg::*; + type ASM = X86_64Assembler; + + // input: + // + // rcx: pointer to the jmp_buf + // rdx: stack pointer + + // mingw_getsp: + // lea rax [ rsp + 8 ] + // ret + // + // _setjmp: + // mov [rcx + 0x00] rdx + // mov [rcx + 0x08] rbx + // mov [rcx + 0x18] rbp # note 0x10 is not used yet! + // mov [rcx + 0x20] rsi + // mov [rcx + 0x28] rdi + // mov [rcx + 0x30] r12 + // mov [rcx + 0x38] r13 + // mov [rcx + 0x40] r14 + // mov [rcx + 0x48] r15 + // lea r8 [rsp + 0x08] + // mov [rcx + 0x10] r8 + // mov r8 [rsp] + // mov [rcx + 0x50] r8 + // + // stmxcsr [rcx + 0x58] + // fnstcw word ptr [rcx + 0x5C] + // + // mobdxq xmmword ptr [rcx + 0x60], xmm6 + // mobdxq xmmword ptr [rcx + 0x70], xmm7 + // mobdxq xmmword ptr [rcx + 0x80], xmm8 + // mobdxq xmmword ptr [rcx + 0x90], xmm9 + // mobdxq xmmword ptr [rcx + 0xa0], xmm10 + // mobdxq xmmword ptr [rcx + 0xb0], xmm11 + // mobdxq xmmword ptr [rcx + 0xc0], xmm12 + // mobdxq xmmword ptr [rcx + 0xd0], xmm13 + // mobdxq xmmword ptr [rcx + 0xe0], xmm14 + // mobdxq xmmword ptr [rcx + 0xf0], xmm15 + // + // xor eax, eax + // ret + + let env = RCX; + debug_assert_eq!(env, Self::GENERAL_PARAM_REGS[0]); + + ASM::mov_mem64_offset32_reg64(buf, env, 0x00, RDX); + ASM::mov_mem64_offset32_reg64(buf, env, 0x08, RBX); + // NOTE: 0x10 is unused here! + ASM::mov_mem64_offset32_reg64(buf, env, 0x18, RBP); + ASM::mov_mem64_offset32_reg64(buf, env, 0x20, RSI); + ASM::mov_mem64_offset32_reg64(buf, env, 0x28, RDI); + ASM::mov_mem64_offset32_reg64(buf, env, 0x30, R12); + ASM::mov_mem64_offset32_reg64(buf, env, 0x38, R13); + ASM::mov_mem64_offset32_reg64(buf, env, 0x40, R14); + ASM::mov_mem64_offset32_reg64(buf, env, 0x48, R15); + + // go one value up (as if setjmp wasn't called) + lea_reg64_offset8(buf, R8, RSP, 0x8); + ASM::mov_mem64_offset32_reg64(buf, env, 0x10, R8); + + // store the current stack pointer + ASM::mov_reg64_mem64_offset32(buf, R8, RSP, 0); + ASM::mov_mem64_offset32_reg64(buf, env, 0x50, R8); + + // zero out eax, so we return 0 (we do a 64-bit xor for convenience) + ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); + + // now the windows implementation goes on to store xmm registers and sse2 stuff. + // we skip that for now + + ASM::ret(buf) } fn longjmp(_buf: &mut Vec<'_, u8>) { - todo!() + // do nothing, longjmp is part of roc_panic } - fn roc_panic(_buf: &mut Vec<'_, u8>, _relocs: &mut Vec<'_, Relocation>) { - todo!() + fn roc_panic(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>) { + use X86_64GeneralReg::*; + type ASM = X86_64Assembler; + + // a *const RocStr + let roc_str_ptr = RCX; + debug_assert_eq!(roc_str_ptr, Self::GENERAL_PARAM_REGS[0]); + + // a 32-bit integer + let panic_tag = RDX; + debug_assert_eq!(panic_tag, Self::GENERAL_PARAM_REGS[1]); + + // the setlongjmp_buffer + let env = R8; + ASM::data_pointer(buf, relocs, String::from("setlongjmp_buffer"), env); + ASM::mov_reg64_mem64_offset32(buf, env, env, 0); + + // move the roc_str bytes into the setlongjmp_buffer + for offset in [0, 8, 16] { + ASM::mov_reg64_mem64_offset32(buf, R9, RCX, offset); + ASM::mov_mem64_offset32_reg64(buf, env, 0x58 + offset, R9); + } + + // now, time to move all the registers back to how they were + ASM::mov_reg64_mem64_offset32(buf, RDX, env, 0x00); + ASM::mov_reg64_mem64_offset32(buf, RBX, env, 0x08); + // again 0x10 is skipped here + ASM::mov_reg64_mem64_offset32(buf, RBP, env, 0x18); + ASM::mov_reg64_mem64_offset32(buf, RSI, env, 0x20); + ASM::mov_reg64_mem64_offset32(buf, RDI, env, 0x28); + ASM::mov_reg64_mem64_offset32(buf, R12, env, 0x30); + ASM::mov_reg64_mem64_offset32(buf, R13, env, 0x38); + ASM::mov_reg64_mem64_offset32(buf, R14, env, 0x40); + ASM::mov_reg64_mem64_offset32(buf, R15, env, 0x48); + + // value of rsp before setjmp call + ASM::mov_reg64_mem64_offset32(buf, RSP, env, 0x10); + + // set up the return values. The windows fastcall calling convention has only one return + // register, and we need to return two values, so we use some space in the setlongjmp_buffer + + // a pointer to the error message + ASM::mov_reg64_imm64(buf, R9, 0x58); + ASM::add_reg64_reg64_reg64(buf, R9, R9, env); + ASM::mov_mem64_offset32_reg64(buf, env, 0x70, R9); + + // the panic_tag; 1 is added to differentiate from 0 (which indicates success) + ASM::add_reg64_reg64_imm32(buf, RDX, RDX, 1); + ASM::mov_mem64_offset32_reg64(buf, env, 0x70, RDX); + + jmp_reg64_offset8(buf, env, 0x50) } } From da3635de8e8606228c4b2ddfb9fbe96f4cb3c493 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 29 Jul 2023 17:16:50 +0200 Subject: [PATCH 15/25] cleanup --- crates/compiler/gen_dev/src/generic64/mod.rs | 7 ++----- crates/compiler/gen_dev/src/object_builder.rs | 13 ------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 65538bee94..98a9dfc5ca 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -919,12 +919,9 @@ impl< fn build_roc_panic(&mut self) -> (&'a [u8], Vec<'a, Relocation>) { let mut out = bumpalo::vec![in self.env.arena]; + let mut relocs = bumpalo::vec![in self.env.arena]; - let before = self.relocs.len(); - - CC::roc_panic(&mut out, &mut self.relocs); - - let relocs = self.relocs.split_off(before); + CC::roc_panic(&mut out, &mut relocs); (out.into_bump_slice(), relocs) } diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 4469efbc78..86c753eb3d 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -320,13 +320,6 @@ fn build_object<'a, B: Backend<'a>>( "free".into(), ); - // generate_wrapper( - // &mut backend, - // &mut output, - // "roc_panic".into(), - // "roc_builtins.utils.test_panic".into(), - // ); - // Extra symbols only required on unix systems. if matches!(output.format(), BinaryFormat::Elf | BinaryFormat::MachO) { generate_wrapper( @@ -357,12 +350,6 @@ fn build_object<'a, B: Backend<'a>>( let exposed_proc = build_exposed_proc(&mut backend, &proc); let exposed_generic_proc = build_exposed_generic_proc(&mut backend, &proc); - // ModuleId, - // &mut STLayoutInterner<'a>, - // &mut Interns, - // &mut CodeGenHelp<'a>, - // &mut Vec<'a, CallerProc<'a>>, - let (module_id, layout_interner, interns, code_gen_help, _) = backend.module_interns_helpers_mut(); From c4b8a6e8f21e72faf44a60a272a427a22564cbc6 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Sun, 30 Jul 2023 22:57:06 +1000 Subject: [PATCH 16/25] windows patches --- .../compiler/gen_dev/src/generic64/x86_64.rs | 664 +++++++++++++++--- crates/compiler/gen_dev/src/object_builder.rs | 33 +- crates/compiler/test_gen/src/helpers/dev.rs | 4 +- 3 files changed, 583 insertions(+), 118 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index f793ee1c0c..a8c6aa2554 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -744,15 +744,213 @@ impl X64_64SystemVStoreArgs { } } +struct X64_64WindowsFastCallStoreArgs { + general_i: usize, + float_i: usize, + tmp_stack_offset: i32, +} + +impl X64_64WindowsFastCallStoreArgs { + const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = X86_64WindowsFastcall::GENERAL_PARAM_REGS; + const GENERAL_RETURN_REGS: &'static [X86_64GeneralReg] = X86_64WindowsFastcall::GENERAL_RETURN_REGS; + + const FLOAT_PARAM_REGS: &'static [X86_64FloatReg] = X86_64WindowsFastcall::FLOAT_PARAM_REGS; + const FLOAT_RETURN_REGS: &'static [X86_64FloatReg] = X86_64WindowsFastcall::FLOAT_RETURN_REGS; + + fn store_arg<'a>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + match layout_interner.get_repr(in_layout) { + single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), + pointer_layouts!() => self.store_arg_general(buf, storage_manager, sym), + single_register_floats!() => self.store_arg_float(buf, storage_manager, sym), + LayoutRepr::I128 | LayoutRepr::U128 => { + let (offset, _) = storage_manager.stack_offset_and_size(&sym); + + if self.general_i + 1 < Self::GENERAL_PARAM_REGS.len() { + let reg1 = Self::GENERAL_PARAM_REGS[self.general_i]; + let reg2 = Self::GENERAL_PARAM_REGS[self.general_i + 1]; + + X86_64Assembler::mov_reg64_base32(buf, reg1, offset); + X86_64Assembler::mov_reg64_base32(buf, reg2, offset + 8); + + self.general_i += 2; + } else { + // Copy to stack using return reg as buffer. + let reg = Self::GENERAL_RETURN_REGS[0]; + + X86_64Assembler::mov_reg64_base32(buf, reg, offset); + X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset, reg); + + X86_64Assembler::mov_reg64_base32(buf, reg, offset + 8); + X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset + 8, reg); + + self.tmp_stack_offset += 16; + } + } + _ if layout_interner.stack_size(in_layout) == 0 => {} + _ if layout_interner.stack_size(in_layout) > 16 => { + // TODO: Double check this. + // Just copy onto the stack. + // Use return reg as buffer because it will be empty right now. + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + debug_assert_eq!(base_offset % 8, 0); + for i in (0..size as i32).step_by(8) { + X86_64Assembler::mov_reg64_base32( + buf, + Self::GENERAL_RETURN_REGS[0], + base_offset + i, + ); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset + i, + Self::GENERAL_RETURN_REGS[0], + ); + } + self.tmp_stack_offset += size as i32; + } + LayoutRepr::LambdaSet(lambda_set) => self.store_arg( + buf, + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + LayoutRepr::Struct { .. } => { + // for now, just also store this on the stack + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + debug_assert_eq!(base_offset % 8, 0); + for i in (0..size as i32).step_by(8) { + X86_64Assembler::mov_reg64_base32( + buf, + Self::GENERAL_RETURN_REGS[0], + base_offset + i, + ); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset + i, + Self::GENERAL_RETURN_REGS[0], + ); + } + self.tmp_stack_offset += size as i32; + } + LayoutRepr::Union(UnionLayout::NonRecursive(_)) => { + type ASM = X86_64Assembler; + + let tmp_reg = Self::GENERAL_RETURN_REGS[0]; + let stack_offset = self.tmp_stack_offset; + + let mut copied = 0; + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + + if size - copied >= 8 { + for _ in (0..(size - copied)).step_by(8) { + ASM::mov_reg64_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg64(buf, stack_offset + copied as i32, tmp_reg); + + copied += 8; + } + } + + if size - copied >= 4 { + for _ in (0..(size - copied)).step_by(4) { + ASM::mov_reg32_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg32(buf, stack_offset + copied as i32, tmp_reg); + + copied += 4; + } + } + + if size - copied >= 2 { + for _ in (0..(size - copied)).step_by(2) { + ASM::mov_reg16_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg16(buf, stack_offset + copied as i32, tmp_reg); + + copied += 2; + } + } + + if size - copied >= 1 { + for _ in (0..(size - copied)).step_by(1) { + ASM::mov_reg8_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg8(buf, stack_offset + copied as i32, tmp_reg); + + copied += 1; + } + } + + self.tmp_stack_offset += size as i32; + } + _ => { + todo!( + "calling with arg type, {:?}", + layout_interner.dbg(in_layout) + ); + } + } + } + + fn store_arg_general<'a>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>, + sym: Symbol, + ) { + match Self::GENERAL_PARAM_REGS.get(self.general_i) { + Some(reg) => { + storage_manager.load_to_specified_general_reg(buf, &sym, *reg); + self.general_i += 1; + } + None => { + // Copy to stack using return reg as buffer. + let tmp = Self::GENERAL_RETURN_REGS[0]; + + storage_manager.load_to_specified_general_reg(buf, &sym, tmp); + X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset, tmp); + + self.tmp_stack_offset += 8; + } + } + } + + fn store_arg_float<'a>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>, + sym: Symbol, + ) { + match Self::FLOAT_PARAM_REGS.get(self.float_i) { + Some(reg) => { + storage_manager.load_to_specified_float_reg(buf, &sym, *reg); + self.float_i += 1; + } + None => { + // Copy to stack using return reg as buffer. + let tmp = Self::FLOAT_RETURN_REGS[0]; + + storage_manager.load_to_specified_float_reg(buf, &sym, tmp); + X86_64Assembler::mov_stack32_freg64(buf, self.tmp_stack_offset, tmp); + + self.tmp_stack_offset += 8; + } + } + } +} + +type X86_64StorageManager<'a, 'r, CallConv> = + StorageManager<'a, 'r, X86_64GeneralReg, X86_64FloatReg, X86_64Assembler, CallConv>; + struct X64_64SystemVLoadArgs { general_i: usize, float_i: usize, argument_offset: i32, } -type X86_64StorageManager<'a, 'r, CallConv> = - StorageManager<'a, 'r, X86_64GeneralReg, X86_64FloatReg, X86_64Assembler, CallConv>; - impl X64_64SystemVLoadArgs { fn load_arg<'a>( &mut self, @@ -834,6 +1032,94 @@ impl X64_64SystemVLoadArgs { } } + +struct X64_64WindowsFastCallLoadArgs { + general_i: usize, + float_i: usize, + argument_offset: i32, +} + +impl X64_64WindowsFastCallLoadArgs { + fn load_arg<'a>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + let stack_size = layout_interner.stack_size(in_layout); + match layout_interner.get_repr(in_layout) { + single_register_integers!() => self.load_arg_general(storage_manager, sym), + pointer_layouts!() => self.load_arg_general(storage_manager, sym), + single_register_floats!() => self.load_arg_float(storage_manager, sym), + _ if stack_size == 0 => { + storage_manager.no_data(&sym); + } + _ if stack_size > 16 => { + // TODO: Double check this. + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + LayoutRepr::LambdaSet(lambda_set) => self.load_arg( + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + LayoutRepr::Struct { .. } => { + // for now, just also store this on the stack + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + LayoutRepr::Builtin(Builtin::Int(IntWidth::U128 | IntWidth::I128)) => { + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + LayoutRepr::Union(UnionLayout::NonRecursive(_)) => { + // for now, just also store this on the stack + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + _ => { + todo!( + "Loading args with layout {:?}", + layout_interner.dbg(in_layout) + ); + } + } + } + + fn load_arg_general( + &mut self, + storage_manager: &mut X86_64StorageManager<'_, '_, X86_64WindowsFastcall>, + sym: Symbol, + ) { + if self.general_i < X86_64WindowsFastcall::GENERAL_PARAM_REGS.len() { + let reg = X86_64WindowsFastcall::GENERAL_PARAM_REGS[self.general_i]; + storage_manager.general_reg_arg(&sym, reg); + self.general_i += 1; + } else { + storage_manager.primitive_stack_arg(&sym, self.argument_offset); + self.argument_offset += 8; + } + } + + fn load_arg_float( + &mut self, + storage_manager: &mut X86_64StorageManager<'_, '_, X86_64WindowsFastcall>, + sym: Symbol, + ) { + if self.float_i < X86_64WindowsFastcall::FLOAT_PARAM_REGS.len() { + let reg = X86_64WindowsFastcall::FLOAT_PARAM_REGS[self.float_i]; + storage_manager.float_reg_arg(&sym, reg); + self.float_i += 1; + } else { + storage_manager.primitive_stack_arg(&sym, self.argument_offset); + self.argument_offset += 8; + } + } +} + impl X86_64SystemV { fn returns_via_arg_pointer<'a>( interner: &STLayoutInterner<'a>, @@ -982,48 +1268,67 @@ impl CallConv for X86_64Windo args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, ) { - let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 16; // 16 is the size of the pushed return address and base pointer. + let returns_via_pointer = + X86_64WindowsFastcall::returns_via_arg_pointer(layout_interner, ret_layout); - let mut general_registers_used = 0; - let mut float_registers_used = 0; + let mut state = X64_64WindowsFastCallLoadArgs { + general_i: usize::from(returns_via_pointer), + float_i: 0, + // 16 is the size of the pushed return address and base pointer. + argument_offset: X86_64WindowsFastcall::SHADOW_SPACE_SIZE as i32 + 16, + }; - if X86_64WindowsFastcall::returns_via_arg_pointer(layout_interner, ret_layout) { - storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]); - general_registers_used += 1; + if returns_via_pointer { + storage_manager.ret_pointer_arg(X86_64WindowsFastcall::GENERAL_PARAM_REGS[0]); } - for (layout, sym) in args.iter() { - match layout_interner.get_repr(*layout) { - single_register_integers!() => { - match Self::GENERAL_PARAM_REGS.get(general_registers_used) { - Some(reg) => { - storage_manager.general_reg_arg(sym, *reg); - general_registers_used += 1; - } - None => { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - } - single_register_floats!() => { - match Self::FLOAT_PARAM_REGS.get(float_registers_used) { - Some(reg) => { - storage_manager.float_reg_arg(sym, *reg); - float_registers_used += 1; - } - None => { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - } - _ if layout_interner.stack_size(*layout) == 0 => {} - x => { - todo!("Loading args with layout {:?}", x); - } - } + for (in_layout, sym) in args.iter() { + state.load_arg(storage_manager, layout_interner, *sym, *in_layout); } + + // --- old below TODO remove + // let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 16; // 16 is the size of the pushed return address and base pointer. + + // let mut general_registers_used = 0; + // let mut float_registers_used = 0; + + // if X86_64WindowsFastcall::returns_via_arg_pointer(layout_interner, ret_layout) { + // storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]); + // general_registers_used += 1; + // } + + // for (layout, sym) in args.iter() { + // match layout_interner.get_repr(*layout) { + // single_register_integers!() => { + // match Self::GENERAL_PARAM_REGS.get(general_registers_used) { + // Some(reg) => { + // storage_manager.general_reg_arg(sym, *reg); + // general_registers_used += 1; + // } + // None => { + // storage_manager.primitive_stack_arg(sym, arg_offset); + // arg_offset += 8; + // } + // } + // } + // single_register_floats!() => { + // match Self::FLOAT_PARAM_REGS.get(float_registers_used) { + // Some(reg) => { + // storage_manager.float_reg_arg(sym, *reg); + // float_registers_used += 1; + // } + // None => { + // storage_manager.primitive_stack_arg(sym, arg_offset); + // arg_offset += 8; + // } + // } + // } + // _ if layout_interner.stack_size(*layout) == 0 => {} + // x => { + // todo!("Loading args with layout {:?}", x); + // } + // } + // } } #[inline(always)] @@ -1043,64 +1348,106 @@ impl CallConv for X86_64Windo arg_layouts: &[InLayout<'a>], ret_layout: &InLayout<'a>, ) { - let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; + let mut general_i = 0; + if Self::returns_via_arg_pointer(layout_interner, ret_layout) { - // Save space on the stack for the arg we will return. - storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout)); - todo!("claim first parama reg for the address"); + + // Save space on the stack for the result we will be return. + let base_offset = + storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout)); + + // Set the first reg to the address base + offset. + let ret_reg = Self::GENERAL_PARAM_REGS[general_i]; + general_i += 1; + X86_64Assembler::add_reg64_reg64_imm32( + buf, + ret_reg, + X86_64GeneralReg::RBP, + base_offset, + ); } - let mut general_registers_used = 0; - let mut float_registers_used = 0; + let mut state = X64_64WindowsFastCallStoreArgs { + general_i, + float_i: 0, + tmp_stack_offset: Self::SHADOW_SPACE_SIZE as i32, + }; - for (sym, layout) in args.iter().zip(arg_layouts.iter()) { - match layout_interner.get_repr(*layout) { - single_register_integers!() => { - match Self::GENERAL_PARAM_REGS.get(general_registers_used) { - Some(reg) => { - storage_manager.load_to_specified_general_reg(buf, sym, *reg); - general_registers_used += 1; - } - None => { - // Copy to stack using return reg as buffer. - let tmp = Self::GENERAL_RETURN_REGS[0]; - - storage_manager.load_to_specified_general_reg(buf, sym, tmp); - X86_64Assembler::mov_stack32_reg64(buf, tmp_stack_offset, tmp); - - tmp_stack_offset += 8; - } - } - } - single_register_floats!() => { - match Self::FLOAT_PARAM_REGS.get(float_registers_used) { - Some(reg) => { - storage_manager.load_to_specified_float_reg(buf, sym, *reg); - float_registers_used += 1; - } - None => { - // Copy to stack using return reg as buffer. - let tmp = Self::FLOAT_RETURN_REGS[0]; - - storage_manager.load_to_specified_float_reg(buf, sym, tmp); - X86_64Assembler::mov_stack32_freg64(buf, tmp_stack_offset, tmp); - - tmp_stack_offset += 8; - } - } - } - _ if layout_interner.stack_size(*layout) == 0 => {} - x => { - todo!("calling with arg type, {:?}", x); - } - } + for (sym, in_layout) in args.iter().zip(arg_layouts.iter()) { + state.store_arg(buf, storage_manager, layout_interner, *sym, *in_layout); } - storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32); + + storage_manager.update_fn_call_stack_size(state.tmp_stack_offset as u32); + + // let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; + // let mut general_registers_used = 0; + // let mut float_registers_used = 0; + + // if Self::returns_via_arg_pointer(layout_interner, ret_layout) { + // // Save space on the stack for the arg we will return. + // let base_offset = storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout)); + + // // Set the first reg to the address base + offset. + // let ret_reg = Self::GENERAL_PARAM_REGS[general_registers_used]; + // general_registers_used += 1; + // X86_64Assembler::add_reg64_reg64_imm32( + // buf, + // ret_reg, + // X86_64GeneralReg::RBP, + // base_offset, + // ); + + // } + + // for (sym, layout) in args.iter().zip(arg_layouts.iter()) { + // match layout_interner.get_repr(*layout) { + // single_register_integers!() => { + // match Self::GENERAL_PARAM_REGS.get(general_registers_used) { + // Some(reg) => { + // storage_manager.load_to_specified_general_reg(buf, sym, *reg); + // general_registers_used += 1; + // } + // None => { + // // Copy to stack using return reg as buffer. + // let tmp = Self::GENERAL_RETURN_REGS[0]; + + // storage_manager.load_to_specified_general_reg(buf, sym, tmp); + // X86_64Assembler::mov_stack32_reg64(buf, tmp_stack_offset, tmp); + + // tmp_stack_offset += 8; + // } + // } + // } + // single_register_floats!() => { + // match Self::FLOAT_PARAM_REGS.get(float_registers_used) { + // Some(reg) => { + // storage_manager.load_to_specified_float_reg(buf, sym, *reg); + // float_registers_used += 1; + // } + // None => { + // // Copy to stack using return reg as buffer. + // let tmp = Self::FLOAT_RETURN_REGS[0]; + + // storage_manager.load_to_specified_float_reg(buf, sym, tmp); + // X86_64Assembler::mov_stack32_freg64(buf, tmp_stack_offset, tmp); + + // tmp_stack_offset += 8; + // } + // } + // } + + // _ if layout_interner.stack_size(*layout) == 0 => {} + // x => { + // todo!("calling with arg type, {:?}", x); + // } + // } + // } + // storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32); } fn return_complex_symbol<'a>( - _buf: &mut Vec<'a, u8>, - _storage_manager: &mut StorageManager< + buf: &mut Vec<'a, u8>, + storage_manager: &mut StorageManager< 'a, '_, X86_64GeneralReg, @@ -1108,16 +1455,57 @@ impl CallConv for X86_64Windo X86_64Assembler, X86_64WindowsFastcall, >, - _layout_interner: &mut STLayoutInterner<'a>, - _sym: &Symbol, - _layout: &InLayout<'a>, + layout_interner: &mut STLayoutInterner<'a>, + sym: &Symbol, + layout: &InLayout<'a>, ) { - todo!("Returning complex symbols for X86_64"); + match layout_interner.get_repr(*layout) { + single_register_layouts!() => { + internal_error!("single register layouts are not complex symbols"); + } + _ if layout_interner.stack_size(*layout) == 0 => {} + _ if !Self::returns_via_arg_pointer(layout_interner, layout) => { + let (base_offset, size) = storage_manager.stack_offset_and_size(sym); + debug_assert_eq!(base_offset % 8, 0); + if size <= 8 { + X86_64Assembler::mov_reg64_base32( + buf, + Self::GENERAL_RETURN_REGS[0], + base_offset, + ); + // } else if size <= 16 { + // X86_64Assembler::mov_reg64_base32( + // buf, + // Self::GENERAL_RETURN_REGS[0], + // base_offset, + // ); + // X86_64Assembler::mov_reg64_base32( + // buf, + // Self::GENERAL_RETURN_REGS[1], + // base_offset + 8, + // ); + } else { + internal_error!( + "types that don't return via arg pointer must be less than 8 bytes" + ); + } + } + _ => { + // This is a large type returned via the arg pointer. + storage_manager.copy_symbol_to_arg_pointer(buf, sym, layout); + // Also set the return reg to the arg pointer. + storage_manager.load_to_specified_general_reg( + buf, + &Symbol::RET_POINTER, + Self::GENERAL_RETURN_REGS[0], + ); + } + } } fn load_returned_complex_symbol<'a>( - _buf: &mut Vec<'a, u8>, - _storage_manager: &mut StorageManager< + buf: &mut Vec<'a, u8>, + storage_manager: &mut StorageManager< 'a, '_, X86_64GeneralReg, @@ -1125,11 +1513,41 @@ impl CallConv for X86_64Windo X86_64Assembler, X86_64WindowsFastcall, >, - _layout_interner: &mut STLayoutInterner<'a>, - _sym: &Symbol, - _layout: &InLayout<'a>, + layout_interner: &mut STLayoutInterner<'a>, + sym: &Symbol, + layout: &InLayout<'a>, ) { - todo!("Loading returned complex symbols for X86_64"); + match layout_interner.get_repr(*layout) { + single_register_layouts!() => { + internal_error!("single register layouts are not complex symbols"); + } + _ if layout_interner.stack_size(*layout) == 0 => { + storage_manager.no_data(sym); + } + _ if !Self::returns_via_arg_pointer(layout_interner, layout) => { + let size = layout_interner.stack_size(*layout); + let offset = storage_manager.claim_stack_area(sym, size); + if size <= 8 { + X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]); + // } else if size <= 16 { + // X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]); + // X86_64Assembler::mov_base32_reg64( + // buf, + // offset + 8, + // Self::GENERAL_RETURN_REGS[1], + // ); + } else { + internal_error!( + "types that don't return via arg pointer must be less than 8 bytes" + ); + } + } + _ => { + // This should have been recieved via an arg pointer. + // That means the value is already loaded onto the stack area we allocated before the call. + // Nothing to do. + } + } } fn setjmp(buf: &mut Vec<'_, u8>) { @@ -1177,8 +1595,9 @@ impl CallConv for X86_64Windo // xor eax, eax // ret - let env = RCX; - debug_assert_eq!(env, Self::GENERAL_PARAM_REGS[0]); + let result_pointer = RCX; + let env = RDX; + debug_assert_eq!(env, Self::GENERAL_PARAM_REGS[1]); ASM::mov_mem64_offset32_reg64(buf, env, 0x00, RDX); ASM::mov_mem64_offset32_reg64(buf, env, 0x08, RBX); @@ -1199,12 +1618,17 @@ impl CallConv for X86_64Windo ASM::mov_reg64_mem64_offset32(buf, R8, RSP, 0); ASM::mov_mem64_offset32_reg64(buf, env, 0x50, R8); - // zero out eax, so we return 0 (we do a 64-bit xor for convenience) - ASM::xor_reg64_reg64_reg64(buf, RAX, RAX, RAX); + // zero out the fields of the result pointer + ASM::mov_reg64_imm64(buf, R8, 0x00); + ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R8); + ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x08, R8); // now the windows implementation goes on to store xmm registers and sse2 stuff. // we skip that for now + // store the result pointer into the env so that longjmp can retrieve it + ASM::mov_mem64_offset32_reg64(buf, env, 0x58, result_pointer); + ASM::ret(buf) } @@ -1217,12 +1641,13 @@ impl CallConv for X86_64Windo type ASM = X86_64Assembler; // a *const RocStr - let roc_str_ptr = RCX; - debug_assert_eq!(roc_str_ptr, Self::GENERAL_PARAM_REGS[0]); + let roc_str_ptr = R11; + ASM::mov_reg64_imm64(buf, roc_str_ptr, 16 + 24); // 24 is width of a rocstr + ASM::add_reg64_reg64_reg64(buf, roc_str_ptr, roc_str_ptr, RSP); // a 32-bit integer - let panic_tag = RDX; - debug_assert_eq!(panic_tag, Self::GENERAL_PARAM_REGS[1]); + let panic_tag = RCX; + debug_assert_eq!(panic_tag, Self::GENERAL_PARAM_REGS[0]); // the setlongjmp_buffer let env = R8; @@ -1231,8 +1656,8 @@ impl CallConv for X86_64Windo // move the roc_str bytes into the setlongjmp_buffer for offset in [0, 8, 16] { - ASM::mov_reg64_mem64_offset32(buf, R9, RCX, offset); - ASM::mov_mem64_offset32_reg64(buf, env, 0x58 + offset, R9); + ASM::mov_reg64_mem64_offset32(buf, R9, roc_str_ptr, offset); + ASM::mov_mem64_offset32_reg64(buf, env, 0x60 + offset, R9); } // now, time to move all the registers back to how they were @@ -1252,15 +1677,22 @@ impl CallConv for X86_64Windo // set up the return values. The windows fastcall calling convention has only one return // register, and we need to return two values, so we use some space in the setlongjmp_buffer - + let result_pointer = R9; + ASM::mov_reg64_mem64_offset32(buf, result_pointer, env, 0x58); + // a pointer to the error message - ASM::mov_reg64_imm64(buf, R9, 0x58); - ASM::add_reg64_reg64_reg64(buf, R9, R9, env); - ASM::mov_mem64_offset32_reg64(buf, env, 0x70, R9); + ASM::mov_reg64_imm64(buf, R10, 0x60); + ASM::add_reg64_reg64_reg64(buf, R10, R10, env); + ASM::mov_mem64_offset32_reg64(buf, env, 0x70, R10); + // write a pointer to the error message into result_pointer + ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R10); + // the panic_tag; 1 is added to differentiate from 0 (which indicates success) - ASM::add_reg64_reg64_imm32(buf, RDX, RDX, 1); - ASM::mov_mem64_offset32_reg64(buf, env, 0x70, RDX); + ASM::add_reg64_reg64_imm32(buf, R10, RDX, 1); + + // write the panic tag into the result_pointer + ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x08, R10); jmp_reg64_offset8(buf, env, 0x50) } diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 86c753eb3d..6fd8dd354d 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -74,6 +74,27 @@ pub fn build_module<'a, 'r>( ), ) } + Triple { + architecture: TargetArch::X86_64, + binary_format: TargetBF::Coff, + .. + } if cfg!(feature = "target-x86_64") => { + let backend = new_backend_64bit::< + x86_64::X86_64GeneralReg, + x86_64::X86_64FloatReg, + x86_64::X86_64Assembler, + x86_64::X86_64WindowsFastcall, + >(env, TargetInfo::default_x86_64(), interns, layout_interner); + build_object( + procedures, + backend, + Object::new( + BinaryFormat::Coff, + Architecture::X86_64, + Endianness::Little + ), + ) + } Triple { architecture: TargetArch::Aarch64(_), binary_format: TargetBF::Elf, @@ -122,7 +143,8 @@ fn define_setlongjmp_buffer(output: &mut Object) -> SymbolId { let bss_section = output.section_id(StandardSection::Data); // 8 registers + 3 words for a RocStr - const SIZE: usize = (8 + 3) * core::mem::size_of::(); + // TODO 50 is the wrong size here, look at implementation and put correct value in here + const SIZE: usize = (8 + 50) * core::mem::size_of::(); let symbol = Symbol { name: b"setlongjmp_buffer".to_vec(), @@ -335,6 +357,15 @@ fn build_object<'a, B: Backend<'a>>( "roc_shm_open".into(), "shm_open".into(), ); + } else if matches!(output.format(), BinaryFormat::Coff) { + // TODO figure out why this symbol is required, it should not be required + // Without this it does not build on Windows + generate_wrapper( + &mut backend, + &mut output, + "roc_getppid".into(), + "malloc".into(), + ); } } diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index b9140c688d..fb0c990925 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -215,8 +215,10 @@ pub fn helper( let builtins_host_tempfile = roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile"); + // TODO make this an envrionment variable if false { - std::fs::copy(&app_o_file, "/tmp/app.o").unwrap(); + let file_path = std::env::temp_dir().join("app.o"); + std::fs::copy(&app_o_file, file_path).unwrap(); } let (mut child, dylib_path) = link( From b000f3819aa7e56d7a2742cd9c4e913b93a3f7e7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 17:30:43 +0200 Subject: [PATCH 17/25] formatting --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 16 ++++++++-------- crates/compiler/gen_dev/src/object_builder.rs | 10 +++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index a8c6aa2554..c27e9c57e4 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -751,8 +751,10 @@ struct X64_64WindowsFastCallStoreArgs { } impl X64_64WindowsFastCallStoreArgs { - const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = X86_64WindowsFastcall::GENERAL_PARAM_REGS; - const GENERAL_RETURN_REGS: &'static [X86_64GeneralReg] = X86_64WindowsFastcall::GENERAL_RETURN_REGS; + const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = + X86_64WindowsFastcall::GENERAL_PARAM_REGS; + const GENERAL_RETURN_REGS: &'static [X86_64GeneralReg] = + X86_64WindowsFastcall::GENERAL_RETURN_REGS; const FLOAT_PARAM_REGS: &'static [X86_64FloatReg] = X86_64WindowsFastcall::FLOAT_PARAM_REGS; const FLOAT_RETURN_REGS: &'static [X86_64FloatReg] = X86_64WindowsFastcall::FLOAT_RETURN_REGS; @@ -1032,7 +1034,6 @@ impl X64_64SystemVLoadArgs { } } - struct X64_64WindowsFastCallLoadArgs { general_i: usize, float_i: usize, @@ -1351,7 +1352,6 @@ impl CallConv for X86_64Windo let mut general_i = 0; if Self::returns_via_arg_pointer(layout_interner, ret_layout) { - // Save space on the stack for the result we will be return. let base_offset = storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout)); @@ -1378,7 +1378,7 @@ impl CallConv for X86_64Windo } storage_manager.update_fn_call_stack_size(state.tmp_stack_offset as u32); - + // let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; // let mut general_registers_used = 0; // let mut float_registers_used = 0; @@ -1435,7 +1435,7 @@ impl CallConv for X86_64Windo // } // } // } - + // _ if layout_interner.stack_size(*layout) == 0 => {} // x => { // todo!("calling with arg type, {:?}", x); @@ -1679,7 +1679,7 @@ impl CallConv for X86_64Windo // register, and we need to return two values, so we use some space in the setlongjmp_buffer let result_pointer = R9; ASM::mov_reg64_mem64_offset32(buf, result_pointer, env, 0x58); - + // a pointer to the error message ASM::mov_reg64_imm64(buf, R10, 0x60); ASM::add_reg64_reg64_reg64(buf, R10, R10, env); @@ -1687,7 +1687,7 @@ impl CallConv for X86_64Windo // write a pointer to the error message into result_pointer ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R10); - + // the panic_tag; 1 is added to differentiate from 0 (which indicates success) ASM::add_reg64_reg64_imm32(buf, R10, RDX, 1); diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 6fd8dd354d..62acda1cb5 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -86,13 +86,9 @@ pub fn build_module<'a, 'r>( x86_64::X86_64WindowsFastcall, >(env, TargetInfo::default_x86_64(), interns, layout_interner); build_object( - procedures, - backend, - Object::new( - BinaryFormat::Coff, - Architecture::X86_64, - Endianness::Little - ), + procedures, + backend, + Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little), ) } Triple { From 6e4b41a2f4c4a27488447dfaf060ada0e3618cb7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 17:31:08 +0200 Subject: [PATCH 18/25] use the correct register --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index c27e9c57e4..17a2288eb2 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1689,7 +1689,7 @@ impl CallConv for X86_64Windo ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R10); // the panic_tag; 1 is added to differentiate from 0 (which indicates success) - ASM::add_reg64_reg64_imm32(buf, R10, RDX, 1); + ASM::add_reg64_reg64_imm32(buf, R10, panic_tag, 1); // write the panic tag into the result_pointer ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x08, R10); From 4cfa7c124d7fdf5bb28a8dea06493544734f8065 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 18:17:36 +0200 Subject: [PATCH 19/25] better way of fetching the error message --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 1 - crates/compiler/test_gen/src/helpers/dev.rs | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 17a2288eb2..755596a87b 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1683,7 +1683,6 @@ impl CallConv for X86_64Windo // a pointer to the error message ASM::mov_reg64_imm64(buf, R10, 0x60); ASM::add_reg64_reg64_reg64(buf, R10, R10, env); - ASM::mov_mem64_offset32_reg64(buf, env, 0x70, R10); // write a pointer to the error message into result_pointer ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R10); diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index fb0c990925..e2238b2703 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -283,7 +283,10 @@ impl RocCallResult { match self.tag { 0 => Ok(unsafe { self.value.assume_init() }), n => Err({ - let msg: &RocStr = unsafe { &*self.error_msg }; + let mut msg = RocStr::default(); + + unsafe { std::ptr::swap(&mut msg, self.error_msg) }; + let tag = (n - 1) as u32; let tag = tag .try_into() From f65a2db500be5a58e81d125534a62cdf22e833a8 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 18:28:17 +0200 Subject: [PATCH 20/25] run some dev backend tests on windows CI --- .github/workflows/windows_tests.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows_tests.yml b/.github/workflows/windows_tests.yml index 3c930d54b1..82920c2a5f 100644 --- a/.github/workflows/windows_tests.yml +++ b/.github/workflows/windows_tests.yml @@ -50,7 +50,11 @@ jobs: # Why are these tests not build with previous command? => fingerprint error. Use `CARGO_LOG=cargo::core::compiler::fingerprint=info` to investigate - name: Build specific tests without running. Twice for zig lld-link error. - run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli + run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen + + - name: Test setjmp/longjmp logic + run: cargo test-gen-dev --locked --release nat_alias && cargo test-gen-dev --locked --release a_crash - name: Actually run the tests. - run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli \ No newline at end of file + run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli + From c9531bf733f64bacd107b51ec719619b2c4a3504 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 20:57:07 +0200 Subject: [PATCH 21/25] remove dead code --- .../compiler/gen_dev/src/generic64/x86_64.rs | 127 ------------------ 1 file changed, 127 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 755596a87b..9cb7d14311 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1286,50 +1286,6 @@ impl CallConv for X86_64Windo for (in_layout, sym) in args.iter() { state.load_arg(storage_manager, layout_interner, *sym, *in_layout); } - - // --- old below TODO remove - // let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 16; // 16 is the size of the pushed return address and base pointer. - - // let mut general_registers_used = 0; - // let mut float_registers_used = 0; - - // if X86_64WindowsFastcall::returns_via_arg_pointer(layout_interner, ret_layout) { - // storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]); - // general_registers_used += 1; - // } - - // for (layout, sym) in args.iter() { - // match layout_interner.get_repr(*layout) { - // single_register_integers!() => { - // match Self::GENERAL_PARAM_REGS.get(general_registers_used) { - // Some(reg) => { - // storage_manager.general_reg_arg(sym, *reg); - // general_registers_used += 1; - // } - // None => { - // storage_manager.primitive_stack_arg(sym, arg_offset); - // arg_offset += 8; - // } - // } - // } - // single_register_floats!() => { - // match Self::FLOAT_PARAM_REGS.get(float_registers_used) { - // Some(reg) => { - // storage_manager.float_reg_arg(sym, *reg); - // float_registers_used += 1; - // } - // None => { - // storage_manager.primitive_stack_arg(sym, arg_offset); - // arg_offset += 8; - // } - // } - // } - // _ if layout_interner.stack_size(*layout) == 0 => {} - // x => { - // todo!("Loading args with layout {:?}", x); - // } - // } - // } } #[inline(always)] @@ -1378,71 +1334,6 @@ impl CallConv for X86_64Windo } storage_manager.update_fn_call_stack_size(state.tmp_stack_offset as u32); - - // let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; - // let mut general_registers_used = 0; - // let mut float_registers_used = 0; - - // if Self::returns_via_arg_pointer(layout_interner, ret_layout) { - // // Save space on the stack for the arg we will return. - // let base_offset = storage_manager.claim_stack_area(dst, layout_interner.stack_size(*ret_layout)); - - // // Set the first reg to the address base + offset. - // let ret_reg = Self::GENERAL_PARAM_REGS[general_registers_used]; - // general_registers_used += 1; - // X86_64Assembler::add_reg64_reg64_imm32( - // buf, - // ret_reg, - // X86_64GeneralReg::RBP, - // base_offset, - // ); - - // } - - // for (sym, layout) in args.iter().zip(arg_layouts.iter()) { - // match layout_interner.get_repr(*layout) { - // single_register_integers!() => { - // match Self::GENERAL_PARAM_REGS.get(general_registers_used) { - // Some(reg) => { - // storage_manager.load_to_specified_general_reg(buf, sym, *reg); - // general_registers_used += 1; - // } - // None => { - // // Copy to stack using return reg as buffer. - // let tmp = Self::GENERAL_RETURN_REGS[0]; - - // storage_manager.load_to_specified_general_reg(buf, sym, tmp); - // X86_64Assembler::mov_stack32_reg64(buf, tmp_stack_offset, tmp); - - // tmp_stack_offset += 8; - // } - // } - // } - // single_register_floats!() => { - // match Self::FLOAT_PARAM_REGS.get(float_registers_used) { - // Some(reg) => { - // storage_manager.load_to_specified_float_reg(buf, sym, *reg); - // float_registers_used += 1; - // } - // None => { - // // Copy to stack using return reg as buffer. - // let tmp = Self::FLOAT_RETURN_REGS[0]; - - // storage_manager.load_to_specified_float_reg(buf, sym, tmp); - // X86_64Assembler::mov_stack32_freg64(buf, tmp_stack_offset, tmp); - - // tmp_stack_offset += 8; - // } - // } - // } - - // _ if layout_interner.stack_size(*layout) == 0 => {} - // x => { - // todo!("calling with arg type, {:?}", x); - // } - // } - // } - // storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32); } fn return_complex_symbol<'a>( @@ -1473,17 +1364,6 @@ impl CallConv for X86_64Windo Self::GENERAL_RETURN_REGS[0], base_offset, ); - // } else if size <= 16 { - // X86_64Assembler::mov_reg64_base32( - // buf, - // Self::GENERAL_RETURN_REGS[0], - // base_offset, - // ); - // X86_64Assembler::mov_reg64_base32( - // buf, - // Self::GENERAL_RETURN_REGS[1], - // base_offset + 8, - // ); } else { internal_error!( "types that don't return via arg pointer must be less than 8 bytes" @@ -1529,13 +1409,6 @@ impl CallConv for X86_64Windo let offset = storage_manager.claim_stack_area(sym, size); if size <= 8 { X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]); - // } else if size <= 16 { - // X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]); - // X86_64Assembler::mov_base32_reg64( - // buf, - // offset + 8, - // Self::GENERAL_RETURN_REGS[1], - // ); } else { internal_error!( "types that don't return via arg pointer must be less than 8 bytes" From c5db42631ef907260847b4d32012a7dbbaf1d6ad Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 30 Jul 2023 21:20:23 +0200 Subject: [PATCH 22/25] some code sharing --- .../compiler/gen_dev/src/generic64/x86_64.rs | 222 +++++++----------- 1 file changed, 79 insertions(+), 143 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 9cb7d14311..7a7fa0e931 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -546,6 +546,60 @@ impl CallConv for X86_64Syste } } +fn copy_symbol_to_stack<'a, CC>( + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, '_, CC>, + sym: Symbol, + tmp_reg: X86_64GeneralReg, + stack_offset: i32, +) -> u32 +where + CC: CallConv, +{ + type ASM = X86_64Assembler; + + let mut copied = 0; + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + + if size - copied >= 8 { + for _ in (0..(size - copied)).step_by(8) { + ASM::mov_reg64_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg64(buf, stack_offset + copied as i32, tmp_reg); + + copied += 8; + } + } + + if size - copied >= 4 { + for _ in (0..(size - copied)).step_by(4) { + ASM::mov_reg32_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg32(buf, stack_offset + copied as i32, tmp_reg); + + copied += 4; + } + } + + if size - copied >= 2 { + for _ in (0..(size - copied)).step_by(2) { + ASM::mov_reg16_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg16(buf, stack_offset + copied as i32, tmp_reg); + + copied += 2; + } + } + + if size - copied >= 1 { + for _ in (0..(size - copied)).step_by(1) { + ASM::mov_reg8_base32(buf, tmp_reg, base_offset + copied as i32); + ASM::mov_stack32_reg8(buf, stack_offset + copied as i32, tmp_reg); + + copied += 1; + } + } + + size +} + struct X64_64SystemVStoreArgs { general_i: usize, float_i: usize, @@ -567,6 +621,9 @@ impl X64_64SystemVStoreArgs { sym: Symbol, in_layout: InLayout<'a>, ) { + // we use the return register as a temporary register; it will be overwritten anyway + let tmp_reg = Self::GENERAL_RETURN_REGS[0]; + match layout_interner.get_repr(in_layout) { single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), pointer_layouts!() => self.store_arg_general(buf, storage_manager, sym), @@ -599,21 +656,10 @@ impl X64_64SystemVStoreArgs { _ if layout_interner.stack_size(in_layout) > 16 => { // TODO: Double check this. // Just copy onto the stack. - // Use return reg as buffer because it will be empty right now. - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - debug_assert_eq!(base_offset % 8, 0); - for i in (0..size as i32).step_by(8) { - X86_64Assembler::mov_reg64_base32( - buf, - Self::GENERAL_RETURN_REGS[0], - base_offset + i, - ); - X86_64Assembler::mov_stack32_reg64( - buf, - self.tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } + let stack_offset = self.tmp_stack_offset; + + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); + self.tmp_stack_offset += size as i32; } LayoutRepr::LambdaSet(lambda_set) => self.store_arg( @@ -624,67 +670,16 @@ impl X64_64SystemVStoreArgs { lambda_set.runtime_representation(), ), LayoutRepr::Struct { .. } => { - // for now, just also store this on the stack - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - debug_assert_eq!(base_offset % 8, 0); - for i in (0..size as i32).step_by(8) { - X86_64Assembler::mov_reg64_base32( - buf, - Self::GENERAL_RETURN_REGS[0], - base_offset + i, - ); - X86_64Assembler::mov_stack32_reg64( - buf, - self.tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } + let stack_offset = self.tmp_stack_offset; + + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); + self.tmp_stack_offset += size as i32; } LayoutRepr::Union(UnionLayout::NonRecursive(_)) => { - type ASM = X86_64Assembler; - - let tmp_reg = Self::GENERAL_RETURN_REGS[0]; let stack_offset = self.tmp_stack_offset; - let mut copied = 0; - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - - if size - copied >= 8 { - for _ in (0..(size - copied)).step_by(8) { - ASM::mov_reg64_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg64(buf, stack_offset + copied as i32, tmp_reg); - - copied += 8; - } - } - - if size - copied >= 4 { - for _ in (0..(size - copied)).step_by(4) { - ASM::mov_reg32_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg32(buf, stack_offset + copied as i32, tmp_reg); - - copied += 4; - } - } - - if size - copied >= 2 { - for _ in (0..(size - copied)).step_by(2) { - ASM::mov_reg16_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg16(buf, stack_offset + copied as i32, tmp_reg); - - copied += 2; - } - } - - if size - copied >= 1 { - for _ in (0..(size - copied)).step_by(1) { - ASM::mov_reg8_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg8(buf, stack_offset + copied as i32, tmp_reg); - - copied += 1; - } - } + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); self.tmp_stack_offset += size as i32; } @@ -767,6 +762,9 @@ impl X64_64WindowsFastCallStoreArgs { sym: Symbol, in_layout: InLayout<'a>, ) { + // we use the return register as a temporary register; it will be overwritten anyway + let tmp_reg = Self::GENERAL_RETURN_REGS[0]; + match layout_interner.get_repr(in_layout) { single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), pointer_layouts!() => self.store_arg_general(buf, storage_manager, sym), @@ -797,23 +795,11 @@ impl X64_64WindowsFastCallStoreArgs { } _ if layout_interner.stack_size(in_layout) == 0 => {} _ if layout_interner.stack_size(in_layout) > 16 => { - // TODO: Double check this. - // Just copy onto the stack. - // Use return reg as buffer because it will be empty right now. - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - debug_assert_eq!(base_offset % 8, 0); - for i in (0..size as i32).step_by(8) { - X86_64Assembler::mov_reg64_base32( - buf, - Self::GENERAL_RETURN_REGS[0], - base_offset + i, - ); - X86_64Assembler::mov_stack32_reg64( - buf, - self.tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } + // for now, just copy onto the stack. + let stack_offset = self.tmp_stack_offset; + + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); + self.tmp_stack_offset += size as i32; } LayoutRepr::LambdaSet(lambda_set) => self.store_arg( @@ -825,66 +811,16 @@ impl X64_64WindowsFastCallStoreArgs { ), LayoutRepr::Struct { .. } => { // for now, just also store this on the stack - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - debug_assert_eq!(base_offset % 8, 0); - for i in (0..size as i32).step_by(8) { - X86_64Assembler::mov_reg64_base32( - buf, - Self::GENERAL_RETURN_REGS[0], - base_offset + i, - ); - X86_64Assembler::mov_stack32_reg64( - buf, - self.tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } + let stack_offset = self.tmp_stack_offset; + + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); + self.tmp_stack_offset += size as i32; } LayoutRepr::Union(UnionLayout::NonRecursive(_)) => { - type ASM = X86_64Assembler; - - let tmp_reg = Self::GENERAL_RETURN_REGS[0]; let stack_offset = self.tmp_stack_offset; - let mut copied = 0; - let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); - - if size - copied >= 8 { - for _ in (0..(size - copied)).step_by(8) { - ASM::mov_reg64_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg64(buf, stack_offset + copied as i32, tmp_reg); - - copied += 8; - } - } - - if size - copied >= 4 { - for _ in (0..(size - copied)).step_by(4) { - ASM::mov_reg32_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg32(buf, stack_offset + copied as i32, tmp_reg); - - copied += 4; - } - } - - if size - copied >= 2 { - for _ in (0..(size - copied)).step_by(2) { - ASM::mov_reg16_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg16(buf, stack_offset + copied as i32, tmp_reg); - - copied += 2; - } - } - - if size - copied >= 1 { - for _ in (0..(size - copied)).step_by(1) { - ASM::mov_reg8_base32(buf, tmp_reg, base_offset + copied as i32); - ASM::mov_stack32_reg8(buf, stack_offset + copied as i32, tmp_reg); - - copied += 1; - } - } + let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset); self.tmp_stack_offset += size as i32; } From 5448730cfd09f7730393b436f90d6a9be035926d Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 31 Jul 2023 18:23:28 +0200 Subject: [PATCH 23/25] if let vs if --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 7a7fa0e931..9251175d39 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -944,8 +944,7 @@ impl X64_64SystemVLoadArgs { storage_manager: &mut X86_64StorageManager<'_, '_, X86_64SystemV>, sym: Symbol, ) { - if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() { - let reg = X86_64SystemV::GENERAL_PARAM_REGS[self.general_i]; + if let Some(reg) = X86_64SystemV::GENERAL_PARAM_REGS.get(self.general_i) { storage_manager.general_reg_arg(&sym, reg); self.general_i += 1; } else { @@ -959,8 +958,7 @@ impl X64_64SystemVLoadArgs { storage_manager: &mut X86_64StorageManager<'_, '_, X86_64SystemV>, sym: Symbol, ) { - if self.float_i < X86_64SystemV::FLOAT_PARAM_REGS.len() { - let reg = X86_64SystemV::FLOAT_PARAM_REGS[self.float_i]; + if let Some(reg) = X86_64SystemV::FLOAT_PARAM_REGS.get(self.float_i) { storage_manager.float_reg_arg(&sym, reg); self.float_i += 1; } else { @@ -1031,8 +1029,7 @@ impl X64_64WindowsFastCallLoadArgs { storage_manager: &mut X86_64StorageManager<'_, '_, X86_64WindowsFastcall>, sym: Symbol, ) { - if self.general_i < X86_64WindowsFastcall::GENERAL_PARAM_REGS.len() { - let reg = X86_64WindowsFastcall::GENERAL_PARAM_REGS[self.general_i]; + if let Some(reg) = X86_64WindowsFastcall::GENERAL_PARAM_REGS.get(self.general_i) { storage_manager.general_reg_arg(&sym, reg); self.general_i += 1; } else { @@ -1046,8 +1043,7 @@ impl X64_64WindowsFastCallLoadArgs { storage_manager: &mut X86_64StorageManager<'_, '_, X86_64WindowsFastcall>, sym: Symbol, ) { - if self.float_i < X86_64WindowsFastcall::FLOAT_PARAM_REGS.len() { - let reg = X86_64WindowsFastcall::FLOAT_PARAM_REGS[self.float_i]; + if let Some(reg) = X86_64WindowsFastcall::FLOAT_PARAM_REGS.get(self.float_i) { storage_manager.float_reg_arg(&sym, reg); self.float_i += 1; } else { From 2a6060f9b917b1c76f60aaf0aea7b51269bf1508 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 31 Jul 2023 19:03:34 +0200 Subject: [PATCH 24/25] shave off an instruction --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 9251175d39..faa6e7002c 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1447,8 +1447,7 @@ impl CallConv for X86_64Windo // a *const RocStr let roc_str_ptr = R11; - ASM::mov_reg64_imm64(buf, roc_str_ptr, 16 + 24); // 24 is width of a rocstr - ASM::add_reg64_reg64_reg64(buf, roc_str_ptr, roc_str_ptr, RSP); + ASM::add_reg64_reg64_imm32(buf, roc_str_ptr, RSP, 16 + 24); // 24 is width of a rocstr // a 32-bit integer let panic_tag = RCX; From afc3bd5c15ee243b477388a5d773fdb8c5d37528 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 1 Aug 2023 00:43:13 +0200 Subject: [PATCH 25/25] fix missing dereference --- crates/compiler/gen_dev/src/generic64/x86_64.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index faa6e7002c..918330fc58 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -945,7 +945,7 @@ impl X64_64SystemVLoadArgs { sym: Symbol, ) { if let Some(reg) = X86_64SystemV::GENERAL_PARAM_REGS.get(self.general_i) { - storage_manager.general_reg_arg(&sym, reg); + storage_manager.general_reg_arg(&sym, *reg); self.general_i += 1; } else { storage_manager.primitive_stack_arg(&sym, self.argument_offset); @@ -959,7 +959,7 @@ impl X64_64SystemVLoadArgs { sym: Symbol, ) { if let Some(reg) = X86_64SystemV::FLOAT_PARAM_REGS.get(self.float_i) { - storage_manager.float_reg_arg(&sym, reg); + storage_manager.float_reg_arg(&sym, *reg); self.float_i += 1; } else { storage_manager.primitive_stack_arg(&sym, self.argument_offset); @@ -1030,7 +1030,7 @@ impl X64_64WindowsFastCallLoadArgs { sym: Symbol, ) { if let Some(reg) = X86_64WindowsFastcall::GENERAL_PARAM_REGS.get(self.general_i) { - storage_manager.general_reg_arg(&sym, reg); + storage_manager.general_reg_arg(&sym, *reg); self.general_i += 1; } else { storage_manager.primitive_stack_arg(&sym, self.argument_offset); @@ -1044,7 +1044,7 @@ impl X64_64WindowsFastCallLoadArgs { sym: Symbol, ) { if let Some(reg) = X86_64WindowsFastcall::FLOAT_PARAM_REGS.get(self.float_i) { - storage_manager.float_reg_arg(&sym, reg); + storage_manager.float_reg_arg(&sym, *reg); self.float_i += 1; } else { storage_manager.primitive_stack_arg(&sym, self.argument_offset);