forked from toolshed/abra
chore: go mod vendor / tidy
This commit is contained in:
29
vendor/github.com/mmcloughlin/avo/LICENSE
generated
vendored
Normal file
29
vendor/github.com/mmcloughlin/avo/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Michael McLoughlin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
45
vendor/github.com/mmcloughlin/avo/attr/attr.go
generated
vendored
Normal file
45
vendor/github.com/mmcloughlin/avo/attr/attr.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// Package attr provides attributes for text and data sections.
|
||||
package attr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Attribute represents TEXT or DATA flags.
|
||||
type Attribute uint16
|
||||
|
||||
//go:generate go run make_textflag.go -output ztextflag.go
|
||||
|
||||
// Asm returns a representation of the attributes in assembly syntax. This may use macros from "textflags.h"; see ContainsTextFlags() to determine if this header is required.
|
||||
func (a Attribute) Asm() string {
|
||||
parts, rest := a.split()
|
||||
if len(parts) == 0 || rest != 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d", rest))
|
||||
}
|
||||
return strings.Join(parts, "|")
|
||||
}
|
||||
|
||||
// ContainsTextFlags returns whether the Asm() representation requires macros in "textflags.h".
|
||||
func (a Attribute) ContainsTextFlags() bool {
|
||||
flags, _ := a.split()
|
||||
return len(flags) > 0
|
||||
}
|
||||
|
||||
// split splits a into known flags and any remaining bits.
|
||||
func (a Attribute) split() ([]string, Attribute) {
|
||||
var flags []string
|
||||
var rest Attribute
|
||||
for a != 0 {
|
||||
i := uint(bits.TrailingZeros16(uint16(a)))
|
||||
bit := Attribute(1) << i
|
||||
if flag := attrname[bit]; flag != "" {
|
||||
flags = append(flags, flag)
|
||||
} else {
|
||||
rest |= bit
|
||||
}
|
||||
a ^= bit
|
||||
}
|
||||
return flags, rest
|
||||
}
|
39
vendor/github.com/mmcloughlin/avo/attr/textflag.h
generated
vendored
Normal file
39
vendor/github.com/mmcloughlin/avo/attr/textflag.h
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// Code generated by downloading from https://github.com/golang/go/raw/go1.16.2/src/runtime/textflag.h. DO NOT EDIT.
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file defines flags attached to various functions
|
||||
// and data objects. The compilers, assemblers, and linker must
|
||||
// all agree on these values.
|
||||
//
|
||||
// Keep in sync with src/cmd/internal/obj/textflag.go.
|
||||
|
||||
// Don't profile the marked routine. This flag is deprecated.
|
||||
#define NOPROF 1
|
||||
// It is ok for the linker to get multiple of these symbols. It will
|
||||
// pick one of the duplicates to use.
|
||||
#define DUPOK 2
|
||||
// Don't insert stack check preamble.
|
||||
#define NOSPLIT 4
|
||||
// Put this data in a read-only section.
|
||||
#define RODATA 8
|
||||
// This data contains no pointers.
|
||||
#define NOPTR 16
|
||||
// This is a wrapper function and should not count as disabling 'recover'.
|
||||
#define WRAPPER 32
|
||||
// This function uses its incoming context register.
|
||||
#define NEEDCTXT 64
|
||||
// Allocate a word of thread local storage and store the offset from the
|
||||
// thread local base to the thread local storage in this variable.
|
||||
#define TLSBSS 256
|
||||
// Do not insert instructions to allocate a stack frame for this function.
|
||||
// Only valid on functions that declare a frame size of 0.
|
||||
// TODO(mwhudson): only implemented for ppc64x at present.
|
||||
#define NOFRAME 512
|
||||
// Function can call reflect.Type.Method or reflect.Type.MethodByName.
|
||||
#define REFLECTMETHOD 1024
|
||||
// Function is the top of the call stack. Call stack unwinders should stop
|
||||
// at this function.
|
||||
#define TOPFRAME 2048
|
90
vendor/github.com/mmcloughlin/avo/attr/ztextflag.go
generated
vendored
Normal file
90
vendor/github.com/mmcloughlin/avo/attr/ztextflag.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
// Code generated by make_textflag.go. DO NOT EDIT.
|
||||
|
||||
package attr
|
||||
|
||||
// Attribute values defined in textflag.h.
|
||||
const (
|
||||
// Don't profile the marked routine. This flag is deprecated.
|
||||
NOPROF Attribute = 1
|
||||
|
||||
// It is ok for the linker to get multiple of these symbols. It will
|
||||
// pick one of the duplicates to use.
|
||||
DUPOK Attribute = 2
|
||||
|
||||
// Don't insert stack check preamble.
|
||||
NOSPLIT Attribute = 4
|
||||
|
||||
// Put this data in a read-only section.
|
||||
RODATA Attribute = 8
|
||||
|
||||
// This data contains no pointers.
|
||||
NOPTR Attribute = 16
|
||||
|
||||
// This is a wrapper function and should not count as disabling 'recover'.
|
||||
WRAPPER Attribute = 32
|
||||
|
||||
// This function uses its incoming context register.
|
||||
NEEDCTXT Attribute = 64
|
||||
|
||||
// Allocate a word of thread local storage and store the offset from the
|
||||
// thread local base to the thread local storage in this variable.
|
||||
TLSBSS Attribute = 256
|
||||
|
||||
// Do not insert instructions to allocate a stack frame for this function.
|
||||
// Only valid on functions that declare a frame size of 0.
|
||||
NOFRAME Attribute = 512
|
||||
|
||||
// Function can call reflect.Type.Method or reflect.Type.MethodByName.
|
||||
REFLECTMETHOD Attribute = 1024
|
||||
|
||||
// Function is the top of the call stack. Call stack unwinders should stop
|
||||
// at this function.
|
||||
TOPFRAME Attribute = 2048
|
||||
)
|
||||
|
||||
var attrname = map[Attribute]string{
|
||||
NOPROF: "NOPROF",
|
||||
DUPOK: "DUPOK",
|
||||
NOSPLIT: "NOSPLIT",
|
||||
RODATA: "RODATA",
|
||||
NOPTR: "NOPTR",
|
||||
WRAPPER: "WRAPPER",
|
||||
NEEDCTXT: "NEEDCTXT",
|
||||
TLSBSS: "TLSBSS",
|
||||
NOFRAME: "NOFRAME",
|
||||
REFLECTMETHOD: "REFLECTMETHOD",
|
||||
TOPFRAME: "TOPFRAME",
|
||||
}
|
||||
|
||||
// NOPROF reports whether the NOPROF flag is set.
|
||||
func (a Attribute) NOPROF() bool { return (a & NOPROF) != 0 }
|
||||
|
||||
// DUPOK reports whether the DUPOK flag is set.
|
||||
func (a Attribute) DUPOK() bool { return (a & DUPOK) != 0 }
|
||||
|
||||
// NOSPLIT reports whether the NOSPLIT flag is set.
|
||||
func (a Attribute) NOSPLIT() bool { return (a & NOSPLIT) != 0 }
|
||||
|
||||
// RODATA reports whether the RODATA flag is set.
|
||||
func (a Attribute) RODATA() bool { return (a & RODATA) != 0 }
|
||||
|
||||
// NOPTR reports whether the NOPTR flag is set.
|
||||
func (a Attribute) NOPTR() bool { return (a & NOPTR) != 0 }
|
||||
|
||||
// WRAPPER reports whether the WRAPPER flag is set.
|
||||
func (a Attribute) WRAPPER() bool { return (a & WRAPPER) != 0 }
|
||||
|
||||
// NEEDCTXT reports whether the NEEDCTXT flag is set.
|
||||
func (a Attribute) NEEDCTXT() bool { return (a & NEEDCTXT) != 0 }
|
||||
|
||||
// TLSBSS reports whether the TLSBSS flag is set.
|
||||
func (a Attribute) TLSBSS() bool { return (a & TLSBSS) != 0 }
|
||||
|
||||
// NOFRAME reports whether the NOFRAME flag is set.
|
||||
func (a Attribute) NOFRAME() bool { return (a & NOFRAME) != 0 }
|
||||
|
||||
// REFLECTMETHOD reports whether the REFLECTMETHOD flag is set.
|
||||
func (a Attribute) REFLECTMETHOD() bool { return (a & REFLECTMETHOD) != 0 }
|
||||
|
||||
// TOPFRAME reports whether the TOPFRAME flag is set.
|
||||
func (a Attribute) TOPFRAME() bool { return (a & TOPFRAME) != 0 }
|
18
vendor/github.com/mmcloughlin/avo/build/attr.go
generated
vendored
Normal file
18
vendor/github.com/mmcloughlin/avo/build/attr.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package build
|
||||
|
||||
import "github.com/mmcloughlin/avo/attr"
|
||||
|
||||
// TEXT and DATA attribute values included for convenience.
|
||||
const (
|
||||
NOPROF = attr.NOPROF
|
||||
DUPOK = attr.DUPOK
|
||||
NOSPLIT = attr.NOSPLIT
|
||||
RODATA = attr.RODATA
|
||||
NOPTR = attr.NOPTR
|
||||
WRAPPER = attr.WRAPPER
|
||||
NEEDCTXT = attr.NEEDCTXT
|
||||
TLSBSS = attr.TLSBSS
|
||||
NOFRAME = attr.NOFRAME
|
||||
REFLECTMETHOD = attr.REFLECTMETHOD
|
||||
TOPFRAME = attr.TOPFRAME
|
||||
)
|
171
vendor/github.com/mmcloughlin/avo/build/cli.go
generated
vendored
Normal file
171
vendor/github.com/mmcloughlin/avo/build/cli.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/mmcloughlin/avo/pass"
|
||||
"github.com/mmcloughlin/avo/printer"
|
||||
)
|
||||
|
||||
// Config contains options for an avo main function.
|
||||
type Config struct {
|
||||
ErrOut io.Writer
|
||||
MaxErrors int // max errors to report; 0 means unlimited
|
||||
CPUProfile io.WriteCloser
|
||||
Passes []pass.Interface
|
||||
}
|
||||
|
||||
// Main is the standard main function for an avo program. This extracts the
|
||||
// result from the build Context (logging and exiting on error), and performs
|
||||
// configured passes.
|
||||
func Main(cfg *Config, context *Context) int {
|
||||
diag := log.New(cfg.ErrOut, "", 0)
|
||||
|
||||
if cfg.CPUProfile != nil {
|
||||
defer cfg.CPUProfile.Close()
|
||||
if err := pprof.StartCPUProfile(cfg.CPUProfile); err != nil {
|
||||
diag.Println("could not start CPU profile: ", err)
|
||||
return 1
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
f, err := context.Result()
|
||||
if err != nil {
|
||||
LogError(diag, err, cfg.MaxErrors)
|
||||
return 1
|
||||
}
|
||||
|
||||
p := pass.Concat(cfg.Passes...)
|
||||
if err := p.Execute(f); err != nil {
|
||||
diag.Println(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Flags represents CLI flags for an avo program.
|
||||
type Flags struct {
|
||||
errout *outputValue
|
||||
allerrors bool
|
||||
cpuprof *outputValue
|
||||
pkg string
|
||||
printers []*printerValue
|
||||
}
|
||||
|
||||
// NewFlags initializes avo flags for the given FlagSet.
|
||||
func NewFlags(fs *flag.FlagSet) *Flags {
|
||||
f := &Flags{}
|
||||
|
||||
f.errout = newOutputValue(os.Stderr)
|
||||
fs.Var(f.errout, "log", "diagnostics output")
|
||||
|
||||
fs.BoolVar(&f.allerrors, "e", false, "no limit on number of errors reported")
|
||||
|
||||
f.cpuprof = newOutputValue(nil)
|
||||
fs.Var(f.cpuprof, "cpuprofile", "write cpu profile to `file`")
|
||||
|
||||
fs.StringVar(&f.pkg, "pkg", "", "package name (defaults to current directory name)")
|
||||
|
||||
goasm := newPrinterValue(printer.NewGoAsm, os.Stdout)
|
||||
fs.Var(goasm, "out", "assembly output")
|
||||
f.printers = append(f.printers, goasm)
|
||||
|
||||
stubs := newPrinterValue(printer.NewStubs, nil)
|
||||
fs.Var(stubs, "stubs", "go stub file")
|
||||
f.printers = append(f.printers, stubs)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Config builds a configuration object based on flag values.
|
||||
func (f *Flags) Config() *Config {
|
||||
pc := printer.NewGoRunConfig()
|
||||
if f.pkg != "" {
|
||||
pc.Pkg = f.pkg
|
||||
}
|
||||
passes := []pass.Interface{pass.Compile}
|
||||
for _, pv := range f.printers {
|
||||
p := pv.Build(pc)
|
||||
if p != nil {
|
||||
passes = append(passes, p)
|
||||
}
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
ErrOut: f.errout.w,
|
||||
MaxErrors: 10,
|
||||
CPUProfile: f.cpuprof.w,
|
||||
Passes: passes,
|
||||
}
|
||||
|
||||
if f.allerrors {
|
||||
cfg.MaxErrors = 0
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type outputValue struct {
|
||||
w io.WriteCloser
|
||||
filename string
|
||||
}
|
||||
|
||||
func newOutputValue(dflt io.WriteCloser) *outputValue {
|
||||
return &outputValue{w: dflt}
|
||||
}
|
||||
|
||||
func (o *outputValue) String() string {
|
||||
if o == nil {
|
||||
return ""
|
||||
}
|
||||
return o.filename
|
||||
}
|
||||
|
||||
func (o *outputValue) Set(s string) error {
|
||||
o.filename = s
|
||||
if s == "-" {
|
||||
o.w = nopwritecloser{os.Stdout}
|
||||
return nil
|
||||
}
|
||||
f, err := os.Create(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.w = f
|
||||
return nil
|
||||
}
|
||||
|
||||
type printerValue struct {
|
||||
*outputValue
|
||||
Builder printer.Builder
|
||||
}
|
||||
|
||||
func newPrinterValue(b printer.Builder, dflt io.WriteCloser) *printerValue {
|
||||
return &printerValue{
|
||||
outputValue: newOutputValue(dflt),
|
||||
Builder: b,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printerValue) Build(cfg printer.Config) pass.Interface {
|
||||
if p.outputValue.w == nil {
|
||||
return nil
|
||||
}
|
||||
return &pass.Output{
|
||||
Writer: p.outputValue.w,
|
||||
Printer: p.Builder(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
// nopwritecloser wraps a Writer and provides a null implementation of Close().
|
||||
type nopwritecloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopwritecloser) Close() error { return nil }
|
224
vendor/github.com/mmcloughlin/avo/build/context.go
generated
vendored
Normal file
224
vendor/github.com/mmcloughlin/avo/build/context.go
generated
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/mmcloughlin/avo/attr"
|
||||
"github.com/mmcloughlin/avo/buildtags"
|
||||
"github.com/mmcloughlin/avo/gotypes"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
//go:generate avogen -output zinstructions.go build
|
||||
//go:generate avogen -output zinstructions_test.go buildtest
|
||||
|
||||
// Context maintains state for incrementally building an avo File.
|
||||
type Context struct {
|
||||
pkg *packages.Package
|
||||
file *ir.File
|
||||
function *ir.Function
|
||||
global *ir.Global
|
||||
errs ErrorList
|
||||
reg.Collection
|
||||
}
|
||||
|
||||
// NewContext initializes an empty build Context.
|
||||
func NewContext() *Context {
|
||||
return &Context{
|
||||
file: ir.NewFile(),
|
||||
Collection: *reg.NewCollection(),
|
||||
}
|
||||
}
|
||||
|
||||
// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
|
||||
func (c *Context) Package(path string) {
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.NeedTypes | packages.NeedDeps | packages.NeedImports,
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, path)
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
pkg := pkgs[0]
|
||||
if len(pkg.Errors) > 0 {
|
||||
for _, err := range pkg.Errors {
|
||||
c.adderror(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
c.pkg = pkg
|
||||
}
|
||||
|
||||
// Constraints sets build constraints for the file.
|
||||
func (c *Context) Constraints(t buildtags.ConstraintsConvertable) {
|
||||
cs := t.ToConstraints()
|
||||
if err := cs.Validate(); err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
c.file.Constraints = cs
|
||||
}
|
||||
|
||||
// Constraint appends a constraint to the file's build constraints.
|
||||
func (c *Context) Constraint(t buildtags.ConstraintConvertable) {
|
||||
c.Constraints(append(c.file.Constraints, t.ToConstraint()))
|
||||
}
|
||||
|
||||
// ConstraintExpr appends a constraint to the file's build constraints. The
|
||||
// constraint to add is parsed from the given expression. The expression should
|
||||
// look the same as the content following "// +build " in regular build
|
||||
// constraint comments.
|
||||
func (c *Context) ConstraintExpr(expr string) {
|
||||
constraint, err := buildtags.ParseConstraint(expr)
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
c.Constraint(constraint)
|
||||
}
|
||||
|
||||
// Function starts building a new function with the given name.
|
||||
func (c *Context) Function(name string) {
|
||||
c.function = ir.NewFunction(name)
|
||||
c.file.AddSection(c.function)
|
||||
}
|
||||
|
||||
// Doc sets documentation comment lines for the currently active function.
|
||||
func (c *Context) Doc(lines ...string) {
|
||||
c.activefunc().Doc = lines
|
||||
}
|
||||
|
||||
// Pragma adds a compiler directive to the currently active function.
|
||||
func (c *Context) Pragma(directive string, args ...string) {
|
||||
c.activefunc().AddPragma(directive, args...)
|
||||
}
|
||||
|
||||
// Attributes sets function attributes for the currently active function.
|
||||
func (c *Context) Attributes(a attr.Attribute) {
|
||||
c.activefunc().Attributes = a
|
||||
}
|
||||
|
||||
// Signature sets the signature for the currently active function.
|
||||
func (c *Context) Signature(s *gotypes.Signature) {
|
||||
c.activefunc().SetSignature(s)
|
||||
}
|
||||
|
||||
// SignatureExpr parses the signature expression and sets it as the active function's signature.
|
||||
func (c *Context) SignatureExpr(expr string) {
|
||||
s, err := gotypes.ParseSignatureInPackage(c.types(), expr)
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
c.Signature(s)
|
||||
}
|
||||
|
||||
// Implement starts building a function of the given name, whose type is
|
||||
// specified by a stub in the containing package.
|
||||
func (c *Context) Implement(name string) {
|
||||
pkg := c.types()
|
||||
if pkg == nil {
|
||||
c.adderrormessage("no package specified")
|
||||
return
|
||||
}
|
||||
s, err := gotypes.LookupSignature(pkg, name)
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
c.Function(name)
|
||||
c.Signature(s)
|
||||
}
|
||||
|
||||
func (c *Context) types() *types.Package {
|
||||
if c.pkg == nil {
|
||||
return nil
|
||||
}
|
||||
return c.pkg.Types
|
||||
}
|
||||
|
||||
// AllocLocal allocates size bytes in the stack of the currently active function.
|
||||
// Returns a reference to the base pointer for the newly allocated region.
|
||||
func (c *Context) AllocLocal(size int) operand.Mem {
|
||||
return c.activefunc().AllocLocal(size)
|
||||
}
|
||||
|
||||
// Instruction adds an instruction to the active function.
|
||||
func (c *Context) Instruction(i *ir.Instruction) {
|
||||
c.activefunc().AddInstruction(i)
|
||||
}
|
||||
|
||||
// Label adds a label to the active function.
|
||||
func (c *Context) Label(name string) {
|
||||
c.activefunc().AddLabel(ir.Label(name))
|
||||
}
|
||||
|
||||
// Comment adds comment lines to the active function.
|
||||
func (c *Context) Comment(lines ...string) {
|
||||
c.activefunc().AddComment(lines...)
|
||||
}
|
||||
|
||||
// Commentf adds a formtted comment line.
|
||||
func (c *Context) Commentf(format string, a ...any) {
|
||||
c.Comment(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
func (c *Context) activefunc() *ir.Function {
|
||||
if c.function == nil {
|
||||
c.adderrormessage("no active function")
|
||||
return ir.NewFunction("")
|
||||
}
|
||||
return c.function
|
||||
}
|
||||
|
||||
// StaticGlobal adds a new static data section to the file and returns a pointer to it.
|
||||
func (c *Context) StaticGlobal(name string) operand.Mem {
|
||||
c.global = ir.NewStaticGlobal(name)
|
||||
c.file.AddSection(c.global)
|
||||
return c.global.Base()
|
||||
}
|
||||
|
||||
// DataAttributes sets the attributes on the current active global data section.
|
||||
func (c *Context) DataAttributes(a attr.Attribute) {
|
||||
c.activeglobal().Attributes = a
|
||||
}
|
||||
|
||||
// AddDatum adds constant v at offset to the current active global data section.
|
||||
func (c *Context) AddDatum(offset int, v operand.Constant) {
|
||||
if err := c.activeglobal().AddDatum(ir.NewDatum(offset, v)); err != nil {
|
||||
c.adderror(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendDatum appends a constant to the current active global data section.
|
||||
func (c *Context) AppendDatum(v operand.Constant) {
|
||||
c.activeglobal().Append(v)
|
||||
}
|
||||
|
||||
func (c *Context) activeglobal() *ir.Global {
|
||||
if c.global == nil {
|
||||
c.adderrormessage("no active global")
|
||||
return ir.NewStaticGlobal("")
|
||||
}
|
||||
return c.global
|
||||
}
|
||||
|
||||
func (c *Context) adderror(err error) {
|
||||
c.errs.addext(err)
|
||||
}
|
||||
|
||||
func (c *Context) adderrormessage(msg string) {
|
||||
c.adderror(errors.New(msg))
|
||||
}
|
||||
|
||||
// Result returns the built file and any accumulated errors.
|
||||
func (c *Context) Result() (*ir.File, error) {
|
||||
return c.file, c.errs.Err()
|
||||
}
|
2
vendor/github.com/mmcloughlin/avo/build/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/build/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package build provides an assembly-like interface for incremental building of avo Files.
|
||||
package build
|
91
vendor/github.com/mmcloughlin/avo/build/error.go
generated
vendored
Normal file
91
vendor/github.com/mmcloughlin/avo/build/error.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mmcloughlin/avo/internal/stack"
|
||||
"github.com/mmcloughlin/avo/src"
|
||||
)
|
||||
|
||||
// Error represents an error during building, optionally tagged with the position at which it happened.
|
||||
type Error struct {
|
||||
Position src.Position
|
||||
Err error
|
||||
}
|
||||
|
||||
// exterr constructs an Error with position derived from the first frame in the
|
||||
// call stack outside this package.
|
||||
func exterr(err error) Error {
|
||||
e := Error{Err: err}
|
||||
if f := stack.ExternalCaller(); f != nil {
|
||||
e.Position = src.FramePosition(*f).Relwd()
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
msg := e.Err.Error()
|
||||
if e.Position.IsValid() {
|
||||
return e.Position.String() + ": " + msg
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// ErrorList is a collection of errors for a source file.
|
||||
type ErrorList []Error
|
||||
|
||||
// Add appends an error to the list.
|
||||
func (e *ErrorList) Add(err Error) {
|
||||
*e = append(*e, err)
|
||||
}
|
||||
|
||||
// AddAt appends an error at position p.
|
||||
func (e *ErrorList) AddAt(p src.Position, err error) {
|
||||
e.Add(Error{p, err})
|
||||
}
|
||||
|
||||
// addext appends an error to the list, tagged with the first external position
|
||||
// outside this package.
|
||||
func (e *ErrorList) addext(err error) {
|
||||
e.Add(exterr(err))
|
||||
}
|
||||
|
||||
// Err returns an error equivalent to this error list.
|
||||
// If the list is empty, Err returns nil.
|
||||
func (e ErrorList) Err() error {
|
||||
if len(e) == 0 {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e ErrorList) Error() string {
|
||||
switch len(e) {
|
||||
case 0:
|
||||
return "no errors"
|
||||
case 1:
|
||||
return e[0].Error()
|
||||
}
|
||||
return fmt.Sprintf("%s (and %d more errors)", e[0], len(e)-1)
|
||||
}
|
||||
|
||||
// LogError logs a list of errors, one error per line, if the err parameter is
|
||||
// an ErrorList. Otherwise it just logs the err string. Reports at most max
|
||||
// errors, or unlimited if max is 0.
|
||||
func LogError(l *log.Logger, err error, max int) {
|
||||
var list ErrorList
|
||||
if errors.As(err, &list) {
|
||||
for i, e := range list {
|
||||
if max > 0 && i == max {
|
||||
l.Print("too many errors")
|
||||
return
|
||||
}
|
||||
l.Printf("%s\n", e)
|
||||
}
|
||||
} else if err != nil {
|
||||
l.Printf("%s\n", err)
|
||||
}
|
||||
}
|
163
vendor/github.com/mmcloughlin/avo/build/global.go
generated
vendored
Normal file
163
vendor/github.com/mmcloughlin/avo/build/global.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/mmcloughlin/avo/attr"
|
||||
"github.com/mmcloughlin/avo/buildtags"
|
||||
"github.com/mmcloughlin/avo/gotypes"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// ctx provides a global build context.
|
||||
var ctx = NewContext()
|
||||
|
||||
// TEXT starts building a new function called name, with attributes a, and sets its signature (see SignatureExpr).
|
||||
func TEXT(name string, a attr.Attribute, signature string) {
|
||||
ctx.Function(name)
|
||||
ctx.Attributes(a)
|
||||
ctx.SignatureExpr(signature)
|
||||
}
|
||||
|
||||
// GLOBL declares a new static global data section with the given attributes.
|
||||
func GLOBL(name string, a attr.Attribute) operand.Mem {
|
||||
// TODO(mbm): should this be static?
|
||||
g := ctx.StaticGlobal(name)
|
||||
ctx.DataAttributes(a)
|
||||
return g
|
||||
}
|
||||
|
||||
// DATA adds a data value to the active data section.
|
||||
func DATA(offset int, v operand.Constant) {
|
||||
ctx.AddDatum(offset, v)
|
||||
}
|
||||
|
||||
var flags = NewFlags(flag.CommandLine)
|
||||
|
||||
// Generate builds and compiles the avo file built with the global context. This
|
||||
// should be the final line of any avo program. Configuration is determined from command-line flags.
|
||||
func Generate() {
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
cfg := flags.Config()
|
||||
|
||||
status := Main(cfg, ctx)
|
||||
|
||||
// To record coverage of integration tests we wrap main() functions in a test
|
||||
// functions. In this case we need the main function to terminate, therefore we
|
||||
// only exit for failure status codes.
|
||||
if status != 0 {
|
||||
os.Exit(status)
|
||||
}
|
||||
}
|
||||
|
||||
// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
|
||||
func Package(path string) { ctx.Package(path) }
|
||||
|
||||
// Constraints sets build constraints for the file.
|
||||
func Constraints(t buildtags.ConstraintsConvertable) { ctx.Constraints(t) }
|
||||
|
||||
// Constraint appends a constraint to the file's build constraints.
|
||||
func Constraint(t buildtags.ConstraintConvertable) { ctx.Constraint(t) }
|
||||
|
||||
// ConstraintExpr appends a constraint to the file's build constraints. The
|
||||
// constraint to add is parsed from the given expression. The expression should
|
||||
// look the same as the content following "// +build " in regular build
|
||||
// constraint comments.
|
||||
func ConstraintExpr(expr string) { ctx.ConstraintExpr(expr) }
|
||||
|
||||
// GP8L allocates and returns a general-purpose 8-bit register (low byte).
|
||||
func GP8L() reg.GPVirtual { return ctx.GP8L() }
|
||||
|
||||
// GP8H allocates and returns a general-purpose 8-bit register (high byte).
|
||||
func GP8H() reg.GPVirtual { return ctx.GP8H() }
|
||||
|
||||
// GP8 allocates and returns a general-purpose 8-bit register (low byte).
|
||||
func GP8() reg.GPVirtual { return ctx.GP8() }
|
||||
|
||||
// GP16 allocates and returns a general-purpose 16-bit register.
|
||||
func GP16() reg.GPVirtual { return ctx.GP16() }
|
||||
|
||||
// GP32 allocates and returns a general-purpose 32-bit register.
|
||||
func GP32() reg.GPVirtual { return ctx.GP32() }
|
||||
|
||||
// GP64 allocates and returns a general-purpose 64-bit register.
|
||||
func GP64() reg.GPVirtual { return ctx.GP64() }
|
||||
|
||||
// XMM allocates and returns a 128-bit vector register.
|
||||
func XMM() reg.VecVirtual { return ctx.XMM() }
|
||||
|
||||
// YMM allocates and returns a 256-bit vector register.
|
||||
func YMM() reg.VecVirtual { return ctx.YMM() }
|
||||
|
||||
// ZMM allocates and returns a 512-bit vector register.
|
||||
func ZMM() reg.VecVirtual { return ctx.ZMM() }
|
||||
|
||||
// K allocates and returns an opmask register.
|
||||
func K() reg.OpmaskVirtual { return ctx.K() }
|
||||
|
||||
// Param returns a the named argument of the active function.
|
||||
func Param(name string) gotypes.Component { return ctx.Param(name) }
|
||||
|
||||
// ParamIndex returns the ith argument of the active function.
|
||||
func ParamIndex(i int) gotypes.Component { return ctx.ParamIndex(i) }
|
||||
|
||||
// Return returns a the named return value of the active function.
|
||||
func Return(name string) gotypes.Component { return ctx.Return(name) }
|
||||
|
||||
// ReturnIndex returns the ith argument of the active function.
|
||||
func ReturnIndex(i int) gotypes.Component { return ctx.ReturnIndex(i) }
|
||||
|
||||
// Load the function argument src into register dst. Returns the destination
|
||||
// register. This is syntactic sugar: it will attempt to select the right MOV
|
||||
// instruction based on the types involved.
|
||||
func Load(src gotypes.Component, dst reg.Register) reg.Register { return ctx.Load(src, dst) }
|
||||
|
||||
// Store register src into return value dst. This is syntactic sugar: it will
|
||||
// attempt to select the right MOV instruction based on the types involved.
|
||||
func Store(src reg.Register, dst gotypes.Component) { ctx.Store(src, dst) }
|
||||
|
||||
// Dereference loads a pointer and returns its element type.
|
||||
func Dereference(ptr gotypes.Component) gotypes.Component { return ctx.Dereference(ptr) }
|
||||
|
||||
// Function starts building a new function with the given name.
|
||||
func Function(name string) { ctx.Function(name) }
|
||||
|
||||
// Doc sets documentation comment lines for the currently active function.
|
||||
func Doc(lines ...string) { ctx.Doc(lines...) }
|
||||
|
||||
// Pragma adds a compiler directive to the currently active function.
|
||||
func Pragma(directive string, args ...string) { ctx.Pragma(directive, args...) }
|
||||
|
||||
// Attributes sets function attributes for the currently active function.
|
||||
func Attributes(a attr.Attribute) { ctx.Attributes(a) }
|
||||
|
||||
// SignatureExpr parses the signature expression and sets it as the active function's signature.
|
||||
func SignatureExpr(expr string) { ctx.SignatureExpr(expr) }
|
||||
|
||||
// Implement starts building a function of the given name, whose type is
|
||||
// specified by a stub in the containing package.
|
||||
func Implement(name string) { ctx.Implement(name) }
|
||||
|
||||
// AllocLocal allocates size bytes in the stack of the currently active function.
|
||||
// Returns a reference to the base pointer for the newly allocated region.
|
||||
func AllocLocal(size int) operand.Mem { return ctx.AllocLocal(size) }
|
||||
|
||||
// Label adds a label to the active function.
|
||||
func Label(name string) { ctx.Label(name) }
|
||||
|
||||
// Comment adds comment lines to the active function.
|
||||
func Comment(lines ...string) { ctx.Comment(lines...) }
|
||||
|
||||
// Commentf adds a formtted comment line.
|
||||
func Commentf(format string, a ...any) { ctx.Commentf(format, a...) }
|
||||
|
||||
// ConstData builds a static data section containing just the given constant.
|
||||
func ConstData(name string, v operand.Constant) operand.Mem { return ctx.ConstData(name, v) }
|
||||
|
||||
// Instruction adds an instruction to the active function.
|
||||
func Instruction(i *ir.Instruction) { ctx.Instruction(i) }
|
69
vendor/github.com/mmcloughlin/avo/build/pseudo.go
generated
vendored
Normal file
69
vendor/github.com/mmcloughlin/avo/build/pseudo.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/mmcloughlin/avo/attr"
|
||||
"github.com/mmcloughlin/avo/gotypes"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
//go:generate avogen -output zmov.go mov
|
||||
|
||||
// Param returns a the named argument of the active function.
|
||||
func (c *Context) Param(name string) gotypes.Component {
|
||||
return c.activefunc().Signature.Params().Lookup(name)
|
||||
}
|
||||
|
||||
// ParamIndex returns the ith argument of the active function.
|
||||
func (c *Context) ParamIndex(i int) gotypes.Component {
|
||||
return c.activefunc().Signature.Params().At(i)
|
||||
}
|
||||
|
||||
// Return returns a the named return value of the active function.
|
||||
func (c *Context) Return(name string) gotypes.Component {
|
||||
return c.activefunc().Signature.Results().Lookup(name)
|
||||
}
|
||||
|
||||
// ReturnIndex returns the ith argument of the active function.
|
||||
func (c *Context) ReturnIndex(i int) gotypes.Component {
|
||||
return c.activefunc().Signature.Results().At(i)
|
||||
}
|
||||
|
||||
// Load the function argument src into register dst. Returns the destination
|
||||
// register. This is syntactic sugar: it will attempt to select the right MOV
|
||||
// instruction based on the types involved.
|
||||
func (c *Context) Load(src gotypes.Component, dst reg.Register) reg.Register {
|
||||
b, err := src.Resolve()
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return dst
|
||||
}
|
||||
c.mov(b.Addr, dst, int(gotypes.Sizes.Sizeof(b.Type)), int(dst.Size()), b.Type)
|
||||
return dst
|
||||
}
|
||||
|
||||
// Store register src into return value dst. This is syntactic sugar: it will
|
||||
// attempt to select the right MOV instruction based on the types involved.
|
||||
func (c *Context) Store(src reg.Register, dst gotypes.Component) {
|
||||
b, err := dst.Resolve()
|
||||
if err != nil {
|
||||
c.adderror(err)
|
||||
return
|
||||
}
|
||||
c.mov(src, b.Addr, int(src.Size()), int(gotypes.Sizes.Sizeof(b.Type)), b.Type)
|
||||
}
|
||||
|
||||
// Dereference loads a pointer and returns its element type.
|
||||
func (c *Context) Dereference(ptr gotypes.Component) gotypes.Component {
|
||||
r := c.GP64()
|
||||
c.Load(ptr, r)
|
||||
return ptr.Dereference(r)
|
||||
}
|
||||
|
||||
// ConstData builds a static data section containing just the given constant.
|
||||
func (c *Context) ConstData(name string, v operand.Constant) operand.Mem {
|
||||
g := c.StaticGlobal(name)
|
||||
c.DataAttributes(attr.RODATA | attr.NOPTR)
|
||||
c.AppendDatum(v)
|
||||
return g
|
||||
}
|
86852
vendor/github.com/mmcloughlin/avo/build/zinstructions.go
generated
vendored
Normal file
86852
vendor/github.com/mmcloughlin/avo/build/zinstructions.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
294
vendor/github.com/mmcloughlin/avo/build/zmov.go
generated
vendored
Normal file
294
vendor/github.com/mmcloughlin/avo/build/zmov.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
// Code generated by command: avogen -output zmov.go mov. DO NOT EDIT.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
func (c *Context) mov(a, b operand.Op, an, bn int, t *types.Basic) {
|
||||
switch {
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVB(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 1 && operand.IsM8(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVB(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVB(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVB(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVB(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVD(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 4 && operand.IsM32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVD(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVD(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVD(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVD(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVQ(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsM64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVQ(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVQ(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVQ(a, b)
|
||||
case an == 8 && operand.IsR64(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVQ(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVW(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 2 && operand.IsM16(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVW(a, b)
|
||||
case an == 8 && operand.IsK(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVW(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVW(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 8 && operand.IsK(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.KMOVW(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 1 && operand.IsR8(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVB(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 1 && operand.IsM8(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVB(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 1 && operand.IsR8(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVB(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBLSX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBLSX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBLZX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBLZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBLZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 4 && operand.IsR32(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBLZX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBQSX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBQSX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBQZX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBQZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBQZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 8 && operand.IsR64(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBQZX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBWSX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVBWSX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBWZX(a, b)
|
||||
case an == 1 && operand.IsM8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBWZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVBWZX(a, b)
|
||||
case an == 1 && operand.IsR8(a) && bn == 2 && operand.IsR16(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVBWZX(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVL(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 4 && operand.IsM32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVL(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVL(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVLQSX(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVLQSX(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVLQZX(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 8 && operand.IsR64(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVLQZX(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVOU(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVOU(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVOU(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsR64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsM32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsM64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsR64(a) && bn == 8 && operand.IsM64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsR64(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVQ(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSD(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsM64(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSD(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSD(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSS(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsM32(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSS(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.MOVSS(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVW(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 2 && operand.IsM16(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVW(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 2 && operand.IsR16(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.MOVW(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVWLSX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVWLSX(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVWLZX(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVWLZX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVWLZX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 4 && operand.IsR32(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVWLZX(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVWQSX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == types.IsInteger:
|
||||
c.MOVWQSX(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVWQZX(a, b)
|
||||
case an == 2 && operand.IsM16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVWQZX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsUnsigned)) == (types.IsInteger|types.IsUnsigned):
|
||||
c.MOVWQZX(a, b)
|
||||
case an == 2 && operand.IsR16(a) && bn == 8 && operand.IsR64(b) && (t.Info()&types.IsBoolean) != 0:
|
||||
c.MOVWQZX(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVD(a, b)
|
||||
case an == 4 && operand.IsR32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVD(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsM32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVD(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsR32(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVD(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 32 && operand.IsM256(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsM256(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 32 && operand.IsM256(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsM256(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 64 && operand.IsM512(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsM512(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU16(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 32 && operand.IsM256(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsM256(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 64 && operand.IsM512(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsM512(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU32(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 32 && operand.IsM256(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsM256(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 64 && operand.IsM512(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsM512(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU64(a, b)
|
||||
case an == 16 && operand.IsM128(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 32 && operand.IsM256(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsM128(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsM256(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 32 && operand.IsYMM(a) && bn == 32 && operand.IsYMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 64 && operand.IsM512(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsM512(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 64 && operand.IsZMM(a) && bn == 64 && operand.IsZMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVDQU8(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVQ(a, b)
|
||||
case an == 8 && operand.IsR64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsM64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsR64(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVQ(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&(types.IsInteger|types.IsBoolean)) != 0:
|
||||
c.VMOVQ(a, b)
|
||||
case an == 8 && operand.IsM64(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.VMOVSD(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 8 && operand.IsM64(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.VMOVSD(a, b)
|
||||
case an == 4 && operand.IsM32(a) && bn == 16 && operand.IsXMM(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.VMOVSS(a, b)
|
||||
case an == 16 && operand.IsXMM(a) && bn == 4 && operand.IsM32(b) && (t.Info()&types.IsFloat) != 0:
|
||||
c.VMOVSS(a, b)
|
||||
default:
|
||||
c.adderrormessage("could not deduce mov instruction")
|
||||
}
|
||||
}
|
312
vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go
generated
vendored
Normal file
312
vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go
generated
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
// Package buildtags provides types for representing and manipulating build constraints.
|
||||
//
|
||||
// In Go, build constraints are represented as comments in source code together with file naming conventions. For example
|
||||
//
|
||||
// // +build linux,386 darwin,!cgo
|
||||
// // +build !purego
|
||||
//
|
||||
// Any terms provided in the filename can be thought of as an implicit extra
|
||||
// constraint comment line. Collectively, these are referred to as
|
||||
// “constraints”. Each line is a “constraint”. Within each constraint the
|
||||
// space-separated terms are “options”, and within that the comma-separated
|
||||
// items are “terms” which may be negated with at most one exclaimation mark.
|
||||
//
|
||||
// These represent a boolean formulae. The constraints are evaluated as the AND
|
||||
// of constraint lines; a constraint is evaluated as the OR of its options and
|
||||
// an option is evaluated as the AND of its terms. Overall build constraints are
|
||||
// a boolean formula that is an AND of ORs of ANDs.
|
||||
//
|
||||
// This level of complexity is rarely used in Go programs. Therefore this
|
||||
// package aims to provide access to all these layers of nesting if required,
|
||||
// but make it easy to forget about for basic use cases too.
|
||||
package buildtags
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/go/build/doc.go#L73-L92
|
||||
//
|
||||
// // A build constraint is evaluated as the OR of space-separated options;
|
||||
// // each option evaluates as the AND of its comma-separated terms;
|
||||
// // and each term is an alphanumeric word or, preceded by !, its negation.
|
||||
// // That is, the build constraint:
|
||||
// //
|
||||
// // // +build linux,386 darwin,!cgo
|
||||
// //
|
||||
// // corresponds to the boolean formula:
|
||||
// //
|
||||
// // (linux AND 386) OR (darwin AND (NOT cgo))
|
||||
// //
|
||||
// // A file may have multiple build constraints. The overall constraint is the AND
|
||||
// // of the individual constraints. That is, the build constraints:
|
||||
// //
|
||||
// // // +build linux darwin
|
||||
// // // +build 386
|
||||
// //
|
||||
// // corresponds to the boolean formula:
|
||||
// //
|
||||
// // (linux OR darwin) AND 386
|
||||
//
|
||||
|
||||
// Interface represents a build constraint.
|
||||
type Interface interface {
|
||||
ConstraintsConvertable
|
||||
fmt.GoStringer
|
||||
Evaluate(v map[string]bool) bool
|
||||
Validate() error
|
||||
}
|
||||
|
||||
// ConstraintsConvertable can be converted to a Constraints object.
|
||||
type ConstraintsConvertable interface {
|
||||
ToConstraints() Constraints
|
||||
}
|
||||
|
||||
// ConstraintConvertable can be converted to a Constraint.
|
||||
type ConstraintConvertable interface {
|
||||
ToConstraint() Constraint
|
||||
}
|
||||
|
||||
// OptionConvertable can be converted to an Option.
|
||||
type OptionConvertable interface {
|
||||
ToOption() Option
|
||||
}
|
||||
|
||||
// Constraints represents the AND of a list of Constraint lines.
|
||||
type Constraints []Constraint
|
||||
|
||||
// And builds Constraints that will be true if all of its constraints are true.
|
||||
func And(cs ...ConstraintConvertable) Constraints {
|
||||
constraints := Constraints{}
|
||||
for _, c := range cs {
|
||||
constraints = append(constraints, c.ToConstraint())
|
||||
}
|
||||
return constraints
|
||||
}
|
||||
|
||||
// ToConstraints returns cs.
|
||||
func (cs Constraints) ToConstraints() Constraints { return cs }
|
||||
|
||||
// Validate validates the constraints set.
|
||||
func (cs Constraints) Validate() error {
|
||||
for _, c := range cs {
|
||||
if err := c.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evaluate the boolean formula represented by cs under the given assignment of
|
||||
// tag values. This is the AND of the values of the constituent Constraints.
|
||||
func (cs Constraints) Evaluate(v map[string]bool) bool {
|
||||
r := true
|
||||
for _, c := range cs {
|
||||
r = r && c.Evaluate(v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GoString represents Constraints as +build comment lines.
|
||||
func (cs Constraints) GoString() string {
|
||||
s := ""
|
||||
for _, c := range cs {
|
||||
s += c.GoString()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Constraint represents the OR of a list of Options.
|
||||
type Constraint []Option
|
||||
|
||||
// Any builds a Constraint that will be true if any of its options are true.
|
||||
func Any(opts ...OptionConvertable) Constraint {
|
||||
c := Constraint{}
|
||||
for _, opt := range opts {
|
||||
c = append(c, opt.ToOption())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ParseConstraint parses a space-separated list of options.
|
||||
func ParseConstraint(expr string) (Constraint, error) {
|
||||
c := Constraint{}
|
||||
for _, field := range strings.Fields(expr) {
|
||||
opt, err := ParseOption(field)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
c = append(c, opt)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ToConstraints returns the list of constraints containing just c.
|
||||
func (c Constraint) ToConstraints() Constraints { return Constraints{c} }
|
||||
|
||||
// ToConstraint returns c.
|
||||
func (c Constraint) ToConstraint() Constraint { return c }
|
||||
|
||||
// Validate validates the constraint.
|
||||
func (c Constraint) Validate() error {
|
||||
for _, o := range c {
|
||||
if err := o.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evaluate the boolean formula represented by c under the given assignment of
|
||||
// tag values. This is the OR of the values of the constituent Options.
|
||||
func (c Constraint) Evaluate(v map[string]bool) bool {
|
||||
r := false
|
||||
for _, o := range c {
|
||||
r = r || o.Evaluate(v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GoString represents the Constraint as one +build comment line.
|
||||
func (c Constraint) GoString() string {
|
||||
s := "// +build"
|
||||
for _, o := range c {
|
||||
s += " " + o.GoString()
|
||||
}
|
||||
return s + "\n"
|
||||
}
|
||||
|
||||
// Option represents the AND of a list of Terms.
|
||||
type Option []Term
|
||||
|
||||
// Opt builds an Option from the list of Terms.
|
||||
func Opt(terms ...Term) Option {
|
||||
return Option(terms)
|
||||
}
|
||||
|
||||
// ParseOption parses a comma-separated list of terms.
|
||||
func ParseOption(expr string) (Option, error) {
|
||||
opt := Option{}
|
||||
for _, t := range strings.Split(expr, ",") {
|
||||
opt = append(opt, Term(t))
|
||||
}
|
||||
return opt, opt.Validate()
|
||||
}
|
||||
|
||||
// ToConstraints returns Constraints containing just this option.
|
||||
func (o Option) ToConstraints() Constraints { return o.ToConstraint().ToConstraints() }
|
||||
|
||||
// ToConstraint returns a Constraint containing just this option.
|
||||
func (o Option) ToConstraint() Constraint { return Constraint{o} }
|
||||
|
||||
// ToOption returns o.
|
||||
func (o Option) ToOption() Option { return o }
|
||||
|
||||
// Validate validates o.
|
||||
func (o Option) Validate() error {
|
||||
for _, t := range o {
|
||||
if err := t.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid term %q: %w", t, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evaluate the boolean formula represented by o under the given assignment of
|
||||
// tag values. This is the AND of the values of the constituent Terms.
|
||||
func (o Option) Evaluate(v map[string]bool) bool {
|
||||
r := true
|
||||
for _, t := range o {
|
||||
r = r && t.Evaluate(v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GoString represents the Option as a comma-separated list of terms.
|
||||
func (o Option) GoString() string {
|
||||
var ts []string
|
||||
for _, t := range o {
|
||||
ts = append(ts, t.GoString())
|
||||
}
|
||||
return strings.Join(ts, ",")
|
||||
}
|
||||
|
||||
// Term is an atomic term in a build constraint: an identifier or its negation.
|
||||
type Term string
|
||||
|
||||
// Not returns a term for the negation of ident.
|
||||
func Not(ident string) Term {
|
||||
return Term("!" + ident)
|
||||
}
|
||||
|
||||
// ToConstraints returns Constraints containing just this term.
|
||||
func (t Term) ToConstraints() Constraints { return t.ToOption().ToConstraints() }
|
||||
|
||||
// ToConstraint returns a Constraint containing just this term.
|
||||
func (t Term) ToConstraint() Constraint { return t.ToOption().ToConstraint() }
|
||||
|
||||
// ToOption returns an Option containing just this term.
|
||||
func (t Term) ToOption() Option { return Option{t} }
|
||||
|
||||
// IsNegated reports whether t is the negation of an identifier.
|
||||
func (t Term) IsNegated() bool { return strings.HasPrefix(string(t), "!") }
|
||||
|
||||
// Name returns the identifier for this term.
|
||||
func (t Term) Name() string {
|
||||
return strings.TrimPrefix(string(t), "!")
|
||||
}
|
||||
|
||||
// Validate the term.
|
||||
func (t Term) Validate() error {
|
||||
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L110-L112
|
||||
//
|
||||
// if strings.HasPrefix(name, "!!") { // bad syntax, reject always
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
if strings.HasPrefix(string(t), "!!") {
|
||||
return errors.New("at most one '!' allowed")
|
||||
}
|
||||
|
||||
if len(t.Name()) == 0 {
|
||||
return errors.New("empty tag name")
|
||||
}
|
||||
|
||||
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L121-L127
|
||||
//
|
||||
// // Tags must be letters, digits, underscores or dots.
|
||||
// // Unlike in Go identifiers, all digits are fine (e.g., "386").
|
||||
// for _, c := range name {
|
||||
// if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
for _, c := range t.Name() {
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||
return fmt.Errorf("character '%c' disallowed in tags", c)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evaluate the term under the given set of identifier values.
|
||||
func (t Term) Evaluate(v map[string]bool) bool {
|
||||
return (t.Validate() == nil) && (v[t.Name()] == !t.IsNegated())
|
||||
}
|
||||
|
||||
// GoString returns t.
|
||||
func (t Term) GoString() string { return string(t) }
|
||||
|
||||
// SetTags builds a set where the given list of identifiers are true.
|
||||
func SetTags(idents ...string) map[string]bool {
|
||||
v := map[string]bool{}
|
||||
for _, ident := range idents {
|
||||
v[ident] = true
|
||||
}
|
||||
return v
|
||||
}
|
46
vendor/github.com/mmcloughlin/avo/buildtags/syntax.go
generated
vendored
Normal file
46
vendor/github.com/mmcloughlin/avo/buildtags/syntax.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package buildtags
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PlusBuildSyntaxSupported reports whether the current Go version supports the
|
||||
// "// +build" constraint syntax.
|
||||
func PlusBuildSyntaxSupported() bool { return plusbuild }
|
||||
|
||||
// GoBuildSyntaxSupported reports whether the current Go version supports the
|
||||
// "//go:build" constraint syntax.
|
||||
func GoBuildSyntaxSupported() bool { return gobuild }
|
||||
|
||||
// Format constraints according to the syntax supported by the current Go version.
|
||||
func Format(t ConstraintsConvertable) (string, error) {
|
||||
// Print build tags to minimal Go source that can be passed to go/format.
|
||||
src := t.ToConstraints().GoString() + "\npackage stub"
|
||||
|
||||
// Format them.
|
||||
formatted, err := format.Source([]byte(src))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("format build constraints: %w", err)
|
||||
}
|
||||
|
||||
// Extract the comment lines.
|
||||
buf := bytes.NewReader(formatted)
|
||||
scanner := bufio.NewScanner(buf)
|
||||
output := ""
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if (PlusBuildSyntaxSupported() && strings.HasPrefix(line, "// +build")) ||
|
||||
(GoBuildSyntaxSupported() && strings.HasPrefix(line, "//go:build")) {
|
||||
output += line + "\n"
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", fmt.Errorf("parse formatted build constraints: %w", err)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go116.go
generated
vendored
Normal file
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go116.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !go1.17
|
||||
|
||||
package buildtags
|
||||
|
||||
const (
|
||||
plusbuild = true
|
||||
gobuild = false
|
||||
)
|
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go117.go
generated
vendored
Normal file
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go117.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build go1.17 && !go1.18
|
||||
|
||||
package buildtags
|
||||
|
||||
const (
|
||||
plusbuild = true
|
||||
gobuild = true
|
||||
)
|
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go118.go
generated
vendored
Normal file
8
vendor/github.com/mmcloughlin/avo/buildtags/syntax_go118.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build go1.18
|
||||
|
||||
package buildtags
|
||||
|
||||
const (
|
||||
plusbuild = false
|
||||
gobuild = true
|
||||
)
|
253
vendor/github.com/mmcloughlin/avo/gotypes/components.go
generated
vendored
Normal file
253
vendor/github.com/mmcloughlin/avo/gotypes/components.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
package gotypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// Sizes provides type sizes used by the standard Go compiler on amd64.
|
||||
var Sizes = types.SizesFor("gc", "amd64")
|
||||
|
||||
// PointerSize is the size of a pointer on amd64.
|
||||
var PointerSize = Sizes.Sizeof(types.Typ[types.UnsafePointer])
|
||||
|
||||
// Basic represents a primitive/basic type at a given memory address.
|
||||
type Basic struct {
|
||||
Addr operand.Mem
|
||||
Type *types.Basic
|
||||
}
|
||||
|
||||
// Component provides access to sub-components of a Go type.
|
||||
type Component interface {
|
||||
// When the component has no further sub-components, Resolve will return a
|
||||
// reference to the components type and memory address. If there was an error
|
||||
// during any previous calls to Component methods, they will be returned at
|
||||
// resolution time.
|
||||
Resolve() (*Basic, error)
|
||||
Dereference(r reg.Register) Component // dereference a pointer
|
||||
Base() Component // base pointer of a string or slice
|
||||
Len() Component // length of a string or slice
|
||||
Cap() Component // capacity of a slice
|
||||
Real() Component // real part of a complex value
|
||||
Imag() Component // imaginary part of a complex value
|
||||
Index(int) Component // index into an array
|
||||
Field(string) Component // access a struct field
|
||||
}
|
||||
|
||||
// componenterr is an error that also provides a null implementation of the
|
||||
// Component interface. This enables us to return an error from Component
|
||||
// methods whilst also allowing method chaining to continue.
|
||||
type componenterr string
|
||||
|
||||
func errorf(format string, args ...any) Component {
|
||||
return componenterr(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (c componenterr) Error() string { return string(c) }
|
||||
func (c componenterr) Resolve() (*Basic, error) { return nil, c }
|
||||
func (c componenterr) Dereference(r reg.Register) Component { return c }
|
||||
func (c componenterr) Base() Component { return c }
|
||||
func (c componenterr) Len() Component { return c }
|
||||
func (c componenterr) Cap() Component { return c }
|
||||
func (c componenterr) Real() Component { return c }
|
||||
func (c componenterr) Imag() Component { return c }
|
||||
func (c componenterr) Index(int) Component { return c }
|
||||
func (c componenterr) Field(string) Component { return c }
|
||||
|
||||
type component struct {
|
||||
typ types.Type
|
||||
addr operand.Mem
|
||||
}
|
||||
|
||||
// NewComponent builds a component for the named type at the given address.
|
||||
func NewComponent(t types.Type, addr operand.Mem) Component {
|
||||
return &component{
|
||||
typ: t,
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *component) Resolve() (*Basic, error) {
|
||||
b := toprimitive(c.typ)
|
||||
if b == nil {
|
||||
return nil, errors.New("component is not primitive")
|
||||
}
|
||||
return &Basic{
|
||||
Addr: c.addr,
|
||||
Type: b,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *component) Dereference(r reg.Register) Component {
|
||||
p, ok := c.typ.Underlying().(*types.Pointer)
|
||||
if !ok {
|
||||
return errorf("not pointer type")
|
||||
}
|
||||
return NewComponent(p.Elem(), operand.Mem{Base: r})
|
||||
}
|
||||
|
||||
// Reference: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/reflect/value.go#L1800-L1804
|
||||
//
|
||||
// type SliceHeader struct {
|
||||
// Data uintptr
|
||||
// Len int
|
||||
// Cap int
|
||||
// }
|
||||
var slicehdroffsets = Sizes.Offsetsof([]*types.Var{
|
||||
types.NewField(token.NoPos, nil, "Data", types.Typ[types.Uintptr], false),
|
||||
types.NewField(token.NoPos, nil, "Len", types.Typ[types.Int], false),
|
||||
types.NewField(token.NoPos, nil, "Cap", types.Typ[types.Int], false),
|
||||
})
|
||||
|
||||
func (c *component) Base() Component {
|
||||
if !isslice(c.typ) && !isstring(c.typ) {
|
||||
return errorf("only slices and strings have base pointers")
|
||||
}
|
||||
return c.sub("_base", int(slicehdroffsets[0]), types.Typ[types.Uintptr])
|
||||
}
|
||||
|
||||
func (c *component) Len() Component {
|
||||
if !isslice(c.typ) && !isstring(c.typ) {
|
||||
return errorf("only slices and strings have length fields")
|
||||
}
|
||||
return c.sub("_len", int(slicehdroffsets[1]), types.Typ[types.Int])
|
||||
}
|
||||
|
||||
func (c *component) Cap() Component {
|
||||
if !isslice(c.typ) {
|
||||
return errorf("only slices have capacity fields")
|
||||
}
|
||||
return c.sub("_cap", int(slicehdroffsets[2]), types.Typ[types.Int])
|
||||
}
|
||||
|
||||
func (c *component) Real() Component {
|
||||
if !iscomplex(c.typ) {
|
||||
return errorf("only complex types have real values")
|
||||
}
|
||||
f := complextofloat(c.typ)
|
||||
return c.sub("_real", 0, f)
|
||||
}
|
||||
|
||||
func (c *component) Imag() Component {
|
||||
if !iscomplex(c.typ) {
|
||||
return errorf("only complex types have imaginary values")
|
||||
}
|
||||
f := complextofloat(c.typ)
|
||||
return c.sub("_imag", int(Sizes.Sizeof(f)), f)
|
||||
}
|
||||
|
||||
func (c *component) Index(i int) Component {
|
||||
a, ok := c.typ.Underlying().(*types.Array)
|
||||
if !ok {
|
||||
return errorf("not array type")
|
||||
}
|
||||
if int64(i) >= a.Len() {
|
||||
return errorf("array index out of bounds")
|
||||
}
|
||||
// Reference: https://github.com/golang/tools/blob/bcd4e47d02889ebbc25c9f4bf3d27e4124b0bf9d/go/analysis/passes/asmdecl/asmdecl.go#L482-L494
|
||||
//
|
||||
// case asmArray:
|
||||
// tu := t.Underlying().(*types.Array)
|
||||
// elem := tu.Elem()
|
||||
// // Calculate offset of each element array.
|
||||
// fields := []*types.Var{
|
||||
// types.NewVar(token.NoPos, nil, "fake0", elem),
|
||||
// types.NewVar(token.NoPos, nil, "fake1", elem),
|
||||
// }
|
||||
// offsets := arch.sizes.Offsetsof(fields)
|
||||
// elemoff := int(offsets[1])
|
||||
// for i := 0; i < int(tu.Len()); i++ {
|
||||
// cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
|
||||
// }
|
||||
//
|
||||
elem := a.Elem()
|
||||
elemsize := int(Sizes.Sizeof(types.NewArray(elem, 2)) - Sizes.Sizeof(types.NewArray(elem, 1)))
|
||||
return c.sub("_"+strconv.Itoa(i), i*elemsize, elem)
|
||||
}
|
||||
|
||||
func (c *component) Field(n string) Component {
|
||||
s, ok := c.typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return errorf("not struct type")
|
||||
}
|
||||
// Reference: https://github.com/golang/tools/blob/13ba8ad772dfbf0f451b5dd0679e9c5605afc05d/go/analysis/passes/asmdecl/asmdecl.go#L471-L480
|
||||
//
|
||||
// case asmStruct:
|
||||
// tu := t.Underlying().(*types.Struct)
|
||||
// fields := make([]*types.Var, tu.NumFields())
|
||||
// for i := 0; i < tu.NumFields(); i++ {
|
||||
// fields[i] = tu.Field(i)
|
||||
// }
|
||||
// offsets := arch.sizes.Offsetsof(fields)
|
||||
// for i, f := range fields {
|
||||
// cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
|
||||
// }
|
||||
//
|
||||
fields := make([]*types.Var, s.NumFields())
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
fields[i] = s.Field(i)
|
||||
}
|
||||
offsets := Sizes.Offsetsof(fields)
|
||||
for i, f := range fields {
|
||||
if f.Name() == n {
|
||||
return c.sub("_"+n, int(offsets[i]), f.Type())
|
||||
}
|
||||
}
|
||||
return errorf("struct does not have field '%s'", n)
|
||||
}
|
||||
|
||||
func (c *component) sub(suffix string, offset int, t types.Type) *component {
|
||||
s := *c
|
||||
if s.addr.Symbol.Name != "" {
|
||||
s.addr.Symbol.Name += suffix
|
||||
}
|
||||
s.addr = s.addr.Offset(offset)
|
||||
s.typ = t
|
||||
return &s
|
||||
}
|
||||
|
||||
func isslice(t types.Type) bool {
|
||||
_, ok := t.Underlying().(*types.Slice)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isstring(t types.Type) bool {
|
||||
b, ok := t.Underlying().(*types.Basic)
|
||||
return ok && b.Kind() == types.String
|
||||
}
|
||||
|
||||
func iscomplex(t types.Type) bool {
|
||||
b, ok := t.Underlying().(*types.Basic)
|
||||
return ok && (b.Info()&types.IsComplex) != 0
|
||||
}
|
||||
|
||||
func complextofloat(t types.Type) types.Type {
|
||||
switch Sizes.Sizeof(t) {
|
||||
case 16:
|
||||
return types.Typ[types.Float64]
|
||||
case 8:
|
||||
return types.Typ[types.Float32]
|
||||
}
|
||||
panic("bad")
|
||||
}
|
||||
|
||||
// toprimitive determines whether t is primitive (cannot be reduced into
|
||||
// components). If it is, it returns the basic type for t, otherwise returns
|
||||
// nil.
|
||||
func toprimitive(t types.Type) *types.Basic {
|
||||
switch b := t.(type) {
|
||||
case *types.Basic:
|
||||
if (b.Info() & (types.IsString | types.IsComplex)) == 0 {
|
||||
return b
|
||||
}
|
||||
case *types.Pointer:
|
||||
return types.Typ[types.Uintptr]
|
||||
}
|
||||
return nil
|
||||
}
|
2
vendor/github.com/mmcloughlin/avo/gotypes/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/gotypes/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package gotypes provides helpers for interacting with Go types within avo functions.
|
||||
package gotypes
|
193
vendor/github.com/mmcloughlin/avo/gotypes/signature.go
generated
vendored
Normal file
193
vendor/github.com/mmcloughlin/avo/gotypes/signature.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
package gotypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
// Signature represents a Go function signature.
|
||||
type Signature struct {
|
||||
pkg *types.Package
|
||||
sig *types.Signature
|
||||
params *Tuple
|
||||
results *Tuple
|
||||
}
|
||||
|
||||
// NewSignature constructs a Signature.
|
||||
func NewSignature(pkg *types.Package, sig *types.Signature) *Signature {
|
||||
s := &Signature{
|
||||
pkg: pkg,
|
||||
sig: sig,
|
||||
}
|
||||
s.init()
|
||||
return s
|
||||
}
|
||||
|
||||
// NewSignatureVoid builds the void signature "func()".
|
||||
func NewSignatureVoid() *Signature {
|
||||
return NewSignature(nil, types.NewSignatureType(nil, nil, nil, nil, nil, false))
|
||||
}
|
||||
|
||||
// LookupSignature returns the signature of the named function in the provided package.
|
||||
func LookupSignature(pkg *types.Package, name string) (*Signature, error) {
|
||||
scope := pkg.Scope()
|
||||
obj := scope.Lookup(name)
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("could not find function \"%s\"", name)
|
||||
}
|
||||
s, ok := obj.Type().(*types.Signature)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("object \"%s\" does not have signature type", name)
|
||||
}
|
||||
return NewSignature(pkg, s), nil
|
||||
}
|
||||
|
||||
// ParseSignature builds a Signature by parsing a Go function type expression.
|
||||
// The function type must reference builtin types only; see
|
||||
// ParseSignatureInPackage if custom types are required.
|
||||
func ParseSignature(expr string) (*Signature, error) {
|
||||
return ParseSignatureInPackage(nil, expr)
|
||||
}
|
||||
|
||||
// ParseSignatureInPackage builds a Signature by parsing a Go function type
|
||||
// expression. The expression may reference types in the provided package.
|
||||
func ParseSignatureInPackage(pkg *types.Package, expr string) (*Signature, error) {
|
||||
tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tv.Value != nil {
|
||||
return nil, errors.New("signature expression should have nil value")
|
||||
}
|
||||
s, ok := tv.Type.(*types.Signature)
|
||||
if !ok {
|
||||
return nil, errors.New("provided type is not a function signature")
|
||||
}
|
||||
return NewSignature(pkg, s), nil
|
||||
}
|
||||
|
||||
// Params returns the function signature argument types.
|
||||
func (s *Signature) Params() *Tuple { return s.params }
|
||||
|
||||
// Results returns the function return types.
|
||||
func (s *Signature) Results() *Tuple { return s.results }
|
||||
|
||||
// Bytes returns the total size of the function arguments and return values.
|
||||
func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes() }
|
||||
|
||||
// String writes Signature as a string. This does not include the "func" keyword.
|
||||
func (s *Signature) String() string {
|
||||
var buf bytes.Buffer
|
||||
types.WriteSignature(&buf, s.sig, func(pkg *types.Package) string {
|
||||
if pkg == s.pkg {
|
||||
return ""
|
||||
}
|
||||
return pkg.Name()
|
||||
})
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (s *Signature) init() {
|
||||
p := s.sig.Params()
|
||||
r := s.sig.Results()
|
||||
|
||||
// Compute parameter offsets. Note that if the function has results,
|
||||
// additional padding up to max align is inserted between parameters and
|
||||
// results.
|
||||
vs := tuplevars(p)
|
||||
vs = append(vs, types.NewParam(token.NoPos, nil, "sentinel", types.Typ[types.Uint64]))
|
||||
paramsoffsets := Sizes.Offsetsof(vs)
|
||||
paramssize := paramsoffsets[p.Len()]
|
||||
if r.Len() == 0 {
|
||||
paramssize = structsize(vs[:p.Len()])
|
||||
}
|
||||
s.params = newTuple(p, paramsoffsets, paramssize, "arg")
|
||||
|
||||
// Result offsets.
|
||||
vs = tuplevars(r)
|
||||
resultsoffsets := Sizes.Offsetsof(vs)
|
||||
resultssize := structsize(vs)
|
||||
for i := range resultsoffsets {
|
||||
resultsoffsets[i] += paramssize
|
||||
}
|
||||
s.results = newTuple(r, resultsoffsets, resultssize, "ret")
|
||||
}
|
||||
|
||||
// Tuple represents a tuple of variables, such as function arguments or results.
|
||||
type Tuple struct {
|
||||
components []Component
|
||||
byname map[string]Component
|
||||
size int
|
||||
}
|
||||
|
||||
func newTuple(t *types.Tuple, offsets []int64, size int64, defaultprefix string) *Tuple {
|
||||
tuple := &Tuple{
|
||||
byname: map[string]Component{},
|
||||
size: int(size),
|
||||
}
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
v := t.At(i)
|
||||
name := v.Name()
|
||||
if name == "" {
|
||||
name = defaultprefix
|
||||
if i > 0 {
|
||||
name += strconv.Itoa(i)
|
||||
}
|
||||
}
|
||||
addr := operand.NewParamAddr(name, int(offsets[i]))
|
||||
c := NewComponent(v.Type(), addr)
|
||||
tuple.components = append(tuple.components, c)
|
||||
if v.Name() != "" {
|
||||
tuple.byname[v.Name()] = c
|
||||
}
|
||||
}
|
||||
return tuple
|
||||
}
|
||||
|
||||
// Lookup returns the variable with the given name.
|
||||
func (t *Tuple) Lookup(name string) Component {
|
||||
e := t.byname[name]
|
||||
if e == nil {
|
||||
return errorf("unknown variable \"%s\"", name)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// At returns the variable at index i.
|
||||
func (t *Tuple) At(i int) Component {
|
||||
if i >= len(t.components) {
|
||||
return errorf("index out of range")
|
||||
}
|
||||
return t.components[i]
|
||||
}
|
||||
|
||||
// Bytes returns the size of the Tuple. This may include additional padding.
|
||||
func (t *Tuple) Bytes() int { return t.size }
|
||||
|
||||
func tuplevars(t *types.Tuple) []*types.Var {
|
||||
vs := make([]*types.Var, t.Len())
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
vs[i] = t.At(i)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// structsize computes the size of a struct containing the given variables as
|
||||
// fields. It would be equivalent to calculating the size of types.NewStruct(vs,
|
||||
// nil), apart from the fact that NewStruct panics if multiple fields have the
|
||||
// same name, and this happens for example if the variables represent return
|
||||
// types from a function.
|
||||
func structsize(vs []*types.Var) int64 {
|
||||
n := len(vs)
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
offsets := Sizes.Offsetsof(vs)
|
||||
return offsets[n-1] + Sizes.Sizeof(vs[n-1].Type())
|
||||
}
|
101
vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go
generated
vendored
Normal file
101
vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// Package prnt provides common functionality for code generators.
|
||||
package prnt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build/constraint"
|
||||
"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 ...any) {
|
||||
g.Printf(format, args...)
|
||||
g.NL()
|
||||
}
|
||||
|
||||
// Printf prints to the internal buffer.
|
||||
func (g *Generator) Printf(format string, args ...any) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// BuildConstraint outputs a build constraint.
|
||||
func (g *Generator) BuildConstraint(expr string) {
|
||||
line := fmt.Sprintf("//go:build %s", expr)
|
||||
if _, err := constraint.Parse(line); err != nil {
|
||||
g.AddError(err)
|
||||
}
|
||||
g.Linef(line)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
73
vendor/github.com/mmcloughlin/avo/internal/stack/stack.go
generated
vendored
Normal file
73
vendor/github.com/mmcloughlin/avo/internal/stack/stack.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
// Package stack provides helpers for querying the callstack.
|
||||
package stack
|
||||
|
||||
import (
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frames returns at most max callstack Frames, starting with its caller and
|
||||
// skipping skip Frames.
|
||||
func Frames(skip, max int) []runtime.Frame {
|
||||
pc := make([]uintptr, max)
|
||||
n := runtime.Callers(skip+2, pc)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
pc = pc[:n]
|
||||
frames := runtime.CallersFrames(pc)
|
||||
var fs []runtime.Frame
|
||||
for {
|
||||
f, more := frames.Next()
|
||||
fs = append(fs, f)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
// Match returns the first stack frame for which the predicate function returns
|
||||
// true. Returns nil if no match is found. Starts matching after skip frames,
|
||||
// starting with its caller.
|
||||
func Match(skip int, predicate func(runtime.Frame) bool) *runtime.Frame {
|
||||
i, n := skip+1, 16
|
||||
for {
|
||||
fs := Frames(i, n)
|
||||
for j, f := range fs {
|
||||
if predicate(f) {
|
||||
return &fs[j]
|
||||
}
|
||||
}
|
||||
if len(fs) < n {
|
||||
break
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Main returns the main() function Frame.
|
||||
func Main() *runtime.Frame {
|
||||
return Match(1, func(f runtime.Frame) bool {
|
||||
return f.Function == "main.main"
|
||||
})
|
||||
}
|
||||
|
||||
// ExternalCaller returns the first frame outside the callers package.
|
||||
func ExternalCaller() *runtime.Frame {
|
||||
var first *runtime.Frame
|
||||
return Match(1, func(f runtime.Frame) bool {
|
||||
if first == nil {
|
||||
first = &f
|
||||
}
|
||||
return pkg(first.Function) != pkg(f.Function)
|
||||
})
|
||||
}
|
||||
|
||||
func pkg(ident string) string {
|
||||
dir, name := path.Split(ident)
|
||||
parts := strings.Split(name, ".")
|
||||
return dir + parts[0]
|
||||
}
|
2
vendor/github.com/mmcloughlin/avo/ir/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/ir/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package ir provides the intermediate representation of avo programs.
|
||||
package ir
|
365
vendor/github.com/mmcloughlin/avo/ir/ir.go
generated
vendored
Normal file
365
vendor/github.com/mmcloughlin/avo/ir/ir.go
generated
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
package ir
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mmcloughlin/avo/attr"
|
||||
"github.com/mmcloughlin/avo/buildtags"
|
||||
"github.com/mmcloughlin/avo/gotypes"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// Node is a part of a Function.
|
||||
type Node interface {
|
||||
node()
|
||||
}
|
||||
|
||||
// Label within a function.
|
||||
type Label string
|
||||
|
||||
func (l Label) node() {}
|
||||
|
||||
// Comment represents a multi-line comment.
|
||||
type Comment struct {
|
||||
Lines []string
|
||||
}
|
||||
|
||||
func (c *Comment) node() {}
|
||||
|
||||
// NewComment builds a Comment consisting of the provided lines.
|
||||
func NewComment(lines ...string) *Comment {
|
||||
return &Comment{
|
||||
Lines: lines,
|
||||
}
|
||||
}
|
||||
|
||||
// Instruction is a single instruction in a function.
|
||||
type Instruction struct {
|
||||
Opcode string
|
||||
Suffixes []string
|
||||
Operands []operand.Op
|
||||
|
||||
Inputs []operand.Op
|
||||
Outputs []operand.Op
|
||||
|
||||
IsTerminal bool
|
||||
IsBranch bool
|
||||
IsConditional bool
|
||||
CancellingInputs bool
|
||||
|
||||
// ISA is the list of required instruction set extensions.
|
||||
ISA []string
|
||||
|
||||
// CFG.
|
||||
Pred []*Instruction
|
||||
Succ []*Instruction
|
||||
|
||||
// LiveIn/LiveOut are sets of live register IDs pre/post execution.
|
||||
LiveIn reg.MaskSet
|
||||
LiveOut reg.MaskSet
|
||||
}
|
||||
|
||||
func (i *Instruction) node() {}
|
||||
|
||||
// OpcodeWithSuffixes returns the full opcode, including dot-separated suffixes.
|
||||
func (i *Instruction) OpcodeWithSuffixes() string {
|
||||
opcode := i.Opcode
|
||||
for _, s := range i.Suffixes {
|
||||
opcode += "." + s
|
||||
}
|
||||
return opcode
|
||||
}
|
||||
|
||||
// IsUnconditionalBranch reports whether i is an unconditional branch.
|
||||
func (i Instruction) IsUnconditionalBranch() bool {
|
||||
return i.IsBranch && !i.IsConditional
|
||||
}
|
||||
|
||||
// TargetLabel returns the label referenced by this instruction. Returns nil if
|
||||
// no label is referenced.
|
||||
func (i Instruction) TargetLabel() *Label {
|
||||
if !i.IsBranch {
|
||||
return nil
|
||||
}
|
||||
if len(i.Operands) == 0 {
|
||||
return nil
|
||||
}
|
||||
if ref, ok := i.Operands[0].(operand.LabelRef); ok {
|
||||
lbl := Label(ref)
|
||||
return &lbl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers returns all registers involved in the instruction.
|
||||
func (i Instruction) Registers() []reg.Register {
|
||||
var rs []reg.Register
|
||||
for _, op := range i.Operands {
|
||||
rs = append(rs, operand.Registers(op)...)
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// InputRegisters returns all registers read by this instruction.
|
||||
func (i Instruction) InputRegisters() []reg.Register {
|
||||
var rs []reg.Register
|
||||
for _, op := range i.Inputs {
|
||||
rs = append(rs, operand.Registers(op)...)
|
||||
}
|
||||
if i.CancellingInputs && rs[0] == rs[1] {
|
||||
rs = []reg.Register{}
|
||||
}
|
||||
for _, op := range i.Outputs {
|
||||
if operand.IsMem(op) {
|
||||
rs = append(rs, operand.Registers(op)...)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// OutputRegisters returns all registers written by this instruction.
|
||||
func (i Instruction) OutputRegisters() []reg.Register {
|
||||
var rs []reg.Register
|
||||
for _, op := range i.Outputs {
|
||||
if r, ok := op.(reg.Register); ok {
|
||||
rs = append(rs, r)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// Section is a part of a file.
|
||||
type Section interface {
|
||||
section()
|
||||
}
|
||||
|
||||
// File represents an assembly file.
|
||||
type File struct {
|
||||
Constraints buildtags.Constraints
|
||||
Includes []string
|
||||
Sections []Section
|
||||
}
|
||||
|
||||
// NewFile initializes an empty file.
|
||||
func NewFile() *File {
|
||||
return &File{}
|
||||
}
|
||||
|
||||
// AddSection appends a Section to the file.
|
||||
func (f *File) AddSection(s Section) {
|
||||
f.Sections = append(f.Sections, s)
|
||||
}
|
||||
|
||||
// Functions returns all functions in the file.
|
||||
func (f *File) Functions() []*Function {
|
||||
var fns []*Function
|
||||
for _, s := range f.Sections {
|
||||
if fn, ok := s.(*Function); ok {
|
||||
fns = append(fns, fn)
|
||||
}
|
||||
}
|
||||
return fns
|
||||
}
|
||||
|
||||
// Pragma represents a function compiler directive.
|
||||
type Pragma struct {
|
||||
Directive string
|
||||
Arguments []string
|
||||
}
|
||||
|
||||
// Function represents an assembly function.
|
||||
type Function struct {
|
||||
Name string
|
||||
Attributes attr.Attribute
|
||||
Pragmas []Pragma
|
||||
Doc []string
|
||||
Signature *gotypes.Signature
|
||||
LocalSize int
|
||||
|
||||
Nodes []Node
|
||||
|
||||
// LabelTarget maps from label name to the following instruction.
|
||||
LabelTarget map[Label]*Instruction
|
||||
|
||||
// Register allocation.
|
||||
Allocation reg.Allocation
|
||||
|
||||
// ISA is the list of required instruction set extensions.
|
||||
ISA []string
|
||||
}
|
||||
|
||||
func (f *Function) section() {}
|
||||
|
||||
// NewFunction builds an empty function of the given name.
|
||||
func NewFunction(name string) *Function {
|
||||
return &Function{
|
||||
Name: name,
|
||||
Signature: gotypes.NewSignatureVoid(),
|
||||
}
|
||||
}
|
||||
|
||||
// AddPragma adds a pragma to this function.
|
||||
func (f *Function) AddPragma(directive string, args ...string) {
|
||||
f.Pragmas = append(f.Pragmas, Pragma{
|
||||
Directive: directive,
|
||||
Arguments: args,
|
||||
})
|
||||
}
|
||||
|
||||
// SetSignature sets the function signature.
|
||||
func (f *Function) SetSignature(s *gotypes.Signature) {
|
||||
f.Signature = s
|
||||
}
|
||||
|
||||
// AllocLocal allocates size bytes in this function's stack.
|
||||
// Returns a reference to the base pointer for the newly allocated region.
|
||||
func (f *Function) AllocLocal(size int) operand.Mem {
|
||||
ptr := operand.NewStackAddr(f.LocalSize)
|
||||
f.LocalSize += size
|
||||
return ptr
|
||||
}
|
||||
|
||||
// AddInstruction appends an instruction to f.
|
||||
func (f *Function) AddInstruction(i *Instruction) {
|
||||
f.AddNode(i)
|
||||
}
|
||||
|
||||
// AddLabel appends a label to f.
|
||||
func (f *Function) AddLabel(l Label) {
|
||||
f.AddNode(l)
|
||||
}
|
||||
|
||||
// AddComment adds comment lines to f.
|
||||
func (f *Function) AddComment(lines ...string) {
|
||||
f.AddNode(NewComment(lines...))
|
||||
}
|
||||
|
||||
// AddNode appends a Node to f.
|
||||
func (f *Function) AddNode(n Node) {
|
||||
f.Nodes = append(f.Nodes, n)
|
||||
}
|
||||
|
||||
// Instructions returns just the list of instruction nodes.
|
||||
func (f *Function) Instructions() []*Instruction {
|
||||
var is []*Instruction
|
||||
for _, n := range f.Nodes {
|
||||
i, ok := n.(*Instruction)
|
||||
if ok {
|
||||
is = append(is, i)
|
||||
}
|
||||
}
|
||||
return is
|
||||
}
|
||||
|
||||
// Labels returns just the list of label nodes.
|
||||
func (f *Function) Labels() []Label {
|
||||
var lbls []Label
|
||||
for _, n := range f.Nodes {
|
||||
lbl, ok := n.(Label)
|
||||
if ok {
|
||||
lbls = append(lbls, lbl)
|
||||
}
|
||||
}
|
||||
return lbls
|
||||
}
|
||||
|
||||
// Stub returns the Go function declaration.
|
||||
func (f *Function) Stub() string {
|
||||
return "func " + f.Name + f.Signature.String()
|
||||
}
|
||||
|
||||
// FrameBytes returns the size of the stack frame in bytes.
|
||||
func (f *Function) FrameBytes() int {
|
||||
return f.LocalSize
|
||||
}
|
||||
|
||||
// ArgumentBytes returns the size of the arguments in bytes.
|
||||
func (f *Function) ArgumentBytes() int {
|
||||
return f.Signature.Bytes()
|
||||
}
|
||||
|
||||
// Datum represents a data element at a particular offset of a data section.
|
||||
type Datum struct {
|
||||
Offset int
|
||||
Value operand.Constant
|
||||
}
|
||||
|
||||
// NewDatum builds a Datum from the given constant.
|
||||
func NewDatum(offset int, v operand.Constant) Datum {
|
||||
return Datum{
|
||||
Offset: offset,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Interval returns the range of bytes this datum will occupy within its section.
|
||||
func (d Datum) Interval() (int, int) {
|
||||
return d.Offset, d.Offset + d.Value.Bytes()
|
||||
}
|
||||
|
||||
// Overlaps returns whether d overlaps with other.
|
||||
func (d Datum) Overlaps(other Datum) bool {
|
||||
s, e := d.Interval()
|
||||
so, eo := other.Interval()
|
||||
return !(eo <= s || e <= so)
|
||||
}
|
||||
|
||||
// Global represents a DATA section.
|
||||
type Global struct {
|
||||
Symbol operand.Symbol
|
||||
Attributes attr.Attribute
|
||||
Data []Datum
|
||||
Size int
|
||||
}
|
||||
|
||||
// NewGlobal constructs an empty DATA section.
|
||||
func NewGlobal(sym operand.Symbol) *Global {
|
||||
return &Global{
|
||||
Symbol: sym,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStaticGlobal is a convenience for building a static DATA section.
|
||||
func NewStaticGlobal(name string) *Global {
|
||||
return NewGlobal(operand.NewStaticSymbol(name))
|
||||
}
|
||||
|
||||
func (g *Global) section() {}
|
||||
|
||||
// Base returns a pointer to the start of the data section.
|
||||
func (g *Global) Base() operand.Mem {
|
||||
return operand.NewDataAddr(g.Symbol, 0)
|
||||
}
|
||||
|
||||
// Grow ensures that the data section has at least the given size.
|
||||
func (g *Global) Grow(size int) {
|
||||
if g.Size < size {
|
||||
g.Size = size
|
||||
}
|
||||
}
|
||||
|
||||
// AddDatum adds d to this data section, growing it if necessary. Errors if the datum overlaps with existing data.
|
||||
func (g *Global) AddDatum(d Datum) error {
|
||||
for _, other := range g.Data {
|
||||
if d.Overlaps(other) {
|
||||
return errors.New("overlaps existing datum")
|
||||
}
|
||||
}
|
||||
g.add(d)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append the constant to the end of the data section.
|
||||
func (g *Global) Append(v operand.Constant) {
|
||||
g.add(Datum{
|
||||
Offset: g.Size,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Global) add(d Datum) {
|
||||
_, end := d.Interval()
|
||||
g.Grow(end)
|
||||
g.Data = append(g.Data, d)
|
||||
}
|
282
vendor/github.com/mmcloughlin/avo/operand/checks.go
generated
vendored
Normal file
282
vendor/github.com/mmcloughlin/avo/operand/checks.go
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
package operand
|
||||
|
||||
import "github.com/mmcloughlin/avo/reg"
|
||||
|
||||
// Pure type assertion checks:
|
||||
|
||||
// IsRegister returns whether op has type reg.Register.
|
||||
func IsRegister(op Op) bool { _, ok := op.(reg.Register); return ok }
|
||||
|
||||
// IsMem returns whether op has type Mem.
|
||||
func IsMem(op Op) bool { _, ok := op.(Mem); return ok }
|
||||
|
||||
// IsRel returns whether op has type Rel.
|
||||
func IsRel(op Op) bool { _, ok := op.(Rel); return ok }
|
||||
|
||||
// Checks corresponding to specific operand types in the Intel Manual:
|
||||
|
||||
// Is1 returns true if op is the immediate constant 1.
|
||||
func Is1(op Op) bool {
|
||||
i, ok := op.(U8)
|
||||
return ok && i == 1
|
||||
}
|
||||
|
||||
// Is3 returns true if op is the immediate constant 3.
|
||||
func Is3(op Op) bool {
|
||||
i, ok := op.(U8)
|
||||
return ok && i == 3
|
||||
}
|
||||
|
||||
// IsIMM2U returns true if op is a 2-bit unsigned immediate (less than 4).
|
||||
func IsIMM2U(op Op) bool {
|
||||
i, ok := op.(U8)
|
||||
return ok && i < 4
|
||||
}
|
||||
|
||||
// IsIMM8 returns true is op is an 8-bit immediate.
|
||||
func IsIMM8(op Op) bool {
|
||||
_, uok := op.(U8)
|
||||
_, iok := op.(I8)
|
||||
return uok || iok
|
||||
}
|
||||
|
||||
// IsIMM16 returns true is op is a 16-bit immediate.
|
||||
func IsIMM16(op Op) bool {
|
||||
_, uok := op.(U16)
|
||||
_, iok := op.(I16)
|
||||
return uok || iok
|
||||
}
|
||||
|
||||
// IsIMM32 returns true is op is a 32-bit immediate.
|
||||
func IsIMM32(op Op) bool {
|
||||
_, uok := op.(U32)
|
||||
_, iok := op.(I32)
|
||||
return uok || iok
|
||||
}
|
||||
|
||||
// IsIMM64 returns true is op is a 64-bit immediate.
|
||||
func IsIMM64(op Op) bool {
|
||||
_, uok := op.(U64)
|
||||
_, iok := op.(I64)
|
||||
return uok || iok
|
||||
}
|
||||
|
||||
// IsAL returns true if op is the AL register.
|
||||
func IsAL(op Op) bool {
|
||||
return op == reg.AL
|
||||
}
|
||||
|
||||
// IsCL returns true if op is the CL register.
|
||||
func IsCL(op Op) bool {
|
||||
return op == reg.CL
|
||||
}
|
||||
|
||||
// IsAX returns true if op is the 16-bit AX register.
|
||||
func IsAX(op Op) bool {
|
||||
return op == reg.AX
|
||||
}
|
||||
|
||||
// IsEAX returns true if op is the 32-bit EAX register.
|
||||
func IsEAX(op Op) bool {
|
||||
return op == reg.EAX
|
||||
}
|
||||
|
||||
// IsRAX returns true if op is the 64-bit RAX register.
|
||||
func IsRAX(op Op) bool {
|
||||
return op == reg.RAX
|
||||
}
|
||||
|
||||
// IsR8 returns true if op is an 8-bit general-purpose register.
|
||||
func IsR8(op Op) bool {
|
||||
return IsGP(op, 1)
|
||||
}
|
||||
|
||||
// IsR16 returns true if op is a 16-bit general-purpose register.
|
||||
func IsR16(op Op) bool {
|
||||
return IsGP(op, 2)
|
||||
}
|
||||
|
||||
// IsR32 returns true if op is a 32-bit general-purpose register.
|
||||
func IsR32(op Op) bool {
|
||||
return IsGP(op, 4)
|
||||
}
|
||||
|
||||
// IsR64 returns true if op is a 64-bit general-purpose register.
|
||||
func IsR64(op Op) bool {
|
||||
return IsGP(op, 8)
|
||||
}
|
||||
|
||||
// IsPseudo returns true if op is a pseudo register.
|
||||
func IsPseudo(op Op) bool {
|
||||
return IsRegisterKind(op, reg.KindPseudo)
|
||||
}
|
||||
|
||||
// IsGP returns true if op is a general-purpose register of size n bytes.
|
||||
func IsGP(op Op, n uint) bool {
|
||||
return IsRegisterKindSize(op, reg.KindGP, n)
|
||||
}
|
||||
|
||||
// IsXMM0 returns true if op is the X0 register.
|
||||
func IsXMM0(op Op) bool {
|
||||
return op == reg.X0
|
||||
}
|
||||
|
||||
// IsXMM returns true if op is a 128-bit XMM register.
|
||||
func IsXMM(op Op) bool {
|
||||
return IsRegisterKindSize(op, reg.KindVector, 16)
|
||||
}
|
||||
|
||||
// IsYMM returns true if op is a 256-bit YMM register.
|
||||
func IsYMM(op Op) bool {
|
||||
return IsRegisterKindSize(op, reg.KindVector, 32)
|
||||
}
|
||||
|
||||
// IsZMM returns true if op is a 512-bit ZMM register.
|
||||
func IsZMM(op Op) bool {
|
||||
return IsRegisterKindSize(op, reg.KindVector, 64)
|
||||
}
|
||||
|
||||
// IsK returns true if op is an Opmask register.
|
||||
func IsK(op Op) bool {
|
||||
return IsRegisterKind(op, reg.KindOpmask)
|
||||
}
|
||||
|
||||
// IsRegisterKindSize returns true if op is a register of the given kind and size in bytes.
|
||||
func IsRegisterKindSize(op Op, k reg.Kind, n uint) bool {
|
||||
r, ok := op.(reg.Register)
|
||||
return ok && r.Kind() == k && r.Size() == n
|
||||
}
|
||||
|
||||
// IsRegisterKind returns true if op is a register of the given kind.
|
||||
func IsRegisterKind(op Op, k reg.Kind) bool {
|
||||
r, ok := op.(reg.Register)
|
||||
return ok && r.Kind() == k
|
||||
}
|
||||
|
||||
// IsM returns true if op is a 16-, 32- or 64-bit memory operand.
|
||||
func IsM(op Op) bool {
|
||||
// TODO(mbm): confirm "m" check is defined correctly
|
||||
// Intel manual: "A 16-, 32- or 64-bit operand in memory."
|
||||
return IsM16(op) || IsM32(op) || IsM64(op)
|
||||
}
|
||||
|
||||
// IsM8 returns true if op is an 8-bit memory operand.
|
||||
func IsM8(op Op) bool {
|
||||
// TODO(mbm): confirm "m8" check is defined correctly
|
||||
// Intel manual: "A byte operand in memory, usually expressed as a variable or
|
||||
// array name, but pointed to by the DS:(E)SI or ES:(E)DI registers. In 64-bit
|
||||
// mode, it is pointed to by the RSI or RDI registers."
|
||||
return IsMSize(op, 1)
|
||||
}
|
||||
|
||||
// IsM16 returns true if op is a 16-bit memory operand.
|
||||
func IsM16(op Op) bool {
|
||||
return IsMSize(op, 2)
|
||||
}
|
||||
|
||||
// IsM32 returns true if op is a 16-bit memory operand.
|
||||
func IsM32(op Op) bool {
|
||||
return IsMSize(op, 4)
|
||||
}
|
||||
|
||||
// IsM64 returns true if op is a 64-bit memory operand.
|
||||
func IsM64(op Op) bool {
|
||||
return IsMSize(op, 8)
|
||||
}
|
||||
|
||||
// IsMSize returns true if op is a memory operand using general-purpose address
|
||||
// registers of the given size in bytes.
|
||||
func IsMSize(op Op, n uint) bool {
|
||||
// TODO(mbm): should memory operands have a size attribute as well?
|
||||
// TODO(mbm): m8,m16,m32,m64 checks do not actually check size
|
||||
m, ok := op.(Mem)
|
||||
return ok && IsMReg(m.Base) && (m.Index == nil || IsMReg(m.Index))
|
||||
}
|
||||
|
||||
// IsMReg returns true if op is a register that can be used in a memory operand.
|
||||
func IsMReg(op Op) bool {
|
||||
return IsPseudo(op) || IsRegisterKind(op, reg.KindGP)
|
||||
}
|
||||
|
||||
// IsM128 returns true if op is a 128-bit memory operand.
|
||||
func IsM128(op Op) bool {
|
||||
// TODO(mbm): should "m128" be the same as "m64"?
|
||||
return IsM64(op)
|
||||
}
|
||||
|
||||
// IsM256 returns true if op is a 256-bit memory operand.
|
||||
func IsM256(op Op) bool {
|
||||
// TODO(mbm): should "m256" be the same as "m64"?
|
||||
return IsM64(op)
|
||||
}
|
||||
|
||||
// IsM512 returns true if op is a 512-bit memory operand.
|
||||
func IsM512(op Op) bool {
|
||||
// TODO(mbm): should "m512" be the same as "m64"?
|
||||
return IsM64(op)
|
||||
}
|
||||
|
||||
// IsVM32X returns true if op is a vector memory operand with 32-bit XMM index.
|
||||
func IsVM32X(op Op) bool {
|
||||
return IsVmx(op)
|
||||
}
|
||||
|
||||
// IsVM64X returns true if op is a vector memory operand with 64-bit XMM index.
|
||||
func IsVM64X(op Op) bool {
|
||||
return IsVmx(op)
|
||||
}
|
||||
|
||||
// IsVmx returns true if op is a vector memory operand with XMM index.
|
||||
func IsVmx(op Op) bool {
|
||||
return isvm(op, IsXMM)
|
||||
}
|
||||
|
||||
// IsVM32Y returns true if op is a vector memory operand with 32-bit YMM index.
|
||||
func IsVM32Y(op Op) bool {
|
||||
return IsVmy(op)
|
||||
}
|
||||
|
||||
// IsVM64Y returns true if op is a vector memory operand with 64-bit YMM index.
|
||||
func IsVM64Y(op Op) bool {
|
||||
return IsVmy(op)
|
||||
}
|
||||
|
||||
// IsVmy returns true if op is a vector memory operand with YMM index.
|
||||
func IsVmy(op Op) bool {
|
||||
return isvm(op, IsYMM)
|
||||
}
|
||||
|
||||
// IsVM32Z returns true if op is a vector memory operand with 32-bit ZMM index.
|
||||
func IsVM32Z(op Op) bool {
|
||||
return IsVmz(op)
|
||||
}
|
||||
|
||||
// IsVM64Z returns true if op is a vector memory operand with 64-bit ZMM index.
|
||||
func IsVM64Z(op Op) bool {
|
||||
return IsVmz(op)
|
||||
}
|
||||
|
||||
// IsVmz returns true if op is a vector memory operand with ZMM index.
|
||||
func IsVmz(op Op) bool {
|
||||
return isvm(op, IsZMM)
|
||||
}
|
||||
|
||||
func isvm(op Op, idx func(Op) bool) bool {
|
||||
m, ok := op.(Mem)
|
||||
return ok && IsR64(m.Base) && idx(m.Index)
|
||||
}
|
||||
|
||||
// IsREL8 returns true if op is an 8-bit offset relative to instruction pointer.
|
||||
func IsREL8(op Op) bool {
|
||||
r, ok := op.(Rel)
|
||||
return ok && r == Rel(int8(r))
|
||||
}
|
||||
|
||||
// IsREL32 returns true if op is an offset relative to instruction pointer, or a
|
||||
// label reference.
|
||||
func IsREL32(op Op) bool {
|
||||
// TODO(mbm): should labels be considered separately?
|
||||
_, rel := op.(Rel)
|
||||
_, label := op.(LabelRef)
|
||||
return rel || label
|
||||
}
|
64
vendor/github.com/mmcloughlin/avo/operand/const.go
generated
vendored
Normal file
64
vendor/github.com/mmcloughlin/avo/operand/const.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package operand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constant represents a constant literal.
|
||||
type Constant interface {
|
||||
Op
|
||||
Bytes() int
|
||||
constant()
|
||||
}
|
||||
|
||||
//go:generate go run make_const.go -output zconst.go
|
||||
|
||||
// Special cases for floating point string representation.
|
||||
//
|
||||
// Issue 387 pointed out that floating point values that happen to be integers
|
||||
// need to have a decimal point to be parsed correctly.
|
||||
|
||||
// String returns a representation the 32-bit float which is guaranteed to be
|
||||
// parsed as a floating point constant by the Go assembler.
|
||||
func (f F32) String() string { return asmfloat(float64(f), 32) }
|
||||
|
||||
// String returns a representation the 64-bit float which is guaranteed to be
|
||||
// parsed as a floating point constant by the Go assembler.
|
||||
func (f F64) String() string { return asmfloat(float64(f), 64) }
|
||||
|
||||
// asmfloat represents x as a string such that the assembler scanner will always
|
||||
// recognize it as a float. Specifically, ensure that when x is an integral
|
||||
// value, the result will still have a decimal point.
|
||||
func asmfloat(x float64, bits int) string {
|
||||
s := strconv.FormatFloat(x, 'f', -1, bits)
|
||||
if !strings.ContainsRune(s, '.') {
|
||||
s += ".0"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// String is a string constant.
|
||||
type String string
|
||||
|
||||
// Asm returns an assembly syntax representation of the string s.
|
||||
func (s String) Asm() string { return fmt.Sprintf("$%q", s) }
|
||||
|
||||
// Bytes returns the length of s.
|
||||
func (s String) Bytes() int { return len(s) }
|
||||
|
||||
func (s String) constant() {}
|
||||
|
||||
// Imm returns an unsigned integer constant with size guessed from x.
|
||||
func Imm(x uint64) Constant {
|
||||
switch {
|
||||
case uint64(uint8(x)) == x:
|
||||
return U8(x)
|
||||
case uint64(uint16(x)) == x:
|
||||
return U16(x)
|
||||
case uint64(uint32(x)) == x:
|
||||
return U32(x)
|
||||
}
|
||||
return U64(x)
|
||||
}
|
2
vendor/github.com/mmcloughlin/avo/operand/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/operand/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package operand provides types for instruction operands.
|
||||
package operand
|
151
vendor/github.com/mmcloughlin/avo/operand/types.go
generated
vendored
Normal file
151
vendor/github.com/mmcloughlin/avo/operand/types.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package operand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// Op is an operand.
|
||||
type Op interface {
|
||||
Asm() string
|
||||
}
|
||||
|
||||
// Symbol represents a symbol name.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Static bool // only visible in current source file
|
||||
}
|
||||
|
||||
// NewStaticSymbol builds a static Symbol. Static symbols are only visible in the current source file.
|
||||
func NewStaticSymbol(name string) Symbol {
|
||||
return Symbol{Name: name, Static: true}
|
||||
}
|
||||
|
||||
func (s Symbol) String() string {
|
||||
n := s.Name
|
||||
if s.Static {
|
||||
n += "<>"
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Mem represents a memory reference.
|
||||
type Mem struct {
|
||||
Symbol Symbol
|
||||
Disp int
|
||||
Base reg.Register
|
||||
Index reg.Register
|
||||
Scale uint8
|
||||
}
|
||||
|
||||
// NewParamAddr is a convenience to build a Mem operand pointing to a function
|
||||
// parameter, which is a named offset from the frame pointer pseudo register.
|
||||
func NewParamAddr(name string, offset int) Mem {
|
||||
return Mem{
|
||||
Symbol: Symbol{
|
||||
Name: name,
|
||||
Static: false,
|
||||
},
|
||||
Disp: offset,
|
||||
Base: reg.FramePointer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStackAddr returns a memory reference relative to the stack pointer.
|
||||
func NewStackAddr(offset int) Mem {
|
||||
return Mem{
|
||||
Disp: offset,
|
||||
Base: reg.StackPointer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDataAddr returns a memory reference relative to the named data symbol.
|
||||
func NewDataAddr(sym Symbol, offset int) Mem {
|
||||
return Mem{
|
||||
Symbol: sym,
|
||||
Disp: offset,
|
||||
Base: reg.StaticBase,
|
||||
}
|
||||
}
|
||||
|
||||
// Offset returns a reference to m plus idx bytes.
|
||||
func (m Mem) Offset(idx int) Mem {
|
||||
a := m
|
||||
a.Disp += idx
|
||||
return a
|
||||
}
|
||||
|
||||
// Idx returns a new memory reference with (Index, Scale) set to (r, s).
|
||||
func (m Mem) Idx(r reg.Register, s uint8) Mem {
|
||||
a := m
|
||||
a.Index = r
|
||||
a.Scale = s
|
||||
return a
|
||||
}
|
||||
|
||||
// Asm returns an assembly syntax representation of m.
|
||||
func (m Mem) Asm() string {
|
||||
a := m.Symbol.String()
|
||||
if a != "" {
|
||||
a += fmt.Sprintf("%+d", m.Disp)
|
||||
} else if m.Disp != 0 {
|
||||
a += fmt.Sprintf("%d", m.Disp)
|
||||
}
|
||||
if m.Base != nil {
|
||||
a += fmt.Sprintf("(%s)", m.Base.Asm())
|
||||
}
|
||||
if m.Index != nil && m.Scale != 0 {
|
||||
a += fmt.Sprintf("(%s*%d)", m.Index.Asm(), m.Scale)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Rel is an offset relative to the instruction pointer.
|
||||
type Rel int32
|
||||
|
||||
// Asm returns an assembly syntax representation of r.
|
||||
func (r Rel) Asm() string {
|
||||
return fmt.Sprintf(".%+d", r)
|
||||
}
|
||||
|
||||
// LabelRef is a reference to a label.
|
||||
type LabelRef string
|
||||
|
||||
// Asm returns an assembly syntax representation of l.
|
||||
func (l LabelRef) Asm() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
// Registers returns the list of all operands involved in the given operand.
|
||||
func Registers(op Op) []reg.Register {
|
||||
switch op := op.(type) {
|
||||
case reg.Register:
|
||||
return []reg.Register{op}
|
||||
case Mem:
|
||||
var r []reg.Register
|
||||
if op.Base != nil {
|
||||
r = append(r, op.Base)
|
||||
}
|
||||
if op.Index != nil {
|
||||
r = append(r, op.Index)
|
||||
}
|
||||
return r
|
||||
case Constant, Rel, LabelRef:
|
||||
return nil
|
||||
}
|
||||
panic("unknown operand type")
|
||||
}
|
||||
|
||||
// ApplyAllocation returns an operand with allocated registers replaced. Registers missing from the allocation are left alone.
|
||||
func ApplyAllocation(op Op, a reg.Allocation) Op {
|
||||
switch op := op.(type) {
|
||||
case reg.Register:
|
||||
return a.LookupRegisterDefault(op)
|
||||
case Mem:
|
||||
op.Base = a.LookupRegisterDefault(op.Base)
|
||||
op.Index = a.LookupRegisterDefault(op.Index)
|
||||
return op
|
||||
}
|
||||
return op
|
||||
}
|
75
vendor/github.com/mmcloughlin/avo/operand/zconst.go
generated
vendored
Normal file
75
vendor/github.com/mmcloughlin/avo/operand/zconst.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by make_const.go. DO NOT EDIT.
|
||||
|
||||
package operand
|
||||
|
||||
import "fmt"
|
||||
|
||||
// I8 is a 8-bit signed integer constant.
|
||||
type I8 int8
|
||||
|
||||
func (i I8) Asm() string { return fmt.Sprintf("$%+d", i) }
|
||||
func (i I8) Bytes() int { return 1 }
|
||||
func (i I8) constant() {}
|
||||
|
||||
// U8 is a 8-bit unsigned integer constant.
|
||||
type U8 uint8
|
||||
|
||||
func (u U8) Asm() string { return fmt.Sprintf("$%#02x", u) }
|
||||
func (u U8) Bytes() int { return 1 }
|
||||
func (u U8) constant() {}
|
||||
|
||||
// I16 is a 16-bit signed integer constant.
|
||||
type I16 int16
|
||||
|
||||
func (i I16) Asm() string { return fmt.Sprintf("$%+d", i) }
|
||||
func (i I16) Bytes() int { return 2 }
|
||||
func (i I16) constant() {}
|
||||
|
||||
// U16 is a 16-bit unsigned integer constant.
|
||||
type U16 uint16
|
||||
|
||||
func (u U16) Asm() string { return fmt.Sprintf("$%#04x", u) }
|
||||
func (u U16) Bytes() int { return 2 }
|
||||
func (u U16) constant() {}
|
||||
|
||||
// F32 is a 32-bit floating point constant.
|
||||
type F32 float32
|
||||
|
||||
func (f F32) Asm() string { return fmt.Sprintf("$(%s)", f) }
|
||||
func (f F32) Bytes() int { return 4 }
|
||||
func (f F32) constant() {}
|
||||
|
||||
// I32 is a 32-bit signed integer constant.
|
||||
type I32 int32
|
||||
|
||||
func (i I32) Asm() string { return fmt.Sprintf("$%+d", i) }
|
||||
func (i I32) Bytes() int { return 4 }
|
||||
func (i I32) constant() {}
|
||||
|
||||
// U32 is a 32-bit unsigned integer constant.
|
||||
type U32 uint32
|
||||
|
||||
func (u U32) Asm() string { return fmt.Sprintf("$%#08x", u) }
|
||||
func (u U32) Bytes() int { return 4 }
|
||||
func (u U32) constant() {}
|
||||
|
||||
// F64 is a 64-bit floating point constant.
|
||||
type F64 float64
|
||||
|
||||
func (f F64) Asm() string { return fmt.Sprintf("$(%s)", f) }
|
||||
func (f F64) Bytes() int { return 8 }
|
||||
func (f F64) constant() {}
|
||||
|
||||
// I64 is a 64-bit signed integer constant.
|
||||
type I64 int64
|
||||
|
||||
func (i I64) Asm() string { return fmt.Sprintf("$%+d", i) }
|
||||
func (i I64) Bytes() int { return 8 }
|
||||
func (i I64) constant() {}
|
||||
|
||||
// U64 is a 64-bit unsigned integer constant.
|
||||
type U64 uint64
|
||||
|
||||
func (u U64) Asm() string { return fmt.Sprintf("$%#016x", u) }
|
||||
func (u U64) Bytes() int { return 8 }
|
||||
func (u U64) constant() {}
|
213
vendor/github.com/mmcloughlin/avo/pass/alloc.go
generated
vendored
Normal file
213
vendor/github.com/mmcloughlin/avo/pass/alloc.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// edge is an edge of the interference graph, indicating that registers X and Y
|
||||
// must be in non-conflicting registers.
|
||||
type edge struct {
|
||||
X, Y reg.ID
|
||||
}
|
||||
|
||||
// Allocator is a graph-coloring register allocator.
|
||||
type Allocator struct {
|
||||
registers []reg.ID
|
||||
priority map[reg.ID]int
|
||||
allocation reg.Allocation
|
||||
edges []*edge
|
||||
possible map[reg.ID][]reg.ID
|
||||
}
|
||||
|
||||
// NewAllocator builds an allocator for the given physical registers.
|
||||
func NewAllocator(rs []reg.Physical) (*Allocator, error) {
|
||||
// Set of IDs, excluding restricted registers.
|
||||
idset := map[reg.ID]bool{}
|
||||
for _, r := range rs {
|
||||
if (r.Info() & reg.Restricted) != 0 {
|
||||
continue
|
||||
}
|
||||
idset[r.ID()] = true
|
||||
}
|
||||
|
||||
if len(idset) == 0 {
|
||||
return nil, errors.New("no allocatable registers")
|
||||
}
|
||||
|
||||
// Produce slice of unique register IDs.
|
||||
var ids []reg.ID
|
||||
for id := range idset {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
a := &Allocator{
|
||||
registers: ids,
|
||||
priority: map[reg.ID]int{},
|
||||
allocation: reg.NewEmptyAllocation(),
|
||||
possible: map[reg.ID][]reg.ID{},
|
||||
}
|
||||
a.sortregisters()
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// NewAllocatorForKind builds an allocator for the given kind of registers.
|
||||
func NewAllocatorForKind(k reg.Kind) (*Allocator, error) {
|
||||
f := reg.FamilyOfKind(k)
|
||||
if f == nil {
|
||||
return nil, errors.New("unknown register family")
|
||||
}
|
||||
return NewAllocator(f.Registers())
|
||||
}
|
||||
|
||||
// SetPriority sets the priority of the given regiser to p. Higher priority
|
||||
// registers are preferred in allocations. By default all registers have 0
|
||||
// priority. Priority will only apply to subsequent Add() calls, therefore
|
||||
// typically all SetPriority calls should happen at allocator initialization.
|
||||
func (a *Allocator) SetPriority(id reg.ID, p int) {
|
||||
a.priority[id] = p
|
||||
a.sortregisters()
|
||||
}
|
||||
|
||||
// sortregisters sorts the list of available registers: higher priority first,
|
||||
// falling back to sorting by ID.
|
||||
func (a *Allocator) sortregisters() {
|
||||
sort.Slice(a.registers, func(i, j int) bool {
|
||||
ri, rj := a.registers[i], a.registers[j]
|
||||
pi, pj := a.priority[ri], a.priority[rj]
|
||||
return (pi > pj) || (pi == pj && ri < rj)
|
||||
})
|
||||
}
|
||||
|
||||
// AddInterferenceSet records that r interferes with every register in s. Convenience wrapper around AddInterference.
|
||||
func (a *Allocator) AddInterferenceSet(r reg.Register, s reg.MaskSet) {
|
||||
for id, mask := range s {
|
||||
if (r.Mask() & mask) != 0 {
|
||||
a.AddInterference(r.ID(), id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddInterference records that x and y must be assigned to non-conflicting physical registers.
|
||||
func (a *Allocator) AddInterference(x, y reg.ID) {
|
||||
a.Add(x)
|
||||
a.Add(y)
|
||||
a.edges = append(a.edges, &edge{X: x, Y: y})
|
||||
}
|
||||
|
||||
// Add adds a register to be allocated. Does nothing if the register has already been added.
|
||||
func (a *Allocator) Add(v reg.ID) {
|
||||
if !v.IsVirtual() {
|
||||
return
|
||||
}
|
||||
if _, found := a.possible[v]; found {
|
||||
return
|
||||
}
|
||||
a.possible[v] = a.possibleregisters(v)
|
||||
}
|
||||
|
||||
// Allocate allocates physical registers.
|
||||
func (a *Allocator) Allocate() (reg.Allocation, error) {
|
||||
for {
|
||||
if err := a.update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if a.remaining() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
v := a.mostrestricted()
|
||||
if err := a.alloc(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return a.allocation, nil
|
||||
}
|
||||
|
||||
// update possible allocations based on edges.
|
||||
func (a *Allocator) update() error {
|
||||
var rem []*edge
|
||||
for _, e := range a.edges {
|
||||
x := a.allocation.LookupDefault(e.X)
|
||||
y := a.allocation.LookupDefault(e.Y)
|
||||
switch {
|
||||
case x.IsVirtual() && y.IsVirtual():
|
||||
rem = append(rem, e)
|
||||
continue
|
||||
case x.IsPhysical() && y.IsPhysical():
|
||||
if x == y {
|
||||
return errors.New("impossible register allocation")
|
||||
}
|
||||
case x.IsPhysical() && y.IsVirtual():
|
||||
a.discardconflicting(y, x)
|
||||
case x.IsVirtual() && y.IsPhysical():
|
||||
a.discardconflicting(x, y)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
a.edges = rem
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mostrestricted returns the virtual register with the least possibilities.
|
||||
func (a *Allocator) mostrestricted() reg.ID {
|
||||
n := int(math.MaxInt32)
|
||||
var v reg.ID
|
||||
for w, p := range a.possible {
|
||||
// On a tie, choose the smallest ID in numeric order. This avoids
|
||||
// non-deterministic allocations due to map iteration order.
|
||||
if len(p) < n || (len(p) == n && w < v) {
|
||||
n = len(p)
|
||||
v = w
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// discardconflicting removes registers from vs possible list that conflict with p.
|
||||
func (a *Allocator) discardconflicting(v, p reg.ID) {
|
||||
a.possible[v] = filterregisters(a.possible[v], func(r reg.ID) bool {
|
||||
return r != p
|
||||
})
|
||||
}
|
||||
|
||||
// alloc attempts to allocate a register to v.
|
||||
func (a *Allocator) alloc(v reg.ID) error {
|
||||
ps := a.possible[v]
|
||||
if len(ps) == 0 {
|
||||
return errors.New("failed to allocate registers")
|
||||
}
|
||||
p := ps[0]
|
||||
a.allocation[v] = p
|
||||
delete(a.possible, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// remaining returns the number of unallocated registers.
|
||||
func (a *Allocator) remaining() int {
|
||||
return len(a.possible)
|
||||
}
|
||||
|
||||
// possibleregisters returns all allocate-able registers for the given virtual.
|
||||
func (a *Allocator) possibleregisters(v reg.ID) []reg.ID {
|
||||
return filterregisters(a.registers, func(r reg.ID) bool {
|
||||
return v.Kind() == r.Kind()
|
||||
})
|
||||
}
|
||||
|
||||
func filterregisters(in []reg.ID, predicate func(reg.ID) bool) []reg.ID {
|
||||
var rs []reg.ID
|
||||
for _, r := range in {
|
||||
if predicate(r) {
|
||||
rs = append(rs, r)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
81
vendor/github.com/mmcloughlin/avo/pass/cfg.go
generated
vendored
Normal file
81
vendor/github.com/mmcloughlin/avo/pass/cfg.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
)
|
||||
|
||||
// LabelTarget populates the LabelTarget of the given function. This maps from
|
||||
// label name to the following instruction.
|
||||
func LabelTarget(fn *ir.Function) error {
|
||||
target := map[ir.Label]*ir.Instruction{}
|
||||
var pending []ir.Label
|
||||
for _, node := range fn.Nodes {
|
||||
switch n := node.(type) {
|
||||
case ir.Label:
|
||||
if _, found := target[n]; found {
|
||||
return fmt.Errorf("duplicate label \"%s\"", n)
|
||||
}
|
||||
pending = append(pending, n)
|
||||
case *ir.Instruction:
|
||||
for _, label := range pending {
|
||||
target[label] = n
|
||||
}
|
||||
pending = nil
|
||||
}
|
||||
}
|
||||
if len(pending) != 0 {
|
||||
return errors.New("function ends with label")
|
||||
}
|
||||
fn.LabelTarget = target
|
||||
return nil
|
||||
}
|
||||
|
||||
// CFG constructs the call-flow-graph for the function.
|
||||
func CFG(fn *ir.Function) error {
|
||||
is := fn.Instructions()
|
||||
n := len(is)
|
||||
|
||||
// Populate successors.
|
||||
for i := 0; i < n; i++ {
|
||||
cur := is[i]
|
||||
var nxt *ir.Instruction
|
||||
if i+1 < n {
|
||||
nxt = is[i+1]
|
||||
}
|
||||
|
||||
// If it's a branch, locate the target.
|
||||
if cur.IsBranch {
|
||||
lbl := cur.TargetLabel()
|
||||
if lbl == nil {
|
||||
return errors.New("no label for branch instruction")
|
||||
}
|
||||
target, found := fn.LabelTarget[*lbl]
|
||||
if !found {
|
||||
return fmt.Errorf("unknown label %q", *lbl)
|
||||
}
|
||||
cur.Succ = append(cur.Succ, target)
|
||||
}
|
||||
|
||||
// Otherwise, could continue to the following instruction.
|
||||
switch {
|
||||
case cur.IsTerminal:
|
||||
case cur.IsUnconditionalBranch():
|
||||
default:
|
||||
cur.Succ = append(cur.Succ, nxt)
|
||||
}
|
||||
}
|
||||
|
||||
// Populate predecessors.
|
||||
for _, i := range is {
|
||||
for _, s := range i.Succ {
|
||||
if s != nil {
|
||||
s.Pred = append(s.Pred, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
123
vendor/github.com/mmcloughlin/avo/pass/cleanup.go
generated
vendored
Normal file
123
vendor/github.com/mmcloughlin/avo/pass/cleanup.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
// PruneJumpToFollowingLabel removes jump instructions that target an
|
||||
// immediately following label.
|
||||
func PruneJumpToFollowingLabel(fn *ir.Function) error {
|
||||
for i := 0; i+1 < len(fn.Nodes); i++ {
|
||||
node := fn.Nodes[i]
|
||||
next := fn.Nodes[i+1]
|
||||
|
||||
// This node is an unconditional jump.
|
||||
inst, ok := node.(*ir.Instruction)
|
||||
if !ok || !inst.IsBranch || inst.IsConditional {
|
||||
continue
|
||||
}
|
||||
|
||||
target := inst.TargetLabel()
|
||||
if target == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// And the jump target is the immediately following node.
|
||||
lbl, ok := next.(ir.Label)
|
||||
if !ok || lbl != *target {
|
||||
continue
|
||||
}
|
||||
|
||||
// Then the jump is unnecessary and can be removed.
|
||||
fn.Nodes = deletenode(fn.Nodes, i)
|
||||
i--
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PruneDanglingLabels removes labels that are not referenced by any branches.
|
||||
func PruneDanglingLabels(fn *ir.Function) error {
|
||||
// Count label references.
|
||||
count := map[ir.Label]int{}
|
||||
for _, n := range fn.Nodes {
|
||||
i, ok := n.(*ir.Instruction)
|
||||
if !ok || !i.IsBranch {
|
||||
continue
|
||||
}
|
||||
|
||||
target := i.TargetLabel()
|
||||
if target == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
count[*target]++
|
||||
}
|
||||
|
||||
// Look for labels with no references.
|
||||
for i := 0; i < len(fn.Nodes); i++ {
|
||||
node := fn.Nodes[i]
|
||||
lbl, ok := node.(ir.Label)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if count[lbl] == 0 {
|
||||
fn.Nodes = deletenode(fn.Nodes, i)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PruneSelfMoves removes move instructions from one register to itself.
|
||||
func PruneSelfMoves(fn *ir.Function) error {
|
||||
return removeinstructions(fn, func(i *ir.Instruction) bool {
|
||||
switch i.Opcode {
|
||||
case "MOVB", "MOVW", "MOVL", "MOVQ":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return operand.IsRegister(i.Operands[0]) && operand.IsRegister(i.Operands[1]) && i.Operands[0] == i.Operands[1]
|
||||
})
|
||||
}
|
||||
|
||||
// removeinstructions deletes instructions from the given function which match predicate.
|
||||
func removeinstructions(fn *ir.Function, predicate func(*ir.Instruction) bool) error {
|
||||
// Removal of instructions has the potential to invalidate CFG structures.
|
||||
// Clear them to prevent accidental use of stale structures after this pass.
|
||||
invalidatecfg(fn)
|
||||
|
||||
for i := 0; i < len(fn.Nodes); i++ {
|
||||
n := fn.Nodes[i]
|
||||
|
||||
inst, ok := n.(*ir.Instruction)
|
||||
if !ok || !predicate(inst) {
|
||||
continue
|
||||
}
|
||||
|
||||
fn.Nodes = deletenode(fn.Nodes, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deletenode deletes node i from nodes and returns the resulting slice.
|
||||
func deletenode(nodes []ir.Node, i int) []ir.Node {
|
||||
n := len(nodes)
|
||||
copy(nodes[i:], nodes[i+1:])
|
||||
nodes[n-1] = nil
|
||||
return nodes[:n-1]
|
||||
}
|
||||
|
||||
// invalidatecfg clears CFG structures.
|
||||
func invalidatecfg(fn *ir.Function) {
|
||||
fn.LabelTarget = nil
|
||||
for _, i := range fn.Instructions() {
|
||||
i.Pred = nil
|
||||
i.Succ = nil
|
||||
}
|
||||
}
|
31
vendor/github.com/mmcloughlin/avo/pass/isa.go
generated
vendored
Normal file
31
vendor/github.com/mmcloughlin/avo/pass/isa.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
)
|
||||
|
||||
// RequiredISAExtensions determines ISA extensions required for the given
|
||||
// function. Populates the ISA field.
|
||||
func RequiredISAExtensions(fn *ir.Function) error {
|
||||
// Collect ISA set.
|
||||
set := map[string]bool{}
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, isa := range i.ISA {
|
||||
set[isa] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(set) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Populate the function's ISA field with the unique sorted list.
|
||||
for isa := range set {
|
||||
fn.ISA = append(fn.ISA, isa)
|
||||
}
|
||||
sort.Strings(fn.ISA)
|
||||
|
||||
return nil
|
||||
}
|
101
vendor/github.com/mmcloughlin/avo/pass/pass.go
generated
vendored
Normal file
101
vendor/github.com/mmcloughlin/avo/pass/pass.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// Package pass implements processing passes on avo Files.
|
||||
package pass
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/printer"
|
||||
)
|
||||
|
||||
// Compile pass compiles an avo file. Upon successful completion the avo file
|
||||
// may be printed to Go assembly.
|
||||
var Compile = Concat(
|
||||
Verify,
|
||||
FunctionPass(PruneJumpToFollowingLabel),
|
||||
FunctionPass(PruneDanglingLabels),
|
||||
FunctionPass(LabelTarget),
|
||||
FunctionPass(CFG),
|
||||
InstructionPass(ZeroExtend32BitOutputs),
|
||||
FunctionPass(Liveness),
|
||||
FunctionPass(AllocateRegisters),
|
||||
FunctionPass(BindRegisters),
|
||||
FunctionPass(VerifyAllocation),
|
||||
FunctionPass(EnsureBasePointerCalleeSaved),
|
||||
Func(IncludeTextFlagHeader),
|
||||
FunctionPass(PruneSelfMoves),
|
||||
FunctionPass(RequiredISAExtensions),
|
||||
)
|
||||
|
||||
// Interface for a processing pass.
|
||||
type Interface interface {
|
||||
Execute(*ir.File) error
|
||||
}
|
||||
|
||||
// Func adapts a function to the pass Interface.
|
||||
type Func func(*ir.File) error
|
||||
|
||||
// Execute calls p.
|
||||
func (p Func) Execute(f *ir.File) error {
|
||||
return p(f)
|
||||
}
|
||||
|
||||
// FunctionPass is a convenience for implementing a full file pass with a
|
||||
// function that operates on each avo Function independently.
|
||||
type FunctionPass func(*ir.Function) error
|
||||
|
||||
// Execute calls p on every function in the file. Exits on the first error.
|
||||
func (p FunctionPass) Execute(f *ir.File) error {
|
||||
for _, fn := range f.Functions() {
|
||||
if err := p(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstructionPass is a convenience for implementing a full file pass with a
|
||||
// function that operates on each Instruction independently.
|
||||
type InstructionPass func(*ir.Instruction) error
|
||||
|
||||
// Execute calls p on every instruction in the file. Exits on the first error.
|
||||
func (p InstructionPass) Execute(f *ir.File) error {
|
||||
for _, fn := range f.Functions() {
|
||||
for _, i := range fn.Instructions() {
|
||||
if err := p(i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Concat returns a pass that executes the given passes in order, stopping on the first error.
|
||||
func Concat(passes ...Interface) Interface {
|
||||
return Func(func(f *ir.File) error {
|
||||
for _, p := range passes {
|
||||
if err := p.Execute(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Output pass prints a file.
|
||||
type Output struct {
|
||||
Writer io.WriteCloser
|
||||
Printer printer.Printer
|
||||
}
|
||||
|
||||
// Execute prints f with the configured Printer and writes output to Writer.
|
||||
func (o *Output) Execute(f *ir.File) error {
|
||||
b, err := o.Printer.Print(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = o.Writer.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Writer.Close()
|
||||
}
|
223
vendor/github.com/mmcloughlin/avo/pass/reg.go
generated
vendored
Normal file
223
vendor/github.com/mmcloughlin/avo/pass/reg.go
generated
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mmcloughlin/avo/gotypes"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
"github.com/mmcloughlin/avo/reg"
|
||||
)
|
||||
|
||||
// ZeroExtend32BitOutputs applies the rule that "32-bit operands generate a
|
||||
// 32-bit result, zero-extended to a 64-bit result in the destination
|
||||
// general-purpose register" (Intel Software Developer’s Manual, Volume 1,
|
||||
// 3.4.1.1).
|
||||
func ZeroExtend32BitOutputs(i *ir.Instruction) error {
|
||||
for j, op := range i.Outputs {
|
||||
if !operand.IsR32(op) {
|
||||
continue
|
||||
}
|
||||
r, ok := op.(reg.GP)
|
||||
if !ok {
|
||||
panic("r32 operand should satisfy reg.GP")
|
||||
}
|
||||
i.Outputs[j] = r.As64()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Liveness computes register liveness.
|
||||
func Liveness(fn *ir.Function) error {
|
||||
// Note this implementation is initially naive so as to be "obviously correct".
|
||||
// There are a well-known optimizations we can apply if necessary.
|
||||
|
||||
is := fn.Instructions()
|
||||
|
||||
// Process instructions in reverse: poor approximation to topological sort.
|
||||
// TODO(mbm): process instructions in topological sort order
|
||||
for l, r := 0, len(is)-1; l < r; l, r = l+1, r-1 {
|
||||
is[l], is[r] = is[r], is[l]
|
||||
}
|
||||
|
||||
// Initialize.
|
||||
for _, i := range is {
|
||||
i.LiveIn = reg.NewMaskSetFromRegisters(i.InputRegisters())
|
||||
i.LiveOut = reg.NewEmptyMaskSet()
|
||||
}
|
||||
|
||||
// Iterative dataflow analysis.
|
||||
for {
|
||||
changes := false
|
||||
|
||||
for _, i := range is {
|
||||
// out[n] = UNION[s IN succ[n]] in[s]
|
||||
for _, s := range i.Succ {
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
changes = i.LiveOut.Update(s.LiveIn) || changes
|
||||
}
|
||||
|
||||
// in[n] = use[n] UNION (out[n] - def[n])
|
||||
def := reg.NewMaskSetFromRegisters(i.OutputRegisters())
|
||||
changes = i.LiveIn.Update(i.LiveOut.Difference(def)) || changes
|
||||
}
|
||||
|
||||
if !changes {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllocateRegisters performs register allocation.
|
||||
func AllocateRegisters(fn *ir.Function) error {
|
||||
// Initialize one allocator per kind.
|
||||
as := map[reg.Kind]*Allocator{}
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.Registers() {
|
||||
k := r.Kind()
|
||||
if _, found := as[k]; !found {
|
||||
a, err := NewAllocatorForKind(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
as[k] = a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// De-prioritize the base pointer register. This can be used as a general
|
||||
// purpose register, but it's callee-save so needs to be saved/restored if
|
||||
// it is clobbered. For this reason we prefer to avoid using it unless
|
||||
// forced to by register pressure.
|
||||
for k, a := range as {
|
||||
f := reg.FamilyOfKind(k)
|
||||
for _, r := range f.Registers() {
|
||||
if (r.Info() & reg.BasePointer) != 0 {
|
||||
// Negative priority penalizes this register relative to all
|
||||
// others (having default zero priority).
|
||||
a.SetPriority(r.ID(), -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate registers to be allocated.
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.Registers() {
|
||||
as[r.Kind()].Add(r.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// Record register interferences.
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, d := range i.OutputRegisters() {
|
||||
k := d.Kind()
|
||||
out := i.LiveOut.OfKind(k)
|
||||
out.DiscardRegister(d)
|
||||
as[k].AddInterferenceSet(d, out)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute register allocation.
|
||||
fn.Allocation = reg.NewEmptyAllocation()
|
||||
for _, a := range as {
|
||||
al, err := a.Allocate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fn.Allocation.Merge(al); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindRegisters applies the result of register allocation, replacing all virtual registers with their assigned physical registers.
|
||||
func BindRegisters(fn *ir.Function) error {
|
||||
for _, i := range fn.Instructions() {
|
||||
for idx := range i.Operands {
|
||||
i.Operands[idx] = operand.ApplyAllocation(i.Operands[idx], fn.Allocation)
|
||||
}
|
||||
for idx := range i.Inputs {
|
||||
i.Inputs[idx] = operand.ApplyAllocation(i.Inputs[idx], fn.Allocation)
|
||||
}
|
||||
for idx := range i.Outputs {
|
||||
i.Outputs[idx] = operand.ApplyAllocation(i.Outputs[idx], fn.Allocation)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyAllocation performs sanity checks following register allocation.
|
||||
func VerifyAllocation(fn *ir.Function) error {
|
||||
// All registers should be physical.
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.Registers() {
|
||||
if reg.ToPhysical(r) == nil {
|
||||
return errors.New("non physical register found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnsureBasePointerCalleeSaved ensures that the base pointer register will be
|
||||
// saved and restored if it has been clobbered by the function.
|
||||
func EnsureBasePointerCalleeSaved(fn *ir.Function) error {
|
||||
// Check to see if the base pointer is written to.
|
||||
clobbered := false
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.OutputRegisters() {
|
||||
if p := reg.ToPhysical(r); p != nil && (p.Info()®.BasePointer) != 0 {
|
||||
clobbered = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !clobbered {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function clobbers the base pointer register so we need to ensure it
|
||||
// will be saved and restored. The Go assembler will do this automatically,
|
||||
// with a few exceptions detailed below. In summary, we can usually ensure
|
||||
// this happens by ensuring the function is not frameless (apart from
|
||||
// NOFRAME functions).
|
||||
//
|
||||
// Reference: https://github.com/golang/go/blob/3f4977bd5800beca059defb5de4dc64cd758cbb9/src/cmd/internal/obj/x86/obj6.go#L591-L609
|
||||
//
|
||||
// var bpsize int
|
||||
// if ctxt.Arch.Family == sys.AMD64 &&
|
||||
// !p.From.Sym.NoFrame() && // (1) below
|
||||
// !(autoffset == 0 && p.From.Sym.NoSplit()) && // (2) below
|
||||
// !(autoffset == 0 && !hasCall) { // (3) below
|
||||
// // Make room to save a base pointer.
|
||||
// // There are 2 cases we must avoid:
|
||||
// // 1) If noframe is set (which we do for functions which tail call).
|
||||
// // 2) Scary runtime internals which would be all messed up by frame pointers.
|
||||
// // We detect these using a heuristic: frameless nosplit functions.
|
||||
// // TODO: Maybe someday we label them all with NOFRAME and get rid of this heuristic.
|
||||
// // For performance, we also want to avoid:
|
||||
// // 3) Frameless leaf functions
|
||||
// bpsize = ctxt.Arch.PtrSize
|
||||
// autoffset += int32(bpsize)
|
||||
// p.To.Offset += int64(bpsize)
|
||||
// } else {
|
||||
// bpsize = 0
|
||||
// }
|
||||
//
|
||||
if fn.Attributes.NOFRAME() {
|
||||
return errors.New("NOFRAME function clobbers base pointer register")
|
||||
}
|
||||
|
||||
if fn.LocalSize == 0 {
|
||||
fn.AllocLocal(int(gotypes.PointerSize))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
42
vendor/github.com/mmcloughlin/avo/pass/textflag.go
generated
vendored
Normal file
42
vendor/github.com/mmcloughlin/avo/pass/textflag.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"github.com/mmcloughlin/avo/attr"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
)
|
||||
|
||||
// IncludeTextFlagHeader includes textflag.h if necessary.
|
||||
func IncludeTextFlagHeader(f *ir.File) error {
|
||||
const textflagheader = "textflag.h"
|
||||
|
||||
// Check if we already have it.
|
||||
for _, path := range f.Includes {
|
||||
if path == textflagheader {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add it if necessary.
|
||||
if requirestextflags(f) {
|
||||
f.Includes = append(f.Includes, textflagheader)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// requirestextflags returns whether the file uses flags in the textflags.h header.
|
||||
func requirestextflags(f *ir.File) bool {
|
||||
for _, s := range f.Sections {
|
||||
var a attr.Attribute
|
||||
switch s := s.(type) {
|
||||
case *ir.Function:
|
||||
a = s.Attributes
|
||||
case *ir.Global:
|
||||
a = s.Attributes
|
||||
}
|
||||
if a.ContainsTextFlags() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
32
vendor/github.com/mmcloughlin/avo/pass/verify.go
generated
vendored
Normal file
32
vendor/github.com/mmcloughlin/avo/pass/verify.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
// Verify pass validates an avo file.
|
||||
var Verify = Concat(
|
||||
InstructionPass(VerifyMemOperands),
|
||||
)
|
||||
|
||||
// VerifyMemOperands checks the instruction's memory operands.
|
||||
func VerifyMemOperands(i *ir.Instruction) error {
|
||||
for _, op := range i.Operands {
|
||||
m, ok := op.(operand.Mem)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Base == nil {
|
||||
return errors.New("bad memory operand: missing base register")
|
||||
}
|
||||
|
||||
if m.Index != nil && m.Scale == 0 {
|
||||
return errors.New("bad memory operand: index register with scale 0")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
192
vendor/github.com/mmcloughlin/avo/printer/goasm.go
generated
vendored
Normal file
192
vendor/github.com/mmcloughlin/avo/printer/goasm.go
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mmcloughlin/avo/buildtags"
|
||||
"github.com/mmcloughlin/avo/internal/prnt"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
// dot is the pesky unicode dot used in Go assembly.
|
||||
const dot = "\u00b7"
|
||||
|
||||
type goasm struct {
|
||||
cfg Config
|
||||
prnt.Generator
|
||||
|
||||
instructions []*ir.Instruction
|
||||
clear bool
|
||||
}
|
||||
|
||||
// NewGoAsm constructs a printer for writing Go assembly files.
|
||||
func NewGoAsm(cfg Config) Printer {
|
||||
return &goasm{cfg: cfg}
|
||||
}
|
||||
|
||||
func (p *goasm) Print(f *ir.File) ([]byte, error) {
|
||||
p.header(f)
|
||||
for _, s := range f.Sections {
|
||||
switch s := s.(type) {
|
||||
case *ir.Function:
|
||||
p.function(s)
|
||||
case *ir.Global:
|
||||
p.global(s)
|
||||
default:
|
||||
panic("unknown section type")
|
||||
}
|
||||
}
|
||||
return p.Result()
|
||||
}
|
||||
|
||||
func (p *goasm) header(f *ir.File) {
|
||||
p.Comment(p.cfg.GeneratedWarning())
|
||||
|
||||
if len(f.Constraints) > 0 {
|
||||
constraints, err := buildtags.Format(f.Constraints)
|
||||
if err != nil {
|
||||
p.AddError(err)
|
||||
}
|
||||
p.NL()
|
||||
p.Printf(constraints)
|
||||
}
|
||||
|
||||
if len(f.Includes) > 0 {
|
||||
p.NL()
|
||||
p.includes(f.Includes)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *goasm) includes(paths []string) {
|
||||
for _, path := range paths {
|
||||
p.Printf("#include \"%s\"\n", path)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *goasm) function(f *ir.Function) {
|
||||
p.NL()
|
||||
p.Comment(f.Stub())
|
||||
|
||||
if len(f.ISA) > 0 {
|
||||
p.Comment("Requires: " + strings.Join(f.ISA, ", "))
|
||||
}
|
||||
|
||||
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L166-L176
|
||||
//
|
||||
// if p.As == ATEXT {
|
||||
// // If there are attributes, print them. Otherwise, skip the comma.
|
||||
// // In short, print one of these two:
|
||||
// // TEXT foo(SB), DUPOK|NOSPLIT, $0
|
||||
// // TEXT foo(SB), $0
|
||||
// s := p.From.Sym.Attribute.TextAttrString()
|
||||
// if s != "" {
|
||||
// fmt.Fprintf(&buf, "%s%s", sep, s)
|
||||
// sep = ", "
|
||||
// }
|
||||
// }
|
||||
//
|
||||
p.Printf("TEXT %s%s(SB)", dot, f.Name)
|
||||
if f.Attributes != 0 {
|
||||
p.Printf(", %s", f.Attributes.Asm())
|
||||
}
|
||||
p.Printf(", %s\n", textsize(f))
|
||||
|
||||
p.clear = true
|
||||
for _, node := range f.Nodes {
|
||||
switch n := node.(type) {
|
||||
case *ir.Instruction:
|
||||
p.instruction(n)
|
||||
if n.IsTerminal || n.IsUnconditionalBranch() {
|
||||
p.flush()
|
||||
}
|
||||
case ir.Label:
|
||||
p.flush()
|
||||
p.ensureclear()
|
||||
p.Printf("%s:\n", n)
|
||||
case *ir.Comment:
|
||||
p.flush()
|
||||
p.ensureclear()
|
||||
for _, line := range n.Lines {
|
||||
p.Printf("\t// %s\n", line)
|
||||
}
|
||||
default:
|
||||
panic("unexpected node type")
|
||||
}
|
||||
}
|
||||
p.flush()
|
||||
}
|
||||
|
||||
func (p *goasm) instruction(i *ir.Instruction) {
|
||||
p.instructions = append(p.instructions, i)
|
||||
p.clear = false
|
||||
}
|
||||
|
||||
func (p *goasm) flush() {
|
||||
if len(p.instructions) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine instruction width. Instructions with no operands are not
|
||||
// considered in this calculation.
|
||||
width := 0
|
||||
for _, i := range p.instructions {
|
||||
opcode := i.OpcodeWithSuffixes()
|
||||
if len(i.Operands) > 0 && len(opcode) > width {
|
||||
width = len(opcode)
|
||||
}
|
||||
}
|
||||
|
||||
// Output instruction block.
|
||||
for _, i := range p.instructions {
|
||||
if len(i.Operands) > 0 {
|
||||
p.Printf("\t%-*s%s\n", width+1, i.OpcodeWithSuffixes(), joinOperands(i.Operands))
|
||||
} else {
|
||||
p.Printf("\t%s\n", i.OpcodeWithSuffixes())
|
||||
}
|
||||
}
|
||||
|
||||
p.instructions = nil
|
||||
}
|
||||
|
||||
func (p *goasm) ensureclear() {
|
||||
if !p.clear {
|
||||
p.NL()
|
||||
p.clear = true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *goasm) global(g *ir.Global) {
|
||||
p.NL()
|
||||
for _, d := range g.Data {
|
||||
a := operand.NewDataAddr(g.Symbol, d.Offset)
|
||||
p.Printf("DATA %s/%d, %s\n", a.Asm(), d.Value.Bytes(), d.Value.Asm())
|
||||
}
|
||||
p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size)
|
||||
}
|
||||
|
||||
func textsize(f *ir.Function) string {
|
||||
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265
|
||||
//
|
||||
// case TYPE_TEXTSIZE:
|
||||
// if a.Val.(int32) == objabi.ArgsSizeUnknown {
|
||||
// str = fmt.Sprintf("$%d", a.Offset)
|
||||
// } else {
|
||||
// str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
|
||||
// }
|
||||
//
|
||||
s := "$" + strconv.Itoa(f.FrameBytes())
|
||||
if argsize := f.ArgumentBytes(); argsize > 0 {
|
||||
return s + "-" + strconv.Itoa(argsize)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func joinOperands(operands []operand.Op) string {
|
||||
asm := make([]string, len(operands))
|
||||
for i, op := range operands {
|
||||
asm[i] = op.Asm()
|
||||
}
|
||||
return strings.Join(asm, ", ")
|
||||
}
|
98
vendor/github.com/mmcloughlin/avo/printer/printer.go
generated
vendored
Normal file
98
vendor/github.com/mmcloughlin/avo/printer/printer.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
// Package printer implements printing of avo files in various formats.
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/mmcloughlin/avo/internal/stack"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
)
|
||||
|
||||
// Printer can produce output for an avo File.
|
||||
type Printer interface {
|
||||
Print(*ir.File) ([]byte, error)
|
||||
}
|
||||
|
||||
// Builder can construct a printer.
|
||||
type Builder func(Config) Printer
|
||||
|
||||
// Config represents general printing configuration.
|
||||
type Config struct {
|
||||
// Command-line arguments passed to the generator. If provided, this will be
|
||||
// included in a code generation warning.
|
||||
Argv []string
|
||||
|
||||
// Name of the code generator.
|
||||
Name string
|
||||
|
||||
// Name of Go package the generated code will belong to.
|
||||
Pkg string
|
||||
}
|
||||
|
||||
// NewDefaultConfig produces a config with Name "avo".
|
||||
// The package name is guessed from the current directory.
|
||||
func NewDefaultConfig() Config {
|
||||
return Config{
|
||||
Name: "avo",
|
||||
Pkg: pkg(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewArgvConfig constructs a Config from os.Args.
|
||||
// The package name is guessed from the current directory.
|
||||
func NewArgvConfig() Config {
|
||||
return Config{
|
||||
Argv: os.Args,
|
||||
Pkg: pkg(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewGoRunConfig produces a Config for a generator that's expected to be
|
||||
// executed via "go run ...".
|
||||
func NewGoRunConfig() Config {
|
||||
path := mainfile()
|
||||
if path == "" {
|
||||
return NewDefaultConfig()
|
||||
}
|
||||
argv := []string{"go", "run", filepath.Base(path)}
|
||||
if len(os.Args) > 1 {
|
||||
argv = append(argv, os.Args[1:]...)
|
||||
}
|
||||
return Config{
|
||||
Argv: argv,
|
||||
Pkg: pkg(),
|
||||
}
|
||||
}
|
||||
|
||||
// GeneratedBy returns a description of the code generator.
|
||||
func (c Config) GeneratedBy() string {
|
||||
if c.Argv == nil {
|
||||
return c.Name
|
||||
}
|
||||
return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
|
||||
}
|
||||
|
||||
// GeneratedWarning returns text for a code generation warning. Conforms to https://golang.org/s/generatedcode.
|
||||
func (c Config) GeneratedWarning() string {
|
||||
return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
|
||||
}
|
||||
|
||||
// mainfile attempts to determine the file path of the main function by
|
||||
// inspecting the stack. Returns empty string on failure.
|
||||
func mainfile() string {
|
||||
if m := stack.Main(); m != nil {
|
||||
return m.File
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// pkg guesses the name of the package from the working directory.
|
||||
func pkg() string {
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
return filepath.Base(cwd)
|
||||
}
|
||||
return ""
|
||||
}
|
61
vendor/github.com/mmcloughlin/avo/printer/stubs.go
generated
vendored
Normal file
61
vendor/github.com/mmcloughlin/avo/printer/stubs.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"go/format"
|
||||
|
||||
"github.com/mmcloughlin/avo/buildtags"
|
||||
"github.com/mmcloughlin/avo/internal/prnt"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
)
|
||||
|
||||
type stubs struct {
|
||||
cfg Config
|
||||
prnt.Generator
|
||||
}
|
||||
|
||||
// NewStubs constructs a printer for writing stub function declarations.
|
||||
func NewStubs(cfg Config) Printer {
|
||||
return &stubs{cfg: cfg}
|
||||
}
|
||||
|
||||
func (s *stubs) Print(f *ir.File) ([]byte, error) {
|
||||
s.Comment(s.cfg.GeneratedWarning())
|
||||
|
||||
if len(f.Constraints) > 0 {
|
||||
constraints, err := buildtags.Format(f.Constraints)
|
||||
if err != nil {
|
||||
s.AddError(err)
|
||||
}
|
||||
s.NL()
|
||||
s.Printf(constraints)
|
||||
}
|
||||
|
||||
s.NL()
|
||||
s.Printf("package %s\n", s.cfg.Pkg)
|
||||
for _, fn := range f.Functions() {
|
||||
s.NL()
|
||||
s.Comment(fn.Doc...)
|
||||
for _, pragma := range fn.Pragmas {
|
||||
s.pragma(pragma)
|
||||
}
|
||||
s.Printf("%s\n", fn.Stub())
|
||||
}
|
||||
|
||||
// Apply formatting to the result. This is the simplest way to ensure
|
||||
// comment formatting rules introduced in Go 1.19 are applied. See
|
||||
// https://go.dev/doc/comment.
|
||||
src, err := s.Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return format.Source(src)
|
||||
}
|
||||
|
||||
func (s *stubs) pragma(p ir.Pragma) {
|
||||
s.Printf("//go:%s", p.Directive)
|
||||
for _, arg := range p.Arguments {
|
||||
s.Printf(" %s", arg)
|
||||
}
|
||||
s.NL()
|
||||
}
|
57
vendor/github.com/mmcloughlin/avo/reg/collection.go
generated
vendored
Normal file
57
vendor/github.com/mmcloughlin/avo/reg/collection.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package reg
|
||||
|
||||
// Collection represents a collection of virtual registers. This is primarily
|
||||
// useful for allocating virtual registers with distinct IDs.
|
||||
type Collection struct {
|
||||
idx map[Kind]Index
|
||||
}
|
||||
|
||||
// NewCollection builds an empty register collection.
|
||||
func NewCollection() *Collection {
|
||||
return &Collection{
|
||||
idx: map[Kind]Index{},
|
||||
}
|
||||
}
|
||||
|
||||
// VirtualRegister allocates and returns a new virtual register of the given kind and width.
|
||||
func (c *Collection) VirtualRegister(k Kind, s Spec) Virtual {
|
||||
idx := c.idx[k]
|
||||
c.idx[k]++
|
||||
return NewVirtual(idx, k, s)
|
||||
}
|
||||
|
||||
// GP8L allocates and returns a general-purpose 8-bit register (low byte).
|
||||
func (c *Collection) GP8L() GPVirtual { return c.GP(S8L) }
|
||||
|
||||
// GP8H allocates and returns a general-purpose 8-bit register (high byte).
|
||||
func (c *Collection) GP8H() GPVirtual { return c.GP(S8H) }
|
||||
|
||||
// GP8 allocates and returns a general-purpose 8-bit register (low byte).
|
||||
func (c *Collection) GP8() GPVirtual { return c.GP8L() }
|
||||
|
||||
// GP16 allocates and returns a general-purpose 16-bit register.
|
||||
func (c *Collection) GP16() GPVirtual { return c.GP(S16) }
|
||||
|
||||
// GP32 allocates and returns a general-purpose 32-bit register.
|
||||
func (c *Collection) GP32() GPVirtual { return c.GP(S32) }
|
||||
|
||||
// GP64 allocates and returns a general-purpose 64-bit register.
|
||||
func (c *Collection) GP64() GPVirtual { return c.GP(S64) }
|
||||
|
||||
// GP allocates and returns a general-purpose register of the given width.
|
||||
func (c *Collection) GP(s Spec) GPVirtual { return newgpv(c.VirtualRegister(KindGP, s)) }
|
||||
|
||||
// XMM allocates and returns a 128-bit vector register.
|
||||
func (c *Collection) XMM() VecVirtual { return c.Vec(S128) }
|
||||
|
||||
// YMM allocates and returns a 256-bit vector register.
|
||||
func (c *Collection) YMM() VecVirtual { return c.Vec(S256) }
|
||||
|
||||
// ZMM allocates and returns a 512-bit vector register.
|
||||
func (c *Collection) ZMM() VecVirtual { return c.Vec(S512) }
|
||||
|
||||
// Vec allocates and returns a vector register of the given width.
|
||||
func (c *Collection) Vec(s Spec) VecVirtual { return newvecv(c.VirtualRegister(KindVector, s)) }
|
||||
|
||||
// K allocates and returns an opmask register.
|
||||
func (c *Collection) K() OpmaskVirtual { return newopmaskv(c.VirtualRegister(KindOpmask, S64)) }
|
2
vendor/github.com/mmcloughlin/avo/reg/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/reg/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package reg provides types for physical and virtual registers, and definitions of x86-64 register families.
|
||||
package reg
|
112
vendor/github.com/mmcloughlin/avo/reg/set.go
generated
vendored
Normal file
112
vendor/github.com/mmcloughlin/avo/reg/set.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
package reg
|
||||
|
||||
// MaskSet maps register IDs to masks.
|
||||
type MaskSet map[ID]uint16
|
||||
|
||||
// NewEmptyMaskSet builds an empty register mask set.
|
||||
func NewEmptyMaskSet() MaskSet {
|
||||
return MaskSet{}
|
||||
}
|
||||
|
||||
// NewMaskSetFromRegisters forms a mask set from the given register list.
|
||||
func NewMaskSetFromRegisters(rs []Register) MaskSet {
|
||||
s := NewEmptyMaskSet()
|
||||
for _, r := range rs {
|
||||
s.AddRegister(r)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Clone returns a copy of s.
|
||||
func (s MaskSet) Clone() MaskSet {
|
||||
c := NewEmptyMaskSet()
|
||||
for id, mask := range s {
|
||||
c.Add(id, mask)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Add mask to the given register ID.
|
||||
// Reports whether this made any change to the set.
|
||||
func (s MaskSet) Add(id ID, mask uint16) bool {
|
||||
if (s[id] & mask) == mask {
|
||||
return false
|
||||
}
|
||||
s[id] |= mask
|
||||
return true
|
||||
}
|
||||
|
||||
// AddRegister is a convenience for adding the register's (ID, mask) to the set.
|
||||
// Reports whether this made any change to the set.
|
||||
func (s MaskSet) AddRegister(r Register) bool {
|
||||
return s.Add(r.ID(), r.Mask())
|
||||
}
|
||||
|
||||
// Discard clears masked bits from register ID.
|
||||
// Reports whether this made any change to the set.
|
||||
func (s MaskSet) Discard(id ID, mask uint16) bool {
|
||||
if curr, found := s[id]; !found || (curr&mask) == 0 {
|
||||
return false
|
||||
}
|
||||
s[id] &^= mask
|
||||
if s[id] == 0 {
|
||||
delete(s, id)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DiscardRegister is a convenience for discarding the register's (ID, mask) from the set.
|
||||
// Reports whether this made any change to the set.
|
||||
func (s MaskSet) DiscardRegister(r Register) bool {
|
||||
return s.Discard(r.ID(), r.Mask())
|
||||
}
|
||||
|
||||
// Update adds masks in t to s.
|
||||
// Reports whether this made any change to the set.
|
||||
func (s MaskSet) Update(t MaskSet) bool {
|
||||
change := false
|
||||
for id, mask := range t {
|
||||
change = s.Add(id, mask) || change
|
||||
}
|
||||
return change
|
||||
}
|
||||
|
||||
// Difference returns the set of registers in s but not t.
|
||||
func (s MaskSet) Difference(t MaskSet) MaskSet {
|
||||
d := s.Clone()
|
||||
d.DifferenceUpdate(t)
|
||||
return d
|
||||
}
|
||||
|
||||
// DifferenceUpdate removes every element of t from s.
|
||||
func (s MaskSet) DifferenceUpdate(t MaskSet) bool {
|
||||
change := false
|
||||
for id, mask := range t {
|
||||
change = s.Discard(id, mask) || change
|
||||
}
|
||||
return change
|
||||
}
|
||||
|
||||
// Equals returns true if s and t contain the same masks.
|
||||
func (s MaskSet) Equals(t MaskSet) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for id, mask := range s {
|
||||
if _, found := t[id]; !found || mask != t[id] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// OfKind returns the set of elements of s with kind k.
|
||||
func (s MaskSet) OfKind(k Kind) MaskSet {
|
||||
t := NewEmptyMaskSet()
|
||||
for id, mask := range s {
|
||||
if id.Kind() == k {
|
||||
t.Add(id, mask)
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
305
vendor/github.com/mmcloughlin/avo/reg/types.go
generated
vendored
Normal file
305
vendor/github.com/mmcloughlin/avo/reg/types.go
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
package reg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Kind is a class of registers.
|
||||
type Kind uint8
|
||||
|
||||
// Index of a register within a kind.
|
||||
type Index uint16
|
||||
|
||||
// Family is a collection of Physical registers of a common kind.
|
||||
type Family struct {
|
||||
Kind Kind
|
||||
registers []Physical
|
||||
}
|
||||
|
||||
// define builds a register and adds it to the Family.
|
||||
func (f *Family) define(s Spec, idx Index, name string, flags ...Info) Physical {
|
||||
r := newregister(f, s, idx, name, flags...)
|
||||
f.add(r)
|
||||
return r
|
||||
}
|
||||
|
||||
// add r to the family.
|
||||
func (f *Family) add(r Physical) {
|
||||
if r.Kind() != f.Kind {
|
||||
panic("bad kind")
|
||||
}
|
||||
f.registers = append(f.registers, r)
|
||||
}
|
||||
|
||||
// Virtual returns a virtual register from this family's kind.
|
||||
func (f *Family) Virtual(idx Index, s Spec) Virtual {
|
||||
return NewVirtual(idx, f.Kind, s)
|
||||
}
|
||||
|
||||
// Registers returns the registers in this family.
|
||||
func (f *Family) Registers() []Physical {
|
||||
return append([]Physical(nil), f.registers...)
|
||||
}
|
||||
|
||||
// Lookup returns the register with given physical index and spec. Returns nil if no such register exists.
|
||||
func (f *Family) Lookup(idx Index, s Spec) Physical {
|
||||
for _, r := range f.registers {
|
||||
if r.PhysicalIndex() == idx && r.Mask() == s.Mask() {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID is a register identifier.
|
||||
type ID uint32
|
||||
|
||||
// newid builds a new register ID from the virtual flag v, kind and index.
|
||||
func newid(v uint8, kind Kind, idx Index) ID {
|
||||
return ID(v) | (ID(kind) << 8) | (ID(idx) << 16)
|
||||
}
|
||||
|
||||
// IsVirtual reports whether this is an ID for a virtual register.
|
||||
func (id ID) IsVirtual() bool { return (id & 1) == 1 }
|
||||
|
||||
// IsPhysical reports whether this is an ID for a physical register.
|
||||
func (id ID) IsPhysical() bool { return !id.IsVirtual() }
|
||||
|
||||
// Kind extracts the kind from the register ID.
|
||||
func (id ID) Kind() Kind { return Kind(id >> 8) }
|
||||
|
||||
// Index extracts the index from the register ID.
|
||||
func (id ID) Index() Index { return Index(id >> 16) }
|
||||
|
||||
// Register represents a virtual or physical register.
|
||||
type Register interface {
|
||||
ID() ID
|
||||
Kind() Kind
|
||||
Size() uint
|
||||
Mask() uint16
|
||||
Asm() string
|
||||
as(Spec) Register
|
||||
spec() Spec
|
||||
register()
|
||||
}
|
||||
|
||||
// Equal reports whether a and b are equal registers.
|
||||
func Equal(a, b Register) bool {
|
||||
return (a.ID() == b.ID()) && (a.Mask() == b.Mask())
|
||||
}
|
||||
|
||||
// Virtual is a register of a given type and size, not yet allocated to a physical register.
|
||||
type Virtual interface {
|
||||
VirtualIndex() Index
|
||||
Register
|
||||
}
|
||||
|
||||
// ToVirtual converts r to Virtual if possible, otherwise returns nil.
|
||||
func ToVirtual(r Register) Virtual {
|
||||
if v, ok := r.(Virtual); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type virtual struct {
|
||||
idx Index
|
||||
kind Kind
|
||||
Spec
|
||||
}
|
||||
|
||||
// NewVirtual builds a Virtual register.
|
||||
func NewVirtual(idx Index, k Kind, s Spec) Virtual {
|
||||
return virtual{
|
||||
idx: idx,
|
||||
kind: k,
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (v virtual) ID() ID { return newid(1, v.kind, v.idx) }
|
||||
func (v virtual) VirtualIndex() Index { return v.idx }
|
||||
func (v virtual) Kind() Kind { return v.kind }
|
||||
|
||||
func (v virtual) Asm() string {
|
||||
// TODO(mbm): decide on virtual register syntax
|
||||
return fmt.Sprintf("<virtual:%v:%v:%v>", v.idx, v.Kind(), v.Size())
|
||||
}
|
||||
|
||||
func (v virtual) as(s Spec) Register {
|
||||
return virtual{
|
||||
idx: v.idx,
|
||||
kind: v.kind,
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (v virtual) spec() Spec { return v.Spec }
|
||||
func (v virtual) register() {}
|
||||
|
||||
// Info is a bitmask of register properties.
|
||||
type Info uint8
|
||||
|
||||
// Defined register Info flags.
|
||||
const (
|
||||
None Info = 0
|
||||
Restricted Info = 1 << iota
|
||||
BasePointer
|
||||
)
|
||||
|
||||
// Physical is a concrete register.
|
||||
type Physical interface {
|
||||
PhysicalIndex() Index
|
||||
Info() Info
|
||||
Register
|
||||
}
|
||||
|
||||
// ToPhysical converts r to Physical if possible, otherwise returns nil.
|
||||
func ToPhysical(r Register) Physical {
|
||||
if p, ok := r.(Physical); ok {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// register implements Physical.
|
||||
type register struct {
|
||||
family *Family
|
||||
idx Index
|
||||
name string
|
||||
info Info
|
||||
Spec
|
||||
}
|
||||
|
||||
func newregister(f *Family, s Spec, idx Index, name string, flags ...Info) register {
|
||||
r := register{
|
||||
family: f,
|
||||
idx: idx,
|
||||
name: name,
|
||||
info: None,
|
||||
Spec: s,
|
||||
}
|
||||
for _, flag := range flags {
|
||||
r.info |= flag
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r register) ID() ID { return newid(0, r.Kind(), r.idx) }
|
||||
func (r register) PhysicalIndex() Index { return r.idx }
|
||||
func (r register) Kind() Kind { return r.family.Kind }
|
||||
func (r register) Asm() string { return r.name }
|
||||
func (r register) Info() Info { return r.info }
|
||||
|
||||
func (r register) as(s Spec) Register {
|
||||
return r.family.Lookup(r.PhysicalIndex(), s)
|
||||
}
|
||||
|
||||
func (r register) spec() Spec { return r.Spec }
|
||||
func (r register) register() {}
|
||||
|
||||
// Spec defines the size of a register as well as the bit ranges it occupies in
|
||||
// an underlying physical register.
|
||||
type Spec uint16
|
||||
|
||||
// Spec values required for x86-64.
|
||||
const (
|
||||
S0 Spec = 0x0 // zero value reserved for pseudo registers
|
||||
S8L Spec = 0x1
|
||||
S8H Spec = 0x2
|
||||
S8 = S8L
|
||||
S16 Spec = 0x3
|
||||
S32 Spec = 0x7
|
||||
S64 Spec = 0xf
|
||||
S128 Spec = 0x1f
|
||||
S256 Spec = 0x3f
|
||||
S512 Spec = 0x7f
|
||||
)
|
||||
|
||||
// Mask returns a mask representing which bytes of an underlying register are
|
||||
// used by this register. This is almost always the low bytes, except for the
|
||||
// case of the high-byte registers. If bit n of the mask is set, this means
|
||||
// bytes 2^(n-1) to 2^n-1 are used.
|
||||
func (s Spec) Mask() uint16 {
|
||||
return uint16(s)
|
||||
}
|
||||
|
||||
// Size returns the register width in bytes.
|
||||
func (s Spec) Size() uint {
|
||||
x := uint(s)
|
||||
return (x >> 1) + (x & 1)
|
||||
}
|
||||
|
||||
// LookupPhysical returns the physical register with the given parameters, or nil if not found.
|
||||
func LookupPhysical(k Kind, idx Index, s Spec) Physical {
|
||||
f := FamilyOfKind(k)
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
return f.Lookup(idx, s)
|
||||
}
|
||||
|
||||
// LookupID returns the physical register with the given id and spec, or nil if not found.
|
||||
func LookupID(id ID, s Spec) Physical {
|
||||
if id.IsVirtual() {
|
||||
return nil
|
||||
}
|
||||
return LookupPhysical(id.Kind(), id.Index(), s)
|
||||
}
|
||||
|
||||
// Allocation records a register allocation.
|
||||
type Allocation map[ID]ID
|
||||
|
||||
// NewEmptyAllocation builds an empty register allocation.
|
||||
func NewEmptyAllocation() Allocation {
|
||||
return Allocation{}
|
||||
}
|
||||
|
||||
// Merge allocations from b into a. Errors if there is disagreement on a common
|
||||
// register.
|
||||
func (a Allocation) Merge(b Allocation) error {
|
||||
for id, p := range b {
|
||||
if alt, found := a[id]; found && alt != p {
|
||||
return errors.New("disagreement on overlapping register")
|
||||
}
|
||||
a[id] = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupDefault returns the register ID assigned by this allocation, returning
|
||||
// id if none is found.
|
||||
func (a Allocation) LookupDefault(id ID) ID {
|
||||
if _, found := a[id]; found {
|
||||
return a[id]
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// LookupRegister the allocation for register r, or return nil if there is none.
|
||||
func (a Allocation) LookupRegister(r Register) Physical {
|
||||
// Return immediately if it is already a physical register.
|
||||
if p := ToPhysical(r); p != nil {
|
||||
return p
|
||||
}
|
||||
|
||||
// Lookup an allocation for this virtual ID.
|
||||
id, found := a[r.ID()]
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
return LookupID(id, r.spec())
|
||||
}
|
||||
|
||||
// LookupRegisterDefault returns the register assigned to r, or r itself if there is none.
|
||||
func (a Allocation) LookupRegisterDefault(r Register) Register {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
if p := a.LookupRegister(r); p != nil {
|
||||
return p
|
||||
}
|
||||
return r
|
||||
}
|
383
vendor/github.com/mmcloughlin/avo/reg/x86.go
generated
vendored
Normal file
383
vendor/github.com/mmcloughlin/avo/reg/x86.go
generated
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
package reg
|
||||
|
||||
// Register kinds.
|
||||
const (
|
||||
KindPseudo Kind = iota
|
||||
KindGP
|
||||
KindVector
|
||||
KindOpmask
|
||||
)
|
||||
|
||||
// Declare register families.
|
||||
var (
|
||||
Pseudo = &Family{Kind: KindPseudo}
|
||||
GeneralPurpose = &Family{Kind: KindGP}
|
||||
Vector = &Family{Kind: KindVector}
|
||||
Opmask = &Family{Kind: KindOpmask}
|
||||
|
||||
Families = []*Family{
|
||||
Pseudo,
|
||||
GeneralPurpose,
|
||||
Vector,
|
||||
Opmask,
|
||||
}
|
||||
)
|
||||
|
||||
var familiesByKind = map[Kind]*Family{}
|
||||
|
||||
func init() {
|
||||
for _, f := range Families {
|
||||
familiesByKind[f.Kind] = f
|
||||
}
|
||||
}
|
||||
|
||||
// FamilyOfKind returns the Family of registers of the given kind, or nil if not found.
|
||||
func FamilyOfKind(k Kind) *Family {
|
||||
return familiesByKind[k]
|
||||
}
|
||||
|
||||
// Pseudo registers.
|
||||
var (
|
||||
FramePointer = Pseudo.define(S0, 0, "FP")
|
||||
ProgramCounter = Pseudo.define(S0, 0, "PC")
|
||||
StaticBase = Pseudo.define(S0, 0, "SB")
|
||||
StackPointer = Pseudo.define(S0, 0, "SP")
|
||||
)
|
||||
|
||||
// GP provides additional methods for general purpose registers.
|
||||
type GP interface {
|
||||
As8() Register
|
||||
As8L() Register
|
||||
As8H() Register
|
||||
As16() Register
|
||||
As32() Register
|
||||
As64() Register
|
||||
}
|
||||
|
||||
// GPPhysical is a general-purpose physical register.
|
||||
type GPPhysical interface {
|
||||
Physical
|
||||
GP
|
||||
}
|
||||
|
||||
type gpp struct {
|
||||
Physical
|
||||
}
|
||||
|
||||
func newgpp(r Physical) GPPhysical { return gpp{Physical: r} }
|
||||
|
||||
func (p gpp) As8() Register { return newgpp(p.as(S8).(Physical)) }
|
||||
func (p gpp) As8L() Register { return newgpp(p.as(S8L).(Physical)) }
|
||||
func (p gpp) As8H() Register { return newgpp(p.as(S8H).(Physical)) }
|
||||
func (p gpp) As16() Register { return newgpp(p.as(S16).(Physical)) }
|
||||
func (p gpp) As32() Register { return newgpp(p.as(S32).(Physical)) }
|
||||
func (p gpp) As64() Register { return newgpp(p.as(S64).(Physical)) }
|
||||
|
||||
// GPVirtual is a general-purpose virtual register.
|
||||
type GPVirtual interface {
|
||||
Virtual
|
||||
GP
|
||||
}
|
||||
|
||||
type gpv struct {
|
||||
Virtual
|
||||
}
|
||||
|
||||
func newgpv(v Virtual) GPVirtual { return gpv{Virtual: v} }
|
||||
|
||||
func (v gpv) As8() Register { return newgpv(v.as(S8).(Virtual)) }
|
||||
func (v gpv) As8L() Register { return newgpv(v.as(S8L).(Virtual)) }
|
||||
func (v gpv) As8H() Register { return newgpv(v.as(S8H).(Virtual)) }
|
||||
func (v gpv) As16() Register { return newgpv(v.as(S16).(Virtual)) }
|
||||
func (v gpv) As32() Register { return newgpv(v.as(S32).(Virtual)) }
|
||||
func (v gpv) As64() Register { return newgpv(v.as(S64).(Virtual)) }
|
||||
|
||||
func gp(s Spec, id Index, name string, flags ...Info) GPPhysical {
|
||||
r := newgpp(newregister(GeneralPurpose, s, id, name, flags...))
|
||||
GeneralPurpose.add(r)
|
||||
return r
|
||||
}
|
||||
|
||||
// General purpose registers.
|
||||
var (
|
||||
// Low byte.
|
||||
AL = gp(S8L, 0, "AL")
|
||||
CL = gp(S8L, 1, "CL")
|
||||
DL = gp(S8L, 2, "DL")
|
||||
BL = gp(S8L, 3, "BL")
|
||||
|
||||
// High byte.
|
||||
AH = gp(S8H, 0, "AH")
|
||||
CH = gp(S8H, 1, "CH")
|
||||
DH = gp(S8H, 2, "DH")
|
||||
BH = gp(S8H, 3, "BH")
|
||||
|
||||
// 8-bit.
|
||||
SPB = gp(S8, 4, "SP", Restricted)
|
||||
BPB = gp(S8, 5, "BP", BasePointer)
|
||||
SIB = gp(S8, 6, "SI")
|
||||
DIB = gp(S8, 7, "DI")
|
||||
R8B = gp(S8, 8, "R8")
|
||||
R9B = gp(S8, 9, "R9")
|
||||
R10B = gp(S8, 10, "R10")
|
||||
R11B = gp(S8, 11, "R11")
|
||||
R12B = gp(S8, 12, "R12")
|
||||
R13B = gp(S8, 13, "R13")
|
||||
R14B = gp(S8, 14, "R14")
|
||||
R15B = gp(S8, 15, "R15")
|
||||
|
||||
// 16-bit.
|
||||
AX = gp(S16, 0, "AX")
|
||||
CX = gp(S16, 1, "CX")
|
||||
DX = gp(S16, 2, "DX")
|
||||
BX = gp(S16, 3, "BX")
|
||||
SP = gp(S16, 4, "SP", Restricted)
|
||||
BP = gp(S16, 5, "BP", BasePointer)
|
||||
SI = gp(S16, 6, "SI")
|
||||
DI = gp(S16, 7, "DI")
|
||||
R8W = gp(S16, 8, "R8")
|
||||
R9W = gp(S16, 9, "R9")
|
||||
R10W = gp(S16, 10, "R10")
|
||||
R11W = gp(S16, 11, "R11")
|
||||
R12W = gp(S16, 12, "R12")
|
||||
R13W = gp(S16, 13, "R13")
|
||||
R14W = gp(S16, 14, "R14")
|
||||
R15W = gp(S16, 15, "R15")
|
||||
|
||||
// 32-bit.
|
||||
EAX = gp(S32, 0, "AX")
|
||||
ECX = gp(S32, 1, "CX")
|
||||
EDX = gp(S32, 2, "DX")
|
||||
EBX = gp(S32, 3, "BX")
|
||||
ESP = gp(S32, 4, "SP", Restricted)
|
||||
EBP = gp(S32, 5, "BP", BasePointer)
|
||||
ESI = gp(S32, 6, "SI")
|
||||
EDI = gp(S32, 7, "DI")
|
||||
R8L = gp(S32, 8, "R8")
|
||||
R9L = gp(S32, 9, "R9")
|
||||
R10L = gp(S32, 10, "R10")
|
||||
R11L = gp(S32, 11, "R11")
|
||||
R12L = gp(S32, 12, "R12")
|
||||
R13L = gp(S32, 13, "R13")
|
||||
R14L = gp(S32, 14, "R14")
|
||||
R15L = gp(S32, 15, "R15")
|
||||
|
||||
// 64-bit.
|
||||
RAX = gp(S64, 0, "AX")
|
||||
RCX = gp(S64, 1, "CX")
|
||||
RDX = gp(S64, 2, "DX")
|
||||
RBX = gp(S64, 3, "BX")
|
||||
RSP = gp(S64, 4, "SP", Restricted)
|
||||
RBP = gp(S64, 5, "BP", BasePointer)
|
||||
RSI = gp(S64, 6, "SI")
|
||||
RDI = gp(S64, 7, "DI")
|
||||
R8 = gp(S64, 8, "R8")
|
||||
R9 = gp(S64, 9, "R9")
|
||||
R10 = gp(S64, 10, "R10")
|
||||
R11 = gp(S64, 11, "R11")
|
||||
R12 = gp(S64, 12, "R12")
|
||||
R13 = gp(S64, 13, "R13")
|
||||
R14 = gp(S64, 14, "R14")
|
||||
R15 = gp(S64, 15, "R15")
|
||||
)
|
||||
|
||||
// Vec provides methods for vector registers.
|
||||
type Vec interface {
|
||||
AsX() Register
|
||||
AsY() Register
|
||||
AsZ() Register
|
||||
}
|
||||
|
||||
// VecPhysical is a physical vector register.
|
||||
type VecPhysical interface {
|
||||
Physical
|
||||
Vec
|
||||
}
|
||||
|
||||
type vecp struct {
|
||||
Physical
|
||||
Vec
|
||||
}
|
||||
|
||||
func newvecp(r Physical) VecPhysical { return vecp{Physical: r} }
|
||||
|
||||
func (p vecp) AsX() Register { return newvecp(p.as(S128).(Physical)) }
|
||||
func (p vecp) AsY() Register { return newvecp(p.as(S256).(Physical)) }
|
||||
func (p vecp) AsZ() Register { return newvecp(p.as(S512).(Physical)) }
|
||||
|
||||
// VecVirtual is a virtual vector register.
|
||||
type VecVirtual interface {
|
||||
Virtual
|
||||
Vec
|
||||
}
|
||||
|
||||
type vecv struct {
|
||||
Virtual
|
||||
Vec
|
||||
}
|
||||
|
||||
func newvecv(v Virtual) VecVirtual { return vecv{Virtual: v} }
|
||||
|
||||
func (v vecv) AsX() Register { return newvecv(v.as(S128).(Virtual)) }
|
||||
func (v vecv) AsY() Register { return newvecv(v.as(S256).(Virtual)) }
|
||||
func (v vecv) AsZ() Register { return newvecv(v.as(S512).(Virtual)) }
|
||||
|
||||
func vec(s Spec, id Index, name string, flags ...Info) VecPhysical {
|
||||
r := newvecp(newregister(Vector, s, id, name, flags...))
|
||||
Vector.add(r)
|
||||
return r
|
||||
}
|
||||
|
||||
// Vector registers.
|
||||
var (
|
||||
// 128-bit.
|
||||
X0 = vec(S128, 0, "X0")
|
||||
X1 = vec(S128, 1, "X1")
|
||||
X2 = vec(S128, 2, "X2")
|
||||
X3 = vec(S128, 3, "X3")
|
||||
X4 = vec(S128, 4, "X4")
|
||||
X5 = vec(S128, 5, "X5")
|
||||
X6 = vec(S128, 6, "X6")
|
||||
X7 = vec(S128, 7, "X7")
|
||||
X8 = vec(S128, 8, "X8")
|
||||
X9 = vec(S128, 9, "X9")
|
||||
X10 = vec(S128, 10, "X10")
|
||||
X11 = vec(S128, 11, "X11")
|
||||
X12 = vec(S128, 12, "X12")
|
||||
X13 = vec(S128, 13, "X13")
|
||||
X14 = vec(S128, 14, "X14")
|
||||
X15 = vec(S128, 15, "X15")
|
||||
X16 = vec(S128, 16, "X16")
|
||||
X17 = vec(S128, 17, "X17")
|
||||
X18 = vec(S128, 18, "X18")
|
||||
X19 = vec(S128, 19, "X19")
|
||||
X20 = vec(S128, 20, "X20")
|
||||
X21 = vec(S128, 21, "X21")
|
||||
X22 = vec(S128, 22, "X22")
|
||||
X23 = vec(S128, 23, "X23")
|
||||
X24 = vec(S128, 24, "X24")
|
||||
X25 = vec(S128, 25, "X25")
|
||||
X26 = vec(S128, 26, "X26")
|
||||
X27 = vec(S128, 27, "X27")
|
||||
X28 = vec(S128, 28, "X28")
|
||||
X29 = vec(S128, 29, "X29")
|
||||
X30 = vec(S128, 30, "X30")
|
||||
X31 = vec(S128, 31, "X31")
|
||||
|
||||
// 256-bit.
|
||||
Y0 = vec(S256, 0, "Y0")
|
||||
Y1 = vec(S256, 1, "Y1")
|
||||
Y2 = vec(S256, 2, "Y2")
|
||||
Y3 = vec(S256, 3, "Y3")
|
||||
Y4 = vec(S256, 4, "Y4")
|
||||
Y5 = vec(S256, 5, "Y5")
|
||||
Y6 = vec(S256, 6, "Y6")
|
||||
Y7 = vec(S256, 7, "Y7")
|
||||
Y8 = vec(S256, 8, "Y8")
|
||||
Y9 = vec(S256, 9, "Y9")
|
||||
Y10 = vec(S256, 10, "Y10")
|
||||
Y11 = vec(S256, 11, "Y11")
|
||||
Y12 = vec(S256, 12, "Y12")
|
||||
Y13 = vec(S256, 13, "Y13")
|
||||
Y14 = vec(S256, 14, "Y14")
|
||||
Y15 = vec(S256, 15, "Y15")
|
||||
Y16 = vec(S256, 16, "Y16")
|
||||
Y17 = vec(S256, 17, "Y17")
|
||||
Y18 = vec(S256, 18, "Y18")
|
||||
Y19 = vec(S256, 19, "Y19")
|
||||
Y20 = vec(S256, 20, "Y20")
|
||||
Y21 = vec(S256, 21, "Y21")
|
||||
Y22 = vec(S256, 22, "Y22")
|
||||
Y23 = vec(S256, 23, "Y23")
|
||||
Y24 = vec(S256, 24, "Y24")
|
||||
Y25 = vec(S256, 25, "Y25")
|
||||
Y26 = vec(S256, 26, "Y26")
|
||||
Y27 = vec(S256, 27, "Y27")
|
||||
Y28 = vec(S256, 28, "Y28")
|
||||
Y29 = vec(S256, 29, "Y29")
|
||||
Y30 = vec(S256, 30, "Y30")
|
||||
Y31 = vec(S256, 31, "Y31")
|
||||
|
||||
// 512-bit.
|
||||
Z0 = vec(S512, 0, "Z0")
|
||||
Z1 = vec(S512, 1, "Z1")
|
||||
Z2 = vec(S512, 2, "Z2")
|
||||
Z3 = vec(S512, 3, "Z3")
|
||||
Z4 = vec(S512, 4, "Z4")
|
||||
Z5 = vec(S512, 5, "Z5")
|
||||
Z6 = vec(S512, 6, "Z6")
|
||||
Z7 = vec(S512, 7, "Z7")
|
||||
Z8 = vec(S512, 8, "Z8")
|
||||
Z9 = vec(S512, 9, "Z9")
|
||||
Z10 = vec(S512, 10, "Z10")
|
||||
Z11 = vec(S512, 11, "Z11")
|
||||
Z12 = vec(S512, 12, "Z12")
|
||||
Z13 = vec(S512, 13, "Z13")
|
||||
Z14 = vec(S512, 14, "Z14")
|
||||
Z15 = vec(S512, 15, "Z15")
|
||||
Z16 = vec(S512, 16, "Z16")
|
||||
Z17 = vec(S512, 17, "Z17")
|
||||
Z18 = vec(S512, 18, "Z18")
|
||||
Z19 = vec(S512, 19, "Z19")
|
||||
Z20 = vec(S512, 20, "Z20")
|
||||
Z21 = vec(S512, 21, "Z21")
|
||||
Z22 = vec(S512, 22, "Z22")
|
||||
Z23 = vec(S512, 23, "Z23")
|
||||
Z24 = vec(S512, 24, "Z24")
|
||||
Z25 = vec(S512, 25, "Z25")
|
||||
Z26 = vec(S512, 26, "Z26")
|
||||
Z27 = vec(S512, 27, "Z27")
|
||||
Z28 = vec(S512, 28, "Z28")
|
||||
Z29 = vec(S512, 29, "Z29")
|
||||
Z30 = vec(S512, 30, "Z30")
|
||||
Z31 = vec(S512, 31, "Z31")
|
||||
)
|
||||
|
||||
// OpmaskPhysical is a opmask physical register.
|
||||
type OpmaskPhysical interface {
|
||||
Physical
|
||||
}
|
||||
|
||||
type opmaskp struct {
|
||||
Physical
|
||||
}
|
||||
|
||||
func newopmaskp(r Physical) OpmaskPhysical { return opmaskp{Physical: r} }
|
||||
|
||||
// OpmaskVirtual is a virtual opmask register.
|
||||
type OpmaskVirtual interface {
|
||||
Virtual
|
||||
}
|
||||
|
||||
type opmaskv struct {
|
||||
Virtual
|
||||
}
|
||||
|
||||
func newopmaskv(v Virtual) OpmaskVirtual { return opmaskv{Virtual: v} }
|
||||
|
||||
func opmask(s Spec, id Index, name string, flags ...Info) OpmaskPhysical {
|
||||
r := newopmaskp(newregister(Opmask, s, id, name, flags...))
|
||||
Opmask.add(r)
|
||||
return r
|
||||
}
|
||||
|
||||
// Opmask registers.
|
||||
//
|
||||
// Note that while K0 is a physical opmask register (it is a valid opmask source
|
||||
// and destination operand), it cannot be used as an opmask predicate value
|
||||
// because in that context K0 means "all true" or "no mask" regardless of the
|
||||
// actual contents of the physical register. For that reason, K0 should never be
|
||||
// assigned as a "general purpose" opmask register. However, it can be
|
||||
// explicitly operated upon by name as non-predicate operand, for example to
|
||||
// hold a constant or temporary value during calculations on other opmask
|
||||
// registers.
|
||||
var (
|
||||
K0 = opmask(S64, 0, "K0", Restricted)
|
||||
K1 = opmask(S64, 1, "K1")
|
||||
K2 = opmask(S64, 2, "K2")
|
||||
K3 = opmask(S64, 3, "K3")
|
||||
K4 = opmask(S64, 4, "K4")
|
||||
K5 = opmask(S64, 5, "K5")
|
||||
K6 = opmask(S64, 6, "K6")
|
||||
K7 = opmask(S64, 7, "K7")
|
||||
)
|
62
vendor/github.com/mmcloughlin/avo/src/src.go
generated
vendored
Normal file
62
vendor/github.com/mmcloughlin/avo/src/src.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Package src provides types for working with source files.
|
||||
package src
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Position represents a position in a source file.
|
||||
type Position struct {
|
||||
Filename string
|
||||
Line int // 1-up
|
||||
}
|
||||
|
||||
// FramePosition returns the Position of the given stack frame.
|
||||
func FramePosition(f runtime.Frame) Position {
|
||||
return Position{
|
||||
Filename: f.File,
|
||||
Line: f.Line,
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid reports whether the position is valid: Line must be positive, but
|
||||
// Filename may be empty.
|
||||
func (p Position) IsValid() bool {
|
||||
return p.Line > 0
|
||||
}
|
||||
|
||||
// String represents Position as a string.
|
||||
func (p Position) String() string {
|
||||
if !p.IsValid() {
|
||||
return "-"
|
||||
}
|
||||
var s string
|
||||
if p.Filename != "" {
|
||||
s += p.Filename + ":"
|
||||
}
|
||||
s += strconv.Itoa(p.Line)
|
||||
return s
|
||||
}
|
||||
|
||||
// Rel returns Position relative to basepath. If the given filename cannot be
|
||||
// expressed relative to basepath the position will be returned unchanged.
|
||||
func (p Position) Rel(basepath string) Position {
|
||||
q := p
|
||||
if rel, err := filepath.Rel(basepath, q.Filename); err == nil {
|
||||
q.Filename = rel
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// Relwd returns Position relative to the current working directory. Returns p
|
||||
// unchanged if the working directory cannot be determined, or the filename
|
||||
// cannot be expressed relative to the working directory.
|
||||
func (p Position) Relwd() Position {
|
||||
if wd, err := os.Getwd(); err == nil {
|
||||
return p.Rel(wd)
|
||||
}
|
||||
return p
|
||||
}
|
2
vendor/github.com/mmcloughlin/avo/x86/doc.go
generated
vendored
Normal file
2
vendor/github.com/mmcloughlin/avo/x86/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package x86 provides constructors for all x86-64 instructions.
|
||||
package x86
|
5
vendor/github.com/mmcloughlin/avo/x86/gen.go
generated
vendored
Normal file
5
vendor/github.com/mmcloughlin/avo/x86/gen.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
package x86
|
||||
|
||||
//go:generate avogen -output zoptab.go optab
|
||||
//go:generate avogen -output zctors.go ctors
|
||||
//go:generate avogen -output zctors_test.go ctorstest
|
130
vendor/github.com/mmcloughlin/avo/x86/optab.go
generated
vendored
Normal file
130
vendor/github.com/mmcloughlin/avo/x86/optab.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
package x86
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
"github.com/mmcloughlin/avo/operand"
|
||||
)
|
||||
|
||||
// build constructs an instruction object from a list of acceptable forms, and
|
||||
// given input operands and suffixes.
|
||||
func build(forms []form, suffixes sffxs, ops []operand.Op) (*ir.Instruction, error) {
|
||||
for i := range forms {
|
||||
f := &forms[i]
|
||||
if f.match(suffixes, ops) {
|
||||
return f.build(suffixes, ops), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("bad operands")
|
||||
}
|
||||
|
||||
// form represents an instruction form.
|
||||
type form struct {
|
||||
Opcode opc
|
||||
SuffixesClass sffxscls
|
||||
Features feature
|
||||
ISAs isas
|
||||
Arity uint8
|
||||
Operands oprnds
|
||||
}
|
||||
|
||||
// feature is a flags enumeration type representing instruction properties.
|
||||
type feature uint8
|
||||
|
||||
const (
|
||||
featureTerminal feature = 1 << iota
|
||||
featureBranch
|
||||
featureConditionalBranch
|
||||
featureCancellingInputs
|
||||
)
|
||||
|
||||
// oprnds is a list of explicit and implicit operands of an instruction form.
|
||||
// The size of the array is output by optab generator.
|
||||
type oprnds [maxoperands]oprnd
|
||||
|
||||
// oprnd represents an explicit or implicit operand to an instruction form.
|
||||
type oprnd struct {
|
||||
Type uint8
|
||||
Implicit bool
|
||||
Action action
|
||||
}
|
||||
|
||||
// action an instruction form applies to an operand.
|
||||
type action uint8
|
||||
|
||||
const (
|
||||
actionN action = iota
|
||||
actionR
|
||||
actionW
|
||||
actionRW action = actionR | actionW
|
||||
)
|
||||
|
||||
// Read reports if the action includes read.
|
||||
func (a action) Read() bool { return (a & actionR) != 0 }
|
||||
|
||||
// Read reports if the action includes write.
|
||||
func (a action) Write() bool { return (a & actionW) != 0 }
|
||||
|
||||
// match reports whether this form matches the given suffixes and operand
|
||||
// list.
|
||||
func (f *form) match(suffixes sffxs, ops []operand.Op) bool {
|
||||
// Match suffix.
|
||||
accept := f.SuffixesClass.SuffixesSet()
|
||||
if !accept[suffixes] {
|
||||
return false
|
||||
}
|
||||
|
||||
// Match operands.
|
||||
if len(ops) != int(f.Arity) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, op := range ops {
|
||||
t := oprndtype(f.Operands[i].Type)
|
||||
if !t.Match(op) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// build the full instruction object for this form and the given suffixes and
|
||||
// operands. Assumes the form already matches the inputs.
|
||||
func (f *form) build(suffixes sffxs, ops []operand.Op) *ir.Instruction {
|
||||
// Base instruction properties.
|
||||
i := &ir.Instruction{
|
||||
Opcode: f.Opcode.String(),
|
||||
Suffixes: suffixes.Strings(),
|
||||
Operands: ops,
|
||||
IsTerminal: (f.Features & featureTerminal) != 0,
|
||||
IsBranch: (f.Features & featureBranch) != 0,
|
||||
IsConditional: (f.Features & featureConditionalBranch) != 0,
|
||||
CancellingInputs: (f.Features & featureCancellingInputs) != 0,
|
||||
ISA: f.ISAs.List(),
|
||||
}
|
||||
|
||||
// Input/output operands.
|
||||
for _, spec := range f.Operands {
|
||||
if spec.Type == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
var op operand.Op
|
||||
if spec.Implicit {
|
||||
op = implreg(spec.Type).Register()
|
||||
} else {
|
||||
op, ops = ops[0], ops[1:]
|
||||
}
|
||||
|
||||
if spec.Action.Read() {
|
||||
i.Inputs = append(i.Inputs, op)
|
||||
}
|
||||
if spec.Action.Write() {
|
||||
i.Outputs = append(i.Outputs, op)
|
||||
}
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
38615
vendor/github.com/mmcloughlin/avo/x86/zctors.go
generated
vendored
Normal file
38615
vendor/github.com/mmcloughlin/avo/x86/zctors.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
16385
vendor/github.com/mmcloughlin/avo/x86/zoptab.go
generated
vendored
Normal file
16385
vendor/github.com/mmcloughlin/avo/x86/zoptab.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user