Skip to content

Commit ec62a54

Browse files
maximecbXrXr
andauthored
Export stats primitives from Rust to CRuby (ruby#196)
* Export stats primitives from Rust to CRuby * Run yjit.rb during init again and move primitive functions from yjit.h I removed code to run yjit.rb during init from `inits.c` earlier as an expedient way to get builds working. It's led to confusion about why RubyVM::YJIT isn't showing up. Sorry! As for the functions added to yjit.h, I moved them to yjit_iface.c because I think we want to keep yjit.h a shortlist of declaration for code external to YJIT. The primitive functions only need to be visible before the `yjit.rbinc` include. I hope you don't mind me pushing to your branch. Feel free to tell me to stop it. * preemptive diff minimization * Remove unnecessary stubs * Try to fix !YJIT_BUILD builds too lazy to spin up a separate build config locally so sorry if this fails CI again 😅 * Fix again * Stub Init_builtin_yjit() in vm.o rather than in header Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
1 parent 7e95ae9 commit ec62a54

File tree

7 files changed

+44
-118
lines changed

7 files changed

+44
-118
lines changed

inits.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ rb_call_builtin_inits(void)
9898
BUILTIN(array);
9999
BUILTIN(kernel);
100100
BUILTIN(timev);
101+
BUILTIN(yjit);
101102
BUILTIN(nilclass);
102103
BUILTIN(marshal);
103104
Init_builtin_prelude();

vm.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3961,6 +3961,11 @@ Init_vm_objects(void)
39613961
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000);
39623962
}
39633963

3964+
/* Stub for builtin function when not building YJIT units*/
3965+
#if !YJIT_BUILD
3966+
void Init_builtin_yjit(void) {}
3967+
#endif
3968+
39643969
/* top self */
39653970

39663971
static VALUE

yjit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ rb_get_iseq_body_param_opt_num(rb_iseq_t* iseq) {
379379
return iseq->body->param.opt_num;
380380
}
381381

382-
VALUE*
382+
const VALUE*
383383
rb_get_iseq_body_param_opt_table(rb_iseq_t* iseq) {
384384
return iseq->body->param.opt_table;
385385
}

yjit.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,14 @@
4444
// Expose these as declarations since we are building YJIT.
4545
bool rb_yjit_enabled_p(void);
4646
unsigned rb_yjit_call_threshold(void);
47-
4847
void rb_yjit_invalidate_all_method_lookup_assumptions(void);
4948
void rb_yjit_method_lookup_change(VALUE klass, ID mid);
5049
void rb_yjit_cme_invalidate(VALUE cme);
5150
void rb_yjit_collect_vm_usage_insn(int insn);
5251
void rb_yjit_collect_binding_alloc(void);
5352
void rb_yjit_collect_binding_set(void);
5453
bool rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec);
55-
void rb_yjit_init();
54+
void rb_yjit_init(void);
5655
void rb_yjit_bop_redefined(VALUE klass, const rb_method_entry_t *me, enum ruby_basic_operators bop);
5756
void rb_yjit_constant_state_changed(void);
5857
void rb_yjit_iseq_mark(const struct rb_iseq_constant_body *body);

yjit.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,18 +134,16 @@ def self.disasm_block(cs, block, highlight)
134134
# Return a hash for statistics generated for the --yjit-stats command line option.
135135
# Return nil when option is not passed or unavailable.
136136
def self.runtime_stats
137-
# defined in yjit_iface.c
138-
Primitive.get_yjit_stats
137+
Primitive.rb_yjit_get_stats
139138
end
140139

141140
# Discard statistics collected for --yjit-stats.
142141
def self.reset_stats!
143-
# defined in yjit_iface.c
144-
Primitive.reset_stats_bang
142+
Primitive.rb_yjit_reset_stats_bang
145143
end
146144

147145
def self.stats_enabled?
148-
Primitive.yjit_stats_enabled_p
146+
Primitive.rb_yjit_stats_enabled_p
149147
end
150148

151149
def self.enabled?
@@ -157,7 +155,7 @@ def self.simulate_oom!
157155
end
158156

159157
# Avoid calling a method here to not interfere with compilation tests
160-
if Primitive.yjit_stats_enabled_p
158+
if Primitive.rb_yjit_stats_enabled_p
161159
at_exit { _print_stats }
162160
end
163161

yjit/src/stats.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ use crate::cruby::*;
55
use crate::options::*;
66
use crate::yjit::{yjit_enabled_p};
77

8-
// TODO
9-
//extern const int rb_vm_max_insn_name_size;
10-
118
// YJIT exit counts for each instruction type
129
static mut EXIT_OP_COUNT: [u64; VM_INSTRUCTION_SIZE] = [0; VM_INSTRUCTION_SIZE];
1310

@@ -139,10 +136,24 @@ make_counters!(
139136

140137
//===========================================================================
141138

142-
/// Primitive called in yjit.rb. Export all YJIT statistics as a Ruby hash.
143-
/// This should be wrapped on the C side with RB_VM_LOCK_ENTER()
139+
/// Primitive called in yjit.rb
140+
/// Check if stats generation is enabled
144141
#[no_mangle]
145-
pub extern "C" fn rb_yjit_get_yjit_stats(ec: EcPtr, ruby_self: VALUE) -> VALUE {
142+
pub extern "C" fn rb_yjit_stats_enabled_p(ec: EcPtr, ruby_self: VALUE) -> VALUE {
143+
#[cfg(feature = "stats")]
144+
if get_option!(gen_stats) {
145+
return Qtrue
146+
}
147+
148+
return Qfalse;
149+
}
150+
151+
/// Primitive called in yjit.rb.
152+
/// Export all YJIT statistics as a Ruby hash.
153+
/// This needs to be wrapped on the C side with RB_VM_LOCK_ENTER()
154+
#[no_mangle]
155+
pub extern "C" fn rb_yjit_gen_stats_dict(ec: EcPtr, ruby_self: VALUE) -> VALUE {
156+
146157
// Return Qnil if YJIT isn't enabled
147158
if !yjit_enabled_p() {
148159
return Qnil;
@@ -151,8 +162,6 @@ pub extern "C" fn rb_yjit_get_yjit_stats(ec: EcPtr, ruby_self: VALUE) -> VALUE {
151162
unsafe {
152163
let hash = rb_hash_new();
153164

154-
//RB_VM_LOCK_ENTER();
155-
156165
/*
157166
{
158167
VALUE key = ID2SYM(rb_intern("inline_code_size"));
@@ -221,8 +230,6 @@ pub extern "C" fn rb_yjit_get_yjit_stats(ec: EcPtr, ruby_self: VALUE) -> VALUE {
221230
*/
222231
}
223232

224-
//RB_VM_LOCK_LEAVE();
225-
226233
return hash;
227234
}
228235
}

yjit_iface.c

Lines changed: 15 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -546,108 +546,18 @@ comments_for(rb_execution_context_t *ec, VALUE self, VALUE start_address, VALUE
546546
return comment_array;
547547
}
548548

549-
static VALUE
550-
yjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self)
551-
{
552-
// FIXME: moved to Rust
553-
return Qfalse;
554-
}
555-
556-
// Primitive called in yjit.rb. Export all YJIT statistics as a Ruby hash.
557-
static VALUE
558-
get_yjit_stats(rb_execution_context_t *ec, VALUE self)
549+
// Primitive called in yjit.rb.
550+
// Export all YJIT statistics as a Ruby hash.
551+
VALUE
552+
rb_yjit_get_stats(rb_execution_context_t *ec, VALUE self)
559553
{
560-
// TODO: take VM lock and call into Rust code
561-
return Qnil;
562-
563-
/*
564-
// Return Qnil if YJIT isn't enabled
565-
if (cb == NULL) {
566-
return Qnil;
567-
}
568-
569-
VALUE hash = rb_hash_new();
570-
554+
VALUE stats_hash;
571555
RB_VM_LOCK_ENTER();
572-
573-
{
574-
VALUE key = ID2SYM(rb_intern("inline_code_size"));
575-
VALUE value = LL2NUM((long long)cb->write_pos);
576-
rb_hash_aset(hash, key, value);
577-
578-
key = ID2SYM(rb_intern("outlined_code_size"));
579-
value = LL2NUM((long long)ocb->write_pos);
580-
rb_hash_aset(hash, key, value);
581-
}
582-
583-
#if YJIT_STATS
584-
if (rb_yjit_opts.gen_stats) {
585-
// Indicate that the complete set of stats is available
586-
rb_hash_aset(hash, ID2SYM(rb_intern("all_stats")), Qtrue);
587-
588-
int64_t *counter_reader = (int64_t *)&yjit_runtime_counters;
589-
int64_t *counter_reader_end = &yjit_runtime_counters.last_member;
590-
591-
// For each counter in yjit_counter_names, add that counter as
592-
// a key/value pair.
593-
594-
// Iterate through comma separated counter name list
595-
char *name_reader = yjit_counter_names;
596-
char *counter_name_end = yjit_counter_names + sizeof(yjit_counter_names);
597-
while (name_reader < counter_name_end && counter_reader < counter_reader_end) {
598-
if (*name_reader == ',' || *name_reader == ' ') {
599-
name_reader++;
600-
continue;
601-
}
602-
603-
// Compute length of counter name
604-
int name_len;
605-
char *name_end;
606-
{
607-
name_end = strchr(name_reader, ',');
608-
if (name_end == NULL) break;
609-
name_len = (int)(name_end - name_reader);
610-
}
611-
612-
// Put counter into hash
613-
VALUE key = ID2SYM(rb_intern2(name_reader, name_len));
614-
VALUE value = LL2NUM((long long)*counter_reader);
615-
rb_hash_aset(hash, key, value);
616-
617-
counter_reader++;
618-
name_reader = name_end;
619-
}
620-
621-
// For each entry in exit_op_count, add a stats entry with key "exit_INSTRUCTION_NAME"
622-
// and the value is the count of side exits for that instruction.
623-
624-
char key_string[rb_vm_max_insn_name_size + 6]; // Leave room for "exit_" and a final NUL
625-
for (int i = 0; i < VM_INSTRUCTION_SIZE; i++) {
626-
const char *i_name = insn_name(i); // Look up Ruby's NUL-terminated insn name string
627-
snprintf(key_string, rb_vm_max_insn_name_size + 6, "%s%s", "exit_", i_name);
628-
629-
VALUE key = ID2SYM(rb_intern(key_string));
630-
VALUE value = LL2NUM((long long)exit_op_count[i]);
631-
rb_hash_aset(hash, key, value);
632-
}
633-
}
634-
#endif
635-
556+
VALUE rb_yjit_gen_stats_dict(rb_execution_context_t *ec, VALUE self);
557+
stats_hash = rb_yjit_gen_stats_dict(ec, self);
636558
RB_VM_LOCK_LEAVE();
637559

638-
return hash;
639-
*/
640-
}
641-
642-
// Primitive called in yjit.rb. Zero out all the counters.
643-
static VALUE
644-
reset_stats_bang(rb_execution_context_t *ec, VALUE self)
645-
{
646-
#if YJIT_STATS
647-
memset(&exit_op_count, 0, sizeof(exit_op_count));
648-
memset(&yjit_runtime_counters, 0, sizeof(yjit_runtime_counters));
649-
#endif // if YJIT_STATS
650-
return Qnil;
560+
return stats_hash;
651561
}
652562

653563
// Primitive for yjit.rb. For testing running out of executable memory
@@ -662,6 +572,12 @@ simulate_oom_bang(rb_execution_context_t *ec, VALUE self)
662572
return Qnil;
663573
}
664574

575+
// Primitives used by yjit.rb
576+
VALUE rb_yjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
577+
VALUE rb_yjit_get_stats(rb_execution_context_t *ec, VALUE self);
578+
VALUE rb_yjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
579+
580+
// Preprocessed yjit.rb generated during build
665581
#include "yjit.rbinc"
666582

667583
#if YJIT_STATS
@@ -849,7 +765,7 @@ outgoing_ids(VALUE self)
849765

850766
// Can raise RuntimeError
851767
void
852-
rb_yjit_init()
768+
rb_yjit_init(void)
853769
{
854770
if (!PLATFORM_SUPPORTED_P || !JIT_ENABLED) {
855771
return;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy