From fba349e65883b7b9541433f7716e41d27c7e8a69 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 20 Oct 2025 10:55:52 -0400 Subject: [PATCH 1/2] ZJIT: Implement expandarray (#14847) Only support the simple case: no splat or rest. lobsters before:
``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (60.5% of total 11,039,954): Kernel#is_a?: 1,030,769 ( 9.3%) String#<<: 851,954 ( 7.7%) Hash#[]=: 742,941 ( 6.7%) Regexp#match?: 399,894 ( 3.6%) String#empty?: 353,775 ( 3.2%) Hash#key?: 349,147 ( 3.2%) String#start_with?: 334,961 ( 3.0%) Kernel#respond_to?: 316,528 ( 2.9%) ObjectSpace::WeakKeyMap#[]: 238,978 ( 2.2%) TrueClass#===: 235,771 ( 2.1%) FalseClass#===: 231,144 ( 2.1%) Array#include?: 211,385 ( 1.9%) Hash#fetch: 204,702 ( 1.9%) Kernel#block_given?: 181,797 ( 1.6%) Kernel#dup: 179,341 ( 1.6%) BasicObject#!=: 175,997 ( 1.6%) Class#new: 168,079 ( 1.5%) Kernel#kind_of?: 165,600 ( 1.5%) String#==: 157,735 ( 1.4%) Module#clock_gettime: 144,992 ( 1.3%) Top-20 not annotated C methods (61.4% of total 11,202,087): Kernel#is_a?: 1,212,660 (10.8%) String#<<: 851,954 ( 7.6%) Hash#[]=: 743,120 ( 6.6%) Regexp#match?: 399,894 ( 3.6%) String#empty?: 361,013 ( 3.2%) Hash#key?: 349,147 ( 3.1%) String#start_with?: 334,961 ( 3.0%) Kernel#respond_to?: 316,528 ( 2.8%) ObjectSpace::WeakKeyMap#[]: 238,978 ( 2.1%) TrueClass#===: 235,771 ( 2.1%) FalseClass#===: 231,144 ( 2.1%) Array#include?: 211,385 ( 1.9%) Hash#fetch: 204,702 ( 1.8%) Kernel#block_given?: 191,666 ( 1.7%) Kernel#dup: 179,348 ( 1.6%) BasicObject#!=: 176,181 ( 1.6%) Class#new: 168,079 ( 1.5%) Kernel#kind_of?: 165,634 ( 1.5%) String#==: 163,667 ( 1.5%) Module#clock_gettime: 144,992 ( 1.3%) Top-2 not optimized method types for send (100.0% of total 72,318): cfunc: 48,055 (66.4%) iseq: 24,263 (33.6%) Top-6 not optimized method types for send_without_block (100.0% of total 4,523,682): iseq: 2,271,936 (50.2%) bmethod: 985,636 (21.8%) optimized: 949,703 (21.0%) alias: 310,747 ( 6.9%) null: 5,106 ( 0.1%) cfunc: 554 ( 0.0%) Top-13 not optimized instructions (100.0% of total 4,293,171): invokesuper: 2,373,404 (55.3%) invokeblock: 811,926 (18.9%) sendforward: 505,452 (11.8%) opt_eq: 451,754 (10.5%) opt_plus: 74,404 ( 1.7%) opt_minus: 36,228 ( 0.8%) opt_send_without_block: 21,792 ( 0.5%) opt_neq: 7,231 ( 0.2%) opt_mult: 6,752 ( 0.2%) opt_or: 3,753 ( 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,530,724): send_without_block_polymorphic: 9,722,491 (38.1%) send_no_profiles: 5,894,788 (23.1%) send_without_block_not_optimized_method_type: 4,523,682 (17.7%) not_optimized_instruction: 4,293,171 (16.8%) send_without_block_no_profiles: 998,746 ( 3.9%) send_not_optimized_method_type: 72,318 ( 0.3%) send_without_block_cfunc_array_variadic: 15,134 ( 0.1%) obj_to_string_not_string: 9,765 ( 0.0%) send_without_block_direct_too_many_args: 629 ( 0.0%) Top-9 unhandled YARV insns (100.0% of total 690,950): expandarray: 328,490 (47.5%) checkkeyword: 190,694 (27.6%) getclassvariable: 59,901 ( 8.7%) invokesuperforward: 49,503 ( 7.2%) getblockparam: 49,119 ( 7.1%) opt_duparray_send: 11,978 ( 1.7%) getconstant: 952 ( 0.1%) checkmatch: 290 ( 0.0%) once: 23 ( 0.0%) Top-3 compile error reasons (100.0% of total 3,718,636): register_spill_on_alloc: 3,418,255 (91.9%) register_spill_on_ccall: 182,018 ( 4.9%) exception_handler: 118,363 ( 3.2%) Top-14 side exit reasons (100.0% of total 10,860,385): compile_error: 3,718,636 (34.2%) guard_type_failure: 2,638,926 (24.3%) guard_shape_failure: 1,917,209 (17.7%) unhandled_yarv_insn: 690,950 ( 6.4%) block_param_proxy_not_iseq_or_ifunc: 535,789 ( 4.9%) unhandled_kwarg: 455,347 ( 4.2%) patchpoint: 370,476 ( 3.4%) unknown_newarray_send: 314,786 ( 2.9%) unhandled_splat: 122,071 ( 1.1%) unhandled_hir_insn: 76,397 ( 0.7%) block_param_proxy_modified: 19,193 ( 0.2%) obj_to_string_fallback: 566 ( 0.0%) guard_type_not_failure: 22 ( 0.0%) interrupt: 17 ( 0.0%) send_count: 62,244,604 dynamic_send_count: 25,530,724 (41.0%) optimized_send_count: 36,713,880 (59.0%) iseq_optimized_send_count: 18,587,512 (29.9%) inline_cfunc_optimized_send_count: 7,086,414 (11.4%) non_variadic_cfunc_optimized_send_count: 8,375,754 (13.5%) variadic_cfunc_optimized_send_count: 2,664,200 ( 4.3%) dynamic_getivar_count: 7,365,995 dynamic_setivar_count: 7,245,005 compiled_iseq_count: 4,796 failed_iseq_count: 447 compile_time: 814ms profile_time: 9ms gc_time: 9ms invalidation_time: 72ms vm_write_pc_count: 64,156,223 vm_write_sp_count: 62,812,449 vm_write_locals_count: 62,812,449 vm_write_stack_count: 62,812,449 vm_write_to_parent_iseq_local_count: 292,458 vm_read_from_parent_iseq_local_count: 6,599,701 code_region_bytes: 22,953,984 side_exit_count: 10,860,385 total_insn_count: 517,606,340 vm_insn_count: 162,979,530 zjit_insn_count: 354,626,810 ratio_in_zjit: 68.5% ```
lobsters after:
``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (59.9% of total 11,291,815): Kernel#is_a?: 1,046,269 ( 9.3%) String#<<: 851,954 ( 7.5%) Hash#[]=: 743,274 ( 6.6%) Regexp#match?: 399,894 ( 3.5%) String#empty?: 353,775 ( 3.1%) Hash#key?: 349,147 ( 3.1%) String#start_with?: 334,961 ( 3.0%) Kernel#respond_to?: 316,502 ( 2.8%) ObjectSpace::WeakKeyMap#[]: 238,978 ( 2.1%) TrueClass#===: 235,771 ( 2.1%) FalseClass#===: 231,144 ( 2.0%) String#sub!: 219,579 ( 1.9%) Array#include?: 211,385 ( 1.9%) Hash#fetch: 204,702 ( 1.8%) Kernel#block_given?: 181,797 ( 1.6%) Kernel#dup: 179,341 ( 1.6%) BasicObject#!=: 175,997 ( 1.6%) Class#new: 168,079 ( 1.5%) Kernel#kind_of?: 165,600 ( 1.5%) String#==: 157,742 ( 1.4%) Top-20 not annotated C methods (60.9% of total 11,466,928): Kernel#is_a?: 1,239,923 (10.8%) String#<<: 851,954 ( 7.4%) Hash#[]=: 743,453 ( 6.5%) Regexp#match?: 399,894 ( 3.5%) String#empty?: 361,013 ( 3.1%) Hash#key?: 349,147 ( 3.0%) String#start_with?: 334,961 ( 2.9%) Kernel#respond_to?: 316,502 ( 2.8%) ObjectSpace::WeakKeyMap#[]: 238,978 ( 2.1%) TrueClass#===: 235,771 ( 2.1%) FalseClass#===: 231,144 ( 2.0%) String#sub!: 219,579 ( 1.9%) Array#include?: 211,385 ( 1.8%) Hash#fetch: 204,702 ( 1.8%) Kernel#block_given?: 191,666 ( 1.7%) Kernel#dup: 179,348 ( 1.6%) BasicObject#!=: 176,181 ( 1.5%) Class#new: 168,079 ( 1.5%) Kernel#kind_of?: 165,634 ( 1.4%) String#==: 163,674 ( 1.4%) Top-2 not optimized method types for send (100.0% of total 72,318): cfunc: 48,055 (66.4%) iseq: 24,263 (33.6%) Top-6 not optimized method types for send_without_block (100.0% of total 4,524,016): iseq: 2,272,269 (50.2%) bmethod: 985,636 (21.8%) optimized: 949,704 (21.0%) alias: 310,747 ( 6.9%) null: 5,106 ( 0.1%) cfunc: 554 ( 0.0%) Top-13 not optimized instructions (100.0% of total 4,294,241): invokesuper: 2,375,446 (55.3%) invokeblock: 810,955 (18.9%) sendforward: 505,451 (11.8%) opt_eq: 451,754 (10.5%) opt_plus: 74,404 ( 1.7%) opt_minus: 36,228 ( 0.8%) opt_send_without_block: 21,792 ( 0.5%) opt_neq: 7,231 ( 0.2%) opt_mult: 6,752 ( 0.2%) opt_or: 3,753 ( 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,534,542): send_without_block_polymorphic: 9,723,469 (38.1%) send_no_profiles: 5,896,023 (23.1%) send_without_block_not_optimized_method_type: 4,524,016 (17.7%) not_optimized_instruction: 4,294,241 (16.8%) send_without_block_no_profiles: 998,947 ( 3.9%) send_not_optimized_method_type: 72,318 ( 0.3%) send_without_block_cfunc_array_variadic: 15,134 ( 0.1%) obj_to_string_not_string: 9,765 ( 0.0%) send_without_block_direct_too_many_args: 629 ( 0.0%) Top-8 unhandled YARV insns (100.0% of total 362,460): checkkeyword: 190,694 (52.6%) getclassvariable: 59,901 (16.5%) invokesuperforward: 49,503 (13.7%) getblockparam: 49,119 (13.6%) opt_duparray_send: 11,978 ( 3.3%) getconstant: 952 ( 0.3%) checkmatch: 290 ( 0.1%) once: 23 ( 0.0%) Top-3 compile error reasons (100.0% of total 3,798,744): register_spill_on_alloc: 3,495,669 (92.0%) register_spill_on_ccall: 184,712 ( 4.9%) exception_handler: 118,363 ( 3.1%) Top-15 side exit reasons (100.0% of total 10,637,319): compile_error: 3,798,744 (35.7%) guard_type_failure: 2,655,504 (25.0%) guard_shape_failure: 1,917,217 (18.0%) block_param_proxy_not_iseq_or_ifunc: 535,789 ( 5.0%) unhandled_kwarg: 455,492 ( 4.3%) patchpoint: 370,478 ( 3.5%) unhandled_yarv_insn: 362,460 ( 3.4%) unknown_newarray_send: 314,786 ( 3.0%) unhandled_splat: 122,071 ( 1.1%) unhandled_hir_insn: 83,066 ( 0.8%) block_param_proxy_modified: 19,193 ( 0.2%) guard_int_equals_failure: 1,914 ( 0.0%) obj_to_string_fallback: 566 ( 0.0%) guard_type_not_failure: 22 ( 0.0%) interrupt: 17 ( 0.0%) send_count: 62,495,067 dynamic_send_count: 25,534,542 (40.9%) optimized_send_count: 36,960,525 (59.1%) iseq_optimized_send_count: 18,582,072 (29.7%) inline_cfunc_optimized_send_count: 7,086,638 (11.3%) non_variadic_cfunc_optimized_send_count: 8,392,657 (13.4%) variadic_cfunc_optimized_send_count: 2,899,158 ( 4.6%) dynamic_getivar_count: 7,365,994 dynamic_setivar_count: 7,248,500 compiled_iseq_count: 4,780 failed_iseq_count: 463 compile_time: 816ms profile_time: 9ms gc_time: 11ms invalidation_time: 70ms vm_write_pc_count: 64,363,541 vm_write_sp_count: 63,022,221 vm_write_locals_count: 63,022,221 vm_write_stack_count: 63,022,221 vm_write_to_parent_iseq_local_count: 292,458 vm_read_from_parent_iseq_local_count: 6,850,977 code_region_bytes: 23,019,520 side_exit_count: 10,637,319 total_insn_count: 517,303,190 vm_insn_count: 160,562,103 zjit_insn_count: 356,741,087 ratio_in_zjit: 69.0% ```
railsbench before:
``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (66.1% of total 25,524,934): Hash#[]=: 1,700,237 ( 6.7%) String#getbyte: 1,572,123 ( 6.2%) String#<<: 1,494,022 ( 5.9%) Kernel#is_a?: 1,429,930 ( 5.6%) String#empty?: 1,370,323 ( 5.4%) Regexp#match?: 1,235,067 ( 4.8%) Kernel#respond_to?: 1,198,251 ( 4.7%) Hash#key?: 1,087,406 ( 4.3%) String#setbyte: 810,022 ( 3.2%) Integer#^: 766,624 ( 3.0%) Kernel#block_given?: 603,613 ( 2.4%) String#==: 590,409 ( 2.3%) Class#new: 506,216 ( 2.0%) Hash#delete: 455,288 ( 1.8%) BasicObject#!=: 428,771 ( 1.7%) Hash#fetch: 408,621 ( 1.6%) String#ascii_only?: 373,915 ( 1.5%) ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.1%) NilClass#===: 277,244 ( 1.1%) Kernel#Array: 269,590 ( 1.1%) Top-20 not annotated C methods (66.8% of total 25,392,654): Hash#[]=: 1,700,416 ( 6.7%) String#getbyte: 1,572,123 ( 6.2%) Kernel#is_a?: 1,515,672 ( 6.0%) String#<<: 1,494,022 ( 5.9%) String#empty?: 1,370,478 ( 5.4%) Regexp#match?: 1,235,067 ( 4.9%) Kernel#respond_to?: 1,198,251 ( 4.7%) Hash#key?: 1,087,406 ( 4.3%) String#setbyte: 810,022 ( 3.2%) Integer#^: 766,624 ( 3.0%) Kernel#block_given?: 603,613 ( 2.4%) String#==: 601,115 ( 2.4%) Class#new: 506,216 ( 2.0%) Hash#delete: 455,288 ( 1.8%) BasicObject#!=: 428,876 ( 1.7%) Hash#fetch: 408,621 ( 1.6%) String#ascii_only?: 373,915 ( 1.5%) ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.1%) NilClass#===: 277,244 ( 1.1%) Kernel#Array: 269,590 ( 1.1%) Top-2 not optimized method types for send (100.0% of total 186,159): iseq: 112,747 (60.6%) cfunc: 73,412 (39.4%) Top-6 not optimized method types for send_without_block (100.0% of total 8,142,248): iseq: 3,464,671 (42.6%) optimized: 2,632,884 (32.3%) bmethod: 1,290,701 (15.9%) alias: 706,020 ( 8.7%) null: 47,942 ( 0.6%) cfunc: 30 ( 0.0%) Top-11 not optimized instructions (100.0% of total 8,394,873): invokesuper: 5,602,274 (66.7%) invokeblock: 1,764,936 (21.0%) sendforward: 551,832 ( 6.6%) opt_eq: 441,959 ( 5.3%) opt_plus: 31,635 ( 0.4%) opt_send_without_block: 1,163 ( 0.0%) opt_lt: 372 ( 0.0%) opt_mult: 251 ( 0.0%) opt_ge: 193 ( 0.0%) opt_neq: 149 ( 0.0%) opt_or: 109 ( 0.0%) Top-8 send fallback reasons (100.0% of total 40,748,753): send_without_block_polymorphic: 12,933,923 (31.7%) send_no_profiles: 9,033,636 (22.2%) not_optimized_instruction: 8,394,873 (20.6%) send_without_block_not_optimized_method_type: 8,142,248 (20.0%) send_without_block_no_profiles: 1,839,228 ( 4.5%) send_without_block_cfunc_array_variadic: 215,046 ( 0.5%) send_not_optimized_method_type: 186,159 ( 0.5%) obj_to_string_not_string: 3,640 ( 0.0%) Top-9 unhandled YARV insns (100.0% of total 1,604,456): getclassvariable: 458,136 (28.6%) getblockparam: 455,921 (28.4%) checkkeyword: 265,425 (16.5%) invokesuperforward: 239,383 (14.9%) expandarray: 137,305 ( 8.6%) getconstant: 48,100 ( 3.0%) checkmatch: 149 ( 0.0%) once: 23 ( 0.0%) opt_duparray_send: 14 ( 0.0%) Top-3 compile error reasons (100.0% of total 5,570,130): register_spill_on_alloc: 4,994,130 (89.7%) exception_handler: 356,784 ( 6.4%) register_spill_on_ccall: 219,216 ( 3.9%) Top-13 side exit reasons (100.0% of total 12,412,181): compile_error: 5,570,130 (44.9%) unhandled_yarv_insn: 1,604,456 (12.9%) guard_shape_failure: 1,462,872 (11.8%) guard_type_failure: 845,891 ( 6.8%) block_param_proxy_not_iseq_or_ifunc: 765,968 ( 6.2%) unhandled_kwarg: 658,341 ( 5.3%) patchpoint: 504,437 ( 4.1%) unhandled_splat: 446,990 ( 3.6%) unknown_newarray_send: 332,740 ( 2.7%) unhandled_hir_insn: 160,205 ( 1.3%) block_param_proxy_modified: 59,589 ( 0.5%) obj_to_string_fallback: 553 ( 0.0%) interrupt: 9 ( 0.0%) send_count: 119,067,587 dynamic_send_count: 40,748,753 (34.2%) optimized_send_count: 78,318,834 (65.8%) iseq_optimized_send_count: 39,936,542 (33.5%) inline_cfunc_optimized_send_count: 12,857,358 (10.8%) non_variadic_cfunc_optimized_send_count: 19,722,584 (16.6%) variadic_cfunc_optimized_send_count: 5,802,350 ( 4.9%) dynamic_getivar_count: 10,980,323 dynamic_setivar_count: 12,962,726 compiled_iseq_count: 2,531 failed_iseq_count: 245 compile_time: 414ms profile_time: 21ms gc_time: 33ms invalidation_time: 5ms vm_write_pc_count: 129,093,714 vm_write_sp_count: 126,023,084 vm_write_locals_count: 126,023,084 vm_write_stack_count: 126,023,084 vm_write_to_parent_iseq_local_count: 385,461 vm_read_from_parent_iseq_local_count: 11,266,484 code_region_bytes: 12,156,928 side_exit_count: 12,412,181 total_insn_count: 866,780,158 vm_insn_count: 216,821,134 zjit_insn_count: 649,959,024 ratio_in_zjit: 75.0% ```
railsbench after:
``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (66.0% of total 25,597,895): Hash#[]=: 1,724,042 ( 6.7%) String#getbyte: 1,572,123 ( 6.1%) String#<<: 1,494,022 ( 5.8%) Kernel#is_a?: 1,429,946 ( 5.6%) String#empty?: 1,370,323 ( 5.4%) Regexp#match?: 1,235,067 ( 4.8%) Kernel#respond_to?: 1,198,251 ( 4.7%) Hash#key?: 1,087,406 ( 4.2%) String#setbyte: 810,022 ( 3.2%) Integer#^: 766,624 ( 3.0%) Kernel#block_given?: 603,613 ( 2.4%) String#==: 590,699 ( 2.3%) Class#new: 506,216 ( 2.0%) Hash#delete: 455,288 ( 1.8%) BasicObject#!=: 428,771 ( 1.7%) Hash#fetch: 408,621 ( 1.6%) String#ascii_only?: 373,915 ( 1.5%) ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.1%) NilClass#===: 277,244 ( 1.1%) Kernel#Array: 269,590 ( 1.1%) Top-20 not annotated C methods (66.7% of total 25,465,615): Hash#[]=: 1,724,221 ( 6.8%) String#getbyte: 1,572,123 ( 6.2%) Kernel#is_a?: 1,515,688 ( 6.0%) String#<<: 1,494,022 ( 5.9%) String#empty?: 1,370,478 ( 5.4%) Regexp#match?: 1,235,067 ( 4.8%) Kernel#respond_to?: 1,198,251 ( 4.7%) Hash#key?: 1,087,406 ( 4.3%) String#setbyte: 810,022 ( 3.2%) Integer#^: 766,624 ( 3.0%) Kernel#block_given?: 603,613 ( 2.4%) String#==: 601,405 ( 2.4%) Class#new: 506,216 ( 2.0%) Hash#delete: 455,288 ( 1.8%) BasicObject#!=: 428,876 ( 1.7%) Hash#fetch: 408,621 ( 1.6%) String#ascii_only?: 373,915 ( 1.5%) ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.1%) NilClass#===: 277,244 ( 1.1%) Kernel#Array: 269,590 ( 1.1%) Top-2 not optimized method types for send (100.0% of total 186,159): iseq: 112,747 (60.6%) cfunc: 73,412 (39.4%) Top-6 not optimized method types for send_without_block (100.0% of total 8,142,248): iseq: 3,464,671 (42.6%) optimized: 2,632,884 (32.3%) bmethod: 1,290,701 (15.9%) alias: 706,020 ( 8.7%) null: 47,942 ( 0.6%) cfunc: 30 ( 0.0%) Top-11 not optimized instructions (100.0% of total 8,442,456): invokesuper: 5,649,857 (66.9%) invokeblock: 1,764,936 (20.9%) sendforward: 551,832 ( 6.5%) opt_eq: 441,959 ( 5.2%) opt_plus: 31,635 ( 0.4%) opt_send_without_block: 1,163 ( 0.0%) opt_lt: 372 ( 0.0%) opt_mult: 251 ( 0.0%) opt_ge: 193 ( 0.0%) opt_neq: 149 ( 0.0%) opt_or: 109 ( 0.0%) Top-8 send fallback reasons (100.0% of total 40,796,314): send_without_block_polymorphic: 12,933,921 (31.7%) send_no_profiles: 9,033,616 (22.1%) not_optimized_instruction: 8,442,456 (20.7%) send_without_block_not_optimized_method_type: 8,142,248 (20.0%) send_without_block_no_profiles: 1,839,228 ( 4.5%) send_without_block_cfunc_array_variadic: 215,046 ( 0.5%) send_not_optimized_method_type: 186,159 ( 0.5%) obj_to_string_not_string: 3,640 ( 0.0%) Top-8 unhandled YARV insns (100.0% of total 1,467,151): getclassvariable: 458,136 (31.2%) getblockparam: 455,921 (31.1%) checkkeyword: 265,425 (18.1%) invokesuperforward: 239,383 (16.3%) getconstant: 48,100 ( 3.3%) checkmatch: 149 ( 0.0%) once: 23 ( 0.0%) opt_duparray_send: 14 ( 0.0%) Top-3 compile error reasons (100.0% of total 5,825,923): register_spill_on_alloc: 5,225,940 (89.7%) exception_handler: 356,784 ( 6.1%) register_spill_on_ccall: 243,199 ( 4.2%) Top-13 side exit reasons (100.0% of total 12,530,763): compile_error: 5,825,923 (46.5%) unhandled_yarv_insn: 1,467,151 (11.7%) guard_shape_failure: 1,462,876 (11.7%) guard_type_failure: 845,913 ( 6.8%) block_param_proxy_not_iseq_or_ifunc: 765,968 ( 6.1%) unhandled_kwarg: 658,341 ( 5.3%) patchpoint: 504,437 ( 4.0%) unhandled_splat: 446,990 ( 3.6%) unknown_newarray_send: 332,740 ( 2.7%) unhandled_hir_insn: 160,273 ( 1.3%) block_param_proxy_modified: 59,589 ( 0.5%) obj_to_string_fallback: 553 ( 0.0%) interrupt: 9 ( 0.0%) send_count: 119,163,569 dynamic_send_count: 40,796,314 (34.2%) optimized_send_count: 78,367,255 (65.8%) iseq_optimized_send_count: 39,911,967 (33.5%) inline_cfunc_optimized_send_count: 12,857,393 (10.8%) non_variadic_cfunc_optimized_send_count: 19,770,401 (16.6%) variadic_cfunc_optimized_send_count: 5,827,494 ( 4.9%) dynamic_getivar_count: 10,980,323 dynamic_setivar_count: 12,986,381 compiled_iseq_count: 2,523 failed_iseq_count: 252 compile_time: 420ms profile_time: 21ms gc_time: 30ms invalidation_time: 4ms vm_write_pc_count: 128,973,665 vm_write_sp_count: 125,926,968 vm_write_locals_count: 125,926,968 vm_write_stack_count: 125,926,968 vm_write_to_parent_iseq_local_count: 385,752 vm_read_from_parent_iseq_local_count: 11,267,766 code_region_bytes: 12,189,696 side_exit_count: 12,530,763 total_insn_count: 866,667,490 vm_insn_count: 217,813,201 zjit_insn_count: 648,854,289 ratio_in_zjit: 74.9% ```
--- jit.c | 6 ++ test/ruby/test_zjit.rb | 30 ++++++++ yjit.c | 6 -- yjit/bindgen/src/main.rs | 2 +- yjit/src/codegen.rs | 4 +- yjit/src/cruby_bindings.inc.rs | 2 +- zjit/bindgen/src/main.rs | 1 + zjit/src/codegen.rs | 14 +++- zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/hir.rs | 137 +++++++++++++++++++++++++++++++-- zjit/src/hir_type/mod.rs | 17 ++++ zjit/src/stats.rs | 1 + 12 files changed, 204 insertions(+), 17 deletions(-) diff --git a/jit.c b/jit.c index f233a2f01f1c8e..0b491f0481d875 100644 --- a/jit.c +++ b/jit.c @@ -450,6 +450,12 @@ rb_yarv_ary_entry_internal(VALUE ary, long offset) return rb_ary_entry_internal(ary, offset); } +long +rb_jit_array_len(VALUE a) +{ + return rb_array_len(a); +} + void rb_set_cfp_pc(struct rb_control_frame_struct *cfp, const VALUE *pc) { diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index faf717096a4c77..b0c717bc24b435 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -1714,6 +1714,36 @@ def test = RUBY_COPYRIGHT }, call_threshold: 1, insns: [:opt_getconstant_path] end + def test_expandarray_no_splat + assert_compiles '[3, 4]', %q{ + def test(o) + a, b = o + [a, b] + end + test [3, 4] + }, call_threshold: 1, insns: [:expandarray] + end + + def test_expandarray_splat + assert_compiles '[3, [4]]', %q{ + def test(o) + a, *b = o + [a, b] + end + test [3, 4] + }, call_threshold: 1, insns: [:expandarray] + end + + def test_expandarray_splat_post + assert_compiles '[3, [4], 5]', %q{ + def test(o) + a, *b, c = o + [a, b, c] + end + test [3, 4, 5] + }, call_threshold: 1, insns: [:expandarray] + end + def test_getconstant_path_autoload # A constant-referencing expression can run arbitrary code through Kernel#autoload. Dir.mktmpdir('autoload') do |tmpdir| diff --git a/yjit.c b/yjit.c index 57b09d73b0bceb..598fe5716704d0 100644 --- a/yjit.c +++ b/yjit.c @@ -69,12 +69,6 @@ STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM); // The "_yjit_" part is for trying to be informative. We might want different // suffixes for symbols meant for Rust and symbols meant for broader CRuby. -long -rb_yjit_array_len(VALUE a) -{ - return rb_array_len(a); -} - # define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) // For a given raw_sample (frame), set the hash with the caller's diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 29b17346cd90ae..0d4d57e0695941 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -381,7 +381,7 @@ fn main() { .allowlist_function("rb_METHOD_ENTRY_VISI") .allowlist_function("rb_RCLASS_ORIGIN") .allowlist_function("rb_method_basic_definition_p") - .allowlist_function("rb_yjit_array_len") + .allowlist_function("rb_jit_array_len") .allowlist_function("rb_obj_class") .allowlist_function("rb_obj_is_proc") .allowlist_function("rb_vm_base_ptr") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 25a8545e859172..bf758a4f62bd21 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2305,7 +2305,7 @@ fn gen_expandarray( } // Get the compile-time array length - let comptime_len = unsafe { rb_yjit_array_len(comptime_recv) as u32 }; + let comptime_len = unsafe { rb_jit_array_len(comptime_recv) as u32 }; // Move the array from the stack and check that it's an array. guard_object_is_array( @@ -7603,7 +7603,7 @@ fn gen_send_iseq( gen_counter_incr(jit, asm, Counter::send_iseq_splat_not_array); return None; } else { - unsafe { rb_yjit_array_len(array) as u32} + unsafe { rb_jit_array_len(array) as u32} }; // Arity check accounting for size of the splat. When callee has rest parameters, we insert diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 1e34440460edd1..0a14a699284268 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1113,7 +1113,6 @@ extern "C" { lines: *mut ::std::os::raw::c_int, ) -> ::std::os::raw::c_int; pub fn rb_jit_cont_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void); - pub fn rb_yjit_array_len(a: VALUE) -> ::std::os::raw::c_long; pub fn rb_yjit_exit_locations_dict( yjit_raw_samples: *mut VALUE, yjit_line_samples: *mut ::std::os::raw::c_int, @@ -1250,6 +1249,7 @@ extern "C" { pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int; pub fn rb_assert_cme_handle(handle: VALUE); pub fn rb_yarv_ary_entry_internal(ary: VALUE, offset: ::std::os::raw::c_long) -> VALUE; + pub fn rb_jit_array_len(a: VALUE) -> ::std::os::raw::c_long; pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); pub fn rb_jit_shape_too_complex_p(shape_id: shape_id_t) -> bool; diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index b54e9404fdb10e..f13b61acf04609 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -272,6 +272,7 @@ fn main() { .allowlist_function("rb_jit_mark_executable") .allowlist_function("rb_jit_mark_unused") .allowlist_function("rb_jit_get_page_size") + .allowlist_function("rb_jit_array_len") .allowlist_function("rb_zjit_iseq_builtin_attrs") .allowlist_function("rb_zjit_iseq_inspect") .allowlist_function("rb_zjit_iseq_insn_set") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 42501de2423888..ea4a7ecc7c8c21 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -356,6 +356,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)), Insn::ArrayArefFixnum { array, index, .. } => gen_aref_fixnum(asm, opnd!(array), opnd!(index)), + Insn::ArrayLength { array } => gen_array_length(asm, opnd!(array)), Insn::ObjectAlloc { val, state } => gen_object_alloc(jit, asm, opnd!(val), &function.frame_state(*state)), &Insn::ObjectAllocClass { class, state } => gen_object_alloc_class(asm, class, &function.frame_state(state)), Insn::StringCopy { val, chilled, state } => gen_string_copy(asm, opnd!(val), *chilled, &function.frame_state(*state)), @@ -1258,6 +1259,10 @@ fn gen_aref_fixnum( asm_ccall!(asm, rb_ary_entry, array, unboxed_idx) } +fn gen_array_length(asm: &mut Assembler, array: Opnd) -> lir::Opnd { + asm_ccall!(asm, rb_jit_array_len, array) +} + /// Compile a new hash instruction fn gen_new_hash( jit: &mut JITState, @@ -1589,8 +1594,13 @@ fn gen_guard_type_not(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, g } /// Compile an identity check with a side exit -fn gen_guard_bit_equals(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, expected: VALUE, state: &FrameState) -> lir::Opnd { - asm.cmp(val, Opnd::Value(expected)); +fn gen_guard_bit_equals(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, expected: crate::hir::Const, state: &FrameState) -> lir::Opnd { + let expected_opnd: Opnd = match expected { + crate::hir::Const::Value(v) => { Opnd::Value(v) } + crate::hir::Const::CInt64(v) => { v.into() } + _ => panic!("gen_guard_bit_equals: unexpected hir::Const {:?}", expected), + }; + asm.cmp(val, expected_opnd); asm.jnz(side_exit(jit, state, GuardBitEquals(expected))); val } diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index ffaafed5ff33c2..c67e229a8009e7 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1045,6 +1045,7 @@ unsafe extern "C" { pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int; pub fn rb_assert_cme_handle(handle: VALUE); pub fn rb_yarv_ary_entry_internal(ary: VALUE, offset: ::std::os::raw::c_long) -> VALUE; + pub fn rb_jit_array_len(a: VALUE) -> ::std::os::raw::c_long; pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); pub fn rb_jit_shape_too_complex_p(shape_id: shape_id_t) -> bool; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index bccd27fc39dcbe..73b7cca8d08cdd 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -270,7 +270,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum Const { Value(VALUE), CBool(bool), @@ -460,7 +460,7 @@ pub enum SideExitReason { GuardType(Type), GuardTypeNot(Type), GuardShape(ShapeId), - GuardBitEquals(VALUE), + GuardBitEquals(Const), PatchPoint(Invariant), CalleeSideExit, ObjToStringFallback, @@ -583,6 +583,8 @@ pub enum Insn { /// Push `val` onto `array`, where `array` is already `Array`. ArrayPush { array: InsnId, val: InsnId, state: InsnId }, ArrayArefFixnum { array: InsnId, index: InsnId }, + /// Return the length of the array as a C `long` ([`types::CInt64`]) + ArrayLength { array: InsnId }, HashAref { hash: InsnId, key: InsnId, state: InsnId }, HashDup { val: InsnId, state: InsnId }, @@ -768,8 +770,8 @@ pub enum Insn { /// Side-exit if val doesn't have the expected type. GuardType { val: InsnId, guard_type: Type, state: InsnId }, GuardTypeNot { val: InsnId, guard_type: Type, state: InsnId }, - /// Side-exit if val is not the expected VALUE. - GuardBitEquals { val: InsnId, expected: VALUE, state: InsnId }, + /// Side-exit if val is not the expected Const. + GuardBitEquals { val: InsnId, expected: Const, state: InsnId }, /// Side-exit if val doesn't have the expected shape. GuardShape { val: InsnId, shape: ShapeId, state: InsnId }, /// Side-exit if the block param has been modified or the block handler for the frame @@ -899,6 +901,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::ArrayArefFixnum { array, index, .. } => { write!(f, "ArrayArefFixnum {array}, {index}") } + Insn::ArrayLength { array } => { + write!(f, "ArrayLength {array}") + } Insn::NewHash { elements, .. } => { write!(f, "NewHash")?; let mut prefix = " "; @@ -1604,6 +1609,7 @@ impl Function { &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, &NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) }, &ArrayArefFixnum { array, index } => ArrayArefFixnum { array: find!(array), index: find!(index) }, + &ArrayLength { array } => ArrayLength { array: find!(array) }, &ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) }, &SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state }, &GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state }, @@ -1691,6 +1697,7 @@ impl Function { Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, Insn::ArrayArefFixnum { .. } => types::BasicObject, + Insn::ArrayLength { .. } => types::CInt64, Insn::HashAref { .. } => types::BasicObject, Insn::NewHash { .. } => types::HashExact, Insn::HashDup { .. } => types::HashExact, @@ -1703,7 +1710,7 @@ impl Function { &Insn::CCallVariadic { return_type, .. } => return_type, Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type), Insn::GuardTypeNot { .. } => types::BasicObject, - Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_value(*expected)), + Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_const(*expected)), Insn::GuardShape { val, .. } => self.type_of(*val), Insn::FixnumAdd { .. } => types::Fixnum, Insn::FixnumSub { .. } => types::Fixnum, @@ -2803,6 +2810,9 @@ impl Function { worklist.push_back(array); worklist.push_back(index); } + &Insn::ArrayLength { array } => { + worklist.push_back(array); + } &Insn::HashAref { hash, key, state } => { worklist.push_back(hash); worklist.push_back(key); @@ -4428,6 +4438,31 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.stack_push(result); } } + YARVINSN_expandarray => { + let num = get_arg(pc, 0).as_u64(); + let flag = get_arg(pc, 1).as_u64(); + if flag != 0 { + // We don't (yet) handle 0x01 (rest args), 0x02 (post args), or 0x04 + // (reverse?) + // + // Unhandled opcode; side-exit into the interpreter + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledYARVInsn(opcode) }); + break; // End the block + } + let val = state.stack_pop()?; + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, }); + let length = fun.push_insn(block, Insn::ArrayLength { array }); + fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), state: exit_id }); + for i in (0..num).rev() { + // TODO(max): Add a short-cut path for long indices into an array where the + // index is known to be in-bounds + let index = fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(i.try_into().unwrap())) }); + let element = fun.push_insn(block, Insn::ArrayArefFixnum { array, index }); + state.stack_push(element); + } + } _ => { // Unhandled opcode; side-exit into the interpreter let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); @@ -7914,6 +7949,98 @@ mod tests { Return v17 "); } + + #[test] + fn test_expandarray_no_splat() { + eval(r#" + def test(o) + a, b = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): + v20:ArrayExact = GuardType v13, ArrayExact + v21:CInt64 = ArrayLength v20 + v22:CInt64[2] = GuardBitEquals v21, CInt64(2) + v23:Fixnum[1] = Const Value(1) + v24:BasicObject = ArrayArefFixnum v20, v23 + v25:Fixnum[0] = Const Value(0) + v26:BasicObject = ArrayArefFixnum v20, v25 + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v13 + "); + } + + #[test] + fn test_expandarray_splat() { + eval(r#" + def test(o) + a, *b = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@6 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:NilClass, v15:NilClass): + SideExit UnhandledYARVInsn(expandarray) + "); + } + + #[test] + fn test_expandarray_splat_post() { + eval(r#" + def test(o) + a, *b, c = o + end + "#); + assert_contains_opcode("test", YARVINSN_expandarray); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:NilClass, v17:NilClass, v18:NilClass): + SideExit UnhandledYARVInsn(expandarray) + "); + } } #[cfg(test)] diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index ffde7e458d3b28..f24161657e8ec7 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -238,6 +238,23 @@ impl Type { } } + pub fn from_const(val: Const) -> Type { + match val { + Const::Value(v) => Self::from_value(v), + Const::CBool(v) => Self::from_cbool(v), + Const::CInt8(v) => Self::from_cint(types::CInt8, v as i64), + Const::CInt16(v) => Self::from_cint(types::CInt16, v as i64), + Const::CInt32(v) => Self::from_cint(types::CInt32, v as i64), + Const::CInt64(v) => Self::from_cint(types::CInt64, v as i64), + Const::CUInt8(v) => Self::from_cint(types::CUInt8, v as i64), + Const::CUInt16(v) => Self::from_cint(types::CUInt16, v as i64), + Const::CUInt32(v) => Self::from_cint(types::CUInt32, v as i64), + Const::CUInt64(v) => Self::from_cint(types::CUInt64, v as i64), + Const::CPtr(v) => Self::from_cptr(v), + Const::CDouble(v) => Self::from_double(v), + } + } + pub fn from_profiled_type(val: ProfiledType) -> Type { if val.is_fixnum() { types::Fixnum } else if val.is_flonum() { types::Flonum } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 843806e5be5e89..50f6e61f5c242e 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -140,6 +140,7 @@ make_counters! { exit_guard_type_failure, exit_guard_type_not_failure, exit_guard_bit_equals_failure, + exit_guard_int_equals_failure, exit_guard_shape_failure, exit_patchpoint_bop_redefined, exit_patchpoint_method_redefined, From 33f1af6779a22ab84633b43d42dcf273a7e3bbe9 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 20 Oct 2025 12:22:53 -0400 Subject: [PATCH 2/2] ZJIT: Remove idx from hir::Insn::Param (#14872) It turns out that we don't use it anywhere. --- zjit/src/codegen.rs | 6 +++--- zjit/src/hir.rs | 21 +++++++++------------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index ea4a7ecc7c8c21..1f04e61dbc9757 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -264,9 +264,9 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul asm.write_label(label); // Compile all parameters - for &insn_id in block.params() { + for (idx, &insn_id) in block.params().enumerate() { match function.find(insn_id) { - Insn::Param { idx } => { + Insn::Param => { jit.opnds[insn_id.0] = Some(gen_param(&mut asm, idx)); }, insn => unreachable!("Non-param insn found in block.params: {insn:?}"), @@ -367,7 +367,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::StringGetbyteFixnum { string, index } => gen_string_getbyte_fixnum(asm, opnd!(string), opnd!(index)), Insn::StringIntern { val, state } => gen_intern(asm, opnd!(val), &function.frame_state(*state)), Insn::ToRegexp { opt, values, state } => gen_toregexp(jit, asm, *opt, opnds!(values), &function.frame_state(*state)), - Insn::Param { idx } => unreachable!("block.insns should not have Insn::Param({idx})"), + Insn::Param => unreachable!("block.insns should not have Insn::Param"), Insn::Snapshot { .. } => return Ok(()), // we don't need to do anything for this instruction at the moment Insn::Jump(branch) => no_output!(gen_jump(jit, asm, branch)), Insn::IfTrue { val, target } => no_output!(gen_if_true(jit, asm, opnd!(val), target)), diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 73b7cca8d08cdd..370ed568579e0c 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -552,7 +552,7 @@ pub enum SendFallbackReason { pub enum Insn { Const { val: Const }, /// SSA block parameter. Also used for function parameters in the function's entry block. - Param { idx: usize }, + Param, StringCopy { val: InsnId, chilled: bool, state: InsnId }, StringIntern { val: InsnId, state: InsnId }, @@ -888,7 +888,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.inner { Insn::Const { val } => { write!(f, "Const {}", val.print(self.ptr_map)) } - Insn::Param { idx } => { write!(f, "Param {idx}") } + Insn::Param => { write!(f, "Param") } Insn::NewArray { elements, .. } => { write!(f, "NewArray")?; let mut prefix = " "; @@ -3661,18 +3661,15 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } // Load basic block params first - let self_param = fun.push_insn(block, Insn::Param { idx: SELF_PARAM_IDX }); + let self_param = fun.push_insn(block, Insn::Param); let mut state = { let mut result = FrameState::new(iseq); - let mut idx = 1; let local_size = if insn_idx == 0 { num_locals(iseq) } else { incoming_state.locals.len() }; for _ in 0..local_size { - result.locals.push(fun.push_insn(block, Insn::Param { idx })); - idx += 1; + result.locals.push(fun.push_insn(block, Insn::Param)); } for _ in incoming_state.stack { - result.stack.push(fun.push_insn(block, Insn::Param { idx })); - idx += 1; + result.stack.push(fun.push_insn(block, Insn::Param)); } result }; @@ -4581,11 +4578,11 @@ fn compile_jit_entry_state(fun: &mut Function, jit_entry_block: BlockId) -> (Ins let iseq = fun.iseq; let param_size = unsafe { get_iseq_body_param_size(iseq) }.as_usize(); - let self_param = fun.push_insn(jit_entry_block, Insn::Param { idx: SELF_PARAM_IDX }); + let self_param = fun.push_insn(jit_entry_block, Insn::Param); let mut entry_state = FrameState::new(iseq); for local_idx in 0..num_locals(iseq) { if local_idx < param_size { - entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Param { idx: local_idx + 1 })); // +1 for self + entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Param)); } else { entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Const { val: Const::Value(Qnil) })); } @@ -4935,7 +4932,7 @@ mod infer_tests { function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } }); let v1 = function.push_insn(entry, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(4)) }); function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![v1] })); - let param = function.push_insn(exit, Insn::Param { idx: 0 }); + let param = function.push_insn(exit, Insn::Param); crate::cruby::with_rubyvm(|| { function.infer_types(); }); @@ -4954,7 +4951,7 @@ mod infer_tests { function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } }); let v1 = function.push_insn(entry, Insn::Const { val: Const::Value(Qfalse) }); function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![v1] })); - let param = function.push_insn(exit, Insn::Param { idx: 0 }); + let param = function.push_insn(exit, Insn::Param); crate::cruby::with_rubyvm(|| { function.infer_types(); assert_bit_equal(function.type_of(param), types::TrueClass.union(types::FalseClass));