Skip to content

Commit

Permalink
gotypes: support struct fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mmcloughlin committed Dec 18, 2018
1 parent 7f3efa1 commit bc9a2aa
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 28 deletions.
69 changes: 63 additions & 6 deletions examples/components/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,69 @@ import (
func main() {
Package("github.com/mmcloughlin/avo/examples/components")

// Add confirms that we correctly deduce the packing of Struct.
TEXT("Add", "func(x uint64, s Struct, y uint64) uint64")
x := Load(Param("x"), GP64v())
y := Load(Param("y"), GP64v())
ADDQ(x, y)
Store(y, ReturnIndex(0))
TEXT("FieldByte", "func(s Struct) byte")
b := Load(Param("s").Field("Byte"), GP8v())
Store(b, ReturnIndex(0))
RET()

TEXT("FieldInt8", "func(s Struct) int8")
i8 := Load(Param("s").Field("Int8"), GP8v())
Store(i8, ReturnIndex(0))
RET()

TEXT("FieldUint16", "func(s Struct) uint16")
u16 := Load(Param("s").Field("Uint16"), GP16v())
Store(u16, ReturnIndex(0))
RET()

TEXT("FieldInt32", "func(s Struct) int32")
i32 := Load(Param("s").Field("Int32"), GP32v())
Store(i32, ReturnIndex(0))
RET()

TEXT("FieldUint64", "func(s Struct) uint64")
u64 := Load(Param("s").Field("Uint64"), GP64v())
Store(u64, ReturnIndex(0))
RET()

TEXT("FieldFloat32", "func(s Struct) float32")
f32 := Load(Param("s").Field("Float32"), Xv())
Store(f32, ReturnIndex(0))
RET()

TEXT("FieldFloat64", "func(s Struct) float64")
f64 := Load(Param("s").Field("Float64"), Xv())
Store(f64, ReturnIndex(0))
RET()

TEXT("FieldStringLen", "func(s Struct) int")
l := Load(Param("s").Field("String").Len(), GP64v())
Store(l, ReturnIndex(0))
RET()

TEXT("FieldSliceCap", "func(s Struct) int")
c := Load(Param("s").Field("Slice").Cap(), GP64v())
Store(c, ReturnIndex(0))
RET()

TEXT("FieldArrayTwoBTwo", "func(s Struct) byte")
b2 := Load(Param("s").Field("Array").Index(2).Field("B").Index(2), GP8v())
Store(b2, ReturnIndex(0))
RET()

TEXT("FieldArrayOneC", "func(s Struct) uint16")
c1 := Load(Param("s").Field("Array").Index(1).Field("C"), GP16v())
Store(c1, ReturnIndex(0))
RET()

TEXT("FieldComplex64Imag", "func(s Struct) float32")
c64i := Load(Param("s").Field("Complex64").Imag(), Xv())
Store(c64i, ReturnIndex(0))
RET()

TEXT("FieldComplex128Real", "func(s Struct) float64")
c128r := Load(Param("s").Field("Complex128").Real(), Xv())
Store(c128r, ReturnIndex(0))
RET()

Generate()
Expand Down
4 changes: 3 additions & 1 deletion examples/components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package components

type Struct struct {
Byte byte
Uint32 uint32
Int8 int8
Uint16 uint16
Int32 int32
Uint64 uint64
Float32 float32
Float64 float64
Expand Down
70 changes: 64 additions & 6 deletions examples/components/components.s
Original file line number Diff line number Diff line change
@@ -1,10 +1,68 @@

#include "textflag.h"

// func Add(x uint64, s Struct, y uint64) uint64
TEXT ·Add(SB),0,$0-200
MOVQ x(FP), AX
MOVQ y+184(FP), CX
ADDQ AX, CX
MOVQ CX, ret+192(FP)
// func FieldByte(s Struct) byte
TEXT ·FieldByte(SB),0,$0-184
MOVB s_Byte(FP), AL
MOVB AL, ret+176(FP)
RET
// func FieldInt8(s Struct) int8
TEXT ·FieldInt8(SB),0,$0-184
MOVB s_Int8+1(FP), AL
MOVB AL, ret+176(FP)
RET
// func FieldUint16(s Struct) uint16
TEXT ·FieldUint16(SB),0,$0-184
MOVW s_Uint16+2(FP), AX
MOVW AX, ret+176(FP)
RET
// func FieldInt32(s Struct) int32
TEXT ·FieldInt32(SB),0,$0-184
MOVL s_Int32+4(FP), AX
MOVL AX, ret+176(FP)
RET
// func FieldUint64(s Struct) uint64
TEXT ·FieldUint64(SB),0,$0-184
MOVQ s_Uint64+8(FP), AX
MOVQ AX, ret+176(FP)
RET
// func FieldFloat32(s Struct) float32
TEXT ·FieldFloat32(SB),0,$0-184
MOVSS s_Float32+16(FP), X0
MOVSS X0, ret+176(FP)
RET
// func FieldFloat64(s Struct) float64
TEXT ·FieldFloat64(SB),0,$0-184
MOVSD s_Float64+24(FP), X0
MOVSD X0, ret+176(FP)
RET
// func FieldStringLen(s Struct) int
TEXT ·FieldStringLen(SB),0,$0-184
MOVQ s_String_len+40(FP), AX
MOVQ AX, ret+176(FP)
RET
// func FieldSliceCap(s Struct) int
TEXT ·FieldSliceCap(SB),0,$0-184
MOVQ s_Slice_cap+64(FP), AX
MOVQ AX, ret+176(FP)
RET
// func FieldArrayTwoBTwo(s Struct) byte
TEXT ·FieldArrayTwoBTwo(SB),0,$0-184
MOVB s_Array_2_B_2+114(FP), AL
MOVB AL, ret+176(FP)
RET
// func FieldArrayOneC(s Struct) uint16
TEXT ·FieldArrayOneC(SB),0,$0-184
MOVW s_Array_1_C+100(FP), AX
MOVW AX, ret+176(FP)
RET
// func FieldComplex64Imag(s Struct) float32
TEXT ·FieldComplex64Imag(SB),0,$0-184
MOVSS s_Complex64_imag+156(FP), X0
MOVSS X0, ret+176(FP)
RET
// func FieldComplex128Real(s Struct) float64
TEXT ·FieldComplex128Real(SB),0,$0-184
MOVSD s_Complex128_real+160(FP), X0
MOVSD X0, ret+176(FP)
RET
26 changes: 22 additions & 4 deletions examples/components/components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,27 @@ import (

//go:generate go run asm.go -out components.s -stubs stub.go

func TestAdd(t *testing.T) {
expect := func(x uint64, s Struct, y uint64) uint64 { return x + y }
if err := quick.CheckEqual(Add, expect, nil); err != nil {
t.Fatal(err)
func TestFunctionsEqual(t *testing.T) {
cases := []struct {
f, g interface{}
}{
{FieldByte, func(s Struct) byte { return s.Byte }},
{FieldInt8, func(s Struct) int8 { return s.Int8 }},
{FieldUint16, func(s Struct) uint16 { return s.Uint16 }},
{FieldInt32, func(s Struct) int32 { return s.Int32 }},
{FieldUint64, func(s Struct) uint64 { return s.Uint64 }},
{FieldFloat32, func(s Struct) float32 { return s.Float32 }},
{FieldFloat64, func(s Struct) float64 { return s.Float64 }},
{FieldStringLen, func(s Struct) int { return len(s.String) }},
{FieldSliceCap, func(s Struct) int { return cap(s.Slice) }},
{FieldArrayTwoBTwo, func(s Struct) byte { return s.Array[2].B[2] }},
{FieldArrayOneC, func(s Struct) uint16 { return s.Array[1].C }},
{FieldComplex64Imag, func(s Struct) float32 { return imag(s.Complex64) }},
{FieldComplex128Real, func(s Struct) float64 { return real(s.Complex128) }},
}
for _, c := range cases {
if err := quick.CheckEqual(c.f, c.g, nil); err != nil {
t.Fatal(err)
}
}
}
14 changes: 13 additions & 1 deletion examples/components/stub.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
package components

func Add(x uint64, s Struct, y uint64) uint64
func FieldByte(s Struct) byte
func FieldInt8(s Struct) int8
func FieldUint16(s Struct) uint16
func FieldInt32(s Struct) int32
func FieldUint64(s Struct) uint64
func FieldFloat32(s Struct) float32
func FieldFloat64(s Struct) float64
func FieldStringLen(s Struct) int
func FieldSliceCap(s Struct) int
func FieldArrayTwoBTwo(s Struct) byte
func FieldArrayOneC(s Struct) uint16
func FieldComplex64Imag(s Struct) float32
func FieldComplex128Real(s Struct) float64
55 changes: 45 additions & 10 deletions gotypes/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gotypes

import (
"errors"
"fmt"
"go/token"
"go/types"
"strconv"
Expand All @@ -24,10 +25,15 @@ type Component interface {
Real() Component
Imag() Component
Index(int) Component
Field(string) Component
}

type componenterr string

func errorf(format string, args ...interface{}) 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) Base() Component { return c }
Expand All @@ -36,6 +42,7 @@ 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 {
name string
Expand Down Expand Up @@ -82,36 +89,36 @@ var slicehdroffsets = Sizes.Offsetsof([]*types.Var{

func (c *component) Base() Component {
if !isslice(c.typ) && !isstring(c.typ) {
return componenterr("only slices and strings have base pointers")
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 componenterr("only slices and strings have length fields")
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 componenterr("only slices have capacity fields")
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 componenterr("only complex types have real values")
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 componenterr("only complex types have imaginary values")
return errorf("only complex types have imaginary values")
}
f := complextofloat(c.typ)
return c.sub("_imag", int(Sizes.Sizeof(f)), f)
Expand All @@ -120,10 +127,10 @@ func (c *component) Imag() Component {
func (c *component) Index(i int) Component {
a, ok := c.typ.(*types.Array)
if !ok {
return componenterr("not array type")
return errorf("not array type")
}
if int64(i) >= a.Len() {
return componenterr("array index out of bounds")
return errorf("array index out of bounds")
}
// Reference: https://github.com/golang/tools/blob/bcd4e47d02889ebbc25c9f4bf3d27e4124b0bf9d/go/analysis/passes/asmdecl/asmdecl.go#L482-L494
//
Expand All @@ -146,6 +153,37 @@ func (c *component) Index(i int) Component {
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
s.name += suffix
Expand All @@ -154,9 +192,6 @@ func (c *component) sub(suffix string, offset int, t types.Type) *component {
return &s
}

// TODO(mbm): gotypes.Component handling for structs
// TODO(mbm): gotypes.Component handling for complex64/128

func isslice(t types.Type) bool {
_, ok := t.(*types.Slice)
return ok
Expand Down

0 comments on commit bc9a2aa

Please sign in to comment.