first pass at DATA sections
This commit is contained in:
82
ast.go
82
ast.go
@@ -1,6 +1,8 @@
|
|||||||
package avo
|
package avo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo/gotypes"
|
"github.com/mmcloughlin/avo/gotypes"
|
||||||
"github.com/mmcloughlin/avo/operand"
|
"github.com/mmcloughlin/avo/operand"
|
||||||
"github.com/mmcloughlin/avo/reg"
|
"github.com/mmcloughlin/avo/reg"
|
||||||
@@ -99,6 +101,10 @@ func NewFile() *File {
|
|||||||
return &File{}
|
return &File{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *File) AddSection(s Section) {
|
||||||
|
f.Sections = append(f.Sections, s)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *File) Functions() []*Function {
|
func (f *File) Functions() []*Function {
|
||||||
var fns []*Function
|
var fns []*Function
|
||||||
for _, s := range f.Sections {
|
for _, s := range f.Sections {
|
||||||
@@ -124,7 +130,7 @@ type Function struct {
|
|||||||
Allocation reg.Allocation
|
Allocation reg.Allocation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Function) section() {}
|
func (f *Function) section() {}
|
||||||
|
|
||||||
func NewFunction(name string) *Function {
|
func NewFunction(name string) *Function {
|
||||||
return &Function{
|
return &Function{
|
||||||
@@ -181,3 +187,77 @@ func (f *Function) FrameBytes() int {
|
|||||||
func (f *Function) ArgumentBytes() int {
|
func (f *Function) ArgumentBytes() int {
|
||||||
return f.Signature.Bytes()
|
return f.Signature.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Datum struct {
|
||||||
|
Offset int
|
||||||
|
Value operand.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()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Datum) Overlaps(other Datum) bool {
|
||||||
|
s, e := d.Interval()
|
||||||
|
so, eo := other.Interval()
|
||||||
|
return !(eo <= s || e <= so)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Global struct {
|
||||||
|
Symbol operand.Symbol
|
||||||
|
Data []Datum
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGlobal(sym operand.Symbol) *Global {
|
||||||
|
return &Global{
|
||||||
|
Symbol: sym,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStaticGlobal(name string) *Global {
|
||||||
|
return NewGlobal(operand.NewStaticSymbol(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Global) section() {}
|
||||||
|
|
||||||
|
func (g *Global) Base() operand.Mem {
|
||||||
|
return operand.NewDataAddr(g.Symbol, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Global) Grow(size int) {
|
||||||
|
if g.Size < size {
|
||||||
|
g.Size = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type Context struct {
|
|||||||
pkg *packages.Package
|
pkg *packages.Package
|
||||||
file *avo.File
|
file *avo.File
|
||||||
function *avo.Function
|
function *avo.Function
|
||||||
|
global *avo.Global
|
||||||
errs []error
|
errs []error
|
||||||
reg.Collection
|
reg.Collection
|
||||||
}
|
}
|
||||||
@@ -47,7 +48,7 @@ func (c *Context) Package(path string) {
|
|||||||
|
|
||||||
func (c *Context) Function(name string) {
|
func (c *Context) Function(name string) {
|
||||||
c.function = avo.NewFunction(name)
|
c.function = avo.NewFunction(name)
|
||||||
c.file.Sections = append(c.file.Sections, c.function)
|
c.file.AddSection(c.function)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Signature(s *gotypes.Signature) {
|
func (c *Context) Signature(s *gotypes.Signature) {
|
||||||
@@ -92,6 +93,30 @@ func (c *Context) activefunc() *avo.Function {
|
|||||||
|
|
||||||
//go:generate avogen -output zinstructions.go build
|
//go:generate avogen -output zinstructions.go build
|
||||||
|
|
||||||
|
func (c *Context) StaticGlobal(name string) operand.Mem {
|
||||||
|
c.global = avo.NewStaticGlobal(name)
|
||||||
|
c.file.AddSection(c.global)
|
||||||
|
return c.global.Base()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AddDatum(offset int, v operand.Constant) {
|
||||||
|
if err := c.activeglobal().AddDatum(avo.NewDatum(offset, v)); err != nil {
|
||||||
|
c.AddError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AppendDatum(v operand.Constant) {
|
||||||
|
c.activeglobal().Append(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) activeglobal() *avo.Global {
|
||||||
|
if c.global == nil {
|
||||||
|
c.AddErrorMessage("no active global")
|
||||||
|
return avo.NewStaticGlobal("")
|
||||||
|
}
|
||||||
|
return c.global
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) AddError(err error) {
|
func (c *Context) AddError(err error) {
|
||||||
c.errs = append(c.errs, err)
|
c.errs = append(c.errs, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ func TEXT(name, signature string) {
|
|||||||
|
|
||||||
func LABEL(name string) { ctx.Label(avo.Label(name)) }
|
func LABEL(name string) { ctx.Label(avo.Label(name)) }
|
||||||
|
|
||||||
|
func GLOBL(name string) operand.Mem {
|
||||||
|
return ctx.StaticGlobal(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DATA(offset int, v operand.Constant) {
|
||||||
|
ctx.AddDatum(offset, v)
|
||||||
|
}
|
||||||
|
|
||||||
var flags = NewFlags(flag.CommandLine)
|
var flags = NewFlags(flag.CommandLine)
|
||||||
|
|
||||||
func Generate() {
|
func Generate() {
|
||||||
|
|||||||
34
examples/data/asm.go
Normal file
34
examples/data/asm.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
. "github.com/mmcloughlin/avo/build"
|
||||||
|
. "github.com/mmcloughlin/avo/operand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bytes := GLOBL("bytes")
|
||||||
|
DATA(0, U64(0x0011223344556677))
|
||||||
|
DATA(8, String("strconst"))
|
||||||
|
DATA(16, F32(math.Pi))
|
||||||
|
DATA(24, F64(math.Pi))
|
||||||
|
DATA(32, U32(0x00112233))
|
||||||
|
DATA(36, U16(0x4455))
|
||||||
|
DATA(38, U8(0x66))
|
||||||
|
DATA(39, U8(0x77))
|
||||||
|
|
||||||
|
// TEXT ·DataAt(SB),0,$0-9
|
||||||
|
TEXT("DataAt", "func(i int) byte")
|
||||||
|
i := Load(Param("i"), GP64v()) // MOVQ i+0(FP), AX
|
||||||
|
ptr := Mem{Base: GP64v()}
|
||||||
|
LEAQ(bytes, ptr.Base) // LEAQ b<>+0x00(SB), BX
|
||||||
|
b := GP8v()
|
||||||
|
MOVB(ptr.Idx(i, 1), b) // MOVB 0(BX)(AX*1), CL
|
||||||
|
Store(b, ReturnIndex(0)) // MOVB CL, ret+8(FP)
|
||||||
|
RET() // RET
|
||||||
|
|
||||||
|
Generate()
|
||||||
|
}
|
||||||
21
examples/data/data.s
Normal file
21
examples/data/data.s
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Code generated by command: go run asm.go -out data.s -stubs stub.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA bytes<>(SB)/8, $0x0011223344556677
|
||||||
|
DATA bytes<>+8(SB)/8, $"strconst"
|
||||||
|
DATA bytes<>+16(SB)/4, $(3.1415927)
|
||||||
|
DATA bytes<>+24(SB)/8, $(3.141592653589793)
|
||||||
|
DATA bytes<>+32(SB)/4, $0x00112233
|
||||||
|
DATA bytes<>+36(SB)/2, $0x4455
|
||||||
|
DATA bytes<>+38(SB)/1, $0x66
|
||||||
|
DATA bytes<>+39(SB)/1, $0x77
|
||||||
|
GLOBL ·bytes<>(SB), RODATA, $40
|
||||||
|
|
||||||
|
// func DataAt(i int) byte
|
||||||
|
TEXT ·DataAt(SB),0,$0-9
|
||||||
|
MOVQ i(FP), AX
|
||||||
|
LEAQ bytes<>(SB), CX
|
||||||
|
MOVB (CX)(AX*1), AL
|
||||||
|
MOVB AL, ret+8(FP)
|
||||||
|
RET
|
||||||
29
examples/data/data_test.go
Normal file
29
examples/data/data_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run asm.go -out data.s -stubs stub.go
|
||||||
|
|
||||||
|
func TestDataAt(t *testing.T) {
|
||||||
|
order := binary.LittleEndian
|
||||||
|
expect := make([]byte, 40)
|
||||||
|
order.PutUint64(expect[0:], 0x0011223344556677) // DATA(0, U64(0x0011223344556677))
|
||||||
|
copy(expect[8:], []byte("strconst")) // DATA(8, String("strconst"))
|
||||||
|
order.PutUint32(expect[16:], math.Float32bits(math.Pi)) // DATA(16, F32(math.Pi))
|
||||||
|
order.PutUint64(expect[24:], math.Float64bits(math.Pi)) // DATA(24, F64(math.Pi))
|
||||||
|
order.PutUint32(expect[32:], 0x00112233) // DATA(32, U32(0x00112233))
|
||||||
|
order.PutUint16(expect[36:], 0x4455) // DATA(36, U16(0x4455))
|
||||||
|
expect[38] = 0x66 // DATA(38, U8(0x66))
|
||||||
|
expect[39] = 0x77 // DATA(39, U8(0x77))
|
||||||
|
|
||||||
|
for i, e := range expect {
|
||||||
|
b := DataAt(i)
|
||||||
|
if b != e {
|
||||||
|
t.Errorf("DataAt(%d) = %#02x; expected %#02x", i, b, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
examples/data/stub.go
Normal file
5
examples/data/stub.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Code generated by command: go run asm.go -out data.s -stubs stub.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
package data
|
||||||
|
|
||||||
|
func DataAt(i int) byte
|
||||||
@@ -15,16 +15,16 @@ func main() {
|
|||||||
|
|
||||||
// Store message values on the stack.
|
// Store message values on the stack.
|
||||||
w := AllocLocal(64)
|
w := AllocLocal(64)
|
||||||
W := func(r int) Mem { return w.Idx((r % 16) * 4) }
|
W := func(r int) Mem { return w.Offset((r % 16) * 4) }
|
||||||
|
|
||||||
// Load initial hash.
|
// Load initial hash.
|
||||||
h0, h1, h2, h3, h4 := GP32v(), GP32v(), GP32v(), GP32v(), GP32v()
|
h0, h1, h2, h3, h4 := GP32v(), GP32v(), GP32v(), GP32v(), GP32v()
|
||||||
|
|
||||||
MOVL(h.Idx(0), h0)
|
MOVL(h.Offset(0), h0)
|
||||||
MOVL(h.Idx(4), h1)
|
MOVL(h.Offset(4), h1)
|
||||||
MOVL(h.Idx(8), h2)
|
MOVL(h.Offset(8), h2)
|
||||||
MOVL(h.Idx(12), h3)
|
MOVL(h.Offset(12), h3)
|
||||||
MOVL(h.Idx(16), h4)
|
MOVL(h.Offset(16), h4)
|
||||||
|
|
||||||
// Initialize registers.
|
// Initialize registers.
|
||||||
a, b, c, d, e := GP32v(), GP32v(), GP32v(), GP32v(), GP32v()
|
a, b, c, d, e := GP32v(), GP32v(), GP32v(), GP32v(), GP32v()
|
||||||
@@ -52,7 +52,7 @@ func main() {
|
|||||||
// Load message value.
|
// Load message value.
|
||||||
u := GP32v()
|
u := GP32v()
|
||||||
if r < 16 {
|
if r < 16 {
|
||||||
MOVL(m.Idx(4*r), u)
|
MOVL(m.Offset(4*r), u)
|
||||||
BSWAPL(u)
|
BSWAPL(u)
|
||||||
} else {
|
} else {
|
||||||
MOVL(W(r-3), u)
|
MOVL(W(r-3), u)
|
||||||
@@ -85,11 +85,11 @@ func main() {
|
|||||||
ADDL(e, h4)
|
ADDL(e, h4)
|
||||||
|
|
||||||
// Store results back.
|
// Store results back.
|
||||||
MOVL(h0, h.Idx(0))
|
MOVL(h0, h.Offset(0))
|
||||||
MOVL(h1, h.Idx(4))
|
MOVL(h1, h.Offset(4))
|
||||||
MOVL(h2, h.Idx(8))
|
MOVL(h2, h.Offset(8))
|
||||||
MOVL(h3, h.Idx(12))
|
MOVL(h3, h.Offset(12))
|
||||||
MOVL(h4, h.Idx(16))
|
MOVL(h4, h.Offset(16))
|
||||||
RET()
|
RET()
|
||||||
|
|
||||||
Generate()
|
Generate()
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ type Symbol struct {
|
|||||||
Static bool
|
Static bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewStaticSymbol(name string) Symbol {
|
||||||
|
return Symbol{Name: name, Static: true}
|
||||||
|
}
|
||||||
|
|
||||||
func (s Symbol) String() string {
|
func (s Symbol) String() string {
|
||||||
n := s.Name
|
n := s.Name
|
||||||
if s.Static {
|
if s.Static {
|
||||||
@@ -51,12 +55,27 @@ func NewStackAddr(offset int) Mem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Mem) Idx(idx int) Mem {
|
func NewDataAddr(sym Symbol, offset int) Mem {
|
||||||
|
return Mem{
|
||||||
|
Symbol: sym,
|
||||||
|
Disp: offset,
|
||||||
|
Base: reg.StaticBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Mem) Offset(idx int) Mem {
|
||||||
a := m
|
a := m
|
||||||
a.Disp += idx
|
a.Disp += idx
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Mem) Idx(r reg.Register, s uint8) Mem {
|
||||||
|
a := m
|
||||||
|
a.Index = r
|
||||||
|
a.Scale = s
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
func (m Mem) Asm() string {
|
func (m Mem) Asm() string {
|
||||||
a := m.Symbol.String()
|
a := m.Symbol.String()
|
||||||
if m.Disp != 0 {
|
if m.Disp != 0 {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ func TestMemAsm(t *testing.T) {
|
|||||||
{Mem{Symbol: Symbol{Name: "foo"}, Base: reg.StaticBase, Disp: -7}, "foo-7(SB)"},
|
{Mem{Symbol: Symbol{Name: "foo"}, Base: reg.StaticBase, Disp: -7}, "foo-7(SB)"},
|
||||||
{Mem{Symbol: Symbol{Name: "bar", Static: true}, Base: reg.StaticBase, Disp: 4, Index: reg.R11, Scale: 4}, "bar<>+4(SB)(R11*4)"},
|
{Mem{Symbol: Symbol{Name: "bar", Static: true}, Base: reg.StaticBase, Disp: 4, Index: reg.R11, Scale: 4}, "bar<>+4(SB)(R11*4)"},
|
||||||
{NewParamAddr("param", 16), "param+16(FP)"},
|
{NewParamAddr("param", 16), "param+16(FP)"},
|
||||||
|
{NewStackAddr(42), "42(SP)"},
|
||||||
|
{NewDataAddr(Symbol{Name: "data", Static: true}, 13), "data<>+13(SB)"},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
got := c.Mem.Asm()
|
got := c.Mem.Asm()
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ func (p *goasm) Print(f *avo.File) ([]byte, error) {
|
|||||||
switch s := s.(type) {
|
switch s := s.(type) {
|
||||||
case *avo.Function:
|
case *avo.Function:
|
||||||
p.function(s)
|
p.function(s)
|
||||||
|
case *avo.Global:
|
||||||
|
p.global(s)
|
||||||
default:
|
default:
|
||||||
panic("unknown section type")
|
panic("unknown section type")
|
||||||
}
|
}
|
||||||
@@ -64,6 +66,16 @@ func (p *goasm) function(f *avo.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *goasm) global(g *avo.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())
|
||||||
|
}
|
||||||
|
// TODO(mbm): replace hardcoded RODATA with an attributes list
|
||||||
|
p.Printf("GLOBL %s%s(SB), RODATA, $%d\n", dot, g.Symbol, g.Size)
|
||||||
|
}
|
||||||
|
|
||||||
func joinOperands(operands []operand.Op) string {
|
func joinOperands(operands []operand.Op) string {
|
||||||
asm := make([]string, len(operands))
|
asm := make([]string, len(operands))
|
||||||
for i, op := range operands {
|
for i, op := range operands {
|
||||||
|
|||||||
Reference in New Issue
Block a user