github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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", "runtime"}, 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{"encoding/binary", "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|gcWriteBarrier)\\(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 runtime.GC() // use some frame 1024 return b 1025 } 1026 `, 1027 pos: []string{"b\\+24\\(SP\\)"}, 1028 }, 1029 { 1030 // check load combining 1031 fn: ` 1032 func f73(a, b byte) (byte,byte) { 1033 return f73(f73(a,b)) 1034 } 1035 `, 1036 pos: []string{"\tMOVW\t"}, 1037 }, 1038 { 1039 fn: ` 1040 func f74(a, b uint16) (uint16,uint16) { 1041 return f74(f74(a,b)) 1042 } 1043 `, 1044 pos: []string{"\tMOVL\t"}, 1045 }, 1046 { 1047 fn: ` 1048 func f75(a, b uint32) (uint32,uint32) { 1049 return f75(f75(a,b)) 1050 } 1051 `, 1052 pos: []string{"\tMOVQ\t"}, 1053 }, 1054 { 1055 fn: ` 1056 func f76(a, b uint64) (uint64,uint64) { 1057 return f76(f76(a,b)) 1058 } 1059 `, 1060 pos: []string{"\tMOVUPS\t"}, 1061 }, 1062 // Make sure we don't put pointers in SSE registers across safe points. 1063 { 1064 fn: ` 1065 func $(p, q *[2]*int) { 1066 a, b := p[0], p[1] 1067 runtime.GC() 1068 q[0], q[1] = a, b 1069 } 1070 `, 1071 neg: []string{"MOVUPS"}, 1072 }, 1073 { 1074 // check that stack store is optimized away 1075 fn: ` 1076 func $() int { 1077 var x int 1078 return *(&x) 1079 } 1080 `, 1081 pos: []string{"TEXT\t.*, [$]0-8"}, 1082 }, 1083 // math.Abs using integer registers 1084 { 1085 fn: ` 1086 func $(x float64) float64 { 1087 return math.Abs(x) 1088 } 1089 `, 1090 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,"}, 1091 }, 1092 // math.Copysign using integer registers 1093 { 1094 fn: ` 1095 func $(x, y float64) float64 { 1096 return math.Copysign(x, y) 1097 } 1098 `, 1099 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,", "\tSHRQ\t[$]63,", "\tSHLQ\t[$]63,", "\tORQ\t"}, 1100 }, 1101 // int <-> fp moves 1102 { 1103 fn: ` 1104 func $(x float64) uint64 { 1105 return math.Float64bits(x+1) + 1 1106 } 1107 `, 1108 pos: []string{"\tMOVQ\tX.*, [^X].*"}, 1109 }, 1110 { 1111 fn: ` 1112 func $(x float32) uint32 { 1113 return math.Float32bits(x+1) + 1 1114 } 1115 `, 1116 pos: []string{"\tMOVL\tX.*, [^X].*"}, 1117 }, 1118 { 1119 fn: ` 1120 func $(x uint64) float64 { 1121 return math.Float64frombits(x+1) + 1 1122 } 1123 `, 1124 pos: []string{"\tMOVQ\t[^X].*, X.*"}, 1125 }, 1126 { 1127 fn: ` 1128 func $(x uint32) float32 { 1129 return math.Float32frombits(x+1) + 1 1130 } 1131 `, 1132 pos: []string{"\tMOVL\t[^X].*, X.*"}, 1133 }, 1134 { 1135 fn: ` 1136 func $(x uint32) bool { 1137 return x > 4 1138 } 1139 `, 1140 pos: []string{"\tSETHI\t\\("}, 1141 }, 1142 // Check that len() and cap() div by a constant power of two 1143 // are compiled into SHRQ. 1144 { 1145 fn: ` 1146 func $(a []int) int { 1147 return len(a) / 1024 1148 } 1149 `, 1150 pos: []string{"\tSHRQ\t\\$10,"}, 1151 }, 1152 { 1153 fn: ` 1154 func $(s string) int { 1155 return len(s) / (4097 >> 1) 1156 } 1157 `, 1158 pos: []string{"\tSHRQ\t\\$11,"}, 1159 }, 1160 { 1161 fn: ` 1162 func $(a []int) int { 1163 return cap(a) / ((1 << 11) + 2048) 1164 } 1165 `, 1166 pos: []string{"\tSHRQ\t\\$12,"}, 1167 }, 1168 // Check that len() and cap() mod by a constant power of two 1169 // are compiled into ANDQ. 1170 { 1171 fn: ` 1172 func $(a []int) int { 1173 return len(a) % 1024 1174 } 1175 `, 1176 pos: []string{"\tANDQ\t\\$1023,"}, 1177 }, 1178 { 1179 fn: ` 1180 func $(s string) int { 1181 return len(s) % (4097 >> 1) 1182 } 1183 `, 1184 pos: []string{"\tANDQ\t\\$2047,"}, 1185 }, 1186 { 1187 fn: ` 1188 func $(a []int) int { 1189 return cap(a) % ((1 << 11) + 2048) 1190 } 1191 `, 1192 pos: []string{"\tANDQ\t\\$4095,"}, 1193 }, 1194 { 1195 // Test that small memmove was replaced with direct movs 1196 fn: ` 1197 func $() { 1198 x := [...]byte{1, 2, 3, 4, 5, 6, 7} 1199 copy(x[1:], x[:]) 1200 } 1201 `, 1202 neg: []string{"memmove"}, 1203 }, 1204 { 1205 // Same as above but with different size 1206 fn: ` 1207 func $() { 1208 x := [...]byte{1, 2, 3, 4} 1209 copy(x[1:], x[:]) 1210 } 1211 `, 1212 neg: []string{"memmove"}, 1213 }, 1214 { 1215 // Same as above but with different size 1216 fn: ` 1217 func $() { 1218 x := [...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 1219 copy(x[1:], x[:]) 1220 } 1221 `, 1222 neg: []string{"memmove"}, 1223 }, 1224 } 1225 1226 var linux386Tests = []*asmTest{ 1227 { 1228 fn: ` 1229 func f0(b []byte) uint32 { 1230 return binary.LittleEndian.Uint32(b) 1231 } 1232 `, 1233 pos: []string{"\tMOVL\t\\(.*\\),"}, 1234 }, 1235 { 1236 fn: ` 1237 func f1(b []byte, i int) uint32 { 1238 return binary.LittleEndian.Uint32(b[i:]) 1239 } 1240 `, 1241 pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 1242 }, 1243 1244 // multiplication by powers of two 1245 { 1246 fn: ` 1247 func $(n int) int { 1248 return 32*n 1249 } 1250 `, 1251 pos: []string{"SHLL"}, 1252 neg: []string{"IMULL"}, 1253 }, 1254 { 1255 fn: ` 1256 func $(n int) int { 1257 return -64*n 1258 } 1259 `, 1260 pos: []string{"SHLL"}, 1261 neg: []string{"IMULL"}, 1262 }, 1263 1264 // multiplication merging tests 1265 { 1266 fn: ` 1267 func $(n int) int { 1268 return 9*n + 14*n 1269 }`, 1270 pos: []string{"\tIMULL\t[$]23"}, // 23*n 1271 }, 1272 { 1273 fn: ` 1274 func $(a, n int) int { 1275 return 19*a + a*n 1276 }`, 1277 pos: []string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a 1278 }, 1279 { 1280 // check that stack store is optimized away 1281 fn: ` 1282 func $() int { 1283 var x int 1284 return *(&x) 1285 } 1286 `, 1287 pos: []string{"TEXT\t.*, [$]0-4"}, 1288 }, 1289 { 1290 fn: ` 1291 func mul3(n int) int { 1292 return 23*n - 9*n 1293 }`, 1294 pos: []string{"\tIMULL\t[$]14"}, // 14*n 1295 }, 1296 { 1297 fn: ` 1298 func mul4(a, n int) int { 1299 return n*a - a*19 1300 }`, 1301 pos: []string{"\tADDL\t[$]-19", "\tIMULL"}, // (n-19)*a 1302 }, 1303 // Check that len() and cap() div by a constant power of two 1304 // are compiled into SHRL. 1305 { 1306 fn: ` 1307 func $(a []int) int { 1308 return len(a) / 1024 1309 } 1310 `, 1311 pos: []string{"\tSHRL\t\\$10,"}, 1312 }, 1313 { 1314 fn: ` 1315 func $(s string) int { 1316 return len(s) / (4097 >> 1) 1317 } 1318 `, 1319 pos: []string{"\tSHRL\t\\$11,"}, 1320 }, 1321 { 1322 fn: ` 1323 func $(a []int) int { 1324 return cap(a) / ((1 << 11) + 2048) 1325 } 1326 `, 1327 pos: []string{"\tSHRL\t\\$12,"}, 1328 }, 1329 // Check that len() and cap() mod by a constant power of two 1330 // are compiled into ANDL. 1331 { 1332 fn: ` 1333 func $(a []int) int { 1334 return len(a) % 1024 1335 } 1336 `, 1337 pos: []string{"\tANDL\t\\$1023,"}, 1338 }, 1339 { 1340 fn: ` 1341 func $(s string) int { 1342 return len(s) % (4097 >> 1) 1343 } 1344 `, 1345 pos: []string{"\tANDL\t\\$2047,"}, 1346 }, 1347 { 1348 fn: ` 1349 func $(a []int) int { 1350 return cap(a) % ((1 << 11) + 2048) 1351 } 1352 `, 1353 pos: []string{"\tANDL\t\\$4095,"}, 1354 }, 1355 { 1356 // Test that small memmove was replaced with direct movs 1357 fn: ` 1358 func $() { 1359 x := [...]byte{1, 2, 3, 4, 5, 6, 7} 1360 copy(x[1:], x[:]) 1361 } 1362 `, 1363 neg: []string{"memmove"}, 1364 }, 1365 { 1366 // Same as above but with different size 1367 fn: ` 1368 func $() { 1369 x := [...]byte{1, 2, 3, 4} 1370 copy(x[1:], x[:]) 1371 } 1372 `, 1373 neg: []string{"memmove"}, 1374 }, 1375 } 1376 1377 var linuxS390XTests = []*asmTest{ 1378 { 1379 fn: ` 1380 func f0(b []byte) uint32 { 1381 return binary.LittleEndian.Uint32(b) 1382 } 1383 `, 1384 pos: []string{"\tMOVWBR\t\\(.*\\),"}, 1385 }, 1386 { 1387 fn: ` 1388 func f1(b []byte, i int) uint32 { 1389 return binary.LittleEndian.Uint32(b[i:]) 1390 } 1391 `, 1392 pos: []string{"\tMOVWBR\t\\(.*\\)\\(.*\\*1\\),"}, 1393 }, 1394 { 1395 fn: ` 1396 func f2(b []byte) uint64 { 1397 return binary.LittleEndian.Uint64(b) 1398 } 1399 `, 1400 pos: []string{"\tMOVDBR\t\\(.*\\),"}, 1401 }, 1402 { 1403 fn: ` 1404 func f3(b []byte, i int) uint64 { 1405 return binary.LittleEndian.Uint64(b[i:]) 1406 } 1407 `, 1408 pos: []string{"\tMOVDBR\t\\(.*\\)\\(.*\\*1\\),"}, 1409 }, 1410 { 1411 fn: ` 1412 func f4(b []byte) uint32 { 1413 return binary.BigEndian.Uint32(b) 1414 } 1415 `, 1416 pos: []string{"\tMOVWZ\t\\(.*\\),"}, 1417 }, 1418 { 1419 fn: ` 1420 func f5(b []byte, i int) uint32 { 1421 return binary.BigEndian.Uint32(b[i:]) 1422 } 1423 `, 1424 pos: []string{"\tMOVWZ\t\\(.*\\)\\(.*\\*1\\),"}, 1425 }, 1426 { 1427 fn: ` 1428 func f6(b []byte) uint64 { 1429 return binary.BigEndian.Uint64(b) 1430 } 1431 `, 1432 pos: []string{"\tMOVD\t\\(.*\\),"}, 1433 }, 1434 { 1435 fn: ` 1436 func f7(b []byte, i int) uint64 { 1437 return binary.BigEndian.Uint64(b[i:]) 1438 } 1439 `, 1440 pos: []string{"\tMOVD\t\\(.*\\)\\(.*\\*1\\),"}, 1441 }, 1442 { 1443 fn: ` 1444 func f8(x uint64) uint64 { 1445 return x<<7 + x>>57 1446 } 1447 `, 1448 pos: []string{"\tRLLG\t[$]7,"}, 1449 }, 1450 { 1451 fn: ` 1452 func f9(x uint64) uint64 { 1453 return x<<7 | x>>57 1454 } 1455 `, 1456 pos: []string{"\tRLLG\t[$]7,"}, 1457 }, 1458 { 1459 fn: ` 1460 func f10(x uint64) uint64 { 1461 return x<<7 ^ x>>57 1462 } 1463 `, 1464 pos: []string{"\tRLLG\t[$]7,"}, 1465 }, 1466 { 1467 fn: ` 1468 func f11(x uint32) uint32 { 1469 return x<<7 + x>>25 1470 } 1471 `, 1472 pos: []string{"\tRLL\t[$]7,"}, 1473 }, 1474 { 1475 fn: ` 1476 func f12(x uint32) uint32 { 1477 return x<<7 | x>>25 1478 } 1479 `, 1480 pos: []string{"\tRLL\t[$]7,"}, 1481 }, 1482 { 1483 fn: ` 1484 func f13(x uint32) uint32 { 1485 return x<<7 ^ x>>25 1486 } 1487 `, 1488 pos: []string{"\tRLL\t[$]7,"}, 1489 }, 1490 // Fused multiply-add/sub instructions. 1491 { 1492 fn: ` 1493 func f14(x, y, z float64) float64 { 1494 return x * y + z 1495 } 1496 `, 1497 pos: []string{"\tFMADD\t"}, 1498 }, 1499 { 1500 fn: ` 1501 func f15(x, y, z float64) float64 { 1502 return x * y - z 1503 } 1504 `, 1505 pos: []string{"\tFMSUB\t"}, 1506 }, 1507 { 1508 fn: ` 1509 func f16(x, y, z float32) float32 { 1510 return x * y + z 1511 } 1512 `, 1513 pos: []string{"\tFMADDS\t"}, 1514 }, 1515 { 1516 fn: ` 1517 func f17(x, y, z float32) float32 { 1518 return x * y - z 1519 } 1520 `, 1521 pos: []string{"\tFMSUBS\t"}, 1522 }, 1523 // Intrinsic tests for math/bits 1524 { 1525 fn: ` 1526 func f18(a uint64) int { 1527 return bits.TrailingZeros64(a) 1528 } 1529 `, 1530 pos: []string{"\tFLOGR\t"}, 1531 }, 1532 { 1533 fn: ` 1534 func f19(a uint32) int { 1535 return bits.TrailingZeros32(a) 1536 } 1537 `, 1538 pos: []string{"\tFLOGR\t", "\tMOVWZ\t"}, 1539 }, 1540 { 1541 fn: ` 1542 func f20(a uint16) int { 1543 return bits.TrailingZeros16(a) 1544 } 1545 `, 1546 pos: []string{"\tFLOGR\t", "\tOR\t\\$65536,"}, 1547 }, 1548 { 1549 fn: ` 1550 func f21(a uint8) int { 1551 return bits.TrailingZeros8(a) 1552 } 1553 `, 1554 pos: []string{"\tFLOGR\t", "\tOR\t\\$256,"}, 1555 }, 1556 // Intrinsic tests for math/bits 1557 { 1558 fn: ` 1559 func f22(a uint64) uint64 { 1560 return bits.ReverseBytes64(a) 1561 } 1562 `, 1563 pos: []string{"\tMOVDBR\t"}, 1564 }, 1565 { 1566 fn: ` 1567 func f23(a uint32) uint32 { 1568 return bits.ReverseBytes32(a) 1569 } 1570 `, 1571 pos: []string{"\tMOVWBR\t"}, 1572 }, 1573 { 1574 fn: ` 1575 func f24(a uint64) int { 1576 return bits.Len64(a) 1577 } 1578 `, 1579 pos: []string{"\tFLOGR\t"}, 1580 }, 1581 { 1582 fn: ` 1583 func f25(a uint32) int { 1584 return bits.Len32(a) 1585 } 1586 `, 1587 pos: []string{"\tFLOGR\t"}, 1588 }, 1589 { 1590 fn: ` 1591 func f26(a uint16) int { 1592 return bits.Len16(a) 1593 } 1594 `, 1595 pos: []string{"\tFLOGR\t"}, 1596 }, 1597 { 1598 fn: ` 1599 func f27(a uint8) int { 1600 return bits.Len8(a) 1601 } 1602 `, 1603 pos: []string{"\tFLOGR\t"}, 1604 }, 1605 { 1606 fn: ` 1607 func f28(a uint) int { 1608 return bits.Len(a) 1609 } 1610 `, 1611 pos: []string{"\tFLOGR\t"}, 1612 }, 1613 { 1614 fn: ` 1615 func f29(a uint64) int { 1616 return bits.LeadingZeros64(a) 1617 } 1618 `, 1619 pos: []string{"\tFLOGR\t"}, 1620 }, 1621 { 1622 fn: ` 1623 func f30(a uint32) int { 1624 return bits.LeadingZeros32(a) 1625 } 1626 `, 1627 pos: []string{"\tFLOGR\t"}, 1628 }, 1629 { 1630 fn: ` 1631 func f31(a uint16) int { 1632 return bits.LeadingZeros16(a) 1633 } 1634 `, 1635 pos: []string{"\tFLOGR\t"}, 1636 }, 1637 { 1638 fn: ` 1639 func f32(a uint8) int { 1640 return bits.LeadingZeros8(a) 1641 } 1642 `, 1643 pos: []string{"\tFLOGR\t"}, 1644 }, 1645 { 1646 fn: ` 1647 func f33(a uint) int { 1648 return bits.LeadingZeros(a) 1649 } 1650 `, 1651 pos: []string{"\tFLOGR\t"}, 1652 }, 1653 // Intrinsic tests for math. 1654 { 1655 fn: ` 1656 func ceil(x float64) float64 { 1657 return math.Ceil(x) 1658 } 1659 `, 1660 pos: []string{"\tFIDBR\t[$]6"}, 1661 }, 1662 { 1663 fn: ` 1664 func floor(x float64) float64 { 1665 return math.Floor(x) 1666 } 1667 `, 1668 pos: []string{"\tFIDBR\t[$]7"}, 1669 }, 1670 { 1671 fn: ` 1672 func round(x float64) float64 { 1673 return math.Round(x) 1674 } 1675 `, 1676 pos: []string{"\tFIDBR\t[$]1"}, 1677 }, 1678 { 1679 fn: ` 1680 func trunc(x float64) float64 { 1681 return math.Trunc(x) 1682 } 1683 `, 1684 pos: []string{"\tFIDBR\t[$]5"}, 1685 }, 1686 { 1687 fn: ` 1688 func roundToEven(x float64) float64 { 1689 return math.RoundToEven(x) 1690 } 1691 `, 1692 pos: []string{"\tFIDBR\t[$]4"}, 1693 }, 1694 { 1695 // check that stack store is optimized away 1696 fn: ` 1697 func $() int { 1698 var x int 1699 return *(&x) 1700 } 1701 `, 1702 pos: []string{"TEXT\t.*, [$]0-8"}, 1703 }, 1704 // Constant propagation through raw bits conversions. 1705 { 1706 // uint32 constant converted to float32 constant 1707 fn: ` 1708 func $(x float32) float32 { 1709 if x > math.Float32frombits(0x3f800000) { 1710 return -x 1711 } 1712 return x 1713 } 1714 `, 1715 pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"}, 1716 }, 1717 { 1718 // float32 constant converted to uint32 constant 1719 fn: ` 1720 func $(x uint32) uint32 { 1721 if x > math.Float32bits(1) { 1722 return -x 1723 } 1724 return x 1725 } 1726 `, 1727 neg: []string{"\tFMOVS\t"}, 1728 }, 1729 // Constant propagation through float comparisons. 1730 { 1731 fn: ` 1732 func $() bool { 1733 return 0.5 == float64(uint32(1)) || 1734 1.5 > float64(uint64(1<<63)) || 1735 math.NaN() == math.NaN() 1736 } 1737 `, 1738 pos: []string{"\tMOV(B|BZ|D)\t[$]0,"}, 1739 neg: []string{"\tFCMPU\t", "\tMOV(B|BZ|D)\t[$]1,"}, 1740 }, 1741 { 1742 fn: ` 1743 func $() bool { 1744 return float32(0.5) <= float32(int64(1)) && 1745 float32(1.5) >= float32(int32(-1<<31)) && 1746 float32(math.NaN()) != float32(math.NaN()) 1747 } 1748 `, 1749 pos: []string{"\tMOV(B|BZ|D)\t[$]1,"}, 1750 neg: []string{"\tCEBR\t", "\tMOV(B|BZ|D)\t[$]0,"}, 1751 }, 1752 // math tests 1753 { 1754 fn: ` 1755 func $(x float64) float64 { 1756 return math.Abs(x) 1757 } 1758 `, 1759 pos: []string{"\tLPDFR\t"}, 1760 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1761 }, 1762 { 1763 fn: ` 1764 func $(x float32) float32 { 1765 return float32(math.Abs(float64(x))) 1766 } 1767 `, 1768 pos: []string{"\tLPDFR\t"}, 1769 neg: []string{"\tLDEBR\t", "\tLEDBR\t"}, // no float64 conversion 1770 }, 1771 { 1772 fn: ` 1773 func $(x float64) float64 { 1774 return math.Float64frombits(math.Float64bits(x)|1<<63) 1775 } 1776 `, 1777 pos: []string{"\tLNDFR\t"}, 1778 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1779 }, 1780 { 1781 fn: ` 1782 func $(x float64) float64 { 1783 return -math.Abs(x) 1784 } 1785 `, 1786 pos: []string{"\tLNDFR\t"}, 1787 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1788 }, 1789 { 1790 fn: ` 1791 func $(x, y float64) float64 { 1792 return math.Copysign(x, y) 1793 } 1794 `, 1795 pos: []string{"\tCPSDR\t"}, 1796 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1797 }, 1798 { 1799 fn: ` 1800 func $(x float64) float64 { 1801 return math.Copysign(x, -1) 1802 } 1803 `, 1804 pos: []string{"\tLNDFR\t"}, 1805 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1806 }, 1807 { 1808 fn: ` 1809 func $(x float64) float64 { 1810 return math.Copysign(-1, x) 1811 } 1812 `, 1813 pos: []string{"\tCPSDR\t"}, 1814 neg: []string{"\tMOVD\t"}, // no integer loads/stores 1815 }, 1816 } 1817 1818 var linuxARMTests = []*asmTest{ 1819 // multiplication by powers of two 1820 { 1821 fn: ` 1822 func $(n int) int { 1823 return 16*n 1824 } 1825 `, 1826 pos: []string{"\tSLL\t[$]4"}, 1827 neg: []string{"\tMUL\t"}, 1828 }, 1829 { 1830 fn: ` 1831 func $(n int) int { 1832 return -32*n 1833 } 1834 `, 1835 pos: []string{"\tSLL\t[$]5"}, 1836 neg: []string{"\tMUL\t"}, 1837 }, 1838 1839 { 1840 fn: ` 1841 func f0(x uint32) uint32 { 1842 return x<<7 + x>>25 1843 } 1844 `, 1845 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1846 }, 1847 { 1848 fn: ` 1849 func f1(x uint32) uint32 { 1850 return x<<7 | x>>25 1851 } 1852 `, 1853 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1854 }, 1855 { 1856 fn: ` 1857 func f2(x uint32) uint32 { 1858 return x<<7 ^ x>>25 1859 } 1860 `, 1861 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1862 }, 1863 { 1864 fn: ` 1865 func f3(a uint64) int { 1866 return bits.Len64(a) 1867 } 1868 `, 1869 pos: []string{"\tCLZ\t"}, 1870 }, 1871 { 1872 fn: ` 1873 func f4(a uint32) int { 1874 return bits.Len32(a) 1875 } 1876 `, 1877 pos: []string{"\tCLZ\t"}, 1878 }, 1879 { 1880 fn: ` 1881 func f5(a uint16) int { 1882 return bits.Len16(a) 1883 } 1884 `, 1885 pos: []string{"\tCLZ\t"}, 1886 }, 1887 { 1888 fn: ` 1889 func f6(a uint8) int { 1890 return bits.Len8(a) 1891 } 1892 `, 1893 pos: []string{"\tCLZ\t"}, 1894 }, 1895 { 1896 fn: ` 1897 func f7(a uint) int { 1898 return bits.Len(a) 1899 } 1900 `, 1901 pos: []string{"\tCLZ\t"}, 1902 }, 1903 { 1904 fn: ` 1905 func f8(a uint64) int { 1906 return bits.LeadingZeros64(a) 1907 } 1908 `, 1909 pos: []string{"\tCLZ\t"}, 1910 }, 1911 { 1912 fn: ` 1913 func f9(a uint32) int { 1914 return bits.LeadingZeros32(a) 1915 } 1916 `, 1917 pos: []string{"\tCLZ\t"}, 1918 }, 1919 { 1920 fn: ` 1921 func f10(a uint16) int { 1922 return bits.LeadingZeros16(a) 1923 } 1924 `, 1925 pos: []string{"\tCLZ\t"}, 1926 }, 1927 { 1928 fn: ` 1929 func f11(a uint8) int { 1930 return bits.LeadingZeros8(a) 1931 } 1932 `, 1933 pos: []string{"\tCLZ\t"}, 1934 }, 1935 { 1936 fn: ` 1937 func f12(a uint) int { 1938 return bits.LeadingZeros(a) 1939 } 1940 `, 1941 pos: []string{"\tCLZ\t"}, 1942 }, 1943 { 1944 // make sure assembly output has matching offset and base register. 1945 fn: ` 1946 func f13(a, b int) int { 1947 runtime.GC() // use some frame 1948 return b 1949 } 1950 `, 1951 pos: []string{"b\\+4\\(FP\\)"}, 1952 }, 1953 { 1954 // check that stack store is optimized away 1955 fn: ` 1956 func $() int { 1957 var x int 1958 return *(&x) 1959 } 1960 `, 1961 pos: []string{"TEXT\t.*, [$]-4-4"}, 1962 }, 1963 } 1964 1965 var linuxARM64Tests = []*asmTest{ 1966 // multiplication by powers of two 1967 { 1968 fn: ` 1969 func $(n int) int { 1970 return 64*n 1971 } 1972 `, 1973 pos: []string{"\tLSL\t[$]6"}, 1974 neg: []string{"\tMUL\t"}, 1975 }, 1976 { 1977 fn: ` 1978 func $(n int) int { 1979 return -128*n 1980 } 1981 `, 1982 pos: []string{"\tLSL\t[$]7"}, 1983 neg: []string{"\tMUL\t"}, 1984 }, 1985 1986 { 1987 fn: ` 1988 func f0(x uint64) uint64 { 1989 return x<<7 + x>>57 1990 } 1991 `, 1992 pos: []string{"\tROR\t[$]57,"}, 1993 }, 1994 { 1995 fn: ` 1996 func f1(x uint64) uint64 { 1997 return x<<7 | x>>57 1998 } 1999 `, 2000 pos: []string{"\tROR\t[$]57,"}, 2001 }, 2002 { 2003 fn: ` 2004 func f2(x uint64) uint64 { 2005 return x<<7 ^ x>>57 2006 } 2007 `, 2008 pos: []string{"\tROR\t[$]57,"}, 2009 }, 2010 { 2011 fn: ` 2012 func f3(x uint32) uint32 { 2013 return x<<7 + x>>25 2014 } 2015 `, 2016 pos: []string{"\tRORW\t[$]25,"}, 2017 }, 2018 { 2019 fn: ` 2020 func f4(x uint32) uint32 { 2021 return x<<7 | x>>25 2022 } 2023 `, 2024 pos: []string{"\tRORW\t[$]25,"}, 2025 }, 2026 { 2027 fn: ` 2028 func f5(x uint32) uint32 { 2029 return x<<7 ^ x>>25 2030 } 2031 `, 2032 pos: []string{"\tRORW\t[$]25,"}, 2033 }, 2034 { 2035 fn: ` 2036 func f22(a uint64) uint64 { 2037 return bits.ReverseBytes64(a) 2038 } 2039 `, 2040 pos: []string{"\tREV\t"}, 2041 }, 2042 { 2043 fn: ` 2044 func f23(a uint32) uint32 { 2045 return bits.ReverseBytes32(a) 2046 } 2047 `, 2048 pos: []string{"\tREVW\t"}, 2049 }, 2050 { 2051 fn: ` 2052 func f24(a uint64) int { 2053 return bits.Len64(a) 2054 } 2055 `, 2056 pos: []string{"\tCLZ\t"}, 2057 }, 2058 { 2059 fn: ` 2060 func f25(a uint32) int { 2061 return bits.Len32(a) 2062 } 2063 `, 2064 pos: []string{"\tCLZ\t"}, 2065 }, 2066 { 2067 fn: ` 2068 func f26(a uint16) int { 2069 return bits.Len16(a) 2070 } 2071 `, 2072 pos: []string{"\tCLZ\t"}, 2073 }, 2074 { 2075 fn: ` 2076 func f27(a uint8) int { 2077 return bits.Len8(a) 2078 } 2079 `, 2080 pos: []string{"\tCLZ\t"}, 2081 }, 2082 { 2083 fn: ` 2084 func f28(a uint) int { 2085 return bits.Len(a) 2086 } 2087 `, 2088 pos: []string{"\tCLZ\t"}, 2089 }, 2090 { 2091 fn: ` 2092 func f29(a uint64) int { 2093 return bits.LeadingZeros64(a) 2094 } 2095 `, 2096 pos: []string{"\tCLZ\t"}, 2097 }, 2098 { 2099 fn: ` 2100 func f30(a uint32) int { 2101 return bits.LeadingZeros32(a) 2102 } 2103 `, 2104 pos: []string{"\tCLZ\t"}, 2105 }, 2106 { 2107 fn: ` 2108 func f31(a uint16) int { 2109 return bits.LeadingZeros16(a) 2110 } 2111 `, 2112 pos: []string{"\tCLZ\t"}, 2113 }, 2114 { 2115 fn: ` 2116 func f32(a uint8) int { 2117 return bits.LeadingZeros8(a) 2118 } 2119 `, 2120 pos: []string{"\tCLZ\t"}, 2121 }, 2122 { 2123 fn: ` 2124 func f33(a uint) int { 2125 return bits.LeadingZeros(a) 2126 } 2127 `, 2128 pos: []string{"\tCLZ\t"}, 2129 }, 2130 { 2131 fn: ` 2132 func f34(a uint64) uint64 { 2133 return a & ((1<<63)-1) 2134 } 2135 `, 2136 pos: []string{"\tAND\t"}, 2137 }, 2138 { 2139 fn: ` 2140 func f35(a uint64) uint64 { 2141 return a & (1<<63) 2142 } 2143 `, 2144 pos: []string{"\tAND\t"}, 2145 }, 2146 { 2147 // make sure offsets are folded into load and store. 2148 fn: ` 2149 func f36(_, a [20]byte) (b [20]byte) { 2150 b = a 2151 return 2152 } 2153 `, 2154 pos: []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"}, 2155 }, 2156 { 2157 // check that stack store is optimized away 2158 fn: ` 2159 func $() int { 2160 var x int 2161 return *(&x) 2162 } 2163 `, 2164 pos: []string{"TEXT\t.*, [$]-8-8"}, 2165 }, 2166 { 2167 // check that we don't emit comparisons for constant shift 2168 fn: ` 2169 //go:nosplit 2170 func $(x int) int { 2171 return x << 17 2172 } 2173 `, 2174 pos: []string{"LSL\t\\$17"}, 2175 neg: []string{"CMP"}, 2176 }, 2177 } 2178 2179 var linuxMIPSTests = []*asmTest{ 2180 { 2181 fn: ` 2182 func f0(a uint64) int { 2183 return bits.Len64(a) 2184 } 2185 `, 2186 pos: []string{"\tCLZ\t"}, 2187 }, 2188 { 2189 fn: ` 2190 func f1(a uint32) int { 2191 return bits.Len32(a) 2192 } 2193 `, 2194 pos: []string{"\tCLZ\t"}, 2195 }, 2196 { 2197 fn: ` 2198 func f2(a uint16) int { 2199 return bits.Len16(a) 2200 } 2201 `, 2202 pos: []string{"\tCLZ\t"}, 2203 }, 2204 { 2205 fn: ` 2206 func f3(a uint8) int { 2207 return bits.Len8(a) 2208 } 2209 `, 2210 pos: []string{"\tCLZ\t"}, 2211 }, 2212 { 2213 fn: ` 2214 func f4(a uint) int { 2215 return bits.Len(a) 2216 } 2217 `, 2218 pos: []string{"\tCLZ\t"}, 2219 }, 2220 { 2221 fn: ` 2222 func f5(a uint64) int { 2223 return bits.LeadingZeros64(a) 2224 } 2225 `, 2226 pos: []string{"\tCLZ\t"}, 2227 }, 2228 { 2229 fn: ` 2230 func f6(a uint32) int { 2231 return bits.LeadingZeros32(a) 2232 } 2233 `, 2234 pos: []string{"\tCLZ\t"}, 2235 }, 2236 { 2237 fn: ` 2238 func f7(a uint16) int { 2239 return bits.LeadingZeros16(a) 2240 } 2241 `, 2242 pos: []string{"\tCLZ\t"}, 2243 }, 2244 { 2245 fn: ` 2246 func f8(a uint8) int { 2247 return bits.LeadingZeros8(a) 2248 } 2249 `, 2250 pos: []string{"\tCLZ\t"}, 2251 }, 2252 { 2253 fn: ` 2254 func f9(a uint) int { 2255 return bits.LeadingZeros(a) 2256 } 2257 `, 2258 pos: []string{"\tCLZ\t"}, 2259 }, 2260 { 2261 // check that stack store is optimized away 2262 fn: ` 2263 func $() int { 2264 var x int 2265 return *(&x) 2266 } 2267 `, 2268 pos: []string{"TEXT\t.*, [$]-4-4"}, 2269 }, 2270 } 2271 2272 var linuxMIPS64Tests = []*asmTest{ 2273 { 2274 // check that we don't emit comparisons for constant shift 2275 fn: ` 2276 func $(x int) int { 2277 return x << 17 2278 } 2279 `, 2280 pos: []string{"SLLV\t\\$17"}, 2281 neg: []string{"SGT"}, 2282 }, 2283 } 2284 2285 var linuxPPC64LETests = []*asmTest{ 2286 // Fused multiply-add/sub instructions. 2287 { 2288 fn: ` 2289 func f0(x, y, z float64) float64 { 2290 return x * y + z 2291 } 2292 `, 2293 pos: []string{"\tFMADD\t"}, 2294 }, 2295 { 2296 fn: ` 2297 func f1(x, y, z float64) float64 { 2298 return x * y - z 2299 } 2300 `, 2301 pos: []string{"\tFMSUB\t"}, 2302 }, 2303 { 2304 fn: ` 2305 func f2(x, y, z float32) float32 { 2306 return x * y + z 2307 } 2308 `, 2309 pos: []string{"\tFMADDS\t"}, 2310 }, 2311 { 2312 fn: ` 2313 func f3(x, y, z float32) float32 { 2314 return x * y - z 2315 } 2316 `, 2317 pos: []string{"\tFMSUBS\t"}, 2318 }, 2319 { 2320 fn: ` 2321 func f4(x uint32) uint32 { 2322 return x<<7 | x>>25 2323 } 2324 `, 2325 pos: []string{"\tROTLW\t"}, 2326 }, 2327 { 2328 fn: ` 2329 func f5(x uint32) uint32 { 2330 return x<<7 + x>>25 2331 } 2332 `, 2333 pos: []string{"\tROTLW\t"}, 2334 }, 2335 { 2336 fn: ` 2337 func f6(x uint32) uint32 { 2338 return x<<7 ^ x>>25 2339 } 2340 `, 2341 pos: []string{"\tROTLW\t"}, 2342 }, 2343 { 2344 fn: ` 2345 func f7(x uint64) uint64 { 2346 return x<<7 | x>>57 2347 } 2348 `, 2349 pos: []string{"\tROTL\t"}, 2350 }, 2351 { 2352 fn: ` 2353 func f8(x uint64) uint64 { 2354 return x<<7 + x>>57 2355 } 2356 `, 2357 pos: []string{"\tROTL\t"}, 2358 }, 2359 { 2360 fn: ` 2361 func f9(x uint64) uint64 { 2362 return x<<7 ^ x>>57 2363 } 2364 `, 2365 pos: []string{"\tROTL\t"}, 2366 }, 2367 { 2368 fn: ` 2369 func f10(a uint32) uint32 { 2370 return bits.RotateLeft32(a, 9) 2371 } 2372 `, 2373 pos: []string{"\tROTLW\t"}, 2374 }, 2375 { 2376 fn: ` 2377 func f11(a uint64) uint64 { 2378 return bits.RotateLeft64(a, 37) 2379 } 2380 `, 2381 pos: []string{"\tROTL\t"}, 2382 }, 2383 2384 { 2385 fn: ` 2386 func f12(a, b float64) float64 { 2387 return math.Copysign(a, b) 2388 } 2389 `, 2390 pos: []string{"\tFCPSGN\t"}, 2391 }, 2392 2393 { 2394 fn: ` 2395 func f13(a float64) float64 { 2396 return math.Abs(a) 2397 } 2398 `, 2399 pos: []string{"\tFABS\t"}, 2400 }, 2401 2402 { 2403 fn: ` 2404 func f14(b []byte) uint16 { 2405 return binary.LittleEndian.Uint16(b) 2406 } 2407 `, 2408 pos: []string{"\tMOVHZ\t"}, 2409 }, 2410 { 2411 fn: ` 2412 func f15(b []byte) uint32 { 2413 return binary.LittleEndian.Uint32(b) 2414 } 2415 `, 2416 pos: []string{"\tMOVWZ\t"}, 2417 }, 2418 2419 { 2420 fn: ` 2421 func f16(b []byte) uint64 { 2422 return binary.LittleEndian.Uint64(b) 2423 } 2424 `, 2425 pos: []string{"\tMOVD\t"}, 2426 neg: []string{"MOVBZ", "MOVHZ", "MOVWZ"}, 2427 }, 2428 2429 { 2430 fn: ` 2431 func f17(b []byte, v uint16) { 2432 binary.LittleEndian.PutUint16(b, v) 2433 } 2434 `, 2435 pos: []string{"\tMOVH\t"}, 2436 }, 2437 2438 { 2439 fn: ` 2440 func f18(b []byte, v uint32) { 2441 binary.LittleEndian.PutUint32(b, v) 2442 } 2443 `, 2444 pos: []string{"\tMOVW\t"}, 2445 }, 2446 2447 { 2448 fn: ` 2449 func f19(b []byte, v uint64) { 2450 binary.LittleEndian.PutUint64(b, v) 2451 } 2452 `, 2453 pos: []string{"\tMOVD\t"}, 2454 neg: []string{"MOVB", "MOVH", "MOVW"}, 2455 }, 2456 2457 { 2458 // check that stack store is optimized away 2459 fn: ` 2460 func $() int { 2461 var x int 2462 return *(&x) 2463 } 2464 `, 2465 pos: []string{"TEXT\t.*, [$]0-8"}, 2466 }, 2467 // Constant propagation through raw bits conversions. 2468 { 2469 // uint32 constant converted to float32 constant 2470 fn: ` 2471 func $(x float32) float32 { 2472 if x > math.Float32frombits(0x3f800000) { 2473 return -x 2474 } 2475 return x 2476 } 2477 `, 2478 pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"}, 2479 }, 2480 { 2481 // float32 constant converted to uint32 constant 2482 fn: ` 2483 func $(x uint32) uint32 { 2484 if x > math.Float32bits(1) { 2485 return -x 2486 } 2487 return x 2488 } 2489 `, 2490 neg: []string{"\tFMOVS\t"}, 2491 }, 2492 } 2493 2494 var plan9AMD64Tests = []*asmTest{ 2495 // We should make sure that the compiler doesn't generate floating point 2496 // instructions for non-float operations on Plan 9, because floating point 2497 // operations are not allowed in the note handler. 2498 // Array zeroing. 2499 { 2500 fn: ` 2501 func $() [16]byte { 2502 var a [16]byte 2503 return a 2504 } 2505 `, 2506 pos: []string{"\tMOVQ\t\\$0, \"\""}, 2507 }, 2508 // Array copy. 2509 { 2510 fn: ` 2511 func $(a [16]byte) (b [16]byte) { 2512 b = a 2513 return 2514 } 2515 `, 2516 pos: []string{"\tMOVQ\t\"\"\\.a\\+[0-9]+\\(SP\\), (AX|CX)", "\tMOVQ\t(AX|CX), \"\"\\.b\\+[0-9]+\\(SP\\)"}, 2517 }, 2518 } 2519 2520 // TestLineNumber checks to make sure the generated assembly has line numbers 2521 // see issue #16214 2522 func TestLineNumber(t *testing.T) { 2523 testenv.MustHaveGoBuild(t) 2524 dir, err := ioutil.TempDir("", "TestLineNumber") 2525 if err != nil { 2526 t.Fatalf("could not create directory: %v", err) 2527 } 2528 defer os.RemoveAll(dir) 2529 2530 src := filepath.Join(dir, "x.go") 2531 err = ioutil.WriteFile(src, []byte(issue16214src), 0644) 2532 if err != nil { 2533 t.Fatalf("could not write file: %v", err) 2534 } 2535 2536 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) 2537 out, err := cmd.CombinedOutput() 2538 if err != nil { 2539 t.Fatalf("fail to run go tool compile: %v", err) 2540 } 2541 2542 if strings.Contains(string(out), "unknown line number") { 2543 t.Errorf("line number missing in assembly:\n%s", out) 2544 } 2545 } 2546 2547 var issue16214src = ` 2548 package main 2549 2550 func Mod32(x uint32) uint32 { 2551 return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos 2552 } 2553 `