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

ruby-changes:50573

From: k0kubun <ko1@a...>
Date: Sat, 10 Mar 2018 23:52:24 +0900 (JST)
Subject: [ruby-changes:50573] k0kubun:r62717 (trunk): compile.c: mark all ISeq ancestors as catch_except_p

k0kubun	2018-03-10 23:52:12 +0900 (Sat, 10 Mar 2018)

  New Revision: 62717

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62717

  Log:
    compile.c: mark all ISeq ancestors as catch_except_p
    
    This change assumes that continuously reading `parent_iseq` from block
    ISeq would reach non-block ISeq finally.
    
    test/ruby/test_jit.rb: add test that catches 2-depth exception
    
    Combination of r62654 and r62678 caused following error in this test.
    
    -e:12:in `wrapper': Stack consistency error (sp: 14, bp: 13) (fatal)
    == disasm: #<ISeq:wrapper@-e:10 (10,0)-(12,3)> (catch: FALSE)===========
    local table (size: 2, argc: 2 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
    [ 2] paths<Arg> [ 1] prefixes<Arg>
    0000 putself                                                          (  11)[LiCa]
    0001 getlocal_WC_0    paths
    0003 getlocal_WC_0    prefixes
    0005 opt_send_without_block <callinfo!mid:catch_true, argc:2, FCALL|ARGS_SIMPLE>, <callcache>
    0008 leave                                                            (  12)[Re]
    
    As you can see, it says `catch: FALSE`, but obviously it catches
    exception raised from `return path`.
    
    As of r62655, it was kind of intentional because I only cared about
    expiration of JIT-ed frame and I've thought calling `vm_exec` is only
    needed once for it. So r62654 was NOT actually checking if it may catch
    exception.
    
    But for r62678, obviously we should set catch_except_p=TRUE for all
    ISeqs which may catch exception. Otherwise catch table lookup would
    fail.
    
    With this bugfix, code generated by r62655 might be worse, but at least
    while loop can be marked as `catch: FALSE` as expected.

  Modified files:
    trunk/compile.c
    trunk/test/ruby/test_jit.rb
Index: compile.c
===================================================================
--- compile.c	(revision 62716)
+++ compile.c	(revision 62717)
@@ -1246,6 +1246,15 @@ new_child_iseq_ifunc(rb_iseq_t *iseq, co https://github.com/ruby/ruby/blob/trunk/compile.c#L1246
     return ret_iseq;
 }
 
+static void
+set_catch_except_p(struct rb_iseq_constant_body *body)
+{
+    body->catch_except_p = TRUE;
+    if (body->parent_iseq != NULL) {
+        set_catch_except_p(body->parent_iseq->body);
+    }
+}
+
 /* Set body->catch_except_p to TRUE if the ISeq may catch an exception. If it is FALSE,
    JIT-ed code may be optimized.  If we are extremely conservative, we should set TRUE
    if catch table exists.  But we want to optimize while loop, which always has catch
@@ -1273,7 +1282,7 @@ update_catch_except_flags(struct rb_iseq https://github.com/ruby/ruby/blob/trunk/compile.c#L1282
 #endif
             if (insn == BIN(throw)) {
                 struct rb_iseq_constant_body *parent_body = body->parent_iseq->body;
-                parent_body->catch_except_p = TRUE;
+                set_catch_except_p(parent_body);
             }
             pos += insn_len(insn);
         }
Index: test/ruby/test_jit.rb
===================================================================
--- test/ruby/test_jit.rb	(revision 62716)
+++ test/ruby/test_jit.rb	(revision 62717)
@@ -510,6 +510,25 @@ class TestJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_jit.rb#L510
     end;
   end
 
+  def test_catching_deep_exception
+    assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 4)
+    begin;
+      def catch_true(paths, prefixes) # catch_except_p: TRUE
+        prefixes.each do |prefix| # catch_except_p: TRUE
+          paths.each do |path| # catch_except_p: FALSE
+            return path
+          end
+        end
+      end
+
+      def wrapper(paths, prefixes)
+        catch_true(paths, prefixes)
+      end
+
+      print wrapper(['1'], ['2'])
+    end;
+  end
+
   private
 
   # The shortest way to test one proc

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

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