diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml index c4c94681f0e387..51e0e4db973ca7 100644 --- a/.github/auto_request_review.yml +++ b/.github/auto_request_review.yml @@ -11,6 +11,7 @@ files: 'doc/zjit*': [team:jit] 'test/ruby/test_zjit*': [team:jit] 'defs/jit.mk': [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. options: ignore_draft: true # This currently doesn't work as intended. We want to skip reviews when only diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index b44187b0fb1cd8..fc538fe51be876 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -145,6 +145,7 @@ jobs: test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}") test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}") + set -x make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \ RUN_OPTS="$RUN_OPTS" \ SPECOPTS="$SPECOPTS" diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 1fac6e1d9395ae..e086582e2430b3 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -195,6 +195,7 @@ jobs: test -n "${LAUNCHABLE_STDOUT}" && exec 1> >(tee "${LAUNCHABLE_STDOUT}") test -n "${LAUNCHABLE_STDERR}" && exec 2> >(tee "${LAUNCHABLE_STDERR}") + set -x make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \ RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" \ YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS" YJIT_BINDGEN_DIFF_OPTS="$YJIT_BINDGEN_DIFF_OPTS" diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 987a26a62bf6f0..4262cc87b9cfec 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -33,9 +33,15 @@ jobs: matrix: include: - test_task: 'check' - configure: '--enable-zjit=dev' run_opts: '--zjit-call-threshold=1' specopts: '-T --zjit-call-threshold=1' + configure: '--enable-zjit=dev' + test_all_opts: '--seed=46450' + + - test_task: 'check' + run_opts: '--zjit-call-threshold=1 --zjit-disable-hir-opt' + specopts: '-T --zjit-call-threshold=1 -T --zjit-disable-hir-opt' + configure: '--enable-zjit=dev' test_all_opts: '--seed=46450' - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb @@ -112,11 +118,12 @@ jobs: echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV - name: make ${{ matrix.test_task }} - run: >- - make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} - RUN_OPTS="$RUN_OPTS" - SPECOPTS="$SPECOPTS" - TESTOPTS="$TESTOPTS" + run: | + set -x + make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \ + RUN_OPTS="$RUN_OPTS" \ + SPECOPTS="$SPECOPTS" \ + TESTOPTS="$TESTOPTS" timeout-minutes: 60 env: RUBY_TESTOPTS: '-q --tty=no' diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index c85a3799a006ca..5dbe9ec52449c8 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -52,9 +52,15 @@ jobs: matrix: include: - test_task: 'check' - configure: '--enable-zjit=dev' run_opts: '--zjit-call-threshold=1' specopts: '-T --zjit-call-threshold=1' + configure: '--enable-zjit=dev' + test_all_opts: '--seed=39471' + + - test_task: 'check' + run_opts: '--zjit-call-threshold=1 --zjit-disable-hir-opt' + specopts: '-T --zjit-call-threshold=1 -T --zjit-disable-hir-opt' + configure: '--enable-zjit=dev' test_all_opts: '--seed=39471' - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb @@ -152,11 +158,12 @@ jobs: echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV - name: make ${{ matrix.test_task }} - run: >- - make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} - RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" - TESTOPTS="$TESTOPTS" - ZJIT_BINDGEN_DIFF_OPTS="$ZJIT_BINDGEN_DIFF_OPTS" + run: | + set -x + make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} \ + RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" \ + TESTOPTS="$TESTOPTS" \ + ZJIT_BINDGEN_DIFF_OPTS="$ZJIT_BINDGEN_DIFF_OPTS" timeout-minutes: 90 env: RUBY_TESTOPTS: '-q --tty=no' diff --git a/encoding.c b/encoding.c index bda40eb04392c7..da434cda1a4ff0 100644 --- a/encoding.c +++ b/encoding.c @@ -152,6 +152,8 @@ enc_list_update(int index, rb_raw_encoding *encoding) RBASIC_CLEAR_CLASS(new_list); /* initialize encoding data */ rb_ary_store(new_list, index, enc_new(encoding)); + rb_ary_freeze(new_list); + FL_SET_RAW(new_list, RUBY_FL_SHAREABLE); RUBY_ATOMIC_VALUE_SET(rb_encoding_list, new_list); } } @@ -349,6 +351,35 @@ enc_table_expand(struct enc_table *enc_table, int newsize) return newsize; } +/* Load an encoding using the values from base_encoding */ +static void +enc_load_from_base(struct enc_table *enc_table, int index, rb_encoding *base_encoding) +{ + ASSERT_vm_locking(); + + struct rb_encoding_entry *ent = &enc_table->list[index]; + + if (ent->loaded) { + return; + } + + rb_raw_encoding *encoding = (rb_raw_encoding *)ent->enc; + RUBY_ASSERT(encoding); + + // FIXME: Before the base is loaded, the encoding may be accessed + // concurrently by other Ractors. + // We're copying all fields from base_encoding except name and + // ruby_encoding_index which we preserve from the original. Since these are + // the only fields other threads should read it is likely safe despite + // technically being a data race. + rb_raw_encoding tmp_encoding = *base_encoding; + tmp_encoding.name = encoding->name; + tmp_encoding.ruby_encoding_index = encoding->ruby_encoding_index; + *encoding = tmp_encoding; + + RUBY_ATOMIC_SET(ent->loaded, encoding->max_enc_len); +} + static int enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_encoding *base_encoding) { @@ -357,45 +388,33 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc struct rb_encoding_entry *ent = &enc_table->list[index]; rb_raw_encoding *encoding; - if (ent->loaded) { - RUBY_ASSERT(ent->base == base_encoding); - RUBY_ASSERT(!strcmp(name, ent->name)); - return index; - } + RUBY_ASSERT(!ent->loaded); + RUBY_ASSERT(!ent->name); + RUBY_ASSERT(!ent->enc); + RUBY_ASSERT(!ent->base); - if (!valid_encoding_name_p(name)) return -1; - if (!ent->name) { - ent->name = name = strdup(name); - } - else if (STRCASECMP(name, ent->name)) { - return -1; - } - encoding = (rb_raw_encoding *)ent->enc; - if (!encoding) { - encoding = xmalloc(sizeof(rb_encoding)); - } + RUBY_ASSERT(valid_encoding_name_p(name)); - rb_raw_encoding tmp_encoding; - if (base_encoding) { - tmp_encoding = *base_encoding; - } - else { - memset(&tmp_encoding, 0, sizeof(*ent->enc)); - } - tmp_encoding.name = name; - tmp_encoding.ruby_encoding_index = index; + ent->name = name = strdup(name); - // FIXME: If encoding already existed, it may be concurrently accessed - // It's technically invalid to write to this memory as it's read, but as all - // values are set up it _probably_ works. - *encoding = tmp_encoding; + encoding = ZALLOC(rb_raw_encoding); + encoding->name = name; + encoding->ruby_encoding_index = index; ent->enc = encoding; - st_insert(enc_table->names, (st_data_t)name, (st_data_t)index); + + if (st_insert(enc_table->names, (st_data_t)name, (st_data_t)index)) { + rb_bug("encoding name was somehow registered twice"); + } enc_list_update(index, encoding); - // max_enc_len is used to mark a fully loaded encoding. - RUBY_ATOMIC_SET(ent->loaded, encoding->max_enc_len); + if (base_encoding) { + enc_load_from_base(enc_table, index, base_encoding); + } + else { + /* it should not be loaded yet */ + RUBY_ASSERT(!encoding->max_enc_len); + } return index; } @@ -405,6 +424,8 @@ enc_register(struct enc_table *enc_table, const char *name, rb_encoding *encodin { ASSERT_vm_locking(); + if (!valid_encoding_name_p(name)) return -1; + int index = enc_table->count; enc_table->count = enc_table_expand(enc_table, index + 1); @@ -445,7 +466,7 @@ rb_enc_register(const char *name, rb_encoding *encoding) index = enc_register(enc_table, name, encoding); } else if (rb_enc_autoload_p(oldenc) || !ENC_DUMMY_P(oldenc)) { - enc_register_at(enc_table, index, name, encoding); + enc_load_from_base(enc_table, index, encoding); } else { rb_raise(rb_eArgError, "encoding %s is already registered", name); @@ -562,7 +583,7 @@ enc_replicate_with_index(struct enc_table *enc_table, const char *name, rb_encod idx = enc_register(enc_table, name, origenc); } else { - idx = enc_register_at(enc_table, idx, name, origenc); + enc_load_from_base(enc_table, idx, origenc); } if (idx >= 0) { set_base_encoding(enc_table, idx, origenc); @@ -816,41 +837,28 @@ enc_autoload_body(rb_encoding *enc) GLOBAL_ENC_TABLE_LOCKING(enc_table) { base = enc_table->list[ENC_TO_ENCINDEX(enc)].base; - if (base) { - do { - if (i >= enc_table->count) { - i = -1; - break; - } - } while (enc_table->list[i].enc != base && (++i, 1)); - } } - - if (i != -1) { - if (base) { - bool do_register = true; - if (rb_enc_autoload_p(base)) { - if (rb_enc_autoload(base) < 0) { - do_register = false; - i = -1; - } + if (base) { + bool do_register = true; + if (rb_enc_autoload_p(base)) { + if (rb_enc_autoload(base) < 0) { + do_register = false; + i = -1; } + } - if (do_register) { - GLOBAL_ENC_TABLE_LOCKING(enc_table) { - i = enc->ruby_encoding_index; - enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base); - RUBY_ASSERT(((rb_raw_encoding *)enc)->ruby_encoding_index == i); - } + if (do_register) { + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + i = ENC_TO_ENCINDEX(enc); + enc_load_from_base(enc_table, i, base); + RUBY_ASSERT(((rb_raw_encoding *)enc)->ruby_encoding_index == i); } - - i &= ENC_INDEX_MASK; - } - else { - i = -2; } } + else { + i = -2; + } return i; } diff --git a/imemo.c b/imemo.c index 5a5ec4a4d382e8..346fae3b869ca0 100644 --- a/imemo.c +++ b/imemo.c @@ -153,7 +153,7 @@ imemo_fields_complex_wb_i(st_data_t key, st_data_t value, st_data_t arg) VALUE rb_imemo_fields_new_complex_tbl(VALUE owner, st_table *tbl) { - VALUE fields = imemo_fields_new(owner, sizeof(struct rb_fields)); + VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields)); IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl; FL_SET_RAW(fields, OBJ_FIELD_HEAP); st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields); diff --git a/prism/prism.c b/prism/prism.c index 42a821e53584a9..6dd0b2ae738588 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -14443,6 +14443,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (accepted_newline) { pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); } + + // If this is a command call and an argument takes a block, + // there can be no further arguments. For example, + // `foo(bar 1 do end, 2)` should be rejected. + if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) { + pm_call_node_t *call = (pm_call_node_t *) argument; + if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + break; + } + } } else { // If there is no comma at the end of the argument list then we're // done parsing arguments and can break out of this loop. diff --git a/prism/util/pm_string.c b/prism/util/pm_string.c index cf79885fddbdff..a7493c468b166e 100644 --- a/prism/util/pm_string.c +++ b/prism/util/pm_string.c @@ -1,5 +1,7 @@ #include "prism/util/pm_string.h" +static const uint8_t empty_source[] = ""; + /** * Returns the size of the pm_string_t struct. This is necessary to allocate the * correct amount of memory in the FFI backend. @@ -133,8 +135,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { // the source to a constant empty string and return. if (file_size == 0) { pm_string_file_handle_close(&handle); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -182,8 +183,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { if (size == 0) { close(fd); - static const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -225,8 +225,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { // the source to a constant empty string and return. if (file_size == 0) { pm_string_file_handle_close(&handle); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -278,8 +277,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { size_t size = (size_t) sb.st_size; if (size == 0) { close(fd); - static const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } diff --git a/test/prism/errors/command_calls.txt b/test/prism/errors/command_calls.txt index 19812a1d0a6780..6601e5fbbc622c 100644 --- a/test/prism/errors/command_calls.txt +++ b/test/prism/errors/command_calls.txt @@ -1,3 +1,10 @@ [a b] ^ unexpected local variable or method; expected a `,` separator for the array elements + +[ + a b do + ^ unexpected local variable or method; expected a `,` separator for the array elements + end, +] + diff --git a/test/prism/errors/command_calls_34.txt b/test/prism/errors/command_calls_34.txt new file mode 100644 index 00000000000000..ce62bc1492a3a0 --- /dev/null +++ b/test/prism/errors/command_calls_34.txt @@ -0,0 +1,24 @@ +foo(bar 1 do end, 2) + ^ invalid comma + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + +foo(bar 1 do end,) + ^ invalid comma + +foo(1, bar 2 do end) + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ')', ignoring it + +foo(1, bar 2) + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/fixtures/command_method_call_2.txt b/test/prism/fixtures/command_method_call_2.txt new file mode 100644 index 00000000000000..165c45987aa944 --- /dev/null +++ b/test/prism/fixtures/command_method_call_2.txt @@ -0,0 +1,3 @@ +foo(bar baz do end) + +foo(bar baz, bat) diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index b4b656fcf49b48..ddb6ffb40c6632 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -27,6 +27,8 @@ class FixturesTest < TestCase # Leaving these out until they are supported by parse.y. except << "leading_logical.txt" except << "endless_methods_command_call.txt" + # https://bugs.ruby-lang.org/issues/21168#note-5 + except << "command_method_call_2.txt" Fixture.each(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 4eacbab3e1170d..3a0da1a2d87d67 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -48,6 +48,9 @@ class LexTest < TestCase # https://bugs.ruby-lang.org/issues/17398#note-12 except << "endless_methods_command_call.txt" + # https://bugs.ruby-lang.org/issues/21168#note-5 + except << "command_method_call_2.txt" + Fixture.each(except: except) do |fixture| define_method(fixture.test_name) { assert_lex(fixture) } end diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 950e7118af526a..9a3224e8ef8843 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -33,7 +33,8 @@ class LocalsTest < TestCase # Leaving these out until they are supported by parse.y. "leading_logical.txt", - "endless_methods_command_call.txt" + "endless_methods_command_call.txt", + "command_method_call_2.txt" ] Fixture.each(except: except) do |fixture| diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 98740f09734043..10b5fca5eaefef 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -70,6 +70,9 @@ class ParserTest < TestCase # Ruby >= 3.5 specific syntax "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] # These files contain code that is being parsed incorrectly by the parser diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 39325137ba07f2..4916ec8d9de752 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -33,6 +33,9 @@ class RipperTest < TestCase # https://bugs.ruby-lang.org/issues/17398#note-12 "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] # Skip these tests that we haven't implemented yet. diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index b21ad81391ed1e..ec55e41967c6a0 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -79,6 +79,9 @@ class RubyParserTest < TestCase # Ruby >= 3.5 specific syntax "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] Fixture.each(except: failures) do |fixture| diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index ea1481ca2d65c1..f383beeb3b1788 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -997,31 +997,87 @@ def test end def test_opt_hash_freeze - assert_compiles '{}', <<~RUBY, insns: [:opt_hash_freeze] + assert_compiles "[{}, 5]", %q{ + def test = {}.freeze + result = [test] + class Hash + def freeze = 5 + end + result << test + }, insns: [:opt_hash_freeze], call_threshold: 1 + end + + def test_opt_hash_freeze_rewritten + assert_compiles "5", %q{ + class Hash + def freeze = 5 + end def test = {}.freeze test - RUBY + }, insns: [:opt_hash_freeze], call_threshold: 1 end def test_opt_ary_freeze - assert_compiles '[]', <<~RUBY, insns: [:opt_ary_freeze] + assert_compiles "[[], 5]", %q{ + def test = [].freeze + result = [test] + class Array + def freeze = 5 + end + result << test + }, insns: [:opt_ary_freeze], call_threshold: 1 + end + + def test_opt_ary_freeze_rewritten + assert_compiles "5", %q{ + class Array + def freeze = 5 + end def test = [].freeze test - RUBY + }, insns: [:opt_ary_freeze], call_threshold: 1 end def test_opt_str_freeze - assert_compiles '""', <<~RUBY, insns: [:opt_str_freeze] - def test = "".freeze + assert_compiles "[\"\", 5]", %q{ + def test = ''.freeze + result = [test] + class String + def freeze = 5 + end + result << test + }, insns: [:opt_str_freeze], call_threshold: 1 + end + + def test_opt_str_freeze_rewritten + assert_compiles "5", %q{ + class String + def freeze = 5 + end + def test = ''.freeze test - RUBY + }, insns: [:opt_str_freeze], call_threshold: 1 end def test_opt_str_uminus - assert_compiles '""', <<~RUBY, insns: [:opt_str_uminus] - def test = -"" + assert_compiles "[\"\", 5]", %q{ + def test = -'' + result = [test] + class String + def -@ = 5 + end + result << test + }, insns: [:opt_str_uminus], call_threshold: 1 + end + + def test_opt_str_uminus_rewritten + assert_compiles "5", %q{ + class String + def -@ = 5 + end + def test = -'' test - RUBY + }, insns: [:opt_str_uminus], call_threshold: 1 end def test_new_array_empty diff --git a/zjit.rb b/zjit.rb index 42cfe1cb91bea4..e3d9d4c728f459 100644 --- a/zjit.rb +++ b/zjit.rb @@ -64,6 +64,8 @@ def stats_string :vm_write_sp_count, :vm_write_locals_count, :vm_write_stack_count, + :vm_write_to_parent_iseq_local_count, + :vm_read_from_parent_iseq_local_count, :code_region_bytes, :side_exit_count, diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index c4fd625818044a..c4233521cce7fc 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -336,6 +336,7 @@ fn main() { .allowlist_function("rb_yarv_class_of") .allowlist_function("rb_zjit_class_initialized_p") .allowlist_function("rb_zjit_class_has_default_allocator") + .allowlist_function("rb_zjit_class_get_alloc_func") .allowlist_function("rb_get_ec_cfp") .allowlist_function("rb_get_cfp_iseq") .allowlist_function("rb_get_cfp_pc") diff --git a/zjit/src/asm/arm64/mod.rs b/zjit/src/asm/arm64/mod.rs index 63ac7823f18d98..176e51ee973e84 100644 --- a/zjit/src/asm/arm64/mod.rs +++ b/zjit/src/asm/arm64/mod.rs @@ -1212,13 +1212,12 @@ fn cbz_cbnz(num_bits: u8, op: bool, offset: InstructionOffset, rt: u8) -> [u8; 4 #[cfg(test)] mod tests { use super::*; - use crate::assertions::assert_disasm; + use insta::assert_snapshot; - /// Check that the bytes for an instruction sequence match a hex string - fn check_bytes(bytes: &str, run: R) where R: FnOnce(&mut super::CodeBlock) { + fn compile(run: R) -> CodeBlock where R: FnOnce(&mut super::CodeBlock) { let mut cb = super::CodeBlock::new_dummy(); run(&mut cb); - assert_eq!(format!("{:x}", cb), bytes); + cb } #[test] @@ -1246,94 +1245,130 @@ mod tests { #[test] fn test_add_reg() { - check_bytes("2000028b", |cb| add(cb, X0, X1, X2)); + let cb = compile(|cb| add(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"2000028b"); } #[test] fn test_add_uimm() { - check_bytes("201c0091", |cb| add(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| add(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c0091"); } #[test] fn test_add_imm_positive() { - check_bytes("201c0091", |cb| add(cb, X0, X1, A64Opnd::new_imm(7))); + let cb = compile(|cb| add(cb, X0, X1, A64Opnd::new_imm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c0091"); } #[test] fn test_add_imm_negative() { - check_bytes("201c00d1", |cb| add(cb, X0, X1, A64Opnd::new_imm(-7))); + let cb = compile(|cb| add(cb, X0, X1, A64Opnd::new_imm(-7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00d1"); } #[test] fn test_adds_reg() { - check_bytes("200002ab", |cb| adds(cb, X0, X1, X2)); + let cb = compile(|cb| adds(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adds x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"200002ab"); } #[test] fn test_adds_uimm() { - check_bytes("201c00b1", |cb| adds(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| adds(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adds x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00b1"); } #[test] fn test_adds_imm_positive() { - check_bytes("201c00b1", |cb| adds(cb, X0, X1, A64Opnd::new_imm(7))); + let cb = compile(|cb| adds(cb, X0, X1, A64Opnd::new_imm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adds x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00b1"); } #[test] fn test_adds_imm_negative() { - check_bytes("201c00f1", |cb| adds(cb, X0, X1, A64Opnd::new_imm(-7))); + let cb = compile(|cb| adds(cb, X0, X1, A64Opnd::new_imm(-7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: subs x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00f1"); } #[test] fn test_adr() { - check_bytes("aa000010", |cb| adr(cb, X10, A64Opnd::new_imm(20))); + let cb = compile(|cb| adr(cb, X10, A64Opnd::new_imm(20))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adr x10, #0x14")); + assert_snapshot!(cb.hexdump(), @"aa000010"); } #[test] fn test_adrp() { - check_bytes("4a000090", |cb| adrp(cb, X10, A64Opnd::new_imm(0x8000))); + let cb = compile(|cb| adrp(cb, X10, A64Opnd::new_imm(0x8000))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adrp x10, #0x8000")); + assert_snapshot!(cb.hexdump(), @"4a000090"); } #[test] fn test_and_register() { - check_bytes("2000028a", |cb| and(cb, X0, X1, X2)); + let cb = compile(|cb| and(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"2000028a"); } #[test] fn test_and_immediate() { - check_bytes("20084092", |cb| and(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| and(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"20084092"); } #[test] fn test_and_32b_immediate() { - check_bytes("404c0012", |cb| and(cb, W0, W2, A64Opnd::new_uimm(0xfffff))); + let cb = compile(|cb| and(cb, W0, W2, A64Opnd::new_uimm(0xfffff))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and w0, w2, #0xfffff")); + assert_snapshot!(cb.hexdump(), @"404c0012"); } #[test] fn test_ands_register() { - check_bytes("200002ea", |cb| ands(cb, X0, X1, X2)); + let cb = compile(|cb| ands(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ands x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"200002ea"); } #[test] fn test_ands_immediate() { - check_bytes("200840f2", |cb| ands(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| ands(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ands x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"200840f2"); } #[test] fn test_asr() { - check_bytes("b4fe4a93", |cb| asr(cb, X20, X21, A64Opnd::new_uimm(10))); + let cb = compile(|cb| asr(cb, X20, X21, A64Opnd::new_uimm(10))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: asr x20, x21, #0xa")); + assert_snapshot!(cb.hexdump(), @"b4fe4a93"); } #[test] fn test_bcond() { let offset = InstructionOffset::from_insns(0x100); - check_bytes("01200054", |cb| bcond(cb, Condition::NE, offset)); + let cb = compile(|cb| bcond(cb, Condition::NE, offset)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: b.ne #0x400")); + assert_snapshot!(cb.hexdump(), @"01200054"); } #[test] fn test_b() { let offset = InstructionOffset::from_insns((1 << 25) - 1); - check_bytes("ffffff15", |cb| b(cb, offset)); + let cb = compile(|cb| b(cb, offset)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: b #0x7fffffc")); + assert_snapshot!(cb.hexdump(), @"ffffff15"); } #[test] @@ -1341,7 +1376,7 @@ mod tests { fn test_b_too_big() { // There are 26 bits available let offset = InstructionOffset::from_insns(1 << 25); - check_bytes("", |cb| b(cb, offset)); + compile(|cb| b(cb, offset)); } #[test] @@ -1349,13 +1384,15 @@ mod tests { fn test_b_too_small() { // There are 26 bits available let offset = InstructionOffset::from_insns(-(1 << 25) - 1); - check_bytes("", |cb| b(cb, offset)); + compile(|cb| b(cb, offset)); } #[test] fn test_bl() { let offset = InstructionOffset::from_insns(-(1 << 25)); - check_bytes("00000096", |cb| bl(cb, offset)); + let cb = compile(|cb| bl(cb, offset)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: bl #0xfffffffff8000000")); + assert_snapshot!(cb.hexdump(), @"00000096"); } #[test] @@ -1363,7 +1400,7 @@ mod tests { fn test_bl_too_big() { // There are 26 bits available let offset = InstructionOffset::from_insns(1 << 25); - check_bytes("", |cb| bl(cb, offset)); + compile(|cb| bl(cb, offset)); } #[test] @@ -1371,385 +1408,544 @@ mod tests { fn test_bl_too_small() { // There are 26 bits available let offset = InstructionOffset::from_insns(-(1 << 25) - 1); - check_bytes("", |cb| bl(cb, offset)); + compile(|cb| bl(cb, offset)); } #[test] fn test_blr() { - check_bytes("80023fd6", |cb| blr(cb, X20)); + let cb = compile(|cb| blr(cb, X20)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: blr x20")); + assert_snapshot!(cb.hexdump(), @"80023fd6"); } #[test] fn test_br() { - check_bytes("80021fd6", |cb| br(cb, X20)); + let cb = compile(|cb| br(cb, X20)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: br x20")); + assert_snapshot!(cb.hexdump(), @"80021fd6"); } #[test] fn test_cbz() { let offset = InstructionOffset::from_insns(-1); - check_bytes("e0ffffb4e0ffff34", |cb| { + let cb = compile(|cb| { cbz(cb, X0, offset); cbz(cb, W0, offset); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: cbz x0, #0xfffffffffffffffc + 0x4: cbz w0, #0 + ")); + assert_snapshot!(cb.hexdump(), @"e0ffffb4e0ffff34"); } #[test] fn test_cbnz() { let offset = InstructionOffset::from_insns(2); - check_bytes("540000b554000035", |cb| { + let cb = compile(|cb| { cbnz(cb, X20, offset); cbnz(cb, W20, offset); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: cbnz x20, #8 + 0x4: cbnz w20, #0xc + ")); + assert_snapshot!(cb.hexdump(), @"540000b554000035"); } #[test] fn test_brk_none() { - check_bytes("00003ed4", |cb| brk(cb, A64Opnd::None)); + let cb = compile(|cb| brk(cb, A64Opnd::None)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: brk #0xf000")); + assert_snapshot!(cb.hexdump(), @"00003ed4"); } #[test] fn test_brk_uimm() { - check_bytes("c00120d4", |cb| brk(cb, A64Opnd::new_uimm(14))); + let cb = compile(|cb| brk(cb, A64Opnd::new_uimm(14))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: brk #0xe")); + assert_snapshot!(cb.hexdump(), @"c00120d4"); } #[test] fn test_cmp_register() { - check_bytes("5f010beb", |cb| cmp(cb, X10, X11)); + let cb = compile(|cb| cmp(cb, X10, X11)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp x10, x11")); + assert_snapshot!(cb.hexdump(), @"5f010beb"); } #[test] fn test_cmp_immediate() { - check_bytes("5f3900f1", |cb| cmp(cb, X10, A64Opnd::new_uimm(14))); + let cb = compile(|cb| cmp(cb, X10, A64Opnd::new_uimm(14))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp x10, #0xe")); + assert_snapshot!(cb.hexdump(), @"5f3900f1"); } #[test] fn test_csel() { - check_bytes("6a018c9a", |cb| csel(cb, X10, X11, X12, Condition::EQ)); + let cb = compile(|cb| csel(cb, X10, X11, X12, Condition::EQ)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: csel x10, x11, x12, eq")); + assert_snapshot!(cb.hexdump(), @"6a018c9a"); } #[test] fn test_eor_register() { - check_bytes("6a010cca", |cb| eor(cb, X10, X11, X12)); + let cb = compile(|cb| eor(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: eor x10, x11, x12")); + assert_snapshot!(cb.hexdump(), @"6a010cca"); } #[test] fn test_eor_immediate() { - check_bytes("6a0940d2", |cb| eor(cb, X10, X11, A64Opnd::new_uimm(7))); + let cb = compile(|cb| eor(cb, X10, X11, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: eor x10, x11, #7")); + assert_snapshot!(cb.hexdump(), @"6a0940d2"); } #[test] fn test_eor_32b_immediate() { - check_bytes("29040152", |cb| eor(cb, W9, W1, A64Opnd::new_uimm(0x80000001))); + let cb = compile(|cb| eor(cb, W9, W1, A64Opnd::new_uimm(0x80000001))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: eor w9, w1, #0x80000001")); + assert_snapshot!(cb.hexdump(), @"29040152"); } #[test] fn test_ldaddal() { - check_bytes("8b01eaf8", |cb| ldaddal(cb, X10, X11, X12)); + let cb = compile(|cb| ldaddal(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldaddal x10, x11, [x12]")); + assert_snapshot!(cb.hexdump(), @"8b01eaf8"); } #[test] fn test_ldaxr() { - check_bytes("6afd5fc8", |cb| ldaxr(cb, X10, X11)); + let cb = compile(|cb| ldaxr(cb, X10, X11)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldaxr x10, [x11]")); + assert_snapshot!(cb.hexdump(), @"6afd5fc8"); } #[test] fn test_ldp() { - check_bytes("8a2d4da9", |cb| ldp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| ldp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldp x10, x11, [x12, #0xd0]")); + assert_snapshot!(cb.hexdump(), @"8a2d4da9"); } #[test] fn test_ldp_pre() { - check_bytes("8a2dcda9", |cb| ldp_pre(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| ldp_pre(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldp x10, x11, [x12, #0xd0]!")); + assert_snapshot!(cb.hexdump(), @"8a2dcda9"); } #[test] fn test_ldp_post() { - check_bytes("8a2dcda8", |cb| ldp_post(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| ldp_post(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldp x10, x11, [x12], #0xd0")); + assert_snapshot!(cb.hexdump(), @"8a2dcda8"); } #[test] fn test_ldr() { - check_bytes("6a696cf8", |cb| ldr(cb, X10, X11, X12)); + let cb = compile(|cb| ldr(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldr x10, [x11, x12]")); + assert_snapshot!(cb.hexdump(), @"6a696cf8"); } #[test] fn test_ldr_literal() { - check_bytes("40010058", |cb| ldr_literal(cb, X0, 10.into())); + let cb = compile(|cb| ldr_literal(cb, X0, 10.into())); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldr x0, #0x28")); + assert_snapshot!(cb.hexdump(), @"40010058"); } #[test] fn test_ldr_post() { - check_bytes("6a0541f8", |cb| ldr_post(cb, X10, A64Opnd::new_mem(64, X11, 16))); + let cb = compile(|cb| ldr_post(cb, X10, A64Opnd::new_mem(64, X11, 16))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldr x10, [x11], #0x10")); + assert_snapshot!(cb.hexdump(), @"6a0541f8"); } #[test] fn test_ldr_pre() { - check_bytes("6a0d41f8", |cb| ldr_pre(cb, X10, A64Opnd::new_mem(64, X11, 16))); + let cb = compile(|cb| ldr_pre(cb, X10, A64Opnd::new_mem(64, X11, 16))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldr x10, [x11, #0x10]!")); + assert_snapshot!(cb.hexdump(), @"6a0d41f8"); } #[test] fn test_ldrh() { - check_bytes("6a194079", |cb| ldrh(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| ldrh(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldrh w10, [x11, #0xc]")); + assert_snapshot!(cb.hexdump(), @"6a194079"); } #[test] fn test_ldrh_pre() { - check_bytes("6acd4078", |cb| ldrh_pre(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| ldrh_pre(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldrh w10, [x11, #0xc]!")); + assert_snapshot!(cb.hexdump(), @"6acd4078"); } #[test] fn test_ldrh_post() { - check_bytes("6ac54078", |cb| ldrh_post(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| ldrh_post(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldrh w10, [x11], #0xc")); + assert_snapshot!(cb.hexdump(), @"6ac54078"); } #[test] fn test_ldurh_memory() { - check_bytes("2a004078", |cb| ldurh(cb, W10, A64Opnd::new_mem(64, X1, 0))); - check_bytes("2ab04778", |cb| ldurh(cb, W10, A64Opnd::new_mem(64, X1, 123))); + let cb = compile(|cb| { + ldurh(cb, W10, A64Opnd::new_mem(64, X1, 0)); + ldurh(cb, W10, A64Opnd::new_mem(64, X1, 123)); + }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: ldurh w10, [x1] + 0x4: ldurh w10, [x1, #0x7b] + ")); + assert_snapshot!(cb.hexdump(), @"2a0040782ab04778"); } #[test] fn test_ldur_memory() { - check_bytes("20b047f8", |cb| ldur(cb, X0, A64Opnd::new_mem(64, X1, 123))); + let cb = compile(|cb| ldur(cb, X0, A64Opnd::new_mem(64, X1, 123))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldur x0, [x1, #0x7b]")); + assert_snapshot!(cb.hexdump(), @"20b047f8"); } #[test] fn test_ldur_register() { - check_bytes("200040f8", |cb| ldur(cb, X0, X1)); + let cb = compile(|cb| ldur(cb, X0, X1)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldur x0, [x1]")); + assert_snapshot!(cb.hexdump(), @"200040f8"); } #[test] fn test_ldursw() { - check_bytes("6ab187b8", |cb| ldursw(cb, X10, A64Opnd::new_mem(64, X11, 123))); + let cb = compile(|cb| ldursw(cb, X10, A64Opnd::new_mem(64, X11, 123))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldursw x10, [x11, #0x7b]")); + assert_snapshot!(cb.hexdump(), @"6ab187b8"); } #[test] fn test_lsl() { - check_bytes("6ac572d3", |cb| lsl(cb, X10, X11, A64Opnd::new_uimm(14))); + let cb = compile(|cb| lsl(cb, X10, X11, A64Opnd::new_uimm(14))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lsl x10, x11, #0xe")); + assert_snapshot!(cb.hexdump(), @"6ac572d3"); } #[test] fn test_lsr() { - check_bytes("6afd4ed3", |cb| lsr(cb, X10, X11, A64Opnd::new_uimm(14))); + let cb = compile(|cb| lsr(cb, X10, X11, A64Opnd::new_uimm(14))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lsr x10, x11, #0xe")); + assert_snapshot!(cb.hexdump(), @"6afd4ed3"); } #[test] fn test_mov_registers() { - check_bytes("ea030baa", |cb| mov(cb, X10, X11)); + let cb = compile(|cb| mov(cb, X10, X11)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x10, x11")); + assert_snapshot!(cb.hexdump(), @"ea030baa"); } #[test] fn test_mov_immediate() { - check_bytes("eaf300b2", |cb| mov(cb, X10, A64Opnd::new_uimm(0x5555555555555555))); + let cb = compile(|cb| mov(cb, X10, A64Opnd::new_uimm(0x5555555555555555))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: orr x10, xzr, #0x5555555555555555")); + assert_snapshot!(cb.hexdump(), @"eaf300b2"); } #[test] fn test_mov_32b_immediate() { - check_bytes("ea070132", |cb| mov(cb, W10, A64Opnd::new_uimm(0x80000001))); + let cb = compile(|cb| mov(cb, W10, A64Opnd::new_uimm(0x80000001))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov w10, #-0x7fffffff")); + assert_snapshot!(cb.hexdump(), @"ea070132"); } #[test] fn test_mov_into_sp() { - check_bytes("1f000091", |cb| mov(cb, X31, X0)); + let cb = compile(|cb| mov(cb, X31, X0)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov sp, x0")); + assert_snapshot!(cb.hexdump(), @"1f000091"); } #[test] fn test_mov_from_sp() { - check_bytes("e0030091", |cb| mov(cb, X0, X31)); + let cb = compile(|cb| mov(cb, X0, X31)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, sp")); + assert_snapshot!(cb.hexdump(), @"e0030091"); } #[test] fn test_movk() { - check_bytes("600fa0f2", |cb| movk(cb, X0, A64Opnd::new_uimm(123), 16)); + let cb = compile(|cb| movk(cb, X0, A64Opnd::new_uimm(123), 16)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movk x0, #0x7b, lsl #16")); + assert_snapshot!(cb.hexdump(), @"600fa0f2"); } #[test] fn test_movn() { - check_bytes("600fa092", |cb| movn(cb, X0, A64Opnd::new_uimm(123), 16)); + let cb = compile(|cb| movn(cb, X0, A64Opnd::new_uimm(123), 16)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, #-0x7b0001")); + assert_snapshot!(cb.hexdump(), @"600fa092"); } #[test] fn test_movz() { - check_bytes("600fa0d2", |cb| movz(cb, X0, A64Opnd::new_uimm(123), 16)); + let cb = compile(|cb| movz(cb, X0, A64Opnd::new_uimm(123), 16)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, #0x7b0000")); + assert_snapshot!(cb.hexdump(), @"600fa0d2"); } #[test] fn test_mrs() { - check_bytes("0a423bd5", |cb| mrs(cb, X10, SystemRegister::NZCV)); + let cb = compile(|cb| mrs(cb, X10, SystemRegister::NZCV)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mrs x10, nzcv")); + assert_snapshot!(cb.hexdump(), @"0a423bd5"); } #[test] fn test_msr() { - check_bytes("0a421bd5", |cb| msr(cb, SystemRegister::NZCV, X10)); + let cb = compile(|cb| msr(cb, SystemRegister::NZCV, X10)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: msr nzcv, x10")); + assert_snapshot!(cb.hexdump(), @"0a421bd5"); } #[test] fn test_mul() { - check_bytes("6a7d0c9b", |cb| mul(cb, X10, X11, X12)); + let cb = compile(|cb| mul(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mul x10, x11, x12")); + assert_snapshot!(cb.hexdump(), @"6a7d0c9b"); } #[test] fn test_mvn() { - check_bytes("ea032baa", |cb| mvn(cb, X10, X11)); + let cb = compile(|cb| mvn(cb, X10, X11)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mvn x10, x11")); + assert_snapshot!(cb.hexdump(), @"ea032baa"); } #[test] fn test_nop() { - check_bytes("1f2003d5", nop); + let cb = compile(nop); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop")); + assert_snapshot!(cb.hexdump(), @"1f2003d5"); } #[test] fn test_orn() { - check_bytes("6a012caa", |cb| orn(cb, X10, X11, X12)); + let cb = compile(|cb| orn(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: orn x10, x11, x12")); + assert_snapshot!(cb.hexdump(), @"6a012caa"); } #[test] fn test_orr_register() { - check_bytes("6a010caa", |cb| orr(cb, X10, X11, X12)); + let cb = compile(|cb| orr(cb, X10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: orr x10, x11, x12")); + assert_snapshot!(cb.hexdump(), @"6a010caa"); } #[test] fn test_orr_immediate() { - check_bytes("6a0940b2", |cb| orr(cb, X10, X11, A64Opnd::new_uimm(7))); + let cb = compile(|cb| orr(cb, X10, X11, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: orr x10, x11, #7")); + assert_snapshot!(cb.hexdump(), @"6a0940b2"); } #[test] fn test_orr_32b_immediate() { - check_bytes("6a010032", |cb| orr(cb, W10, W11, A64Opnd::new_uimm(1))); + let cb = compile(|cb| orr(cb, W10, W11, A64Opnd::new_uimm(1))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: orr w10, w11, #1")); + assert_snapshot!(cb.hexdump(), @"6a010032"); } #[test] fn test_ret_none() { - check_bytes("c0035fd6", |cb| ret(cb, A64Opnd::None)); + let cb = compile(|cb| ret(cb, A64Opnd::None)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ret")); + assert_snapshot!(cb.hexdump(), @"c0035fd6"); } #[test] fn test_ret_register() { - check_bytes("80025fd6", |cb| ret(cb, X20)); + let cb = compile(|cb| ret(cb, X20)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ret x20")); + assert_snapshot!(cb.hexdump(), @"80025fd6"); } #[test] fn test_stlxr() { - check_bytes("8bfd0ac8", |cb| stlxr(cb, W10, X11, X12)); + let cb = compile(|cb| stlxr(cb, W10, X11, X12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stlxr w10, x11, [x12]")); + assert_snapshot!(cb.hexdump(), @"8bfd0ac8"); } #[test] fn test_stp() { - check_bytes("8a2d0da9", |cb| stp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| stp(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x10, x11, [x12, #0xd0]")); + assert_snapshot!(cb.hexdump(), @"8a2d0da9"); } #[test] fn test_stp_pre() { - check_bytes("8a2d8da9", |cb| stp_pre(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| stp_pre(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x10, x11, [x12, #0xd0]!")); + assert_snapshot!(cb.hexdump(), @"8a2d8da9"); } #[test] fn test_stp_post() { - check_bytes("8a2d8da8", |cb| stp_post(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + let cb = compile(|cb| stp_post(cb, X10, X11, A64Opnd::new_mem(64, X12, 208))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x10, x11, [x12], #0xd0")); + assert_snapshot!(cb.hexdump(), @"8a2d8da8"); } #[test] fn test_str_post() { - check_bytes("6a051ff8", |cb| str_post(cb, X10, A64Opnd::new_mem(64, X11, -16))); + let cb = compile(|cb| str_post(cb, X10, A64Opnd::new_mem(64, X11, -16))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: str x10, [x11], #0xfffffffffffffff0")); + assert_snapshot!(cb.hexdump(), @"6a051ff8"); } #[test] fn test_str_pre() { - check_bytes("6a0d1ff8", |cb| str_pre(cb, X10, A64Opnd::new_mem(64, X11, -16))); + let cb = compile(|cb| str_pre(cb, X10, A64Opnd::new_mem(64, X11, -16))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: str x10, [x11, #-0x10]!")); + assert_snapshot!(cb.hexdump(), @"6a0d1ff8"); } #[test] fn test_strh() { - check_bytes("6a190079", |cb| strh(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| strh(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: strh w10, [x11, #0xc]")); + assert_snapshot!(cb.hexdump(), @"6a190079"); } #[test] fn test_strh_pre() { - check_bytes("6acd0078", |cb| strh_pre(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| strh_pre(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: strh w10, [x11, #0xc]!")); + assert_snapshot!(cb.hexdump(), @"6acd0078"); } #[test] fn test_strh_post() { - check_bytes("6ac50078", |cb| strh_post(cb, W10, A64Opnd::new_mem(64, X11, 12))); + let cb = compile(|cb| strh_post(cb, W10, A64Opnd::new_mem(64, X11, 12))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: strh w10, [x11], #0xc")); + assert_snapshot!(cb.hexdump(), @"6ac50078"); } #[test] fn test_stur_64_bits() { - check_bytes("6a0108f8", |cb| stur(cb, X10, A64Opnd::new_mem(64, X11, 128))); + let cb = compile(|cb| stur(cb, X10, A64Opnd::new_mem(64, X11, 128))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stur x10, [x11, #0x80]")); + assert_snapshot!(cb.hexdump(), @"6a0108f8"); } #[test] fn test_stur_32_bits() { - check_bytes("6a0108b8", |cb| stur(cb, X10, A64Opnd::new_mem(32, X11, 128))); + let cb = compile(|cb| stur(cb, X10, A64Opnd::new_mem(32, X11, 128))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stur w10, [x11, #0x80]")); + assert_snapshot!(cb.hexdump(), @"6a0108b8"); } #[test] fn test_sub_reg() { - check_bytes("200002cb", |cb| sub(cb, X0, X1, X2)); + let cb = compile(|cb| sub(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"200002cb"); } #[test] fn test_sub_uimm() { - check_bytes("201c00d1", |cb| sub(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| sub(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00d1"); } #[test] fn test_sub_imm_positive() { - check_bytes("201c00d1", |cb| sub(cb, X0, X1, A64Opnd::new_imm(7))); + let cb = compile(|cb| sub(cb, X0, X1, A64Opnd::new_imm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00d1"); } #[test] fn test_sub_imm_negative() { - check_bytes("201c0091", |cb| sub(cb, X0, X1, A64Opnd::new_imm(-7))); + let cb = compile(|cb| sub(cb, X0, X1, A64Opnd::new_imm(-7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c0091"); } #[test] fn test_subs_reg() { - check_bytes("200002eb", |cb| subs(cb, X0, X1, X2)); + let cb = compile(|cb| subs(cb, X0, X1, X2)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: subs x0, x1, x2")); + assert_snapshot!(cb.hexdump(), @"200002eb"); } #[test] fn test_subs_imm_positive() { - check_bytes("201c00f1", |cb| subs(cb, X0, X1, A64Opnd::new_imm(7))); + let cb = compile(|cb| subs(cb, X0, X1, A64Opnd::new_imm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: subs x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00f1"); } #[test] fn test_subs_imm_negative() { - check_bytes("201c00b1", |cb| subs(cb, X0, X1, A64Opnd::new_imm(-7))); + let cb = compile(|cb| subs(cb, X0, X1, A64Opnd::new_imm(-7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adds x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00b1"); } #[test] fn test_subs_uimm() { - check_bytes("201c00f1", |cb| subs(cb, X0, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| subs(cb, X0, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: subs x0, x1, #7")); + assert_snapshot!(cb.hexdump(), @"201c00f1"); } #[test] fn test_sxtw() { - check_bytes("6a7d4093", |cb| sxtw(cb, X10, W11)); + let cb = compile(|cb| sxtw(cb, X10, W11)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sxtw x10, w11")); + assert_snapshot!(cb.hexdump(), @"6a7d4093"); } #[test] fn test_tbnz() { - check_bytes("4a005037", |cb| tbnz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2))); + let cb = compile(|cb| tbnz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: tbnz w10, #0xa, #8")); + assert_snapshot!(cb.hexdump(), @"4a005037"); } #[test] fn test_tbz() { - check_bytes("4a005036", |cb| tbz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2))); + let cb = compile(|cb| tbz(cb, X10, A64Opnd::UImm(10), A64Opnd::Imm(2))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: tbz w10, #0xa, #8")); + assert_snapshot!(cb.hexdump(), @"4a005036"); } #[test] fn test_tst_register() { - check_bytes("1f0001ea", |cb| tst(cb, X0, X1)); + let cb = compile(|cb| tst(cb, X0, X1)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: tst x0, x1")); + assert_snapshot!(cb.hexdump(), @"1f0001ea"); } #[test] fn test_tst_immediate() { - check_bytes("3f0840f2", |cb| tst(cb, X1, A64Opnd::new_uimm(7))); + let cb = compile(|cb| tst(cb, X1, A64Opnd::new_uimm(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: tst x1, #7")); + assert_snapshot!(cb.hexdump(), @"3f0840f2"); } #[test] fn test_tst_32b_immediate() { - check_bytes("1f3c0072", |cb| tst(cb, W0, A64Opnd::new_uimm(0xffff))); + let cb = compile(|cb| tst(cb, W0, A64Opnd::new_uimm(0xffff))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: tst w0, #0xffff")); + assert_snapshot!(cb.hexdump(), @"1f3c0072"); } #[test] @@ -1760,10 +1956,11 @@ mod tests { add_extended(&mut cb, X30, X30, X30); add_extended(&mut cb, X31, X31, X31); - assert_disasm!(cb, "6a61298bde633e8bff633f8b", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add x10, x11, x9, uxtx 0x4: add x30, x30, x30, uxtx 0x8: add sp, sp, xzr - "); + ")); + assert_snapshot!(cb.hexdump(), @"6a61298bde633e8bff633f8b"); } } diff --git a/zjit/src/asm/mod.rs b/zjit/src/asm/mod.rs index e07ac0a48c3d89..2ac864047cabb5 100644 --- a/zjit/src/asm/mod.rs +++ b/zjit/src/asm/mod.rs @@ -281,6 +281,24 @@ impl CodeBlock { pub fn mark_all_executable(&mut self) { self.mem_block.borrow_mut().mark_all_executable(); } + + /// Call a func with the disasm of generated code for testing + #[allow(unused_variables)] + #[cfg(test)] + pub fn with_disasm(&self, func: T) where T: Fn(String) { + #[cfg(feature = "disasm")] + { + let start_addr = self.get_ptr(0).raw_addr(self); + let end_addr = self.get_write_ptr().raw_addr(self); + func(crate::disasm::disasm_addr_range(self, start_addr, end_addr)); + } + } + + /// Return the hex dump of generated code for testing + #[cfg(test)] + pub fn hexdump(&self) -> String { + format!("{:x}", self) + } } /// Produce hex string output from the bytes in a code block diff --git a/zjit/src/asm/x86_64/tests.rs b/zjit/src/asm/x86_64/tests.rs index 0268846e10c883..a095d73f89faa1 100644 --- a/zjit/src/asm/x86_64/tests.rs +++ b/zjit/src/asm/x86_64/tests.rs @@ -1,5 +1,7 @@ #![cfg(test)] +use insta::assert_snapshot; + use crate::asm::x86_64::*; /// Check that the bytes for an instruction sequence match a hex string @@ -9,364 +11,745 @@ fn check_bytes(bytes: &str, run: R) where R: FnOnce(&mut super::CodeBlock) { assert_eq!(format!("{:x}", cb), bytes); } +fn compile(run: R) -> CodeBlock where R: FnOnce(&mut super::CodeBlock) { + let mut cb = super::CodeBlock::new_dummy(); + run(&mut cb); + cb +} + #[test] fn test_add() { - check_bytes("80c103", |cb| add(cb, CL, imm_opnd(3))); - check_bytes("00d9", |cb| add(cb, CL, BL)); - check_bytes("4000e1", |cb| add(cb, CL, SPL)); - check_bytes("6601d9", |cb| add(cb, CX, BX)); - check_bytes("4801d8", |cb| add(cb, RAX, RBX)); - check_bytes("01d1", |cb| add(cb, ECX, EDX)); - check_bytes("4c01f2", |cb| add(cb, RDX, R14)); - check_bytes("480110", |cb| add(cb, mem_opnd(64, RAX, 0), RDX)); - check_bytes("480310", |cb| add(cb, RDX, mem_opnd(64, RAX, 0))); - check_bytes("48035008", |cb| add(cb, RDX, mem_opnd(64, RAX, 8))); - check_bytes("480390ff000000", |cb| add(cb, RDX, mem_opnd(64, RAX, 255))); - check_bytes("4881407fff000000", |cb| add(cb, mem_opnd(64, RAX, 127), imm_opnd(255))); - check_bytes("0110", |cb| add(cb, mem_opnd(32, RAX, 0), EDX)); - check_bytes("4883c408", |cb| add(cb, RSP, imm_opnd(8))); - check_bytes("83c108", |cb| add(cb, ECX, imm_opnd(8))); - check_bytes("81c1ff000000", |cb| add(cb, ECX, imm_opnd(255))); + let cb01 = compile(|cb| add(cb, CL, imm_opnd(3))); + let cb02 = compile(|cb| add(cb, CL, BL)); + let cb03 = compile(|cb| add(cb, CL, SPL)); + let cb04 = compile(|cb| add(cb, CX, BX)); + let cb05 = compile(|cb| add(cb, RAX, RBX)); + let cb06 = compile(|cb| add(cb, ECX, EDX)); + let cb07 = compile(|cb| add(cb, RDX, R14)); + let cb08 = compile(|cb| add(cb, mem_opnd(64, RAX, 0), RDX)); + let cb09 = compile(|cb| add(cb, RDX, mem_opnd(64, RAX, 0))); + let cb10 = compile(|cb| add(cb, RDX, mem_opnd(64, RAX, 8))); + let cb11 = compile(|cb| add(cb, RDX, mem_opnd(64, RAX, 255))); + let cb12 = compile(|cb| add(cb, mem_opnd(64, RAX, 127), imm_opnd(255))); + let cb13 = compile(|cb| add(cb, mem_opnd(32, RAX, 0), EDX)); + let cb14 = compile(|cb| add(cb, RSP, imm_opnd(8))); + let cb15 = compile(|cb| add(cb, ECX, imm_opnd(8))); + let cb16 = compile(|cb| add(cb, ECX, imm_opnd(255))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add cl, 3")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add cl, bl")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add cl, spl")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add cx, bx")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rax, rbx")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add ecx, edx")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rdx, r14")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add qword ptr [rax], rdx")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rdx, qword ptr [rax]")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rdx, qword ptr [rax + 8]")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rdx, qword ptr [rax + 0xff]")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add qword ptr [rax + 0x7f], 0xff")); + cb13.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add dword ptr [rax], edx")); + cb14.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add rsp, 8")); + cb15.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add ecx, 8")); + cb16.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add ecx, 0xff")); + + assert_snapshot!(cb01.hexdump(), @"80c103"); + assert_snapshot!(cb02.hexdump(), @"00d9"); + assert_snapshot!(cb03.hexdump(), @"4000e1"); + assert_snapshot!(cb04.hexdump(), @"6601d9"); + assert_snapshot!(cb05.hexdump(), @"4801d8"); + assert_snapshot!(cb06.hexdump(), @"01d1"); + assert_snapshot!(cb07.hexdump(), @"4c01f2"); + assert_snapshot!(cb08.hexdump(), @"480110"); + assert_snapshot!(cb09.hexdump(), @"480310"); + assert_snapshot!(cb10.hexdump(), @"48035008"); + assert_snapshot!(cb11.hexdump(), @"480390ff000000"); + assert_snapshot!(cb12.hexdump(), @"4881407fff000000"); + assert_snapshot!(cb13.hexdump(), @"0110"); + assert_snapshot!(cb14.hexdump(), @"4883c408"); + assert_snapshot!(cb15.hexdump(), @"83c108"); + assert_snapshot!(cb16.hexdump(), @"81c1ff000000"); } #[test] fn test_add_unsigned() { // ADD r/m8, imm8 - check_bytes("4180c001", |cb| add(cb, R8B, uimm_opnd(1))); - check_bytes("4180c07f", |cb| add(cb, R8B, imm_opnd(i8::MAX.into()))); - + let cb1 = compile(|cb| add(cb, R8B, uimm_opnd(1))); + let cb2 = compile(|cb| add(cb, R8B, imm_opnd(i8::MAX.into()))); // ADD r/m16, imm16 - check_bytes("664183c001", |cb| add(cb, R8W, uimm_opnd(1))); - check_bytes("664181c0ff7f", |cb| add(cb, R8W, uimm_opnd(i16::MAX.try_into().unwrap()))); - + let cb3 = compile(|cb| add(cb, R8W, uimm_opnd(1))); + let cb4 = compile(|cb| add(cb, R8W, uimm_opnd(i16::MAX.try_into().unwrap()))); // ADD r/m32, imm32 - check_bytes("4183c001", |cb| add(cb, R8D, uimm_opnd(1))); - check_bytes("4181c0ffffff7f", |cb| add(cb, R8D, uimm_opnd(i32::MAX.try_into().unwrap()))); - + let cb5 = compile(|cb| add(cb, R8D, uimm_opnd(1))); + let cb6 = compile(|cb| add(cb, R8D, uimm_opnd(i32::MAX.try_into().unwrap()))); // ADD r/m64, imm32 - check_bytes("4983c001", |cb| add(cb, R8, uimm_opnd(1))); - check_bytes("4981c0ffffff7f", |cb| add(cb, R8, uimm_opnd(i32::MAX.try_into().unwrap()))); + let cb7 = compile(|cb| add(cb, R8, uimm_opnd(1))); + let cb8 = compile(|cb| add(cb, R8, uimm_opnd(i32::MAX.try_into().unwrap()))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8b, 1")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8b, 0x7f")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8w, 1")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8w, 0x7fff")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8d, 1")); + cb6.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8d, 0x7fffffff")); + cb7.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8, 1")); + cb8.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r8, 0x7fffffff")); + + assert_snapshot!(cb1.hexdump(), @"4180c001"); + assert_snapshot!(cb2.hexdump(), @"4180c07f"); + assert_snapshot!(cb3.hexdump(), @"664183c001"); + assert_snapshot!(cb4.hexdump(), @"664181c0ff7f"); + assert_snapshot!(cb5.hexdump(), @"4183c001"); + assert_snapshot!(cb6.hexdump(), @"4181c0ffffff7f"); + assert_snapshot!(cb7.hexdump(), @"4983c001"); + assert_snapshot!(cb8.hexdump(), @"4981c0ffffff7f"); } #[test] fn test_and() { - check_bytes("4421e5", |cb| and(cb, EBP, R12D)); - check_bytes("48832008", |cb| and(cb, mem_opnd(64, RAX, 0), imm_opnd(0x08))); + let cb1 = compile(|cb| and(cb, EBP, R12D)); + let cb2 = compile(|cb| and(cb, mem_opnd(64, RAX, 0), imm_opnd(0x08))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and ebp, r12d")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and qword ptr [rax], 8")); + + assert_snapshot!(cb1.hexdump(), @"4421e5"); + assert_snapshot!(cb2.hexdump(), @"48832008"); } #[test] fn test_call_label() { - check_bytes("e8fbffffff", |cb| { + let cb = compile(|cb| { let label_idx = cb.new_label("fn".to_owned()); call_label(cb, label_idx); cb.link_labels(); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: call 0")); + assert_snapshot!(cb.hexdump(), @"e8fbffffff"); } #[test] fn test_call_ptr() { // calling a lower address - check_bytes("e8fbffffff", |cb| { + let cb = compile(|cb| { let ptr = cb.get_write_ptr(); call_ptr(cb, RAX, ptr.raw_ptr(cb)); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: call 0")); + assert_snapshot!(cb.hexdump(), @"e8fbffffff"); } #[test] fn test_call_reg() { - check_bytes("ffd0", |cb| call(cb, RAX)); + let cb = compile(|cb| call(cb, RAX)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: call rax")); + assert_snapshot!(cb.hexdump(), @"ffd0"); } #[test] fn test_call_mem() { - check_bytes("ff542408", |cb| call(cb, mem_opnd(64, RSP, 8))); + let cb = compile(|cb| call(cb, mem_opnd(64, RSP, 8))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: call qword ptr [rsp + 8]")); + assert_snapshot!(cb.hexdump(), @"ff542408"); } #[test] fn test_cmovcc() { - check_bytes("0f4ff7", |cb| cmovg(cb, ESI, EDI)); - check_bytes("0f4f750c", |cb| cmovg(cb, ESI, mem_opnd(32, RBP, 12))); - check_bytes("0f4cc1", |cb| cmovl(cb, EAX, ECX)); - check_bytes("480f4cdd", |cb| cmovl(cb, RBX, RBP)); - check_bytes("0f4e742404", |cb| cmovle(cb, ESI, mem_opnd(32, RSP, 4))); + let cb1 = compile(|cb| cmovg(cb, ESI, EDI)); + let cb2 = compile(|cb| cmovg(cb, ESI, mem_opnd(32, RBP, 12))); + let cb3 = compile(|cb| cmovl(cb, EAX, ECX)); + let cb4 = compile(|cb| cmovl(cb, RBX, RBP)); + let cb5 = compile(|cb| cmovle(cb, ESI, mem_opnd(32, RSP, 4))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmovg esi, edi")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmovg esi, dword ptr [rbp + 0xc]")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmovl eax, ecx")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmovl rbx, rbp")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmovle esi, dword ptr [rsp + 4]")); + + assert_snapshot!(cb1.hexdump(), @"0f4ff7"); + assert_snapshot!(cb2.hexdump(), @"0f4f750c"); + assert_snapshot!(cb3.hexdump(), @"0f4cc1"); + assert_snapshot!(cb4.hexdump(), @"480f4cdd"); + assert_snapshot!(cb5.hexdump(), @"0f4e742404"); } #[test] fn test_cmp() { - check_bytes("38d1", |cb| cmp(cb, CL, DL)); - check_bytes("39f9", |cb| cmp(cb, ECX, EDI)); - check_bytes("493b1424", |cb| cmp(cb, RDX, mem_opnd(64, R12, 0))); - check_bytes("4883f802", |cb| cmp(cb, RAX, imm_opnd(2))); - check_bytes("81f900000080", |cb| cmp(cb, ECX, uimm_opnd(0x8000_0000))); + let cb1 = compile(|cb| cmp(cb, CL, DL)); + let cb2 = compile(|cb| cmp(cb, ECX, EDI)); + let cb3 = compile(|cb| cmp(cb, RDX, mem_opnd(64, R12, 0))); + let cb4 = compile(|cb| cmp(cb, RAX, imm_opnd(2))); + let cb5 = compile(|cb| cmp(cb, ECX, uimm_opnd(0x8000_0000))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp cl, dl")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp ecx, edi")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp rdx, qword ptr [r12]")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp rax, 2")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp ecx, 0x80000000")); + + assert_snapshot!(cb1.hexdump(), @"38d1"); + assert_snapshot!(cb2.hexdump(), @"39f9"); + assert_snapshot!(cb3.hexdump(), @"493b1424"); + assert_snapshot!(cb4.hexdump(), @"4883f802"); + assert_snapshot!(cb5.hexdump(), @"81f900000080"); } #[test] fn test_cqo() { - check_bytes("4899", cqo); + let cb = compile(cqo); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cqo")); + assert_snapshot!(cb.hexdump(), @"4899"); } #[test] fn test_imul() { - check_bytes("480fafc3", |cb| imul(cb, RAX, RBX)); - check_bytes("480faf10", |cb| imul(cb, RDX, mem_opnd(64, RAX, 0))); - + let cb1 = compile(|cb| imul(cb, RAX, RBX)); + let cb2 = compile(|cb| imul(cb, RDX, mem_opnd(64, RAX, 0))); // Operands flipped for encoding since multiplication is commutative - check_bytes("480faf10", |cb| imul(cb, mem_opnd(64, RAX, 0), RDX)); + let cb3 = compile(|cb| imul(cb, mem_opnd(64, RAX, 0), RDX)); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: imul rax, rbx")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: imul rdx, qword ptr [rax]")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: imul rdx, qword ptr [rax]")); + + assert_snapshot!(cb1.hexdump(), @"480fafc3"); + assert_snapshot!(cb2.hexdump(), @"480faf10"); + assert_snapshot!(cb3.hexdump(), @"480faf10"); } #[test] fn test_jge_label() { - check_bytes("0f8dfaffffff", |cb| { + let cb = compile(|cb| { let label_idx = cb.new_label("loop".to_owned()); jge_label(cb, label_idx); cb.link_labels(); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: jge 0")); + assert_snapshot!(cb.hexdump(), @"0f8dfaffffff"); } #[test] fn test_jmp_label() { // Forward jump - check_bytes("e900000000", |cb| { + let cb1 = compile(|cb| { let label_idx = cb.new_label("next".to_owned()); jmp_label(cb, label_idx); cb.write_label(label_idx); cb.link_labels(); }); - // Backwards jump - check_bytes("e9fbffffff", |cb| { + let cb2 = compile(|cb| { let label_idx = cb.new_label("loop".to_owned()); cb.write_label(label_idx); jmp_label(cb, label_idx); cb.link_labels(); }); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: jmp 5")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: jmp 0")); + + assert_snapshot!(cb1.hexdump(), @"e900000000"); + assert_snapshot!(cb2.hexdump(), @"e9fbffffff"); } #[test] fn test_jmp_rm() { - check_bytes("41ffe4", |cb| jmp_rm(cb, R12)); + let cb = compile(|cb| jmp_rm(cb, R12)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: jmp r12")); + assert_snapshot!(cb.hexdump(), @"41ffe4"); } #[test] fn test_jo_label() { - check_bytes("0f80faffffff", |cb| { + let cb = compile(|cb| { let label_idx = cb.new_label("loop".to_owned()); jo_label(cb, label_idx); cb.link_labels(); }); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: jo 0")); + assert_snapshot!(cb.hexdump(), @"0f80faffffff"); } #[test] fn test_lea() { - check_bytes("488d5108", |cb| lea(cb, RDX, mem_opnd(64, RCX, 8))); - check_bytes("488d0500000000", |cb| lea(cb, RAX, mem_opnd(8, RIP, 0))); - check_bytes("488d0505000000", |cb| lea(cb, RAX, mem_opnd(8, RIP, 5))); - check_bytes("488d3d05000000", |cb| lea(cb, RDI, mem_opnd(8, RIP, 5))); + let cb1 = compile(|cb| lea(cb, RDX, mem_opnd(64, RCX, 8))); + let cb2 = compile(|cb| lea(cb, RAX, mem_opnd(8, RIP, 0))); + let cb3 = compile(|cb| lea(cb, RAX, mem_opnd(8, RIP, 5))); + let cb4 = compile(|cb| lea(cb, RDI, mem_opnd(8, RIP, 5))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lea rdx, [rcx + 8]")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lea rax, [rip]")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lea rax, [rip + 5]")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lea rdi, [rip + 5]")); + + assert_snapshot!(cb1.hexdump(), @"488d5108"); + assert_snapshot!(cb2.hexdump(), @"488d0500000000"); + assert_snapshot!(cb3.hexdump(), @"488d0505000000"); + assert_snapshot!(cb4.hexdump(), @"488d3d05000000"); } #[test] fn test_mov() { - check_bytes("b807000000", |cb| mov(cb, EAX, imm_opnd(7))); - check_bytes("b8fdffffff", |cb| mov(cb, EAX, imm_opnd(-3))); - check_bytes("41bf03000000", |cb| mov(cb, R15, imm_opnd(3))); - check_bytes("89d8", |cb| mov(cb, EAX, EBX)); - check_bytes("89c8", |cb| mov(cb, EAX, ECX)); - check_bytes("8b9380000000", |cb| mov(cb, EDX, mem_opnd(32, RBX, 128))); - check_bytes("488b442404", |cb| mov(cb, RAX, mem_opnd(64, RSP, 4))); - + let cb01 = compile(|cb| mov(cb, EAX, imm_opnd(7))); + let cb02 = compile(|cb| mov(cb, EAX, imm_opnd(-3))); + let cb03 = compile(|cb| mov(cb, R15, imm_opnd(3))); + let cb04 = compile(|cb| mov(cb, EAX, EBX)); + let cb05 = compile(|cb| mov(cb, EAX, ECX)); + let cb06 = compile(|cb| mov(cb, EDX, mem_opnd(32, RBX, 128))); + let cb07 = compile(|cb| mov(cb, RAX, mem_opnd(64, RSP, 4))); // Test `mov rax, 3` => `mov eax, 3` optimization - check_bytes("41b834000000", |cb| mov(cb, R8, imm_opnd(0x34))); - check_bytes("49b80000008000000000", |cb| mov(cb, R8, imm_opnd(0x80000000))); - check_bytes("49b8ffffffffffffffff", |cb| mov(cb, R8, imm_opnd(-1))); - - check_bytes("b834000000", |cb| mov(cb, RAX, imm_opnd(0x34))); - check_bytes("48b8020000000000c0ff", |cb| mov(cb, RAX, imm_opnd(-18014398509481982))); - check_bytes("48b80000008000000000", |cb| mov(cb, RAX, imm_opnd(0x80000000))); - check_bytes("48b8ccffffffffffffff", |cb| mov(cb, RAX, imm_opnd(-52))); // yasm thinks this could use a dword immediate instead of qword - check_bytes("48b8ffffffffffffffff", |cb| mov(cb, RAX, imm_opnd(-1))); // yasm thinks this could use a dword immediate instead of qword - check_bytes("4488c9", |cb| mov(cb, CL, R9B)); - check_bytes("4889c3", |cb| mov(cb, RBX, RAX)); - check_bytes("4889df", |cb| mov(cb, RDI, RBX)); - check_bytes("40b60b", |cb| mov(cb, SIL, imm_opnd(11))); - - check_bytes("c60424fd", |cb| mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3))); - check_bytes("48c7470801000000", |cb| mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1))); - //check_bytes("67c7400411000000", |cb| mov(cb, mem_opnd(32, EAX, 4), imm_opnd(0x34))); // We don't distinguish between EAX and RAX here - that's probably fine? - check_bytes("c7400411000000", |cb| mov(cb, mem_opnd(32, RAX, 4), imm_opnd(17))); - check_bytes("c7400401000080", |cb| mov(cb, mem_opnd(32, RAX, 4), uimm_opnd(0x80000001))); - check_bytes("41895814", |cb| mov(cb, mem_opnd(32, R8, 20), EBX)); - check_bytes("4d8913", |cb| mov(cb, mem_opnd(64, R11, 0), R10)); - check_bytes("48c742f8f4ffffff", |cb| mov(cb, mem_opnd(64, RDX, -8), imm_opnd(-12))); + let cb08 = compile(|cb| mov(cb, R8, imm_opnd(0x34))); + let cb09 = compile(|cb| mov(cb, R8, imm_opnd(0x80000000))); + let cb10 = compile(|cb| mov(cb, R8, imm_opnd(-1))); + let cb11 = compile(|cb| mov(cb, RAX, imm_opnd(0x34))); + let cb12 = compile(|cb| mov(cb, RAX, imm_opnd(-18014398509481982))); + let cb13 = compile(|cb| mov(cb, RAX, imm_opnd(0x80000000))); + let cb14 = compile(|cb| mov(cb, RAX, imm_opnd(-52))); // yasm thinks this could use a dword immediate instead of qword + let cb15 = compile(|cb| mov(cb, RAX, imm_opnd(-1))); // yasm thinks this could use a dword immediate instead of qword + let cb16 = compile(|cb| mov(cb, CL, R9B)); + let cb17 = compile(|cb| mov(cb, RBX, RAX)); + let cb18 = compile(|cb| mov(cb, RDI, RBX)); + let cb19 = compile(|cb| mov(cb, SIL, imm_opnd(11))); + let cb20 = compile(|cb| mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3))); + let cb21 = compile(|cb| mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1))); + //let cb = compile(|cb| mov(cb, mem_opnd(32, EAX, 4), imm_opnd(0x34))); // We don't distinguish between EAX and RAX here - that's probably fine? + let cb22 = compile(|cb| mov(cb, mem_opnd(32, RAX, 4), imm_opnd(17))); + let cb23 = compile(|cb| mov(cb, mem_opnd(32, RAX, 4), uimm_opnd(0x80000001))); + let cb24 = compile(|cb| mov(cb, mem_opnd(32, R8, 20), EBX)); + let cb25 = compile(|cb| mov(cb, mem_opnd(64, R11, 0), R10)); + let cb26 = compile(|cb| mov(cb, mem_opnd(64, RDX, -8), imm_opnd(-12))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 7")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 0xfffffffd")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r15d, 3")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, ebx")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, ecx")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov edx, dword ptr [rbx + 0x80]")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rax, qword ptr [rsp + 4]")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 0x34")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0x80000000")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0xffffffffffffffff")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 0x34")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0xffc0000000000002")); + cb13.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0x80000000")); + cb14.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0xffffffffffffffcc")); + cb15.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0xffffffffffffffff")); + cb16.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov cl, r9b")); + cb17.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rbx, rax")); + cb18.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rdi, rbx")); + cb19.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov sil, 0xb")); + cb20.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov byte ptr [rsp], 0xfd")); + cb21.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov qword ptr [rdi + 8], 1")); + cb22.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov dword ptr [rax + 4], 0x11")); + cb23.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov dword ptr [rax + 4], 0x80000001")); + cb24.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov dword ptr [r8 + 0x14], ebx")); + cb25.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov qword ptr [r11], r10")); + cb26.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov qword ptr [rdx - 8], 0xfffffffffffffff4")); + + assert_snapshot!(cb01.hexdump(), @"b807000000"); + assert_snapshot!(cb02.hexdump(), @"b8fdffffff"); + assert_snapshot!(cb03.hexdump(), @"41bf03000000"); + assert_snapshot!(cb04.hexdump(), @"89d8"); + assert_snapshot!(cb05.hexdump(), @"89c8"); + assert_snapshot!(cb06.hexdump(), @"8b9380000000"); + assert_snapshot!(cb07.hexdump(), @"488b442404"); + assert_snapshot!(cb08.hexdump(), @"41b834000000"); + assert_snapshot!(cb09.hexdump(), @"49b80000008000000000"); + assert_snapshot!(cb10.hexdump(), @"49b8ffffffffffffffff"); + assert_snapshot!(cb11.hexdump(), @"b834000000"); + assert_snapshot!(cb12.hexdump(), @"48b8020000000000c0ff"); + assert_snapshot!(cb13.hexdump(), @"48b80000008000000000"); + assert_snapshot!(cb14.hexdump(), @"48b8ccffffffffffffff"); + assert_snapshot!(cb15.hexdump(), @"48b8ffffffffffffffff"); + assert_snapshot!(cb16.hexdump(), @"4488c9"); + assert_snapshot!(cb17.hexdump(), @"4889c3"); + assert_snapshot!(cb18.hexdump(), @"4889df"); + assert_snapshot!(cb19.hexdump(), @"40b60b"); + assert_snapshot!(cb20.hexdump(), @"c60424fd"); + assert_snapshot!(cb21.hexdump(), @"48c7470801000000"); + assert_snapshot!(cb22.hexdump(), @"c7400411000000"); + assert_snapshot!(cb23.hexdump(), @"c7400401000080"); + assert_snapshot!(cb24.hexdump(), @"41895814"); + assert_snapshot!(cb25.hexdump(), @"4d8913"); + assert_snapshot!(cb26.hexdump(), @"48c742f8f4ffffff"); } #[test] fn test_movabs() { - check_bytes("49b83400000000000000", |cb| movabs(cb, R8, 0x34)); - check_bytes("49b80000008000000000", |cb| movabs(cb, R8, 0x80000000)); + let cb1 = compile(|cb| movabs(cb, R8, 0x34)); + let cb2 = compile(|cb| movabs(cb, R8, 0x80000000)); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0x34")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0x80000000")); + + assert_snapshot!(cb1.hexdump(), @"49b83400000000000000"); + assert_snapshot!(cb2.hexdump(), @"49b80000008000000000"); } #[test] fn test_mov_unsigned() { // MOV AL, imm8 - check_bytes("b001", |cb| mov(cb, AL, uimm_opnd(1))); - check_bytes("b0ff", |cb| mov(cb, AL, uimm_opnd(u8::MAX.into()))); - + let cb01 = compile(|cb| mov(cb, AL, uimm_opnd(1))); + let cb02 = compile(|cb| mov(cb, AL, uimm_opnd(u8::MAX.into()))); // MOV AX, imm16 - check_bytes("66b80100", |cb| mov(cb, AX, uimm_opnd(1))); - check_bytes("66b8ffff", |cb| mov(cb, AX, uimm_opnd(u16::MAX.into()))); - + let cb03 = compile(|cb| mov(cb, AX, uimm_opnd(1))); + let cb04 = compile(|cb| mov(cb, AX, uimm_opnd(u16::MAX.into()))); // MOV EAX, imm32 - check_bytes("b801000000", |cb| mov(cb, EAX, uimm_opnd(1))); - check_bytes("b8ffffffff", |cb| mov(cb, EAX, uimm_opnd(u32::MAX.into()))); - check_bytes("41b800000000", |cb| mov(cb, R8, uimm_opnd(0))); - check_bytes("41b8ffffffff", |cb| mov(cb, R8, uimm_opnd(0xFF_FF_FF_FF))); - + let cb05 = compile(|cb| mov(cb, EAX, uimm_opnd(1))); + let cb06 = compile(|cb| mov(cb, EAX, uimm_opnd(u32::MAX.into()))); + let cb07 = compile(|cb| mov(cb, R8, uimm_opnd(0))); + let cb08 = compile(|cb| mov(cb, R8, uimm_opnd(0xFF_FF_FF_FF))); // MOV RAX, imm64, will move down into EAX since it fits into 32 bits - check_bytes("b801000000", |cb| mov(cb, RAX, uimm_opnd(1))); - check_bytes("b8ffffffff", |cb| mov(cb, RAX, uimm_opnd(u32::MAX.into()))); - + let cb09 = compile(|cb| mov(cb, RAX, uimm_opnd(1))); + let cb10 = compile(|cb| mov(cb, RAX, uimm_opnd(u32::MAX.into()))); // MOV RAX, imm64, will not move down into EAX since it does not fit into 32 bits - check_bytes("48b80000000001000000", |cb| mov(cb, RAX, uimm_opnd(u32::MAX as u64 + 1))); - check_bytes("48b8ffffffffffffffff", |cb| mov(cb, RAX, uimm_opnd(u64::MAX))); - check_bytes("49b8ffffffffffffffff", |cb| mov(cb, R8, uimm_opnd(u64::MAX))); - + let cb11 = compile(|cb| mov(cb, RAX, uimm_opnd(u32::MAX as u64 + 1))); + let cb12 = compile(|cb| mov(cb, RAX, uimm_opnd(u64::MAX))); + let cb13 = compile(|cb| mov(cb, R8, uimm_opnd(u64::MAX))); // MOV r8, imm8 - check_bytes("41b001", |cb| mov(cb, R8B, uimm_opnd(1))); - check_bytes("41b0ff", |cb| mov(cb, R8B, uimm_opnd(u8::MAX.into()))); - + let cb14 = compile(|cb| mov(cb, R8B, uimm_opnd(1))); + let cb15 = compile(|cb| mov(cb, R8B, uimm_opnd(u8::MAX.into()))); // MOV r16, imm16 - check_bytes("6641b80100", |cb| mov(cb, R8W, uimm_opnd(1))); - check_bytes("6641b8ffff", |cb| mov(cb, R8W, uimm_opnd(u16::MAX.into()))); - + let cb16 = compile(|cb| mov(cb, R8W, uimm_opnd(1))); + let cb17 = compile(|cb| mov(cb, R8W, uimm_opnd(u16::MAX.into()))); // MOV r32, imm32 - check_bytes("41b801000000", |cb| mov(cb, R8D, uimm_opnd(1))); - check_bytes("41b8ffffffff", |cb| mov(cb, R8D, uimm_opnd(u32::MAX.into()))); - + let cb18 = compile(|cb| mov(cb, R8D, uimm_opnd(1))); + let cb19 = compile(|cb| mov(cb, R8D, uimm_opnd(u32::MAX.into()))); // MOV r64, imm64, will move down into 32 bit since it fits into 32 bits - check_bytes("41b801000000", |cb| mov(cb, R8, uimm_opnd(1))); - + let cb20 = compile(|cb| mov(cb, R8, uimm_opnd(1))); // MOV r64, imm64, will not move down into 32 bit since it does not fit into 32 bits - check_bytes("49b8ffffffffffffffff", |cb| mov(cb, R8, uimm_opnd(u64::MAX))); + let cb21 = compile(|cb| mov(cb, R8, uimm_opnd(u64::MAX))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov al, 1")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov al, 0xff")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov ax, 1")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov ax, 0xffff")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 1")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 0xffffffff")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 0")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 0xffffffff")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 1")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 0xffffffff")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0x100000000")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0xffffffffffffffff")); + cb13.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0xffffffffffffffff")); + cb14.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8b, 1")); + cb15.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8b, 0xff")); + cb16.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8w, 1")); + cb17.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8w, 0xffff")); + cb18.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 1")); + cb19.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 0xffffffff")); + cb20.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r8d, 1")); + cb21.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r8, 0xffffffffffffffff")); + + assert_snapshot!(cb01.hexdump(), @"b001"); + assert_snapshot!(cb02.hexdump(), @"b0ff"); + assert_snapshot!(cb03.hexdump(), @"66b80100"); + assert_snapshot!(cb04.hexdump(), @"66b8ffff"); + assert_snapshot!(cb05.hexdump(), @"b801000000"); + assert_snapshot!(cb06.hexdump(), @"b8ffffffff"); + assert_snapshot!(cb07.hexdump(), @"41b800000000"); + assert_snapshot!(cb08.hexdump(), @"41b8ffffffff"); + assert_snapshot!(cb09.hexdump(), @"b801000000"); + assert_snapshot!(cb10.hexdump(), @"b8ffffffff"); + assert_snapshot!(cb11.hexdump(), @"48b80000000001000000"); + assert_snapshot!(cb12.hexdump(), @"48b8ffffffffffffffff"); + assert_snapshot!(cb13.hexdump(), @"49b8ffffffffffffffff"); + assert_snapshot!(cb14.hexdump(), @"41b001"); + assert_snapshot!(cb15.hexdump(), @"41b0ff"); + assert_snapshot!(cb16.hexdump(), @"6641b80100"); + assert_snapshot!(cb17.hexdump(), @"6641b8ffff"); + assert_snapshot!(cb18.hexdump(), @"41b801000000"); + assert_snapshot!(cb19.hexdump(), @"41b8ffffffff"); + assert_snapshot!(cb20.hexdump(), @"41b801000000"); + assert_snapshot!(cb21.hexdump(), @"49b8ffffffffffffffff"); } #[test] fn test_mov_iprel() { - check_bytes("8b0500000000", |cb| mov(cb, EAX, mem_opnd(32, RIP, 0))); - check_bytes("8b0505000000", |cb| mov(cb, EAX, mem_opnd(32, RIP, 5))); + let cb1 = compile(|cb| mov(cb, EAX, mem_opnd(32, RIP, 0))); + let cb2 = compile(|cb| mov(cb, EAX, mem_opnd(32, RIP, 5))); + let cb3 = compile(|cb| mov(cb, RAX, mem_opnd(64, RIP, 0))); + let cb4 = compile(|cb| mov(cb, RAX, mem_opnd(64, RIP, 5))); + let cb5 = compile(|cb| mov(cb, RDI, mem_opnd(64, RIP, 5))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, dword ptr [rip]")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, dword ptr [rip + 5]")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rax, qword ptr [rip]")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rax, qword ptr [rip + 5]")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov rdi, qword ptr [rip + 5]")); - check_bytes("488b0500000000", |cb| mov(cb, RAX, mem_opnd(64, RIP, 0))); - check_bytes("488b0505000000", |cb| mov(cb, RAX, mem_opnd(64, RIP, 5))); - check_bytes("488b3d05000000", |cb| mov(cb, RDI, mem_opnd(64, RIP, 5))); + assert_snapshot!(cb1.hexdump(), @"8b0500000000"); + assert_snapshot!(cb2.hexdump(), @"8b0505000000"); + assert_snapshot!(cb3.hexdump(), @"488b0500000000"); + assert_snapshot!(cb4.hexdump(), @"488b0505000000"); + assert_snapshot!(cb5.hexdump(), @"488b3d05000000"); } #[test] fn test_movsx() { - check_bytes("660fbec0", |cb| movsx(cb, AX, AL)); - check_bytes("0fbed0", |cb| movsx(cb, EDX, AL)); - check_bytes("480fbec3", |cb| movsx(cb, RAX, BL)); - check_bytes("0fbfc8", |cb| movsx(cb, ECX, AX)); - check_bytes("4c0fbed9", |cb| movsx(cb, R11, CL)); - check_bytes("4c6354240c", |cb| movsx(cb, R10, mem_opnd(32, RSP, 12))); - check_bytes("480fbe0424", |cb| movsx(cb, RAX, mem_opnd(8, RSP, 0))); - check_bytes("490fbf5504", |cb| movsx(cb, RDX, mem_opnd(16, R13, 4))); + let cb1 = compile(|cb| movsx(cb, AX, AL)); + let cb2 = compile(|cb| movsx(cb, EDX, AL)); + let cb3 = compile(|cb| movsx(cb, RAX, BL)); + let cb4 = compile(|cb| movsx(cb, ECX, AX)); + let cb5 = compile(|cb| movsx(cb, R11, CL)); + let cb6 = compile(|cb| movsx(cb, R10, mem_opnd(32, RSP, 12))); + let cb7 = compile(|cb| movsx(cb, RAX, mem_opnd(8, RSP, 0))); + let cb8 = compile(|cb| movsx(cb, RDX, mem_opnd(16, R13, 4))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx ax, al")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx edx, al")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx rax, bl")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx ecx, ax")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx r11, cl")); + cb6.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsxd r10, dword ptr [rsp + 0xc]")); + cb7.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx rax, byte ptr [rsp]")); + cb8.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movsx rdx, word ptr [r13 + 4]")); + + assert_snapshot!(cb1.hexdump(), @"660fbec0"); + assert_snapshot!(cb2.hexdump(), @"0fbed0"); + assert_snapshot!(cb3.hexdump(), @"480fbec3"); + assert_snapshot!(cb4.hexdump(), @"0fbfc8"); + assert_snapshot!(cb5.hexdump(), @"4c0fbed9"); + assert_snapshot!(cb6.hexdump(), @"4c6354240c"); + assert_snapshot!(cb7.hexdump(), @"480fbe0424"); + assert_snapshot!(cb8.hexdump(), @"490fbf5504"); } #[test] fn test_nop() { - check_bytes("90", |cb| nop(cb, 1)); - check_bytes("6690", |cb| nop(cb, 2)); - check_bytes("0f1f00", |cb| nop(cb, 3)); - check_bytes("0f1f4000", |cb| nop(cb, 4)); - check_bytes("0f1f440000", |cb| nop(cb, 5)); - check_bytes("660f1f440000", |cb| nop(cb, 6)); - check_bytes("0f1f8000000000", |cb| nop(cb, 7)); - check_bytes("0f1f840000000000", |cb| nop(cb, 8)); - check_bytes("660f1f840000000000", |cb| nop(cb, 9)); - check_bytes("660f1f84000000000090", |cb| nop(cb, 10)); - check_bytes("660f1f8400000000006690", |cb| nop(cb, 11)); - check_bytes("660f1f8400000000000f1f00", |cb| nop(cb, 12)); + let cb01 = compile(|cb| nop(cb, 1)); + let cb02 = compile(|cb| nop(cb, 2)); + let cb03 = compile(|cb| nop(cb, 3)); + let cb04 = compile(|cb| nop(cb, 4)); + let cb05 = compile(|cb| nop(cb, 5)); + let cb06 = compile(|cb| nop(cb, 6)); + let cb07 = compile(|cb| nop(cb, 7)); + let cb08 = compile(|cb| nop(cb, 8)); + let cb09 = compile(|cb| nop(cb, 9)); + let cb10 = compile(|cb| nop(cb, 10)); + let cb11 = compile(|cb| nop(cb, 11)); + let cb12 = compile(|cb| nop(cb, 12)); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop dword ptr [rax]")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop dword ptr [rax]")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop dword ptr [rax + rax]")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop word ptr [rax + rax]")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop dword ptr [rax]")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop dword ptr [rax + rax]")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: nop word ptr [rax + rax]")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: nop word ptr [rax + rax] + 0x9: nop + ")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: nop word ptr [rax + rax] + 0x9: nop + ")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: nop word ptr [rax + rax] + 0x9: nop dword ptr [rax] + ")); + + assert_snapshot!(cb01.hexdump(), @"90"); + assert_snapshot!(cb02.hexdump(), @"6690"); + assert_snapshot!(cb03.hexdump(), @"0f1f00"); + assert_snapshot!(cb04.hexdump(), @"0f1f4000"); + assert_snapshot!(cb05.hexdump(), @"0f1f440000"); + assert_snapshot!(cb06.hexdump(), @"660f1f440000"); + assert_snapshot!(cb07.hexdump(), @"0f1f8000000000"); + assert_snapshot!(cb08.hexdump(), @"0f1f840000000000"); + assert_snapshot!(cb09.hexdump(), @"660f1f840000000000"); + assert_snapshot!(cb10.hexdump(), @"660f1f84000000000090"); + assert_snapshot!(cb11.hexdump(), @"660f1f8400000000006690"); + assert_snapshot!(cb12.hexdump(), @"660f1f8400000000000f1f00"); } #[test] fn test_not() { - check_bytes("66f7d0", |cb| not(cb, AX)); - check_bytes("f7d0", |cb| not(cb, EAX)); - check_bytes("49f71424", |cb| not(cb, mem_opnd(64, R12, 0))); - check_bytes("f794242d010000", |cb| not(cb, mem_opnd(32, RSP, 301))); - check_bytes("f71424", |cb| not(cb, mem_opnd(32, RSP, 0))); - check_bytes("f7542403", |cb| not(cb, mem_opnd(32, RSP, 3))); - check_bytes("f75500", |cb| not(cb, mem_opnd(32, RBP, 0))); - check_bytes("f7550d", |cb| not(cb, mem_opnd(32, RBP, 13))); - check_bytes("48f7d0", |cb| not(cb, RAX)); - check_bytes("49f7d3", |cb| not(cb, R11)); - check_bytes("f710", |cb| not(cb, mem_opnd(32, RAX, 0))); - check_bytes("f716", |cb| not(cb, mem_opnd(32, RSI, 0))); - check_bytes("f717", |cb| not(cb, mem_opnd(32, RDI, 0))); - check_bytes("f75237", |cb| not(cb, mem_opnd(32, RDX, 55))); - check_bytes("f79239050000", |cb| not(cb, mem_opnd(32, RDX, 1337))); - check_bytes("f752c9", |cb| not(cb, mem_opnd(32, RDX, -55))); - check_bytes("f792d5fdffff", |cb| not(cb, mem_opnd(32, RDX, -555))); + let cb01 = compile(|cb| not(cb, AX)); + let cb02 = compile(|cb| not(cb, EAX)); + let cb03 = compile(|cb| not(cb, mem_opnd(64, R12, 0))); + let cb04 = compile(|cb| not(cb, mem_opnd(32, RSP, 301))); + let cb05 = compile(|cb| not(cb, mem_opnd(32, RSP, 0))); + let cb06 = compile(|cb| not(cb, mem_opnd(32, RSP, 3))); + let cb07 = compile(|cb| not(cb, mem_opnd(32, RBP, 0))); + let cb08 = compile(|cb| not(cb, mem_opnd(32, RBP, 13))); + let cb09 = compile(|cb| not(cb, RAX)); + let cb10 = compile(|cb| not(cb, R11)); + let cb11 = compile(|cb| not(cb, mem_opnd(32, RAX, 0))); + let cb12 = compile(|cb| not(cb, mem_opnd(32, RSI, 0))); + let cb13 = compile(|cb| not(cb, mem_opnd(32, RDI, 0))); + let cb14 = compile(|cb| not(cb, mem_opnd(32, RDX, 55))); + let cb15 = compile(|cb| not(cb, mem_opnd(32, RDX, 1337))); + let cb16 = compile(|cb| not(cb, mem_opnd(32, RDX, -55))); + let cb17 = compile(|cb| not(cb, mem_opnd(32, RDX, -555))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not ax")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not eax")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not qword ptr [r12]")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rsp + 0x12d]")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rsp]")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rsp + 3]")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rbp]")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rbp + 0xd]")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not rax")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not r11")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rax]")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rsi]")); + cb13.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rdi]")); + cb14.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rdx + 0x37]")); + cb15.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rdx + 0x539]")); + cb16.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rdx - 0x37]")); + cb17.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: not dword ptr [rdx - 0x22b]")); + + assert_snapshot!(cb01.hexdump(), @"66f7d0"); + assert_snapshot!(cb02.hexdump(), @"f7d0"); + assert_snapshot!(cb03.hexdump(), @"49f71424"); + assert_snapshot!(cb04.hexdump(), @"f794242d010000"); + assert_snapshot!(cb05.hexdump(), @"f71424"); + assert_snapshot!(cb06.hexdump(), @"f7542403"); + assert_snapshot!(cb07.hexdump(), @"f75500"); + assert_snapshot!(cb08.hexdump(), @"f7550d"); + assert_snapshot!(cb09.hexdump(), @"48f7d0"); + assert_snapshot!(cb10.hexdump(), @"49f7d3"); + assert_snapshot!(cb11.hexdump(), @"f710"); + assert_snapshot!(cb12.hexdump(), @"f716"); + assert_snapshot!(cb13.hexdump(), @"f717"); + assert_snapshot!(cb14.hexdump(), @"f75237"); + assert_snapshot!(cb15.hexdump(), @"f79239050000"); + assert_snapshot!(cb16.hexdump(), @"f752c9"); + assert_snapshot!(cb17.hexdump(), @"f792d5fdffff"); } #[test] fn test_or() { - check_bytes("09f2", |cb| or(cb, EDX, ESI)); + let cb = compile(|cb| or(cb, EDX, ESI)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: or edx, esi")); + assert_snapshot!(cb.hexdump(), @"09f2"); } #[test] fn test_pop() { - check_bytes("58", |cb| pop(cb, RAX)); - check_bytes("5b", |cb| pop(cb, RBX)); - check_bytes("5c", |cb| pop(cb, RSP)); - check_bytes("5d", |cb| pop(cb, RBP)); - check_bytes("415c", |cb| pop(cb, R12)); - check_bytes("8f00", |cb| pop(cb, mem_opnd(64, RAX, 0))); - check_bytes("418f00", |cb| pop(cb, mem_opnd(64, R8, 0))); - check_bytes("418f4003", |cb| pop(cb, mem_opnd(64, R8, 3))); - check_bytes("8f44c803", |cb| pop(cb, mem_opnd_sib(64, RAX, RCX, 8, 3))); - check_bytes("418f44c803", |cb| pop(cb, mem_opnd_sib(64, R8, RCX, 8, 3))); + let cb01 = compile(|cb| pop(cb, RAX)); + let cb02 = compile(|cb| pop(cb, RBX)); + let cb03 = compile(|cb| pop(cb, RSP)); + let cb04 = compile(|cb| pop(cb, RBP)); + let cb05 = compile(|cb| pop(cb, R12)); + let cb06 = compile(|cb| pop(cb, mem_opnd(64, RAX, 0))); + let cb07 = compile(|cb| pop(cb, mem_opnd(64, R8, 0))); + let cb08 = compile(|cb| pop(cb, mem_opnd(64, R8, 3))); + let cb09 = compile(|cb| pop(cb, mem_opnd_sib(64, RAX, RCX, 8, 3))); + let cb10 = compile(|cb| pop(cb, mem_opnd_sib(64, R8, RCX, 8, 3))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop rax")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop rbx")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop rsp")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop rbp")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop r12")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop qword ptr [rax]")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop qword ptr [r8]")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop qword ptr [r8 + 3]")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop qword ptr [rax + rcx*8 + 3]")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: pop qword ptr [r8 + rcx*8 + 3]")); + + assert_snapshot!(cb01.hexdump(), @"58"); + assert_snapshot!(cb02.hexdump(), @"5b"); + assert_snapshot!(cb03.hexdump(), @"5c"); + assert_snapshot!(cb04.hexdump(), @"5d"); + assert_snapshot!(cb05.hexdump(), @"415c"); + assert_snapshot!(cb06.hexdump(), @"8f00"); + assert_snapshot!(cb07.hexdump(), @"418f00"); + assert_snapshot!(cb08.hexdump(), @"418f4003"); + assert_snapshot!(cb09.hexdump(), @"8f44c803"); + assert_snapshot!(cb10.hexdump(), @"418f44c803"); } #[test] fn test_push() { - check_bytes("50", |cb| push(cb, RAX)); - check_bytes("53", |cb| push(cb, RBX)); - check_bytes("4154", |cb| push(cb, R12)); - check_bytes("ff30", |cb| push(cb, mem_opnd(64, RAX, 0))); - check_bytes("41ff30", |cb| push(cb, mem_opnd(64, R8, 0))); - check_bytes("41ff7003", |cb| push(cb, mem_opnd(64, R8, 3))); - check_bytes("ff74c803", |cb| push(cb, mem_opnd_sib(64, RAX, RCX, 8, 3))); - check_bytes("41ff74c803", |cb| push(cb, mem_opnd_sib(64, R8, RCX, 8, 3))); + let cb1 = compile(|cb| push(cb, RAX)); + let cb2 = compile(|cb| push(cb, RBX)); + let cb3 = compile(|cb| push(cb, R12)); + let cb4 = compile(|cb| push(cb, mem_opnd(64, RAX, 0))); + let cb5 = compile(|cb| push(cb, mem_opnd(64, R8, 0))); + let cb6 = compile(|cb| push(cb, mem_opnd(64, R8, 3))); + let cb7 = compile(|cb| push(cb, mem_opnd_sib(64, RAX, RCX, 8, 3))); + let cb8 = compile(|cb| push(cb, mem_opnd_sib(64, R8, RCX, 8, 3))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push rax")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push rbx")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push r12")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push qword ptr [rax]")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push qword ptr [r8]")); + cb6.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push qword ptr [r8 + 3]")); + cb7.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push qword ptr [rax + rcx*8 + 3]")); + cb8.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push qword ptr [r8 + rcx*8 + 3]")); + + assert_snapshot!(cb1.hexdump(), @"50"); + assert_snapshot!(cb2.hexdump(), @"53"); + assert_snapshot!(cb3.hexdump(), @"4154"); + assert_snapshot!(cb4.hexdump(), @"ff30"); + assert_snapshot!(cb5.hexdump(), @"41ff30"); + assert_snapshot!(cb6.hexdump(), @"41ff7003"); + assert_snapshot!(cb7.hexdump(), @"ff74c803"); + assert_snapshot!(cb8.hexdump(), @"41ff74c803"); } #[test] fn test_ret() { - check_bytes("c3", ret); + let cb = compile(ret); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ret")); + assert_snapshot!(cb.hexdump(), @"c3"); } #[test] fn test_sal() { - check_bytes("66d1e1", |cb| sal(cb, CX, uimm_opnd(1))); - check_bytes("d1e1", |cb| sal(cb, ECX, uimm_opnd(1))); - check_bytes("c1e505", |cb| sal(cb, EBP, uimm_opnd(5))); - check_bytes("d1642444", |cb| sal(cb, mem_opnd(32, RSP, 68), uimm_opnd(1))); - check_bytes("48d3e1", |cb| sal(cb, RCX, CL)); + let cb1 = compile(|cb| sal(cb, CX, uimm_opnd(1))); + let cb2 = compile(|cb| sal(cb, ECX, uimm_opnd(1))); + let cb3 = compile(|cb| sal(cb, EBP, uimm_opnd(5))); + let cb4 = compile(|cb| sal(cb, mem_opnd(32, RSP, 68), uimm_opnd(1))); + let cb5 = compile(|cb| sal(cb, RCX, CL)); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shl cx, 1")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shl ecx, 1")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shl ebp, 5")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shl dword ptr [rsp + 0x44], 1")); + cb5.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shl rcx, cl")); + + assert_snapshot!(cb1.hexdump(), @"66d1e1"); + assert_snapshot!(cb2.hexdump(), @"d1e1"); + assert_snapshot!(cb3.hexdump(), @"c1e505"); + assert_snapshot!(cb4.hexdump(), @"d1642444"); + assert_snapshot!(cb5.hexdump(), @"48d3e1"); } #[test] fn test_sar() { - check_bytes("d1fa", |cb| sar(cb, EDX, uimm_opnd(1))); + let cb = compile(|cb| sar(cb, EDX, uimm_opnd(1))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sar edx, 1")); + assert_snapshot!(cb.hexdump(), @"d1fa"); } #[test] fn test_shr() { - check_bytes("49c1ee07", |cb| shr(cb, R14, uimm_opnd(7))); + let cb = compile(|cb| shr(cb, R14, uimm_opnd(7))); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: shr r14, 7")); + assert_snapshot!(cb.hexdump(), @"49c1ee07"); } #[test] fn test_sub() { - check_bytes("83e801", |cb| sub(cb, EAX, imm_opnd(1))); - check_bytes("4883e802", |cb| sub(cb, RAX, imm_opnd(2))); + let cb1 = compile(|cb| sub(cb, EAX, imm_opnd(1))); + let cb2 = compile(|cb| sub(cb, RAX, imm_opnd(2))); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub eax, 1")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub rax, 2")); + + assert_snapshot!(cb1.hexdump(), @"83e801"); + assert_snapshot!(cb2.hexdump(), @"4883e802"); } #[test] @@ -374,44 +757,95 @@ fn test_sub() { fn test_sub_uimm_too_large() { // This immediate becomes a different value after // sign extension, so not safe to encode. - check_bytes("ff", |cb| sub(cb, RCX, uimm_opnd(0x8000_0000))); + compile(|cb| sub(cb, RCX, uimm_opnd(0x8000_0000))); } #[test] fn test_test() { - check_bytes("84c0", |cb| test(cb, AL, AL)); - check_bytes("6685c0", |cb| test(cb, AX, AX)); - check_bytes("f6c108", |cb| test(cb, CL, uimm_opnd(8))); - check_bytes("f6c207", |cb| test(cb, DL, uimm_opnd(7))); - check_bytes("f6c108", |cb| test(cb, RCX, uimm_opnd(8))); - check_bytes("f6420808", |cb| test(cb, mem_opnd(8, RDX, 8), uimm_opnd(8))); - check_bytes("f64208ff", |cb| test(cb, mem_opnd(8, RDX, 8), uimm_opnd(255))); - check_bytes("66f7c2ffff", |cb| test(cb, DX, uimm_opnd(0xffff))); - check_bytes("66f74208ffff", |cb| test(cb, mem_opnd(16, RDX, 8), uimm_opnd(0xffff))); - check_bytes("f60601", |cb| test(cb, mem_opnd(8, RSI, 0), uimm_opnd(1))); - check_bytes("f6461001", |cb| test(cb, mem_opnd(8, RSI, 16), uimm_opnd(1))); - check_bytes("f646f001", |cb| test(cb, mem_opnd(8, RSI, -16), uimm_opnd(1))); - check_bytes("854640", |cb| test(cb, mem_opnd(32, RSI, 64), EAX)); - check_bytes("4885472a", |cb| test(cb, mem_opnd(64, RDI, 42), RAX)); - check_bytes("4885c0", |cb| test(cb, RAX, RAX)); - check_bytes("4885f0", |cb| test(cb, RAX, RSI)); - check_bytes("48f74640f7ffffff", |cb| test(cb, mem_opnd(64, RSI, 64), imm_opnd(!0x08))); - check_bytes("48f7464008000000", |cb| test(cb, mem_opnd(64, RSI, 64), imm_opnd(0x08))); - check_bytes("48f7c108000000", |cb| test(cb, RCX, imm_opnd(0x08))); - //check_bytes("48a9f7ffff0f", |cb| test(cb, RAX, imm_opnd(0x0FFFFFF7))); + let cb01 = compile(|cb| test(cb, AL, AL)); + let cb02 = compile(|cb| test(cb, AX, AX)); + let cb03 = compile(|cb| test(cb, CL, uimm_opnd(8))); + let cb04 = compile(|cb| test(cb, DL, uimm_opnd(7))); + let cb05 = compile(|cb| test(cb, RCX, uimm_opnd(8))); + let cb06 = compile(|cb| test(cb, mem_opnd(8, RDX, 8), uimm_opnd(8))); + let cb07 = compile(|cb| test(cb, mem_opnd(8, RDX, 8), uimm_opnd(255))); + let cb08 = compile(|cb| test(cb, DX, uimm_opnd(0xffff))); + let cb09 = compile(|cb| test(cb, mem_opnd(16, RDX, 8), uimm_opnd(0xffff))); + let cb10 = compile(|cb| test(cb, mem_opnd(8, RSI, 0), uimm_opnd(1))); + let cb11 = compile(|cb| test(cb, mem_opnd(8, RSI, 16), uimm_opnd(1))); + let cb12 = compile(|cb| test(cb, mem_opnd(8, RSI, -16), uimm_opnd(1))); + let cb13 = compile(|cb| test(cb, mem_opnd(32, RSI, 64), EAX)); + let cb14 = compile(|cb| test(cb, mem_opnd(64, RDI, 42), RAX)); + let cb15 = compile(|cb| test(cb, RAX, RAX)); + let cb16 = compile(|cb| test(cb, RAX, RSI)); + let cb17 = compile(|cb| test(cb, mem_opnd(64, RSI, 64), imm_opnd(!0x08))); + let cb18 = compile(|cb| test(cb, mem_opnd(64, RSI, 64), imm_opnd(0x08))); + let cb19 = compile(|cb| test(cb, RCX, imm_opnd(0x08))); + + cb01.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test al, al")); + cb02.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test ax, ax")); + cb03.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test cl, 8")); + cb04.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test dl, 7")); + cb05.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test cl, 8")); + cb06.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test byte ptr [rdx + 8], 8")); + cb07.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test byte ptr [rdx + 8], 0xff")); + cb08.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test dx, 0xffff")); + cb09.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test word ptr [rdx + 8], 0xffff")); + cb10.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test byte ptr [rsi], 1")); + cb11.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test byte ptr [rsi + 0x10], 1")); + cb12.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test byte ptr [rsi - 0x10], 1")); + cb13.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test dword ptr [rsi + 0x40], eax")); + cb14.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test qword ptr [rdi + 0x2a], rax")); + cb15.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test rax, rax")); + cb16.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test rax, rsi")); + cb17.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test qword ptr [rsi + 0x40], -9")); + cb18.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test qword ptr [rsi + 0x40], 8")); + cb19.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test rcx, 8")); + + assert_snapshot!(cb01.hexdump(), @"84c0"); + assert_snapshot!(cb02.hexdump(), @"6685c0"); + assert_snapshot!(cb03.hexdump(), @"f6c108"); + assert_snapshot!(cb04.hexdump(), @"f6c207"); + assert_snapshot!(cb05.hexdump(), @"f6c108"); + assert_snapshot!(cb06.hexdump(), @"f6420808"); + assert_snapshot!(cb07.hexdump(), @"f64208ff"); + assert_snapshot!(cb08.hexdump(), @"66f7c2ffff"); + assert_snapshot!(cb09.hexdump(), @"66f74208ffff"); + assert_snapshot!(cb10.hexdump(), @"f60601"); + assert_snapshot!(cb11.hexdump(), @"f6461001"); + assert_snapshot!(cb12.hexdump(), @"f646f001"); + assert_snapshot!(cb13.hexdump(), @"854640"); + assert_snapshot!(cb14.hexdump(), @"4885472a"); + assert_snapshot!(cb15.hexdump(), @"4885c0"); + assert_snapshot!(cb16.hexdump(), @"4885f0"); + assert_snapshot!(cb17.hexdump(), @"48f74640f7ffffff"); + assert_snapshot!(cb18.hexdump(), @"48f7464008000000"); + assert_snapshot!(cb19.hexdump(), @"48f7c108000000"); } #[test] fn test_xchg() { - check_bytes("4891", |cb| xchg(cb, RAX, RCX)); - check_bytes("4995", |cb| xchg(cb, RAX, R13)); - check_bytes("4887d9", |cb| xchg(cb, RCX, RBX)); - check_bytes("4d87f9", |cb| xchg(cb, R9, R15)); + let cb1 = compile(|cb| xchg(cb, RAX, RCX)); + let cb2 = compile(|cb| xchg(cb, RAX, R13)); + let cb3 = compile(|cb| xchg(cb, RCX, RBX)); + let cb4 = compile(|cb| xchg(cb, R9, R15)); + + cb1.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xchg rcx, rax")); + cb2.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xchg r13, rax")); + cb3.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xchg rcx, rbx")); + cb4.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xchg r9, r15")); + + assert_snapshot!(cb1.hexdump(), @"4891"); + assert_snapshot!(cb2.hexdump(), @"4995"); + assert_snapshot!(cb3.hexdump(), @"4887d9"); + assert_snapshot!(cb4.hexdump(), @"4d87f9"); } #[test] fn test_xor() { - check_bytes("31c0", |cb| xor(cb, EAX, EAX)); + let cb = compile(|cb| xor(cb, EAX, EAX)); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xor eax, eax")); + assert_snapshot!(cb.hexdump(), @"31c0"); } #[test] @@ -437,25 +871,3 @@ fn basic_capstone_usage() -> std::result::Result<(), capstone::Error> { )), } } - -#[test] -#[ignore] -#[cfg(feature = "disasm")] -fn block_comments() { - let mut cb = super::CodeBlock::new_dummy(); - - let first_write_ptr = cb.get_write_ptr().raw_addr(&cb); - cb.add_comment("Beginning"); - xor(&mut cb, EAX, EAX); // 2 bytes long - let second_write_ptr = cb.get_write_ptr().raw_addr(&cb); - cb.add_comment("Two bytes in"); - cb.add_comment("Still two bytes in"); - cb.add_comment("Still two bytes in"); // Duplicate, should be ignored - test(&mut cb, mem_opnd(64, RSI, 64), imm_opnd(!0x08)); // 8 bytes long - let third_write_ptr = cb.get_write_ptr().raw_addr(&cb); - cb.add_comment("Ten bytes in"); - - assert_eq!(&vec!( "Beginning".to_string() ), cb.comments_at(first_write_ptr).unwrap()); - assert_eq!(&vec!( "Two bytes in".to_string(), "Still two bytes in".to_string() ), cb.comments_at(second_write_ptr).unwrap()); - assert_eq!(&vec!( "Ten bytes in".to_string() ), cb.comments_at(third_write_ptr).unwrap()); -} diff --git a/zjit/src/assertions.rs b/zjit/src/assertions.rs deleted file mode 100644 index 0dacc938fc5724..00000000000000 --- a/zjit/src/assertions.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// Assert that CodeBlock has the code specified with hex. In addition, if tested with -/// `cargo test --all-features`, it also checks it generates the specified disasm. -#[cfg(test)] -macro_rules! assert_disasm { - ($cb:expr, $hex:expr, $disasm:expr) => { - #[cfg(feature = "disasm")] - { - use $crate::disasm::disasm_addr_range; - use $crate::cruby::unindent; - let disasm = disasm_addr_range( - &$cb, - $cb.get_ptr(0).raw_addr(&$cb), - $cb.get_write_ptr().raw_addr(&$cb), - ); - assert_eq!(unindent(&disasm, false), unindent(&$disasm, true)); - } - assert_eq!(format!("{:x}", $cb), $hex); - }; -} -#[cfg(test)] -pub(crate) use assert_disasm; diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index 50e7074de11222..34e939957f702a 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -78,7 +78,6 @@ impl From for A64Opnd { Opnd::None => panic!( "Attempted to lower an Opnd::None. This often happens when an out operand was not allocated for an instruction because the output of the instruction was not used. Please ensure you are using the output." ), - } } } @@ -809,7 +808,6 @@ impl Assembler } emit_load_value(cb, Assembler::SCRATCH0, dst_addr); br(cb, Assembler::SCRATCH0); - } */ } else { @@ -1425,7 +1423,7 @@ fn merge_three_reg_mov( #[cfg(test)] mod tests { use super::*; - use crate::assertions::assert_disasm; + use insta::assert_snapshot; static TEMP_REGS: [Reg; 5] = [X1_REG, X9_REG, X10_REG, X14_REG, X15_REG]; @@ -1441,11 +1439,12 @@ mod tests { asm.mov(Opnd::Reg(TEMP_REGS[0]), out); asm.compile_with_num_regs(&mut cb, 2); - assert_disasm!(cb, "600080d2207d009be10300aa", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, #3 0x4: mul x0, x9, x0 0x8: mov x1, x0 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"600080d2207d009be10300aa"); } #[test] @@ -1459,10 +1458,11 @@ mod tests { asm.mov(sp, new_sp); asm.compile_with_num_regs(&mut cb, 2); - assert_disasm!(cb, "ff830091ff8300d1", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add sp, sp, #0x20 0x4: sub sp, sp, #0x20 - "); + ")); + assert_snapshot!(cb.hexdump(), @"ff830091ff8300d1"); } #[test] @@ -1474,10 +1474,11 @@ mod tests { asm.add_into(Opnd::Reg(X20_REG), 0x20.into()); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "ff230091948200b1", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add sp, sp, #8 0x4: adds x20, x20, #0x20 - "); + ")); + assert_snapshot!(cb.hexdump(), @"ff230091948200b1"); } #[test] @@ -1488,11 +1489,12 @@ mod tests { asm.load_into(Opnd::Reg(X1_REG), difference); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "000180d2000005ebe10300aa", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, #8 0x4: subs x0, x0, x5 0x8: mov x1, x0 - "); + ")); + assert_snapshot!(cb.hexdump(), @"000180d2000005ebe10300aa"); } #[test] @@ -1503,10 +1505,11 @@ mod tests { asm.cret(ret_val); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "000040f8c0035fd6", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldur x0, [x0] 0x4: ret - "); + ")); + assert_snapshot!(cb.hexdump(), @"000040f8c0035fd6"); } #[test] @@ -1567,7 +1570,7 @@ mod tests { asm.frame_setup(THREE_REGS, 3); asm.frame_teardown(THREE_REGS); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f5831ff8ff8300d1b44f7fa9b5835ef8bf030091fd7bc1a8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x29, x30, [sp, #-0x10]! 0x4: mov x29, sp 0x8: stp x20, x19, [sp, #-0x10]! @@ -1577,7 +1580,8 @@ mod tests { 0x18: ldur x21, [x29, #-0x18] 0x1c: mov sp, x29 0x20: ldp x29, x30, [sp], #0x10 - "); + ")); + assert_snapshot!(cb.hexdump(), @"fd7bbfa9fd030091f44fbfa9f5831ff8ff8300d1b44f7fa9b5835ef8bf030091fd7bc1a8"); } // Test 3 preserved regs (odd), even slot_count @@ -1586,7 +1590,7 @@ mod tests { asm.frame_setup(THREE_REGS, 4); asm.frame_teardown(THREE_REGS); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f5831ff8ffc300d1b44f7fa9b5835ef8bf030091fd7bc1a8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x29, x30, [sp, #-0x10]! 0x4: mov x29, sp 0x8: stp x20, x19, [sp, #-0x10]! @@ -1596,7 +1600,8 @@ mod tests { 0x18: ldur x21, [x29, #-0x18] 0x1c: mov sp, x29 0x20: ldp x29, x30, [sp], #0x10 - "); + ")); + assert_snapshot!(cb.hexdump(), @"fd7bbfa9fd030091f44fbfa9f5831ff8ffc300d1b44f7fa9b5835ef8bf030091fd7bc1a8"); } // Test 4 preserved regs (even), odd slot_count @@ -1606,7 +1611,7 @@ mod tests { asm.frame_setup(FOUR_REGS, 3); asm.frame_teardown(FOUR_REGS); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f657bfa9ff8300d1b44f7fa9b6577ea9bf030091fd7bc1a8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: stp x29, x30, [sp, #-0x10]! 0x4: mov x29, sp 0x8: stp x20, x19, [sp, #-0x10]! @@ -1616,7 +1621,8 @@ mod tests { 0x18: ldp x22, x21, [x29, #-0x20] 0x1c: mov sp, x29 0x20: ldp x29, x30, [sp], #0x10 - "); + ")); + assert_snapshot!(cb.hexdump(), @"fd7bbfa9fd030091f44fbfa9f657bfa9ff8300d1b44f7fa9b6577ea9bf030091fd7bc1a8"); } } @@ -1656,7 +1662,7 @@ mod tests { } asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "e07b40b2e063208b000180d22000a0f2e063208b000083d2e063208be0230891e02308d1e0ff8292e063208b00ff9fd2c0ffbff2e0ffdff2e0fffff2e063208be08361b2e063208b", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" 0x0: orr x0, xzr, #0x7fffffff 0x4: add x0, sp, x0 0x8: mov x0, #8 @@ -1675,7 +1681,8 @@ mod tests { 0x3c: add x0, sp, x0 0x40: orr x0, xzr, #0xffffffff80000000 0x44: add x0, sp, x0 - "); + ")); + assert_snapshot!(cb.hexdump(), @"e07b40b2e063208b000180d22000a0f2e063208b000083d2e063208be0230891e02308d1e0ff8292e063208b00ff9fd2c0ffbff2e0ffdff2e0fffff2e063208be08361b2e063208b"); } #[test] @@ -1690,7 +1697,7 @@ mod tests { asm.store(large_mem, large_mem); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "f0170cd1100240f8100000f8100040f8f1170cd1300200f8f0170cd1100240f8f1170cd1300200f8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub x16, sp, #0x305 0x4: ldur x16, [x16] 0x8: stur x16, [x0] @@ -1701,7 +1708,8 @@ mod tests { 0x1c: ldur x16, [x16] 0x20: sub x17, sp, #0x305 0x24: stur x16, [x17] - "); + ")); + assert_snapshot!(cb.hexdump(), @"f0170cd1100240f8100000f8100040f8f1170cd1300200f8f0170cd1100240f8f1170cd1300200f8"); } #[test] @@ -1717,13 +1725,14 @@ mod tests { let gc_offsets = asm.arm64_emit(&mut cb).unwrap(); assert_eq!(1, gc_offsets.len(), "VALUE source operand should be reported as gc offset"); - assert_disasm!(cb, "50000058030000140010000000000000b00200f8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldr x16, #8 0x4: b #0x10 0x8: .byte 0x00, 0x10, 0x00, 0x00 0xc: .byte 0x00, 0x00, 0x00, 0x00 0x10: stur x16, [x21] - "); + ")); + assert_snapshot!(cb.hexdump(), @"50000058030000140010000000000000b00200f8"); } #[test] @@ -1968,10 +1977,11 @@ mod tests { asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "000001ca400000f8", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: eor x0, x0, x1 0x4: stur x0, [x2] - "); + ")); + assert_snapshot!(cb.hexdump(), @"000001ca400000f8"); } #[test] @@ -2005,9 +2015,8 @@ mod tests { asm.mov(Opnd::Reg(TEMP_REGS[0]), Opnd::mem(64, CFP, 8)); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "618240f8", {" - 0x0: ldur x1, [x19, #8] - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: ldur x1, [x19, #8]")); + assert_snapshot!(cb.hexdump(), @"618240f8"); } #[test] @@ -2018,10 +2027,11 @@ mod tests { asm.mov(Opnd::Reg(TEMP_REGS[0]), Opnd::UImm(0x10000)); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "e1ff9fd2e10370b2", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x1, #0xffff 0x4: orr x1, xzr, #0x10000 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"e1ff9fd2e10370b2"); } #[test] @@ -2032,11 +2042,12 @@ mod tests { asm.mov(Opnd::Reg(TEMP_REGS[0]), out); asm.compile_with_num_regs(&mut cb, 2); - assert_disasm!(cb, "800280d2010080d201b0819a", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x0, #0x14 0x4: mov x1, #0 0x8: csel x1, x0, x1, lt - "}); + ")); + assert_snapshot!(cb.hexdump(), @"800280d2010080d201b0819a"); } #[test] @@ -2074,10 +2085,11 @@ mod tests { asm.mov(Opnd::Reg(TEMP_REGS[0]), out); asm.compile_with_num_regs(&mut cb, 2); - assert_disasm!(cb, "200500b1010400b1", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: adds x0, x9, #1 0x4: adds x1, x0, #1 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"200500b1010400b1"); } #[test] @@ -2091,10 +2103,11 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "100080d200023fd6", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x16, #0 0x4: blr x16 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"100080d200023fd6"); } #[test] @@ -2110,13 +2123,14 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "f00300aae00301aae10310aa100080d200023fd6", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x16, x0 0x4: mov x0, x1 0x8: mov x1, x16 0xc: mov x16, #0 0x10: blr x16 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"f00300aae00301aae10310aa100080d200023fd6"); } #[test] @@ -2133,7 +2147,7 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "f00302aae20303aae30310aaf00300aae00301aae10310aa100080d200023fd6", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x16, x2 0x4: mov x2, x3 0x8: mov x3, x16 @@ -2142,7 +2156,8 @@ mod tests { 0x14: mov x1, x16 0x18: mov x16, #0 0x1c: blr x16 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"f00302aae20303aae30310aaf00300aae00301aae10310aa100080d200023fd6"); } #[test] @@ -2158,14 +2173,14 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "f00300aae00301aae10302aae20310aa100080d200023fd6", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov x16, x0 0x4: mov x0, x1 0x8: mov x1, x2 0xc: mov x2, x16 0x10: mov x16, #0 0x14: blr x16 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"f00300aae00301aae10302aae20310aa100080d200023fd6"); } - } diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs index b18510c29a2972..d857c017dc72d4 100644 --- a/zjit/src/backend/x86_64/mod.rs +++ b/zjit/src/backend/x86_64/mod.rs @@ -605,7 +605,6 @@ impl Assembler scratch } src @ (Opnd::None | Opnd::VReg { .. }) => panic!("Unexpected source operand during x86_emit: {src:?}") - }; mov(cb, dest.into(), src); } @@ -919,7 +918,7 @@ impl Assembler #[cfg(test)] mod tests { - use crate::assertions::assert_disasm; + use insta::assert_snapshot; use super::*; fn setup_asm() -> (Assembler, CodeBlock) { @@ -934,7 +933,11 @@ mod tests { let _ = asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c04881c0ff000000"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: add rax, 0xff + ")); + assert_snapshot!(cb.hexdump(), @"4889c04881c0ff000000"); } #[test] @@ -945,7 +948,12 @@ mod tests { let _ = asm.add(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c01d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: movabs r11, 0xffffffffffff + 0xd: add rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"4889c049bbffffffffffff00004c01d8"); } #[test] @@ -956,7 +964,11 @@ mod tests { let _ = asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c04881e0ff000000"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: and rax, 0xff + ")); + assert_snapshot!(cb.hexdump(), @"4889c04881e0ff000000"); } #[test] @@ -967,7 +979,12 @@ mod tests { let _ = asm.and(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c21d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: movabs r11, 0xffffffffffff + 0xd: and rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"4889c049bbffffffffffff00004c21d8"); } #[test] @@ -977,9 +994,8 @@ mod tests { asm.cmp(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "4881f8ff000000", " - 0x0: cmp rax, 0xff - "); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp rax, 0xff")); + assert_snapshot!(cb.hexdump(), @"4881f8ff000000"); } #[test] @@ -989,10 +1005,11 @@ mod tests { asm.cmp(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "49bbffffffffffff00004c39d8", " - 0x0: movabs r11, 0xffffffffffff - 0xa: cmp rax, r11 - "); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: movabs r11, 0xffffffffffff + 0xa: cmp rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"49bbffffffffffff00004c39d8"); } #[test] @@ -1002,9 +1019,8 @@ mod tests { asm.cmp(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "4883f8ff", " - 0x0: cmp rax, -1 - "); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp rax, -1")); + assert_snapshot!(cb.hexdump(), @"4883f8ff"); } #[test] @@ -1016,7 +1032,8 @@ mod tests { asm.cmp(shape_opnd, Opnd::UImm(0xF000)); asm.compile_with_num_regs(&mut cb, 0); - assert_eq!(format!("{:x}", cb), "6681780600f0"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp word ptr [rax + 6], 0xf000")); + assert_snapshot!(cb.hexdump(), @"6681780600f0"); } #[test] @@ -1028,7 +1045,8 @@ mod tests { asm.cmp(shape_opnd, Opnd::UImm(0xF000_0000)); asm.compile_with_num_regs(&mut cb, 0); - assert_eq!(format!("{:x}", cb), "817804000000f0"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp dword ptr [rax + 4], 0xf0000000")); + assert_snapshot!(cb.hexdump(), @"817804000000f0"); } #[test] @@ -1039,7 +1057,11 @@ mod tests { let _ = asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c04881c8ff000000"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: or rax, 0xff + ")); + assert_snapshot!(cb.hexdump(), @"4889c04881c8ff000000"); } #[test] @@ -1050,7 +1072,12 @@ mod tests { let _ = asm.or(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c09d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: movabs r11, 0xffffffffffff + 0xd: or rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"4889c049bbffffffffffff00004c09d8"); } #[test] @@ -1061,7 +1088,11 @@ mod tests { let _ = asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c04881e8ff000000"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: sub rax, 0xff + ")); + assert_snapshot!(cb.hexdump(), @"4889c04881e8ff000000"); } #[test] @@ -1072,7 +1103,12 @@ mod tests { let _ = asm.sub(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c29d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: movabs r11, 0xffffffffffff + 0xd: sub rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"4889c049bbffffffffffff00004c29d8"); } #[test] @@ -1082,9 +1118,8 @@ mod tests { asm.test(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "48f7c0ff000000", " - 0x0: test rax, 0xff - "); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: test rax, 0xff")); + assert_snapshot!(cb.hexdump(), @"48f7c0ff000000"); } #[test] @@ -1094,7 +1129,11 @@ mod tests { asm.test(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 0); - assert_eq!(format!("{:x}", cb), "49bbffffffffffff00004c85d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: movabs r11, 0xffffffffffff + 0xa: test rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"49bbffffffffffff00004c85d8"); } #[test] @@ -1105,7 +1144,11 @@ mod tests { let _ = asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c04881f0ff000000"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: xor rax, 0xff + ")); + assert_snapshot!(cb.hexdump(), @"4889c04881f0ff000000"); } #[test] @@ -1116,7 +1159,12 @@ mod tests { let _ = asm.xor(Opnd::Reg(RAX_REG), Opnd::UImm(0xFFFF_FFFF_FFFF)); asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4889c049bbffffffffffff00004c31d8"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, rax + 0x3: movabs r11, 0xffffffffffff + 0xd: xor rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"4889c049bbffffffffffff00004c31d8"); } #[test] @@ -1127,9 +1175,8 @@ mod tests { asm.mov(SP, sp); // should be merged to lea asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "488d5b08", {" - 0x0: lea rbx, [rbx + 8] - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: lea rbx, [rbx + 8]")); + assert_snapshot!(cb.hexdump(), @"488d5b08"); } #[test] @@ -1141,10 +1188,11 @@ mod tests { asm.mov(Opnd::mem(64, SP, 0), sp); // should NOT be merged to lea asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "488d4308488903", {" - 0x0: lea rax, [rbx + 8] - 0x4: mov qword ptr [rbx], rax - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: movabs r11, 0xffffffffffff + 0xa: cmp rax, r11 + ")); + assert_snapshot!(cb.hexdump(), @"49bbffffffffffff00004c39d8"); } #[test] @@ -1158,7 +1206,15 @@ mod tests { asm.mov(Opnd::Reg(RAX_REG), result); asm.compile_with_num_regs(&mut cb, 2); - assert_eq!(format!("{:x}", cb), "488b43084885c0b814000000b900000000480f45c14889c0"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov rax, qword ptr [rbx + 8] + 0x4: test rax, rax + 0x7: mov eax, 0x14 + 0xc: mov ecx, 0 + 0x11: cmovne rax, rcx + 0x15: mov rax, rax + ")); + assert_snapshot!(cb.hexdump(), @"488b43084885c0b814000000b900000000480f45c14889c0"); } #[test] @@ -1169,9 +1225,8 @@ mod tests { asm.mov(CFP, sp); // should be merged to add asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "4983c540", {" - 0x0: add r13, 0x40 - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983c540"); } #[test] @@ -1181,9 +1236,8 @@ mod tests { asm.add_into(CFP, Opnd::UImm(0x40)); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "4983c540", {" - 0x0: add r13, 0x40 - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: add r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983c540"); } #[test] @@ -1194,9 +1248,8 @@ mod tests { asm.mov(CFP, sp); // should be merged to add asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "4983ed40", {" - 0x0: sub r13, 0x40 - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983ed40"); } #[test] @@ -1206,9 +1259,8 @@ mod tests { asm.sub_into(CFP, Opnd::UImm(0x40)); asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "4983ed40", {" - 0x0: sub r13, 0x40 - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: sub r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983ed40"); } #[test] @@ -1219,7 +1271,8 @@ mod tests { asm.mov(CFP, sp); // should be merged to add asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4983e540"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: and r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983e540"); } #[test] @@ -1230,7 +1283,8 @@ mod tests { asm.mov(CFP, sp); // should be merged to add asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4983cd40"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: or r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983cd40"); } #[test] @@ -1241,7 +1295,8 @@ mod tests { asm.mov(CFP, sp); // should be merged to add asm.compile_with_num_regs(&mut cb, 1); - assert_eq!(format!("{:x}", cb), "4983f540"); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: xor r13, 0x40")); + assert_snapshot!(cb.hexdump(), @"4983f540"); } #[test] @@ -1255,10 +1310,11 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "b800000000ffd0", {" - 0x0: mov eax, 0 - 0x5: call rax - "}); + cb.with_disasm(|disasm| assert_snapshot!(disasm, @r" + 0x0: mov eax, 0 + 0x5: call rax + ")); + assert_snapshot!(cb.hexdump(), @"b800000000ffd0"); } #[test] @@ -1274,13 +1330,14 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "4989f34889fe4c89dfb800000000ffd0", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r11, rsi 0x3: mov rsi, rdi 0x6: mov rdi, r11 0x9: mov eax, 0 0xe: call rax - "}); + ")); + assert_snapshot!(cb.hexdump(), @"4989f34889fe4c89dfb800000000ffd0"); } #[test] @@ -1297,7 +1354,7 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "4989f34889fe4c89df4989cb4889d14c89dab800000000ffd0", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r11, rsi 0x3: mov rsi, rdi 0x6: mov rdi, r11 @@ -1306,7 +1363,8 @@ mod tests { 0xf: mov rdx, r11 0x12: mov eax, 0 0x17: call rax - "}); + ")); + assert_snapshot!(cb.hexdump(), @"4989f34889fe4c89df4989cb4889d14c89dab800000000ffd0"); } #[test] @@ -1322,14 +1380,15 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, ALLOC_REGS.len()); - assert_disasm!(cb, "4989f34889d64889fa4c89dfb800000000ffd0", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov r11, rsi 0x3: mov rsi, rdx 0x6: mov rdx, rdi 0x9: mov rdi, r11 0xc: mov eax, 0 0x11: call rax - "}); + ")); + assert_snapshot!(cb.hexdump(), @"4989f34889d64889fa4c89dfb800000000ffd0"); } #[test] @@ -1349,7 +1408,7 @@ mod tests { ]); asm.compile_with_num_regs(&mut cb, 3); - assert_disasm!(cb, "b801000000b902000000ba030000004889c74889ce4989cb4889d14c89dab800000000ffd0", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov eax, 1 0x5: mov ecx, 2 0xa: mov edx, 3 @@ -1360,7 +1419,8 @@ mod tests { 0x1b: mov rdx, r11 0x1e: mov eax, 0 0x23: call rax - "}); + ")); + assert_snapshot!(cb.hexdump(), @"b801000000b902000000ba030000004889c74889ce4989cb4889d14c89dab800000000ffd0"); } #[test] @@ -1377,12 +1437,13 @@ mod tests { asm.compile_with_num_regs(&mut cb, 1); - assert_disasm!(cb, "48837b1001bf04000000480f4f3b48893b", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: cmp qword ptr [rbx + 0x10], 1 0x5: mov edi, 4 0xa: cmovg rdi, qword ptr [rbx] 0xe: mov qword ptr [rbx], rdi - "}); + ")); + assert_snapshot!(cb.hexdump(), @"48837b1001bf04000000480f4f3b48893b"); } #[test] @@ -1396,12 +1457,14 @@ mod tests { asm.compile_with_num_regs(&mut cb, 3); - assert_disasm!(cb, "48b830198dc8227f0000b904000000480f44c1488903", {" + #[cfg(feature = "disasm")] + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs rax, 0x7f22c88d1930 0xa: mov ecx, 4 0xf: cmove rax, rcx 0x13: mov qword ptr [rbx], rax - "}); + ")); + assert_snapshot!(cb.hexdump(), @"48b830198dc8227f0000b904000000480f44c1488903"); } #[test] @@ -1413,10 +1476,12 @@ mod tests { asm.mov(shape_opnd, Opnd::Imm(0x8000_0001)); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "c70001000080c70001000080", {" + #[cfg(feature = "disasm")] + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: mov dword ptr [rax], 0x80000001 0x6: mov dword ptr [rax], 0x80000001 - "}); + ")); + assert_snapshot!(cb.hexdump(), @"c70001000080c70001000080"); } #[test] @@ -1431,7 +1496,7 @@ mod tests { asm.frame_teardown(&[]); asm.compile_with_num_regs(&mut cb, 0); - assert_disasm!(cb, "554889e541555341544883ec084c8b6df8488b5df04c8b65e84889ec5dc3554889e54883ec304889ec5d", {" + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: push rbp 0x1: mov rbp, rsp 0x4: push r13 @@ -1449,7 +1514,8 @@ mod tests { 0x22: sub rsp, 0x30 0x26: mov rsp, rbp 0x29: pop rbp - "}); + ")); + assert_snapshot!(cb.hexdump(), @"554889e541555341544883ec084c8b6df8488b5df04c8b65e84889ec5dc3554889e54883ec304889ec5d"); } #[test] @@ -1463,9 +1529,10 @@ mod tests { let gc_offsets = asm.x86_emit(&mut cb).unwrap(); assert_eq!(1, gc_offsets.len(), "VALUE source operand should be reported as gc offset"); - assert_disasm!(cb, "49bb00100000000000004c891b", " + cb.with_disasm(|disasm| assert_snapshot!(disasm, @" 0x0: movabs r11, 0x1000 0xa: mov qword ptr [rbx], r11 - "); + ")); + assert_snapshot!(cb.hexdump(), @"49bb00100000000000004c891b"); } } diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 165e68c791e190..03228db0bb7c01 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -346,7 +346,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::Const { val: Const::Value(val) } => gen_const(*val), Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"), Insn::NewArray { elements, state } => gen_new_array(asm, opnds!(elements), &function.frame_state(*state)), - Insn::NewHash { elements, state } => gen_new_hash(jit, asm, elements, &function.frame_state(*state)), + Insn::NewHash { elements, state } => gen_new_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), Insn::NewRange { low, high, flag, state } => gen_new_range(jit, asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)), @@ -532,6 +532,9 @@ fn gen_defined(jit: &JITState, asm: &mut Assembler, op_type: usize, obj: VALUE, /// We generate this instruction with level=0 only when the local variable is on the heap, so we /// can't optimize the level=0 case using the SP register. fn gen_getlocal_with_ep(asm: &mut Assembler, local_ep_offset: u32, level: u32) -> lir::Opnd { + if level > 0 { + gen_incr_counter(asm, Counter::vm_read_from_parent_iseq_local_count); + } let ep = gen_get_ep(asm, level); let offset = -(SIZEOF_VALUE_I32 * i32::try_from(local_ep_offset).unwrap_or_else(|_| panic!("Could not convert local_ep_offset {local_ep_offset} to i32"))); asm.load(Opnd::mem(64, ep, offset)) @@ -541,6 +544,9 @@ fn gen_getlocal_with_ep(asm: &mut Assembler, local_ep_offset: u32, level: u32) - /// We generate this instruction with level=0 only when the local variable is on the heap, so we /// can't optimize the level=0 case using the SP register. fn gen_setlocal_with_ep(asm: &mut Assembler, val: Opnd, val_type: Type, local_ep_offset: u32, level: u32) { + if level > 0 { + gen_incr_counter(asm, Counter::vm_write_to_parent_iseq_local_count); + } let ep = gen_get_ep(asm, level); // When we've proved that we're writing an immediate, @@ -1253,7 +1259,7 @@ fn gen_new_array( fn gen_new_hash( jit: &mut JITState, asm: &mut Assembler, - elements: &[(InsnId, InsnId)], + elements: Vec, state: &FrameState, ) -> lir::Opnd { gen_prepare_non_leaf_call(jit, asm, state); @@ -1262,19 +1268,10 @@ fn gen_new_hash( let new_hash = asm_ccall!(asm, rb_hash_new_with_size, lir::Opnd::Imm(cap)); if !elements.is_empty() { - let mut pairs = Vec::new(); - for (key_id, val_id) in elements.iter() { - let key = jit.get_opnd(*key_id); - let val = jit.get_opnd(*val_id); - pairs.push(key); - pairs.push(val); - } - - let argv = gen_push_opnds(jit, asm, &pairs); - let argc = (elements.len() * 2) as ::std::os::raw::c_long; - asm_ccall!(asm, rb_hash_bulk_insert, lir::Opnd::Imm(argc), argv, new_hash); + let argv = gen_push_opnds(jit, asm, &elements); + asm_ccall!(asm, rb_hash_bulk_insert, elements.len().into(), argv, new_hash); - gen_pop_opnds(asm, &pairs); + gen_pop_opnds(asm, &elements); } new_hash @@ -1317,9 +1314,16 @@ fn gen_object_alloc_class(asm: &mut Assembler, class: VALUE, state: &FrameState) // Allocating an object for a known class with default allocator is leaf; see doc for // `ObjectAllocClass`. gen_prepare_leaf_call_with_gc(asm, state); - assert!(unsafe { rb_zjit_class_has_default_allocator(class) }, "class must have default allocator"); - // TODO(max): inline code to allocate an instance - asm_ccall!(asm, rb_class_allocate_instance, class.into()) + if unsafe { rb_zjit_class_has_default_allocator(class) } { + // TODO(max): inline code to allocate an instance + asm_ccall!(asm, rb_class_allocate_instance, class.into()) + } else { + assert!(class_has_leaf_allocator(class), "class passed to ObjectAllocClass must have a leaf allocator"); + let alloc_func = unsafe { rb_zjit_class_get_alloc_func(class) }; + assert!(alloc_func.is_some(), "class {} passed to ObjectAllocClass must have an allocator", get_class_name(class)); + asm_comment!(asm, "call allocator for class {}", get_class_name(class)); + asm.ccall(alloc_func.unwrap() as *const u8, vec![class.into()]) + } } /// Compile code that exits from JIT code with a return value @@ -2009,8 +2013,12 @@ fn gen_push_opnds(jit: &mut JITState, asm: &mut Assembler, opnds: &[Opnd]) -> li let frame_size = aligned_stack_bytes(jit.c_stack_slots); let allocation_size = aligned_stack_bytes(n); - asm_comment!(asm, "allocate {} bytes on C stack for {} values", allocation_size, n); - asm.sub_into(NATIVE_STACK_PTR, allocation_size.into()); + if n != 0 { + asm_comment!(asm, "allocate {} bytes on C stack for {} values", allocation_size, n); + asm.sub_into(NATIVE_STACK_PTR, allocation_size.into()); + } else { + asm_comment!(asm, "no opnds to allocate"); + } // Calculate the total offset from NATIVE_BASE_PTR to our buffer let total_offset_from_base = (frame_size + allocation_size) as i32; @@ -2027,6 +2035,11 @@ fn gen_push_opnds(jit: &mut JITState, asm: &mut Assembler, opnds: &[Opnd]) -> li } fn gen_pop_opnds(asm: &mut Assembler, opnds: &[Opnd]) { + if opnds.is_empty() { + asm_comment!(asm, "no opnds to restore"); + return + } + asm_comment!(asm, "restore C stack pointer"); let allocation_size = aligned_stack_bytes(opnds.len()); asm.add_into(NATIVE_STACK_PTR, allocation_size.into()); diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 374faa6d972543..82d0582da40fd5 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1218,6 +1218,19 @@ pub fn get_class_name(class: VALUE) -> String { }).unwrap_or_else(|| "Unknown".to_string()) } +pub fn class_has_leaf_allocator(class: VALUE) -> bool { + // empty_hash_alloc + if class == unsafe { rb_cHash } { return true; } + // empty_ary_alloc + if class == unsafe { rb_cArray } { return true; } + // empty_str_alloc + if class == unsafe { rb_cString } { return true; } + // rb_reg_s_alloc + if class == unsafe { rb_cRegexp } { return true; } + // rb_class_allocate_instance + unsafe { rb_zjit_class_has_default_allocator(class) } +} + /// Interned ID values for Ruby symbols and method names. /// See [type@crate::cruby::ID] and usages outside of ZJIT. pub(crate) mod ids { diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index dfdb6c0f2915e2..5a06d99f9ee661 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -948,6 +948,7 @@ unsafe extern "C" { recv: VALUE, ) -> *const rb_callable_method_entry_struct; pub fn rb_zjit_class_initialized_p(klass: VALUE) -> bool; + pub fn rb_zjit_class_get_alloc_func(klass: VALUE) -> rb_alloc_func_t; pub fn rb_zjit_class_has_default_allocator(klass: VALUE) -> bool; pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 48cbd423b3cd95..7305bc007d934f 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -481,6 +481,7 @@ pub enum MethodType { Optimized, Missing, Refined, + Null, } impl From for MethodType { @@ -548,7 +549,7 @@ pub enum Insn { ToNewArray { val: InsnId, state: InsnId }, NewArray { elements: Vec, state: InsnId }, /// NewHash contains a vec of (key, value) pairs - NewHash { elements: Vec<(InsnId,InsnId)>, state: InsnId }, + NewHash { elements: Vec, state: InsnId }, NewRange { low: InsnId, high: InsnId, flag: RangeType, state: InsnId }, NewRangeFixnum { low: InsnId, high: InsnId, flag: RangeType, state: InsnId }, ArrayDup { val: InsnId, state: InsnId }, @@ -815,9 +816,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::NewHash { elements, .. } => { write!(f, "NewHash")?; let mut prefix = " "; - for (key, value) in elements { - write!(f, "{prefix}{key}: {value}")?; - prefix = ", "; + for chunk in elements.chunks(2) { + if let [key, value] = chunk { + write!(f, "{prefix}{key}: {value}")?; + prefix = ", "; + } } Ok(()) } @@ -1461,13 +1464,7 @@ impl Function { &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, &DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state }, &NewArray { ref elements, state } => NewArray { elements: find_vec!(elements), state: find!(state) }, - &NewHash { ref elements, state } => { - let mut found_elements = vec![]; - for &(key, value) in elements { - found_elements.push((find!(key), find!(value))); - } - NewHash { elements: found_elements, state: find!(state) } - } + &NewHash { ref elements, state } => NewHash { elements: find_vec!(elements), state: find!(state) }, &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, &NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) }, &ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) }, @@ -1852,6 +1849,9 @@ impl Function { // Do method lookup let mut cme = unsafe { rb_callable_method_entry(klass, mid) }; if cme.is_null() { + if let Insn::SendWithoutBlock { def_type: insn_def_type, .. } = &mut self.insns[insn_id.0] { + *insn_def_type = Some(MethodType::Null); + } self.push_insn_id(block, insn_id); continue; } // Load an overloaded cme if applicable. See vm_search_cc(). @@ -1864,6 +1864,9 @@ impl Function { // TODO(max): Handle other kinds of parameter passing let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; if !can_direct_send(iseq) { + if let Insn::SendWithoutBlock { def_type: insn_def_type, .. } = &mut self.insns[insn_id.0] { + *insn_def_type = Some(MethodType::from(def_type)); + } self.push_insn_id(block, insn_id); continue; } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); @@ -1967,15 +1970,16 @@ impl Function { // a singleton class, or has a custom allocator, ObjectAlloc might raise an // exception or run arbitrary code. // - // We also need to check if the class is initialized or a singleton before trying to read the allocator, otherwise it might raise. + // We also need to check if the class is initialized or a singleton before + // trying to read the allocator, otherwise it might raise. if !unsafe { rb_zjit_class_initialized_p(class) } { self.push_insn_id(block, insn_id); continue; } if unsafe { rb_zjit_singleton_class_p(class) } { self.push_insn_id(block, insn_id); continue; } - if !unsafe { rb_zjit_class_has_default_allocator(class) } { - // Custom or NULL allocator; could run arbitrary code. + if !class_has_leaf_allocator(class) { + // Custom, known unsafe, or NULL allocator; could run arbitrary code. self.push_insn_id(block, insn_id); continue; } let replacement = self.push_insn(block, Insn::ObjectAllocClass { class, state }); @@ -2362,17 +2366,11 @@ impl Function { worklist.push_back(state); } &Insn::ArrayMax { ref elements, state } + | &Insn::NewHash { ref elements, state } | &Insn::NewArray { ref elements, state } => { worklist.extend(elements); worklist.push_back(state); } - &Insn::NewHash { ref elements, state } => { - for &(key, value) in elements { - worklist.push_back(key); - worklist.push_back(value); - } - worklist.push_back(state); - } &Insn::NewRange { low, high, state, .. } | &Insn::NewRangeFixnum { low, high, state, .. } => { worklist.push_back(low); @@ -3423,7 +3421,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { for _ in 0..(count/2) { let value = state.stack_pop()?; let key = state.stack_pop()?; - elements.push((key, value)); + elements.push(value); + elements.push(key); } elements.reverse(); state.stack_push(fun.push_insn(block, Insn::NewHash { elements, state: exit_id })); @@ -3690,31 +3689,58 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, def_type: None, state: exit_id }); state.stack_push(send); } - YARVINSN_opt_hash_freeze | - YARVINSN_opt_ary_freeze | - YARVINSN_opt_str_freeze | - YARVINSN_opt_str_uminus => { - // NB: these instructions have the recv for the call at get_arg(0) - let cd: *const rb_call_data = get_arg(pc, 1).as_ptr(); - let call_info = unsafe { rb_get_call_data_ci(cd) }; - let flags = unsafe { rb_vm_ci_flag(call_info) }; - if let Err(call_type) = unhandled_call_type(flags) { - // Can't handle the call type; side-exit into the interpreter - let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledCallType(call_type) }); + YARVINSN_opt_hash_freeze => { + let klass = HASH_REDEFINED_OP_FLAG; + let bop = BOP_FREEZE; + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + if unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, klass) } { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass, bop }, state: exit_id }); + let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); + state.stack_push(recv); + } else { + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass, bop }) }); break; // End the block } - let argc = unsafe { vm_ci_argc((*cd).ci) }; - let name = insn_name(opcode as usize); - assert_eq!(0, argc, "{name} should not have args"); - let args = vec![]; - + } + YARVINSN_opt_ary_freeze => { + let klass = ARRAY_REDEFINED_OP_FLAG; + let bop = BOP_FREEZE; let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, def_type: None, state: exit_id }); - state.stack_push(send); + if unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, klass) } { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass, bop }, state: exit_id }); + let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); + state.stack_push(recv); + } else { + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass, bop }) }); + break; // End the block + } + } + YARVINSN_opt_str_freeze => { + let klass = STRING_REDEFINED_OP_FLAG; + let bop = BOP_FREEZE; + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + if unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, klass) } { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass, bop }, state: exit_id }); + let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); + state.stack_push(recv); + } else { + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass, bop }) }); + break; // End the block + } + } + YARVINSN_opt_str_uminus => { + let klass = STRING_REDEFINED_OP_FLAG; + let bop = BOP_UMINUS; + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); + if unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, klass) } { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::BOPRedefined { klass, bop }, state: exit_id }); + let recv = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); + state.stack_push(recv); + } else { + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::PatchPoint(Invariant::BOPRedefined { klass, bop }) }); + break; // End the block + } } - YARVINSN_leave => { let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state }); fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); @@ -4791,13 +4817,29 @@ mod tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v6:BasicObject = SendWithoutBlock v5, :freeze + PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts Return v6 "); } + #[test] + fn test_opt_hash_freeze_rewritten() { + eval(" + class Hash + def freeze; 5; end + end + def test = {}.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_hash_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(v0:BasicObject): + SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + #[test] fn test_opt_ary_freeze() { eval(" @@ -4807,13 +4849,29 @@ mod tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v6:BasicObject = SendWithoutBlock v5, :freeze + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts Return v6 "); } + #[test] + fn test_opt_ary_freeze_rewritten() { + eval(" + class Array + def freeze; 5; end + end + def test = [].freeze + "); + assert_contains_opcode("test", YARVINSN_opt_ary_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(v0:BasicObject): + SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + #[test] fn test_opt_str_freeze() { eval(" @@ -4823,13 +4881,29 @@ mod tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v6:BasicObject = SendWithoutBlock v5, :freeze + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts Return v6 "); } + #[test] + fn test_opt_str_freeze_rewritten() { + eval(" + class String + def freeze; 5; end + end + def test = ''.freeze + "); + assert_contains_opcode("test", YARVINSN_opt_str_freeze); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(v0:BasicObject): + SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE)) + "); + } + #[test] fn test_opt_str_uminus() { eval(" @@ -4839,13 +4913,29 @@ mod tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v6:BasicObject = SendWithoutBlock v5, :-@ + PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts Return v6 "); } + #[test] + fn test_opt_str_uminus_rewritten() { + eval(" + class String + def -@; 5; end + end + def test = -'' + "); + assert_contains_opcode("test", YARVINSN_opt_str_uminus); + assert_snapshot!(hir_string("test"), @r" + fn test@:5: + bb0(v0:BasicObject): + SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS)) + "); + } + #[test] fn test_setlocal_getlocal() { eval(" @@ -7325,11 +7415,11 @@ mod opt_tests { fn test@:3: bb0(v0:BasicObject): v1:NilClass = Const Value(nil) - v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v7:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v8:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v10:StringExact = StringCopy v8 - v12:RangeExact = NewRange v6 NewRangeInclusive v10 + v12:RangeExact = NewRange v7 NewRangeInclusive v10 PatchPoint NoEPEscape(test) v17:Fixnum[0] = Const Value(0) CheckInterrupts @@ -8297,6 +8387,165 @@ mod opt_tests { "); } + #[test] + fn test_opt_new_object() { + eval(" + def test = Object.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Object) + v34:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Object@0x1008, new@0x1010, cme:0x1018) + v37:HeapObject[class_exact:Object] = ObjectAllocClass VALUE(0x1008) + PatchPoint MethodRedefined(Object@0x1008, initialize@0x1040, cme:0x1048) + v39:NilClass = CCall initialize@0x1070, v37 + CheckInterrupts + CheckInterrupts + Return v37 + "); + } + + #[test] + fn test_opt_new_basic_object() { + eval(" + def test = BasicObject.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, BasicObject) + v34:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1010, cme:0x1018) + v37:HeapObject[class_exact:BasicObject] = ObjectAllocClass VALUE(0x1008) + PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1040, cme:0x1048) + v39:NilClass = CCall initialize@0x1070, v37 + CheckInterrupts + CheckInterrupts + Return v37 + "); + } + + #[test] + fn test_opt_new_hash() { + eval(" + def test = Hash.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Hash) + v34:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Hash@0x1008, new@0x1010, cme:0x1018) + v37:HashExact = ObjectAllocClass VALUE(0x1008) + v12:BasicObject = SendWithoutBlock v37, :initialize + CheckInterrupts + CheckInterrupts + Return v37 + "); + } + + #[test] + fn test_opt_new_array() { + eval(" + def test = Array.new 1 + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Array) + v36:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + v7:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Array@0x1008, new@0x1010, cme:0x1018) + PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) + v45:BasicObject = CCallVariadic new@0x1048, v36, v7 + CheckInterrupts + Return v45 + "); + } + + #[test] + fn test_opt_new_set() { + eval(" + def test = Set.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Set) + v34:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + PatchPoint MethodRedefined(Set@0x1008, new@0x1010, cme:0x1018) + v10:HeapObject = ObjectAlloc v34 + PatchPoint MethodRedefined(Set@0x1008, initialize@0x1040, cme:0x1048) + v39:BasicObject = CCallVariadic initialize@0x1070, v10 + CheckInterrupts + CheckInterrupts + Return v10 + "); + } + + #[test] + fn test_opt_new_string() { + eval(" + def test = String.new + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, String) + v34:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + PatchPoint MethodRedefined(String@0x1008, new@0x1010, cme:0x1018) + PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) + v43:BasicObject = CCallVariadic new@0x1048, v34 + CheckInterrupts + Return v43 + "); + } + + #[test] + fn test_opt_new_regexp() { + eval(" + def test = Regexp.new '' + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v0:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, Regexp) + v38:Class[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v6:NilClass = Const Value(nil) + v7:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v9:StringExact = StringCopy v7 + PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) + v41:HeapObject[class_exact:Regexp] = ObjectAllocClass VALUE(0x1008) + PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) + v44:BasicObject = CCallVariadic initialize@0x1078, v41, v9 + CheckInterrupts + CheckInterrupts + Return v41 + "); + } + #[test] fn test_opt_length() { eval(" @@ -8384,10 +8633,10 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v5 + Return v6 "); } @@ -8402,10 +8651,7 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:5: bb0(v0:BasicObject): - v5:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v6:BasicObject = SendWithoutBlock v5, :freeze - CheckInterrupts - Return v6 + SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) "); } @@ -8417,11 +8663,11 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts - Return v5 + Return v6 "); } @@ -8465,10 +8711,10 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v5 + Return v6 "); } @@ -8480,11 +8726,11 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts - Return v5 + Return v6 "); } @@ -8528,10 +8774,10 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v5 + Return v6 "); } @@ -8543,11 +8789,11 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts - Return v5 + Return v6 "); } @@ -8593,10 +8839,10 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) CheckInterrupts - Return v5 + Return v6 "); } @@ -8608,11 +8854,11 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) CheckInterrupts - Return v5 + Return v6 "); } @@ -8783,13 +9029,13 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v7:Fixnum[1] = Const Value(1) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF) - v19:Fixnum[5] = Const Value(5) + v18:Fixnum[5] = Const Value(5) CheckInterrupts - Return v19 + Return v18 "); } @@ -8801,13 +9047,13 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v7:Fixnum[-3] = Const Value(-3) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF) - v19:Fixnum[4] = Const Value(4) + v18:Fixnum[4] = Const Value(4) CheckInterrupts - Return v19 + Return v18 "); } @@ -8819,13 +9065,13 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v7:Fixnum[-10] = Const Value(-10) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF) - v19:NilClass = Const Value(nil) + v18:NilClass = Const Value(nil) CheckInterrupts - Return v19 + Return v18 "); } @@ -8837,13 +9083,13 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:2: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v7:Fixnum[10] = Const Value(10) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF) - v19:NilClass = Const Value(nil) + v18:NilClass = Const Value(nil) CheckInterrupts - Return v19 + Return v18 "); } @@ -8858,10 +9104,10 @@ mod opt_tests { assert_snapshot!(hir_string("test"), @r" fn test@:5: bb0(v0:BasicObject): - v5:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) + v6:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v7:Fixnum[10] = Const Value(10) - v11:BasicObject = SendWithoutBlock v5, :[], v7 + v11:BasicObject = SendWithoutBlock v6, :[], v7 CheckInterrupts Return v11 "); diff --git a/zjit/src/lib.rs b/zjit/src/lib.rs index e6f743fa1e6782..e58cf2bec4787b 100644 --- a/zjit/src/lib.rs +++ b/zjit/src/lib.rs @@ -26,7 +26,5 @@ mod disasm; mod options; mod profile; mod invariants; -#[cfg(test)] -mod assertions; mod bitset; mod gc; diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 0a0daa5d9c54d6..012b8be250b43f 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -150,12 +150,17 @@ make_counters! { send_fallback_optimized, send_fallback_missing, send_fallback_refined, + send_fallback_null, // Writes to the VM frame vm_write_pc_count, vm_write_sp_count, vm_write_locals_count, vm_write_stack_count, + vm_write_to_parent_iseq_local_count, + vm_read_from_parent_iseq_local_count, + // TODO(max): Implement + // vm_reify_stack_count, } /// Increase a counter by a specified amount @@ -259,6 +264,7 @@ pub fn send_fallback_counter(def_type: crate::hir::MethodType) -> Counter { Optimized => send_fallback_optimized, Missing => send_fallback_missing, Refined => send_fallback_refined, + Null => send_fallback_null, } }