Files
avo/internal/prnt/printer.go
Michael McLoughlin b76e849b5c 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 19:02:39 -08:00

97 lines
2.5 KiB
Go

// Package prnt provides common functionality for code generators.
package prnt
import (
"bytes"
"fmt"
"io"
"strings"
)
// Generator provides convenience methods for code generators. In particular it
// provides fmt-like methods which print to an internal buffer. It also allows
// any errors to be stored so they can be checked at the end, rather than having
// error checks obscuring the code generation.
type Generator struct {
buf bytes.Buffer
level int // current indentation level
indent string // indentation string
pending bool // if there's a pending indentation
err error // saved error from printing
}
// Raw provides direct access to the underlying output stream.
func (g *Generator) Raw() io.Writer {
return &g.buf
}
// SetIndentString sets the string used for one level of indentation. Use
// Indent() and Dedent() to control indent level.
func (g *Generator) SetIndentString(indent string) {
g.indent = indent
}
// Indent increments the indent level.
func (g *Generator) Indent() {
g.level++
}
// Dedent decrements the indent level.
func (g *Generator) Dedent() {
g.level--
}
// Linef prints formatted output terminated with a new line.
func (g *Generator) Linef(format string, args ...interface{}) {
g.Printf(format, args...)
g.NL()
}
// Printf prints to the internal buffer.
func (g *Generator) Printf(format string, args ...interface{}) {
if g.err != nil {
return
}
if g.pending {
indent := strings.Repeat(g.indent, g.level)
format = indent + format
g.pending = false
}
_, err := fmt.Fprintf(&g.buf, format, args...)
g.AddError(err)
}
// NL prints a new line.
func (g *Generator) NL() {
g.Printf("\n")
g.pending = true
}
// Comment writes comment lines prefixed with "// ".
func (g *Generator) Comment(lines ...string) {
for _, line := range lines {
line = strings.TrimSpace("// " + line)
g.Printf("%s\n", line)
}
}
// BuildTag outputs a build tag.
func (g *Generator) BuildTag(tag string) {
g.Comment("+build " + tag)
}
// AddError records an error in code generation. The first non-nil error will
// prevent printing operations from writing anything else, and the error will be
// returned from Result().
func (g *Generator) AddError(err error) {
if err != nil && g.err == nil {
g.err = err
}
}
// Result returns the printed bytes. If any error was recorded with AddError
// during code generation, the first such error will be returned here.
func (g *Generator) Result() ([]byte, error) {
return g.buf.Bytes(), g.err
}