diff --git a/examples/add/add.s b/examples/add/add.s index cec93e46..5d867411 100644 --- a/examples/add/add.s +++ b/examples/add/add.s @@ -3,7 +3,7 @@ #include "textflag.h" // func Add(x uint64, y uint64) uint64 -TEXT ·Add(SB),0,$0-24 +TEXT ·Add(SB), 0, $0-24 MOVQ x(FP), AX MOVQ y+8(FP), CX ADDQ AX, CX diff --git a/examples/complex/complex.s b/examples/complex/complex.s index 44d369bc..b58af205 100644 --- a/examples/complex/complex.s +++ b/examples/complex/complex.s @@ -3,19 +3,19 @@ #include "textflag.h" // func Real(x complex128) float64 -TEXT ·Real(SB),0,$0-24 +TEXT ·Real(SB), 0, $0-24 MOVSD x_real(FP), X0 MOVSD X0, ret+16(FP) RET // func Imag(x complex128) float64 -TEXT ·Imag(SB),0,$0-24 +TEXT ·Imag(SB), 0, $0-24 MOVSD x_imag+8(FP), X0 MOVSD X0, ret+16(FP) RET // func Norm(x complex128) float64 -TEXT ·Norm(SB),0,$0-24 +TEXT ·Norm(SB), 0, $0-24 MOVSD x_real(FP), X0 MOVSD x_imag+8(FP), X1 MULSD X0, X0 diff --git a/examples/components/components.s b/examples/components/components.s index ec1c3dc6..a7103601 100644 --- a/examples/components/components.s +++ b/examples/components/components.s @@ -3,103 +3,103 @@ #include "textflag.h" // func StringLen(s string) int -TEXT ·StringLen(SB),0,$0-24 +TEXT ·StringLen(SB), 0, $0-24 MOVQ s_len+8(FP), AX MOVQ AX, ret+16(FP) RET // func SliceLen(s []int) int -TEXT ·SliceLen(SB),0,$0-32 +TEXT ·SliceLen(SB), 0, $0-32 MOVQ s_len+8(FP), AX MOVQ AX, ret+24(FP) RET // func SliceCap(s []int) int -TEXT ·SliceCap(SB),0,$0-32 +TEXT ·SliceCap(SB), 0, $0-32 MOVQ s_cap+16(FP), AX MOVQ AX, ret+24(FP) RET // func ArrayThree(a [7]uint64) uint64 -TEXT ·ArrayThree(SB),0,$0-64 +TEXT ·ArrayThree(SB), 0, $0-64 MOVQ a_3+24(FP), AX MOVQ AX, ret+56(FP) RET // func FieldByte(s Struct) byte -TEXT ·FieldByte(SB),0,$0-177 +TEXT ·FieldByte(SB), 0, $0-177 MOVB s_Byte(FP), AL MOVB AL, ret+176(FP) RET // func FieldInt8(s Struct) int8 -TEXT ·FieldInt8(SB),0,$0-177 +TEXT ·FieldInt8(SB), 0, $0-177 MOVB s_Int8+1(FP), AL MOVB AL, ret+176(FP) RET // func FieldUint16(s Struct) uint16 -TEXT ·FieldUint16(SB),0,$0-178 +TEXT ·FieldUint16(SB), 0, $0-178 MOVW s_Uint16+2(FP), AX MOVW AX, ret+176(FP) RET // func FieldInt32(s Struct) int32 -TEXT ·FieldInt32(SB),0,$0-180 +TEXT ·FieldInt32(SB), 0, $0-180 MOVL s_Int32+4(FP), AX MOVL AX, ret+176(FP) RET // func FieldUint64(s Struct) uint64 -TEXT ·FieldUint64(SB),0,$0-184 +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-180 +TEXT ·FieldFloat32(SB), 0, $0-180 MOVSS s_Float32+16(FP), X0 MOVSS X0, ret+176(FP) RET // func FieldFloat64(s Struct) float64 -TEXT ·FieldFloat64(SB),0,$0-184 +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 +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 +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-177 +TEXT ·FieldArrayTwoBTwo(SB), 0, $0-177 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-178 +TEXT ·FieldArrayOneC(SB), 0, $0-178 MOVW s_Array_1_C+100(FP), AX MOVW AX, ret+176(FP) RET // func FieldComplex64Imag(s Struct) float32 -TEXT ·FieldComplex64Imag(SB),0,$0-180 +TEXT ·FieldComplex64Imag(SB), 0, $0-180 MOVSS s_Complex64_imag+156(FP), X0 MOVSS X0, ret+176(FP) RET // func FieldComplex128Real(s Struct) float64 -TEXT ·FieldComplex128Real(SB),0,$0-184 +TEXT ·FieldComplex128Real(SB), 0, $0-184 MOVSD s_Complex128_real+160(FP), X0 MOVSD X0, ret+176(FP) RET diff --git a/examples/data/asm.go b/examples/data/asm.go index e4841dfb..c1144ac8 100644 --- a/examples/data/asm.go +++ b/examples/data/asm.go @@ -20,15 +20,14 @@ func main() { 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 + i := Load(Param("i"), GP64v()) ptr := Mem{Base: GP64v()} - LEAQ(bytes, ptr.Base) // LEAQ b<>+0x00(SB), BX + LEAQ(bytes, ptr.Base) 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 + MOVB(ptr.Idx(i, 1), b) + Store(b, ReturnIndex(0)) + RET() Generate() } diff --git a/examples/data/data.s b/examples/data/data.s index dcc372f1..d722ea5c 100644 --- a/examples/data/data.s +++ b/examples/data/data.s @@ -10,10 +10,10 @@ 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 +GLOBL bytes<>(SB), RODATA, $40 // func DataAt(i int) byte -TEXT ·DataAt(SB),0,$0-9 +TEXT ·DataAt(SB), 0, $0-9 MOVQ i(FP), AX LEAQ bytes<>(SB), CX MOVB (CX)(AX*1), AL diff --git a/examples/fnv1a/fnv1a.s b/examples/fnv1a/fnv1a.s index d54530ce..b466b59e 100644 --- a/examples/fnv1a/fnv1a.s +++ b/examples/fnv1a/fnv1a.s @@ -3,7 +3,7 @@ #include "textflag.h" // func Hash64(data []byte) uint64 -TEXT ·Hash64(SB),0,$0-32 +TEXT ·Hash64(SB), 0, $0-32 MOVQ data_base(FP), CX MOVQ data_len+8(FP), BX MOVQ $0xcbf29ce484222325, AX diff --git a/examples/sha1/sha1.s b/examples/sha1/sha1.s index 5cc844df..d5e3752c 100644 --- a/examples/sha1/sha1.s +++ b/examples/sha1/sha1.s @@ -3,7 +3,7 @@ #include "textflag.h" // func block(h *[5]uint32, m []byte) -TEXT ·block(SB),0,$64-32 +TEXT ·block(SB), 0, $64-32 MOVQ h(FP), AX MOVQ m_base+8(FP), CX MOVL (AX), DX diff --git a/examples/stadtx/stadtx.s b/examples/stadtx/stadtx.s index 44f5e47a..54e45268 100644 --- a/examples/stadtx/stadtx.s +++ b/examples/stadtx/stadtx.s @@ -3,7 +3,7 @@ #include "textflag.h" // func Hash(state *State, key []byte) uint64 -TEXT ·Hash(SB),0,$0-40 +TEXT ·Hash(SB), 0, $0-40 MOVQ state(FP), AX MOVQ key_base+8(FP), CX MOVQ key_len+16(FP), DX diff --git a/examples/sum/sum.s b/examples/sum/sum.s index 3ebb7ef6..24d042ef 100644 --- a/examples/sum/sum.s +++ b/examples/sum/sum.s @@ -3,7 +3,7 @@ #include "textflag.h" // func Sum(xs []uint64) uint64 -TEXT ·Sum(SB),0,$0-32 +TEXT ·Sum(SB), 0, $0-32 MOVQ xs_base(FP), AX MOVQ xs_len+8(FP), CX XORQ DX, DX diff --git a/printer/goasm.go b/printer/goasm.go index 4b3ece22..4104e305 100644 --- a/printer/goasm.go +++ b/printer/goasm.go @@ -1,6 +1,7 @@ package printer import ( + "strconv" "strings" "github.com/mmcloughlin/avo" @@ -48,7 +49,7 @@ func (p *goasm) include(path string) { func (p *goasm) function(f *avo.Function) { p.NL() p.Comment(f.Stub()) - p.Printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name, f.FrameBytes(), f.ArgumentBytes()) + p.Printf("TEXT %s%s(SB), 0, %s\n", dot, f.Name, textsize(f)) for _, node := range f.Nodes { switch n := node.(type) { @@ -73,7 +74,24 @@ func (p *goasm) global(g *avo.Global) { 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) + p.Printf("GLOBL %s(SB), RODATA, $%d\n", g.Symbol, g.Size) +} + +func textsize(f *avo.Function) string { + // Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265 + // + // case TYPE_TEXTSIZE: + // if a.Val.(int32) == objabi.ArgsSizeUnknown { + // str = fmt.Sprintf("$%d", a.Offset) + // } else { + // str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32)) + // } + // + s := "$" + strconv.Itoa(f.FrameBytes()) + if argsize := f.ArgumentBytes(); argsize > 0 { + return s + "-" + strconv.Itoa(argsize) + } + return s } func joinOperands(operands []operand.Op) string { diff --git a/printer/goasm_test.go b/printer/goasm_test.go new file mode 100644 index 00000000..0ab7d392 --- /dev/null +++ b/printer/goasm_test.go @@ -0,0 +1,63 @@ +package printer_test + +import ( + "testing" + + "github.com/mmcloughlin/avo/build" + "github.com/mmcloughlin/avo/printer" + "github.com/mmcloughlin/avo/reg" +) + +func TestBasic(t *testing.T) { + ctx := build.NewContext() + ctx.Function("add") + ctx.SignatureExpr("func(x, y uint64) uint64") + x := ctx.Load(ctx.Param("x"), reg.RAX) + y := ctx.Load(ctx.Param("y"), reg.R9) + ctx.ADDQ(x, y) + ctx.Store(y, ctx.ReturnIndex(0)) + ctx.RET() + + AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ + "// Code generated by avo. DO NOT EDIT.", + "", + "#include \"textflag.h\"", + "", + "// func add(x uint64, y uint64) uint64", + "TEXT ·add(SB), 0, $0-24", + "\tMOVQ\tx(FP), AX", + "\tMOVQ\ty+8(FP), R9", + "\tADDQ\tAX, R9", + "\tMOVQ\tR9, ret+16(FP)", + "\tRET", + "", + }) +} + +func TestTextSize(t *testing.T) { + ctx := build.NewContext() + + ctx.Function("noargs") + ctx.SignatureExpr("func()") + ctx.AllocLocal(16) + ctx.RET() + + ctx.Function("withargs") + ctx.SignatureExpr("func(x, y uint64) uint64") + ctx.RET() + + AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ + "// Code generated by avo. DO NOT EDIT.", + "", + "#include \"textflag.h\"", + "", + "// func noargs()", + "TEXT ·noargs(SB), 0, $16", // expect only the frame size + "\tRET", + "", + "// func withargs(x uint64, y uint64) uint64", + "TEXT ·withargs(SB), 0, $0-24", // expect both frame size and argument size + "\tRET", + "", + }) +} diff --git a/printer/util_test.go b/printer/util_test.go new file mode 100644 index 00000000..16c6efee --- /dev/null +++ b/printer/util_test.go @@ -0,0 +1,43 @@ +package printer_test + +import ( + "strings" + "testing" + + "github.com/mmcloughlin/avo/build" + "github.com/mmcloughlin/avo/printer" +) + +func AssertPrintsLines(t *testing.T, ctx *build.Context, pb printer.Builder, expect []string) { + t.Helper() + + output := Print(t, ctx, pb) + lines := strings.Split(output, "\n") + + if len(expect) != len(lines) { + t.Fatalf("have %d lines of output; expected %d", len(lines), len(expect)) + } + + for i := range expect { + if expect[i] != lines[i] { + t.Errorf("mismatch on line %d:\n\tgot\t%s\n\texpect\t%s\n", i, lines[i], expect[i]) + } + } +} + +func Print(t *testing.T, ctx *build.Context, pb printer.Builder) string { + t.Helper() + + f, errs := ctx.Result() + if errs != nil { + t.Fatal(errs) + } + + p := pb(printer.NewDefaultConfig()) + b, err := p.Print(f) + if err != nil { + t.Fatal(err) + } + + return string(b) +}