ZJIT: Add new ZJIT types for Set (#13743)

This commit is contained in:
Stan Lo 2025-06-30 17:58:32 +01:00 committed by GitHub
parent 44e4b02754
commit dc1b55a387
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 66 additions and 13 deletions

2
zjit.c
View file

@ -32,6 +32,8 @@
#include <errno.h>
RUBY_EXTERN VALUE rb_cSet; // defined in set.c and it's not exposed yet
uint32_t
rb_zjit_get_page_size(void)
{

View file

@ -186,6 +186,7 @@ fn main() {
.allowlist_var("rb_cThread")
.allowlist_var("rb_cArray")
.allowlist_var("rb_cHash")
.allowlist_var("rb_cSet")
.allowlist_var("rb_cClass")
.allowlist_var("rb_cISeq")

View file

@ -900,6 +900,7 @@ unsafe extern "C" {
lines: *mut ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
pub fn rb_jit_cont_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void);
pub static mut rb_cSet: VALUE;
pub fn rb_zjit_get_page_size() -> u32;
pub fn rb_zjit_reserve_addr_space(mem_size: u32) -> *mut u8;
pub fn rb_zjit_profile_disable(iseq: *const rb_iseq_t);

View file

@ -6235,4 +6235,24 @@ mod opt_tests {
Return v11
"#]]);
}
#[test]
fn test_set_type_from_constant() {
eval("
MY_SET = Set.new
def test = MY_SET
test
test
");
assert_optimized_method_hir("test", expect![[r#"
fn test:
bb0(v0:BasicObject):
PatchPoint SingleRactorMode
PatchPoint StableConstantNames(0x1000, MY_SET)
v7:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
Return v7
"#]]);
}
}

View file

@ -72,6 +72,7 @@ base_type "String"
base_type "Array"
base_type "Hash"
base_type "Range"
base_type "Set"
(integer, integer_exact) = base_type "Integer"
# CRuby partitions Integer into immediate and non-immediate variants.

View file

@ -9,7 +9,7 @@ mod bits {
pub const BasicObjectSubclass: u64 = 1u64 << 3;
pub const Bignum: u64 = 1u64 << 4;
pub const BoolExact: u64 = FalseClassExact | TrueClassExact;
pub const BuiltinExact: u64 = ArrayExact | BasicObjectExact | FalseClassExact | FloatExact | HashExact | IntegerExact | NilClassExact | ObjectExact | RangeExact | StringExact | SymbolExact | TrueClassExact;
pub const BuiltinExact: u64 = ArrayExact | BasicObjectExact | FalseClassExact | FloatExact | HashExact | IntegerExact | NilClassExact | ObjectExact | RangeExact | SetExact | StringExact | SymbolExact | TrueClassExact;
pub const CBool: u64 = 1u64 << 5;
pub const CDouble: u64 = 1u64 << 6;
pub const CInt: u64 = CSigned | CUnsigned;
@ -48,26 +48,29 @@ mod bits {
pub const NilClass: u64 = NilClassExact | NilClassSubclass;
pub const NilClassExact: u64 = 1u64 << 28;
pub const NilClassSubclass: u64 = 1u64 << 29;
pub const Object: u64 = Array | FalseClass | Float | Hash | Integer | NilClass | ObjectExact | ObjectSubclass | Range | String | Symbol | TrueClass;
pub const Object: u64 = Array | FalseClass | Float | Hash | Integer | NilClass | ObjectExact | ObjectSubclass | Range | Set | String | Symbol | TrueClass;
pub const ObjectExact: u64 = 1u64 << 30;
pub const ObjectSubclass: u64 = 1u64 << 31;
pub const Range: u64 = RangeExact | RangeSubclass;
pub const RangeExact: u64 = 1u64 << 32;
pub const RangeSubclass: u64 = 1u64 << 33;
pub const RubyValue: u64 = BasicObject | CallableMethodEntry | Undef;
pub const StaticSymbol: u64 = 1u64 << 34;
pub const Set: u64 = SetExact | SetSubclass;
pub const SetExact: u64 = 1u64 << 34;
pub const SetSubclass: u64 = 1u64 << 35;
pub const StaticSymbol: u64 = 1u64 << 36;
pub const String: u64 = StringExact | StringSubclass;
pub const StringExact: u64 = 1u64 << 35;
pub const StringSubclass: u64 = 1u64 << 36;
pub const Subclass: u64 = ArraySubclass | BasicObjectSubclass | FalseClassSubclass | FloatSubclass | HashSubclass | IntegerSubclass | NilClassSubclass | ObjectSubclass | RangeSubclass | StringSubclass | SymbolSubclass | TrueClassSubclass;
pub const StringExact: u64 = 1u64 << 37;
pub const StringSubclass: u64 = 1u64 << 38;
pub const Subclass: u64 = ArraySubclass | BasicObjectSubclass | FalseClassSubclass | FloatSubclass | HashSubclass | IntegerSubclass | NilClassSubclass | ObjectSubclass | RangeSubclass | SetSubclass | StringSubclass | SymbolSubclass | TrueClassSubclass;
pub const Symbol: u64 = SymbolExact | SymbolSubclass;
pub const SymbolExact: u64 = DynamicSymbol | StaticSymbol;
pub const SymbolSubclass: u64 = 1u64 << 37;
pub const SymbolSubclass: u64 = 1u64 << 39;
pub const TrueClass: u64 = TrueClassExact | TrueClassSubclass;
pub const TrueClassExact: u64 = 1u64 << 38;
pub const TrueClassSubclass: u64 = 1u64 << 39;
pub const Undef: u64 = 1u64 << 40;
pub const AllBitPatterns: [(&'static str, u64); 67] = [
pub const TrueClassExact: u64 = 1u64 << 40;
pub const TrueClassSubclass: u64 = 1u64 << 41;
pub const Undef: u64 = 1u64 << 42;
pub const AllBitPatterns: [(&'static str, u64); 70] = [
("Any", Any),
("RubyValue", RubyValue),
("Immediate", Immediate),
@ -87,6 +90,9 @@ mod bits {
("StringExact", StringExact),
("SymbolExact", SymbolExact),
("StaticSymbol", StaticSymbol),
("Set", Set),
("SetSubclass", SetSubclass),
("SetExact", SetExact),
("Range", Range),
("RangeSubclass", RangeSubclass),
("RangeExact", RangeExact),
@ -136,7 +142,7 @@ mod bits {
("ArrayExact", ArrayExact),
("Empty", Empty),
];
pub const NumTypeBits: u64 = 41;
pub const NumTypeBits: u64 = 43;
}
pub mod types {
use super::*;
@ -195,6 +201,9 @@ pub mod types {
pub const RangeExact: Type = Type::from_bits(bits::RangeExact);
pub const RangeSubclass: Type = Type::from_bits(bits::RangeSubclass);
pub const RubyValue: Type = Type::from_bits(bits::RubyValue);
pub const Set: Type = Type::from_bits(bits::Set);
pub const SetExact: Type = Type::from_bits(bits::SetExact);
pub const SetSubclass: Type = Type::from_bits(bits::SetSubclass);
pub const StaticSymbol: Type = Type::from_bits(bits::StaticSymbol);
pub const String: Type = Type::from_bits(bits::String);
pub const StringExact: Type = Type::from_bits(bits::StringExact);

View file

@ -1,6 +1,6 @@
#![allow(non_upper_case_globals)]
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH};
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange};
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange, rb_cSet};
use crate::cruby::ClassRelationship;
use crate::cruby::get_class_name;
use crate::cruby::rb_mRubyVMFrozenCore;
@ -195,6 +195,9 @@ impl Type {
else if is_string_exact(val) {
Type { bits: bits::StringExact, spec: Specialization::Object(val) }
}
else if val.class_of() == unsafe { rb_cSet } {
Type { bits: bits::SetExact, spec: Specialization::Object(val) }
}
else if val.class_of() == unsafe { rb_cObject } {
Type { bits: bits::ObjectExact, spec: Specialization::Object(val) }
}
@ -394,6 +397,7 @@ impl Type {
if self.is_subtype(types::NilClassExact) { return Some(unsafe { rb_cNilClass }); }
if self.is_subtype(types::ObjectExact) { return Some(unsafe { rb_cObject }); }
if self.is_subtype(types::RangeExact) { return Some(unsafe { rb_cRange }); }
if self.is_subtype(types::SetExact) { return Some(unsafe { rb_cSet }); }
if self.is_subtype(types::StringExact) { return Some(unsafe { rb_cString }); }
if self.is_subtype(types::SymbolExact) { return Some(unsafe { rb_cSymbol }); }
if self.is_subtype(types::TrueClassExact) { return Some(unsafe { rb_cTrueClass }); }
@ -585,6 +589,21 @@ mod tests {
assert_eq!(types::Integer.inexact_ruby_class(), None);
}
#[test]
fn set() {
assert_subtype(types::SetExact, types::Set);
assert_subtype(types::SetSubclass, types::Set);
}
#[test]
fn set_has_ruby_class() {
crate::cruby::with_rubyvm(|| {
assert_eq!(types::SetExact.runtime_exact_ruby_class(), Some(unsafe { rb_cSet }));
assert_eq!(types::Set.runtime_exact_ruby_class(), None);
assert_eq!(types::SetSubclass.runtime_exact_ruby_class(), None);
});
}
#[test]
fn display_exact_bits_match() {
assert_eq!(format!("{}", Type::fixnum(4)), "Fixnum[4]");