diff options
-rw-r--r-- | .github/workflows/windows.yml | 8 | ||||
-rw-r--r-- | common.mk | 5 | ||||
-rw-r--r-- | enc/depend | 1 | ||||
-rw-r--r-- | enc/trans/iso2022.trans | 3 | ||||
-rw-r--r-- | ext/json/generator/extconf.rb | 4 | ||||
-rw-r--r-- | ext/json/generator/generator.c | 30 | ||||
-rw-r--r-- | ext/json/generator/simd.h | 6 | ||||
-rw-r--r-- | ext/psych/lib/psych/core_ext.rb | 14 | ||||
-rw-r--r-- | include/ruby/atomic.h | 26 | ||||
-rw-r--r-- | include/ruby/internal/attr/nonstring.h | 32 | ||||
-rw-r--r-- | marshal.c | 3 | ||||
-rw-r--r-- | ractor.c | 10 | ||||
-rw-r--r-- | regenc.h | 3 | ||||
-rw-r--r-- | shape.c | 37 | ||||
-rw-r--r-- | signal.c | 3 | ||||
-rw-r--r-- | siphash.c | 3 | ||||
-rw-r--r-- | string.c | 3 | ||||
-rw-r--r-- | symbol.c | 3 | ||||
-rw-r--r-- | template/id.c.tmpl | 3 | ||||
-rw-r--r-- | template/prelude.c.tmpl | 5 | ||||
-rw-r--r-- | test/.excludes-mmtk/TestEtc.rb | 1 | ||||
-rw-r--r-- | test/.excludes-mmtk/TestObjSpace.rb | 1 | ||||
-rw-r--r-- | test/.excludes-mmtk/TestObjectSpace.rb | 1 | ||||
-rw-r--r-- | test/objspace/test_objspace.rb | 5 | ||||
-rw-r--r-- | test/psych/test_psych_set.rb | 57 | ||||
-rw-r--r-- | test/psych/test_set.rb | 61 | ||||
-rw-r--r-- | test/ruby/test_iseq.rb | 40 | ||||
-rw-r--r-- | tool/test-coverage.rb | 4 | ||||
-rw-r--r-- | vm_core.h | 2 | ||||
-rw-r--r-- | zjit/src/hir.rs | 112 | ||||
-rw-r--r-- | zjit/zjit.mk | 17 |
31 files changed, 375 insertions, 128 deletions
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4675239528..03c0f7a4f5 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -25,13 +25,6 @@ jobs: strategy: matrix: include: - - os: 2019 - vc: 2015 - vcvars: '10.0.14393.0 -vcvars_ver=14.0' # The oldest Windows 10 SDK w/ VC++ 2015 toolset (v140) - test_task: check - - os: 2019 - vc: 2019 - test_task: check - os: 2022 vc: 2019 vcvars: '10.0.22621.0 -vcvars_ver=14.2' # The defautl Windows 11 SDK and toolset are broken at windows-2022 @@ -65,7 +58,6 @@ jobs: env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} - OS_VER: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} VCPKG_DEFAULT_TRIPLET: ${{ matrix.target || 'x64' }}-windows RUBY_OPT_DIR: ${{ matrix.os == '11-arm' && 'C' || 'D' }}:/a/ruby/ruby/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET% @@ -9992,6 +9992,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -10613,6 +10614,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -16937,6 +16939,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h signal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -17705,6 +17708,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h string.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -18170,6 +18174,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h diff --git a/enc/depend b/enc/depend index 2918a90a05..dcf65a129b 100644 --- a/enc/depend +++ b/enc/depend @@ -7016,6 +7016,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h +enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h enc/trans/iso2022.$(OBJEXT): internal/attr/packed_struct.h enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h diff --git a/enc/trans/iso2022.trans b/enc/trans/iso2022.trans index a25a4a12a1..b0c635d574 100644 --- a/enc/trans/iso2022.trans +++ b/enc/trans/iso2022.trans @@ -1,4 +1,5 @@ #include "transcode_data.h" +#include "ruby/internal/attr/nonstring.h" <% map = { @@ -436,7 +437,7 @@ rb_cp50221_encoder = { /* JIS0201 to JIS0208 conversion table */ enum {tbl0208_num = 0xDF - 0xA1 + 1}; -static const char tbl0208[tbl0208_num][2] = { +RBIMPL_ATTR_NONSTRING() static const char tbl0208[tbl0208_num][2] = { "\x21\x23", "\x21\x56", "\x21\x57", "\x21\x22", "\x21\x26", "\x25\x72", "\x25\x21", "\x25\x23", "\x25\x25", "\x25\x27", "\x25\x29", "\x25\x63", diff --git a/ext/json/generator/extconf.rb b/ext/json/generator/extconf.rb index e44890e2ed..60372ee558 100644 --- a/ext/json/generator/extconf.rb +++ b/ext/json/generator/extconf.rb @@ -18,7 +18,7 @@ else return 0; } SRC - $defs.push("-DENABLE_SIMD") + $defs.push("-DJSON_ENABLE_SIMD") end end @@ -29,7 +29,7 @@ else return 0; } SRC - $defs.push("-DENABLE_SIMD") + $defs.push("-DJSON_ENABLE_SIMD") end have_header('cpuid.h') diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 536c2aa1b7..06ab8010d9 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -112,7 +112,7 @@ typedef struct _search_state { const char *cursor; FBuffer *buffer; -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD const char *chunk_base; const char *chunk_end; bool has_matches; @@ -124,7 +124,7 @@ typedef struct _search_state { #else #error "Unknown SIMD Implementation." #endif /* HAVE_SIMD_NEON */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ } search_state; #if (defined(__GNUC__ ) || defined(__clang__)) @@ -189,15 +189,11 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search) case '\r': fbuffer_append(search->buffer, "\\r", 2); break; case '\t': fbuffer_append(search->buffer, "\\t", 2); break; default: { - if (ch < ' ') { - const char *hexdig = "0123456789abcdef"; - char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; - scratch[4] = hexdig[(ch >> 4) & 0xf]; - scratch[5] = hexdig[ch & 0xf]; - fbuffer_append(search->buffer, scratch, 6); - } else { - fbuffer_append_char(search->buffer, ch); - } + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); break; } } @@ -265,7 +261,7 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) search->cursor = (search->ptr += ch_len); } -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) { @@ -537,7 +533,7 @@ static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(se #endif /* HAVE_SIMD_SSE2 */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ static const unsigned char script_safe_escape_table[256] = { // ASCII Control Characters @@ -1302,11 +1298,11 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat search.cursor = search.ptr; search.end = search.ptr + len; -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD search.matches_mask = 0; search.has_matches = false; search.chunk_base = NULL; -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ switch(rb_enc_str_coderange(obj)) { case ENC_CODERANGE_7BIT: @@ -2174,7 +2170,7 @@ void Init_generator(void) switch(find_simd_implementation()) { -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD #ifdef HAVE_SIMD_NEON case SIMD_NEON: search_escape_basic_impl = search_escape_basic_neon; @@ -2185,7 +2181,7 @@ void Init_generator(void) search_escape_basic_impl = search_escape_basic_sse2; break; #endif /* HAVE_SIMD_SSE2 */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ default: search_escape_basic_impl = search_escape_basic; break; diff --git a/ext/json/generator/simd.h b/ext/json/generator/simd.h index 2fbc93169d..b12890cb09 100644 --- a/ext/json/generator/simd.h +++ b/ext/json/generator/simd.h @@ -4,7 +4,7 @@ typedef enum { SIMD_SSE2 } SIMD_Implementation; -#ifdef ENABLE_SIMD +#ifdef JSON_ENABLE_SIMD #ifdef __clang__ #if __has_builtin(__builtin_ctzll) @@ -56,6 +56,7 @@ static SIMD_Implementation find_simd_implementation(void) { return SIMD_NEON; } +#define HAVE_SIMD 1 #define HAVE_SIMD_NEON 1 uint8x16x4_t load_uint8x16_4(const unsigned char *table) { @@ -74,6 +75,7 @@ uint8x16x4_t load_uint8x16_4(const unsigned char *table) { #ifdef HAVE_X86INTRIN_H #include <x86intrin.h> +#define HAVE_SIMD 1 #define HAVE_SIMD_SSE2 1 #ifdef HAVE_CPUID_H @@ -101,7 +103,7 @@ static SIMD_Implementation find_simd_implementation(void) { #endif /* HAVE_X86INTRIN_H */ #endif /* X86_64 Support */ -#endif /* ENABLE_SIMD */ +#endif /* JSON_ENABLE_SIMD */ #ifndef FIND_SIMD_IMPLEMENTATION_DEFINED static SIMD_Implementation find_simd_implementation(void) { diff --git a/ext/psych/lib/psych/core_ext.rb b/ext/psych/lib/psych/core_ext.rb index 0721a133c3..950b20f2d6 100644 --- a/ext/psych/lib/psych/core_ext.rb +++ b/ext/psych/lib/psych/core_ext.rb @@ -17,3 +17,17 @@ end if defined?(::IRB) require_relative 'y' end + + +# TODO: how best to check for builtin Set? +if defined?(::Set) && Object.const_source_location(:Set) == ["ruby", 0] + class Set + def encode_with(coder) + coder["hash"] = to_h + end + + def init_with(coder) + replace(coder["hash"].keys) + end + end +end diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h index e0977d21aa..2f5090e62f 100644 --- a/include/ruby/atomic.h +++ b/include/ruby/atomic.h @@ -302,6 +302,19 @@ typedef unsigned int rb_atomic_t; RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var)) /** +* Identical to #RUBY_ATOMIC_SET, except it expects its arguments are +* `void*`. There are cases where ::rb_atomic_t is 32bit while ::VALUE is +* 64bit. This should be used for pointer related operations to support such +* platforms. +* +* @param var A variable of `void*`. +* @param val Value to set. +* @post `var` holds `val`. +*/ +#define RUBY_ATOMIC_PTR_SET(var, val) \ + rbimpl_atomic_ptr_set((volatile void **)&(var), (val)) + +/** * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`. * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This * should be used for size related operations to support such platforms. @@ -791,6 +804,19 @@ rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) +static inline void +rbimpl_atomic_ptr_set(volatile void **ptr, void *val) +{ + RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t)); + + const size_t sval = RBIMPL_CAST((size_t)val); + volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); + rbimpl_atomic_size_set(sptr, sval); +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline VALUE rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val) { diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h new file mode 100644 index 0000000000..de26e926d4 --- /dev/null +++ b/include/ruby/internal/attr/nonstring.h @@ -0,0 +1,32 @@ +#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_ATTR_NONSTRING_H +/** + * @file + * @author Ruby developers <ruby-core@ruby-lang.org> + * @copyright This file is a part of the programming language Ruby. + * Permission is hereby granted, to either redistribute and/or + * modify this file, provided that the conditions mentioned in the + * file COPYING are met. Consult the file for details. + * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are + * implementation details. Don't take them as canon. They could + * rapidly appear then vanish. The name (path) of this header file + * is also an implementation detail. Do not expect it to persist + * at the place it is now. Developers are free to move it anywhere + * anytime at will. + * @note To ruby-core: remember that this header can be possibly + * recursively included from extension libraries written in C++. + * Do not expect for instance `__VA_ARGS__` is always available. + * We assume C99 for ruby itself but we don't assume languages of + * extension libraries. They could be written in C++98. + * @brief Defines #RBIMPL_ATTR_NONSTRING. + */ +#include "ruby/internal/has/attribute.h" + +/** Wraps (or simulates) `__attribute__((nonstring))` */ +#if RBIMPL_HAS_ATTRIBUTE(nonstring) +# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring)) +#else +# define RBIMPL_ATTR_NONSTRING() /* void */ +#endif + +#endif /* RBIMPL_ATTR_NONSTRING_H */ @@ -40,6 +40,7 @@ #include "ruby/util.h" #include "builtin.h" #include "shape.h" +#include "ruby/internal/attr/nonstring.h" #define BITSPERSHORT (2*CHAR_BIT) #define SHORTMASK ((1<<BITSPERSHORT)-1) @@ -1515,7 +1516,7 @@ name_equal(const char *name, size_t nlen, const char *p, long l) static int sym2encidx(VALUE sym, VALUE val) { - static const char name_encoding[8] = "encoding"; + RBIMPL_ATTR_NONSTRING() static const char name_encoding[8] = "encoding"; const char *p; long l; if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return -1; @@ -39,7 +39,8 @@ static void ASSERT_ractor_unlocking(rb_ractor_t *r) { #if RACTOR_CHECK_MODE > 0 - if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) { + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) { rb_bug("recursive ractor locking"); } #endif @@ -49,7 +50,8 @@ static void ASSERT_ractor_locking(rb_ractor_t *r) { #if RACTOR_CHECK_MODE > 0 - if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) { + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) { rp(r->sync.locked_by); rb_bug("ractor lock is not acquired."); } @@ -77,7 +79,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line) static void ractor_lock_self(rb_ractor_t *cr, const char *file, int line) { - VM_ASSERT(cr == GET_RACTOR()); + VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline())); #if RACTOR_CHECK_MODE > 0 VM_ASSERT(cr->sync.locked_by != cr->pub.self); #endif @@ -99,7 +101,7 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line) static void ractor_unlock_self(rb_ractor_t *cr, const char *file, int line) { - VM_ASSERT(cr == GET_RACTOR()); + VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline())); #if RACTOR_CHECK_MODE > 0 VM_ASSERT(cr->sync.locked_by == cr->pub.self); #endif @@ -118,6 +118,9 @@ typedef struct { typedef struct { short int len; +#if defined(__has_attribute) && __has_attribute(nonstring) + __attribute__((nonstring)) +#endif const UChar name[6]; int ctype; } PosixBracketEntryType; @@ -499,13 +499,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo *variation_created = false; + // Fast path: if the shape has a single child, we can check it without a lock + struct rb_id_table *edges = RUBY_ATOMIC_PTR_LOAD(shape->edges); + if (edges && SINGLE_CHILD_P(edges)) { + rb_shape_t *child = SINGLE_CHILD(edges); + if (child->edge_name == id) { + return child; + } + } + RB_VM_LOCK_ENTER(); { + // The situation may have changed while we waited for the lock. + // So we load the edge again. + edges = RUBY_ATOMIC_PTR_LOAD(shape->edges); + // If the current shape has children - if (shape->edges) { + if (edges) { // Check if it only has one child - if (SINGLE_CHILD_P(shape->edges)) { - rb_shape_t *child = SINGLE_CHILD(shape->edges); + if (SINGLE_CHILD_P(edges)) { + rb_shape_t *child = SINGLE_CHILD(edges); // If the one child has a matching edge name, then great, // we found what we want. if (child->edge_name == id) { @@ -515,7 +528,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo else { // If it has more than one child, do a hash lookup to find it. VALUE lookup_result; - if (rb_id_table_lookup(shape->edges, id, &lookup_result)) { + if (rb_id_table_lookup(edges, id, &lookup_result)) { res = (rb_shape_t *)lookup_result; } } @@ -531,22 +544,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo else { rb_shape_t *new_shape = rb_shape_alloc_new_child(id, shape, shape_type); - if (!shape->edges) { + if (!edges) { // If the shape had no edge yet, we can directly set the new child - shape->edges = TAG_SINGLE_CHILD(new_shape); + edges = TAG_SINGLE_CHILD(new_shape); } else { // If the edge was single child we need to allocate a table. if (SINGLE_CHILD_P(shape->edges)) { - rb_shape_t *old_child = SINGLE_CHILD(shape->edges); - shape->edges = rb_id_table_create(2); - rb_id_table_insert(shape->edges, old_child->edge_name, (VALUE)old_child); + rb_shape_t *old_child = SINGLE_CHILD(edges); + edges = rb_id_table_create(2); + rb_id_table_insert(edges, old_child->edge_name, (VALUE)old_child); } - rb_id_table_insert(shape->edges, new_shape->edge_name, (VALUE)new_shape); + rb_id_table_insert(edges, new_shape->edge_name, (VALUE)new_shape); *variation_created = true; } + // We must use an atomic when setting the edges to ensure the writes + // from rb_shape_alloc_new_child are committed. + RUBY_ATOMIC_PTR_SET(shape->edges, edges); + res = new_shape; } } @@ -45,6 +45,7 @@ #include "ruby_atomic.h" #include "vm_core.h" #include "ractor_core.h" +#include "ruby/internal/attr/nonstring.h" #ifdef NEED_RUBY_ATOMIC_OPS rb_atomic_t @@ -976,7 +977,7 @@ check_reserved_signal_(const char *name, size_t name_len, int signo) if (prev) { ssize_t RB_UNUSED_VAR(err); static const int stderr_fd = 2; -#define NOZ(name, str) name[sizeof(str)-1] = str +#define NOZ(name, str) RBIMPL_ATTR_NONSTRING() name[sizeof(str)-1] = str static const char NOZ(msg1, " received in "); static const char NOZ(msg2, " handler\n"); @@ -140,6 +140,9 @@ xor64_to(uint64_t *v, const uint64_t s) #endif static const union { +#if defined(__has_attribute) && __has_attribute(nonstring) + __attribute__((nonstring)) +#endif char bin[32]; uint64_t u64[4]; } sip_init_state_bin = {"uespemos""modnarod""arenegyl""setybdet"}; @@ -46,6 +46,7 @@ #include "ruby/util.h" #include "ruby_assert.h" #include "vm_sync.h" +#include "ruby/internal/attr/nonstring.h" #if defined HAVE_CRYPT_R # if defined HAVE_CRYPT_H @@ -11971,7 +11972,7 @@ enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr) encidx = rb_enc_to_index(enc); #define DEFAULT_REPLACE_CHAR(str) do { \ - static const char replace[sizeof(str)-1] = str; \ + RBIMPL_ATTR_NONSTRING() static const char replace[sizeof(str)-1] = str; \ rep = replace; replen = (int)sizeof(replace); \ } while (0) @@ -22,6 +22,7 @@ #include "symbol.h" #include "vm_sync.h" #include "builtin.h" +#include "ruby/internal/attr/nonstring.h" #if defined(USE_SYMBOL_GC) && !(USE_SYMBOL_GC+0) # undef USE_SYMBOL_GC @@ -171,7 +172,7 @@ rb_id_attrset(ID id) /* make new symbol and ID */ if (!(str = lookup_id_str(id))) { - static const char id_types[][8] = { + RBIMPL_ATTR_NONSTRING() static const char id_types[][8] = { "local", "instance", "invalid", diff --git a/template/id.c.tmpl b/template/id.c.tmpl index 5b9e879730..5aa8e47ce7 100644 --- a/template/id.c.tmpl +++ b/template/id.c.tmpl @@ -22,7 +22,8 @@ ops = ids[:token_op].uniq {|id, op, token| token && op} static const struct { unsigned short token; - const char name[3], term; + RBIMPL_ATTR_NONSTRING() const char name[3]; + const char term; } op_tbl[] = { % ops.each do |_id, op, token| % next unless token diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index 675973b913..04f65ec5e3 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -88,6 +88,7 @@ Prelude.new(output, ARGV, vpath).instance_eval do #include "internal/ruby_parser.h" #include "internal/warnings.h" #include "iseq.h" +#include "ruby/internal/attr/nonstring.h" #include "ruby/ruby.h" #include "vm_core.h" @@ -107,12 +108,12 @@ static const struct { % size += line.size % next % end - char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ + RBIMPL_ATTR_NONSTRING() char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ % size = line.size % beg = n % } % if size > 0 - char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ + RBIMPL_ATTR_NONSTRING() char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ % end } prelude_code<%=i%><%=%> = { % size = 0 diff --git a/test/.excludes-mmtk/TestEtc.rb b/test/.excludes-mmtk/TestEtc.rb new file mode 100644 index 0000000000..746f5ba321 --- /dev/null +++ b/test/.excludes-mmtk/TestEtc.rb @@ -0,0 +1 @@ +exclude(:test_ractor_parallel, "glibc error: Mutex lock with MarkSweep debug") diff --git a/test/.excludes-mmtk/TestObjSpace.rb b/test/.excludes-mmtk/TestObjSpace.rb index 200faced19..05666e46f0 100644 --- a/test/.excludes-mmtk/TestObjSpace.rb +++ b/test/.excludes-mmtk/TestObjSpace.rb @@ -1,4 +1,3 @@ exclude(:test_dump_all_full, "testing behaviour specific to default GC") exclude(:test_dump_flag_age, "testing behaviour specific to default GC") exclude(:test_dump_flags, "testing behaviour specific to default GC") -exclude(:test_finalizer, "times out in debug mode on Ubuntu") diff --git a/test/.excludes-mmtk/TestObjectSpace.rb b/test/.excludes-mmtk/TestObjectSpace.rb new file mode 100644 index 0000000000..a92be8090c --- /dev/null +++ b/test/.excludes-mmtk/TestObjectSpace.rb @@ -0,0 +1 @@ +exclude(:test_finalizer, "times out in debug mode on Ubuntu") diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 39fa72e7dd..326cf22e1f 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -203,8 +203,9 @@ class TestObjSpace < Test::Unit::TestCase assert_equal(line1, ObjectSpace.allocation_sourceline(o1)) assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1)) assert_equal(c1, ObjectSpace.allocation_generation(o1)) - assert_equal(self.class.name, ObjectSpace.allocation_class_path(o1)) - assert_equal(__method__, ObjectSpace.allocation_method_id(o1)) + # These assertions fail under coverage measurement: https://bugs.ruby-lang.org/issues/21298 + #assert_equal(self.class.name, ObjectSpace.allocation_class_path(o1)) + #assert_equal(__method__, ObjectSpace.allocation_method_id(o1)) assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2)) assert_equal(line2, ObjectSpace.allocation_sourceline(o2)) diff --git a/test/psych/test_psych_set.rb b/test/psych/test_psych_set.rb new file mode 100644 index 0000000000..c72cd73f18 --- /dev/null +++ b/test/psych/test_psych_set.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +require_relative 'helper' + +module Psych + class TestPsychSet < TestCase + def setup + super + @set = Psych::Set.new + @set['foo'] = 'bar' + @set['bar'] = 'baz' + end + + def test_dump + assert_match(/!set/, Psych.dump(@set)) + end + + def test_roundtrip + assert_cycle(@set) + end + + ### + # FIXME: Syck should also support !!set as shorthand + def test_load_from_yaml + loaded = Psych.unsafe_load(<<-eoyml) +--- !set +foo: bar +bar: baz + eoyml + assert_equal(@set, loaded) + end + + def test_loaded_class + assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set))) + end + + def test_set_shorthand + loaded = Psych.unsafe_load(<<-eoyml) +--- !!set +foo: bar +bar: baz + eoyml + assert_instance_of(Psych::Set, loaded) + end + + def test_set_self_reference + @set['self'] = @set + assert_cycle(@set) + end + + def test_stringify_names + @set[:symbol] = :value + + assert_match(/^:symbol: :value/, Psych.dump(@set)) + assert_match(/^symbol: :value/, Psych.dump(@set, stringify_names: true)) + end + end +end diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb index b4968d3425..ccd591c626 100644 --- a/test/psych/test_set.rb +++ b/test/psych/test_set.rb @@ -1,57 +1,36 @@ +# encoding: UTF-8 # frozen_string_literal: true require_relative 'helper' +require 'set' unless defined?(Set) module Psych class TestSet < TestCase def setup - super - @set = Psych::Set.new - @set['foo'] = 'bar' - @set['bar'] = 'baz' + @set = ::Set.new([1, 2, 3]) end def test_dump - assert_match(/!set/, Psych.dump(@set)) + assert_equal <<~YAML, Psych.dump(@set) + --- !ruby/object:Set + hash: + 1: true + 2: true + 3: true + YAML end - def test_roundtrip - assert_cycle(@set) - end - - ### - # FIXME: Syck should also support !!set as shorthand - def test_load_from_yaml - loaded = Psych.unsafe_load(<<-eoyml) ---- !set -foo: bar -bar: baz - eoyml - assert_equal(@set, loaded) + def test_load + assert_equal @set, Psych.load(<<~YAML, permitted_classes: [::Set]) + --- !ruby/object:Set + hash: + 1: true + 2: true + 3: true + YAML end - def test_loaded_class - assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set))) - end - - def test_set_shorthand - loaded = Psych.unsafe_load(<<-eoyml) ---- !!set -foo: bar -bar: baz - eoyml - assert_instance_of(Psych::Set, loaded) - end - - def test_set_self_reference - @set['self'] = @set - assert_cycle(@set) - end - - def test_stringify_names - @set[:symbol] = :value - - assert_match(/^:symbol: :value/, Psych.dump(@set)) - assert_match(/^symbol: :value/, Psych.dump(@set, stringify_names: true)) + def test_roundtrip + assert_equal @set, Psych.load(Psych.dump(@set), permitted_classes: [::Set]) end end end diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 032f78f6a8..86c1f51dde 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -92,7 +92,7 @@ class TestISeq < Test::Unit::TestCase 42 end EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_forwardable @@ -102,7 +102,7 @@ class TestISeq < Test::Unit::TestCase def foo(...); bar(...); end } EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval.new.foo(40, 2)) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval.new.foo(40, 2)) end def test_super_with_block @@ -112,7 +112,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_block_hash_0 @@ -123,7 +123,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_block_and_kwrest @@ -133,7 +133,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_lambda_with_ractor_roundtrip @@ -143,7 +143,7 @@ class TestISeq < Test::Unit::TestCase Ractor.make_shareable(y) y.call EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_anonymous_block @@ -153,7 +153,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_ractor_unshareable_outer_variable @@ -182,7 +182,7 @@ class TestISeq < Test::Unit::TestCase # shareable_constant_value: literal REGEX = /#{}/ # [Bug #20569] RUBY - assert_includes iseq.to_binary, "REGEX".b + assert_includes iseq_to_binary(iseq), "REGEX".b end def test_disasm_encoding @@ -566,16 +566,20 @@ class TestISeq < Test::Unit::TestCase } end + def iseq_to_binary(iseq) + iseq.to_binary + rescue RuntimeError => e + omit e.message if /compile with coverage/ =~ e.message + raise + end + def assert_iseq_to_binary(code, mesg = nil) iseq = RubyVM::InstructionSequence.compile(code) bin = assert_nothing_raised(mesg) do - iseq.to_binary - rescue RuntimeError => e - omit e.message if /compile with coverage/ =~ e.message - raise + iseq_to_binary(iseq) end 10.times do - bin2 = iseq.to_binary + bin2 = iseq_to_binary(iseq) assert_equal(bin, bin2, message(mesg) {diff hexdump(bin), hexdump(bin2)}) end iseq2 = RubyVM::InstructionSequence.load_from_binary(bin) @@ -593,7 +597,7 @@ class TestISeq < Test::Unit::TestCase def test_to_binary_with_hidden_local_variables assert_iseq_to_binary("for _foo in bar; end") - bin = RubyVM::InstructionSequence.compile(<<-RUBY).to_binary + bin = iseq_to_binary(RubyVM::InstructionSequence.compile(<<-RUBY)) Object.new.instance_eval do a = [] def self.bar; [1] end @@ -668,7 +672,7 @@ class TestISeq < Test::Unit::TestCase end RUBY - iseq_bin = iseq.to_binary + iseq_bin = iseq_to_binary(iseq) iseq = ISeq.load_from_binary(iseq_bin) lines = [] TracePoint.new(tracepoint_type){|tp| @@ -764,7 +768,7 @@ class TestISeq < Test::Unit::TestCase def test_iseq_builtin_load Tempfile.create(["builtin", ".iseq"]) do |f| f.binmode - f.write(RubyVM::InstructionSequence.of(1.method(:abs)).to_binary) + f.write(iseq_to_binary(RubyVM::InstructionSequence.of(1.method(:abs)))) f.close assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}") begin; @@ -857,7 +861,7 @@ class TestISeq < Test::Unit::TestCase def test_loading_kwargs_memory_leak assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true) - a = RubyVM::InstructionSequence.compile("foo(bar: :baz)").to_binary + a = iseq_to_binary(RubyVM::InstructionSequence.compile("foo(bar: :baz)")) begin; 1_000_000.times do RubyVM::InstructionSequence.load_from_binary(a) @@ -868,7 +872,7 @@ class TestISeq < Test::Unit::TestCase def test_ibf_bignum iseq = RubyVM::InstructionSequence.compile("0x0"+"_0123_4567_89ab_cdef"*5) expected = iseq.eval - result = RubyVM::InstructionSequence.load_from_binary(iseq.to_binary).eval + result = RubyVM::InstructionSequence.load_from_binary(iseq_to_binary(iseq)).eval assert_equal expected, result, proc {sprintf("expected: %x, result: %x", expected, result)} end diff --git a/tool/test-coverage.rb b/tool/test-coverage.rb index 055577feea..28ef0bf7f8 100644 --- a/tool/test-coverage.rb +++ b/tool/test-coverage.rb @@ -114,6 +114,10 @@ pid = $$ pwd = Dir.pwd at_exit do + # Some tests leave GC.stress enabled, causing slow coverage processing. + # Reset it here to avoid performance issues. + GC.stress = false + exit_exc = $! Dir.chdir(pwd) do @@ -2118,7 +2118,7 @@ rb_vm_check_ints(rb_execution_context_t *ec) VM_ASSERT(ruby_assert_critical_section_entered == 0); #endif - VM_ASSERT(ec == GET_EC()); + VM_ASSERT(ec == rb_current_ec_noinline()); if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) { rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index d46f5f486f..d54617efe4 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -619,7 +619,7 @@ impl<T: Copy + Into<usize> + PartialEq> UnionFind<T> { } /// Find the set representative for `insn` without doing path compression. - pub fn find_const(&self, insn: T) -> T { + fn find_const(&self, insn: T) -> T { let mut result = insn; loop { match self.at(result) { @@ -645,7 +645,7 @@ pub struct Function { // TODO: get method name and source location from the ISEQ insns: Vec<Insn>, - union_find: UnionFind<InsnId>, + union_find: std::cell::RefCell<UnionFind<InsnId>>, insn_types: Vec<Type>, blocks: Vec<Block>, entry_block: BlockId, @@ -657,7 +657,7 @@ impl Function { iseq, insns: vec![], insn_types: vec![], - union_find: UnionFind::new(), + union_find: UnionFind::new().into(), blocks: vec![Block::default()], entry_block: BlockId(0), } @@ -740,7 +740,7 @@ impl Function { macro_rules! find { ( $x:expr ) => { { - self.union_find.find_const($x) + self.union_find.borrow_mut().find($x) } }; } @@ -749,12 +749,12 @@ impl Function { { BranchEdge { target: $edge.target, - args: $edge.args.iter().map(|x| self.union_find.find_const(*x)).collect(), + args: $edge.args.iter().map(|x| find!(*x)).collect(), } } }; } - let insn_id = self.union_find.find_const(insn_id); + let insn_id = self.union_find.borrow_mut().find(insn_id); use Insn::*; match &self.insns[insn_id.0] { result@(PutSelf | Const {..} | Param {..} | NewArray {..} | GetConstantPath {..} @@ -822,12 +822,12 @@ impl Function { /// Replace `insn` with the new instruction `replacement`, which will get appended to `insns`. fn make_equal_to(&mut self, insn: InsnId, replacement: InsnId) { // Don't push it to the block - self.union_find.make_equal_to(insn, replacement); + self.union_find.borrow_mut().make_equal_to(insn, replacement); } fn type_of(&self, insn: InsnId) -> Type { assert!(self.insns[insn.0].has_output()); - self.insn_types[self.union_find.find_const(insn).0] + self.insn_types[self.union_find.borrow_mut().find(insn).0] } /// Check if the type of `insn` is a subtype of `ty`. @@ -1600,6 +1600,10 @@ fn compute_jump_targets(iseq: *const rb_iseq_t) -> Vec<u32> { let offset = get_arg(pc, 0).as_i64(); jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); } + YARVINSN_opt_new => { + let offset = get_arg(pc, 1).as_i64(); + jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); + } YARVINSN_leave | YARVINSN_opt_invokebuiltin_delegate_leave => { if insn_idx < iseq_size { jump_targets.insert(insn_idx); @@ -1800,6 +1804,17 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { }); queue.push_back((state.clone(), target, target_idx)); } + YARVINSN_opt_new => { + let offset = get_arg(pc, 1).as_i64(); + // TODO(max): Check interrupts + let target_idx = insn_idx_at_offset(insn_idx, offset); + let target = insn_idx_to_block[&target_idx]; + // Skip the fast-path and go straight to the fallback code. We will let the + // optimizer take care of the converting Class#new->alloc+initialize instead. + fun.push_insn(block, Insn::Jump(BranchEdge { target, args: state.as_args() })); + queue.push_back((state.clone(), target, target_idx)); + break; // Don't enqueue the next block as a successor + } YARVINSN_jump => { let offset = get_arg(pc, 0).as_i64(); // TODO(max): Check interrupts @@ -1963,19 +1978,19 @@ mod union_find_tests { } #[test] - fn test_find_const_returns_target() { + fn test_find_returns_target() { let mut uf = UnionFind::new(); uf.make_equal_to(3, 4); - assert_eq!(uf.find_const(3usize), 4); + assert_eq!(uf.find(3usize), 4); } #[test] - fn test_find_const_returns_transitive_target() { + fn test_find_returns_transitive_target() { let mut uf = UnionFind::new(); uf.make_equal_to(3, 4); uf.make_equal_to(4, 5); - assert_eq!(uf.find_const(3usize), 5); - assert_eq!(uf.find_const(4usize), 5); + assert_eq!(uf.find(3usize), 5); + assert_eq!(uf.find(4usize), 5); } #[test] @@ -2796,6 +2811,26 @@ mod tests { "); assert_compile_fails("test", ParseError::UnknownOpcode("sendforward".into())) } + + #[test] + fn test_opt_new() { + eval(" + class C; end + def test = C.new + "); + assert_method_hir("test", expect![[r#" + fn test: + bb0(): + v1:BasicObject = GetConstantPath 0x1000 + v2:NilClassExact = Const Value(nil) + Jump bb1(v2, v1) + bb1(v4:NilClassExact, v5:BasicObject): + v8:BasicObject = SendWithoutBlock v5, :new + Jump bb2(v8, v4) + bb2(v10:BasicObject, v11:NilClassExact): + Return v10 + "#]]); + } } #[cfg(test)] @@ -3688,4 +3723,55 @@ mod opt_tests { Return v5 "#]]); } + + #[test] + fn test_opt_new_no_initialize() { + eval(" + class C; end + def test = C.new + test + "); + assert_optimized_method_hir("test", expect![[r#" + fn test: + bb0(): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v16:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v2:NilClassExact = Const Value(nil) + Jump bb1(v2, v16) + bb1(v4:NilClassExact, v5:BasicObject[VALUE(0x1008)]): + v8:BasicObject = SendWithoutBlock v5, :new + Jump bb2(v8, v4) + bb2(v10:BasicObject, v11:NilClassExact): + Return v10 + "#]]); + } + + #[test] + fn test_opt_new_initialize() { + eval(" + class C + def initialize x + @x = x + end + end + def test = C.new 1 + test + "); + assert_optimized_method_hir("test", expect![[r#" + fn test: + bb0(): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v18:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v2:NilClassExact = Const Value(nil) + v3:Fixnum[1] = Const Value(1) + Jump bb1(v2, v18, v3) + bb1(v5:NilClassExact, v6:BasicObject[VALUE(0x1008)], v7:Fixnum[1]): + v10:BasicObject = SendWithoutBlock v6, :new, v7 + Jump bb2(v10, v5) + bb2(v12:BasicObject, v13:NilClassExact): + Return v12 + "#]]); + } } diff --git a/zjit/zjit.mk b/zjit/zjit.mk index d24d1a19c4..91cf861a39 100644 --- a/zjit/zjit.mk +++ b/zjit/zjit.mk @@ -99,7 +99,7 @@ ZJIT_BINDGEN_DIFF_OPTS = # Generate Rust bindings. See source for details. # Needs `./configure --enable-zjit=dev` and Clang. ifneq ($(strip $(CARGO)),) # if configure found Cargo -.PHONY: zjit-bindgen zjit-bindgen-show-unused +.PHONY: zjit-bindgen zjit-bindgen-show-unused zjit-test zjit-test-lldb zjit-bindgen: zjit.$(OBJEXT) ZJIT_SRC_ROOT_PATH='$(top_srcdir)' BINDGEN_JIT_NAME=zjit $(CARGO) run --manifest-path '$(top_srcdir)/zjit/bindgen/Cargo.toml' -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(Q) if [ 'x$(HAVE_GIT)' = xyes ]; then $(GIT) -C "$(top_srcdir)" diff $(ZJIT_BINDGEN_DIFF_OPTS) zjit/src/cruby_bindings.inc.rs; fi @@ -116,6 +116,21 @@ zjit-test: libminiruby.a CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \ $(CARGO) nextest run --manifest-path '$(top_srcdir)/zjit/Cargo.toml' $(ZJIT_TESTS) +# Run a ZJIT test written with Rust #[test] under LLDB +zjit-test-lldb: libminiruby.a + $(Q)set -eu; \ + if [ -z '$(ZJIT_TESTS)' ]; then \ + echo "Please pass a ZJIT_TESTS=... filter to make."; \ + echo "Many tests only work when it's the only test in the process."; \ + exit 1; \ + fi; \ + exe_path=`RUBY_BUILD_DIR='$(TOP_BUILD_DIR)' \ + RUBY_LD_FLAGS='$(LDFLAGS) $(XLDFLAGS) $(MAINLIBS)' \ + CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \ + $(CARGO) nextest list --manifest-path '$(top_srcdir)/zjit/Cargo.toml' --message-format json --list-type=binaries-only | \ + $(BASERUBY) -rjson -e 'puts JSON.load(STDIN.read).dig("rust-binaries", "zjit", "binary-path")'`; \ + exec lldb $$exe_path -- --test-threads=1 $(ZJIT_TESTS) + # A library for booting miniruby in tests. # Why not use libruby-static.a for this? # - Initialization of the full ruby involves dynamic linking for e.g. transcoding implementations |