github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/cmd/compile/internal/gc/asm_test.go (about) 1 // Copyright 2016 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 package gc 6 7 import ( 8 "bytes" 9 "fmt" 10 "internal/testenv" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "regexp" 16 "runtime" 17 "strings" 18 "testing" 19 ) 20 21 // TestAssembly checks to make sure the assembly generated for 22 // functions contains certain expected instructions. 23 func TestAssembly(t *testing.T) { 24 testenv.MustHaveGoBuild(t) 25 if runtime.GOOS == "windows" { 26 // TODO: remove if we can get "go tool compile -S" to work on windows. 27 t.Skipf("skipping test: recursive windows compile not working") 28 } 29 dir, err := ioutil.TempDir("", "TestAssembly") 30 if err != nil { 31 t.Fatalf("could not create directory: %v", err) 32 } 33 defer os.RemoveAll(dir) 34 35 nameRegexp := regexp.MustCompile("func \\w+") 36 t.Run("platform", func(t *testing.T) { 37 for _, ats := range allAsmTests { 38 ats := ats 39 t.Run(ats.os+"/"+ats.arch, func(tt *testing.T) { 40 tt.Parallel() 41 42 asm := ats.compileToAsm(tt, dir) 43 44 for _, at := range ats.tests { 45 funcName := nameRegexp.FindString(at.function)[len("func "):] 46 fa := funcAsm(tt, asm, funcName) 47 if fa != "" { 48 at.verifyAsm(tt, fa) 49 } 50 } 51 }) 52 } 53 }) 54 } 55 56 var nextTextRegexp = regexp.MustCompile(`\n\S`) 57 58 // funcAsm returns the assembly listing for the given function name. 59 func funcAsm(t *testing.T, asm string, funcName string) string { 60 if i := strings.Index(asm, fmt.Sprintf("TEXT\t\"\".%s(SB)", funcName)); i >= 0 { 61 asm = asm[i:] 62 } else { 63 t.Errorf("could not find assembly for function %v", funcName) 64 return "" 65 } 66 67 // Find the next line that doesn't begin with whitespace. 68 loc := nextTextRegexp.FindStringIndex(asm) 69 if loc != nil { 70 asm = asm[:loc[0]] 71 } 72 73 return asm 74 } 75 76 type asmTest struct { 77 // function to compile, must be named fX, 78 // where X is this test's index in asmTests.tests. 79 function string 80 // regexps that must match the generated assembly 81 regexps []string 82 } 83 84 func (at asmTest) verifyAsm(t *testing.T, fa string) { 85 for _, r := range at.regexps { 86 if b, err := regexp.MatchString(r, fa); !b || err != nil { 87 t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, at.function, fa) 88 } 89 } 90 } 91 92 type asmTests struct { 93 arch string 94 os string 95 imports []string 96 tests []*asmTest 97 } 98 99 func (ats *asmTests) generateCode() []byte { 100 var buf bytes.Buffer 101 fmt.Fprintln(&buf, "package main") 102 for _, s := range ats.imports { 103 fmt.Fprintf(&buf, "import %q\n", s) 104 } 105 106 for _, t := range ats.tests { 107 fmt.Fprintln(&buf, t.function) 108 } 109 110 return buf.Bytes() 111 } 112 113 // compile compiles the package pkg for architecture arch and 114 // returns the generated assembly. dir is a scratch directory. 115 func (ats *asmTests) compileToAsm(t *testing.T, dir string) string { 116 // create test directory 117 testDir := filepath.Join(dir, fmt.Sprintf("%s_%s", ats.arch, ats.os)) 118 err := os.Mkdir(testDir, 0700) 119 if err != nil { 120 t.Fatalf("could not create directory: %v", err) 121 } 122 123 // Create source. 124 src := filepath.Join(testDir, "test.go") 125 err = ioutil.WriteFile(src, ats.generateCode(), 0600) 126 if err != nil { 127 t.Fatalf("error writing code: %v", err) 128 } 129 130 // First, install any dependencies we need. This builds the required export data 131 // for any packages that are imported. 132 for _, i := range ats.imports { 133 out := filepath.Join(testDir, i+".a") 134 135 if s := ats.runGo(t, "build", "-o", out, "-gcflags=-dolinkobj=false", i); s != "" { 136 t.Fatalf("Stdout = %s\nWant empty", s) 137 } 138 } 139 140 // Now, compile the individual file for which we want to see the generated assembly. 141 asm := ats.runGo(t, "tool", "compile", "-I", testDir, "-S", "-o", filepath.Join(testDir, "out.o"), src) 142 return asm 143 } 144 145 // runGo runs go command with the given args and returns stdout string. 146 // go is run with GOARCH and GOOS set as ats.arch and ats.os respectively 147 func (ats *asmTests) runGo(t *testing.T, args ...string) string { 148 var stdout, stderr bytes.Buffer 149 cmd := exec.Command(testenv.GoToolPath(t), args...) 150 cmd.Env = append(os.Environ(), "GOARCH="+ats.arch, "GOOS="+ats.os) 151 cmd.Stdout = &stdout 152 cmd.Stderr = &stderr 153 154 if err := cmd.Run(); err != nil { 155 t.Fatalf("error running cmd: %v\nstdout:\n%sstderr:\n%s\n", err, stdout.String(), stderr.String()) 156 } 157 158 if s := stderr.String(); s != "" { 159 t.Fatalf("Stderr = %s\nWant empty", s) 160 } 161 162 return stdout.String() 163 } 164 165 var allAsmTests = []*asmTests{ 166 { 167 arch: "amd64", 168 os: "linux", 169 imports: []string{"encoding/binary", "math/bits", "unsafe"}, 170 tests: linuxAMD64Tests, 171 }, 172 { 173 arch: "386", 174 os: "linux", 175 imports: []string{"encoding/binary"}, 176 tests: linux386Tests, 177 }, 178 { 179 arch: "s390x", 180 os: "linux", 181 imports: []string{"encoding/binary", "math/bits"}, 182 tests: linuxS390XTests, 183 }, 184 { 185 arch: "arm", 186 os: "linux", 187 imports: []string{"math/bits"}, 188 tests: linuxARMTests, 189 }, 190 { 191 arch: "arm64", 192 os: "linux", 193 imports: []string{"math/bits"}, 194 tests: linuxARM64Tests, 195 }, 196 { 197 arch: "mips", 198 os: "linux", 199 imports: []string{"math/bits"}, 200 tests: linuxMIPSTests, 201 }, 202 { 203 arch: "ppc64le", 204 os: "linux", 205 tests: linuxPPC64LETests, 206 }, 207 } 208 209 var linuxAMD64Tests = []*asmTest{ 210 { 211 ` 212 func f0(x int) int { 213 return x * 64 214 } 215 `, 216 []string{"\tSHLQ\t\\$6,"}, 217 }, 218 { 219 ` 220 func f1(x int) int { 221 return x * 96 222 } 223 `, 224 []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"}, 225 }, 226 // Load-combining tests. 227 { 228 ` 229 func f2(b []byte) uint64 { 230 return binary.LittleEndian.Uint64(b) 231 } 232 `, 233 []string{"\tMOVQ\t\\(.*\\),"}, 234 }, 235 { 236 ` 237 func f3(b []byte, i int) uint64 { 238 return binary.LittleEndian.Uint64(b[i:]) 239 } 240 `, 241 []string{"\tMOVQ\t\\(.*\\)\\(.*\\*1\\),"}, 242 }, 243 { 244 ` 245 func f4(b []byte) uint32 { 246 return binary.LittleEndian.Uint32(b) 247 } 248 `, 249 []string{"\tMOVL\t\\(.*\\),"}, 250 }, 251 { 252 ` 253 func f5(b []byte, i int) uint32 { 254 return binary.LittleEndian.Uint32(b[i:]) 255 } 256 `, 257 []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 258 }, 259 { 260 ` 261 func f6(b []byte) uint64 { 262 return binary.BigEndian.Uint64(b) 263 } 264 `, 265 []string{"\tBSWAPQ\t"}, 266 }, 267 { 268 ` 269 func f7(b []byte, i int) uint64 { 270 return binary.BigEndian.Uint64(b[i:]) 271 } 272 `, 273 []string{"\tBSWAPQ\t"}, 274 }, 275 { 276 ` 277 func f8(b []byte, v uint64) { 278 binary.BigEndian.PutUint64(b, v) 279 } 280 `, 281 []string{"\tBSWAPQ\t"}, 282 }, 283 { 284 ` 285 func f9(b []byte, i int, v uint64) { 286 binary.BigEndian.PutUint64(b[i:], v) 287 } 288 `, 289 []string{"\tBSWAPQ\t"}, 290 }, 291 { 292 ` 293 func f10(b []byte) uint32 { 294 return binary.BigEndian.Uint32(b) 295 } 296 `, 297 []string{"\tBSWAPL\t"}, 298 }, 299 { 300 ` 301 func f11(b []byte, i int) uint32 { 302 return binary.BigEndian.Uint32(b[i:]) 303 } 304 `, 305 []string{"\tBSWAPL\t"}, 306 }, 307 { 308 ` 309 func f12(b []byte, v uint32) { 310 binary.BigEndian.PutUint32(b, v) 311 } 312 `, 313 []string{"\tBSWAPL\t"}, 314 }, 315 { 316 ` 317 func f13(b []byte, i int, v uint32) { 318 binary.BigEndian.PutUint32(b[i:], v) 319 } 320 `, 321 []string{"\tBSWAPL\t"}, 322 }, 323 { 324 ` 325 func f14(b []byte) uint16 { 326 return binary.BigEndian.Uint16(b) 327 } 328 `, 329 []string{"\tROLW\t\\$8,"}, 330 }, 331 { 332 ` 333 func f15(b []byte, i int) uint16 { 334 return binary.BigEndian.Uint16(b[i:]) 335 } 336 `, 337 []string{"\tROLW\t\\$8,"}, 338 }, 339 { 340 ` 341 func f16(b []byte, v uint16) { 342 binary.BigEndian.PutUint16(b, v) 343 } 344 `, 345 []string{"\tROLW\t\\$8,"}, 346 }, 347 { 348 ` 349 func f17(b []byte, i int, v uint16) { 350 binary.BigEndian.PutUint16(b[i:], v) 351 } 352 `, 353 []string{"\tROLW\t\\$8,"}, 354 }, 355 // Structure zeroing. See issue #18370. 356 { 357 ` 358 type T1 struct { 359 a, b, c int 360 } 361 func f18(t *T1) { 362 *t = T1{} 363 } 364 `, 365 []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)"}, 366 }, 367 // SSA-able composite literal initialization. Issue 18872. 368 { 369 ` 370 type T18872 struct { 371 a, b, c, d int 372 } 373 374 func f18872(p *T18872) { 375 *p = T18872{1, 2, 3, 4} 376 } 377 `, 378 []string{"\tMOVQ\t[$]1", "\tMOVQ\t[$]2", "\tMOVQ\t[$]3", "\tMOVQ\t[$]4"}, 379 }, 380 // Also test struct containing pointers (this was special because of write barriers). 381 { 382 ` 383 type T2 struct { 384 a, b, c *int 385 } 386 func f19(t *T2) { 387 *t = T2{} 388 } 389 `, 390 []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)", "\tCALL\truntime\\.writebarrierptr\\(SB\\)"}, 391 }, 392 // Rotate tests 393 { 394 ` 395 func f20(x uint64) uint64 { 396 return x<<7 | x>>57 397 } 398 `, 399 []string{"\tROLQ\t[$]7,"}, 400 }, 401 { 402 ` 403 func f21(x uint64) uint64 { 404 return x<<7 + x>>57 405 } 406 `, 407 []string{"\tROLQ\t[$]7,"}, 408 }, 409 { 410 ` 411 func f22(x uint64) uint64 { 412 return x<<7 ^ x>>57 413 } 414 `, 415 []string{"\tROLQ\t[$]7,"}, 416 }, 417 { 418 ` 419 func f23(x uint32) uint32 { 420 return x<<7 + x>>25 421 } 422 `, 423 []string{"\tROLL\t[$]7,"}, 424 }, 425 { 426 ` 427 func f24(x uint32) uint32 { 428 return x<<7 | x>>25 429 } 430 `, 431 []string{"\tROLL\t[$]7,"}, 432 }, 433 { 434 ` 435 func f25(x uint32) uint32 { 436 return x<<7 ^ x>>25 437 } 438 `, 439 []string{"\tROLL\t[$]7,"}, 440 }, 441 { 442 ` 443 func f26(x uint16) uint16 { 444 return x<<7 + x>>9 445 } 446 `, 447 []string{"\tROLW\t[$]7,"}, 448 }, 449 { 450 ` 451 func f27(x uint16) uint16 { 452 return x<<7 | x>>9 453 } 454 `, 455 []string{"\tROLW\t[$]7,"}, 456 }, 457 { 458 ` 459 func f28(x uint16) uint16 { 460 return x<<7 ^ x>>9 461 } 462 `, 463 []string{"\tROLW\t[$]7,"}, 464 }, 465 { 466 ` 467 func f29(x uint8) uint8 { 468 return x<<7 + x>>1 469 } 470 `, 471 []string{"\tROLB\t[$]7,"}, 472 }, 473 { 474 ` 475 func f30(x uint8) uint8 { 476 return x<<7 | x>>1 477 } 478 `, 479 []string{"\tROLB\t[$]7,"}, 480 }, 481 { 482 ` 483 func f31(x uint8) uint8 { 484 return x<<7 ^ x>>1 485 } 486 `, 487 []string{"\tROLB\t[$]7,"}, 488 }, 489 // Rotate after inlining (see issue 18254). 490 { 491 ` 492 func f32(x uint32) uint32 { 493 return g(x, 7) 494 } 495 func g(x uint32, k uint) uint32 { 496 return x<<k | x>>(32-k) 497 } 498 `, 499 []string{"\tROLL\t[$]7,"}, 500 }, 501 { 502 ` 503 func f33(m map[int]int) int { 504 return m[5] 505 } 506 `, 507 []string{"\tMOVQ\t[$]5,"}, 508 }, 509 // Direct use of constants in fast map access calls. Issue 19015. 510 { 511 ` 512 func f34(m map[int]int) bool { 513 _, ok := m[5] 514 return ok 515 } 516 `, 517 []string{"\tMOVQ\t[$]5,"}, 518 }, 519 { 520 ` 521 func f35(m map[string]int) int { 522 return m["abc"] 523 } 524 `, 525 []string{"\"abc\""}, 526 }, 527 { 528 ` 529 func f36(m map[string]int) bool { 530 _, ok := m["abc"] 531 return ok 532 } 533 `, 534 []string{"\"abc\""}, 535 }, 536 // Bit test ops on amd64, issue 18943. 537 { 538 ` 539 func f37(a, b uint64) int { 540 if a&(1<<(b&63)) != 0 { 541 return 1 542 } 543 return -1 544 } 545 `, 546 []string{"\tBTQ\t"}, 547 }, 548 { 549 ` 550 func f38(a, b uint64) bool { 551 return a&(1<<(b&63)) != 0 552 } 553 `, 554 []string{"\tBTQ\t"}, 555 }, 556 { 557 ` 558 func f39(a uint64) int { 559 if a&(1<<60) != 0 { 560 return 1 561 } 562 return -1 563 } 564 `, 565 []string{"\tBTQ\t\\$60"}, 566 }, 567 { 568 ` 569 func f40(a uint64) bool { 570 return a&(1<<60) != 0 571 } 572 `, 573 []string{"\tBTQ\t\\$60"}, 574 }, 575 // Intrinsic tests for math/bits 576 { 577 ` 578 func f41(a uint64) int { 579 return bits.TrailingZeros64(a) 580 } 581 `, 582 []string{"\tBSFQ\t", "\tMOVL\t\\$64,", "\tCMOVQEQ\t"}, 583 }, 584 { 585 ` 586 func f42(a uint32) int { 587 return bits.TrailingZeros32(a) 588 } 589 `, 590 []string{"\tBSFQ\t", "\tORQ\t[^$]", "\tMOVQ\t\\$4294967296,"}, 591 }, 592 { 593 ` 594 func f43(a uint16) int { 595 return bits.TrailingZeros16(a) 596 } 597 `, 598 []string{"\tBSFQ\t", "\tORQ\t\\$65536,"}, 599 }, 600 { 601 ` 602 func f44(a uint8) int { 603 return bits.TrailingZeros8(a) 604 } 605 `, 606 []string{"\tBSFQ\t", "\tORQ\t\\$256,"}, 607 }, 608 { 609 ` 610 func f45(a uint64) uint64 { 611 return bits.ReverseBytes64(a) 612 } 613 `, 614 []string{"\tBSWAPQ\t"}, 615 }, 616 { 617 ` 618 func f46(a uint32) uint32 { 619 return bits.ReverseBytes32(a) 620 } 621 `, 622 []string{"\tBSWAPL\t"}, 623 }, 624 { 625 ` 626 func f47(a uint16) uint16 { 627 return bits.ReverseBytes16(a) 628 } 629 `, 630 []string{"\tROLW\t\\$8,"}, 631 }, 632 { 633 ` 634 func f48(a uint64) int { 635 return bits.Len64(a) 636 } 637 `, 638 []string{"\tBSRQ\t"}, 639 }, 640 { 641 ` 642 func f49(a uint32) int { 643 return bits.Len32(a) 644 } 645 `, 646 []string{"\tBSRQ\t"}, 647 }, 648 { 649 ` 650 func f50(a uint16) int { 651 return bits.Len16(a) 652 } 653 `, 654 []string{"\tBSRQ\t"}, 655 }, 656 /* see ssa.go 657 { 658 ` 659 func f51(a uint8) int { 660 return bits.Len8(a) 661 } 662 `, 663 []string{"\tBSRQ\t"}, 664 }, 665 */ 666 { 667 ` 668 func f52(a uint) int { 669 return bits.Len(a) 670 } 671 `, 672 []string{"\tBSRQ\t"}, 673 }, 674 { 675 ` 676 func f53(a uint64) int { 677 return bits.LeadingZeros64(a) 678 } 679 `, 680 []string{"\tBSRQ\t"}, 681 }, 682 { 683 ` 684 func f54(a uint32) int { 685 return bits.LeadingZeros32(a) 686 } 687 `, 688 []string{"\tBSRQ\t"}, 689 }, 690 { 691 ` 692 func f55(a uint16) int { 693 return bits.LeadingZeros16(a) 694 } 695 `, 696 []string{"\tBSRQ\t"}, 697 }, 698 /* see ssa.go 699 { 700 ` 701 func f56(a uint8) int { 702 return bits.LeadingZeros8(a) 703 } 704 `, 705 []string{"\tBSRQ\t"}, 706 }, 707 */ 708 { 709 ` 710 func f57(a uint) int { 711 return bits.LeadingZeros(a) 712 } 713 `, 714 []string{"\tBSRQ\t"}, 715 }, 716 { 717 ` 718 func pop1(x uint64) int { 719 return bits.OnesCount64(x) 720 }`, 721 []string{"\tPOPCNTQ\t", "support_popcnt"}, 722 }, 723 { 724 ` 725 func pop2(x uint32) int { 726 return bits.OnesCount32(x) 727 }`, 728 []string{"\tPOPCNTL\t", "support_popcnt"}, 729 }, 730 { 731 ` 732 func pop3(x uint16) int { 733 return bits.OnesCount16(x) 734 }`, 735 []string{"\tPOPCNTL\t", "support_popcnt"}, 736 }, 737 { 738 ` 739 func pop4(x uint) int { 740 return bits.OnesCount(x) 741 }`, 742 []string{"\tPOPCNTQ\t", "support_popcnt"}, 743 }, 744 // multiplication merging tests 745 { 746 ` 747 func mul1(n int) int { 748 return 15*n + 31*n 749 }`, 750 []string{"\tIMULQ\t[$]46"}, // 46*n 751 }, 752 { 753 ` 754 func mul2(n int) int { 755 return 5*n + 7*(n+1) + 11*(n+2) 756 }`, 757 []string{"\tIMULQ\t[$]23", "\tADDQ\t[$]29"}, // 23*n + 29 758 }, 759 { 760 ` 761 func mul3(a, n int) int { 762 return a*n + 19*n 763 }`, 764 []string{"\tADDQ\t[$]19", "\tIMULQ"}, // (a+19)*n 765 }, 766 767 // see issue 19595. 768 // We want to merge load+op in f58, but not in f59. 769 { 770 ` 771 func f58(p, q *int) { 772 x := *p 773 *q += x 774 }`, 775 []string{"\tADDQ\t\\("}, 776 }, 777 { 778 ` 779 func f59(p, q *int) { 780 x := *p 781 for i := 0; i < 10; i++ { 782 *q += x 783 } 784 }`, 785 []string{"\tADDQ\t[A-Z]"}, 786 }, 787 // Floating-point strength reduction 788 { 789 ` 790 func f60(f float64) float64 { 791 return f * 2.0 792 }`, 793 []string{"\tADDSD\t"}, 794 }, 795 { 796 ` 797 func f62(f float64) float64 { 798 return f / 16.0 799 }`, 800 []string{"\tMULSD\t"}, 801 }, 802 { 803 ` 804 func f63(f float64) float64 { 805 return f / 0.125 806 }`, 807 []string{"\tMULSD\t"}, 808 }, 809 { 810 ` 811 func f64(f float64) float64 { 812 return f / 0.5 813 }`, 814 []string{"\tADDSD\t"}, 815 }, 816 // Check that compare to constant string uses 2/4/8 byte compares 817 { 818 ` 819 func f65(a string) bool { 820 return a == "xx" 821 }`, 822 []string{"\tCMPW\t[A-Z]"}, 823 }, 824 { 825 ` 826 func f66(a string) bool { 827 return a == "xxxx" 828 }`, 829 []string{"\tCMPL\t[A-Z]"}, 830 }, 831 { 832 ` 833 func f67(a string) bool { 834 return a == "xxxxxxxx" 835 }`, 836 []string{"\tCMPQ\t[A-Z]"}, 837 }, 838 // Non-constant rotate 839 { 840 `func rot64l(x uint64, y int) uint64 { 841 z := uint(y & 63) 842 return x << z | x >> (64-z) 843 }`, 844 []string{"\tROLQ\t"}, 845 }, 846 { 847 `func rot64r(x uint64, y int) uint64 { 848 z := uint(y & 63) 849 return x >> z | x << (64-z) 850 }`, 851 []string{"\tRORQ\t"}, 852 }, 853 { 854 `func rot32l(x uint32, y int) uint32 { 855 z := uint(y & 31) 856 return x << z | x >> (32-z) 857 }`, 858 []string{"\tROLL\t"}, 859 }, 860 { 861 `func rot32r(x uint32, y int) uint32 { 862 z := uint(y & 31) 863 return x >> z | x << (32-z) 864 }`, 865 []string{"\tRORL\t"}, 866 }, 867 { 868 `func rot16l(x uint16, y int) uint16 { 869 z := uint(y & 15) 870 return x << z | x >> (16-z) 871 }`, 872 []string{"\tROLW\t"}, 873 }, 874 { 875 `func rot16r(x uint16, y int) uint16 { 876 z := uint(y & 15) 877 return x >> z | x << (16-z) 878 }`, 879 []string{"\tRORW\t"}, 880 }, 881 { 882 `func rot8l(x uint8, y int) uint8 { 883 z := uint(y & 7) 884 return x << z | x >> (8-z) 885 }`, 886 []string{"\tROLB\t"}, 887 }, 888 { 889 `func rot8r(x uint8, y int) uint8 { 890 z := uint(y & 7) 891 return x >> z | x << (8-z) 892 }`, 893 []string{"\tRORB\t"}, 894 }, 895 // Check that array compare uses 2/4/8 byte compares 896 { 897 ` 898 func f68(a,b [2]byte) bool { 899 return a == b 900 }`, 901 []string{"\tCMPW\t[A-Z]"}, 902 }, 903 { 904 ` 905 func f69(a,b [3]uint16) bool { 906 return a == b 907 }`, 908 []string{"\tCMPL\t[A-Z]"}, 909 }, 910 { 911 ` 912 func f70(a,b [15]byte) bool { 913 return a == b 914 }`, 915 []string{"\tCMPQ\t[A-Z]"}, 916 }, 917 { 918 ` 919 func f71(a,b unsafe.Pointer) bool { // This was a TODO in mapaccess1_faststr 920 return *((*[4]byte)(a)) != *((*[4]byte)(b)) 921 }`, 922 []string{"\tCMPL\t[A-Z]"}, 923 }, 924 { 925 // make sure assembly output has matching offset and base register. 926 ` 927 func f72(a, b int) int { 928 var x [16]byte // use some frame 929 _ = x 930 return b 931 } 932 `, 933 []string{"b\\+40\\(SP\\)"}, 934 }, 935 } 936 937 var linux386Tests = []*asmTest{ 938 { 939 ` 940 func f0(b []byte) uint32 { 941 return binary.LittleEndian.Uint32(b) 942 } 943 `, 944 []string{"\tMOVL\t\\(.*\\),"}, 945 }, 946 { 947 ` 948 func f1(b []byte, i int) uint32 { 949 return binary.LittleEndian.Uint32(b[i:]) 950 } 951 `, 952 []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 953 }, 954 // multiplication merging tests 955 { 956 ` 957 func mul1(n int) int { 958 return 9*n + 14*n 959 }`, 960 []string{"\tIMULL\t[$]23"}, // 23*n 961 }, 962 { 963 ` 964 func mul2(a, n int) int { 965 return 19*a + a*n 966 }`, 967 []string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a 968 }, 969 } 970 971 var linuxS390XTests = []*asmTest{ 972 { 973 ` 974 func f0(b []byte) uint32 { 975 return binary.LittleEndian.Uint32(b) 976 } 977 `, 978 []string{"\tMOVWBR\t\\(.*\\),"}, 979 }, 980 { 981 ` 982 func f1(b []byte, i int) uint32 { 983 return binary.LittleEndian.Uint32(b[i:]) 984 } 985 `, 986 []string{"\tMOVWBR\t\\(.*\\)\\(.*\\*1\\),"}, 987 }, 988 { 989 ` 990 func f2(b []byte) uint64 { 991 return binary.LittleEndian.Uint64(b) 992 } 993 `, 994 []string{"\tMOVDBR\t\\(.*\\),"}, 995 }, 996 { 997 ` 998 func f3(b []byte, i int) uint64 { 999 return binary.LittleEndian.Uint64(b[i:]) 1000 } 1001 `, 1002 []string{"\tMOVDBR\t\\(.*\\)\\(.*\\*1\\),"}, 1003 }, 1004 { 1005 ` 1006 func f4(b []byte) uint32 { 1007 return binary.BigEndian.Uint32(b) 1008 } 1009 `, 1010 []string{"\tMOVWZ\t\\(.*\\),"}, 1011 }, 1012 { 1013 ` 1014 func f5(b []byte, i int) uint32 { 1015 return binary.BigEndian.Uint32(b[i:]) 1016 } 1017 `, 1018 []string{"\tMOVWZ\t\\(.*\\)\\(.*\\*1\\),"}, 1019 }, 1020 { 1021 ` 1022 func f6(b []byte) uint64 { 1023 return binary.BigEndian.Uint64(b) 1024 } 1025 `, 1026 []string{"\tMOVD\t\\(.*\\),"}, 1027 }, 1028 { 1029 ` 1030 func f7(b []byte, i int) uint64 { 1031 return binary.BigEndian.Uint64(b[i:]) 1032 } 1033 `, 1034 []string{"\tMOVD\t\\(.*\\)\\(.*\\*1\\),"}, 1035 }, 1036 { 1037 ` 1038 func f8(x uint64) uint64 { 1039 return x<<7 + x>>57 1040 } 1041 `, 1042 []string{"\tRLLG\t[$]7,"}, 1043 }, 1044 { 1045 ` 1046 func f9(x uint64) uint64 { 1047 return x<<7 | x>>57 1048 } 1049 `, 1050 []string{"\tRLLG\t[$]7,"}, 1051 }, 1052 { 1053 ` 1054 func f10(x uint64) uint64 { 1055 return x<<7 ^ x>>57 1056 } 1057 `, 1058 []string{"\tRLLG\t[$]7,"}, 1059 }, 1060 { 1061 ` 1062 func f11(x uint32) uint32 { 1063 return x<<7 + x>>25 1064 } 1065 `, 1066 []string{"\tRLL\t[$]7,"}, 1067 }, 1068 { 1069 ` 1070 func f12(x uint32) uint32 { 1071 return x<<7 | x>>25 1072 } 1073 `, 1074 []string{"\tRLL\t[$]7,"}, 1075 }, 1076 { 1077 ` 1078 func f13(x uint32) uint32 { 1079 return x<<7 ^ x>>25 1080 } 1081 `, 1082 []string{"\tRLL\t[$]7,"}, 1083 }, 1084 // Fused multiply-add/sub instructions. 1085 { 1086 ` 1087 func f14(x, y, z float64) float64 { 1088 return x * y + z 1089 } 1090 `, 1091 []string{"\tFMADD\t"}, 1092 }, 1093 { 1094 ` 1095 func f15(x, y, z float64) float64 { 1096 return x * y - z 1097 } 1098 `, 1099 []string{"\tFMSUB\t"}, 1100 }, 1101 { 1102 ` 1103 func f16(x, y, z float32) float32 { 1104 return x * y + z 1105 } 1106 `, 1107 []string{"\tFMADDS\t"}, 1108 }, 1109 { 1110 ` 1111 func f17(x, y, z float32) float32 { 1112 return x * y - z 1113 } 1114 `, 1115 []string{"\tFMSUBS\t"}, 1116 }, 1117 // Intrinsic tests for math/bits 1118 { 1119 ` 1120 func f18(a uint64) int { 1121 return bits.TrailingZeros64(a) 1122 } 1123 `, 1124 []string{"\tFLOGR\t"}, 1125 }, 1126 { 1127 ` 1128 func f19(a uint32) int { 1129 return bits.TrailingZeros32(a) 1130 } 1131 `, 1132 []string{"\tFLOGR\t", "\tMOVWZ\t"}, 1133 }, 1134 { 1135 ` 1136 func f20(a uint16) int { 1137 return bits.TrailingZeros16(a) 1138 } 1139 `, 1140 []string{"\tFLOGR\t", "\tOR\t\\$65536,"}, 1141 }, 1142 { 1143 ` 1144 func f21(a uint8) int { 1145 return bits.TrailingZeros8(a) 1146 } 1147 `, 1148 []string{"\tFLOGR\t", "\tOR\t\\$256,"}, 1149 }, 1150 // Intrinsic tests for math/bits 1151 { 1152 ` 1153 func f22(a uint64) uint64 { 1154 return bits.ReverseBytes64(a) 1155 } 1156 `, 1157 []string{"\tMOVDBR\t"}, 1158 }, 1159 { 1160 ` 1161 func f23(a uint32) uint32 { 1162 return bits.ReverseBytes32(a) 1163 } 1164 `, 1165 []string{"\tMOVWBR\t"}, 1166 }, 1167 { 1168 ` 1169 func f24(a uint64) int { 1170 return bits.Len64(a) 1171 } 1172 `, 1173 []string{"\tFLOGR\t"}, 1174 }, 1175 { 1176 ` 1177 func f25(a uint32) int { 1178 return bits.Len32(a) 1179 } 1180 `, 1181 []string{"\tFLOGR\t"}, 1182 }, 1183 { 1184 ` 1185 func f26(a uint16) int { 1186 return bits.Len16(a) 1187 } 1188 `, 1189 []string{"\tFLOGR\t"}, 1190 }, 1191 { 1192 ` 1193 func f27(a uint8) int { 1194 return bits.Len8(a) 1195 } 1196 `, 1197 []string{"\tFLOGR\t"}, 1198 }, 1199 { 1200 ` 1201 func f28(a uint) int { 1202 return bits.Len(a) 1203 } 1204 `, 1205 []string{"\tFLOGR\t"}, 1206 }, 1207 { 1208 ` 1209 func f29(a uint64) int { 1210 return bits.LeadingZeros64(a) 1211 } 1212 `, 1213 []string{"\tFLOGR\t"}, 1214 }, 1215 { 1216 ` 1217 func f30(a uint32) int { 1218 return bits.LeadingZeros32(a) 1219 } 1220 `, 1221 []string{"\tFLOGR\t"}, 1222 }, 1223 { 1224 ` 1225 func f31(a uint16) int { 1226 return bits.LeadingZeros16(a) 1227 } 1228 `, 1229 []string{"\tFLOGR\t"}, 1230 }, 1231 { 1232 ` 1233 func f32(a uint8) int { 1234 return bits.LeadingZeros8(a) 1235 } 1236 `, 1237 []string{"\tFLOGR\t"}, 1238 }, 1239 { 1240 ` 1241 func f33(a uint) int { 1242 return bits.LeadingZeros(a) 1243 } 1244 `, 1245 []string{"\tFLOGR\t"}, 1246 }, 1247 } 1248 1249 var linuxARMTests = []*asmTest{ 1250 { 1251 ` 1252 func f0(x uint32) uint32 { 1253 return x<<7 + x>>25 1254 } 1255 `, 1256 []string{"\tMOVW\tR[0-9]+@>25,"}, 1257 }, 1258 { 1259 ` 1260 func f1(x uint32) uint32 { 1261 return x<<7 | x>>25 1262 } 1263 `, 1264 []string{"\tMOVW\tR[0-9]+@>25,"}, 1265 }, 1266 { 1267 ` 1268 func f2(x uint32) uint32 { 1269 return x<<7 ^ x>>25 1270 } 1271 `, 1272 []string{"\tMOVW\tR[0-9]+@>25,"}, 1273 }, 1274 { 1275 ` 1276 func f3(a uint64) int { 1277 return bits.Len64(a) 1278 } 1279 `, 1280 []string{"\tCLZ\t"}, 1281 }, 1282 { 1283 ` 1284 func f4(a uint32) int { 1285 return bits.Len32(a) 1286 } 1287 `, 1288 []string{"\tCLZ\t"}, 1289 }, 1290 { 1291 ` 1292 func f5(a uint16) int { 1293 return bits.Len16(a) 1294 } 1295 `, 1296 []string{"\tCLZ\t"}, 1297 }, 1298 { 1299 ` 1300 func f6(a uint8) int { 1301 return bits.Len8(a) 1302 } 1303 `, 1304 []string{"\tCLZ\t"}, 1305 }, 1306 { 1307 ` 1308 func f7(a uint) int { 1309 return bits.Len(a) 1310 } 1311 `, 1312 []string{"\tCLZ\t"}, 1313 }, 1314 { 1315 ` 1316 func f8(a uint64) int { 1317 return bits.LeadingZeros64(a) 1318 } 1319 `, 1320 []string{"\tCLZ\t"}, 1321 }, 1322 { 1323 ` 1324 func f9(a uint32) int { 1325 return bits.LeadingZeros32(a) 1326 } 1327 `, 1328 []string{"\tCLZ\t"}, 1329 }, 1330 { 1331 ` 1332 func f10(a uint16) int { 1333 return bits.LeadingZeros16(a) 1334 } 1335 `, 1336 []string{"\tCLZ\t"}, 1337 }, 1338 { 1339 ` 1340 func f11(a uint8) int { 1341 return bits.LeadingZeros8(a) 1342 } 1343 `, 1344 []string{"\tCLZ\t"}, 1345 }, 1346 { 1347 ` 1348 func f12(a uint) int { 1349 return bits.LeadingZeros(a) 1350 } 1351 `, 1352 []string{"\tCLZ\t"}, 1353 }, 1354 { 1355 // make sure assembly output has matching offset and base register. 1356 ` 1357 func f13(a, b int) int { 1358 var x [16]byte // use some frame 1359 _ = x 1360 return b 1361 } 1362 `, 1363 []string{"b\\+4\\(FP\\)"}, 1364 }, 1365 } 1366 1367 var linuxARM64Tests = []*asmTest{ 1368 { 1369 ` 1370 func f0(x uint64) uint64 { 1371 return x<<7 + x>>57 1372 } 1373 `, 1374 []string{"\tROR\t[$]57,"}, 1375 }, 1376 { 1377 ` 1378 func f1(x uint64) uint64 { 1379 return x<<7 | x>>57 1380 } 1381 `, 1382 []string{"\tROR\t[$]57,"}, 1383 }, 1384 { 1385 ` 1386 func f2(x uint64) uint64 { 1387 return x<<7 ^ x>>57 1388 } 1389 `, 1390 []string{"\tROR\t[$]57,"}, 1391 }, 1392 { 1393 ` 1394 func f3(x uint32) uint32 { 1395 return x<<7 + x>>25 1396 } 1397 `, 1398 []string{"\tRORW\t[$]25,"}, 1399 }, 1400 { 1401 ` 1402 func f4(x uint32) uint32 { 1403 return x<<7 | x>>25 1404 } 1405 `, 1406 []string{"\tRORW\t[$]25,"}, 1407 }, 1408 { 1409 ` 1410 func f5(x uint32) uint32 { 1411 return x<<7 ^ x>>25 1412 } 1413 `, 1414 []string{"\tRORW\t[$]25,"}, 1415 }, 1416 { 1417 ` 1418 func f22(a uint64) uint64 { 1419 return bits.ReverseBytes64(a) 1420 } 1421 `, 1422 []string{"\tREV\t"}, 1423 }, 1424 { 1425 ` 1426 func f23(a uint32) uint32 { 1427 return bits.ReverseBytes32(a) 1428 } 1429 `, 1430 []string{"\tREVW\t"}, 1431 }, 1432 { 1433 ` 1434 func f24(a uint64) int { 1435 return bits.Len64(a) 1436 } 1437 `, 1438 []string{"\tCLZ\t"}, 1439 }, 1440 { 1441 ` 1442 func f25(a uint32) int { 1443 return bits.Len32(a) 1444 } 1445 `, 1446 []string{"\tCLZ\t"}, 1447 }, 1448 { 1449 ` 1450 func f26(a uint16) int { 1451 return bits.Len16(a) 1452 } 1453 `, 1454 []string{"\tCLZ\t"}, 1455 }, 1456 { 1457 ` 1458 func f27(a uint8) int { 1459 return bits.Len8(a) 1460 } 1461 `, 1462 []string{"\tCLZ\t"}, 1463 }, 1464 { 1465 ` 1466 func f28(a uint) int { 1467 return bits.Len(a) 1468 } 1469 `, 1470 []string{"\tCLZ\t"}, 1471 }, 1472 { 1473 ` 1474 func f29(a uint64) int { 1475 return bits.LeadingZeros64(a) 1476 } 1477 `, 1478 []string{"\tCLZ\t"}, 1479 }, 1480 { 1481 ` 1482 func f30(a uint32) int { 1483 return bits.LeadingZeros32(a) 1484 } 1485 `, 1486 []string{"\tCLZ\t"}, 1487 }, 1488 { 1489 ` 1490 func f31(a uint16) int { 1491 return bits.LeadingZeros16(a) 1492 } 1493 `, 1494 []string{"\tCLZ\t"}, 1495 }, 1496 { 1497 ` 1498 func f32(a uint8) int { 1499 return bits.LeadingZeros8(a) 1500 } 1501 `, 1502 []string{"\tCLZ\t"}, 1503 }, 1504 { 1505 ` 1506 func f33(a uint) int { 1507 return bits.LeadingZeros(a) 1508 } 1509 `, 1510 []string{"\tCLZ\t"}, 1511 }, 1512 { 1513 ` 1514 func f34(a uint64) uint64 { 1515 return a & ((1<<63)-1) 1516 } 1517 `, 1518 []string{"\tAND\t"}, 1519 }, 1520 { 1521 ` 1522 func f35(a uint64) uint64 { 1523 return a & (1<<63) 1524 } 1525 `, 1526 []string{"\tAND\t"}, 1527 }, 1528 { 1529 // make sure offsets are folded into load and store. 1530 ` 1531 func f36(_, a [20]byte) (b [20]byte) { 1532 b = a 1533 return 1534 } 1535 `, 1536 []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"}, 1537 }, 1538 } 1539 1540 var linuxMIPSTests = []*asmTest{ 1541 { 1542 ` 1543 func f0(a uint64) int { 1544 return bits.Len64(a) 1545 } 1546 `, 1547 []string{"\tCLZ\t"}, 1548 }, 1549 { 1550 ` 1551 func f1(a uint32) int { 1552 return bits.Len32(a) 1553 } 1554 `, 1555 []string{"\tCLZ\t"}, 1556 }, 1557 { 1558 ` 1559 func f2(a uint16) int { 1560 return bits.Len16(a) 1561 } 1562 `, 1563 []string{"\tCLZ\t"}, 1564 }, 1565 { 1566 ` 1567 func f3(a uint8) int { 1568 return bits.Len8(a) 1569 } 1570 `, 1571 []string{"\tCLZ\t"}, 1572 }, 1573 { 1574 ` 1575 func f4(a uint) int { 1576 return bits.Len(a) 1577 } 1578 `, 1579 []string{"\tCLZ\t"}, 1580 }, 1581 { 1582 ` 1583 func f5(a uint64) int { 1584 return bits.LeadingZeros64(a) 1585 } 1586 `, 1587 []string{"\tCLZ\t"}, 1588 }, 1589 { 1590 ` 1591 func f6(a uint32) int { 1592 return bits.LeadingZeros32(a) 1593 } 1594 `, 1595 []string{"\tCLZ\t"}, 1596 }, 1597 { 1598 ` 1599 func f7(a uint16) int { 1600 return bits.LeadingZeros16(a) 1601 } 1602 `, 1603 []string{"\tCLZ\t"}, 1604 }, 1605 { 1606 ` 1607 func f8(a uint8) int { 1608 return bits.LeadingZeros8(a) 1609 } 1610 `, 1611 []string{"\tCLZ\t"}, 1612 }, 1613 { 1614 ` 1615 func f9(a uint) int { 1616 return bits.LeadingZeros(a) 1617 } 1618 `, 1619 []string{"\tCLZ\t"}, 1620 }, 1621 } 1622 1623 var linuxPPC64LETests = []*asmTest{ 1624 // Fused multiply-add/sub instructions. 1625 { 1626 ` 1627 func f0(x, y, z float64) float64 { 1628 return x * y + z 1629 } 1630 `, 1631 []string{"\tFMADD\t"}, 1632 }, 1633 { 1634 ` 1635 func f1(x, y, z float64) float64 { 1636 return x * y - z 1637 } 1638 `, 1639 []string{"\tFMSUB\t"}, 1640 }, 1641 { 1642 ` 1643 func f2(x, y, z float32) float32 { 1644 return x * y + z 1645 } 1646 `, 1647 []string{"\tFMADDS\t"}, 1648 }, 1649 { 1650 ` 1651 func f3(x, y, z float32) float32 { 1652 return x * y - z 1653 } 1654 `, 1655 []string{"\tFMSUBS\t"}, 1656 }, 1657 { 1658 ` 1659 func f4(x uint32) uint32 { 1660 return x<<7 | x>>25 1661 } 1662 `, 1663 []string{"\tROTLW\t"}, 1664 }, 1665 { 1666 ` 1667 func f5(x uint32) uint32 { 1668 return x<<7 + x>>25 1669 } 1670 `, 1671 []string{"\tROTLW\t"}, 1672 }, 1673 { 1674 ` 1675 func f6(x uint32) uint32 { 1676 return x<<7 ^ x>>25 1677 } 1678 `, 1679 []string{"\tROTLW\t"}, 1680 }, 1681 { 1682 ` 1683 func f7(x uint64) uint64 { 1684 return x<<7 | x>>57 1685 } 1686 `, 1687 []string{"\tROTL\t"}, 1688 }, 1689 { 1690 ` 1691 func f8(x uint64) uint64 { 1692 return x<<7 + x>>57 1693 } 1694 `, 1695 []string{"\tROTL\t"}, 1696 }, 1697 { 1698 ` 1699 func f9(x uint64) uint64 { 1700 return x<<7 ^ x>>57 1701 } 1702 `, 1703 []string{"\tROTL\t"}, 1704 }, 1705 } 1706 1707 // TestLineNumber checks to make sure the generated assembly has line numbers 1708 // see issue #16214 1709 func TestLineNumber(t *testing.T) { 1710 testenv.MustHaveGoBuild(t) 1711 dir, err := ioutil.TempDir("", "TestLineNumber") 1712 if err != nil { 1713 t.Fatalf("could not create directory: %v", err) 1714 } 1715 defer os.RemoveAll(dir) 1716 1717 src := filepath.Join(dir, "x.go") 1718 err = ioutil.WriteFile(src, []byte(issue16214src), 0644) 1719 if err != nil { 1720 t.Fatalf("could not write file: %v", err) 1721 } 1722 1723 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) 1724 out, err := cmd.CombinedOutput() 1725 if err != nil { 1726 t.Fatalf("fail to run go tool compile: %v", err) 1727 } 1728 1729 if strings.Contains(string(out), "unknown line number") { 1730 t.Errorf("line number missing in assembly:\n%s", out) 1731 } 1732 } 1733 1734 var issue16214src = ` 1735 package main 1736 1737 func Mod32(x uint32) uint32 { 1738 return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos 1739 } 1740 `