Skip to content

Commit e9e4647

Browse files
authored
ZJIT: add an unreachable instruction (ruby#16901)
Unreachable instructions terminate blocks. We'll use this mostly for testing as a terminator instruction (since traditional BB's will require all blocks to end with a terminator)
1 parent 12bb895 commit e9e4647

8 files changed

Lines changed: 87 additions & 7 deletions

File tree

zjit/src/asm/arm64/inst/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod sbfm;
2626
mod shift_imm;
2727
mod sys_reg;
2828
mod test_bit;
29+
mod udf;
2930

3031
pub use atomic::Atomic;
3132
pub use branch::Branch;
@@ -52,3 +53,4 @@ pub use sbfm::SBFM;
5253
pub use shift_imm::ShiftImm;
5354
pub use sys_reg::SysReg;
5455
pub use test_bit::TestBit;
56+
pub use udf::Udf;

zjit/src/asm/arm64/inst/udf.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// The struct that represents an A64 permanently undefined instruction.
2+
///
3+
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
4+
/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 |
5+
/// | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
6+
/// | imm16..................................................|
7+
/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
8+
///
9+
pub struct Udf {
10+
/// The immediate value encoded in the instruction
11+
imm16: u16
12+
}
13+
14+
impl Udf {
15+
/// UDF - Permanently Undefined
16+
/// <https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/UDF--Permanently-Undefined->
17+
pub fn udf(imm16: u16) -> Self {
18+
Self { imm16 }
19+
}
20+
}
21+
22+
impl From<Udf> for u32 {
23+
/// Convert an instruction into a 32-bit value.
24+
fn from(inst: Udf) -> Self {
25+
inst.imm16 as u32
26+
}
27+
}
28+
29+
impl From<Udf> for [u8; 4] {
30+
/// Convert an instruction into a 4 byte array.
31+
fn from(inst: Udf) -> [u8; 4] {
32+
let result: u32 = inst.into();
33+
result.to_le_bytes()
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::*;
40+
41+
#[test]
42+
fn test_udf() {
43+
let result: u32 = Udf::udf(0).into();
44+
assert_eq!(0x00000000, result);
45+
}
46+
47+
#[test]
48+
fn test_udf_imm() {
49+
let result: u32 = Udf::udf(1).into();
50+
assert_eq!(0x00000001, result);
51+
}
52+
}

zjit/src/asm/arm64/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ pub fn brk(cb: &mut CodeBlock, imm16: A64Opnd) {
321321
cb.write_bytes(&bytes);
322322
}
323323

324+
/// UDF - permanently undefined instruction
325+
pub fn udf(cb: &mut CodeBlock, imm16: u16) {
326+
let bytes: [u8; 4] = Udf::udf(imm16).into();
327+
cb.write_bytes(&bytes);
328+
}
329+
324330
/// CMP - compare rn and rm, update flags
325331
pub fn cmp(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) {
326332
let bytes: [u8; 4] = match (rn, rm) {

zjit/src/backend/arm64/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,9 @@ impl Assembler {
15611561
Insn::Breakpoint => {
15621562
brk(cb, A64Opnd::None);
15631563
},
1564+
Insn::Abort => {
1565+
udf(cb, u16::MAX);
1566+
},
15641567
Insn::CSelZ { truthy, falsy, out } |
15651568
Insn::CSelE { truthy, falsy, out } => {
15661569
csel(cb, out.into(), truthy.into(), falsy.into(), Condition::EQ);

zjit/src/backend/lir.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,9 @@ pub enum Insn {
653653
#[allow(dead_code)]
654654
Breakpoint,
655655

656+
// Abort the process
657+
Abort,
658+
656659
/// Add a comment into the IR at the point that this instruction is added.
657660
/// It won't have any impact on that actual compiled code.
658661
Comment(String),
@@ -895,6 +898,7 @@ impl Insn {
895898
Insn::And { .. } => "And",
896899
Insn::BakeString(_) => "BakeString",
897900
Insn::Breakpoint => "Breakpoint",
901+
Insn::Abort => "Abort",
898902
Insn::Comment(_) => "Comment",
899903
Insn::Cmp { .. } => "Cmp",
900904
Insn::CPop { .. } => "CPop",
@@ -1185,7 +1189,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
11851189
}
11861190

11871191
Insn::BakeString(_) |
1188-
Insn::Breakpoint |
1192+
Insn::Breakpoint | Insn::Abort |
11891193
Insn::Comment(_) |
11901194
Insn::CPop { .. } |
11911195
Insn::PadPatchPoint |
@@ -1363,7 +1367,7 @@ impl<'a> InsnOpndMutIterator<'a> {
13631367
}
13641368

13651369
Insn::BakeString(_) |
1366-
Insn::Breakpoint |
1370+
Insn::Breakpoint | Insn::Abort |
13671371
Insn::Comment(_) |
13681372
Insn::CPop { .. } |
13691373
Insn::FrameSetup { .. } |
@@ -3465,6 +3469,11 @@ impl Assembler {
34653469
self.push_insn(Insn::Breakpoint);
34663470
}
34673471

3472+
#[allow(dead_code)]
3473+
pub fn abort(&mut self) {
3474+
self.push_insn(Insn::Abort);
3475+
}
3476+
34683477
/// Call a C function without PosMarkers
34693478
pub fn ccall(&mut self, fptr: *const u8, opnds: Vec<Opnd>) -> Opnd {
34703479
let canary_opnd = self.set_stack_canary();

zjit/src/backend/x86_64/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ impl Assembler {
10891089
},
10901090

10911091
Insn::Breakpoint => int3(cb),
1092+
Insn::Abort => ud2(cb),
10921093

10931094
Insn::CSelZ { truthy, falsy, out } => {
10941095
emit_csel(cb, *truthy, *falsy, *out, cmovz, cmovnz);

zjit/src/codegen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
754754
Insn::ObjToString { val, cd, state, .. } => gen_objtostring(jit, asm, opnd!(val), *cd, &function.frame_state(*state)),
755755
&Insn::CheckInterrupts { state } => no_output!(gen_check_interrupts(jit, asm, &function.frame_state(state))),
756756
Insn::BreakPoint => no_output!(asm.breakpoint()),
757+
Insn::Unreachable => no_output!(asm.abort()),
757758
&Insn::HashDup { val, state } => { gen_hash_dup(asm, opnd!(val), &function.frame_state(state)) },
758759
&Insn::HashAref { hash, key, state } => { gen_hash_aref(jit, asm, opnd!(hash), opnd!(key), &function.frame_state(state)) },
759760
&Insn::HashAset { hash, key, val, state } => { no_output!(gen_hash_aset(jit, asm, opnd!(hash), opnd!(key), opnd!(val), &function.frame_state(state))) },

zjit/src/hir.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,11 @@ pub enum Insn {
11471147
CheckInterrupts { state: InsnId },
11481148

11491149
BreakPoint,
1150+
1151+
/// Only use this instruction in tests where you need to end a block with
1152+
/// a terminator, but don't ever expect the code to be executed. This
1153+
/// instruction should never be generated from iseq_to_hir
1154+
Unreachable,
11501155
}
11511156

11521157
/// Macro that enumerates all operands of an Insn, dispatching to caller-provided
@@ -1165,7 +1170,7 @@ macro_rules! for_each_operand_impl {
11651170
| Insn::LoadEC
11661171
| Insn::GetEP { .. }
11671172
| Insn::LoadSelf
1168-
| Insn::BreakPoint
1173+
| Insn::BreakPoint | Insn::Unreachable
11691174
| Insn::PutSpecialObject { .. }
11701175
| Insn::IncrCounter(_)
11711176
| Insn::IncrCounterPtr { .. } => {}
@@ -1471,7 +1476,7 @@ impl Insn {
14711476
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
14721477
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
14731478
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
1474-
| Insn::CheckInterrupts { .. } | Insn::BreakPoint
1479+
| Insn::CheckInterrupts { .. } | Insn::BreakPoint | Insn::Unreachable
14751480
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. }
14761481
| Insn::ArrayAset { .. } => false,
14771482
_ => true,
@@ -1698,7 +1703,7 @@ impl Insn {
16981703
abstract_heaps::Control
16991704
),
17001705
Insn::Entries { .. } => effects::Any,
1701-
Insn::BreakPoint => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control),
1706+
Insn::BreakPoint | Insn::Unreachable => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control),
17021707
}
17031708
}
17041709

@@ -2223,6 +2228,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
22232228
Insn::CheckInterrupts { .. } => write!(f, "CheckInterrupts"),
22242229
Insn::IsA { val, class } => write!(f, "IsA {val}, {class}"),
22252230
Insn::BreakPoint => write!(f, "BreakPoint"),
2231+
Insn::Unreachable => write!(f, "Unreachable"),
22262232
}
22272233
}
22282234
}
@@ -2837,7 +2843,7 @@ impl Function {
28372843
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
28382844
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. }
28392845
| Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
2840-
| Insn::CheckInterrupts { .. } | Insn::BreakPoint
2846+
| Insn::CheckInterrupts { .. } | Insn::BreakPoint | Insn::Unreachable
28412847
| Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } =>
28422848
panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]),
28432849
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
@@ -5810,7 +5816,7 @@ impl Function {
58105816
| Insn::LoadSP
58115817
| Insn::LoadEC
58125818
| Insn::GetEP { .. }
5813-
| Insn::BreakPoint
5819+
| Insn::BreakPoint | Insn::Unreachable
58145820
| Insn::LoadSelf
58155821
| Insn::Snapshot { .. }
58165822
| Insn::Jump { .. }

0 commit comments

Comments
 (0)