blob: bbddf67bf78db3c1ccace43f41b6e864747f9b03 [file] [log] [blame]
// Copyright 2017 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 main
import (
"bytes"
"regexp"
"strings"
"testing"
"golang.org/x/arch/x86/x86csv"
)
func TestYtabKey(t *testing.T) {
type testCase struct {
insts []*x86csv.Inst
key string
}
test := func(key string, goSyntaxes ...string) testCase {
insts := make([]*x86csv.Inst, len(goSyntaxes))
for i, stx := range goSyntaxes {
insts[i] = &x86csv.Inst{Go: stx}
}
return testCase{insts: insts, key: key}
}
tests := []testCase{
test("", "VZEROALL"),
test("i8,x/m,x", "VAESKEYGENASSIST imm8, xmm2/m128, xmm1"),
test("x/m,xV,x;y/m,yV,y", "VADDPD xmm2/m128, xmmV, xmm1", "VADDPD ymm2/m256, ymmV, ymm1"),
test("x/m,xV,x", "VADDSD xmm2/m64, xmmV, xmm1"),
test("x/m,x", "VAESIMC xmm2/m128, xmm1"),
test("r/m,i8;r/m,i8;r/m,i8;r/m,i8", "XOR r/m16, imm8", "XOR r/m32, imm8", "XOR r/m64, imm8", "XOR r/m8, imm8"),
test("xV,yV", "OP1 xmm1V, ymm2V "),
test("x,y", "OP2 xmm, ymm"),
test("x/m,r/m", "OP3 xmm3/m32,r/m32"),
}
for _, test := range tests {
op := test.insts[0].GoOpcode()
key := ytabKey(op, test.insts)
if key != test.key {
t.Errorf("ytabKey(%s, ...)\nwant: '%s'\nhave: '%s'", op, key, test.key)
}
}
}
func TestVexExpr(t *testing.T) {
tests := map[string]string{
"VEX.NDS.256.0F.WIG": "vexNDS|vex256|vex0F|vexWIG",
"VEX.256.66.0F.WIG": "vexNOVSR|vex256|vex66|vex0F|vexWIG",
"VEX.128.66.0F38.WIG": "vexNOVSR|vex128|vex66|vex0F38|vexWIG",
"VEX.NDS.LIG.F2.0F.WIG": "vexNDS|vexLIG|vexF2|vex0F|vexWIG",
"VEX.NDD.LIG.F2.0F.WIG": "vexNDD|vexLIG|vexF2|vex0F|vexWIG",
"VEX.DDS.LIG.F2.0F.WIG": "vexDDS|vexLIG|vexF2|vex0F|vexWIG",
"VEX.NDS.0F": "vexNDS|vex0F",
"VEX.0F": "vexNOVSR|vex0F",
"VEX.0F.W0": "vexNOVSR|vex0F|vexW0",
"VEX.66.0F.W1": "vexNOVSR|vex66|vex0F|vexW1",
}
for input, want := range tests {
have := vexExpr(input)
if have != want {
t.Errorf("vexPrefixExpr(%q)\nwant: %#v\nhave: %#v", input, want, have)
}
}
}
func TestParseEncoding(t *testing.T) {
tests := map[string]encoding{
"VEX.LZ.0F38.W0 F2 /r": {
vex: "VEX.LZ.0F38.W0",
opbyte: "F2",
},
"VEX.NDD.LZ.0F38.W0 F3 /1": {
vex: "VEX.NDD.LZ.0F38.W0",
opbyte: "F3",
opdigit: "1",
},
"VEX.NDS.128.66.0F3A.W0 4B /r /is4": {
vex: "VEX.NDS.128.66.0F3A.W0",
opbyte: "4B",
},
}
for input, want := range tests {
have := parseEncoding(input)
if have != want {
t.Errorf("vexPrefixExpr(%q)\nwant: %#v\nhave: %#v", input, want, have)
}
}
}
func TestGenerateAenum(t *testing.T) {
input := `// Code generated by x86avxgen; DO NOT EDIT.
package eax
const (
unrelatedOne = iota
unrelatedTwo
)
/*
Leading multiline comment;
Line 2.
*/
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p x86
const (
AAAA = iota + 4*iota // AAAA comment.
ACCC // ACCC comment.
ABBB
AFFF // AFFF comment.
ALAST // ALAST comment.
)
// Top-level floating comment.
`
expected := `// Code generated by x86avxgen; DO NOT EDIT.
package eax
const (
unrelatedOne = iota
unrelatedTwo
)
// Top-level floating comment.
/*
Leading multiline comment;
Line 2.
*/
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p x86
const (
AAAA = iota + 4*iota // AAAA comment.
ABBB
ACCC // ACCC comment.
ADDD
AEEE
AFFF // AFFF comment.
AZZZ
ALAST // ALAST comment.
)
`
r := strings.NewReader(input)
var buf bytes.Buffer
err := generateAenum(r, &buf, []string{
"ZZZ",
"EEE",
"DDD",
})
if err != nil {
t.Fatal(err)
}
output := buf.String()
if expected != output {
t.Errorf("output mismatch:\nwant: %s\nhave: %s",
expected, output)
}
}
func TestUncommentTestLine(t *testing.T) {
// Note that is should also fix XMM0 to X0.
input := `
//TODO: ADCXL (BX), DX // 660f38f613
//TODO: ADCXL (R11), DX // 66410f38f613
//TODO: ADDSUBPD (BX), X2 // 660fd013
//TODO: BLENDVPD XMM0, (BX), X2 // 660f381513`
want := `
ADCXL (BX), DX // 660f38f613
ADCXL (R11), DX // 66410f38f613
ADDSUBPD (BX), X2 // 660fd013
BLENDVPD X0, (BX), X2 // 660f381513`
lines := strings.Split(input, "\n")
for i := range lines {
if len(lines[i]) > 10 {
lines[i] = uncommentedTestLine(lines[i])
}
}
have := strings.Join(lines, "\n")
if want != have {
t.Errorf("output mismatch:\nwant: `%s`\nhave: `%s`",
want, have)
}
}
func TestOutput(t *testing.T) {
// Using already existing AVX optabs to check generated output.
// This does not cover new instructions though.
// These lines can be retrieved by:
// $ grep ', Pvex,' src/cmd/internal/obj/x86/asm6.go
existingOptabs := `
{AANDNL, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W0, 0xF2}},
{AANDNQ, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W1, 0xF2}},
{ABEXTRL, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W0, 0xF7}},
{ABEXTRQ, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W1, 0xF7}},
{ABZHIL, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W0, 0xF5}},
{ABZHIQ, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_0F38_W1, 0xF5}},
{AMULXL, yvex_r3, Pvex, [23]uint8{VEX_NDD_LZ_F2_0F38_W0, 0xF6}},
{AMULXQ, yvex_r3, Pvex, [23]uint8{VEX_NDD_LZ_F2_0F38_W1, 0xF6}},
{APDEPL, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_F2_0F38_W0, 0xF5}},
{APDEPQ, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_F2_0F38_W1, 0xF5}},
{APEXTL, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_F3_0F38_W0, 0xF5}},
{APEXTQ, yvex_r3, Pvex, [23]uint8{VEX_NDS_LZ_F3_0F38_W1, 0xF5}},
{ASARXL, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_F3_0F38_W0, 0xF7}},
{ASARXQ, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_F3_0F38_W1, 0xF7}},
{ASHLXL, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_66_0F38_W0, 0xF7}},
{ASHLXQ, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_66_0F38_W1, 0xF7}},
{ASHRXL, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_F2_0F38_W0, 0xF7}},
{ASHRXQ, yvex_vmr3, Pvex, [23]uint8{VEX_NDS_LZ_F2_0F38_W1, 0xF7}},
{AVMOVDQU, yvex_vmovdqa, Pvex, [23]uint8{VEX_NOVSR_128_F3_0F_WIG, 0x6F, VEX_NOVSR_128_F3_0F_WIG, 0x7F, VEX_NOVSR_256_F3_0F_WIG, 0x6F, VEX_NOVSR_256_F3_0F_WIG, 0x7F}},
{AVMOVDQA, yvex_vmovdqa, Pvex, [23]uint8{VEX_NOVSR_128_66_0F_WIG, 0x6F, VEX_NOVSR_128_66_0F_WIG, 0x7F, VEX_NOVSR_256_66_0F_WIG, 0x6F, VEX_NOVSR_256_66_0F_WIG, 0x7F}},
{AVMOVNTDQ, yvex_vmovntdq, Pvex, [23]uint8{VEX_NOVSR_128_66_0F_WIG, 0xE7, VEX_NOVSR_256_66_0F_WIG, 0xE7}},
{AVPCMPEQB, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0x74, VEX_NDS_256_66_0F_WIG, 0x74}},
{AVPXOR, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0xEF, VEX_NDS_256_66_0F_WIG, 0xEF}},
{AVPMOVMSKB, yvex_xyr2, Pvex, [23]uint8{VEX_NOVSR_128_66_0F_WIG, 0xD7, VEX_NOVSR_256_66_0F_WIG, 0xD7}},
{AVPAND, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0xDB, VEX_NDS_256_66_0F_WIG, 0xDB}},
{AVPBROADCASTB, yvex_vpbroadcast, Pvex, [23]uint8{VEX_NOVSR_128_66_0F38_W0, 0x78, VEX_NOVSR_256_66_0F38_W0, 0x78}},
{AVPTEST, yvex_xy2, Pvex, [23]uint8{VEX_NOVSR_128_66_0F38_WIG, 0x17, VEX_NOVSR_256_66_0F38_WIG, 0x17}},
{AVPSHUFB, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F38_WIG, 0x00, VEX_NDS_256_66_0F38_WIG, 0x00}},
{AVPSHUFD, yvex_xyi3, Pvex, [23]uint8{VEX_NOVSR_128_66_0F_WIG, 0x70, VEX_NOVSR_256_66_0F_WIG, 0x70, VEX_NOVSR_128_66_0F_WIG, 0x70, VEX_NOVSR_256_66_0F_WIG, 0x70}},
{AVPOR, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0xeb, VEX_NDS_256_66_0F_WIG, 0xeb}},
{AVPADDQ, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0xd4, VEX_NDS_256_66_0F_WIG, 0xd4}},
{AVPADDD, yvex_xy3, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0xfe, VEX_NDS_256_66_0F_WIG, 0xfe}},
{AVADDSD, yvex_x3, Pvex, [23]uint8{VEX_NDS_128_F2_0F_WIG, 0x58}},
{AVSUBSD, yvex_x3, Pvex, [23]uint8{VEX_NDS_128_F2_0F_WIG, 0x5c}},
{AVFMADD213SD, yvex_x3, Pvex, [23]uint8{VEX_DDS_LIG_66_0F38_W1, 0xa9}},
{AVFMADD231SD, yvex_x3, Pvex, [23]uint8{VEX_DDS_LIG_66_0F38_W1, 0xb9}},
{AVFNMADD213SD, yvex_x3, Pvex, [23]uint8{VEX_DDS_LIG_66_0F38_W1, 0xad}},
{AVFNMADD231SD, yvex_x3, Pvex, [23]uint8{VEX_DDS_LIG_66_0F38_W1, 0xbd}},
{AVPSLLD, yvex_shift, Pvex, [23]uint8{VEX_NDS_128_66_0F_WIG, 0x72, 0xf0, VEX_NDS_256_66_0F_WIG, 0x72, 0xf0, VEX_NDD_128_66_0F_WIG, 0xf2, VEX_NDD_256_66_0F_WIG, 0xf2}},
{AVPSLLQ, yvex_shift, Pvex, [23]uint8{VEX_NDD_128_66_0F_WIG, 0x73, 0xf0, VEX_NDD_256_66_0F_WIG, 0x73, 0xf0, VEX_NDS_128_66_0F_WIG, 0xf3, VEX_NDS_256_66_0F_WIG, 0xf3}},
{AVPSRLD, yvex_shift, Pvex, [23]uint8{VEX_NDD_128_66_0F_WIG, 0x72, 0xd0, VEX_NDD_256_66_0F_WIG, 0x72, 0xd0, VEX_NDD_128_66_0F_WIG, 0xd2, VEX_NDD_256_66_0F_WIG, 0xd2}},
{AVPSRLQ, yvex_shift, Pvex, [23]uint8{VEX_NDD_128_66_0F_WIG, 0x73, 0xd0, VEX_NDD_256_66_0F_WIG, 0x73, 0xd0, VEX_NDS_128_66_0F_WIG, 0xd3, VEX_NDS_256_66_0F_WIG, 0xd3}},
{AVPSRLDQ, yvex_shift_dq, Pvex, [23]uint8{VEX_NDD_128_66_0F_WIG, 0x73, 0xd8, VEX_NDD_256_66_0F_WIG, 0x73, 0xd8}},
{AVPSLLDQ, yvex_shift_dq, Pvex, [23]uint8{VEX_NDD_128_66_0F_WIG, 0x73, 0xf8, VEX_NDD_256_66_0F_WIG, 0x73, 0xf8}},
{AVPERM2F128, yvex_yyi4, Pvex, [23]uint8{VEX_NDS_256_66_0F3A_W0, 0x06}},
{AVPALIGNR, yvex_yyi4, Pvex, [23]uint8{VEX_NDS_256_66_0F3A_WIG, 0x0f}},
{AVPBLENDD, yvex_yyi4, Pvex, [23]uint8{VEX_NDS_256_66_0F3A_WIG, 0x02}},
{AVINSERTI128, yvex_xyi4, Pvex, [23]uint8{VEX_NDS_256_66_0F3A_WIG, 0x38}},
{AVPERM2I128, yvex_yyi4, Pvex, [23]uint8{VEX_NDS_256_66_0F3A_WIG, 0x46}},
{ARORXL, yvex_ri3, Pvex, [23]uint8{VEX_NOVSR_LZ_F2_0F3A_W0, 0xf0}},
{ARORXQ, yvex_ri3, Pvex, [23]uint8{VEX_NOVSR_LZ_F2_0F3A_W1, 0xf0}},
{AVBROADCASTSD, yvex_vpbroadcast_sd, Pvex, [23]uint8{VEX_NOVSR_256_66_0F38_W0, 0x19}},
{AVBROADCASTSS, yvex_vpbroadcast, Pvex, [23]uint8{VEX_NOVSR_128_66_0F38_W0, 0x18, VEX_NOVSR_256_66_0F38_W0, 0x18}},
{AVMOVDDUP, yvex_xy2, Pvex, [23]uint8{VEX_NOVSR_128_F2_0F_WIG, 0x12, VEX_NOVSR_256_F2_0F_WIG, 0x12}},
{AVMOVSHDUP, yvex_xy2, Pvex, [23]uint8{VEX_NOVSR_128_F3_0F_WIG, 0x16, VEX_NOVSR_256_F3_0F_WIG, 0x16}},
{AVMOVSLDUP, yvex_xy2, Pvex, [23]uint8{VEX_NOVSR_128_F3_0F_WIG, 0x12, VEX_NOVSR_256_F3_0F_WIG, 0x12}},`
// Preprocess "existingOptabs" to make generated output comparable.
{
// 1. Inline prefix byte expressions.
// $ egrep 'VEX_[_A-Z0-9]+\s*=' src/cmd/internal/obj/x86/asm6.go
prefixFixer := strings.NewReplacer(
"VEX_DDS_LIG_66_0F38_W1", "vexDDS | vexLIG | vex66 | vex0F38 | vexW1",
"VEX_NDD_128_66_0F_WIG", "vexNDD | vex128 | vex66 | vex0F | vexWIG",
"VEX_NDD_256_66_0F_WIG", "vexNDD | vex256 | vex66 | vex0F | vexWIG",
"VEX_NDD_LZ_F2_0F38_W0", "vexNDD | vexLZ | vexF2 | vex0F38 | vexW0",
"VEX_NDD_LZ_F2_0F38_W1", "vexNDD | vexLZ | vexF2 | vex0F38 | vexW1",
"VEX_NDS_128_66_0F_WIG", "vexNDS | vex128 | vex66 | vex0F | vexWIG",
"VEX_NDS_128_66_0F38_WIG", "vexNDS | vex128 | vex66 | vex0F38 | vexWIG",
"VEX_NDS_128_F2_0F_WIG", "vexNDS | vex128 | vexF2 | vex0F | vexWIG",
"VEX_NDS_256_66_0F_WIG", "vexNDS | vex256 | vex66 | vex0F | vexWIG",
"VEX_NDS_256_66_0F38_WIG", "vexNDS | vex256 | vex66 | vex0F38 | vexWIG",
"VEX_NDS_256_66_0F3A_W0", "vexNDS | vex256 | vex66 | vex0F3A | vexW0",
"VEX_NDS_256_66_0F3A_WIG", "vexNDS | vex256 | vex66 | vex0F3A | vexWIG",
"VEX_NDS_LZ_0F38_W0", "vexNDS | vexLZ | vex0F38 | vexW0",
"VEX_NDS_LZ_0F38_W1", "vexNDS | vexLZ | vex0F38 | vexW1",
"VEX_NDS_LZ_66_0F38_W0", "vexNDS | vexLZ | vex66 | vex0F38 | vexW0",
"VEX_NDS_LZ_66_0F38_W1", "vexNDS | vexLZ | vex66 | vex0F38 | vexW1",
"VEX_NDS_LZ_F2_0F38_W0", "vexNDS | vexLZ | vexF2 | vex0F38 | vexW0",
"VEX_NDS_LZ_F2_0F38_W1", "vexNDS | vexLZ | vexF2 | vex0F38 | vexW1",
"VEX_NDS_LZ_F3_0F38_W0", "vexNDS | vexLZ | vexF3 | vex0F38 | vexW0",
"VEX_NDS_LZ_F3_0F38_W1", "vexNDS | vexLZ | vexF3 | vex0F38 | vexW1",
"VEX_NOVSR_128_66_0F_WIG", "vexNOVSR | vex128 | vex66 | vex0F | vexWIG",
"VEX_NOVSR_128_66_0F38_W0", "vexNOVSR | vex128 | vex66 | vex0F38 | vexW0",
"VEX_NOVSR_128_66_0F38_WIG", "vexNOVSR | vex128 | vex66 | vex0F38 | vexWIG",
"VEX_NOVSR_128_F2_0F_WIG", "vexNOVSR | vex128 | vexF2 | vex0F | vexWIG",
"VEX_NOVSR_128_F3_0F_WIG", "vexNOVSR | vex128 | vexF3 | vex0F | vexWIG",
"VEX_NOVSR_256_66_0F_WIG", "vexNOVSR | vex256 | vex66 | vex0F | vexWIG",
"VEX_NOVSR_256_66_0F38_W0", "vexNOVSR | vex256 | vex66 | vex0F38 | vexW0",
"VEX_NOVSR_256_66_0F38_WIG", "vexNOVSR | vex256 | vex66 | vex0F38 | vexWIG",
"VEX_NOVSR_256_F2_0F_WIG", "vexNOVSR | vex256 | vexF2 | vex0F | vexWIG",
"VEX_NOVSR_256_F3_0F_WIG", "vexNOVSR | vex256 | vexF3 | vex0F | vexWIG",
"VEX_NOVSR_LZ_F2_0F3A_W0", "vexNOVSR | vexLZ | vexF2 | vex0F3A | vexW0",
"VEX_NOVSR_LZ_F2_0F3A_W1", "vexNOVSR | vexLZ | vexF2 | vex0F3A | vexW1",
)
existingOptabs = prefixFixer.Replace(existingOptabs)
// 2. Normalize hex literals.
// Some optabs use 0xaa style, others use 0xAA.
// Generated optabs always use upper case style (as in x86.csv).
rxHexLit := regexp.MustCompile(` 0x[0-9a-f]{2}`)
existingOptabs = rxHexLit.ReplaceAllStringFunc(existingOptabs, func(m string) string {
return " 0x" + strings.ToUpper(m[len(" 0x"):])
})
}
r, err := specRowReader("../" + specFile)
if err != nil {
t.Fatalf("open row reader: %v", err)
}
var newOptabs bytes.Buffer
_, err = doGenerateVexOptabs(r, &newOptabs)
if err != nil {
t.Fatalf("generate vex optabs: %v", err)
}
rxOptabID := regexp.MustCompile(`[A-Z_][A-Z_0-9]*`)
linesToMap := func(lines []string) map[string]string {
m := make(map[string]string, len(lines))
for _, l := range lines {
name := rxOptabID.FindString(l)
if name != "" {
m[name] = l
}
}
return m
}
expectedChanges := map[string]string{
// Before: 256/Y variant.
// Now: 256/Y + 128/X variants.
"AVPALIGNR": "{AVPALIGNR, yvex_vpalignr, Pvex, [23]uint8{vexNDS | vex128 | vex66 | vex0F3A | vexWIG, 0x0F, vexNDS | vex256 | vex66 | vex0F3A | vexWIG, 0x0F}}",
"AVPBLENDD": "{AVPBLENDD, yvex_vpalignr, Pvex, [23]uint8{vexNDS | vex128 | vex66 | vex0F3A | vexW0, 0x02, vexNDS | vex256 | vex66 | vex0F3A | vexW0, 0x02}}",
// Before: R+R variants.
// Now: R+R and R+M variants.
"AVBROADCASTSS": "{AVBROADCASTSS, yvex_vpbroadcast_ss, Pvex, [23]uint8{vexNOVSR | vex128 | vex66 | vex0F38 | vexW0, 0x18, vexNOVSR | vex128 | vex66 | vex0F38 | vexW0, 0x18, vexNOVSR | vex256 | vex66 | vex0F38 | vexW0, 0x18, vexNOVSR | vex256 | vex66 | vex0F38 | vexW0, 0x18}}",
"AVBROADCASTSD": "{AVBROADCASTSD, yvex_vpbroadcast_sd, Pvex, [23]uint8{vexNOVSR | vex256 | vex66 | vex0F38 | vexW0, 0x19, vexNOVSR | vex256 | vex66 | vex0F38 | vexW0, 0x19}}",
// Before: VEX.L=128 (vex128).
// Now: VEX.L=IGNORE (vexLIG); as in "x86.csv".
"AVSUBSD": "{AVSUBSD, yvex_x3, Pvex, [23]uint8{vexNDS | vexLIG | vexF2 | vex0F | vexWIG, 0x5C}}",
"AVADDSD": "{AVADDSD, yvex_x3, Pvex, [23]uint8{vexNDS | vexLIG | vexF2 | vex0F | vexWIG, 0x58}}",
// Before: VEX.W=IGNORE (vexWIG).
// Now: VEX.W=W0 (vexW0); as in "x86.csv".
"AVINSERTI128": "{AVINSERTI128, yvex_xyi4, Pvex, [23]uint8{vexNDS | vex256 | vex66 | vex0F3A | vexW0, 0x38}}",
"AVPERM2I128": "{AVPERM2I128, yvex_yyi4, Pvex, [23]uint8{vexNDS | vex256 | vex66 | vex0F3A | vexW0, 0x46}}",
}
reportError := func(name, want, have string) {
t.Errorf("%s: output mismatch\n\twant:'%s'\n\thave:'%s'",
name, want, have)
}
// Perform check.
haveLines := linesToMap(strings.Split(newOptabs.String(), "\n"))
wantLines := linesToMap(strings.Split(existingOptabs, "\n"))
for name, wantLine := range wantLines {
haveLine := haveLines[name]
haveLine = strings.Trim(haveLine, " \t,")
wantLine = strings.Trim(wantLine, " \t,")
if haveLine == "" {
t.Errorf("%s: not found", name)
continue
}
if line := expectedChanges[name]; line != "" {
if line != haveLine {
reportError(name, line, haveLine)
}
continue
}
if !strings.Contains(haveLine, wantLine) {
reportError(name, wantLine, haveLine)
}
}
}