Skip to content

Commit

Permalink
YJIT: Lookup IDs on boot instead of binding to them
Browse files Browse the repository at this point in the history
Previously, the version-controlled `cruby_bindings.inc.rs` file
contained the build-time artifact `id.h`, which nobu mentioned hinders
the goal of having fewer magic numbers in the repository.

Lookup the IDs YJIT needs on boot. It costs cycles, but it's fine since
YJIT only uses a handful of IDs at the moment. No perceptible
degradation to boot time found in my testing.
  • Loading branch information
XrXr committed Oct 17, 2023
1 parent 01b5d1d commit 1f7234c
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 222 deletions.
4 changes: 1 addition & 3 deletions yjit/bindgen/src/main.rs
Expand Up @@ -208,6 +208,7 @@ fn main() {

// From include/ruby/internal/symbol.h
.allowlist_function("rb_intern")
.allowlist_function("rb_intern2")
.allowlist_function("rb_id2sym")
.allowlist_function("rb_id2name")
.allowlist_function("rb_sym2id")
Expand Down Expand Up @@ -244,9 +245,6 @@ fn main() {
// From include/ruby/internal/hash.h
.allowlist_type("ruby_rhash_flags") // really old C extension API

// Autogenerated into id.h
.allowlist_type("ruby_method_ids")

// From method.h
.allowlist_type("rb_method_visibility_t")
.allowlist_type("rb_method_type_t")
Expand Down
10 changes: 5 additions & 5 deletions yjit/src/codegen.rs
Expand Up @@ -3640,13 +3640,13 @@ fn gen_opt_newarray_send(
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
) -> Option<CodegenStatus> {
let method = jit.get_arg(1).as_u32();
let method = jit.get_arg(1).as_u64();

if method == idMin {
if method == ID!(min) {
gen_opt_newarray_min(jit, asm, _ocb)
} else if method == idMax {
} else if method == ID!(max) {
gen_opt_newarray_max(jit, asm, _ocb)
} else if method == idHash {
} else if method == ID!(hash) {
gen_opt_newarray_hash(jit, asm, _ocb)
} else {
None
Expand Down Expand Up @@ -4984,7 +4984,7 @@ fn jit_obj_respond_to(
(METHOD_VISI_UNDEF, _) => {
// No method, we can return false given respond_to_missing? hasn't been overridden.
// In the future, we might want to jit the call to respond_to_missing?
if !assume_method_basic_definition(jit, asm, ocb, recv_class, idRespond_to_missing.into()) {
if !assume_method_basic_definition(jit, asm, ocb, recv_class, ID!(respond_to_missing).into()) {
return false;
}
Qfalse
Expand Down
48 changes: 48 additions & 0 deletions yjit/src/cruby.rs
Expand Up @@ -735,3 +735,51 @@ mod manual_defs {
pub const RUBY_OFFSET_ICE_VALUE: i32 = 8;
}
pub use manual_defs::*;

/// Interned ID values for Ruby symbols and method names.
/// See [crate::cruby::ID] and usages outside of YJIT.
pub(crate) mod ids {
use std::sync::atomic::AtomicU64;
/// Globals to cache IDs on boot. Atomic to use with relaxed ordering
/// so reads can happen without `unsafe`. Initialization is done
/// single-threaded and release-acquire on [crate::yjit::YJIT_ENABLED]
/// makes sure we read the cached values after initialization is done.
macro_rules! def_ids {
($(name: $ident:ident content: $str:literal)*) => {
$(
#[doc = concat!("[crate::cruby::ID] for `", stringify!($str), "`")]
pub static $ident: AtomicU64 = AtomicU64::new(0);
)*

pub(crate) fn init() {
$(
let content = &$str;
let ptr: *const u8 = content.as_ptr();

// Lookup and cache each ID
$ident.store(
unsafe { $crate::cruby::rb_intern2(ptr.cast(), content.len() as _) },
std::sync::atomic::Ordering::Relaxed
);
)*

}
}
}

def_ids! {
name: NULL content: b""
name: min content: b"min"
name: max content: b"max"
name: hash content: b"hash"
name: respond_to_missing content: b"respond_to_missing?"
}
}

/// Get an CRuby `ID` to an interned string, e.g. a particular method name.
macro_rules! ID {
($id_name:ident) => {
$crate::cruby::ids::$id_name.load(std::sync::atomic::Ordering::Relaxed)
}
}
pub(crate) use ID;
214 changes: 1 addition & 213 deletions yjit/src/cruby_bindings.inc.rs
Expand Up @@ -280,219 +280,6 @@ pub const RUBY_ENCINDEX_EUC_JP: ruby_preserved_encindex = 10;
pub const RUBY_ENCINDEX_Windows_31J: ruby_preserved_encindex = 11;
pub const RUBY_ENCINDEX_BUILTIN_MAX: ruby_preserved_encindex = 12;
pub type ruby_preserved_encindex = u32;
pub const idDot2: ruby_method_ids = 128;
pub const idDot3: ruby_method_ids = 129;
pub const idUPlus: ruby_method_ids = 132;
pub const idUMinus: ruby_method_ids = 133;
pub const idPow: ruby_method_ids = 134;
pub const idCmp: ruby_method_ids = 135;
pub const idPLUS: ruby_method_ids = 43;
pub const idMINUS: ruby_method_ids = 45;
pub const idMULT: ruby_method_ids = 42;
pub const idDIV: ruby_method_ids = 47;
pub const idMOD: ruby_method_ids = 37;
pub const idLTLT: ruby_method_ids = 136;
pub const idGTGT: ruby_method_ids = 137;
pub const idLT: ruby_method_ids = 60;
pub const idLE: ruby_method_ids = 138;
pub const idGT: ruby_method_ids = 62;
pub const idGE: ruby_method_ids = 139;
pub const idEq: ruby_method_ids = 140;
pub const idEqq: ruby_method_ids = 141;
pub const idNeq: ruby_method_ids = 142;
pub const idNot: ruby_method_ids = 33;
pub const idAnd: ruby_method_ids = 38;
pub const idOr: ruby_method_ids = 124;
pub const idBackquote: ruby_method_ids = 96;
pub const idEqTilde: ruby_method_ids = 143;
pub const idNeqTilde: ruby_method_ids = 144;
pub const idAREF: ruby_method_ids = 145;
pub const idASET: ruby_method_ids = 146;
pub const idCOLON2: ruby_method_ids = 147;
pub const idANDOP: ruby_method_ids = 148;
pub const idOROP: ruby_method_ids = 149;
pub const idANDDOT: ruby_method_ids = 150;
pub const tPRESERVED_ID_BEGIN: ruby_method_ids = 150;
pub const idNilP: ruby_method_ids = 151;
pub const idNULL: ruby_method_ids = 152;
pub const idEmptyP: ruby_method_ids = 153;
pub const idEqlP: ruby_method_ids = 154;
pub const idRespond_to: ruby_method_ids = 155;
pub const idRespond_to_missing: ruby_method_ids = 156;
pub const idIFUNC: ruby_method_ids = 157;
pub const idCFUNC: ruby_method_ids = 158;
pub const id_core_set_method_alias: ruby_method_ids = 159;
pub const id_core_set_variable_alias: ruby_method_ids = 160;
pub const id_core_undef_method: ruby_method_ids = 161;
pub const id_core_define_method: ruby_method_ids = 162;
pub const id_core_define_singleton_method: ruby_method_ids = 163;
pub const id_core_set_postexe: ruby_method_ids = 164;
pub const id_core_hash_merge_ptr: ruby_method_ids = 165;
pub const id_core_hash_merge_kwd: ruby_method_ids = 166;
pub const id_core_raise: ruby_method_ids = 167;
pub const id_core_sprintf: ruby_method_ids = 168;
pub const id_debug_created_info: ruby_method_ids = 169;
pub const tPRESERVED_ID_END: ruby_method_ids = 170;
pub const tTOKEN_LOCAL_BEGIN: ruby_method_ids = 169;
pub const tMax: ruby_method_ids = 170;
pub const tMin: ruby_method_ids = 171;
pub const tHash: ruby_method_ids = 172;
pub const tFreeze: ruby_method_ids = 173;
pub const tInspect: ruby_method_ids = 174;
pub const tIntern: ruby_method_ids = 175;
pub const tObject_id: ruby_method_ids = 176;
pub const tConst_added: ruby_method_ids = 177;
pub const tConst_missing: ruby_method_ids = 178;
pub const tMethodMissing: ruby_method_ids = 179;
pub const tMethod_added: ruby_method_ids = 180;
pub const tSingleton_method_added: ruby_method_ids = 181;
pub const tMethod_removed: ruby_method_ids = 182;
pub const tSingleton_method_removed: ruby_method_ids = 183;
pub const tMethod_undefined: ruby_method_ids = 184;
pub const tSingleton_method_undefined: ruby_method_ids = 185;
pub const tLength: ruby_method_ids = 186;
pub const tSize: ruby_method_ids = 187;
pub const tGets: ruby_method_ids = 188;
pub const tSucc: ruby_method_ids = 189;
pub const tEach: ruby_method_ids = 190;
pub const tProc: ruby_method_ids = 191;
pub const tLambda: ruby_method_ids = 192;
pub const tSend: ruby_method_ids = 193;
pub const t__send__: ruby_method_ids = 194;
pub const t__recursive_key__: ruby_method_ids = 195;
pub const tInitialize: ruby_method_ids = 196;
pub const tInitialize_copy: ruby_method_ids = 197;
pub const tInitialize_clone: ruby_method_ids = 198;
pub const tInitialize_dup: ruby_method_ids = 199;
pub const tTo_int: ruby_method_ids = 200;
pub const tTo_ary: ruby_method_ids = 201;
pub const tTo_str: ruby_method_ids = 202;
pub const tTo_sym: ruby_method_ids = 203;
pub const tTo_hash: ruby_method_ids = 204;
pub const tTo_proc: ruby_method_ids = 205;
pub const tTo_io: ruby_method_ids = 206;
pub const tTo_a: ruby_method_ids = 207;
pub const tTo_s: ruby_method_ids = 208;
pub const tTo_i: ruby_method_ids = 209;
pub const tTo_f: ruby_method_ids = 210;
pub const tTo_r: ruby_method_ids = 211;
pub const tBt: ruby_method_ids = 212;
pub const tBt_locations: ruby_method_ids = 213;
pub const tCall: ruby_method_ids = 214;
pub const tMesg: ruby_method_ids = 215;
pub const tException: ruby_method_ids = 216;
pub const tLocals: ruby_method_ids = 217;
pub const tNOT: ruby_method_ids = 218;
pub const tAND: ruby_method_ids = 219;
pub const tOR: ruby_method_ids = 220;
pub const tDiv: ruby_method_ids = 221;
pub const tDivmod: ruby_method_ids = 222;
pub const tFdiv: ruby_method_ids = 223;
pub const tQuo: ruby_method_ids = 224;
pub const tName: ruby_method_ids = 225;
pub const tNil: ruby_method_ids = 226;
pub const tPath: ruby_method_ids = 227;
pub const tUScore: ruby_method_ids = 228;
pub const tNUMPARAM_1: ruby_method_ids = 229;
pub const tNUMPARAM_2: ruby_method_ids = 230;
pub const tNUMPARAM_3: ruby_method_ids = 231;
pub const tNUMPARAM_4: ruby_method_ids = 232;
pub const tNUMPARAM_5: ruby_method_ids = 233;
pub const tNUMPARAM_6: ruby_method_ids = 234;
pub const tNUMPARAM_7: ruby_method_ids = 235;
pub const tNUMPARAM_8: ruby_method_ids = 236;
pub const tNUMPARAM_9: ruby_method_ids = 237;
pub const tDefault: ruby_method_ids = 238;
pub const tTOKEN_LOCAL_END: ruby_method_ids = 239;
pub const tTOKEN_INSTANCE_BEGIN: ruby_method_ids = 238;
pub const tTOKEN_INSTANCE_END: ruby_method_ids = 239;
pub const tTOKEN_GLOBAL_BEGIN: ruby_method_ids = 238;
pub const tLASTLINE: ruby_method_ids = 239;
pub const tBACKREF: ruby_method_ids = 240;
pub const tERROR_INFO: ruby_method_ids = 241;
pub const tTOKEN_GLOBAL_END: ruby_method_ids = 242;
pub const tTOKEN_CONST_BEGIN: ruby_method_ids = 241;
pub const tTOKEN_CONST_END: ruby_method_ids = 242;
pub const tTOKEN_CLASS_BEGIN: ruby_method_ids = 241;
pub const tTOKEN_CLASS_END: ruby_method_ids = 242;
pub const tTOKEN_ATTRSET_BEGIN: ruby_method_ids = 241;
pub const tTOKEN_ATTRSET_END: ruby_method_ids = 242;
pub const tNEXT_ID: ruby_method_ids = 242;
pub const idMax: ruby_method_ids = 2721;
pub const idMin: ruby_method_ids = 2737;
pub const idHash: ruby_method_ids = 2753;
pub const idFreeze: ruby_method_ids = 2769;
pub const idInspect: ruby_method_ids = 2785;
pub const idIntern: ruby_method_ids = 2801;
pub const idObject_id: ruby_method_ids = 2817;
pub const idConst_added: ruby_method_ids = 2833;
pub const idConst_missing: ruby_method_ids = 2849;
pub const idMethodMissing: ruby_method_ids = 2865;
pub const idMethod_added: ruby_method_ids = 2881;
pub const idSingleton_method_added: ruby_method_ids = 2897;
pub const idMethod_removed: ruby_method_ids = 2913;
pub const idSingleton_method_removed: ruby_method_ids = 2929;
pub const idMethod_undefined: ruby_method_ids = 2945;
pub const idSingleton_method_undefined: ruby_method_ids = 2961;
pub const idLength: ruby_method_ids = 2977;
pub const idSize: ruby_method_ids = 2993;
pub const idGets: ruby_method_ids = 3009;
pub const idSucc: ruby_method_ids = 3025;
pub const idEach: ruby_method_ids = 3041;
pub const idProc: ruby_method_ids = 3057;
pub const idLambda: ruby_method_ids = 3073;
pub const idSend: ruby_method_ids = 3089;
pub const id__send__: ruby_method_ids = 3105;
pub const id__recursive_key__: ruby_method_ids = 3121;
pub const idInitialize: ruby_method_ids = 3137;
pub const idInitialize_copy: ruby_method_ids = 3153;
pub const idInitialize_clone: ruby_method_ids = 3169;
pub const idInitialize_dup: ruby_method_ids = 3185;
pub const idTo_int: ruby_method_ids = 3201;
pub const idTo_ary: ruby_method_ids = 3217;
pub const idTo_str: ruby_method_ids = 3233;
pub const idTo_sym: ruby_method_ids = 3249;
pub const idTo_hash: ruby_method_ids = 3265;
pub const idTo_proc: ruby_method_ids = 3281;
pub const idTo_io: ruby_method_ids = 3297;
pub const idTo_a: ruby_method_ids = 3313;
pub const idTo_s: ruby_method_ids = 3329;
pub const idTo_i: ruby_method_ids = 3345;
pub const idTo_f: ruby_method_ids = 3361;
pub const idTo_r: ruby_method_ids = 3377;
pub const idBt: ruby_method_ids = 3393;
pub const idBt_locations: ruby_method_ids = 3409;
pub const idCall: ruby_method_ids = 3425;
pub const idMesg: ruby_method_ids = 3441;
pub const idException: ruby_method_ids = 3457;
pub const idLocals: ruby_method_ids = 3473;
pub const idNOT: ruby_method_ids = 3489;
pub const idAND: ruby_method_ids = 3505;
pub const idOR: ruby_method_ids = 3521;
pub const idDiv: ruby_method_ids = 3537;
pub const idDivmod: ruby_method_ids = 3553;
pub const idFdiv: ruby_method_ids = 3569;
pub const idQuo: ruby_method_ids = 3585;
pub const idName: ruby_method_ids = 3601;
pub const idNil: ruby_method_ids = 3617;
pub const idPath: ruby_method_ids = 3633;
pub const idUScore: ruby_method_ids = 3649;
pub const idNUMPARAM_1: ruby_method_ids = 3665;
pub const idNUMPARAM_2: ruby_method_ids = 3681;
pub const idNUMPARAM_3: ruby_method_ids = 3697;
pub const idNUMPARAM_4: ruby_method_ids = 3713;
pub const idNUMPARAM_5: ruby_method_ids = 3729;
pub const idNUMPARAM_6: ruby_method_ids = 3745;
pub const idNUMPARAM_7: ruby_method_ids = 3761;
pub const idNUMPARAM_8: ruby_method_ids = 3777;
pub const idNUMPARAM_9: ruby_method_ids = 3793;
pub const idDefault: ruby_method_ids = 3809;
pub const idLASTLINE: ruby_method_ids = 3831;
pub const idBACKREF: ruby_method_ids = 3847;
pub const idERROR_INFO: ruby_method_ids = 3863;
pub const tLAST_OP_ID: ruby_method_ids = 169;
pub const idLAST_OP_ID: ruby_method_ids = 10;
pub type ruby_method_ids = u32;
pub const BOP_PLUS: ruby_basic_operators = 0;
pub const BOP_MINUS: ruby_basic_operators = 1;
pub const BOP_MULT: ruby_basic_operators = 2;
Expand Down Expand Up @@ -1122,6 +909,7 @@ extern "C" {
pub fn rb_sym2id(obj: VALUE) -> ID;
pub fn rb_id2sym(id: ID) -> VALUE;
pub fn rb_intern(name: *const ::std::os::raw::c_char) -> ID;
pub fn rb_intern2(name: *const ::std::os::raw::c_char, len: ::std::os::raw::c_long) -> ID;
pub fn rb_id2name(id: ID) -> *const ::std::os::raw::c_char;
pub fn rb_class2name(klass: VALUE) -> *const ::std::os::raw::c_char;
pub fn rb_obj_is_kind_of(obj: VALUE, klass: VALUE) -> VALUE;
Expand Down
2 changes: 1 addition & 1 deletion yjit/src/invariants.rs
Expand Up @@ -174,7 +174,7 @@ pub fn track_stable_constant_names_assumption(uninit_block: BlockRef, idlist: *c
uninit_block: BlockRef,
id: ID,
) {
if id == idNULL as u64 {
if id == ID!(NULL) {
// Used for :: prefix
return;
}
Expand Down
1 change: 1 addition & 0 deletions yjit/src/yjit.rs
Expand Up @@ -58,6 +58,7 @@ pub extern "C" fn rb_yjit_init_rust() {
Invariants::init();
CodegenGlobals::init();
YjitExitLocations::init();
ids::init();

rb_bug_panic_hook();

Expand Down

0 comments on commit 1f7234c

Please sign in to comment.