// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package disasm import ( "testing" "sourcedock.dev/petrbalvin/gasm-sdk/arch" ) // TestUnknownNaming pins the loong64 re-rendering table over the corpus // rows whose operations x/arch decodes but names "Unknown" or "?": every // row's bytes are the corpus's own expected-encoding comments // (machine-checked by the toolchain's assembler test suite) and every text // is the toolchain's own spelling, operand order included. func TestUnknownNaming(t *testing.T) { for _, tt := range []struct { code []byte text string }{ // loong64enc1.s: EXTWB R4, R5 // 855c0000 and EXTWH R4, R5 // 85580000. {[]byte{0x85, 0x5c, 0x00, 0x00}, "EXTWB R4, R5"}, {[]byte{0x85, 0x58, 0x00, 0x00}, "EXTWH R4, R5"}, // loong64enc1.s: the bit and reverse families. {[]byte{0x85, 0x40, 0x00, 0x00}, "REVH2W R4, R5"}, {[]byte{0x85, 0x44, 0x00, 0x00}, "REVHV R4, R5"}, {[]byte{0x85, 0x48, 0x00, 0x00}, "BITREV4B R4, R5"}, {[]byte{0x85, 0x4c, 0x00, 0x00}, "BITREV8B R4, R5"}, // loong64enc1.s: ANDN and ORN, two- and three-operand forms. {[]byte{0xa6, 0x90, 0x16, 0x00}, "ANDN R4, R5, R6"}, {[]byte{0xa5, 0x90, 0x16, 0x00}, "ANDN R4, R5"}, {[]byte{0xa6, 0x10, 0x16, 0x00}, "ORN R4, R5, R6"}, {[]byte{0xa5, 0x10, 0x16, 0x00}, "ORN R4, R5"}, // loong64enc1.s: MULWVW and MULWVWU. {[]byte{0xa5, 0x10, 0x1f, 0x00}, "MULWVW R4, R5"}, {[]byte{0xa6, 0x10, 0x1f, 0x00}, "MULWVW R4, R5, R6"}, {[]byte{0xa5, 0x90, 0x1f, 0x00}, "MULWVWU R4, R5"}, {[]byte{0xa6, 0x90, 0x1f, 0x00}, "MULWVWU R4, R5, R6"}, // loong64enc1.s: the pointer store and load pair. The // displacement is the scaled field read the way the toolchain // takes it back: 32764, not the renderer's -4. {[]byte{0x85, 0x04, 0x00, 0x25}, "MOVWP R5, 4(R4)"}, {[]byte{0x85, 0x00, 0x00, 0x25}, "MOVWP R5, 0(R4)"}, {[]byte{0x85, 0xfc, 0x7f, 0x25}, "MOVWP R5, 32764(R4)"}, {[]byte{0xa4, 0xfc, 0x7f, 0x24}, "MOVWP 32764(R5), R4"}, {[]byte{0xa4, 0x04, 0x00, 0x24}, "MOVWP 4(R5), R4"}, {[]byte{0xa4, 0x00, 0x00, 0x26}, "MOVVP 0(R5), R4"}, {[]byte{0x85, 0xfc, 0x7f, 0x27}, "MOVVP R5, 32764(R4)"}, // loong64enc1.s: the acquire loads and release stores. {[]byte{0xa4, 0x80, 0x57, 0x38}, "LLACQW (R5), R4"}, {[]byte{0xa4, 0x88, 0x57, 0x38}, "LLACQV (R5), R4"}, {[]byte{0xc4, 0x14, 0x57, 0x38}, "SCQ R4, R5, (R6)"}, {[]byte{0xc4, 0x84, 0x57, 0x38}, "SCRELW R4, (R6)"}, {[]byte{0xc4, 0x8c, 0x57, 0x38}, "SCRELV R4, (R6)"}, // loong64enc1.s: the floating-point minima and maxima. {[]byte{0xa6, 0x90, 0x0c, 0x01}, "FMAXAF F4, F5, F6"}, {[]byte{0xa5, 0x90, 0x0c, 0x01}, "FMAXAF F4, F5"}, {[]byte{0xa6, 0x10, 0x0d, 0x01}, "FMAXAD F4, F5, F6"}, {[]byte{0xa6, 0x90, 0x0e, 0x01}, "FMINAF F4, F5, F6"}, {[]byte{0xa6, 0x10, 0x0f, 0x01}, "FMINAD F4, F5, F6"}, // loong64enc1.s: FSEL, FSCALEB and FLOGB. {[]byte{0x43, 0x04, 0x00, 0x0d}, "FSEL FCC0, F1, F2, F3"}, {[]byte{0x42, 0x84, 0x00, 0x0d}, "FSEL FCC1, F1, F2, F2"}, {[]byte{0xa6, 0x90, 0x10, 0x01}, "FSCALEBF F4, F5, F6"}, {[]byte{0xa6, 0x10, 0x11, 0x01}, "FSCALEBD F4, F5, F6"}, {[]byte{0x85, 0x24, 0x14, 0x01}, "FLOGBF F4, F5"}, {[]byte{0x85, 0x28, 0x14, 0x01}, "FLOGBD F4, F5"}, // loong64enc1.s: PRELD, all three hint rows. {[]byte{0x80, 0x00, 0xc0, 0x2a}, "PRELD (R4), $0"}, {[]byte{0x88, 0xfc, 0xff, 0x2a}, "PRELD -1(R4), $8"}, {[]byte{0x9f, 0x20, 0xc0, 0x2a}, "PRELD 8(R4), $31"}, // loong64enc1.s: the address-add-and-shift-left family, whose // rj and rk the renderer prints crossed. {[]byte{0x86, 0x94, 0x05, 0x00}, "ALSLW $4, R4, R5, R6"}, {[]byte{0x86, 0x94, 0x07, 0x00}, "ALSLWU $4, R4, R5, R6"}, {[]byte{0x86, 0x94, 0x2d, 0x00}, "ALSLV $4, R4, R5, R6"}, } { ins, err := Decode(arch.LOONG64, tt.code, 0) if err != nil { t.Errorf("% x: %v", tt.code, err) continue } if ins.Text != tt.text || ins.Len != len(tt.code) { t.Errorf("% x: %q (%d bytes), want %q (%d)", tt.code, ins.Text, ins.Len, tt.text, len(tt.code)) } } } // TestUnknownNamingBoundaries pins what stays untouched: the ADDU16I.D // render. The truth of the naming question, established against the live // toolchain: the assembler spells the family ADDV16 (loong64enc1.s pins // the rows and go tool asm refuses "ADDU16I.D" as input), while the // toolchain's own disassembler names the encoding ADDU16I.D with the // "Unknown" prefix x/arch gives the ops its Plan 9 map lacks, and `go tool // objdump` prints exactly this text ("Unknown ADDU16I.D $16, R4" for the // 0x10004084 the loong64enc1.s corpus carries). The render is therefore // the toolchain's own on both counts, name and prefix, and stays. func TestUnknownNamingBoundaries(t *testing.T) { for _, tt := range []struct { code []byte text string }{ // loong64enc1.s: ADDV16 $(-32768<<16), R4, R5 // 85000012. {[]byte{0x85, 0x00, 0x00, 0x12}, "Unknown ADDU16I.D $-32768, R4, R5"}, // loong64enc1.s: ADDV16 $(0<<16), R4, R5 // 85000010. {[]byte{0x85, 0x00, 0x00, 0x10}, "Unknown ADDU16I.D $0, R4, R5"}, // loong64enc1.s: ADDV16 $(8<<16), R4, R5 // 85200010. {[]byte{0x85, 0x20, 0x00, 0x10}, "Unknown ADDU16I.D $8, R4, R5"}, // loong64enc1.s: ADDV16 $(32767<<16), R4, R5 // 85fcff11. {[]byte{0x85, 0xfc, 0xff, 0x11}, "Unknown ADDU16I.D $32767, R4, R5"}, // loong64enc1.s: ADDV16 $(16<<16), R4 // 84400010; the destination- // only form collapses in the renderer when rd equals rj. {[]byte{0x84, 0x40, 0x00, 0x10}, "Unknown ADDU16I.D $16, R4"}, {[]byte{0x85, 0x00, 0x17, 0x00}, "MOVW R4, R5"}, } { ins, err := Decode(arch.LOONG64, tt.code, 0) if err != nil { t.Errorf("% x: %v", tt.code, err) continue } if ins.Text != tt.text { t.Errorf("% x: %q, want %q", tt.code, ins.Text, tt.text) } } }