Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(312)

Delta Between Two Patch Sets: src/cmd/internal/rsc.io/x86/x86asm/ext_test.go

Issue 152570049: [dev.power64] code review 152570049: all: merge default into dev.power64 (Closed)
Left Patch Set: Created 10 years, 4 months ago
Right Patch Set: diff -r 36f7fc9495481ed67a159eea0eb2fac35b7c46a5 https://code.google.com/p/go Created 10 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/internal/rsc.io/x86/x86asm/decode_test.go ('k') | src/cmd/internal/rsc.io/x86/x86asm/gnu.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2014 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
5 // Support for testing against external disassembler program.
6
7 package x86asm
8
9 import (
10 "bufio"
11 "bytes"
12 "encoding/hex"
13 "flag"
14 "fmt"
15 "io/ioutil"
16 "log"
17 "math/rand"
18 "os"
19 "os/exec"
20 "regexp"
21 "runtime"
22 "strings"
23 "testing"
24 "time"
25 )
26
27 var (
28 printTests = flag.Bool("printtests", false, "print test cases that exerc ise new code paths")
29 dumpTest = flag.Bool("dump", false, "dump all encodings")
30 mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
31 longTest = flag.Bool("long", false, "long test")
32 keep = flag.Bool("keep", false, "keep object files around")
33 debug = false
34 )
35
36 // A ExtInst represents a single decoded instruction parsed
37 // from an external disassembler's output.
38 type ExtInst struct {
39 addr uint32
40 enc [32]byte
41 nenc int
42 text string
43 }
44
45 func (r ExtInst) String() string {
46 return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
47 }
48
49 // An ExtDis is a connection between an external disassembler and a test.
50 type ExtDis struct {
51 Arch int
52 Dec chan ExtInst
53 File *os.File
54 Size int
55 KeepFile bool
56 Cmd *exec.Cmd
57 }
58
59 // Run runs the given command - the external disassembler - and returns
60 // a buffered reader of its standard output.
61 func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
62 if *keep {
63 log.Printf("%s\n", strings.Join(cmd, " "))
64 }
65 ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
66 out, err := ext.Cmd.StdoutPipe()
67 if err != nil {
68 return nil, fmt.Errorf("stdoutpipe: %v", err)
69 }
70 if err := ext.Cmd.Start(); err != nil {
71 return nil, fmt.Errorf("exec: %v", err)
72 }
73
74 b := bufio.NewReaderSize(out, 1<<20)
75 return b, nil
76 }
77
78 // Wait waits for the command started with Run to exit.
79 func (ext *ExtDis) Wait() error {
80 return ext.Cmd.Wait()
81 }
82
83 // testExtDis tests a set of byte sequences against an external disassembler.
84 // The disassembler is expected to produce the given syntax and be run
85 // in the given architecture mode (16, 32, or 64-bit).
86 // The extdis function must start the external disassembler
87 // and then parse its output, sending the parsed instructions on ext.Dec.
88 // The generate function calls its argument f once for each byte sequence
89 // to be tested. The generate function itself will be called twice, and it must
90 // make the same sequence of calls to f each time.
91 // When a disassembly does not match the internal decoding,
92 // allowedMismatch determines whether this mismatch should be
93 // allowed, or else considered an error.
94 func testExtDis(
95 t *testing.T,
96 syntax string,
97 arch int,
98 extdis func(ext *ExtDis) error,
99 generate func(f func([]byte)),
100 allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) boo l,
101 ) {
102 start := time.Now()
103 ext := &ExtDis{
104 Dec: make(chan ExtInst),
105 Arch: arch,
106 }
107 errc := make(chan error)
108
109 // First pass: write instructions to input file for external disassemble r.
110 file, f, size, err := writeInst(generate)
111 if err != nil {
112 t.Fatal(err)
113 }
114 ext.Size = size
115 ext.File = f
116 defer func() {
117 f.Close()
118 if !*keep {
119 os.Remove(file)
120 }
121 }()
122
123 // Second pass: compare disassembly against our decodings.
124 var (
125 totalTests = 0
126 totalSkips = 0
127 totalErrors = 0
128
129 errors = make([]string, 0, 100) // sampled errors, at most cap
130 )
131 go func() {
132 errc <- extdis(ext)
133 }()
134 generate(func(enc []byte) {
135 dec, ok := <-ext.Dec
136 if !ok {
137 t.Errorf("decoding stream ended early")
138 return
139 }
140 inst, text := disasm(syntax, arch, pad(enc))
141 totalTests++
142 if *dumpTest {
143 fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
144 }
145 if text != dec.text || inst.Len != dec.nenc {
146 suffix := ""
147 if allowedMismatch(text, size, &inst, dec) {
148 totalSkips++
149 if !*mismatch {
150 return
151 }
152 suffix += " (allowed mismatch)"
153 }
154 totalErrors++
155 if len(errors) >= cap(errors) {
156 j := rand.Intn(totalErrors)
157 if j >= cap(errors) {
158 return
159 }
160 errors = append(errors[:j], errors[j+1:]...)
161 }
162 errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d , want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
163 }
164 })
165
166 if *mismatch {
167 totalErrors -= totalSkips
168 }
169
170 for _, b := range errors {
171 t.Log(b)
172 }
173
174 if totalErrors > 0 {
175 t.Fail()
176 }
177 t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/s econd", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(star t).Seconds())
178
179 if err := <-errc; err != nil {
180 t.Fatal("external disassembler: %v", err)
181 }
182
183 }
184
185 const start = 0x8000 // start address of text
186
187 // writeInst writes the generated byte sequences to a new file
188 // starting at offset start. That file is intended to be the input to
189 // the external disassembler.
190 func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
191 f, err = ioutil.TempFile("", "x86map")
192 if err != nil {
193 return
194 }
195
196 file = f.Name()
197
198 f.Seek(start, 0)
199 w := bufio.NewWriter(f)
200 defer w.Flush()
201 size = 0
202 generate(func(x []byte) {
203 if len(x) > 16 {
204 x = x[:16]
205 }
206 if debug {
207 fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
208 }
209 w.Write(x)
210 w.Write(pops[len(x):])
211 size += len(pops)
212 })
213 return file, f, size, nil
214 }
215
216 // 0x5F is a single-byte pop instruction.
217 // We pad the bytes we want decoded with enough 0x5Fs
218 // that no matter what state the instruction stream is in
219 // after reading our bytes, the pops will get us back to
220 // a forced instruction boundary.
221 var pops = []byte{
222 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
223 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
224 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
225 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
226 }
227
228 // pad pads the code sequenc with pops.
229 func pad(enc []byte) []byte {
230 return append(enc[:len(enc):len(enc)], pops...)
231 }
232
233 // disasm returns the decoded instruction and text
234 // for the given source bytes, using the given syntax and mode.
235 func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
236 // If printTests is set, we record the coverage value
237 // before and after, and we write out the inputs for which
238 // coverage went up, in the format expected in testdata/decode.text.
239 // This produces a fairly small set of test cases that exercise nearly
240 // all the code.
241 var cover float64
242 if *printTests {
243 cover -= coverage()
244 }
245
246 inst, err := decode1(src, mode, syntax == "gnu")
247 if err != nil {
248 text = "error: " + err.Error()
249 } else {
250 switch syntax {
251 case "gnu":
252 text = GNUSyntax(inst)
253 case "intel":
254 text = IntelSyntax(inst)
255 case "plan9":
256 text = Plan9Syntax(inst, 0, nil)
257 default:
258 text = "error: unknown syntax " + syntax
259 }
260 }
261
262 if *printTests {
263 cover += coverage()
264 if cover > 0 {
265 max := len(src)
266 if max > 16 && inst.Len <= 16 {
267 max = 16
268 }
269 fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[in st.Len:max], mode, syntax, text)
270 }
271 }
272
273 return
274 }
275
276 // coverage returns a floating point number denoting the
277 // test coverage until now. The number increases when new code paths are exercis ed,
278 // both in the Go program and in the decoder byte code.
279 func coverage() float64 {
280 /*
281 testing.Coverage is not in the main distribution.
282 The implementation, which must go in package testing, is:
283
284 // Coverage reports the current code coverage as a fraction in t he range [0, 1].
285 func Coverage() float64 {
286 var n, d int64
287 for _, counters := range cover.Counters {
288 for _, c := range counters {
289 if c > 0 {
290 n++
291 }
292 d++
293 }
294 }
295 if d == 0 {
296 return 0
297 }
298 return float64(n) / float64(d)
299 }
300 */
301
302 var f float64
303 // f += testing.Coverage()
304 f += decodeCoverage()
305 return f
306 }
307
308 func decodeCoverage() float64 {
309 n := 0
310 for _, t := range decoderCover {
311 if t {
312 n++
313 }
314 }
315 return float64(1+n) / float64(1+len(decoderCover))
316 }
317
318 // Helpers for writing disassembler output parsers.
319
320 // isPrefix reports whether text is the name of an instruction prefix.
321 func isPrefix(text string) bool {
322 return prefixByte[text] > 0
323 }
324
325 // prefixByte maps instruction prefix text to actual prefix byte values.
326 var prefixByte = map[string]byte{
327 "es": 0x26,
328 "cs": 0x2e,
329 "ss": 0x36,
330 "ds": 0x3e,
331 "fs": 0x64,
332 "gs": 0x65,
333 "data16": 0x66,
334 "addr16": 0x67,
335 "lock": 0xf0,
336 "repn": 0xf2,
337 "repne": 0xf2,
338 "rep": 0xf3,
339 "repe": 0xf3,
340 "xacquire": 0xf2,
341 "xrelease": 0xf3,
342 "bnd": 0xf2,
343 "addr32": 0x66,
344 "data32": 0x67,
345 }
346
347 // hasPrefix reports whether any of the space-separated words in the text s
348 // begins with any of the given prefixes.
349 func hasPrefix(s string, prefixes ...string) bool {
350 for _, prefix := range prefixes {
351 for s := s; s != ""; {
352 if strings.HasPrefix(s, prefix) {
353 return true
354 }
355 i := strings.Index(s, " ")
356 if i < 0 {
357 break
358 }
359 s = s[i+1:]
360 }
361 }
362 return false
363 }
364
365 // contains reports whether the text s contains any of the given substrings.
366 func contains(s string, substrings ...string) bool {
367 for _, sub := range substrings {
368 if strings.Contains(s, sub) {
369 return true
370 }
371 }
372 return false
373 }
374
375 // isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
376 func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
377
378 // parseHex parses the hexadecimal byte dump in hex,
379 // appending the parsed bytes to raw and returning the updated slice.
380 // The returned bool signals whether any invalid hex was found.
381 // Spaces and tabs between bytes are okay but any other non-hex is not.
382 func parseHex(hex []byte, raw []byte) ([]byte, bool) {
383 hex = trimSpace(hex)
384 for j := 0; j < len(hex); {
385 for hex[j] == ' ' || hex[j] == '\t' {
386 j++
387 }
388 if j >= len(hex) {
389 break
390 }
391 if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
392 return nil, false
393 }
394 raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
395 j += 2
396 }
397 return raw, true
398 }
399
400 var unhex = [256]byte{
401 '0': 0,
402 '1': 1,
403 '2': 2,
404 '3': 3,
405 '4': 4,
406 '5': 5,
407 '6': 6,
408 '7': 7,
409 '8': 8,
410 '9': 9,
411 'A': 10,
412 'B': 11,
413 'C': 12,
414 'D': 13,
415 'E': 14,
416 'F': 15,
417 'a': 10,
418 'b': 11,
419 'c': 12,
420 'd': 13,
421 'e': 14,
422 'f': 15,
423 }
424
425 // index is like bytes.Index(s, []byte(t)) but avoids the allocation.
426 func index(s []byte, t string) int {
427 i := 0
428 for {
429 j := bytes.IndexByte(s[i:], t[0])
430 if j < 0 {
431 return -1
432 }
433 i = i + j
434 if i+len(t) > len(s) {
435 return -1
436 }
437 for k := 1; k < len(t); k++ {
438 if s[i+k] != t[k] {
439 goto nomatch
440 }
441 }
442 return i
443 nomatch:
444 i++
445 }
446 }
447
448 // fixSpace rewrites runs of spaces, tabs, and newline characters into single sp aces in s.
449 // If s must be rewritten, it is rewritten in place.
450 func fixSpace(s []byte) []byte {
451 s = trimSpace(s)
452 for i := 0; i < len(s); i++ {
453 if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1 ] == ' ' {
454 goto Fix
455 }
456 }
457 return s
458
459 Fix:
460 b := s
461 w := 0
462 for i := 0; i < len(s); i++ {
463 c := s[i]
464 if c == '\t' || c == '\n' {
465 c = ' '
466 }
467 if c == ' ' && w > 0 && b[w-1] == ' ' {
468 continue
469 }
470 b[w] = c
471 w++
472 }
473 if w > 0 && b[w-1] == ' ' {
474 w--
475 }
476 return b[:w]
477 }
478
479 // trimSpace trims leading and trailing space from s, returning a subslice of s.
480 func trimSpace(s []byte) []byte {
481 j := len(s)
482 for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
483 j--
484 }
485 i := 0
486 for i < j && (s[i] == ' ' || s[i] == '\t') {
487 i++
488 }
489 return s[i:j]
490 }
491
492 // pcrel and pcrelw match instructions using relative addressing mode.
493 var (
494 pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|x begin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
495 pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p [nt])?) 0x([0-9a-f]+)$`)
496 )
497
498 // Generators.
499 //
500 // The test cases are described as functions that invoke a callback repeatedly,
501 // with a new input sequence each time. These helpers make writing those
502 // a little easier.
503
504 // hexCases generates the cases written in hexadecimal in the encoded string.
505 // Spaces in 'encoded' separate entire test cases, not individual bytes.
506 func hexCases(t *testing.T, encoded string) func(func([]byte)) {
507 return func(try func([]byte)) {
508 for _, x := range strings.Fields(encoded) {
509 src, err := hex.DecodeString(x)
510 if err != nil {
511 t.Errorf("parsing %q: %v", x, err)
512 }
513 try(src)
514 }
515 }
516 }
517
518 // testdataCases generates the test cases recorded in testdata/decode.txt.
519 // It only uses the inputs; it ignores the answers recorded in that file.
520 func testdataCases(t *testing.T) func(func([]byte)) {
521 var codes [][]byte
522 data, err := ioutil.ReadFile("testdata/decode.txt")
523 if err != nil {
524 t.Fatal(err)
525 }
526 for _, line := range strings.Split(string(data), "\n") {
527 line = strings.TrimSpace(line)
528 if line == "" || strings.HasPrefix(line, "#") {
529 continue
530 }
531 f := strings.Fields(line)[0]
532 i := strings.Index(f, "|")
533 if i < 0 {
534 t.Errorf("parsing %q: missing | separator", f)
535 continue
536 }
537 if i%2 != 0 {
538 t.Errorf("parsing %q: misaligned | separator", f)
539 }
540 code, err := hex.DecodeString(f[:i] + f[i+1:])
541 if err != nil {
542 t.Errorf("parsing %q: %v", f, err)
543 continue
544 }
545 codes = append(codes, code)
546 }
547
548 return func(try func([]byte)) {
549 for _, code := range codes {
550 try(code)
551 }
552 }
553 }
554
555 // manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes.
556 // The relative ordering of the prefixes within the combinations varies determin istically.
557 func manyPrefixes(try func([]byte)) {
558 var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
559 var enc []byte
560 for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
561 enc = enc[:0]
562 for j, p := range prefixBytes {
563 if i&(1<<uint(j)) != 0 {
564 enc = append(enc, p)
565 }
566 }
567 if len(enc) > 0 {
568 k := i % len(enc)
569 enc[0], enc[k] = enc[k], enc[0]
570 }
571 try(enc)
572 }
573 }
574
575 // basicPrefixes geneartes 8 different possible prefix cases: no prefix
576 // and then one each of seven different prefix bytes.
577 func basicPrefixes(try func([]byte)) {
578 try(nil)
579 for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
580 try([]byte{b})
581 }
582 }
583
584 func rexPrefixes(try func([]byte)) {
585 try(nil)
586 for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
587 try([]byte{b})
588 }
589 }
590
591 // concat takes two generators and returns a generator for the
592 // cross product of the two, concatenating the results from each.
593 func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
594 return func(try func([]byte)) {
595 gen1(func(enc1 []byte) {
596 gen2(func(enc2 []byte) {
597 try(append(enc1[:len(enc1):len(enc1)], enc2...))
598 })
599 })
600 }
601 }
602
603 // concat3 takes three generators and returns a generator for the
604 // cross product of the three, concatenating the results from each.
605 func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
606 return func(try func([]byte)) {
607 gen1(func(enc1 []byte) {
608 gen2(func(enc2 []byte) {
609 gen3(func(enc3 []byte) {
610 try(append(append(enc1[:len(enc1):len(en c1)], enc2...), enc3...))
611 })
612 })
613 })
614 }
615 }
616
617 // concat4 takes four generators and returns a generator for the
618 // cross product of the four, concatenating the results from each.
619 func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
620 return func(try func([]byte)) {
621 gen1(func(enc1 []byte) {
622 gen2(func(enc2 []byte) {
623 gen3(func(enc3 []byte) {
624 gen4(func(enc4 []byte) {
625 try(append(append(append(enc1[:l en(enc1):len(enc1)], enc2...), enc3...), enc4...))
626 })
627 })
628 })
629 })
630 }
631 }
632
633 // filter generates the sequences from gen that satisfy ok.
634 func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
635 return func(try func([]byte)) {
636 gen(func(enc []byte) {
637 if ok(enc) {
638 try(enc)
639 }
640 })
641 }
642 }
643
644 // enum8bit generates all possible 1-byte sequences, followed by distinctive pad ding.
645 func enum8bit(try func([]byte)) {
646 for i := 0; i < 1<<8; i++ {
647 try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x 88})
648 }
649 }
650
651 // enum8bit generates all possible 2-byte sequences, followed by distinctive pad ding.
652 func enum16bit(try func([]byte)) {
653 for i := 0; i < 1<<16; i++ {
654 try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
655 }
656 }
657
658 // enum24bit generates all possible 3-byte sequences, followed by distinctive pa dding.
659 func enum24bit(try func([]byte)) {
660 for i := 0; i < 1<<24; i++ {
661 try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x3 3, 0x44, 0x55, 0x66, 0x77, 0x88})
662 }
663 }
664
665 // enumModRM generates all possible modrm bytes and, for modrm values that indic ate
666 // a following sib byte, all possible modrm, sib combinations.
667 func enumModRM(try func([]byte)) {
668 for i := 0; i < 256; i++ {
669 if (i>>3)&07 == 04 && i>>6 != 3 { // has sib
670 for j := 0; j < 256; j++ {
671 try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33 , 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
672 try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33 , 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
673 }
674 } else {
675 try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x6 6, 0x77, 0x88}) // byte encodings
676 try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x6 6, 0x77, 0x88}) // word encodings
677 }
678 }
679 }
680
681 // fixed generates the single case b.
682 // It's mainly useful to prepare an argument for concat or concat3.
683 func fixed(b ...byte) func(func([]byte)) {
684 return func(try func([]byte)) {
685 try(b)
686 }
687 }
688
689 // testBasic runs the given test function with cases all using opcode as the ini tial opcode bytes.
690 // It runs three phases:
691 //
692 // First, zero-or-one prefixes followed by opcode followed by all possible 1-byt e values.
693 // If in -short mode, that's all.
694 //
695 // Second, zero-or-one prefixes followed by opcode followed by all possible 2-by te values.
696 // If not in -long mode, that's all. This phase and the next run in parallel wit h other tests
697 // (using t.Parallel).
698 //
699 // Finally, opcode followed by all possible 3-byte values. The test can take a v ery long time
700 // and prints progress messages to package log.
701 func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
702 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
703 if testing.Short() {
704 return
705 }
706
707 t.Parallel()
708 testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
709 if !*longTest {
710 return
711 }
712
713 name := caller(2)
714 op1 := make([]byte, len(opcode)+1)
715 copy(op1, opcode)
716 for i := 0; i < 256; i++ {
717 log.Printf("%s 24-bit: %d/256\n", name, i)
718 op1[len(opcode)] = byte(i)
719 testfn(t, concat(fixed(op1...), enum16bit))
720 }
721 }
722
723 func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opc ode ...byte) {
724 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), e num8bit), isValidREX))
725 if testing.Short() {
726 return
727 }
728
729 t.Parallel()
730 testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), e num16bit), isValidREX))
731 if !*longTest {
732 return
733 }
734
735 name := caller(2)
736 op1 := make([]byte, len(opcode)+1)
737 copy(op1, opcode)
738 for i := 0; i < 256; i++ {
739 log.Printf("%s 24-bit: %d/256\n", name, i)
740 op1[len(opcode)] = byte(i)
741 testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
742 }
743 }
744
745 // testPrefix runs the given test function for all many prefix possibilities
746 // followed by all possible 1-byte sequences.
747 //
748 // If in -long mode, it then runs a test of all the prefix possibilities followe d
749 // by all possible 2-byte sequences.
750 func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
751 t.Parallel()
752 testfn(t, concat(manyPrefixes, enum8bit))
753 if testing.Short() || !*longTest {
754 return
755 }
756
757 name := caller(2)
758 for i := 0; i < 256; i++ {
759 log.Printf("%s 16-bit: %d/256\n", name, i)
760 testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
761 }
762 }
763
764 func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
765 t.Parallel()
766 testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidRE X))
767 if testing.Short() || !*longTest {
768 return
769 }
770
771 name := caller(2)
772 for i := 0; i < 256; i++ {
773 log.Printf("%s 16-bit: %d/256\n", name, i)
774 testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i )), enum8bit), isValidREX))
775 }
776 }
777
778 func caller(skip int) string {
779 pc, _, _, _ := runtime.Caller(skip)
780 f := runtime.FuncForPC(pc)
781 name := "?"
782 if f != nil {
783 name = f.Name()
784 if i := strings.LastIndex(name, "."); i >= 0 {
785 name = name[i+1:]
786 }
787 }
788 return name
789 }
790
791 func isValidREX(x []byte) bool {
792 i := 0
793 for i < len(x) && isPrefixByte(x[i]) {
794 i++
795 }
796 if i < len(x) && Prefix(x[i]).IsREX() {
797 i++
798 if i < len(x) {
799 return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
800 }
801 }
802 return true
803 }
804
805 func isPrefixByte(b byte) bool {
806 switch b {
807 case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
808 return true
809 }
810 return false
811 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b