kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 830bc7b70276572fcda41678e495c9606ef69fe4
parent 93d60d7d4a25b700ec107367f8b0d7765f67e2bf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 17:21:19 -0700

asm: support symbol-minus-symbol differences; aa64 disasm signed regoff loads

Assembler: `.quad _end - _start` (and narrower data widths) no longer panics.
parse_add records `sym1 - sym2` as a difference; a post-parse resolve pass
(resolve_sym_diffs, modeled on relax_local_branches) lowers it once every label
is defined — needed because the minuend is often a forward/external ref. Three
outcomes, matching GNU as:
  (a) both symbols in one section -> assembly-time constant folded into the field
  (b) subtrahend local to the fixup's section, minuend anywhere (incl. an
      external/linker-script _end) -> PC-relative reloc on the minuend with a
      corrected addend. This is the arm64/riscv kernel image_size header field
      (R_AARCH64_PREL64 _end+ofs); the mechanism reloc_apply.c already documented.
  (c) cross-section / undefined subtrahend -> an R_ADD/R_SUB pair the linker
      applies additively (Mach-O SUBTRACTOR, RISC-V ELF R_RISCV_ADD/SUB, or
      consumed by kit's own linker). aarch64/x86-64 ELF have no wire encoding for
      this pair, same as GNU as.

Disasm: the aa64 ISA table was missing the signed register-offset loads
(ldrsb/ldrsh/ldrsw [Xn, Xm]) and print_ldst_regoff derived register width from
size (wrong for signed loads, where width comes from opc). Added the 5 entries
and made the printer opc-aware. Fixes the pre-existing glob_string round-trip
failures; kit now matches llvm-mc incl. uxtw/lsl/sxtw extends.

Tests: encode/{aa64_symdiff_const,aa64_symdiff_reloc,aa64_symdiff_pair,
rv64_symdiff_pair}, decode/aa64_ldrs_regoff. Removes the fixed TODO item.

Diffstat:
Mdoc/plan/TODO.md | 16----------------
Msrc/arch/aa64/isa.c | 15++++++++++++++-
Msrc/asm/asm.c | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Atest/asm/decode/aa64_ldrs_regoff.expected.txt | 8++++++++
Atest/asm/decode/aa64_ldrs_regoff.hex | 1+
Atest/asm/decode/aa64_ldrs_regoff.targets | 1+
Atest/asm/encode/aa64_symdiff_const.expected.hex | 1+
Atest/asm/encode/aa64_symdiff_const.s | 12++++++++++++
Atest/asm/encode/aa64_symdiff_const.targets | 1+
Atest/asm/encode/aa64_symdiff_pair.E.skip | 1+
Atest/asm/encode/aa64_symdiff_pair.J.skip | 1+
Atest/asm/encode/aa64_symdiff_pair.expected | 1+
Atest/asm/encode/aa64_symdiff_pair.s | 36++++++++++++++++++++++++++++++++++++
Atest/asm/encode/aa64_symdiff_pair.targets | 1+
Atest/asm/encode/aa64_symdiff_reloc.expected | 1+
Atest/asm/encode/aa64_symdiff_reloc.s | 29+++++++++++++++++++++++++++++
Atest/asm/encode/aa64_symdiff_reloc.targets | 1+
Atest/asm/encode/rv64_symdiff_pair.expected | 1+
Atest/asm/encode/rv64_symdiff_pair.s | 29+++++++++++++++++++++++++++++
Atest/asm/encode/rv64_symdiff_pair.targets | 1+
20 files changed, 293 insertions(+), 24 deletions(-)

diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md @@ -85,22 +85,6 @@ Add new deferred fixes below as they are discovered. the existing scratch between mem-staging and operand-staging — a register-policy decision, hence "reviewed change." -- **Assembler rejects symbol-minus-symbol differences (`.quad _end - _start`).** - kit's own assembler panics with `asm: cannot subtract symbol from constant` - (`src/asm/asm.c` `parse_add`, ~line 391). Only `sym - constant` and `sym - .` - (PC-relative) are handled; a `sym1 - sym2` difference (a link-time-resolvable - constant when both are in one section, or a relocation pair otherwise) is not. - GNU as supports it. This hides on hosts where the only path that assembles such - sources is clang: e.g. `test/link/cases/35_linker_script_kernel/entry.aa64.S` - uses `.quad _end - _start` for its Image-header `image_size`, but that fixture's - R-lane assembles with clang and its kit-assembled E-lane is qemu-gated (skipped - on macOS). It surfaces the moment kit's assembler is driven directly - (`kit build-exe`/`kit as`) on such a source — notably an author-written flat - kernel `Image` header that computes `image_size = _end - _start` (see - doc/plan/KERNEL.md). Fix in `parse_add`: when both operands carry a symbol, - record a symbol-difference expression and lower it to a constant (same section) - or a relocation pair, rather than panicking. - ## Deferred dedups & abstraction cleanups ("use the shared seam") - **A.2 — optimizer-path inline-asm dedup.** The three `aa_/x64_/rv_asm_block_native` diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c @@ -598,6 +598,15 @@ const AA64InsnDesc aa64_insn_table[] = { {MN("ldr"), 0xB8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, {MN("str"), 0xF8200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, {MN("ldr"), 0xF8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + /* Signed sub-word loads share the format but use opc=10 (Xt) / opc=11 (Wt) + * — the destination width comes from opc, not size (handled in + * print_ldst_regoff). size=10 opc=10 is LDRSW (no Wt form; that opc=11 slot + * is PRFM, left undecoded). */ + {MN("ldrsb"), 0x38A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrsb"), 0x38E00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrsh"), 0x78A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrsh"), 0x78E00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrsw"), 0xB8A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, /* ----- FP data-processing (2 source): FMUL / FDIV / FADD / FSUB ----- * ftype (bits23:22) free, read for the s/d/h register prefix. */ @@ -1368,9 +1377,13 @@ static void print_logimm(StrBuf* sb, u32 w) { * for option=011 (UXTX), with shift amount S ? size : 0. */ static void print_ldst_regoff(StrBuf* sb, u32 w) { u32 size = (w >> 30) & 3u; + u32 opc = (w >> 22) & 3u; u32 option = (w >> 13) & 7u; u32 s_bit = (w >> 12) & 1u; - int xt = (size == 3u); + /* Destination width: LDR/STR (opc 00/01) are 64-bit only when size==11; + * the signed sub-word loads pick width from opc — opc=10 sign-extends to + * Xt, opc=11 to Wt — independent of size. */ + int xt = (opc == 2u) ? 1 : (opc == 3u) ? 0 : (size == 3u); emit_reg(sb, w & 0x1fu, xt, 0); strbuf_puts(sb, ", ["); emit_reg(sb, (w >> 5) & 0x1fu, /*sf=*/1, /*sp_means_sp=*/1); diff --git a/src/asm/asm.c b/src/asm/asm.c @@ -31,6 +31,7 @@ #include "core/heap.h" #include "core/pool.h" #include "core/slice.h" +#include "core/vec.h" #include "obj/obj.h" #include "obj/reloc_apply.h" @@ -45,6 +46,19 @@ typedef struct AsmEqu { } AsmEqu; HASHMAP_DEFINE(SymEquMap, Sym, AsmEqu, hash_u32); +/* A `sym1 - sym2` data difference (e.g. `.quad _end - _start`) recorded at + * emit time and resolved after the parse, once every label is defined. The + * field at <section,offset> already holds the net constant addend. */ +typedef struct AsmDiff { + ObjSecId section; /* fixup site section */ + u32 offset; /* fixup site offset within `section` */ + u32 width; /* 1/2/4/8 — the data width */ + ObjSymId minuend; /* sym1 */ + ObjSymId subtrahend; /* sym2 */ + i64 addend; /* net constant addend already written into the field */ + SrcLoc loc; /* the directive's location, for late diagnostics */ +} AsmDiff; + struct AsmDriver { Compiler* c; AsmLexer* lex; @@ -63,6 +77,11 @@ struct AsmDriver { SymSymMap sym_map; SymEquMap equ_map; + /* Deferred `sym1 - sym2` data differences (resolve_sym_diffs). */ + AsmDiff* diffs; + u32 ndiffs; + u32 diffs_cap; + Sym n_text, n_data, n_rodata, n_bss; ArchAsm* arch_asm; @@ -224,22 +243,23 @@ static void promote_undef_externs(AsmDriver* d) { /* ---- expression evaluator (constants + sym ± const) ---- */ typedef struct AsmExpr { - ObjSymId sym; + ObjSymId sym; /* minuend, or the sole symbol */ + ObjSymId sym2; /* subtrahend: nonzero => value is `(sym - sym2) + const` */ i64 value; u8 is_here; /* the location-counter token `.` (no sym, no value yet) */ u8 pcrel; /* `sym - .`: emit a PC-relative data reloc instead of absolute */ } AsmExpr; static AsmExpr expr_c(i64 v) { - AsmExpr e = {OBJ_SYM_NONE, v, 0, 0}; + AsmExpr e = {OBJ_SYM_NONE, OBJ_SYM_NONE, v, 0, 0}; return e; } static AsmExpr expr_s(ObjSymId s, i64 v) { - AsmExpr e = {s, v, 0, 0}; + AsmExpr e = {s, OBJ_SYM_NONE, v, 0, 0}; return e; } static AsmExpr expr_here(void) { - AsmExpr e = {OBJ_SYM_NONE, 0, 1, 0}; + AsmExpr e = {OBJ_SYM_NONE, OBJ_SYM_NONE, 0, 1, 0}; return e; } @@ -383,13 +403,23 @@ static AsmExpr parse_add(AsmDriver* d) { if (op == '+') { if (a.sym && b.sym) d_panicf(d, "asm: cannot add two symbols"); if (b.sym) { + /* const + (b.sym [- b.sym2] + k): adopt b's symbol(s). */ a.sym = b.sym; + a.sym2 = b.sym2; a.value += b.value; } else a.value += b.value; } else { - if (b.sym) d_panicf(d, "asm: cannot subtract symbol from constant"); - a.value -= b.value; + if (b.sym) { + /* sym1 - sym2: a symbol difference. Resolves to a link-time constant + * (both symbols in one section) or a relocation pair otherwise. */ + if (!a.sym) d_panicf(d, "asm: cannot subtract symbol from constant"); + if (a.sym2 || b.sym2) + d_panicf(d, "asm: more than one symbol difference in expression"); + a.sym2 = b.sym; + a.value -= b.value; + } else + a.value -= b.value; } } } @@ -659,10 +689,34 @@ static void emit_le(AsmDriver* d, u64 v, u32 width) { mc_emit_bytes(d->mc, buf, width); } +/* Record a `sym1 - sym2` data difference for resolution after the parse, + * once every label is defined. The field already holds the net addend. */ +static void record_sym_diff(AsmDriver* d, ObjSecId sec, u32 ofs, u32 width, + ObjSymId minuend, ObjSymId subtrahend, i64 addend) { + if (VEC_GROW(d->heap, d->diffs, d->diffs_cap, d->ndiffs + 1)) + d_panicf(d, "asm: out of memory recording symbol difference"); + AsmDiff* df = &d->diffs[d->ndiffs++]; + df->section = sec; + df->offset = ofs; + df->width = width; + df->minuend = minuend; + df->subtrahend = subtrahend; + df->addend = addend; + df->loc = d_loc(d); +} + static void emit_int_directive(AsmDriver* d, u32 width) { for (;;) { AsmExpr e = parse_expr(d); - if (e.sym) { + if (e.sym && e.sym2) { + /* `sym1 - sym2` difference: emit the net addend as a placeholder and + * defer resolution until all labels are defined (a forward-referenced + * minuend like `_end` is still undefined here). */ + (void)asm_driver_cur_section(d); + u32 ofs = mc_pos(d->mc); + emit_le(d, (u64)e.value, width); + record_sym_diff(d, d->cur_sec, ofs, width, e.sym, e.sym2, e.value); + } else if (e.sym) { RelocKind k; if (e.pcrel) { /* `sym - .`: PC-relative data. Only the 32/64-bit widths codegen @@ -1265,6 +1319,95 @@ static void do_directive(AsmDriver* d, Sym name) { d_skip_to_eol(d); } +/* ---- deferred symbol-difference resolution ---- + * + * `.quad sym1 - sym2` (and the narrower data widths) is recorded at emit time + * and lowered here, once the parse has defined every label. Three outcomes, + * matching GNU as: + * + * (a) Both symbols defined in one section — the difference is a pure + * assembly-time constant (the section base cancels). Fold it into the + * field; no relocation. Works for any width. + * (b) The subtrahend is defined in the fixup's own section (the minuend may + * be anywhere, including undefined / linker-script-provided like `_end`). + * Reduce to a PC-relative relocation against the minuend: at the fixup + * site P, `M + A' - P` with `A' = addend + offset - sym2.value` equals + * `M - sym2` because `P - offset` is the section base and + * `base + sym2.value == sym2`. This is the arm64/riscv kernel + * `image_size = _end - _start` case. Needs .long/.quad. + * (c) Otherwise (cross-section, or an undefined subtrahend) — an R_ADD/R_SUB + * relocation pair the linker applies additively to the field (which holds + * the net addend): `field += minuend; field -= subtrahend`. Emitted + * adjacently so the Mach-O writer fuses them into a SUBTRACTOR pair and + * RISC-V ELF maps each to the R_RISCV_ADD / R_RISCV_SUB families. */ +static void patch_le(AsmDriver* d, ObjSecId sec, u32 ofs, u64 v, u32 width) { + u8 buf[8]; + for (u32 i = 0; i < width; ++i) buf[i] = (u8)(v >> (8 * i)); + obj_patch(d->ob, sec, ofs, buf, width); +} + +static void resolve_sym_diffs(AsmDriver* d) { + for (u32 i = 0; i < d->ndiffs; ++i) { + AsmDiff* df = &d->diffs[i]; + const ObjSym* m = obj_symbol_get(d->ob, df->minuend); + const ObjSym* s = obj_symbol_get(d->ob, df->subtrahend); + int m_def = m && m->section_id != OBJ_SEC_NONE; + int s_def = s && s->section_id != OBJ_SEC_NONE; + + /* (a) constant fold */ + if (m_def && s_def && m->section_id == s->section_id) { + i64 c = (i64)m->value - (i64)s->value + df->addend; + patch_le(d, df->section, df->offset, (u64)c, df->width); + continue; + } + + /* (b) subtrahend in the fixup's section -> PC-relative reloc on minuend */ + if (s_def && s->section_id == df->section) { + RelocKind k; + if (df->width == 8) + k = R_PC64; + else if (df->width == 4) + k = R_PC32; + else + compiler_panic(d->c, df->loc, + "asm: PC-relative symbol difference needs .long/.quad"); + i64 ad = df->addend + (i64)df->offset - (i64)s->value; + patch_le(d, df->section, df->offset, (u64)ad, df->width); + mc_emit_reloc_at(d->mc, df->section, df->offset, k, df->minuend, ad, 1, 0); + continue; + } + + /* (c) general difference -> R_ADD/R_SUB pair */ + { + RelocKind ka, ks; + switch (df->width) { + case 1: + ka = R_ADD8; + ks = R_SUB8; + break; + case 2: + ka = R_ADD16; + ks = R_SUB16; + break; + case 4: + ka = R_ADD32; + ks = R_SUB32; + break; + case 8: + ka = R_ADD64; + ks = R_SUB64; + break; + default: + compiler_panic(d->c, df->loc, "asm: bad symbol-difference width %u", + (unsigned)df->width); + } + mc_emit_reloc_at(d->mc, df->section, df->offset, ka, df->minuend, 0, 1, 0); + mc_emit_reloc_at(d->mc, df->section, df->offset, ks, df->subtrahend, 0, 1, + 0); + } + } +} + /* ---- same-section branch relaxation ---- * * The per-arch parser emits a relocation for every symbolic branch target, @@ -1411,6 +1554,7 @@ void asm_driver_close_inline(AsmDriver* d) { SymSecMap_fini(&d->sec_map); SymSymMap_fini(&d->sym_map); SymEquMap_fini(&d->equ_map); + if (d->diffs) d->heap->free(d->heap, d->diffs, sizeof(AsmDiff) * d->diffs_cap); Heap* heap = d->heap; heap->free(heap, d, sizeof *d); } @@ -1477,6 +1621,7 @@ void asm_parse(Compiler* c, AsmLexer* l, MCEmitter* mc) { d_skip_to_eol(&d); } + resolve_sym_diffs(&d); promote_undef_externs(&d); relax_local_branches(&d); @@ -1484,4 +1629,5 @@ void asm_parse(Compiler* c, AsmLexer* l, MCEmitter* mc) { SymSecMap_fini(&d.sec_map); SymSymMap_fini(&d.sym_map); SymEquMap_fini(&d.equ_map); + if (d.diffs) d.heap->free(d.heap, d.diffs, sizeof(AsmDiff) * d.diffs_cap); } diff --git a/test/asm/decode/aa64_ldrs_regoff.expected.txt b/test/asm/decode/aa64_ldrs_regoff.expected.txt @@ -0,0 +1,8 @@ +0: ldrsb x3, [x1, x2] +4: ldrsb w4, [x5, x6] +8: ldrsh x7, [x8, x9] +c: ldrsh w10, [x11, x12] +10: ldrsw x13, [x14, x15] +14: ldrsb x16, [x17, w18, uxtw] +18: ldrsw x19, [x20, x21, lsl #2] +1c: ldrsh x22, [x23, w24, sxtw #1] diff --git a/test/asm/decode/aa64_ldrs_regoff.hex b/test/asm/decode/aa64_ldrs_regoff.hex @@ -0,0 +1 @@ +23 68 a2 38 a4 68 e6 38 07 69 a9 78 6a 69 ec 78 cd 69 af b8 30 4a b2 38 93 7a b5 b8 f6 da b8 78 diff --git a/test/asm/decode/aa64_ldrs_regoff.targets b/test/asm/decode/aa64_ldrs_regoff.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/encode/aa64_symdiff_const.expected.hex b/test/asm/encode/aa64_symdiff_const.expected.hex @@ -0,0 +1 @@ +000000000400000000000000fcffffffffffffff18000000 diff --git a/test/asm/encode/aa64_symdiff_const.s b/test/asm/encode/aa64_symdiff_const.s @@ -0,0 +1,12 @@ +// Symbol-difference (sym1 - sym2): when both symbols are defined in the +// same section, the difference is a link-time constant and the assembler +// folds it directly into the data field. `end` is a forward reference, +// exercising the deferred post-parse resolution. +.text +a: + .byte 0, 0, 0, 0 // a = 0 +b: + .quad b - a // = 4 + .quad a - b // = -4 + .long end - a // = 24 (forward ref) +end: diff --git a/test/asm/encode/aa64_symdiff_const.targets b/test/asm/encode/aa64_symdiff_const.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/encode/aa64_symdiff_pair.E.skip b/test/asm/encode/aa64_symdiff_pair.E.skip @@ -0,0 +1 @@ +aarch64 ELF has no wire encoding for R_ADD64/R_SUB64 (cross-section symbol difference); applied in-process by D / final link only diff --git a/test/asm/encode/aa64_symdiff_pair.J.skip b/test/asm/encode/aa64_symdiff_pair.J.skip @@ -0,0 +1 @@ +aarch64 ELF has no wire encoding for R_ADD64/R_SUB64 (cross-section symbol difference); applied in-process by D / final link only diff --git a/test/asm/encode/aa64_symdiff_pair.expected b/test/asm/encode/aa64_symdiff_pair.expected @@ -0,0 +1 @@ +0 diff --git a/test/asm/encode/aa64_symdiff_pair.s b/test/asm/encode/aa64_symdiff_pair.s @@ -0,0 +1,36 @@ +// General symbol difference: neither symbol lives in the fixup's section, +// so `diff` (in .text) holding (m - s) — with `s` in .data and `m` in +// .rodata — lowers to an R_ADD64/R_SUB64 relocation pair the linker applies +// additively. test_main recomputes (m - s) from runtime addresses and +// returns 0 iff the linker-resolved value matches. +// +// Runs the in-process D lane only: aarch64 ELF has no wire encoding for the +// R_ADD64/R_SUB64 pair (a cross-section difference is not representable in a +// relocatable aarch64 object — the same limitation GNU as has), so the +// --emit-based J/E lanes are skipped. The pair *is* applied correctly when +// consumed by kit's own linker (D here; a final `kit build-exe` link too), +// and round-trips on RISC-V ELF — see rv64_symdiff_pair. +.text +.globl test_main +test_main: + adrp x0, diff + add x0, x0, :lo12:diff + ldr x0, [x0] // linker-resolved (m - s) + adrp x1, m + add x1, x1, :lo12:m + adrp x2, s + add x2, x2, :lo12:s + sub x1, x1, x2 // runtime (m - s) + subs x0, x0, x1 + cset w0, ne // 0 iff equal + ret +diff: + .quad m - s +.data + .skip 3 +s: + .byte 0 +.section .rodata + .skip 5 +m: + .byte 0 diff --git a/test/asm/encode/aa64_symdiff_pair.targets b/test/asm/encode/aa64_symdiff_pair.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/encode/aa64_symdiff_reloc.expected b/test/asm/encode/aa64_symdiff_reloc.expected @@ -0,0 +1 @@ +0 diff --git a/test/asm/encode/aa64_symdiff_reloc.s b/test/asm/encode/aa64_symdiff_reloc.s @@ -0,0 +1,29 @@ +// Cross-section symbol difference resolved at link time. `diff` holds +// (m - s): the subtrahend `s` is local to .text (the fixup's own section) +// while the minuend `m` lives in .data, so the difference cannot be a +// pure assembly-time constant and lowers to a PC-relative relocation +// against `m`. test_main recomputes (m - s) from the runtime addresses +// and returns 0 iff the linker-resolved value stored in `diff` matches — +// a self-check that needs no fixed section layout. No `.expected.hex` +// golden: the H lane skips and only the exec lanes (D/J/E) run. +.text +.globl test_main +test_main: + adrp x0, diff + add x0, x0, :lo12:diff + ldr x0, [x0] // linker-resolved (m - s) + adrp x1, m + add x1, x1, :lo12:m + adrp x2, s + add x2, x2, :lo12:s + sub x1, x1, x2 // runtime (m - s) + subs x0, x0, x1 + cset w0, ne // 0 iff equal + ret +s: +diff: + .quad m - s + .data + .skip 7 +m: + .byte 0 diff --git a/test/asm/encode/aa64_symdiff_reloc.targets b/test/asm/encode/aa64_symdiff_reloc.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/encode/rv64_symdiff_pair.expected b/test/asm/encode/rv64_symdiff_pair.expected @@ -0,0 +1 @@ +0 diff --git a/test/asm/encode/rv64_symdiff_pair.s b/test/asm/encode/rv64_symdiff_pair.s @@ -0,0 +1,29 @@ +// General symbol difference: neither symbol lives in the fixup's section, +// so `diff` (in .text) holding (m - s) — with `s` in .data and `m` in +// .rodata — lowers to an R_ADD64/R_SUB64 relocation pair the linker +// applies additively. RISC-V ELF carries these as R_RISCV_ADD64 / +// R_RISCV_SUB64, so the relocatable object round-trips (the same pair has +// no aarch64/x86-64 ELF wire encoding). test_main recomputes (m - s) from +// runtime addresses and returns 0 iff the linker-resolved value matches. +// No `.expected.hex`: the H lane skips and only the exec lanes run. +.text +.globl test_main +test_main: + lla a0, diff + ld a0, 0(a0) // linker-resolved (m - s) + lla a1, m + lla a2, s + sub a1, a1, a2 // runtime (m - s) + sub a0, a0, a1 + snez a0, a0 // 0 iff equal + ret +diff: + .quad m - s +.data + .skip 3 +s: + .byte 0 +.section .rodata + .skip 5 +m: + .byte 0 diff --git a/test/asm/encode/rv64_symdiff_pair.targets b/test/asm/encode/rv64_symdiff_pair.targets @@ -0,0 +1 @@ +rv64