From b968122beef4e18b04d9a1a1f1186af37f00fe21 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 12 Feb 2023 02:08:02 +0100 Subject: [PATCH] simplify systemv argument storing and loading --- .../compiler/gen_dev/src/generic64/storage.rs | 16 + .../compiler/gen_dev/src/generic64/x86_64.rs | 387 ++++++++++-------- crates/compiler/gen_dev/src/lib.rs | 12 +- crates/compiler/module/src/symbol.rs | 1 + crates/compiler/test_gen/src/gen_list.rs | 6 +- 5 files changed, 254 insertions(+), 168 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 343025082a..6a48887841 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -787,6 +787,22 @@ impl< self.copy_symbol_to_stack_offset_help(buf, size, from_offset, to_offset) } }, + Layout::Boxed(_) => { + // like a 64-bit integer + debug_assert_eq!(to_offset % 8, 0); + let reg = self.load_to_general_reg(buf, sym); + ASM::mov_base32_reg64(buf, to_offset, reg); + } + Layout::LambdaSet(lambda_set) => { + // like its runtime representation + self.copy_symbol_to_stack_offset( + layout_interner, + buf, + to_offset, + sym, + &lambda_set.runtime_representation(), + ) + } _ if layout_interner.stack_size(*layout) == 0 => {} // TODO: Verify this is always true. // The dev backend does not deal with refcounting and does not care about if data is safe to memcpy. diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index c1d9e77d21..05520a3c79 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -261,59 +261,22 @@ impl CallConv for X86_64Syste 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 mut general_i = 0; - let mut float_i = 0; - if X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout) { - storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]); - general_i += 1; + let returns_via_pointer = + X86_64SystemV::returns_via_arg_pointer(layout_interner, &ret_layout); + + let mut state = X64_64SystemVLoadArgs { + general_i: if returns_via_pointer { 1 } else { 0 }, + float_i: 0, + // 16 is the size of the pushed return address and base pointer. + argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16, + }; + + if returns_via_pointer { + storage_manager.ret_pointer_arg(X86_64SystemV::GENERAL_PARAM_REGS[0]); } + for (in_layout, sym) in args.iter() { - let stack_size = layout_interner.stack_size(*in_layout); - match *in_layout { - single_register_integers!() => { - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]); - general_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - single_register_floats!() => { - if float_i < Self::FLOAT_PARAM_REGS.len() { - storage_manager.float_reg_arg(sym, Self::FLOAT_PARAM_REGS[float_i]); - float_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - _ if stack_size == 0 => { - storage_manager.no_data_arg(sym); - } - _ if stack_size > 16 => { - // TODO: Double check this. - storage_manager.complex_stack_arg(sym, arg_offset, stack_size); - arg_offset += stack_size as i32; - } - other => match layout_interner.get(other) { - Layout::Boxed(_) => { - // boxed layouts are pointers, which we treat as 64-bit integers - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager - .general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]); - general_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - _ => { - todo!("Loading args with layout {:?}", layout_interner.dbg(other)); - } - }, - } + state.load_arg(storage_manager, layout_interner, *sym, *in_layout); } } @@ -334,9 +297,8 @@ impl CallConv for X86_64Syste arg_layouts: &[InLayout<'a>], ret_layout: &InLayout<'a>, ) { - let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; let mut general_i = 0; - let mut float_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 = @@ -352,110 +314,17 @@ impl CallConv for X86_64Syste ); } - for (sym, layout) in args.iter().zip(arg_layouts.iter()) { - match *layout { - single_register_integers!() => { - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_PARAM_REGS[general_i], - ); - general_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_reg64( - buf, - tmp_stack_offset, - Self::GENERAL_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - single_register_floats!() => { - if float_i < Self::FLOAT_PARAM_REGS.len() { - storage_manager.load_to_specified_float_reg( - buf, - sym, - Self::FLOAT_PARAM_REGS[float_i], - ); - float_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_float_reg( - buf, - sym, - Self::FLOAT_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_freg64( - buf, - tmp_stack_offset, - Self::FLOAT_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - x if layout_interner.stack_size(x) == 0 => {} - x if layout_interner.stack_size(x) > 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, - tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } - tmp_stack_offset += size as i32; - } - other => { - // look at the layout in more detail - match layout_interner.get(other) { - Layout::Boxed(_) => { - // treat boxed like a 64-bit integer - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_PARAM_REGS[general_i], - ); - general_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_reg64( - buf, - tmp_stack_offset, - Self::GENERAL_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - _ => { - todo!("calling with arg type, {:?}", layout_interner.dbg(other)); - } - } - } - } + let mut state = X64_64SystemVStoreArgs { + general_i, + float_i: 0, + tmp_stack_offset: Self::SHADOW_SPACE_SIZE as i32, + }; + + 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); } fn return_complex_symbol<'a, 'r>( @@ -562,6 +431,203 @@ impl CallConv for X86_64Syste } } +struct X64_64SystemVStoreArgs { + general_i: usize, + float_i: usize, + tmp_stack_offset: i32, +} + +impl X64_64SystemVStoreArgs { + const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = X86_64SystemV::GENERAL_PARAM_REGS; + const GENERAL_RETURN_REGS: &'static [X86_64GeneralReg] = X86_64SystemV::GENERAL_RETURN_REGS; + + const FLOAT_PARAM_REGS: &'static [X86_64FloatReg] = X86_64SystemV::FLOAT_PARAM_REGS; + const FLOAT_RETURN_REGS: &'static [X86_64FloatReg] = X86_64SystemV::FLOAT_RETURN_REGS; + + fn store_arg<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + match in_layout { + single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), + single_register_floats!() => self.store_arg_float(buf, storage_manager, sym), + x if layout_interner.stack_size(x) == 0 => {} + x if layout_interner.stack_size(x) > 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; + } + other => { + // look at the layout in more detail + match layout_interner.get(other) { + Layout::Boxed(_) => { + // treat boxed like a 64-bit integer + self.store_arg_general(buf, storage_manager, sym) + } + Layout::LambdaSet(lambda_set) => self.store_arg( + buf, + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + _ => { + todo!("calling with arg type, {:?}", layout_interner.dbg(other)); + } + } + } + } + } + + fn store_arg_general<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < Self::GENERAL_PARAM_REGS.len() { + storage_manager.load_to_specified_general_reg( + buf, + &sym, + Self::GENERAL_PARAM_REGS[self.general_i], + ); + self.general_i += 1; + } else { + // Copy to stack using return reg as buffer. + storage_manager.load_to_specified_general_reg(buf, &sym, Self::GENERAL_RETURN_REGS[0]); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset, + Self::GENERAL_RETURN_REGS[0], + ); + self.tmp_stack_offset += 8; + } + } + + fn store_arg_float<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.float_i < Self::FLOAT_PARAM_REGS.len() { + storage_manager.load_to_specified_float_reg( + buf, + &sym, + Self::FLOAT_PARAM_REGS[self.float_i], + ); + self.float_i += 1; + } else { + // Copy to stack using return reg as buffer. + storage_manager.load_to_specified_float_reg(buf, &sym, Self::FLOAT_RETURN_REGS[0]); + X86_64Assembler::mov_stack32_freg64( + buf, + self.tmp_stack_offset, + Self::FLOAT_RETURN_REGS[0], + ); + self.tmp_stack_offset += 8; + } + } +} + +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, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + let stack_size = layout_interner.stack_size(in_layout); + match in_layout { + single_register_integers!() => 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_arg(&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; + } + other => match layout_interner.get(other) { + Layout::Boxed(_) => { + // boxed layouts are pointers, which we treat as 64-bit integers + self.load_arg_general(storage_manager, sym) + } + Layout::LambdaSet(lambda_set) => self.load_arg( + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + _ => { + dbg!(other, layout_interner.get(other)); + todo!("Loading args with layout {:?}", layout_interner.dbg(other)); + } + }, + } + } + + fn load_arg_general<'a, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() { + let reg = X86_64SystemV::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<'a, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() { + let reg = X86_64SystemV::FLOAT_PARAM_REGS[self.general_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>, @@ -705,14 +771,7 @@ impl CallConv for X86_64Windo #[inline(always)] fn load_args<'a, 'r>( _buf: &mut Vec<'a, u8>, - storage_manager: &mut StorageManager< - 'a, - 'r, - X86_64GeneralReg, - X86_64FloatReg, - X86_64Assembler, - X86_64WindowsFastcall, - >, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64WindowsFastcall>, layout_interner: &mut STLayoutInterner<'a>, args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 77eec1fe02..ffd99b95e6 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -1056,13 +1056,23 @@ trait Backend<'a> { self.free_symbol(&Symbol::DEV_TMP) } Symbol::STR_IS_VALID_SCALAR => { + // just call the function + let layout_id = LayoutIds::default().get(func_sym, ret_layout); + let fn_name = self.symbol_to_string(func_sym, layout_id); + // Now that the arguments are needed, load them if they are literals. + self.load_literal_symbols(args); + self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout) + } + other => { + eprintln!("maybe {other:?} should have a custom implementation?"); + + // just call the function let layout_id = LayoutIds::default().get(func_sym, ret_layout); let fn_name = self.symbol_to_string(func_sym, layout_id); // Now that the arguments are needed, load them if they are literals. self.load_literal_symbols(args); self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout) } - _ => todo!("the function, {:?}", func_sym), } } diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index ca6d0fb1fa..3a93cbfac6 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1402,6 +1402,7 @@ define_builtins! { 77 LIST_COUNT_IF: "countIf" 78 LIST_WALK_FROM: "walkFrom" 79 LIST_WALK_FROM_UNTIL: "walkFromUntil" + 80 LIST_ITER_HELP: "iterHelp" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index 251e1ec9b0..229da7d3eb 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -2140,7 +2140,7 @@ fn get_set_unique_int_list_i64() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn get_set_unique_int_list_i8() { assert_evals_to!( indoc!( @@ -2592,7 +2592,7 @@ fn list_literal_increment_decrement() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn list_pass_to_function() { assert_evals_to!( indoc!( @@ -2612,7 +2612,7 @@ fn list_pass_to_function() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn list_pass_to_set() { assert_evals_to!( indoc!(