Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75e9fd771b | ||
|
|
863926abd6 | ||
|
|
241e7256f6 | ||
|
|
6556b85abf | ||
|
|
289cabe993 | ||
|
|
d6cf7cfa44 | ||
|
|
4cc2f0eba5 |
@@ -245,3 +245,104 @@ func main() {
|
|||||||
t.Error("binary does not contain expected symbol")
|
t.Error("binary does not contain expected symbol")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGOObjectAARCH64DataSymbolLink does for symbol-valued DATA fields what
|
||||||
|
// the rt0 files do ("DATA _rt0…lib+0(SB)/8, $_rt0…lib(SB)"): the gasm object
|
||||||
|
// carries an R_ADDR against the file's own TEXT symbol, the toolchain links
|
||||||
|
// it, and the binary is checked for the symbol (no arm64 host to run it).
|
||||||
|
func TestGOObjectAARCH64DataSymbolLink(t *testing.T) {
|
||||||
|
goBin, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("no Go toolchain available")
|
||||||
|
}
|
||||||
|
dir := t.TempDir()
|
||||||
|
asmSrc := `#include "textflag.h"
|
||||||
|
GLOBL entry(SB), NOPTR, $8
|
||||||
|
DATA entry+0(SB)/8, $·keepme(SB)
|
||||||
|
|
||||||
|
TEXT ·keepme(SB), NOSPLIT, $0-0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·entryptr(SB), NOSPLIT, $0-8
|
||||||
|
MOVD entry+0(SB), R4
|
||||||
|
MOVD R4, ret+0(FP)
|
||||||
|
RET
|
||||||
|
`
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "main_arm64.s"), []byte(asmSrc), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mainSrc := `package main
|
||||||
|
|
||||||
|
func keepme()
|
||||||
|
func entryptr() uintptr
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if entryptr() == 0 {
|
||||||
|
panic("the entry word is empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte(mainSrc), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module a64dlink\n\ngo 1.21\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
build := exec.Command(goBin, "build", "-x", "-work", "-o", filepath.Join(dir, "prog"), ".")
|
||||||
|
build.Dir = dir
|
||||||
|
build.Env = append(os.Environ(), "GOARCH=arm64")
|
||||||
|
buildLog, err := build.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||||
|
}
|
||||||
|
var work, linkLine, asmObj string
|
||||||
|
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(line, "WORK="):
|
||||||
|
work = strings.TrimPrefix(line, "WORK=")
|
||||||
|
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_arm64.s") && !strings.Contains(line, "-gensymabis"):
|
||||||
|
asmObj = fieldAfter(line, "-o")
|
||||||
|
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
||||||
|
linkLine = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if work == "" || asmObj == "" || linkLine == "" {
|
||||||
|
t.Skipf("could not parse build log (work=%q asmObj=%q link=%q)", work, asmObj, linkLine)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(work)
|
||||||
|
asmObj = strings.ReplaceAll(asmObj, "$WORK", work)
|
||||||
|
linkLine = strings.ReplaceAll(linkLine, "$WORK", work)
|
||||||
|
|
||||||
|
src, err := os.ReadFile(filepath.Join(dir, "main_arm64.s"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, errs := parser.Parse("main_arm64.s", string(src))
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFileARM64(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AssembleFileARM64: %v", err)
|
||||||
|
}
|
||||||
|
gasmObj, err := img.GOObjectAARCH64("a64dlink", "main_arm64.s")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GOObjectAARCH64: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(asmObj, gasmObj, 0o644); err != nil {
|
||||||
|
t.Fatalf("write gasm object: %v", err)
|
||||||
|
}
|
||||||
|
linkCmd := exec.Command("bash", "-c", "cd "+dir+" && "+linkLine)
|
||||||
|
linkCmd.Env = append(os.Environ(), "GOARCH=arm64")
|
||||||
|
if out, err := linkCmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("re-link with gasm object: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
binData, err := os.ReadFile(filepath.Join(dir, "prog"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(binData), "keepme") {
|
||||||
|
t.Error("binary does not contain the keepme symbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2856,8 +2856,14 @@ func encodeARM64Sys(mnem string, ops []*ast.Operand) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return a64wordLE(0xd503201f | uint32(v)<<5), nil
|
return a64wordLE(0xd503201f | uint32(v)<<5), nil
|
||||||
case "BTI":
|
case "BTI":
|
||||||
|
// The toolchain requires the landing-pad kind: bare BTI is
|
||||||
|
// rejected ("missing operand"), and only the uppercase C/J/JC
|
||||||
|
// spellings assemble (0xd503245f/49f/4df).
|
||||||
|
if len(ops) != 1 {
|
||||||
|
return nil, fmt.Errorf("%s expects C, J, or JC", mnem)
|
||||||
|
}
|
||||||
op := operandRegName(ops[0])
|
op := operandRegName(ops[0])
|
||||||
base, ok := map[string]uint32{"C": 0xd503245f}[op]
|
base, ok := map[string]uint32{"C": 0xd503245f, "J": 0xd503249f, "JC": 0xd50324df}[op]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s: unknown kind %q", mnem, op)
|
return nil, fmt.Errorf("%s: unknown kind %q", mnem, op)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -675,6 +675,36 @@ func TestArm64AcquireRelease(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestArm64BTI pins the landing-pad family against the toolchain words:
|
||||||
|
// only the uppercase C/J/JC spellings assemble, and bare BTI is a
|
||||||
|
// diagnostic, never a panic.
|
||||||
|
func TestArm64BTI(t *testing.T) {
|
||||||
|
got := arm64Words(t, "\tBTI C\n\tBTI J\n\tBTI JC\n")
|
||||||
|
want := []uint32{
|
||||||
|
0xd503245f, // BTI C
|
||||||
|
0xd503249f, // BTI J
|
||||||
|
0xd50324df, // BTI JC
|
||||||
|
0xd65f03c0, // RET
|
||||||
|
}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatalf("got %d words, want %d", len(got), len(want))
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if got[i] != want[i] {
|
||||||
|
t.Errorf("word %d = %#x, want %#x", i, got[i], want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, src := range []string{"\tBTI\n", "\tBTI c\n", "\tBTI B\n"} {
|
||||||
|
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := AssembleFileARM64(f); err == nil {
|
||||||
|
t.Errorf("BTI spelling %q should be rejected, as go tool asm rejects it", src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestArm64System pins BRK, SVC, the barriers, cache maintenance and the
|
// TestArm64System pins BRK, SVC, the barriers, cache maintenance and the
|
||||||
// system register accesses.
|
// system register accesses.
|
||||||
func TestArm64System(t *testing.T) {
|
func TestArm64System(t *testing.T) {
|
||||||
|
|||||||
+65
-10
@@ -43,6 +43,9 @@ const (
|
|||||||
stInfoShift = 4
|
stInfoShift = 4
|
||||||
|
|
||||||
rX8664PC32 = 2
|
rX8664PC32 = 2
|
||||||
|
// R_X86_64_32 (debug/elf): the absolute 32-bit address of a symbol, the
|
||||||
|
// R_ADDR shape a 4-byte DATA field carries.
|
||||||
|
rX8664Abs32 = 10
|
||||||
// R_X86_64_TPOFF32 (debug/elf): the local-exec TLS offset the stack
|
// R_X86_64_TPOFF32 (debug/elf): the local-exec TLS offset the stack
|
||||||
// guard loads from FS. 20 is R_X86_64_TLSLD, a different relocation.
|
// guard loads from FS. 20 is R_X86_64_TLSLD, a different relocation.
|
||||||
rX8664TPOFF32 = 23
|
rX8664TPOFF32 = 23
|
||||||
@@ -158,6 +161,50 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The data symbols' symbol-valued DATA fields ("DATA s+0(SB)/8,
|
||||||
|
// $other(SB)") become .rela.data entries: an absolute relocation of the
|
||||||
|
// DATA line's width at the field's data-section offset, S + A with no
|
||||||
|
// PC term. Widths 4 and 8 have ELF relocation shapes; narrower fields
|
||||||
|
// cannot hold an address, so they are refused rather than truncated.
|
||||||
|
var dataRelas []elfRela
|
||||||
|
for _, d := range img.DataSyms {
|
||||||
|
for _, r := range d.Relocs {
|
||||||
|
idx, ok := symIdx[r.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("data relocation references unknown symbol %q", r.Name)
|
||||||
|
}
|
||||||
|
var typ uint32
|
||||||
|
switch r.Siz {
|
||||||
|
case 8:
|
||||||
|
typ = rX8664Abs64
|
||||||
|
case 4:
|
||||||
|
typ = rX8664Abs32
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("DATA %q: a symbol value of width %d has no ELF relocation", d.Name, r.Siz)
|
||||||
|
}
|
||||||
|
dataRelas = append(dataRelas, elfRela{
|
||||||
|
off: uint64(d.Offset + r.Off),
|
||||||
|
sym: idx,
|
||||||
|
typ: typ,
|
||||||
|
addend: r.Addend,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section presence: .rela.text only when there are code relocations,
|
||||||
|
// .rela.data only when a DATA line holds a symbol value.
|
||||||
|
hasRela := len(relas) > 0
|
||||||
|
hasDataRela := len(dataRelas) > 0
|
||||||
|
nSections := 6 // NULL, .text, .data, .symtab, .strtab, .shstrtab
|
||||||
|
if hasRela {
|
||||||
|
nSections++
|
||||||
|
}
|
||||||
|
if hasDataRela {
|
||||||
|
nSections++
|
||||||
|
}
|
||||||
|
secSymtab, secStrtab := 3, 4
|
||||||
|
secShstr := nSections - 1
|
||||||
|
|
||||||
// Serialise the string tables.
|
// Serialise the string tables.
|
||||||
stNames := newElfStrtab()
|
stNames := newElfStrtab()
|
||||||
for _, s := range syms {
|
for _, s := range syms {
|
||||||
@@ -167,19 +214,13 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
for _, n := range []string{".text", ".data", ".symtab", ".strtab", ".rela.text", ".shstrtab"} {
|
for _, n := range []string{".text", ".data", ".symtab", ".strtab", ".rela.text", ".shstrtab"} {
|
||||||
stSections.add(n)
|
stSections.add(n)
|
||||||
}
|
}
|
||||||
|
if hasDataRela {
|
||||||
|
stSections.add(".rela.data")
|
||||||
|
}
|
||||||
for _, n := range dwarfSectionNames {
|
for _, n := range dwarfSectionNames {
|
||||||
stSections.add(n)
|
stSections.add(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section presence: .rela.text only when there are relocations.
|
|
||||||
hasRela := len(relas) > 0
|
|
||||||
nSections := 6 // NULL, .text, .data, .symtab, .strtab, .shstrtab
|
|
||||||
if hasRela {
|
|
||||||
nSections = 7
|
|
||||||
}
|
|
||||||
secSymtab, secStrtab := 3, 4
|
|
||||||
secShstr := nSections - 1
|
|
||||||
|
|
||||||
// Lay the file out: header, section data, section headers.
|
// Lay the file out: header, section data, section headers.
|
||||||
var out []byte
|
var out []byte
|
||||||
out = append(out, make([]byte, 64)...) // ELF header, filled last
|
out = append(out, make([]byte, 64)...) // ELF header, filled last
|
||||||
@@ -214,7 +255,7 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
strtabOff := len(out)
|
strtabOff := len(out)
|
||||||
out = append(out, stNames.bytes()...)
|
out = append(out, stNames.bytes()...)
|
||||||
|
|
||||||
var relaOff int
|
var relaOff, relaDataOff int
|
||||||
if hasRela {
|
if hasRela {
|
||||||
align(8)
|
align(8)
|
||||||
relaOff = len(out)
|
relaOff = len(out)
|
||||||
@@ -226,6 +267,17 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
out = append(out, b[:]...)
|
out = append(out, b[:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if hasDataRela {
|
||||||
|
align(8)
|
||||||
|
relaDataOff = len(out)
|
||||||
|
for _, r := range dataRelas {
|
||||||
|
var b [24]byte
|
||||||
|
le.PutUint64(b[0:], r.off)
|
||||||
|
le.PutUint64(b[8:], uint64(r.sym)<<32|uint64(r.typ))
|
||||||
|
le.PutUint64(b[16:], uint64(r.addend))
|
||||||
|
out = append(out, b[:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shstrOff := len(out)
|
shstrOff := len(out)
|
||||||
out = append(out, stSections.bytes()...)
|
out = append(out, stSections.bytes()...)
|
||||||
@@ -284,6 +336,9 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
if hasRela {
|
if hasRela {
|
||||||
putSh(".rela.text", shtRela, 0, relaOff, 24*len(relas), secSymtab, secText, 8, 24)
|
putSh(".rela.text", shtRela, 0, relaOff, 24*len(relas), secSymtab, secText, 8, 24)
|
||||||
}
|
}
|
||||||
|
if hasDataRela {
|
||||||
|
putSh(".rela.data", shtRela, 0, relaDataOff, 24*len(dataRelas), secSymtab, secData, 8, 24)
|
||||||
|
}
|
||||||
putSh(".shstrtab", shtStrtab, 0, shstrOff, len(stSections.bytes()), 0, 0, 1, 0)
|
putSh(".shstrtab", shtStrtab, 0, shstrOff, len(stSections.bytes()), 0, 0, 1, 0)
|
||||||
|
|
||||||
// DWARF section headers; their indices follow the write order.
|
// DWARF section headers; their indices follow the write order.
|
||||||
|
|||||||
+108
@@ -750,3 +750,111 @@ func readFormSkip(t *testing.T, r *ulebIter, form uint64) {
|
|||||||
t.Fatalf("unsupported form %#x", form)
|
t.Fatalf("unsupported form %#x", form)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestELFObjectDataRelocation checks that a symbol-valued DATA field ("DATA
|
||||||
|
// s+0(SB)/8, $other(SB)") reaches the ELF object as a .rela.data entry: an
|
||||||
|
// absolute 64-bit relocation at the field's offset within .data, against
|
||||||
|
// the named symbol, external targets included.
|
||||||
|
func TestELFObjectDataRelocation(t *testing.T) {
|
||||||
|
f, errs := parser.Parse("t_amd64.s", `#include "textflag.h"
|
||||||
|
TEXT ·Keep(SB), NOSPLIT, $0-8
|
||||||
|
RET
|
||||||
|
GLOBL holder(SB), NOPTR, $24
|
||||||
|
DATA holder+0(SB)/8, $·Keep+5(SB)
|
||||||
|
DATA holder+8(SB)/8, $holder(SB)
|
||||||
|
DATA holder+16(SB)/8, $extvar(SB)
|
||||||
|
`)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("assemble: %v", err)
|
||||||
|
}
|
||||||
|
obj, err := img.ELFObject()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ELFObject: %v", err)
|
||||||
|
}
|
||||||
|
ef, err := elf.NewFile(bytes.NewReader(obj))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parse emitted object: %v", err)
|
||||||
|
}
|
||||||
|
defer ef.Close()
|
||||||
|
relaData := ef.Section(".rela.data")
|
||||||
|
if relaData == nil {
|
||||||
|
t.Fatal("missing .rela.data section")
|
||||||
|
}
|
||||||
|
if relaData.Link == 0 || ef.Sections[relaData.Link].Name != ".symtab" {
|
||||||
|
t.Errorf(".rela.data sh_link = %d, want the .symtab index", relaData.Link)
|
||||||
|
}
|
||||||
|
if ef.Sections[relaData.Info].Name != ".data" {
|
||||||
|
t.Errorf(".rela.data sh_info = %d, want the .data index", relaData.Info)
|
||||||
|
}
|
||||||
|
relas, err := relaData.Data()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var got []struct {
|
||||||
|
off uint64
|
||||||
|
sym uint32
|
||||||
|
typ uint32
|
||||||
|
addend int64
|
||||||
|
}
|
||||||
|
for i := 0; i+24 <= len(relas); i += 24 {
|
||||||
|
got = append(got, struct {
|
||||||
|
off uint64
|
||||||
|
sym uint32
|
||||||
|
typ uint32
|
||||||
|
addend int64
|
||||||
|
}{
|
||||||
|
off: binary.LittleEndian.Uint64(relas[i:]),
|
||||||
|
// r_info packs the type in the low dword and the symbol index
|
||||||
|
// in the high dword.
|
||||||
|
typ: binary.LittleEndian.Uint32(relas[i+8:]),
|
||||||
|
sym: binary.LittleEndian.Uint32(relas[i+12:]),
|
||||||
|
addend: int64(binary.LittleEndian.Uint64(relas[i+16:])),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// debug/elf hides the table's null entry, so raw index s names syms[s-1].
|
||||||
|
syms, err := ef.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
name := func(idx uint32) string {
|
||||||
|
if idx >= 1 && int(idx) <= len(syms) {
|
||||||
|
return syms[idx-1].Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// The offsets are data-section-relative: the field's DATA offset plus
|
||||||
|
// the symbol's position in .data (the layout aligns each symbol to 16).
|
||||||
|
base := uint64(0)
|
||||||
|
for _, d := range img.DataSyms {
|
||||||
|
if d.Name == "holder" {
|
||||||
|
base = uint64(d.Offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
want := []struct {
|
||||||
|
off uint64
|
||||||
|
typ uint32
|
||||||
|
addend int64
|
||||||
|
target string
|
||||||
|
}{
|
||||||
|
{off: base + 0, typ: uint32(elf.R_X86_64_64), addend: 5, target: "Keep"},
|
||||||
|
{off: base + 8, typ: uint32(elf.R_X86_64_64), addend: 0, target: "holder"},
|
||||||
|
{off: base + 16, typ: uint32(elf.R_X86_64_64), addend: 0, target: "extvar"},
|
||||||
|
}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatalf(".rela.data entries = %d, want %d", len(got), len(want))
|
||||||
|
}
|
||||||
|
for i, w := range want {
|
||||||
|
g := got[i]
|
||||||
|
if g.off != w.off || g.typ != w.typ || g.addend != w.addend {
|
||||||
|
t.Errorf("entry %d = {off %d typ %d addend %d}, want {off %d typ %d addend %d}",
|
||||||
|
i, g.off, g.typ, g.addend, w.off, w.typ, w.addend)
|
||||||
|
}
|
||||||
|
if n := name(g.sym); n != w.target {
|
||||||
|
t.Errorf("entry %d names %q, want %q", i, n, w.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -463,6 +463,61 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
|
|||||||
symRelocs[si] = append(symRelocs[si], rec[:]...)
|
symRelocs[si] = append(symRelocs[si], rec[:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The data symbols' own relocations: the symbol-valued DATA fields
|
||||||
|
// ("DATA s+0(SB)/8, $other(SB)"). The toolchain patches each field
|
||||||
|
// with the target's absolute address through an R_ADDR of the DATA
|
||||||
|
// line's width, on every architecture (the code relocations are
|
||||||
|
// per-architecture PC-relative shapes; a data pointer word is not), so
|
||||||
|
// this mapping bypasses relocField. The definitions were appended in
|
||||||
|
// DataSyms order, so data symbol i is definition index i.
|
||||||
|
for i, d := range img.DataSyms {
|
||||||
|
for _, r := range d.Relocs {
|
||||||
|
if r.Kind != RelAddr {
|
||||||
|
return nil, fmt.Errorf("GOOBJ emission: data symbol %q carries a non-data relocation", d.Name)
|
||||||
|
}
|
||||||
|
var rec [23]byte
|
||||||
|
binary.LittleEndian.PutUint32(rec[0:], uint32(int32(r.Off)))
|
||||||
|
rec[4] = r.Siz
|
||||||
|
binary.LittleEndian.PutUint16(rec[5:], relocAddr)
|
||||||
|
binary.LittleEndian.PutUint64(rec[7:], uint64(r.Addend))
|
||||||
|
switch {
|
||||||
|
case r.External && r.Name == goobjBuiltinMorestack:
|
||||||
|
binary.LittleEndian.PutUint32(rec[15:], pkgIdxBuiltin)
|
||||||
|
binary.LittleEndian.PutUint32(rec[19:], goobjBuiltinMorestackNoctxt)
|
||||||
|
case r.External:
|
||||||
|
pkg, name := splitQualified(r.Name)
|
||||||
|
if pkg == "" {
|
||||||
|
return nil, fmt.Errorf("GOOBJ emission: external symbol %q has no package prefix", r.Name)
|
||||||
|
}
|
||||||
|
pIdx, ok := extPkgIdx[pkg]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("GOOBJ emission: package %q not resolved", pkg)
|
||||||
|
}
|
||||||
|
sIdx, ok := extSymIdx[pkg+"·"+name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("GOOBJ emission: symbol %s·%s not resolved", pkg, name)
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint32(rec[15:], uint32(pIdx))
|
||||||
|
binary.LittleEndian.PutUint32(rec[19:], uint32(sIdx))
|
||||||
|
default:
|
||||||
|
if di, ok := defIdx[r.Name]; ok {
|
||||||
|
binary.LittleEndian.PutUint32(rec[15:], pkgIdxSelf)
|
||||||
|
binary.LittleEndian.PutUint32(rec[19:], uint32(di))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// A DATA field may hold the address of a TEXT function of
|
||||||
|
// the same file (the rt0 lib entry spelling), which is a
|
||||||
|
// non-package definition.
|
||||||
|
ni, isText := textNpIdx[r.Name]
|
||||||
|
if !isText {
|
||||||
|
return nil, fmt.Errorf("GOOBJ emission: reference to unknown symbol %q", r.Name)
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint32(rec[15:], pkgIdxNone)
|
||||||
|
binary.LittleEndian.PutUint32(rec[19:], uint32(ni))
|
||||||
|
}
|
||||||
|
symRelocs[i] = append(symRelocs[i], rec[:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
// The DWARF symbols' own relocations (the function address references).
|
// The DWARF symbols' own relocations (the function address references).
|
||||||
for _, ds := range dwarfRelocs {
|
for _, ds := range dwarfRelocs {
|
||||||
for _, r := range ds.relocs {
|
for _, r := range ds.relocs {
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The differential kernels for the DATA-path and front-end gaps are kept in
|
||||||
|
// testdata/verify beside the campaign's other kernels; the verify package's
|
||||||
|
// suites are not open to the asm package, so this test is their runner: each
|
||||||
|
// kernel assembles through gasm and through go tool asm, and the functions'
|
||||||
|
// bytes must agree with the relocation sites masked on both sides.
|
||||||
|
|
||||||
|
// toolAsmObject assembles path with the installed toolchain's assembler for
|
||||||
|
// goarch ("" = the host) and returns the object bytes.
|
||||||
|
func toolAsmObject(t *testing.T, path, goarch string) []byte {
|
||||||
|
t.Helper()
|
||||||
|
goBin, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("no Go toolchain available")
|
||||||
|
}
|
||||||
|
out, err := exec.Command(goBin, "env", "GOROOT").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("go env GOROOT: %v", err)
|
||||||
|
}
|
||||||
|
includeDir := filepath.Join(strings.TrimSpace(string(out)), "pkg", "include")
|
||||||
|
|
||||||
|
pkg := strings.TrimSuffix(filepath.Base(path), ".s")
|
||||||
|
pkg = strings.TrimSuffix(pkg, "_amd64")
|
||||||
|
pkg = strings.TrimSuffix(pkg, "_arm64")
|
||||||
|
|
||||||
|
objPath := filepath.Join(t.TempDir(), "oracle.o")
|
||||||
|
cmd := exec.Command(goBin, "tool", "asm", "-I", includeDir, "-p", pkg, "-o", objPath, path)
|
||||||
|
if goarch != "" {
|
||||||
|
environ := os.Environ()
|
||||||
|
env := make([]string, 0, len(environ)+1)
|
||||||
|
for _, e := range environ {
|
||||||
|
if !strings.HasPrefix(e, "GOARCH=") {
|
||||||
|
env = append(env, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Env = append(env, "GOARCH="+goarch)
|
||||||
|
}
|
||||||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("go tool asm %s: %v\n%s", filepath.Base(path), err, out)
|
||||||
|
}
|
||||||
|
obj, err := os.ReadFile(objPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// oracleFuncCode extracts the non-package TEXT functions' code bytes from a
|
||||||
|
// toolchain object, keyed by the name the object records (pkg.name).
|
||||||
|
func oracleFuncCode(t *testing.T, obj []byte) map[string][]byte {
|
||||||
|
t.Helper()
|
||||||
|
v := openGoobj(t, obj)
|
||||||
|
le := binary.LittleEndian
|
||||||
|
const symSize = 21
|
||||||
|
nps := v.syms(blkNonpkgdef)
|
||||||
|
data := v.blk(blkData)
|
||||||
|
didx := v.blk(blkDataIdx)
|
||||||
|
preceding := 0
|
||||||
|
for _, bi := range []int{blkSymdef, blkHashed64def, blkHasheddef} {
|
||||||
|
preceding += len(v.blk(bi)) / symSize
|
||||||
|
}
|
||||||
|
total := preceding + len(nps)
|
||||||
|
out := make(map[string][]byte, len(nps))
|
||||||
|
for i, s := range nps {
|
||||||
|
if s.typ != kindSTEXT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
start := le.Uint32(didx[4*(preceding+i):])
|
||||||
|
end := uint32(len(data))
|
||||||
|
if preceding+i+1 < total {
|
||||||
|
end = le.Uint32(didx[4*(preceding+i+1):])
|
||||||
|
}
|
||||||
|
out[s.name] = data[start:end]
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskCode zeroes every relocation field, the way the toolchain's object
|
||||||
|
// leaves them for the linker.
|
||||||
|
func maskCode(code []byte, relocs []Reloc) []byte {
|
||||||
|
for _, r := range relocs {
|
||||||
|
for j := r.Off; j < r.Off+4 && j < len(code); j++ {
|
||||||
|
code[j] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// code assembles src for amd64 and returns the image's code bytes.
|
||||||
|
func code(path, src string) []byte {
|
||||||
|
f, errs := parser.Parse(path, src)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
img, err := AssembleFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return img.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDifferentialKernels pins the new kernels against the oracle.
|
||||||
|
func TestDifferentialKernels(t *testing.T) {
|
||||||
|
if runtime.GOARCH != "amd64" {
|
||||||
|
t.Skip("the amd64 kernels assume an amd64 host assembler default")
|
||||||
|
}
|
||||||
|
for _, k := range []struct {
|
||||||
|
path string
|
||||||
|
goarch string
|
||||||
|
arm64 bool
|
||||||
|
}{
|
||||||
|
{filepath.Join("..", "testdata", "verify", "datarel_amd64.s"), "", false},
|
||||||
|
{filepath.Join("..", "testdata", "verify", "divslash_amd64.s"), "", false},
|
||||||
|
{filepath.Join("..", "testdata", "verify", "semicolons_amd64.s"), "", false},
|
||||||
|
{filepath.Join("..", "testdata", "verify", "datarel_arm64.s"), "arm64", true},
|
||||||
|
{filepath.Join("..", "testdata", "verify", "divslash_arm64.s"), "arm64", true},
|
||||||
|
} {
|
||||||
|
t.Run(filepath.Base(k.path), func(t *testing.T) {
|
||||||
|
src, err := os.ReadFile(k.path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read: %v", err)
|
||||||
|
}
|
||||||
|
f, errs := parser.Parse(k.path, string(src))
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
var img *Image
|
||||||
|
if k.arm64 {
|
||||||
|
img, err = AssembleFileARM64(f)
|
||||||
|
} else {
|
||||||
|
img, err = AssembleFile(f)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("assemble: %v", err)
|
||||||
|
}
|
||||||
|
gt := oracleFuncCode(t, toolAsmObject(t, k.path, k.goarch))
|
||||||
|
// The oracle keys its functions by the qualified object name
|
||||||
|
// (pkg.name); match on the local part.
|
||||||
|
byLocal := make(map[string][]byte, len(gt))
|
||||||
|
for name, code := range gt {
|
||||||
|
if _, after, ok := strings.Cut(name, "."); ok {
|
||||||
|
name = after
|
||||||
|
}
|
||||||
|
byLocal[name] = code
|
||||||
|
}
|
||||||
|
|
||||||
|
matched := 0
|
||||||
|
for _, fn := range img.Funcs {
|
||||||
|
gasmCode := maskCode(append([]byte(nil), img.Code[fn.Offset:fn.Offset+fn.Size]...), fn.Relocs)
|
||||||
|
goCode, ok := byLocal[fn.Name]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: not in ground truth (%d functions: %v)", fn.Name, len(gt), keysOf(byLocal))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
goCode = maskCode(append([]byte(nil), goCode...), fn.Relocs)
|
||||||
|
cmpLen := min(len(goCode), len(gasmCode))
|
||||||
|
if !bytes.Equal(gasmCode[:cmpLen], goCode[:cmpLen]) {
|
||||||
|
t.Errorf("%s: MISMATCH gasm=%d go=%d bytes\ngasm %x\ngo %x", fn.Name, len(gasmCode), len(goCode), gasmCode, goCode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, b := range goCode[len(gasmCode):] {
|
||||||
|
if b != 0 {
|
||||||
|
t.Errorf("%s: non-zero trailing bytes in go tool asm output", fn.Name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matched++
|
||||||
|
t.Logf("%s: MATCH (%d bytes)", fn.Name, len(gasmCode))
|
||||||
|
}
|
||||||
|
if matched == 0 {
|
||||||
|
t.Fatal("no functions matched")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keysOf(m map[string][]byte) []string {
|
||||||
|
out := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
out = append(out, k)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSemicolonSpellingParity pins that the ';' statement separator changes
|
||||||
|
// nothing about the encoding: the one-line spelling assembles to exactly the
|
||||||
|
// bytes of the same statements written one per line.
|
||||||
|
func TestSemicolonSpellingParity(t *testing.T) {
|
||||||
|
for _, tt := range []struct{ one, two string }{
|
||||||
|
{"\tROLQ $3, DI; ROLQ $13, DI\n", "\tROLQ $3, DI\n\tROLQ $13, DI\n"},
|
||||||
|
{"\tREP; MOVSQ\n", "\tREP\n\tMOVSQ\n"},
|
||||||
|
{"\tXORQ AX, AX; XORQ CX, CX\n", "\tXORQ AX, AX\n\tXORQ CX, CX\n"},
|
||||||
|
} {
|
||||||
|
one := code("t.s", "TEXT \u00b7f(SB), NOSPLIT, $0\n"+tt.one+"\tRET\n")
|
||||||
|
two := code("t.s", "TEXT \u00b7f(SB), NOSPLIT, $0\n"+tt.two+"\tRET\n")
|
||||||
|
if !bytes.Equal(one, two) {
|
||||||
|
t.Errorf("semicolon spelling %q: %x, want the two-line bytes %x", tt.one, one, two)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
-3
@@ -103,6 +103,7 @@ const (
|
|||||||
RelArm64Branch // R_CALLARM64 (BL instruction)
|
RelArm64Branch // R_CALLARM64 (BL instruction)
|
||||||
RelArm64LDST64 // R_ARM64_PCREL_LDST64 (ADRP + 64-bit LDR/STR pair)
|
RelArm64LDST64 // R_ARM64_PCREL_LDST64 (ADRP + 64-bit LDR/STR pair)
|
||||||
RelLoong64Branch // R_CALLLOONG64 (BL instruction)
|
RelLoong64Branch // R_CALLLOONG64 (BL instruction)
|
||||||
|
RelAddr // R_ADDR: the absolute address of a symbol held in a DATA field
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reloc struct {
|
type Reloc struct {
|
||||||
@@ -112,13 +113,17 @@ type Reloc struct {
|
|||||||
// Addend select the target: the symbol plus the byte offset. An
|
// Addend select the target: the symbol plus the byte offset. An
|
||||||
// External relocation names a symbol no GLOBL in the file defines;
|
// External relocation names a symbol no GLOBL in the file defines;
|
||||||
// the object-file emitters carry it into the output's relocation
|
// the object-file emitters carry it into the output's relocation
|
||||||
// table.
|
// table. Siz is the width of the patched field and is set only for
|
||||||
|
// data-field relocations (RelAddr, Off relative to the data symbol),
|
||||||
|
// whose width is the DATA line's; code relocations take their width
|
||||||
|
// from the architecture's instruction encoding.
|
||||||
Off int
|
Off int
|
||||||
After int
|
After int
|
||||||
Name string
|
Name string
|
||||||
Addend int64
|
Addend int64
|
||||||
External bool
|
External bool
|
||||||
Kind RelocKind
|
Kind RelocKind
|
||||||
|
Siz uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataSymbol describes one GLOBL symbol laid out in the data section.
|
// DataSymbol describes one GLOBL symbol laid out in the data section.
|
||||||
@@ -130,6 +135,11 @@ type DataSymbol struct {
|
|||||||
Static bool // the <> marker: file-local, not exported
|
Static bool // the <> marker: file-local, not exported
|
||||||
Rodata bool // the RODATA flag: read-only data
|
Rodata bool // the RODATA flag: read-only data
|
||||||
Dupok bool // the DUPOK flag: duplicate-OK
|
Dupok bool // the DUPOK flag: duplicate-OK
|
||||||
|
// Relocs carries the symbol-valued DATA initialisers ("DATA s+0(SB)/8,
|
||||||
|
// $other(SB)"): fields of this symbol's data that hold another symbol's
|
||||||
|
// address, resolved by the linker. Off is relative to the symbol's
|
||||||
|
// data start.
|
||||||
|
Relocs []Reloc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the whole image: code, then data.
|
// Bytes returns the whole image: code, then data.
|
||||||
@@ -257,6 +267,22 @@ func AssembleFile(f *ast.File) (*Image, error) {
|
|||||||
img.Funcs[i].Relocs = append(img.Funcs[i].Relocs, reloc)
|
img.Funcs[i].Relocs = append(img.Funcs[i].Relocs, reloc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The data symbols' symbol-valued DATA fields resolve the same way the
|
||||||
|
// code references do: a name the file defines (GLOBL or TEXT) stays an
|
||||||
|
// internal reference the emitters resolve, anything else is external.
|
||||||
|
// img.DataSyms was laid out in dataSyms order, so the indexes line up.
|
||||||
|
for i := range img.DataSyms {
|
||||||
|
for _, r := range dataSyms[i].relocs {
|
||||||
|
reloc := r
|
||||||
|
if _, ok := img.Symbols[reloc.Name]; !ok {
|
||||||
|
if _, ok := textOff[reloc.Name]; !ok {
|
||||||
|
reloc.External = true
|
||||||
|
externals[reloc.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.DataSyms[i].Relocs = append(img.DataSyms[i].Relocs, reloc)
|
||||||
|
}
|
||||||
|
}
|
||||||
for name := range externals {
|
for name := range externals {
|
||||||
img.Externals = append(img.Externals, name)
|
img.Externals = append(img.Externals, name)
|
||||||
}
|
}
|
||||||
@@ -429,6 +455,23 @@ func markExternals(img *Image, dataSyms []dataSym) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The declared data symbols carry the file's own relocations (the
|
||||||
|
// symbol-valued DATA fields); the layouts appended img.DataSyms in
|
||||||
|
// dataSyms order, so the indexes line up. The trailing entries (the
|
||||||
|
// pooled arm64 literals) have no source relocations.
|
||||||
|
for i := range img.DataSyms {
|
||||||
|
if i >= len(dataSyms) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, r := range dataSyms[i].relocs {
|
||||||
|
reloc := r
|
||||||
|
if !known[reloc.Name] {
|
||||||
|
reloc.External = true
|
||||||
|
externals[reloc.Name] = true
|
||||||
|
}
|
||||||
|
img.DataSyms[i].Relocs = append(img.DataSyms[i].Relocs, reloc)
|
||||||
|
}
|
||||||
|
}
|
||||||
for name := range externals {
|
for name := range externals {
|
||||||
img.Externals = append(img.Externals, name)
|
img.Externals = append(img.Externals, name)
|
||||||
}
|
}
|
||||||
@@ -444,6 +487,9 @@ type dataSym struct {
|
|||||||
static bool
|
static bool
|
||||||
rodata bool
|
rodata bool
|
||||||
dupok bool
|
dupok bool
|
||||||
|
// relocs are the symbol-valued DATA fields, in declaration order; Off
|
||||||
|
// is relative to the symbol's data start.
|
||||||
|
relocs []Reloc
|
||||||
}
|
}
|
||||||
|
|
||||||
// collectData gathers the file's static symbols (GLOBL) and their initial
|
// collectData gathers the file's static symbols (GLOBL) and their initial
|
||||||
@@ -511,8 +557,8 @@ func collectData(f *ast.File) ([]dataSym, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("DATA %q: no matching GLOBL", dd.Name.Name)
|
return nil, fmt.Errorf("DATA %q: no matching GLOBL", dd.Name.Name)
|
||||||
}
|
}
|
||||||
if dd.Value == nil || !dd.Value.Imm.HasVal {
|
if dd.Value == nil {
|
||||||
return nil, fmt.Errorf("DATA %q: value must be an integer immediate", dd.Name.Name)
|
return nil, fmt.Errorf("DATA %q: missing value", dd.Name.Name)
|
||||||
}
|
}
|
||||||
w := dd.Width
|
w := dd.Width
|
||||||
switch w {
|
switch w {
|
||||||
@@ -525,6 +571,25 @@ func collectData(f *ast.File) ([]dataSym, error) {
|
|||||||
if off < 0 || off+int64(w) > int64(len(buf)) {
|
if off < 0 || off+int64(w) > int64(len(buf)) {
|
||||||
return nil, fmt.Errorf("DATA %q+%d/%d exceeds GLOBL size %d", dd.Name.Name, off, w, len(buf))
|
return nil, fmt.Errorf("DATA %q+%d/%d exceeds GLOBL size %d", dd.Name.Name, off, w, len(buf))
|
||||||
}
|
}
|
||||||
|
// A symbol value ("DATA s+0(SB)/8, $other(SB)", the rt0 spelling)
|
||||||
|
// leaves the field zero and records a relocation against the named
|
||||||
|
// symbol: the linker patches the absolute address at this data
|
||||||
|
// offset. The toolchain emits the same shape, an R_ADDR of the
|
||||||
|
// DATA width with the value's offset as the addend, on every
|
||||||
|
// architecture.
|
||||||
|
if sym := dd.Value.Imm.Sym; !dd.Value.Imm.HasVal && sym != nil {
|
||||||
|
syms[i].relocs = append(syms[i].relocs, Reloc{
|
||||||
|
Off: int(off),
|
||||||
|
Name: sym.Name,
|
||||||
|
Addend: sym.Offset,
|
||||||
|
Kind: RelAddr,
|
||||||
|
Siz: uint8(w),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !dd.Value.Imm.HasVal {
|
||||||
|
return nil, fmt.Errorf("DATA %q: value must be an integer immediate or a symbol address", dd.Name.Name)
|
||||||
|
}
|
||||||
v := dd.Value.Imm.Val
|
v := dd.Value.Imm.Val
|
||||||
if dd.Value.Imm.Neg {
|
if dd.Value.Imm.Neg {
|
||||||
v = -v
|
v = -v
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
package asm
|
package asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -166,3 +170,251 @@ func TestCollectDataNumericFlags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCollectDataSymbolValue covers the symbol-valued DATA field ("DATA
|
||||||
|
// s+0(SB)/8, $other(SB)", the rt0 spelling): the field stays zero in the
|
||||||
|
// image and the relocation is recorded against the named symbol, whatever
|
||||||
|
// the file defines (a TEXT function, a GLOBL) or leaves external.
|
||||||
|
func TestCollectDataSymbolValue(t *testing.T) {
|
||||||
|
src := `#include "textflag.h"
|
||||||
|
TEXT ·Keep(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ target+0(FP), AX
|
||||||
|
RET
|
||||||
|
GLOBL holder(SB), NOPTR, $32
|
||||||
|
DATA holder+0(SB)/8, $·Keep(SB)
|
||||||
|
DATA holder+8(SB)/8, $·Keep+5(SB)
|
||||||
|
DATA holder+16(SB)/8, $holder(SB)
|
||||||
|
GLOBL spare(SB), NOPTR, $8
|
||||||
|
DATA spare+0(SB)/8, $extvar(SB)
|
||||||
|
`
|
||||||
|
f, errs := parser.Parse("f_amd64.s", src)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("assemble: %v", err)
|
||||||
|
}
|
||||||
|
byName := map[string]DataSymbol{}
|
||||||
|
for _, d := range img.DataSyms {
|
||||||
|
byName[d.Name] = d
|
||||||
|
}
|
||||||
|
want := []struct {
|
||||||
|
sym string
|
||||||
|
off int
|
||||||
|
name string
|
||||||
|
addend int64
|
||||||
|
ext bool
|
||||||
|
}{
|
||||||
|
{"holder", 0, "Keep", 0, false},
|
||||||
|
{"holder", 8, "Keep", 5, false},
|
||||||
|
{"holder", 16, "holder", 0, false},
|
||||||
|
{"spare", 0, "extvar", 0, true},
|
||||||
|
}
|
||||||
|
var flat []struct {
|
||||||
|
sym string
|
||||||
|
r Reloc
|
||||||
|
}
|
||||||
|
for _, d := range img.DataSyms {
|
||||||
|
for _, r := range d.Relocs {
|
||||||
|
flat = append(flat, struct {
|
||||||
|
sym string
|
||||||
|
r Reloc
|
||||||
|
}{d.Name, r})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(flat) != len(want) {
|
||||||
|
t.Fatalf("data relocations = %d, want %d", len(flat), len(want))
|
||||||
|
}
|
||||||
|
for i, w := range want {
|
||||||
|
g := flat[i]
|
||||||
|
r := g.r
|
||||||
|
if g.sym != w.sym {
|
||||||
|
t.Errorf("relocation %d sits on %q, want %q", i, g.sym, w.sym)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if r.Off != w.off || r.Name != w.name || r.Addend != w.addend || r.External != w.ext {
|
||||||
|
t.Errorf("relocation %d = {+%d %q addend %d ext %v}, want {+%d %q addend %d ext %v}",
|
||||||
|
i, r.Off, r.Name, r.Addend, r.External, w.off, w.name, w.addend, w.ext)
|
||||||
|
}
|
||||||
|
if r.Kind != RelAddr {
|
||||||
|
t.Errorf("relocation %d kind = %v, want RelAddr", i, r.Kind)
|
||||||
|
}
|
||||||
|
if r.Siz != 8 {
|
||||||
|
t.Errorf("relocation %d siz = %d, want 8", i, r.Siz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The fields themselves stay zero: only the linker fills them.
|
||||||
|
for _, b := range img.Data {
|
||||||
|
if b != 0 {
|
||||||
|
t.Fatal("data section is not all zero before relocation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(img.Externals) != 1 || img.Externals[0] != "extvar" {
|
||||||
|
t.Errorf("Externals = %v, want [extvar]", img.Externals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGOObjectDataSymbolReloc pins the GOOBJ record a symbol-valued DATA
|
||||||
|
// field produces, against the shape the toolchain emits for the same
|
||||||
|
// source: an R_ADDR of the DATA width at the field offset, pkgIdxNone plus
|
||||||
|
// the non-package definition index when the target is the file's own TEXT
|
||||||
|
// function (the rt0 lib entry spelling).
|
||||||
|
func TestGOObjectDataSymbolReloc(t *testing.T) {
|
||||||
|
f, errs := parser.Parse("f_amd64.s", `#include "textflag.h"
|
||||||
|
TEXT ·Keep(SB), NOSPLIT, $0-8
|
||||||
|
RET
|
||||||
|
GLOBL holder(SB), NOPTR, $16
|
||||||
|
DATA holder+0(SB)/8, $·Keep+5(SB)
|
||||||
|
`)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("assemble: %v", err)
|
||||||
|
}
|
||||||
|
obj, err := img.GOObject("main", "f_amd64.s")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GOObject: %v", err)
|
||||||
|
}
|
||||||
|
v := openGoobj(t, obj)
|
||||||
|
// Walk every relocation record; the data record is the one of Siz 8
|
||||||
|
// and type R_ADDR.
|
||||||
|
var off, add int64
|
||||||
|
var pkg, sym uint32
|
||||||
|
found := false
|
||||||
|
for data := v.blk(blkReloc); len(data) >= 23; data = data[23:] {
|
||||||
|
if data[4] != 8 || binary.LittleEndian.Uint16(data[5:]) != relocAddr {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
off = int64(int32(binary.LittleEndian.Uint32(data[0:])))
|
||||||
|
add = int64(binary.LittleEndian.Uint64(data[7:]))
|
||||||
|
pkg = binary.LittleEndian.Uint32(data[15:])
|
||||||
|
sym = binary.LittleEndian.Uint32(data[19:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("no data relocation record in the object")
|
||||||
|
}
|
||||||
|
if off != 0 || add != 5 {
|
||||||
|
t.Errorf("data reloc = {off %d addend %d}, want {off 0 addend 5}", off, add)
|
||||||
|
}
|
||||||
|
if pkg != pkgIdxNone {
|
||||||
|
t.Errorf("data reloc pkg = %#x, want pkgIdxNone (the TEXT function)", pkg)
|
||||||
|
}
|
||||||
|
// The function's non-package definition index: the four pc tables
|
||||||
|
// precede it, so index 4.
|
||||||
|
if sym != 4 {
|
||||||
|
t.Errorf("data reloc sym = %d, want 4", sym)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGOObjectDataSymbolLink is the end-to-end proof for symbol-valued DATA
|
||||||
|
// fields: the gasm object is substituted for the toolchain's and re-linked,
|
||||||
|
// then executed, and the linked data word must hold the real address of the
|
||||||
|
// function the DATA line named (runtime.FuncForPC identifies it).
|
||||||
|
func TestGOObjectDataSymbolLink(t *testing.T) {
|
||||||
|
goBin, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("no Go toolchain available")
|
||||||
|
}
|
||||||
|
dir := t.TempDir()
|
||||||
|
asmSrc := `#include "textflag.h"
|
||||||
|
GLOBL entry(SB), NOPTR, $8
|
||||||
|
DATA entry+0(SB)/8, $·keepme(SB)
|
||||||
|
|
||||||
|
TEXT ·keepme(SB), NOSPLIT, $0-0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·entryptr(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ entry+0(SB), AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
`
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "main_amd64.s"), []byte(asmSrc), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mainSrc := `package main
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
func keepme()
|
||||||
|
func entryptr() uintptr
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
pc := entryptr()
|
||||||
|
fn := runtime.FuncForPC(pc)
|
||||||
|
if fn == nil {
|
||||||
|
panic("the entry word does not point at a function")
|
||||||
|
}
|
||||||
|
if fn.Name() != "main.keepme" {
|
||||||
|
panic("the entry word points at " + fn.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte(mainSrc), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module dlink\n\ngo 1.21\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture the build: the package archive's asm object and the link line.
|
||||||
|
build := exec.Command(goBin, "build", "-x", "-work", "-o", filepath.Join(dir, "prog"), ".")
|
||||||
|
build.Dir = dir
|
||||||
|
buildLog, err := build.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||||
|
}
|
||||||
|
var work, linkLine, asmObj string
|
||||||
|
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(line, "WORK="):
|
||||||
|
work = strings.TrimPrefix(line, "WORK=")
|
||||||
|
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_amd64.s") && !strings.Contains(line, "-gensymabis"):
|
||||||
|
asmObj = fieldAfter(line, "-o")
|
||||||
|
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
||||||
|
linkLine = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if work == "" || asmObj == "" || linkLine == "" {
|
||||||
|
t.Skipf("could not parse build log (work=%q asmObj=%q link=%q)", work, asmObj, linkLine)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(work)
|
||||||
|
asmObj = strings.ReplaceAll(asmObj, "$WORK", work)
|
||||||
|
linkLine = strings.ReplaceAll(linkLine, "$WORK", work)
|
||||||
|
|
||||||
|
// Assemble the same source with gasm and substitute the object.
|
||||||
|
src, err := os.ReadFile(filepath.Join(dir, "main_amd64.s"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, errs := parser.Parse("main_amd64.s", string(src))
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AssembleFile: %v", err)
|
||||||
|
}
|
||||||
|
gasmObj, err := img.GOObject("dlink", "main_amd64.s")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GOObject: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(asmObj, gasmObj, 0o644); err != nil {
|
||||||
|
t.Fatalf("write gasm object: %v", err)
|
||||||
|
}
|
||||||
|
linkCmd := exec.Command("bash", "-c", "cd "+dir+" && "+linkLine)
|
||||||
|
if out, err := linkCmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("re-link with gasm object: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The linked program must run and find the right function behind the
|
||||||
|
// data word.
|
||||||
|
out, err := exec.Command(filepath.Join(dir, "prog")).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("linked program failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -372,6 +372,10 @@ func loong64InstrSize(instr *ast.Instr, fi loong64FrameInfo) int {
|
|||||||
return len(loong64Return(fi))
|
return len(loong64Return(fi))
|
||||||
}
|
}
|
||||||
switch mnem {
|
switch mnem {
|
||||||
|
case "END", "FUNCDATA", "PCDATA":
|
||||||
|
return 0 // bookkeeping statements contribute no bytes
|
||||||
|
case "GETCALLERPC":
|
||||||
|
return 4 // or rd, r1, r0
|
||||||
case "TEQ", "TNE":
|
case "TEQ", "TNE":
|
||||||
return 8 // bne/beq over the BREAK, then BREAK
|
return 8 // bne/beq over the BREAK, then BREAK
|
||||||
case "PRELDX":
|
case "PRELDX":
|
||||||
@@ -480,6 +484,36 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
|
|||||||
return nil, fmt.Errorf("WORD expects 1 operand, got %d", len(ops))
|
return nil, fmt.Errorf("WORD expects 1 operand, got %d", len(ops))
|
||||||
}
|
}
|
||||||
return l64wordLE(uint32(immFromOperand(ops[0]))), nil
|
return l64wordLE(uint32(immFromOperand(ops[0]))), nil
|
||||||
|
case "END", "FUNCDATA", "PCDATA", "GETCALLERPC":
|
||||||
|
// The assembler's bookkeeping statements. END, FUNCDATA and PCDATA
|
||||||
|
// contribute no bytes, the same shapes GOARCH=loong64 go tool asm
|
||||||
|
// accepts and emits nothing for; GETCALLERPC reads the caller's
|
||||||
|
// address out of R1 (RA) as or rd, r1, r0.
|
||||||
|
switch mnem {
|
||||||
|
case "END":
|
||||||
|
if len(ops) != 0 {
|
||||||
|
return nil, fmt.Errorf("END expects no operands, got %d", len(ops))
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
case "FUNCDATA":
|
||||||
|
if len(ops) != 2 || !isImmOperand(ops[0]) {
|
||||||
|
return nil, fmt.Errorf("FUNCDATA expects $n, sym(SB)")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
case "PCDATA":
|
||||||
|
if len(ops) != 2 || !isImmOperand(ops[0]) || !isImmOperand(ops[1]) {
|
||||||
|
return nil, fmt.Errorf("PCDATA expects $n, $n")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(ops) != 1 || isMemOperand(ops[0]) || loong64RegClass(operandRegName(ops[0])) != l64ClsGR {
|
||||||
|
return nil, fmt.Errorf("GETCALLERPC expects a general register")
|
||||||
|
}
|
||||||
|
rd := loong64RegNum(operandRegName(ops[0]))
|
||||||
|
if rd < 0 {
|
||||||
|
return nil, fmt.Errorf("GETCALLERPC: invalid register operand")
|
||||||
|
}
|
||||||
|
return l64wordLE(l64rrr(l64movRegTable["MOVV"].op, 0, 1, rd)), nil
|
||||||
case "NEGW", "NEGV":
|
case "NEGW", "NEGV":
|
||||||
// The integer negation pseudo is a subtract from zero:
|
// The integer negation pseudo is a subtract from zero:
|
||||||
// NEGW src, dst → sub.w r0, src, dst.
|
// NEGW src, dst → sub.w r0, src, dst.
|
||||||
@@ -2084,6 +2118,30 @@ func encodeLOONG64Vector(instr *ast.Instr, mnem string, fi loong64FrameInfo) ([]
|
|||||||
return l64wordLE(l64rr(l64InstrTable[mnem].op, vj, fcc)), true, nil
|
return l64wordLE(l64rr(l64InstrTable[mnem].op, vj, fcc)), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Four-register forms (vshuf.b): INSTR va, vk, vj, vd.
|
||||||
|
if l64Vec4R[mnem] {
|
||||||
|
if len(ops) != 4 {
|
||||||
|
return nil, true, fmt.Errorf("%s expects 4 operands, got %d", mnem, len(ops))
|
||||||
|
}
|
||||||
|
va, err := vec(ops[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
vk, err := vec(ops[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
vj, err := vec(ops[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
vd, err := vec(ops[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
return l64wordLE(l64InstrTable[mnem].op | uint32(va&0x1f)<<15 | uint32(vk&0x1f)<<10 | uint32(vj&0x1f)<<5 | uint32(vd&0x1f)), true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Three-register forms: INSTR vk, vj, vd or INSTR vk, vd (vj = vd).
|
// Three-register forms: INSTR vk, vj, vd or INSTR vk, vd (vj = vd).
|
||||||
if len(ops) != 2 && len(ops) != 3 {
|
if len(ops) != 2 && len(ops) != 3 {
|
||||||
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||||
|
|||||||
+447
-10
@@ -278,6 +278,7 @@ const (
|
|||||||
l64Fpreld // preld (2RI12 + 5-bit hint)
|
l64Fpreld // preld (2RI12 + 5-bit hint)
|
||||||
l64Fvvv // 3R vector (LSX/LASX): op | vk<<10 | vj<<5 | vd
|
l64Fvvv // 3R vector (LSX/LASX): op | vk<<10 | vj<<5 | vd
|
||||||
l64Fvcf // vector-to-condition: op | subop<<10 | vj<<5 | fcc
|
l64Fvcf // vector-to-condition: op | subop<<10 | vj<<5 | fcc
|
||||||
|
l64Fvvvv // 4R vector shuffle: op | va<<15 | vk<<10 | vj<<5 | vd
|
||||||
)
|
)
|
||||||
|
|
||||||
// l64Enc is one instruction's encoding: its bit layout (format) and the
|
// l64Enc is one instruction's encoding: its bit layout (format) and the
|
||||||
@@ -336,6 +337,10 @@ var l64VecImmInfo = map[string]l64VecImmEnc{}
|
|||||||
// vpcnt.v).
|
// vpcnt.v).
|
||||||
var l64Vec2R = map[string]bool{}
|
var l64Vec2R = map[string]bool{}
|
||||||
|
|
||||||
|
// l64Vec4R marks the four-operand vector mnemonics (INSTR va, vk, vj, vd,
|
||||||
|
// such as vshuf.b).
|
||||||
|
var l64Vec4R = map[string]bool{}
|
||||||
|
|
||||||
// l64VmovqOps holds the VMOVQ/XVMOVQ opcode constants (pre-shifted to bit
|
// l64VmovqOps holds the VMOVQ/XVMOVQ opcode constants (pre-shifted to bit
|
||||||
// 15), read off `go tool objdump` of GOARCH=loong64 `go tool asm` kernels.
|
// 15), read off `go tool objdump` of GOARCH=loong64 `go tool asm` kernels.
|
||||||
type l64VmovqEnc struct {
|
type l64VmovqEnc struct {
|
||||||
@@ -445,6 +450,14 @@ func init() {
|
|||||||
// bank is the FP registers (the toolchain spells it `FFINTDV F0, F1`),
|
// bank is the FP registers (the toolchain spells it `FFINTDV F0, F1`),
|
||||||
// so the entry stays on the 2R integer/FP format.
|
// so the entry stays on the 2R integer/FP format.
|
||||||
"FFINTDV": 0x474a << 10,
|
"FFINTDV": 0x474a << 10,
|
||||||
|
// The rest of the scalar conversions (all F-bank, 2R).
|
||||||
|
"FFINTFW": 0x4744 << 10, // ffint.s.w
|
||||||
|
"FFINTFV": 0x4746 << 10, // ffint.s.l
|
||||||
|
"FFINTDW": 0x4748 << 10, // ffint.d.w
|
||||||
|
"FTINTWF": 0x46c1 << 10, // ftint.w.s
|
||||||
|
"FTINTWD": 0x46c2 << 10, // ftint.w.d
|
||||||
|
"FTINTVF": 0x46c9 << 10, // ftint.l.s
|
||||||
|
"FTINTVD": 0x46ca << 10, // ftint.l.d
|
||||||
}
|
}
|
||||||
for m, op := range rr {
|
for m, op := range rr {
|
||||||
l64InstrTable[m] = l64Enc{format: l64Frr, op: op}
|
l64InstrTable[m] = l64Enc{format: l64Frr, op: op}
|
||||||
@@ -568,6 +581,12 @@ func init() {
|
|||||||
"AMADDDBW": 0x070D4 << 15, "AMADDDBV": 0x070D5 << 15,
|
"AMADDDBW": 0x070D4 << 15, "AMADDDBV": 0x070D5 << 15,
|
||||||
"AMANDDBW": 0x070D6 << 15, "AMANDDBV": 0x070D7 << 15,
|
"AMANDDBW": 0x070D6 << 15, "AMANDDBV": 0x070D7 << 15,
|
||||||
"AMORDBW": 0x070D8 << 15, "AMORDBV": 0x070D9 << 15,
|
"AMORDBW": 0x070D8 << 15, "AMORDBV": 0x070D9 << 15,
|
||||||
|
// The remaining _dbar exchange variants (loong64enc1.s).
|
||||||
|
"AMXORDBW": 0x070DA << 15, "AMXORDBV": 0x070DB << 15,
|
||||||
|
"AMMAXDBW": 0x070DC << 15, "AMMAXDBV": 0x070DD << 15,
|
||||||
|
"AMMINDBW": 0x070DE << 15, "AMMINDBV": 0x070DF << 15,
|
||||||
|
"AMMAXDBWU": 0x070E0 << 15, "AMMAXDBVU": 0x070E1 << 15,
|
||||||
|
"AMMINDBWU": 0x070E2 << 15, "AMMINDBVU": 0x070E3 << 15,
|
||||||
}
|
}
|
||||||
for m, op := range am {
|
for m, op := range am {
|
||||||
l64InstrTable[m] = l64Enc{format: l64Fam, op: op}
|
l64InstrTable[m] = l64Enc{format: l64Fam, op: op}
|
||||||
@@ -590,6 +609,240 @@ func init() {
|
|||||||
"XVANDV": {0xEA4C << 15, true}, "XVXORV": {0xEA4E << 15, true},
|
"XVANDV": {0xEA4C << 15, true}, "XVXORV": {0xEA4E << 15, true},
|
||||||
"XVSEQB": {0xE800 << 15, true}, "XVSEQV": {0xE803 << 15, true},
|
"XVSEQB": {0xE800 << 15, true}, "XVSEQV": {0xE803 << 15, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The integer and FP add/subtract families: [X]VADD and [X]VSUB by lane
|
||||||
|
// width, plus the [X]VSADD/[X]VSSUB saturating pairs.
|
||||||
|
// Opcodes transcribed from the toolchain's loong64enc1.s.
|
||||||
|
addsub := map[string]l64Vec3Enc{
|
||||||
|
"VADDB": {0xE014 << 15, false}, "VADDH": {0xE015 << 15, false},
|
||||||
|
"VADDD": {0xE262 << 15, false}, "VADDF": {0xE261 << 15, false},
|
||||||
|
"VADDQ": {0xE25A << 15, false},
|
||||||
|
"VSUBB": {0xE018 << 15, false}, "VSUBH": {0xE019 << 15, false},
|
||||||
|
"VSUBW": {0xE01A << 15, false}, "VSUBV": {0xE01B << 15, false},
|
||||||
|
"VSUBQ": {0xE25B << 15, false},
|
||||||
|
"VSUBF": {0xE265 << 15, false}, "VSUBD": {0xE266 << 15, false},
|
||||||
|
"VSADDB": {0xE08C << 15, false}, "VSADDH": {0xE08D << 15, false},
|
||||||
|
"VSADDW": {0xE08E << 15, false}, "VSADDV": {0xE08F << 15, false},
|
||||||
|
"VSADDBU": {0xE094 << 15, false}, "VSADDHU": {0xE095 << 15, false},
|
||||||
|
"VSADDWU": {0xE096 << 15, false}, "VSADDVU": {0xE097 << 15, false},
|
||||||
|
"VSSUBB": {0xE090 << 15, false}, "VSSUBH": {0xE091 << 15, false},
|
||||||
|
"VSSUBW": {0xE092 << 15, false}, "VSSUBV": {0xE093 << 15, false},
|
||||||
|
"VSSUBBU": {0xE098 << 15, false}, "VSSUBHU": {0xE099 << 15, false},
|
||||||
|
"VSSUBWU": {0xE09A << 15, false}, "VSSUBVU": {0xE09B << 15, false},
|
||||||
|
"XVADDB": {0xE814 << 15, true}, "XVADDH": {0xE815 << 15, true},
|
||||||
|
"XVADDW": {0xE816 << 15, true},
|
||||||
|
"XVADDD": {0xEA62 << 15, true}, "XVADDF": {0xEA61 << 15, true},
|
||||||
|
"XVADDQ": {0xEA5A << 15, true},
|
||||||
|
"XVSUBB": {0xE818 << 15, true}, "XVSUBH": {0xE819 << 15, true},
|
||||||
|
"XVSUBW": {0xE81A << 15, true}, "XVSUBV": {0xE81B << 15, true},
|
||||||
|
"XVSUBQ": {0xEA5B << 15, true},
|
||||||
|
"XVSUBF": {0xEA65 << 15, true}, "XVSUBD": {0xEA66 << 15, true},
|
||||||
|
"XVSADDB": {0xE88C << 15, true}, "XVSADDH": {0xE88D << 15, true},
|
||||||
|
"XVSADDW": {0xE88E << 15, true}, "XVSADDV": {0xE88F << 15, true},
|
||||||
|
"XVSADDBU": {0xE894 << 15, true}, "XVSADDHU": {0xE895 << 15, true},
|
||||||
|
"XVSADDWU": {0xE896 << 15, true}, "XVSADDVU": {0xE897 << 15, true},
|
||||||
|
"XVSSUBB": {0xE890 << 15, true}, "XVSSUBH": {0xE891 << 15, true},
|
||||||
|
"XVSSUBW": {0xE892 << 15, true}, "XVSSUBV": {0xE893 << 15, true},
|
||||||
|
"XVSSUBBU": {0xE898 << 15, true}, "XVSSUBHU": {0xE899 << 15, true},
|
||||||
|
"XVSSUBWU": {0xE89A << 15, true}, "XVSSUBVU": {0xE89B << 15, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The multiply families: plain and high-half [X]VMUL/[X]VMUH, the
|
||||||
|
// widening [X]VMULW{EV,OD} ladder and its accumulating [X]VMADDW twins,
|
||||||
|
// plus the [X]VMADD/[X]VMSUB fused multiply-add and the [X]VDIV/[X]VMOD
|
||||||
|
// divide and modulo pairs.
|
||||||
|
muldiv := map[string]l64Vec3Enc{
|
||||||
|
"VMULB": {0xE108 << 15, false}, "VMULH": {0xE109 << 15, false},
|
||||||
|
"VMULW": {0xE10A << 15, false}, "VMULV": {0xE10B << 15, false},
|
||||||
|
"VMUHB": {0xE10C << 15, false}, "VMUHH": {0xE10D << 15, false},
|
||||||
|
"VMUHW": {0xE10E << 15, false}, "VMUHV": {0xE10F << 15, false},
|
||||||
|
"VMUHBU": {0xE110 << 15, false}, "VMUHHU": {0xE111 << 15, false},
|
||||||
|
"VMUHWU": {0xE112 << 15, false}, "VMUHVU": {0xE113 << 15, false},
|
||||||
|
"VMULWEVHB": {0xE120 << 15, false}, "VMULWEVWH": {0xE121 << 15, false},
|
||||||
|
"VMULWEVVW": {0xE122 << 15, false}, "VMULWEVQV": {0xE123 << 15, false},
|
||||||
|
"VMULWODHB": {0xE124 << 15, false}, "VMULWODWH": {0xE125 << 15, false},
|
||||||
|
"VMULWODVW": {0xE126 << 15, false}, "VMULWODQV": {0xE127 << 15, false},
|
||||||
|
"VMULWEVHBU": {0xE130 << 15, false}, "VMULWEVWHU": {0xE131 << 15, false},
|
||||||
|
"VMULWEVVWU": {0xE132 << 15, false}, "VMULWEVQVU": {0xE133 << 15, false},
|
||||||
|
"VMULWODHBU": {0xE134 << 15, false}, "VMULWODWHU": {0xE135 << 15, false},
|
||||||
|
"VMULWODVWU": {0xE136 << 15, false}, "VMULWODQVU": {0xE137 << 15, false},
|
||||||
|
"VMULWEVHBUB": {0xE140 << 15, false}, "VMULWEVWHUH": {0xE141 << 15, false},
|
||||||
|
"VMULWEVVWUW": {0xE142 << 15, false}, "VMULWEVQVUV": {0xE143 << 15, false},
|
||||||
|
"VMULWODHBUB": {0xE144 << 15, false}, "VMULWODWHUH": {0xE145 << 15, false},
|
||||||
|
"VMULWODVWUW": {0xE146 << 15, false}, "VMULWODQVUV": {0xE147 << 15, false},
|
||||||
|
"VMADDB": {0xE150 << 15, false}, "VMADDH": {0xE151 << 15, false},
|
||||||
|
"VMADDW": {0xE152 << 15, false}, "VMADDV": {0xE153 << 15, false},
|
||||||
|
"VMSUBB": {0xE154 << 15, false}, "VMSUBH": {0xE155 << 15, false},
|
||||||
|
"VMSUBW": {0xE156 << 15, false}, "VMSUBV": {0xE157 << 15, false},
|
||||||
|
"VMADDWEVHB": {0xE158 << 15, false}, "VMADDWEVWH": {0xE159 << 15, false},
|
||||||
|
"VMADDWEVVW": {0xE15A << 15, false}, "VMADDWEVQV": {0xE15B << 15, false},
|
||||||
|
"VMADDWODHB": {0xE15C << 15, false}, "VMADDWODWH": {0xE15D << 15, false},
|
||||||
|
"VMADDWODVW": {0xE15E << 15, false}, "VMADDWODQV": {0xE15F << 15, false},
|
||||||
|
"VMADDWEVHBU": {0xE168 << 15, false}, "VMADDWEVWHU": {0xE169 << 15, false},
|
||||||
|
"VMADDWEVVWU": {0xE16A << 15, false}, "VMADDWEVQVU": {0xE16B << 15, false},
|
||||||
|
"VMADDWODHBU": {0xE16C << 15, false}, "VMADDWODWHU": {0xE16D << 15, false},
|
||||||
|
"VMADDWODVWU": {0xE16E << 15, false}, "VMADDWODQVU": {0xE16F << 15, false},
|
||||||
|
"VMADDWEVHBUB": {0xE178 << 15, false}, "VMADDWEVWHUH": {0xE179 << 15, false},
|
||||||
|
"VMADDWEVVWUW": {0xE17A << 15, false}, "VMADDWEVQVUV": {0xE17B << 15, false},
|
||||||
|
"VMADDWODHBUB": {0xE17C << 15, false}, "VMADDWODWHUH": {0xE17D << 15, false},
|
||||||
|
"VMADDWODVWUW": {0xE17E << 15, false}, "VMADDWODQVUV": {0xE17F << 15, false},
|
||||||
|
"VDIVB": {0xE1C0 << 15, false}, "VDIVH": {0xE1C1 << 15, false},
|
||||||
|
"VDIVW": {0xE1C2 << 15, false}, "VDIVV": {0xE1C3 << 15, false},
|
||||||
|
"VMODB": {0xE1C4 << 15, false}, "VMODH": {0xE1C5 << 15, false},
|
||||||
|
"VMODW": {0xE1C6 << 15, false}, "VMODV": {0xE1C7 << 15, false},
|
||||||
|
"VDIVBU": {0xE1C8 << 15, false}, "VDIVHU": {0xE1C9 << 15, false},
|
||||||
|
"VDIVWU": {0xE1CA << 15, false}, "VDIVVU": {0xE1CB << 15, false},
|
||||||
|
"VMODBU": {0xE1CC << 15, false}, "VMODHU": {0xE1CD << 15, false},
|
||||||
|
"VMODWU": {0xE1CE << 15, false}, "VMODVU": {0xE1CF << 15, false},
|
||||||
|
"VMULF": {0xE271 << 15, false}, "VMULD": {0xE272 << 15, false},
|
||||||
|
"VDIVF": {0xE275 << 15, false}, "VDIVD": {0xE276 << 15, false},
|
||||||
|
"XVMULB": {0xE908 << 15, true}, "XVMULH": {0xE909 << 15, true},
|
||||||
|
"XVMULW": {0xE90A << 15, true}, "XVMULV": {0xE90B << 15, true},
|
||||||
|
"XVMUHB": {0xE90C << 15, true}, "XVMUHH": {0xE90D << 15, true},
|
||||||
|
"XVMUHW": {0xE90E << 15, true}, "XVMUHV": {0xE90F << 15, true},
|
||||||
|
"XVMUHBU": {0xE910 << 15, true}, "XVMUHHU": {0xE911 << 15, true},
|
||||||
|
"XVMUHWU": {0xE912 << 15, true}, "XVMUHVU": {0xE913 << 15, true},
|
||||||
|
"XVMULWEVHB": {0xE920 << 15, true}, "XVMULWEVWH": {0xE921 << 15, true},
|
||||||
|
"XVMULWEVVW": {0xE922 << 15, true}, "XVMULWEVQV": {0xE923 << 15, true},
|
||||||
|
"XVMULWODHB": {0xE924 << 15, true}, "XVMULWODWH": {0xE925 << 15, true},
|
||||||
|
"XVMULWODVW": {0xE926 << 15, true}, "XVMULWODQV": {0xE927 << 15, true},
|
||||||
|
"XVMULWEVHBU": {0xE930 << 15, true}, "XVMULWEVWHU": {0xE931 << 15, true},
|
||||||
|
"XVMULWEVVWU": {0xE932 << 15, true}, "XVMULWEVQVU": {0xE933 << 15, true},
|
||||||
|
"XVMULWODHBU": {0xE934 << 15, true}, "XVMULWODWHU": {0xE935 << 15, true},
|
||||||
|
"XVMULWODVWU": {0xE936 << 15, true}, "XVMULWODQVU": {0xE937 << 15, true},
|
||||||
|
"XVMULWEVHBUB": {0xE940 << 15, true}, "XVMULWEVWHUH": {0xE941 << 15, true},
|
||||||
|
"XVMULWEVVWUW": {0xE942 << 15, true}, "XVMULWEVQVUV": {0xE943 << 15, true},
|
||||||
|
"XVMULWODHBUB": {0xE944 << 15, true}, "XVMULWODWHUH": {0xE945 << 15, true},
|
||||||
|
"XVMULWODVWUW": {0xE946 << 15, true}, "XVMULWODQVUV": {0xE947 << 15, true},
|
||||||
|
"XVMADDB": {0xE950 << 15, true}, "XVMADDH": {0xE951 << 15, true},
|
||||||
|
"XVMADDW": {0xE952 << 15, true}, "XVMADDV": {0xE953 << 15, true},
|
||||||
|
"XVMSUBB": {0xE954 << 15, true}, "XVMSUBH": {0xE955 << 15, true},
|
||||||
|
"XVMSUBW": {0xE956 << 15, true}, "XVMSUBV": {0xE957 << 15, true},
|
||||||
|
"XVMADDWEVHB": {0xE958 << 15, true}, "XVMADDWEVWH": {0xE959 << 15, true},
|
||||||
|
"XVMADDWEVVW": {0xE95A << 15, true}, "XVMADDWEVQV": {0xE95B << 15, true},
|
||||||
|
"XVMADDWODHB": {0xE95C << 15, true}, "XVMADDWODWH": {0xE95D << 15, true},
|
||||||
|
"XVMADDWODVW": {0xE95E << 15, true}, "XVMADDWODQV": {0xE95F << 15, true},
|
||||||
|
"XVMADDWEVHBU": {0xE968 << 15, true}, "XVMADDWEVWHU": {0xE969 << 15, true},
|
||||||
|
"XVMADDWEVVWU": {0xE96A << 15, true}, "XVMADDWEVQVU": {0xE96B << 15, true},
|
||||||
|
"XVMADDWODHBU": {0xE96C << 15, true}, "XVMADDWODWHU": {0xE96D << 15, true},
|
||||||
|
"XVMADDWODVWU": {0xE96E << 15, true}, "XVMADDWODQVU": {0xE96F << 15, true},
|
||||||
|
"XVMADDWEVHBUB": {0xE978 << 15, true}, "XVMADDWEVWHUH": {0xE979 << 15, true},
|
||||||
|
"XVMADDWEVVWUW": {0xE97A << 15, true}, "XVMADDWEVQVUV": {0xE97B << 15, true},
|
||||||
|
"XVMADDWODHBUB": {0xE97C << 15, true}, "XVMADDWODWHUH": {0xE97D << 15, true},
|
||||||
|
"XVMADDWODVWUW": {0xE97E << 15, true}, "XVMADDWODQVUV": {0xE97F << 15, true},
|
||||||
|
"XVDIVB": {0xE9C0 << 15, true}, "XVDIVH": {0xE9C1 << 15, true},
|
||||||
|
"XVDIVW": {0xE9C2 << 15, true}, "XVDIVV": {0xE9C3 << 15, true},
|
||||||
|
"XVMODB": {0xE9C4 << 15, true}, "XVMODH": {0xE9C5 << 15, true},
|
||||||
|
"XVMODW": {0xE9C6 << 15, true}, "XVMODV": {0xE9C7 << 15, true},
|
||||||
|
"XVDIVBU": {0xE9C8 << 15, true}, "XVDIVHU": {0xE9C9 << 15, true},
|
||||||
|
"XVDIVWU": {0xE9CA << 15, true}, "XVDIVVU": {0xE9CB << 15, true},
|
||||||
|
"XVMODBU": {0xE9CC << 15, true}, "XVMODHU": {0xE9CD << 15, true},
|
||||||
|
"XVMODWU": {0xE9CE << 15, true}, "XVMODVU": {0xE9CF << 15, true},
|
||||||
|
"XVMULF": {0xEA71 << 15, true}, "XVMULD": {0xEA72 << 15, true},
|
||||||
|
"XVDIVF": {0xEA75 << 15, true}, "XVDIVD": {0xEA76 << 15, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lane-wise shifts and rotates (three-register forms; the immediate
|
||||||
|
// forms live in l64VecImmInfo), the interleave families, the bit
|
||||||
|
// clear/set/rev register forms, the remaining logic and compare
|
||||||
|
// spellings, the widening add/subtract ladder and the vector FP
|
||||||
|
// arithmetic.
|
||||||
|
vecmisc := map[string]l64Vec3Enc{
|
||||||
|
"VSLLB": {0xE1D0 << 15, false}, "VSLLH": {0xE1D1 << 15, false},
|
||||||
|
"VSLLW": {0xE1D2 << 15, false}, "VSLLV": {0xE1D3 << 15, false},
|
||||||
|
"VSRLB": {0xE1D4 << 15, false}, "VSRLH": {0xE1D5 << 15, false},
|
||||||
|
"VSRLW": {0xE1D6 << 15, false}, "VSRLV": {0xE1D7 << 15, false},
|
||||||
|
"VSRAH": {0xE1D9 << 15, false}, "VSRAW": {0xE1DA << 15, false},
|
||||||
|
"VSRAV": {0xE1DB << 15, false},
|
||||||
|
"VROTRB": {0xE1DC << 15, false}, "VROTRH": {0xE1DD << 15, false},
|
||||||
|
"VROTRV": {0xE1DF << 15, false},
|
||||||
|
"VILVLB": {0xE234 << 15, false}, "VILVLH": {0xE235 << 15, false},
|
||||||
|
"VILVLW": {0xE236 << 15, false}, "VILVLV": {0xE237 << 15, false},
|
||||||
|
"VILVHB": {0xE238 << 15, false}, "VILVHH": {0xE239 << 15, false},
|
||||||
|
"VILVHW": {0xE23A << 15, false}, "VILVHV": {0xE23B << 15, false},
|
||||||
|
"VBITCLRB": {0xE218 << 15, false}, "VBITCLRH": {0xE219 << 15, false},
|
||||||
|
"VBITCLRW": {0xE21A << 15, false}, "VBITCLRV": {0xE21B << 15, false},
|
||||||
|
"VBITSETB": {0xE21C << 15, false}, "VBITSETH": {0xE21D << 15, false},
|
||||||
|
"VBITSETW": {0xE21E << 15, false}, "VBITSETV": {0xE21F << 15, false},
|
||||||
|
"VBITREVB": {0xE220 << 15, false}, "VBITREVH": {0xE221 << 15, false},
|
||||||
|
"VBITREVW": {0xE222 << 15, false}, "VBITREVV": {0xE223 << 15, false},
|
||||||
|
"VORV": {0xE24D << 15, false}, "VNORV": {0xE24F << 15, false},
|
||||||
|
"VANDNV": {0xE250 << 15, false}, "VORNV": {0xE251 << 15, false},
|
||||||
|
"VSEQH": {0xE001 << 15, false}, "VSEQW": {0xE002 << 15, false},
|
||||||
|
"VSLTB": {0xE00C << 15, false}, "VSLTH": {0xE00D << 15, false},
|
||||||
|
"VSLTW": {0xE00E << 15, false}, "VSLTV": {0xE00F << 15, false},
|
||||||
|
"VSLTBU": {0xE010 << 15, false}, "VSLTHU": {0xE011 << 15, false},
|
||||||
|
"VSLTWU": {0xE012 << 15, false}, "VSLTVU": {0xE013 << 15, false},
|
||||||
|
"VADDWEVHB": {0xE03C << 15, false}, "VADDWEVWH": {0xE03D << 15, false},
|
||||||
|
"VADDWEVVW": {0xE03E << 15, false}, "VADDWEVQV": {0xE03F << 15, false},
|
||||||
|
"VSUBWEVHB": {0xE040 << 15, false}, "VSUBWEVWH": {0xE041 << 15, false},
|
||||||
|
"VSUBWEVVW": {0xE042 << 15, false}, "VSUBWEVQV": {0xE043 << 15, false},
|
||||||
|
"VADDWODHB": {0xE044 << 15, false}, "VADDWODWH": {0xE045 << 15, false},
|
||||||
|
"VADDWODVW": {0xE046 << 15, false}, "VADDWODQV": {0xE047 << 15, false},
|
||||||
|
"VSUBWODHB": {0xE048 << 15, false}, "VSUBWODWH": {0xE049 << 15, false},
|
||||||
|
"VSUBWODVW": {0xE04A << 15, false}, "VSUBWODQV": {0xE04B << 15, false},
|
||||||
|
"VSUBWEVHBU": {0xE060 << 15, false}, "VSUBWEVWHU": {0xE061 << 15, false},
|
||||||
|
"VSUBWEVVWU": {0xE062 << 15, false}, "VSUBWEVQVU": {0xE063 << 15, false},
|
||||||
|
"VADDWEVHBU": {0xE05C << 15, false}, "VADDWEVWHU": {0xE05D << 15, false},
|
||||||
|
"VADDWEVVWU": {0xE05E << 15, false}, "VADDWEVQVU": {0xE05F << 15, false},
|
||||||
|
"VADDWODHBU": {0xE064 << 15, false}, "VADDWODWHU": {0xE065 << 15, false},
|
||||||
|
"VADDWODVWU": {0xE066 << 15, false}, "VADDWODQVU": {0xE067 << 15, false},
|
||||||
|
"VSUBWODHBU": {0xE068 << 15, false}, "VSUBWODWHU": {0xE069 << 15, false},
|
||||||
|
"VSUBWODVWU": {0xE06A << 15, false}, "VSUBWODQVU": {0xE06B << 15, false},
|
||||||
|
"VSHUFH": {0xE2F5 << 15, false}, "VSHUFW": {0xE2F6 << 15, false},
|
||||||
|
"VSHUFV": {0xE2F7 << 15, false},
|
||||||
|
"XVSLLB": {0xE9D0 << 15, true}, "XVSLLH": {0xE9D1 << 15, true},
|
||||||
|
"XVSLLW": {0xE9D2 << 15, true}, "XVSLLV": {0xE9D3 << 15, true},
|
||||||
|
"XVSRLB": {0xE9D4 << 15, true}, "XVSRLH": {0xE9D5 << 15, true},
|
||||||
|
"XVSRLW": {0xE9D6 << 15, true}, "XVSRLV": {0xE9D7 << 15, true},
|
||||||
|
"XVSRAB": {0xE9D8 << 15, true}, "XVSRAH": {0xE9D9 << 15, true},
|
||||||
|
"XVSRAW": {0xE9DA << 15, true}, "XVSRAV": {0xE9DB << 15, true},
|
||||||
|
"XVROTRB": {0xE9DC << 15, true}, "XVROTRH": {0xE9DD << 15, true},
|
||||||
|
"XVROTRW": {0xE9DE << 15, true}, "XVROTRV": {0xE9DF << 15, true},
|
||||||
|
"XVILVLB": {0xEA34 << 15, true}, "XVILVLH": {0xEA35 << 15, true},
|
||||||
|
"XVILVLW": {0xEA36 << 15, true}, "XVILVLV": {0xEA37 << 15, true},
|
||||||
|
"XVILVHB": {0xEA38 << 15, true}, "XVILVHH": {0xEA39 << 15, true},
|
||||||
|
"XVILVHW": {0xEA3A << 15, true}, "XVILVHV": {0xEA3B << 15, true},
|
||||||
|
"XVBITCLRB": {0xEA18 << 15, true}, "XVBITCLRH": {0xEA19 << 15, true},
|
||||||
|
"XVBITCLRW": {0xEA1A << 15, true}, "XVBITCLRV": {0xEA1B << 15, true},
|
||||||
|
"XVBITSETB": {0xEA1C << 15, true}, "XVBITSETH": {0xEA1D << 15, true},
|
||||||
|
"XVBITSETW": {0xEA1E << 15, true}, "XVBITSETV": {0xEA1F << 15, true},
|
||||||
|
"XVBITREVB": {0xEA20 << 15, true}, "XVBITREVH": {0xEA21 << 15, true},
|
||||||
|
"XVBITREVW": {0xEA22 << 15, true}, "XVBITREVV": {0xEA23 << 15, true},
|
||||||
|
"XVORV": {0xEA4D << 15, true}, "XVNORV": {0xEA4F << 15, true},
|
||||||
|
"XVANDNV": {0xEA50 << 15, true}, "XVORNV": {0xEA51 << 15, true},
|
||||||
|
"XVSEQH": {0xE801 << 15, true}, "XVSEQW": {0xE802 << 15, true},
|
||||||
|
"XVSLTB": {0xE80C << 15, true}, "XVSLTH": {0xE80D << 15, true},
|
||||||
|
"XVSLTW": {0xE80E << 15, true}, "XVSLTV": {0xE80F << 15, true},
|
||||||
|
"XVSLTBU": {0xE810 << 15, true}, "XVSLTHU": {0xE811 << 15, true},
|
||||||
|
"XVSLTWU": {0xE812 << 15, true}, "XVSLTVU": {0xE813 << 15, true},
|
||||||
|
"XVADDWEVHB": {0xE83C << 15, true}, "XVADDWEVWH": {0xE83D << 15, true},
|
||||||
|
"XVADDWEVVW": {0xE83E << 15, true}, "XVADDWEVQV": {0xE83F << 15, true},
|
||||||
|
"XVSUBWEVHB": {0xE840 << 15, true}, "XVSUBWEVWH": {0xE841 << 15, true},
|
||||||
|
"XVSUBWEVVW": {0xE842 << 15, true}, "XVSUBWEVQV": {0xE843 << 15, true},
|
||||||
|
"XVADDWODHB": {0xE844 << 15, true}, "XVADDWODWH": {0xE845 << 15, true},
|
||||||
|
"XVADDWODVW": {0xE846 << 15, true}, "XVADDWODQV": {0xE847 << 15, true},
|
||||||
|
"XVSUBWODHB": {0xE848 << 15, true}, "XVSUBWODWH": {0xE849 << 15, true},
|
||||||
|
"XVSUBWODVW": {0xE84A << 15, true}, "XVSUBWODQV": {0xE84B << 15, true},
|
||||||
|
"XVADDWEVHBU": {0xE85C << 15, true}, "XVADDWEVWHU": {0xE85D << 15, true},
|
||||||
|
"XVADDWEVVWU": {0xE85E << 15, true}, "XVADDWEVQVU": {0xE85F << 15, true},
|
||||||
|
"XVSUBWEVHBU": {0xE860 << 15, true}, "XVSUBWEVWHU": {0xE861 << 15, true},
|
||||||
|
"XVSUBWEVVWU": {0xE862 << 15, true}, "XVSUBWEVQVU": {0xE863 << 15, true},
|
||||||
|
"XVADDWODHBU": {0xE864 << 15, true}, "XVADDWODWHU": {0xE865 << 15, true},
|
||||||
|
"XVADDWODVWU": {0xE866 << 15, true}, "XVADDWODQVU": {0xE867 << 15, true},
|
||||||
|
"XVSUBWODHBU": {0xE868 << 15, true}, "XVSUBWODWHU": {0xE869 << 15, true},
|
||||||
|
"XVSUBWODVWU": {0xE86A << 15, true}, "XVSUBWODQVU": {0xE86B << 15, true},
|
||||||
|
"XVSHUFH": {0xEAF5 << 15, true}, "XVSHUFW": {0xEAF6 << 15, true},
|
||||||
|
"XVSHUFV": {0xEAF7 << 15, true},
|
||||||
|
}
|
||||||
|
for _, tab := range []map[string]l64Vec3Enc{addsub, muldiv, vecmisc} {
|
||||||
|
for m, e := range tab {
|
||||||
|
if _, dup := vec3[m]; dup {
|
||||||
|
panic("loong64: duplicate vector mnemonic " + m)
|
||||||
|
}
|
||||||
|
vec3[m] = e
|
||||||
|
}
|
||||||
|
}
|
||||||
for m, e := range vec3 {
|
for m, e := range vec3 {
|
||||||
l64InstrTable[m] = l64Enc{format: l64Fvvv, op: e.op}
|
l64InstrTable[m] = l64Enc{format: l64Fvvv, op: e.op}
|
||||||
l64VecBank[m] = e.lasx
|
l64VecBank[m] = e.lasx
|
||||||
@@ -597,21 +850,156 @@ func init() {
|
|||||||
|
|
||||||
// Immediate forms: INSTR $imm, vj, vd (or INSTR $imm, vd). The immediate
|
// Immediate forms: INSTR $imm, vj, vd (or INSTR $imm, vd). The immediate
|
||||||
// range, bias and field mask are the ones the toolchain encodes: vandi.b
|
// range, bias and field mask are the ones the toolchain encodes: vandi.b
|
||||||
// stores the raw 8-bit constant, vsrai.b stores imm+8 (byte-lane bias),
|
// stores the raw 8-bit constant, vsrari.b stores imm+8 (lane-width
|
||||||
// vseqi.b and vseqi.d store 5-bit and 7-bit two's-complement values.
|
// bias), the si5 compares store 5-bit two's-complement values and vseqi.d
|
||||||
// The mnemonics that also have a register form (VSEQB, VSEQV, VSRAB,
|
// a 7-bit field the toolchain range-checks down to si5.
|
||||||
// VROTRW) keep their three-register entry in l64InstrTable; the
|
// The mnemonics that also have a register form (the shifts, the bit
|
||||||
// dispatcher picks the immediate opcode from l64VecImmInfo by operand
|
// clear/set/rev families, VSEQ and the logic immediates) keep their
|
||||||
// kind, so the immediate entries must not overwrite the table.
|
// three-register entry in l64InstrTable; the dispatcher picks the
|
||||||
|
// immediate opcode from l64VecImmInfo by operand kind, so the immediate
|
||||||
|
// entries must not overwrite the table.
|
||||||
vecImm := map[string]l64VecImmEnc{
|
vecImm := map[string]l64VecImmEnc{
|
||||||
"VANDB": {0xE7A0 << 15, false, 0, 255, 0, 0xFF},
|
"VANDB": {0xE7A0 << 15, false, 0, 255, 0, 0xFF},
|
||||||
"XVANDB": {0xEFA0 << 15, true, 0, 255, 0, 0xFF},
|
"XVANDB": {0xEFA0 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VORB": {0xE7A8 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVORB": {0xEFA8 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VXORB": {0xE7B0 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVXORB": {0xEFB0 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VNORB": {0xE7B8 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVNORB": {0xEFB8 << 15, true, 0, 255, 0, 0xFF},
|
||||||
"VSEQB": {0xE500 << 15, false, -16, 15, 0, 0x1F},
|
"VSEQB": {0xE500 << 15, false, -16, 15, 0, 0x1F},
|
||||||
"XVSEQB": {0xE900 << 15, true, -16, 15, 0, 0x1F},
|
"XVSEQB": {0xE900 << 15, true, -16, 15, 0, 0x1F},
|
||||||
"VSEQV": {0xE503 << 15, false, -64, 63, 0, 0x7F},
|
// vseqi.h/w accept the same si5 window as vseqi.b; vseqi.d carries a
|
||||||
"XVSEQV": {0xE903 << 15, true, -64, 63, 0, 0x7F},
|
// 7-bit field, but the toolchain range-checks it down to si5 as well
|
||||||
|
// (GOARCH=loong64 go tool asm rejects VSEQV $32 and VSEQV $-64).
|
||||||
|
"VSEQH": {0xE501 << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSEQH": {0xED01 << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSEQW": {0xE502 << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSEQW": {0xED02 << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSEQV": {0xE503 << 15, false, -16, 15, 0, 0x7F},
|
||||||
|
"XVSEQV": {0xE903 << 15, true, -16, 15, 0, 0x7F},
|
||||||
|
// vslti compares against a signed (or, in the U spellings, unsigned)
|
||||||
|
// si5/ui5 constant.
|
||||||
|
"VSLTB": {0xE50C << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSLTB": {0xED0C << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSLTH": {0xE50D << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSLTH": {0xED0D << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSLTW": {0xE50E << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSLTW": {0xED0E << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSLTV": {0xE50F << 15, false, -16, 15, 0, 0x1F},
|
||||||
|
"XVSLTV": {0xED0F << 15, true, -16, 15, 0, 0x1F},
|
||||||
|
"VSLTBU": {0xE510 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSLTBU": {0xED10 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSLTHU": {0xE511 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSLTHU": {0xED11 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSLTWU": {0xE512 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSLTWU": {0xED12 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSLTVU": {0xE513 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSLTVU": {0xED13 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
// vaddi/vsubi take ui5 constants for every width on this toolchain
|
||||||
|
// (VADDVU $32 is rejected by the oracle although the field is ui8).
|
||||||
|
"VADDBU": {0xE514 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVADDBU": {0xED14 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VADDHU": {0xE515 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVADDHU": {0xED15 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VADDWU": {0xE516 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVADDWU": {0xED16 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VADDVU": {0xE517 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVADDVU": {0xED17 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSUBBU": {0xE518 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSUBBU": {0xED18 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSUBHU": {0xE519 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSUBHU": {0xED19 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSUBWU": {0xE51A << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSUBWU": {0xED1A << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSUBVU": {0xE51B << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSUBVU": {0xED1B << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
// The shift/rotate immediates ride in a width-sized field whose upper
|
||||||
|
// bits carry the lane-width code: vslli.b stores ui3 at [12:0] with
|
||||||
|
// bits [14:13] inside the opcode, vslli.h ui4 under a 4 bit mask, and
|
||||||
|
// the .w/.d spellings a raw ui5/ui6.
|
||||||
|
"VSLLB": {0x732C2000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVSLLB": {0x772C2000, true, 0, 7, 0, 0x7},
|
||||||
|
"VSLLH": {0x732C4000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVSLLH": {0x772C4000, true, 0, 15, 0, 0xF},
|
||||||
|
"VSLLW": {0xE659 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSLLW": {0xEE59 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSLLV": {0xE65A << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVSLLV": {0xEE5A << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
"VSRLB": {0x73302000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVSRLB": {0x77302000, true, 0, 7, 0, 0x7},
|
||||||
|
"VSRLH": {0x73304000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVSRLH": {0x77304000, true, 0, 15, 0, 0xF},
|
||||||
|
"VSRLW": {0xE661 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSRLW": {0xEE61 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSRLV": {0xE662 << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVSRLV": {0xEE62 << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
// vsrari/vrotri bias the field so the lane-width code rides above the
|
||||||
|
// shift amount (.b adds 8, .h 16, .w 32; .d is a raw ui6).
|
||||||
"VSRAB": {0xE668 << 15, false, 0, 7, 8, 0x1F},
|
"VSRAB": {0xE668 << 15, false, 0, 7, 8, 0x1F},
|
||||||
|
"XVSRAB": {0xEE68 << 15, true, 0, 7, 8, 0x1F},
|
||||||
|
"VSRAH": {0x73344000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVSRAH": {0x77344000, true, 0, 15, 0, 0xF},
|
||||||
|
"VSRAW": {0xE669 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVSRAW": {0xEE69 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VSRAV": {0xE66A << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVSRAV": {0xEE6A << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
"VROTRB": {0x72A02000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVROTRB": {0x76A02000, true, 0, 7, 0, 0x7},
|
||||||
|
"VROTRH": {0x72A04000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVROTRH": {0x76A04000, true, 0, 15, 0, 0xF},
|
||||||
"VROTRW": {0xE541 << 15, false, 0, 31, 0, 0x1F},
|
"VROTRW": {0xE541 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVROTRW": {0xED41 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VROTRV": {0xE542 << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVROTRV": {0xED42 << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
// vbitclri/vbitseti/vbitrevi follow the same width-coded layout.
|
||||||
|
"VBITCLRB": {0x73102000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVBITCLRB": {0x77102000, true, 0, 7, 0, 0x7},
|
||||||
|
"VBITCLRH": {0x73104000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVBITCLRH": {0x77104000, true, 0, 15, 0, 0xF},
|
||||||
|
"VBITCLRW": {0xE621 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVBITCLRW": {0xEE21 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VBITCLRV": {0xE622 << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVBITCLRV": {0xEE22 << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
"VBITSETB": {0x73142000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVBITSETB": {0x77142000, true, 0, 7, 0, 0x7},
|
||||||
|
"VBITSETH": {0x73144000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVBITSETH": {0x77144000, true, 0, 15, 0, 0xF},
|
||||||
|
"VBITSETW": {0xE629 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVBITSETW": {0xEE29 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VBITSETV": {0xE62A << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVBITSETV": {0xEE2A << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
"VBITREVB": {0x73182000, false, 0, 7, 0, 0x7},
|
||||||
|
"XVBITREVB": {0x77182000, true, 0, 7, 0, 0x7},
|
||||||
|
"VBITREVH": {0x73184000, false, 0, 15, 0, 0xF},
|
||||||
|
"XVBITREVH": {0x77184000, true, 0, 15, 0, 0xF},
|
||||||
|
"VBITREVW": {0xE631 << 15, false, 0, 31, 0, 0x1F},
|
||||||
|
"XVBITREVW": {0xEE31 << 15, true, 0, 31, 0, 0x1F},
|
||||||
|
"VBITREVV": {0xE632 << 15, false, 0, 63, 0, 0x3F},
|
||||||
|
"XVBITREVV": {0xEE32 << 15, true, 0, 63, 0, 0x3F},
|
||||||
|
// The 4-bit-select shuffles and the byte-extract/insert permutations
|
||||||
|
// take ui8 (the .d shuffle ui4 range-checked to 0..15 by the
|
||||||
|
// toolchain) packing both position nibbles.
|
||||||
|
"VSHUF4IB": {0xE720 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVSHUF4IB": {0xEF20 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VSHUF4IH": {0xE728 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVSHUF4IH": {0xEF28 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VSHUF4IW": {0xE730 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVSHUF4IW": {0xEF30 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VSHUF4IV": {0xE738 << 15, false, 0, 15, 0, 0xFF},
|
||||||
|
"XVSHUF4IV": {0xEF38 << 15, true, 0, 15, 0, 0xFF},
|
||||||
|
"VPERMIW": {0xE7C8 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVPERMIW": {0xEFC8 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"XVPERMIV": {0xEFD0 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"XVPERMIQ": {0xEFD8 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VEXTRINSB": {0xE718 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVEXTRINSB": {0xEF18 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VEXTRINSH": {0xE710 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVEXTRINSH": {0xEF10 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VEXTRINSW": {0xE708 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVEXTRINSW": {0xEF08 << 15, true, 0, 255, 0, 0xFF},
|
||||||
|
"VEXTRINSV": {0xE700 << 15, false, 0, 255, 0, 0xFF},
|
||||||
|
"XVEXTRINSV": {0xEF00 << 15, true, 0, 255, 0, 0xFF},
|
||||||
}
|
}
|
||||||
for m, e := range vecImm {
|
for m, e := range vecImm {
|
||||||
l64VecImmInfo[m] = e
|
l64VecImmInfo[m] = e
|
||||||
@@ -625,22 +1013,71 @@ func init() {
|
|||||||
"VSETANYEQB": 0xE539<<15 | 8<<10, "XVSETANYEQB": 0xED39<<15 | 8<<10,
|
"VSETANYEQB": 0xE539<<15 | 8<<10, "XVSETANYEQB": 0xED39<<15 | 8<<10,
|
||||||
"VSETANYEQV": 0xE539<<15 | 11<<10, "XVSETANYEQV": 0xED39<<15 | 11<<10,
|
"VSETANYEQV": 0xE539<<15 | 11<<10, "XVSETANYEQV": 0xED39<<15 | 11<<10,
|
||||||
"VSETALLNEV": 0xE539<<15 | 15<<10, "XVSETALLNEV": 0xED39<<15 | 15<<10,
|
"VSETALLNEV": 0xE539<<15 | 15<<10, "XVSETALLNEV": 0xED39<<15 | 15<<10,
|
||||||
|
"VSETEQV": 0xE539<<15 | 6<<10, "XVSETEQV": 0xED39<<15 | 6<<10,
|
||||||
|
"VSETANYEQH": 0xE539<<15 | 9<<10, "XVSETANYEQH": 0xED39<<15 | 9<<10,
|
||||||
|
"VSETANYEQW": 0xE539<<15 | 10<<10, "XVSETANYEQW": 0xED39<<15 | 10<<10,
|
||||||
|
"VSETALLNEB": 0xE539<<15 | 12<<10, "XVSETALLNEB": 0xED39<<15 | 12<<10,
|
||||||
|
"VSETALLNEH": 0xE539<<15 | 13<<10, "XVSETALLNEH": 0xED39<<15 | 13<<10,
|
||||||
|
"VSETALLNEW": 0xE539<<15 | 14<<10, "XVSETALLNEW": 0xED39<<15 | 14<<10,
|
||||||
}
|
}
|
||||||
for m, op := range vecCf {
|
for m, op := range vecCf {
|
||||||
l64InstrTable[m] = l64Enc{format: l64Fvcf, op: op}
|
l64InstrTable[m] = l64Enc{format: l64Fvcf, op: op}
|
||||||
l64VecBank[m] = strings.HasPrefix(m, "XV")
|
l64VecBank[m] = strings.HasPrefix(m, "XV")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lane popcount: INSTR vj, vd (the 2R layout with the opcode extending
|
// Lane popcount and the two-operand vector FP/unary spellings: INSTR vj,
|
||||||
// over the unused vk field).
|
// vd (the 2R layout with the opcode extending over the unused vk field;
|
||||||
|
// the low byte of each constant is the instruction's own sub-op).
|
||||||
vec2r := map[string]l64Vec3Enc{
|
vec2r := map[string]l64Vec3Enc{
|
||||||
"VPCNTV": {0x1CA70B << 10, false}, "XVPCNTV": {0x1DA70B << 10, true},
|
"VPCNTV": {0x1CA70B << 10, false}, "XVPCNTV": {0x1DA70B << 10, true},
|
||||||
}
|
}
|
||||||
|
// The rest of the lane popcounts, the vector negations and the vector FP
|
||||||
|
// unary conversions (loong64enc1.s).
|
||||||
|
vec2rMore := map[string]l64Vec3Enc{
|
||||||
|
"VPCNTB": {0x1CA708 << 10, false}, "VPCNTH": {0x1CA709 << 10, false},
|
||||||
|
"VPCNTW": {0x1CA70A << 10, false},
|
||||||
|
"VNEGB": {0x1CA70C << 10, false}, "VNEGH": {0x1CA70D << 10, false},
|
||||||
|
"VNEGW": {0x1CA70E << 10, false}, "VNEGV": {0x1CA70F << 10, false},
|
||||||
|
"VFCLASSF": {0x1CA735 << 10, false}, "VFCLASSD": {0x1CA736 << 10, false},
|
||||||
|
"VFSQRTF": {0x1CA739 << 10, false}, "VFSQRTD": {0x1CA73A << 10, false},
|
||||||
|
"VFRECIPF": {0x1CA73D << 10, false}, "VFRECIPD": {0x1CA73E << 10, false},
|
||||||
|
"VFRSQRTF": {0x1CA741 << 10, false}, "VFRSQRTD": {0x1CA742 << 10, false},
|
||||||
|
"VFRINTF": {0x1CA74D << 10, false}, "VFRINTD": {0x1CA74E << 10, false},
|
||||||
|
"VFRINTRMF": {0x1CA751 << 10, false}, "VFRINTRMD": {0x1CA752 << 10, false},
|
||||||
|
"VFRINTRPF": {0x1CA755 << 10, false}, "VFRINTRPD": {0x1CA756 << 10, false},
|
||||||
|
"VFRINTRZF": {0x1CA759 << 10, false}, "VFRINTRZD": {0x1CA75A << 10, false},
|
||||||
|
"VFRINTRNEF": {0x1CA75D << 10, false}, "VFRINTRNED": {0x1CA75E << 10, false},
|
||||||
|
"XVPCNTB": {0x1DA708 << 10, true}, "XVPCNTH": {0x1DA709 << 10, true},
|
||||||
|
"XVPCNTW": {0x1DA70A << 10, true},
|
||||||
|
"XVNEGB": {0x1DA70C << 10, true}, "XVNEGH": {0x1DA70D << 10, true},
|
||||||
|
"XVNEGW": {0x1DA70E << 10, true}, "XVNEGV": {0x1DA70F << 10, true},
|
||||||
|
"XVFCLASSF": {0x1DA735 << 10, true}, "XVFCLASSD": {0x1DA736 << 10, true},
|
||||||
|
"XVFSQRTF": {0x1DA739 << 10, true}, "XVFSQRTD": {0x1DA73A << 10, true},
|
||||||
|
"XVFRECIPF": {0x1DA73D << 10, true}, "XVFRECIPD": {0x1DA73E << 10, true},
|
||||||
|
"XVFRSQRTF": {0x1DA741 << 10, true}, "XVFRSQRTD": {0x1DA742 << 10, true},
|
||||||
|
"XVFRINTF": {0x1DA74D << 10, true}, "XVFRINTD": {0x1DA74E << 10, true},
|
||||||
|
"XVFRINTRMF": {0x1DA751 << 10, true}, "XVFRINTRMD": {0x1DA752 << 10, true},
|
||||||
|
"XVFRINTRPF": {0x1DA755 << 10, true}, "XVFRINTRPD": {0x1DA756 << 10, true},
|
||||||
|
"XVFRINTRZF": {0x1DA759 << 10, true}, "XVFRINTRZD": {0x1DA75A << 10, true},
|
||||||
|
"XVFRINTRNEF": {0x1DA75D << 10, true}, "XVFRINTRNED": {0x1DA75E << 10, true},
|
||||||
|
}
|
||||||
|
maps.Copy(vec2r, vec2rMore)
|
||||||
for m, e := range vec2r {
|
for m, e := range vec2r {
|
||||||
l64InstrTable[m] = l64Enc{format: l64Frr, op: e.op}
|
l64InstrTable[m] = l64Enc{format: l64Frr, op: e.op}
|
||||||
l64VecBank[m] = e.lasx
|
l64VecBank[m] = e.lasx
|
||||||
l64Vec2R[m] = true
|
l64Vec2R[m] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The four-register byte shuffle: INSTR va, vk, vj, vd (the operand the
|
||||||
|
// table reads in each field position, va at bits [19:15]).
|
||||||
|
vec4r := map[string]l64Vec3Enc{
|
||||||
|
"VSHUFB": {0x0D50 << 16, false}, "XVSHUFB": {0x0D60 << 16, true},
|
||||||
|
}
|
||||||
|
for m, e := range vec4r {
|
||||||
|
l64InstrTable[m] = l64Enc{format: l64Fvvvv, op: e.op}
|
||||||
|
l64VecBank[m] = e.lasx
|
||||||
|
l64Vec4R[m] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// l64FpMovTable maps (mnemonic, from-class, to-class) to the 2R opcode of the
|
// l64FpMovTable maps (mnemonic, from-class, to-class) to the 2R opcode of the
|
||||||
|
|||||||
@@ -507,6 +507,218 @@ TEXT ·v(SB), NOSPLIT, $0
|
|||||||
0x4C000020,
|
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
|
// TestLOONG64_vectorErrors pins the register-class and range diagnostics of
|
||||||
@@ -545,6 +757,36 @@ func TestLOONG64_vectorErrors(t *testing.T) {
|
|||||||
`TEXT ·e(SB), NOSPLIT, $0
|
`TEXT ·e(SB), NOSPLIT, $0
|
||||||
VROTRW $32, V1, V2
|
VROTRW $32, V1, V2
|
||||||
RET
|
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.
|
// VSET* wants an FCC flag, not a vector register.
|
||||||
`TEXT ·e(SB), NOSPLIT, $0
|
`TEXT ·e(SB), NOSPLIT, $0
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/constant"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/arch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go_asm.h is the header the Go compiler writes for every package that
|
||||||
|
// carries assembly (the compiler's -asmhdr output): "#define const_NAME
|
||||||
|
// value" for each package constant, and for each named struct type
|
||||||
|
// "#define TYPE__size size" plus one "#define TYPE_field offset" per field.
|
||||||
|
// GOROOT assembly includes it, and a standalone assembler has no compiler
|
||||||
|
// to have produced it, so gasm generates the equivalent itself: the package
|
||||||
|
// the .s file lives in is parsed and type-checked here, with the target
|
||||||
|
// architecture's own sizes, and the same defines are written out.
|
||||||
|
//
|
||||||
|
// The emitter mirrors cmd/compile's dumpasmhdr exactly: constants come out
|
||||||
|
// as "const_NAME", struct entries as "NAME__size" followed by the fields in
|
||||||
|
// declaration order, blank names are skipped, and float and complex
|
||||||
|
// constants are omitted (the assembler carries integers, bools and strings
|
||||||
|
// only). Aliases to structs are emitted, generic types are not: they have
|
||||||
|
// no fixed size. A define the assembly references but this header does not
|
||||||
|
// carry surfaces later as the assembler's own "undefined" diagnostic naming
|
||||||
|
// the define, which is the honest failure.
|
||||||
|
|
||||||
|
// goAsmInclude matches the #include "go_asm.h" directive, tolerant of
|
||||||
|
// whitespace, so the wiring knows which files need a generated header
|
||||||
|
// before the preprocessor runs and would report the header as missing.
|
||||||
|
var goAsmInclude = regexp.MustCompile(`(?m)^\s*#\s*include\s+"go_asm\.h"`)
|
||||||
|
|
||||||
|
// needsGoAsmHeader reports whether src includes go_asm.h.
|
||||||
|
func needsGoAsmHeader(src string) bool {
|
||||||
|
return goAsmInclude.MatchString(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// goAsmHeaderResolved reports whether the include of go_asm.h from a file in
|
||||||
|
// asmDir already resolves: to a header in the package directory itself, or
|
||||||
|
// in one of the -I directories, the way the preprocessor searches. Only an
|
||||||
|
// unresolved include is generated for; a header someone placed by hand is
|
||||||
|
// the tool the author chose, and it also wins the preprocessor's own search
|
||||||
|
// order, so generating a second copy would be dead weight at best.
|
||||||
|
func goAsmHeaderResolved(asmDir string, dirs []string) bool {
|
||||||
|
candidates := []string{filepath.Join(asmDir, "go_asm.h")}
|
||||||
|
for _, d := range dirs {
|
||||||
|
candidates = append(candidates, filepath.Join(d, "go_asm.h"))
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if st, err := os.Stat(candidate); err == nil && !st.IsDir() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateGoAsmHeader type-checks the Go package in pkgDir for goarch,
|
||||||
|
// writes its go_asm.h equivalent into dir, and returns dir. The caller
|
||||||
|
// owns the directory and its removal.
|
||||||
|
func generateGoAsmHeader(pkgDir, goarch, dir string) (string, error) {
|
||||||
|
imp := newSourceImporter(goarch)
|
||||||
|
if imp.sizes == nil {
|
||||||
|
return "", fmt.Errorf("go_asm.h: unknown GOARCH %q", goarch)
|
||||||
|
}
|
||||||
|
bp, err := imp.ctxt.ImportDir(pkgDir, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("go_asm.h for GOARCH %s in %s: %w", goarch, pkgDir, err)
|
||||||
|
}
|
||||||
|
files, errs := imp.parse(bp)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return "", fmt.Errorf("go_asm.h for GOARCH %s in %s: %s", goarch, pkgDir, errorList(errs))
|
||||||
|
}
|
||||||
|
_, info, errs := imp.checkPackage(bp, files)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return "", fmt.Errorf("go_asm.h for GOARCH %s in %s: package does not type-check: %s", goarch, pkgDir, errorList(errs))
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
fmt.Fprintf(&b, "// generated by gasm from package %s (GOARCH %s)\n\n", bp.Name, goarch)
|
||||||
|
// Files in the build's own order and declarations in source order: the
|
||||||
|
// same walk the compiler's reader makes, so the header reads the same
|
||||||
|
// way the toolchain's does. Order carries no meaning to the assembler
|
||||||
|
// (defines form a table), only to a human diffing against one.
|
||||||
|
for _, f := range files {
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
gd, ok := decl.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, spec := range gd.Specs {
|
||||||
|
switch gd.Tok {
|
||||||
|
case token.CONST:
|
||||||
|
vs, ok := spec.(*ast.ValueSpec)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, name := range vs.Names {
|
||||||
|
emitConst(&b, info.Defs[name], name.Name)
|
||||||
|
}
|
||||||
|
case token.TYPE:
|
||||||
|
ts, ok := spec.(*ast.TypeSpec)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
emitStruct(&b, imp.sizes, info.Defs[ts.Name], ts.Name.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
return "", fmt.Errorf("go_asm.h for GOARCH %s in %s: %w", goarch, pkgDir, err)
|
||||||
|
}
|
||||||
|
out := filepath.Join(dir, "go_asm.h")
|
||||||
|
if err := os.WriteFile(out, []byte(b.String()), 0o644); err != nil {
|
||||||
|
return "", fmt.Errorf("go_asm.h for GOARCH %s in %s: %w", goarch, pkgDir, err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitConst writes one const define, skipping what the toolchain skips:
|
||||||
|
// blank names, and float and complex values the assembler has no syntax for.
|
||||||
|
func emitConst(b *strings.Builder, obj types.Object, name string) {
|
||||||
|
c, ok := obj.(*types.Const)
|
||||||
|
if !ok || name == "_" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch c.Val().Kind() {
|
||||||
|
case constant.Float, constant.Complex, constant.Unknown:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "#define const_%s %s\n", name, c.Val().ExactString())
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitStruct writes one named struct type's size and field offsets,
|
||||||
|
// skipping what the toolchain skips: blank names, non-struct types, and
|
||||||
|
// generic types, whose size depends on their instantiation.
|
||||||
|
func emitStruct(b *strings.Builder, sizes types.Sizes, obj types.Object, name string) {
|
||||||
|
tn, ok := obj.(*types.TypeName)
|
||||||
|
if !ok || name == "_" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := types.Unalias(tn.Type())
|
||||||
|
// Generic types are spelled *types.Named with a type-parameter list;
|
||||||
|
// a plain struct type or an instantiated one carries none.
|
||||||
|
if named, ok := t.(*types.Named); ok && named.TypeParams().Len() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st, ok := t.Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "#define %s__size %d\n", name, sizes.Sizeof(t))
|
||||||
|
fields := make([]*types.Var, st.NumFields())
|
||||||
|
for i := range st.NumFields() {
|
||||||
|
fields[i] = st.Field(i)
|
||||||
|
}
|
||||||
|
for i, off := range sizes.Offsetsof(fields) {
|
||||||
|
fld := fields[i]
|
||||||
|
if fld.Name() == "_" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "#define %s_%s %d\n", name, fld.Name(), off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// errorList renders at most three errors, enough to say what is wrong
|
||||||
|
// without burying the diagnostic the caller actually reads.
|
||||||
|
func errorList(errs []error) string {
|
||||||
|
if len(errs) > 3 {
|
||||||
|
errs = errs[:3]
|
||||||
|
}
|
||||||
|
msgs := make([]string, len(errs))
|
||||||
|
for i, err := range errs {
|
||||||
|
msgs[i] = err.Error()
|
||||||
|
}
|
||||||
|
return strings.Join(msgs, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourceImporter type-checks imported packages from source with the target
|
||||||
|
// architecture's sizes. go/importer's "source" importer pins the host
|
||||||
|
// GOARCH, which would lay out imported types (internal/cpu, internal/abi)
|
||||||
|
// for the wrong target on a cross-architecture header, so the recursion is
|
||||||
|
// carried here with one build context and one sizes instance per
|
||||||
|
// architecture.
|
||||||
|
type sourceImporter struct {
|
||||||
|
fset *token.FileSet
|
||||||
|
ctxt *build.Context
|
||||||
|
sizes types.Sizes
|
||||||
|
pkgs map[string]*types.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSourceImporter returns the importer for one target architecture.
|
||||||
|
// Cgo is disabled so the file set is deterministic and independent of the
|
||||||
|
// host's C toolchain: cgo-tagged files drop out of the build exactly as
|
||||||
|
// they do from a CGO_ENABLED=0 build, whose assembly is what gasm targets.
|
||||||
|
func newSourceImporter(goarch string) *sourceImporter {
|
||||||
|
ctxt := new(build.Context)
|
||||||
|
*ctxt = build.Default
|
||||||
|
ctxt.GOARCH = goarch
|
||||||
|
ctxt.CgoEnabled = false
|
||||||
|
return &sourceImporter{
|
||||||
|
fset: token.NewFileSet(),
|
||||||
|
ctxt: ctxt,
|
||||||
|
sizes: types.SizesFor("gc", goarch),
|
||||||
|
pkgs: map[string]*types.Package{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import type-checks one imported package and memoises it. "unsafe" must
|
||||||
|
// resolve to go/types' own package, never to the source in GOROOT/src/unsafe:
|
||||||
|
// the source declares Sizeof and Offsetof as ordinary functions over
|
||||||
|
// ArbitraryType, and checking against that signature rejects half the
|
||||||
|
// unsafe arithmetic the gc compiler accepts, which is exactly the divergence
|
||||||
|
// srcimporter guards against the same way.
|
||||||
|
func (im *sourceImporter) Import(path string) (*types.Package, error) {
|
||||||
|
if path == "unsafe" {
|
||||||
|
return types.Unsafe, nil
|
||||||
|
}
|
||||||
|
if p, ok := im.pkgs[path]; ok {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
bp, err := im.ctxt.Import(path, "", 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files, errs := im.parse(bp)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, errors.New(errorList(errs))
|
||||||
|
}
|
||||||
|
pkg, _, _ := im.checkPackage(bp, files)
|
||||||
|
im.pkgs[path] = pkg
|
||||||
|
return pkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse reads the build package's Go files. Import-level failures (no Go
|
||||||
|
// files for the target, unreadable files) come back as errors, and the
|
||||||
|
// type-check decides the rest.
|
||||||
|
func (im *sourceImporter) parse(bp *build.Package) ([]*ast.File, []error) {
|
||||||
|
if len(bp.GoFiles) == 0 {
|
||||||
|
return nil, []error{fmt.Errorf("no Go source files for GOOS=%s GOARCH=%s", im.ctxt.GOOS, im.ctxt.GOARCH)}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
files []*ast.File
|
||||||
|
errs []error
|
||||||
|
)
|
||||||
|
for _, name := range bp.GoFiles {
|
||||||
|
f, err := parser.ParseFile(im.fset, filepath.Join(bp.Dir, name), nil, parser.SkipObjectResolution)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
return files, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPackage type-checks one package's files with the importer's sizes,
|
||||||
|
// recording every error: a header from a package that does not type-check
|
||||||
|
// could silently mis-state an offset, so the caller refuses the header
|
||||||
|
// rather than trusting it. The returned Defs map backs the root package's
|
||||||
|
// emission walk; imports only need the checked package itself.
|
||||||
|
func (im *sourceImporter) checkPackage(bp *build.Package, files []*ast.File) (*types.Package, *types.Info, []error) {
|
||||||
|
var errs []error
|
||||||
|
conf := &types.Config{
|
||||||
|
Importer: im,
|
||||||
|
Sizes: im.sizes,
|
||||||
|
Error: func(err error) { errs = append(errs, err) },
|
||||||
|
}
|
||||||
|
info := &types.Info{Defs: map[*ast.Ident]types.Object{}}
|
||||||
|
pkg, _ := conf.Check(bp.ImportPath, im.fset, files, info)
|
||||||
|
return pkg, info, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// asmhdrCache generates one go_asm.h per package directory and target
|
||||||
|
// architecture under one temp root, for callers that assemble many files
|
||||||
|
// (the corpus audit). Failures are cached too: a package that does not
|
||||||
|
// type-check must not be re-checked once per file.
|
||||||
|
type asmhdrCache struct {
|
||||||
|
root string
|
||||||
|
dirs map[string]string // "pkgDir\x00goarch" -> directory holding go_asm.h
|
||||||
|
errs map[string]error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAsmhdrCache() (*asmhdrCache, error) {
|
||||||
|
root, err := os.MkdirTemp("", "gasm-asmhdr")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &asmhdrCache{root: root, dirs: map[string]string{}, errs: map[string]error{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dirFor returns the directory holding the generated go_asm.h for pkgDir
|
||||||
|
// and goarch, generating it on first use.
|
||||||
|
func (c *asmhdrCache) dirFor(pkgDir, goarch string) (string, error) {
|
||||||
|
key := pkgDir + "\x00" + goarch
|
||||||
|
if dir, ok := c.dirs[key]; ok {
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
if err, ok := c.errs[key]; ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dir := filepath.Join(c.root, fmt.Sprintf("h%d_%s", len(c.dirs), goarch))
|
||||||
|
if _, err := generateGoAsmHeader(pkgDir, goarch, dir); err != nil {
|
||||||
|
c.errs[key] = err
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
c.dirs[key] = dir
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// close removes the temp root.
|
||||||
|
func (c *asmhdrCache) close() { os.RemoveAll(c.root) }
|
||||||
|
|
||||||
|
// ensureGoAsmHeader prepares the include directory a file that includes
|
||||||
|
// go_asm.h needs: the generated header for the package in path's directory,
|
||||||
|
// for the file's target architecture. It reports a usage error when the
|
||||||
|
// architecture cannot be determined, and passes through the generator's
|
||||||
|
// diagnostics, which name the package.
|
||||||
|
func ensureGoAsmHeader(path string, target arch.Arch, cache *asmhdrCache) (string, func(), error) {
|
||||||
|
if path == "-" {
|
||||||
|
return "", nil, errors.New("cannot generate go_asm.h for standard input (no package directory)")
|
||||||
|
}
|
||||||
|
if target == arch.Unknown {
|
||||||
|
return "", nil, errors.New("a file that includes go_asm.h needs a target architecture: name the file _<arch>.s or pass -GOARCH")
|
||||||
|
}
|
||||||
|
if cache != nil {
|
||||||
|
dir, err := cache.dirFor(filepath.Dir(path), goarchName(target))
|
||||||
|
return dir, func() {}, err
|
||||||
|
}
|
||||||
|
root, err := os.MkdirTemp("", "gasm-asmhdr")
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
dir, err := generateGoAsmHeader(filepath.Dir(path), goarchName(target), root)
|
||||||
|
if err != nil {
|
||||||
|
os.RemoveAll(root)
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return dir, func() { os.RemoveAll(root) }, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// writePkg lays out a minimal Go package in a temp directory.
|
||||||
|
func writePkg(t *testing.T, files map[string]string) string {
|
||||||
|
t.Helper()
|
||||||
|
dir := t.TempDir()
|
||||||
|
for name, src := range files {
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(src), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateFor generates the header for dir and returns its text.
|
||||||
|
func generateFor(t *testing.T, dir, goarch string) string {
|
||||||
|
t.Helper()
|
||||||
|
hdrDir, err := generateGoAsmHeader(dir, goarch, t.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generateGoAsmHeader(%q, %s): %v", dir, goarch, err)
|
||||||
|
}
|
||||||
|
b, err := os.ReadFile(filepath.Join(hdrDir, "go_asm.h"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateGoAsmHeaderShape(t *testing.T) {
|
||||||
|
dir := writePkg(t, map[string]string{"sample.go": `package sample
|
||||||
|
|
||||||
|
const bufSize = 1024
|
||||||
|
|
||||||
|
const (
|
||||||
|
a = iota * 8
|
||||||
|
b
|
||||||
|
c
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
strConst = "hello"
|
||||||
|
boolConst = true
|
||||||
|
floatConst = 1.5
|
||||||
|
_ = "the blank identifier is skipped"
|
||||||
|
)
|
||||||
|
|
||||||
|
const shift = 1 << 20
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
r int64
|
||||||
|
w int64
|
||||||
|
_ [4]byte
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type scalar int
|
||||||
|
|
||||||
|
type aliased struct {
|
||||||
|
k uint32
|
||||||
|
v uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type alias = aliased
|
||||||
|
`})
|
||||||
|
hdr := generateFor(t, dir, "amd64")
|
||||||
|
want := []string{
|
||||||
|
"#define const_bufSize 1024",
|
||||||
|
// iota resolves through go/types, one define per name.
|
||||||
|
"#define const_a 0",
|
||||||
|
"#define const_b 8",
|
||||||
|
"#define const_c 16",
|
||||||
|
`#define const_strConst "hello"`,
|
||||||
|
"#define const_boolConst true",
|
||||||
|
// Floats are the toolchain's own skip, as are blank names.
|
||||||
|
"#define const_shift 1048576",
|
||||||
|
// The blank field still occupies its bytes: the pad after w runs to
|
||||||
|
// the string's 8-byte alignment.
|
||||||
|
"#define reader__size 40",
|
||||||
|
"#define reader_r 0",
|
||||||
|
"#define reader_w 8",
|
||||||
|
"#define reader_name 24",
|
||||||
|
// Non-struct named types carry no defines; aliases to structs do.
|
||||||
|
"#define aliased__size 8",
|
||||||
|
"#define aliased_k 0",
|
||||||
|
"#define aliased_v 4",
|
||||||
|
"#define alias__size 8",
|
||||||
|
"#define alias_k 0",
|
||||||
|
"#define alias_v 4",
|
||||||
|
}
|
||||||
|
for _, w := range want {
|
||||||
|
if !strings.Contains(hdr, w+"\n") {
|
||||||
|
t.Errorf("header misses %q\ngot:\n%s", w, hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, banned := range []string{"#define const_floatConst", "#define _ ", "#define scalar"} {
|
||||||
|
if strings.Contains(hdr, banned) {
|
||||||
|
t.Errorf("header must not carry %s\ngot:\n%s", banned, hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateGoAsmHeaderPerArch(t *testing.T) {
|
||||||
|
dir := writePkg(t, map[string]string{
|
||||||
|
"common.go": `package perarch
|
||||||
|
|
||||||
|
type layout struct {
|
||||||
|
a int32
|
||||||
|
p uintptr
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
// The build-tagged file set is part of the contract: a per-arch
|
||||||
|
// package is exactly how internal/cpu declares its layouts.
|
||||||
|
"const_amd64.go": `//go:build amd64
|
||||||
|
|
||||||
|
package perarch
|
||||||
|
|
||||||
|
const flavour = 1
|
||||||
|
`,
|
||||||
|
"const_arm64.go": `//go:build arm64
|
||||||
|
|
||||||
|
package perarch
|
||||||
|
|
||||||
|
const flavour = 2
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
amd64 := generateFor(t, dir, "amd64")
|
||||||
|
arm64 := generateFor(t, dir, "arm64")
|
||||||
|
if !strings.Contains(amd64, "#define const_flavour 1\n") {
|
||||||
|
t.Errorf("amd64 header misses const_flavour 1:\n%s", amd64)
|
||||||
|
}
|
||||||
|
if !strings.Contains(arm64, "#define const_flavour 2\n") {
|
||||||
|
t.Errorf("arm64 header misses const_flavour 2:\n%s", arm64)
|
||||||
|
}
|
||||||
|
if strings.Contains(arm64, "#define const_flavour 1\n") {
|
||||||
|
t.Errorf("arm64 header must not carry the amd64 file's value")
|
||||||
|
}
|
||||||
|
// SizesFor makes the layout the target's: uintptr is 4 bytes wide on
|
||||||
|
// 386 and 8 on amd64, which must move p and grow the struct.
|
||||||
|
if !strings.Contains(amd64, "#define layout__size 16\n") || !strings.Contains(amd64, "#define layout_p 8\n") {
|
||||||
|
t.Errorf("amd64 layout wrong:\n%s", amd64)
|
||||||
|
}
|
||||||
|
w386 := generateFor(t, dir, "386")
|
||||||
|
if !strings.Contains(w386, "#define layout__size 8\n") || !strings.Contains(w386, "#define layout_p 4\n") {
|
||||||
|
t.Errorf("386 layout wrong:\n%s", w386)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateGoAsmHeaderErrors(t *testing.T) {
|
||||||
|
t.Run("type error", func(t *testing.T) {
|
||||||
|
dir := writePkg(t, map[string]string{"bad.go": `package bad
|
||||||
|
|
||||||
|
const x = undefinedIdent
|
||||||
|
`})
|
||||||
|
_, err := generateGoAsmHeader(dir, "amd64", t.TempDir())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("generation must fail for a package that does not type-check")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), dir) {
|
||||||
|
t.Errorf("error must name the package directory: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "type-check") {
|
||||||
|
t.Errorf("error must say the package does not type-check: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("no go files", func(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
_, err := generateGoAsmHeader(dir, "amd64", t.TempDir())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("generation must fail without Go files")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), dir) {
|
||||||
|
t.Errorf("error must name the package directory: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNeedsGoAsmHeader(t *testing.T) {
|
||||||
|
yes := "#include \"go_asm.h\"\n#include \"textflag.h\"\n"
|
||||||
|
no := "#include \"textflag.h\"\n#include \"funcdata.h\"\n"
|
||||||
|
if !needsGoAsmHeader(yes) {
|
||||||
|
t.Error("needsGoAsmHeader(missing on a go_asm.h include)")
|
||||||
|
}
|
||||||
|
if needsGoAsmHeader(no) {
|
||||||
|
t.Error("needsGoAsmHeader claims other headers need generation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoAsmHeaderResolved(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
if goAsmHeaderResolved(dir, nil) {
|
||||||
|
t.Error("resolved with no header anywhere")
|
||||||
|
}
|
||||||
|
other := t.TempDir()
|
||||||
|
if goAsmHeaderResolved(dir, []string{other}) {
|
||||||
|
t.Error("resolved with an empty -I directory")
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "go_asm.h"), nil, 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !goAsmHeaderResolved(dir, nil) {
|
||||||
|
t.Error("not resolved with the header in the package directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOtherGOOSFile(t *testing.T) {
|
||||||
|
for path, want := range map[string]bool{
|
||||||
|
"/x/sys_windows_amd64.s": true,
|
||||||
|
"/x/rt0_js_wasm.s": true,
|
||||||
|
"/x/sys_darwin_arm64.s": true,
|
||||||
|
"/x/sys_linux_amd64.s": false,
|
||||||
|
"/x/time_linux_amd64.s": false,
|
||||||
|
"/x/memmove_amd64.s": false,
|
||||||
|
"/x/generic.s": false,
|
||||||
|
} {
|
||||||
|
if got := otherGOOSFile(path); got != want {
|
||||||
|
t.Errorf("otherGOOSFile(%q) = %v, want %v", path, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRunCorpusAuditGoAsm covers the audit wiring end to end: a package
|
||||||
|
// beside its kernel, the kernel living off the generated defines, and the
|
||||||
|
// histogram recording a generation failure as its own reason.
|
||||||
|
func TestRunCorpusAuditGoAsm(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
write := func(name, src string) {
|
||||||
|
t.Helper()
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(src), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write("pkg.go", `package corpus
|
||||||
|
|
||||||
|
const pageSize = 4096
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
magic uint64
|
||||||
|
flags uint64
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
write("kern_amd64.s", "#include \"go_asm.h\"\nTEXT \xc2\xb7f(SB), NOSPLIT, $0-16\n\tMOVQ\t$const_pageSize, AX\n\tMOVQ\t$header__size, BX\n\tRET\n")
|
||||||
|
// The defines live in the file's own package; a kernel in a directory
|
||||||
|
// without Go files has no package to generate from.
|
||||||
|
if err := os.MkdirAll(filepath.Join(dir, "sub"), 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
write(filepath.Join("sub", "lonely_arm64.s"), "#include \"go_asm.h\"\nTEXT \xc2\xb7g(SB), NOSPLIT, $0-0\n\tRET\n")
|
||||||
|
|
||||||
|
stats, err := runCorpusAudit(dir, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("runCorpusAudit: %v", err)
|
||||||
|
}
|
||||||
|
get := func(name string) *corpusTally {
|
||||||
|
for i, tg := range stats.targets {
|
||||||
|
if tg.name == name {
|
||||||
|
return stats.tallies[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatalf("no tally for %s", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if a := get("amd64"); a.attempted != 1 || a.assembled != 1 {
|
||||||
|
t.Errorf("amd64 = %d/%d, want 1/1", a.assembled, a.attempted)
|
||||||
|
}
|
||||||
|
// lonely_arm64.s is an arm64 file whose package cannot be generated.
|
||||||
|
if a := get("arm64"); a.attempted != 1 || a.assembled != 0 {
|
||||||
|
t.Errorf("arm64 = %d/%d, want 0/1", a.assembled, a.attempted)
|
||||||
|
}
|
||||||
|
if r := get("arm64").reasons["go_asm.h generation failed"]; r != 1 {
|
||||||
|
t.Errorf("arm64 go_asm.h failure count = %d, want 1", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGenerateGoAsmHeaderRuntime pins the generator against the real thing:
|
||||||
|
// the runtime package, whose header the toolchain's own -asmhdr output was
|
||||||
|
// sampled from. Skipped in short mode: it type-checks the whole package.
|
||||||
|
func TestGenerateGoAsmHeaderRuntime(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("type-checks the whole runtime package")
|
||||||
|
}
|
||||||
|
dir, err := generateGoAsmHeader("/usr/local/go/src/runtime", "amd64", t.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generateGoAsmHeader(runtime): %v", err)
|
||||||
|
}
|
||||||
|
b, err := os.ReadFile(dir + "/go_asm.h")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hdr := string(b)
|
||||||
|
for _, want := range []string{
|
||||||
|
"#define const_hashSize 8\n",
|
||||||
|
"#define const_avxSupported 1\n",
|
||||||
|
"#define const_pageSize 8192\n",
|
||||||
|
"#define g_stackguard0 16\n",
|
||||||
|
"#define m__size ",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(hdr, want) {
|
||||||
|
t.Errorf("runtime header misses %q", want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+98
-8
@@ -418,7 +418,9 @@ func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
|||||||
// The toolchain's shipped headers (funcdata.h and friends) define the
|
// The toolchain's shipped headers (funcdata.h and friends) define the
|
||||||
// macros GOROOT files include; a corpus audit measures those files, so
|
// macros GOROOT files include; a corpus audit measures those files, so
|
||||||
// the header directory joins the search path automatically. go_asm.h
|
// the header directory joins the search path automatically. go_asm.h
|
||||||
// is compiler-generated per package and stays unresolvable on purpose.
|
// is compiler-generated per package, so it is not resolved from here:
|
||||||
|
// files that include it get one generated per target architecture,
|
||||||
|
// which runCorpusAudit arranges.
|
||||||
if out, err := exec.Command("go", "env", "GOROOT").Output(); err == nil {
|
if out, err := exec.Command("go", "env", "GOROOT").Output(); err == nil {
|
||||||
pkgInclude := filepath.Join(strings.TrimSpace(string(out)), "pkg", "include")
|
pkgInclude := filepath.Join(strings.TrimSpace(string(out)), "pkg", "include")
|
||||||
if fi, err := os.Stat(pkgInclude); err == nil && fi.IsDir() {
|
if fi, err := os.Stat(pkgInclude); err == nil && fi.IsDir() {
|
||||||
@@ -462,9 +464,15 @@ var goPortSuffixes = []string{
|
|||||||
"s390x", "wasm",
|
"s390x", "wasm",
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherPortFile reports whether the file's name carries a Go-architecture
|
// otherPortFile reports whether the file belongs to a build no supported
|
||||||
// suffix gasm does not support.
|
// target ever compiles: either its name carries a Go-architecture suffix
|
||||||
|
// gasm does not support, or, for a file with no architecture suffix at all,
|
||||||
|
// it names another GOOS, which go/build drops from the file set
|
||||||
|
// (rt0_js_wasm.s is a javascript build, not a generic one).
|
||||||
func otherPortFile(path string) bool {
|
func otherPortFile(path string) bool {
|
||||||
|
if otherGOOSFile(path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
base := path
|
base := path
|
||||||
if i := strings.LastIndexByte(base, '/'); i >= 0 {
|
if i := strings.LastIndexByte(base, '/'); i >= 0 {
|
||||||
base = base[i+1:]
|
base = base[i+1:]
|
||||||
@@ -477,6 +485,29 @@ func otherPortFile(path string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// goOSNames are the GOOS values go/build recognises in file names.
|
||||||
|
var goOSNames = map[string]bool{
|
||||||
|
"aix": true, "darwin": true, "dragonfly": true, "freebsd": true,
|
||||||
|
"ios": true, "js": true, "linux": true, "netbsd": true,
|
||||||
|
"openbsd": true, "plan9": true, "solaris": true, "wasip1": true,
|
||||||
|
"windows": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherGOOSFile reports whether the file's name names a GOOS other than the
|
||||||
|
// host's, by go/build's file-name rules.
|
||||||
|
func otherGOOSFile(path string) bool {
|
||||||
|
base := path
|
||||||
|
if i := strings.LastIndexByte(base, '/'); i >= 0 {
|
||||||
|
base = base[i+1:]
|
||||||
|
}
|
||||||
|
for seg := range strings.SplitSeq(strings.TrimSuffix(base, ".s"), "_") {
|
||||||
|
if goOSNames[seg] && seg != runtime.GOOS {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
||||||
files, err := asmFiles(root)
|
files, err := asmFiles(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -497,12 +528,20 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
// its name allows assembles it.
|
// its name allows assembles it.
|
||||||
full, generic, otherPort := 0, 0, 0
|
full, generic, otherPort := 0, 0, 0
|
||||||
|
|
||||||
|
// Header generation is created on first use, so a corpus with no
|
||||||
|
// go_asm.h includes never pays for a temp directory.
|
||||||
|
var hdr *asmhdrCache
|
||||||
|
defer func() {
|
||||||
|
if hdr != nil {
|
||||||
|
hdr.close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, path := range files {
|
for _, path := range files {
|
||||||
src, err := readSource(path)
|
src, err := readSource(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
|
||||||
|
|
||||||
var wanted []int // indexes into targets
|
var wanted []int // indexes into targets
|
||||||
if a := arch.FromFilename(path); a != arch.Unknown {
|
if a := arch.FromFilename(path); a != arch.Unknown {
|
||||||
@@ -513,10 +552,11 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
}
|
}
|
||||||
} else if otherPortFile(path) {
|
} else if otherPortFile(path) {
|
||||||
// A file named for a Go port gasm does not support (arm,
|
// A file named for a Go port gasm does not support (arm,
|
||||||
// 386, s390x, ...) is compiled by no supported-arch build,
|
// 386, s390x, ...) or for another GOOS is compiled by no
|
||||||
// so it is neither generic nor a per-arch attempt: counting
|
// supported-arch build, so it is neither generic nor a
|
||||||
// it as generic would make the headline unreachably low
|
// per-arch attempt: counting it as generic would make the
|
||||||
// for reasons no supported target can fix.
|
// headline unreachably low for reasons no supported target
|
||||||
|
// can fix.
|
||||||
otherPort++
|
otherPort++
|
||||||
} else {
|
} else {
|
||||||
generic++
|
generic++
|
||||||
@@ -525,6 +565,54 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A file that includes go_asm.h parses against a per-target header:
|
||||||
|
// the defines differ per architecture (internal/cpu's layout, for
|
||||||
|
// one), so the parse cannot be shared the way a header-free file's
|
||||||
|
// can. A generation failure is a failure for every target, named
|
||||||
|
// for the package rather than a bare "include not found". A header
|
||||||
|
// already resolvable in the package directory or the -I list is
|
||||||
|
// left alone.
|
||||||
|
if len(wanted) > 0 && needsGoAsmHeader(src) && !goAsmHeaderResolved(filepath.Dir(path), dirs) {
|
||||||
|
if hdr == nil {
|
||||||
|
if hdr, err = newAsmhdrCache(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkgDir := filepath.Dir(path)
|
||||||
|
ok := true
|
||||||
|
for _, i := range wanted {
|
||||||
|
tg, t := targets[i], tallies[i]
|
||||||
|
t.attempted++
|
||||||
|
hdrDir, err := hdr.dirFor(pkgDir, goarchName(tg.a))
|
||||||
|
if err != nil {
|
||||||
|
ok = false
|
||||||
|
t.fail(path, corpusReason(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
||||||
|
Expand: true,
|
||||||
|
IncludeDirs: append(slices.Clone(dirs), hdrDir),
|
||||||
|
})
|
||||||
|
if len(errs) > 0 {
|
||||||
|
ok = false
|
||||||
|
t.fail(path, corpusReason(errs[0]))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := assembleFile(tg.a, f); err != nil {
|
||||||
|
ok = false
|
||||||
|
t.fail(path, corpusReason(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.assembled++
|
||||||
|
}
|
||||||
|
if ok && len(wanted) > 0 {
|
||||||
|
full++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
||||||
|
|
||||||
ok := true
|
ok := true
|
||||||
for _, i := range wanted {
|
for _, i := range wanted {
|
||||||
tg, t := targets[i], tallies[i]
|
tg, t := targets[i], tallies[i]
|
||||||
@@ -581,6 +669,8 @@ func printCorpusStats(s *corpusStats) {
|
|||||||
func corpusReason(err error) string {
|
func corpusReason(err error) string {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
switch {
|
switch {
|
||||||
|
case strings.Contains(msg, "go_asm.h for GOARCH"):
|
||||||
|
return "go_asm.h generation failed"
|
||||||
case strings.Contains(msg, "unsupported"), strings.Contains(msg, "cannot encode"):
|
case strings.Contains(msg, "unsupported"), strings.Contains(msg, "cannot encode"):
|
||||||
return "instruction not encodable"
|
return "instruction not encodable"
|
||||||
case strings.Contains(msg, "undefined label"):
|
case strings.Contains(msg, "undefined label"):
|
||||||
|
|||||||
@@ -503,6 +503,12 @@ system toolchain; goobj emits the Go toolchain's own object format, which
|
|||||||
cmd/link consumes directly (it requires -p, the package path, and the
|
cmd/link consumes directly (it requires -p, the package path, and the
|
||||||
installed Go toolchain: the object preamble is captured from go tool asm
|
installed Go toolchain: the object preamble is captured from go tool asm
|
||||||
and the format version from go version).
|
and the format version from go version).
|
||||||
|
|
||||||
|
A file that includes go_asm.h gets that header generated automatically from
|
||||||
|
the package it lives in (the .go files beside it, type-checked for the
|
||||||
|
target architecture, the toolchain's own defines), so GOROOT assembly
|
||||||
|
assembles without a compiler. A package that has no Go files for the
|
||||||
|
target or does not type-check is a hard error naming the package.
|
||||||
`)
|
`)
|
||||||
out := fs.String("o", "", "write the output to this file")
|
out := fs.String("o", "", "write the output to this file")
|
||||||
format := fs.String("format", "raw", "output format: raw (concatenated image), elf or goobj (Go object)")
|
format := fs.String("format", "raw", "output format: raw (concatenated image), elf or goobj (Go object)")
|
||||||
@@ -538,6 +544,23 @@ and the format version from go version).
|
|||||||
fmt.Fprintln(os.Stderr, "gasm:", err)
|
fmt.Fprintln(os.Stderr, "gasm:", err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
// A file that includes go_asm.h cannot assemble without the package's
|
||||||
|
// defines, and without a compiler nothing else has generated them, so
|
||||||
|
// gasm produces the equivalent itself: automatic, because the compiler
|
||||||
|
// behaves the same way and a flag would only ever be forgotten. A
|
||||||
|
// generation failure is fatal and names the package: assembling against
|
||||||
|
// a missing header would fail later with a bare "undefined" instead.
|
||||||
|
// A go_asm.h that already resolves (placed by hand, or passed with -I)
|
||||||
|
// is left alone.
|
||||||
|
if needsGoAsmHeader(src) && !goAsmHeaderResolved(filepath.Dir(path), dirs) {
|
||||||
|
hdrDir, cleanup, err := ensureGoAsmHeader(path, targetArch, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "gasm asm:", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
dirs = append(dirs, hdrDir)
|
||||||
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
||||||
|
|||||||
+16
-6
@@ -254,11 +254,12 @@ func renderPreproc(line []token.Token) string {
|
|||||||
line[2].Kind == token.String {
|
line[2].Kind == token.String {
|
||||||
return "#include " + line[2].Text
|
return "#include " + line[2].Text
|
||||||
}
|
}
|
||||||
parts := make([]string, 0, len(line)-1)
|
// The body of a directive, a macro definition included, is an ordinary
|
||||||
for _, t := range line[1:] {
|
// token run: rendering it through renderOps applies the same punctuation
|
||||||
parts = append(parts, t.Text)
|
// rules as everywhere else, so a macro body keeps its canonical spelling
|
||||||
}
|
// ($v, (a, b), the ';' separators between statements) instead of being
|
||||||
return "#" + strings.Join(parts, " ")
|
// spread with a space between every token.
|
||||||
|
return "#" + renderOps(line[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderOps re-spaces a run of operand tokens into canonical form. It never
|
// renderOps re-spaces a run of operand tokens into canonical form. It never
|
||||||
@@ -348,6 +349,13 @@ func spaceBetween(prev, cur token.Token) bool {
|
|||||||
return false
|
return false
|
||||||
case token.Comma:
|
case token.Comma:
|
||||||
return false
|
return false
|
||||||
|
case token.Semicolon:
|
||||||
|
// A ';' is a statement separator on the assembly path, not an
|
||||||
|
// operand: dropping it would fuse two statements into a line the
|
||||||
|
// assembler rejects, so it must survive as punctuation. It glues
|
||||||
|
// to the statement it ends and the next statement takes one space,
|
||||||
|
// matching the toolchain's listing style.
|
||||||
|
return false
|
||||||
case token.Star, token.Plus, token.Minus, token.Slash, token.Pipe:
|
case token.Star, token.Plus, token.Minus, token.Slash, token.Pipe:
|
||||||
return false
|
return false
|
||||||
case token.LShift, token.RShift, token.Arrow, token.At:
|
case token.LShift, token.RShift, token.Arrow, token.At:
|
||||||
@@ -376,7 +384,9 @@ func spaceBetween(prev, cur token.Token) bool {
|
|||||||
return false
|
return false
|
||||||
case token.LAngle, token.RAngle:
|
case token.LAngle, token.RAngle:
|
||||||
return false
|
return false
|
||||||
case token.Comma:
|
case token.Comma, token.Semicolon:
|
||||||
|
// The statement after a ';' separator takes its own space, exactly
|
||||||
|
// like the operand after a comma.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ package format
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/ast"
|
||||||
"sourcedock.dev/petrbalvin/gasm-devkit/lexer"
|
"sourcedock.dev/petrbalvin/gasm-devkit/lexer"
|
||||||
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||||
"sourcedock.dev/petrbalvin/gasm-devkit/token"
|
"sourcedock.dev/petrbalvin/gasm-devkit/token"
|
||||||
@@ -298,6 +300,185 @@ func TestCRLFInputIsNormalisedToLF(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSemicolonSeparators pins the treatment of ';' statement separators.
|
||||||
|
// The separator is load-bearing on the assembly path, where the parser reads
|
||||||
|
// semicolon-separated statements: a formatter that drops it fuses two
|
||||||
|
// statements into a line the assembler rejects, which is data corruption.
|
||||||
|
// Each row pins the canonical spelling, one space after the ';', tight
|
||||||
|
// before it, the way the toolchain's own sources and listings write it.
|
||||||
|
func TestSemicolonSeparators(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
in string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "between instructions, tight",
|
||||||
|
in: "TEXT ·f(SB), $0\nBYTE $0x48;BYTE $0xc7\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tBYTE $0x48; BYTE $0xc7\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "between instructions, spaced",
|
||||||
|
in: "TEXT ·f(SB), $0\nBYTE $0x48 ; BYTE $0xc7\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tBYTE $0x48; BYTE $0xc7\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "after a label",
|
||||||
|
in: "TEXT ·f(SB), $0\nlabel: BYTE $1; BYTE $2\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\nlabel:\n\tBYTE $1; BYTE $2\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// The continuation-spliced macro shape of the runtime sources:
|
||||||
|
// the lexer makes one logical line of the backslash continuations.
|
||||||
|
name: "inside a macro body, continued",
|
||||||
|
in: "#define MOVLTOREG(v, off) \\\n\tMOVL $v, AX; \\\n\tMOVL AX, ret+off(FP)\n",
|
||||||
|
want: "#define MOVLTOREG(v, off) MOVL $v, AX; MOVL AX, ret+off(FP)\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inside a macro body, one line",
|
||||||
|
in: "#define PEAS BYTE $0x0a; BYTE $0x0b\n",
|
||||||
|
want: "#define PEAS BYTE $0x0a; BYTE $0x0b\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "several separators in one line",
|
||||||
|
in: "TEXT ·f(SB), $0\nBYTE $1; BYTE $2; BYTE $3\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tBYTE $1; BYTE $2; BYTE $3\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two separators back to back",
|
||||||
|
in: "TEXT ·f(SB), $0\nBYTE $1;; BYTE $2\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tBYTE $1;; BYTE $2\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inside a line comment, untouched",
|
||||||
|
in: "TEXT ·f(SB), $0\n// keep; the; separators\nBYTE $1\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\t// keep; the; separators\n\tBYTE $1\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "after a statement, before a comment",
|
||||||
|
in: "TEXT ·f(SB), $0\nMOVQ AX, BX; // tail\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tMOVQ AX, BX; // tail\n\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "last character on a line",
|
||||||
|
in: "TEXT ·f(SB), $0\nBYTE $1;\nRET\n",
|
||||||
|
want: "TEXT ·f(SB), $0\n\tBYTE $1;\n\tRET\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got := Source(tc.in)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Fatalf("formatting mismatch:\n--- got ---\n%q\n--- want ---\n%q", got, tc.want)
|
||||||
|
}
|
||||||
|
if again := Source(got); again != got {
|
||||||
|
t.Fatalf("not idempotent:\n%q", again)
|
||||||
|
}
|
||||||
|
if in, out := strings.Count(tc.in, ";"), strings.Count(got, ";"); in != out {
|
||||||
|
t.Fatalf("semicolon count changed: %d -> %d\n%s", in, out, got)
|
||||||
|
}
|
||||||
|
if _, errs := parser.Parse("in.s", got); len(errs) > 0 {
|
||||||
|
t.Fatalf("formatted output no longer parses: %v", errs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSemicolonStatementRoundTrip proves the formatter's contract on the
|
||||||
|
// path where ';' separates statements: parse the source the way the
|
||||||
|
// assembler does, format it, re-parse the formatted text and compare the
|
||||||
|
// statement sequence. Raw operand texts are token-joined, so they are
|
||||||
|
// insensitive to the whitespace a format pass chooses, and the comparison
|
||||||
|
// can only fail when a token is lost: dropping a ';' fuses two statements
|
||||||
|
// into one, exactly the corruption the released formatter committed.
|
||||||
|
func TestSemicolonStatementRoundTrip(t *testing.T) {
|
||||||
|
src := "#define MOVLTOREG(v, off) \\\n" +
|
||||||
|
"\tMOVL $v, AX; \\\n" +
|
||||||
|
"\tMOVL AX, ret+off(FP)\n" +
|
||||||
|
"\n" +
|
||||||
|
"TEXT ·f(SB), NOSPLIT, $0\n" +
|
||||||
|
"BYTE $0x48; BYTE $0xc7\n" +
|
||||||
|
"first: BYTE $1; BYTE $2\n" +
|
||||||
|
"MOVLTOREG($42, 0)\n" +
|
||||||
|
"RET\n"
|
||||||
|
|
||||||
|
before, errs := parser.ParseWithOptions("in.s", src, parser.Options{Expand: true})
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("source does not parse: %v", errs)
|
||||||
|
}
|
||||||
|
formatted := Source(src)
|
||||||
|
after, errs := parser.ParseWithOptions("in.s", formatted, parser.Options{Expand: true})
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("formatted source does not parse: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
want, got := stmtSignature(before), stmtSignature(after)
|
||||||
|
if !slices.Equal(got, want) {
|
||||||
|
t.Fatalf("statement sequence changed:\n--- before ---\n%q\n--- after ---\n%q", want, got)
|
||||||
|
}
|
||||||
|
if again := Source(formatted); again != formatted {
|
||||||
|
t.Fatalf("not idempotent:\n%q", again)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The two BYTE statements on the first line must stay two: one fused
|
||||||
|
// statement here is the exact defect this package once shipped.
|
||||||
|
var bytes []string
|
||||||
|
for _, stmt := range stmtSignature(after) {
|
||||||
|
if rest, ok := strings.CutPrefix(stmt, "instr BYTE "); ok {
|
||||||
|
bytes = append(bytes, rest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if want := []string{"$ 0x48", "$ 0xc7", "$ 1", "$ 2"}; !slices.Equal(bytes, want) {
|
||||||
|
t.Fatalf("BYTE statements after expansion = %q, want %q", bytes, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stmtSignature flattens a parsed file into one string per declaration and
|
||||||
|
// statement, in source order. Every component is token-derived, so the
|
||||||
|
// signature is stable across format passes and moves only when a token is
|
||||||
|
// lost or gained.
|
||||||
|
func stmtSignature(f *ast.File) []string {
|
||||||
|
var out []string
|
||||||
|
for _, d := range f.Decls {
|
||||||
|
switch d := d.(type) {
|
||||||
|
case *ast.Text:
|
||||||
|
out = append(out, "text "+d.Name.Raw)
|
||||||
|
for _, s := range d.Body {
|
||||||
|
out = append(out, stmtText(s))
|
||||||
|
}
|
||||||
|
case *ast.Globl:
|
||||||
|
out = append(out, "globl "+d.Name.Raw)
|
||||||
|
case *ast.Data:
|
||||||
|
out = append(out, "data "+d.Name.Raw)
|
||||||
|
case *ast.Include:
|
||||||
|
out = append(out, "include "+d.Header.Text)
|
||||||
|
case *ast.Preproc:
|
||||||
|
out = append(out, "preproc "+d.Raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, s := range f.Orphans {
|
||||||
|
out = append(out, stmtText(s))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// stmtText renders one statement for stmtSignature.
|
||||||
|
func stmtText(s ast.Stmt) string {
|
||||||
|
switch s := s.(type) {
|
||||||
|
case *ast.Label:
|
||||||
|
return "label " + s.Name.Text
|
||||||
|
case *ast.Instr:
|
||||||
|
parts := make([]string, 0, len(s.Operands)+1)
|
||||||
|
parts = append(parts, s.Mnemonic.Text)
|
||||||
|
for _, op := range s.Operands {
|
||||||
|
parts = append(parts, op.Raw)
|
||||||
|
}
|
||||||
|
return "instr " + strings.Join(parts, " ")
|
||||||
|
default:
|
||||||
|
return "stmt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// lexOperands lexes a single operand string and drops the EOF token.
|
// lexOperands lexes a single operand string and drops the EOF token.
|
||||||
func lexOperands(s string) []token.Token {
|
func lexOperands(s string) []token.Token {
|
||||||
toks := lexer.Tokenize(s)
|
toks := lexer.Tokenize(s)
|
||||||
|
|||||||
+11
-3
@@ -16,8 +16,16 @@ import (
|
|||||||
"sourcedock.dev/petrbalvin/gasm-devkit/token"
|
"sourcedock.dev/petrbalvin/gasm-devkit/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// middleDot is the Plan 9 symbol separator (U+00B7), used in ·funcName(SB).
|
const (
|
||||||
const middleDot = '\u00B7'
|
// middleDot is the Plan 9 symbol separator (U+00B7), used in
|
||||||
|
// ·funcName(SB): it stands for the period between package path and name.
|
||||||
|
middleDot = '\u00B7'
|
||||||
|
// divisionSlash is the Plan 9 path separator (U+2215), used inside the
|
||||||
|
// package path of a symbol: internal∕runtime∕atomic·Xchg. Like the
|
||||||
|
// middle dot it is an identifier character, so a package path containing
|
||||||
|
// it lexes as one name; the ordinary slash (U+002F) stays punctuation.
|
||||||
|
divisionSlash = '\u2215'
|
||||||
|
)
|
||||||
|
|
||||||
// Lexer scans a source string one token at a time.
|
// Lexer scans a source string one token at a time.
|
||||||
type Lexer struct {
|
type Lexer struct {
|
||||||
@@ -460,7 +468,7 @@ func isHexDigit(r rune) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isIdentStart(r rune) bool {
|
func isIdentStart(r rune) bool {
|
||||||
return r == '_' || r == middleDot || unicode.IsLetter(r)
|
return r == '_' || r == middleDot || r == divisionSlash || unicode.IsLetter(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isIdentChar(r rune) bool {
|
func isIdentChar(r rune) bool {
|
||||||
|
|||||||
@@ -182,6 +182,19 @@ func TestNulIsIllegal(t *testing.T) {
|
|||||||
eq(t, texts("MOVQ \x00 AX"), []string{"MOVQ", "\x00", "AX"})
|
eq(t, texts("MOVQ \x00 AX"), []string{"MOVQ", "\x00", "AX"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDivisionSlashInIdentifiers(t *testing.T) {
|
||||||
|
// U+2215 DIVISION SLASH is an identifier character, the way the
|
||||||
|
// toolchain's tokenizer treats it: the package path of a symbol is
|
||||||
|
// written with it (internal∕runtime∕atomic·Xchg) and must lex as one
|
||||||
|
// name. The ordinary slash (U+002F) stays punctuation.
|
||||||
|
eq(t, texts("CALL internal∕runtime∕atomic·Xchg(SB)"),
|
||||||
|
[]string{"CALL", "internal∕runtime∕atomic·Xchg", "(", "SB", ")"})
|
||||||
|
eq(t, texts("MOVQ sync∕atomic·Align(SB), AX"),
|
||||||
|
[]string{"MOVQ", "sync∕atomic·Align", "(", "SB", ")", ",", "AX"})
|
||||||
|
// It may also begin a name, like any letter of the toolchain's rule.
|
||||||
|
eq(t, kinds("∕x"), []token.Kind{token.Ident})
|
||||||
|
}
|
||||||
|
|
||||||
// TestOffsetsAroundInvalidByte pins Position.Offset against the original
|
// TestOffsetsAroundInvalidByte pins Position.Offset against the original
|
||||||
// bytes: an invalid UTF-8 byte decodes to RuneError but advances the offset
|
// bytes: an invalid UTF-8 byte decodes to RuneError but advances the offset
|
||||||
// table by exactly one byte, so every later position stays a true byte
|
// table by exactly one byte, so every later position stays a true byte
|
||||||
|
|||||||
+41
-2
@@ -32,9 +32,8 @@ func (e Error) Error() string {
|
|||||||
// returned file is usable even when errors is non-empty.
|
// returned file is usable even when errors is non-empty.
|
||||||
func Parse(path, src string) (*ast.File, []error) {
|
func Parse(path, src string) (*ast.File, []error) {
|
||||||
tokens := lexer.Tokenize(src)
|
tokens := lexer.Tokenize(src)
|
||||||
lines := splitLines(tokens)
|
|
||||||
p := &state{path: path}
|
p := &state{path: path}
|
||||||
p.parse(lines)
|
p.parse(statementLines(tokens))
|
||||||
return p.file, p.errs
|
return p.file, p.errs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +72,46 @@ func splitLines(tokens []token.Token) [][]token.Token {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statementLines turns the token stream into the logical lines the parser
|
||||||
|
// reads: physical lines split at the ';' statement separators, exactly the
|
||||||
|
// way the expansion path treats the expanded bodies. The runtime writes
|
||||||
|
// "ROLQ $3, DI; ROLQ $13, DI" and "REP; MOVSB" in plain files, and the
|
||||||
|
// separator carries no meaning beyond the break. Comments are statement
|
||||||
|
// text, not structure: the lexer delivers a whole comment as one token, so
|
||||||
|
// a ';' inside a comment is never a separator; a comment after a statement
|
||||||
|
// stays on that statement's line; and a comment that sits between
|
||||||
|
// statements (the runtime's "NO_LOCAL_POINTERS; /* … */" style) stands as
|
||||||
|
// its own logical line, like a whole-line comment.
|
||||||
|
func statementLines(tokens []token.Token) [][]token.Token {
|
||||||
|
var out [][]token.Token
|
||||||
|
var cur []token.Token
|
||||||
|
flush := func() {
|
||||||
|
if len(cur) > 0 {
|
||||||
|
out = append(out, cur)
|
||||||
|
cur = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range tokens {
|
||||||
|
switch t.Kind {
|
||||||
|
case token.EOF:
|
||||||
|
// The stream's terminator is not statement content.
|
||||||
|
case token.Newline, token.Semicolon:
|
||||||
|
flush()
|
||||||
|
case token.Comment:
|
||||||
|
if len(cur) > 0 {
|
||||||
|
cur = append(cur, t)
|
||||||
|
} else {
|
||||||
|
out = append(out, []token.Token{t})
|
||||||
|
}
|
||||||
|
flush()
|
||||||
|
default:
|
||||||
|
cur = append(cur, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func (p *state) parse(lines [][]token.Token) {
|
func (p *state) parse(lines [][]token.Token) {
|
||||||
p.file = &ast.File{Path: p.path, Macros: map[string]bool{}}
|
p.file = &ast.File{Path: p.path, Macros: map[string]bool{}}
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
|||||||
@@ -401,3 +401,99 @@ func TestInt64MinimumImmediate(t *testing.T) {
|
|||||||
t.Errorf("imm.Float = %q, want empty", imm.Float)
|
t.Errorf("imm.Float = %q, want empty", imm.Float)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDivisionSlashPackagePath covers the runtime's package-path spelling:
|
||||||
|
// U+2215 DIVISION SLASH separates the elements of an import path inside a
|
||||||
|
// symbol (internal∕runtime∕atomic·Xchg), and the middle dot still separates
|
||||||
|
// the package from the name. The whole spelling must reach the symbol, not
|
||||||
|
// stop at the first slash.
|
||||||
|
func TestDivisionSlashPackagePath(t *testing.T) {
|
||||||
|
file, errs := Parse("t.s", "TEXT \u00b7f(SB), $0\n\tCALL internal∕runtime∕atomic·Xchg(SB)\n\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse errors: %v", errs)
|
||||||
|
}
|
||||||
|
txt := file.Decls[0].(*ast.Text)
|
||||||
|
instr := txt.Body[0].(*ast.Instr)
|
||||||
|
sym := instr.Operands[0].Addr.Sym
|
||||||
|
if sym == nil {
|
||||||
|
t.Fatal("operand carries no symbol")
|
||||||
|
}
|
||||||
|
if sym.Pkg != "internal∕runtime∕atomic" {
|
||||||
|
t.Errorf("pkg = %q, want internal∕runtime∕atomic", sym.Pkg)
|
||||||
|
}
|
||||||
|
if sym.Name != "Xchg" {
|
||||||
|
t.Errorf("name = %q, want Xchg", sym.Name)
|
||||||
|
}
|
||||||
|
if sym.Raw != "internal∕runtime∕atomic·Xchg(SB)" {
|
||||||
|
t.Errorf("raw = %q", sym.Raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSemicolonStatements covers the plain parse path: ';' separates
|
||||||
|
// statements on one line exactly as it does inside macro expansion, and a
|
||||||
|
// ';' inside a comment is comment text.
|
||||||
|
func TestSemicolonStatements(t *testing.T) {
|
||||||
|
file, errs := Parse("t.s", "TEXT \u00b7f(SB), $0\n\tROLQ $3, DI; ROLQ $13, DI\n\tMOVQ AX, BX // note; still comment\n\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse errors: %v", errs)
|
||||||
|
}
|
||||||
|
txt := file.Decls[0].(*ast.Text)
|
||||||
|
if len(txt.Body) != 4 {
|
||||||
|
t.Fatalf("body = %d statements, want 4", len(txt.Body))
|
||||||
|
}
|
||||||
|
first := txt.Body[0].(*ast.Instr)
|
||||||
|
if first.Mnemonic.Text != "ROLQ" || len(first.Operands) != 2 {
|
||||||
|
t.Errorf("first statement = %+v, want ROLQ with two operands", first.Mnemonic)
|
||||||
|
}
|
||||||
|
second := txt.Body[1].(*ast.Instr)
|
||||||
|
if second.Mnemonic.Text != "ROLQ" || len(second.Operands) != 2 {
|
||||||
|
t.Errorf("second statement = %s, want ROLQ with two operands", second.Mnemonic.Text)
|
||||||
|
}
|
||||||
|
// The trailing comment belongs to the second MOVQ, semicolon included.
|
||||||
|
third := txt.Body[2].(*ast.Instr)
|
||||||
|
if third.Mnemonic.Text != "MOVQ" || third.Comment != "note; still comment" {
|
||||||
|
t.Errorf("third = %s, comment %q", third.Mnemonic.Text, third.Comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSemicolonAfterLabel covers a label sharing its line with two
|
||||||
|
// statements.
|
||||||
|
func TestSemicolonAfterLabel(t *testing.T) {
|
||||||
|
file, errs := Parse("t.s", "TEXT \u00b7f(SB), $0\nloop: NOP; NOP\n\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse errors: %v", errs)
|
||||||
|
}
|
||||||
|
txt := file.Decls[0].(*ast.Text)
|
||||||
|
if len(txt.Body) != 4 {
|
||||||
|
t.Fatalf("body = %d statements, want 4 (label, two instructions, RET)", len(txt.Body))
|
||||||
|
}
|
||||||
|
if _, ok := txt.Body[0].(*ast.Label); !ok {
|
||||||
|
t.Errorf("first statement = %T, want *ast.Label", txt.Body[0])
|
||||||
|
}
|
||||||
|
for i, want := range []string{"NOP", "NOP", "RET"} {
|
||||||
|
in, ok := txt.Body[i+1].(*ast.Instr)
|
||||||
|
if !ok || in.Mnemonic.Text != want {
|
||||||
|
t.Errorf("statement %d = %v, want %s", i+1, txt.Body[i+1], want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseEqualsZeroOptions pins the contract that ParseWithOptions with
|
||||||
|
// the zero Options reproduces Parse, here for the semicolon split.
|
||||||
|
func TestParseEqualsZeroOptions(t *testing.T) {
|
||||||
|
src := "TEXT \u00b7f(SB), $0\n\tNOP; NOP\n\tRET\n"
|
||||||
|
a, errsA := Parse("t.s", src)
|
||||||
|
b, errsB := ParseWithOptions("t.s", src, Options{})
|
||||||
|
if len(errsA) > 0 || len(errsB) > 0 {
|
||||||
|
t.Fatalf("errors: %v / %v", errsA, errsB)
|
||||||
|
}
|
||||||
|
ta, tb := texts(a), texts(b)
|
||||||
|
if len(ta) != len(tb) {
|
||||||
|
t.Fatalf("decl counts differ: %d vs %d", len(ta), len(tb))
|
||||||
|
}
|
||||||
|
for i := range ta {
|
||||||
|
if len(ta[i].Body) != len(tb[i].Body) {
|
||||||
|
t.Fatalf("TEXT %d: body lengths differ: %d vs %d", i, len(ta[i].Body), len(tb[i].Body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ func ParseWithOptions(path, src string, opts Options) (*ast.File, []error) {
|
|||||||
lines = pp.fileLines(path, tokens, token.Position{})
|
lines = pp.fileLines(path, tokens, token.Position{})
|
||||||
errs = pp.errs
|
errs = pp.errs
|
||||||
} else {
|
} else {
|
||||||
lines = splitLines(tokens)
|
lines = statementLines(tokens)
|
||||||
}
|
}
|
||||||
p := &state{path: path}
|
p := &state{path: path}
|
||||||
p.parse(lines)
|
p.parse(lines)
|
||||||
|
|||||||
@@ -462,7 +462,9 @@ TEXT ·f(SB), NOSPLIT, $0
|
|||||||
|
|
||||||
func TestParseUnchangedWithoutExpand(t *testing.T) {
|
func TestParseUnchangedWithoutExpand(t *testing.T) {
|
||||||
// Without Expand the preprocessor must not exist: a macro invocation
|
// Without Expand the preprocessor must not exist: a macro invocation
|
||||||
// stays an unexpanded instruction line and ';' keeps the old parse.
|
// stays an unexpanded instruction line. The ';' statement separator is
|
||||||
|
// not part of the preprocessor: the plain parse path splits on it the
|
||||||
|
// same way the expansion path does, so both spellings agree.
|
||||||
f, errs := Parse("t_amd64.s", `
|
f, errs := Parse("t_amd64.s", `
|
||||||
#define TWICE ADDQ AX, AX
|
#define TWICE ADDQ AX, AX
|
||||||
TEXT ·f(SB), NOSPLIT, $0
|
TEXT ·f(SB), NOSPLIT, $0
|
||||||
@@ -480,7 +482,7 @@ TEXT ·f(SB), NOSPLIT, $0
|
|||||||
mnemonics = append(mnemonics, in.Mnemonic.Text)
|
mnemonics = append(mnemonics, in.Mnemonic.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.Join(mnemonics, " ") != "TWICE BYTE RET" {
|
if strings.Join(mnemonics, " ") != "TWICE BYTE BYTE RET" {
|
||||||
t.Errorf("non-expanding parse changed: %v", mnemonics)
|
t.Errorf("non-expanding parse changed: %v", mnemonics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+44
@@ -0,0 +1,44 @@
|
|||||||
|
// Differential kernel: the _dbar (acquire/release) atomic exchange
|
||||||
|
// variants against the Go toolchain's loong64enc1.s rows.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·AMXORDBW(SB), NOSPLIT, $0
|
||||||
|
AMXORDBW R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMXORDBV(SB), NOSPLIT, $0
|
||||||
|
AMXORDBV R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMAXDBW(SB), NOSPLIT, $0
|
||||||
|
AMMAXDBW R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMAXDBV(SB), NOSPLIT, $0
|
||||||
|
AMMAXDBV R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMINDBW(SB), NOSPLIT, $0
|
||||||
|
AMMINDBW R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMINDBV(SB), NOSPLIT, $0
|
||||||
|
AMMINDBV R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMAXDBWU(SB), NOSPLIT, $0
|
||||||
|
AMMAXDBWU R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMAXDBVU(SB), NOSPLIT, $0
|
||||||
|
AMMAXDBVU R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMINDBWU(SB), NOSPLIT, $0
|
||||||
|
AMMINDBWU R14, (R13), R12
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·AMMINDBVU(SB), NOSPLIT, $0
|
||||||
|
AMMINDBVU R14, (R13), R12
|
||||||
|
RET
|
||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The kernel exercises the symbol-valued DATA spelling the runtime's rt0
|
||||||
|
// files use: a data word holding the address of a symbol, resolved by the
|
||||||
|
// linker through a relocation at the field.
|
||||||
|
|
||||||
|
// func lookup() ptr
|
||||||
|
TEXT ·lookup(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ handlers+8(SB), AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func handler() int64
|
||||||
|
TEXT ·handler(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ $42, AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
GLOBL handlers(SB), NOPTR, $24
|
||||||
|
DATA handlers+0(SB)/8, $·handler(SB)
|
||||||
|
DATA handlers+8(SB)/8, $table(SB)
|
||||||
|
DATA handlers+16(SB)/8, $·handler+5(SB)
|
||||||
|
|
||||||
|
GLOBL table(SB), RODATA, $8
|
||||||
|
DATA table+0(SB)/8, $0x123456789abcdef0
|
||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The kernel exercises the symbol-valued DATA spelling the runtime's rt0
|
||||||
|
// files use: a data word holding the address of a symbol, resolved by the
|
||||||
|
// linker through a relocation at the field.
|
||||||
|
|
||||||
|
// func lookup() ptr
|
||||||
|
TEXT ·lookup(SB), NOSPLIT, $0-8
|
||||||
|
MOVD handlers+8(SB), R4
|
||||||
|
MOVD R4, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func handler() int64
|
||||||
|
TEXT ·handler(SB), NOSPLIT, $0-8
|
||||||
|
MOVZ $42, R4
|
||||||
|
MOVD R4, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
GLOBL handlers(SB), NOPTR, $24
|
||||||
|
DATA handlers+0(SB)/8, $·handler(SB)
|
||||||
|
DATA handlers+8(SB)/8, $table(SB)
|
||||||
|
DATA handlers+16(SB)/8, $extentry(SB)
|
||||||
|
|
||||||
|
GLOBL table(SB), RODATA, $8
|
||||||
|
DATA table+0(SB)/8, $0x123456789abcdef0
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The kernel exercises the U+2215 DIVISION SLASH inside a symbol's package
|
||||||
|
// path: internal∕runtime∕atomic·Xchg, the spelling sync/atomic/asm.s uses.
|
||||||
|
// The middle dot (U+00B7) still separates the package path from the name.
|
||||||
|
|
||||||
|
// func swap(a, b int64) int64
|
||||||
|
TEXT ·swap(SB), NOSPLIT, $0-24
|
||||||
|
MOVQ a+0(FP), DI
|
||||||
|
MOVQ b+8(FP), SI
|
||||||
|
CALL internal∕runtime∕atomic·Xchg(SB)
|
||||||
|
MOVQ AX, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func note() int64
|
||||||
|
TEXT ·note(SB), NOSPLIT, $0-8
|
||||||
|
CALL runtime∕debug·SetGCPercent(SB)
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The kernel exercises the U+2215 DIVISION SLASH inside a symbol's package
|
||||||
|
// path: internal∕runtime∕atomic·Xchg, the spelling sync/atomic/asm.s uses.
|
||||||
|
// The middle dot (U+00B7) still separates the package path from the name.
|
||||||
|
|
||||||
|
// func swap(a, b int64) int64
|
||||||
|
TEXT ·swap(SB), NOSPLIT, $0-24
|
||||||
|
MOVD a+0(FP), R4
|
||||||
|
MOVD b+8(FP), R5
|
||||||
|
CALL internal∕runtime∕atomic·Xchg(SB)
|
||||||
|
MOVD R4, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func note() int64
|
||||||
|
TEXT ·note(SB), NOSPLIT, $0-8
|
||||||
|
CALL runtime∕debug·SetGCPercent(SB)
|
||||||
|
MOVD R0, ret+0(FP)
|
||||||
|
RET
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
// Differential kernel: the bookkeeping statements the assembler accepts and
|
||||||
|
// encodes to nothing (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·end(SB), NOSPLIT, $0
|
||||||
|
END
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·funcdata(SB), NOSPLIT, $0
|
||||||
|
FUNCDATA $0, ref(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·pcdata(SB), NOSPLIT, $0
|
||||||
|
PCDATA $0, $1
|
||||||
|
PCDATA $1, $-2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·getcallerpc(SB), NOSPLIT, $0
|
||||||
|
GETCALLERPC R4
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·mixed(SB), NOSPLIT, $0
|
||||||
|
PCDATA $0, $1
|
||||||
|
ADDV R4, R5, R6
|
||||||
|
FUNCDATA $1, ref(SB)
|
||||||
|
GETCALLERPC R7
|
||||||
|
RET
|
||||||
|
|
||||||
|
ref:
|
||||||
|
RET
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The kernel exercises the ';' statement separator in a plain file, the way
|
||||||
|
// the runtime writes it ("ROLQ $3, DI; ROLQ $13, DI", "REP; MOVSQ"). Each
|
||||||
|
// statement assembles exactly as it would on a line of its own.
|
||||||
|
|
||||||
|
// func rol(x int64) int64
|
||||||
|
TEXT ·rol(SB), NOSPLIT, $0-16
|
||||||
|
ROLQ $3, DI; ROLQ $13, DI
|
||||||
|
MOVQ DI, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func move(dst, src unsafe.Pointer)
|
||||||
|
TEXT ·move(SB), NOSPLIT, $0-16
|
||||||
|
REP ; MOVSQ
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·paired(SB), NOSPLIT, $0-8
|
||||||
|
XORQ AX, AX; XORQ CX, CX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
// Differential kernel: the arith_add vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VADDB(SB), NOSPLIT, $0
|
||||||
|
VADDB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDH(SB), NOSPLIT, $0
|
||||||
|
VADDH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDQ(SB), NOSPLIT, $0
|
||||||
|
VADDQ V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDB(SB), NOSPLIT, $0
|
||||||
|
XVADDB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDH(SB), NOSPLIT, $0
|
||||||
|
XVADDH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDW(SB), NOSPLIT, $0
|
||||||
|
XVADDW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDQ(SB), NOSPLIT, $0
|
||||||
|
XVADDQ X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDBU(SB), NOSPLIT, $0
|
||||||
|
VADDBU $1, V2
|
||||||
|
VADDBU $1, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDHU(SB), NOSPLIT, $0
|
||||||
|
VADDHU $2, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWU(SB), NOSPLIT, $0
|
||||||
|
VADDWU $3, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDVU(SB), NOSPLIT, $0
|
||||||
|
VADDVU $4, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDBU(SB), NOSPLIT, $0
|
||||||
|
XVADDBU $9, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDHU(SB), NOSPLIT, $0
|
||||||
|
XVADDHU $10, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWU(SB), NOSPLIT, $0
|
||||||
|
XVADDWU $11, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDVU(SB), NOSPLIT, $0
|
||||||
|
XVADDVU $12, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVHB(SB), NOSPLIT, $0
|
||||||
|
VADDWEVHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVWH(SB), NOSPLIT, $0
|
||||||
|
VADDWEVWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVVW(SB), NOSPLIT, $0
|
||||||
|
VADDWEVVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVQV(SB), NOSPLIT, $0
|
||||||
|
VADDWEVQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODHB(SB), NOSPLIT, $0
|
||||||
|
VADDWODHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODWH(SB), NOSPLIT, $0
|
||||||
|
VADDWODWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODVW(SB), NOSPLIT, $0
|
||||||
|
VADDWODVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODQV(SB), NOSPLIT, $0
|
||||||
|
VADDWODQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVHB(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVWH(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVVW(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVQV(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODHB(SB), NOSPLIT, $0
|
||||||
|
XVADDWODHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODWH(SB), NOSPLIT, $0
|
||||||
|
XVADDWODWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODVW(SB), NOSPLIT, $0
|
||||||
|
XVADDWODVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODQV(SB), NOSPLIT, $0
|
||||||
|
XVADDWODQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVHBU(SB), NOSPLIT, $0
|
||||||
|
VADDWEVHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVWHU(SB), NOSPLIT, $0
|
||||||
|
VADDWEVWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVVWU(SB), NOSPLIT, $0
|
||||||
|
VADDWEVVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWEVQVU(SB), NOSPLIT, $0
|
||||||
|
VADDWEVQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODHBU(SB), NOSPLIT, $0
|
||||||
|
VADDWODHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODWHU(SB), NOSPLIT, $0
|
||||||
|
VADDWODWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODVWU(SB), NOSPLIT, $0
|
||||||
|
VADDWODVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWODQVU(SB), NOSPLIT, $0
|
||||||
|
VADDWODQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVHBU(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVWHU(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVVWU(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWEVQVU(SB), NOSPLIT, $0
|
||||||
|
XVADDWEVQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODHBU(SB), NOSPLIT, $0
|
||||||
|
XVADDWODHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODWHU(SB), NOSPLIT, $0
|
||||||
|
XVADDWODWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODVWU(SB), NOSPLIT, $0
|
||||||
|
XVADDWODVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDWODQVU(SB), NOSPLIT, $0
|
||||||
|
XVADDWODQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDF(SB), NOSPLIT, $0
|
||||||
|
VADDF V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDD(SB), NOSPLIT, $0
|
||||||
|
VADDD V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDF(SB), NOSPLIT, $0
|
||||||
|
XVADDF X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVADDD(SB), NOSPLIT, $0
|
||||||
|
XVADDD X1, X2, X3
|
||||||
|
RET
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
// Differential kernel: the arith_sat vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSADDB(SB), NOSPLIT, $0
|
||||||
|
VSADDB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDH(SB), NOSPLIT, $0
|
||||||
|
VSADDH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDW(SB), NOSPLIT, $0
|
||||||
|
VSADDW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDV(SB), NOSPLIT, $0
|
||||||
|
VSADDV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBB(SB), NOSPLIT, $0
|
||||||
|
VSSUBB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBH(SB), NOSPLIT, $0
|
||||||
|
VSSUBH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBW(SB), NOSPLIT, $0
|
||||||
|
VSSUBW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBV(SB), NOSPLIT, $0
|
||||||
|
VSSUBV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDB(SB), NOSPLIT, $0
|
||||||
|
XVSADDB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDH(SB), NOSPLIT, $0
|
||||||
|
XVSADDH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDW(SB), NOSPLIT, $0
|
||||||
|
XVSADDW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDV(SB), NOSPLIT, $0
|
||||||
|
XVSADDV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBB(SB), NOSPLIT, $0
|
||||||
|
XVSSUBB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBH(SB), NOSPLIT, $0
|
||||||
|
XVSSUBH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBW(SB), NOSPLIT, $0
|
||||||
|
XVSSUBW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBV(SB), NOSPLIT, $0
|
||||||
|
XVSSUBV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDBU(SB), NOSPLIT, $0
|
||||||
|
VSADDBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDHU(SB), NOSPLIT, $0
|
||||||
|
VSADDHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDWU(SB), NOSPLIT, $0
|
||||||
|
VSADDWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSADDVU(SB), NOSPLIT, $0
|
||||||
|
VSADDVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBBU(SB), NOSPLIT, $0
|
||||||
|
VSSUBBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBHU(SB), NOSPLIT, $0
|
||||||
|
VSSUBHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBWU(SB), NOSPLIT, $0
|
||||||
|
VSSUBWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSSUBVU(SB), NOSPLIT, $0
|
||||||
|
VSSUBVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDBU(SB), NOSPLIT, $0
|
||||||
|
XVSADDBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDHU(SB), NOSPLIT, $0
|
||||||
|
XVSADDHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDWU(SB), NOSPLIT, $0
|
||||||
|
XVSADDWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSADDVU(SB), NOSPLIT, $0
|
||||||
|
XVSADDVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBBU(SB), NOSPLIT, $0
|
||||||
|
XVSSUBBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBHU(SB), NOSPLIT, $0
|
||||||
|
XVSSUBHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBWU(SB), NOSPLIT, $0
|
||||||
|
XVSSUBWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSSUBVU(SB), NOSPLIT, $0
|
||||||
|
XVSSUBVU X1, X2, X3
|
||||||
|
RET
|
||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
// Differential kernel: the arith_sub vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSUBB(SB), NOSPLIT, $0
|
||||||
|
VSUBB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBH(SB), NOSPLIT, $0
|
||||||
|
VSUBH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBW(SB), NOSPLIT, $0
|
||||||
|
VSUBW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBV(SB), NOSPLIT, $0
|
||||||
|
VSUBV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBQ(SB), NOSPLIT, $0
|
||||||
|
VSUBQ V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBB(SB), NOSPLIT, $0
|
||||||
|
XVSUBB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBH(SB), NOSPLIT, $0
|
||||||
|
XVSUBH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBW(SB), NOSPLIT, $0
|
||||||
|
XVSUBW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBV(SB), NOSPLIT, $0
|
||||||
|
XVSUBV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBQ(SB), NOSPLIT, $0
|
||||||
|
XVSUBQ X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBBU(SB), NOSPLIT, $0
|
||||||
|
VSUBBU $5, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBHU(SB), NOSPLIT, $0
|
||||||
|
VSUBHU $6, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWU(SB), NOSPLIT, $0
|
||||||
|
VSUBWU $7, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBVU(SB), NOSPLIT, $0
|
||||||
|
VSUBVU $8, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBBU(SB), NOSPLIT, $0
|
||||||
|
XVSUBBU $13, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBHU(SB), NOSPLIT, $0
|
||||||
|
XVSUBHU $14, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWU $15, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBVU(SB), NOSPLIT, $0
|
||||||
|
XVSUBVU $16, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVHB(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVWH(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVVW(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVQV(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODHB(SB), NOSPLIT, $0
|
||||||
|
VSUBWODHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODWH(SB), NOSPLIT, $0
|
||||||
|
VSUBWODWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODVW(SB), NOSPLIT, $0
|
||||||
|
VSUBWODVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODQV(SB), NOSPLIT, $0
|
||||||
|
VSUBWODQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVHB(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVWH(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVVW(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVQV(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODHB(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODWH(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODVW(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODQV(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVHBU(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVWHU(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVVWU(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWEVQVU(SB), NOSPLIT, $0
|
||||||
|
VSUBWEVQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODHBU(SB), NOSPLIT, $0
|
||||||
|
VSUBWODHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODWHU(SB), NOSPLIT, $0
|
||||||
|
VSUBWODWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODVWU(SB), NOSPLIT, $0
|
||||||
|
VSUBWODVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWODQVU(SB), NOSPLIT, $0
|
||||||
|
VSUBWODQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVHBU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVWHU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVVWU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWEVQVU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWEVQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODHBU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODWHU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODVWU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBWODQVU(SB), NOSPLIT, $0
|
||||||
|
XVSUBWODQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBF(SB), NOSPLIT, $0
|
||||||
|
VSUBF V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBD(SB), NOSPLIT, $0
|
||||||
|
VSUBD V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBF(SB), NOSPLIT, $0
|
||||||
|
XVSUBF X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSUBD(SB), NOSPLIT, $0
|
||||||
|
XVSUBD X1, X2, X3
|
||||||
|
RET
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
// Differential kernel: the bitops vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VBITCLRB(SB), NOSPLIT, $0
|
||||||
|
VBITCLRB V1, V2, V3
|
||||||
|
VBITCLRB $7, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRH(SB), NOSPLIT, $0
|
||||||
|
VBITCLRH V1, V2, V3
|
||||||
|
VBITCLRH $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRW(SB), NOSPLIT, $0
|
||||||
|
VBITCLRW V1, V2, V3
|
||||||
|
VBITCLRW $31, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRV(SB), NOSPLIT, $0
|
||||||
|
VBITCLRV V1, V2, V3
|
||||||
|
VBITCLRV $63, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETB(SB), NOSPLIT, $0
|
||||||
|
VBITSETB V1, V2, V3
|
||||||
|
VBITSETB $7, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETH(SB), NOSPLIT, $0
|
||||||
|
VBITSETH V1, V2, V3
|
||||||
|
VBITSETH $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETW(SB), NOSPLIT, $0
|
||||||
|
VBITSETW V1, V2, V3
|
||||||
|
VBITSETW $31, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETV(SB), NOSPLIT, $0
|
||||||
|
VBITSETV V1, V2, V3
|
||||||
|
VBITSETV $63, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVB(SB), NOSPLIT, $0
|
||||||
|
VBITREVB V1, V2, V3
|
||||||
|
VBITREVB $7, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVH(SB), NOSPLIT, $0
|
||||||
|
VBITREVH V1, V2, V3
|
||||||
|
VBITREVH $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVW(SB), NOSPLIT, $0
|
||||||
|
VBITREVW V1, V2, V3
|
||||||
|
VBITREVW $31, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVV(SB), NOSPLIT, $0
|
||||||
|
VBITREVV V1, V2, V3
|
||||||
|
VBITREVV $63, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITCLRB(SB), NOSPLIT, $0
|
||||||
|
XVBITCLRB X3, X2, X1
|
||||||
|
XVBITCLRB $7, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITCLRH(SB), NOSPLIT, $0
|
||||||
|
XVBITCLRH X3, X2, X1
|
||||||
|
XVBITCLRH $15, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITCLRW(SB), NOSPLIT, $0
|
||||||
|
XVBITCLRW X3, X2, X1
|
||||||
|
XVBITCLRW $31, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITCLRV(SB), NOSPLIT, $0
|
||||||
|
XVBITCLRV X3, X2, X1
|
||||||
|
XVBITCLRV $63, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITSETB(SB), NOSPLIT, $0
|
||||||
|
XVBITSETB X3, X2, X1
|
||||||
|
XVBITSETB $7, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITSETH(SB), NOSPLIT, $0
|
||||||
|
XVBITSETH X3, X2, X1
|
||||||
|
XVBITSETH $15, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITSETW(SB), NOSPLIT, $0
|
||||||
|
XVBITSETW X3, X2, X1
|
||||||
|
XVBITSETW $31, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITSETV(SB), NOSPLIT, $0
|
||||||
|
XVBITSETV X3, X2, X1
|
||||||
|
XVBITSETV $63, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITREVB(SB), NOSPLIT, $0
|
||||||
|
XVBITREVB X3, X2, X1
|
||||||
|
XVBITREVB $7, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITREVH(SB), NOSPLIT, $0
|
||||||
|
XVBITREVH X3, X2, X1
|
||||||
|
XVBITREVH $15, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITREVW(SB), NOSPLIT, $0
|
||||||
|
XVBITREVW X3, X2, X1
|
||||||
|
XVBITREVW $31, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVBITREVV(SB), NOSPLIT, $0
|
||||||
|
XVBITREVV X3, X2, X1
|
||||||
|
XVBITREVV $63, X2, X1
|
||||||
|
RET
|
||||||
+419
@@ -0,0 +1,419 @@
|
|||||||
|
// Differential kernel: immediate range boundaries for the vector
|
||||||
|
// immediate forms (min, mid and max of each accepted window).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSEQBbounds(SB), NOSPLIT, $0
|
||||||
|
VSEQB $-16, V2, V3
|
||||||
|
VSEQB $15, V2, V3
|
||||||
|
VSEQB $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSEQHbounds(SB), NOSPLIT, $0
|
||||||
|
VSEQH $-16, V2, V3
|
||||||
|
VSEQH $15, V2, V3
|
||||||
|
VSEQH $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSEQWbounds(SB), NOSPLIT, $0
|
||||||
|
VSEQW $-16, V2, V3
|
||||||
|
VSEQW $15, V2, V3
|
||||||
|
VSEQW $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSEQVbounds(SB), NOSPLIT, $0
|
||||||
|
VSEQV $-16, V2, V3
|
||||||
|
VSEQV $15, V2, V3
|
||||||
|
VSEQV $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTBbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTB $-16, V2, V3
|
||||||
|
VSLTB $15, V2, V3
|
||||||
|
VSLTB $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTHbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTH $-16, V2, V3
|
||||||
|
VSLTH $15, V2, V3
|
||||||
|
VSLTH $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTWbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTW $-16, V2, V3
|
||||||
|
VSLTW $15, V2, V3
|
||||||
|
VSLTW $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTVbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTV $-16, V2, V3
|
||||||
|
VSLTV $15, V2, V3
|
||||||
|
VSLTV $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTBUbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTBU $0, V2, V3
|
||||||
|
VSLTBU $31, V2, V3
|
||||||
|
VSLTBU $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTHUbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTHU $0, V2, V3
|
||||||
|
VSLTHU $31, V2, V3
|
||||||
|
VSLTHU $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTWUbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTWU $0, V2, V3
|
||||||
|
VSLTWU $31, V2, V3
|
||||||
|
VSLTWU $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTVUbounds(SB), NOSPLIT, $0
|
||||||
|
VSLTVU $0, V2, V3
|
||||||
|
VSLTVU $31, V2, V3
|
||||||
|
VSLTVU $15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDBUbounds(SB), NOSPLIT, $0
|
||||||
|
VADDBU $0, V2, V3
|
||||||
|
VADDBU $31, V2, V3
|
||||||
|
VADDBU $15, V2, V3
|
||||||
|
VADDBU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDHUbounds(SB), NOSPLIT, $0
|
||||||
|
VADDHU $0, V2, V3
|
||||||
|
VADDHU $31, V2, V3
|
||||||
|
VADDHU $15, V2, V3
|
||||||
|
VADDHU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDWUbounds(SB), NOSPLIT, $0
|
||||||
|
VADDWU $0, V2, V3
|
||||||
|
VADDWU $31, V2, V3
|
||||||
|
VADDWU $15, V2, V3
|
||||||
|
VADDWU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VADDVUbounds(SB), NOSPLIT, $0
|
||||||
|
VADDVU $0, V2, V3
|
||||||
|
VADDVU $31, V2, V3
|
||||||
|
VADDVU $15, V2, V3
|
||||||
|
VADDVU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBBUbounds(SB), NOSPLIT, $0
|
||||||
|
VSUBBU $0, V2, V3
|
||||||
|
VSUBBU $31, V2, V3
|
||||||
|
VSUBBU $15, V2, V3
|
||||||
|
VSUBBU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBHUbounds(SB), NOSPLIT, $0
|
||||||
|
VSUBHU $0, V2, V3
|
||||||
|
VSUBHU $31, V2, V3
|
||||||
|
VSUBHU $15, V2, V3
|
||||||
|
VSUBHU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBWUbounds(SB), NOSPLIT, $0
|
||||||
|
VSUBWU $0, V2, V3
|
||||||
|
VSUBWU $31, V2, V3
|
||||||
|
VSUBWU $15, V2, V3
|
||||||
|
VSUBWU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSUBVUbounds(SB), NOSPLIT, $0
|
||||||
|
VSUBVU $0, V2, V3
|
||||||
|
VSUBVU $31, V2, V3
|
||||||
|
VSUBVU $15, V2, V3
|
||||||
|
VSUBVU $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VANDBbounds(SB), NOSPLIT, $0
|
||||||
|
VANDB $0, V2, V3
|
||||||
|
VANDB $255, V2, V3
|
||||||
|
VANDB $127, V2, V3
|
||||||
|
VANDB $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRBbounds(SB), NOSPLIT, $0
|
||||||
|
VBITCLRB $0, V2, V3
|
||||||
|
VBITCLRB $7, V2, V3
|
||||||
|
VBITCLRB $3, V2, V3
|
||||||
|
VBITCLRB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRHbounds(SB), NOSPLIT, $0
|
||||||
|
VBITCLRH $0, V2, V3
|
||||||
|
VBITCLRH $15, V2, V3
|
||||||
|
VBITCLRH $7, V2, V3
|
||||||
|
VBITCLRH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRVbounds(SB), NOSPLIT, $0
|
||||||
|
VBITCLRV $0, V2, V3
|
||||||
|
VBITCLRV $63, V2, V3
|
||||||
|
VBITCLRV $31, V2, V3
|
||||||
|
VBITCLRV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITCLRWbounds(SB), NOSPLIT, $0
|
||||||
|
VBITCLRW $0, V2, V3
|
||||||
|
VBITCLRW $31, V2, V3
|
||||||
|
VBITCLRW $15, V2, V3
|
||||||
|
VBITCLRW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVBbounds(SB), NOSPLIT, $0
|
||||||
|
VBITREVB $0, V2, V3
|
||||||
|
VBITREVB $7, V2, V3
|
||||||
|
VBITREVB $3, V2, V3
|
||||||
|
VBITREVB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVHbounds(SB), NOSPLIT, $0
|
||||||
|
VBITREVH $0, V2, V3
|
||||||
|
VBITREVH $15, V2, V3
|
||||||
|
VBITREVH $7, V2, V3
|
||||||
|
VBITREVH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVVbounds(SB), NOSPLIT, $0
|
||||||
|
VBITREVV $0, V2, V3
|
||||||
|
VBITREVV $63, V2, V3
|
||||||
|
VBITREVV $31, V2, V3
|
||||||
|
VBITREVV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITREVWbounds(SB), NOSPLIT, $0
|
||||||
|
VBITREVW $0, V2, V3
|
||||||
|
VBITREVW $31, V2, V3
|
||||||
|
VBITREVW $15, V2, V3
|
||||||
|
VBITREVW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETBbounds(SB), NOSPLIT, $0
|
||||||
|
VBITSETB $0, V2, V3
|
||||||
|
VBITSETB $7, V2, V3
|
||||||
|
VBITSETB $3, V2, V3
|
||||||
|
VBITSETB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETHbounds(SB), NOSPLIT, $0
|
||||||
|
VBITSETH $0, V2, V3
|
||||||
|
VBITSETH $15, V2, V3
|
||||||
|
VBITSETH $7, V2, V3
|
||||||
|
VBITSETH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETVbounds(SB), NOSPLIT, $0
|
||||||
|
VBITSETV $0, V2, V3
|
||||||
|
VBITSETV $63, V2, V3
|
||||||
|
VBITSETV $31, V2, V3
|
||||||
|
VBITSETV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VBITSETWbounds(SB), NOSPLIT, $0
|
||||||
|
VBITSETW $0, V2, V3
|
||||||
|
VBITSETW $31, V2, V3
|
||||||
|
VBITSETW $15, V2, V3
|
||||||
|
VBITSETW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSBbounds(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSB $0, V2, V3
|
||||||
|
VEXTRINSB $255, V2, V3
|
||||||
|
VEXTRINSB $127, V2, V3
|
||||||
|
VEXTRINSB $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSHbounds(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSH $0, V2, V3
|
||||||
|
VEXTRINSH $255, V2, V3
|
||||||
|
VEXTRINSH $127, V2, V3
|
||||||
|
VEXTRINSH $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSVbounds(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSV $0, V2, V3
|
||||||
|
VEXTRINSV $255, V2, V3
|
||||||
|
VEXTRINSV $127, V2, V3
|
||||||
|
VEXTRINSV $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSWbounds(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSW $0, V2, V3
|
||||||
|
VEXTRINSW $255, V2, V3
|
||||||
|
VEXTRINSW $127, V2, V3
|
||||||
|
VEXTRINSW $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNORBbounds(SB), NOSPLIT, $0
|
||||||
|
VNORB $0, V2, V3
|
||||||
|
VNORB $255, V2, V3
|
||||||
|
VNORB $127, V2, V3
|
||||||
|
VNORB $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VORBbounds(SB), NOSPLIT, $0
|
||||||
|
VORB $0, V2, V3
|
||||||
|
VORB $255, V2, V3
|
||||||
|
VORB $127, V2, V3
|
||||||
|
VORB $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VPERMIWbounds(SB), NOSPLIT, $0
|
||||||
|
VPERMIW $0, V2, V3
|
||||||
|
VPERMIW $255, V2, V3
|
||||||
|
VPERMIW $127, V2, V3
|
||||||
|
VPERMIW $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRBbounds(SB), NOSPLIT, $0
|
||||||
|
VROTRB $0, V2, V3
|
||||||
|
VROTRB $7, V2, V3
|
||||||
|
VROTRB $3, V2, V3
|
||||||
|
VROTRB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRHbounds(SB), NOSPLIT, $0
|
||||||
|
VROTRH $0, V2, V3
|
||||||
|
VROTRH $15, V2, V3
|
||||||
|
VROTRH $7, V2, V3
|
||||||
|
VROTRH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRVbounds(SB), NOSPLIT, $0
|
||||||
|
VROTRV $0, V2, V3
|
||||||
|
VROTRV $63, V2, V3
|
||||||
|
VROTRV $31, V2, V3
|
||||||
|
VROTRV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRWbounds(SB), NOSPLIT, $0
|
||||||
|
VROTRW $0, V2, V3
|
||||||
|
VROTRW $31, V2, V3
|
||||||
|
VROTRW $15, V2, V3
|
||||||
|
VROTRW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IBbounds(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IB $0, V2, V3
|
||||||
|
VSHUF4IB $255, V2, V3
|
||||||
|
VSHUF4IB $127, V2, V3
|
||||||
|
VSHUF4IB $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IHbounds(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IH $0, V2, V3
|
||||||
|
VSHUF4IH $255, V2, V3
|
||||||
|
VSHUF4IH $127, V2, V3
|
||||||
|
VSHUF4IH $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IVbounds(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IV $0, V2, V3
|
||||||
|
VSHUF4IV $15, V2, V3
|
||||||
|
VSHUF4IV $7, V2, V3
|
||||||
|
VSHUF4IV $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IWbounds(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IW $0, V2, V3
|
||||||
|
VSHUF4IW $255, V2, V3
|
||||||
|
VSHUF4IW $127, V2, V3
|
||||||
|
VSHUF4IW $127, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLBbounds(SB), NOSPLIT, $0
|
||||||
|
VSLLB $0, V2, V3
|
||||||
|
VSLLB $7, V2, V3
|
||||||
|
VSLLB $3, V2, V3
|
||||||
|
VSLLB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLHbounds(SB), NOSPLIT, $0
|
||||||
|
VSLLH $0, V2, V3
|
||||||
|
VSLLH $15, V2, V3
|
||||||
|
VSLLH $7, V2, V3
|
||||||
|
VSLLH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLVbounds(SB), NOSPLIT, $0
|
||||||
|
VSLLV $0, V2, V3
|
||||||
|
VSLLV $63, V2, V3
|
||||||
|
VSLLV $31, V2, V3
|
||||||
|
VSLLV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLWbounds(SB), NOSPLIT, $0
|
||||||
|
VSLLW $0, V2, V3
|
||||||
|
VSLLW $31, V2, V3
|
||||||
|
VSLLW $15, V2, V3
|
||||||
|
VSLLW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRABbounds(SB), NOSPLIT, $0
|
||||||
|
VSRAB $0, V2, V3
|
||||||
|
VSRAB $7, V2, V3
|
||||||
|
VSRAB $3, V2, V3
|
||||||
|
VSRAB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAHbounds(SB), NOSPLIT, $0
|
||||||
|
VSRAH $0, V2, V3
|
||||||
|
VSRAH $15, V2, V3
|
||||||
|
VSRAH $7, V2, V3
|
||||||
|
VSRAH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAVbounds(SB), NOSPLIT, $0
|
||||||
|
VSRAV $0, V2, V3
|
||||||
|
VSRAV $63, V2, V3
|
||||||
|
VSRAV $31, V2, V3
|
||||||
|
VSRAV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAWbounds(SB), NOSPLIT, $0
|
||||||
|
VSRAW $0, V2, V3
|
||||||
|
VSRAW $31, V2, V3
|
||||||
|
VSRAW $15, V2, V3
|
||||||
|
VSRAW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLBbounds(SB), NOSPLIT, $0
|
||||||
|
VSRLB $0, V2, V3
|
||||||
|
VSRLB $7, V2, V3
|
||||||
|
VSRLB $3, V2, V3
|
||||||
|
VSRLB $3, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLHbounds(SB), NOSPLIT, $0
|
||||||
|
VSRLH $0, V2, V3
|
||||||
|
VSRLH $15, V2, V3
|
||||||
|
VSRLH $7, V2, V3
|
||||||
|
VSRLH $7, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLVbounds(SB), NOSPLIT, $0
|
||||||
|
VSRLV $0, V2, V3
|
||||||
|
VSRLV $63, V2, V3
|
||||||
|
VSRLV $31, V2, V3
|
||||||
|
VSRLV $31, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLWbounds(SB), NOSPLIT, $0
|
||||||
|
VSRLW $0, V2, V3
|
||||||
|
VSRLW $31, V2, V3
|
||||||
|
VSRLW $15, V2, V3
|
||||||
|
VSRLW $15, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VXORBbounds(SB), NOSPLIT, $0
|
||||||
|
VXORB $0, V2, V3
|
||||||
|
VXORB $255, V2, V3
|
||||||
|
VXORB $127, V2, V3
|
||||||
|
VXORB $127, V2
|
||||||
|
RET
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
// Differential kernel: the divmod vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VDIVB(SB), NOSPLIT, $0
|
||||||
|
VDIVB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVH(SB), NOSPLIT, $0
|
||||||
|
VDIVH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVW(SB), NOSPLIT, $0
|
||||||
|
VDIVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVV(SB), NOSPLIT, $0
|
||||||
|
VDIVV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVBU(SB), NOSPLIT, $0
|
||||||
|
VDIVBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVHU(SB), NOSPLIT, $0
|
||||||
|
VDIVHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVWU(SB), NOSPLIT, $0
|
||||||
|
VDIVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVVU(SB), NOSPLIT, $0
|
||||||
|
VDIVVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODB(SB), NOSPLIT, $0
|
||||||
|
VMODB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODH(SB), NOSPLIT, $0
|
||||||
|
VMODH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODW(SB), NOSPLIT, $0
|
||||||
|
VMODW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODV(SB), NOSPLIT, $0
|
||||||
|
VMODV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODBU(SB), NOSPLIT, $0
|
||||||
|
VMODBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODHU(SB), NOSPLIT, $0
|
||||||
|
VMODHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODWU(SB), NOSPLIT, $0
|
||||||
|
VMODWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMODVU(SB), NOSPLIT, $0
|
||||||
|
VMODVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVB(SB), NOSPLIT, $0
|
||||||
|
XVDIVB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVH(SB), NOSPLIT, $0
|
||||||
|
XVDIVH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVW(SB), NOSPLIT, $0
|
||||||
|
XVDIVW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVV(SB), NOSPLIT, $0
|
||||||
|
XVDIVV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVBU(SB), NOSPLIT, $0
|
||||||
|
XVDIVBU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVHU(SB), NOSPLIT, $0
|
||||||
|
XVDIVHU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVWU(SB), NOSPLIT, $0
|
||||||
|
XVDIVWU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVVU(SB), NOSPLIT, $0
|
||||||
|
XVDIVVU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODB(SB), NOSPLIT, $0
|
||||||
|
XVMODB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODH(SB), NOSPLIT, $0
|
||||||
|
XVMODH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODW(SB), NOSPLIT, $0
|
||||||
|
XVMODW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODV(SB), NOSPLIT, $0
|
||||||
|
XVMODV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODBU(SB), NOSPLIT, $0
|
||||||
|
XVMODBU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODHU(SB), NOSPLIT, $0
|
||||||
|
XVMODHU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODWU(SB), NOSPLIT, $0
|
||||||
|
XVMODWU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMODVU(SB), NOSPLIT, $0
|
||||||
|
XVMODVU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVF(SB), NOSPLIT, $0
|
||||||
|
VDIVF V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VDIVD(SB), NOSPLIT, $0
|
||||||
|
VDIVD V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVF(SB), NOSPLIT, $0
|
||||||
|
XVDIVF X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVDIVD(SB), NOSPLIT, $0
|
||||||
|
XVDIVD X1, X2, X3
|
||||||
|
RET
|
||||||
Vendored
+176
@@ -0,0 +1,176 @@
|
|||||||
|
// Differential kernel: the fp vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·FFINTFW(SB), NOSPLIT, $0
|
||||||
|
FFINTFW F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FFINTFV(SB), NOSPLIT, $0
|
||||||
|
FFINTFV F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FFINTDW(SB), NOSPLIT, $0
|
||||||
|
FFINTDW F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FTINTWF(SB), NOSPLIT, $0
|
||||||
|
FTINTWF F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FTINTWD(SB), NOSPLIT, $0
|
||||||
|
FTINTWD F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FTINTVF(SB), NOSPLIT, $0
|
||||||
|
FTINTVF F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·FTINTVD(SB), NOSPLIT, $0
|
||||||
|
FTINTVD F0, F1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFSQRTF(SB), NOSPLIT, $0
|
||||||
|
VFSQRTF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFSQRTD(SB), NOSPLIT, $0
|
||||||
|
VFSQRTD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRECIPF(SB), NOSPLIT, $0
|
||||||
|
VFRECIPF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRECIPD(SB), NOSPLIT, $0
|
||||||
|
VFRECIPD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRSQRTF(SB), NOSPLIT, $0
|
||||||
|
VFRSQRTF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRSQRTD(SB), NOSPLIT, $0
|
||||||
|
VFRSQRTD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFSQRTF(SB), NOSPLIT, $0
|
||||||
|
XVFSQRTF X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFSQRTD(SB), NOSPLIT, $0
|
||||||
|
XVFSQRTD X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRECIPF(SB), NOSPLIT, $0
|
||||||
|
XVFRECIPF X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRECIPD(SB), NOSPLIT, $0
|
||||||
|
XVFRECIPD X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRSQRTF(SB), NOSPLIT, $0
|
||||||
|
XVFRSQRTF X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRSQRTD(SB), NOSPLIT, $0
|
||||||
|
XVFRSQRTD X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRNEF(SB), NOSPLIT, $0
|
||||||
|
VFRINTRNEF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRNED(SB), NOSPLIT, $0
|
||||||
|
VFRINTRNED V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRZF(SB), NOSPLIT, $0
|
||||||
|
VFRINTRZF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRZD(SB), NOSPLIT, $0
|
||||||
|
VFRINTRZD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRPF(SB), NOSPLIT, $0
|
||||||
|
VFRINTRPF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRPD(SB), NOSPLIT, $0
|
||||||
|
VFRINTRPD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRMF(SB), NOSPLIT, $0
|
||||||
|
VFRINTRMF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTRMD(SB), NOSPLIT, $0
|
||||||
|
VFRINTRMD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTF(SB), NOSPLIT, $0
|
||||||
|
VFRINTF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFRINTD(SB), NOSPLIT, $0
|
||||||
|
VFRINTD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRNEF(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRNEF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRNED(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRNED X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRZF(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRZF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRZD(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRZD X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRPF(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRPF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRPD(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRPD X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRMF(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRMF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTRMD(SB), NOSPLIT, $0
|
||||||
|
XVFRINTRMD X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTF(SB), NOSPLIT, $0
|
||||||
|
XVFRINTF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFRINTD(SB), NOSPLIT, $0
|
||||||
|
XVFRINTD X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFCLASSF(SB), NOSPLIT, $0
|
||||||
|
VFCLASSF V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VFCLASSD(SB), NOSPLIT, $0
|
||||||
|
VFCLASSD V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFCLASSF(SB), NOSPLIT, $0
|
||||||
|
XVFCLASSF X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVFCLASSD(SB), NOSPLIT, $0
|
||||||
|
XVFCLASSD X1, X2
|
||||||
|
RET
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
// Differential kernel: the interleave vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VILVLB(SB), NOSPLIT, $0
|
||||||
|
VILVLB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVLH(SB), NOSPLIT, $0
|
||||||
|
VILVLH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVLW(SB), NOSPLIT, $0
|
||||||
|
VILVLW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVLV(SB), NOSPLIT, $0
|
||||||
|
VILVLV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVHB(SB), NOSPLIT, $0
|
||||||
|
VILVHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVHH(SB), NOSPLIT, $0
|
||||||
|
VILVHH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVHW(SB), NOSPLIT, $0
|
||||||
|
VILVHW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VILVHV(SB), NOSPLIT, $0
|
||||||
|
VILVHV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVLB(SB), NOSPLIT, $0
|
||||||
|
XVILVLB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVLH(SB), NOSPLIT, $0
|
||||||
|
XVILVLH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVLW(SB), NOSPLIT, $0
|
||||||
|
XVILVLW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVLV(SB), NOSPLIT, $0
|
||||||
|
XVILVLV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVHB(SB), NOSPLIT, $0
|
||||||
|
XVILVHB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVHH(SB), NOSPLIT, $0
|
||||||
|
XVILVHH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVHW(SB), NOSPLIT, $0
|
||||||
|
XVILVHW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVILVHV(SB), NOSPLIT, $0
|
||||||
|
XVILVHV X3, X2, X1
|
||||||
|
RET
|
||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
// Differential kernel: the logic vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSEQH(SB), NOSPLIT, $0
|
||||||
|
VSEQH V1, V2, V3
|
||||||
|
VSEQH $1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSEQW(SB), NOSPLIT, $0
|
||||||
|
VSEQW V1, V2, V3
|
||||||
|
VSEQW $8, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSEQH(SB), NOSPLIT, $0
|
||||||
|
XVSEQH X3, X2, X4
|
||||||
|
XVSEQH $3, X2, X4
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSEQW(SB), NOSPLIT, $0
|
||||||
|
XVSEQW X3, X2, X4
|
||||||
|
XVSEQW $12, X2, X4
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTB(SB), NOSPLIT, $0
|
||||||
|
VSLTB V1, V2, V3
|
||||||
|
VSLTB $1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTH(SB), NOSPLIT, $0
|
||||||
|
VSLTH V1, V2, V3
|
||||||
|
VSLTH $-16, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTW(SB), NOSPLIT, $0
|
||||||
|
VSLTW V1, V2, V3
|
||||||
|
VSLTW $-16, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTV(SB), NOSPLIT, $0
|
||||||
|
VSLTV V1, V2, V3
|
||||||
|
VSLTV $-15, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTB(SB), NOSPLIT, $0
|
||||||
|
XVSLTB X1, X2, X3
|
||||||
|
XVSLTB $1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTH(SB), NOSPLIT, $0
|
||||||
|
XVSLTH X1, X2, X3
|
||||||
|
XVSLTH $-16, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTW(SB), NOSPLIT, $0
|
||||||
|
XVSLTW X1, X2, X3
|
||||||
|
XVSLTW $-16, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTV(SB), NOSPLIT, $0
|
||||||
|
XVSLTV X1, X2, X3
|
||||||
|
XVSLTV $-16, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTBU(SB), NOSPLIT, $0
|
||||||
|
VSLTBU V1, V2, V3
|
||||||
|
VSLTBU $0, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTHU(SB), NOSPLIT, $0
|
||||||
|
VSLTHU V1, V2, V3
|
||||||
|
VSLTHU $31, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTWU(SB), NOSPLIT, $0
|
||||||
|
VSLTWU V1, V2, V3
|
||||||
|
VSLTWU $16, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLTVU(SB), NOSPLIT, $0
|
||||||
|
VSLTVU V1, V2, V3
|
||||||
|
VSLTVU $1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTBU(SB), NOSPLIT, $0
|
||||||
|
XVSLTBU X1, X2, X3
|
||||||
|
XVSLTBU $0, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTHU(SB), NOSPLIT, $0
|
||||||
|
XVSLTHU X1, X2, X3
|
||||||
|
XVSLTHU $31, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTWU(SB), NOSPLIT, $0
|
||||||
|
XVSLTWU X1, X2, X3
|
||||||
|
XVSLTWU $8, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLTVU(SB), NOSPLIT, $0
|
||||||
|
XVSLTVU X1, X2, X3
|
||||||
|
XVSLTVU $0, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VORV(SB), NOSPLIT, $0
|
||||||
|
VORV V1, V2, V3
|
||||||
|
VORV V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNORV(SB), NOSPLIT, $0
|
||||||
|
VNORV V1, V2, V3
|
||||||
|
VNORV V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VANDNV(SB), NOSPLIT, $0
|
||||||
|
VANDNV V1, V2, V3
|
||||||
|
VANDNV V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VORNV(SB), NOSPLIT, $0
|
||||||
|
VORNV V1, V2, V3
|
||||||
|
VORNV V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VORB(SB), NOSPLIT, $0
|
||||||
|
VORB $64, V2, V3
|
||||||
|
VORB $64, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VXORB(SB), NOSPLIT, $0
|
||||||
|
VXORB $128, V2, V3
|
||||||
|
VXORB $128, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNORB(SB), NOSPLIT, $0
|
||||||
|
VNORB $255, V2, V3
|
||||||
|
VNORB $255, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVORV(SB), NOSPLIT, $0
|
||||||
|
XVORV X1, X2, X3
|
||||||
|
XVORV X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNORV(SB), NOSPLIT, $0
|
||||||
|
XVNORV X1, X2, X3
|
||||||
|
XVNORV X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVANDNV(SB), NOSPLIT, $0
|
||||||
|
XVANDNV X1, X2, X3
|
||||||
|
XVANDNV X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVORNV(SB), NOSPLIT, $0
|
||||||
|
XVORNV X1, X2, X3
|
||||||
|
XVORNV X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVORB(SB), NOSPLIT, $0
|
||||||
|
XVORB $1, X2, X3
|
||||||
|
XVORB $1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVXORB(SB), NOSPLIT, $0
|
||||||
|
XVXORB $127, X2, X3
|
||||||
|
XVXORB $127, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNORB(SB), NOSPLIT, $0
|
||||||
|
XVNORB $255, X2, X3
|
||||||
|
XVNORB $255, X2
|
||||||
|
RET
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// Differential kernel: the madd vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VMADDB(SB), NOSPLIT, $0
|
||||||
|
VMADDB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDH(SB), NOSPLIT, $0
|
||||||
|
VMADDH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDV(SB), NOSPLIT, $0
|
||||||
|
VMADDV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDB(SB), NOSPLIT, $0
|
||||||
|
XVMADDB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDH(SB), NOSPLIT, $0
|
||||||
|
XVMADDH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDV(SB), NOSPLIT, $0
|
||||||
|
XVMADDV X1, X2, X3
|
||||||
|
RET
|
||||||
+204
@@ -0,0 +1,204 @@
|
|||||||
|
// Differential kernel: the maddwiden vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VMADDW(SB), NOSPLIT, $0
|
||||||
|
VMADDW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDW(SB), NOSPLIT, $0
|
||||||
|
XVMADDW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVHB(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVWH(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVVW(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVQV(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODHB(SB), NOSPLIT, $0
|
||||||
|
VMADDWODHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODWH(SB), NOSPLIT, $0
|
||||||
|
VMADDWODWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODVW(SB), NOSPLIT, $0
|
||||||
|
VMADDWODVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODQV(SB), NOSPLIT, $0
|
||||||
|
VMADDWODQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVHB(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVWH(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVVW(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVQV(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODHB(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODWH(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODVW(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODQV(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVHBU(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVWHU(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVVWU(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVQVU(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODHBU(SB), NOSPLIT, $0
|
||||||
|
VMADDWODHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODWHU(SB), NOSPLIT, $0
|
||||||
|
VMADDWODWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODVWU(SB), NOSPLIT, $0
|
||||||
|
VMADDWODVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODQVU(SB), NOSPLIT, $0
|
||||||
|
VMADDWODQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVHBU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVWHU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVVWU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVQVU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODHBU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODWHU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODVWU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODQVU(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVHBUB(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVHBUB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVWHUH(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVWHUH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVVWUW(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVVWUW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWEVQVUV(SB), NOSPLIT, $0
|
||||||
|
VMADDWEVQVUV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODHBUB(SB), NOSPLIT, $0
|
||||||
|
VMADDWODHBUB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODWHUH(SB), NOSPLIT, $0
|
||||||
|
VMADDWODWHUH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODVWUW(SB), NOSPLIT, $0
|
||||||
|
VMADDWODVWUW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMADDWODQVUV(SB), NOSPLIT, $0
|
||||||
|
VMADDWODQVUV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVHBUB(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVHBUB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVWHUH(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVWHUH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVVWUW(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVVWUW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWEVQVUV(SB), NOSPLIT, $0
|
||||||
|
XVMADDWEVQVUV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODHBUB(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODHBUB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODWHUH(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODWHUH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODVWUW(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODVWUW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMADDWODQVUV(SB), NOSPLIT, $0
|
||||||
|
XVMADDWODQVUV X1, X2, X3
|
||||||
|
RET
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
// Differential kernel: the msub vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VMSUBB(SB), NOSPLIT, $0
|
||||||
|
VMSUBB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMSUBH(SB), NOSPLIT, $0
|
||||||
|
VMSUBH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMSUBW(SB), NOSPLIT, $0
|
||||||
|
VMSUBW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMSUBV(SB), NOSPLIT, $0
|
||||||
|
VMSUBV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMSUBB(SB), NOSPLIT, $0
|
||||||
|
XVMSUBB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMSUBH(SB), NOSPLIT, $0
|
||||||
|
XVMSUBH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMSUBW(SB), NOSPLIT, $0
|
||||||
|
XVMSUBW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMSUBV(SB), NOSPLIT, $0
|
||||||
|
XVMSUBV X1, X2, X3
|
||||||
|
RET
|
||||||
Vendored
+108
@@ -0,0 +1,108 @@
|
|||||||
|
// Differential kernel: the mul vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VMULB(SB), NOSPLIT, $0
|
||||||
|
VMULB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULH(SB), NOSPLIT, $0
|
||||||
|
VMULH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULV(SB), NOSPLIT, $0
|
||||||
|
VMULV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHB(SB), NOSPLIT, $0
|
||||||
|
VMUHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHH(SB), NOSPLIT, $0
|
||||||
|
VMUHH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHW(SB), NOSPLIT, $0
|
||||||
|
VMUHW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHV(SB), NOSPLIT, $0
|
||||||
|
VMUHV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHBU(SB), NOSPLIT, $0
|
||||||
|
VMUHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHHU(SB), NOSPLIT, $0
|
||||||
|
VMUHHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHWU(SB), NOSPLIT, $0
|
||||||
|
VMUHWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMUHVU(SB), NOSPLIT, $0
|
||||||
|
VMUHVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULB(SB), NOSPLIT, $0
|
||||||
|
XVMULB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULH(SB), NOSPLIT, $0
|
||||||
|
XVMULH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULV(SB), NOSPLIT, $0
|
||||||
|
XVMULV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHB(SB), NOSPLIT, $0
|
||||||
|
XVMUHB X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHH(SB), NOSPLIT, $0
|
||||||
|
XVMUHH X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHW(SB), NOSPLIT, $0
|
||||||
|
XVMUHW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHV(SB), NOSPLIT, $0
|
||||||
|
XVMUHV X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHBU(SB), NOSPLIT, $0
|
||||||
|
XVMUHBU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHHU(SB), NOSPLIT, $0
|
||||||
|
XVMUHHU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHWU(SB), NOSPLIT, $0
|
||||||
|
XVMUHWU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMUHVU(SB), NOSPLIT, $0
|
||||||
|
XVMUHVU X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULF(SB), NOSPLIT, $0
|
||||||
|
VMULF V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULD(SB), NOSPLIT, $0
|
||||||
|
VMULD V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULF(SB), NOSPLIT, $0
|
||||||
|
XVMULF X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULD(SB), NOSPLIT, $0
|
||||||
|
XVMULD X1, X2, X3
|
||||||
|
RET
|
||||||
+204
@@ -0,0 +1,204 @@
|
|||||||
|
// Differential kernel: the mulwiden vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VMULW(SB), NOSPLIT, $0
|
||||||
|
VMULW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULW(SB), NOSPLIT, $0
|
||||||
|
XVMULW X3, X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVHB(SB), NOSPLIT, $0
|
||||||
|
VMULWEVHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVWH(SB), NOSPLIT, $0
|
||||||
|
VMULWEVWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVVW(SB), NOSPLIT, $0
|
||||||
|
VMULWEVVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVQV(SB), NOSPLIT, $0
|
||||||
|
VMULWEVQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODHB(SB), NOSPLIT, $0
|
||||||
|
VMULWODHB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODWH(SB), NOSPLIT, $0
|
||||||
|
VMULWODWH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODVW(SB), NOSPLIT, $0
|
||||||
|
VMULWODVW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODQV(SB), NOSPLIT, $0
|
||||||
|
VMULWODQV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVHBU(SB), NOSPLIT, $0
|
||||||
|
VMULWEVHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVWHU(SB), NOSPLIT, $0
|
||||||
|
VMULWEVWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVVWU(SB), NOSPLIT, $0
|
||||||
|
VMULWEVVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVQVU(SB), NOSPLIT, $0
|
||||||
|
VMULWEVQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODHBU(SB), NOSPLIT, $0
|
||||||
|
VMULWODHBU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODWHU(SB), NOSPLIT, $0
|
||||||
|
VMULWODWHU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODVWU(SB), NOSPLIT, $0
|
||||||
|
VMULWODVWU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODQVU(SB), NOSPLIT, $0
|
||||||
|
VMULWODQVU V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVHB(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVWH(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVVW(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVQV(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODHB(SB), NOSPLIT, $0
|
||||||
|
XVMULWODHB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODWH(SB), NOSPLIT, $0
|
||||||
|
XVMULWODWH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODVW(SB), NOSPLIT, $0
|
||||||
|
XVMULWODVW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODQV(SB), NOSPLIT, $0
|
||||||
|
XVMULWODQV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVHBU(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVWHU(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVVWU(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVQVU(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODHBU(SB), NOSPLIT, $0
|
||||||
|
XVMULWODHBU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODWHU(SB), NOSPLIT, $0
|
||||||
|
XVMULWODWHU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODVWU(SB), NOSPLIT, $0
|
||||||
|
XVMULWODVWU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODQVU(SB), NOSPLIT, $0
|
||||||
|
XVMULWODQVU X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVHBUB(SB), NOSPLIT, $0
|
||||||
|
VMULWEVHBUB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVWHUH(SB), NOSPLIT, $0
|
||||||
|
VMULWEVWHUH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVVWUW(SB), NOSPLIT, $0
|
||||||
|
VMULWEVVWUW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWEVQVUV(SB), NOSPLIT, $0
|
||||||
|
VMULWEVQVUV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODHBUB(SB), NOSPLIT, $0
|
||||||
|
VMULWODHBUB V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODWHUH(SB), NOSPLIT, $0
|
||||||
|
VMULWODWHUH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODVWUW(SB), NOSPLIT, $0
|
||||||
|
VMULWODVWUW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VMULWODQVUV(SB), NOSPLIT, $0
|
||||||
|
VMULWODQVUV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVHBUB(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVHBUB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVWHUH(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVWHUH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVVWUW(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVVWUW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWEVQVUV(SB), NOSPLIT, $0
|
||||||
|
XVMULWEVQVUV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODHBUB(SB), NOSPLIT, $0
|
||||||
|
XVMULWODHBUB X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODWHUH(SB), NOSPLIT, $0
|
||||||
|
XVMULWODWHUH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODVWUW(SB), NOSPLIT, $0
|
||||||
|
XVMULWODVWUW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVMULWODQVUV(SB), NOSPLIT, $0
|
||||||
|
XVMULWODQVUV X1, X2, X3
|
||||||
|
RET
|
||||||
+214
@@ -0,0 +1,214 @@
|
|||||||
|
// Differential kernel: the shift vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSLLB(SB), NOSPLIT, $0
|
||||||
|
VSLLB V1, V2, V3
|
||||||
|
VSLLB $0, V1, V2
|
||||||
|
VSLLB $7, V1, V2
|
||||||
|
VSLLB $5, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLH(SB), NOSPLIT, $0
|
||||||
|
VSLLH V1, V2, V3
|
||||||
|
VSLLH $0, V1, V2
|
||||||
|
VSLLH $15, V1, V2
|
||||||
|
VSLLH $10, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLW(SB), NOSPLIT, $0
|
||||||
|
VSLLW V1, V2, V3
|
||||||
|
VSLLW $0, V1, V2
|
||||||
|
VSLLW $31, V1, V2
|
||||||
|
VSLLW $11, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSLLV(SB), NOSPLIT, $0
|
||||||
|
VSLLV V1, V2, V3
|
||||||
|
VSLLV $0, V1, V2
|
||||||
|
VSLLV $63, V1, V2
|
||||||
|
VSLLV $30, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLB(SB), NOSPLIT, $0
|
||||||
|
VSRLB V1, V2, V3
|
||||||
|
VSRLB $0, V1, V2
|
||||||
|
VSRLB $7, V1, V2
|
||||||
|
VSRLB $4, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLH(SB), NOSPLIT, $0
|
||||||
|
VSRLH V1, V2, V3
|
||||||
|
VSRLH $0, V1, V2
|
||||||
|
VSRLH $15, V1, V2
|
||||||
|
VSRLH $9, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLW(SB), NOSPLIT, $0
|
||||||
|
VSRLW V1, V2, V3
|
||||||
|
VSRLW $0, V1, V2
|
||||||
|
VSRLW $31, V1, V2
|
||||||
|
VSRLW $16, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRLV(SB), NOSPLIT, $0
|
||||||
|
VSRLV V1, V2, V3
|
||||||
|
VSRLV $0, V1, V2
|
||||||
|
VSRLV $63, V1, V2
|
||||||
|
VSRLV $40, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAH(SB), NOSPLIT, $0
|
||||||
|
VSRAH V1, V2, V3
|
||||||
|
VSRAH $0, V1, V2
|
||||||
|
VSRAH $15, V1, V2
|
||||||
|
VSRAH $8, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAW(SB), NOSPLIT, $0
|
||||||
|
VSRAW V1, V2, V3
|
||||||
|
VSRAW $0, V1, V2
|
||||||
|
VSRAW $31, V1, V2
|
||||||
|
VSRAW $12, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSRAV(SB), NOSPLIT, $0
|
||||||
|
VSRAV V1, V2, V3
|
||||||
|
VSRAV $0, V1, V2
|
||||||
|
VSRAV $63, V1, V2
|
||||||
|
VSRAV $50, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRB(SB), NOSPLIT, $0
|
||||||
|
VROTRB V1, V2, V3
|
||||||
|
VROTRB $0, V1, V2
|
||||||
|
VROTRB $7, V1, V2
|
||||||
|
VROTRB $3, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRH(SB), NOSPLIT, $0
|
||||||
|
VROTRH V1, V2, V3
|
||||||
|
VROTRH $0, V1, V2
|
||||||
|
VROTRH $15, V1, V2
|
||||||
|
VROTRH $5, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VROTRV(SB), NOSPLIT, $0
|
||||||
|
VROTRV V1, V2, V3
|
||||||
|
VROTRV $0, V1, V2
|
||||||
|
VROTRV $63, V1, V2
|
||||||
|
VROTRV $52, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLLB(SB), NOSPLIT, $0
|
||||||
|
XVSLLB X3, X2, X1
|
||||||
|
XVSLLB $0, X2, X1
|
||||||
|
XVSLLB $7, X2, X1
|
||||||
|
XVSLLB $4, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLLH(SB), NOSPLIT, $0
|
||||||
|
XVSLLH X3, X2, X1
|
||||||
|
XVSLLH $0, X2, X1
|
||||||
|
XVSLLH $15, X2, X1
|
||||||
|
XVSLLH $8, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLLW(SB), NOSPLIT, $0
|
||||||
|
XVSLLW X3, X2, X1
|
||||||
|
XVSLLW $0, X2, X1
|
||||||
|
XVSLLW $31, X2, X1
|
||||||
|
XVSLLW $13, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSLLV(SB), NOSPLIT, $0
|
||||||
|
XVSLLV X3, X2, X1
|
||||||
|
XVSLLV $0, X2, X1
|
||||||
|
XVSLLV $63, X2, X1
|
||||||
|
XVSLLV $36, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRLB(SB), NOSPLIT, $0
|
||||||
|
XVSRLB X3, X2, X1
|
||||||
|
XVSRLB $0, X2, X1
|
||||||
|
XVSRLB $7, X2, X1
|
||||||
|
XVSRLB $5, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRLH(SB), NOSPLIT, $0
|
||||||
|
XVSRLH X3, X2, X1
|
||||||
|
XVSRLH $0, X2, X1
|
||||||
|
XVSRLH $15, X2, X1
|
||||||
|
XVSRLH $9, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRLW(SB), NOSPLIT, $0
|
||||||
|
XVSRLW X3, X2, X1
|
||||||
|
XVSRLW $0, X2, X1
|
||||||
|
XVSRLW $31, X2, X1
|
||||||
|
XVSRLW $14, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRLV(SB), NOSPLIT, $0
|
||||||
|
XVSRLV X3, X2, X1
|
||||||
|
XVSRLV $0, X2, X1
|
||||||
|
XVSRLV $63, X2, X1
|
||||||
|
XVSRLV $45, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRAB(SB), NOSPLIT, $0
|
||||||
|
XVSRAB X3, X2, X1
|
||||||
|
XVSRAB $0, X2, X1
|
||||||
|
XVSRAB $7, X2, X1
|
||||||
|
XVSRAB $6, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRAH(SB), NOSPLIT, $0
|
||||||
|
XVSRAH X3, X2, X1
|
||||||
|
XVSRAH $0, X2, X1
|
||||||
|
XVSRAH $15, X2, X1
|
||||||
|
XVSRAH $10, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRAW(SB), NOSPLIT, $0
|
||||||
|
XVSRAW X3, X2, X1
|
||||||
|
XVSRAW $0, X2, X1
|
||||||
|
XVSRAW $31, X2, X1
|
||||||
|
XVSRAW $16, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSRAV(SB), NOSPLIT, $0
|
||||||
|
XVSRAV X3, X2, X1
|
||||||
|
XVSRAV $0, X2, X1
|
||||||
|
XVSRAV $63, X2, X1
|
||||||
|
XVSRAV $48, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVROTRB(SB), NOSPLIT, $0
|
||||||
|
XVROTRB X3, X2, X1
|
||||||
|
XVROTRB $0, X2, X1
|
||||||
|
XVROTRB $7, X2, X1
|
||||||
|
XVROTRB $3, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVROTRH(SB), NOSPLIT, $0
|
||||||
|
XVROTRH X3, X2, X1
|
||||||
|
XVROTRH $0, X2, X1
|
||||||
|
XVROTRH $15, X2, X1
|
||||||
|
XVROTRH $13, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVROTRW(SB), NOSPLIT, $0
|
||||||
|
XVROTRW X3, X2, X1
|
||||||
|
XVROTRW $0, X2, X1
|
||||||
|
XVROTRW $31, X2, X1
|
||||||
|
XVROTRW $24, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVROTRV(SB), NOSPLIT, $0
|
||||||
|
XVROTRV X3, X2, X1
|
||||||
|
XVROTRV $0, X2, X1
|
||||||
|
XVROTRV $63, X2, X1
|
||||||
|
XVROTRV $52, X2
|
||||||
|
RET
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
// Differential kernel: the shuffle vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IB(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IB $0, V2, V1
|
||||||
|
VSHUF4IB $16, V2, V1
|
||||||
|
VSHUF4IB $255, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IH(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IH $0, V2, V1
|
||||||
|
VSHUF4IH $128, V2, V1
|
||||||
|
VSHUF4IH $255, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IW(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IW $0, V2, V1
|
||||||
|
VSHUF4IW $96, V2, V1
|
||||||
|
VSHUF4IW $255, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUF4IV(SB), NOSPLIT, $0
|
||||||
|
VSHUF4IV $0, V2, V1
|
||||||
|
VSHUF4IV $8, V2, V1
|
||||||
|
VSHUF4IV $15, V2, V1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUF4IB(SB), NOSPLIT, $0
|
||||||
|
XVSHUF4IB $0, X1, X2
|
||||||
|
XVSHUF4IB $16, X1, X2
|
||||||
|
XVSHUF4IB $255, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUF4IH(SB), NOSPLIT, $0
|
||||||
|
XVSHUF4IH $0, X1, X2
|
||||||
|
XVSHUF4IH $128, X1, X2
|
||||||
|
XVSHUF4IH $255, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUF4IW(SB), NOSPLIT, $0
|
||||||
|
XVSHUF4IW $0, X1, X2
|
||||||
|
XVSHUF4IW $96, X1, X2
|
||||||
|
XVSHUF4IW $255, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUF4IV(SB), NOSPLIT, $0
|
||||||
|
XVSHUF4IV $0, X1, X2
|
||||||
|
XVSHUF4IV $8, X1, X2
|
||||||
|
XVSHUF4IV $15, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUFH(SB), NOSPLIT, $0
|
||||||
|
VSHUFH V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUFW(SB), NOSPLIT, $0
|
||||||
|
VSHUFW V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUFV(SB), NOSPLIT, $0
|
||||||
|
VSHUFV V1, V2, V3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUFH(SB), NOSPLIT, $0
|
||||||
|
XVSHUFH X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUFW(SB), NOSPLIT, $0
|
||||||
|
XVSHUFW X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUFV(SB), NOSPLIT, $0
|
||||||
|
XVSHUFV X1, X2, X3
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSHUFB(SB), NOSPLIT, $0
|
||||||
|
VSHUFB V1, V2, V3, V4
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSHUFB(SB), NOSPLIT, $0
|
||||||
|
XVSHUFB X1, X2, X3, X4
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VPERMIW(SB), NOSPLIT, $0
|
||||||
|
VPERMIW $0x1B, V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPERMIW(SB), NOSPLIT, $0
|
||||||
|
XVPERMIW $0x2B, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPERMIV(SB), NOSPLIT, $0
|
||||||
|
XVPERMIV $0x3B, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPERMIQ(SB), NOSPLIT, $0
|
||||||
|
XVPERMIQ $0x4B, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSB(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSB $0x18, V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSH(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSH $0x27, V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSW(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSW $0x36, V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VEXTRINSV(SB), NOSPLIT, $0
|
||||||
|
VEXTRINSV $0x45, V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVEXTRINSB(SB), NOSPLIT, $0
|
||||||
|
XVEXTRINSB $0x54, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVEXTRINSH(SB), NOSPLIT, $0
|
||||||
|
XVEXTRINSH $0x63, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVEXTRINSW(SB), NOSPLIT, $0
|
||||||
|
XVEXTRINSW $0x72, X1, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVEXTRINSV(SB), NOSPLIT, $0
|
||||||
|
XVEXTRINSV $0x81, X1, X2
|
||||||
|
RET
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
// Differential kernel: the unary vector slice against the Go
|
||||||
|
// toolchain's loong64enc1.s rows (gasm v. go tool asm, byte for byte).
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·VPCNTB(SB), NOSPLIT, $0
|
||||||
|
VPCNTB V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VPCNTH(SB), NOSPLIT, $0
|
||||||
|
VPCNTH V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VPCNTW(SB), NOSPLIT, $0
|
||||||
|
VPCNTW V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPCNTB(SB), NOSPLIT, $0
|
||||||
|
XVPCNTB X3, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPCNTH(SB), NOSPLIT, $0
|
||||||
|
XVPCNTH X3, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVPCNTW(SB), NOSPLIT, $0
|
||||||
|
XVPCNTW X3, X2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNEGB(SB), NOSPLIT, $0
|
||||||
|
VNEGB V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNEGH(SB), NOSPLIT, $0
|
||||||
|
VNEGH V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNEGW(SB), NOSPLIT, $0
|
||||||
|
VNEGW V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VNEGV(SB), NOSPLIT, $0
|
||||||
|
VNEGV V1, V2
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNEGB(SB), NOSPLIT, $0
|
||||||
|
XVNEGB X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNEGH(SB), NOSPLIT, $0
|
||||||
|
XVNEGH X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNEGW(SB), NOSPLIT, $0
|
||||||
|
XVNEGW X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVNEGV(SB), NOSPLIT, $0
|
||||||
|
XVNEGV X2, X1
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETEQV(SB), NOSPLIT, $0
|
||||||
|
VSETEQV V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETEQV(SB), NOSPLIT, $0
|
||||||
|
XVSETEQV X1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETANYEQH(SB), NOSPLIT, $0
|
||||||
|
VSETANYEQH V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETANYEQW(SB), NOSPLIT, $0
|
||||||
|
VSETANYEQW V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETALLNEB(SB), NOSPLIT, $0
|
||||||
|
VSETALLNEB V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETALLNEH(SB), NOSPLIT, $0
|
||||||
|
VSETALLNEH V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·VSETALLNEW(SB), NOSPLIT, $0
|
||||||
|
VSETALLNEW V1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETANYEQH(SB), NOSPLIT, $0
|
||||||
|
XVSETANYEQH X1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETANYEQW(SB), NOSPLIT, $0
|
||||||
|
XVSETANYEQW X1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETALLNEB(SB), NOSPLIT, $0
|
||||||
|
XVSETALLNEB X1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETALLNEH(SB), NOSPLIT, $0
|
||||||
|
XVSETALLNEH X1, FCC0
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·XVSETALLNEW(SB), NOSPLIT, $0
|
||||||
|
XVSETALLNEW X1, FCC0
|
||||||
|
RET
|
||||||
@@ -31,6 +31,25 @@ func TestGroundTruthLOONG64(t *testing.T) {
|
|||||||
"../testdata/verify/vector_loong64.s",
|
"../testdata/verify/vector_loong64.s",
|
||||||
"../testdata/verify/pcalign_loong64.s",
|
"../testdata/verify/pcalign_loong64.s",
|
||||||
"../testdata/verify/l64forms_loong64.s",
|
"../testdata/verify/l64forms_loong64.s",
|
||||||
|
"../testdata/verify/vector_arith_add_loong64.s",
|
||||||
|
"../testdata/verify/vector_arith_sub_loong64.s",
|
||||||
|
"../testdata/verify/vector_arith_sat_loong64.s",
|
||||||
|
"../testdata/verify/vector_logic_loong64.s",
|
||||||
|
"../testdata/verify/vector_madd_loong64.s",
|
||||||
|
"../testdata/verify/vector_maddwiden_loong64.s",
|
||||||
|
"../testdata/verify/vector_mul_loong64.s",
|
||||||
|
"../testdata/verify/vector_mulwiden_loong64.s",
|
||||||
|
"../testdata/verify/vector_msub_loong64.s",
|
||||||
|
"../testdata/verify/vector_divmod_loong64.s",
|
||||||
|
"../testdata/verify/vector_shift_loong64.s",
|
||||||
|
"../testdata/verify/vector_bitops_loong64.s",
|
||||||
|
"../testdata/verify/vector_interleave_loong64.s",
|
||||||
|
"../testdata/verify/vector_unary_loong64.s",
|
||||||
|
"../testdata/verify/vector_shuffle_loong64.s",
|
||||||
|
"../testdata/verify/vector_fp_loong64.s",
|
||||||
|
"../testdata/verify/vector_bounds_loong64.s",
|
||||||
|
"../testdata/verify/amdb_loong64.s",
|
||||||
|
"../testdata/verify/pseudos_loong64.s",
|
||||||
"trampoline_loong64.s",
|
"trampoline_loong64.s",
|
||||||
} {
|
} {
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user