Project

General

Profile

Actions

Bug #22126

closed

Stack underflow for partial DCE and loops

Bug #22126: Stack underflow for partial DCE and loops

Added by viralpraxis (Iaroslav Kurbatov) about 23 hours ago. Updated about 12 hours ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-06-23T13:29:36Z master 13fe77dd2b) +PRISM [x86_64-linux]
[ruby-core:125818]

Description

ruby --parser prism -e  'if []; else; a => [*, 42, *]; end'
-- raw disasm--------
   trace: 1
   0000 putnil                                                           (   1)
   0001 leave                                                            (   1)
 <L018> [sp: 1, unremovable: 0, refcnt: 1]
   0002 dup                                                              (   1)
   0003 topn                 2                                           (   1)
   0005 opt_le               <calldata:<=, 1>                            (   1)
   0007 branchunless         <L021>                                      (   1)
   0009 topn                 3                                           (   1)
   0011 topn                 1                                           (   1)
   0013 opt_aref             <calldata:[], 1>                            (   1)
   0015 putobject            42                                          (   1)
   0017 dupn                 2                                           (   1)
   0019 checkmatch           2                                           (   1)
   0021 dup                                                              (   1)
   0022 branchif             <L023>                                      (   1)
   0024 putspecialobject     1                                           (   1)
   0026 putobject            "%p === %p does not return true"            (   1)
   0028 topn                 3                                           (   1)
   0030 topn                 5                                           (   1)
   0032 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0034 setn                 10                                          (   1)
   0036 putobject            false                                       (   1)
   0038 setn                 12                                          (   1)
   0040 pop                                                              (   1)
   0041 pop                                                              (   1)
 <L023> [sp: 4, unremovable: 0, refcnt: 1]
   0042 setn                 2                                           (   1)
   0044 pop                                                              (   1)
   0045 pop                                                              (   1)
   0046 branchif             <L020>                                      (   1)
   0048 putobject_INT2FIX_1_                                             (   1)
   0049 opt_plus             <calldata:+, 1>                             (   1)
   0051 jump                 <L018>                                      (   1)
 <L021> [sp: 1, unremovable: 0, refcnt: 1]
*  0053 adjuststack          3                                           (   1)
   0055 putspecialobject     1                                           (   1)
   0057 putobject            "%p does not match to find pattern"         (   1)
   0059 topn                 2                                           (   1)
   0061 opt_send_without_block <calldata:core#sprintf, 2>                (   1)
   0063 setn                 4                                           (   1)
   0065 putobject            false                                       (   1)
   0067 setn                 6                                           (   1)
   0069 pop                                                              (   1)
   0070 pop                                                              (   1)
   0071 jump                 <L012>                                      (   1)
 <L020> [sp: 1, unremovable: 0, refcnt: 1]
   0073 adjuststack          3                                           (   1)
   0075 pop                                                              (   1)
   0076 jump                 <L003>                                      (   1)
 <L012> [sp: -1, unremovable: 0, refcnt: 1]
   0078 pop                                                              (   1)
   0079 putspecialobject     1                                           (   1)
   0081 topn                 4                                           (   1)
   0083 branchif             <L024>                                      (   1)
   0085 putobject            NoMatchingPatternError                      (   1)
   0087 putspecialobject     1                                           (   1)
   0089 putobject            "%p: %s"                                    (   1)
   0091 topn                 4                                           (   1)
   0093 topn                 7                                           (   1)
   0095 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0097 opt_send_without_block <calldata:core#raise, 2>                  (   1)
   0099 jump                 <L025>                                      (   1)
 <L024> [sp: -1, unremovable: 0, refcnt: 1]
   0101 putobject            NoMatchingPatternKeyError                   (   1)
   0103 putspecialobject     1                                           (   1)
   0105 putobject            "%p: %s"                                    (   1)
   0107 topn                 4                                           (   1)
   0109 topn                 7                                           (   1)
   0111 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0113 topn                 7                                           (   1)
   0115 topn                 9                                           (   1)
   0117 opt_send_without_block <calldata:new, 3>                         (   1)
   0119 opt_send_without_block <calldata:core#raise, 1>                  (   1)
 <L025> [sp: -1, unremovable: 0, refcnt: 1]
   0121 adjuststack          7                                           (   1)
   0123 putnil                                                           (   1)
   0124 leave                                                            (   1)
 <L003> [sp: -1, unremovable: 0, refcnt: 1]
   0125 adjuststack          6                                           (   1)
   0127 putnil                                                           (   1)
   0128 leave                                                            (   1)
---------------------
-e:1: argument stack underflow (-2)
-e: compile error (SyntaxError)

proposed patch: https://github.com/ruby/ruby/pull/17449

every version since 3.4.0-preview2 is affected

Updated by nobu (Nobuyoshi Nakada) about 12 hours ago Actions #1 [ruby-core:125829]

  • Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED

viralpraxis (Iaroslav Kurbatov) wrote:

every version since 3.4.0-preview2 is affected

Ruby 3.3 with prism hangs up instead of stack underflow.

Updated by viralpraxis (Iaroslav Kurbatov) about 12 hours ago Actions #2

  • Status changed from Open to Closed

Applied in changeset git|1f473ed633a364f8c89b69a76adfb22a4bf21f96.


[Bug #22126] Fix partial DCE with loop back-edges

The old remove_unreachable_chunk walked the dead code once and tried
to do two things at the same time -- count label references inside that
chunk and figure out what exactly to delete.

That breaks for loops -- the loop label comes before the jump back to it;
on the first walk, the label looks like it has no references inside the
chunk yet, so the optimizer thinks something outside the dead code still
needs it. It stops there and deletes only the setup code before the loop,
leaving the loop body behind.

I've changed the algorithm to use two passes to mitigate the issue;
I believe the performance overhead is neglible

$ ruby --parser parse.y -e 'if []; else; a => [*, 42, *]; end'
$ ruby --parser prism -e   'if []; else; a => [*, 42, *]; end'
-- raw disasm--------
   trace: 1
   0000 putnil                                                           (   1)
   0001 leave                                                            (   1)
 <L018> [sp: 1, unremovable: 0, refcnt: 1]
   0002 dup                                                              (   1)
   0003 topn                 2                                           (   1)
   0005 opt_le               <calldata:<=, 1>                            (   1)
   0007 branchunless         <L021>                                      (   1)
   0009 topn                 3                                           (   1)
   0011 topn                 1                                           (   1)
   0013 opt_aref             <calldata:[], 1>                            (   1)
   0015 putobject            42                                          (   1)
   0017 dupn                 2                                           (   1)
   0019 checkmatch           2                                           (   1)
   0021 dup                                                              (   1)
   0022 branchif             <L023>                                      (   1)
   0024 putspecialobject     1                                           (   1)
   0026 putobject            "%p === %p does not return true"            (   1)
   0028 topn                 3                                           (   1)
   0030 topn                 5                                           (   1)
   0032 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0034 setn                 10                                          (   1)
   0036 putobject            false                                       (   1)
   0038 setn                 12                                          (   1)
   0040 pop                                                              (   1)
   0041 pop                                                              (   1)
 <L023> [sp: 4, unremovable: 0, refcnt: 1]
   0042 setn                 2                                           (   1)
   0044 pop                                                              (   1)
   0045 pop                                                              (   1)
   0046 branchif             <L020>                                      (   1)
   0048 putobject_INT2FIX_1_                                             (   1)
   0049 opt_plus             <calldata:+, 1>                             (   1)
   0051 jump                 <L018>                                      (   1)
 <L021> [sp: 1, unremovable: 0, refcnt: 1]
*  0053 adjuststack          3                                           (   1)
   0055 putspecialobject     1                                           (   1)
   0057 putobject            "%p does not match to find pattern"         (   1)
   0059 topn                 2                                           (   1)
   0061 opt_send_without_block <calldata:core#sprintf, 2>                (   1)
   0063 setn                 4                                           (   1)
   0065 putobject            false                                       (   1)
   0067 setn                 6                                           (   1)
   0069 pop                                                              (   1)
   0070 pop                                                              (   1)
   0071 jump                 <L012>                                      (   1)
 <L020> [sp: 1, unremovable: 0, refcnt: 1]
   0073 adjuststack          3                                           (   1)
   0075 pop                                                              (   1)
   0076 jump                 <L003>                                      (   1)
 <L012> [sp: -1, unremovable: 0, refcnt: 1]
   0078 pop                                                              (   1)
   0079 putspecialobject     1                                           (   1)
   0081 topn                 4                                           (   1)
   0083 branchif             <L024>                                      (   1)
   0085 putobject            NoMatchingPatternError                      (   1)
   0087 putspecialobject     1                                           (   1)
   0089 putobject            "%p: %s"                                    (   1)
   0091 topn                 4                                           (   1)
   0093 topn                 7                                           (   1)
   0095 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0097 opt_send_without_block <calldata:core#raise, 2>                  (   1)
   0099 jump                 <L025>                                      (   1)
 <L024> [sp: -1, unremovable: 0, refcnt: 1]
   0101 putobject            NoMatchingPatternKeyError                   (   1)
   0103 putspecialobject     1                                           (   1)
   0105 putobject            "%p: %s"                                    (   1)
   0107 topn                 4                                           (   1)
   0109 topn                 7                                           (   1)
   0111 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
   0113 topn                 7                                           (   1)
   0115 topn                 9                                           (   1)
   0117 opt_send_without_block <calldata:new, 3>                         (   1)
   0119 opt_send_without_block <calldata:core#raise, 1>                  (   1)
 <L025> [sp: -1, unremovable: 0, refcnt: 1]
   0121 adjuststack          7                                           (   1)
   0123 putnil                                                           (   1)
   0124 leave                                                            (   1)
 <L003> [sp: -1, unremovable: 0, refcnt: 1]
   0125 adjuststack          6                                           (   1)
   0127 putnil                                                           (   1)
   0128 leave                                                            (   1)
---------------------
-e:1: argument stack underflow (-2)
-e: compile error (SyntaxError)

Reproducible on every version since 3.4.0-preview2
(docker run -e ALL_RUBY_SHOW_DUP=yes -e ALL_RUBY_SINCE=3.4 --rm rubylang/all-ruby ./all-ruby -We 'if []; else; a => [*, 42, *]; end')

There seem to be another issue related to Prism's DCE & loops:

$ ruby --parser parse.y --dump insn -e 'if []; else; while true; end; end'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,33)>
0000 putnil                                                           (   1)[Li]
0001 leave
$ ruby --parser prism --dump insn -e 'if []; else; while true; end; end'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,33)>
0000 newarray                               0                         (   1)[Li]
0002 branchunless                           11
0004 putnil
0005 leave
0006 pop
0007 jump                                   11
0009 putnil
0010 pop
0011 jump                                   11
0013 putnil
0014 leave

the reason branchunless is not eliminated is that prism does not emit
NODE_ZLIST like parse.y does and instead emits PM_ARRAY_NODE-0
which is not treated as a always-truthy value by the peephole-optmizer.

Actions

Also available in: PDF Atom