@@ -4,10 +4,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/attr"
|
"github.com/mmcloughlin/avo/attr"
|
||||||
"github.com/mmcloughlin/avo/buildtags"
|
"github.com/mmcloughlin/avo/buildtags"
|
||||||
"github.com/mmcloughlin/avo/gotypes"
|
"github.com/mmcloughlin/avo/gotypes"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
"github.com/mmcloughlin/avo/reg"
|
"github.com/mmcloughlin/avo/reg"
|
||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
@@ -16,9 +16,9 @@ import (
|
|||||||
// Context maintains state for incrementally building an avo File.
|
// Context maintains state for incrementally building an avo File.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
pkg *packages.Package
|
pkg *packages.Package
|
||||||
file *avo.File
|
file *ir.File
|
||||||
function *avo.Function
|
function *ir.Function
|
||||||
global *avo.Global
|
global *ir.Global
|
||||||
errs ErrorList
|
errs ErrorList
|
||||||
reg.Collection
|
reg.Collection
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ type Context struct {
|
|||||||
// NewContext initializes an empty build Context.
|
// NewContext initializes an empty build Context.
|
||||||
func NewContext() *Context {
|
func NewContext() *Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
file: avo.NewFile(),
|
file: ir.NewFile(),
|
||||||
Collection: *reg.NewCollection(),
|
Collection: *reg.NewCollection(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func (c *Context) ConstraintExpr(expr string) {
|
|||||||
|
|
||||||
// Function starts building a new function with the given name.
|
// Function starts building a new function with the given name.
|
||||||
func (c *Context) Function(name string) {
|
func (c *Context) Function(name string) {
|
||||||
c.function = avo.NewFunction(name)
|
c.function = ir.NewFunction(name)
|
||||||
c.file.AddSection(c.function)
|
c.file.AddSection(c.function)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,19 +124,19 @@ func (c *Context) AllocLocal(size int) operand.Mem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Instruction adds an instruction to the active function.
|
// Instruction adds an instruction to the active function.
|
||||||
func (c *Context) Instruction(i *avo.Instruction) {
|
func (c *Context) Instruction(i *ir.Instruction) {
|
||||||
c.activefunc().AddInstruction(i)
|
c.activefunc().AddInstruction(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label adds a label to the active function.
|
// Label adds a label to the active function.
|
||||||
func (c *Context) Label(name string) {
|
func (c *Context) Label(name string) {
|
||||||
c.activefunc().AddLabel(avo.Label(name))
|
c.activefunc().AddLabel(ir.Label(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) activefunc() *avo.Function {
|
func (c *Context) activefunc() *ir.Function {
|
||||||
if c.function == nil {
|
if c.function == nil {
|
||||||
c.adderrormessage("no active function")
|
c.adderrormessage("no active function")
|
||||||
return avo.NewFunction("")
|
return ir.NewFunction("")
|
||||||
}
|
}
|
||||||
return c.function
|
return c.function
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ func (c *Context) activefunc() *avo.Function {
|
|||||||
|
|
||||||
// StaticGlobal adds a new static data section to the file and returns a pointer to it.
|
// StaticGlobal adds a new static data section to the file and returns a pointer to it.
|
||||||
func (c *Context) StaticGlobal(name string) operand.Mem {
|
func (c *Context) StaticGlobal(name string) operand.Mem {
|
||||||
c.global = avo.NewStaticGlobal(name)
|
c.global = ir.NewStaticGlobal(name)
|
||||||
c.file.AddSection(c.global)
|
c.file.AddSection(c.global)
|
||||||
return c.global.Base()
|
return c.global.Base()
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ func (c *Context) DataAttributes(a attr.Attribute) {
|
|||||||
|
|
||||||
// AddDatum adds constant v at offset to the current active global data section.
|
// AddDatum adds constant v at offset to the current active global data section.
|
||||||
func (c *Context) AddDatum(offset int, v operand.Constant) {
|
func (c *Context) AddDatum(offset int, v operand.Constant) {
|
||||||
if err := c.activeglobal().AddDatum(avo.NewDatum(offset, v)); err != nil {
|
if err := c.activeglobal().AddDatum(ir.NewDatum(offset, v)); err != nil {
|
||||||
c.adderror(err)
|
c.adderror(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,10 +167,10 @@ func (c *Context) AppendDatum(v operand.Constant) {
|
|||||||
c.activeglobal().Append(v)
|
c.activeglobal().Append(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) activeglobal() *avo.Global {
|
func (c *Context) activeglobal() *ir.Global {
|
||||||
if c.global == nil {
|
if c.global == nil {
|
||||||
c.adderrormessage("no active global")
|
c.adderrormessage("no active global")
|
||||||
return avo.NewStaticGlobal("")
|
return ir.NewStaticGlobal("")
|
||||||
}
|
}
|
||||||
return c.global
|
return c.global
|
||||||
}
|
}
|
||||||
@@ -184,6 +184,6 @@ func (c *Context) adderrormessage(msg string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Result returns the built file and any accumulated errors.
|
// Result returns the built file and any accumulated errors.
|
||||||
func (c *Context) Result() (*avo.File, error) {
|
func (c *Context) Result() (*ir.File, error) {
|
||||||
return c.file, c.errs.Err()
|
return c.file, c.errs.Err()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
pkg = "github.com/mmcloughlin/avo"
|
pkg = "github.com/mmcloughlin/avo"
|
||||||
instType = "avo.Instruction"
|
|
||||||
operandType = "operand.Op"
|
operandType = "operand.Op"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (c *ctors) Generate(is []inst.Instruction) ([]byte, error) {
|
|||||||
c.Printf("package x86\n\n")
|
c.Printf("package x86\n\n")
|
||||||
c.Printf("import (\n")
|
c.Printf("import (\n")
|
||||||
c.Printf("\t\"errors\"\n")
|
c.Printf("\t\"errors\"\n")
|
||||||
c.Printf("\t\"%s\"\n", pkg)
|
c.Printf("\tintrep \"%s/ir\"\n", pkg)
|
||||||
c.Printf("\t\"%s/reg\"\n", pkg)
|
c.Printf("\t\"%s/reg\"\n", pkg)
|
||||||
c.Printf("\t\"%s/operand\"\n", pkg)
|
c.Printf("\t\"%s/operand\"\n", pkg)
|
||||||
c.Printf(")\n\n")
|
c.Printf(")\n\n")
|
||||||
@@ -46,7 +46,7 @@ func (c *ctors) instruction(i inst.Instruction) {
|
|||||||
|
|
||||||
s := params(i)
|
s := params(i)
|
||||||
|
|
||||||
c.Printf("func %s(%s) (*%s, error) {\n", i.Opcode, s.ParameterList(), instType)
|
c.Printf("func %s(%s) (*intrep.Instruction, error) {\n", i.Opcode, s.ParameterList())
|
||||||
c.forms(i, s)
|
c.forms(i, s)
|
||||||
c.Printf("}\n\n")
|
c.Printf("}\n\n")
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func (c *ctors) forms(i inst.Instruction, s signature) {
|
|||||||
|
|
||||||
func construct(i inst.Instruction, f inst.Form, s signature) string {
|
func construct(i inst.Instruction, f inst.Form, s signature) string {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
fmt.Fprintf(buf, "%s{\n", instType)
|
fmt.Fprintf(buf, "intrep.Instruction{\n")
|
||||||
fmt.Fprintf(buf, "\tOpcode: %#v,\n", i.Opcode)
|
fmt.Fprintf(buf, "\tOpcode: %#v,\n", i.Opcode)
|
||||||
fmt.Fprintf(buf, "\tOperands: %s,\n", s.ParameterSlice())
|
fmt.Fprintf(buf, "\tOperands: %s,\n", s.ParameterSlice())
|
||||||
|
|
||||||
|
|||||||
2
ir/doc.go
Normal file
2
ir/doc.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package ir provides the intermediate representation of avo programs.
|
||||||
|
package ir
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package avo
|
package ir
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
14
pass/cfg.go
14
pass/cfg.go
@@ -4,16 +4,16 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
"github.com/mmcloughlin/avo/ir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LabelTarget populates the LabelTarget of the given function. This maps from
|
// LabelTarget populates the LabelTarget of the given function. This maps from
|
||||||
// label name to the following instruction.
|
// label name to the following instruction.
|
||||||
func LabelTarget(fn *avo.Function) error {
|
func LabelTarget(fn *ir.Function) error {
|
||||||
target := map[avo.Label]*avo.Instruction{}
|
target := map[ir.Label]*ir.Instruction{}
|
||||||
for idx := 0; idx < len(fn.Nodes); idx++ {
|
for idx := 0; idx < len(fn.Nodes); idx++ {
|
||||||
// Is this a label?
|
// Is this a label?
|
||||||
lbl, ok := fn.Nodes[idx].(avo.Label)
|
lbl, ok := fn.Nodes[idx].(ir.Label)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ func LabelTarget(fn *avo.Function) error {
|
|||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
// Should be an instruction.
|
// Should be an instruction.
|
||||||
i, ok := fn.Nodes[idx].(*avo.Instruction)
|
i, ok := fn.Nodes[idx].(*ir.Instruction)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("instruction should follow a label")
|
return errors.New("instruction should follow a label")
|
||||||
}
|
}
|
||||||
@@ -38,14 +38,14 @@ func LabelTarget(fn *avo.Function) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CFG constructs the call-flow-graph for the function.
|
// CFG constructs the call-flow-graph for the function.
|
||||||
func CFG(fn *avo.Function) error {
|
func CFG(fn *ir.Function) error {
|
||||||
is := fn.Instructions()
|
is := fn.Instructions()
|
||||||
n := len(is)
|
n := len(is)
|
||||||
|
|
||||||
// Populate successors.
|
// Populate successors.
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
cur := is[i]
|
cur := is[i]
|
||||||
var nxt *avo.Instruction
|
var nxt *ir.Instruction
|
||||||
if i+1 < n {
|
if i+1 < n {
|
||||||
nxt = is[i+1]
|
nxt = is[i+1]
|
||||||
}
|
}
|
||||||
|
|||||||
117
pass/cfg_test.go
117
pass/cfg_test.go
@@ -5,22 +5,21 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLabelTarget(t *testing.T) {
|
func TestLabelTarget(t *testing.T) {
|
||||||
expect := map[avo.Label]*avo.Instruction{
|
expect := map[ir.Label]*ir.Instruction{
|
||||||
"lblA": {Opcode: "A"},
|
"lblA": {Opcode: "A"},
|
||||||
"lblB": {Opcode: "B"},
|
"lblB": {Opcode: "B"},
|
||||||
}
|
}
|
||||||
|
|
||||||
f := avo.NewFunction("happypath")
|
f := ir.NewFunction("happypath")
|
||||||
for lbl, i := range expect {
|
for lbl, i := range expect {
|
||||||
f.AddLabel(lbl)
|
f.AddLabel(lbl)
|
||||||
f.AddInstruction(i)
|
f.AddInstruction(i)
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "IDK"})
|
f.AddInstruction(&ir.Instruction{Opcode: "IDK"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := LabelTarget(f); err != nil {
|
if err := LabelTarget(f); err != nil {
|
||||||
@@ -33,11 +32,11 @@ func TestLabelTarget(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelTargetDuplicate(t *testing.T) {
|
func TestLabelTargetDuplicate(t *testing.T) {
|
||||||
f := avo.NewFunction("dupelabel")
|
f := ir.NewFunction("dupelabel")
|
||||||
f.AddLabel(avo.Label("lblA"))
|
f.AddLabel(ir.Label("lblA"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddLabel(avo.Label("lblA"))
|
f.AddLabel(ir.Label("lblA"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
|
|
||||||
err := LabelTarget(f)
|
err := LabelTarget(f)
|
||||||
|
|
||||||
@@ -47,9 +46,9 @@ func TestLabelTargetDuplicate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelTargetEndsWithLabel(t *testing.T) {
|
func TestLabelTargetEndsWithLabel(t *testing.T) {
|
||||||
f := avo.NewFunction("endswithlabel")
|
f := ir.NewFunction("endswithlabel")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddLabel(avo.Label("theend"))
|
f.AddLabel(ir.Label("theend"))
|
||||||
|
|
||||||
err := LabelTarget(f)
|
err := LabelTarget(f)
|
||||||
|
|
||||||
@@ -59,10 +58,10 @@ func TestLabelTargetEndsWithLabel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelTargetInstructionFollowLabel(t *testing.T) {
|
func TestLabelTargetInstructionFollowLabel(t *testing.T) {
|
||||||
f := avo.NewFunction("expectinstafterlabel")
|
f := ir.NewFunction("expectinstafterlabel")
|
||||||
f.AddLabel(avo.Label("lblA"))
|
f.AddLabel(ir.Label("lblA"))
|
||||||
f.AddLabel(avo.Label("lblB"))
|
f.AddLabel(ir.Label("lblB"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
|
|
||||||
err := LabelTarget(f)
|
err := LabelTarget(f)
|
||||||
|
|
||||||
@@ -72,9 +71,9 @@ func TestLabelTargetInstructionFollowLabel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGSingleBasicBlock(t *testing.T) {
|
func TestCFGSingleBasicBlock(t *testing.T) {
|
||||||
f := avo.NewFunction("simple")
|
f := ir.NewFunction("simple")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "B"})
|
f.AddInstruction(&ir.Instruction{Opcode: "B"})
|
||||||
f.AddInstruction(Terminal("RET"))
|
f.AddInstruction(Terminal("RET"))
|
||||||
|
|
||||||
if err := ComputeCFG(t, f); err != nil {
|
if err := ComputeCFG(t, f); err != nil {
|
||||||
@@ -95,11 +94,11 @@ func TestCFGSingleBasicBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGCondBranch(t *testing.T) {
|
func TestCFGCondBranch(t *testing.T) {
|
||||||
f := avo.NewFunction("condbranch")
|
f := ir.NewFunction("condbranch")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddLabel(avo.Label("lblB"))
|
f.AddLabel(ir.Label("lblB"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "B"})
|
f.AddInstruction(&ir.Instruction{Opcode: "B"})
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "C"})
|
f.AddInstruction(&ir.Instruction{Opcode: "C"})
|
||||||
f.AddInstruction(CondBranch("J", "lblB"))
|
f.AddInstruction(CondBranch("J", "lblB"))
|
||||||
f.AddInstruction(Terminal("RET"))
|
f.AddInstruction(Terminal("RET"))
|
||||||
|
|
||||||
@@ -117,10 +116,10 @@ func TestCFGCondBranch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGUncondBranch(t *testing.T) {
|
func TestCFGUncondBranch(t *testing.T) {
|
||||||
f := avo.NewFunction("uncondbranch")
|
f := ir.NewFunction("uncondbranch")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddLabel(avo.Label("lblB"))
|
f.AddLabel(ir.Label("lblB"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "B"})
|
f.AddInstruction(&ir.Instruction{Opcode: "B"})
|
||||||
f.AddInstruction(UncondBranch("JMP", "lblB"))
|
f.AddInstruction(UncondBranch("JMP", "lblB"))
|
||||||
|
|
||||||
if err := ComputeCFG(t, f); err != nil {
|
if err := ComputeCFG(t, f); err != nil {
|
||||||
@@ -135,11 +134,11 @@ func TestCFGUncondBranch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGJumpForward(t *testing.T) {
|
func TestCFGJumpForward(t *testing.T) {
|
||||||
f := avo.NewFunction("forward")
|
f := ir.NewFunction("forward")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddInstruction(CondBranch("J", "done"))
|
f.AddInstruction(CondBranch("J", "done"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "B"})
|
f.AddInstruction(&ir.Instruction{Opcode: "B"})
|
||||||
f.AddLabel(avo.Label("done"))
|
f.AddLabel(ir.Label("done"))
|
||||||
f.AddInstruction(Terminal("RET"))
|
f.AddInstruction(Terminal("RET"))
|
||||||
|
|
||||||
if err := ComputeCFG(t, f); err != nil {
|
if err := ComputeCFG(t, f); err != nil {
|
||||||
@@ -155,13 +154,13 @@ func TestCFGJumpForward(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGMultiReturn(t *testing.T) {
|
func TestCFGMultiReturn(t *testing.T) {
|
||||||
f := avo.NewFunction("multireturn")
|
f := ir.NewFunction("multireturn")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddInstruction(CondBranch("J", "fork"))
|
f.AddInstruction(CondBranch("J", "fork"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "B"})
|
f.AddInstruction(&ir.Instruction{Opcode: "B"})
|
||||||
f.AddInstruction(Terminal("RET1"))
|
f.AddInstruction(Terminal("RET1"))
|
||||||
f.AddLabel(avo.Label("fork"))
|
f.AddLabel(ir.Label("fork"))
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "C"})
|
f.AddInstruction(&ir.Instruction{Opcode: "C"})
|
||||||
f.AddInstruction(Terminal("RET2"))
|
f.AddInstruction(Terminal("RET2"))
|
||||||
|
|
||||||
if err := ComputeCFG(t, f); err != nil {
|
if err := ComputeCFG(t, f); err != nil {
|
||||||
@@ -179,8 +178,8 @@ func TestCFGMultiReturn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGShortLoop(t *testing.T) {
|
func TestCFGShortLoop(t *testing.T) {
|
||||||
f := avo.NewFunction("shortloop")
|
f := ir.NewFunction("shortloop")
|
||||||
f.AddLabel(avo.Label("cycle"))
|
f.AddLabel(ir.Label("cycle"))
|
||||||
f.AddInstruction(UncondBranch("JMP", "cycle"))
|
f.AddInstruction(UncondBranch("JMP", "cycle"))
|
||||||
|
|
||||||
if err := ComputeCFG(t, f); err != nil {
|
if err := ComputeCFG(t, f); err != nil {
|
||||||
@@ -193,8 +192,8 @@ func TestCFGShortLoop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGUndefinedLabel(t *testing.T) {
|
func TestCFGUndefinedLabel(t *testing.T) {
|
||||||
f := avo.NewFunction("undeflabel")
|
f := ir.NewFunction("undeflabel")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddInstruction(CondBranch("J", "undef"))
|
f.AddInstruction(CondBranch("J", "undef"))
|
||||||
|
|
||||||
err := ComputeCFG(t, f)
|
err := ComputeCFG(t, f)
|
||||||
@@ -205,9 +204,9 @@ func TestCFGUndefinedLabel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCFGMissingLabel(t *testing.T) {
|
func TestCFGMissingLabel(t *testing.T) {
|
||||||
f := avo.NewFunction("missinglabel")
|
f := ir.NewFunction("missinglabel")
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "A"})
|
f.AddInstruction(&ir.Instruction{Opcode: "A"})
|
||||||
f.AddInstruction(&avo.Instruction{Opcode: "J", IsBranch: true}) // no label operand
|
f.AddInstruction(&ir.Instruction{Opcode: "J", IsBranch: true}) // no label operand
|
||||||
err := ComputeCFG(t, f)
|
err := ComputeCFG(t, f)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expect error on missing label")
|
t.Fatal("expect error on missing label")
|
||||||
@@ -215,13 +214,13 @@ func TestCFGMissingLabel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Terminal builds a terminal instruction.
|
// Terminal builds a terminal instruction.
|
||||||
func Terminal(opcode string) *avo.Instruction {
|
func Terminal(opcode string) *ir.Instruction {
|
||||||
return &avo.Instruction{Opcode: opcode, IsTerminal: true}
|
return &ir.Instruction{Opcode: opcode, IsTerminal: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CondBranch builds a conditional branch instruction to the given label.
|
// CondBranch builds a conditional branch instruction to the given label.
|
||||||
func CondBranch(opcode, lbl string) *avo.Instruction {
|
func CondBranch(opcode, lbl string) *ir.Instruction {
|
||||||
return &avo.Instruction{
|
return &ir.Instruction{
|
||||||
Opcode: opcode,
|
Opcode: opcode,
|
||||||
Operands: []operand.Op{operand.LabelRef(lbl)},
|
Operands: []operand.Op{operand.LabelRef(lbl)},
|
||||||
IsBranch: true,
|
IsBranch: true,
|
||||||
@@ -230,8 +229,8 @@ func CondBranch(opcode, lbl string) *avo.Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UncondBranch builds an unconditional branch instruction to the given label.
|
// UncondBranch builds an unconditional branch instruction to the given label.
|
||||||
func UncondBranch(opcode, lbl string) *avo.Instruction {
|
func UncondBranch(opcode, lbl string) *ir.Instruction {
|
||||||
return &avo.Instruction{
|
return &ir.Instruction{
|
||||||
Opcode: opcode,
|
Opcode: opcode,
|
||||||
Operands: []operand.Op{operand.LabelRef(lbl)},
|
Operands: []operand.Op{operand.LabelRef(lbl)},
|
||||||
IsBranch: true,
|
IsBranch: true,
|
||||||
@@ -239,7 +238,7 @@ func UncondBranch(opcode, lbl string) *avo.Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ComputeCFG(t *testing.T, f *avo.Function) error {
|
func ComputeCFG(t *testing.T, f *ir.Function) error {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if err := LabelTarget(f); err != nil {
|
if err := LabelTarget(f); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -247,11 +246,11 @@ func ComputeCFG(t *testing.T, f *avo.Function) error {
|
|||||||
return CFG(f)
|
return CFG(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssertSuccessors(t *testing.T, f *avo.Function, expect map[string][]string) {
|
func AssertSuccessors(t *testing.T, f *ir.Function, expect map[string][]string) {
|
||||||
AssertEqual(t, "successors", OpcodeSuccessorGraph(f), expect)
|
AssertEqual(t, "successors", OpcodeSuccessorGraph(f), expect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssertPredecessors(t *testing.T, f *avo.Function, expect map[string][]string) {
|
func AssertPredecessors(t *testing.T, f *ir.Function, expect map[string][]string) {
|
||||||
AssertEqual(t, "predecessors", OpcodePredecessorGraph(f), expect)
|
AssertEqual(t, "predecessors", OpcodePredecessorGraph(f), expect)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,17 +263,17 @@ func AssertEqual(t *testing.T, what string, got, expect interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OpcodeSuccessorGraph builds a map from opcode name to successor opcode names.
|
// OpcodeSuccessorGraph builds a map from opcode name to successor opcode names.
|
||||||
func OpcodeSuccessorGraph(f *avo.Function) map[string][]string {
|
func OpcodeSuccessorGraph(f *ir.Function) map[string][]string {
|
||||||
return OpcodeGraph(f, func(i *avo.Instruction) []*avo.Instruction { return i.Succ })
|
return OpcodeGraph(f, func(i *ir.Instruction) []*ir.Instruction { return i.Succ })
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpcodePredecessorGraph builds a map from opcode name to predecessor opcode names.
|
// OpcodePredecessorGraph builds a map from opcode name to predecessor opcode names.
|
||||||
func OpcodePredecessorGraph(f *avo.Function) map[string][]string {
|
func OpcodePredecessorGraph(f *ir.Function) map[string][]string {
|
||||||
return OpcodeGraph(f, func(i *avo.Instruction) []*avo.Instruction { return i.Pred })
|
return OpcodeGraph(f, func(i *ir.Instruction) []*ir.Instruction { return i.Pred })
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpcodeGraph builds a map from opcode name to neighboring opcode names. Each list of neighbors is sorted.
|
// OpcodeGraph builds a map from opcode name to neighboring opcode names. Each list of neighbors is sorted.
|
||||||
func OpcodeGraph(f *avo.Function, neighbors func(*avo.Instruction) []*avo.Instruction) map[string][]string {
|
func OpcodeGraph(f *ir.Function, neighbors func(*ir.Instruction) []*ir.Instruction) map[string][]string {
|
||||||
g := map[string][]string{}
|
g := map[string][]string{}
|
||||||
for _, i := range f.Instructions() {
|
for _, i := range f.Instructions() {
|
||||||
opcodes := []string{}
|
opcodes := []string{}
|
||||||
|
|||||||
16
pass/pass.go
16
pass/pass.go
@@ -4,7 +4,7 @@ package pass
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/printer"
|
"github.com/mmcloughlin/avo/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,23 +22,23 @@ var Compile = Concat(
|
|||||||
|
|
||||||
// Interface for a processing pass.
|
// Interface for a processing pass.
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
Execute(*avo.File) error
|
Execute(*ir.File) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func adapts a function to the pass Interface.
|
// Func adapts a function to the pass Interface.
|
||||||
type Func func(*avo.File) error
|
type Func func(*ir.File) error
|
||||||
|
|
||||||
// Execute calls p.
|
// Execute calls p.
|
||||||
func (p Func) Execute(f *avo.File) error {
|
func (p Func) Execute(f *ir.File) error {
|
||||||
return p(f)
|
return p(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FunctionPass is a convenience for implementing a full file pass with a
|
// FunctionPass is a convenience for implementing a full file pass with a
|
||||||
// function that operates on each avo Function independently.
|
// function that operates on each avo Function independently.
|
||||||
type FunctionPass func(*avo.Function) error
|
type FunctionPass func(*ir.Function) error
|
||||||
|
|
||||||
// Execute calls p on every function in the file. Exits on the first error.
|
// Execute calls p on every function in the file. Exits on the first error.
|
||||||
func (p FunctionPass) Execute(f *avo.File) error {
|
func (p FunctionPass) Execute(f *ir.File) error {
|
||||||
for _, fn := range f.Functions() {
|
for _, fn := range f.Functions() {
|
||||||
if err := p(fn); err != nil {
|
if err := p(fn); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -49,7 +49,7 @@ func (p FunctionPass) Execute(f *avo.File) error {
|
|||||||
|
|
||||||
// Concat returns a pass that executes the given passes in order, stopping on the first error.
|
// Concat returns a pass that executes the given passes in order, stopping on the first error.
|
||||||
func Concat(passes ...Interface) Interface {
|
func Concat(passes ...Interface) Interface {
|
||||||
return Func(func(f *avo.File) error {
|
return Func(func(f *ir.File) error {
|
||||||
for _, p := range passes {
|
for _, p := range passes {
|
||||||
if err := p.Execute(f); err != nil {
|
if err := p.Execute(f); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -66,7 +66,7 @@ type Output struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute prints f with the configured Printer and writes output to Writer.
|
// Execute prints f with the configured Printer and writes output to Writer.
|
||||||
func (o *Output) Execute(f *avo.File) error {
|
func (o *Output) Execute(f *ir.File) error {
|
||||||
b, err := o.Printer.Print(f)
|
b, err := o.Printer.Print(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
10
pass/reg.go
10
pass/reg.go
@@ -3,13 +3,13 @@ package pass
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
"github.com/mmcloughlin/avo/reg"
|
"github.com/mmcloughlin/avo/reg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Liveness computes register liveness.
|
// Liveness computes register liveness.
|
||||||
func Liveness(fn *avo.Function) error {
|
func Liveness(fn *ir.Function) error {
|
||||||
// Note this implementation is initially naive so as to be "obviously correct".
|
// Note this implementation is initially naive so as to be "obviously correct".
|
||||||
// There are a well-known optimizations we can apply if necessary.
|
// There are a well-known optimizations we can apply if necessary.
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func Liveness(fn *avo.Function) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AllocateRegisters performs register allocation.
|
// AllocateRegisters performs register allocation.
|
||||||
func AllocateRegisters(fn *avo.Function) error {
|
func AllocateRegisters(fn *ir.Function) error {
|
||||||
// Populate allocators (one per kind).
|
// Populate allocators (one per kind).
|
||||||
as := map[reg.Kind]*Allocator{}
|
as := map[reg.Kind]*Allocator{}
|
||||||
for _, i := range fn.Instructions() {
|
for _, i := range fn.Instructions() {
|
||||||
@@ -110,7 +110,7 @@ func AllocateRegisters(fn *avo.Function) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BindRegisters applies the result of register allocation, replacing all virtual registers with their assigned physical registers.
|
// BindRegisters applies the result of register allocation, replacing all virtual registers with their assigned physical registers.
|
||||||
func BindRegisters(fn *avo.Function) error {
|
func BindRegisters(fn *ir.Function) error {
|
||||||
for _, i := range fn.Instructions() {
|
for _, i := range fn.Instructions() {
|
||||||
for idx := range i.Operands {
|
for idx := range i.Operands {
|
||||||
i.Operands[idx] = operand.ApplyAllocation(i.Operands[idx], fn.Allocation)
|
i.Operands[idx] = operand.ApplyAllocation(i.Operands[idx], fn.Allocation)
|
||||||
@@ -120,7 +120,7 @@ func BindRegisters(fn *avo.Function) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VerifyAllocation performs sanity checks following register allocation.
|
// VerifyAllocation performs sanity checks following register allocation.
|
||||||
func VerifyAllocation(fn *avo.Function) error {
|
func VerifyAllocation(fn *ir.Function) error {
|
||||||
// All registers should be physical.
|
// All registers should be physical.
|
||||||
for _, i := range fn.Instructions() {
|
for _, i := range fn.Instructions() {
|
||||||
for _, r := range i.Registers() {
|
for _, r := range i.Registers() {
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo/internal/test"
|
"github.com/mmcloughlin/avo/internal/test"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/reg"
|
"github.com/mmcloughlin/avo/reg"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo/pass"
|
"github.com/mmcloughlin/avo/pass"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/build"
|
"github.com/mmcloughlin/avo/build"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
)
|
)
|
||||||
@@ -57,7 +57,7 @@ func AssertRegistersMatchSet(t *testing.T, rs []reg.Register, s reg.Set) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConstructLiveness(t *testing.T, ctx *build.Context) *avo.Function {
|
func ConstructLiveness(t *testing.T, ctx *build.Context) *ir.Function {
|
||||||
f, err := ctx.Result()
|
f, err := ctx.Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
build.LogError(test.Logger(t), err, 0)
|
build.LogError(test.Logger(t), err, 0)
|
||||||
@@ -70,7 +70,7 @@ func ConstructLiveness(t *testing.T, ctx *build.Context) *avo.Function {
|
|||||||
}
|
}
|
||||||
fn := fns[0]
|
fn := fns[0]
|
||||||
|
|
||||||
passes := []func(*avo.Function) error{
|
passes := []func(*ir.Function) error{
|
||||||
pass.LabelTarget,
|
pass.LabelTarget,
|
||||||
pass.CFG,
|
pass.CFG,
|
||||||
pass.Liveness,
|
pass.Liveness,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package pass
|
package pass
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/attr"
|
"github.com/mmcloughlin/avo/attr"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IncludeTextFlagHeader includes textflag.h if necessary.
|
// IncludeTextFlagHeader includes textflag.h if necessary.
|
||||||
func IncludeTextFlagHeader(f *avo.File) error {
|
func IncludeTextFlagHeader(f *ir.File) error {
|
||||||
const textflagheader = "textflag.h"
|
const textflagheader = "textflag.h"
|
||||||
|
|
||||||
// Check if we already have it.
|
// Check if we already have it.
|
||||||
@@ -25,13 +25,13 @@ func IncludeTextFlagHeader(f *avo.File) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// requirestextflags returns whether the file uses flags in the textflags.h header.
|
// requirestextflags returns whether the file uses flags in the textflags.h header.
|
||||||
func requirestextflags(f *avo.File) bool {
|
func requirestextflags(f *ir.File) bool {
|
||||||
for _, s := range f.Sections {
|
for _, s := range f.Sections {
|
||||||
var a attr.Attribute
|
var a attr.Attribute
|
||||||
switch s := s.(type) {
|
switch s := s.(type) {
|
||||||
case *avo.Function:
|
case *ir.Function:
|
||||||
a = s.Attributes
|
a = s.Attributes
|
||||||
case *avo.Global:
|
case *ir.Global:
|
||||||
a = s.Attributes
|
a = s.Attributes
|
||||||
}
|
}
|
||||||
if a.ContainsTextFlags() {
|
if a.ContainsTextFlags() {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/internal/prnt"
|
"github.com/mmcloughlin/avo/internal/prnt"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,13 +22,13 @@ func NewGoAsm(cfg Config) Printer {
|
|||||||
return &goasm{cfg: cfg}
|
return &goasm{cfg: cfg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *goasm) Print(f *avo.File) ([]byte, error) {
|
func (p *goasm) Print(f *ir.File) ([]byte, error) {
|
||||||
p.header(f)
|
p.header(f)
|
||||||
for _, s := range f.Sections {
|
for _, s := range f.Sections {
|
||||||
switch s := s.(type) {
|
switch s := s.(type) {
|
||||||
case *avo.Function:
|
case *ir.Function:
|
||||||
p.function(s)
|
p.function(s)
|
||||||
case *avo.Global:
|
case *ir.Global:
|
||||||
p.global(s)
|
p.global(s)
|
||||||
default:
|
default:
|
||||||
panic("unknown section type")
|
panic("unknown section type")
|
||||||
@@ -37,7 +37,7 @@ func (p *goasm) Print(f *avo.File) ([]byte, error) {
|
|||||||
return p.Result()
|
return p.Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *goasm) header(f *avo.File) {
|
func (p *goasm) header(f *ir.File) {
|
||||||
p.Comment(p.cfg.GeneratedWarning())
|
p.Comment(p.cfg.GeneratedWarning())
|
||||||
|
|
||||||
if len(f.Constraints) > 0 {
|
if len(f.Constraints) > 0 {
|
||||||
@@ -57,7 +57,7 @@ func (p *goasm) includes(paths []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *goasm) function(f *avo.Function) {
|
func (p *goasm) function(f *ir.Function) {
|
||||||
p.NL()
|
p.NL()
|
||||||
p.Comment(f.Stub())
|
p.Comment(f.Stub())
|
||||||
|
|
||||||
@@ -83,13 +83,13 @@ func (p *goasm) function(f *avo.Function) {
|
|||||||
|
|
||||||
for _, node := range f.Nodes {
|
for _, node := range f.Nodes {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *avo.Instruction:
|
case *ir.Instruction:
|
||||||
if len(n.Operands) > 0 {
|
if len(n.Operands) > 0 {
|
||||||
p.Printf("\t%s\t%s\n", n.Opcode, joinOperands(n.Operands))
|
p.Printf("\t%s\t%s\n", n.Opcode, joinOperands(n.Operands))
|
||||||
} else {
|
} else {
|
||||||
p.Printf("\t%s\n", n.Opcode)
|
p.Printf("\t%s\n", n.Opcode)
|
||||||
}
|
}
|
||||||
case avo.Label:
|
case ir.Label:
|
||||||
p.Printf("%s:\n", n)
|
p.Printf("%s:\n", n)
|
||||||
default:
|
default:
|
||||||
panic("unexpected node type")
|
panic("unexpected node type")
|
||||||
@@ -97,7 +97,7 @@ func (p *goasm) function(f *avo.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *goasm) global(g *avo.Global) {
|
func (p *goasm) global(g *ir.Global) {
|
||||||
p.NL()
|
p.NL()
|
||||||
for _, d := range g.Data {
|
for _, d := range g.Data {
|
||||||
a := operand.NewDataAddr(g.Symbol, d.Offset)
|
a := operand.NewDataAddr(g.Symbol, d.Offset)
|
||||||
@@ -106,7 +106,7 @@ func (p *goasm) global(g *avo.Global) {
|
|||||||
p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size)
|
p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func textsize(f *avo.Function) string {
|
func textsize(f *ir.Function) string {
|
||||||
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265
|
// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265
|
||||||
//
|
//
|
||||||
// case TYPE_TEXTSIZE:
|
// case TYPE_TEXTSIZE:
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/internal/stack"
|
"github.com/mmcloughlin/avo/internal/stack"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Printer can produce output for an avo File.
|
// Printer can produce output for an avo File.
|
||||||
type Printer interface {
|
type Printer interface {
|
||||||
Print(*avo.File) ([]byte, error)
|
Print(*ir.File) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder can construct a printer.
|
// Builder can construct a printer.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package printer
|
package printer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mmcloughlin/avo"
|
|
||||||
"github.com/mmcloughlin/avo/internal/prnt"
|
"github.com/mmcloughlin/avo/internal/prnt"
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stubs struct {
|
type stubs struct {
|
||||||
@@ -15,7 +15,7 @@ func NewStubs(cfg Config) Printer {
|
|||||||
return &stubs{cfg: cfg}
|
return &stubs{cfg: cfg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubs) Print(f *avo.File) ([]byte, error) {
|
func (s *stubs) Print(f *ir.File) ([]byte, error) {
|
||||||
s.Comment(s.cfg.GeneratedWarning())
|
s.Comment(s.cfg.GeneratedWarning())
|
||||||
|
|
||||||
if len(f.Constraints) > 0 {
|
if len(f.Constraints) > 0 {
|
||||||
|
|||||||
7608
x86/zctors.go
7608
x86/zctors.go
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user