Add SAR/SARQ to both assemblers' opcode tables (cstage cmd/w6a +
wwstage selfhost/cmd/w6a) — REX.W + D3 /7, parallel to SHR's D3 /5.
Encoding is the unary-on-CL form (SAR r/m64, CL), the only variant
the cgen emits today. cstage cgen + wwstage cgen sweep all 12 SHRQ
emission sites (6 per stage) so signed RSHIFT and signed RSHIFTEQ
route through SARQ (arithmetic, sign-extends MSB) instead of SHRQ
(logical, zero-fill). Pre-fix `let i: i32 = -200; i >>= 2;`
produced 0x3FFFFFCE (1073741774) instead of -50; cs==ww held because
BOTH stages emitted SHRQ, so the 990-997 byte-id gates were
gate-blind to this silent miscompile.
Sites covered (per stage 6, same shape in both):
- plain TK_RSHIFT (cgbin / N_BIN ordered binop) — derives unsignd
from operand types via type_isunsigned / nodeisunsigned, picks
SHRQ vs SARQ at emit
- chained-ptr-field compound RSHIFTEQ (cgen.c:3281-3317 area)
- N_INDEX-lhs compound RSHIFTEQ (#133-expanded N_INDEX site)
- deref-target compound RSHIFTEQ
- top-level let compound RSHIFTEQ
- IDENT-local compound RSHIFTEQ
All sites reuse the in-scope unsignd variable from the surrounding
SLASHEQ block (or derive one locally when not available). LSHIFTEQ
unchanged — SHL == SAL at the encoder, no signedness dispatch needed.
912_sar_shr_run: 5 rows. i32_neg_rshifteq (lead's repro, was wrong
1073741774 → now -50), i64_neg_rshifteq (wider type), i32_pos_
rshifteq (positive control, SARQ ≡ SHRQ on positives, no regression),
u32_rshifteq (unsigned control, still SHRQ), i32_neg_rshift_binop
(plain >> not compound, cgbin TK_RSHIFT site). Exit codes use small
absolute values with u8 wrap (-50 = 206) per Unix 8-bit exit.
Bootstrap-NEUTRAL — `grep -rE '>>=|>>\b'` in lib/+selfhost/ (excl.
combined.ww) returned zero callers of signed RSHIFT today; the only
asm shifts are on previously-broken paths. 990-997 + combined_ww_
fresh stay green. Closes the silent-misbehavior class on signed
right-shift across all 12 cgen emission paths in one fold per
rule-11. Foundation for Eisel-Lemire (strconv fold-4) big-int signed
shifts.
121 lines
2.7 KiB
C
121 lines
2.7 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 — byte zero-extended */
|
|
A_MOVZWQ, /* movzx r64, r/m16 — word zero-extended */
|
|
A_MOVSXD, /* movsxd r64, r/m32 — i32 sign-extended */
|
|
A_MOVSWQ, /* movsx r64, r/m16 — i16 sign-extended */
|
|
A_MOVSBQ, /* movsx r64, r/m8 — i8 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_CQO, /* sign-extend RAX into RDX:RAX; the signed-division
|
|
* prep that pairs with IDIVQ (DIVQ pairs with a
|
|
* MOVQ $0, DX zero-fill). */
|
|
A_NEGQ,
|
|
A_NOTQ,
|
|
A_ANDQ,
|
|
A_ORQ,
|
|
A_XORQ,
|
|
A_SHLQ,
|
|
A_SHRQ,
|
|
A_SARQ,
|
|
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_JP, /* jump on parity (PF=1): UCOMISD unordered, #97 */
|
|
|
|
A_SYSCALL,
|
|
|
|
A_LAST
|
|
};
|
|
|
|
const char *anames(int); /* opcode -> mnemonic */
|
|
const char *rnames(int); /* register -> name */
|
|
|
|
#endif
|