diff --git a/asm/loong64_assemble.go b/asm/loong64_assemble.go index 4fabc3c..961bb5c 100644 --- a/asm/loong64_assemble.go +++ b/asm/loong64_assemble.go @@ -872,7 +872,10 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo case l64Firr16: // ADDV16: INSTR $imm, rd or INSTR $imm, rj, rd; the immediate must be - // a multiple of 65536 and is shifted right by 16. + // a multiple of 65536 and is shifted right by 16. Both registers are + // general: the toolchain's class match refuses an F, V or X bank + // register here ("illegal combination ADDV16 ... FREG ..."), and the + // 5-bit numbering would otherwise encode it silently. if len(ops) < 2 || !isImmOperand(ops[0]) { return nil, fmt.Errorf("%s expects an immediate operand", mnem) } @@ -880,6 +883,11 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo if v&0xFFFF != 0 { return nil, fmt.Errorf("%s: the constant must be a multiple of 65536", mnem) } + for _, op := range ops[1:] { + if c := loong64RegClass(operandRegName(op)); c != l64ClsGR { + return nil, fmt.Errorf("%s: illegal combination: the registers must be general", mnem) + } + } rd := l64Reg(ops[len(ops)-1]) rj := rd if len(ops) == 3 { diff --git a/asm/loong64_encode_test.go b/asm/loong64_encode_test.go index 962074c..57ba90b 100644 --- a/asm/loong64_encode_test.go +++ b/asm/loong64_encode_test.go @@ -1105,3 +1105,43 @@ TEXT ·d(SB), NOSPLIT, $0 }) } } + +// TestLOONG64_addv16 pins the ADDV16 immediate family, the toolchain's +// assembler name for the addu16i.d hardware instruction (loong64enc1.s +// spells it ADDV16; the spelling ADDU16I.D is the decoder's name for the +// encoding and no assembler input at all). The immediate must be a +// multiple of 65536 and rides the instruction field shifted right by 16; +// the oracle words are the toolchain's own loong64enc1.s rows, each also +// confirmed against GOARCH=loong64 go tool asm. +func TestLOONG64_addv16(t *testing.T) { + fn := firstTextLOONG64(t, `#include "textflag.h" +TEXT ·a16(SB), NOSPLIT, $0 + ADDV16 $(-32768<<16), R4, R5 + ADDV16 $(0<<16), R4, R5 + ADDV16 $(8<<16), R4, R5 + ADDV16 $(32767<<16), R4, R5 + ADDV16 $(16<<16), R4 + RET +`) + code := assembleLOONG64Helper(t, fn) + wantWords(t, code, + 0x12000085, // addu16i.d r5, r4, -32768 + 0x10000085, // addu16i.d r5, r4, 0 + 0x10002085, // addu16i.d r5, r4, 8 + 0x11FFFC85, // addu16i.d r5, r4, 32767 + 0x10004084, // addu16i.d r4, r4, 16 (destination-only form) + 0x4C000020, + ) + // The non-multiples the loong64error.s catalogue pins (its rows 3 and + // 4): $1 and $65535 both refuse. + for _, src := range []string{ + "ADDV16 $1, R4, R5", + "ADDV16 $65535, R4, R5", + "ADDV16 $(16<<16), F4", + } { + fn := firstTextLOONG64(t, "#include \"textflag.h\"\nTEXT ·e(SB), NOSPLIT, $0\n\t"+src+"\n\tRET\n") + if _, _, _, _, _, err := assembleLOONG64(fn); err == nil { + t.Errorf("%q: expected an error, got none", src) + } + } +}