2020-11-24 18:10:11 -05:00
|
|
|
// Copyright 2020 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 gc
|
|
|
|
|
|
|
|
|
|
// This file contains utility routines and harness infrastructure used
|
|
|
|
|
// by the ABI tests in "abiutils_test.go".
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/compile/internal/ir"
|
|
|
|
|
"cmd/compile/internal/types"
|
|
|
|
|
"cmd/internal/src"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"text/scanner"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func mkParamResultField(t *types.Type, s *types.Sym, which ir.Class) *types.Field {
|
|
|
|
|
field := types.NewField(src.NoXPos, s, t)
|
|
|
|
|
n := NewName(s)
|
2020-12-23 00:02:08 -05:00
|
|
|
n.Class_ = which
|
2020-11-24 18:10:11 -05:00
|
|
|
field.Nname = n
|
|
|
|
|
n.SetType(t)
|
|
|
|
|
return field
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mkstruct is a helper routine to create a struct type with fields
|
|
|
|
|
// of the types specified in 'fieldtypes'.
|
|
|
|
|
func mkstruct(fieldtypes []*types.Type) *types.Type {
|
|
|
|
|
fields := make([]*types.Field, len(fieldtypes))
|
|
|
|
|
for k, t := range fieldtypes {
|
|
|
|
|
if t == nil {
|
|
|
|
|
panic("bad -- field has no type")
|
|
|
|
|
}
|
|
|
|
|
f := types.NewField(src.NoXPos, nil, t)
|
|
|
|
|
fields[k] = f
|
|
|
|
|
}
|
|
|
|
|
s := types.NewStruct(types.LocalPkg, fields)
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mkFuncType(rcvr *types.Type, ins []*types.Type, outs []*types.Type) *types.Type {
|
|
|
|
|
q := lookup("?")
|
|
|
|
|
inf := []*types.Field{}
|
|
|
|
|
for _, it := range ins {
|
|
|
|
|
inf = append(inf, mkParamResultField(it, q, ir.PPARAM))
|
|
|
|
|
}
|
|
|
|
|
outf := []*types.Field{}
|
|
|
|
|
for _, ot := range outs {
|
|
|
|
|
outf = append(outf, mkParamResultField(ot, q, ir.PPARAMOUT))
|
|
|
|
|
}
|
|
|
|
|
var rf *types.Field
|
|
|
|
|
if rcvr != nil {
|
|
|
|
|
rf = mkParamResultField(rcvr, q, ir.PPARAM)
|
|
|
|
|
}
|
|
|
|
|
return types.NewSignature(types.LocalPkg, rf, inf, outf)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type expectedDump struct {
|
|
|
|
|
dump string
|
|
|
|
|
file string
|
|
|
|
|
line int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tokenize(src string) []string {
|
|
|
|
|
var s scanner.Scanner
|
|
|
|
|
s.Init(strings.NewReader(src))
|
|
|
|
|
res := []string{}
|
|
|
|
|
for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
|
|
|
|
|
res = append(res, s.TokenText())
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func verifyParamResultOffset(t *testing.T, f *types.Field, r ABIParamAssignment, which string, idx int) int {
|
2020-12-14 13:20:06 -05:00
|
|
|
n := ir.AsNode(f.Nname).(*ir.Name)
|
2020-12-17 02:56:26 -05:00
|
|
|
if n.FrameOffset() != int64(r.Offset) {
|
2020-11-24 18:10:11 -05:00
|
|
|
t.Errorf("%s %d: got offset %d wanted %d t=%v",
|
2020-12-23 00:02:08 -05:00
|
|
|
which, idx, r.Offset, n.Offset_, f.Type)
|
2020-11-24 18:10:11 -05:00
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func makeExpectedDump(e string) expectedDump {
|
|
|
|
|
return expectedDump{dump: e}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func difftokens(atoks []string, etoks []string) string {
|
|
|
|
|
if len(atoks) != len(etoks) {
|
|
|
|
|
return fmt.Sprintf("expected %d tokens got %d",
|
|
|
|
|
len(etoks), len(atoks))
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < len(etoks); i++ {
|
|
|
|
|
if etoks[i] == atoks[i] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("diff at token %d: expected %q got %q",
|
|
|
|
|
i, etoks[i], atoks[i])
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func abitest(t *testing.T, ft *types.Type, exp expectedDump) {
|
|
|
|
|
|
|
|
|
|
dowidth(ft)
|
|
|
|
|
|
|
|
|
|
// Analyze with full set of registers.
|
|
|
|
|
regRes := ABIAnalyze(ft, configAMD64)
|
|
|
|
|
regResString := strings.TrimSpace(regRes.String())
|
|
|
|
|
|
|
|
|
|
// Check results.
|
|
|
|
|
reason := difftokens(tokenize(regResString), tokenize(exp.dump))
|
|
|
|
|
if reason != "" {
|
|
|
|
|
t.Errorf("\nexpected:\n%s\ngot:\n%s\nreason: %s",
|
|
|
|
|
strings.TrimSpace(exp.dump), regResString, reason)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Analyze again with empty register set.
|
|
|
|
|
empty := ABIConfig{}
|
|
|
|
|
emptyRes := ABIAnalyze(ft, empty)
|
|
|
|
|
emptyResString := emptyRes.String()
|
|
|
|
|
|
|
|
|
|
// Walk the results and make sure the offsets assigned match
|
|
|
|
|
// up with those assiged by dowidth. This checks to make sure that
|
|
|
|
|
// when we have no available registers the ABI assignment degenerates
|
|
|
|
|
// back to the original ABI0.
|
|
|
|
|
|
|
|
|
|
// receiver
|
|
|
|
|
failed := 0
|
|
|
|
|
rfsl := ft.Recvs().Fields().Slice()
|
|
|
|
|
poff := 0
|
|
|
|
|
if len(rfsl) != 0 {
|
|
|
|
|
failed |= verifyParamResultOffset(t, rfsl[0], emptyRes.inparams[0], "receiver", 0)
|
|
|
|
|
poff = 1
|
|
|
|
|
}
|
|
|
|
|
// params
|
|
|
|
|
pfsl := ft.Params().Fields().Slice()
|
|
|
|
|
for k, f := range pfsl {
|
|
|
|
|
verifyParamResultOffset(t, f, emptyRes.inparams[k+poff], "param", k)
|
|
|
|
|
}
|
|
|
|
|
// results
|
|
|
|
|
ofsl := ft.Results().Fields().Slice()
|
|
|
|
|
for k, f := range ofsl {
|
|
|
|
|
failed |= verifyParamResultOffset(t, f, emptyRes.outparams[k], "result", k)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if failed != 0 {
|
|
|
|
|
t.Logf("emptyres:\n%s\n", emptyResString)
|
|
|
|
|
}
|
|
|
|
|
}
|