mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
fully implement sign/zero extension
This commit is contained in:
parent
4e38a4ce32
commit
a3d5e7bee5
3 changed files with 160 additions and 25 deletions
|
@ -459,13 +459,17 @@ impl<
|
||||||
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive {
|
Stack(ReferencedPrimitive {
|
||||||
base_offset, size, ..
|
base_offset,
|
||||||
}) if base_offset % 8 == 0 && *size == 8 => {
|
size,
|
||||||
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
sign_extend,
|
||||||
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
}) => {
|
||||||
}
|
debug_assert!(*size <= 8);
|
||||||
Stack(ReferencedPrimitive { .. }) => {
|
|
||||||
todo!("loading referenced primitives")
|
if *sign_extend {
|
||||||
|
ASM::movsx_reg64_base32(buf, reg, *base_offset, *size as u8)
|
||||||
|
} else {
|
||||||
|
ASM::movzx_reg64_base32(buf, reg, *base_offset, *size as u8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Stack(Complex { size, .. }) => {
|
Stack(Complex { size, .. }) => {
|
||||||
internal_error!(
|
internal_error!(
|
||||||
|
|
|
@ -262,10 +262,10 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
ret_layout: &InLayout<'a>,
|
ret_layout: &InLayout<'a>,
|
||||||
) {
|
) {
|
||||||
let returns_via_pointer =
|
let returns_via_pointer =
|
||||||
X86_64SystemV::returns_via_arg_pointer(layout_interner, &ret_layout);
|
X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout);
|
||||||
|
|
||||||
let mut state = X64_64SystemVLoadArgs {
|
let mut state = X64_64SystemVLoadArgs {
|
||||||
general_i: if returns_via_pointer { 1 } else { 0 },
|
general_i: usize::from(returns_via_pointer),
|
||||||
float_i: 0,
|
float_i: 0,
|
||||||
// 16 is the size of the pushed return address and base pointer.
|
// 16 is the size of the pushed return address and base pointer.
|
||||||
argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16,
|
argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16,
|
||||||
|
@ -613,7 +613,6 @@ impl X64_64SystemVLoadArgs {
|
||||||
self.argument_offset += stack_size as i32;
|
self.argument_offset += stack_size as i32;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
dbg!(other, layout_interner.get(other));
|
|
||||||
todo!("Loading args with layout {:?}", layout_interner.dbg(other));
|
todo!("Loading args with layout {:?}", layout_interner.dbg(other));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1483,7 +1482,9 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
debug_assert!(size <= 8);
|
debug_assert!(size <= 8);
|
||||||
match size {
|
match size {
|
||||||
8 => Self::mov_reg64_base32(buf, dst, offset),
|
8 => Self::mov_reg64_base32(buf, dst, offset),
|
||||||
4 | 2 | 1 => todo!("sign extending {size} byte values"),
|
4 => movsx_reg64_base32_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
||||||
|
2 => movsx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
||||||
|
1 => movsx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
||||||
_ => internal_error!("Invalid size for sign extension: {size}"),
|
_ => internal_error!("Invalid size for sign extension: {size}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1492,7 +1493,12 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
debug_assert!(size <= 8);
|
debug_assert!(size <= 8);
|
||||||
match size {
|
match size {
|
||||||
8 => Self::mov_reg64_base32(buf, dst, offset),
|
8 => Self::mov_reg64_base32(buf, dst, offset),
|
||||||
4 | 2 => todo!("zero extending {size} byte values"),
|
4 => {
|
||||||
|
// The Intel documentation (3.4.1.1 General-Purpose Registers in 64-Bit Mode in manual Basic Architecture))
|
||||||
|
// 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
|
||||||
|
Self::mov_reg64_base32(buf, dst, offset)
|
||||||
|
}
|
||||||
|
2 => movzx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
||||||
1 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
1 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
|
||||||
_ => internal_error!("Invalid size for zero extension: {size}"),
|
_ => internal_error!("Invalid size for zero extension: {size}"),
|
||||||
}
|
}
|
||||||
|
@ -2372,7 +2378,7 @@ fn mov_reg16_base16_offset32(
|
||||||
mov_reg_base_offset32(buf, RegisterWidth::W16, dst, base, offset)
|
mov_reg_base_offset32(buf, RegisterWidth::W16, dst, base, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOV r/m16,r16` -> Move r16 to r/m16.
|
/// `MOV r/m8,r8` -> Move r8 to r/m8.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mov_reg8_base8_offset32(
|
fn mov_reg8_base8_offset32(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -2383,6 +2389,86 @@ fn mov_reg8_base8_offset32(
|
||||||
mov_reg_base_offset32(buf, RegisterWidth::W8, dst, base, offset)
|
mov_reg_base_offset32(buf, RegisterWidth::W8, dst, base, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
opcode: &[u8],
|
||||||
|
) {
|
||||||
|
let rex = add_rm_extension(base, REX_W);
|
||||||
|
let rex = add_reg_extension(dst, rex);
|
||||||
|
let dst_mod = (dst as u8 % 8) << 3;
|
||||||
|
let base_mod = base as u8 % 8;
|
||||||
|
buf.reserve(9);
|
||||||
|
|
||||||
|
// our output is a 64-bit value, so rex is always needed
|
||||||
|
buf.push(rex);
|
||||||
|
buf.extend(opcode);
|
||||||
|
buf.push(0x80 | dst_mod | base_mod);
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MOVSX r64,r/m32` -> Move r/m32 with sign extention to r64, where m32 references a base + offset.
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base32_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
movsx_reg64_base_offset32(buf, dst, base, offset, &[0x63])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MOVSX r64,r/m16` -> Move r/m16 with sign extention to r64, where m16 references a base + offset.
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base16_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
movsx_reg64_base_offset32(buf, dst, base, offset, &[0x0F, 0xBF])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MOVSX r64,r/m8` -> Move r/m8 with sign extention to r64, where m8 references a base + offset.
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base8_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
movsx_reg64_base_offset32(buf, dst, base, offset, &[0x0F, 0xBE])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn movzx_reg64_base_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
opcode: u8,
|
||||||
|
) {
|
||||||
|
let rex = add_rm_extension(base, REX_W);
|
||||||
|
let rex = add_reg_extension(dst, rex);
|
||||||
|
let dst_mod = (dst as u8 % 8) << 3;
|
||||||
|
let base_mod = base as u8 % 8;
|
||||||
|
buf.reserve(9);
|
||||||
|
buf.extend([rex, 0x0F, opcode, 0x80 | dst_mod | base_mod]);
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
/// `MOVZX r64,r/m8` -> Move r/m8 with zero extention to r64, where m8 references a base + offset.
|
/// `MOVZX r64,r/m8` -> Move r/m8 with zero extention to r64, where m8 references a base + offset.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movzx_reg64_base8_offset32(
|
fn movzx_reg64_base8_offset32(
|
||||||
|
@ -2391,17 +2477,18 @@ fn movzx_reg64_base8_offset32(
|
||||||
base: X86_64GeneralReg,
|
base: X86_64GeneralReg,
|
||||||
offset: i32,
|
offset: i32,
|
||||||
) {
|
) {
|
||||||
let rex = add_rm_extension(base, REX_W);
|
movzx_reg64_base_offset32(buf, dst, base, offset, 0xB6)
|
||||||
let rex = add_reg_extension(dst, rex);
|
}
|
||||||
let dst_mod = (dst as u8 % 8) << 3;
|
|
||||||
let base_mod = base as u8 % 8;
|
/// `MOVZX r64,r/m16` -> Move r/m16 with zero extention to r64, where m16 references a base + offset.
|
||||||
buf.reserve(9);
|
#[inline(always)]
|
||||||
buf.extend([rex, 0x0F, 0xB6, 0x80 | dst_mod | base_mod]);
|
fn movzx_reg64_base16_offset32(
|
||||||
// Using RSP or R12 requires a secondary index byte.
|
buf: &mut Vec<'_, u8>,
|
||||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
dst: X86_64GeneralReg,
|
||||||
buf.push(0x24);
|
base: X86_64GeneralReg,
|
||||||
}
|
offset: i32,
|
||||||
buf.extend(offset.to_le_bytes());
|
) {
|
||||||
|
movzx_reg64_base_offset32(buf, dst, base, offset, 0xB7)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register.
|
/// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register.
|
||||||
|
@ -3274,6 +3361,50 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movsx_reg64_base32_offset32() {
|
||||||
|
disassembler_test!(
|
||||||
|
movsx_reg64_base32_offset32,
|
||||||
|
|reg1, reg2, imm| format!("movsxd {}, dword ptr [{} + 0x{:x}]", reg1, reg2, imm),
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
[TEST_I32]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movsx_reg64_base16_offset32() {
|
||||||
|
disassembler_test!(
|
||||||
|
movsx_reg64_base16_offset32,
|
||||||
|
|reg1, reg2, imm| format!("movsx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm),
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
[TEST_I32]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movsx_reg64_base8_offset32() {
|
||||||
|
disassembler_test!(
|
||||||
|
movsx_reg64_base8_offset32,
|
||||||
|
|reg1, reg2, imm| format!("movsx {}, byte ptr [{} + 0x{:x}]", reg1, reg2, imm),
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
[TEST_I32]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movzx_reg64_base16_offset32() {
|
||||||
|
disassembler_test!(
|
||||||
|
movzx_reg64_base16_offset32,
|
||||||
|
|reg1, reg2, imm| format!("movzx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm),
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
ALL_GENERAL_REGS,
|
||||||
|
[TEST_I32]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_movzx_reg64_base8_offset32() {
|
fn test_movzx_reg64_base8_offset32() {
|
||||||
disassembler_test!(
|
disassembler_test!(
|
||||||
|
|
|
@ -1871,7 +1871,7 @@ fn str_append_scalar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||||
fn str_walk_scalars() {
|
fn str_walk_scalars() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue