Skip to content

Commit 5df8a11

Browse files
committed
x86 pending
1 parent afa65f3 commit 5df8a11

1 file changed

Lines changed: 84 additions & 40 deletions

File tree

zjit/src/backend/x86_64/mod.rs

Lines changed: 84 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -293,38 +293,11 @@ impl Assembler
293293

294294
asm.push_insn(insn);
295295
},
296-
Insn::Mov { dest, src } | Insn::Store { dest, src } => {
297-
match (&dest, &src) {
298-
(Opnd::Mem(_), Opnd::Mem(_)) => {
299-
// We load opnd1 because for mov, opnd0 is the output
300-
let opnd1 = asm.load(*src);
301-
asm.mov(*dest, opnd1);
302-
},
303-
(Opnd::Mem(Mem { num_bits, .. }), Opnd::UImm(value)) => {
304-
// For 64 bit destinations, 32-bit values will be sign-extended
305-
if *num_bits == 64 && imm_num_bits(*value as i64) > 32 {
306-
let opnd1 = asm.load(*src);
307-
asm.mov(*dest, opnd1);
308-
} else {
309-
asm.mov(*dest, *src);
310-
}
311-
},
312-
(Opnd::Mem(Mem { num_bits, .. }), Opnd::Imm(value)) => {
313-
// For 64 bit destinations, 32-bit values will be sign-extended
314-
if *num_bits == 64 && imm_num_bits(*value) > 32 {
315-
let opnd1 = asm.load(*src);
316-
asm.mov(*dest, opnd1);
317-
} else if uimm_num_bits(*value as u64) <= *num_bits {
318-
// If the bit string is short enough for the destination, use the unsigned representation.
319-
// Note that 64-bit and negative values are ruled out.
320-
asm.mov(*dest, Opnd::UImm(*value as u64));
321-
} else {
322-
asm.mov(*dest, *src);
323-
}
324-
},
325-
_ => {
326-
asm.mov(*dest, *src);
327-
}
296+
Insn::Mov { dest, src } => {
297+
if let Opnd::Mem(_) = dest {
298+
asm.store(*dest, *src);
299+
} else {
300+
asm.mov(*dest, *src);
328301
}
329302
},
330303
Insn::Not { opnd, .. } => {
@@ -440,6 +413,14 @@ impl Assembler
440413
}
441414
}
442415

416+
fn emit_load_gc_value(cb: &mut CodeBlock, gc_offsets: &mut Vec<CodePtr>, dest_reg: X86Opnd, value: VALUE) {
417+
// Using movabs because mov might write value in 32 bits
418+
movabs(cb, dest_reg, value.0 as _);
419+
// The pointer immediate is encoded as the last part of the mov written out
420+
let ptr_offset = cb.get_write_ptr().sub_bytes(SIZEOF_VALUE);
421+
gc_offsets.push(ptr_offset);
422+
}
423+
443424
// List of GC offsets
444425
let mut gc_offsets: Vec<CodePtr> = Vec::new();
445426

@@ -552,20 +533,71 @@ impl Assembler
552533
shr(cb, opnd.into(), shift.into())
553534
},
554535

555-
Insn::Store { dest, src } => {
556-
mov(cb, dest.into(), src.into());
557-
},
536+
store_insn @ Insn::Store { dest, src } => {
537+
let &Opnd::Mem(Mem { num_bits, base: MemBase::Reg(base_reg_no), disp: _ }) = dest else {
538+
panic!("Unexpected Insn::Store destination in x64_emit: {dest:?}");
539+
};
540+
541+
// This kind of tricky clobber can only happen for explicit use of SCRATCH_REG,
542+
// so we panic to get the author to change their code.
543+
#[track_caller]
544+
fn assert_no_clobber(store_insn: &Insn, user_use: u8, backend_use: Reg) {
545+
assert_ne!(
546+
backend_use.reg_no,
547+
user_use,
548+
"Emitting {store_insn:?} would clobber {user_use:?}, in conflict with its semantics"
549+
);
550+
}
551+
552+
let scratch = X86Opnd::Reg(Self::SCRATCH_REG);
553+
let src = match src {
554+
Opnd::Reg(_) => src.into(),
555+
&Opnd::Mem(_) => {
556+
assert_no_clobber(store_insn, base_reg_no, Self::SCRATCH_REG);
557+
mov(cb, scratch, src.into());
558+
scratch
559+
}
560+
&Opnd::Imm(imm) => {
561+
// For 64 bit destinations, 32-bit values will be sign-extended
562+
if num_bits == 64 && imm_num_bits(imm) > 32 {
563+
assert_no_clobber(store_insn, base_reg_no, Self::SCRATCH_REG);
564+
mov(cb, scratch, src.into());
565+
scratch
566+
} else if uimm_num_bits(imm as u64) <= num_bits {
567+
// If the bit string is short enough for the destination, use the unsigned representation.
568+
// Note that 64-bit and negative values are ruled out.
569+
uimm_opnd(imm as u64)
570+
} else {
571+
src.into()
572+
}
573+
}
574+
&Opnd::UImm(imm) => {
575+
// For 64 bit destinations, 32-bit values will be sign-extended
576+
if num_bits == 64 && imm_num_bits(imm as i64) > 32 {
577+
assert_no_clobber(store_insn, base_reg_no, Self::SCRATCH_REG);
578+
mov(cb, scratch, src.into());
579+
scratch
580+
} else {
581+
src.into()
582+
}
583+
}
584+
&Opnd::Value(value) => {
585+
assert_no_clobber(store_insn, base_reg_no, Self::SCRATCH_REG);
586+
emit_load_gc_value(cb, &mut gc_offsets, scratch, value);
587+
scratch
588+
}
589+
src @ (Opnd::None | Opnd::VReg { .. }) => panic!("Unexpected source operand during x86_emit: {src:?}")
590+
591+
};
592+
mov(cb, dest.into(), src);
593+
}
558594

559595
// This assumes only load instructions can contain references to GC'd Value operands
560596
Insn::Load { opnd, out } |
561597
Insn::LoadInto { dest: out, opnd } => {
562598
match opnd {
563599
Opnd::Value(val) if val.heap_object_p() => {
564-
// Using movabs because mov might write value in 32 bits
565-
movabs(cb, out.into(), val.0 as _);
566-
// The pointer immediate is encoded as the last part of the mov written out
567-
let ptr_offset = cb.get_write_ptr().sub_bytes(SIZEOF_VALUE);
568-
gc_offsets.push(ptr_offset);
600+
emit_load_gc_value(cb, &mut gc_offsets, out.into(), *val);
569601
}
570602
_ => mov(cb, out.into(), opnd.into())
571603
}
@@ -1352,4 +1384,16 @@ mod tests {
13521384
0x29: pop rbp
13531385
"});
13541386
}
1387+
1388+
#[test]
1389+
fn test_store_value_without_split() {
1390+
let (mut asm, mut cb) = setup_asm();
1391+
1392+
let imitation_heap_value = VALUE(0x1000);
1393+
assert!(imitation_heap_value.heap_object_p());
1394+
asm.store(Opnd::mem(VALUE_BITS, SP, 0), imitation_heap_value.into());
1395+
1396+
let gc_offsets = asm.x86_emit(&mut cb).unwrap();
1397+
assert_eq!(1, gc_offsets.len(), "VALUE source operand should be reported as gc offset");
1398+
}
13551399
}

0 commit comments

Comments
 (0)