Project

General

Profile

Misc #20630

Updated by zack.ref@gmail.com (Zack Deveau) 5 months ago

### Description 

 I would like to propose two potential optimizations to the way we currently compile `NODE_CONST` nodes in `compile.c`. I've included commits for each on a related PR. 

 - PR: https://github.com/ruby/ruby/pull/11154 
 - `NODE_CONST` compilation: [9b6d613](https://github.com/ruby/ruby/commit/9b6d613e19c1132ace64583ac23c94fcbc77f1df) 
 - `opt_getconstant_path` peephole optimization: [b8ece8c](https://github.com/ruby/ruby/commit/b8ece8ca6e2d60cb7c627927aa89c1e8980ad0eb) 

 These commits pass `test-all` locally. 

 ### `NODE_CONST` Compilation 

 From what I can tell `NODE_CONST` doesn't appear to follow the conventional use of `popped` found in other similar nodes. For example `NODE_IVAR`, when `popped` (return value not used), will avoid adding the YARV instruction altogether: 
 ```C 
       case NODE_IVAR:{ 
         debugi("nd_vid", RNODE_IVAR(node)->nd_vid); 
         if (!popped) { 
             ADD_INSN2(ret, node, getinstancevariable, 
                       ID2SYM(RNODE_IVAR(node)->nd_vid), 
                       get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid)); 
         } 
         break; 
       } 

 ``` 

 When compiling a `NODE_CONST` that is `popped`, we instead add either `get_constant` or the optimized `opt_getconstant_path` instruction, followed by adding a `pop`. 

 I've modified it to follow the convention found elsewhere to avoid adding either instruction(s) in cases where we don't need the return value. `test-all` passes locally and I can't think of meaningful side effects (other than we avoid exercising the cache). Please let me know if I'm missing any! 

 ### `opt_getconstant_path` peephole optimization 

 `iseq_peephole_optimize` includes an optimization for `insn -> pop` sequences. Most of the `get_x` instructions are included but we don't appear to include `opt_getconstant_path`. (potentially since it was only recently added in 2022 by @jhawthorn ?) 

 I've included it and attempted to account for any meaningful side effects (here again I can't think of any other than we avoid exercising the cache). Please let me know if I missed any. 


 ### Results 

 `test.rb` 
 ```Ruby 
 NETSCAPE = "navigator" 
 [NETSCAPE, NETSCAPE, NETSCAPE, NETSCAPE] 
 1 
 ``` 

 `ruby 3.4.0dev (2024-07-11T19:49:14Z master 6fc83118bb) [arm64-darwin23]` 
 ``` 
 💾 ➜     ruby --dump insn ./test.rb 
 == disasm: #<ISeq:<main>@./test.rb:1 (1,0)-(3,1)> 
 0000 putchilledstring                         "navigator"                 (     1)[Li] 
 0002 putspecialobject                         3 
 0004 setconstant                              :NETSCAPE 
 0006 opt_getconstant_path                     <ic:0 NETSCAPE>             (     2)[Li] 
 0008 pop 
 0009 opt_getconstant_path                     <ic:1 NETSCAPE> 
 0011 pop 
 0012 opt_getconstant_path                     <ic:2 NETSCAPE> 
 0014 pop 
 0015 opt_getconstant_path                     <ic:3 NETSCAPE> 
 0017 pop 
 0018 putobject_INT2FIX_1_                                               (     3)[Li] 
 0019 leave 
 ``` 


 `with optimizations` 
 ``` 
 💾 ➜     ./build/miniruby --dump insn ./test.rb 
 == disasm: #<ISeq:<main>@./test.rb:1 (1,0)-(3,1)> 
 0000 putchilledstring                         "navigator"                 (     1)[Li] 
 0002 putspecialobject                         3 
 0004 setconstant                              :NETSCAPE 
 0006 putobject_INT2FIX_1_                                               (     3)[Li] 
 0007 leave 
 ``` 

Back