| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package compare |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| "cmd/internal/src" |
| "cmd/internal/sys" |
| "testing" |
| ) |
| |
| type typefn func() *types.Type |
| |
| func init() { |
| // These are the few constants that need to be initialized in order to use |
| // the types package without using the typecheck package by calling |
| // typecheck.InitUniverse() (the normal way to initialize the types package). |
| types.PtrSize = 8 |
| types.RegSize = 8 |
| types.MaxWidth = 1 << 50 |
| typecheck.InitUniverse() |
| base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}} |
| } |
| |
| func TestEqStructCost(t *testing.T) { |
| newByteField := func(parent *types.Type, offset int64) *types.Field { |
| f := types.NewField(src.XPos{}, parent.Sym(), types.ByteType) |
| f.Offset = offset |
| return f |
| } |
| newArrayField := func(parent *types.Type, offset int64, len int64, kind types.Kind) *types.Field { |
| f := types.NewField(src.XPos{}, parent.Sym(), types.NewArray(types.Types[kind], len)) |
| // Call Type.Size here to force the size calculation to be done. If not done here the size returned later is incorrect. |
| f.Type.Size() |
| f.Offset = offset |
| return f |
| } |
| newField := func(parent *types.Type, offset int64, kind types.Kind) *types.Field { |
| f := types.NewField(src.XPos{}, parent.Sym(), types.Types[kind]) |
| f.Offset = offset |
| return f |
| } |
| tt := []struct { |
| name string |
| cost int64 |
| nonMergeLoadCost int64 |
| tfn typefn |
| }{ |
| {"struct without fields", 0, 0, |
| func() *types.Type { |
| return types.NewStruct([]*types.Field{}) |
| }}, |
| {"struct with 1 byte field", 1, 1, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := []*types.Field{ |
| newByteField(parent, 0), |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 8 byte fields", 1, 8, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 8) |
| for i := range fields { |
| fields[i] = newByteField(parent, int64(i)) |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 16 byte fields", 2, 16, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 16) |
| for i := range fields { |
| fields[i] = newByteField(parent, int64(i)) |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 32 byte fields", 4, 32, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 32) |
| for i := range fields { |
| fields[i] = newByteField(parent, int64(i)) |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 2 int32 fields", 1, 2, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 2) |
| for i := range fields { |
| fields[i] = newField(parent, int64(i*4), types.TINT32) |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 2 int32 fields and 1 int64", 2, 3, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 3) |
| fields[0] = newField(parent, int64(0), types.TINT32) |
| fields[1] = newField(parent, int64(4), types.TINT32) |
| fields[2] = newField(parent, int64(8), types.TINT64) |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 1 int field and 1 string", 3, 3, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 2) |
| fields[0] = newField(parent, int64(0), types.TINT64) |
| fields[1] = newField(parent, int64(8), types.TSTRING) |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 2 strings", 4, 4, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := make([]*types.Field, 2) |
| fields[0] = newField(parent, int64(0), types.TSTRING) |
| fields[1] = newField(parent, int64(8), types.TSTRING) |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with 1 large byte array field", 26, 101, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := []*types.Field{ |
| newArrayField(parent, 0, 101, types.TUINT16), |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| {"struct with string array field", 4, 4, |
| func() *types.Type { |
| parent := types.NewStruct([]*types.Field{}) |
| fields := []*types.Field{ |
| newArrayField(parent, 0, 2, types.TSTRING), |
| } |
| parent.SetFields(fields) |
| return parent |
| }, |
| }, |
| } |
| |
| for _, tc := range tt { |
| t.Run(tc.name, func(t *testing.T) { |
| want := tc.cost |
| base.Ctxt.Arch.CanMergeLoads = true |
| actual := EqStructCost(tc.tfn()) |
| if actual != want { |
| t.Errorf("CanMergeLoads=true EqStructCost(%v) = %d, want %d", tc.tfn, actual, want) |
| } |
| |
| base.Ctxt.Arch.CanMergeLoads = false |
| want = tc.nonMergeLoadCost |
| actual = EqStructCost(tc.tfn()) |
| if actual != want { |
| t.Errorf("CanMergeLoads=false EqStructCost(%v) = %d, want %d", tc.tfn, actual, want) |
| } |
| }) |
| } |
| } |