diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..1d9251d --- /dev/null +++ b/examples/README.md @@ -0,0 +1,6 @@ +# Examples + +Feature demonstrations: + +* **[add](add):** Add two numbers. The "Hello World!" of `avo`. +* **[args](args):** Loading function arguments. diff --git a/examples/args/README.md b/examples/args/README.md new file mode 100644 index 0000000..6844b75 --- /dev/null +++ b/examples/args/README.md @@ -0,0 +1,117 @@ +# args + +Demonstrates how to reference function parameters in `avo`. + +## Basics + +Use `Param()` to reference arguments by name. The `Load()` function can be used to load the argument into a register (this will select the correct `MOV` instruction for you). Likewise `Store` and `ReturnIndex` can be used to write the return value. The following function will return its second argument. + +[embedmd]:# (asm.go go /.*TEXT.*Second/ /RET.*/) +```go + TEXT("Second", "func(x, y int32) int32") + y := Load(Param("y"), GP32v()) + Store(y, ReturnIndex(0)) + RET() +``` + +This `avo` code will generate the following assembly. Note that parameter references are named to conform to [`asmdecl`](https://godoc.org/golang.org/x/tools/go/analysis/passes/asmdecl) rules enforced by `go vet`. + +[embedmd]:# (args.s s /.*func Second/ /RET/) +```s +// func Second(x int32, y int32) int32 +TEXT ·Second(SB), $0-12 + MOVL y+4(FP), AX + MOVL AX, ret+8(FP) + RET +``` + +Primitive types can be loaded as above. Other types consist of sub-components which must be loaded into registers independently; for example strings, slices, arrays, structs and complex values. + +## Strings and Slices + +Strings and slices actually consist of multiple components under the hood: see [`reflect.StringHeader`](https://golang.org/pkg/reflect/#StringHeader) and [`reflect.SliceHeader`](https://golang.org/pkg/reflect/#SliceHeader). The following `avo` code allows you to load the string length. + +[embedmd]:# (asm.go go /.*TEXT.*StringLen/ /RET.*/) +```go + TEXT("StringLen", "func(s string) int") + strlen := Load(Param("s").Len(), GP64v()) + Store(strlen, ReturnIndex(0)) + RET() +``` + +The same code would work for a slice argument. Likewise `Param(...).Base()` and `Param(...).Cap()` will load the base pointer and capacity (slice only). + +## Array Indexing + +Arrays can be indexed with the `Index()` method. For example, the following returns the third element of the passed array. + +[embedmd]:# (asm.go go /.*TEXT.*ArrayThree/ /RET.*/) +```go + TEXT("ArrayThree", "func(a [7]uint64) uint64") + a3 := Load(Param("a").Index(3), GP64v()) + Store(a3, ReturnIndex(0)) + RET() +``` + +## Struct Fields + +Struct fields can be accessed with the `Field()` method. Note that this _requires_ the package to be specified, so that `avo` can parse the type definition. In this example we specify the package with the line: + +[embedmd]:# (asm.go go /.*Package\(.*/) +```go + Package("github.com/mmcloughlin/avo/examples/args") +``` + +This package contains the struct definition: + +[embedmd]:# (args.go go /type Struct/ /^}/) +```go +type Struct struct { + Byte byte + Int8 int8 + Uint16 uint16 + Int32 int32 + Uint64 uint64 + Float32 float32 + Float64 float64 + String string + Slice []Sub + Array [5]Sub + Complex64 complex64 + Complex128 complex128 +} +``` + +The following function will return the `Float64` field from this struct. + +[embedmd]:# (asm.go go /.*TEXT.*FieldFloat64/ /RET.*/) +```go + TEXT("FieldFloat64", "func(s Struct) float64") + f64 := Load(Param("s").Field("Float64"), Xv()) + Store(f64, ReturnIndex(0)) + RET() +``` + +## Complex Values + +Complex types `complex{64,128}` are actually just pairs of `float{32,64}` values. These can be accessed with the `Real()` and `Imag()` methods. For example the following function returns the imaginary part of the `Complex64` struct field. + +[embedmd]:# (asm.go go /.*TEXT.*FieldComplex64Imag/ /RET.*/) +```go + TEXT("FieldComplex64Imag", "func(s Struct) float32") + c64i := Load(Param("s").Field("Complex64").Imag(), Xv()) + Store(c64i, ReturnIndex(0)) + RET() +``` + +## Nested Data Structures + +The above methods may be composed to reference arbitrarily nested data structures. For example, the following returns `s.Array[2].B[2]`. + +[embedmd]:# (asm.go go /.*TEXT.*FieldArrayTwoBTwo/ /RET.*/) +```go + TEXT("FieldArrayTwoBTwo", "func(s Struct) byte") + b2 := Load(Param("s").Field("Array").Index(2).Field("B").Index(2), GP8v()) + Store(b2, ReturnIndex(0)) + RET() +``` diff --git a/examples/args/args.s b/examples/args/args.s index 90204f7..59bb192 100644 --- a/examples/args/args.s +++ b/examples/args/args.s @@ -1,5 +1,11 @@ // Code generated by command: go run asm.go -out args.s -stubs stub.go. DO NOT EDIT. +// func Second(x int32, y int32) int32 +TEXT ·Second(SB), $0-12 + MOVL y+4(FP), AX + MOVL AX, ret+8(FP) + RET + // func StringLen(s string) int TEXT ·StringLen(SB), $0-24 MOVQ s_len+8(FP), AX diff --git a/examples/args/args_test.go b/examples/args/args_test.go index c501510..e8f1d95 100644 --- a/examples/args/args_test.go +++ b/examples/args/args_test.go @@ -11,6 +11,7 @@ func TestFunctionsEqual(t *testing.T) { cases := []struct { f, g interface{} }{ + {Second, func(x, y int32) int32 { return y }}, {StringLen, func(s string) int { return len(s) }}, {SliceLen, func(s []int) int { return len(s) }}, {SliceCap, func(s []int) int { return cap(s) }}, diff --git a/examples/args/asm.go b/examples/args/asm.go index 34a327d..762f734 100644 --- a/examples/args/asm.go +++ b/examples/args/asm.go @@ -9,6 +9,11 @@ import ( func main() { Package("github.com/mmcloughlin/avo/examples/args") + TEXT("Second", "func(x, y int32) int32") + y := Load(Param("y"), GP32v()) + Store(y, ReturnIndex(0)) + RET() + TEXT("StringLen", "func(s string) int") strlen := Load(Param("s").Len(), GP64v()) Store(strlen, ReturnIndex(0)) diff --git a/examples/args/stub.go b/examples/args/stub.go index 1c46272..b962a19 100644 --- a/examples/args/stub.go +++ b/examples/args/stub.go @@ -2,6 +2,8 @@ package args +func Second(x int32, y int32) int32 + func StringLen(s string) int func SliceLen(s []int) int