mirror of
https://github.com/ruby/ruby.git
synced 2025-09-15 08:33:58 +02:00
ZJIT: Replace GetConstantPath with Const if the IC is not empty (#13183)
* Add rb_zjit_constcache_shareable * Add rb_zjit_multi_ractor_p * Replace GetConstantPath with Const if the IC is not empty
This commit is contained in:
parent
1e416685fd
commit
6052b12de4
Notes:
git
2025-04-28 19:10:41 +00:00
Merged-By: k0kubun <takashikkbn@gmail.com>
4 changed files with 126 additions and 2 deletions
12
zjit.c
12
zjit.c
|
@ -525,6 +525,12 @@ rb_BASIC_OP_UNREDEFINED_P(enum ruby_basic_operators bop, uint32_t klass)
|
||||||
return BASIC_OP_UNREDEFINED_P(bop, klass);
|
return BASIC_OP_UNREDEFINED_P(bop, klass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rb_zjit_multi_ractor_p(void)
|
||||||
|
{
|
||||||
|
return rb_multi_ractor_p();
|
||||||
|
}
|
||||||
|
|
||||||
// For debug builds
|
// For debug builds
|
||||||
void
|
void
|
||||||
rb_assert_iseq_handle(VALUE handle)
|
rb_assert_iseq_handle(VALUE handle)
|
||||||
|
@ -532,6 +538,12 @@ rb_assert_iseq_handle(VALUE handle)
|
||||||
RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq));
|
RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rb_zjit_constcache_shareable(const struct iseq_inline_constant_cache_entry *ice)
|
||||||
|
{
|
||||||
|
return (ice->flags & IMEMO_CONST_CACHE_SHAREABLE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_assert_cme_handle(VALUE handle)
|
rb_assert_cme_handle(VALUE handle)
|
||||||
{
|
{
|
||||||
|
|
|
@ -409,6 +409,8 @@ fn main() {
|
||||||
.allowlist_function("rb_get_cfp_ep")
|
.allowlist_function("rb_get_cfp_ep")
|
||||||
.allowlist_function("rb_get_cfp_ep_level")
|
.allowlist_function("rb_get_cfp_ep_level")
|
||||||
.allowlist_function("rb_get_cme_def_type")
|
.allowlist_function("rb_get_cme_def_type")
|
||||||
|
.allowlist_function("rb_zjit_multi_ractor_p")
|
||||||
|
.allowlist_function("rb_zjit_constcache_shareable")
|
||||||
.allowlist_function("rb_get_cme_def_body_attr_id")
|
.allowlist_function("rb_get_cme_def_body_attr_id")
|
||||||
.allowlist_function("rb_get_symbol_id")
|
.allowlist_function("rb_get_symbol_id")
|
||||||
.allowlist_function("rb_get_cme_def_body_optimized_type")
|
.allowlist_function("rb_get_cme_def_body_optimized_type")
|
||||||
|
|
2
zjit/src/cruby_bindings.inc.rs
generated
2
zjit/src/cruby_bindings.inc.rs
generated
|
@ -995,7 +995,9 @@ unsafe extern "C" {
|
||||||
pub fn rb_RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool;
|
pub fn rb_RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool;
|
||||||
pub fn rb_RSTRUCT_LEN(st: VALUE) -> ::std::os::raw::c_long;
|
pub fn rb_RSTRUCT_LEN(st: VALUE) -> ::std::os::raw::c_long;
|
||||||
pub fn rb_BASIC_OP_UNREDEFINED_P(bop: ruby_basic_operators, klass: u32) -> bool;
|
pub fn rb_BASIC_OP_UNREDEFINED_P(bop: ruby_basic_operators, klass: u32) -> bool;
|
||||||
|
pub fn rb_zjit_multi_ractor_p() -> bool;
|
||||||
pub fn rb_assert_iseq_handle(handle: VALUE);
|
pub fn rb_assert_iseq_handle(handle: VALUE);
|
||||||
|
pub fn rb_zjit_constcache_shareable(ice: *const iseq_inline_constant_cache_entry) -> bool;
|
||||||
pub fn rb_assert_cme_handle(handle: VALUE);
|
pub fn rb_assert_cme_handle(handle: VALUE);
|
||||||
pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int;
|
pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int;
|
||||||
pub fn rb_zjit_vm_unlock(
|
pub fn rb_zjit_vm_unlock(
|
||||||
|
|
112
zjit/src/hir.rs
112
zjit/src/hir.rs
|
@ -113,6 +113,13 @@ pub enum Invariant {
|
||||||
/// The method ID of the method we want to assume unchanged
|
/// The method ID of the method we want to assume unchanged
|
||||||
method: ID,
|
method: ID,
|
||||||
},
|
},
|
||||||
|
/// A list of constant expression path segments that must have not been written to for the
|
||||||
|
/// following code to be valid.
|
||||||
|
StableConstantNames {
|
||||||
|
idlist: *const ID,
|
||||||
|
},
|
||||||
|
/// There is one ractor running. If a non-root ractor gets spawned, this is invalidated.
|
||||||
|
SingleRactorMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Invariant {
|
impl Invariant {
|
||||||
|
@ -161,6 +168,22 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
|
||||||
self.ptr_map.map_id(method.0)
|
self.ptr_map.map_id(method.0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Invariant::StableConstantNames { idlist } => {
|
||||||
|
write!(f, "StableConstantNames({:p}, ", self.ptr_map.map_ptr(idlist))?;
|
||||||
|
let mut idx = 0;
|
||||||
|
let mut sep = "";
|
||||||
|
loop {
|
||||||
|
let id = unsafe { *idlist.wrapping_add(idx) };
|
||||||
|
if id.0 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write!(f, "{sep}{}", id.contents_lossy())?;
|
||||||
|
sep = "::";
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
Invariant::SingleRactorMode => write!(f, "SingleRactorMode"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +315,7 @@ pub enum Insn {
|
||||||
// with IfTrue/IfFalse in the backend to generate jcc.
|
// with IfTrue/IfFalse in the backend to generate jcc.
|
||||||
Test { val: InsnId },
|
Test { val: InsnId },
|
||||||
Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId },
|
Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId },
|
||||||
GetConstantPath { ic: *const u8 },
|
GetConstantPath { ic: *const iseq_inline_constant_cache },
|
||||||
|
|
||||||
//NewObject?
|
//NewObject?
|
||||||
//SetIvar {},
|
//SetIvar {},
|
||||||
|
@ -1013,6 +1036,25 @@ impl Function {
|
||||||
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, iseq, args, state });
|
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, iseq, args, state });
|
||||||
self.make_equal_to(insn_id, send_direct);
|
self.make_equal_to(insn_id, send_direct);
|
||||||
}
|
}
|
||||||
|
Insn::GetConstantPath { ic } => {
|
||||||
|
let idlist: *const ID = unsafe { (*ic).segments };
|
||||||
|
let ice = unsafe { (*ic).entry };
|
||||||
|
if ice.is_null() {
|
||||||
|
self.push_insn_id(block, insn_id); continue;
|
||||||
|
}
|
||||||
|
let cref_sensitive = !unsafe { (*ice).ic_cref }.is_null();
|
||||||
|
let multi_ractor_mode = unsafe { rb_zjit_multi_ractor_p() };
|
||||||
|
if cref_sensitive || multi_ractor_mode {
|
||||||
|
self.push_insn_id(block, insn_id); continue;
|
||||||
|
}
|
||||||
|
// Assume single-ractor mode.
|
||||||
|
self.push_insn(block, Insn::PatchPoint(Invariant::SingleRactorMode));
|
||||||
|
// Invalidate output code on any constant writes associated with constants
|
||||||
|
// referenced after the PatchPoint.
|
||||||
|
self.push_insn(block, Insn::PatchPoint(Invariant::StableConstantNames { idlist }));
|
||||||
|
let replacement = self.push_insn(block, Insn::Const { val: Const::Value(unsafe { (*ice).value }) });
|
||||||
|
self.make_equal_to(insn_id, replacement);
|
||||||
|
}
|
||||||
_ => { self.push_insn_id(block, insn_id); }
|
_ => { self.push_insn_id(block, insn_id); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1714,7 +1756,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||||
state.stack_push(fun.push_insn(block, Insn::Defined { op_type, obj, pushval, v }));
|
state.stack_push(fun.push_insn(block, Insn::Defined { op_type, obj, pushval, v }));
|
||||||
}
|
}
|
||||||
YARVINSN_opt_getconstant_path => {
|
YARVINSN_opt_getconstant_path => {
|
||||||
let ic = get_arg(pc, 0).as_ptr::<u8>();
|
let ic = get_arg(pc, 0).as_ptr();
|
||||||
state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic }));
|
state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic }));
|
||||||
}
|
}
|
||||||
YARVINSN_branchunless => {
|
YARVINSN_branchunless => {
|
||||||
|
@ -3565,4 +3607,70 @@ mod opt_tests {
|
||||||
Return v7
|
Return v7
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dont_replace_get_constant_path_with_empty_ic() {
|
||||||
|
eval("
|
||||||
|
def test = Kernel
|
||||||
|
");
|
||||||
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
|
fn test:
|
||||||
|
bb0():
|
||||||
|
v1:BasicObject = GetConstantPath 0x1000
|
||||||
|
Return v1
|
||||||
|
"#]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dont_replace_get_constant_path_with_invalidated_ic() {
|
||||||
|
eval("
|
||||||
|
def test = Kernel
|
||||||
|
test
|
||||||
|
Kernel = 5
|
||||||
|
");
|
||||||
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
|
fn test:
|
||||||
|
bb0():
|
||||||
|
v1:BasicObject = GetConstantPath 0x1000
|
||||||
|
Return v1
|
||||||
|
"#]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replace_get_constant_path_with_const() {
|
||||||
|
eval("
|
||||||
|
def test = Kernel
|
||||||
|
test
|
||||||
|
");
|
||||||
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
|
fn test:
|
||||||
|
bb0():
|
||||||
|
PatchPoint SingleRactorMode
|
||||||
|
PatchPoint StableConstantNames(0x1000, Kernel)
|
||||||
|
v5:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
|
||||||
|
Return v5
|
||||||
|
"#]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replace_nested_get_constant_path_with_const() {
|
||||||
|
eval("
|
||||||
|
module Foo
|
||||||
|
module Bar
|
||||||
|
class C
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def test = Foo::Bar::C
|
||||||
|
test
|
||||||
|
");
|
||||||
|
assert_optimized_method_hir("test", expect![[r#"
|
||||||
|
fn test:
|
||||||
|
bb0():
|
||||||
|
PatchPoint SingleRactorMode
|
||||||
|
PatchPoint StableConstantNames(0x1000, Foo::Bar::C)
|
||||||
|
v5:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
|
||||||
|
Return v5
|
||||||
|
"#]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue