2026-08-13 11:24:44 +02:00
|
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
|
|
package asm
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
2026-09-26 11:08:43 +02:00
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
|
2026-08-13 11:24:44 +02:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_sys exercises the no-operand system instructions and the
|
|
|
|
|
|
// bare-data pseudo-instructions. The words match `go tool asm`
|
|
|
|
|
|
// (GOARCH=loong64) for the same source.
|
|
|
|
|
|
func TestLOONG64_sys(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·sys(SB), NOSPLIT, $0
|
|
|
|
|
|
NOOP
|
|
|
|
|
|
UNDEF
|
|
|
|
|
|
WORD $0x12345678
|
|
|
|
|
|
SYSCALL $0x10
|
|
|
|
|
|
BREAK $0x20
|
|
|
|
|
|
DBAR $1
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x03400000, // andi r0, r0, 0 (NOOP)
|
|
|
|
|
|
0x002A0000, // break 0 (UNDEF)
|
|
|
|
|
|
0x12345678, // WORD
|
|
|
|
|
|
0x002B0010, // syscall 0x10
|
|
|
|
|
|
0x002A0020, // break 0x20
|
|
|
|
|
|
0x38720001, // dbar 1
|
|
|
|
|
|
0x4C000020, // jirl r0, r1, 0
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_branches21 exercises the single-register branch forms: the
|
|
|
|
|
|
// 21-bit BEQZ/BNEZ/BLTZ/BGEZ and the rd-field BGTZ/BLEZ.
|
|
|
|
|
|
func TestLOONG64_branches21(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·b21(SB), NOSPLIT, $0
|
|
|
|
|
|
BEQZ R4, done
|
|
|
|
|
|
BNEZ R5, done
|
|
|
|
|
|
BLTZ R6, done
|
|
|
|
|
|
BGEZ R7, done
|
|
|
|
|
|
BGTZ R8, done
|
|
|
|
|
|
BLEZ R9, done
|
|
|
|
|
|
done:
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x40001880, // beqz r4, +6
|
|
|
|
|
|
0x440014A0, // bnez r5, +5
|
|
|
|
|
|
0x600010C0, // bltz r6, +4
|
|
|
|
|
|
0x64000CE0, // bgez r7, +3
|
|
|
|
|
|
0x60000808, // bgtz r8, +2 (register in the rd field)
|
|
|
|
|
|
0x64000409, // blez r9, +1
|
|
|
|
|
|
0x4C000020, // jirl r0, r1, 0
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_fma exercises the four fused multiply-add forms (4 and 3
|
|
|
|
|
|
// operand spellings).
|
|
|
|
|
|
func TestLOONG64_fma(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·fma(SB), NOSPLIT, $0
|
|
|
|
|
|
FMADDD F0, F1, F2, F3
|
|
|
|
|
|
FMSUBD F4, F5, F6
|
|
|
|
|
|
FNMADDD F7, F8, F9, F10
|
|
|
|
|
|
FNMSUBD F11, F12, F13
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x08200443, // fmadd.d f3, f2, f1, f0
|
|
|
|
|
|
0x086214C6, // fmsub.d f6, f5, f5, f4
|
|
|
|
|
|
0x08A3A12A, // fnmadd.d f10, f9, f8, f7
|
|
|
|
|
|
0x08E5B1AD, // fnmsub.d f13, f12, f12, f11
|
|
|
|
|
|
0x4C000020,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_bitops exercises BSTRINS/BSTRPICK (the 6-bit msb/lsb fields)
|
|
|
|
|
|
// and ALSL (the sa−1 shift field).
|
|
|
|
|
|
func TestLOONG64_bitops(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·bits(SB), NOSPLIT, $0
|
|
|
|
|
|
BSTRINSW $3, R4, $0, R5
|
|
|
|
|
|
BSTRINSV $3, R4, $1, R6
|
|
|
|
|
|
BSTRPICKW $3, R4, $0, R5
|
|
|
|
|
|
BSTRPICKV $6, R7, $0, R8
|
|
|
|
|
|
ALSLW $1, R4, R5, R6
|
|
|
|
|
|
ALSLW $4, R7, R8, R9
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x00630085, // bstrins.w r5, r4, $3, $0
|
|
|
|
|
|
0x00830486, // bstrins.d r6, r4, $3, $1
|
|
|
|
|
|
0x00638085, // bstrpick.w r5, r4, $3, $0
|
|
|
|
|
|
0x00C600E8, // bstrpick.d r8, r7, $6, $0
|
|
|
|
|
|
0x00041486, // alsl.w r6, r5, r4, $1 (sa-1)
|
|
|
|
|
|
0x0005A0E9, // alsl.w r9, r8, r7, $4
|
|
|
|
|
|
0x4C000020,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_ptr exercises the 14-bit-offset memory forms (LL/SC/MOVWP/
|
|
|
|
|
|
// MOVVP with the offset scaled by 4) and PRELD.
|
|
|
|
|
|
func TestLOONG64_ptr(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·ptr(SB), NOSPLIT, $0
|
|
|
|
|
|
LLW 8(R14), R15
|
|
|
|
|
|
SCW R16, -4(R17)
|
|
|
|
|
|
MOVWP 16(R18), R19
|
|
|
|
|
|
MOVVP R20, 24(R21)
|
|
|
|
|
|
PRELD 32(R22), $0
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x200009CF, // ll.w r15, 8(r14)
|
|
|
|
|
|
0x21FFFE30, // sc.w r16, -4(r17)
|
|
|
|
|
|
0x24001253, // ldptr.w r19, 16(r18)
|
|
|
|
|
|
0x27001AB4, // stptr.d r20, 24(r21)
|
|
|
|
|
|
0x2AC082C0, // preld 32(r22), 0
|
|
|
|
|
|
0x4C000020,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_atomics exercises the AM* read-modify-write forms and
|
|
|
|
|
|
// RDTIME, plus the MOVV FP→GP move.
|
|
|
|
|
|
func TestLOONG64_atomics(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·atoms(SB), NOSPLIT, $0
|
|
|
|
|
|
AMADDW R4, (R5), R6
|
|
|
|
|
|
RDTIMED R7, R8
|
|
|
|
|
|
MOVV F1, R2
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x386110A6, // amadd.w r6, r5, r4
|
|
|
|
|
|
0x000068E8, // rdtime.d r8, r7
|
|
|
|
|
|
0x0114B822, // movfr2gr.d r2, f1
|
|
|
|
|
|
0x4C000020,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_lu52 exercises the LU52I.D immediate form (a gasm extension
|
|
|
|
|
|
// the toolchain reaches only through its MOVV expansion).
|
|
|
|
|
|
func TestLOONG64_lu52(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·lu52(SB), NOSPLIT, $0
|
|
|
|
|
|
LU52ID $0x345, R10
|
|
|
|
|
|
LU52ID $0x123, R11, R12
|
|
|
|
|
|
ADDV16 $0x10000, R13
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x030D154A, // lu52i.d r10, r10, 0x345
|
|
|
|
|
|
0x03048D6C, // lu52i.d r12, r11, 0x123
|
|
|
|
|
|
0x100005AD, // addu16i.d r13, r13, 0x10000>>16
|
|
|
|
|
|
0x4C000020,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_sbRefs checks the static-symbol reference forms through the
|
|
|
|
|
|
// full file assembly: each pcalau12i+addi.d/ld/st pair carries the
|
|
|
|
|
|
// R_LOONG64_ADDR_HI/LO relocation pair, and the immediate fields are left
|
|
|
|
|
|
// zero for the linker.
|
|
|
|
|
|
func TestLOONG64_sbRefs(t *testing.T) {
|
|
|
|
|
|
f, errs := parser.Parse("sb_loong64.s", `#include "textflag.h"
|
|
|
|
|
|
TEXT ·sb(SB), NOSPLIT, $0
|
|
|
|
|
|
MOVV $·table(SB), R4
|
|
|
|
|
|
MOVV ·table+8(SB), R5
|
|
|
|
|
|
MOVV R6, ·table(SB)
|
|
|
|
|
|
RET
|
|
|
|
|
|
|
|
|
|
|
|
GLOBL ·table(SB), RODATA, $8
|
|
|
|
|
|
DATA ·table+0(SB)/8, $42
|
|
|
|
|
|
`)
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
|
|
t.Fatalf("parse: %v", errs)
|
|
|
|
|
|
}
|
|
|
|
|
|
img, err := AssembleFileLOONG64(f)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("AssembleFileLOONG64: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
fn := img.Funcs[0]
|
|
|
|
|
|
if fn.Size != 28 {
|
|
|
|
|
|
t.Fatalf("function size = %d, want 28", fn.Size)
|
|
|
|
|
|
}
|
|
|
|
|
|
var hi, lo int
|
|
|
|
|
|
// The three references: $·table (0), ·table+8 (8), ·table (0).
|
|
|
|
|
|
wantAdd := []int64{0, 0, 8, 8, 0, 0}
|
|
|
|
|
|
for i, r := range fn.Relocs {
|
|
|
|
|
|
wantKind := RelLoong64AddrHi
|
|
|
|
|
|
wantOff := (i / 2) * 8
|
|
|
|
|
|
if i%2 == 1 {
|
|
|
|
|
|
wantKind = RelLoong64AddrLo
|
|
|
|
|
|
wantOff += 4
|
|
|
|
|
|
}
|
|
|
|
|
|
if r.Kind != wantKind || r.Off != wantOff || r.Name != "table" || r.Addend != wantAdd[i] {
|
|
|
|
|
|
t.Errorf("reloc %d = {kind %v off %d name %q addend %d}", i, r.Kind, r.Off, r.Name, r.Addend)
|
|
|
|
|
|
}
|
|
|
|
|
|
if r.Kind == RelLoong64AddrHi {
|
|
|
|
|
|
hi++
|
|
|
|
|
|
} else {
|
|
|
|
|
|
lo++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if hi != 3 || lo != 3 {
|
|
|
|
|
|
t.Errorf("relocs = %d hi + %d lo, want 3 + 3", hi, lo)
|
|
|
|
|
|
}
|
|
|
|
|
|
// The image carries the zero-immediate pair encodings (the linker
|
|
|
|
|
|
// fills the immediate fields from the relocations).
|
|
|
|
|
|
code := img.Code[fn.Offset : fn.Offset+fn.Size]
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x1A000004, // pcalau12i r4, 0
|
|
|
|
|
|
0x02C00084, // addi.d r4, r4, 0
|
|
|
|
|
|
0x1A00001E, // pcalau12i r30, 0
|
|
|
|
|
|
0x28C003C5, // ld.d r5, 0(r30)
|
|
|
|
|
|
0x1A00001E, // pcalau12i r30, 0
|
|
|
|
|
|
0x29C003C6, // st.d r6, 0(r30)
|
|
|
|
|
|
0x4C000020, // jirl r0, r1, 0
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_errors checks the encoder's error paths: undefined labels,
|
2026-09-20 06:44:51 +02:00
|
|
|
|
// invalid register operands and operand-count mismatches. The X0 and
|
|
|
|
|
|
// AMADDW cases follow the oracle: GOARCH=loong64 go tool asm rejects
|
|
|
|
|
|
// `BEQZ X0` (the X bank is not an integer register) and the two-register
|
|
|
|
|
|
// `AMADDW R4, R5` (the AM* family is strictly `val, (addr), result`).
|
2026-08-13 11:24:44 +02:00
|
|
|
|
func TestLOONG64_errors(t *testing.T) {
|
|
|
|
|
|
cases := []string{
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
JMP nowhere
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
BEQZ X0, done
|
|
|
|
|
|
done:
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
ADDV R4
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
FMADDD F0, F1
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
AMADDW R4, R5
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
WORD
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
PRELD 32(R4)
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
`TEXT ·e(SB), NOSPLIT, $0
|
|
|
|
|
|
ALSLW $5, R4, R5, R6
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
}
|
|
|
|
|
|
for i, src := range cases {
|
|
|
|
|
|
fn := firstTextLOONG64(t, src)
|
|
|
|
|
|
if _, _, _, _, _, err := assembleLOONG64(fn); err == nil {
|
|
|
|
|
|
t.Errorf("case %d: expected an error, got none", i)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_pcsp checks the stack-adjustment table of a framed function:
|
|
|
|
|
|
// the prologue raises the SP delta by autosize (in effect from the third
|
|
|
|
|
|
// instruction) and the RET's epilogue restores it to zero, with the pc deltas
|
2026-09-16 23:12:31 +02:00
|
|
|
|
// in MinLC (4) units; byte-identical to `go tool asm`.
|
2026-08-13 11:24:44 +02:00
|
|
|
|
func TestLOONG64_pcsp(t *testing.T) {
|
|
|
|
|
|
cases := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
src string
|
|
|
|
|
|
want []byte
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
"leaf",
|
|
|
|
|
|
`#include "textflag.h"
|
|
|
|
|
|
TEXT ·leaf(SB), NOSPLIT, $8-0
|
|
|
|
|
|
MOVV R4, R5
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
[]byte{0x02, 0x02, 0x20, 0x03, 0x1f, 0x01, 0x00},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"nonleaf",
|
|
|
|
|
|
`#include "textflag.h"
|
|
|
|
|
|
TEXT ·nonleaf(SB), NOSPLIT, $8-0
|
|
|
|
|
|
MOVV R4, R5
|
|
|
|
|
|
JAL (R12)
|
|
|
|
|
|
RET
|
|
|
|
|
|
`,
|
|
|
|
|
|
[]byte{0x02, 0x02, 0x20, 0x05, 0x1f, 0x01, 0x00},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
|
t.Run(c.name, func(t *testing.T) {
|
|
|
|
|
|
f, errs := parser.Parse("pcsp_loong64.s", c.src)
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
|
|
t.Fatalf("parse: %v", errs)
|
|
|
|
|
|
}
|
|
|
|
|
|
img, err := AssembleFileLOONG64(f)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("AssembleFileLOONG64: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := pcspTable(img.Funcs[0], 4); !bytes.Equal(got, c.want) {
|
|
|
|
|
|
t.Errorf("pcsp = % x, want % x", got, c.want)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_sbRefsUndefined checks that a reference to a symbol no GLOBL
|
|
|
|
|
|
// defines assembles into a relocation and is rejected at object emission.
|
|
|
|
|
|
func TestLOONG64_sbRefsUndefined(t *testing.T) {
|
|
|
|
|
|
f, errs := parser.Parse("sb_loong64.s", `#include "textflag.h"
|
|
|
|
|
|
TEXT ·sb(SB), NOSPLIT, $0
|
|
|
|
|
|
MOVV missing(SB), R4
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
|
|
t.Fatalf("parse: %v", errs)
|
|
|
|
|
|
}
|
|
|
|
|
|
img, err := AssembleFileLOONG64(f)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("AssembleFileLOONG64: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(img.Funcs[0].Relocs) != 2 {
|
|
|
|
|
|
t.Fatalf("relocs = %d, want the HI/LO pair", len(img.Funcs[0].Relocs))
|
|
|
|
|
|
}
|
|
|
|
|
|
if _, err := img.GOObjectLOONG64("p", "sb_loong64.s"); err == nil {
|
|
|
|
|
|
t.Error("expected an unknown-symbol error at emission")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-19 23:49:13 +02:00
|
|
|
|
// TestLOONG64_movImmToFp checks the immediate-to-FP move: MOVW $c, Fd is the
|
|
|
|
|
|
// only spelling the toolchain accepts, expanding to ori (or addi.w for the
|
|
|
|
|
|
// negative span) into R30 plus movgr2fr.w. The pinned words are the
|
|
|
|
|
|
// toolchain's own bytes; the other widths and out-of-range constants are
|
|
|
|
|
|
// illegal combinations there and are diagnosed here.
|
2026-08-13 11:24:44 +02:00
|
|
|
|
func TestLOONG64_movImmToFp(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·fpmov(SB), NOSPLIT, $0
|
2026-09-19 23:49:13 +02:00
|
|
|
|
MOVW $0x1, F0
|
2026-08-13 11:24:44 +02:00
|
|
|
|
MOVW $0x2, F4
|
2026-09-19 23:49:13 +02:00
|
|
|
|
MOVW $-1, F4
|
2026-08-13 11:24:44 +02:00
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
want := []byte{
|
2026-09-19 23:49:13 +02:00
|
|
|
|
0x1e, 0x04, 0x80, 0x03, // ori r30, r0, 1
|
|
|
|
|
|
0xc0, 0xa7, 0x14, 0x01, // movgr2fr.w f0, r30
|
|
|
|
|
|
0x1e, 0x08, 0x80, 0x03, // ori r30, r0, 2
|
|
|
|
|
|
0xc4, 0xa7, 0x14, 0x01, // movgr2fr.w f4, r30
|
|
|
|
|
|
0x1e, 0xfc, 0xbf, 0x02, // addi.w r30, r0, -1
|
|
|
|
|
|
0xc4, 0xa7, 0x14, 0x01, // movgr2fr.w f4, r30
|
2026-08-13 11:24:44 +02:00
|
|
|
|
0x20, 0x00, 0x00, 0x4c, // jirl r0, r1, 0
|
|
|
|
|
|
}
|
|
|
|
|
|
if !bytes.Equal(code, want) {
|
|
|
|
|
|
t.Errorf("code = % x\nwant % x", code, want)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-19 23:49:13 +02:00
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_movImmToFpErrors checks the immediate-to-FP diagnostics: the
|
|
|
|
|
|
// widths the toolchain rejects as illegal combinations, and constants beyond
|
|
|
|
|
|
// the 12-bit ori/addi.w span (the toolchain never materialises a wider
|
|
|
|
|
|
// constant on this path).
|
|
|
|
|
|
func TestLOONG64_movImmToFpErrors(t *testing.T) {
|
|
|
|
|
|
cases := []string{
|
|
|
|
|
|
"MOVV $1, F0",
|
|
|
|
|
|
"MOVF $2, F4",
|
|
|
|
|
|
"MOVD $2, F4",
|
|
|
|
|
|
"MOVW $100000, F1",
|
|
|
|
|
|
"MOVW $-2049, F1",
|
|
|
|
|
|
"MOVW $4096, F1",
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, src := range cases {
|
|
|
|
|
|
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("%s: expected an error, got none", src)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_branch16Unsigned pins the unsigned two-operand branches: with
|
|
|
|
|
|
// one register BLTU/BGEU keep the register-register form against R0 (never
|
|
|
|
|
|
// taken), the toolchain's encoding, where a beqz would test the wrong
|
|
|
|
|
|
// condition; the three-operand forms are unchanged.
|
|
|
|
|
|
func TestLOONG64_branch16Unsigned(t *testing.T) {
|
|
|
|
|
|
fn := firstTextLOONG64(t, `#include "textflag.h"
|
|
|
|
|
|
TEXT ·u(SB), NOSPLIT, $0
|
|
|
|
|
|
BLTU R4, done
|
|
|
|
|
|
BGEU R5, done
|
|
|
|
|
|
BLTU R6, R7, done
|
|
|
|
|
|
BGEU R8, R9, done
|
|
|
|
|
|
done:
|
|
|
|
|
|
RET
|
|
|
|
|
|
`)
|
|
|
|
|
|
code := assembleLOONG64Helper(t, fn)
|
|
|
|
|
|
wantWords(t, code,
|
|
|
|
|
|
0x68001080, // bltu r4, r0, +4
|
|
|
|
|
|
0x6C000CA0, // bgeu r5, r0, +3
|
|
|
|
|
|
0x680008C7, // bltu r6, r7, +2
|
|
|
|
|
|
0x6C000509, // bgeu r8, r9, +1
|
|
|
|
|
|
0x4C000020, // jirl r0, r1, 0
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestLOONG64_bitFieldRange checks the BSTRINS/BSTRPICK bit-number
|
|
|
|
|
|
// validation, mirroring the toolchain's "illegal bit number" rule: 0..31 for
|
|
|
|
|
|
// the .w forms, 0..63 for the .d forms, and lsb <= msb.
|
|
|
|
|
|
func TestLOONG64_bitFieldRange(t *testing.T) {
|
|
|
|
|
|
cases := []string{
|
|
|
|
|
|
"BSTRINSW $32, R4, $0, R5",
|
|
|
|
|
|
"BSTRPICKW $31, R4, $32, R5",
|
|
|
|
|
|
"BSTRINSV $64, R4, $0, R5",
|
|
|
|
|
|
"BSTRPICKV $3, R4, $4, R5",
|
|
|
|
|
|
"BSTRINSW $-1, R4, $0, R5",
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, src := range cases {
|
|
|
|
|
|
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("%s: expected an error, got none", src)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|