// 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) } }) } }