Add codegen for NewArray instruction (https://github.com/Shopify/zjit/pull/110)
* Show failing test * Add second test case * Add empty NewArray setup * Update opt_tests and fix NewArray instantiation * Add code generation for NewArray * Add NewArray ordering test
This commit is contained in:
parent
1b95e9c4a0
commit
490a6d8ef9
Notes:
git
2025-04-18 13:47:38 +00:00
@ -187,6 +187,31 @@ class TestZJIT < Test::Unit::TestCase
|
|||||||
}, call_threshold: 2
|
}, call_threshold: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_new_array_empty
|
||||||
|
assert_compiles '[]', %q{
|
||||||
|
def test = []
|
||||||
|
test
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_new_array_nonempty
|
||||||
|
assert_compiles '[5]', %q{
|
||||||
|
def a = 5
|
||||||
|
def test = [a]
|
||||||
|
test
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_new_array_order
|
||||||
|
assert_compiles '[3, 2, 1]', %q{
|
||||||
|
def a = 3
|
||||||
|
def b = 2
|
||||||
|
def c = 1
|
||||||
|
def test = [a, b, c]
|
||||||
|
test
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def test_array_dup
|
def test_array_dup
|
||||||
assert_compiles '[1, 2, 3]', %q{
|
assert_compiles '[1, 2, 3]', %q{
|
||||||
def test = [1,2,3]
|
def test = [1,2,3]
|
||||||
|
@ -243,6 +243,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
|||||||
let out_opnd = match insn {
|
let out_opnd = match insn {
|
||||||
Insn::PutSelf => gen_putself(),
|
Insn::PutSelf => gen_putself(),
|
||||||
Insn::Const { val: Const::Value(val) } => gen_const(*val),
|
Insn::Const { val: Const::Value(val) } => gen_const(*val),
|
||||||
|
Insn::NewArray { elements, state } => gen_new_array(jit, asm, elements, &function.frame_state(*state)),
|
||||||
Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)),
|
Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)),
|
||||||
Insn::Param { idx } => unreachable!("block.insns should not have Insn::Param({idx})"),
|
Insn::Param { idx } => unreachable!("block.insns should not have Insn::Param({idx})"),
|
||||||
Insn::Snapshot { .. } => return Some(()), // we don't need to do anything for this instruction at the moment
|
Insn::Snapshot { .. } => return Some(()), // we don't need to do anything for this instruction at the moment
|
||||||
@ -509,6 +510,37 @@ fn gen_array_dup(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile a new array instruction
|
||||||
|
fn gen_new_array(
|
||||||
|
jit: &mut JITState,
|
||||||
|
asm: &mut Assembler,
|
||||||
|
elements: &Vec<InsnId>,
|
||||||
|
state: &FrameState,
|
||||||
|
) -> lir::Opnd {
|
||||||
|
asm_comment!(asm, "call rb_ary_new");
|
||||||
|
|
||||||
|
// Save PC
|
||||||
|
gen_save_pc(asm, state);
|
||||||
|
|
||||||
|
let length: ::std::os::raw::c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long");
|
||||||
|
|
||||||
|
let new_array = asm.ccall(
|
||||||
|
rb_ary_new_capa as *const u8,
|
||||||
|
vec![lir::Opnd::Imm(length)],
|
||||||
|
);
|
||||||
|
|
||||||
|
for i in 0..elements.len() {
|
||||||
|
let insn_id = elements.get(i as usize).expect("Element should exist at index");
|
||||||
|
let val = jit.get_opnd(*insn_id).unwrap();
|
||||||
|
asm.ccall(
|
||||||
|
rb_ary_push as *const u8,
|
||||||
|
vec![new_array, val]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_array
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile code that exits from JIT code with a return value
|
/// Compile code that exits from JIT code with a return value
|
||||||
fn gen_return(asm: &mut Assembler, val: lir::Opnd) -> Option<()> {
|
fn gen_return(asm: &mut Assembler, val: lir::Opnd) -> Option<()> {
|
||||||
// Pop the current frame (ec->cfp++)
|
// Pop the current frame (ec->cfp++)
|
||||||
|
@ -298,7 +298,7 @@ pub enum Insn {
|
|||||||
StringCopy { val: InsnId },
|
StringCopy { val: InsnId },
|
||||||
StringIntern { val: InsnId },
|
StringIntern { val: InsnId },
|
||||||
|
|
||||||
NewArray { elements: Vec<InsnId> },
|
NewArray { elements: Vec<InsnId>, state: InsnId },
|
||||||
ArraySet { array: InsnId, idx: usize, val: InsnId },
|
ArraySet { array: InsnId, idx: usize, val: InsnId },
|
||||||
ArrayDup { val: InsnId, state: InsnId },
|
ArrayDup { val: InsnId, state: InsnId },
|
||||||
|
|
||||||
@ -423,7 +423,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
|||||||
match &self.inner {
|
match &self.inner {
|
||||||
Insn::Const { val } => { write!(f, "Const {}", val.print(self.ptr_map)) }
|
Insn::Const { val } => { write!(f, "Const {}", val.print(self.ptr_map)) }
|
||||||
Insn::Param { idx } => { write!(f, "Param {idx}") }
|
Insn::Param { idx } => { write!(f, "Param {idx}") }
|
||||||
Insn::NewArray { elements } => {
|
Insn::NewArray { elements, .. } => {
|
||||||
write!(f, "NewArray")?;
|
write!(f, "NewArray")?;
|
||||||
let mut prefix = " ";
|
let mut prefix = " ";
|
||||||
for element in elements {
|
for element in elements {
|
||||||
@ -1633,12 +1633,13 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
}
|
}
|
||||||
YARVINSN_newarray => {
|
YARVINSN_newarray => {
|
||||||
let count = get_arg(pc, 0).as_usize();
|
let count = get_arg(pc, 0).as_usize();
|
||||||
|
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
|
||||||
let mut elements = vec![];
|
let mut elements = vec![];
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
elements.push(state.stack_pop()?);
|
elements.push(state.stack_pop()?);
|
||||||
}
|
}
|
||||||
elements.reverse();
|
elements.reverse();
|
||||||
state.stack_push(fun.push_insn(block, Insn::NewArray { elements }));
|
state.stack_push(fun.push_insn(block, Insn::NewArray { elements, state: exit_id }));
|
||||||
}
|
}
|
||||||
YARVINSN_duparray => {
|
YARVINSN_duparray => {
|
||||||
let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
|
let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
|
||||||
@ -2046,15 +2047,16 @@ mod infer_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn newarray() {
|
fn newarray() {
|
||||||
let mut function = Function::new(std::ptr::null());
|
let mut function = Function::new(std::ptr::null());
|
||||||
let val = function.push_insn(function.entry_block, Insn::NewArray { elements: vec![] });
|
// Fake FrameState index of 0usize
|
||||||
|
let val = function.push_insn(function.entry_block, Insn::NewArray { elements: vec![], state: InsnId(0usize) });
|
||||||
assert_bit_equal(function.infer_type(val), types::ArrayExact);
|
assert_bit_equal(function.infer_type(val), types::ArrayExact);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn arraydup() {
|
fn arraydup() {
|
||||||
let mut function = Function::new(std::ptr::null());
|
let mut function = Function::new(std::ptr::null());
|
||||||
let arr = function.push_insn(function.entry_block, Insn::NewArray { elements: vec![] });
|
|
||||||
// Fake FrameState index of 0usize
|
// Fake FrameState index of 0usize
|
||||||
|
let arr = function.push_insn(function.entry_block, Insn::NewArray { elements: vec![], state: InsnId(0usize) });
|
||||||
let val = function.push_insn(function.entry_block, Insn::ArrayDup { val: arr, state: InsnId(0usize) });
|
let val = function.push_insn(function.entry_block, Insn::ArrayDup { val: arr, state: InsnId(0usize) });
|
||||||
assert_bit_equal(function.infer_type(val), types::ArrayExact);
|
assert_bit_equal(function.infer_type(val), types::ArrayExact);
|
||||||
}
|
}
|
||||||
@ -2168,8 +2170,8 @@ mod tests {
|
|||||||
assert_method_hir("test", expect![[r#"
|
assert_method_hir("test", expect![[r#"
|
||||||
fn test:
|
fn test:
|
||||||
bb0():
|
bb0():
|
||||||
v1:ArrayExact = NewArray
|
v2:ArrayExact = NewArray
|
||||||
Return v1
|
Return v2
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2179,8 +2181,8 @@ mod tests {
|
|||||||
assert_method_hir("test", expect![[r#"
|
assert_method_hir("test", expect![[r#"
|
||||||
fn test:
|
fn test:
|
||||||
bb0(v0:BasicObject):
|
bb0(v0:BasicObject):
|
||||||
v2:ArrayExact = NewArray v0
|
v3:ArrayExact = NewArray v0
|
||||||
Return v2
|
Return v3
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2190,8 +2192,8 @@ mod tests {
|
|||||||
assert_method_hir("test", expect![[r#"
|
assert_method_hir("test", expect![[r#"
|
||||||
fn test:
|
fn test:
|
||||||
bb0(v0:BasicObject, v1:BasicObject):
|
bb0(v0:BasicObject, v1:BasicObject):
|
||||||
v3:ArrayExact = NewArray v0, v1
|
v4:ArrayExact = NewArray v0, v1
|
||||||
Return v3
|
Return v4
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2989,8 +2991,8 @@ mod opt_tests {
|
|||||||
assert_optimized_method_hir("test", expect![[r#"
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
fn test:
|
fn test:
|
||||||
bb0():
|
bb0():
|
||||||
v3:Fixnum[5] = Const Value(5)
|
v4:Fixnum[5] = Const Value(5)
|
||||||
Return v3
|
Return v4
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3006,8 +3008,8 @@ mod opt_tests {
|
|||||||
assert_optimized_method_hir("test", expect![[r#"
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
fn test:
|
fn test:
|
||||||
bb0(v0:BasicObject):
|
bb0(v0:BasicObject):
|
||||||
v4:Fixnum[5] = Const Value(5)
|
v5:Fixnum[5] = Const Value(5)
|
||||||
Return v4
|
Return v5
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user