8000 Track single-ractor mode (#221) · kddnewton/ruby@535c145 · GitHub
[go: up one dir, main page]

Skip to content

Commit 535c145

Browse files
authored
Track single-ractor mode (ruby#221)
* Some various cleanup in invariants * Implemented rb_yjit_cme_invalidate for the callback when cmes get invalidated. * Removed the MethodLookupDependency struct in favor of just using a tuple to simplify. * Removed Invariants.get_bop_assumptions because it wasn't consistent with the rest of the code in the file. * Removed the test since it was really just testing whether HashMap insertion worked. * Removed the stuff was yjit_iface.c that was duplicating functions from Rust. * Track single-ractor mode assumptions. * Comments and readability
1 parent 3ab25fa commit 535c145

File tree

8 files changed

+67
-193
lines changed

8 files changed

+67
-193
lines changed

vm_method.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ vm_cme_invalidate(rb_callable_method_entry_t *cme)
123123
METHOD_ENTRY_INVALIDATED_SET(cme);
124124
RB_DEBUG_COUNTER_INC(cc_cme_invalidate);
125125

126-
rb_yjit_cme_invalidate((VALUE)cme);
126+
RB_VM_LOCK_ENTER();
127+
rb_yjit_cme_invalidate(cme);
128+
RB_VM_LOCK_LEAVE();
127129
}
128130

129131
void

yjit.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,11 @@ rb_RCLASS_ORIGIN(VALUE c)
645645
return RCLASS_ORIGIN(c);
646646
}
647647

648+
bool
649+
rb_yjit_multi_ractor_p(void) {
650+
return rb_multi_ractor_p();
651+
}
652+
648653
#include "yjit_iface.c"
649654

650655
#endif // if JIT_ENABLED && PLATFORM_SUPPORTED_P

yjit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ bool rb_yjit_enabled_p(void);
4646
unsigned rb_yjit_call_threshold(void);
4747
void rb_yjit_invalidate_all_method_lookup_assumptions(void);
4848
void rb_yjit_method_lookup_change(VALUE klass, ID mid);
49-
void rb_yjit_cme_invalidate(VALUE cme);
49+
void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme);
5050
void rb_yjit_collect_vm_usage_insn(int insn);
5151
void rb_yjit_collect_binding_alloc(void);
5252
void rb_yjit_collect_binding_set(void);
@@ -69,7 +69,7 @@ static inline bool rb_yjit_enabled_p(void) { return false; }
6969
static inline unsigned rb_yjit_call_threshold(void) { return UINT_MAX; }
7070
static inline void rb_yjit_invalidate_all_method_lookup_assumptions(void) {}
7171
static inline void rb_yjit_method_lookup_change(VALUE klass, ID mid) {}
72-
static inline void rb_yjit_cme_invalidate(VALUE cme) {}
72+
static inline void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
7373
static inline void rb_yjit_collect_vm_usage_insn(int insn) {}
7474
static inline void rb_yjit_collect_binding_alloc(void) {}
7575
static inline void rb_yjit_collect_binding_set(void) {}

yjit/bindgen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ fn main() {
195195
.allowlist_function("rb_leaf_builtin_function")
196196
.allowlist_function("rb_set_cfp_(pc|sp)")
197197
.allowlist_function("rb_cfp_get_iseq")
198+
.allowlist_function("rb_yjit_multi_ractor_p")
198199

199200
// Not sure why it's picking these up, but don't.
200201
.blocklist_type("FILE")

yjit/src/codegen.rs

Lines changed: 1 addition & 1 deletion
F438
Original file line numberDiff line numberDiff line change
@@ -4924,7 +4924,7 @@ fn gen_opt_getinlinecache(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBl
49244924
else {
49254925
// Optimize for single ractor mode.
49264926
// FIXME: This leaks when st_insert raises NoMemoryError
4927-
if !assume_single_ractor_mode(jit) {
4927+
if !assume_single_ractor_mode(jit, ocb) {
49284928
return CantCompile;
49294929
}
49304930

yjit/src/cruby_bindings.inc.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,6 @@ extern "C" {
672672
extern "C" {
673673
pub fn rb_cfp_get_iseq(cfp: *mut rb_control_frame_struct) -> *mut rb_iseq_t;
674674
}
675+
extern "C" {
676+
pub fn rb_yjit_multi_ractor_p() -> bool;
677+
}

yjit/src/invariants.rs

Lines changed: 52 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ use std::collections::HashMap;
1515
// assume_single_ractor_mode(jit)
1616
// assume_stable_global_constant_state(jit);
1717

18-
struct MethodLookupDependency {
19-
block: BlockRef,
20-
mid: ID
21-
}
22-
2318
/// Used to track all of the various block references that contain assumptions
2419
/// about the state of the virtual machine.
2520
pub struct Invariants {
@@ -33,7 +28,13 @@ pub struct Invariants {
3328
/// Tracks block assumptions about method lookup. Maps a class to a table of
3429
/// method ID points to a set of blocks. While a block `b` is in the table,
3530
/// b->callee_cme == rb_callable_method_entry(klass, mid).
36-
method_lookup: HashMap<VALUE, HashMap<ID, Vec<MethodLookupDependency>>>
31+
method_lookup: HashMap<VALUE, HashMap<ID, Vec<(BlockRef, ID)>>>< 10000 span class=pl-kos>,
32+
33+
/// Tracks the set of blocks that are assuming the interpreter is running
34+
/// with only one ractor. This is important for things like accessing
35+
/// constants which can have different semantics when multiple ractors are
36+
/// running.
37+
single_ractor: Vec<BlockRef>
3738
}
3839

3940
/// Private singleton instance of the invariants global struct.
@@ -46,7 +47,8 @@ impl Invariants {
4647
INVARIANTS = Some(Invariants {
4748
basic_operators: HashMap::new(),
4849
cme_validity: HashMap::new(),
49-
method_lookup: HashMap::new()
50+
method_lookup: HashMap::new(),
51+
single_ractor: Vec::new()
5052
});
5153
}
5254
}
@@ -55,12 +57,6 @@ impl Invariants {
5557
pub fn get_instance() -> &'static mut Invariants {
5658
unsafe { INVARIANTS.as_mut().unwrap() }
5759
}
58-
59-
/// Returns the vector of blocks that are currently assuming the given basic
60-
/// operator on the given class has not been redefined.
61-
pub fn get_bop_assumptions(klass: RedefinitionFlag, bop: ruby_basic_operators) -> &'static mut Vec<BlockRef> {
62-
Invariants::get_instance().basic_operators.entry((klass, bop)).or_insert(Vec::new())
63-
}
6460
}
6561

6662
/// A public function that can be called from within the code generation
@@ -69,7 +65,15 @@ impl Invariants {
6965
pub fn assume_bop_not_redefined(jit: &mut JITState, ocb: &mut OutlinedCb, klass: RedefinitionFlag, bop: ruby_basic_operators) -> bool {
7066
if unsafe { BASIC_OP_UNREDEFINED_P(bop, klass) } {
7167
jit_ensure_block_entry_exit(jit, ocb);
72-
Invariants::get_bop_assumptions(klass, bop).push(jit.get_block());
68+
69+
// First, fetch the entry in the list of basic operators that
70+
// corresponds to this class and basic operator tuple.
71+
let entry = Invariants::get_instance().basic_operators.entry((klass, bop));
72+
73+
// Next, add the current block to the list of blocks that are assuming
74+
// this basic operator is not redefined.
75+
entry.or_insert(Vec::new()).push(jit.get_block());
76+
7377
return true;
7478
} else {
7579
return false;
@@ -99,18 +103,41 @@ pub fn assume_method_lookup_stable(jit: &mut JITState, ocb: &mut OutlinedCb, rec
99103
Invariants::get_instance().method_lookup
100104
.entry(receiver_klass).or_insert(HashMap::new())
101105
.entry(mid).or_insert(Vec::new())
102-
.push(MethodLookupDependency { block: block.clone(), mid });
106+
.push((block.clone(), mid));
107+
}
108+
109+
/// Tracks that a block is assuming it is operating in single-ractor mode.
110+
pub fn assume_single_ractor_mode(jit: &mut JITState, ocb: &mut OutlinedCb) -> bool {
111+
if unsafe { rb_yjit_multi_ractor_p() } {
112+
false
113+
} else {
114+
jit_ensure_block_entry_exit(jit, ocb);
115+
Invariants::get_instance().single_ractor.push(jit.get_block());
116+
true
117+
}
103118
}
104119

105120
/// Called when a basic operation is redefined.
106121
#[no_mangle]
107122
pub extern "C" fn rb_yjit_bop_redefined(klass: RedefinitionFlag, bop: ruby_basic_operators) {
108-
for block in Invariants::get_bop_assumptions(klass, bop).iter() {
123+
for block in Invariants::get_instance().basic_operators.entry((klass, bop)).or_insert(Vec::new()).iter() {
109124
invalidate_block_version(block);
110125
incr_counter!(invalidate_bop_redefined);
111126
}
112127
}
113128

129+
/// Callback for when a cme becomes invalid. Invalidate all blocks that depend
130+
/// on the given cme being valid.
131+
#[no_mangle]
132+
pub extern "C" fn rb_yjit_cme_invalidate(callee_cme: *const rb_callable_method_entry_t) {
133+
Invariants::get_instance().cme_validity.remove(&callee_cme).map(|blocks| {
134+
for block in blocks.iter() {
135+
invalidate_block_version(block);
136+
incr_counter!(invalidate_method_lookup);
137+
}
138+
});
139+
}
140+
114141
/// Callback for when rb_callable_method_entry(klass, mid) is going to change.
115142
/// Invalidate blocks that assume stable method lookup of `mid` in `klass` when this happens.
116143
/// This needs to be wrapped on the C side with RB_VM_LOCK_ENTER().
@@ -123,61 +150,28 @@ pub extern "C" fn rb_yjit_method_lookup_change(klass: VALUE, mid: ID) {
123150

124151
Invariants::get_instance().method_lookup.entry(klass).and_modify(|deps| {
125152
deps.remove(&mid).map(|deps| {
126-
for dep in deps.iter() {
127-
invalidate_block_version(&dep.block);
153+
for (block, mid) in deps.iter() {
154+
invalidate_block_version(block);
128155
incr_counter!(invalidate_method_lookup);
129156
}
130157
});
131158
});
132159
}
133160

134-
#[cfg(test)]
135-
mod tests {
136-
use super::*;
137-
138-
#[test]
139-
fn test_get_bop_assumptions() {
140-
Invariants::init();
141-
142-
let block = Block::new(BLOCKID_NULL, &Context::default());
143-
let bops = &mut Invariants::get_instance().basic_operators;
144-
145-
// Configure the set of assumptions such that one block is assuming
146-
// Integer#+ is not redefined and one block is assuming String#+ is not
147-
// redefined.
148-
bops.insert((INTEGER_REDEFINED_OP_FLAG, BOP_PLUS), vec![block.clone()]);
149-
bops.insert((STRING_REDEFINED_OP_FLAG, BOP_PLUS), vec![block.clone()]);
150-
151-
assert_eq!(Invariants::get_bop_assumptions(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS).len(), 1);
161+
/// Callback for then Ruby is about to spawn a ractor. In that case we need to
162+
/// invalidate every block that is assuming single ractor mode.
163+
#[no_mangle]
164+
pub extern "C" fn rb_yjit_before_ractor_spawn() {
165+
for block in Invariants::get_instance().single_ractor.iter() {
166+
invalidate_block_version(block);
167+
incr_counter!(invalidate_ractor_spawn);
152168
}
153169
}
154170

155171

156172

157173

158174

159-
160-
//static st_table *blocks_assuming_single_ractor_mode;
161-
162-
// Can raise NoMemoryError.
163-
//RBIMPL_ATTR_NODISCARD()
164-
pub fn assume_single_ractor_mode(jit: &JITState) -> bool
165-
{
166-
todo!()
167-
/*
168-
if (rb_multi_ractor_p()) return false;
169-
170-
jit_ensure_block_entry_exit(jit);
171-
172-
//st_insert(blocks_assuming_single_ractor_mode, (st_data_t)jit->block, 1);
173-
true
174-
*/
175-
}
176-
177-
178-
179-
180-
181175
//static st_table *blocks_assuming_stable_global_constant_state;
182176

183177
// Assume that the global constant state has not changed since call to this function.
@@ -191,69 +185,10 @@ pub fn assume_stable_global_constant_state(jit: &JITState)
191185
*/
192186
}
193187

194-
195-
196188
/*
197-
// Callback for when a cme becomes invalid.
198-
// Invalidate all blocks that depend on cme being valid.
199-
void
200-
rb_yjit_cme_invalidate(VALUE cme)
201-
{
202-
if (!cme_validity_dependency) return;
203-
204-
RUBY_ASSERT(IMEMO_TYPE_P(cme, imemo_ment));
205-
206-
RB_VM_LOCK_ENTER();
207-
208-
// Delete the block set from the table
209-
st_data_t cme_as_st_data = (st_data_t)cme;
210-
st_data_t blocks;
211-
if (st_delete(cme_validity_dependency, &cme_as_st_data, &blocks)) {
212-
st_table *block_set = (st_table *)blocks;
213-
214-
#if YJIT_STATS
215-
yjit_runtime_counters.invalidate_method_lookup += block_set->num_entries;
216-
#endif
217-
218-
// Invalidate each block
219-
st_foreach(block_set, block_set_invalidate_i, 0);
220-
221-
st_free_table(block_set);
222-
}
223-
224-
RB_VM_LOCK_LEAVE();
225-
}
226-
*/
227-
228-
229-
/*
230-
static void
231-
yjit_block_assumptions_free(block_t *block)
232-
{
233-
st_data_t as_st_data = (st_data_t)block;
234-
if (blocks_assuming_stable_global_constant_state) {
235-
st_delete(blocks_assuming_stable_global_constant_state, &as_st_data, NULL);
236-
}
237-
238-
if (blocks_assuming_single_ractor_mode) {
239-
st_delete(blocks_assuming_single_ractor_mode, &as_st_data, NULL);
240-
}
241-
242-
if (blocks_assuming_bops) {
243-
st_delete(blocks_assuming_bops, &as_st_data, NULL);
244-
}
245-
}
246-
*/
247-
248-
249-
250-
251-
/*
252-
// When a block is deleted, remove the assumptions associated with it
253189
static void
254190
yjit_block_assumptions_free(block_t *block)
255191
{
256-
/*
257192
st_data_t as_st_data = (st_data_t)block;
258193
if (blocks_assuming_stable_global_constant_state) {
259194
st_delete(blocks_assuming_stable_global_constant_state, &as_st_data, NULL);
@@ -266,13 +201,9 @@ yjit_block_assumptions_free(block_t *block)
266201
if (blocks_assuming_bops) {
267202
st_delete(blocks_assuming_bops, &as_st_data, NULL);
268203
}
269-
*/
270204
}
271205
*/
272206

273-
274-
275-
276207
/*
277208
// Free the yjit resources associated with an iseq
278209
void
@@ -371,18 +302,6 @@ rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic)
371302
}
372303
RB_VM_LOCK_LEAVE();
373304
}
374-
375-
void
376-
rb_yjit_before_ractor_spawn(void)
377-
{
378-
if (blocks_assuming_single_ractor_mode) {
379-
#if YJIT_STATS
380-
yjit_runtime_counters.invalidate_ractor_spawn += blocks_assuming_single_ractor_mode->num_entries;
381-
#endif
382-
383-
st_foreach(blocks_assuming_single_ractor_mode, block_set_invalidate_i, 0);
384-
}
385-
}
386305
*/
387306

388307
// Invalidate all generated code and patch C method return code to contain

0 commit comments

Comments
 (0)
0