summaryrefslogtreecommitdiff
diff options
-rw-r--r--.github/workflows/windows.yml8
-rw-r--r--common.mk5
-rw-r--r--enc/depend1
-rw-r--r--enc/trans/iso2022.trans3
-rw-r--r--ext/json/generator/extconf.rb4
-rw-r--r--ext/json/generator/generator.c30
-rw-r--r--ext/json/generator/simd.h6
-rw-r--r--ext/psych/lib/psych/core_ext.rb14
-rw-r--r--include/ruby/atomic.h26
-rw-r--r--include/ruby/internal/attr/nonstring.h32
-rw-r--r--marshal.c3
-rw-r--r--ractor.c10
-rw-r--r--regenc.h3
-rw-r--r--shape.c37
-rw-r--r--signal.c3
-rw-r--r--siphash.c3
-rw-r--r--string.c3
-rw-r--r--symbol.c3
-rw-r--r--template/id.c.tmpl3
-rw-r--r--template/prelude.c.tmpl5
-rw-r--r--test/.excludes-mmtk/TestEtc.rb1
-rw-r--r--test/.excludes-mmtk/TestObjSpace.rb1
-rw-r--r--test/.excludes-mmtk/TestObjectSpace.rb1
-rw-r--r--test/objspace/test_objspace.rb5
-rw-r--r--test/psych/test_psych_set.rb57
-rw-r--r--test/psych/test_set.rb61
-rw-r--r--test/ruby/test_iseq.rb40
-rw-r--r--tool/test-coverage.rb4
-rw-r--r--vm_core.h2
-rw-r--r--zjit/src/hir.rs112
-rw-r--r--zjit/zjit.mk17
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%
diff --git a/common.mk b/common.mk
index 754c7a2972..c1fa9ae438 100644
--- a/common.mk
+++ b/common.mk
@@ -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 */
diff --git a/marshal.c b/marshal.c
index c1b19a6e63..e19bd5f708 100644
--- a/marshal.c
+++ b/marshal.c
@@ -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;
diff --git a/ractor.c b/ractor.c
index d50704dec4..bdff0c99fd 100644
--- a/ractor.c
+++ b/ractor.c
@@ -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
diff --git a/regenc.h b/regenc.h
index 352a8d7980..b353ae0f9e 100644
--- a/regenc.h
+++ b/regenc.h
@@ -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;
diff --git a/shape.c b/shape.c
index 67755061c0..5ecde596ae 100644
--- a/shape.c
+++ b/shape.c
@@ -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;
}
}
diff --git a/signal.c b/signal.c
index 6d711fc30d..42edc9071b 100644
--- a/signal.c
+++ b/signal.c
@@ -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");
diff --git a/siphash.c b/siphash.c
index 61b8604fc9..62de622778 100644
--- a/siphash.c
+++ b/siphash.c
@@ -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"};
diff --git a/string.c b/string.c
index 696a24b7fc..3b40dafb53 100644
--- a/string.c
+++ b/string.c
@@ -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)
diff --git a/symbol.c b/symbol.c
index 5f95769292..8ba0b818a8 100644
--- a/symbol.c
+++ b/symbol.c
@@ -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
diff --git a/vm_core.h b/vm_core.h
index a9bd47aaae..37af34df9c 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -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
close