From 8852b4b2deea20f488208dc4730ef149f67d7594 Mon Sep 17 00:00:00 2001 From: nagachika Date: Mon, 17 Jul 2023 13:59:25 +0900 Subject: merge revision(s) 0ce2bdc76dd17aa3d42a352a6244c87a51e7606d: [Backport #19595] YJIT: Fix missing argc check in known cfuncs Previously we were missing a compile-time check that the known cfuncs receive the correct number of arguments. We noticied this because in particular when using ARGS_SPLAT, which also wasn't checked, YJIT would crash on code which was otherwise correct (didn't raise exceptions in the VM). This still supports vararg (argc == -1) cfuncs. I added an additional assertion that when we use the specialized codegen for one of these known functions that the argc are popped off the stack correctly, which should help ensure they're implemented correctly (previously the crash was usually observed on a future `leave` insn). [Bug #19595] --- bootstraptest/test_yjit.rb | 32 ++++++++++++++++++++++++++++++++ yjit/src/codegen.rs | 4 +++- 2 files changed, 35 insertions(+), 1 deletion(-) --- bootstraptest/test_yjit.rb | 32 ++++++++++++++++++++++++++++++++ version.h | 2 +- yjit/src/codegen.rs | 4 +++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 29efba24b3..ea5911ebb9 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1151,6 +1151,38 @@ assert_equal '42', %q{ run } +# splatting an empty array on a specialized method +assert_equal 'ok', %q{ + def run + "ok".to_s(*[]) + end + + run + run +} + +# splatting an single element array on a specialized method +assert_equal '[1]', %q{ + def run + [].<<(*[1]) + end + + run + run +} + +# specialized method with wrong args +assert_equal 'ok', %q{ + def run(x) + "bad".to_s(123) if x + rescue + :ok + end + + run(false) + run(true) +} + # getinstancevariable on Symbol assert_equal '[nil, nil]', %q{ # @foo to exercise the getinstancevariable instruction diff --git a/version.h b/version.h index 833742a29c..54dcdc2d51 100644 --- a/version.h +++ b/version.h @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 2 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 83 +#define RUBY_PATCHLEVEL 84 #include "ruby/version.h" #include "ruby/internal/abi.h" diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f07f5ee3d9..562bc774a9 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4580,10 +4580,12 @@ fn gen_send_cfunc( } // Delegate to codegen for C methods if we have it. - if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 { + if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 && flags & VM_CALL_ARGS_SPLAT == 0 && (cfunc_argc == -1 || argc == cfunc_argc) { let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def }); + let expected_stack_after = asm.ctx.get_stack_size() as i32 - argc; if let Some(known_cfunc_codegen) = codegen_p { if known_cfunc_codegen(jit, ctx, asm, ocb, ci, cme, block, argc, recv_known_klass) { + assert_eq!(expected_stack_after, asm.ctx.get_stack_size() as i32); // cfunc codegen generated code. Terminate the block so // there isn't multiple calls in the same block. jump_to_next_insn(jit, ctx, asm, ocb); -- cgit v1.2.3