diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml index 38496d5cebbebe..b82cd3fbda5508 100644 --- a/.github/auto_request_review.yml +++ b/.github/auto_request_review.yml @@ -12,9 +12,8 @@ files: 'tool/zjit_bisect.rb': [team:jit] 'doc/jit/*': [team:jit] # Skip github workflow files because the team don't necessarily need to review dependabot updates for GitHub Actions. It's noisy in notifications, and they're auto-merged anyway. + '.github/workflows/yjit-*.yml': [] + '.github/workflows/zjit-*.yml': [] options: ignore_draft: true - # This currently doesn't work as intended. We want to skip reviews when only - # cruby_bingings.inc.rs is modified, but this skips reviews even when other - # files are modified as well. To be enabled after fixing the behavior. - #last_files_match_only: true + last_files_match_only: true diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml index 207315a084cc59..e5389bcf22b7bf 100644 --- a/.github/workflows/auto_request_review.yml +++ b/.github/workflows/auto_request_review.yml @@ -14,7 +14,8 @@ jobs: if: ${{ github.repository == 'ruby/ruby' && github.base_ref == 'master' }} steps: - name: Request review based on files changes and/or groups the author belongs to - uses: necojackarc/auto-request-review@e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424 # v0.13.0 + # Using a fork until https://github.com/necojackarc/auto-request-review/pull/135 is merged + uses: k0kubun/auto-request-review@0df295a0ff5c5d302770f589497280132131c63d # master with: # scope: public_repo token: ${{ secrets.MATZBOT_AUTO_REQUEST_REVIEW_TOKEN }} diff --git a/.github/workflows/post_push.yml b/.github/workflows/post_push.yml index 29736240ae3158..237679ebb1a810 100644 --- a/.github/workflows/post_push.yml +++ b/.github/workflows/post_push.yml @@ -15,6 +15,8 @@ jobs: if: ${{ github.repository == 'ruby/ruby' }} steps: - name: Sync git.ruby-lang.org + id: sync-git + continue-on-error: true run: | mkdir -p ~/.ssh (umask 066; printenv RUBY_GIT_SYNC_PRIVATE_KEY > ~/.ssh/id_ed25519) @@ -73,6 +75,8 @@ jobs: if: ${{ github.ref == 'refs/heads/master' }} - name: Push PR notes to GitHub + id: pr-notes + continue-on-error: true run: ruby tool/notes-github-pr.rb "$(pwd)/.git" "$GITHUB_OLD_SHA" "$GITHUB_NEW_SHA" refs/heads/master env: GITHUB_OLD_SHA: ${{ github.event.before }} @@ -83,6 +87,10 @@ jobs: EMAIL: svn-admin@ruby-lang.org if: ${{ github.ref == 'refs/heads/master' }} + - name: Check for failures + run: exit 1 + if: ${{ steps.sync-git.outcome == 'failure' || steps.pr-notes.outcome == 'failure' }} + - uses: ./.github/actions/slack with: SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index a897c86b657e9c..06ed4f6ac334dd 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -169,6 +169,9 @@ def find_openssl_library # added in 3.5.0 have_func("SSL_get0_peer_signature_name(NULL, NULL)", ssl_h) +# added in 4.0.0 +have_func("ASN1_BIT_STRING_set1(NULL, NULL, 0, 0)", "openssl/asn1.h") + Logging::message "=== Checking done. ===\n" # Append flags from environment variables. diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 6592f9cceaf714..ed3b5b7c0f5611 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -29,4 +29,27 @@ # define EVP_PKEY_eq(a, b) EVP_PKEY_cmp(a, b) #endif +/* added in 4.0.0 */ +#ifndef HAVE_ASN1_BIT_STRING_SET1 +static inline int +ASN1_BIT_STRING_set1(ASN1_BIT_STRING *bitstr, const uint8_t *data, + size_t length, int unused_bits) +{ + if (length > INT_MAX || !ASN1_STRING_set(bitstr, data, (int)length)) + return 0; + bitstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); + bitstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; + return 1; +} + +static inline int +ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *bitstr, size_t *length, + int *unused_bits) +{ + *length = bitstr->length; + *unused_bits = bitstr->flags & 0x07; + return 1; +} +#endif + #endif /* _OSSL_OPENSSL_MISSING_H_ */ diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 18fa8edeb5777e..67c03b7f98a952 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -228,7 +228,7 @@ obj_to_asn1int(VALUE obj) } static ASN1_BIT_STRING* -obj_to_asn1bstr(VALUE obj, long unused_bits) +obj_to_asn1bstr(VALUE obj, int unused_bits) { ASN1_BIT_STRING *bstr; @@ -236,11 +236,11 @@ obj_to_asn1bstr(VALUE obj, long unused_bits) ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ "the range 0 to 7"); StringValue(obj); - if(!(bstr = ASN1_BIT_STRING_new())) - ossl_raise(eASN1Error, NULL); - ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj)); - bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ - bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; + if (!(bstr = ASN1_BIT_STRING_new())) + ossl_raise(eASN1Error, "ASN1_BIT_STRING_new"); + if (!ASN1_BIT_STRING_set1(bstr, (uint8_t *)RSTRING_PTR(obj), + RSTRING_LEN(obj), unused_bits)) + ossl_raise(eASN1Error, "ASN1_BIT_STRING_set1"); return bstr; } @@ -364,22 +364,25 @@ decode_int(unsigned char* der, long length) } static VALUE -decode_bstr(unsigned char* der, long length, long *unused_bits) +decode_bstr(unsigned char* der, long length, int *unused_bits) { ASN1_BIT_STRING *bstr; const unsigned char *p; - long len; + size_t len; VALUE ret; + int state; p = der; - if(!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); - len = bstr->length; - *unused_bits = 0; - if(bstr->flags & ASN1_STRING_FLAG_BITS_LEFT) - *unused_bits = bstr->flags & 0x07; - ret = rb_str_new((const char *)bstr->data, len); + if (!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) + ossl_raise(eASN1Error, "d2i_ASN1_BIT_STRING"); + if (!ASN1_BIT_STRING_get_length(bstr, &len, unused_bits)) { + ASN1_BIT_STRING_free(bstr); + ossl_raise(eASN1Error, "ASN1_BIT_STRING_get_length"); + } + ret = ossl_str_new((const char *)ASN1_STRING_get0_data(bstr), len, &state); ASN1_BIT_STRING_free(bstr); + if (state) + rb_jump_tag(state); return ret; } @@ -763,7 +766,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, { VALUE value, asn1data; unsigned char *p; - long flag = 0; + int flag = 0; p = *pp; @@ -820,7 +823,7 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, asn1data = rb_obj_alloc(klass); ossl_asn1_initialize(4, args, asn1data); if(tag == V_ASN1_BIT_STRING){ - rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); + rb_ivar_set(asn1data, sivUNUSED_BITS, INT2NUM(flag)); } } else { diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index ddb67fcf07a7ce..9dd4b466d28443 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -922,7 +922,7 @@ ossl_ocspbres_get_status(VALUE self) VALUE ext = rb_ary_new(); int ext_count = OCSP_SINGLERESP_get_ext_count(single); for (int j = 0; j < ext_count; j++) { - X509_EXTENSION *x509ext = OCSP_SINGLERESP_get_ext(single, j); + const X509_EXTENSION *x509ext = OCSP_SINGLERESP_get_ext(single, j); rb_ary_push(ext, ossl_x509ext_new(x509ext)); } rb_ary_push(ary, ext); @@ -1341,7 +1341,6 @@ static VALUE ossl_ocspsres_get_extensions(VALUE self) { OCSP_SINGLERESP *sres; - X509_EXTENSION *ext; int count, i; VALUE ary; @@ -1350,7 +1349,7 @@ ossl_ocspsres_get_extensions(VALUE self) count = OCSP_SINGLERESP_get_ext_count(sres); ary = rb_ary_new2(count); for (i = 0; i < count; i++) { - ext = OCSP_SINGLERESP_get_ext(sres, i); + const X509_EXTENSION *ext = OCSP_SINGLERESP_get_ext(sres, i); rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ } diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 023361b90f2306..efba33b7529373 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -71,7 +71,6 @@ void Init_ossl_dh(void); * EC */ extern VALUE cEC; -VALUE ossl_ec_new(EVP_PKEY *); void Init_ossl_ec(void); #define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index b31a854a63e9ac..393e08acff0c1e 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -706,7 +706,7 @@ ossl_ts_resp_get_tsa_certificate(VALUE self) TS_RESP *resp; PKCS7 *p7; PKCS7_SIGNER_INFO *ts_info; - X509 *cert; + const X509 *cert; GetTSResponse(self, resp); if (!(p7 = TS_RESP_get_token(resp))) diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h index d25167ee7b0868..71932ef1a9a647 100644 --- a/ext/openssl/ossl_x509.h +++ b/ext/openssl/ossl_x509.h @@ -29,7 +29,7 @@ void Init_ossl_x509(void); */ extern VALUE cX509Attr; -VALUE ossl_x509attr_new(X509_ATTRIBUTE *); +VALUE ossl_x509attr_new(const X509_ATTRIBUTE *); X509_ATTRIBUTE *GetX509AttrPtr(VALUE); void Init_ossl_x509attr(void); @@ -38,7 +38,7 @@ void Init_ossl_x509attr(void); */ extern VALUE cX509Cert; -VALUE ossl_x509_new(X509 *); +VALUE ossl_x509_new(const X509 *); X509 *GetX509CertPtr(VALUE); X509 *DupX509CertPtr(VALUE); void Init_ossl_x509cert(void); @@ -46,7 +46,7 @@ void Init_ossl_x509cert(void); /* * X509CRL */ -VALUE ossl_x509crl_new(X509_CRL *); +VALUE ossl_x509crl_new(const X509_CRL *); X509_CRL *GetX509CRLPtr(VALUE); void Init_ossl_x509crl(void); @@ -55,14 +55,14 @@ void Init_ossl_x509crl(void); */ extern VALUE cX509Ext; -VALUE ossl_x509ext_new(X509_EXTENSION *); +VALUE ossl_x509ext_new(const X509_EXTENSION *); X509_EXTENSION *GetX509ExtPtr(VALUE); void Init_ossl_x509ext(void); /* * X509Name */ -VALUE ossl_x509name_new(X509_NAME *); +VALUE ossl_x509name_new(const X509_NAME *); X509_NAME *GetX509NamePtr(VALUE); void Init_ossl_x509name(void); @@ -77,7 +77,7 @@ void Init_ossl_x509req(void); */ extern VALUE cX509Rev; -VALUE ossl_x509revoked_new(X509_REVOKED *); +VALUE ossl_x509revoked_new(const X509_REVOKED *); X509_REVOKED *DupX509RevokedPtr(VALUE); void Init_ossl_x509revoked(void); diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index 4769e56e1e8501..b0773e7a7dca28 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -48,13 +48,14 @@ static const rb_data_type_t ossl_x509attr_type = { * Public */ VALUE -ossl_x509attr_new(X509_ATTRIBUTE *attr) +ossl_x509attr_new(const X509_ATTRIBUTE *attr) { X509_ATTRIBUTE *new; VALUE obj; obj = NewX509Attr(cX509Attr); - new = X509_ATTRIBUTE_dup(attr); + /* OpenSSL 1.1.1 takes a non-const pointer */ + new = X509_ATTRIBUTE_dup((X509_ATTRIBUTE *)attr); if (!new) ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); SetX509Attr(obj, new); @@ -196,7 +197,7 @@ ossl_x509attr_set_value(VALUE self, VALUE value) ossl_raise(eX509AttrError, "attribute value must be ASN1::Set"); if (X509_ATTRIBUTE_count(attr)) { /* populated, reset first */ - ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); + const ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1); if (!new_attr) { sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free); @@ -240,7 +241,7 @@ ossl_x509attr_get_value(VALUE self) count = X509_ATTRIBUTE_count(attr); for (i = 0; i < count; i++) - sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); + sk_ASN1_TYPE_push(sk, (ASN1_TYPE *)X509_ATTRIBUTE_get0_type(attr, i)); if ((len = i2d_ASN1_SET_ANY(sk, NULL)) <= 0) { sk_ASN1_TYPE_free(sk); diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index 95679c7d24ea72..de246759ab7af3 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -48,13 +48,14 @@ static const rb_data_type_t ossl_x509_type = { * Public */ VALUE -ossl_x509_new(X509 *x509) +ossl_x509_new(const X509 *x509) { X509 *new; VALUE obj; obj = NewX509(cX509Cert); - new = X509_dup(x509); + /* OpenSSL 1.1.1 takes a non-const pointer */ + new = X509_dup((X509 *)x509); if (!new) ossl_raise(eX509CertError, "X509_dup"); SetX509(obj, new); @@ -345,7 +346,7 @@ static VALUE ossl_x509_get_subject(VALUE self) { X509 *x509; - X509_NAME *name; + const X509_NAME *name; GetX509(self, x509); if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ @@ -380,7 +381,7 @@ static VALUE ossl_x509_get_issuer(VALUE self) { X509 *x509; - X509_NAME *name; + const X509_NAME *name; GetX509(self, x509); if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ @@ -603,14 +604,13 @@ ossl_x509_get_extensions(VALUE self) { X509 *x509; int count, i; - X509_EXTENSION *ext; VALUE ary; GetX509(self, x509); count = X509_get_ext_count(x509); ary = rb_ary_new_capa(count); for (i=0; iworld_stopped = false; objspace->gc_count++; + if (current_gc_may_move) objspace->moving_gc_count++; pthread_cond_broadcast(&objspace->cond_world_started); if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) { @@ -492,7 +494,7 @@ rb_mmtk_gc_thread_bug(const char *msg, ...) rb_gc_print_backtrace(); fprintf(stderr, "\n"); - rb_mmtk_resume_mutators(); + rb_mmtk_resume_mutators(false); sleep(5); @@ -1459,6 +1461,7 @@ rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE hash_or_key) enum gc_stat_sym { gc_stat_sym_count, + gc_stat_sym_moving_gc_count, gc_stat_sym_time, gc_stat_sym_total_allocated_objects, gc_stat_sym_total_bytes, @@ -1478,6 +1481,7 @@ setup_gc_stat_symbols(void) if (gc_stat_symbols[0] == 0) { #define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s)) S(count); + S(moving_gc_count); S(time); S(total_allocated_objects); S(total_bytes); @@ -1514,6 +1518,7 @@ rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym) rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr)); SET(count, objspace->gc_count); + SET(moving_gc_count, objspace->moving_gc_count); SET(time, objspace->total_gc_time / (1000 * 1000)); SET(total_allocated_objects, objspace->total_allocated_objects); SET(total_bytes, mmtk_total_bytes()); diff --git a/gc/mmtk/mmtk.h b/gc/mmtk/mmtk.h index 20d268419ddea0..ee338c87efe15e 100644 --- a/gc/mmtk/mmtk.h +++ b/gc/mmtk/mmtk.h @@ -60,7 +60,7 @@ typedef struct MMTk_RubyUpcalls { void (*init_gc_worker_thread)(struct MMTk_GCThreadTLS *gc_worker_tls); bool (*is_mutator)(void); void (*stop_the_world)(void); - void (*resume_mutators)(void); + void (*resume_mutators)(bool gc_may_move); void (*block_for_gc)(MMTk_VMMutatorThread tls); void (*before_updating_jit_code)(void); void (*after_updating_jit_code)(void); diff --git a/gc/mmtk/src/abi.rs b/gc/mmtk/src/abi.rs index 2a0e9113fad2f2..c880302080307b 100644 --- a/gc/mmtk/src/abi.rs +++ b/gc/mmtk/src/abi.rs @@ -301,7 +301,7 @@ pub struct RubyUpcalls { pub init_gc_worker_thread: extern "C" fn(gc_worker_tls: *mut GCThreadTLS), pub is_mutator: extern "C" fn() -> bool, pub stop_the_world: extern "C" fn(), - pub resume_mutators: extern "C" fn(), + pub resume_mutators: extern "C" fn(gc_may_move: bool), pub block_for_gc: extern "C" fn(tls: VMMutatorThread), pub before_updating_jit_code: extern "C" fn(), pub after_updating_jit_code: extern "C" fn(), diff --git a/gc/mmtk/src/collection.rs b/gc/mmtk/src/collection.rs index 83d046aef43092..28daa4f9914256 100644 --- a/gc/mmtk/src/collection.rs +++ b/gc/mmtk/src/collection.rs @@ -48,11 +48,13 @@ impl Collection for VMCollection { } fn resume_mutators(_tls: VMWorkerThread) { - if CURRENT_GC_MAY_MOVE.load(Ordering::Relaxed) { + let current_gc_may_move = CURRENT_GC_MAY_MOVE.load(Ordering::Relaxed); + + if current_gc_may_move { (upcalls().after_updating_jit_code)(); } - (upcalls().resume_mutators)(); + (upcalls().resume_mutators)(current_gc_may_move); } fn block_for_gc(tls: VMMutatorThread) { diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb index e23263048c76c4..b56ae2e2b10487 100644 --- a/lib/bundler/lockfile_generator.rb +++ b/lib/bundler/lockfile_generator.rb @@ -105,9 +105,11 @@ def add_section(name, value) def bundler_checksum return [] if Bundler.gem_version.to_s.end_with?(".dev") + bundler_spec = definition.sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last + return [] unless File.exist?(bundler_spec.cache_file) + require "rubygems/package" - bundler_spec = definition.sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last package = Gem::Package.new(bundler_spec.cache_file) definition.sources.metadata_source.checksum_store.register(bundler_spec, Checksum.from_gem_package(package)) diff --git a/prism_compile.c b/prism_compile.c index 59514697d0c3c4..85e8a2cdfa40d7 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -3749,7 +3749,7 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { if (PM_BRANCH_COVERAGE_P(iseq)) { - uint32_t end_cursor; + uint32_t end_cursor = 0; bool end_found = false; if (call_node->closing_loc.length > 0) { diff --git a/set.c b/set.c index 6bfded02a414ee..8f889529e7e7d3 100644 --- a/set.c +++ b/set.c @@ -655,7 +655,7 @@ set_i_to_set(VALUE set) return set; } - return rb_funcall_passing_block(rb_cSet, id_new, 0, NULL); + return rb_funcall_passing_block(rb_cSet, id_new, 1, &set); } /* diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index d19a610003bf1b..8ab3cc7e8dd6fe 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -2295,6 +2295,10 @@ bundle("lock --add-checksums", artifice: "endpoint") + checksums = checksums_section_when_enabled do |c| + c.no_checksum "warning", "18.0.0" + end + expect(lockfile).to eq <<~L GEM remote: https://gem.repo4/ @@ -2306,10 +2310,7 @@ DEPENDENCIES warning - - CHECKSUMS - warning (18.0.0) - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 9bc5a8e1d187b7..03a3786d80a20d 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -1419,9 +1419,7 @@ #{lockfile_platforms} DEPENDENCIES - - CHECKSUMS - + #{checksums_section_when_enabled} RUBY VERSION #{Bundler::RubyVersion.system} @@ -1708,6 +1706,7 @@ checksums = checksums_section_when_enabled do |c| c.checksum(gem_repo4, "myrack", "1.0") end + checksums.delete("bundler") expect(lockfile).to eq <<~L GEM diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index a1c656bc3c89cd..e3c0fc19ea01f1 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -1355,7 +1355,7 @@ def lock_with(ruby_version = nil) gem "bar", :git => "#{lib_path("bar-1.0")}" G - bundle :install + bundle :install, env: { "BUNDLE_LOCKFILE_CHECKSUMS" => "false" } ruby <<-RUBY, artifice: nil require 'bundler/setup' diff --git a/spec/bundler/support/checksums.rb b/spec/bundler/support/checksums.rb index 03147bf6f4638f..7b69bba6680b55 100644 --- a/spec/bundler/support/checksums.rb +++ b/spec/bundler/support/checksums.rb @@ -3,6 +3,8 @@ module Spec module Checksums class ChecksumsBuilder + attr_reader :bundler_registered + def initialize(enabled = true, &block) @enabled = enabled @checksums = {} @@ -15,6 +17,8 @@ def initialize_copy(original) end def checksum(repo, name, version, platform = Gem::Platform::RUBY, folder = "gems") + @bundler_registered = true if name == "bundler" + name_tuple = Gem::NameTuple.new(name, version, platform) gem_file = File.join(repo, folder, "#{name_tuple.full_name}.gem") File.open(gem_file, "rb") do |f| @@ -50,8 +54,13 @@ def register(name_tuple, checksum) end end - def checksums_section(enabled = true, &block) - ChecksumsBuilder.new(enabled, &block) + def checksums_section(enabled = true, bundler_checksum: true, &block) + ChecksumsBuilder.new(enabled, &block).tap do |builder| + next if builder.bundler_registered || !bundler_checksum + + next if Bundler::VERSION.to_s.end_with?(".dev") + builder.checksum(system_gem_path, "bundler", Bundler::VERSION, Gem::Platform::RUBY, "cache") + end end def checksums_section_when_enabled(target_lockfile = nil, &block) @@ -64,7 +73,7 @@ def checksums_section_when_enabled(target_lockfile = nil, &block) end def checksum_to_lock(*args) - checksums_section do |c| + checksums_section(true, bundler_checksum: false) do |c| c.checksum(*args) end.to_s.sub(/^CHECKSUMS\n/, "").strip end diff --git a/sprintf.c b/sprintf.c index de88a9f4b35a20..234aff76f5d3a1 100644 --- a/sprintf.c +++ b/sprintf.c @@ -440,8 +440,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) { VALUE val = GETARG(); VALUE tmp; - unsigned int c; - int n, encidx; + unsigned int c = 0; + int n, encidx = 0; tmp = rb_check_string_type(val); if (!NIL_P(tmp)) { diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb index 86f51cf4385a01..1716aef3807af9 100644 --- a/test/openssl/test_pkey_rsa.rb +++ b/test/openssl/test_pkey_rsa.rb @@ -462,54 +462,54 @@ def test_private_encoding def test_private_encoding_encrypted rsa = Fixtures.pkey("rsa2048") - encoded = rsa.private_to_der("aes-128-cbc", "abcdef") + encoded = rsa.private_to_der("aes-128-cbc", "abcdefgh") asn1 = OpenSSL::ASN1.decode(encoded) # PKCS #8 EncryptedPrivateKeyInfo assert_kind_of OpenSSL::ASN1::Sequence, asn1 assert_equal 2, asn1.value.size assert_not_equal rsa.private_to_der, encoded - assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef") - assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdef" } + assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdefgh") + assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdefgh" } assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") } - encoded = rsa.private_to_pem("aes-128-cbc", "abcdef") + encoded = rsa.private_to_pem("aes-128-cbc", "abcdefgh") assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0] - assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef") + assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdefgh") # Use openssl instead of certtool due to https://gitlab.com/gnutls/gnutls/-/issues/1632 - # openssl pkcs8 -in test/openssl/fixtures/pkey/rsa2048.pem -topk8 -v2 aes-128-cbc -passout pass:abcdef + # openssl pkcs8 -in test/openssl/fixtures/pkey/rsa2048.pem -topk8 -v2 aes-128-cbc -passout pass:abcdefgh pem = <<~EOF - -----BEGIN ENCRYPTED PRIVATE KEY----- - MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIay5V8CDQi5oCAggA - MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBB6eyagcbsvdQlM1kPcH7kiBIIE - 0Ng1apIyoPAZ4BfC4kMNeSmeAv3XspxqYi3uWzXiNyTcoE6390swrwM6WvdpXvLI - /n/V06krxPZ9X4fBG2kLUzXt5f09lEvmQU1HW1wJGU5Sq3bNeXBrlJF4DzJE4WWd - whVVvNMm44ghdzN/jGSw3z+6d717N+waa7vrpBDsHjhsPNwxpyzUvcFPFysTazxx - kN/dziIBF6SRKi6w8VaJEMQ8czGu5T3jOc2e/1p3/AYhHLPS4NHhLR5OUh0TKqLK - tANAqI9YqCAjhqcYCmN3mMQXY52VfOqG9hlX1x9ZQyqiH7l102EWbPqouk6bCBLQ - wHepPg4uK99Wsdh65qEryNnXQ5ZmO6aGb6T3TFENCaNKmi8Nh+/5dr7J7YfhIwpo - FqHvk0hrZ8r3EQlr8/td0Yb1/IKzeQ34638uXf9UxK7C6o+ilsmJDR4PHJUfZL23 - Yb9qWJ0GEzd5AMsI7x6KuUxSuH9nKniv5Tzyty3Xmb4FwXUyADWE19cVuaT+HrFz - GraKnA3UXbEgWAU48/l4K2HcAHyHDD2Kbp8k+o1zUkH0fWUdfE6OUGtx19Fv44Jh - B7xDngK8K48C6nrj06/DSYfXlb2X7WQiapeG4jt6U57tLH2XAjHCkvu0IBZ+//+P - yIWduEHQ3w8FBRcIsTNJo5CjkGk580TVQB/OBLWfX48Ay3oF9zgnomDIlVjl9D0n - lKxw/KMCLkvB78rUeGbr1Kwj36FhGpTBw3FgcYGa5oWFZTlcOgMTXLqlbb9JnDlA - Zs7Tu0WTyOTV/Dne9nEm39Dzu6wRojiIpmygTD4FI7rmOy3CYNvL3XPv7XQj0hny - Ee/fLxugYlQnwPZSqOVEQY2HsG7AmEHRsvy4bIWIGt+yzAPZixt9MUdJh91ttRt7 - QA/8J1pAsGqEuQpF6UUINZop3J7twfhO4zWYN/NNQ52eWNX2KLfjfGRhrvatzmZ0 - BuCsCI9hwEeE6PTlhbX1Rs177MrDc3vlqz2V3Po0OrFjXAyg9DR/OC4iK5wOG2ZD - 7StVSP8bzwQXsz3fJ0ardKXgnU2YDAP6Vykjgt+nFI09HV/S2faOc2g/UK4Y2khl - J93u/GHMz/Kr3bKWGY1/6nPdIdFheQjsiNhd5gI4tWik2B3QwU9mETToZ2LSvDHU - jYCys576xJLkdMM6nJdq72z4tCoES9IxyHVs4uLjHKIo/ZtKr+8xDo8IL4ax3U8+ - NMhs/lwReHmPGahm1fu9zLRbNCVL7e0zrOqbjvKcSEftObpV/LLcPYXtEm+lZcck - /PMw49HSE364anKEXCH1cyVWJwdZRpFUHvRpLIrpHru7/cthhiEMdLgK1/x8sLob - DiyieLxH1DPeXT4X+z94ER4IuPVOcV5AXc/omghispEX6DNUnn5jC4e3WyabjUbw - MuO9lVH9Wi2/ynExCqVmQkdbTXuLwjni1fJ27Q5zb0aCmhO8eq6P869NCjhJuiUj - NI9XtGLP50YVWE0kL8KEJqnyFudky8Khzk4/dyixQFqin5GfT4vetrLunGHy7lRB - 3LpnFrpMOr+0xr1RW1k9vlmjRsJSiojJfReYO7gH3B5swiww2azogoL+4jhF1Jxh - OYLWdkKhP2jSVGqtIDtny0O4lBm2+hLpWjiI0mJQ7wdA - -----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ+Sg92Hgy8EgVPf7t +Hen1qwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEB5UX2xdDO8/AKA8 ++Y5CZyUEggTQkArh4mMPpnAe3xOcDKMz8KCn5lrLb/6Dla7Rp9LHKGkUfyI11EZt +m+OIriwy9oDQquKyVuLQVGAxXKk+3pyxMqLB0i3hLYamT3vzoPctyVwjuRuKoU3E +CbF0YhCoxvWMvjHsolwYzx00DbLXouE4BGKvPjnhw5hwtdoZ9Px0ZnCXCxVXi8z/ +mlw7a2ptKEiHQVjuPPbttq+dA+ez7pbWonWVod5TMaPtyEZu5XfPD+0pMboceHZg +H8ehgUhV3mzEJiisFGg1q9hj+4BaFl5m4tvqp43inCCdShE78CNnOPzJ7WCjKJqi +jGvHjeMoVx3rZXHcZDAzfIZvDigp9uAfzjRJjpRG8sg5sDQVC7vdUhQDe5TorKT2 +Vb0tdVYxoEpMJ3dhU6Ds5JxMR6GTLjsjTqOkAl6db3HxulwfEpr7YjOpfODR+ttA +BeIcUcMLsDHayIaQaMLIftHxOkfX7UxoFW9CMG5UMQf/m3eEgVUwgK/E5sUJRUTo +yhRzJ4NAP4fgc4YH9tbzvUrhfdCXCBEOn6IlDQL66SZr8Mm+Ggu4Ij4TnKWXLrXL +nSTDDa42kPOvtedKqxC/uXE7rrfh+uyw6J6OjSl6u86TIebndLuDo5DTdWKh8rsg +fvZZ6332dfMp8JC9/4YnYIJdI7acInSoyHp52OB+2+dgYCr5OrZFjjKS7nELVfo7 +OxGy6uH3NHF9qyUEf3MN17TRHI7jP3zKbXcDTPSyxLQkWe/CU5B251CTmoTSidSW +EhKnPlGZYbpVQJ4KGEL5UeY8W9PXQo4Dl7TmXBGvuPqNF8kMB3XrPIph7GmihmX0 +nlJqLk9eiRFmUETS0IdAyKJrm4R9Hf6rjYCbXlaApylyVUdSZ2BxgeoTY9BA6Kgf +3xlgMv01MoUkXMx2+OLIc9MzhButQiDxh3mfS012CjKqUFrJhRSa8DOpUfVgmXpq +/HP4drWamLWYJR8FsmJS11ZYc1EK/ctJTSpqfewvoUGOSHomhh7zXn1Acb6+9/3p +bcrJjoR5K8Jg6NlG4dSNkpY/x92I7bFLXFqELIH5tteDrlQen5eASjaiyPPAoOw8 +IGfOmFS4VUPh1VP6g8Jtn5Hr2qXB3DoQoI6EvUZhJ6GJfi67mx5VKux6G9MzJkix +GU1cL4WzWK2DU0l39UxXjS+4TmOYbrqLVnVMjusX0fwb8LkDC/fVohbhLwhHNwu6 +nSTSEpS9zSDrv1JXFtAtPv6XCSFs6ssPWJMwGSdThn7EfV0GEhG2mCzTyVhwxxQo +6U/Suqq4oMZoracPUCZx0E4u/bb4KBoFA/eBNPJENTR18IiV+D7wAxlxauO3N1t4 +iJxwrrvSgQPmOGuxrh5LVD41UXYUWLtndzabnpByppFn2MbmvrqJgon0MSs84cTA +7scnbPu1V3PpKy/t67gtVw9Ue8hLjrskWB1JPFYr7vRWvJzYjfbflyroF+QEJ3TA +6rTfUC9+ePci6T+i9jF4xcmzqYzRtnGtp5nRUitJGw0uwBTDwzfI2WD6ltvvu7lc +pHuzvY5zEapuu1JhjHLUd+OE8rVVM999DUXo/IDLsWyRCphCiYfVXJNogd9rB0Ta +5AhVgpRhxkarBURZyLTYj7NRxCsbHq7XExJNrIdRG/KlBQfyEyIzZ7E= +-----END ENCRYPTED PRIVATE KEY----- EOF - assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdef") + assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdefgh") end def test_params diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index ce1b2c1e966fd0..e4fd581079c4dd 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -1909,7 +1909,9 @@ def test_tmp_dh_callback } } start_server(ctx_proc: ctx_proc) do |port| - server_connect(port) { |ssl| + ctx = OpenSSL::SSL::SSLContext.new + ctx.groups = "P-256" # Exclude RFC 7919 groups + server_connect(port, ctx) { |ssl| assert called, "dh callback should be called" assert_equal dh.to_der, ssl.tmp_key.to_der } @@ -2172,7 +2174,9 @@ def test_tmp_dh ctx.tmp_dh = dh } start_server(ctx_proc: ctx_proc) do |port| - server_connect(port) { |ssl| + ctx = OpenSSL::SSL::SSLContext.new + ctx.groups = "P-256" # Exclude RFC 7919 groups + server_connect(port, ctx) { |ssl| assert_equal dh.to_der, ssl.tmp_key.to_der } end diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index cc2e1ab2324e33..61182b990b3abf 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -888,6 +888,16 @@ class C; end end; end + def test_define_singleton_initialize + assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + class C + def self.initialize + end + end + end; + end + def test_singleton_cc_invalidation assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") begin; diff --git a/test/ruby/test_set.rb b/test/ruby/test_set.rb index 70a61aa3b5d5fe..46d649ee737b0a 100644 --- a/test/ruby/test_set.rb +++ b/test/ruby/test_set.rb @@ -978,7 +978,8 @@ def test_to_set assert_equal([-10,-8,-6,-4,-2], set.sort) assert_same set, set.to_set - assert_not_same set, set.to_set { |o| o } + transformed = set.to_set { |o| o + 1 } + assert_equal([-9,-7,-5,-3,-1], transformed.sort) end class MyEnum diff --git a/variable.c b/variable.c index 9d0e4e4a2b9eac..1856095f8c33a5 100644 --- a/variable.c +++ b/variable.c @@ -1008,7 +1008,7 @@ VALUE rb_gvar_set(ID id, VALUE val) { VALUE retval; - struct rb_global_entry *entry; + struct rb_global_entry *entry = NULL; const rb_box_t *box = rb_current_box(); bool use_box_tbl = false; @@ -1041,8 +1041,8 @@ rb_gvar_get(ID id) VALUE retval, gvars, key; const rb_box_t *box = rb_current_box(); bool use_box_tbl = false; - struct rb_global_entry *entry; - struct rb_global_variable *var; + struct rb_global_entry *entry = NULL; + struct rb_global_variable *var = NULL; // TODO: use lock-free rb_id_table when it's available for use (doesn't yet exist) RB_VM_LOCKING() { entry = rb_global_entry(id); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2c9bb027528b13..e3f627ae896cff 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -6048,7 +6048,9 @@ vm_define_method(const rb_execution_context_t *ec, VALUE obj, ID id, VALUE iseqv rb_add_method_iseq(klass, id, (const rb_iseq_t *)iseqval, cref, visi); // Set max_iv_count on klasses based on number of ivar sets that are in the initialize method - if (id == idInitialize && klass != rb_cObject && RB_TYPE_P(klass, T_CLASS) && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) { + if (id == idInitialize && klass != rb_cObject && RB_TYPE_P(klass, T_CLASS) && + !RCLASS_SINGLETON_P(klass) && + (rb_get_alloc_func(klass) == rb_class_allocate_instance)) { RCLASS_SET_MAX_IV_COUNT(klass, rb_estimate_iv_count(klass, (const rb_iseq_t *)iseqval)); } diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index aadb2da8cff9c4..2cde7388fbe127 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -747,7 +747,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(jit, asm, opnd!(recv), opnd!(val), function.type_of(val))), &Insn::IsBlockGiven { lep } => gen_is_block_given(asm, opnd!(lep)), Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)), - Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), opnd!(buffer), &function.frame_state(*state)), + Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), (*buffer).map(|buffer| opnd!(buffer)), &function.frame_state(*state)), &Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)), Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), &Insn::IsA { val, class } => gen_is_a(jit, asm, opnd!(val), opnd!(class)), @@ -2047,7 +2047,7 @@ fn gen_array_pack_buffer( asm: &mut Assembler, elements: Vec, fmt: Opnd, - buffer: Opnd, + buffer: Option, state: &FrameState, ) -> lir::Opnd { gen_prepare_non_leaf_call(jit, asm, state); @@ -2055,9 +2055,13 @@ fn gen_array_pack_buffer( let array_len: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); // After gen_prepare_non_leaf_call, the elements are spilled to the Ruby stack. - // The elements are at the bottom of the virtual stack, followed by the fmt, followed by the buffer. + // The elements are at the bottom of the virtual stack, followed by the fmt, and optionally the buffer. // Get a pointer to the first element on the Ruby stack. - let stack_bottom = state.stack().len() - elements.len() - 2; + let stack_bottom = if buffer.is_some() { + state.stack().len() - elements.len() - 2 + } else { + state.stack().len() - elements.len() - 1 + }; let elements_ptr = asm.lea(Opnd::mem(64, SP, stack_bottom as i32 * SIZEOF_VALUE_I32)); unsafe extern "C" { @@ -2066,7 +2070,7 @@ fn gen_array_pack_buffer( asm_ccall!( asm, rb_vm_opt_newarray_pack_buffer, - EC, array_len.into(), elements_ptr, fmt, buffer + EC, array_len.into(), elements_ptr, fmt, buffer.unwrap_or_else(|| Qundef.into()) ) } diff --git a/zjit/src/codegen_tests.rs b/zjit/src/codegen_tests.rs index 4dbc42a5b41192..40278d112e9a4b 100644 --- a/zjit/src/codegen_tests.rs +++ b/zjit/src/codegen_tests.rs @@ -2399,6 +2399,39 @@ fn test_opt_duparray_send_include_p_redefined() { "), @"[:true, :false]"); } +#[test] +fn test_opt_newarray_send_pack() { + eval(r#" + def test(num) + [num].pack('C') + end + test(65) + "#); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(assert_compiles(r#" + [test(65), test(66), test(67)] + "#), @r#"["A", "B", "C"]"#); +} + +#[test] +fn test_opt_newarray_send_pack_redefined() { + eval(r#" + class Array + alias_method :old_pack, :pack + def pack(fmt, buffer: nil) + "override:#{old_pack(fmt, buffer: buffer)}" + end + end + def test(num) + [num].pack('C') + end + "#); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(assert_compiles(r#" + [test(65), test(66), test(67)] + "#), @r#"["override:A", "override:B", "override:C"]"#); +} + #[test] fn test_opt_newarray_send_pack_buffer() { eval(r#" @@ -5386,3 +5419,37 @@ fn test_ep_escape_preserves_keyword_default() { target("x") "#), @"[]"); } + +#[test] +fn test_send_block_to_accepts_no_block() { + // Methods with &nil should raise ArgumentError when called with a block + assert_snapshot!(inspect(" + def m(a, &nil); a end + + def test + m(1) {} + rescue ArgumentError => e + e.message + end + + test + test + "), @r#""no block accepted""#); +} + +#[test] +fn test_send_block_to_method_not_using_block() { + // Passing a block to a method that doesn't use it should still work correctly. + // ZJIT falls back to the interpreter for this case so that unused block + // warnings are properly emitted. + assert_snapshot!(inspect(" + def m_no_block = 42 + + def test + m_no_block {} + end + + test + test + "), @"42"); +} diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 4e38b261308110..e8db2b6af2649a 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -820,7 +820,7 @@ pub enum Insn { ArrayMax { elements: Vec, state: InsnId }, ArrayMin { elements: Vec, state: InsnId }, ArrayInclude { elements: Vec, target: InsnId, state: InsnId }, - ArrayPackBuffer { elements: Vec, fmt: InsnId, buffer: InsnId, state: InsnId }, + ArrayPackBuffer { elements: Vec, fmt: InsnId, buffer: Option, state: InsnId }, DupArrayInclude { ary: VALUE, target: InsnId, state: InsnId }, /// Extend `left` with the elements from `right`. `left` and `right` must both be `Array`. ArrayExtend { left: InsnId, right: InsnId, state: InsnId }, @@ -1179,7 +1179,9 @@ macro_rules! for_each_operand_impl { Insn::ArrayPackBuffer { elements, fmt, buffer, state, .. } => { $visit_many!(elements); $visit_one!(fmt); - $visit_one!(buffer); + if let Some(buffer) = buffer { + $visit_one!(buffer); + } $visit_one!(state); } Insn::DupArrayInclude { target, state, .. } => { @@ -1831,7 +1833,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { for element in elements { write!(f, "{element}, ")?; } - write!(f, "fmt: {fmt}, buf: {buffer}") + write!(f, "fmt: {fmt}")?; + if let Some(buffer) = buffer { + write!(f, ", buf: {buffer}")?; + } + Ok(()) } Insn::DupArrayInclude { ary, target, .. } => { write!(f, "DupArrayInclude {} | {}", ary.print(self.ptr_map), target) @@ -2351,7 +2357,7 @@ pub enum ValidationError { } /// Check if we can do a direct send to the given iseq with the given args. -fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq_t, ci: *const rb_callinfo, send_insn: InsnId, args: &[InsnId]) -> bool { +fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq_t, ci: *const rb_callinfo, send_insn: InsnId, args: &[InsnId], has_block: bool) -> bool { let mut can_send = true; let mut count_failure = |counter| { can_send = false; @@ -2370,6 +2376,16 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq { count_failure(complex_arg_pass_param_block) } if 0 != params.flags.has_kwrest() { count_failure(complex_arg_pass_param_kwrest) } + // If the caller passes a block (literal or &block), we need to fall back to the + // interpreter for two cases it handles that we don't: + // 1. Methods with &nil reject blocks with ArgumentError + // 2. Methods that don't use blocks emit "unused block" warnings + let caller_passes_block = has_block || caller_passes_block_arg; + if caller_passes_block && 0 != params.flags.accepts_no_block() + { count_failure(complex_arg_pass_accepts_no_block) } + if caller_passes_block && 0 == params.flags.use_block() + { count_failure(complex_arg_pass_does_not_use_block) } + if !can_send { function.set_dynamic_send_reason(send_insn, ComplexArgPass); return false; @@ -2610,24 +2626,6 @@ impl Function { id } - fn new_branch_block( - &mut self, - insn_idx: u32, - exit_state: &FrameState, - locals_count: usize, - stack_count: usize, - ) -> (BlockId, InsnId, FrameState, InsnId) { - let block = self.new_block(insn_idx); - let self_param = self.push_insn(block, Insn::Param); - let mut state = exit_state.clone(); - state.locals.clear(); - state.stack.clear(); - state.locals.extend((0..locals_count).map(|_| self.push_insn(block, Insn::Param))); - state.stack.extend((0..stack_count).map(|_| self.push_insn(block, Insn::Param))); - let snapshot = self.push_insn(block, Insn::Snapshot { state: state.clone() }); - (block, self_param, state, snapshot) - } - fn remove_block(&mut self, block_id: BlockId) { if BlockId(self.blocks.len() - 1) != block_id { panic!("Can only remove the last block"); @@ -2935,7 +2933,7 @@ impl Function { &ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) }, &ArrayMin { ref elements, state } => ArrayMin { elements: find_vec!(elements), state: find!(state) }, &ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) }, - &ArrayPackBuffer { ref elements, fmt, buffer, state } => ArrayPackBuffer { elements: find_vec!(elements), fmt: find!(fmt), buffer: find!(buffer), state: find!(state) }, + &ArrayPackBuffer { ref elements, fmt, ref buffer, state } => ArrayPackBuffer { elements: find_vec!(elements), fmt: find!(fmt), buffer: (*buffer).map(|buffer| find!(buffer)), state: find!(state) }, &DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) }, &ArrayHash { ref elements, state } => ArrayHash { elements: find_vec!(elements), state }, &SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state }, @@ -3735,7 +3733,7 @@ impl Function { // Only specialize positional-positional calls // TODO(max): Handle other kinds of parameter passing let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; - if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice()) { + if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice(), has_block) { self.push_insn_id(block, insn_id); continue; } @@ -3774,7 +3772,7 @@ impl Function { let capture = unsafe { proc_block.as_.captured.as_ref() }; let iseq = unsafe { *capture.code.iseq.as_ref() }; - if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice()) { + if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice(), has_block) { self.push_insn_id(block, insn_id); continue; } @@ -4147,7 +4145,7 @@ impl Function { // If not, we can't do direct dispatch. let super_iseq = unsafe { get_def_iseq_ptr((*super_cme).def) }; // TODO: pass Option to can_direct_send when we start specializing `super { ... }`. - if !can_direct_send(self, block, super_iseq, ci, insn_id, args.as_slice()) { + if !can_direct_send(self, block, super_iseq, ci, insn_id, args.as_slice(), false) { self.push_insn_id(block, insn_id); self.set_dynamic_send_reason(insn_id, SuperTargetComplexArgsPass); continue; @@ -6179,7 +6177,9 @@ impl Function { } Insn::ArrayPackBuffer { ref elements, fmt, buffer, .. } => { self.assert_subtype(insn_id, fmt, types::BasicObject)?; - self.assert_subtype(insn_id, buffer, types::BasicObject)?; + if let Some(buffer) = buffer { + self.assert_subtype(insn_id, buffer, types::BasicObject)?; + } for &element in elements { self.assert_subtype(insn_id, element, types::BasicObject)?; } @@ -7159,11 +7159,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let elements = state.stack_pop_n(count - 1)?; (BOP_INCLUDE_P, Insn::ArrayInclude { elements, target, state: exit_id }) } + VM_OPT_NEWARRAY_SEND_PACK => { + let fmt = state.stack_pop()?; + let elements = state.stack_pop_n(count - 1)?; + (BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer: None, state: exit_id }) + } VM_OPT_NEWARRAY_SEND_PACK_BUFFER => { let buffer = state.stack_pop()?; let fmt = state.stack_pop()?; let elements = state.stack_pop_n(count - 2)?; - (BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer, state: exit_id }) + (BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer: Some(buffer), state: exit_id }) } _ => { // Unknown opcode; side-exit into the interpreter @@ -7587,44 +7592,39 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { None }; - let locals_count = state.locals.len(); - let stack_count = state.stack.len(); - let entry_args = state.as_args(self_param); - // `getblockparamproxy` has two semantic paths: // - modified: return the already-materialized block local from EP // - unmodified: inspect the block handler and produce proxy/nil - let (modified_block, modified_self_param, mut modified_state, ..) = - fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count); - let (unmodified_block, unmodified_self_param, mut unmodified_state, unmodified_exit_id) = - fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count); + let modified_block = fun.new_block(branch_insn_idx); + let unmodified_block = fun.new_block(branch_insn_idx); let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx)); + let join_result = fun.push_insn(join_block, Insn::Param); + let join_local = if level == 0 { Some(fun.push_insn(join_block, Insn::Param)) } else { None }; let ep = fun.push_insn(block, Insn::GetEP { level }); let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { ep }); - fun.push_insn(block, Insn::IfTrue { val: is_modified, target: BranchEdge { target: modified_block, args: entry_args.clone() }}); - fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, args: entry_args })); + fun.push_insn(block, Insn::IfTrue { val: is_modified, target: BranchEdge { target: modified_block, args: vec![] }}); + fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, args: vec![] })); // Push modified block: load the block local via EP. - let ep = fun.push_insn(modified_block, Insn::GetEP { level }); let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject); - if level == 0 { - modified_state.setlocal(ep_offset, modified_val); - } - modified_state.stack_push(modified_val); - fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: modified_state.as_args(modified_self_param) })); + let mut modified_args = vec![modified_val]; + if level == 0 { modified_args.push(modified_val); } + fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: modified_args })); // Push unmodified block: inspect the current block handler to // decide whether this path returns `nil` or `BlockParamProxy`. - let ep = fun.push_insn(unmodified_block, Insn::GetEP { level }); let block_handler = fun.push_insn(unmodified_block, Insn::LoadField { recv: ep, id: ID!(_env_data_index_specval), offset: SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL, return_type: types::CInt64 }); + let original_local = if level == 0 { Some(state.getlocal(ep_offset)) } else { None }; match profiled_block_type { Some(ty) if ty.nil_p() => { - fun.push_insn(unmodified_block, Insn::GuardBitEquals { val: block_handler, expected: Const::CInt64(VM_BLOCK_HANDLER_NONE.into()), reason: SideExitReason::BlockParamProxyNotNil, state: unmodified_exit_id }); - unmodified_state.stack_push(fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(Qnil) })); - fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_state.as_args(unmodified_self_param) })); + fun.push_insn(unmodified_block, Insn::GuardBitEquals { val: block_handler, expected: Const::CInt64(VM_BLOCK_HANDLER_NONE.into()), reason: SideExitReason::BlockParamProxyNotNil, state: exit_id }); + let nil_val = fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(Qnil) }); + let mut unmodified_args = vec![nil_val]; + if let Some(local) = original_local { unmodified_args.push(local); } + fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_args })); } _ => { // This handles two cases which are nearly identical @@ -7635,107 +7635,65 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { const _: () = assert!(RUBY_SYMBOL_FLAG & 1 == 0, "guard below rejects symbol block handlers"); // Bail out if the block handler is neither ISEQ nor ifunc - fun.push_insn(unmodified_block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), mask_name: None, reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: unmodified_exit_id }); + fun.push_insn(unmodified_block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), mask_name: None, reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: exit_id }); // TODO(Shopify/ruby#753): GC root, so we should be able to avoid unnecessary GC tracing - unmodified_state.stack_push(fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) })); - fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_state.as_args(unmodified_self_param) })); + let proxy_val = fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) }); + let mut unmodified_args = vec![proxy_val]; + if let Some(local) = original_local { unmodified_args.push(local); } + fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_args })); } } // Continue compilation from the merged continuation block at the next // instruction. - queue.push_back((unmodified_state, join_block, insn_idx, local_inval)); - break; + if let Some(local_param) = join_local { + state.setlocal(ep_offset, local_param); + } + state.stack_push(join_result); + block = join_block; } YARVINSN_getblockparam => { - fn finish_getblockparam_branch( - fun: &mut Function, - block: BlockId, - self_param: InsnId, - state: &mut FrameState, - join_block: BlockId, - ep_offset: u32, - level: u32, - val: InsnId, - ) { - if level == 0 { - state.setlocal(ep_offset, val); - } - state.stack_push(val); - fun.push_insn(block, Insn::Jump(BranchEdge { - target: join_block, - args: state.as_args(self_param), - })); - } - let ep_offset = get_arg(pc, 0).as_u32(); let level = get_arg(pc, 1).as_u32(); let branch_insn_idx = exit_state.insn_idx as u32; // If the block param is already a Proc (modified), read it from EP. // Otherwise, convert it to a Proc and store it to EP. + let modified_block = fun.new_block(branch_insn_idx); + let unmodified_block = fun.new_block(branch_insn_idx); + let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx)); + let join_param = fun.push_insn(join_block, Insn::Param); + let ep = fun.push_insn(block, Insn::GetEP { level }); let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { ep }); - let locals_count = state.locals.len(); - let stack_count = state.stack.len(); - let entry_args = state.as_args(self_param); - - // Set up branch and join blocks. - let (modified_block, modified_self_param, mut modified_state, ..) = - fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count); - let (unmodified_block, unmodified_self_param, mut unmodified_state, unmodified_exit_id) = - fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count); - let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx)); - fun.push_insn(block, Insn::IfTrue { val: is_modified, - target: BranchEdge { target: modified_block, args: entry_args.clone() }, + target: BranchEdge { target: modified_block, args: vec![] }, }); fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, - args: entry_args, + args: vec![], })); // Push modified block: read Proc from EP. - let ep = fun.push_insn(modified_block, Insn::GetEP { level }); - let modified_val = fun.get_local_from_ep(modified_block, - ep, - ep_offset, - level, - types::BasicObject, - ); - finish_getblockparam_branch( - &mut fun, - modified_block, - modified_self_param, - &mut modified_state, - join_block, - ep_offset, - level, - modified_val, - ); + let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject); + fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: vec![modified_val] })); // Push unmodified block: convert block handler to Proc. let unmodified_val = fun.push_insn(unmodified_block, Insn::GetBlockParam { ep_offset, level, - state: unmodified_exit_id, + state: exit_id, }); - finish_getblockparam_branch( - &mut fun, - unmodified_block, - unmodified_self_param, - &mut unmodified_state, - join_block, - ep_offset, - level, - unmodified_val, - ); + fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: vec![unmodified_val] })); // Continue compilation from the join block at the next instruction. - queue.push_back((unmodified_state, join_block, insn_idx, local_inval)); - break; + if level == 0 { + state.setlocal(ep_offset, join_param); + } + state.stack_push(join_param); + block = join_block; } YARVINSN_pop => { state.stack_pop()?; } YARVINSN_dup => { state.stack_push(state.stack_top()?); } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 68850a71395ab0..3cf9a8f3fa69e8 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -4735,19 +4735,17 @@ mod hir_opt_tests { v7:BasicObject = LoadArg :block@1 Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): - v23:CPtr = GetEP 0 - v24:CBool = IsBlockParamModified v23 - IfTrue v24, bb4(v9, v10, v9) - v30:CPtr = GetEP 0 - v31:CInt64 = LoadField v30, :_env_data_index_specval@0x1001 - v32:CInt64 = GuardAnyBitSet v31, CUInt64(1) - v33:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v9, v10, v9, v33) - bb4(v15:BasicObject, v16:BasicObject, v17:BasicObject): - v27:CPtr = GetEP 0 - v28:BasicObject = LoadField v27, :block@0x1010 - Jump bb6(v15, v28, v17, v28) - bb6(v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject): + v17:CPtr = GetEP 0 + v18:CBool = IsBlockParamModified v17 + IfTrue v18, bb4() + v23:CInt64 = LoadField v17, :_env_data_index_specval@0x1001 + v24:CInt64 = GuardAnyBitSet v23, CUInt64(1) + v25:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v25, v10) + bb4(): + v21:BasicObject = LoadField v17, :block@0x1010 + Jump bb6(v21, v21) + bb6(v15:BasicObject, v16:BasicObject): SideExit NoProfileSend recompile "); } @@ -4777,29 +4775,26 @@ mod hir_opt_tests { v9:NilClass = Const Value(nil) Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:NilClass): - v17:CPtr = GetEP 0 - v18:CBool = IsBlockParamModified v17 - IfTrue v18, bb4(v11, v12, v13) - v32:BasicObject = GetBlockParam :block, l0, EP@4 - Jump bb6(v11, v32, v13, v32) - bb4(v19:BasicObject, v20:BasicObject, v21:NilClass): - v29:CPtr = GetEP 0 - v30:BasicObject = LoadField v29, :block@0x1001 - Jump bb6(v19, v30, v21, v30) - bb6(v34:BasicObject, v35:BasicObject, v36:NilClass, v37:BasicObject): - v53:CPtr = GetEP 0 - v54:CBool = IsBlockParamModified v53 - IfTrue v54, bb7(v34, v35, v37, v34) - v60:CPtr = GetEP 0 - v61:CInt64 = LoadField v60, :_env_data_index_specval@0x1002 - v62:CInt64 = GuardAnyBitSet v61, CUInt64(1) - v63:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb9(v34, v35, v37, v34, v63) - bb7(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject): - v57:CPtr = GetEP 0 - v58:BasicObject = LoadField v57, :block@0x1001 - Jump bb9(v43, v58, v45, v46, v58) - bb9(v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject): + v18:CPtr = GetEP 0 + v19:CBool = IsBlockParamModified v18 + IfTrue v19, bb4() + v24:BasicObject = GetBlockParam :block, l0, EP@4 + Jump bb6(v24) + bb4(): + v22:BasicObject = LoadField v18, :block@0x1001 + Jump bb6(v22) + bb6(v17:BasicObject): + v32:CPtr = GetEP 0 + v33:CBool = IsBlockParamModified v32 + IfTrue v33, bb7() + v38:CInt64 = LoadField v32, :_env_data_index_specval@0x1002 + v39:CInt64 = GuardAnyBitSet v38, CUInt64(1) + v40:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb9(v40, v17) + bb7(): + v36:BasicObject = LoadField v32, :block@0x1001 + Jump bb9(v36, v36) + bb9(v30:BasicObject, v31:BasicObject): SideExit NoProfileSend recompile "); } @@ -4827,29 +4822,26 @@ mod hir_opt_tests { v6:NilClass = Const Value(nil) Jump bb3(v5, v6) bb3(v8:BasicObject, v9:NilClass): - v13:CPtr = GetEP 1 - v14:CBool = IsBlockParamModified v13 - IfTrue v14, bb4(v8, v9) - v26:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v8, v9, v26) - bb4(v15:BasicObject, v16:NilClass): - v23:CPtr = GetEP 1 - v24:BasicObject = LoadField v23, :block@0x1000 - Jump bb6(v15, v16, v24) - bb6(v28:BasicObject, v29:NilClass, v30:BasicObject): - v44:CPtr = GetEP 1 - v45:CBool = IsBlockParamModified v44 - IfTrue v45, bb7(v28, v30, v28) - v51:CPtr = GetEP 1 - v52:CInt64 = LoadField v51, :_env_data_index_specval@0x1001 - v53:CInt64 = GuardAnyBitSet v52, CUInt64(1) - v54:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb9(v28, v30, v28, v54) - bb7(v36:BasicObject, v37:BasicObject, v38:BasicObject): - v48:CPtr = GetEP 1 - v49:BasicObject = LoadField v48, :block@0x1000 - Jump bb9(v36, v37, v38, v49) - bb9(v56:BasicObject, v57:BasicObject, v58:BasicObject, v59:BasicObject): + v14:CPtr = GetEP 1 + v15:CBool = IsBlockParamModified v14 + IfTrue v15, bb4() + v20:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v20) + bb4(): + v18:BasicObject = LoadField v14, :block@0x1000 + Jump bb6(v18) + bb6(v13:BasicObject): + v27:CPtr = GetEP 1 + v28:CBool = IsBlockParamModified v27 + IfTrue v28, bb7() + v33:CInt64 = LoadField v27, :_env_data_index_specval@0x1001 + v34:CInt64 = GuardAnyBitSet v33, CUInt64(1) + v35:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb9(v35) + bb7(): + v31:BasicObject = LoadField v27, :block@0x1000 + Jump bb9(v31) + bb9(v26:BasicObject): SideExit NoProfileSend recompile "); } @@ -4873,18 +4865,17 @@ mod hir_opt_tests { v7:BasicObject = LoadArg :block@1 Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): - v14:CPtr = GetEP 0 - v15:CBool = IsBlockParamModified v14 - IfTrue v15, bb4(v9, v10) - v27:BasicObject = GetBlockParam :block, l0, EP@3 - Jump bb6(v9, v27, v27) - bb4(v16:BasicObject, v17:BasicObject): - v24:CPtr = GetEP 0 - v25:BasicObject = LoadField v24, :block@0x1001 - Jump bb6(v16, v25, v25) - bb6(v29:BasicObject, v30:BasicObject, v31:BasicObject): + v15:CPtr = GetEP 0 + v16:CBool = IsBlockParamModified v15 + IfTrue v16, bb4() + v21:BasicObject = GetBlockParam :block, l0, EP@3 + Jump bb6(v21) + bb4(): + v19:BasicObject = LoadField v15, :block@0x1001 + Jump bb6(v19) + bb6(v14:BasicObject): CheckInterrupts - Return v31 + Return v14 "); } @@ -4908,18 +4899,17 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:CPtr = GetEP 1 - v11:CBool = IsBlockParamModified v10 - IfTrue v11, bb4(v6) - v21:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v6, v21) - bb4(v12:BasicObject): - v18:CPtr = GetEP 1 - v19:BasicObject = LoadField v18, :block@0x1000 - Jump bb6(v12, v19) - bb6(v23:BasicObject, v24:BasicObject): + v11:CPtr = GetEP 1 + v12:CBool = IsBlockParamModified v11 + IfTrue v12, bb4() + v17:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v17) + bb4(): + v15:BasicObject = LoadField v11, :block@0x1000 + Jump bb6(v15) + bb6(v10:BasicObject): CheckInterrupts - Return v24 + Return v10 "); } @@ -8018,22 +8008,20 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): v14:ArrayExact = NewArray - v24:CPtr = GetEP 0 - v25:CBool = IsBlockParamModified v24 - IfTrue v25, bb4(v9, v10, v14) - v31:CPtr = GetEP 0 - v32:CInt64 = LoadField v31, :_env_data_index_specval@0x1001 - v33:CInt64 = GuardAnyBitSet v32, CUInt64(1) - v34:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v9, v10, v14, v34) - bb4(v16:BasicObject, v17:BasicObject, v18:ArrayExact): - v28:CPtr = GetEP 0 - v29:BasicObject = LoadField v28, :block@0x1010 - Jump bb6(v16, v29, v18, v29) - bb6(v36:BasicObject, v37:BasicObject, v38:ArrayExact, v39:BasicObject): - v42:BasicObject = Send v38, &block, :map, v39 # SendFallbackReason: Complex argument passing + v18:CPtr = GetEP 0 + v19:CBool = IsBlockParamModified v18 + IfTrue v19, bb4() + v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1001 + v25:CInt64 = GuardAnyBitSet v24, CUInt64(1) + v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v26, v10) + bb4(): + v22:BasicObject = LoadField v18, :block@0x1010 + Jump bb6(v22, v22) + bb6(v16:BasicObject, v17:BasicObject): + v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing CheckInterrupts - Return v42 + Return v29 "); } @@ -8058,22 +8046,20 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): v14:ArrayExact = NewArray - v24:CPtr = GetEP 0 - v25:CBool = IsBlockParamModified v24 - IfTrue v25, bb4(v9, v10, v14) - v31:CPtr = GetEP 0 - v32:CInt64 = LoadField v31, :_env_data_index_specval@0x1001 - v33:CInt64[0] = GuardBitEquals v32, CInt64(0) - v34:NilClass = Const Value(nil) - Jump bb6(v9, v10, v14, v34) - bb4(v16:BasicObject, v17:BasicObject, v18:ArrayExact): - v28:CPtr = GetEP 0 - v29:BasicObject = LoadField v28, :block@0x1002 - Jump bb6(v16, v29, v18, v29) - bb6(v36:BasicObject, v37:BasicObject, v38:ArrayExact, v39:BasicObject): - v42:BasicObject = Send v38, &block, :map, v39 # SendFallbackReason: Complex argument passing + v18:CPtr = GetEP 0 + v19:CBool = IsBlockParamModified v18 + IfTrue v19, bb4() + v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1001 + v25:CInt64[0] = GuardBitEquals v24, CInt64(0) + v26:NilClass = Const Value(nil) + Jump bb6(v26, v10) + bb4(): + v22:BasicObject = LoadField v18, :block@0x1002 + Jump bb6(v22, v22) + bb6(v16:BasicObject, v17:BasicObject): + v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing CheckInterrupts - Return v42 + Return v29 "); } @@ -8099,22 +8085,20 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - v18:CPtr = GetEP 1 - v19:CBool = IsBlockParamModified v18 - IfTrue v19, bb4(v6, v10) - v25:CPtr = GetEP 1 - v26:CInt64 = LoadField v25, :_env_data_index_specval@0x1000 - v27:CInt64 = GuardAnyBitSet v26, CUInt64(1) - v28:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v6, v10, v28) - bb4(v12:BasicObject, v13:ArrayExact): - v22:CPtr = GetEP 1 - v23:BasicObject = LoadField v22, :block@0x1010 - Jump bb6(v12, v13, v23) - bb6(v30:BasicObject, v31:ArrayExact, v32:BasicObject): - v35:BasicObject = Send v31, &block, :map, v32 # SendFallbackReason: Complex argument passing + v13:CPtr = GetEP 1 + v14:CBool = IsBlockParamModified v13 + IfTrue v14, bb4() + v19:CInt64 = LoadField v13, :_env_data_index_specval@0x1000 + v20:CInt64 = GuardAnyBitSet v19, CUInt64(1) + v21:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v21) + bb4(): + v17:BasicObject = LoadField v13, :block@0x1010 + Jump bb6(v17) + bb6(v12:BasicObject): + v24:BasicObject = Send v10, &block, :map, v12 # SendFallbackReason: Complex argument passing CheckInterrupts - Return v35 + Return v24 "); } @@ -11449,6 +11433,8 @@ mod hir_opt_tests { #[test] fn test_inline_send_with_block_with_no_params() { + // Passing a block to a method that doesn't use it falls back to the + // interpreter so that unused block warnings are properly emitted. eval(r#" def callee = 123 def test @@ -11468,16 +11454,16 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] - v21:Fixnum[123] = Const Value(123) + v11:BasicObject = Send v6, 0x1000, :callee # SendFallbackReason: Complex argument passing CheckInterrupts - Return v21 + Return v11 "); } #[test] fn test_inline_send_with_block_with_one_param() { + // Passing a block to a method that doesn't use it falls back to the + // interpreter so that unused block warnings are properly emitted. eval(r#" def callee = 123 def test @@ -11497,16 +11483,16 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] - v21:Fixnum[123] = Const Value(123) + v11:BasicObject = Send v6, 0x1000, :callee # SendFallbackReason: Complex argument passing CheckInterrupts - Return v21 + Return v11 "); } #[test] fn test_inline_send_with_block_with_multiple_params() { + // Passing a block to a method that doesn't use it falls back to the + // interpreter so that unused block warnings are properly emitted. eval(r#" def callee = 123 def test @@ -11526,11 +11512,9 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] - v21:Fixnum[123] = Const Value(123) + v11:BasicObject = Send v6, 0x1000, :callee # SendFallbackReason: Complex argument passing CheckInterrupts - Return v21 + Return v11 "); } @@ -11588,22 +11572,20 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): v14:ArrayExact = NewArray - v24:CPtr = GetEP 0 - v25:CBool = IsBlockParamModified v24 - IfTrue v25, bb4(v9, v10, v14) - v31:CPtr = GetEP 0 - v32:CInt64 = LoadField v31, :_env_data_index_specval@0x1001 - v33:CInt64 = GuardAnyBitSet v32, CUInt64(1) - v34:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v9, v10, v14, v34) - bb4(v16:BasicObject, v17:BasicObject, v18:ArrayExact): - v28:CPtr = GetEP 0 - v29:BasicObject = LoadField v28, :block@0x1010 - Jump bb6(v16, v29, v18, v29) - bb6(v36:BasicObject, v37:BasicObject, v38:ArrayExact, v39:BasicObject): - v42:BasicObject = Send v38, &block, :map, v39 # SendFallbackReason: Complex argument passing + v18:CPtr = GetEP 0 + v19:CBool = IsBlockParamModified v18 + IfTrue v19, bb4() + v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1001 + v25:CInt64 = GuardAnyBitSet v24, CUInt64(1) + v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v26, v10) + bb4(): + v22:BasicObject = LoadField v18, :block@0x1010 + Jump bb6(v22, v22) + bb6(v16:BasicObject, v17:BasicObject): + v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing CheckInterrupts - Return v42 + Return v29 "); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index b23c7068fbc5f9..bcb05f0f196d0d 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2466,21 +2466,19 @@ pub(crate) mod hir_build_tests { bb3(v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:NilClass): v29:ArrayExact = ToArray v19 PatchPoint NoEPEscape(test) - v56:CPtr = GetEP 0 - v57:CBool = IsBlockParamModified v56 - IfTrue v57, bb4(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - Jump bb5(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - bb4(v34:BasicObject, v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass, v40:BasicObject, v41:BasicObject, v42:ArrayExact, v43:BasicObject): - v60:CPtr = GetEP 0 - v61:BasicObject = LoadField v60, :&@0x1004 - Jump bb6(v34, v35, v36, v37, v61, v39, v40, v41, v42, v43, v61) - bb5(v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass, v51:BasicObject, v52:BasicObject, v53:ArrayExact, v54:BasicObject): - v63:CPtr = GetEP 0 - v64:CInt64 = LoadField v63, :_env_data_index_specval@0x1005 - v65:CInt64 = GuardAnyBitSet v64, CUInt64(1) - v66:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v66) - bb6(v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject, v72:BasicObject, v73:NilClass, v74:BasicObject, v75:BasicObject, v76:ArrayExact, v77:BasicObject, v78:BasicObject): + v36:CPtr = GetEP 0 + v37:CBool = IsBlockParamModified v36 + IfTrue v37, bb4() + Jump bb5() + bb4(): + v40:BasicObject = LoadField v36, :&@0x1004 + Jump bb6(v40, v40) + bb5(): + v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1005 + v43:CInt64 = GuardAnyBitSet v42, CUInt64(1) + v44:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v44, v21) + bb6(v34:BasicObject, v35:BasicObject): SideExit SplatKwNotProfiled "); } @@ -2810,7 +2808,56 @@ pub(crate) mod hir_build_tests { v26:BasicObject = Send v16, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) v32:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v33:StringExact = StringCopy v32 - SideExit UnhandledNewarraySend(PACK) + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK) + v36:String = ArrayPackBuffer v16, v17, fmt: v33 + PatchPoint NoEPEscape(test) + v43:ArrayExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v44:ArrayExact = ArrayDup v43 + v46:BasicObject = Send v15, :puts, v44 # SendFallbackReason: Uncategorized(opt_send_without_block) + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v36 + "); + } + + #[test] + fn test_opt_newarray_send_pack_redefined() { + eval(r#" + class Array + def pack(fmt, buffer: nil) = 5 + end + def test(a,b) + sum = a+b + result = [a,b].pack 'C' + puts [1,2,3] + result + end + "#); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @" + fn test@:6: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :a@0x1000 + v4:BasicObject = LoadField v2, :b@0x1001 + v5:NilClass = Const Value(nil) + v6:NilClass = Const Value(nil) + Jump bb3(v1, v3, v4, v5, v6) + bb2(): + EntryPoint JIT(0) + v9:BasicObject = LoadArg :self@0 + v10:BasicObject = LoadArg :a@1 + v11:BasicObject = LoadArg :b@2 + v12:NilClass = Const Value(nil) + v13:NilClass = Const Value(nil) + Jump bb3(v9, v10, v11, v12, v13) + bb3(v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:NilClass, v19:NilClass): + v26:BasicObject = Send v16, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) + v32:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v33:StringExact = StringCopy v32 + SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK)) "); } @@ -3342,20 +3389,19 @@ pub(crate) mod hir_build_tests { v7:BasicObject = LoadArg :block@1 Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): - v14:CPtr = GetEP 0 - v15:CBool = IsBlockParamModified v14 - IfTrue v15, bb4(v9, v10) - Jump bb5(v9, v10) - bb4(v16:BasicObject, v17:BasicObject): - v24:CPtr = GetEP 0 - v25:BasicObject = LoadField v24, :block@0x1001 - Jump bb6(v16, v25, v25) - bb5(v19:BasicObject, v20:BasicObject): - v27:BasicObject = GetBlockParam :block, l0, EP@3 - Jump bb6(v19, v27, v27) - bb6(v29:BasicObject, v30:BasicObject, v31:BasicObject): + v15:CPtr = GetEP 0 + v16:CBool = IsBlockParamModified v15 + IfTrue v16, bb4() + Jump bb5() + bb4(): + v19:BasicObject = LoadField v15, :block@0x1001 + Jump bb6(v19) + bb5(): + v21:BasicObject = GetBlockParam :block, l0, EP@3 + Jump bb6(v21) + bb6(v14:BasicObject): CheckInterrupts - Return v31 + Return v14 "); } @@ -3379,24 +3425,22 @@ pub(crate) mod hir_build_tests { v7:BasicObject = LoadArg :block@1 Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): - v23:CPtr = GetEP 0 - v24:CBool = IsBlockParamModified v23 - IfTrue v24, bb4(v9, v10, v9) - Jump bb5(v9, v10, v9) - bb4(v15:BasicObject, v16:BasicObject, v17:BasicObject): - v27:CPtr = GetEP 0 - v28:BasicObject = LoadField v27, :block@0x1001 - Jump bb6(v15, v28, v17, v28) - bb5(v19:BasicObject, v20:BasicObject, v21:BasicObject): - v30:CPtr = GetEP 0 - v31:CInt64 = LoadField v30, :_env_data_index_specval@0x1002 - v32:CInt64 = GuardAnyBitSet v31, CUInt64(1) - v33:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v19, v20, v21, v33) - bb6(v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject): - v41:BasicObject = Send v37, &block, :tap, v38 # SendFallbackReason: Uncategorized(send) + v17:CPtr = GetEP 0 + v18:CBool = IsBlockParamModified v17 + IfTrue v18, bb4() + Jump bb5() + bb4(): + v21:BasicObject = LoadField v17, :block@0x1001 + Jump bb6(v21, v21) + bb5(): + v23:CInt64 = LoadField v17, :_env_data_index_specval@0x1002 + v24:CInt64 = GuardAnyBitSet v23, CUInt64(1) + v25:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v25, v10) + bb6(v15:BasicObject, v16:BasicObject): + v28:BasicObject = Send v9, &block, :tap, v15 # SendFallbackReason: Uncategorized(send) CheckInterrupts - Return v41 + Return v28 "); } @@ -3425,36 +3469,33 @@ pub(crate) mod hir_build_tests { v9:NilClass = Const Value(nil) Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:NilClass): - v17:CPtr = GetEP 0 - v18:CBool = IsBlockParamModified v17 - IfTrue v18, bb4(v11, v12, v13) - Jump bb5(v11, v12, v13) - bb4(v19:BasicObject, v20:BasicObject, v21:NilClass): - v29:CPtr = GetEP 0 - v30:BasicObject = LoadField v29, :block@0x1001 - Jump bb6(v19, v30, v21, v30) - bb5(v23:BasicObject, v24:BasicObject, v25:NilClass): - v32:BasicObject = GetBlockParam :block, l0, EP@4 - Jump bb6(v23, v32, v25, v32) - bb6(v34:BasicObject, v35:BasicObject, v36:NilClass, v37:BasicObject): - v53:CPtr = GetEP 0 - v54:CBool = IsBlockParamModified v53 - IfTrue v54, bb7(v34, v35, v37, v34) - Jump bb8(v34, v35, v37, v34) - bb7(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject): - v57:CPtr = GetEP 0 - v58:BasicObject = LoadField v57, :block@0x1001 - Jump bb9(v43, v58, v45, v46, v58) - bb8(v48:BasicObject, v49:BasicObject, v50:BasicObject, v51:BasicObject): - v60:CPtr = GetEP 0 - v61:CInt64 = LoadField v60, :_env_data_index_specval@0x1002 - v62:CInt64 = GuardAnyBitSet v61, CUInt64(1) - v63:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb9(v48, v49, v50, v51, v63) - bb9(v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject): - v72:BasicObject = Send v68, &block, :tap, v69 # SendFallbackReason: Uncategorized(send) - CheckInterrupts - Return v72 + v18:CPtr = GetEP 0 + v19:CBool = IsBlockParamModified v18 + IfTrue v19, bb4() + Jump bb5() + bb4(): + v22:BasicObject = LoadField v18, :block@0x1001 + Jump bb6(v22) + bb5(): + v24:BasicObject = GetBlockParam :block, l0, EP@4 + Jump bb6(v24) + bb6(v17:BasicObject): + v32:CPtr = GetEP 0 + v33:CBool = IsBlockParamModified v32 + IfTrue v33, bb7() + Jump bb8() + bb7(): + v36:BasicObject = LoadField v32, :block@0x1001 + Jump bb9(v36, v36) + bb8(): + v38:CInt64 = LoadField v32, :_env_data_index_specval@0x1002 + v39:CInt64 = GuardAnyBitSet v38, CUInt64(1) + v40:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb9(v40, v17) + bb9(v30:BasicObject, v31:BasicObject): + v43:BasicObject = Send v11, &block, :tap, v30 # SendFallbackReason: Uncategorized(send) + CheckInterrupts + Return v43 "); } @@ -3481,36 +3522,33 @@ pub(crate) mod hir_build_tests { v6:NilClass = Const Value(nil) Jump bb3(v5, v6) bb3(v8:BasicObject, v9:NilClass): - v13:CPtr = GetEP 1 - v14:CBool = IsBlockParamModified v13 - IfTrue v14, bb4(v8, v9) - Jump bb5(v8, v9) - bb4(v15:BasicObject, v16:NilClass): - v23:CPtr = GetEP 1 - v24:BasicObject = LoadField v23, :block@0x1000 - Jump bb6(v15, v16, v24) - bb5(v18:BasicObject, v19:NilClass): - v26:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v18, v19, v26) - bb6(v28:BasicObject, v29:NilClass, v30:BasicObject): - v44:CPtr = GetEP 1 - v45:CBool = IsBlockParamModified v44 - IfTrue v45, bb7(v28, v30, v28) - Jump bb8(v28, v30, v28) - bb7(v36:BasicObject, v37:BasicObject, v38:BasicObject): - v48:CPtr = GetEP 1 - v49:BasicObject = LoadField v48, :block@0x1000 - Jump bb9(v36, v37, v38, v49) - bb8(v40:BasicObject, v41:BasicObject, v42:BasicObject): - v51:CPtr = GetEP 1 - v52:CInt64 = LoadField v51, :_env_data_index_specval@0x1001 - v53:CInt64 = GuardAnyBitSet v52, CUInt64(1) - v54:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb9(v40, v41, v42, v54) - bb9(v56:BasicObject, v57:BasicObject, v58:BasicObject, v59:BasicObject): - v62:BasicObject = Send v58, &block, :tap, v59 # SendFallbackReason: Uncategorized(send) - CheckInterrupts - Return v62 + v14:CPtr = GetEP 1 + v15:CBool = IsBlockParamModified v14 + IfTrue v15, bb4() + Jump bb5() + bb4(): + v18:BasicObject = LoadField v14, :block@0x1000 + Jump bb6(v18) + bb5(): + v20:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v20) + bb6(v13:BasicObject): + v27:CPtr = GetEP 1 + v28:CBool = IsBlockParamModified v27 + IfTrue v28, bb7() + Jump bb8() + bb7(): + v31:BasicObject = LoadField v27, :block@0x1000 + Jump bb9(v31) + bb8(): + v33:CInt64 = LoadField v27, :_env_data_index_specval@0x1001 + v34:CInt64 = GuardAnyBitSet v33, CUInt64(1) + v35:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb9(v35) + bb9(v26:BasicObject): + v38:BasicObject = Send v8, &block, :tap, v26 # SendFallbackReason: Uncategorized(send) + CheckInterrupts + Return v38 "); } @@ -3534,20 +3572,19 @@ pub(crate) mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:CPtr = GetEP 1 - v11:CBool = IsBlockParamModified v10 - IfTrue v11, bb4(v6) - Jump bb5(v6) - bb4(v12:BasicObject): - v18:CPtr = GetEP 1 - v19:BasicObject = LoadField v18, :block@0x1000 - Jump bb6(v12, v19) - bb5(v14:BasicObject): - v21:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v14, v21) - bb6(v23:BasicObject, v24:BasicObject): + v11:CPtr = GetEP 1 + v12:CBool = IsBlockParamModified v11 + IfTrue v12, bb4() + Jump bb5() + bb4(): + v15:BasicObject = LoadField v11, :block@0x1000 + Jump bb6(v15) + bb5(): + v17:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v17) + bb6(v10:BasicObject): CheckInterrupts - Return v24 + Return v10 "); } @@ -3640,21 +3677,19 @@ pub(crate) mod hir_build_tests { v9:BasicObject = LoadArg :b@2 Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): - v31:CPtr = GetEP 0 - v32:CBool = IsBlockParamModified v31 - IfTrue v32, bb4(v11, v12, v13, v11, v12) - Jump bb5(v11, v12, v13, v11, v12) - bb4(v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:BasicObject, v23:BasicObject): - v35:CPtr = GetEP 0 - v36:BasicObject = LoadField v35, :b@0x1002 - Jump bb6(v19, v20, v36, v22, v23, v36) - bb5(v25:BasicObject, v26:BasicObject, v27:BasicObject, v28:BasicObject, v29:BasicObject): - v38:CPtr = GetEP 0 - v39:CInt64 = LoadField v38, :_env_data_index_specval@0x1003 - v40:CInt64 = GuardAnyBitSet v39, CUInt64(1) - v41:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v25, v26, v27, v28, v29, v41) - bb6(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject): + v21:CPtr = GetEP 0 + v22:CBool = IsBlockParamModified v21 + IfTrue v22, bb4() + Jump bb5() + bb4(): + v25:BasicObject = LoadField v21, :b@0x1002 + Jump bb6(v25, v25) + bb5(): + v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1003 + v28:CInt64 = GuardAnyBitSet v27, CUInt64(1) + v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v29, v13) + bb6(v19:BasicObject, v20:BasicObject): SideExit SplatKwNotProfiled "); } @@ -3691,25 +3726,23 @@ pub(crate) mod hir_build_tests { bb3(v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:NilClass): v29:ArrayExact = ToArray v19 PatchPoint NoEPEscape(test) - v56:CPtr = GetEP 0 - v57:CBool = IsBlockParamModified v56 - IfTrue v57, bb4(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - Jump bb5(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - bb4(v34:BasicObject, v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass, v40:BasicObject, v41:BasicObject, v42:ArrayExact, v43:BasicObject): - v60:CPtr = GetEP 0 - v61:BasicObject = LoadField v60, :&@0x1004 - Jump bb6(v34, v35, v36, v37, v61, v39, v40, v41, v42, v43, v61) - bb5(v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass, v51:BasicObject, v52:BasicObject, v53:ArrayExact, v54:BasicObject): - v63:CPtr = GetEP 0 - v64:CInt64 = LoadField v63, :_env_data_index_specval@0x1005 - v65:CInt64[0] = GuardBitEquals v64, CInt64(0) - v66:NilClass = Const Value(nil) - Jump bb6(v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v66) - bb6(v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject, v72:BasicObject, v73:NilClass, v74:BasicObject, v75:BasicObject, v76:ArrayExact, v77:BasicObject, v78:BasicObject): - v81:NilClass = GuardType v77, NilClass - v83:BasicObject = Send v74, &block, :foo, v75, v76, v81, v78 # SendFallbackReason: Uncategorized(send) + v36:CPtr = GetEP 0 + v37:CBool = IsBlockParamModified v36 + IfTrue v37, bb4() + Jump bb5() + bb4(): + v40:BasicObject = LoadField v36, :&@0x1004 + Jump bb6(v40, v40) + bb5(): + v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1005 + v43:CInt64[0] = GuardBitEquals v42, CInt64(0) + v44:NilClass = Const Value(nil) + Jump bb6(v44, v21) + bb6(v34:BasicObject, v35:BasicObject): + v47:NilClass = GuardType v20, NilClass + v49:BasicObject = Send v17, &block, :foo, v18, v29, v47, v34 # SendFallbackReason: Uncategorized(send) CheckInterrupts - Return v83 + Return v49 "); } @@ -3737,25 +3770,23 @@ pub(crate) mod hir_build_tests { v9:BasicObject = LoadArg :b@2 Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): - v31:CPtr = GetEP 0 - v32:CBool = IsBlockParamModified v31 - IfTrue v32, bb4(v11, v12, v13, v11, v12) - Jump bb5(v11, v12, v13, v11, v12) - bb4(v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:BasicObject, v23:BasicObject): - v35:CPtr = GetEP 0 - v36:BasicObject = LoadField v35, :b@0x1002 - Jump bb6(v19, v20, v36, v22, v23, v36) - bb5(v25:BasicObject, v26:BasicObject, v27:BasicObject, v28:BasicObject, v29:BasicObject): - v38:CPtr = GetEP 0 - v39:CInt64 = LoadField v38, :_env_data_index_specval@0x1003 - v40:CInt64 = GuardAnyBitSet v39, CUInt64(1) - v41:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v25, v26, v27, v28, v29, v41) - bb6(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject): - v51:HashExact = GuardType v47, HashExact - v53:BasicObject = Send v46, &block, :foo, v51, v48 # SendFallbackReason: Uncategorized(send) + v21:CPtr = GetEP 0 + v22:CBool = IsBlockParamModified v21 + IfTrue v22, bb4() + Jump bb5() + bb4(): + v25:BasicObject = LoadField v21, :b@0x1002 + Jump bb6(v25, v25) + bb5(): + v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1003 + v28:CInt64 = GuardAnyBitSet v27, CUInt64(1) + v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v29, v13) + bb6(v19:BasicObject, v20:BasicObject): + v32:HashExact = GuardType v12, HashExact + v34:BasicObject = Send v11, &block, :foo, v32, v19 # SendFallbackReason: Uncategorized(send) CheckInterrupts - Return v53 + Return v34 "); } @@ -3783,25 +3814,23 @@ pub(crate) mod hir_build_tests { v9:BasicObject = LoadArg :b@2 Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): - v31:CPtr = GetEP 0 - v32:CBool = IsBlockParamModified v31 - IfTrue v32, bb4(v11, v12, v13, v11, v12) - Jump bb5(v11, v12, v13, v11, v12) - bb4(v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:BasicObject, v23:BasicObject): - v35:CPtr = GetEP 0 - v36:BasicObject = LoadField v35, :b@0x1002 - Jump bb6(v19, v20, v36, v22, v23, v36) - bb5(v25:BasicObject, v26:BasicObject, v27:BasicObject, v28:BasicObject, v29:BasicObject): - v38:CPtr = GetEP 0 - v39:CInt64 = LoadField v38, :_env_data_index_specval@0x1003 - v40:CInt64 = GuardAnyBitSet v39, CUInt64(1) - v41:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v25, v26, v27, v28, v29, v41) - bb6(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject): - v51:HashExact = GuardType v47, HashExact - v53:BasicObject = Send v46, &block, :foo, v51, v48 # SendFallbackReason: Uncategorized(send) + v21:CPtr = GetEP 0 + v22:CBool = IsBlockParamModified v21 + IfTrue v22, bb4() + Jump bb5() + bb4(): + v25:BasicObject = LoadField v21, :b@0x1002 + Jump bb6(v25, v25) + bb5(): + v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1003 + v28:CInt64 = GuardAnyBitSet v27, CUInt64(1) + v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v29, v13) + bb6(v19:BasicObject, v20:BasicObject): + v32:HashExact = GuardType v12, HashExact + v34:BasicObject = Send v11, &block, :foo, v32, v19 # SendFallbackReason: Uncategorized(send) CheckInterrupts - Return v53 + Return v34 "); } @@ -3839,21 +3868,19 @@ pub(crate) mod hir_build_tests { bb3(v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:NilClass): v29:ArrayExact = ToArray v19 PatchPoint NoEPEscape(test) - v56:CPtr = GetEP 0 - v57:CBool = IsBlockParamModified v56 - IfTrue v57, bb4(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - Jump bb5(v17, v18, v19, v20, v21, v22, v17, v18, v29, v20) - bb4(v34:BasicObject, v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass, v40:BasicObject, v41:BasicObject, v42:ArrayExact, v43:BasicObject): - v60:CPtr = GetEP 0 - v61:BasicObject = LoadField v60, :&@0x1004 - Jump bb6(v34, v35, v36, v37, v61, v39, v40, v41, v42, v43, v61) - bb5(v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass, v51:BasicObject, v52:BasicObject, v53:ArrayExact, v54:BasicObject): - v63:CPtr = GetEP 0 - v64:CInt64 = LoadField v63, :_env_data_index_specval@0x1005 - v65:CInt64[0] = GuardBitEquals v64, CInt64(0) - v66:NilClass = Const Value(nil) - Jump bb6(v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v66) - bb6(v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject, v72:BasicObject, v73:NilClass, v74:BasicObject, v75:BasicObject, v76:ArrayExact, v77:BasicObject, v78:BasicObject): + v36:CPtr = GetEP 0 + v37:CBool = IsBlockParamModified v36 + IfTrue v37, bb4() + Jump bb5() + bb4(): + v40:BasicObject = LoadField v36, :&@0x1004 + Jump bb6(v40, v40) + bb5(): + v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1005 + v43:CInt64[0] = GuardBitEquals v42, CInt64(0) + v44:NilClass = Const Value(nil) + Jump bb6(v44, v21) + bb6(v34:BasicObject, v35:BasicObject): SideExit SplatKwPolymorphic "); } @@ -3884,21 +3911,19 @@ pub(crate) mod hir_build_tests { v9:BasicObject = LoadArg :block@2 Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): - v31:CPtr = GetEP 0 - v32:CBool = IsBlockParamModified v31 - IfTrue v32, bb4(v11, v12, v13, v11, v12) - Jump bb5(v11, v12, v13, v11, v12) - bb4(v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:BasicObject, v23:BasicObject): - v35:CPtr = GetEP 0 - v36:BasicObject = LoadField v35, :block@0x1002 - Jump bb6(v19, v20, v36, v22, v23, v36) - bb5(v25:BasicObject, v26:BasicObject, v27:BasicObject, v28:BasicObject, v29:BasicObject): - v38:CPtr = GetEP 0 - v39:CInt64 = LoadField v38, :_env_data_index_specval@0x1003 - v40:CInt64 = GuardAnyBitSet v39, CUInt64(1) - v41:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb6(v25, v26, v27, v28, v29, v41) - bb6(v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject): + v21:CPtr = GetEP 0 + v22:CBool = IsBlockParamModified v21 + IfTrue v22, bb4() + Jump bb5() + bb4(): + v25:BasicObject = LoadField v21, :block@0x1002 + Jump bb6(v25, v25) + bb5(): + v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1003 + v28:CInt64 = GuardAnyBitSet v27, CUInt64(1) + v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb6(v29, v13) + bb6(v19:BasicObject, v20:BasicObject): SideExit SplatKwNotNilOrHash "); } @@ -4576,33 +4601,31 @@ pub(crate) mod hir_build_tests { bb3(v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:BasicObject, v22:BasicObject, v23:NilClass): v27:BasicObject = InvokeBuiltin dir_s_open, v18, v19, v20 PatchPoint NoEPEscape(open) - v47:CPtr = GetEP 0 - v48:CBool = IsBlockParamModified v47 - IfTrue v48, bb5(v18, v19, v20, v21, v22, v27) - Jump bb6(v18, v19, v20, v21, v22, v27) - bb5(v33:BasicObject, v34:BasicObject, v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject): - v51:CPtr = GetEP 0 - v52:BasicObject = LoadField v51, :block@0x1004 - Jump bb7(v33, v34, v35, v36, v52, v38, v52) - bb6(v40:BasicObject, v41:BasicObject, v42:BasicObject, v43:BasicObject, v44:BasicObject, v45:BasicObject): - v54:CPtr = GetEP 0 - v55:CInt64 = LoadField v54, :_env_data_index_specval@0x1005 - v56:CInt64 = GuardAnyBitSet v55, CUInt64(1) - v57:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) - Jump bb7(v40, v41, v42, v43, v44, v45, v57) - bb7(v59:BasicObject, v60:BasicObject, v61:BasicObject, v62:BasicObject, v63:BasicObject, v64:BasicObject, v65:BasicObject): - CheckInterrupts - v69:CBool = Test v65 - v70:Falsy = RefineType v65, Falsy - IfFalse v69, bb4(v59, v60, v61, v62, v63, v64) - v72:Truthy = RefineType v65, Truthy - v76:BasicObject = InvokeBlock, v64 # SendFallbackReason: InvokeBlock: not yet specialized - v79:BasicObject = InvokeBuiltin dir_s_close, v59, v64 - CheckInterrupts - Return v76 - bb4(v85:BasicObject, v86:BasicObject, v87:BasicObject, v88:BasicObject, v89:BasicObject, v90:BasicObject): - CheckInterrupts - Return v90 + v35:CPtr = GetEP 0 + v36:CBool = IsBlockParamModified v35 + IfTrue v36, bb5() + Jump bb6() + bb5(): + v39:BasicObject = LoadField v35, :block@0x1004 + Jump bb7(v39, v39) + bb6(): + v41:CInt64 = LoadField v35, :_env_data_index_specval@0x1005 + v42:CInt64 = GuardAnyBitSet v41, CUInt64(1) + v43:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008)) + Jump bb7(v43, v22) + bb7(v33:BasicObject, v34:BasicObject): + CheckInterrupts + v47:CBool = Test v33 + v48:Falsy = RefineType v33, Falsy + IfFalse v47, bb4(v18, v19, v20, v21, v34, v27) + v50:Truthy = RefineType v33, Truthy + v54:BasicObject = InvokeBlock, v27 # SendFallbackReason: InvokeBlock: not yet specialized + v57:BasicObject = InvokeBuiltin dir_s_close, v18, v27 + CheckInterrupts + Return v54 + bb4(v63:BasicObject, v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject): + CheckInterrupts + Return v68 "); } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index db6aef5d8cc31c..61a94b53d5bb74 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -424,6 +424,8 @@ make_counters! { complex_arg_pass_param_kwrest, complex_arg_pass_param_block, complex_arg_pass_param_forwardable, + complex_arg_pass_accepts_no_block, + complex_arg_pass_does_not_use_block, // Unsupported caller side features complex_arg_pass_caller_splat,