Seven fixes across the toolchain, plus three new lib/hash modules
(adler32, crc16, crc32) that surfaced them.
1. `~x` on u8/u16/u32 left the upper bits set: NOTQ inverts the
whole 64-bit register and nothing trimmed it back to type
width, so a returned `u16` would compare 64-bit against a
typed literal and disagree. Both stages now mask after NOTQ
for narrow unsigned: AND $0xFF/0xFFFF for u8/u16, MOVL r,r for
u32 (ANDQ $0xFFFFFFFF sign-extends imm32 and is a no-op).
Signed narrows stay sign-extended and need no fix-up. See
cmd/w6c/cgen.c N_UN TK_TILDE and selfhost cgenexpr.ww cgun
TK_TILDE with new nodeprimwidth helper.
2. w6a had no D_CONST immediate path for ANDQ / ORQ. cgen would
emit `ANDQ $65535, AX` and the rr encoder silently wrote
`21 /r` with garbage reg fields — the mask never happened.
Added `81 /4` (AND) and `81 /1` (OR) imm32 paths in both
cstage and selfhost w6a. The ~width fix above depends on this.
3. `s: []u8` cast as a direct fn argument produced a 0-length
slice. cgexpr for N_CAST left (AX=ptr, BX=len) from the str
source but never set CX (cap), and the arg-push fallback only
pushed AX. cgcast now synthesises CX=BX when target is slice
and source is str; node_isslice / arg-push recognise
cast-to-slice and emit the full (cap, len, ptr) triple. Both
stages.
4. `*[N]T` element-store used 8-byte stride + MOVQ regardless of
T's width. Indexing `buf: *[4]u16` would step 8 bytes and
write 8 bytes per element. Added idx_eff (drills *[N]T → T)
in cstage and the matching pointer-array drill in selfhost
elemsizeof. Also added MOVW / MOVZWQ / MOVSWQ to w6c, w6a,
and selfhost mirrors so 2-byte element stores/loads use the
right opcode (was falling through to MOVQ and trailing 6 bytes
into the next slot).
5. Slicing a top-level fixed array (`g[0:n]` where `g: [N]T` is
a global) computed the base from BP instead of the symbol —
localfind returned 0 and the cgen treated it as a local at
offset 0. Both N_SLICE-as-expression (cgslice) and N_SLICE-
as-call-arg paths now check let_islet / letvartnode and emit
LEAQ name(SB) when the base is a global array (or MOVQ
name(SB) for a global slice/pointer base). Both stages.
6. Top-level `let arr: [N]T = [v0, v1, ...]` link-failed on
cstage — emit_lets bailed when it saw N_ARRLIT init on an
array type, and the sz==8 scalar path then misemitted any
8-byte-sized array (e.g. [4]u16, [8]u8) as a single quad.
emit_lets now walks N_ARRLIT, evaluates each element as an
int/rune/bool/nil literal, packs per-element bytes
little-endian, and honours the trailing `...` repeat marker.
Selfhost already handled the literal-init path; fixed the
parallel sz==8 duplicate-DATAW emit on its side (the array
and the scalar paths both fired, last write winning at link
but the duplicate broke cross-stage byte-identicality on user
code with this shape).
7. w6a's per-line input buffer was a 1KB stack `char buf[1024]`.
A `DATAW` for a [256]u16 emits ~2080 bytes on one line, which
truncated mid-escape; the assembler then re-parsed the
remaining tail as garbage opcodes ("unknown opcode"). Bumped
cstage w6a to a 32K static buffer (selfhost w6a already
allocated per-line via amalloc).
lib: lib/hash/adler32, lib/hash/crc16, lib/hash/crc32 — pure
buffer-subset shape (matching lib/hash/fnv), with per-module
*_test.ww runnable via `ww test lib/hash/<name>`. Adler-32 plus
CRC-16 (CCITT/CMDA2000/DECT/ANSI) and CRC-32 (IEEE/Castagnoli/
Koopman) cover Hare's reference vectors bit-for-bit. Wired into
test/wcc/900_stdlib.c. .gitignore: lib/**/*.s,*.o so `ww test`
droppings stay untracked.
`make test` (26/26), `make bootstrap` (ww2≡ww3≡ww4), and per-module
`ww test` all pass. cgen output is byte-identical across cstage and
selfhost for every repro that previously diverged.
115 lines
2.5 KiB
C
115 lines
2.5 KiB
C
/*
|
|
* 6.out.h — amd64 instruction enum + register names. Mirrors the
|
|
* Plan 9 6c shape (cmd/6c/6.out.h) but trimmed to the subset that
|
|
* w6c emits and w6a consumes in this bootstrap. Each new opcode added
|
|
* here must also gain encoding support in cmd/w6a/asm.c.
|
|
*/
|
|
#ifndef SIX_OUT_H
|
|
#define SIX_OUT_H
|
|
|
|
/* registers — Plan 9 names; lowercase = 8-bit, etc. We use 64-bit. */
|
|
enum {
|
|
D_NONE = 0,
|
|
|
|
/* general purpose 64-bit */
|
|
D_AX, D_CX, D_DX, D_BX,
|
|
D_SP, D_BP, D_SI, D_DI,
|
|
D_R8, D_R9, D_R10, D_R11,
|
|
D_R12, D_R13, D_R14, D_R15,
|
|
|
|
/* SSE/XMM 64-bit float regs */
|
|
D_X0, D_X1, D_X2, D_X3,
|
|
D_X4, D_X5, D_X6, D_X7,
|
|
D_X8, D_X9, D_X10, D_X11,
|
|
D_X12, D_X13, D_X14, D_X15,
|
|
|
|
/* pseudo regs (Plan 9) */
|
|
D_PSP, /* SP pseudo (frame-relative) */
|
|
D_PFP, /* FP pseudo (incoming args) */
|
|
D_PSB, /* SB pseudo (static base) */
|
|
|
|
/* operand kinds; not registers but share the slot */
|
|
D_CONST, /* $N immediate */
|
|
D_BRANCH, /* label reference */
|
|
D_EXTERN, /* external symbol */
|
|
D_INDIR /* offset(reg) memory */
|
|
};
|
|
|
|
/* opcodes — the small set we currently emit & encode */
|
|
enum {
|
|
A_NOP = 0,
|
|
A_TEXT,
|
|
A_DATA,
|
|
A_DATAW, /* writable DATA: lands in .data (RW) instead of .text */
|
|
A_DATAR, /* reloc-only: patch a 64-bit slot in .data with a
|
|
* symbol's runtime VA. Pairs with a prior DATAW
|
|
* that left zero placeholder bytes. */
|
|
A_GLOBL,
|
|
A_END,
|
|
|
|
A_MOVQ,
|
|
A_MOVL,
|
|
A_MOVW,
|
|
A_MOVB,
|
|
A_MOVZBQ, /* movzx r64, r/m8 — load byte zero-extended */
|
|
A_MOVZWQ, /* movzx r64, r/m16 — load word zero-extended */
|
|
A_MOVSXD, /* movsxd r64, r/m32 — load i32 sign-extended */
|
|
A_MOVSWQ, /* movsx r64, r/m16 — load word sign-extended */
|
|
|
|
/* SSE2 scalar double-precision float */
|
|
A_MOVSD, /* xmm/m → xmm and xmm → m */
|
|
A_ADDSD,
|
|
A_SUBSD,
|
|
A_MULSD,
|
|
A_DIVSD,
|
|
A_UCOMISD,
|
|
A_CVTTSD2SI, /* truncate f64 → i64 */
|
|
A_CVTSI2SD, /* convert i64 → f64 */
|
|
|
|
/* SSE scalar single-precision float (f32). Same xmm regs. */
|
|
A_MOVSS,
|
|
A_ADDSS,
|
|
A_SUBSS,
|
|
A_MULSS,
|
|
A_DIVSS,
|
|
A_UCOMISS,
|
|
A_CVTTSS2SI,
|
|
A_CVTSI2SS,
|
|
A_CVTSD2SS, /* f64 → f32 truncate */
|
|
A_CVTSS2SD, /* f32 → f64 widen */
|
|
A_ADDQ,
|
|
A_SUBQ,
|
|
A_IMULQ,
|
|
A_IDIVQ,
|
|
A_DIVQ, /* unsigned 64-bit divide; sibling of IDIVQ */
|
|
A_NEGQ,
|
|
A_NOTQ,
|
|
A_ANDQ,
|
|
A_ORQ,
|
|
A_XORQ,
|
|
A_SHLQ,
|
|
A_SHRQ,
|
|
A_CMPQ,
|
|
|
|
A_PUSHQ,
|
|
A_POPQ,
|
|
A_LEAQ,
|
|
|
|
A_CALL,
|
|
A_RET,
|
|
A_JMP,
|
|
A_JE, A_JNE,
|
|
A_JL, A_JLE, A_JG, A_JGE,
|
|
A_JB, A_JBE, A_JA, A_JAE,
|
|
A_JZ, A_JNZ,
|
|
|
|
A_SYSCALL,
|
|
|
|
A_LAST
|
|
};
|
|
|
|
const char *anames(int); /* opcode -> mnemonic */
|
|
const char *rnames(int); /* register -> name */
|
|
|
|
#endif
|