summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/re.h1
-rw-r--r--re.c8
-rw-r--r--test/ruby/test_box.rb36
-rw-r--r--vm_dump.c3
-rw-r--r--vm_method.c46
-rw-r--r--zjit/src/asm/x86_64/mod.rs3
-rw-r--r--zjit/src/backend/arm64/mod.rs19
-rw-r--r--zjit/src/backend/lir.rs45
-rw-r--r--zjit/src/backend/x86_64/mod.rs28
9 files changed, 131 insertions, 58 deletions
diff --git a/internal/re.h b/internal/re.h
index 3ad364a1a6..6c0aee6d06 100644
--- a/internal/re.h
+++ b/internal/re.h
@@ -68,7 +68,6 @@ VALUE rb_reg_equal(VALUE re1, VALUE re2);
VALUE rb_backref_set_string(VALUE string, long pos, long len);
void rb_match_unbusy(VALUE);
int rb_match_count(VALUE match);
-VALUE rb_reg_new_ary(VALUE ary, int options);
VALUE rb_reg_new_from_values(long cnt, const VALUE *elements, int opt);
VALUE rb_reg_last_defined(VALUE match);
diff --git a/re.c b/re.c
index ec337cd21c..63e2db81ac 100644
--- a/re.c
+++ b/re.c
@@ -3523,16 +3523,10 @@ rb_reg_init_str_enc(VALUE re, VALUE s, rb_encoding *enc, int options)
}
VALUE
-rb_reg_new_ary(VALUE ary, int opt)
-{
- return rb_reg_new_str(rb_reg_preprocess_dregexp(ary, opt), opt);
-}
-
-VALUE
rb_reg_new_from_values(long cnt, const VALUE *elements, int opt)
{
const VALUE ary = rb_ary_tmp_new_from_values(0, cnt, elements);
- VALUE val = rb_reg_new_ary(ary, (int)opt);
+ VALUE val = rb_reg_new_str(rb_reg_preprocess_dregexp(ary, opt), opt);
rb_ary_clear(ary);
return val;
}
diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb
index a39979109f..a425c5eb7d 100644
--- a/test/ruby/test_box.rb
+++ b/test/ruby/test_box.rb
@@ -1180,4 +1180,40 @@ class TestBox < Test::Unit::TestCase
assert_equal :box2, box2.eval("Class.new { include Math }.new.box2_test")
end;
end
+
+ def test_method_invalidation_between_boxes_1
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ b = Ruby::Box.new
+ b.eval(<<~'RUBY')
+ Module.prepend(Module.new)
+ class C; end
+ class D < C; end
+ def C.===(x) = true
+ RUBY
+
+ assert String === "x"
+ assert b # to prevent GCing b
+ end;
+ end
+
+ def test_method_invalidation_between_boxes_2
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ PrepM = Module.new
+ Module.prepend(PrepM)
+ Module.new.include?(Module.new)
+
+ b = Ruby::Box.new
+ b.eval(<<~'RUBY')
+ Module.class_eval { def _test_method; end }
+
+ class C; end
+ class D < C; end
+ def C.include?(x) = true
+ RUBY
+
+ Module.new.include?(Module.new)
+ end;
+ end
end
diff --git a/vm_dump.c b/vm_dump.c
index 4a7b0df5c6..00b03ff58d 100644
--- a/vm_dump.c
+++ b/vm_dump.c
@@ -90,6 +90,9 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
break;
case VM_FRAME_MAGIC_CFUNC:
magic = "CFUNC";
+ if (me) {
+ box = me->def->box;
+ }
break;
case VM_FRAME_MAGIC_IFUNC:
magic = "IFUNC";
diff --git a/vm_method.c b/vm_method.c
index 5a99f684c0..fb34426bf2 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -404,6 +404,30 @@ invalidate_callable_method_entry_in_every_m_table_i(rb_classext_t *ext, bool is_
}
}
+struct collect_per_box_origins_arg {
+ VALUE owner;
+ VALUE klass_housing_cme;
+ VALUE origins; // Array of origins
+};
+
+static void
+collect_per_box_origins_i(rb_classext_t *ext, bool is_prime, VALUE box_value, void *data)
+{
+ struct collect_per_box_origins_arg *arg = (struct collect_per_box_origins_arg *)data;
+ VALUE origin = RCLASSEXT_ORIGIN(ext);
+
+ if (origin == arg->owner || origin == arg->klass_housing_cme) {
+ return;
+ }
+ long len = RARRAY_LEN(arg->origins);
+ for (long i = 0; i < len; i++) {
+ if (RARRAY_AREF(arg->origins, i) == origin) {
+ return;
+ }
+ }
+ rb_ary_push(arg->origins, origin);
+}
+
static void
invalidate_callable_method_entry_in_every_m_table(VALUE klass, ID mid, const rb_callable_method_entry_t *cme)
{
@@ -487,6 +511,28 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
// replace the cme that will be invalid in the all classexts
invalidate_callable_method_entry_in_every_m_table(klass_housing_cme, mid, cme);
+ // owner may be a boxable class with per-box classext copies of its m_tbl
+ // (klass_housing_cme may be a non-boxable origin ICLASS that doesn't cover them)
+ if (klass_housing_cme != owner) {
+ invalidate_callable_method_entry_in_every_m_table(owner, mid, cme);
+ }
+ // Also update per-box origin ICLASSes. When ensure_origin is called in
+ // one box's context, it creates a per-box origin ICLASS whose m_tbl is
+ // a copy of owner's m_tbl at that time. The current execution box may
+ // not see these origins via RCLASS_ORIGIN(owner), so we find them by
+ // iterating all of owner's classexts and checking their origin_ fields.
+ {
+ VALUE origins = rb_ary_new();
+ struct collect_per_box_origins_arg origins_arg = {
+ .owner = owner,
+ .klass_housing_cme = klass_housing_cme,
+ .origins = origins,
+ };
+ rb_class_classext_foreach(owner, collect_per_box_origins_i, &origins_arg);
+ for (long i = 0; i < RARRAY_LEN(origins); i++) {
+ invalidate_callable_method_entry_in_every_m_table(RARRAY_AREF(origins, i), mid, cme);
+ }
+ }
}
vm_cme_invalidate((rb_callable_method_entry_t *)cme);
diff --git a/zjit/src/asm/x86_64/mod.rs b/zjit/src/asm/x86_64/mod.rs
index 628a83b99c..c2733e783f 100644
--- a/zjit/src/asm/x86_64/mod.rs
+++ b/zjit/src/asm/x86_64/mod.rs
@@ -1145,6 +1145,9 @@ pub fn push(cb: &mut CodeBlock, opnd: X86Opnd) {
X86Opnd::Mem(_mem) => {
write_rm(cb, false, false, X86Opnd::None, opnd, Some(6), &[0xff]);
},
+ X86Opnd::Imm(X86Imm { value: 0, .. }) | X86Opnd::UImm(X86UImm { value: 0, .. }) => {
+ cb.write_bytes(&[0x6a, 0x00]);
+ }
_ => unreachable!()
}
}
diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs
index 867b829ecc..abe439cb12 100644
--- a/zjit/src/backend/arm64/mod.rs
+++ b/zjit/src/backend/arm64/mod.rs
@@ -1407,8 +1407,10 @@ impl Assembler {
emit_push(cb, opnd.into());
},
Insn::CPushPair(opnd0, opnd1) => {
+ let first_push = if let Opnd::UImm(0) | Opnd::Imm(0) = opnd0 { X31 } else { opnd0.into() };
+ let second_push = if let Opnd::UImm(0) | Opnd::Imm(0) = opnd1 { X31 } else { opnd1.into() };
// Second operand ends up at the lower stack address
- stp_pre(cb, opnd1.into(), opnd0.into(), A64Opnd::new_mem(64, C_SP_REG, -C_SP_STEP));
+ stp_pre(cb, second_push, first_push, A64Opnd::new_mem(64, C_SP_REG, -C_SP_STEP));
},
Insn::CPop { out } => {
emit_pop(cb, out.into());
@@ -1417,8 +1419,15 @@ impl Assembler {
emit_pop(cb, opnd.into());
},
Insn::CPopPairInto(opnd0, opnd1) => {
+ let mut first_pop = opnd0.into();
+ let second_pop = opnd1.into();
+ // Avoid illegal load pair into the same register
+ // by sinking the first pop to the zero register.
+ if first_pop == second_pop {
+ first_pop = X31;
+ }
// First operand is popped from the lower stack address
- ldp_post(cb, opnd0.into(), opnd1.into(), A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP));
+ ldp_post(cb, first_pop, second_pop, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP));
},
Insn::CCall { fptr, .. } => {
match fptr {
@@ -2792,17 +2801,17 @@ mod tests {
0x10: mov x4, #5
0x14: stp x1, x0, [sp, #-0x10]!
0x18: stp x3, x2, [sp, #-0x10]!
- 0x1c: str x4, [sp, #-0x10]!
+ 0x1c: stp xzr, x4, [sp, #-0x10]!
0x20: mov x16, #0
0x24: blr x16
- 0x28: ldr x4, [sp], #0x10
+ 0x28: ldp xzr, x4, [sp], #0x10
0x2c: ldp x3, x2, [sp], #0x10
0x30: ldp x1, x0, [sp], #0x10
0x34: adds x0, x0, x1
0x38: adds x0, x2, x3
0x3c: adds x0, x2, x4
");
- assert_snapshot!(cb.hexdump(), @"200080d2410080d2620080d2830080d2a40080d2e103bfa9e30bbfa9e40f1ff8100080d200023fd6e40741f8e30bc1a8e103c1a8000001ab400003ab400004ab");
+ assert_snapshot!(cb.hexdump(), @"200080d2410080d2620080d2830080d2a40080d2e103bfa9e30bbfa9ff13bfa9100080d200023fd6ff13c1a8e30bc1a8e103c1a8000001ab400003ab400004ab");
}
#[test]
diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs
index a8d03ad69a..0ef79b215e 100644
--- a/zjit/src/backend/lir.rs
+++ b/zjit/src/backend/lir.rs
@@ -2384,27 +2384,16 @@ impl Assembler
_ => unreachable!(),
})
.collect();
- let survivor_push_groups: Vec<Vec<Opnd>> = survivor_regs
- .chunks(2)
- .map(|group| group.to_vec())
- .collect();
// Push all survivors on the stack, pairing adjacent pushes when possible.
- let needs_alignment = cfg!(target_arch = "x86_64") && survivors.len() % 2 == 1;
- for group in &survivor_push_groups {
- match group.as_slice() {
- [left, right] => new_insns.push(Insn::CPushPair(*left, *right)),
- [reg] => new_insns.push(Insn::CPush(*reg)),
+ for group in survivor_regs.chunks(2) {
+ match group {
+ &[left, right] => new_insns.push(Insn::CPushPair(left, right)),
+ &[reg] => new_insns.push(Insn::CPushPair(reg, 0.into())),
_ => unreachable!(),
}
new_ids.push(None);
}
- // Maintain 16-byte stack alignment for x86_64
- if needs_alignment {
- new_insns.push(Insn::CPush(Opnd::Reg(ALLOC_REGS[0])));
- new_ids.push(None);
- }
-
// Extract arguments from CCall, clear opnds
assert!(opnds.len() <= regs.len());
@@ -2472,17 +2461,11 @@ impl Assembler
new_ids.push(None);
}
- // Pop alignment padding (if needed)
- if needs_alignment {
- new_insns.push(Insn::CPopInto(Opnd::Reg(ALLOC_REGS[0])));
- new_ids.push(None);
- }
-
// Restore all survivors in reverse stack order, pairing adjacent pops when possible.
- for group in survivor_push_groups.iter().rev() {
- match group.as_slice() {
- [left, right] => new_insns.push(Insn::CPopPairInto(*right, *left)),
- [reg] => new_insns.push(Insn::CPopInto(*reg)),
+ for group in survivor_regs.chunks(2).rev() {
+ match group {
+ &[reg] => new_insns.push(Insn::CPopPairInto(reg, reg)),
+ &[left, right] => new_insns.push(Insn::CPopPairInto(right, left)),
_ => unreachable!(),
}
new_ids.push(None);
@@ -4673,8 +4656,8 @@ mod tests {
let insns = &asm.basic_blocks[b1.0].insns;
// Find CPush and CPopInto - they should be balanced.
- let pushes: Vec<_> = insns.iter().filter(|i| matches!(i, Insn::CPush(_))).collect();
- let pops: Vec<_> = insns.iter().filter(|i| matches!(i, Insn::CPopInto(_))).collect();
+ let pushes: Vec<_> = insns.iter().filter(|i| matches!(i, Insn::CPushPair(..))).collect();
+ let pops: Vec<_> = insns.iter().filter(|i| matches!(i, Insn::CPopPairInto(..))).collect();
assert_eq!(pushes.len(), pops.len(), "CPush/CPopInto should be balanced");
assert!(!pushes.is_empty(), "Expected at least one saved register across CCall");
@@ -4684,10 +4667,10 @@ mod tests {
Allocation::Fixed(reg) => Opnd::Reg(reg),
_ => unreachable!(),
};
- let pushed_v1 = pushes.iter().any(|insn| matches!(insn, Insn::CPush(opnd) if *opnd == v1_reg));
- let popped_v1 = pops.iter().any(|insn| matches!(insn, Insn::CPopInto(opnd) if *opnd == v1_reg));
- assert!(pushed_v1, "CPush should save v1's register");
- assert!(popped_v1, "CPopInto should restore v1's register");
+ let pushed_v1 = pushes.iter().any(|insn| matches!(**insn, Insn::CPushPair(first, second) if first == v1_reg || second == v1_reg));
+ let popped_v1 = pops.iter().any(|insn| matches!(**insn, Insn::CPopPairInto(first, second) if first == v1_reg || second == v1_reg));
+ assert!(pushed_v1, "CPushPair should save v1's register");
+ assert!(popped_v1, "CPopPairInto should restore v1's register");
// The CCall should have empty opnds and out = C_RET_OPND (rewritten to regs[0])
let ccall = insns.iter().find(|i| matches!(i, Insn::CCall { .. })).unwrap();
diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs
index d8d930dfce..80abd15a6b 100644
--- a/zjit/src/backend/x86_64/mod.rs
+++ b/zjit/src/backend/x86_64/mod.rs
@@ -1985,22 +1985,22 @@ mod tests {
0x1c: push rdx
0x1d: push rcx
0x1e: push r8
- 0x20: push rdi
- 0x21: mov eax, 0
- 0x26: call rax
- 0x28: pop rdi
+ 0x20: push 0
+ 0x22: mov eax, 0
+ 0x27: call rax
0x29: pop r8
- 0x2b: pop rcx
- 0x2c: pop rdx
- 0x2d: pop rsi
- 0x2e: pop rdi
- 0x2f: add rdi, rsi
- 0x32: mov rdi, rdx
- 0x35: add rdi, rcx
- 0x38: mov rdi, rdx
- 0x3b: add rdi, r8
+ 0x2b: pop r8
+ 0x2d: pop rcx
+ 0x2e: pop rdx
+ 0x2f: pop rsi
+ 0x30: pop rdi
+ 0x31: add rdi, rsi
+ 0x34: mov rdi, rdx
+ 0x37: add rdi, rcx
+ 0x3a: mov rdi, rdx
+ 0x3d: add rdi, r8
");
- assert_snapshot!(cb.hexdump(), @"bf01000000be02000000ba03000000b90400000041b80500000057565251415057b800000000ffd05f4158595a5e5f4801f74889d74801cf4889d74c01c7");
+ assert_snapshot!(cb.hexdump(), @"bf01000000be02000000ba03000000b90400000041b8050000005756525141506a00b800000000ffd041584158595a5e5f4801f74889d74801cf4889d74c01c7");
}
#[test]