ZJIT: Don't push frame for Hash#size (#14871)

`Hash#size` was not in "Top-20 not annotated C methods" on lobsters so our before / after benchmarks are not very helpful for this change.
<details>
<summary>Before</summary>
<br>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (60.9% of total 10,963,289):
                               Kernel#is_a?: 1,047,725 ( 9.6%)
                                  String#<<:   861,497 ( 7.9%)
                                   Hash#[]=:   740,725 ( 6.8%)
                              Regexp#match?:   398,297 ( 3.6%)
                              String#empty?:   354,809 ( 3.2%)
                                  Hash#key?:   349,173 ( 3.2%)
                         String#start_with?:   337,387 ( 3.1%)
                         Kernel#respond_to?:   321,134 ( 2.9%)
                              TrueClass#===:   239,657 ( 2.2%)
                 ObjectSpace::WeakKeyMap#[]:   238,988 ( 2.2%)
                             FalseClass#===:   234,777 ( 2.1%)
                             Array#include?:   213,229 ( 1.9%)
                        Kernel#block_given?:   181,801 ( 1.7%)
                                 Kernel#dup:   179,349 ( 1.6%)
                            Kernel#kind_of?:   174,710 ( 1.6%)
                             BasicObject#!=:   174,448 ( 1.6%)
                                 String.new:   167,716 ( 1.5%)
                                 Hash#fetch:   160,704 ( 1.5%)
                                  String#==:   158,858 ( 1.4%)
                      Process.clock_gettime:   145,002 ( 1.3%)
Top-20 not annotated C methods (61.8% of total 11,128,431):
                               Kernel#is_a?: 1,226,218 (11.0%)
                                  String#<<:   861,497 ( 7.7%)
                                   Hash#[]=:   740,904 ( 6.7%)
                              Regexp#match?:   398,297 ( 3.6%)
                              String#empty?:   362,047 ( 3.3%)
                                  Hash#key?:   349,173 ( 3.1%)
                         String#start_with?:   337,387 ( 3.0%)
                         Kernel#respond_to?:   321,134 ( 2.9%)
                              TrueClass#===:   239,657 ( 2.2%)
                 ObjectSpace::WeakKeyMap#[]:   238,988 ( 2.1%)
                             FalseClass#===:   234,777 ( 2.1%)
                             Array#include?:   213,229 ( 1.9%)
                        Kernel#block_given?:   191,670 ( 1.7%)
                                 Kernel#dup:   179,356 ( 1.6%)
                            Kernel#kind_of?:   174,745 ( 1.6%)
                             BasicObject#!=:   174,632 ( 1.6%)
                                 String.new:   167,716 ( 1.5%)
                                  String#==:   164,789 ( 1.5%)
                                 Hash#fetch:   160,704 ( 1.4%)
                      Process.clock_gettime:   145,002 ( 1.3%)
Top-2 not optimized method types for send (100.0% of total 62,854):
  cfunc: 47,647 (75.8%)
   iseq: 15,207 (24.2%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,497,956):
       iseq: 2,236,049 (49.7%)
    bmethod:   993,299 (22.1%)
  optimized:   949,781 (21.1%)
      alias:   313,166 ( 7.0%)
       null:     5,106 ( 0.1%)
      cfunc:       555 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,255,830):
             invokesuper: 2,371,027 (55.7%)
             invokeblock:   811,314 (19.1%)
             sendforward:   506,486 (11.9%)
                  opt_eq:   415,294 ( 9.8%)
                opt_plus:    77,090 ( 1.8%)
               opt_minus:    36,228 ( 0.9%)
  opt_send_without_block:    20,297 ( 0.5%)
                 opt_neq:     7,248 ( 0.2%)
                opt_mult:     6,754 ( 0.2%)
                  opt_or:     3,617 ( 0.1%)
                  opt_lt:       348 ( 0.0%)
                  opt_ge:        91 ( 0.0%)
                  opt_gt:        36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 24,945,472):
                send_without_block_polymorphic: 9,308,731 (37.3%)
                              send_no_profiles: 5,907,934 (23.7%)
  send_without_block_not_optimized_method_type: 4,497,956 (18.0%)
                     not_optimized_instruction: 4,255,830 (17.1%)
                send_without_block_no_profiles:   887,000 ( 3.6%)
                send_not_optimized_method_type:    62,854 ( 0.3%)
       send_without_block_cfunc_array_variadic:    15,138 ( 0.1%)
                      obj_to_string_not_string:     9,767 ( 0.0%)
       send_without_block_direct_too_many_args:       262 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 707,558):
         expandarray: 347,142 (49.1%)
        checkkeyword: 190,708 (27.0%)
    getclassvariable:  59,296 ( 8.4%)
       getblockparam:  49,122 ( 6.9%)
  invokesuperforward:  48,163 ( 6.8%)
   opt_duparray_send:  11,978 ( 1.7%)
         getconstant:     840 ( 0.1%)
          checkmatch:     290 ( 0.0%)
                once:      19 ( 0.0%)
Top-2 compile error reasons (100.0% of total 3,649,990):
  register_spill_on_alloc: 3,428,507 (93.9%)
  register_spill_on_ccall:   221,483 ( 6.1%)
Top-17 side exit reasons (100.0% of total 10,833,336):
                        compile_error: 3,649,990 (33.7%)
                   guard_type_failure: 2,681,177 (24.7%)
                  guard_shape_failure: 1,897,864 (17.5%)
                  unhandled_yarv_insn:   707,558 ( 6.5%)
  block_param_proxy_not_iseq_or_ifunc:   536,761 ( 5.0%)
                      unhandled_kwarg:   456,394 ( 4.2%)
                unknown_newarray_send:   314,671 ( 2.9%)
     patchpoint_stable_constant_names:   229,825 ( 2.1%)
                      unhandled_splat:   129,577 ( 1.2%)
        patchpoint_no_singleton_class:   108,465 ( 1.0%)
                   unhandled_hir_insn:    76,401 ( 0.7%)
          patchpoint_method_redefined:    20,493 ( 0.2%)
           block_param_proxy_modified:    20,204 ( 0.2%)
              patchpoint_no_ep_escape:     3,765 ( 0.0%)
               obj_to_string_fallback:       156 ( 0.0%)
               guard_type_not_failure:        22 ( 0.0%)
                            interrupt:        13 ( 0.0%)
                             send_count: 67,968,616
                     dynamic_send_count: 24,945,472 (36.7%)
                   optimized_send_count: 43,023,144 (63.3%)
              iseq_optimized_send_count: 18,621,234 (27.4%)
      inline_cfunc_optimized_send_count: 13,438,621 (19.8%)
non_variadic_cfunc_optimized_send_count:  8,333,523 (12.3%)
    variadic_cfunc_optimized_send_count:  2,629,766 ( 3.9%)
dynamic_getivar_count:                        7,351,238
dynamic_setivar_count:                        7,267,701
compiled_iseq_count:                              4,772
failed_iseq_count:                                  465
compile_time:                                   7,006ms
profile_time:                                      52ms
gc_time:                                           46ms
invalidation_time:                                123ms
vm_write_pc_count:                           63,668,147
vm_write_sp_count:                           62,343,075
vm_write_locals_count:                       62,343,075
vm_write_stack_count:                        62,343,075
vm_write_to_parent_iseq_local_count:            292,130
vm_read_from_parent_iseq_local_count:         6,623,223
code_region_bytes:                           22,724,608
side_exit_count:                             10,833,336
total_insn_count:                           519,162,657
vm_insn_count:                              164,942,584
zjit_insn_count:                            354,220,073
ratio_in_zjit:                                    68.2%
```

</details>
<details>
<summary>After</summary>
<br>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (61.1% of total 10,915,774):
                               Kernel#is_a?: 1,027,957 ( 9.4%)
                                  String#<<:   851,954 ( 7.8%)
                                   Hash#[]=:   740,863 ( 6.8%)
                              Regexp#match?:   398,265 ( 3.6%)
                              String#empty?:   353,775 ( 3.2%)
                                  Hash#key?:   349,161 ( 3.2%)
                         String#start_with?:   337,386 ( 3.1%)
                         Kernel#respond_to?:   316,003 ( 2.9%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.2%)
                              TrueClass#===:   235,771 ( 2.2%)
                             FalseClass#===:   231,144 ( 2.1%)
                             Array#include?:   211,340 ( 1.9%)
                                 Hash#fetch:   204,703 ( 1.9%)
                        Kernel#block_given?:   181,791 ( 1.7%)
                                 Kernel#dup:   179,337 ( 1.6%)
                             BasicObject#!=:   174,430 ( 1.6%)
                                 String.new:   166,696 ( 1.5%)
                            Kernel#kind_of?:   165,600 ( 1.5%)
                                  String#==:   154,751 ( 1.4%)
                      Process.clock_gettime:   144,992 ( 1.3%)
Top-20 not annotated C methods (62.0% of total 11,078,184):
                               Kernel#is_a?: 1,209,975 (10.9%)
                                  String#<<:   851,954 ( 7.7%)
                                   Hash#[]=:   741,042 ( 6.7%)
                              Regexp#match?:   398,265 ( 3.6%)
                              String#empty?:   361,013 ( 3.3%)
                                  Hash#key?:   349,161 ( 3.2%)
                         String#start_with?:   337,386 ( 3.0%)
                         Kernel#respond_to?:   316,003 ( 2.9%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.2%)
                              TrueClass#===:   235,771 ( 2.1%)
                             FalseClass#===:   231,144 ( 2.1%)
                             Array#include?:   211,340 ( 1.9%)
                                 Hash#fetch:   204,703 ( 1.8%)
                        Kernel#block_given?:   191,660 ( 1.7%)
                                 Kernel#dup:   179,344 ( 1.6%)
                             BasicObject#!=:   174,614 ( 1.6%)
                                 String.new:   166,696 ( 1.5%)
                            Kernel#kind_of?:   165,634 ( 1.5%)
                                  String#==:   160,682 ( 1.5%)
                      Process.clock_gettime:   144,992 ( 1.3%)
Top-2 not optimized method types for send (100.0% of total 71,084):
  cfunc: 47,638 (67.0%)
   iseq: 23,446 (33.0%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,469,252):
       iseq: 2,217,500 (49.6%)
    bmethod:   985,636 (22.1%)
  optimized:   949,705 (21.2%)
      alias:   310,751 ( 7.0%)
       null:     5,106 ( 0.1%)
      cfunc:       554 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,264,988):
             invokesuper: 2,346,307 (55.0%)
             invokeblock:   809,211 (19.0%)
             sendforward:   505,452 (11.9%)
                  opt_eq:   454,244 (10.7%)
                opt_plus:    74,059 ( 1.7%)
               opt_minus:    36,228 ( 0.8%)
  opt_send_without_block:    21,396 ( 0.5%)
                 opt_neq:     7,247 ( 0.2%)
                opt_mult:     6,752 ( 0.2%)
                  opt_or:     3,617 ( 0.1%)
                  opt_lt:       348 ( 0.0%)
                  opt_ge:        91 ( 0.0%)
                  opt_gt:        36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 25,044,791):
                send_without_block_polymorphic: 9,439,021 (37.7%)
                              send_no_profiles: 5,892,924 (23.5%)
  send_without_block_not_optimized_method_type: 4,469,252 (17.8%)
                     not_optimized_instruction: 4,264,988 (17.0%)
                send_without_block_no_profiles:   882,357 ( 3.5%)
                send_not_optimized_method_type:    71,084 ( 0.3%)
       send_without_block_cfunc_array_variadic:    15,136 ( 0.1%)
                      obj_to_string_not_string:     9,767 ( 0.0%)
       send_without_block_direct_too_many_args:       262 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 688,760):
         expandarray: 328,369 (47.7%)
        checkkeyword: 190,697 (27.7%)
    getclassvariable:  59,286 ( 8.6%)
       getblockparam:  49,119 ( 7.1%)
  invokesuperforward:  48,162 ( 7.0%)
   opt_duparray_send:  11,978 ( 1.7%)
         getconstant:     840 ( 0.1%)
          checkmatch:     290 ( 0.0%)
                once:      19 ( 0.0%)
Top-2 compile error reasons (100.0% of total 3,642,051):
  register_spill_on_alloc: 3,420,578 (93.9%)
  register_spill_on_ccall:   221,473 ( 6.1%)
Top-17 side exit reasons (100.0% of total 10,740,844):
                        compile_error: 3,642,051 (33.9%)
                   guard_type_failure: 2,624,731 (24.4%)
                  guard_shape_failure: 1,902,123 (17.7%)
                  unhandled_yarv_insn:   688,760 ( 6.4%)
  block_param_proxy_not_iseq_or_ifunc:   534,951 ( 5.0%)
                      unhandled_kwarg:   455,354 ( 4.2%)
                unknown_newarray_send:   314,667 ( 2.9%)
     patchpoint_stable_constant_names:   227,790 ( 2.1%)
                      unhandled_splat:   121,916 ( 1.1%)
        patchpoint_no_singleton_class:   108,465 ( 1.0%)
                   unhandled_hir_insn:    76,397 ( 0.7%)
          patchpoint_method_redefined:    20,487 ( 0.2%)
           block_param_proxy_modified:    19,193 ( 0.2%)
              patchpoint_no_ep_escape:     3,765 ( 0.0%)
               obj_to_string_fallback:       156 ( 0.0%)
               guard_type_not_failure:        22 ( 0.0%)
                            interrupt:        16 ( 0.0%)
                             send_count: 67,576,368
                     dynamic_send_count: 25,044,791 (37.1%)
                   optimized_send_count: 42,531,577 (62.9%)
              iseq_optimized_send_count: 18,461,332 (27.3%)
      inline_cfunc_optimized_send_count: 13,154,471 (19.5%)
non_variadic_cfunc_optimized_send_count:  8,243,438 (12.2%)
    variadic_cfunc_optimized_send_count:  2,672,336 ( 4.0%)
dynamic_getivar_count:                        7,322,001
dynamic_setivar_count:                        7,230,445
compiled_iseq_count:                              4,771
failed_iseq_count:                                  466
compile_time:                                   7,134ms
profile_time:                                      52ms
gc_time:                                           46ms
invalidation_time:                                123ms
vm_write_pc_count:                           63,337,758
vm_write_sp_count:                           62,014,782
vm_write_locals_count:                       62,014,782
vm_write_stack_count:                        62,014,782
vm_write_to_parent_iseq_local_count:            292,458
vm_read_from_parent_iseq_local_count:         6,589,698
code_region_bytes:                           22,724,608
side_exit_count:                             10,740,844
total_insn_count:                           515,656,824
vm_insn_count:                              163,676,059
zjit_insn_count:                            351,980,765
ratio_in_zjit:                                    68.3%

```

</details>
This commit is contained in:
Jacob 2025-10-17 15:33:11 -04:00 committed by GitHub
parent 0e5cb74a00
commit 0594646c0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2025-10-17 19:33:47 +00:00
Merged-By: tekknolagi <donotemailthisaddress@bernsteinbear.com>
2 changed files with 58 additions and 0 deletions

View File

@ -201,6 +201,7 @@ pub fn init() -> Annotations {
annotate!(rb_cArray, "join", types::StringExact);
annotate!(rb_cArray, "[]", inline_array_aref);
annotate!(rb_cHash, "[]", inline_hash_aref);
annotate!(rb_cHash, "size", types::Fixnum, no_gc, leaf, elidable);
annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable);
annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable);
annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable);

View File

@ -13390,4 +13390,61 @@ mod opt_tests {
Return v13
");
}
#[test]
fn test_specialize_hash_size() {
eval("
def test(hash) = hash.size
test({foo: 3, bar: 1, baz: 4})
");
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:2:
bb0():
EntryPoint interpreter
v1:BasicObject = LoadSelf
v2:BasicObject = GetLocal l0, SP@4
Jump bb2(v1, v2)
bb1(v5:BasicObject, v6:BasicObject):
EntryPoint JIT(0)
Jump bb2(v5, v6)
bb2(v8:BasicObject, v9:BasicObject):
PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010)
PatchPoint NoSingletonClass(Hash@0x1000)
v25:HashExact = GuardType v9, HashExact
IncrCounter inline_cfunc_optimized_send_count
v27:Fixnum = CCall size@0x1038, v25
CheckInterrupts
Return v27
");
}
#[test]
fn test_eliminate_hash_size() {
eval("
def test(hash)
hash.size
5
end
test({foo: 3, bar: 1, baz: 4})
");
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:3:
bb0():
EntryPoint interpreter
v1:BasicObject = LoadSelf
v2:BasicObject = GetLocal l0, SP@4
Jump bb2(v1, v2)
bb1(v5:BasicObject, v6:BasicObject):
EntryPoint JIT(0)
Jump bb2(v5, v6)
bb2(v8:BasicObject, v9:BasicObject):
PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010)
PatchPoint NoSingletonClass(Hash@0x1000)
v28:HashExact = GuardType v9, HashExact
IncrCounter inline_cfunc_optimized_send_count
v19:Fixnum[5] = Const Value(5)
CheckInterrupts
Return v19
");
}
}