Skip to content

Commit e5c7f16

Browse files
committed
YJIT: x86: Fix panic writing 32-bit number with top bit set
Previously, `asm.mov(m32, imm32)` panicked when `imm32 > 0x80000000`. It attempted to split imm32 into a register before doing the store, but then the register size didn't match the destination size. Instead of splitting, use the `MOV r/m32, imm32` form which works for all 32-bit values. Adjust asserts that assumed that all forms undergo sign extension, which is not true for this case. See: 54edc93
1 parent 4ebe0a1 commit e5c7f16

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

yjit/src/asm/x86_64/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,10 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) {
10271027
}
10281028

10291029
let output_num_bits:u32 = if mem.num_bits > 32 { 32 } else { mem.num_bits.into() };
1030-
assert!(imm_num_bits(imm.value) <= (output_num_bits as u8));
1030+
assert!(
1031+
mem.num_bits < 64 || imm_num_bits(imm.value) <= (output_num_bits as u8),
1032+
"immediate value should be small enough to survive sign extension"
1033+
);
10311034
cb.write_int(imm.value as u64, output_num_bits);
10321035
},
10331036
// M + UImm
@@ -1042,7 +1045,10 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) {
10421045
}
10431046

10441047
let output_num_bits = if mem.num_bits > 32 { 32 } else { mem.num_bits.into() };
1045-
assert!(imm_num_bits(uimm.value as i64) <= (output_num_bits as u8));
1048+
assert!(
1049+
mem.num_bits < 64 || imm_num_bits(uimm.value as i64) <= (output_num_bits as u8),
1050+
"immediate value should be small enough to survive sign extension"
1051+
);
10461052
cb.write_int(uimm.value, output_num_bits);
10471053
},
10481054
// * + Imm/UImm

yjit/src/asm/x86_64/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ fn test_mov() {
193193
check_bytes("48c7470801000000", |cb| mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1)));
194194
//check_bytes("67c7400411000000", |cb| mov(cb, mem_opnd(32, EAX, 4), imm_opnd(0x34))); // We don't distinguish between EAX and RAX here - that's probably fine?
195195
check_bytes("c7400411000000", |cb| mov(cb, mem_opnd(32, RAX, 4), imm_opnd(17)));
196+
check_bytes("c7400401000080", |cb| mov(cb, mem_opnd(32, RAX, 4), uimm_opnd(0x80000001)));
196197
check_bytes("41895814", |cb| mov(cb, mem_opnd(32, R8, 20), EBX));
197198
check_bytes("4d8913", |cb| mov(cb, mem_opnd(64, R11, 0), R10));
198199
check_bytes("48c742f8f4ffffff", |cb| mov(cb, mem_opnd(64, RDX, -8), imm_opnd(-12)));

yjit/src/backend/x86_64/mod.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,19 +315,24 @@ impl Assembler
315315
let opnd1 = asm.load(*src);
316316
asm.mov(*dest, opnd1);
317317
},
318-
(Opnd::Mem(_), Opnd::UImm(value)) => {
319-
// 32-bit values will be sign-extended
320-
if imm_num_bits(*value as i64) > 32 {
318+
(Opnd::Mem(Mem { num_bits, .. }), Opnd::UImm(value)) => {
319+
// For 64 bit destinations, 32-bit values will be sign-extended
320+
if *num_bits == 64 && imm_num_bits(*value as i64) > 32 {
321321
let opnd1 = asm.load(*src);
322322
asm.mov(*dest, opnd1);
323323
} else {
324324
asm.mov(*dest, *src);
325325
}
326326
},
327-
(Opnd::Mem(_), Opnd::Imm(value)) => {
328-
if imm_num_bits(*value) > 32 {
327+
(Opnd::Mem(Mem { num_bits, .. }), Opnd::Imm(value)) => {
328+
// For 64 bit destinations, 32-bit values will be sign-extended
329+
if *num_bits == 64 && imm_num_bits(*value) > 32 {
329330
let opnd1 = asm.load(*src);
330331
asm.mov(*dest, opnd1);
332+
} else if uimm_num_bits(*value as u64) <= *num_bits {
333+
// If the bit string is short enough for the destination, use the unsigned representation.
334+
// Note that 64-bit and negative values are ruled out.
335+
asm.mov(*dest, Opnd::UImm(*value as u64));
331336
} else {
332337
asm.mov(*dest, *src);
333338
}
@@ -1317,4 +1322,19 @@ mod tests {
13171322
0x13: mov qword ptr [rbx], rax
13181323
"});
13191324
}
1325+
1326+
#[test]
1327+
fn test_mov_m32_imm32() {
1328+
let (mut asm, mut cb) = setup_asm();
1329+
1330+
let shape_opnd = Opnd::mem(32, C_RET_OPND, 0);
1331+
asm.mov(shape_opnd, Opnd::UImm(0x8000_0001));
1332+
asm.mov(shape_opnd, Opnd::Imm(0x8000_0001));
1333+
1334+
asm.compile_with_num_regs(&mut cb, 0);
1335+
assert_disasm!(cb, "c70001000080c70001000080", {"
1336+
0x0: mov dword ptr [rax], 0x80000001
1337+
0x6: mov dword ptr [rax], 0x80000001
1338+
"});
1339+
}
13201340
}

0 commit comments

Comments
 (0)