Skip to content

Commit 2eb8d94

Browse files
committed
cmd/asm: add test for verification of instruction encodings
Not much testing yet, but the test now exists. Another step toward #13822. Change-Id: Idb2b06bf53a6113c83008150b4c0b631bb195279 Reviewed-on: https://go-review.googlesource.com/18844 Reviewed-by: Rob Pike <[email protected]> Run-TryBot: Russ Cox <[email protected]>
1 parent d3ff40f commit 2eb8d94

File tree

4 files changed

+132
-20
lines changed

4 files changed

+132
-20
lines changed

src/cmd/asm/internal/asm/endtoend_test.go

Lines changed: 129 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io/ioutil"
1111
"os"
1212
"path/filepath"
13+
"sort"
1314
"strconv"
1415
"strings"
1516
"testing"
@@ -22,9 +23,9 @@ import (
2223
// Output is generated by, in effect, turning on -S and comparing the
2324
// result against a golden file.
2425

25-
func testEndToEnd(t *testing.T, goarch string) {
26+
func testEndToEnd(t *testing.T, goarch, file string) {
2627
lex.InitHist()
27-
input := filepath.Join("testdata", goarch+".s")
28+
input := filepath.Join("testdata", file+".s")
2829
architecture, ctxt := setArch(goarch)
2930
lexer := lex.NewLexer(input, ctxt)
3031
parser := NewParser(ctxt, architecture, lexer)
@@ -33,23 +34,31 @@ func testEndToEnd(t *testing.T, goarch string) {
3334
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
3435
ctxt.Bso = obj.Binitw(os.Stdout)
3536
defer ctxt.Bso.Flush()
36-
ctxt.Diag = t.Errorf
37+
failed := false
38+
ctxt.Diag = func(format string, args ...interface{}) {
39+
failed = true
40+
t.Errorf(format, args...)
41+
}
3742
obj.Binitw(ioutil.Discard)
3843
pList.Firstpc, ok = parser.Parse()
39-
if !ok || t.Failed() {
40-
t.Fatalf("asm: %s assembly failed", goarch)
44+
if !ok || failed {
45+
t.Errorf("asm: %s assembly failed", goarch)
46+
return
4147
}
4248
output := strings.Split(testOut.String(), "\n")
4349

4450
// Reconstruct expected output by independently "parsing" the input.
4551
data, err := ioutil.ReadFile(input)
4652
if err != nil {
47-
t.Fatal(err)
53+
t.Error(err)
54+
return
4855
}
4956
lineno := 0
5057
seq := 0
58+
hexByLine := map[string]string{}
59+
lines := strings.SplitAfter(string(data), "\n")
5160
Diff:
52-
for _, line := range strings.SplitAfter(string(data), "\n") {
61+
for _, line := range lines {
5362
lineno++
5463

5564
// The general form of a test input line is:
@@ -62,14 +71,31 @@ Diff:
6271
}
6372
seq++
6473

74+
var hexes string
6575
switch len(parts) {
6676
default:
6777
t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line)
6878
case 1:
6979
// no comment
7080
case 2:
71-
// one comment, printed form
81+
// might be printed form or hex
82+
note := strings.TrimSpace(parts[1])
83+
if isHexes(note) {
84+
hexes = note
85+
} else {
86+
printed = note
87+
}
88+
case 3:
89+
// printed form, then hex
7290
printed = strings.TrimSpace(parts[1])
91+
hexes = strings.TrimSpace(parts[2])
92+
if !isHexes(hexes) {
93+
t.Errorf("%s:%d: malformed hex instruction encoding: %s", input, lineno, line)
94+
}
95+
}
96+
97+
if hexes != "" {
98+
hexByLine[fmt.Sprintf("%s:%d", input, lineno)] = hexes
7399
}
74100

75101
// Canonicalize spacing in printed form.
@@ -142,28 +168,114 @@ Diff:
142168
t.Errorf("unexpected output: %q", output[0])
143169
output = output[1:]
144170
}
171+
172+
// Checked printing.
173+
// Now check machine code layout.
174+
175+
top := pList.Firstpc
176+
var text *obj.LSym
177+
ok = true
178+
ctxt.Diag = func(format string, args ...interface{}) {
179+
t.Errorf(format, args...)
180+
ok = false
181+
}
182+
obj.Flushplist(ctxt)
183+
184+
for p := top; p != nil; p = p.Link {
185+
if p.As == obj.ATEXT {
186+
text = p.From.Sym
187+
}
188+
hexes := hexByLine[p.Line()]
189+
if hexes == "" {
190+
continue
191+
}
192+
delete(hexByLine, p.Line())
193+
if text == nil {
194+
t.Errorf("%s: instruction outside TEXT", p)
195+
}
196+
size := int64(len(text.P)) - p.Pc
197+
if p.Link != nil {
198+
size = p.Link.Pc - p.Pc
199+
} else if p.Isize != 0 {
200+
size = int64(p.Isize)
201+
}
202+
var code []byte
203+
if p.Pc < int64(len(text.P)) {
204+
code = text.P[p.Pc:]
205+
if size < int64(len(code)) {
206+
code = code[:size]
207+
}
208+
}
209+
codeHex := fmt.Sprintf("%x", code)
210+
if codeHex == "" {
211+
codeHex = "empty"
212+
}
213+
ok := false
214+
for _, hex := range strings.Split(hexes, " or ") {
215+
if codeHex == hex {
216+
ok = true
217+
break
218+
}
219+
}
220+
if !ok {
221+
t.Errorf("%s: have encoding %s, want %s", p, codeHex, hexes)
222+
}
223+
}
224+
225+
if len(hexByLine) > 0 {
226+
var missing []string
227+
for key := range hexByLine {
228+
missing = append(missing, key)
229+
}
230+
sort.Strings(missing)
231+
for _, line := range missing {
232+
t.Errorf("%s: did not find instruction encoding", line)
233+
}
234+
}
235+
145236
}
146237

147-
func TestPPC64EndToEnd(t *testing.T) {
148-
testEndToEnd(t, "ppc64")
238+
func isHexes(s string) bool {
239+
if s == "" {
240+
return false
241+
}
242+
if s == "empty" {
243+
return true
244+
}
245+
for _, f := range strings.Split(s, " or ") {
246+
if f == "" || len(f)%2 != 0 || strings.TrimLeft(f, "0123456789abcdef") != "" {
247+
return false
248+
}
249+
}
250+
return true
251+
}
252+
253+
func Test386EndToEnd(t *testing.T) {
254+
testEndToEnd(t, "386", "386")
149255
}
150256

151257
func TestARMEndToEnd(t *testing.T) {
152-
testEndToEnd(t, "arm")
258+
defer os.Setenv("GOARM", os.Getenv("GOARM"))
259+
260+
for _, goarm := range []string{"5", "6", "7"} {
261+
os.Setenv("GOARM", goarm)
262+
t.Logf("GOARM=%v", os.Getenv("GOARM"))
263+
testEndToEnd(t, "arm", "arm")
264+
}
153265
}
154266

155267
func TestARM64EndToEnd(t *testing.T) {
156-
testEndToEnd(t, "arm64")
268+
testEndToEnd(t, "arm64", "arm64")
157269
}
158270

159271
func TestAMD64EndToEnd(t *testing.T) {
160-
testEndToEnd(t, "amd64")
272+
testEndToEnd(t, "amd64", "amd64")
161273
}
162274

163-
func Test386EndToEnd(t *testing.T) {
164-
testEndToEnd(t, "386")
275+
func TestMIPS64EndToEnd(t *testing.T) {
276+
testEndToEnd(t, "mips64", "mips64")
165277
}
166278

167-
func TestMIPS64EndToEnd(t *testing.T) {
168-
testEndToEnd(t, "mips64")
279+
func TestPPC64EndToEnd(t *testing.T) {
280+
testEndToEnd(t, "ppc64", "ppc64")
169281
}

src/cmd/asm/internal/asm/testdata/amd64.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,4 @@ loop:
122122
LOOP loop // LOOP
123123

124124
// LTYPE0 nonnon { outcode($1, &$2); }
125-
RET
125+
RET // c3

src/cmd/asm/internal/asm/testdata/arm.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ TEXT foo(SB), 7, $0
189189
// outcode($1, $2, &$3, 0, &$5);
190190
// }
191191
ADDD.S F1, F2
192-
MOVF.S $0.5, F2 // MOVF.S $(0.5), F2
192+
MOVF $0.5, F2 // MOVF $(0.5), F2
193193

194194
// LTYPEK cond frcon ',' LFREG ',' freg
195195
// {

src/cmd/internal/obj/arm/asm5.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2737,7 +2737,7 @@ func olhrr(ctxt *obj.Link, i int, b int, r int, sc int) uint32 {
27372737

27382738
func ofsr(ctxt *obj.Link, a int, r int, v int32, b int, sc int, p *obj.Prog) uint32 {
27392739
if sc&C_SBIT != 0 {
2740-
ctxt.Diag(".nil on FLDR/FSTR instruction")
2740+
ctxt.Diag(".nil on FLDR/FSTR instruction: %v", p)
27412741
}
27422742
o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28
27432743
if sc&C_PBIT == 0 {

0 commit comments

Comments
 (0)