From 9243d299e65473eac779d9d2c2247265672650b1 Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Thu, 27 Dec 2018 11:57:46 -0800 Subject: [PATCH] first pass at DATA sections --- ast.go | 82 +++++++++++++++++++++++++++++++++++++- build/context.go | 27 ++++++++++++- build/global.go | 8 ++++ examples/data/asm.go | 34 ++++++++++++++++ examples/data/data.s | 21 ++++++++++ examples/data/data_test.go | 29 ++++++++++++++ examples/data/stub.go | 5 +++ examples/sha1/asm.go | 24 +++++------ operand/types.go | 21 +++++++++- operand/types_test.go | 2 + printer/goasm.go | 12 ++++++ 11 files changed, 250 insertions(+), 15 deletions(-) create mode 100644 examples/data/asm.go create mode 100644 examples/data/data.s create mode 100644 examples/data/data_test.go create mode 100644 examples/data/stub.go diff --git a/ast.go b/ast.go index b4f5cd5..9b62aff 100644 --- a/ast.go +++ b/ast.go @@ -1,6 +1,8 @@ package avo import ( + "errors" + "github.com/mmcloughlin/avo/gotypes" "github.com/mmcloughlin/avo/operand" "github.com/mmcloughlin/avo/reg" @@ -99,6 +101,10 @@ func NewFile() *File { return &File{} } +func (f *File) AddSection(s Section) { + f.Sections = append(f.Sections, s) +} + func (f *File) Functions() []*Function { var fns []*Function for _, s := range f.Sections { @@ -124,7 +130,7 @@ type Function struct { Allocation reg.Allocation } -func (f Function) section() {} +func (f *Function) section() {} func NewFunction(name string) *Function { return &Function{ @@ -181,3 +187,77 @@ func (f *Function) FrameBytes() int { func (f *Function) ArgumentBytes() int { 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) +} diff --git a/build/context.go b/build/context.go index 429bd00..2df2179 100644 --- a/build/context.go +++ b/build/context.go @@ -15,6 +15,7 @@ type Context struct { pkg *packages.Package file *avo.File function *avo.Function + global *avo.Global errs []error reg.Collection } @@ -47,7 +48,7 @@ func (c *Context) Package(path string) { func (c *Context) Function(name string) { 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) { @@ -92,6 +93,30 @@ func (c *Context) activefunc() *avo.Function { //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) { c.errs = append(c.errs, err) } diff --git a/build/global.go b/build/global.go index 5251d95..b4eba59 100644 --- a/build/global.go +++ b/build/global.go @@ -24,6 +24,14 @@ func TEXT(name, signature string) { 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) func Generate() { diff --git a/examples/data/asm.go b/examples/data/asm.go new file mode 100644 index 0000000..e4841df --- /dev/null +++ b/examples/data/asm.go @@ -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() +} diff --git a/examples/data/data.s b/examples/data/data.s new file mode 100644 index 0000000..dcc372f --- /dev/null +++ b/examples/data/data.s @@ -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 diff --git a/examples/data/data_test.go b/examples/data/data_test.go new file mode 100644 index 0000000..c68fa73 --- /dev/null +++ b/examples/data/data_test.go @@ -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) + } + } +} diff --git a/examples/data/stub.go b/examples/data/stub.go new file mode 100644 index 0000000..e3dfcbd --- /dev/null +++ b/examples/data/stub.go @@ -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 diff --git a/examples/sha1/asm.go b/examples/sha1/asm.go index ad95b76..9217817 100644 --- a/examples/sha1/asm.go +++ b/examples/sha1/asm.go @@ -15,16 +15,16 @@ func main() { // Store message values on the stack. 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. h0, h1, h2, h3, h4 := GP32v(), GP32v(), GP32v(), GP32v(), GP32v() - MOVL(h.Idx(0), h0) - MOVL(h.Idx(4), h1) - MOVL(h.Idx(8), h2) - MOVL(h.Idx(12), h3) - MOVL(h.Idx(16), h4) + MOVL(h.Offset(0), h0) + MOVL(h.Offset(4), h1) + MOVL(h.Offset(8), h2) + MOVL(h.Offset(12), h3) + MOVL(h.Offset(16), h4) // Initialize registers. a, b, c, d, e := GP32v(), GP32v(), GP32v(), GP32v(), GP32v() @@ -52,7 +52,7 @@ func main() { // Load message value. u := GP32v() if r < 16 { - MOVL(m.Idx(4*r), u) + MOVL(m.Offset(4*r), u) BSWAPL(u) } else { MOVL(W(r-3), u) @@ -85,11 +85,11 @@ func main() { ADDL(e, h4) // Store results back. - MOVL(h0, h.Idx(0)) - MOVL(h1, h.Idx(4)) - MOVL(h2, h.Idx(8)) - MOVL(h3, h.Idx(12)) - MOVL(h4, h.Idx(16)) + MOVL(h0, h.Offset(0)) + MOVL(h1, h.Offset(4)) + MOVL(h2, h.Offset(8)) + MOVL(h3, h.Offset(12)) + MOVL(h4, h.Offset(16)) RET() Generate() diff --git a/operand/types.go b/operand/types.go index 3e0c3cd..c7788a1 100644 --- a/operand/types.go +++ b/operand/types.go @@ -15,6 +15,10 @@ type Symbol struct { Static bool } +func NewStaticSymbol(name string) Symbol { + return Symbol{Name: name, Static: true} +} + func (s Symbol) String() string { n := s.Name 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.Disp += idx 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 { a := m.Symbol.String() if m.Disp != 0 { diff --git a/operand/types_test.go b/operand/types_test.go index 4750a66..3154fad 100644 --- a/operand/types_test.go +++ b/operand/types_test.go @@ -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: "bar", Static: true}, Base: reg.StaticBase, Disp: 4, Index: reg.R11, Scale: 4}, "bar<>+4(SB)(R11*4)"}, {NewParamAddr("param", 16), "param+16(FP)"}, + {NewStackAddr(42), "42(SP)"}, + {NewDataAddr(Symbol{Name: "data", Static: true}, 13), "data<>+13(SB)"}, } for _, c := range cases { got := c.Mem.Asm() diff --git a/printer/goasm.go b/printer/goasm.go index 3680368..4b3ece2 100644 --- a/printer/goasm.go +++ b/printer/goasm.go @@ -26,6 +26,8 @@ func (p *goasm) Print(f *avo.File) ([]byte, error) { switch s := s.(type) { case *avo.Function: p.function(s) + case *avo.Global: + p.global(s) default: 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 { asm := make([]string, len(operands)) for i, op := range operands {