Files
gasm-sdk/asm/riscv_compressed_test.go
T

142 lines
4.1 KiB
Go
Raw Normal View History

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package asm
import (
"os"
"path/filepath"
"testing"
)
// TestRISCVCompressedDifferential proves the explicit compressed-instruction
// mnemonics against the toolchain: the "C" extension block of the toolchain's
// own testdata (every stack, register, control-transfer, constant-generation,
// shift and register-register spelling it carries) assembled by gasm and by
// go tool asm must agree halfword for halfword. The lines are the oracle's
// own, so a wrong bit pattern, scale or register field names itself through
// the first differing halfword.
func TestRISCVCompressedDifferential(t *testing.T) {
src := `#include "textflag.h"
TEXT ·compressed(SB), NOSPLIT, $0
CLWSP 20(SP), X10
CLDSP 24(SP), X10
CFLDSP 32(SP), F10
CSWSP X10, 20(SP)
CSDSP X10, 24(SP)
CFSDSP F10, 32(SP)
CLW 20(X10), X11
CLD 24(X10), X11
CFLD 32(X10), F11
CSW X11, 20(X10)
CSD X11, 24(X10)
CFSD F11, 32(X10)
CJ 1(PC)
CJR X5
CJALR X5
CBEQZ X10, 1(PC)
CBNEZ X10, 1(PC)
CLI $-32, X5
CLI $31, X5
CLUI $-32, X5
CLUI $31, X5
CADD $-32, X5
CADD $31, X5
CADDI $-32, X5
CADDI $31, X5
CADDW $-32, X5
CADDW $31, X5
CADDIW $-32, X5
CADDIW $31, X5
CADDI16SP $-512, SP
CADDI16SP $496, SP
CADDI4SPN $4, SP, X10
CADDI4SPN $1020, SP, X10
CSLLI $63, X5
CSRLI $63, X10
CSRAI $63, X10
CAND $-32, X10
CAND $31, X10
CANDI $-32, X10
CANDI $31, X10
CMV X6, X5
CADD X9, X8
CAND X9, X8
COR X9, X8
CXOR X9, X8
CSUB X9, X8
CADDW X9, X8
CSUBW X9, X8
CNOP
CEBREAK
RET
`
dir := t.TempDir()
path := filepath.Join(dir, "compressed_riscv64.s")
if err := os.WriteFile(path, []byte(src), 0o644); err != nil {
t.Fatal(err)
}
assertRISCVDifferential(t, path, src, "compressed")
}
// TestRISCVCompressedRange pins the compressed immediate and offset ranges at
// the toolchain's own boundaries: a stack load off the scale or range, a
// non-prime register in the CL/CS and CA shapes, a zero immediate where the
// toolchain forbids one and a CLUI into SP are all rejected on sight.
func TestRISCVCompressedRange(t *testing.T) {
asmOne := func(t *testing.T, stmt string) error {
t.Helper()
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·c(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
_, _, _, _, _, _, err := assembleRISCV(fn)
return err
}
for _, s := range []string{
"CLWSP $0(SP), X10", // never spelled; the parser rejects the shape
} {
if err := asmOne(t, s); err == nil {
t.Errorf("%s must be rejected", s)
}
}
for _, s := range []string{
"CLWSP 21(SP), X10", // not a multiple of 4
"CLWSP 256(SP), X10", // out of range
"CLDSP 25(SP), X10", // not a multiple of 8
"CFLDSP 33(SP), F10", // not a multiple of 8
"CLWSP 20(X10), X10", // base must be SP
"CLW 22(X10), X11", // not a multiple of 4
"CLW 128(X10), X11", // out of range
"CLW 20(X5), X11", // base must be prime
"CLW 20(X10), X5", // rd must be prime
"CLI $32, X5", // out of range
"CLI $-33, X5", // out of range
"CLUI $0, X5", // zero
"CLUI $3, X2", // SP as destination
"CSLLI $0, X5", // zero shift
"CSLLI $64, X5", // out of range
"CSRLI $63, X5", // rd must be prime
"CANDI $63, X10", // out of range
"CMV X0, X5", // X0 in rd
"CMV X5, X0", // X0 in rs2
"CADD X5, X0", // X0 in rs2
"CSUB X5, X5", // X0-free but rd prime required
"CADDI4SPN $4, X5, X10", /* base must be SP */
} {
if err := asmOne(t, s); err == nil {
t.Errorf("%s must be rejected, as go tool asm rejects it", s)
}
}
for _, s := range []string{
"CLWSP 20(SP), X10", "CLDSP 24(SP), X10",
"CLW 20(X10), X11", "CSD X11, 24(X10)",
"CLI $-32, X5", "CLUI $-32, X5",
"CADD $-32, X5", "CADDIW $31, X5",
"CSLLI $63, X5", "CSRLI $63, X10", "CANDI $-32, X10",
"CMV X6, X5", "CADD X9, X8", "CSUB X9, X8", "CADDW X9, X8",
"CADDI16SP $496, SP", "CADDI4SPN $1020, SP, X10",
} {
if err := asmOne(t, s); err != nil {
t.Errorf("%s must assemble: %v", s, err)
}
}
}