8000 Port code to get and set instance vars (#183) · kddnewton/ruby@72cd72e · GitHub
[go: up one dir, main page]

Skip to content

Commit 72cd72e

Browse files
authored
Port code to get and set instance vars (ruby#183)
1 parent 3e66538 commit 72cd72e

File tree

5 files changed

+122
-59
lines changed

5 files changed

+122
-59
lines changed

yjit.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,20 @@ rb_yarv_FL_TEST(VALUE obj, VALUE flags)
298298
return RB_FL_TEST(obj, flags);
299299
}
300300

301+
// The FL_TEST_RAW() macro, normally an internal implementation detail
302+
VALUE
303+
rb_FL_TEST_RAW(VALUE obj, VALUE flags)
304+
{
305+
return FL_TEST_RAW(obj, flags);
306+
}
307+
308+
// The RB_TYPE_P macro
309+
bool
310+
rb_RB_TYPE_P(VALUE obj, enum ruby_value_type t)
311+
{
312+
return RB_TYPE_P(obj, t);
313+
}
314+
301315
// The number of bytes counting from the beginning of the inline code block
302316
// that should not be changed. After patching for global invalidation, no one
303317
// should make changes to the invalidated code region anymore. This is used to

yjit/bindgen/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ fn main() {
7777
.allowlist_function("rb_gvar_(get|set)")
7878

7979
.allowlist_function("rb_attr_get")
80+
.allowlist_function("rb_ivar_get")
81+
82+
.allowlist_function("rb_obj_ensure_iv_index_mapping")
83+
.allowlist_function("rb_get_alloc_func")
84+
.allowlist_function("rb_class_allocate_instance")
8085

8186
// We define VALUE manually
8287
.blocklist_type("VALUE")
@@ -87,6 +92,11 @@ fn main() {
8792
.blocklist_type("rb_execution_context_.*")
8893
.opaque_type("rb_execution_context_.*")
8994

95+
// iseq_inline_iv_cache_entry is opaque
96+
.allowlist_type("IVC") // pointer to cache entry
97+
.opaque_type("iseq_inline_iv_cache_entry")
98+
.blocklist_type("iseq_inline_iv_cache_entry")
99+
90100
// Finish the builder and generate the bindings.
91101
.generate()
92102
// Unwrap the Result and panic on failure.

yjit/src/codegen.rs

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,12 @@ fn jit_peek_at_stack(jit: &JITState, ctx: &Context, n:isize) -> VALUE
202202
}
203203
}
204204

205-
/*
206205
fn jit_peek_at_self(jit: &JITState, ctx: &Context) -> VALUE
207206
{
208207
unsafe { cfp_get_self(ec_get_cfp(jit.ec.unwrap())) }
209208
}
210209

210+
/*
211211
fn jit_peek_at_local(jit: &JITState, ctx: &Context, n: i32) -> VALUE
212212
{
213213
assert!(jit_at_current_insn(jit));
@@ -1944,15 +1944,12 @@ pub const OPT_AREF_MAX_CHAIN_DEPTH:i32 = 2;
19441944
// up to 5 different classes
19451945
pub const SEND_MAX_DEPTH:i32 = 5;
19461946

1947-
/*
1948-
VALUE rb_vm_set_ivar_idx(VALUE obj, uint32_t idx, VALUE val);
1949-
19501947
// Codegen for setting an instance variable.
19511948
// Preconditions:
19521949
// - receiver is in REG0
19531950
// - receiver has the same class as CLASS_OF(comptime_receiver)
19541951
// - no stack push or pops to ctx since the entry to the codegen of the instruction being compiled
1955-
fn gen_set_ivar(jitstate_t *jit, ctx_t *ctx, VALUE recv, VALUE klass, ID ivar_name)
1952+
fn gen_set_ivar(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, recv:VALUE, klass:VALUE, ivar_name: ID) -> CodegenStatus
19561953
{
19571954
// Save the PC and SP because the callee may allocate
19581955
// Note that this modifies REG_SP, which is why we do it first
@@ -1962,13 +1959,14 @@ fn gen_set_ivar(jitstate_t *jit, ctx_t *ctx, VALUE recv, VALUE klass, ID ivar_na
19621959
let val_opnd = ctx.stack_pop(1);
19631960
let recv_opnd = ctx.stack_pop(1);
19641961

1965-
uint32_t ivar_index = rb_obj_ensure_iv_index_mapping(recv, ivar_name);
1962+
let ivar_index:u32 = unsafe { rb_obj_ensure_iv_index_mapping(recv, ivar_name) };
19661963

19671964
// Call rb_vm_set_ivar_idx with the receiver, the index of the ivar, and the value
19681965
mov(cb, C_ARG_REGS[0], recv_opnd);
1969-
mov(cb, C_ARG_REGS[1], imm_opnd(ivar_index));
1966+
mov(cb, C_ARG_REGS[1], imm_opnd(ivar_index.into()));
19701967
mov(cb, C_ARG_REGS[2], val_opnd);
1971-
call_ptr(cb, REG0, (void *)rb_vm_set_ivar_idx);
1968+
let set_ivar_idx = CodePtr::from(rb_vm_set_ivar_idx as *mut u8);
1969+
call_ptr(cb, REG0, set_ivar_idx);
19721970

19731971
let out_opnd = ctx.stack_push(Type::Unknown);
19741972
mov(cb, out_opnd, RAX);
@@ -1981,17 +1979,20 @@ fn gen_set_ivar(jitstate_t *jit, ctx_t *ctx, VALUE recv, VALUE klass, ID ivar_na
19811979
// - receiver is in REG0
19821980
// - receiver has the same class as CLASS_OF(comptime_receiver)
19831981
// - no stack push or pops to ctx since the entry to the codegen of the instruction being compiled
1984-
fn gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE comptime_receiver, ID ivar_name, insn_opnd_t reg0_opnd, uint8_t *side_exit)
1982+
fn gen_get_ivar(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb: &mut OutlinedCb, max_chain_depth: i32, comptime_receiver: VALUE, ivar_name: ID, reg0_opnd: InsnOpnd, side_exit: CodePtr) -> CodegenStatus
19851983
{
1986-
VALUE comptime_val_klass = CLASS_OF(comptime_receiver);
1987-
const ctx_t starting_context = *ctx; // make a copy for use with jit_chain_guard
1984+
let comptime_val_klass = comptime_receiver.class_of();
1985+
let starting_context = ctx.clone(); // make a copy for use with jit_chain_guard
1986+
1987+
let custom_allocator = unsafe { rb_get_alloc_func(comptime_val_klass).unwrap() as *mut u8 };
1988+
let allocate_instance = rb_class_allocate_instance as *mut u8;
19881989

19891990
// If the class uses the default allocator, instances should all be T_OBJECT
19901991
// NOTE: This assumes nobody changes the allocator of the class after allocation.
19911992
// Eventually, we can encode whether an object is T_OBJECT or not
19921993
// inside object shapes.
1993-
if (!RB_TYPE_P(comptime_receiver, T_OBJECT) ||
1994-
rb_get_alloc_func(comptime_val_klass) != rb_class_allocate_instance) {
1994+
if ! unsafe { RB_TYPE_P(comptime_receiver, RUBY_T_OBJECT) } ||
1995+
custom_allocator != allocate_instance {
19951996
// General case. Call rb_ivar_get().
19961997
// VALUE rb_ivar_get(VALUE obj, ID id)
19971998
add_comment(cb, "call rb_ivar_get()");
@@ -2000,18 +2001,19 @@ fn gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE co
20002001
jit_prepare_routine_call(jit, ctx, cb, REG1);
20012002

20022003
mov(cb, C_ARG_REGS[0], REG0);
2003-
mov(cb, C_ARG_REGS[1], imm_opnd((int64_t)ivar_name));
2004-
call_ptr(cb, REG1, (void *)rb_ivar_get);
2004+
mov(cb, C_ARG_REGS[1], uimm_opnd(ivar_name));
2005+
let ivar_get = CodePtr::from(rb_ivar_get as *mut u8);
2006+
call_ptr(cb, REG1, ivar_get);
20052007

2006-
if (!reg0_opnd.is_self) {
2007-
(void)ctx.stack_pop(1);
2008+
if reg0_opnd != InsnOpnd::SelfOpnd {
2009+
ctx.stack_pop(1);
20082010
}
20092011
// Push the ivar on the stack
20102012
let out_opnd = ctx.stack_push(Type::Unknown);
20112013
mov(cb, out_opnd, RAX);
20122014

20132015
// Jump to next instruction. This allows guard chains to share the same successor.
2014-
jit_jump_to_next_insn(jit, ctx);
2016+
jit_jump_to_next_insn(jit, ctx, cb, ocb);
20152017
return EndBlock;
20162018
}
20172019

@@ -2037,31 +2039,33 @@ fn gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE co
20372039
// FIXME: Mapping the index could fail when there is too many ivar names. If we're
20382040
// compiling for a branch stub that can cause the exception to be thrown from the
20392041
// wrong PC.
2040-
uint32_t ivar_index = rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name);
2042+
let ivar_index:usize = unsafe { rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name) } as usize;
20412043

20422044
// Pop receiver if it's on the temp stack
2043-
if (!reg0_opnd.is_self) {
2044-
(void)ctx.stack_pop(1);
2045+
if reg0_opnd != InsnOpnd::SelfOpnd {
2046+
ctx.stack_pop(1);
20452047
}
20462048

20472049
// Compile time self is embedded and the ivar index lands within the object
2048-
if (RB_FL_TEST_RAW(comptime_receiver, ROBJECT_EMBED) && ivar_index < ROBJECT_EMBED_LEN_MAX) {
2050+
let test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED)) != VALUE(0) };
2051+
if test_result && ivar_index < ROBJECT_EMBED_LEN_MAX {
20492052
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
20502053

20512054
// Guard that self is embedded
20522055
// TODO: BT and JC is shorter
20532056
add_comment(cb, "guard embedded getivar");
2054-
let flags_opnd = member_opnd(REG0, struct RBasic, flags);
2055-
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
2057+
let flags_opnd = mem_opnd(64, REG0, RUBY_OFFSET_RBASIC_FLAGS);
2058+
test(cb, flags_opnd, uimm_opnd(ROBJECT_EMBED as u64));
20562059
jit_chain_guard(JCC_JZ, jit, &starting_context, cb, max_chain_depth, counted_exit!(ocb, side_exit, getivar_megamorphic));
20572060

20582061
// Load the variable
2059-
let ivar_opnd = mem_opnd(64, REG0, offsetof(struct RObject, as.ary) + ivar_index * SIZEOF_VALUE);
2062+
let offs = RUBY_OFFSET_ROBJECT_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32;
2063+
let ivar_opnd = mem_opnd(64, REG0, offs);
20602064
mov(cb, REG1, ivar_opnd);
20612065

20622066
// Guard that the variable is not Qundef
2063-
cmp(cb, REG1, imm_opnd(Qundef));
2064-
mov(cb, REG0, imm_opnd(Qnil));
2067+
cmp(cb, REG1, uimm_opnd(Qundef.into()));
2068+
mov(cb, REG0, uimm_opnd(Qnil.into()));
20652069
cmove(cb, REG1, REG0);
20662070

20672071
// Push the ivar on the stack
@@ -2074,29 +2078,29 @@ fn gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE co
20742078
// Guard that value is *not* embedded
20752079
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
20762080
add_comment(cb, "guard extended getivar");
2077-
let flags_opnd = member_opnd(REG0, struct RBasic, flags);
2078-
test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
2081+
let flags_opnd = mem_opnd(64, REG0, RUBY_OFFSET_RBASIC_FLAGS);
2082+
test(cb, flags_opnd, uimm_opnd(ROBJECT_EMBED as u64));
20792083
jit_chain_guard(JCC_JNZ, jit, &starting_context, cb, max_chain_depth, counted_exit!(ocb, side_exit, getivar_megamorphic));
20802084

20812085
// check that the extended table is big enough
2082-
if (ivar_index >= ROBJECT_EMBED_LEN_MAX + 1) {
2086+
if ivar_index >= ROBJECT_EMBED_LEN_MAX + 1 {
20832087
// Check that the slot is inside the extended table (num_slots > index)
2084-
let num_slots = mem_opnd(32, REG0, offsetof(struct RObject, as.heap.numiv));
2085-
cmp(cb, num_slots, imm_opnd(ivar_index));
2088+
let num_slots = mem_opnd(32, REG0, RUBY_OFFSET_ROBJECT_AS_HEAP_NUMIV);
2089+
cmp(cb, num_slots, uimm_opnd(ivar_index as u64));
20862090
jle_ptr(cb, counted_exit!(ocb, side_exit, getivar_idx_out_of_range));
20872091
}
20882092

20892093
// Get a pointer to the extended table
2090-
let tbl_opnd = mem_opnd(64, REG0, offsetof(struct RObject, as.heap.ivptr));
2094+
let tbl_opnd = mem_opnd(64, REG0, RUBY_OFFSET_ROBJECT_AS_HEAP_IVPTR);
20912095
mov(cb, REG0, tbl_opnd);
20922096

20932097
// Read the ivar from the extended table
2094-
let ivar_opnd = mem_opnd(64, REG0, SIZEOF_VALUE * ivar_index);
2098+
let ivar_opnd = mem_opnd(64, REG0, (SIZEOF_VALUE * ivar_index) as i32);
20952099
mov(cb, REG0, ivar_opnd);
20962100

20972101
// Check that the ivar is not Qundef
2098-
cmp(cb, REG0, imm_opnd(Qundef));
2099-
mov(cb, REG1, imm_opnd(Qnil));
2102+
cmp(cb, REG0, uimm_opnd(Qundef.into()));
2103+
mov(cb, REG1, uimm_opnd(Qnil.into()));
21002104
cmove(cb, REG0, REG1);
21012105

21022106
// Push the ivar on the stack
@@ -2105,40 +2109,38 @@ fn gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE co
21052109
}
21062110

21072111
// Jump to next instruction. This allows guard chains to share the same successor.
2108-
jit_jump_to_next_insn(jit, ctx);
2112+
jit_jump_to_next_insn(jit, ctx, cb, ocb);
21092113
EndBlock
21102114
}
21112115

21122116
fn gen_getinstancevariable(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb: &mut OutlinedCb) -> CodegenStatus
21132117
{
21142118
// Defer compilation so we can specialize on a runtime `self`
2115-
if (!jit_at_current_insn(jit)) {
2119+
if !jit_at_current_insn(jit) {
21162120
defer_compilation(jit, cb, ctx);
21172121
return EndBlock;
21182122
}
21192123

2120-
ID ivar_name = (ID)jit_get_arg(jit, 0);
2124+
let ivar_name = jit_get_arg(jit, 0).as_u64();
21212125

2122-
VALUE comptime_val = jit_peek_at_self(jit, ctx);
2123-
VALUE comptime_val_klass = CLASS_OF(comptime_val);
2126+
let comptime_val = jit_peek_at_self(jit, ctx);
2127+
let comptime_val_klass = comptime_val.class_of();
21242128

21252129
// Generate a side exit
2126-
uint8_t *side_exit = get_side_exit(jit, ocb, ctx);
2130+
let side_exit = get_side_exit(jit, ocb, ctx);
21272131

21282132
// Guard that the receiver has the same class as the one from compile time.
2129-
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
2133+
mov(cb, REG0, mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_SELF));
21302134

2131-
jit_guard_known_klass(jit, ctx, cb, comptime_val_klass, OPND_SELF, comptime_val, GETIVAR_MAX_DEPTH, side_exit);
2135+
jit_guard_known_klass(jit, ctx, cb, comptime_val_klass, InsnOpnd::SelfOpnd, comptime_val, GET_IVAR_MAX_DEPTH, side_exit);
21322136

2133-
return gen_get_ivar(jit, ctx, GETIVAR_MAX_DEPTH, comptime_val, ivar_name, OPND_SELF, side_exit);
2137+
gen_get_ivar(jit, ctx, cb, ocb, GET_IVAR_MAX_DEPTH, comptime_val, ivar_name, InsnOpnd::SelfOpnd, side_exit)
21342138
}
21352139

2136-
void rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic);
2137-
21382140
fn gen_setinstancevariable(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb: &mut OutlinedCb) -> CodegenStatus
21392141
{
2140-
ID id = (ID)jit_get_arg(jit, 0);
2141-
IVC ic = (IVC)jit_get_arg(jit, 1);
2142+
let id = jit_get_arg(jit, 0);
2143+
let ic = jit_get_arg(jit, 1).as_u64(); // type IVC
21422144

21432145
// Save the PC and SP because the callee may allocate
21442146
// Note that this modifies REG_SP, which is why we do it first
@@ -2148,16 +2150,17 @@ fn gen_setinstancevariable(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeB
21482150
let val_opnd = ctx.stack_pop(1);
21492151

21502152
// Call rb_vm_setinstancevariable(iseq, obj, id, val, ic);
2151-
mov(cb, C_ARG_REGS[1], member_opnd(REG_CFP, rb_control_frame_t, self));
2153+
mov(cb, C_ARG_REGS[1], mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_SELF));
21522154
mov(cb, C_ARG_REGS[3], val_opnd);
2153-
mov(cb, C_ARG_REGS[2], imm_opnd(id));
2154-
mov(cb, C_ARG_REGS[4], const_ptr_opnd(ic));
2155-
jit_mov_gc_ptr(jit, cb, C_ARG_REGS[0], (VALUE)jit->iseq);
2156-
call_ptr(cb, REG0, (void *)rb_vm_setinstancevariable);
2155+
mov(cb, C_ARG_REGS[2], uimm_opnd(id.into()));
2156+
mov(cb, C_ARG_REGS[4], const_ptr_opnd(ic as *const u8));
2157+
let iseq = VALUE(jit.iseq as usize);
2158+
jit_mov_gc_ptr(jit, cb, C_ARG_REGS[0], iseq);
2159+
let vm_setinstancevar = CodePtr::from(rb_vm_setinstancevariable as *mut u8);
2160+
call_ptr(cb, REG0, vm_setinstancevar);
21572161

21582162
KeepCompiling
21592163
}
2160-
*/
21612164

21622165
fn gen_defined(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb: &mut OutlinedCb) -> CodegenStatus
21632166
{
@@ -5248,11 +5251,10 @@ fn get_gen_fn(opcode: VALUE) -> Option<CodeGenFn>
52485251
OP_DEFINED => Some(gen_defined),
52495252
OP_CHECKKEYWORD => Some(gen_checkkeyword),
52505253
OP_CONCATSTRINGS => Some(gen_concatstrings),
5254+
OP_GETINSTANCEVARIABLE => Some(gen_getinstancevariable),
5255+
OP_SETINSTANCEVARIABLE => Some(gen_setinstancevariable),
52515256

52525257
/*
5253-
yjit_reg_op(BIN(concatstrings), gen_concatstrings);
5254-
yjit_reg_op(BIN(getinstancevariable), gen_getinstancevariable);
5255-
yjit_reg_op(BIN(setinstancevariable), gen_setinstancevariable);
52565258
yjit_reg_op(BIN(opt_eq), gen_opt_eq);
52575259
yjit_reg_op(BIN(opt_neq), gen_opt_neq);
52585260
yjit_reg_op(BIN(opt_aref), gen_opt_aref);

yjit/src/cruby.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,22 @@ extern "C" {
133133
#[link_name = "rb_yarv_str_eql_internal"]
134134
pub fn rb_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE;
135135

136-
#[link_name = "rb_yarv_fl_test"]
136+
#[link_name = "rb_yarv_FL_TEST"]
137137
pub fn FL_TEST(obj: VALUE, flags: VALUE) -> VALUE;
138138

139+
#[link_name = "rb_FL_TEST_RAW"]
140+
pub fn FL_TEST_RAW(obj: VALUE, flags: VALUE) -> VALUE;
141+
142+
#[link_name = "rb_RB_TYPE_P"]
143+
pub fn RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool;
144+
139145
// Ruby only defines these in vm_insnhelper.c, not in any header.
140146
// Parsing it would result in a lot of duplicate definitions.
141147
pub fn rb_vm_opt_mod(recv: VALUE, obj: VALUE) -> VALUE;
142148
pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE;
143149
pub fn rb_vm_defined(ec: EcPtr, reg_cfp: CfpPtr, op_type: rb_num_t, obj: VALUE, v: VALUE) -> bool;
150+
pub fn rb_vm_set_ivar_idx(obj: VALUE, idx: u32, val: VALUE) -> VALUE;
151+
pub fn rb_vm_setinstancevariable(iseq: IseqPtr, obj: VALUE, id: ID, val: VALUE, ic: IVC);
144152
}
145153

146154
pub fn insn_len(opcode:usize) -> u32
@@ -196,6 +204,14 @@ pub struct rb_execution_context_struct {
196204
/// Pointer to an execution context (EC)
197205
pub type EcPtr = *const rb_execution_context_struct;
198206

207+
/// Opaque execution-context type
208+
#[repr(C)]
209+
pub struct iseq_inline_iv_cache_entry {
210+
_data: [u8; 0],
211+
_marker:
212+
core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
213+
}
214+
199215
/// Pointer to a control frame pointer (CFP)
200216
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
201217
#[repr(C)]
@@ -360,6 +376,9 @@ pub const SIZEOF_VALUE: usize = 8;
360376

361377
pub const RUBY_FL_SINGLETON:usize = RUBY_FL_USER_0;
362378

379+
pub const ROBJECT_EMBED:usize = RUBY_FL_USER_1;
380+
pub const ROBJECT_EMBED_LEN_MAX:usize = 3; // This is a complex calculation in ruby/internal/core/robject.h
381+
363382
// Constants from include/ruby/internal/fl_type.h
364383
pub const RUBY_FL_USHIFT:usize = 12;
365384
pub const RUBY_FL_USER_0:usize = 1 << (RUBY_FL_USHIFT + 0);
@@ -396,6 +415,10 @@ pub const RUBY_OFFSET_RARRAY_AS_HEAP_LEN:i32 = 16; // struct RArray, subfield "
396415
pub const RUBY_OFFSET_RARRAY_AS_ARY:i32 = 16; // struct RArray, subfield "as.ary"
397416
pub const RUBY_OFFSET_RARRAY_AS_HEAP_PTR:i32 = 16; // struct RArray, subfield "as.heap.ptr"
398417

418+
pub const RUBY_OFFSET_ROBJECT_AS_ARY:i32 = 16; // struct RObject, subfield "as.ary"
419+
pub const RUBY_OFFSET_ROBJECT_AS_HEAP_NUMIV:i32 = 16; // struct RObject, subfield "as.heap.numiv"
420+
pub const RUBY_OFFSET_ROBJECT_AS_HEAP_IVPTR:i32 = 20; // struct RObject, subfield "as.heap.ivptr"
421+
399422
// Constants from rb_control_frame_t vm_core.h
400423
pub const RUBY_OFFSET_CFP_PC: i32 = 0;
401424
pub const RUBY_OFFSET_CFP_SP: i32 = 8;

0 commit comments

Comments
 (0)
0