Files
avo/internal/api/function.go

178 lines
3.9 KiB
Go
Raw Normal View History

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>
2021-11-12 18:35:36 -08:00
package api
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"text/tabwriter"
2026-03-06 20:14:02 +00:00
"sources.truenas.cloud/code/avo/internal/inst"
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>
2021-11-12 18:35:36 -08:00
)
// Function represents a function that constructs some collection of
// instruction forms.
type Function struct {
Instruction inst.Instruction
Suffixes inst.Suffixes
inst.Forms
}
// Name returns the function name.
func (f *Function) Name() string {
return f.opcodesuffix("_")
}
// Opcode returns the full Go opcode of the instruction built by this function. Includes any suffixes.
func (f *Function) Opcode() string {
return f.opcodesuffix(".")
}
func (f *Function) opcodesuffix(sep string) string {
n := f.Instruction.Opcode
for _, suffix := range f.Suffixes {
n += sep
n += suffix.String()
}
return n
}
// HasSuffix reports whether the function has the provided suffix.
func (f *Function) HasSuffix(suffix inst.Suffix) bool {
for _, s := range f.Suffixes {
if s == suffix {
return true
}
}
return false
}
// Summary returns a summary of the instruction this function constructs.
func (f *Function) Summary() string {
summary := f.Instruction.Summary
if len(f.Suffixes) > 0 {
summary += " (" + strings.Join(f.Suffixes.Summaries(), ", ") + ")"
}
return summary
}
// Doc returns the function document comment as a list of lines.
func (f *Function) Doc() []string {
lines := []string{
fmt.Sprintf("%s: %s.", f.Name(), f.Summary()),
"",
"Forms:",
"",
}
// Write a table of instruction forms.
buf := bytes.NewBuffer(nil)
w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0)
for _, form := range f.Forms {
row := f.Opcode() + "\t" + strings.Join(form.Signature(), "\t") + "\n"
fmt.Fprint(w, row)
}
w.Flush()
tbl := strings.TrimSpace(buf.String())
for _, line := range strings.Split(tbl, "\n") {
lines = append(lines, "\t"+line)
}
return lines
}
// Signature of the function. Derived from the instruction forms generated by this function.
func (f *Function) Signature() Signature {
// Handle the case of forms with multiple arities.
switch {
case f.IsVariadic():
return variadic{name: "ops"}
case f.IsNiladic():
return niladic{}
}
// Generate nice-looking variable names.
n := f.Arity()
ops := make([]string, n)
count := map[string]int{}
for j := range n {
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>
2021-11-12 18:35:36 -08:00
// Collect unique lowercase bytes from first characters of operand types.
s := map[byte]bool{}
for _, form := range f.Forms {
c := form.Operands[j].Type[0]
if 'a' <= c && c <= 'z' {
s[c] = true
}
}
// Operand name is the sorted bytes.
var b []byte
for c := range s {
b = append(b, c)
}
sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
name := string(b)
// Append a counter if we've seen it already.
m := count[name]
count[name]++
if m > 0 {
name += strconv.Itoa(m)
}
ops[j] = name
}
return argslist(ops)
}
// InstructionFunctions builds the list of all functions for a given
// instruction.
func InstructionFunctions(i inst.Instruction) []*Function {
// One function for each possible suffix combination.
bysuffix := map[string]*Function{}
for _, f := range i.Forms {
for _, suffixes := range f.SupportedSuffixes() {
k := suffixes.String()
if _, ok := bysuffix[k]; !ok {
bysuffix[k] = &Function{
Instruction: i,
Suffixes: suffixes,
}
}
bysuffix[k].Forms = append(bysuffix[k].Forms, f)
}
}
// Convert to a sorted slice.
var fns []*Function
for _, fn := range bysuffix {
fns = append(fns, fn)
}
SortFunctions(fns)
return fns
}
// InstructionsFunctions builds all functions for a list of instructions.
func InstructionsFunctions(is []inst.Instruction) []*Function {
var all []*Function
for _, i := range is {
fns := InstructionFunctions(i)
all = append(all, fns...)
}
SortFunctions(all)
return all
}
// SortFunctions sorts a list of functions by name.
func SortFunctions(fns []*Function) {
sort.Slice(fns, func(i, j int) bool {
return fns[i].Name() < fns[j].Name()
})
}