// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package asm import ( "bytes" "encoding/binary" "testing" "sourcedock.dev/petrbalvin/gasm-devkit/ast" "sourcedock.dev/petrbalvin/gasm-devkit/parser" ) // firstTextLOONG64 parses assembly source and returns the first TEXT body. func firstTextLOONG64(t *testing.T, src string) *ast.Text { t.Helper() f, errs := parser.Parse("f_loong64.s", src) if len(errs) > 0 { t.Fatalf("parse: %v", errs) } for _, d := range f.Decls { if fn, ok := d.(*ast.Text); ok { return fn } } t.Fatal("no TEXT found") return nil } // assembleLOONG64Helper assembles one TEXT function and returns its bytes. func assembleLOONG64Helper(t *testing.T, fn *ast.Text) []byte { t.Helper() code, _, _, _, _, err := assembleLOONG64(fn) if err != nil { t.Fatalf("assemble: %v", err) } return code } // wantWords checks that code matches the expected little-endian words. func wantWords(t *testing.T, code []byte, want ...uint32) { t.Helper() got := make([]uint32, 0, len(code)/4) for i := 0; i+4 <= len(code); i += 4 { got = append(got, binary.LittleEndian.Uint32(code[i:])) } if len(got) != len(want) { t.Fatalf("word count = %d, want %d\ncode: % x", len(got), len(want), code) } for i := range want { if got[i] != want[i] { t.Errorf("word %d = %08x, want %08x", i, got[i], want[i]) } } } func TestLOONG64_add(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·add(SB), NOSPLIT, $0-24 MOVV a+0(FP), R4 MOVV b+8(FP), R5 ADDV R5, R4, R4 MOVV R4, ret+16(FP) RET `) code := assembleLOONG64Helper(t, fn) // 5 instructions: two ld.d, add.d, st.d, jirl r0, r1, 0. wantWords(t, code, 0x28C02064, // ld.d r4, 8(r3) 0x28C04065, // ld.d r5, 16(r3) 0x00109484, // add.d r4, r4, r5 0x29C06064, // st.d r4, 24(r3) 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_arithmetic(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·arith(SB), NOSPLIT, $0 ADDV R4, R5, R6 SUBV R7, R8, R9 MULV R10, R11, R12 DIVV R13, R14, R15 AND R16, R17, R18 OR R18, R19, R20 XOR R20, R21, R2 SLLV R2, R23, R24 SRLV R24, R25, R26 SRAV R26, R27, R28 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x001090A6, // add.d r6, r5, r4 0x00119D09, // sub.d r9, r8, r7 0x001DA96C, // mul.d r12, r11, r10 0x002235CF, // div.d r15, r14, r13 0x0014C232, // and r18, r17, r16 0x00154A74, // or r20, r19, r18 0x0015D2A2, // xor r2, r21, r20 0x00188AF8, // sll.d r24, r23, r2 0x0019633A, // srl.d r26, r25, r24 0x0019EB7C, // sra.d r28, r27, r26 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_immediates(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·imm(SB), NOSPLIT, $0 ADDV $42, R4, R5 ADDV $-8, R6 AND $0xff, R7, R8 OR $1, R9, R10 SGT $100, R13, R14 SLLV $4, R15, R16 MOVV $0x12345, R17 MOVV $0, R18 MOVW $0, R19 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x02C0A885, // addi.d r5, r4, 42 0x02FFE0C6, // addi.d r6, r6, -8 0x0343FCE8, // andi r8, r7, 0xff 0x0380052A, // ori r10, r9, 1 0x020191AE, // slti r14, r13, 100 0x004111F0, // slli.d r16, r15, 4 0x14000251, // lu12i.w r17, 0x12 0x038D1631, // ori r17, r17, 0x345 0x00150012, // or r18, r0, r0 0x00170013, // sll.w r19, r0, r0 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_loadStore(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·mem(SB), NOSPLIT, $0 MOVV (R4), R5 MOVV R5, (R6) MOVW 8(R7), R8 MOVB R9, -4(R10) MOVV (R11)(R12), R13 MOVV R14, (R15)(R16) RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x28C00085, // ld.d r5, 0(r4) 0x29C000C5, // st.d r5, 0(r6) 0x288020E8, // ld.w r8, 8(r7) 0x293FF149, // st.b r9, -4(r10) 0x380C316D, // ldx.d r13, r11, r12 0x381C41EE, // stx.d r14, r15, r16 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_branches(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·br(SB), NOSPLIT, $0 BEQ R4, R5, done BNE R6, R7, skip BLT R8, R9, done BGE R10, R11, done BLTU R12, R13, done BGEU R14, R15, done skip: JMP done done: RET `) code := assembleLOONG64Helper(t, fn) // skip is at 0x18 (6 words), done at 0x1c. wantWords(t, code, 0x58001C85, // beq r5, r4, +7 0x5C0018C7, // bne r7, r6, +6 0x60001509, // blt r9, r8, +5 0x6400114B, // bge r11, r10, +4 0x68000D8D, // bltu r13, r12, +3 0x6C0009CF, // bgeu r15, r14, +2 0x50000400, // b done (+1, chain-folded through skip) 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_frame(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·f(SB), NOSPLIT, $32-8 MOVV R4, R5 MOVV arg+0(FP), R6 MOVV R7, local-8(SP) MOVV local-8(SP), R8 MOVV R9, ret+0(FP) RET `) code := assembleLOONG64Helper(t, fn) // autosize = align8(32+8) = 40; prologue stores LR at -40(SP), // opens the frame, stores LR again at 0(SP). The function is a leaf // (no calls), so the epilogue skips the LR restore. FP args are at // autosize+8; SP locals at autosize+offset. wantWords(t, code, 0x29FF6061, // st.d r1, -40(r3) 0x02FF6063, // addi.d r3, r3, -40 0x29C00061, // st.d r1, 0(r3) 0x00150085, // or r5, r4, r0 0x28C0C066, // ld.d r6, 48(r3) arg+0(FP) → 0+40+8 0x29C08067, // st.d r7, 32(r3) local-8(SP) → 40-8 0x28C08068, // ld.d r8, 32(r3) 0x29C0C069, // st.d r9, 48(r3) ret+0(FP) → 0+40+8 0x02C0A063, // addi.d r3, r3, 40 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_jumpChain(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·jc(SB), NOSPLIT, $0 JMP a a: JMP b b: RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x50000800, // b +2 (a, chain-folded to b) 0x50000400, // b +1 (b) 0x4C000020, // jirl r0, r1, 0 ) } func TestLOONG64_dconClasses(t *testing.T) { cases := []struct { v int64 word int // expected word count }{ {0x123456789, 3}, // lu12i.w + ori + lu32i.d {-1, 2}, // addi.d + lu52i.d (the MOV path handles -1 earlier) {0x1000000000000, 2}, // addi.w + lu32i.d {0x123456789abcdef0, 4}, // full sequence {0xFFFFFFFFF, 2}, // lu12i.w + ori {0x1234567800000000, 3}, // addi.w + lu32i.d + lu52i.d } for _, c := range cases { if n := len(l64DconMovWords(0, c.v)); n != c.word { t.Errorf("0x%x: %d words, want %d", c.v, n, c.word) } } } func TestLOONG64_regNames(t *testing.T) { cases := map[string]int{ "R0": 0, "R31": 31, "F0": 0, "F31": 31, "FCC0": 0, "FCC7": 7, "FCSR0": 0, "FCSR3": 3, "ZERO": 0, "RA": 1, "SP": 3, "g": 22, "G": 22, "R32": -1, "FCC8": -1, "X0": -1, "R": -1, "TMP": 30, "CTXT": 29, } for name, want := range cases { if got := loong64RegNum(name); got != want { t.Errorf("loong64RegNum(%q) = %d, want %d", name, got, want) } } // The X/V spellings name the LSX/LASX vector banks, a register class of // their own: the oracle (GOARCH=loong64 go tool asm) rejects `BEQZ X0` // with "unrecognized instruction" while assembling `VADDV V0, V1, V2` // and `XVADDV X0, X1, X2`, so loong64RegNum stays strict and the vector // operands resolve through loong64VecRegNum only. vecCases := map[string]int{ "V0": 0, "V31": 31, "X0": 0, "X31": 31, "R4": -1, "F0": -1, "FCC0": -1, "V32": -1, "X32": -1, "V": -1, "X": -1, } for name, want := range vecCases { if got := loong64VecRegNum(name); got != want { t.Errorf("loong64VecRegNum(%q) = %d, want %d", name, got, want) } } } func TestLOONG64_bytesEqualGroundTruth(t *testing.T) { // A spot-check that assembleLOONG64 emits the same bytes the Go // toolchain does for a small kernel (the full comparison lives in // verify's TestGroundTruthLOONG64). src := `#include "textflag.h" TEXT ·k(SB), NOSPLIT, $0-0 ADDV R4, R5, R6 MOVV $0x100000, R7 BEQ R6, R7, done JMP done done: RET ` fn := firstTextLOONG64(t, src) code := assembleLOONG64Helper(t, fn) want := []byte{ 0xa6, 0x90, 0x10, 0x00, // add.d r6, r5, r4 0x07, 0x20, 0x00, 0x14, // lu12i.w r7, 0x100 0xc7, 0x08, 0x00, 0x58, // beq r7, r6, +2 (done) 0x00, 0x04, 0x00, 0x50, // b +1 (done) 0x20, 0x00, 0x00, 0x4c, // jirl r0, r1, 0 } if !bytes.Equal(code, want) { t.Errorf("code = % x\nwant % x", code, want) } } // TestLOONG64IndirectBranch pins the indirect branch encodings: JMP (Rj) and // JAL (Rj) lower to jirl, and the raw JIRL spelling encodes the written // offset (the Go loong64 assembler deletes raw JIRL instructions entirely, // so this form is a gasm-only superset with faithful semantics). func TestLOONG64IndirectBranch(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·f(SB), NOSPLIT, $0-0 JMP (R4) JIRL R0, R4, 8 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x4C000080, // jirl r0, r4, 0 0x4C002080, // jirl r0, r4, 8 0x4C000020, // jirl r0, r1, 0 (RET) ) // JAL (R5) links, so the toolchain gives the function its autosize-8 // prologue and epilogue around the call and the closing RET. fn = firstTextLOONG64(t, `#include "textflag.h" TEXT ·f(SB), NOSPLIT, $0-0 JAL (R5) RET `) code = assembleLOONG64Helper(t, fn) wantWords(t, code, 0x29FFE061, // st.d r1, -8(r3) (prologue saves RA below the new SP) 0x02FFE063, // addi.d r3, r3, -8 (prologue opens the frame) 0x29C00061, // st.d r1, 0(r3) (prologue saves RA at SP) 0x4C0000A1, // jirl r1, r5, 0 0x28C00061, // ld.d r1, 0(r3) (epilogue restores RA) 0x02C02063, // addi.d r3, r3, 8 0x4C000020, // jirl r0, r1, 0 (RET) ) } // TestLOONG64_vector pins the LSX/LASX slice against words read off // GOARCH=loong64 go tool asm (cross-checked against the toolchain's own // loong64enc1.s): the three-register forms, the immediate forms with their // biases, the vector-to-condition forms, lane popcount, the FP conversion, // FSEL and the VMOVQ move family. func TestLOONG64_vector(t *testing.T) { t.Run("three-register and immediate forms", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VADDV V1, V2, V3 VADDW V1, V2, V3 VADDV V2, V1 VANDV V1, V2 VXORV V1, V2, V3 VSEQB V1, V2, V3 VSEQV V1, V2, V3 VSRAB V1, V2, V3 VROTRW V1, V2, V3 VANDB $0, V2, V3 VANDB $255, V2 VSEQB $3, V2, V3 VSEQV $15, V2, V3 VSEQV $-15, V2, V3 VSRAB $7, V1, V2 VROTRW $16, V1, V2 VPCNTV V1, V2 XVADDV X1, X2, X3 XVXORV X1, X2, X3 XVSEQB X1, X2, X3 XVPCNTV X1, X2 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x700B8443, // vadd.v v3, v2, v1 0x700B0443, // vadd.w 0x700B8821, // vadd.v v1, v1, v2 (two-operand form) 0x71260442, // vand.v v2, v2, v1 0x71270443, // vxor.v 0x70000443, // vseq.b 0x70018443, // vseq.d 0x70EC0443, // vsra.b 0x70EF0443, // vrotr.w 0x73D00043, // vandi.b v3, v2, 0 0x73D3FC42, // vandi.b v2, v2, 255 (two-operand form) 0x72800C43, // vseqi.b v3, v2, 3 0x7281BC43, // vseqi.d v3, v2, 15 0x7281C443, // vseqi.d v3, v2, -15 (7-bit two's complement) 0x73343C22, // vsrai.b v2, v1, 7 (encoded as 7+8) 0x72A0C022, // vrotri.w v2, v1, 16 0x729C2C22, // vpcnt.d v2, v1 0x740B8443, // xvadd.d x3, x2, x1 0x75270443, // xvxor.d 0x74000443, // xvseq.b 0x769C2C22, // xvpcnt.d x2, x1 0x4C000020, ) }) t.Run("vector-to-condition", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VSETNEV V1, FCC0 VSETANYEQB V1, FCC0 VSETANYEQV V2, FCC0 VSETALLNEV V0, FCC0 XVSETNEV X1, FCC0 XVSETALLNEV X1, FCC0 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x729C9C20, // vsetnez.d fcc0, v1 0x729CA020, // vsetanyeqz.b 0x729CAC40, // vsetanyeqz.d 0x729CBC00, // vsetallnez.d 0x769C9C20, // xvsetnez.d 0x769CBC20, // xvsetallnez.d 0x4C000020, ) }) t.Run("FP convert and FSEL", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 FFINTDV F0, F1 FSEL FCC0, F3, F4, F3 FSEL FCC1, F1, F2 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x011D2801, // ffint.d.v f1, f0 0x0D000C83, // fsel f3, f4, f3, fcc0 0x0D008442, // fsel f2, f2, f1, fcc1 0x4C000020, ) }) t.Run("VMOVQ move family", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VMOVQ V1, V9 VMOVQ (R4), V2 VMOVQ 16(R4), V2 VMOVQ V0, (R4) VMOVQ V0, 32(R4) VMOVQ (R4)(R7), V3 VMOVQ V3, (R4)(R7) VMOVQ R6, V0.B16 VMOVQ R6, V12.W4 VMOVQ (R4), V4.W4 XVMOVQ X3, X7 XVMOVQ (R4), X2 XVMOVQ X0, (R4) XVMOVQ (R4)(R7), X4 XVMOVQ X0, (R4)(R7) XVMOVQ R6, X0.B32 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x732D0029, // vori.b v9, v1, 0 (register move) 0x2C000082, // vld v2, r4, 0 0x2C004082, // vld v2, r4, 16 0x2C400080, // vst v0, r4, 0 0x2C408080, // vst v0, r4, 32 0x38401C83, // vldx v3, r4, r7 0x38441C83, // vstx v3, r4, r7 0x729F00C0, // vreplgr2vr.b v0, r6 0x729F08CC, // vreplgr2vr.w v12, r6 0x30200084, // vldrepl.w v4, r4, 0 0x772D0067, // xvori.b x7, x3, 0 0x2C800082, // xvld x2, r4, 0 0x2CC00080, // xvst x0, r4, 0 0x38481C84, // xvldx x4, r4, r7 0x384C1C80, // xvstx x0, r4, r7 0x769F00C0, // xvreplgr2vr.b x0, r6 0x4C000020, ) }) t.Run("element extract and insert", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VMOVQ V0.V[0], R10 VMOVQ V6.V[1], R8 VMOVQ R9, V1.V[0] XVMOVQ X0.V[0], R10 XVMOVQ X5.W[7], R7 XVMOVQ R4, X7.V[3] RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x72EFF00A, // vpickve2gr.d r10, v0, 0 0x72EFF4C8, // vpickve2gr.d r8, v6, 1 0x72EBF121, // vinsgr2vr.d v1, r9, 0 0x76EFE00A, // xvpickve2gr.d r10, x0, 0 0x76EFDCA7, // xvpickve2gr.w r7, x5, 7 0x76EBEC87, // xvinsgr2vr.d x7, r4, 3 0x4C000020, ) }) // The integer and FP add/subtract families with their saturating pairs // and immediate spellings (loong64enc1.s words). t.Run("add and subtract families", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VADDB V1, V2, V3 VADDF V1, V2, V3 VADDD V1, V2, V3 VSUBD V1, V2, V3 VSADDV V1, V2, V3 VSSUBVU V1, V2, V3 VADDBU $1, V2, V1 VADDBU $1, V2 VSUBVU $31, V2 XVSADDV X3, X2, X1 XVSUBD X1, X2, X3 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x700A0443, // vadd.b 0x71308443, // vadd.f 0x71310443, // vadd.d 0x71330443, // vsub.d 0x70478443, // vsadd.v 0x704D8443, // vssub.u.d 0x728A0441, // vaddi.bu v1, v2, 1 0x728A0442, // vaddi.bu v2, v2, 1 (two-operand form) 0x728DFC42, // vsubi.du v2, v2, 31 (two-operand form) 0x74478C41, // xvsadd.d x1, x2, x3 0x75330443, // xvsub.d x3, x2, x1 0x4C000020, ) }) // The multiply, divide and accumulate families. t.Run("multiply and divide families", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VMULV V1, V2, V3 VMUHHU V1, V2, V3 VDIVBU V1, V2, V3 VMODV V1, V2, V3 VMADDB V1, V2, V3 VMSUBV V1, V2, V3 VMULWEVHB V1, V2, V3 VMULWODQV V1, V2, V3 VMADDWEVHBUB V1, V2, V3 XVDIVD X1, X2, X3 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x70858443, // vmul.v 0x70888443, // vmuh.u.d 0x70E40443, // vdiv.u.b 0x70E38443, // vmod.d 0x70A80443, // vmadd.b 0x70AB8443, // vmsub.d 0x70900443, // vmulwev.h.b 0x70938443, // vmulwod.q.d 0x70BC0443, // vmaddwev.h.bu.b 0x753B0443, // xvdiv.d 0x4C000020, ) }) // The shift, bit and interleave families in register and immediate // spellings, with the width-coded shift immediates. t.Run("shift, bit and interleave families", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VSLLV V1, V2, V3 VROTRB V1, V2, V3 VBITCLRV V1, V2, V3 VBITSETW V1, V2, V3 VBITREVV V1, V2, V3 VILVLB V1, V2, V3 VILVHV V1, V2, V3 VSLLB $7, V1, V2 VSLLB $5, V1 VSRLH $15, V1, V2 VSRAW $31, V1, V2 VSRAV $63, V1, V2 VROTRV $63, V1, V2 VBITCLRB $7, V2, V3 VBITREVV $63, V2, V3 VSEQH $-16, V2, V3 VSLTB $1, V2, V3 VSLTHU $31, V2, V3 XVILVLV X3, X2, X1 XVSLLB $7, X2, X1 XVSRAV $63, X2, X1 XVBITREVV $63, X2, X1 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x70E98443, // vsll.d 0x70EE0443, // vrotr.b 0x710D8443, // vbitclr.d 0x710F0443, // vbitset.w 0x71118443, // vbitrev.d 0x711A0443, // vilvl.b 0x711D8443, // vilvh.d 0x732C3C22, // vslli.b v2, v1, 7 0x732C3421, // vslli.b v1, v1, 5 (two-operand form) 0x73307C22, // vsrli.h v2, v1, 15 0x7334FC22, // vsrai.w v2, v1, 31 0x7335FC22, // vsrai.d v2, v1, 63 0x72A1FC22, // vrotri.d v2, v1, 63 0x73103C43, // vbitclri.b v3, v2, 7 0x7319FC43, // vbitrevi.d v3, v2, 63 0x7280C043, // vseqi.h v3, v2, -16 0x72860443, // vslti.b v3, v2, 1 0x7288FC43, // vslti.hu v3, v2, 31 0x751B8C41, // xvilvl.d x1, x2, x3 0x772C3C41, // xvslli.b x1, x2, 7 0x7735FC41, // xvsrai.d x1, x2, 63 0x7719FC41, // xvbitrevi.d x1, x2, 63 0x4C000020, ) }) // The shuffle, select and permutation families, including the // four-register byte shuffle. t.Run("shuffle and permutation families", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VSHUFH V1, V2, V3 VSHUFW V1, V2, V3 VSHUFV V1, V2, V3 VSHUFB V1, V2, V3, V4 XVSHUFB X1, X2, X3, X4 VSHUF4IB $255, V2, V1 VSHUF4IV $15, V2, V1 XVSHUF4IV $15, X1, X2 VEXTRINSB $0x18, V1, V2 XVEXTRINSV $0x81, X1, X2 VPERMIW $0x1B, V1, V2 XVPERMIQ $0x4B, X1, X2 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x717A8443, // vshuf.h 0x717B0443, // vshuf.w 0x717B8443, // vshuf.d 0x0D508864, // vshuf.b v4, v3, v2, v1 0x0D608864, // xvshuf.b 0x7393FC41, // vshuf4i.b v1, v2, 255 0x739C3C41, // vshuf4i.d v1, v2, 15 0x779C3C22, // xvshuf4i.d x2, x1, 15 0x738C6022, // vextrins.b v2, v1, 0x18 0x77820422, // xvextrins.d x2, x1, 0x81 0x73E46C22, // vpermi.w v2, v1, 0x1b 0x77ED2C22, // xvpermi.q x2, x1, 0x4b 0x4C000020, ) }) // The vector FP families, the unary spellings, the compare-to-flag // additions and the scalar int/float conversions. t.Run("FP and conversion families", func(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·v(SB), NOSPLIT, $0 VADDF V1, V2, V3 VMULF V1, V2, V3 VFCLASSD V1, V2 VFSQRTF V1, V2 VFRECIPD V1, V2 VFRSQRTF V1, V2 VFRINTF V1, V2 VFRINTRNED V1, V2 VNEGB V1, V2 VPCNTB V1, V2 XVNEGV X2, X1 XVPCNTW X3, X2 XVFRINTRNEF X1, X2 VSETEQV V1, FCC0 VSETANYEQH V1, FCC0 VSETALLNEB V1, FCC0 XVSETALLNEW X1, FCC0 FFINTFW F0, F1 FTINTVD F0, F1 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x71308443, // vfadd.s 0x71388443, // vfmul.s 0x729CD822, // vfclass.d 0x729CE422, // vfsqrt.s 0x729CF822, // vfrecip.d 0x729D0422, // vfrsqrt.s 0x729D3422, // vfrint.s 0x729D7822, // vfrintne.s 0x729C3022, // vneg.b 0x729C2022, // vpcnt.b 0x769C3C41, // xvneg.d x1, x2 0x769C2862, // xvpcnt.w x2, x3 0x769D7422, // xvfrintne.s x2, x1 0x729C9820, // vseteqz.d fcc0, v1 0x729CA420, // vsetanyeqz.h 0x729CB020, // vsetallnez.b 0x769CB820, // xvsetallnez.w 0x011D1001, // ffint.s.w f1, f0 0x011B2801, // ftint.l.d f1, f0 0x4C000020, ) }) } // TestLOONG64_vectorErrors pins the register-class and range diagnostics of // the vector slice; each shape is rejected by the oracle as well // (GOARCH=loong64 go tool asm). func TestLOONG64_vectorErrors(t *testing.T) { cases := []string{ // Integer registers in vector positions. `TEXT ·e(SB), NOSPLIT, $0 VADDV R4, R5, R6 RET `, // Crossed banks: LSX spellings take V, LASX spellings X. `TEXT ·e(SB), NOSPLIT, $0 VADDV X1, X2, X3 RET `, `TEXT ·e(SB), NOSPLIT, $0 XVADDV V1, V2, V3 RET `, // The LASX bank has no .b/.h element forms. `TEXT ·e(SB), NOSPLIT, $0 XVMOVQ R4, X2.B[0] RET `, // Immediate ranges. `TEXT ·e(SB), NOSPLIT, $0 VANDB $256, V2 RET `, `TEXT ·e(SB), NOSPLIT, $0 VSEQB $16, V2, V3 RET `, `TEXT ·e(SB), NOSPLIT, $0 VROTRW $32, V1, V2 RET `, `TEXT ·e(SB), NOSPLIT, $0 VADDVU $32, V2 RET `, `TEXT ·e(SB), NOSPLIT, $0 VSEQV $32, V2, V3 RET `, `TEXT ·e(SB), NOSPLIT, $0 VSHUF4IV $16, V2, V1 RET `, `TEXT ·e(SB), NOSPLIT, $0 VEXTRINSB $256, V1, V2 RET `, `TEXT ·e(SB), NOSPLIT, $0 VSLTV $-17, V2, V3 RET `, // VSHUFB wants four vector registers. `TEXT ·e(SB), NOSPLIT, $0 VSHUFB V1, V2, V3 RET `, // The FCC forms still refuse vector registers. `TEXT ·e(SB), NOSPLIT, $0 VSETEQV V1, V2 RET `, // VSET* wants an FCC flag, not a vector register. `TEXT ·e(SB), NOSPLIT, $0 VSETNEV V1, V2 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_dbarAtomics pins the _dbar (acquire/release) AMO variants. // The oracle words come from GOARCH=loong64 go tool objdump of kernels // assembled with go tool asm, and match the toolchain's loong64enc1.s. func TestLOONG64_dbarAtomics(t *testing.T) { fn := firstTextLOONG64(t, `#include "textflag.h" TEXT ·atoms(SB), NOSPLIT, $0 AMADDDBW R14, (R13), R12 AMADDDBV R14, (R13), R12 AMANDDBW R5, (R4), R6 AMANDDBV R5, (R4), R6 AMORDBW R5, (R4), R0 AMORDBV R5, (R4), R6 AMSWAPDBW R5, (R4), R6 AMCASDBV R6, (R4), R5 RET `) code := assembleLOONG64Helper(t, fn) wantWords(t, code, 0x386A39AC, // amadd_db.w r12, r13, r14 0x386AB9AC, // amadd_db.d 0x386B1486, // amand_db.w r6, r4, r5 0x386B9486, // amand_db.d 0x386C1480, // amor_db.w r0, r4, r5 0x386C9486, // amor_db.d 0x38691486, // amswap_db.w 0x385B9885, // amcas_db.w 0x4C000020, ) }