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

ruby-changes:72817

From: John <ko1@a...>
Date: Fri, 5 Aug 2022 00:18:47 +0900 (JST)
Subject: [ruby-changes:72817] 7f5f9d19c5 (master): YJIT: Add known_* helpers for Type (#6208)

https://git.ruby-lang.org/ruby.git/commit/?id=7f5f9d19c5

From 7f5f9d19c54d3d5e0c2b2947785d8821b752641d Mon Sep 17 00:00:00 2001
From: John Hawthorn <john@h...>
Date: Thu, 4 Aug 2022 08:18:24 -0700
Subject: YJIT: Add known_* helpers for Type (#6208)

* YJIT: Add known_* helpers for Type

This adds a few helpers to Type which all return Options representing
what is known, from a Ruby perspective, about the type.

This includes:
* known_class_of: If known, the class represented by this type
* known_value_type: If known, the T_ value type
* known_exact_value: If known, the exact VALUE represented by this type
  (currently this is only available for true/false/nil)
* known_truthy: If known, whether or not this value evaluates as true
  (not false or nil)

The goal of this is to abstract away the specifics of the mappings
between types wherever possible from the codegen. For example previously
by introducing Type::CString as a more specific version of
Type::TString, uses of Type::TString in codegen needed to be updated to
check either case. Now by using known_value_type, at least in theory we
can introduce new types with minimal (if any) codegen changes.

I think rust's Option type allows us to represent this uncertainty
fairly well, and should help avoid mistakes, and the matching using this
turned out pretty cleanly.

* YJIT: Use known_value_type for checktype

* YJIT: Use known_value_type for T_STRING check

* YJIT: Use known_class_of in guard_known_klass

* YJIT: Use known truthyness in jit_rb_obj_not

* YJIT: Rename known_class_of => known_class
---
 yjit/src/codegen.rs | 159 +++++++++++++++++++++++-----------------------------
 yjit/src/core.rs    |  54 ++++++++++++++++++
 2 files changed, 125 insertions(+), 88 deletions(-)

diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 0acd1972c3..818e3fbb41 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -2218,22 +2218,16 @@ fn gen_checktype( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L2218
         let val = ctx.stack_pop(1);
 
         // Check if we know from type information
-        match (type_val, val_type) {
-            (RUBY_T_STRING, Type::TString)
-            | (RUBY_T_STRING, Type::CString)
-            | (RUBY_T_ARRAY, Type::Array)
-            | (RUBY_T_HASH, Type::Hash) => {
-                // guaranteed type match
-                let stack_ret = ctx.stack_push(Type::True);
-                mov(cb, stack_ret, uimm_opnd(Qtrue.as_u64()));
-                return KeepCompiling;
-            }
-            _ if val_type.is_imm() || val_type.is_specific() => {
-                // guaranteed not to match T_STRING/T_ARRAY/T_HASH
-                let stack_ret = ctx.stack_push(Type::False);
-                mov(cb, stack_ret, uimm_opnd(Qfalse.as_u64()));
-                return KeepCompiling;
-            }
+        match val_type.known_value_type() {
+            Some(value_type) => {
+                if value_type == type_val {
+                    jit_putobject(jit, ctx, cb, Qtrue);
+                    return KeepCompiling;
+                } else {
+                    jit_putobject(jit, ctx, cb, Qfalse);
+                    return KeepCompiling;
+                }
+            },
             _ => (),
         }
 
@@ -2502,7 +2496,7 @@ fn gen_equality_specialized( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L2496
 
         // Otherwise guard that b is a T_STRING (from type info) or String (from runtime guard)
         let btype = ctx.get_opnd_type(StackOpnd(0));
-        if btype != Type::TString && btype != Type::CString {
+        if btype.known_value_type() != Some(RUBY_T_STRING) {
             mov(cb, REG0, C_ARG_REGS[1]);
             // Note: any T_STRING is valid here, but we check for a ::String for simplicity
             // To pass a mutable static variable (rb_cString) requires an unsafe block
@@ -3405,78 +3399,70 @@ fn jit_guard_known_klass( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3399
 ) {
     let val_type = ctx.get_opnd_type(insn_opnd);
 
+    if val_type.known_class() == Some(known_klass) {
+        // We already know from type information that this is a match
+        return;
+    }
+
     if unsafe { known_klass == rb_cNilClass } {
         assert!(!val_type.is_heap());
-        if val_type != Type::Nil {
-            assert!(val_type.is_unknown());
+        assert!(val_type.is_unknown());
 
-            add_comment(cb, "guard object is nil");
-            cmp(cb, REG0, imm_opnd(Qnil.into()));
-            jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        add_comment(cb, "guard object is nil");
+        cmp(cb, REG0, imm_opnd(Qnil.into()));
+        jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
 
-            ctx.upgrade_opnd_type(insn_opnd, Type::Nil);
-        }
+        ctx.upgrade_opnd_type(insn_opnd, Type::Nil);
     } else if unsafe { known_klass == rb_cTrueClass } {
         assert!(!val_type.is_heap());
-        if val_type != Type::True {
-            assert!(val_type.is_unknown());
+        assert!(val_type.is_unknown());
 
-            add_comment(cb, "guard object is true");
-            cmp(cb, REG0, imm_opnd(Qtrue.into()));
-            jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        add_comment(cb, "guard object is true");
+        cmp(cb, REG0, imm_opnd(Qtrue.into()));
+        jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
 
-            ctx.upgrade_opnd_type(insn_opnd, Type::True);
-        }
+        ctx.upgrade_opnd_type(insn_opnd, Type::True);
     } else if unsafe { known_klass == rb_cFalseClass } {
         assert!(!val_type.is_heap());
-        if val_type != Type::False {
-            assert!(val_type.is_unknown());
+        assert!(val_type.is_unknown());
 
-            add_comment(cb, "guard object is false");
-            assert!(Qfalse.as_i32() == 0);
-            test(cb, REG0, REG0);
-            jit_chain_guard(JCC_JNZ, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        add_comment(cb, "guard object is false");
+        assert!(Qfalse.as_i32() == 0);
+        test(cb, REG0, REG0);
+        jit_chain_guard(JCC_JNZ, jit, ctx, cb, ocb, max_chain_depth, side_exit);
 
-            ctx.upgrade_opnd_type(insn_opnd, Type::False);
-        }
+        ctx.upgrade_opnd_type(insn_opnd, Type::False);
     } else if unsafe { known_klass == rb_cInteger } && sample_instance.fixnum_p() {
-        assert!(!val_type.is_heap());
         // We will guard fixnum and bignum as though they were separate classes
         // BIGNUM can be handled by the general else case below
-        if val_type != Type::Fixnum || !val_type.is_imm() {
-            assert!(val_type.is_unknown());
+        assert!(val_type.is_unknown());
 
-            add_comment(cb, "guard object is fixnum");
-            test(cb, REG0, imm_opnd(RUBY_FIXNUM_FLAG as i64));
-            jit_chain_guard(JCC_JZ, jit, ctx, cb, ocb, max_chain_depth, side_exit);
-            ctx.upgrade_opnd_type(insn_opnd, Type::Fixnum);
-        }
+        add_comment(cb, "guard object is fixnum");
+        test(cb, REG0, imm_opnd(RUBY_FIXNUM_FLAG as i64));
+        jit_chain_guard(JCC_JZ, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        ctx.upgrade_opnd_type(insn_opnd, Type::Fixnum);
     } else if unsafe { known_klass == rb_cSymbol } && sample_instance.static_sym_p() {
         assert!(!val_type.is_heap());
         // We will guard STATIC vs DYNAMIC as though they were separate classes
         // DYNAMIC symbols can be handled by the general else case below
-        if val_type != Type::ImmSymbol || !val_type.is_imm() {
-            assert!(val_type.is_unknown());
-
-            add_comment(cb, "guard object is static symbol");
-            assert!(RUBY_SPECIAL_SHIFT == 8);
-            cmp(cb, REG0_8, uimm_opnd(RUBY_SYMBOL_FLAG as u64));
-            jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
-            ctx.upgrade_opnd_type(insn_opnd, Type::ImmSymbol);
-        }
+        assert!(val_type.is_unknown());
+
+        add_comment(cb, "guard object is static symbol");
+        assert!(RUBY_SPECIAL_SHIFT == 8);
+        cmp(cb, REG0_8, uimm_opnd(RUBY_SYMBOL_FLAG as u64));
+        jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        ctx.upgrade_opnd_type(insn_opnd, Type::ImmSymbol);
     } else if unsafe { known_klass == rb_cFloat } && sample_instance.flonum_p() {
         assert!(!val_type.is_heap());
-        if val_type != Type::Flonum || !val_type.is_imm() {
-            assert!(val_type.is_unknown());
-
-            // We will guard flonum vs heap float as though they were separate classes
-            add_comment(cb, "guard object is flonum");
-            mov(cb, REG1, REG0);
-            and(cb, REG1, uimm_opnd(RUBY_FLONUM_MASK as u64));
-            cmp(cb, REG1, uimm_opnd(RUBY_FLONUM_FLAG as u64));
-            jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
-            ctx.upgrade_opnd_type(insn_opnd, Type::Flonum);
-        }
+        assert!(val_type.is_unknown());
+
+        // We will guard flonum vs heap float as though they were separate classes
+        add_comment(cb, "guard object is flonum");
+        mov(cb, REG1, REG0);
+        and(cb, REG1, uimm_opnd(RUBY_FLONUM_MASK as u64));
+        cmp(cb, REG1, uimm_opnd(RUBY_FLONUM_FLAG as u64));
+        jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+        ctx.upgrade_opnd_type(insn_opnd, Type::Flonum);
     } else if unsafe {
         FL_TEST(known_klass, VALUE(RUBY_FL_SINGLETON as usize)) != VALUE(0)
             && sample_instance == rb_attr_get(known_klass, id__attached__ as ID)
@@ -3496,11 +3482,6 @@ fn jit_guard_known_klass( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3482
         jit_mov_gc_ptr(jit, cb, REG1, sample_instance);
         cmp(cb, REG0, REG1);
         jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_ch (... truncated)

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

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