all: AVX-512 (#217)
Extends avo to support most AVX-512 instruction sets.
The instruction type is extended to support suffixes. The K family of opmask
registers is added to the register package, and the operand package is updated
to support the new operand types. Move instruction deduction in `Load` and
`Store` is extended to support KMOV* and VMOV* forms.
Internal code generation packages were overhauled. Instruction database loading
required various messy changes to account for the additional complexities of the
AVX-512 instruction sets. The internal/api package was added to introduce a
separation between instruction forms in the database, and the functions avo
provides to create them. This was required since with instruction suffixes there
is no longer a one-to-one mapping between instruction constructors and opcodes.
AVX-512 bloated generated source code size substantially, initially increasing
compilation and CI test times to an unacceptable level. Two changes were made to
address this:
1. Instruction constructors in the `x86` package moved to an optab-based
approach. This compiles substantially faster than the verbose code
generation we had before.
2. The most verbose code-generated tests are moved under build tags and
limited to a stress test mode. Stress test builds are run on
schedule but not in regular CI.
An example of AVX-512 accelerated 16-lane MD5 is provided to demonstrate and
test the new functionality.
Updates #20 #163 #229
Co-authored-by: Vaughn Iverson <vsivsi@yahoo.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package inst_test
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -70,6 +71,84 @@ func TestInstructionProperties(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHaveSuffixes(t *testing.T) {
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
if len(f.SupportedSuffixes()) == 0 {
|
||||
t.Errorf("%s: no supported suffixes", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcceptsSuffixes(t *testing.T) {
|
||||
// Verify consistency between the AcceptsSuffixes and SupportedSuffixes methods.
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
expect := false
|
||||
for _, suffixes := range f.SupportedSuffixes() {
|
||||
if len(suffixes) > 0 {
|
||||
expect = true
|
||||
}
|
||||
}
|
||||
|
||||
if got := f.AcceptsSuffixes(); got != expect {
|
||||
t.Errorf("%s: AcceptsSuffixes() = %v; expect %v", i.Opcode, got, expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuffixesClasses(t *testing.T) {
|
||||
// Verify that all instructions in a suffix class support the same suffixes.
|
||||
reps := map[string][]inst.Suffixes{}
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
class := f.SuffixesClass()
|
||||
expect, ok := reps[class]
|
||||
if !ok {
|
||||
t.Logf("new class %q: representative from instruction %s", class, i.Opcode)
|
||||
reps[class] = f.SupportedSuffixes()
|
||||
continue
|
||||
}
|
||||
|
||||
got := f.SupportedSuffixes()
|
||||
if !reflect.DeepEqual(expect, got) {
|
||||
t.Fatalf("suffixes mismatch for class %q", class)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuffixesHaveSummaries(t *testing.T) {
|
||||
set := map[inst.Suffix]bool{}
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
for _, suffixes := range f.SupportedSuffixes() {
|
||||
for _, suffix := range suffixes {
|
||||
set[suffix] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for suffix := range set {
|
||||
if suffix.Summary() == "" {
|
||||
t.Errorf("suffix %q missing summary", suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestISASorted(t *testing.T) {
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
if !sort.StringsAreSorted(f.ISA) {
|
||||
t.Fatalf("%s: isa not sorted", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssembles(t *testing.T) {
|
||||
g := gen.NewAsmTest(printer.NewArgvConfig())
|
||||
b, err := g.Generate(inst.Instructions)
|
||||
@@ -143,17 +222,86 @@ func TestCancellingInputs(t *testing.T) {
|
||||
n := 0
|
||||
for _, op := range f.Operands {
|
||||
if op.Action.Read() {
|
||||
n++
|
||||
switch op.Type {
|
||||
case "r8", "r16", "r32", "r64", "xmm", "ymm":
|
||||
// pass
|
||||
case "r8", "r16", "r32", "r64", "xmm", "ymm", "zmm":
|
||||
n++
|
||||
case "k":
|
||||
// skip mask registers
|
||||
default:
|
||||
t.Errorf("%s: unexpected operand type %q for self-cancelling input", i.Opcode, op.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
if n != 2 {
|
||||
t.Errorf("%s: expected two inputs for self-cancelling form", i.Opcode)
|
||||
if n < 2 {
|
||||
t.Log(f)
|
||||
t.Errorf("%s: expected at least two inputs for self-cancelling form", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagOperandTypes(t *testing.T) {
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
// Check for memory operands.
|
||||
mem := false
|
||||
for _, op := range f.Operands {
|
||||
if strings.HasPrefix(op.Type, "m") {
|
||||
mem = true
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast applies to memory instructions only.
|
||||
if f.Broadcast && !mem {
|
||||
t.Errorf("%s: expect broadcast form to have memory operand", i.Opcode)
|
||||
}
|
||||
|
||||
// Embedded rounding must be register-only.
|
||||
if f.EmbeddedRounding && mem {
|
||||
t.Errorf("%s: embedded-rounding only supported for register-only forms", i.Opcode)
|
||||
}
|
||||
|
||||
// Suppress all exceptions is register only.
|
||||
if f.SuppressAllExceptions && mem {
|
||||
t.Errorf("%s: embedded-rounding only supported for register-only forms", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagCombinations(t *testing.T) {
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
if f.EmbeddedRounding && f.SuppressAllExceptions {
|
||||
t.Errorf("%s: embedded-rounding cannot be combined with suppress-all-exceptions", i.Opcode)
|
||||
}
|
||||
if f.Broadcast && f.EmbeddedRounding {
|
||||
t.Errorf("%s: broadcast cannot be combined with embedded-rounding", i.Opcode)
|
||||
}
|
||||
if f.Broadcast && f.SuppressAllExceptions {
|
||||
t.Errorf("%s: broadcast cannot be combined with suppress-all-exceptions", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroingHasMask(t *testing.T) {
|
||||
for _, i := range inst.Instructions {
|
||||
for _, f := range i.Forms {
|
||||
if !f.Zeroing {
|
||||
continue
|
||||
}
|
||||
|
||||
// Expect mask operand.
|
||||
mask := false
|
||||
for _, op := range f.Operands {
|
||||
if op.Type == "k" {
|
||||
mask = true
|
||||
}
|
||||
}
|
||||
|
||||
if !mask {
|
||||
t.Errorf("%s: expect mask operand if zeroing is enabled", i.Opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user