ruby-changes:69588
From: Maxime <ko1@a...>
Date: Fri, 5 Nov 2021 05:05:56 +0900 (JST)
Subject: [ruby-changes:69588] 2421527d6e (master): YJIT code pages refactoring for code GC (#5073)
https://git.ruby-lang.org/ruby.git/commit/?id=2421527d6e From 2421527d6e4737c371bca0cf7e694f8a2a0f923d Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...> Date: Thu, 4 Nov 2021 13:05:41 -0700 Subject: YJIT code pages refactoring for code GC (#5073) * New code page allocation logic * Fix leaked globals * Fix leaked symbols, yjit asm tests * Make COUNTED_EXIT take a jit argument, so we can eliminate global ocb * Remove extra whitespace * Change block start_pos/end_pos to be pointers instead of uint32_t * Change branch end_pos and start_pos to end_addr, start_addr --- misc/yjit_asm_tests.c | 4 -- yjit_asm.c | 37 +------------ yjit_asm.h | 26 +--------- yjit_codegen.c | 54 +++++++++---------- yjit_core.c | 67 ++++++++++++------------ yjit_core.h | 16 +++--- yjit_iface.c | 141 ++++++++++++++++++++++++++++++++++++++++++-------- yjit_iface.h | 8 +-- 8 files changed, 194 insertions(+), 159 deletions(-) diff --git a/misc/yjit_asm_tests.c b/misc/yjit_asm_tests.c index 0bd11e47528..5708d3abadb 100644 --- a/misc/yjit_asm_tests.c +++ b/misc/yjit_asm_tests.c @@ -426,10 +426,6 @@ void run_runtime_tests(void) https://github.com/ruby/ruby/blob/trunk/misc/yjit_asm_tests.c#L426 int main(int argc, char** argv) { - // suppress -Wunused-function - (void)alloc_code_page; - (void)free_code_page; - run_assembler_tests(); run_runtime_tests(); diff --git a/yjit_asm.c b/yjit_asm.c index 0d074d5e4d5..49844145cb3 100644 --- a/yjit_asm.c +++ b/yjit_asm.c @@ -147,7 +147,7 @@ static uint8_t *align_ptr(uint8_t *ptr, uint32_t multiple) https://github.com/ruby/ruby/blob/trunk/yjit_asm.c#L147 } // Allocate a block of executable memory -uint8_t *alloc_exec_mem(uint32_t mem_size) +static uint8_t *alloc_exec_mem(uint32_t mem_size) { #ifndef _WIN32 uint8_t *mem_block; @@ -221,41 +221,6 @@ uint8_t *alloc_exec_mem(uint32_t mem_size) https://github.com/ruby/ruby/blob/trunk/yjit_asm.c#L221 #endif } -// Head of the list of free code pages -static code_page_t *freelist = NULL; - -// Allocate a single code page from a pool of free pages -code_page_t *alloc_code_page(void) -{ - // If the free list is empty - if (!freelist) { - // Allocate many pages at once - uint8_t *code_chunk = alloc_exec_mem(PAGES_PER_ALLOC * CODE_PAGE_SIZE); - - // Do this in reverse order so we allocate our pages in order - for (int i = PAGES_PER_ALLOC - 1; i >= 0; --i) { - code_page_t *code_page = malloc(sizeof(code_page_t)); - code_page->mem_block = code_chunk + i * CODE_PAGE_SIZE; - assert ((intptr_t)code_page->mem_block % CODE_PAGE_SIZE == 0); - code_page->page_size = CODE_PAGE_SIZE; - code_page->_next = freelist; - freelist = code_page; - } - } - - code_page_t *free_page = freelist; - freelist = freelist->_next; - - return free_page; -} - -// Put a code page back into the allocation pool -void free_code_page(code_page_t *code_page) -{ - code_page->_next = freelist; - freelist = code_page; -} - // Initialize a code block object void cb_init(codeblock_t *cb, uint8_t *mem_block, uint32_t mem_size) { diff --git a/yjit_asm.h b/yjit_asm.h index 30682b5078a..b1b2baae2e5 100644 --- a/yjit_asm.h +++ b/yjit_asm.h @@ -5,12 +5,6 @@ https://github.com/ruby/ruby/blob/trunk/yjit_asm.h#L5 #include <stddef.h> #include <stdbool.h> -// Size of code pages to allocate -#define CODE_PAGE_SIZE 16 * 1024 - -// How many code pages to allocate at once -#define PAGES_PER_ALLOC 512 - // Maximum number of labels to link #define MAX_LABELS 32 @@ -137,20 +131,6 @@ typedef struct X86Opnd https://github.com/ruby/ruby/blob/trunk/yjit_asm.h#L131 } x86opnd_t; -// Struct representing a code page -typedef struct code_page_struct -{ - // Chunk of executable memory - uint8_t *mem_block; - - // Size of the executable memory chunk - uint32_t page_size; - - // Next node in the free list (private) - struct code_page_struct *_next; - -} code_page_t; - // Dummy none/null operand static const x86opnd_t NO_OPND = { OPND_NONE, 0, .as.imm = 0 }; @@ -264,12 +244,10 @@ static inline x86opnd_t const_ptr_opnd(const void *ptr); https://github.com/ruby/ruby/blob/trunk/yjit_asm.h#L244 sizeof(((struct_type*)0)->member_name[0]) * idx) \ ) -// Machine code allocation +// Allocate executable memory static uint8_t *alloc_exec_mem(uint32_t mem_size); -static code_page_t *alloc_code_page(void); -static void free_code_page(code_page_t *code_page); - +// Code block functions static inline void cb_init(codeblock_t *cb, uint8_t *mem_block, uint32_t mem_size); static inline void cb_align_pos(codeblock_t *cb, uint32_t multiple); static inline void cb_set_pos(codeblock_t *cb, uint32_t pos); diff --git a/yjit_codegen.c b/yjit_codegen.c index 327e74b8111..ac79ce1e75e 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -23,7 +23,7 @@ static codegen_fn gen_fns[VM_INSTRUCTION_SIZE] = { NULL }; https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L23 // Map from method entries to code generation functions static st_table *yjit_method_codegen_table = NULL; -// Code for exiting back to the interpreter from the leave insn +// Code for exiting back to the interpreter from the leave instruction static void *leave_exit_code; // Code for full logic of returning from C method and exiting to the interpreter @@ -303,22 +303,22 @@ _gen_counter_inc(codeblock_t *cb, int64_t *counter) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L303 } // Increment a counter then take an existing side exit. -#define COUNTED_EXIT(side_exit, counter_name) _counted_side_exit(side_exit, &(yjit_runtime_counters . counter_name)) +#define COUNTED_EXIT(jit, side_exit, counter_name) _counted_side_exit(jit, side_exit, &(yjit_runtime_counters . counter_name)) static uint8_t * -_counted_side_exit(uint8_t *existing_side_exit, int64_t *counter) +_counted_side_exit(jitstate_t* jit, uint8_t *existing_side_exit, int64_t *counter) { if (!rb_yjit_opts.gen_stats) return existing_side_exit; - uint8_t *start = cb_get_ptr(ocb, ocb->write_pos); - _gen_counter_inc(ocb, counter); - jmp_ptr(ocb, existing_side_exit); + uint8_t *start = cb_get_ptr(jit->ocb, jit->ocb->write_pos); + _gen_counter_inc(jit->ocb, counter); + jmp_ptr(jit->ocb, existing_side_exit); return start; } #else #define GEN_COUNTER_INC(cb, counter_name) ((void)0) -#define COUNTED_EXIT(side_exit, counter_name) side_exit +#define COUNTED_EXIT(jit, side_exit, counter_name) side_exit #endif // if YJIT_STATS @@ -512,7 +512,7 @@ yjit_entry_prologue(codeblock_t *cb, const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L512 cb_align_pos(cb, 64); uint8_t *code_ptr = cb_get_ptr(cb, cb->write_pos); - ADD_COMMENT(cb, "yjit prolog"); + ADD_COMMENT(cb, "yjit entry"); push(cb, REG_CFP); push(cb, REG_EC); @@ -571,8 +571,8 @@ jit_jump_to_next_insn(jitstate_t *jit, const ctx_t *current_context) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L571 // We are at the end of the current instruction. Record the boundary. if (jit->record_boundary_patch_point) { - uint32_t exit_pos = yjit_gen_exit(jit->pc + insn_len(jit->opcode), &reset_depth, ocb); - record_global_inval_patch(cb, exit_pos); + uint32_t exit_pos = yjit_gen_exit(jit->pc + insn_len(jit->opcode), &reset_depth, jit->ocb); + record_global_inval_patch(jit->cb, exit_pos); jit->record_boundary_patch_point = false; } @@ -620,7 +620,7 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L620 }; // Mark the start position of the block - block->start_pos = cb->write_pos; + block->start_addr = cb_get_write_ptr(cb); // For each instruction to compile for (;;) { @@ -704,7 +704,7 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L704 } // Mark the end position of the block - block->end_pos = cb->write_pos; + block->end_addr = cb_get_write_ptr(cb); // Store the index of the last instruction in the block block->end_idx = insn_idx; @@ -1030,8 +1030,8 @@ gen_expandarray(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1030 // Move the array from the stack into REG0 and check that it's an array. mov(cb, REG0, array_opnd); - guard_object_is_heap(cb, REG0, ctx, COUNTED_EXIT(side_exit, expandarray_not_array)); - guard_object_is_array(cb, REG0, REG1, ctx, COUNTED_EXIT(side_exit, expandarray_not_array)); + guard_object_is_heap(cb, REG0, ctx, COUNTED_EXIT(jit, side_exit, expandarray_not_array)); + guard_object_is_array(cb, REG0, REG1, ctx, COUNTED_EXIT(jit, side_exit, expandarray_not_array)); // If we don't actually want any values, then just return. if (num == 0) { @@ -1053,7 +1053,7 @@ gen_expandarray(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1053 // Only handle the case where the number of values in the array is greater // than or equal to the number of values requested. cmp(cb, REG1, imm_opnd(num)); - jl_ptr(cb, COUNTED_EXIT(side_exit, expandarray_rhs_too_small)); + jl_ptr(cb, COUNTED_EXIT(jit, side_exit, expandarray_rhs_too_small)); // Load the address of the embedded array into REG1. // (struct RArray *)(obj)->as.ary @@ -1663,7 +1663,7 @@ gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE compt https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1663 // Check that the slot is inside the extended table (num_slots > index) x86opnd_t num_slots = mem_opnd(32, REG0, offsetof(struct RObject, as.heap.numiv)); cmp(cb, num_slots, (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/