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