1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package test
6
7// This file contains utility routines and harness infrastructure used
8// by the ABI tests in "abiutils_test.go".
9
10import (
11	"cmd/compile/internal/abi"
12	"cmd/compile/internal/ir"
13	"cmd/compile/internal/typecheck"
14	"cmd/compile/internal/types"
15	"cmd/internal/src"
16	"fmt"
17	"strings"
18	"testing"
19	"text/scanner"
20)
21
22func mkParamResultField(t *types.Type, s *types.Sym, which ir.Class) *types.Field {
23	field := types.NewField(src.NoXPos, s, t)
24	n := ir.NewNameAt(src.NoXPos, s, t)
25	n.Class = which
26	field.Nname = n
27	return field
28}
29
30// mkstruct is a helper routine to create a struct type with fields
31// of the types specified in 'fieldtypes'.
32func mkstruct(fieldtypes ...*types.Type) *types.Type {
33	fields := make([]*types.Field, len(fieldtypes))
34	for k, t := range fieldtypes {
35		if t == nil {
36			panic("bad -- field has no type")
37		}
38		f := types.NewField(src.NoXPos, nil, t)
39		fields[k] = f
40	}
41	s := types.NewStruct(fields)
42	return s
43}
44
45func mkFuncType(rcvr *types.Type, ins []*types.Type, outs []*types.Type) *types.Type {
46	q := typecheck.Lookup("?")
47	inf := []*types.Field{}
48	for _, it := range ins {
49		inf = append(inf, mkParamResultField(it, q, ir.PPARAM))
50	}
51	outf := []*types.Field{}
52	for _, ot := range outs {
53		outf = append(outf, mkParamResultField(ot, q, ir.PPARAMOUT))
54	}
55	var rf *types.Field
56	if rcvr != nil {
57		rf = mkParamResultField(rcvr, q, ir.PPARAM)
58	}
59	return types.NewSignature(rf, inf, outf)
60}
61
62type expectedDump struct {
63	dump string
64	file string
65	line int
66}
67
68func tokenize(src string) []string {
69	var s scanner.Scanner
70	s.Init(strings.NewReader(src))
71	res := []string{}
72	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
73		res = append(res, s.TokenText())
74	}
75	return res
76}
77
78func verifyParamResultOffset(t *testing.T, f *types.Field, r abi.ABIParamAssignment, which string, idx int) int {
79	n := f.Nname.(*ir.Name)
80	if n.FrameOffset() != int64(r.Offset()) {
81		t.Errorf("%s %d: got offset %d wanted %d t=%v",
82			which, idx, r.Offset(), n.Offset_, f.Type)
83		return 1
84	}
85	return 0
86}
87
88func makeExpectedDump(e string) expectedDump {
89	return expectedDump{dump: e}
90}
91
92func difftokens(atoks []string, etoks []string) string {
93	if len(atoks) != len(etoks) {
94		return fmt.Sprintf("expected %d tokens got %d",
95			len(etoks), len(atoks))
96	}
97	for i := 0; i < len(etoks); i++ {
98		if etoks[i] == atoks[i] {
99			continue
100		}
101
102		return fmt.Sprintf("diff at token %d: expected %q got %q",
103			i, etoks[i], atoks[i])
104	}
105	return ""
106}
107
108func nrtest(t *testing.T, ft *types.Type, expected int) {
109	types.CalcSize(ft)
110	got := configAMD64.NumParamRegs(ft)
111	if got != expected {
112		t.Errorf("]\nexpected num regs = %d, got %d, type %v", expected, got, ft)
113	}
114}
115
116func abitest(t *testing.T, ft *types.Type, exp expectedDump) {
117
118	types.CalcSize(ft)
119
120	// Analyze with full set of registers.
121	regRes := configAMD64.ABIAnalyze(ft, false)
122	regResString := strings.TrimSpace(regRes.String())
123
124	// Check results.
125	reason := difftokens(tokenize(regResString), tokenize(exp.dump))
126	if reason != "" {
127		t.Errorf("\nexpected:\n%s\ngot:\n%s\nreason: %s",
128			strings.TrimSpace(exp.dump), regResString, reason)
129	}
130
131}
132