blob: 08d17a2a6b2fc8ebf92229b713690e556ab91c29 [file] [log] [blame] [edit]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xmlspec
import (
"flag"
"regexp"
"strings"
"testing"
)
var remoteData = flag.Bool("remote", false, "use remote data")
func getData(t *testing.T) []*InstructionParsed {
if *remoteData {
xmlDir, err := GetArm64XMLSpec(t.TempDir(), ExpectedURL, ExpectedVersion)
if err != nil {
t.Fatalf("GetArm64XMLSpec failed: %v", err)
}
return ParseXMLFiles(xmlDir)
}
return ParseXMLFiles("testdata")
}
func TestParseXMLFiles(t *testing.T) {
insts := getData(t)
// Merely check the parser runs.
t.Logf("Number of instructions: %d\n", len(insts))
}
func TestProcessXMLFiles(t *testing.T) {
insts := getData(t)
ProcessXMLFiles(insts)
specialInsts := map[string]bool{
"HISTSEG -- A64": true,
"REVB, REVH, REVW -- A64": true,
"SXTB, SXTH, SXTW -- A64": true,
"URECPE -- A64": true,
"URSQRTE -- A64": true,
"UXTB, UXTH, UXTW -- A64": true,
}
outer:
for _, inst := range insts {
if inst == nil {
continue
}
if specialInsts[inst.Title] {
// These instructions's size is not encoded in the assembler symbols,
// but specified in the mnemonic and decoding ASL.
// The parser should have special logic to handle them, skip them for now here.
continue
}
isAlias := false
for _, doc := range inst.DocVars {
if doc.Key == "alias_mnemonic" {
isAlias = true
break
}
}
if isAlias {
// Alias instructions are not fully specified in their own XML files,
// skip them.
continue
}
// Check RegDiagram and Encodings all parsed
for _, iclass := range inst.Classes.Iclass {
if !iclass.RegDiagram.Parsed {
continue
}
for _, encoding := range iclass.Encodings {
if !encoding.Parsed {
continue outer
}
}
// Check for all encodings, every named box should be encoded
// by some encoding elements
encodedBoxes := make(map[string]bool)
for _, encoding := range iclass.Encodings {
if encoding.Alias {
// Alias encodings are not fully specified in their own section,
// skip them.
continue
}
for _, operand := range encoding.Operands {
for _, elem := range operand.Elems {
encodedIn := elem.encodedIn
encodedBoxes[encodedIn] = true
if strings.Contains(encodedIn, ":") {
// Some weird instructions like smlalb_z_zzzi
// encodes an immediate in 2 or 3 disjoin fields
// appended together. We need to record all of them.
// They are represented as "(name1 :: name2)" or
// "(name1 :: name2 :: name3)".
// We need to extract these names.
re := regexp.MustCompile(`\((.*?) :: (.*?)( :: (.*?))?\)`)
matches := re.FindStringSubmatch(encodedIn)
if len(matches) == 3 {
encodedBoxes[matches[1]] = true
encodedBoxes[matches[2]] = true
} else if len(matches) == 5 {
encodedBoxes[matches[1]] = true
encodedBoxes[matches[2]] = true
encodedBoxes[matches[4]] = true
}
}
if strings.Contains(encodedIn, "[") {
// Some weird instructions like histcnt_z_p_zz
// encodes only part of a field:
// size[0] (meanwhile size is of size 2).
// We need to record the full field name.
re := regexp.MustCompile(`(.*)\[.*?\]`)
matches := re.FindStringSubmatch(encodedIn)
if len(matches) == 2 {
encodedBoxes[matches[1]] = true
}
}
}
}
}
for name := range iclass.RegDiagram.varBin {
if !encodedBoxes[name] {
t.Errorf("Box %s not encoded in %s", name, inst.file)
}
}
}
}
}