github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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 // This file contains code generation tests. 22 // 23 // Each test is defined in a variable of type asmTest. Tests are 24 // architecture-specific, and they are grouped in arrays of tests, one 25 // for each architecture. 26 // 27 // Each asmTest consists of a function to compile, an array of 28 // positive regexps that must match the generated assembly and 29 // an array of negative regexps that must not match generated assembly. 30 // For example, the following amd64 test 31 // 32 // { 33 // fn: ` 34 // func f0(x int) int { 35 // return x * 64 36 // } 37 // `, 38 // pos: []string{"\tSHLQ\t[$]6,"}, 39 // neg: []string{"MULQ"} 40 // } 41 // 42 // verifies that the code the compiler generates for a multiplication 43 // by 64 contains a 'SHLQ' instruction and does not contain a MULQ. 44 // 45 // Since all the tests for a given architecture are dumped in the same 46 // file, the function names must be unique. As a workaround for this 47 // restriction, the test harness supports the use of a '$' placeholder 48 // for function names. The func f0 above can be also written as 49 // 50 // { 51 // fn: ` 52 // func $(x int) int { 53 // return x * 64 54 // } 55 // `, 56 // pos: []string{"\tSHLQ\t[$]6,"}, 57 // neg: []string{"MULQ"} 58 // } 59 // 60 // Each '$'-function will be given a unique name of form f<N>_<arch>, 61 // where <N> is the test index in the test array, and <arch> is the 62 // test's architecture. 63 // 64 // It is allowed to mix named and unnamed functions in the same test 65 // array; the named functions will retain their original names. 66 67 // TestAssembly checks to make sure the assembly generated for 68 // functions contains certain expected instructions. 69 func TestAssembly(t *testing.T) { 70 testenv.MustHaveGoBuild(t) 71 if runtime.GOOS == "windows" { 72 // TODO: remove if we can get "go tool compile -S" to work on windows. 73 t.Skipf("skipping test: recursive windows compile not working") 74 } 75 dir, err := ioutil.TempDir("", "TestAssembly") 76 if err != nil { 77 t.Fatalf("could not create directory: %v", err) 78 } 79 defer os.RemoveAll(dir) 80 81 nameRegexp := regexp.MustCompile("func \\w+") 82 t.Run("platform", func(t *testing.T) { 83 for _, ats := range allAsmTests { 84 ats := ats 85 t.Run(ats.os+"/"+ats.arch, func(tt *testing.T) { 86 tt.Parallel() 87 88 asm := ats.compileToAsm(tt, dir) 89 90 for i, at := range ats.tests { 91 var funcName string 92 if strings.Contains(at.fn, "func $") { 93 funcName = fmt.Sprintf("f%d_%s", i, ats.arch) 94 } else { 95 funcName = nameRegexp.FindString(at.fn)[len("func "):] 96 } 97 fa := funcAsm(tt, asm, funcName) 98 if fa != "" { 99 at.verifyAsm(tt, fa) 100 } 101 } 102 }) 103 } 104 }) 105 } 106 107 var nextTextRegexp = regexp.MustCompile(`\n\S`) 108 109 // funcAsm returns the assembly listing for the given function name. 110 func funcAsm(t *testing.T, asm string, funcName string) string { 111 if i := strings.Index(asm, fmt.Sprintf("TEXT\t\"\".%s(SB)", funcName)); i >= 0 { 112 asm = asm[i:] 113 } else { 114 t.Errorf("could not find assembly for function %v", funcName) 115 return "" 116 } 117 118 // Find the next line that doesn't begin with whitespace. 119 loc := nextTextRegexp.FindStringIndex(asm) 120 if loc != nil { 121 asm = asm[:loc[0]] 122 } 123 124 return asm 125 } 126 127 type asmTest struct { 128 // function to compile 129 fn string 130 // regular expressions that must match the generated assembly 131 pos []string 132 // regular expressions that must not match the generated assembly 133 neg []string 134 } 135 136 func (at asmTest) verifyAsm(t *testing.T, fa string) { 137 for _, r := range at.pos { 138 if b, err := regexp.MatchString(r, fa); !b || err != nil { 139 t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa) 140 } 141 } 142 for _, r := range at.neg { 143 if b, err := regexp.MatchString(r, fa); b || err != nil { 144 t.Errorf("not expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa) 145 } 146 } 147 } 148 149 type asmTests struct { 150 arch string 151 os string 152 imports []string 153 tests []*asmTest 154 } 155 156 func (ats *asmTests) generateCode() []byte { 157 var buf bytes.Buffer 158 fmt.Fprintln(&buf, "package main") 159 for _, s := range ats.imports { 160 fmt.Fprintf(&buf, "import %q\n", s) 161 } 162 163 for i, t := range ats.tests { 164 function := strings.Replace(t.fn, "func $", fmt.Sprintf("func f%d_%s", i, ats.arch), 1) 165 fmt.Fprintln(&buf, function) 166 } 167 168 return buf.Bytes() 169 } 170 171 // compile compiles the package pkg for architecture arch and 172 // returns the generated assembly. dir is a scratch directory. 173 func (ats *asmTests) compileToAsm(t *testing.T, dir string) string { 174 // create test directory 175 testDir := filepath.Join(dir, fmt.Sprintf("%s_%s", ats.arch, ats.os)) 176 err := os.Mkdir(testDir, 0700) 177 if err != nil { 178 t.Fatalf("could not create directory: %v", err) 179 } 180 181 // Create source. 182 src := filepath.Join(testDir, "test.go") 183 err = ioutil.WriteFile(src, ats.generateCode(), 0600) 184 if err != nil { 185 t.Fatalf("error writing code: %v", err) 186 } 187 188 // First, install any dependencies we need. This builds the required export data 189 // for any packages that are imported. 190 for _, i := range ats.imports { 191 out := filepath.Join(testDir, i+".a") 192 193 if s := ats.runGo(t, "build", "-o", out, "-gcflags=-dolinkobj=false", i); s != "" { 194 t.Fatalf("Stdout = %s\nWant empty", s) 195 } 196 } 197 198 // Now, compile the individual file for which we want to see the generated assembly. 199 asm := ats.runGo(t, "tool", "compile", "-I", testDir, "-S", "-o", filepath.Join(testDir, "out.o"), src) 200 return asm 201 } 202 203 // runGo runs go command with the given args and returns stdout string. 204 // go is run with GOARCH and GOOS set as ats.arch and ats.os respectively 205 func (ats *asmTests) runGo(t *testing.T, args ...string) string { 206 var stdout, stderr bytes.Buffer 207 cmd := exec.Command(testenv.GoToolPath(t), args...) 208 cmd.Env = append(os.Environ(), "GOARCH="+ats.arch, "GOOS="+ats.os) 209 cmd.Stdout = &stdout 210 cmd.Stderr = &stderr 211 212 if err := cmd.Run(); err != nil { 213 t.Fatalf("error running cmd: %v\nstdout:\n%sstderr:\n%s\n", err, stdout.String(), stderr.String()) 214 } 215 216 if s := stderr.String(); s != "" { 217 t.Fatalf("Stderr = %s\nWant empty", s) 218 } 219 220 return stdout.String() 221 } 222 223 var allAsmTests = []*asmTests{ 224 { 225 arch: "amd64", 226 os: "linux", 227 imports: []string{"encoding/binary", "math", "math/bits", "unsafe", "runtime"}, 228 tests: linuxAMD64Tests, 229 }, 230 { 231 arch: "386", 232 os: "linux", 233 imports: []string{"encoding/binary"}, 234 tests: linux386Tests, 235 }, 236 { 237 arch: "s390x", 238 os: "linux", 239 imports: []string{"encoding/binary", "math", "math/bits"}, 240 tests: linuxS390XTests, 241 }, 242 { 243 arch: "arm", 244 os: "linux", 245 imports: []string{"math/bits"}, 246 tests: linuxARMTests, 247 }, 248 { 249 arch: "arm64", 250 os: "linux", 251 imports: []string{"math/bits"}, 252 tests: linuxARM64Tests, 253 }, 254 { 255 arch: "mips", 256 os: "linux", 257 imports: []string{"math/bits"}, 258 tests: linuxMIPSTests, 259 }, 260 { 261 arch: "mips64", 262 os: "linux", 263 tests: linuxMIPS64Tests, 264 }, 265 { 266 arch: "ppc64le", 267 os: "linux", 268 imports: []string{"math", "math/bits"}, 269 tests: linuxPPC64LETests, 270 }, 271 { 272 arch: "amd64", 273 os: "plan9", 274 tests: plan9AMD64Tests, 275 }, 276 } 277 278 var linuxAMD64Tests = []*asmTest{ 279 // multiplication by powers of two 280 { 281 fn: ` 282 func $(n int) int { 283 return n * 64 284 } 285 `, 286 pos: []string{"\tSHLQ\t\\$6,"}, 287 neg: []string{"IMULQ"}, 288 }, 289 { 290 fn: ` 291 func $(n int) int { 292 return -128*n 293 } 294 `, 295 pos: []string{"SHLQ"}, 296 neg: []string{"IMULQ"}, 297 }, 298 299 { 300 fn: ` 301 func $(x int) int { 302 return x * 96 303 } 304 `, 305 pos: []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"}, 306 }, 307 // Load-combining tests. 308 { 309 fn: ` 310 func f2(b []byte) uint64 { 311 return binary.LittleEndian.Uint64(b) 312 } 313 `, 314 pos: []string{"\tMOVQ\t\\(.*\\),"}, 315 }, 316 { 317 fn: ` 318 func f3(b []byte, i int) uint64 { 319 return binary.LittleEndian.Uint64(b[i:]) 320 } 321 `, 322 pos: []string{"\tMOVQ\t\\(.*\\)\\(.*\\*1\\),"}, 323 }, 324 { 325 fn: ` 326 func f4(b []byte) uint32 { 327 return binary.LittleEndian.Uint32(b) 328 } 329 `, 330 pos: []string{"\tMOVL\t\\(.*\\),"}, 331 }, 332 { 333 fn: ` 334 func f5(b []byte, i int) uint32 { 335 return binary.LittleEndian.Uint32(b[i:]) 336 } 337 `, 338 pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 339 }, 340 { 341 fn: ` 342 func f6(b []byte) uint64 { 343 return binary.BigEndian.Uint64(b) 344 } 345 `, 346 pos: []string{"\tBSWAPQ\t"}, 347 }, 348 { 349 fn: ` 350 func f7(b []byte, i int) uint64 { 351 return binary.BigEndian.Uint64(b[i:]) 352 } 353 `, 354 pos: []string{"\tBSWAPQ\t"}, 355 }, 356 { 357 fn: ` 358 func f8(b []byte, v uint64) { 359 binary.BigEndian.PutUint64(b, v) 360 } 361 `, 362 pos: []string{"\tBSWAPQ\t"}, 363 }, 364 { 365 fn: ` 366 func f9(b []byte, i int, v uint64) { 367 binary.BigEndian.PutUint64(b[i:], v) 368 } 369 `, 370 pos: []string{"\tBSWAPQ\t"}, 371 }, 372 { 373 fn: ` 374 func f10(b []byte) uint32 { 375 return binary.BigEndian.Uint32(b) 376 } 377 `, 378 pos: []string{"\tBSWAPL\t"}, 379 }, 380 { 381 fn: ` 382 func f11(b []byte, i int) uint32 { 383 return binary.BigEndian.Uint32(b[i:]) 384 } 385 `, 386 pos: []string{"\tBSWAPL\t"}, 387 }, 388 { 389 fn: ` 390 func f12(b []byte, v uint32) { 391 binary.BigEndian.PutUint32(b, v) 392 } 393 `, 394 pos: []string{"\tBSWAPL\t"}, 395 }, 396 { 397 fn: ` 398 func f13(b []byte, i int, v uint32) { 399 binary.BigEndian.PutUint32(b[i:], v) 400 } 401 `, 402 pos: []string{"\tBSWAPL\t"}, 403 }, 404 { 405 fn: ` 406 func f14(b []byte) uint16 { 407 return binary.BigEndian.Uint16(b) 408 } 409 `, 410 pos: []string{"\tROLW\t\\$8,"}, 411 }, 412 { 413 fn: ` 414 func f15(b []byte, i int) uint16 { 415 return binary.BigEndian.Uint16(b[i:]) 416 } 417 `, 418 pos: []string{"\tROLW\t\\$8,"}, 419 }, 420 { 421 fn: ` 422 func f16(b []byte, v uint16) { 423 binary.BigEndian.PutUint16(b, v) 424 } 425 `, 426 pos: []string{"\tROLW\t\\$8,"}, 427 }, 428 { 429 fn: ` 430 func f17(b []byte, i int, v uint16) { 431 binary.BigEndian.PutUint16(b[i:], v) 432 } 433 `, 434 pos: []string{"\tROLW\t\\$8,"}, 435 }, 436 // Structure zeroing. See issue #18370. 437 { 438 fn: ` 439 type T1 struct { 440 a, b, c int 441 } 442 func $(t *T1) { 443 *t = T1{} 444 } 445 `, 446 pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)"}, 447 }, 448 // SSA-able composite literal initialization. Issue 18872. 449 { 450 fn: ` 451 type T18872 struct { 452 a, b, c, d int 453 } 454 455 func f18872(p *T18872) { 456 *p = T18872{1, 2, 3, 4} 457 } 458 `, 459 pos: []string{"\tMOVQ\t[$]1", "\tMOVQ\t[$]2", "\tMOVQ\t[$]3", "\tMOVQ\t[$]4"}, 460 }, 461 // Also test struct containing pointers (this was special because of write barriers). 462 { 463 fn: ` 464 type T2 struct { 465 a, b, c *int 466 } 467 func f19(t *T2) { 468 *t = T2{} 469 } 470 `, 471 pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)", "\tCALL\truntime\\.writebarrierptr\\(SB\\)"}, 472 }, 473 // Rotate tests 474 { 475 fn: ` 476 func f20(x uint64) uint64 { 477 return x<<7 | x>>57 478 } 479 `, 480 pos: []string{"\tROLQ\t[$]7,"}, 481 }, 482 { 483 fn: ` 484 func f21(x uint64) uint64 { 485 return x<<7 + x>>57 486 } 487 `, 488 pos: []string{"\tROLQ\t[$]7,"}, 489 }, 490 { 491 fn: ` 492 func f22(x uint64) uint64 { 493 return x<<7 ^ x>>57 494 } 495 `, 496 pos: []string{"\tROLQ\t[$]7,"}, 497 }, 498 { 499 fn: ` 500 func f23(x uint32) uint32 { 501 return x<<7 + x>>25 502 } 503 `, 504 pos: []string{"\tROLL\t[$]7,"}, 505 }, 506 { 507 fn: ` 508 func f24(x uint32) uint32 { 509 return x<<7 | x>>25 510 } 511 `, 512 pos: []string{"\tROLL\t[$]7,"}, 513 }, 514 { 515 fn: ` 516 func f25(x uint32) uint32 { 517 return x<<7 ^ x>>25 518 } 519 `, 520 pos: []string{"\tROLL\t[$]7,"}, 521 }, 522 { 523 fn: ` 524 func f26(x uint16) uint16 { 525 return x<<7 + x>>9 526 } 527 `, 528 pos: []string{"\tROLW\t[$]7,"}, 529 }, 530 { 531 fn: ` 532 func f27(x uint16) uint16 { 533 return x<<7 | x>>9 534 } 535 `, 536 pos: []string{"\tROLW\t[$]7,"}, 537 }, 538 { 539 fn: ` 540 func f28(x uint16) uint16 { 541 return x<<7 ^ x>>9 542 } 543 `, 544 pos: []string{"\tROLW\t[$]7,"}, 545 }, 546 { 547 fn: ` 548 func f29(x uint8) uint8 { 549 return x<<7 + x>>1 550 } 551 `, 552 pos: []string{"\tROLB\t[$]7,"}, 553 }, 554 { 555 fn: ` 556 func f30(x uint8) uint8 { 557 return x<<7 | x>>1 558 } 559 `, 560 pos: []string{"\tROLB\t[$]7,"}, 561 }, 562 { 563 fn: ` 564 func f31(x uint8) uint8 { 565 return x<<7 ^ x>>1 566 } 567 `, 568 pos: []string{"\tROLB\t[$]7,"}, 569 }, 570 // Rotate after inlining (see issue 18254). 571 { 572 fn: ` 573 func f32(x uint32) uint32 { 574 return g(x, 7) 575 } 576 func g(x uint32, k uint) uint32 { 577 return x<<k | x>>(32-k) 578 } 579 `, 580 pos: []string{"\tROLL\t[$]7,"}, 581 }, 582 { 583 fn: ` 584 func f33(m map[int]int) int { 585 return m[5] 586 } 587 `, 588 pos: []string{"\tMOVQ\t[$]5,"}, 589 }, 590 // Direct use of constants in fast map access calls. Issue 19015. 591 { 592 fn: ` 593 func f34(m map[int]int) bool { 594 _, ok := m[5] 595 return ok 596 } 597 `, 598 pos: []string{"\tMOVQ\t[$]5,"}, 599 }, 600 { 601 fn: ` 602 func f35(m map[string]int) int { 603 return m["abc"] 604 } 605 `, 606 pos: []string{"\"abc\""}, 607 }, 608 { 609 fn: ` 610 func f36(m map[string]int) bool { 611 _, ok := m["abc"] 612 return ok 613 } 614 `, 615 pos: []string{"\"abc\""}, 616 }, 617 // Bit test ops on amd64, issue 18943. 618 { 619 fn: ` 620 func f37(a, b uint64) int { 621 if a&(1<<(b&63)) != 0 { 622 return 1 623 } 624 return -1 625 } 626 `, 627 pos: []string{"\tBTQ\t"}, 628 }, 629 { 630 fn: ` 631 func f38(a, b uint64) bool { 632 return a&(1<<(b&63)) != 0 633 } 634 `, 635 pos: []string{"\tBTQ\t"}, 636 }, 637 { 638 fn: ` 639 func f39(a uint64) int { 640 if a&(1<<60) != 0 { 641 return 1 642 } 643 return -1 644 } 645 `, 646 pos: []string{"\tBTQ\t\\$60"}, 647 }, 648 { 649 fn: ` 650 func f40(a uint64) bool { 651 return a&(1<<60) != 0 652 } 653 `, 654 pos: []string{"\tBTQ\t\\$60"}, 655 }, 656 // Intrinsic tests for math/bits 657 { 658 fn: ` 659 func f41(a uint64) int { 660 return bits.TrailingZeros64(a) 661 } 662 `, 663 pos: []string{"\tBSFQ\t", "\tMOVL\t\\$64,", "\tCMOVQEQ\t"}, 664 }, 665 { 666 fn: ` 667 func f42(a uint32) int { 668 return bits.TrailingZeros32(a) 669 } 670 `, 671 pos: []string{"\tBSFQ\t", "\tORQ\t[^$]", "\tMOVQ\t\\$4294967296,"}, 672 }, 673 { 674 fn: ` 675 func f43(a uint16) int { 676 return bits.TrailingZeros16(a) 677 } 678 `, 679 pos: []string{"\tBSFQ\t", "\tORQ\t\\$65536,"}, 680 }, 681 { 682 fn: ` 683 func f44(a uint8) int { 684 return bits.TrailingZeros8(a) 685 } 686 `, 687 pos: []string{"\tBSFQ\t", "\tORQ\t\\$256,"}, 688 }, 689 { 690 fn: ` 691 func f45(a uint64) uint64 { 692 return bits.ReverseBytes64(a) 693 } 694 `, 695 pos: []string{"\tBSWAPQ\t"}, 696 }, 697 { 698 fn: ` 699 func f46(a uint32) uint32 { 700 return bits.ReverseBytes32(a) 701 } 702 `, 703 pos: []string{"\tBSWAPL\t"}, 704 }, 705 { 706 fn: ` 707 func f47(a uint16) uint16 { 708 return bits.ReverseBytes16(a) 709 } 710 `, 711 pos: []string{"\tROLW\t\\$8,"}, 712 }, 713 { 714 fn: ` 715 func f48(a uint64) int { 716 return bits.Len64(a) 717 } 718 `, 719 pos: []string{"\tBSRQ\t"}, 720 }, 721 { 722 fn: ` 723 func f49(a uint32) int { 724 return bits.Len32(a) 725 } 726 `, 727 pos: []string{"\tBSRQ\t"}, 728 }, 729 { 730 fn: ` 731 func f50(a uint16) int { 732 return bits.Len16(a) 733 } 734 `, 735 pos: []string{"\tBSRQ\t"}, 736 }, 737 /* see ssa.go 738 { 739 fn:` 740 func f51(a uint8) int { 741 return bits.Len8(a) 742 } 743 `, 744 pos:[]string{"\tBSRQ\t"}, 745 }, 746 */ 747 { 748 fn: ` 749 func f52(a uint) int { 750 return bits.Len(a) 751 } 752 `, 753 pos: []string{"\tBSRQ\t"}, 754 }, 755 { 756 fn: ` 757 func f53(a uint64) int { 758 return bits.LeadingZeros64(a) 759 } 760 `, 761 pos: []string{"\tBSRQ\t"}, 762 }, 763 { 764 fn: ` 765 func f54(a uint32) int { 766 return bits.LeadingZeros32(a) 767 } 768 `, 769 pos: []string{"\tBSRQ\t"}, 770 }, 771 { 772 fn: ` 773 func f55(a uint16) int { 774 return bits.LeadingZeros16(a) 775 } 776 `, 777 pos: []string{"\tBSRQ\t"}, 778 }, 779 /* see ssa.go 780 { 781 fn:` 782 func f56(a uint8) int { 783 return bits.LeadingZeros8(a) 784 } 785 `, 786 pos:[]string{"\tBSRQ\t"}, 787 }, 788 */ 789 { 790 fn: ` 791 func f57(a uint) int { 792 return bits.LeadingZeros(a) 793 } 794 `, 795 pos: []string{"\tBSRQ\t"}, 796 }, 797 { 798 fn: ` 799 func pop1(x uint64) int { 800 return bits.OnesCount64(x) 801 }`, 802 pos: []string{"\tPOPCNTQ\t", "support_popcnt"}, 803 }, 804 { 805 fn: ` 806 func pop2(x uint32) int { 807 return bits.OnesCount32(x) 808 }`, 809 pos: []string{"\tPOPCNTL\t", "support_popcnt"}, 810 }, 811 { 812 fn: ` 813 func pop3(x uint16) int { 814 return bits.OnesCount16(x) 815 }`, 816 pos: []string{"\tPOPCNTL\t", "support_popcnt"}, 817 }, 818 { 819 fn: ` 820 func pop4(x uint) int { 821 return bits.OnesCount(x) 822 }`, 823 pos: []string{"\tPOPCNTQ\t", "support_popcnt"}, 824 }, 825 // multiplication merging tests 826 { 827 fn: ` 828 func mul1(n int) int { 829 return 15*n + 31*n 830 }`, 831 pos: []string{"\tIMULQ\t[$]46"}, // 46*n 832 }, 833 { 834 fn: ` 835 func mul2(n int) int { 836 return 5*n + 7*(n+1) + 11*(n+2) 837 }`, 838 pos: []string{"\tIMULQ\t[$]23", "\tADDQ\t[$]29"}, // 23*n + 29 839 }, 840 { 841 fn: ` 842 func mul3(a, n int) int { 843 return a*n + 19*n 844 }`, 845 pos: []string{"\tADDQ\t[$]19", "\tIMULQ"}, // (a+19)*n 846 }, 847 { 848 fn: ` 849 func mul4(n int) int { 850 return 23*n - 9*n 851 }`, 852 pos: []string{"\tIMULQ\t[$]14"}, // 14*n 853 }, 854 { 855 fn: ` 856 func mul5(a, n int) int { 857 return a*n - 19*n 858 }`, 859 pos: []string{"\tADDQ\t[$]-19", "\tIMULQ"}, // (a-19)*n 860 }, 861 862 // see issue 19595. 863 // We want to merge load+op in f58, but not in f59. 864 { 865 fn: ` 866 func f58(p, q *int) { 867 x := *p 868 *q += x 869 }`, 870 pos: []string{"\tADDQ\t\\("}, 871 }, 872 { 873 fn: ` 874 func f59(p, q *int) { 875 x := *p 876 for i := 0; i < 10; i++ { 877 *q += x 878 } 879 }`, 880 pos: []string{"\tADDQ\t[A-Z]"}, 881 }, 882 // Floating-point strength reduction 883 { 884 fn: ` 885 func f60(f float64) float64 { 886 return f * 2.0 887 }`, 888 pos: []string{"\tADDSD\t"}, 889 }, 890 { 891 fn: ` 892 func f62(f float64) float64 { 893 return f / 16.0 894 }`, 895 pos: []string{"\tMULSD\t"}, 896 }, 897 { 898 fn: ` 899 func f63(f float64) float64 { 900 return f / 0.125 901 }`, 902 pos: []string{"\tMULSD\t"}, 903 }, 904 { 905 fn: ` 906 func f64(f float64) float64 { 907 return f / 0.5 908 }`, 909 pos: []string{"\tADDSD\t"}, 910 }, 911 // Check that compare to constant string uses 2/4/8 byte compares 912 { 913 fn: ` 914 func f65(a string) bool { 915 return a == "xx" 916 }`, 917 pos: []string{"\tCMPW\t[A-Z]"}, 918 }, 919 { 920 fn: ` 921 func f66(a string) bool { 922 return a == "xxxx" 923 }`, 924 pos: []string{"\tCMPL\t[A-Z]"}, 925 }, 926 { 927 fn: ` 928 func f67(a string) bool { 929 return a == "xxxxxxxx" 930 }`, 931 pos: []string{"\tCMPQ\t[A-Z]"}, 932 }, 933 // Non-constant rotate 934 { 935 fn: `func rot64l(x uint64, y int) uint64 { 936 z := uint(y & 63) 937 return x << z | x >> (64-z) 938 }`, 939 pos: []string{"\tROLQ\t"}, 940 }, 941 { 942 fn: `func rot64r(x uint64, y int) uint64 { 943 z := uint(y & 63) 944 return x >> z | x << (64-z) 945 }`, 946 pos: []string{"\tRORQ\t"}, 947 }, 948 { 949 fn: `func rot32l(x uint32, y int) uint32 { 950 z := uint(y & 31) 951 return x << z | x >> (32-z) 952 }`, 953 pos: []string{"\tROLL\t"}, 954 }, 955 { 956 fn: `func rot32r(x uint32, y int) uint32 { 957 z := uint(y & 31) 958 return x >> z | x << (32-z) 959 }`, 960 pos: []string{"\tRORL\t"}, 961 }, 962 { 963 fn: `func rot16l(x uint16, y int) uint16 { 964 z := uint(y & 15) 965 return x << z | x >> (16-z) 966 }`, 967 pos: []string{"\tROLW\t"}, 968 }, 969 { 970 fn: `func rot16r(x uint16, y int) uint16 { 971 z := uint(y & 15) 972 return x >> z | x << (16-z) 973 }`, 974 pos: []string{"\tRORW\t"}, 975 }, 976 { 977 fn: `func rot8l(x uint8, y int) uint8 { 978 z := uint(y & 7) 979 return x << z | x >> (8-z) 980 }`, 981 pos: []string{"\tROLB\t"}, 982 }, 983 { 984 fn: `func rot8r(x uint8, y int) uint8 { 985 z := uint(y & 7) 986 return x >> z | x << (8-z) 987 }`, 988 pos: []string{"\tRORB\t"}, 989 }, 990 // Check that array compare uses 2/4/8 byte compares 991 { 992 fn: ` 993 func f68(a,b [2]byte) bool { 994 return a == b 995 }`, 996 pos: []string{"\tCMPW\t[A-Z]"}, 997 }, 998 { 999 fn: ` 1000 func f69(a,b [3]uint16) bool { 1001 return a == b 1002 }`, 1003 pos: []string{"\tCMPL\t[A-Z]"}, 1004 }, 1005 { 1006 fn: ` 1007 func f70(a,b [15]byte) bool { 1008 return a == b 1009 }`, 1010 pos: []string{"\tCMPQ\t[A-Z]"}, 1011 }, 1012 { 1013 fn: ` 1014 func f71(a,b unsafe.Pointer) bool { // This was a TODO in mapaccess1_faststr 1015 return *((*[4]byte)(a)) != *((*[4]byte)(b)) 1016 }`, 1017 pos: []string{"\tCMPL\t[A-Z]"}, 1018 }, 1019 { 1020 // make sure assembly output has matching offset and base register. 1021 fn: ` 1022 func f72(a, b int) int { 1023 //go:noinline 1024 func() {_, _ = a, b} () // use some frame 1025 return b 1026 } 1027 `, 1028 pos: []string{"b\\+40\\(SP\\)"}, 1029 }, 1030 { 1031 // check load combining 1032 fn: ` 1033 func f73(a, b byte) (byte,byte) { 1034 return f73(f73(a,b)) 1035 } 1036 `, 1037 pos: []string{"\tMOVW\t"}, 1038 }, 1039 { 1040 fn: ` 1041 func f74(a, b uint16) (uint16,uint16) { 1042 return f74(f74(a,b)) 1043 } 1044 `, 1045 pos: []string{"\tMOVL\t"}, 1046 }, 1047 { 1048 fn: ` 1049 func f75(a, b uint32) (uint32,uint32) { 1050 return f75(f75(a,b)) 1051 } 1052 `, 1053 pos: []string{"\tMOVQ\t"}, 1054 }, 1055 { 1056 fn: ` 1057 func f76(a, b uint64) (uint64,uint64) { 1058 return f76(f76(a,b)) 1059 } 1060 `, 1061 pos: []string{"\tMOVUPS\t"}, 1062 }, 1063 // Make sure we don't put pointers in SSE registers across safe points. 1064 { 1065 fn: ` 1066 func $(p, q *[2]*int) { 1067 a, b := p[0], p[1] 1068 runtime.GC() 1069 q[0], q[1] = a, b 1070 } 1071 `, 1072 neg: []string{"MOVUPS"}, 1073 }, 1074 { 1075 // check that stack store is optimized away 1076 fn: ` 1077 func $() int { 1078 var x int 1079 return *(&x) 1080 } 1081 `, 1082 pos: []string{"TEXT\t.*, [$]0-8"}, 1083 }, 1084 // math.Abs using integer registers 1085 { 1086 fn: ` 1087 func $(x float64) float64 { 1088 return math.Abs(x) 1089 } 1090 `, 1091 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,"}, 1092 }, 1093 // math.Copysign using integer registers 1094 { 1095 fn: ` 1096 func $(x, y float64) float64 { 1097 return math.Copysign(x, y) 1098 } 1099 `, 1100 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,", "\tSHRQ\t[$]63,", "\tSHLQ\t[$]63,", "\tORQ\t"}, 1101 }, 1102 // int <-> fp moves 1103 { 1104 fn: ` 1105 func $(x float64) uint64 { 1106 return math.Float64bits(x+1) + 1 1107 } 1108 `, 1109 pos: []string{"\tMOVQ\tX.*, [^X].*"}, 1110 }, 1111 { 1112 fn: ` 1113 func $(x float32) uint32 { 1114 return math.Float32bits(x+1) + 1 1115 } 1116 `, 1117 pos: []string{"\tMOVL\tX.*, [^X].*"}, 1118 }, 1119 { 1120 fn: ` 1121 func $(x uint64) float64 { 1122 return math.Float64frombits(x+1) + 1 1123 } 1124 `, 1125 pos: []string{"\tMOVQ\t[^X].*, X.*"}, 1126 }, 1127 { 1128 fn: ` 1129 func $(x uint32) float32 { 1130 return math.Float32frombits(x+1) + 1 1131 } 1132 `, 1133 pos: []string{"\tMOVL\t[^X].*, X.*"}, 1134 }, 1135 { 1136 fn: ` 1137 func $(x uint32) bool { 1138 return x > 4 1139 } 1140 `, 1141 pos: []string{"\tSETHI\t\\("}, 1142 }, 1143 // Check that len() and cap() div by a constant power of two 1144 // are compiled into SHRQ. 1145 { 1146 fn: ` 1147 func $(a []int) int { 1148 return len(a) / 1024 1149 } 1150 `, 1151 pos: []string{"\tSHRQ\t\\$10,"}, 1152 }, 1153 { 1154 fn: ` 1155 func $(s string) int { 1156 return len(s) / (4097 >> 1) 1157 } 1158 `, 1159 pos: []string{"\tSHRQ\t\\$11,"}, 1160 }, 1161 { 1162 fn: ` 1163 func $(a []int) int { 1164 return cap(a) / ((1 << 11) + 2048) 1165 } 1166 `, 1167 pos: []string{"\tSHRQ\t\\$12,"}, 1168 }, 1169 // Check that len() and cap() mod by a constant power of two 1170 // are compiled into ANDQ. 1171 { 1172 fn: ` 1173 func $(a []int) int { 1174 return len(a) % 1024 1175 } 1176 `, 1177 pos: []string{"\tANDQ\t\\$1023,"}, 1178 }, 1179 { 1180 fn: ` 1181 func $(s string) int { 1182 return len(s) % (4097 >> 1) 1183 } 1184 `, 1185 pos: []string{"\tANDQ\t\\$2047,"}, 1186 }, 1187 { 1188 fn: ` 1189 func $(a []int) int { 1190 return cap(a) % ((1 << 11) + 2048) 1191 } 1192 `, 1193 pos: []string{"\tANDQ\t\\$4095,"}, 1194 }, 1195 } 1196 1197 var linux386Tests = []*asmTest{ 1198 { 1199 fn: ` 1200 func f0(b []byte) uint32 { 1201 return binary.LittleEndian.Uint32(b) 1202 } 1203 `, 1204 pos: []string{"\tMOVL\t\\(.*\\),"}, 1205 }, 1206 { 1207 fn: ` 1208 func f1(b []byte, i int) uint32 { 1209 return binary.LittleEndian.Uint32(b[i:]) 1210 } 1211 `, 1212 pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 1213 }, 1214 1215 // multiplication by powers of two 1216 { 1217 fn: ` 1218 func $(n int) int { 1219 return 32*n 1220 } 1221 `, 1222 pos: []string{"SHLL"}, 1223 neg: []string{"IMULL"}, 1224 }, 1225 { 1226 fn: ` 1227 func $(n int) int { 1228 return -64*n 1229 } 1230 `, 1231 pos: []string{"SHLL"}, 1232 neg: []string{"IMULL"}, 1233 }, 1234 1235 // multiplication merging tests 1236 { 1237 fn: ` 1238 func $(n int) int { 1239 return 9*n + 14*n 1240 }`, 1241 pos: []string{"\tIMULL\t[$]23"}, // 23*n 1242 }, 1243 { 1244 fn: ` 1245 func $(a, n int) int { 1246 return 19*a + a*n 1247 }`, 1248 pos: []string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a 1249 }, 1250 { 1251 // check that stack store is optimized away 1252 fn: ` 1253 func $() int { 1254 var x int 1255 return *(&x) 1256 } 1257 `, 1258 pos: []string{"TEXT\t.*, [$]0-4"}, 1259 }, 1260 { 1261 fn: ` 1262 func mul3(n int) int { 1263 return 23*n - 9*n 1264 }`, 1265 pos: []string{"\tIMULL\t[$]14"}, // 14*n 1266 }, 1267 { 1268 fn: ` 1269 func mul4(a, n int) int { 1270 return n*a - a*19 1271 }`, 1272 pos: []string{"\tADDL\t[$]-19", "\tIMULL"}, // (n-19)*a 1273 }, 1274 // Check that len() and cap() div by a constant power of two 1275 // are compiled into SHRL. 1276 { 1277 fn: ` 1278 func $(a []int) int { 1279 return len(a) / 1024 1280 } 1281 `, 1282 pos: []string{"\tSHRL\t\\$10,"}, 1283 }, 1284 { 1285 fn: ` 1286 func $(s string) int { 1287 return len(s) / (4097 >> 1) 1288 } 1289 `, 1290 pos: []string{"\tSHRL\t\\$11,"}, 1291 }, 1292 { 1293 fn: ` 1294 func $(a []int) int { 1295 return cap(a) / ((1 << 11) + 2048) 1296 } 1297 `, 1298 pos: []string{"\tSHRL\t\\$12,"}, 1299 }, 1300 // Check that len() and cap() mod by a constant power of two 1301 // are compiled into ANDL. 1302 { 1303 fn: ` 1304 func $(a []int) int { 1305 return len(a) % 1024 1306 } 1307 `, 1308 pos: []string{"\tANDL\t\\$1023,"}, 1309 }, 1310 { 1311 fn: ` 1312 func $(s string) int { 1313 return len(s) % (4097 >> 1) 1314 } 1315 `, 1316 pos: []string{"\tANDL\t\\$2047,"}, 1317 }, 1318 { 1319 fn: ` 1320 func $(a []int) int { 1321 return cap(a) % ((1 << 11) + 2048) 1322 } 1323 `, 1324 pos: []string{"\tANDL\t\\$4095,"}, 1325 }, 1326 } 1327 1328 var linuxS390XTests = []*asmTest{ 1329 { 1330 fn: ` 1331 func f0(b []byte) uint32 { 1332 return binary.LittleEndian.Uint32(b) 1333 } 1334 `, 1335 pos: []string{"\tMOVWBR\t\\(.*\\),"}, 1336 }, 1337 { 1338 fn: ` 1339 func f1(b []byte, i int) uint32 { 1340 return binary.LittleEndian.Uint32(b[i:]) 1341 } 1342 `, 1343 pos: []string{"\tMOVWBR\t\\(.*\\)\\(.*\\*1\\),"}, 1344 }, 1345 { 1346 fn: ` 1347 func f2(b []byte) uint64 { 1348 return binary.LittleEndian.Uint64(b) 1349 } 1350 `, 1351 pos: []string{"\tMOVDBR\t\\(.*\\),"}, 1352 }, 1353 { 1354 fn: ` 1355 func f3(b []byte, i int) uint64 { 1356 return binary.LittleEndian.Uint64(b[i:]) 1357 } 1358 `, 1359 pos: []string{"\tMOVDBR\t\\(.*\\)\\(.*\\*1\\),"}, 1360 }, 1361 { 1362 fn: ` 1363 func f4(b []byte) uint32 { 1364 return binary.BigEndian.Uint32(b) 1365 } 1366 `, 1367 pos: []string{"\tMOVWZ\t\\(.*\\),"}, 1368 }, 1369 { 1370 fn: ` 1371 func f5(b []byte, i int) uint32 { 1372 return binary.BigEndian.Uint32(b[i:]) 1373 } 1374 `, 1375 pos: []string{"\tMOVWZ\t\\(.*\\)\\(.*\\*1\\),"}, 1376 }, 1377 { 1378 fn: ` 1379 func f6(b []byte) uint64 { 1380 return binary.BigEndian.Uint64(b) 1381 } 1382 `, 1383 pos: []string{"\tMOVD\t\\(.*\\),"}, 1384 }, 1385 { 1386 fn: ` 1387 func f7(b []byte, i int) uint64 { 1388 return binary.BigEndian.Uint64(b[i:]) 1389 } 1390 `, 1391 pos: []string{"\tMOVD\t\\(.*\\)\\(.*\\*1\\),"}, 1392 }, 1393 { 1394 fn: ` 1395 func f8(x uint64) uint64 { 1396 return x<<7 + x>>57 1397 } 1398 `, 1399 pos: []string{"\tRLLG\t[$]7,"}, 1400 }, 1401 { 1402 fn: ` 1403 func f9(x uint64) uint64 { 1404 return x<<7 | x>>57 1405 } 1406 `, 1407 pos: []string{"\tRLLG\t[$]7,"}, 1408 }, 1409 { 1410 fn: ` 1411 func f10(x uint64) uint64 { 1412 return x<<7 ^ x>>57 1413 } 1414 `, 1415 pos: []string{"\tRLLG\t[$]7,"}, 1416 }, 1417 { 1418 fn: ` 1419 func f11(x uint32) uint32 { 1420 return x<<7 + x>>25 1421 } 1422 `, 1423 pos: []string{"\tRLL\t[$]7,"}, 1424 }, 1425 { 1426 fn: ` 1427 func f12(x uint32) uint32 { 1428 return x<<7 | x>>25 1429 } 1430 `, 1431 pos: []string{"\tRLL\t[$]7,"}, 1432 }, 1433 { 1434 fn: ` 1435 func f13(x uint32) uint32 { 1436 return x<<7 ^ x>>25 1437 } 1438 `, 1439 pos: []string{"\tRLL\t[$]7,"}, 1440 }, 1441 // Fused multiply-add/sub instructions. 1442 { 1443 fn: ` 1444 func f14(x, y, z float64) float64 { 1445 return x * y + z 1446 } 1447 `, 1448 pos: []string{"\tFMADD\t"}, 1449 }, 1450 { 1451 fn: ` 1452 func f15(x, y, z float64) float64 { 1453 return x * y - z 1454 } 1455 `, 1456 pos: []string{"\tFMSUB\t"}, 1457 }, 1458 { 1459 fn: ` 1460 func f16(x, y, z float32) float32 { 1461 return x * y + z 1462 } 1463 `, 1464 pos: []string{"\tFMADDS\t"}, 1465 }, 1466 { 1467 fn: ` 1468 func f17(x, y, z float32) float32 { 1469 return x * y - z 1470 } 1471 `, 1472 pos: []string{"\tFMSUBS\t"}, 1473 }, 1474 // Intrinsic tests for math/bits 1475 { 1476 fn: ` 1477 func f18(a uint64) int { 1478 return bits.TrailingZeros64(a) 1479 } 1480 `, 1481 pos: []string{"\tFLOGR\t"}, 1482 }, 1483 { 1484 fn: ` 1485 func f19(a uint32) int { 1486 return bits.TrailingZeros32(a) 1487 } 1488 `, 1489 pos: []string{"\tFLOGR\t", "\tMOVWZ\t"}, 1490 }, 1491 { 1492 fn: ` 1493 func f20(a uint16) int { 1494 return bits.TrailingZeros16(a) 1495 } 1496 `, 1497 pos: []string{"\tFLOGR\t", "\tOR\t\\$65536,"}, 1498 }, 1499 { 1500 fn: ` 1501 func f21(a uint8) int { 1502 return bits.TrailingZeros8(a) 1503 } 1504 `, 1505 pos: []string{"\tFLOGR\t", "\tOR\t\\$256,"}, 1506 }, 1507 // Intrinsic tests for math/bits 1508 { 1509 fn: ` 1510 func f22(a uint64) uint64 { 1511 return bits.ReverseBytes64(a) 1512 } 1513 `, 1514 pos: []string{"\tMOVDBR\t"}, 1515 }, 1516 { 1517 fn: ` 1518 func f23(a uint32) uint32 { 1519 return bits.ReverseBytes32(a) 1520 } 1521 `, 1522 pos: []string{"\tMOVWBR\t"}, 1523 }, 1524 { 1525 fn: ` 1526 func f24(a uint64) int { 1527 return bits.Len64(a) 1528 } 1529 `, 1530 pos: []string{"\tFLOGR\t"}, 1531 }, 1532 { 1533 fn: ` 1534 func f25(a uint32) int { 1535 return bits.Len32(a) 1536 } 1537 `, 1538 pos: []string{"\tFLOGR\t"}, 1539 }, 1540 { 1541 fn: ` 1542 func f26(a uint16) int { 1543 return bits.Len16(a) 1544 } 1545 `, 1546 pos: []string{"\tFLOGR\t"}, 1547 }, 1548 { 1549 fn: ` 1550 func f27(a uint8) int { 1551 return bits.Len8(a) 1552 } 1553 `, 1554 pos: []string{"\tFLOGR\t"}, 1555 }, 1556 { 1557 fn: ` 1558 func f28(a uint) int { 1559 return bits.Len(a) 1560 } 1561 `, 1562 pos: []string{"\tFLOGR\t"}, 1563 }, 1564 { 1565 fn: ` 1566 func f29(a uint64) int { 1567 return bits.LeadingZeros64(a) 1568 } 1569 `, 1570 pos: []string{"\tFLOGR\t"}, 1571 }, 1572 { 1573 fn: ` 1574 func f30(a uint32) int { 1575 return bits.LeadingZeros32(a) 1576 } 1577 `, 1578 pos: []string{"\tFLOGR\t"}, 1579 }, 1580 { 1581 fn: ` 1582 func f31(a uint16) int { 1583 return bits.LeadingZeros16(a) 1584 } 1585 `, 1586 pos: []string{"\tFLOGR\t"}, 1587 }, 1588 { 1589 fn: ` 1590 func f32(a uint8) int { 1591 return bits.LeadingZeros8(a) 1592 } 1593 `, 1594 pos: []string{"\tFLOGR\t"}, 1595 }, 1596 { 1597 fn: ` 1598 func f33(a uint) int { 1599 return bits.LeadingZeros(a) 1600 } 1601 `, 1602 pos: []string{"\tFLOGR\t"}, 1603 }, 1604 // Intrinsic tests for math. 1605 { 1606 fn: ` 1607 func ceil(x float64) float64 { 1608 return math.Ceil(x) 1609 } 1610 `, 1611 pos: []string{"\tFIDBR\t[$]6"}, 1612 }, 1613 { 1614 fn: ` 1615 func floor(x float64) float64 { 1616 return math.Floor(x) 1617 } 1618 `, 1619 pos: []string{"\tFIDBR\t[$]7"}, 1620 }, 1621 { 1622 fn: ` 1623 func round(x float64) float64 { 1624 return math.Round(x) 1625 } 1626 `, 1627 pos: []string{"\tFIDBR\t[$]1"}, 1628 }, 1629 { 1630 fn: ` 1631 func trunc(x float64) float64 { 1632 return math.Trunc(x) 1633 } 1634 `, 1635 pos: []string{"\tFIDBR\t[$]5"}, 1636 }, 1637 { 1638 // check that stack store is optimized away 1639 fn: ` 1640 func $() int { 1641 var x int 1642 return *(&x) 1643 } 1644 `, 1645 pos: []string{"TEXT\t.*, [$]0-8"}, 1646 }, 1647 // Constant propagation through raw bits conversions. 1648 { 1649 // uint32 constant converted to float32 constant 1650 fn: ` 1651 func $(x float32) float32 { 1652 if x > math.Float32frombits(0x3f800000) { 1653 return -x 1654 } 1655 return x 1656 } 1657 `, 1658 pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"}, 1659 }, 1660 { 1661 // float32 constant converted to uint32 constant 1662 fn: ` 1663 func $(x uint32) uint32 { 1664 if x > math.Float32bits(1) { 1665 return -x 1666 } 1667 return x 1668 } 1669 `, 1670 neg: []string{"\tFMOVS\t"}, 1671 }, 1672 // Constant propagation through float comparisons. 1673 { 1674 fn: ` 1675 func $() bool { 1676 return 0.5 == float64(uint32(1)) || 1677 1.5 > float64(uint64(1<<63)) || 1678 math.NaN() == math.NaN() 1679 } 1680 `, 1681 pos: []string{"\tMOV(B|BZ|D)\t[$]0,"}, 1682 neg: []string{"\tFCMPU\t", "\tMOV(B|BZ|D)\t[$]1,"}, 1683 }, 1684 { 1685 fn: ` 1686 func $() bool { 1687 return float32(0.5) <= float32(int64(1)) && 1688 float32(1.5) >= float32(int32(-1<<31)) && 1689 float32(math.NaN()) != float32(math.NaN()) 1690 } 1691 `, 1692 pos: []string{"\tMOV(B|BZ|D)\t[$]1,"}, 1693 neg: []string{"\tCEBR\t", "\tMOV(B|BZ|D)\t[$]0,"}, 1694 }, 1695 } 1696 1697 var linuxARMTests = []*asmTest{ 1698 { 1699 fn: ` 1700 func f0(x uint32) uint32 { 1701 return x<<7 + x>>25 1702 } 1703 `, 1704 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1705 }, 1706 { 1707 fn: ` 1708 func f1(x uint32) uint32 { 1709 return x<<7 | x>>25 1710 } 1711 `, 1712 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1713 }, 1714 { 1715 fn: ` 1716 func f2(x uint32) uint32 { 1717 return x<<7 ^ x>>25 1718 } 1719 `, 1720 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1721 }, 1722 { 1723 fn: ` 1724 func f3(a uint64) int { 1725 return bits.Len64(a) 1726 } 1727 `, 1728 pos: []string{"\tCLZ\t"}, 1729 }, 1730 { 1731 fn: ` 1732 func f4(a uint32) int { 1733 return bits.Len32(a) 1734 } 1735 `, 1736 pos: []string{"\tCLZ\t"}, 1737 }, 1738 { 1739 fn: ` 1740 func f5(a uint16) int { 1741 return bits.Len16(a) 1742 } 1743 `, 1744 pos: []string{"\tCLZ\t"}, 1745 }, 1746 { 1747 fn: ` 1748 func f6(a uint8) int { 1749 return bits.Len8(a) 1750 } 1751 `, 1752 pos: []string{"\tCLZ\t"}, 1753 }, 1754 { 1755 fn: ` 1756 func f7(a uint) int { 1757 return bits.Len(a) 1758 } 1759 `, 1760 pos: []string{"\tCLZ\t"}, 1761 }, 1762 { 1763 fn: ` 1764 func f8(a uint64) int { 1765 return bits.LeadingZeros64(a) 1766 } 1767 `, 1768 pos: []string{"\tCLZ\t"}, 1769 }, 1770 { 1771 fn: ` 1772 func f9(a uint32) int { 1773 return bits.LeadingZeros32(a) 1774 } 1775 `, 1776 pos: []string{"\tCLZ\t"}, 1777 }, 1778 { 1779 fn: ` 1780 func f10(a uint16) int { 1781 return bits.LeadingZeros16(a) 1782 } 1783 `, 1784 pos: []string{"\tCLZ\t"}, 1785 }, 1786 { 1787 fn: ` 1788 func f11(a uint8) int { 1789 return bits.LeadingZeros8(a) 1790 } 1791 `, 1792 pos: []string{"\tCLZ\t"}, 1793 }, 1794 { 1795 fn: ` 1796 func f12(a uint) int { 1797 return bits.LeadingZeros(a) 1798 } 1799 `, 1800 pos: []string{"\tCLZ\t"}, 1801 }, 1802 { 1803 // make sure assembly output has matching offset and base register. 1804 fn: ` 1805 func f13(a, b int) int { 1806 //go:noinline 1807 func() {_, _ = a, b} () // use some frame 1808 return b 1809 } 1810 `, 1811 pos: []string{"b\\+4\\(FP\\)"}, 1812 }, 1813 { 1814 // check that stack store is optimized away 1815 fn: ` 1816 func $() int { 1817 var x int 1818 return *(&x) 1819 } 1820 `, 1821 pos: []string{"TEXT\t.*, [$]-4-4"}, 1822 }, 1823 } 1824 1825 var linuxARM64Tests = []*asmTest{ 1826 { 1827 fn: ` 1828 func f0(x uint64) uint64 { 1829 return x<<7 + x>>57 1830 } 1831 `, 1832 pos: []string{"\tROR\t[$]57,"}, 1833 }, 1834 { 1835 fn: ` 1836 func f1(x uint64) uint64 { 1837 return x<<7 | x>>57 1838 } 1839 `, 1840 pos: []string{"\tROR\t[$]57,"}, 1841 }, 1842 { 1843 fn: ` 1844 func f2(x uint64) uint64 { 1845 return x<<7 ^ x>>57 1846 } 1847 `, 1848 pos: []string{"\tROR\t[$]57,"}, 1849 }, 1850 { 1851 fn: ` 1852 func f3(x uint32) uint32 { 1853 return x<<7 + x>>25 1854 } 1855 `, 1856 pos: []string{"\tRORW\t[$]25,"}, 1857 }, 1858 { 1859 fn: ` 1860 func f4(x uint32) uint32 { 1861 return x<<7 | x>>25 1862 } 1863 `, 1864 pos: []string{"\tRORW\t[$]25,"}, 1865 }, 1866 { 1867 fn: ` 1868 func f5(x uint32) uint32 { 1869 return x<<7 ^ x>>25 1870 } 1871 `, 1872 pos: []string{"\tRORW\t[$]25,"}, 1873 }, 1874 { 1875 fn: ` 1876 func f22(a uint64) uint64 { 1877 return bits.ReverseBytes64(a) 1878 } 1879 `, 1880 pos: []string{"\tREV\t"}, 1881 }, 1882 { 1883 fn: ` 1884 func f23(a uint32) uint32 { 1885 return bits.ReverseBytes32(a) 1886 } 1887 `, 1888 pos: []string{"\tREVW\t"}, 1889 }, 1890 { 1891 fn: ` 1892 func f24(a uint64) int { 1893 return bits.Len64(a) 1894 } 1895 `, 1896 pos: []string{"\tCLZ\t"}, 1897 }, 1898 { 1899 fn: ` 1900 func f25(a uint32) int { 1901 return bits.Len32(a) 1902 } 1903 `, 1904 pos: []string{"\tCLZ\t"}, 1905 }, 1906 { 1907 fn: ` 1908 func f26(a uint16) int { 1909 return bits.Len16(a) 1910 } 1911 `, 1912 pos: []string{"\tCLZ\t"}, 1913 }, 1914 { 1915 fn: ` 1916 func f27(a uint8) int { 1917 return bits.Len8(a) 1918 } 1919 `, 1920 pos: []string{"\tCLZ\t"}, 1921 }, 1922 { 1923 fn: ` 1924 func f28(a uint) int { 1925 return bits.Len(a) 1926 } 1927 `, 1928 pos: []string{"\tCLZ\t"}, 1929 }, 1930 { 1931 fn: ` 1932 func f29(a uint64) int { 1933 return bits.LeadingZeros64(a) 1934 } 1935 `, 1936 pos: []string{"\tCLZ\t"}, 1937 }, 1938 { 1939 fn: ` 1940 func f30(a uint32) int { 1941 return bits.LeadingZeros32(a) 1942 } 1943 `, 1944 pos: []string{"\tCLZ\t"}, 1945 }, 1946 { 1947 fn: ` 1948 func f31(a uint16) int { 1949 return bits.LeadingZeros16(a) 1950 } 1951 `, 1952 pos: []string{"\tCLZ\t"}, 1953 }, 1954 { 1955 fn: ` 1956 func f32(a uint8) int { 1957 return bits.LeadingZeros8(a) 1958 } 1959 `, 1960 pos: []string{"\tCLZ\t"}, 1961 }, 1962 { 1963 fn: ` 1964 func f33(a uint) int { 1965 return bits.LeadingZeros(a) 1966 } 1967 `, 1968 pos: []string{"\tCLZ\t"}, 1969 }, 1970 { 1971 fn: ` 1972 func f34(a uint64) uint64 { 1973 return a & ((1<<63)-1) 1974 } 1975 `, 1976 pos: []string{"\tAND\t"}, 1977 }, 1978 { 1979 fn: ` 1980 func f35(a uint64) uint64 { 1981 return a & (1<<63) 1982 } 1983 `, 1984 pos: []string{"\tAND\t"}, 1985 }, 1986 { 1987 // make sure offsets are folded into load and store. 1988 fn: ` 1989 func f36(_, a [20]byte) (b [20]byte) { 1990 b = a 1991 return 1992 } 1993 `, 1994 pos: []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"}, 1995 }, 1996 { 1997 // check that stack store is optimized away 1998 fn: ` 1999 func $() int { 2000 var x int 2001 return *(&x) 2002 } 2003 `, 2004 pos: []string{"TEXT\t.*, [$]-8-8"}, 2005 }, 2006 { 2007 // check that we don't emit comparisons for constant shift 2008 fn: ` 2009 //go:nosplit 2010 func $(x int) int { 2011 return x << 17 2012 } 2013 `, 2014 pos: []string{"LSL\t\\$17"}, 2015 neg: []string{"CMP"}, 2016 }, 2017 } 2018 2019 var linuxMIPSTests = []*asmTest{ 2020 { 2021 fn: ` 2022 func f0(a uint64) int { 2023 return bits.Len64(a) 2024 } 2025 `, 2026 pos: []string{"\tCLZ\t"}, 2027 }, 2028 { 2029 fn: ` 2030 func f1(a uint32) int { 2031 return bits.Len32(a) 2032 } 2033 `, 2034 pos: []string{"\tCLZ\t"}, 2035 }, 2036 { 2037 fn: ` 2038 func f2(a uint16) int { 2039 return bits.Len16(a) 2040 } 2041 `, 2042 pos: []string{"\tCLZ\t"}, 2043 }, 2044 { 2045 fn: ` 2046 func f3(a uint8) int { 2047 return bits.Len8(a) 2048 } 2049 `, 2050 pos: []string{"\tCLZ\t"}, 2051 }, 2052 { 2053 fn: ` 2054 func f4(a uint) int { 2055 return bits.Len(a) 2056 } 2057 `, 2058 pos: []string{"\tCLZ\t"}, 2059 }, 2060 { 2061 fn: ` 2062 func f5(a uint64) int { 2063 return bits.LeadingZeros64(a) 2064 } 2065 `, 2066 pos: []string{"\tCLZ\t"}, 2067 }, 2068 { 2069 fn: ` 2070 func f6(a uint32) int { 2071 return bits.LeadingZeros32(a) 2072 } 2073 `, 2074 pos: []string{"\tCLZ\t"}, 2075 }, 2076 { 2077 fn: ` 2078 func f7(a uint16) int { 2079 return bits.LeadingZeros16(a) 2080 } 2081 `, 2082 pos: []string{"\tCLZ\t"}, 2083 }, 2084 { 2085 fn: ` 2086 func f8(a uint8) int { 2087 return bits.LeadingZeros8(a) 2088 } 2089 `, 2090 pos: []string{"\tCLZ\t"}, 2091 }, 2092 { 2093 fn: ` 2094 func f9(a uint) int { 2095 return bits.LeadingZeros(a) 2096 } 2097 `, 2098 pos: []string{"\tCLZ\t"}, 2099 }, 2100 { 2101 // check that stack store is optimized away 2102 fn: ` 2103 func $() int { 2104 var x int 2105 return *(&x) 2106 } 2107 `, 2108 pos: []string{"TEXT\t.*, [$]-4-4"}, 2109 }, 2110 } 2111 2112 var linuxMIPS64Tests = []*asmTest{ 2113 { 2114 // check that we don't emit comparisons for constant shift 2115 fn: ` 2116 func $(x int) int { 2117 return x << 17 2118 } 2119 `, 2120 pos: []string{"SLLV\t\\$17"}, 2121 neg: []string{"SGT"}, 2122 }, 2123 } 2124 2125 var linuxPPC64LETests = []*asmTest{ 2126 // Fused multiply-add/sub instructions. 2127 { 2128 fn: ` 2129 func f0(x, y, z float64) float64 { 2130 return x * y + z 2131 } 2132 `, 2133 pos: []string{"\tFMADD\t"}, 2134 }, 2135 { 2136 fn: ` 2137 func f1(x, y, z float64) float64 { 2138 return x * y - z 2139 } 2140 `, 2141 pos: []string{"\tFMSUB\t"}, 2142 }, 2143 { 2144 fn: ` 2145 func f2(x, y, z float32) float32 { 2146 return x * y + z 2147 } 2148 `, 2149 pos: []string{"\tFMADDS\t"}, 2150 }, 2151 { 2152 fn: ` 2153 func f3(x, y, z float32) float32 { 2154 return x * y - z 2155 } 2156 `, 2157 pos: []string{"\tFMSUBS\t"}, 2158 }, 2159 { 2160 fn: ` 2161 func f4(x uint32) uint32 { 2162 return x<<7 | x>>25 2163 } 2164 `, 2165 pos: []string{"\tROTLW\t"}, 2166 }, 2167 { 2168 fn: ` 2169 func f5(x uint32) uint32 { 2170 return x<<7 + x>>25 2171 } 2172 `, 2173 pos: []string{"\tROTLW\t"}, 2174 }, 2175 { 2176 fn: ` 2177 func f6(x uint32) uint32 { 2178 return x<<7 ^ x>>25 2179 } 2180 `, 2181 pos: []string{"\tROTLW\t"}, 2182 }, 2183 { 2184 fn: ` 2185 func f7(x uint64) uint64 { 2186 return x<<7 | x>>57 2187 } 2188 `, 2189 pos: []string{"\tROTL\t"}, 2190 }, 2191 { 2192 fn: ` 2193 func f8(x uint64) uint64 { 2194 return x<<7 + x>>57 2195 } 2196 `, 2197 pos: []string{"\tROTL\t"}, 2198 }, 2199 { 2200 fn: ` 2201 func f9(x uint64) uint64 { 2202 return x<<7 ^ x>>57 2203 } 2204 `, 2205 pos: []string{"\tROTL\t"}, 2206 }, 2207 { 2208 fn: ` 2209 func f10(a uint32) uint32 { 2210 return bits.RotateLeft32(a, 9) 2211 } 2212 `, 2213 pos: []string{"\tROTLW\t"}, 2214 }, 2215 { 2216 fn: ` 2217 func f11(a uint64) uint64 { 2218 return bits.RotateLeft64(a, 37) 2219 } 2220 `, 2221 pos: []string{"\tROTL\t"}, 2222 }, 2223 2224 { 2225 // check that stack store is optimized away 2226 fn: ` 2227 func $() int { 2228 var x int 2229 return *(&x) 2230 } 2231 `, 2232 pos: []string{"TEXT\t.*, [$]0-8"}, 2233 }, 2234 // Constant propagation through raw bits conversions. 2235 { 2236 // uint32 constant converted to float32 constant 2237 fn: ` 2238 func $(x float32) float32 { 2239 if x > math.Float32frombits(0x3f800000) { 2240 return -x 2241 } 2242 return x 2243 } 2244 `, 2245 pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"}, 2246 }, 2247 { 2248 // float32 constant converted to uint32 constant 2249 fn: ` 2250 func $(x uint32) uint32 { 2251 if x > math.Float32bits(1) { 2252 return -x 2253 } 2254 return x 2255 } 2256 `, 2257 neg: []string{"\tFMOVS\t"}, 2258 }, 2259 } 2260 2261 var plan9AMD64Tests = []*asmTest{ 2262 // We should make sure that the compiler doesn't generate floating point 2263 // instructions for non-float operations on Plan 9, because floating point 2264 // operations are not allowed in the note handler. 2265 // Array zeroing. 2266 { 2267 fn: ` 2268 func $() [16]byte { 2269 var a [16]byte 2270 return a 2271 } 2272 `, 2273 pos: []string{"\tMOVQ\t\\$0, \"\""}, 2274 }, 2275 // Array copy. 2276 { 2277 fn: ` 2278 func $(a [16]byte) (b [16]byte) { 2279 b = a 2280 return 2281 } 2282 `, 2283 pos: []string{"\tMOVQ\t\"\"\\.a\\+[0-9]+\\(SP\\), (AX|CX)", "\tMOVQ\t(AX|CX), \"\"\\.b\\+[0-9]+\\(SP\\)"}, 2284 }, 2285 } 2286 2287 // TestLineNumber checks to make sure the generated assembly has line numbers 2288 // see issue #16214 2289 func TestLineNumber(t *testing.T) { 2290 testenv.MustHaveGoBuild(t) 2291 dir, err := ioutil.TempDir("", "TestLineNumber") 2292 if err != nil { 2293 t.Fatalf("could not create directory: %v", err) 2294 } 2295 defer os.RemoveAll(dir) 2296 2297 src := filepath.Join(dir, "x.go") 2298 err = ioutil.WriteFile(src, []byte(issue16214src), 0644) 2299 if err != nil { 2300 t.Fatalf("could not write file: %v", err) 2301 } 2302 2303 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) 2304 out, err := cmd.CombinedOutput() 2305 if err != nil { 2306 t.Fatalf("fail to run go tool compile: %v", err) 2307 } 2308 2309 if strings.Contains(string(out), "unknown line number") { 2310 t.Errorf("line number missing in assembly:\n%s", out) 2311 } 2312 } 2313 2314 var issue16214src = ` 2315 package main 2316 2317 func Mod32(x uint32) uint32 { 2318 return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos 2319 } 2320 `