[前][次][番号順一覧][スレッド一覧]

ruby-changes:68662

From: Maxime <ko1@a...>
Date: Thu, 21 Oct 2021 08:12:04 +0900 (JST)
Subject: [ruby-changes:68662] 40b70ef7c7 (master): WIP branch generation code

https://git.ruby-lang.org/ruby.git/commit/?id=40b70ef7c7

From 40b70ef7c762701d26539e5a401449d7f3733b5a Mon Sep 17 00:00:00 2001
From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...>
Date: Wed, 16 Dec 2020 17:07:18 -0500
Subject: WIP branch generation code

---
 ujit_codegen.c |  35 +++++++++++--
 ujit_codegen.h |   3 ++
 ujit_core.c    | 157 ++++++++++++++++++++++++++++++++++++++++-----------------
 ujit_core.h    |  54 +++++++++++++++-----
 4 files changed, 189 insertions(+), 60 deletions(-)

diff --git a/ujit_codegen.c b/ujit_codegen.c
index 01785d5fc4..1201c99c66 100644
--- a/ujit_codegen.c
+++ b/ujit_codegen.c
@@ -15,9 +15,6 @@ https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L15
 #include "ujit_asm.h"
 #include "ujit_utils.h"
 
-// Code generation function signature
-typedef bool (*codegen_fn)(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx);
-
 // Map from YARV opcodes to code generation functions
 static st_table *gen_fns;
 
@@ -889,6 +886,38 @@ gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L886
     return true;
 }
 
+void 
+gen_branchunless_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
+{
+    jz_ptr(cb, target0);
+    jmp_ptr(cb, target1);
+}
+
+static bool
+gen_branchunless(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx)
+{
+    // Get the branch target instruction offsets
+    int32_t jump_idx = (int32_t)ctx_get_arg(ctx, 0);
+    int32_t next_idx = ctx->insn_idx + 1;
+    blockid_t jump_block = { ctx->iseq, jump_idx };
+    blockid_t next_block = { ctx->iseq, next_idx };
+
+    // TODO: we need to eventually do an interrupt check when jumping/branching
+    // How can we do this while keeping the check logic out of line?
+	// RUBY_VM_CHECK_INTS(ec);
+
+    // Test if any bit (outside of the Qnil bit) is on
+    // RUBY_Qfalse  /* ...0000 0000 */
+    // RUBY_Qnil    /* ...0000 1000 */
+    x86opnd_t val_opnd = ctx_stack_pop(ctx, 1);
+    test(cb, val_opnd, imm_opnd(~Qnil));
+
+    // Generate the branch instructions
+    gen_branch(cb, ocb, jump_block, next_block, gen_branchunless_branch);
+
+    return true;
+}
+
 void
 ujit_init_codegen(void)
 {
diff --git a/ujit_codegen.h b/ujit_codegen.h
index 3dc3d1bf47..7de90c7877 100644
--- a/ujit_codegen.h
+++ b/ujit_codegen.h
@@ -3,6 +3,9 @@ https://github.com/ruby/ruby/blob/trunk/ujit_codegen.h#L3
 
 #include "stddef.h"
 
+// Code generation function signature
+typedef bool (*codegen_fn)(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx);
+
 uint8_t *ujit_compile_block(const rb_iseq_t *iseq, uint32_t insn_idx, bool gen_entry);
 
 void ujit_init_codegen(void);
diff --git a/ujit_core.c b/ujit_core.c
index 1119e3df91..916e8bc4d8 100644
--- a/ujit_core.c
+++ b/ujit_core.c
@@ -4,54 +4,15 @@ https://github.com/ruby/ruby/blob/trunk/ujit_core.c#L4
 #include "ujit_core.h"
 #include "ujit_codegen.h"
 
+// Maximum number of branch instructions we can track
+#define MAX_BRANCHES 32768
+
 // Table of block versions indexed by (iseq, index) tuples
 st_table * version_tbl;
 
-int blockid_cmp(st_data_t arg0, st_data_t arg1)
-{
-    const blockid_t *block0 = (const blockid_t*)arg0;
-    const blockid_t *block1 = (const blockid_t*)arg1;
-    return block0->iseq == block1->iseq && block0->idx == block1->idx;
-}
-
-st_index_t blockid_hash(st_data_t arg)
-{
-    const blockid_t *blockid = (const blockid_t*)arg;
-    st_index_t hash0 = st_numhash((st_data_t)blockid->iseq);
-    st_index_t hash1 = st_numhash((st_data_t)(uint64_t)blockid->idx);
-
-    // Use XOR to combine the hashes
-    return hash0 ^ hash1;
-}
-
-static const struct st_hash_type hashtype_blockid = {
-    blockid_cmp,
-    blockid_hash,
-};
-
-// Retrieve a basic block version for an (iseq, idx) tuple
-// TODO: we need to add a versioning context here
-uint8_t* get_block_version(const rb_iseq_t *iseq, uint32_t idx)
-{
-    blockid_t blockid = { iseq, idx };
-
-    // If there exists a version for this block id
-    st_data_t st_version;
-    if (rb_st_lookup(version_tbl, (st_data_t)&blockid, &st_version)) {
-        return (uint8_t*)st_version;
-    }
-
-    uint8_t* code_ptr = ujit_compile_block(iseq, idx, false);
-
-    st_insert(version_tbl, (st_data_t)&blockid, (st_data_t)code_ptr);
-
-    return code_ptr;
-}
-
-//
-// Method to generate stubs for branches
-// TODO: get_branch_stub() or get_branch() function
-//
+// Registered branch entries
+branch_t branch_entries[MAX_BRANCHES];
+uint32_t num_branches = 0;
 
 // Get the current instruction opcode from the context object
 int
@@ -118,6 +79,112 @@ ctx_stack_opnd(ctx_t* ctx, int32_t idx) https://github.com/ruby/ruby/blob/trunk/ujit_core.c#L79
     return opnd;
 }
 
+int blockid_cmp(st_data_t arg0, st_data_t arg1)
+{
+    const blockid_t *block0 = (const blockid_t*)arg0;
+    const blockid_t *block1 = (const blockid_t*)arg1;
+    return block0->iseq == block1->iseq && block0->idx == block1->idx;
+}
+
+st_index_t blockid_hash(st_data_t arg)
+{
+    const blockid_t *blockid = (const blockid_t*)arg;
+    st_index_t hash0 = st_numhash((st_data_t)blockid->iseq);
+    st_index_t hash1 = st_numhash((st_data_t)(uint64_t)blockid->idx);
+
+    // Use XOR to combine the hashes
+    return hash0 ^ hash1;
+}
+
+static const struct st_hash_type hashtype_blockid = {
+    blockid_cmp,
+    blockid_hash,
+};
+
+// Called by the generated code when a branch stub is executed
+// Triggers compilation of branches and code patching
+void branch_stub_hit(uint32_t branch_idx, uint32_t target_idx)
+{
+
+
+
+
+    // TODO
+    //uint8_t* code_ptr = ujit_compile_block(blockid.iseq, blockid.idx, false);
+    //st_insert(version_tbl, (st_data_t)&blockid, (st_data_t)code_ptr);
+
+
+
+
+
+
+}
+
+// Retrieve a basic block version for an (iseq, idx) tuple
+uint8_t* find_block_version(blockid_t block)
+{
+    // If there exists a version for this block id
+    st_data_t st_version;
+    if (rb_st_lookup(version_tbl, (st_data_t)&block, &st_version)) {
+        return (uint8_t*)st_version;
+    }
+
+    return NULL;
+}
+
+// Get a version or stub corresponding to a branch target
+// TODO: need incoming and target versioning contexts
+uint8_t* get_branch_target(codeblock_t* ocb, blockid_t target, uint32_t branch_idx, uint32_t target_idx)
+{
+    uint8_t* block_code = find_block_version(target);
+
+    if (block_code)
+        return block_code;
+
+    uint8_t* stub_addr = cb_get_ptr(ocb, ocb->write_pos);
+
+    // Generate an outlined stub that will call
+    // branch_stub_hit(uint32_t branch_idx, uint32_t target_idx)
+
+
+
+
+
+
+
+
+
+
+
+    return stub_addr;
+}
+
+void gen_branch(codeblock_t* cb, codeblock_t* ocb, blockid_t target0, blockid_t target1, branchgen_fn gen_fn)
+{
+    // Get branch targets or stubs (code pointers)
+    uint8_t* target_code0 = get_branch_target(ocb, target0, num_branches, 0);
+    uint8_t* target_code1 = get_branch_target(ocb, target1, num_branches, 1);
+
+    uint32_t start_pos = (uint32_t)cb->write_pos;
+
+    // Call the branch generation function
+    gen_fn(cb, target_code0, target_code1, DEFAULT);
+
+    uint32_t end_pos = (uint32_t)cb->write_pos;
+
+    // Register this branch entry
+    branch_t branch_entry = {
+        start_pos,
+        end_pos,
+        { target0, target1 },
+        gen_fn
+    };
+
+    assert (num_branches < MAX_BRANCHES);
+    branch_entries[num_branches] = branch_entry;
+    num_branches++;
+}
+
 void
 ujit_init_core(void)
 {
diff --git a/ujit_core.h b/ujit_core.h
index 5bcbd89f3c..c6c43de525 100644
--- a/ujit_core.h
+++ b/ujit_core.h
@@ -20,17 +20,6 @@ https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L20
 // Maximum number of versions per block
 #define MAX_VERSIONS 5
 
-// Tuple of (iseq, idx) used to idenfity basic blocks
-typedef struct BlockId
-{
-    // Instruction sequence
-    const rb_iseq_t *iseq;
-
-    // Instruction index
-    const uint32_t idx;
-
-} blockid_t;
-
 // Code generation context
 typedef struct ctx_struct
 {
@@ -39,6 +28,8 @@ typedef struct ctx_struct https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L28
     // Some of the information here is only needed during
     // code generation, eg: current pc
 
+    // FIXME: we probably don't need this? we just need to
+    // know which initial bytecode we're replacing
     // The start of the generated code
     uint8_t *code_ptr;
 
@@ -62,7 +53,42 @@ typedef struct ctx_struct https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L53
 
 } ctx_t;
 
-uint8_t* get_block_version(const rb_iseq_t *iseq, uint32_t idx);
+// Tuple of (iseq, idx) used to idenfity basic blocks
+typedef struct BlockId
+{
+    // Instruction sequence
+    const rb_iseq_t *iseq;
+
+    // Instruction index
+    const uint32_t idx;
+
+} blockid_t;
+
+/// Branch code shape enumeration
+enum uint8_t
+{
+    NEXT0,  // Target 0 is next
+    NEXT1,  // Target 1 is next
+    DEFAULT // Neither target is next
+};
+
+// Branch code generation function signature
+typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
+
+// Store info about an outgoing branch in a code segment
+typedef struct BranchEntry
+{
+    // Positions where the generated code starts and ends
+    uint32_t start_pos;
+    uint32_t end_pos;
+
+    // Branch target blocks
+    blockid_t targets[2];
+
+    // Branch code generation function
+    branchgen_fn gen_fn;
+
+} branch_t;
 
 // Context object methods
 int ctx_get_opcode(ctx_t *ctx);
@@ -72,6 +98,10 @@ x86opnd_t ctx_stack_push(ctx_t* ctx, size_t n); https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L98
 x86opnd_t ctx_stack_pop(ctx_t* ctx, size_t n);
 x86opnd_t ctx_stack_opnd(ctx_t* ctx, int32_t idx);
 
+uint8_t* get_block_version(blockid_t block);
+
+void gen_branch(codeblock_t* cb, codeblock_t* ocb, blockid_t target0, blockid_t target1, branchgen_fn gen_fn);
+
 void ujit_init_core(void);
 
 #endif // #ifndef UJIT_CORE_H
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]