diff options
| author | Aiden Fox Ivey <aiden.foxivey@shopify.com> | 2025-10-15 14:00:44 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-15 18:00:44 +0000 |
| commit | 6a94632d4a92c17f97acdc2856a36a187092efcd (patch) | |
| tree | 7bfab7f03b85e9b3d89c584be55bb937a51a58ae | |
| parent | 27ff586152321a43cc678f65e5f489a2c0f1e9af (diff) | |
ZJIT: Add HashAref to HIR and inline Hash#[] to HashAref (#14838)
Fixes https://github.com/Shopify/ruby/issues/793
## Testing on `liquid-render`:
<details>
<summary>Before patch:</summary>
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (96.8% of total 20,222,783):
Kernel#respond_to?: 9,725,886 (48.1%)
Hash#key?: 4,589,528 (22.7%)
Set#include?: 1,493,789 ( 7.4%)
String#===: 616,183 ( 3.0%)
Hash#[]: 453,675 ( 2.2%)
String#<<: 386,831 ( 1.9%)
Integer#<<: 319,768 ( 1.6%)
Kernel#is_a?: 312,176 ( 1.5%)
Integer#/: 238,502 ( 1.2%)
Kernel#format: 238,502 ( 1.2%)
Array#<<: 220,724 ( 1.1%)
Class#last_match: 179,182 ( 0.9%)
Hash#[]=: 167,728 ( 0.8%)
CGI::EscapeExt#escapeHTML: 106,471 ( 0.5%)
Array#shift: 98,030 ( 0.5%)
Array#unshift: 90,851 ( 0.4%)
String#=~: 90,637 ( 0.4%)
String#start_with?: 88,122 ( 0.4%)
Regexp#===: 85,648 ( 0.4%)
String#empty?: 80,950 ( 0.4%)
Top-20 not annotated C methods (97.0% of total 20,268,253):
Kernel#respond_to?: 9,725,886 (48.0%)
Hash#key?: 4,589,528 (22.6%)
Set#include?: 1,493,789 ( 7.4%)
String#===: 616,183 ( 3.0%)
Hash#[]: 453,675 ( 2.2%)
Kernel#is_a?: 397,366 ( 2.0%)
String#<<: 386,831 ( 1.9%)
Integer#<<: 319,768 ( 1.6%)
Integer#/: 238,502 ( 1.2%)
Kernel#format: 238,502 ( 1.2%)
Array#<<: 220,724 ( 1.1%)
Class#last_match: 179,182 ( 0.9%)
Hash#[]=: 167,728 ( 0.8%)
CGI::EscapeExt#escapeHTML: 106,471 ( 0.5%)
Array#shift: 98,030 ( 0.5%)
Array#unshift: 90,851 ( 0.4%)
String#=~: 90,637 ( 0.4%)
String#start_with?: 88,122 ( 0.4%)
Regexp#===: 85,648 ( 0.4%)
String#empty?: 80,950 ( 0.4%)
Top-2 not optimized method types for send (100.0% of total 1,180):
iseq: 602 (51.0%)
cfunc: 578 (49.0%)
Top-3 not optimized method types for send_without_block (100.0% of total 4,896,785):
iseq: 4,669,764 (95.4%)
optimized: 227,001 ( 4.6%)
alias: 20 ( 0.0%)
Top-9 not optimized instructions (100.0% of total 1,255,287):
invokeblock: 430,174 (34.3%)
opt_neq: 319,471 (25.5%)
opt_and: 319,471 (25.5%)
opt_eq: 127,926 (10.2%)
opt_le: 31,238 ( 2.5%)
invokesuper: 23,409 ( 1.9%)
opt_minus: 2,934 ( 0.2%)
opt_send_without_block: 562 ( 0.0%)
opt_or: 102 ( 0.0%)
Top-7 send fallback reasons (100.0% of total 17,930,659):
send_no_profiles: 6,145,096 (34.3%)
send_without_block_polymorphic: 5,459,600 (30.4%)
send_without_block_not_optimized_method_type: 4,896,785 (27.3%)
not_optimized_instruction: 1,255,287 ( 7.0%)
send_without_block_no_profiles: 170,037 ( 0.9%)
obj_to_string_not_string: 2,674 ( 0.0%)
send_not_optimized_method_type: 1,180 ( 0.0%)
Top-3 unhandled YARV insns (100.0% of total 157,831):
getclassvariable: 157,694 (99.9%)
once: 121 ( 0.1%)
getconstant: 16 ( 0.0%)
Top-2 compile error reasons (100.0% of total 8,905,991):
register_spill_on_alloc: 8,905,891 (100.0%)
register_spill_on_ccall: 100 ( 0.0%)
Top-9 side exit reasons (100.0% of total 26,549,652):
compile_error: 8,905,991 (33.5%)
guard_shape_failure: 6,590,116 (24.8%)
guard_type_failure: 4,882,217 (18.4%)
unhandled_splat: 4,150,547 (15.6%)
unhandled_kwarg: 1,827,728 ( 6.9%)
unhandled_yarv_insn: 157,831 ( 0.6%)
unhandled_hir_insn: 34,072 ( 0.1%)
patchpoint: 1,100 ( 0.0%)
block_param_proxy_not_iseq_or_ifunc: 50 ( 0.0%)
send_count: 72,944,863
dynamic_send_count: 17,930,659 (24.6%)
optimized_send_count: 55,014,204 (75.4%)
iseq_optimized_send_count: 26,520,888 (36.4%)
inline_cfunc_optimized_send_count: 8,270,533 (11.3%)
non_variadic_cfunc_optimized_send_count: 9,344,065 (12.8%)
variadic_cfunc_optimized_send_count: 10,878,718 (14.9%)
dynamic_getivar_count: 2,171,396
dynamic_setivar_count: 1,737,553
compiled_iseq_count: 383
failed_iseq_count: 46
compile_time: 820ms
profile_time: 4ms
gc_time: 22ms
invalidation_time: 0ms
vm_write_pc_count: 71,973,068
vm_write_sp_count: 71,544,492
vm_write_locals_count: 71,544,492
vm_write_stack_count: 71,544,492
vm_write_to_parent_iseq_local_count: 1,070,897
vm_read_from_parent_iseq_local_count: 27,449,010
code_region_bytes: 2,113,536
side_exit_count: 26,549,652
total_insn_count: 908,528,764
vm_insn_count: 484,633,128
zjit_insn_count: 423,895,636
ratio_in_zjit: 46.7%
```
</details>
<details>
<summary>after patch:</summary>
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (97.2% of total 19,769,108):
Kernel#respond_to?: 9,725,886 (49.2%)
Hash#key?: 4,589,528 (23.2%)
Set#include?: 1,493,789 ( 7.6%)
String#===: 616,183 ( 3.1%)
String#<<: 386,831 ( 2.0%)
Integer#<<: 319,768 ( 1.6%)
Kernel#is_a?: 312,176 ( 1.6%)
Integer#/: 238,502 ( 1.2%)
Kernel#format: 238,502 ( 1.2%)
Array#<<: 220,724 ( 1.1%)
Class#last_match: 179,182 ( 0.9%)
Hash#[]=: 167,728 ( 0.8%)
CGI::EscapeExt#escapeHTML: 106,471 ( 0.5%)
Array#shift: 98,030 ( 0.5%)
Array#unshift: 90,851 ( 0.5%)
String#=~: 90,637 ( 0.5%)
String#start_with?: 88,122 ( 0.4%)
Regexp#===: 85,648 ( 0.4%)
String#empty?: 80,950 ( 0.4%)
Array#push: 78,615 ( 0.4%)
Top-20 not annotated C methods (97.4% of total 19,814,578):
Kernel#respond_to?: 9,725,886 (49.1%)
Hash#key?: 4,589,528 (23.2%)
Set#include?: 1,493,789 ( 7.5%)
String#===: 616,183 ( 3.1%)
Kernel#is_a?: 397,366 ( 2.0%)
String#<<: 386,831 ( 2.0%)
Integer#<<: 319,768 ( 1.6%)
Integer#/: 238,502 ( 1.2%)
Kernel#format: 238,502 ( 1.2%)
Array#<<: 220,724 ( 1.1%)
Class#last_match: 179,182 ( 0.9%)
Hash#[]=: 167,728 ( 0.8%)
CGI::EscapeExt#escapeHTML: 106,471 ( 0.5%)
Array#shift: 98,030 ( 0.5%)
Array#unshift: 90,851 ( 0.5%)
String#=~: 90,637 ( 0.5%)
String#start_with?: 88,122 ( 0.4%)
Regexp#===: 85,648 ( 0.4%)
String#empty?: 80,950 ( 0.4%)
Array#push: 78,615 ( 0.4%)
Top-2 not optimized method types for send (100.0% of total 1,180):
iseq: 602 (51.0%)
cfunc: 578 (49.0%)
Top-3 not optimized method types for send_without_block (100.0% of total 4,896,785):
iseq: 4,669,764 (95.4%)
optimized: 227,001 ( 4.6%)
alias: 20 ( 0.0%)
Top-9 not optimized instructions (100.0% of total 1,255,287):
invokeblock: 430,174 (34.3%)
opt_neq: 319,471 (25.5%)
opt_and: 319,471 (25.5%)
opt_eq: 127,926 (10.2%)
opt_le: 31,238 ( 2.5%)
invokesuper: 23,409 ( 1.9%)
opt_minus: 2,934 ( 0.2%)
opt_send_without_block: 562 ( 0.0%)
opt_or: 102 ( 0.0%)
Top-7 send fallback reasons (100.0% of total 17,930,659):
send_no_profiles: 6,145,096 (34.3%)
send_without_block_polymorphic: 5,459,600 (30.4%)
send_without_block_not_optimized_method_type: 4,896,785 (27.3%)
not_optimized_instruction: 1,255,287 ( 7.0%)
send_without_block_no_profiles: 170,037 ( 0.9%)
obj_to_string_not_string: 2,674 ( 0.0%)
send_not_optimized_method_type: 1,180 ( 0.0%)
Top-3 unhandled YARV insns (100.0% of total 157,831):
getclassvariable: 157,694 (99.9%)
once: 121 ( 0.1%)
getconstant: 16 ( 0.0%)
Top-2 compile error reasons (100.0% of total 8,905,991):
register_spill_on_alloc: 8,905,891 (100.0%)
register_spill_on_ccall: 100 ( 0.0%)
Top-9 side exit reasons (100.0% of total 26,549,652):
compile_error: 8,905,991 (33.5%)
guard_shape_failure: 6,590,116 (24.8%)
guard_type_failure: 4,882,217 (18.4%)
unhandled_splat: 4,150,547 (15.6%)
unhandled_kwarg: 1,827,728 ( 6.9%)
unhandled_yarv_insn: 157,831 ( 0.6%)
unhandled_hir_insn: 34,072 ( 0.1%)
patchpoint: 1,100 ( 0.0%)
block_param_proxy_not_iseq_or_ifunc: 50 ( 0.0%)
send_count: 72,491,188
dynamic_send_count: 17,930,659 (24.7%)
optimized_send_count: 54,560,529 (75.3%)
iseq_optimized_send_count: 26,520,888 (36.6%)
inline_cfunc_optimized_send_count: 8,270,533 (11.4%)
non_variadic_cfunc_optimized_send_count: 8,890,390 (12.3%)
variadic_cfunc_optimized_send_count: 10,878,718 (15.0%)
dynamic_getivar_count: 2,171,396
dynamic_setivar_count: 1,737,553
compiled_iseq_count: 383
failed_iseq_count: 46
compile_time: 808ms
profile_time: 4ms
gc_time: 21ms
invalidation_time: 0ms
vm_write_pc_count: 71,973,068
vm_write_sp_count: 71,544,492
vm_write_locals_count: 71,544,492
vm_write_stack_count: 71,544,492
vm_write_to_parent_iseq_local_count: 1,070,897
vm_read_from_parent_iseq_local_count: 27,449,010
code_region_bytes: 2,097,152
side_exit_count: 26,549,652
total_insn_count: 908,528,764
vm_insn_count: 484,633,128
zjit_insn_count: 423,895,636
ratio_in_zjit: 46.7%
```
</details>
## Testing on `lobsters`:
<details>
<summary>Before patch:</summary>
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (71.0% of total 28,729,305):
Hash#[]: 8,490,837 (29.6%)
Kernel#is_a?: 1,861,955 ( 6.5%)
String#<<: 1,773,932 ( 6.2%)
Hash#[]=: 1,159,328 ( 4.0%)
Regexp#match?: 775,654 ( 2.7%)
String#empty?: 724,503 ( 2.5%)
Hash#key?: 691,233 ( 2.4%)
Kernel#respond_to?: 608,714 ( 2.1%)
TrueClass#===: 451,557 ( 1.6%)
FalseClass#===: 442,907 ( 1.5%)
Array#include?: 429,408 ( 1.5%)
ActiveSupport::OrderedOptions#_get: 377,468 ( 1.3%)
String#start_with?: 373,685 ( 1.3%)
ObjectSpace::WeakKeyMap#[]: 356,664 ( 1.2%)
Kernel#kind_of?: 349,451 ( 1.2%)
Kernel#dup: 328,120 ( 1.1%)
Class#new: 310,590 ( 1.1%)
Kernel#block_given?: 307,113 ( 1.1%)
String#==: 290,654 ( 1.0%)
Hash#fetch: 290,533 ( 1.0%)
Top-20 not annotated C methods (71.7% of total 29,033,802):
Hash#[]: 8,490,847 (29.2%)
Kernel#is_a?: 2,231,950 ( 7.7%)
String#<<: 1,773,932 ( 6.1%)
Hash#[]=: 1,159,507 ( 4.0%)
Regexp#match?: 775,654 ( 2.7%)
String#empty?: 739,580 ( 2.5%)
Hash#key?: 691,233 ( 2.4%)
Kernel#respond_to?: 608,714 ( 2.1%)
TrueClass#===: 451,557 ( 1.6%)
FalseClass#===: 442,907 ( 1.5%)
Array#include?: 429,408 ( 1.5%)
ActiveSupport::OrderedOptions#_get: 377,468 ( 1.3%)
String#start_with?: 373,685 ( 1.3%)
ObjectSpace::WeakKeyMap#[]: 356,664 ( 1.2%)
Kernel#kind_of?: 349,486 ( 1.2%)
Kernel#dup: 328,127 ( 1.1%)
Kernel#block_given?: 327,655 ( 1.1%)
Class#new: 310,590 ( 1.1%)
String#==: 296,624 ( 1.0%)
Hash#fetch: 290,533 ( 1.0%)
Top-2 not optimized method types for send (100.0% of total 96,231):
cfunc: 75,873 (78.8%)
iseq: 20,358 (21.2%)
Top-6 not optimized method types for send_without_block (100.0% of total 8,044,793):
iseq: 4,034,262 (50.1%)
bmethod: 1,757,537 (21.8%)
optimized: 1,647,169 (20.5%)
alias: 596,446 ( 7.4%)
null: 8,161 ( 0.1%)
cfunc: 1,218 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 7,507,191):
invokesuper: 4,343,829 (57.9%)
invokeblock: 1,323,655 (17.6%)
sendforward: 842,491 (11.2%)
opt_eq: 722,952 ( 9.6%)
opt_plus: 145,599 ( 1.9%)
opt_minus: 52,269 ( 0.7%)
opt_send_without_block: 39,595 ( 0.5%)
opt_neq: 15,048 ( 0.2%)
opt_mult: 13,826 ( 0.2%)
opt_or: 7,452 ( 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 45,075,567):
send_without_block_polymorphic: 17,072,731 (37.9%)
send_no_profiles: 10,490,735 (23.3%)
send_without_block_not_optimized_method_type: 8,044,793 (17.8%)
not_optimized_instruction: 7,507,191 (16.7%)
send_without_block_no_profiles: 1,816,853 ( 4.0%)
send_not_optimized_method_type: 96,231 ( 0.2%)
send_without_block_cfunc_array_variadic: 31,156 ( 0.1%)
obj_to_string_not_string: 15,303 ( 0.0%)
send_without_block_direct_too_many_args: 574 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 1,279,306):
expandarray: 660,222 (51.6%)
checkkeyword: 316,124 (24.7%)
getclassvariable: 119,678 ( 9.4%)
getblockparam: 88,485 ( 6.9%)
invokesuperforward: 78,843 ( 6.2%)
opt_duparray_send: 14,149 ( 1.1%)
getconstant: 1,496 ( 0.1%)
checkmatch: 290 ( 0.0%)
once: 19 ( 0.0%)
Top-2 compile error reasons (100.0% of total 6,508,618):
register_spill_on_alloc: 6,162,701 (94.7%)
register_spill_on_ccall: 345,917 ( 5.3%)
Top-14 side exit reasons (100.0% of total 19,988,958):
compile_error: 6,508,618 (32.6%)
guard_type_failure: 5,255,050 (26.3%)
guard_shape_failure: 3,698,481 (18.5%)
unhandled_yarv_insn: 1,279,306 ( 6.4%)
block_param_proxy_not_iseq_or_ifunc: 990,585 ( 5.0%)
unhandled_kwarg: 801,146 ( 4.0%)
unknown_newarray_send: 539,110 ( 2.7%)
patchpoint: 496,826 ( 2.5%)
unhandled_splat: 242,104 ( 1.2%)
unhandled_hir_insn: 147,346 ( 0.7%)
block_param_proxy_modified: 29,122 ( 0.1%)
interrupt: 1,072 ( 0.0%)
obj_to_string_fallback: 170 ( 0.0%)
guard_type_not_failure: 22 ( 0.0%)
send_count: 118,969,379
dynamic_send_count: 45,075,567 (37.9%)
optimized_send_count: 73,893,812 (62.1%)
iseq_optimized_send_count: 32,439,432 (27.3%)
inline_cfunc_optimized_send_count: 12,725,075 (10.7%)
non_variadic_cfunc_optimized_send_count: 24,121,279 (20.3%)
variadic_cfunc_optimized_send_count: 4,608,026 ( 3.9%)
dynamic_getivar_count: 13,002,365
dynamic_setivar_count: 12,402,229
compiled_iseq_count: 4,817
failed_iseq_count: 466
compile_time: 8,961ms
profile_time: 68ms
gc_time: 41ms
invalidation_time: 288ms
vm_write_pc_count: 113,940,194
vm_write_sp_count: 111,595,088
vm_write_locals_count: 111,595,088
vm_write_stack_count: 111,595,088
vm_write_to_parent_iseq_local_count: 514,997
vm_read_from_parent_iseq_local_count: 11,288,600
code_region_bytes: 22,970,368
side_exit_count: 19,988,958
total_insn_count: 928,321,939
vm_insn_count: 297,374,855
zjit_insn_count: 630,947,084
ratio_in_zjit: 68.0%
```
</details>
<details>
<summary>after patch:</summary>
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (60.9% of total 19,827,919):
Kernel#is_a?: 1,827,297 ( 9.2%)
String#<<: 1,764,393 ( 8.9%)
Hash#[]=: 1,159,637 ( 5.8%)
Regexp#match?: 775,625 ( 3.9%)
String#empty?: 723,469 ( 3.6%)
Hash#key?: 691,214 ( 3.5%)
Kernel#respond_to?: 602,389 ( 3.0%)
TrueClass#===: 447,671 ( 2.3%)
FalseClass#===: 439,274 ( 2.2%)
Array#include?: 425,491 ( 2.1%)
Hash#fetch: 382,294 ( 1.9%)
String#start_with?: 373,684 ( 1.9%)
ObjectSpace::WeakKeyMap#[]: 356,654 ( 1.8%)
Kernel#kind_of?: 340,341 ( 1.7%)
Kernel#dup: 328,108 ( 1.7%)
Class#new: 309,571 ( 1.6%)
Kernel#block_given?: 307,098 ( 1.5%)
String#==: 286,539 ( 1.4%)
BasicObject#!=: 284,640 ( 1.4%)
String#length: 256,345 ( 1.3%)
Top-20 not annotated C methods (62.1% of total 20,127,933):
Kernel#is_a?: 2,205,849 (11.0%)
String#<<: 1,764,393 ( 8.8%)
Hash#[]=: 1,159,816 ( 5.8%)
Regexp#match?: 775,625 ( 3.9%)
String#empty?: 738,546 ( 3.7%)
Hash#key?: 691,214 ( 3.4%)
Kernel#respond_to?: 602,389 ( 3.0%)
TrueClass#===: 447,671 ( 2.2%)
FalseClass#===: 439,274 ( 2.2%)
Array#include?: 425,491 ( 2.1%)
Hash#fetch: 382,294 ( 1.9%)
String#start_with?: 373,684 ( 1.9%)
ObjectSpace::WeakKeyMap#[]: 356,654 ( 1.8%)
Kernel#kind_of?: 340,375 ( 1.7%)
Kernel#dup: 328,115 ( 1.6%)
Kernel#block_given?: 327,640 ( 1.6%)
Class#new: 309,571 ( 1.5%)
String#==: 292,509 ( 1.5%)
BasicObject#!=: 284,824 ( 1.4%)
String#length: 256,345 ( 1.3%)
Top-2 not optimized method types for send (100.0% of total 113,430):
cfunc: 75,863 (66.9%)
iseq: 37,567 (33.1%)
Top-6 not optimized method types for send_without_block (100.0% of total 8,005,732):
iseq: 4,007,647 (50.1%)
bmethod: 1,750,263 (21.9%)
optimized: 1,647,088 (20.6%)
alias: 591,356 ( 7.4%)
null: 8,161 ( 0.1%)
cfunc: 1,217 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 7,569,803):
invokesuper: 4,320,589 (57.1%)
invokeblock: 1,321,548 (17.5%)
sendforward: 841,452 (11.1%)
opt_eq: 811,601 (10.7%)
opt_plus: 142,565 ( 1.9%)
opt_minus: 52,268 ( 0.7%)
opt_send_without_block: 42,982 ( 0.6%)
opt_neq: 15,047 ( 0.2%)
opt_mult: 13,824 ( 0.2%)
opt_or: 7,452 ( 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 45,409,745):
send_without_block_polymorphic: 17,360,049 (38.2%)
send_no_profiles: 10,502,130 (23.1%)
send_without_block_not_optimized_method_type: 8,005,732 (17.6%)
not_optimized_instruction: 7,569,803 (16.7%)
send_without_block_no_profiles: 1,811,570 ( 4.0%)
send_not_optimized_method_type: 113,430 ( 0.2%)
send_without_block_cfunc_array_variadic: 31,154 ( 0.1%)
obj_to_string_not_string: 15,303 ( 0.0%)
send_without_block_direct_too_many_args: 574 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 1,241,241):
expandarray: 622,183 (50.1%)
checkkeyword: 316,113 (25.5%)
getclassvariable: 119,668 ( 9.6%)
getblockparam: 88,481 ( 7.1%)
invokesuperforward: 78,842 ( 6.4%)
opt_duparray_send: 14,149 ( 1.1%)
getconstant: 1,496 ( 0.1%)
checkmatch: 290 ( 0.0%)
once: 19 ( 0.0%)
Top-2 compile error reasons (100.0% of total 6,521,426):
register_spill_on_alloc: 6,175,519 (94.7%)
register_spill_on_ccall: 345,907 ( 5.3%)
Top-14 side exit reasons (100.0% of total 19,869,193):
compile_error: 6,521,426 (32.8%)
guard_type_failure: 5,167,727 (26.0%)
guard_shape_failure: 3,708,529 (18.7%)
unhandled_yarv_insn: 1,241,241 ( 6.2%)
block_param_proxy_not_iseq_or_ifunc: 990,130 ( 5.0%)
unhandled_kwarg: 800,104 ( 4.0%)
unknown_newarray_send: 539,105 ( 2.7%)
patchpoint: 494,790 ( 2.5%)
unhandled_splat: 229,423 ( 1.2%)
unhandled_hir_insn: 147,342 ( 0.7%)
block_param_proxy_modified: 28,111 ( 0.1%)
interrupt: 1,073 ( 0.0%)
obj_to_string_fallback: 170 ( 0.0%)
guard_type_not_failure: 22 ( 0.0%)
send_count: 109,972,903
dynamic_send_count: 45,409,745 (41.3%)
optimized_send_count: 64,563,158 (58.7%)
iseq_optimized_send_count: 32,205,906 (29.3%)
inline_cfunc_optimized_send_count: 12,529,333 (11.4%)
non_variadic_cfunc_optimized_send_count: 15,123,197 (13.8%)
variadic_cfunc_optimized_send_count: 4,704,722 ( 4.3%)
dynamic_getivar_count: 12,973,226
dynamic_setivar_count: 12,381,984
compiled_iseq_count: 4,816
failed_iseq_count: 467
compile_time: 8,116ms
profile_time: 59ms
gc_time: 35ms
invalidation_time: 289ms
vm_write_pc_count: 113,616,123
vm_write_sp_count: 111,273,109
vm_write_locals_count: 111,273,109
vm_write_stack_count: 111,273,109
vm_write_to_parent_iseq_local_count: 516,816
vm_read_from_parent_iseq_local_count: 11,255,225
code_region_bytes: 22,872,064
side_exit_count: 19,869,193
total_insn_count: 924,733,475
vm_insn_count: 296,183,588
zjit_insn_count: 628,549,887
ratio_in_zjit: 68.0%
```
</details>
| -rw-r--r-- | zjit/src/codegen.rs | 6 | ||||
| -rw-r--r-- | zjit/src/cruby_methods.rs | 9 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 130 |
3 files changed, 144 insertions, 1 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index c5bdbcfe0a..0bf0205b53 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -431,6 +431,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::ObjToString { val, cd, state, .. } => gen_objtostring(jit, asm, opnd!(val), *cd, &function.frame_state(*state)), &Insn::CheckInterrupts { state } => no_output!(gen_check_interrupts(jit, asm, &function.frame_state(state))), &Insn::HashDup { val, state } => { gen_hash_dup(asm, opnd!(val), &function.frame_state(state)) }, + &Insn::HashAref { hash, key, state } => { gen_hash_aref(jit, asm, opnd!(hash), opnd!(key), &function.frame_state(state)) }, &Insn::ArrayPush { array, val, state } => { no_output!(gen_array_push(asm, opnd!(array), opnd!(val), &function.frame_state(state))) }, &Insn::ToNewArray { val, state } => { gen_to_new_array(jit, asm, opnd!(val), &function.frame_state(state)) }, &Insn::ToArray { val, state } => { gen_to_array(jit, asm, opnd!(val), &function.frame_state(state)) }, @@ -860,6 +861,11 @@ fn gen_hash_dup(asm: &mut Assembler, val: Opnd, state: &FrameState) -> lir::Opnd asm_ccall!(asm, rb_hash_resurrect, val) } +fn gen_hash_aref(jit: &mut JITState, asm: &mut Assembler, hash: Opnd, key: Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_non_leaf_call(jit, asm, state); + asm_ccall!(asm, rb_hash_aref, hash, key) +} + fn gen_array_push(asm: &mut Assembler, array: Opnd, val: Opnd, state: &FrameState) { gen_prepare_leaf_call_with_gc(asm, state); asm_ccall!(asm, rb_ary_push, array, val); diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index bc8f1d3b84..27e6b151bf 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -198,6 +198,7 @@ pub fn init() -> Annotations { annotate!(rb_cArray, "reverse", types::ArrayExact, leaf, elidable); annotate!(rb_cArray, "join", types::StringExact); annotate!(rb_cArray, "[]", inline_array_aref); + annotate!(rb_cHash, "[]", inline_hash_aref); 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); @@ -248,3 +249,11 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In } None } + +fn inline_hash_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> { + if let &[key] = args { + let result = fun.push_insn(block, hir::Insn::HashAref { hash: recv, key, state }); + return Some(result); + } + None +} diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 2d1eb6425a..4ad20b9c1d 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -582,6 +582,7 @@ pub enum Insn { ArrayPush { array: InsnId, val: InsnId, state: InsnId }, ArrayArefFixnum { array: InsnId, index: InsnId }, + HashAref { hash: InsnId, key: InsnId, state: InsnId }, HashDup { val: InsnId, state: InsnId }, /// Allocate an instance of the `val` object without calling `#initialize` on it. @@ -923,6 +924,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Insn::ArrayDup { val, .. } => { write!(f, "ArrayDup {val}") } Insn::HashDup { val, .. } => { write!(f, "HashDup {val}") } + Insn::HashAref { hash, key, .. } => { write!(f, "HashAref {hash}, {key}")} Insn::ObjectAlloc { val, .. } => { write!(f, "ObjectAlloc {val}") } &Insn::ObjectAllocClass { class, .. } => { let class_name = get_class_name(class); @@ -1580,6 +1582,7 @@ impl Function { &InvokeBuiltin { bf, ref args, state, return_type } => InvokeBuiltin { bf, args: find_vec!(args), state, return_type }, &ArrayDup { val, state } => ArrayDup { val: find!(val), state }, &HashDup { val, state } => HashDup { val: find!(val), state }, + &HashAref { hash, key, state } => HashAref { hash: find!(hash), key: find!(key), state }, &ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state }, &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, &CCall { cfunc, ref args, name, return_type, elidable } => CCall { cfunc, args: find_vec!(args), name, return_type, elidable }, @@ -1680,6 +1683,7 @@ impl Function { Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, Insn::ArrayArefFixnum { .. } => types::BasicObject, + Insn::HashAref { .. } => types::BasicObject, Insn::NewHash { .. } => types::HashExact, Insn::HashDup { .. } => types::HashExact, Insn::NewRange { .. } => types::RangeExact, @@ -2775,6 +2779,11 @@ impl Function { worklist.push_back(array); worklist.push_back(index); } + &Insn::HashAref { hash, key, state } => { + worklist.push_back(hash); + worklist.push_back(key); + worklist.push_back(state); + } &Insn::Send { recv, ref args, state, .. } | &Insn::SendForward { recv, ref args, state, .. } | &Insn::SendWithoutBlock { recv, ref args, state, .. } @@ -9242,7 +9251,7 @@ mod opt_tests { PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) v26:HashExact = GuardType v9, HashExact - v27:BasicObject = CCallWithFrame []@0x1038, v26, v13 + v27:BasicObject = HashAref v26, v13 CheckInterrupts Return v27 "); @@ -12828,6 +12837,125 @@ mod opt_tests { } #[test] + fn test_hash_aref_literal() { + eval(" + def test + arr = {1 => 3} + arr[1] + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:HashExact = HashDup v13 + v18:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Hash@0x1008) + v31:BasicObject = HashAref v15, v18 + CheckInterrupts + Return v31 + "); + } + + #[test] + fn test_hash_aref_profiled() { + eval(" + def test(hash, key) + hash[key] + end + test({1 => 3}, 1) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v28:HashExact = GuardType v11, HashExact + v29:BasicObject = HashAref v28, v12 + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_hash_aref_subclass() { + eval(" + class C < Hash; end + def test(hash, key) + hash[key] + end + test(C.new({0 => 3}), 0) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v28:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] + v29:BasicObject = HashAref v28, v12 + CheckInterrupts + Return v29 + "); + } + + #[test] + fn test_does_not_fold_hash_aref_with_frozen_hash() { + eval(" + H = {a: 0}.freeze + def test = H[:a] + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, H) + v24:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) + PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) + PatchPoint NoSingletonClass(Hash@0x1018) + v28:BasicObject = HashAref v24, v12 + CheckInterrupts + Return v28 + "); + } + + #[test] fn test_optimize_thread_current() { eval(" def test = Thread.current |
