Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions random.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,21 @@ static const rb_random_interface_t random_mt_if = {
};

static rb_random_mt_t *
rand_mt_start(rb_random_mt_t *r)
rand_mt_start(rb_random_mt_t *r, VALUE obj)
{
if (!genrand_initialized(&r->mt)) {
r->base.seed = rand_init(&random_mt_if, &r->base, random_seed(Qundef));
if (obj) {
RB_OBJ_WRITTEN(obj, Qundef, r->base.seed);
}
}
return r;
}

static rb_random_t *
rand_start(rb_random_mt_t *r)
rand_start(rb_random_mt_t *r, VALUE obj)
{
return &rand_mt_start(r)->base;
return &rand_mt_start(r, obj)->base;
}

static rb_ractor_local_key_t default_rand_key;
Expand Down Expand Up @@ -192,7 +195,13 @@ default_rand(void)
static rb_random_mt_t *
default_mt(void)
{
return rand_mt_start(default_rand());
return rand_mt_start(default_rand(), Qfalse);
}

static rb_random_t *
default_rand_start(void)
{
return &default_mt()->base;
}

unsigned int
Expand Down Expand Up @@ -293,7 +302,7 @@ get_rnd(VALUE obj)
rb_random_t *ptr;
TypedData_Get_Struct(obj, rb_random_t, &rb_random_data_type, ptr);
if (RTYPEDDATA_TYPE(obj) == &random_mt_type)
return rand_start((rb_random_mt_t *)ptr);
return rand_start((rb_random_mt_t *)ptr, obj);
return ptr;
}

Expand All @@ -309,11 +318,11 @@ static rb_random_t *
try_get_rnd(VALUE obj)
{
if (obj == rb_cRandom) {
return rand_start(default_rand());
return default_rand_start();
}
if (!rb_typeddata_is_kind_of(obj, &rb_random_data_type)) return NULL;
if (RTYPEDDATA_TYPE(obj) == &random_mt_type)
return rand_start(DATA_PTR(obj));
return rand_start(DATA_PTR(obj), obj);
rb_random_t *rnd = DATA_PTR(obj);
if (!rnd) {
rb_raise(rb_eArgError, "uninitialized random: %s",
Expand Down Expand Up @@ -829,6 +838,7 @@ rand_mt_copy(VALUE obj, VALUE orig)
mt = &rnd1->mt;

*rnd1 = *rnd2;
RB_OBJ_WRITTEN(obj, Qundef, rnd1->base.seed);
mt->next = mt->state + numberof(mt->state) - mt->left + 1;
return obj;
}
Expand Down Expand Up @@ -916,7 +926,7 @@ rand_mt_load(VALUE obj, VALUE dump)
}
mt->left = (unsigned int)x;
mt->next = mt->state + numberof(mt->state) - x + 1;
rnd->base.seed = rb_to_int(seed);
RB_OBJ_WRITE(obj, &rnd->base.seed, rb_to_int(seed));

return obj;
}
Expand Down Expand Up @@ -975,7 +985,7 @@ static VALUE
rb_f_srand(int argc, VALUE *argv, VALUE obj)
{
VALUE seed, old;
rb_random_mt_t *r = rand_mt_start(default_rand());
rb_random_mt_t *r = default_mt();

if (rb_check_arity(argc, 0, 1) == 0) {
seed = random_seed(obj);
Expand Down Expand Up @@ -1337,7 +1347,7 @@ rb_random_bytes(VALUE obj, long n)
static VALUE
random_s_bytes(VALUE obj, VALUE len)
{
rb_random_t *rnd = rand_start(default_rand());
rb_random_t *rnd = default_rand_start();
return rand_bytes(&random_mt_if, rnd, NUM2LONG(rb_to_int(len)));
}

Expand All @@ -1359,7 +1369,7 @@ random_s_bytes(VALUE obj, VALUE len)
static VALUE
random_s_seed(VALUE obj)
{
rb_random_mt_t *rnd = rand_mt_start(default_rand());
rb_random_mt_t *rnd = default_mt();
return rnd->base.seed;
}

Expand Down Expand Up @@ -1689,7 +1699,7 @@ static VALUE
rb_f_rand(int argc, VALUE *argv, VALUE obj)
{
VALUE vmax;
rb_random_t *rnd = rand_start(default_rand());
rb_random_t *rnd = default_rand_start();

if (rb_check_arity(argc, 0, 1) && !NIL_P(vmax = argv[0])) {
VALUE v = rand_range(obj, rnd, vmax);
Expand All @@ -1716,7 +1726,7 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
static VALUE
random_s_rand(int argc, VALUE *argv, VALUE obj)
{
VALUE v = rand_random(argc, argv, Qnil, rand_start(default_rand()));
VALUE v = rand_random(argc, argv, Qnil, default_rand_start());
check_random_number(v, argv);
return v;
}
Expand Down
6 changes: 5 additions & 1 deletion shape.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ RUBY_FUNC_EXPORTED shape_id_t
rb_obj_shape_id(VALUE obj)
{
if (RB_SPECIAL_CONST_P(obj)) {
return SPECIAL_CONST_SHAPE_ID;
rb_bug("rb_obj_shape_id: called on a special constant");
}

if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
Expand Down Expand Up @@ -1180,6 +1180,7 @@ rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_t
st_delete(table, &id, NULL);
}
rb_obj_init_too_complex(dest, table);
rb_gc_writebarrier_remember(dest);
}

size_t
Expand Down Expand Up @@ -1424,6 +1425,9 @@ rb_shape_parent(VALUE self)
static VALUE
rb_shape_debug_shape(VALUE self, VALUE obj)
{
if (RB_SPECIAL_CONST_P(obj)) {
rb_raise(rb_eArgError, "Can't get shape of special constant");
}
return shape_id_t_to_rb_cShape(rb_obj_shape_id(obj));
}

Expand Down
22 changes: 16 additions & 6 deletions test/ruby/test_shapes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1032,12 +1032,22 @@ def test_array_has_root_shape
assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of([]))
end

def test_true_has_special_const_shape_id
assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(true).id)
end

def test_nil_has_special_const_shape_id
assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(nil).id)
def test_raise_on_special_consts
assert_raise ArgumentError do
RubyVM::Shape.of(true)
end
assert_raise ArgumentError do
RubyVM::Shape.of(false)
end
assert_raise ArgumentError do
RubyVM::Shape.of(nil)
end
assert_raise ArgumentError do
RubyVM::Shape.of(0)
end
assert_raise ArgumentError do
RubyVM::Shape.of(:foo)
end
end

def test_root_shape_transition_to_special_const_on_frozen
Expand Down
4 changes: 2 additions & 2 deletions yjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3104,7 +3104,7 @@ fn gen_set_ivar(

// Get the iv index
let shape_too_complex = comptime_receiver.shape_too_complex();
let ivar_index = if !shape_too_complex {
let ivar_index = if !comptime_receiver.special_const_p() && !shape_too_complex {
let shape_id = comptime_receiver.shape_id_of();
let mut ivar_index: u16 = 0;
if unsafe { rb_shape_get_iv_index(shape_id, ivar_name, &mut ivar_index) } {
Expand Down Expand Up @@ -3369,7 +3369,7 @@ fn gen_definedivar(
// Specialize base on compile time values
let comptime_receiver = jit.peek_at_self();

if comptime_receiver.shape_too_complex() || asm.ctx.get_chain_depth() >= GET_IVAR_MAX_DEPTH {
if comptime_receiver.special_const_p() || comptime_receiver.shape_too_complex() || asm.ctx.get_chain_depth() >= GET_IVAR_MAX_DEPTH {
// Fall back to calling rb_ivar_defined

// Save the PC and SP because the callee may allocate
Expand Down
6 changes: 4 additions & 2 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ fn gen_iseq_call(cb: &mut CodeBlock, caller_iseq: IseqPtr, iseq_call: &Rc<RefCel

// Update the JIT-to-JIT call to call the stub
let stub_addr = stub_ptr.raw_ptr(cb);
let iseq = iseq_call.borrow().iseq;
iseq_call.borrow_mut().regenerate(cb, |asm| {
asm_comment!(asm, "call function stub: {}", iseq_get_location(iseq_call.borrow().iseq, 0));
asm_comment!(asm, "call function stub: {}", iseq_get_location(iseq, 0));
asm.ccall(stub_addr, vec![]);
});
Some(())
Expand Down Expand Up @@ -1520,8 +1521,9 @@ fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>)

// Update the stub to call the code pointer
let code_addr = code_ptr.raw_ptr(cb);
let iseq = iseq_call.borrow().iseq;
iseq_call.borrow_mut().regenerate(cb, |asm| {
asm_comment!(asm, "call compiled function: {}", iseq_get_location(iseq_call.borrow().iseq, 0));
asm_comment!(asm, "call compiled function: {}", iseq_get_location(iseq, 0));
asm.ccall(code_addr, vec![]);
});

Expand Down
70 changes: 57 additions & 13 deletions zjit/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,40 @@ fn profile_operands(profiler: &mut Profiler, profile: &mut IseqProfile, n: usize
let obj = profiler.peek_at_stack((n - i - 1) as isize);
// TODO(max): Handle GC-hidden classes like Array, Hash, etc and make them look normal or
// drop them or something
let ty = ProfiledType::new(obj.class_of(), obj.shape_id_of());
let ty = ProfiledType::new(obj);
unsafe { rb_gc_writebarrier(profiler.iseq.into(), ty.class()) };
types[i].observe(ty);
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Flags(u32);

impl Flags {
const NONE: u32 = 0;
const IS_IMMEDIATE: u32 = 1 << 0;

pub fn none() -> Self { Self(Self::NONE) }

pub fn immediate() -> Self { Self(Self::IS_IMMEDIATE) }
pub fn is_immediate(self) -> bool { (self.0 & Self::IS_IMMEDIATE) != 0 }
}

/// opt_send_without_block/opt_plus/... should store:
/// * the class of the receiver, so we can do method lookup
/// * the shape of the receiver, so we can optimize ivar lookup
/// with those two, pieces of information, we can also determine when an object is an immediate:
/// * Integer + SPECIAL_CONST_SHAPE_ID == Fixnum
/// * Float + SPECIAL_CONST_SHAPE_ID == Flonum
/// * Symbol + SPECIAL_CONST_SHAPE_ID == StaticSymbol
/// * Integer + IS_IMMEDIATE == Fixnum
/// * Float + IS_IMMEDIATE == Flonum
/// * Symbol + IS_IMMEDIATE == StaticSymbol
/// * NilClass == Nil
/// * TrueClass == True
/// * FalseClass == False
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProfiledType {
class: VALUE,
shape: ShapeId,
flags: Flags,
}

impl Default for ProfiledType {
Expand All @@ -127,12 +141,42 @@ impl Default for ProfiledType {
}

impl ProfiledType {
fn new(class: VALUE, shape: ShapeId) -> Self {
Self { class, shape }
fn new(obj: VALUE) -> Self {
if obj == Qfalse {
return Self { class: unsafe { rb_cFalseClass },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
if obj == Qtrue {
return Self { class: unsafe { rb_cTrueClass },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
if obj == Qnil {
return Self { class: unsafe { rb_cNilClass },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
if obj.fixnum_p() {
return Self { class: unsafe { rb_cInteger },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
if obj.flonum_p() {
return Self { class: unsafe { rb_cFloat },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
if obj.static_sym_p() {
return Self { class: unsafe { rb_cSymbol },
shape: INVALID_SHAPE_ID,
flags: Flags::immediate() };
}
Self { class: obj.class_of(), shape: obj.shape_id_of(), flags: Flags::none() }
}

pub fn empty() -> Self {
Self { class: VALUE(0), shape: INVALID_SHAPE_ID }
Self { class: VALUE(0), shape: INVALID_SHAPE_ID, flags: Flags::none() }
}

pub fn is_empty(&self) -> bool {
Expand All @@ -148,27 +192,27 @@ impl ProfiledType {
}

pub fn is_fixnum(&self) -> bool {
self.class == unsafe { rb_cInteger } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cInteger } && self.flags.is_immediate()
}

pub fn is_flonum(&self) -> bool {
self.class == unsafe { rb_cFloat } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cFloat } && self.flags.is_immediate()
}

pub fn is_static_symbol(&self) -> bool {
self.class == unsafe { rb_cSymbol } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cSymbol } && self.flags.is_immediate()
}

pub fn is_nil(&self) -> bool {
self.class == unsafe { rb_cNilClass } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cNilClass } && self.flags.is_immediate()
}

pub fn is_true(&self) -> bool {
self.class == unsafe { rb_cTrueClass } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cTrueClass } && self.flags.is_immediate()
}

pub fn is_false(&self) -> bool {
self.class == unsafe { rb_cFalseClass } && self.shape == SPECIAL_CONST_SHAPE_ID
self.class == unsafe { rb_cFalseClass } && self.flags.is_immediate()
}
}

Expand Down