10000 Merge pull request #186 from Shopify/progress-towards-btest · kddnewton/ruby@b5d2b06 · GitHub
[go: up one dir, main page]

Skip to content

Commit b5d2b06

Browse files
authored
Merge pull request ruby#186 from Shopify/progress-towards-btest
Make progress towards passing make btest
2 parents 661dc50 + 821c345 commit b5d2b06

File tree

6 files changed

+143
-109
lines changed

6 files changed

+143
-109
lines changed

yjit/bindgen/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ fn main() {
9999
.allowlist_function("rb_yjit_mark_executable")
100100
.allowlist_function("rb_yjit_get_page_size")
101101

102+
.allowlist_var("VM_ENV_FLAG_.*")
103+
102104
// We define VALUE manually
103105
.blocklist_type("VALUE")
104106
.opaque_type("rb_iseq_t")

yjit/src/codegen.rs

Lines changed: 3 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ fn gen_exit(exit_pc: *mut VALUE, ctx: &Context, cb: &mut CodeBlock) -> CodePtr
434434
}
435435

436436
// Update CFP->PC
437-
//mov(cb, RAX, const_ptr_opnd(exit_pc as *const u8));
437+
mov(cb, RAX, const_ptr_opnd(exit_pc as *const u8));
438438
mov(cb, mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_PC), RAX);
439439

440440
// Accumulate stats about interpreter exits
@@ -808,7 +808,7 @@ pub fn gen_single_block(blockref: &BlockRef, ec: EcPtr, cb: &mut CodeBlock, ocb:
808808
// Call the code generation function
809809
status = gen_fn(&mut jit, &mut ctx, cb, ocb);
810810
}
811-
dbg!(&status, opcode);
811+
//dbg!(&status, opcode);
812812

813813
// If we can't compile this instruction
814814
// exit to the interpreter and stop compiling
@@ -1746,7 +1746,7 @@ fn gen_setlocal_generic(jit:&mut JITState, ctx: &mut Context, cb: &mut CodeBlock
17461746

17471747
// flags & VM_ENV_FLAG_WB_REQUIRED
17481748
let flags_opnd = mem_opnd(64, REG0, SIZEOF_VALUE as i32 * VM_ENV_DATA_INDEX_FLAGS as i32);
1749-
test(cb, flags_opnd, imm_opnd(VM_ENV_FLAG_WB_REQUIRED as i64));
1749+
test(cb, flags_opnd, uimm_opnd(VM_ENV_FLAG_WB_REQUIRED.into()));
17501750

17511751
// Create a side-exit to fall back to the interpreter
17521752
let side_exit = get_side_exit(jit, ocb, ctx);
@@ -5100,111 +5100,6 @@ fn gen_opt_invokebuiltin_delegate(jit: &mut JITState, ctx: &mut Context, cb: &mu
51005100
51015101
KeepCompiling
51025102
}
5103-
5104-
// Invalidate all generated code and patch C method return code to contain
5105-
// logic for firing the c_return TracePoint event. Once rb_vm_barrier()
5106-
// returns, all other ractors are pausing inside RB_VM_LOCK_ENTER(), which
5107-
// means they are inside a C routine. If there are any generated code on-stack,
5108-
// they are waiting for a return from a C routine. For every routine call, we
5109-
// patch in an exit after the body of the containing VM instruction. This makes
5110-
// it so all the invalidated code exit as soon as execution logically reaches
5111-
// the next VM instruction. The interpreter takes care of firing the tracing
5112-
// event if it so happens that the next VM instruction has one attached.
5113-
//
5114-
// The c_return event needs special handling as our codegen never outputs code
5115-
// that contains tracing logic. If we let the normal output code run until the
5116-
// start of the next VM instruction by relying on the patching scheme above, we
5117-
// would fail to fire the c_return event. The interpreter doesn't fire the
5118-
// event at an instruction boundary, so simply exiting to the interpreter isn't
5119-
// enough. To handle it, we patch in the full logic at the return address. See
5120-
// full_cfunc_return().
5121-
//
5122-
// In addition to patching, we prevent future entries into invalidated code by
5123-
// removing all live blocks from their iseq.
5124-
void
5125-
rb_yjit_tracing_invalidate_all(void)
5126-
{
5127-
if (!rb_yjit_enabled_p()) return;
5128-
5129-
// Stop other ractors since we are going to patch machine code.
5130-
RB_VM_LOCK_ENTER();
5131-
rb_vm_barrier();
5132-
5133-
// Make it so all live block versions are no longer valid branch targets
5134-
rb_objspace_each_objects(tracing_invalidate_all_i, NULL);
5135-
5136-
// Apply patches
5137-
const uint32_t old_pos = cb->write_pos;
5138-
rb_darray_for(global_inval_patches, patch_idx) {
5139-
struct codepage_patch patch = rb_darray_get(global_inval_patches, patch_idx);
5140-
cb.set_pos(patch.inline_patch_pos);
5141-
uint8_t *jump_target = cb_get_ptr(ocb, patch.outlined_target_pos);
5142-
jmp_ptr(cb, jump_target);
5143-
}
5144-
cb.set_pos(old_pos);
5145-
5146-
// Freeze invalidated part of the codepage. We only want to wait for
5147-
// running instances of the code to exit from now on, so we shouldn't
5148-
// change the code. There could be other ractors sleeping in
5149-
// branch_stub_hit(), for example. We could harden this by changing memory
5150-
// protection on the frozen range.
5151-
RUBY_ASSERT_ALWAYS(yjit_codepage_frozen_bytes <= old_pos && "frozen bytes should increase monotonically");
5152-
yjit_codepage_frozen_bytes = old_pos;
5153-
5154-
cb_mark_all_executable(ocb);
5155-
cb_mark_all_executable(cb);
5156-
RB_VM_LOCK_LEAVE();
5157-
}
5158-
5159-
static int
5160-
tracing_invalidate_all_i(void *vstart, void *vend, size_t stride, void *data)
5161-
{
5162-
VALUE v = (VALUE)vstart;
5163-
for (; v != (VALUE)vend; v += stride) {
5164-
void *ptr = asan_poisoned_object_p(v);
5165-
asan_unpoison_object(v, false);
5166-
5167-
if (rb_obj_is_iseq(v)) {
5168-
rb_iseq_t *iseq = (rb_iseq_t *)v;
5169-
invalidate_all_blocks_for_tracing(iseq);
5170-
}
5171-
5172-
asan_poison_object_if(ptr, v);
5173-
}
5174-
return 0;
5175-
}
5176-
5177-
static void
5178-
invalidate_all_blocks_for_tracing(const rb_iseq_t *iseq)
5179-
{
5180-
struct rb_iseq_constant_body *body = iseq->body;
5181-
if (!body) return; // iseq yet to be initialized
5182-
5183-
ASSERT_vm_locking();
5184-
5185-
// Empty all blocks on the iseq so we don't compile new blocks that jump to the
5186-
// invalidted region.
5187-
// TODO Leaking the blocks for now since we might have situations where
5188-
// a different ractor is waiting in branch_stub_hit(). If we free the block
5189-
// that ractor can wake up with a dangling block.
5190-
rb_darray_for(body->yjit_blocks, version_array_idx) {
5191-
rb_yjit_block_array_t version_array = rb_darray_get(body->yjit_blocks, version_array_idx);
5192-
rb_darray_for(version_array, version_idx) {
5193-
// Stop listening for invalidation events like basic operation redefinition.
5194-
block_t *block = rb_darray_get(version_array, version_idx);
5195-
yjit_unlink_method_lookup_dependency(block);
5196-
yjit_block_assumptions_free(block);
5197-
}
5198-
rb_darray_free(version_array);
5199-
}
5200-
rb_darray_free(body->yjit_blocks);
5201-
body->yjit_blocks = NULL;
5202-
5203-
#if USE_MJIT
5204-
// Reset output code entry point
5205-
body->jit_func = NULL;
5206-
#endif
5207-
}
52085103
*/
52095104

52105105

yjit/src/cruby.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ pub const VM_SPECIAL_OBJECT_VMCORE:usize = 0x1;
436436
pub const VM_ENV_DATA_INDEX_SPECVAL:isize = -1;
437437
pub const VM_ENV_DATA_INDEX_FLAGS:isize = 0;
438438
pub const VM_ENV_DATA_SIZE:usize = 3;
439-
pub const VM_ENV_FLAG_WB_REQUIRED:usize = 0x008;
440439

441440
// From vm_callinfo.h
442441
pub const VM_CALL_ARGS_SPLAT:u32 = 1 << VM_CALL_ARGS_SPLAT_bit;

yjit/src/cruby_bindings.inc.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,28 @@ pub const BOP_OR: ruby_basic_operators = 28;
379379
pub const BOP_LAST_: ruby_basic_operators = 29;
380380
pub type ruby_basic_operators = u32;
381381
pub type IVC = *mut iseq_inline_iv_cache_entry;
382+
pub const VM_FRAME_MAGIC_METHOD: u32 = 286326785;
383+
pub const VM_FRAME_MAGIC_BLOCK: u32 = 572653569;
384+
pub const VM_FRAME_MAGIC_CLASS: u32 = 858980353;
385+
pub const VM_FRAME_MAGIC_TOP: u32 = 1145307137;
386+
pub const VM_FRAME_MAGIC_CFUNC: u32 = 1431633921;
387+
pub const VM_FRAME_MAGIC_IFUNC: u32 = 1717960705;
388+
pub const VM_FRAME_MAGIC_EVAL: u32 = 2004287489;
389+
pub const VM_FRAME_MAGIC_RESCUE: u32 = 2022178817;
390+
pub const VM_FRAME_MAGIC_DUMMY: u32 = 2040070145;
391+
pub const VM_FRAME_MAGIC_MASK: u32 = 2147418113;
392+
pub const VM_FRAME_FLAG_FINISH: u32 = 32;
393+
pub const VM_FRAME_FLAG_BMETHOD: u32 = 64;
394+
pub const VM_FRAME_FLAG_CFRAME: u32 = 128;
395+
pub const VM_FRAME_FLAG_LAMBDA: u32 = 256;
396+
pub const VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM: u32 = 512;
397+
pub const VM_FRAME_FLAG_CFRAME_KW: u32 = 1024;
398+
pub const VM_FRAME_FLAG_PASSED: u32 = 2048;
399+
pub const VM_ENV_FLAG_LOCAL: u32 = 2;
400+
pub const VM_ENV_FLAG_ESCAPED: u32 = 4;
401+
pub const VM_ENV_FLAG_WB_REQUIRED: u32 = 8;
402+
pub const VM_ENV_FLAG_ISOLATED: u32 = 16;
403+
pub type _bindgen_ty_12 = u32;
382404
pub const VM_CALL_ARGS_SPLAT_bit: vm_call_flag_bits = 0;
383405
pub const VM_CALL_ARGS_BLOCKARG_bit: vm_call_flag_bits = 1;
384406
pub const VM_CALL_FCALL_bit: vm_call_flag_bits = 2;

yjit/src/invariants.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use crate::core::*;
55
use crate::cruby::*;
66
use crate::codegen::*;
7+
use crate::yjit::{is_yjit_enabled};
78

89
// Invariants to track:
910
// assume_bop_not_redefined(jit, INTEGER_REDEFINED_OP_FLAG, BOP_PLUS)
@@ -412,4 +413,114 @@ rb_yjit_before_ractor_spawn(void)
412413
st_foreach(blocks_assuming_single_ractor_mode, block_set_invalidate_i, 0);
413414
}
414415
}
416+
*/
417+
418+
// Invalidate all generated code and patch C method return code to contain
419+
// logic for firing the c_return TracePoint event. Once rb_vm_barrier()
420+
// returns, all other ractors are pausing inside RB_VM_LOCK_ENTER(), which
421+
// means they are inside a C routine. If there are any generated code on-stack,
422+
// they are waiting for a return from a C routine. For every routine call, we
423+
// patch in an exit after the body of the containing VM instruction. This makes
424+
// it so all the invalidated code exit as soon as execution logically reaches
425+
// the next VM instruction. The interpreter takes care of firing the tracing
426+
// event if it so happens that the next VM instruction has one attached.
427+
//
428+
// The c_return event needs special handling as our codegen never outputs code
429+
// that contains tracing logic. If we let the normal output code run until the
430+
// start of the next VM instruction by relying on the patching scheme above, we
431+
// would fail to fire the c_return event. The interpreter doesn't fire the
432+
// event at an instruction boundary, so simply exiting to the interpreter isn't
433+
// enough. To handle it, we patch in the full logic at the return address. See
434+
// full_cfunc_return().
435+
//
436+
// In addition to patching, we prevent future entries into invalidated code by
437+
// removing all live blocks from their iseq.
438+
#[no_mangle]
439+
pub extern "C" fn rb_yjit_tracing_invalidate_all()
440+
{
441+
if !is_yjit_enabled() { return; }
442+
443+
todo!();
444+
/*
445+
// Stop other ractors since we are going to patch machine code.
446+
RB_VM_LOCK_ENTER();
447+
rb_vm_barrier();
448+
449+
// Make it so all live block versions are no longer valid branch targets
450+
rb_objspace_each_objects(tracing_invalidate_all_i, NULL);
451+
452+
// Apply patches
453+
const uint32_t old_pos = cb->write_pos;
454+
rb_darray_for(global_inval_patches, patch_idx) {
455+
struct codepage_patch patch = rb_darray_get(global_inval_patches, patch_idx);
456+
cb.set_pos(patch.inline_patch_pos);
457+
uint8_t *jump_target = cb_get_ptr(ocb, patch.outlined_target_pos);
458+
jmp_ptr(cb, jump_target);
459+
}
460+
cb.set_pos(old_pos);
461+
462+
// Freeze invalidated part of the codepage. We only want to wait for
463+
// running instances of the code to exit from now on, so we shouldn't
464+
// change the code. There could be other ractors sleeping in
465+
// branch_stub_hit(), for example. We could harden this by changing memory
466+
// protection on the frozen range.
467+
RUBY_ASSERT_ALWAYS(yjit_codepage_frozen_bytes <= old_pos && "frozen bytes should increase monotonically");
468+
yjit_codepage_frozen_bytes = old_pos;
469+
470+
cb_mark_all_executable(ocb);
471+
cb_mark_all_executable(cb);
472+
RB_VM_LOCK_LEAVE();
473+
*/
474+
}
475+
476+
/*
477+
static int
478+
tracing_invalidate_all_i(void *vstart, void *vend, size_t stride, void *data)
479+
{
480+
VALUE v = (VALUE)vstart;
481+
for (; v != (VALUE)vend; v += stride) {
482+
void *ptr = asan_poisoned_object_p(v);
483+
asan_unpoison_object(v, false);
484+
485+
if (rb_obj_is_iseq(v)) {
486+
rb_iseq_t *iseq = (rb_iseq_t *)v;
487+
invalidate_all_blocks_for_tracing(iseq);
488+
}
489+
490+
asan_poison_object_if(ptr, v);
491+
}
492+
return 0;
493+
}
494+
495+
static void
496+
invalidate_all_blocks_for_tracing(const rb_iseq_t *iseq)
497+
{
498+
struct rb_iseq_constant_body *body = iseq->body;
499+
if (!body) return; // iseq yet to be initialized
500+
501+
ASSERT_vm_locking();
502+
503+
// Empty all blocks on the iseq so we don't compile new blocks that jump to the
504+
// invalidted region.
505+
// TODO Leaking the blocks for now since we might have situations where
506+
// a different ractor is waiting in branch_stub_hit(). If we free the block
507+
// that ractor can wake up with a dangling block.
508+
rb_darray_for(body->yjit_blocks, version_array_idx) {
509+
rb_yjit_block_array_t version_array = rb_darray_get(body->yjit_blocks, version_array_idx);
510+
rb_darray_for(version_array, version_idx) {
511+
// Stop listening for invalidation events like basic operation redefinition.
512+
block_t *block = rb_darray_get(version_array, version_idx);
513+
yjit_unlink_method_lookup_dependency(block);
514+
yjit_block_assumptions_free(block);
515+
}
516+
rb_darray_free(version_array);
517+
}
518+
rb_darray_free(body->yjit_blocks);
519+
body->yjit_blocks = NULL;
520+
521+
#if USE_MJIT
522+
// Reset output code entry point
523+
body->jit_func = NULL;
524+
#endif
525+
}
415526
*/

yjit/src/yjit.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pub extern "C" fn rb_yjit_enabled_p() -> raw::c_int {
2929
YJIT_ENABLED.load(Ordering::Acquire).into()
3030
}
3131

32+
/// Like rb_yjit_enabled_p, but for Rust code.
33+
pub fn is_yjit_enabled() -> bool {
34+
YJIT_ENABLED.load(Ordering::Acquire)
35+
}
36+
3237
/// On which invocation of the ISEQ to invoke YJIT?
3338
#[no_mangle]
3439
pub extern "C" fn rb_yjit_call_threshold() -> raw::c_uint {

0 commit comments

Comments
 (0)
0