diff --git a/lib/compiler-singlepass/src/machine_x64.rs b/lib/compiler-singlepass/src/machine_x64.rs index 3f428fe19fe..e2e13a4c7d3 100644 --- a/lib/compiler-singlepass/src/machine_x64.rs +++ b/lib/compiler-singlepass/src/machine_x64.rs @@ -1807,16 +1807,22 @@ impl Machine for MachineX86_64 { fn push_location_for_native(&mut self, loc: Location) { match loc { Location::Imm64(_) => { - // Push R9 value slot to be exchange with `mov`. + // x86_64 does not support `mov imm64, mem`. We must first place the immdiate value + // into a register and then write the register to the memory. Now the problem is + // that there might not be any registers available to clobber. In order to make + // this work out we spill a register thus retaining both the original value of the + // register and producing the required data at the top of the stack. + // + // FIXME(#2723): figure out how to not require spilling a register here. It should + // definitely be possible to `pick_gpr`/`pick_temp_gpr` to grab an otherwise unused + // register and just clobber its value here. self.assembler.emit_push(Size::S64, Location::GPR(GPR::R9)); - self.reserve_unused_temp_gpr(GPR::R9); self.move_location(Size::S64, loc, Location::GPR(GPR::R9)); self.assembler.emit_xchg( Size::S64, Location::GPR(GPR::R9), Location::Memory(GPR::RSP, 0), ); - self.release_gpr(GPR::R9); } Location::SIMD(_) => { // Dummy value slot to be filled with `mov`. diff --git a/tests/compilers/issues.rs b/tests/compilers/issues.rs index a07d9a82bd9..8a23405baff 100644 --- a/tests/compilers/issues.rs +++ b/tests/compilers/issues.rs @@ -225,3 +225,42 @@ fn call_with_static_data_pointers(mut config: crate::Config) -> Result<()> { instance.exports.get_function("repro")?.call(&[])?; Ok(()) } + +/// Exhaustion of GPRs when calling a function with many floating point arguments +/// +/// Note: this one is specific to Singlepass, but we want to test in all +/// available compilers. +#[compiler_test(issues)] +fn regression_gpr_exhaustion_for_calls(mut config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module + (type (;0;) (func (param f64) (result i32))) + (type (;1;) (func (param f64 f64 f64 f64 f64 f64))) + (func (;0;) (type 0) (param f64) (result i32) + local.get 0 + local.get 0 + local.get 0 + local.get 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + i32.const 0 + call_indirect (type 0) + call_indirect (type 1) + drop + drop + drop + drop + i32.const 0) + (table (;0;) 1 1 funcref)) + "#; + let module = Module::new(&store, wat)?; + let imports: ImportObject = imports! {}; + let instance = Instance::new(&module, &imports)?; + Ok(()) +}