diff --git a/cranelift/codegen/src/ir/extfunc.rs b/cranelift/codegen/src/ir/extfunc.rs index d8cccf8653..46f1cf2828 100644 --- a/cranelift/codegen/src/ir/extfunc.rs +++ b/cranelift/codegen/src/ir/extfunc.rs @@ -256,8 +256,19 @@ impl fmt::Display for AbiParam { /// Function argument extension options. /// -/// On some architectures, small integer function arguments are extended to the width of a -/// general-purpose register. +/// On some architectures, small integer function arguments and/or return values are extended to +/// the width of a general-purpose register. +/// +/// This attribute specifies how an argument or return value should be extended *if the platform +/// and ABI require it*. Because the frontend (CLIF generator) does not know anything about the +/// particulars of the target's ABI, and the CLIF should be platform-independent, these attributes +/// specify *how* to extend (according to the signedness of the original program) rather than +/// *whether* to extend. +/// +/// For example, on x86-64, the SystemV ABI does not require extensions of narrow values, so these +/// `ArgumentExtension` attributes are ignored; but in the Baldrdash (SpiderMonkey) ABI on the same +/// platform, all narrow values *are* extended, so these attributes may lead to extra +/// zero/sign-extend instructions in the generated machine code. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum ArgumentExtension { diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs index 90debad44e..19f8b45343 100644 --- a/cranelift/codegen/src/isa/aarch64/abi.rs +++ b/cranelift/codegen/src/isa/aarch64/abi.rs @@ -728,6 +728,19 @@ impl ABIMachineSpec for AArch64MachineDeps { } caller_saved } + + fn get_ext_mode( + call_conv: isa::CallConv, + specified: ir::ArgumentExtension, + ) -> ir::ArgumentExtension { + if call_conv.extends_baldrdash() { + // Baldrdash (SpiderMonkey) always extends args and return values to the full register. + specified + } else { + // No other supported ABI on AArch64 does so. + ir::ArgumentExtension::None + } + } } /// Is this type supposed to be seen on this machine? E.g. references of the diff --git a/cranelift/codegen/src/isa/arm32/abi.rs b/cranelift/codegen/src/isa/arm32/abi.rs index edf1792e52..4b69e1cf43 100644 --- a/cranelift/codegen/src/isa/arm32/abi.rs +++ b/cranelift/codegen/src/isa/arm32/abi.rs @@ -445,6 +445,13 @@ impl ABIMachineSpec for Arm32MachineDeps { } caller_saved } + + fn get_ext_mode( + _call_conv: isa::CallConv, + specified: ir::ArgumentExtension, + ) -> ir::ArgumentExtension { + specified + } } fn is_callee_save(r: RealReg) -> bool { diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs index 425f599c2e..2b9ce29bc8 100644 --- a/cranelift/codegen/src/isa/x64/abi.rs +++ b/cranelift/codegen/src/isa/x64/abi.rs @@ -591,6 +591,19 @@ impl ABIMachineSpec for X64ABIMachineSpec { caller_saved } + + fn get_ext_mode( + call_conv: isa::CallConv, + specified: ir::ArgumentExtension, + ) -> ir::ArgumentExtension { + if call_conv.extends_baldrdash() { + // Baldrdash (SpiderMonkey) always extends args and return values to the full register. + specified + } else { + // No other supported ABI on x64 does so. + ir::ArgumentExtension::None + } + } } impl From for SyntheticAmode { diff --git a/cranelift/codegen/src/isa/x86/abi.rs b/cranelift/codegen/src/isa/x86/abi.rs index 5119bb3241..0117b8918b 100644 --- a/cranelift/codegen/src/isa/x86/abi.rs +++ b/cranelift/codegen/src/isa/x86/abi.rs @@ -144,8 +144,13 @@ impl ArgAssigner for Args { return ValueConversion::VectorSplit.into(); } - // Small integers are extended to the size of a pointer register. - if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) { + // Small integers are extended to the size of a pointer register, but + // only in ABIs that require this. The Baldrdash (SpiderMonkey) ABI + // does, but our other supported ABIs on x86 do not. + if ty.is_int() + && ty.bits() < u16::from(self.pointer_bits) + && self.call_conv.extends_baldrdash() + { match arg.extension { ArgumentExtension::None => {} ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(), diff --git a/cranelift/codegen/src/machinst/abi_impl.rs b/cranelift/codegen/src/machinst/abi_impl.rs index e47379da37..b9774faba9 100644 --- a/cranelift/codegen/src/machinst/abi_impl.rs +++ b/cranelift/codegen/src/machinst/abi_impl.rs @@ -369,6 +369,16 @@ pub trait ABIMachineSpec { /// Get all caller-save registers, that is, registers that we expect /// not to be saved across a call to a callee with the given ABI. fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec>; + + /// Get the needed extension mode, given the mode attached to the argument + /// in the signature and the calling convention. The input (the attribute in + /// the signature) specifies what extension type should be done *if* the ABI + /// requires extension to the full register; this method's return value + /// indicates whether the extension actually *will* be done. + fn get_ext_mode( + call_conv: isa::CallConv, + specified: ir::ArgumentExtension, + ) -> ir::ArgumentExtension; } /// ABI information shared between body (callee) and caller. @@ -770,6 +780,7 @@ impl ABICallee for ABICalleeImpl { &ABIArg::Reg(r, ty, ext, ..) => { let from_bits = ty_bits(ty) as u8; let dest_reg = Writable::from_reg(r.to_reg()); + let ext = M::get_ext_mode(self.sig.call_conv, ext); match (ext, from_bits) { (ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n) if n < word_bits => @@ -793,6 +804,7 @@ impl ABICallee for ABICalleeImpl { // backends (aarch64 and x64) enforce a 128MB limit. let off = i32::try_from(off) .expect("Argument stack offset greater than 2GB; should hit impl limit first"); + let ext = M::get_ext_mode(self.sig.call_conv, ext); // Trash the from_reg; it should be its last use. match (ext, from_bits) { (ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n) @@ -1211,27 +1223,28 @@ impl ABICaller for ABICallerImpl { let word_rc = M::word_reg_class(); let word_bits = M::word_bits() as usize; match &self.sig.args[idx] { - &ABIArg::Reg(reg, ty, ext, _) - if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits => - { - assert_eq!(word_rc, reg.get_class()); - let signed = match ext { - ir::ArgumentExtension::Uext => false, - ir::ArgumentExtension::Sext => true, - _ => unreachable!(), - }; - ctx.emit(M::gen_extend( - Writable::from_reg(reg.to_reg()), - from_reg, - signed, - ty_bits(ty) as u8, - word_bits as u8, - )); - } - &ABIArg::Reg(reg, ty, _, _) => { - ctx.emit(M::gen_move(Writable::from_reg(reg.to_reg()), from_reg, ty)); + &ABIArg::Reg(reg, ty, ext, _) => { + let ext = M::get_ext_mode(self.sig.call_conv, ext); + if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits { + assert_eq!(word_rc, reg.get_class()); + let signed = match ext { + ir::ArgumentExtension::Uext => false, + ir::ArgumentExtension::Sext => true, + _ => unreachable!(), + }; + ctx.emit(M::gen_extend( + Writable::from_reg(reg.to_reg()), + from_reg, + signed, + ty_bits(ty) as u8, + word_bits as u8, + )); + } else { + ctx.emit(M::gen_move(Writable::from_reg(reg.to_reg()), from_reg, ty)); + } } &ABIArg::Stack(off, mut ty, ext, _) => { + let ext = M::get_ext_mode(self.sig.call_conv, ext); if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits { assert_eq!(word_rc, from_reg.get_class()); let signed = match ext { diff --git a/cranelift/filetests/filetests/isa/aarch64/call.clif b/cranelift/filetests/filetests/isa/aarch64/call.clif index 96ec0ff04b..0c8fd48f8f 100644 --- a/cranelift/filetests/filetests/isa/aarch64/call.clif +++ b/cranelift/filetests/filetests/isa/aarch64/call.clif @@ -1,4 +1,5 @@ test compile +set enable_probestack=false target aarch64 function %f1(i64) -> i64 { @@ -18,7 +19,7 @@ block0(v0: i64): ; nextln: ret function %f2(i32) -> i64 { - fn0 = %g(i32 uext) -> i64 + fn0 = %g(i32 uext) -> i64 baldrdash_system_v block0(v0: i32): v1 = call fn0(v0) @@ -27,27 +28,22 @@ block0(v0: i32): ; check: stp fp, lr, [sp, #-16]! ; nextln: mov fp, sp -; nextln: mov w0, w0 +; check: mov w0, w0 ; nextln: ldr x1, 8 ; b 12 ; data ; nextln: blr x1 -; nextln: mov sp, fp +; check: mov sp, fp ; nextln: ldp fp, lr, [sp], #16 ; nextln: ret -function %f3(i32) -> i32 uext { +function %f3(i32) -> i32 uext baldrdash_system_v { block0(v0: i32): return v0 } -; check: stp fp, lr, [sp, #-16]! -; nextln: mov fp, sp -; nextln: mov w0, w0 -; nextln: mov sp, fp -; nextln: ldp fp, lr, [sp], #16 -; nextln: ret +; check: mov w0, w0 function %f4(i32) -> i64 { - fn0 = %g(i32 sext) -> i64 + fn0 = %g(i32 sext) -> i64 baldrdash_system_v block0(v0: i32): v1 = call fn0(v0) @@ -56,24 +52,19 @@ block0(v0: i32): ; check: stp fp, lr, [sp, #-16]! ; nextln: mov fp, sp -; nextln: sxtw x0, w0 +; check: sxtw x0, w0 ; nextln: ldr x1, 8 ; b 12 ; data ; nextln: blr x1 -; nextln: mov sp, fp +; check: mov sp, fp ; nextln: ldp fp, lr, [sp], #16 ; nextln: ret -function %f5(i32) -> i32 sext { +function %f5(i32) -> i32 sext baldrdash_system_v { block0(v0: i32): return v0 } -; check: stp fp, lr, [sp, #-16]! -; nextln: mov fp, sp -; nextln: sxtw x0, w0 -; nextln: mov sp, fp -; nextln: ldp fp, lr, [sp], #16 -; nextln: ret +; check: sxtw x0, w0 function %f6(i8) -> i64 { fn0 = %g(i32, i32, i32, i32, i32, i32, i32, i32, i8 sext) -> i64 @@ -97,8 +88,7 @@ block0(v0: i8): ; nextln: movz x5, #42 ; nextln: movz x6, #42 ; nextln: movz x7, #42 -; nextln: sxtb x8, w8 -; nextln: stur x8, [sp] +; nextln: sturb w8, [sp] ; nextln: ldr x8, 8 ; b 12 ; data ; nextln: blr x8 ; nextln: add sp, sp, #16 @@ -125,8 +115,7 @@ block0(v0: i8): ; nextln: movz x5, #42 ; nextln: movz x6, #42 ; nextln: movz x7, #42 -; nextln: sxtb x9, w9 -; nextln: stur x9, [x8] +; nextln: sturb w9, [x8] ; nextln: mov sp, fp ; nextln: ldp fp, lr, [sp], #16 ; nextln: ret