github.com/Filosottile/go@v0.0.0-20170906193555-dbed9972d994/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/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 tests: linuxPPC64LETests, 269 }, 270 { 271 arch: "amd64", 272 os: "plan9", 273 tests: plan9AMD64Tests, 274 }, 275 } 276 277 var linuxAMD64Tests = []*asmTest{ 278 { 279 fn: ` 280 func f0(x int) int { 281 return x * 64 282 } 283 `, 284 pos: []string{"\tSHLQ\t\\$6,"}, 285 }, 286 { 287 fn: ` 288 func f1(x int) int { 289 return x * 96 290 } 291 `, 292 pos: []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"}, 293 }, 294 // Load-combining tests. 295 { 296 fn: ` 297 func f2(b []byte) uint64 { 298 return binary.LittleEndian.Uint64(b) 299 } 300 `, 301 pos: []string{"\tMOVQ\t\\(.*\\),"}, 302 }, 303 { 304 fn: ` 305 func f3(b []byte, i int) uint64 { 306 return binary.LittleEndian.Uint64(b[i:]) 307 } 308 `, 309 pos: []string{"\tMOVQ\t\\(.*\\)\\(.*\\*1\\),"}, 310 }, 311 { 312 fn: ` 313 func f4(b []byte) uint32 { 314 return binary.LittleEndian.Uint32(b) 315 } 316 `, 317 pos: []string{"\tMOVL\t\\(.*\\),"}, 318 }, 319 { 320 fn: ` 321 func f5(b []byte, i int) uint32 { 322 return binary.LittleEndian.Uint32(b[i:]) 323 } 324 `, 325 pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 326 }, 327 { 328 fn: ` 329 func f6(b []byte) uint64 { 330 return binary.BigEndian.Uint64(b) 331 } 332 `, 333 pos: []string{"\tBSWAPQ\t"}, 334 }, 335 { 336 fn: ` 337 func f7(b []byte, i int) uint64 { 338 return binary.BigEndian.Uint64(b[i:]) 339 } 340 `, 341 pos: []string{"\tBSWAPQ\t"}, 342 }, 343 { 344 fn: ` 345 func f8(b []byte, v uint64) { 346 binary.BigEndian.PutUint64(b, v) 347 } 348 `, 349 pos: []string{"\tBSWAPQ\t"}, 350 }, 351 { 352 fn: ` 353 func f9(b []byte, i int, v uint64) { 354 binary.BigEndian.PutUint64(b[i:], v) 355 } 356 `, 357 pos: []string{"\tBSWAPQ\t"}, 358 }, 359 { 360 fn: ` 361 func f10(b []byte) uint32 { 362 return binary.BigEndian.Uint32(b) 363 } 364 `, 365 pos: []string{"\tBSWAPL\t"}, 366 }, 367 { 368 fn: ` 369 func f11(b []byte, i int) uint32 { 370 return binary.BigEndian.Uint32(b[i:]) 371 } 372 `, 373 pos: []string{"\tBSWAPL\t"}, 374 }, 375 { 376 fn: ` 377 func f12(b []byte, v uint32) { 378 binary.BigEndian.PutUint32(b, v) 379 } 380 `, 381 pos: []string{"\tBSWAPL\t"}, 382 }, 383 { 384 fn: ` 385 func f13(b []byte, i int, v uint32) { 386 binary.BigEndian.PutUint32(b[i:], v) 387 } 388 `, 389 pos: []string{"\tBSWAPL\t"}, 390 }, 391 { 392 fn: ` 393 func f14(b []byte) uint16 { 394 return binary.BigEndian.Uint16(b) 395 } 396 `, 397 pos: []string{"\tROLW\t\\$8,"}, 398 }, 399 { 400 fn: ` 401 func f15(b []byte, i int) uint16 { 402 return binary.BigEndian.Uint16(b[i:]) 403 } 404 `, 405 pos: []string{"\tROLW\t\\$8,"}, 406 }, 407 { 408 fn: ` 409 func f16(b []byte, v uint16) { 410 binary.BigEndian.PutUint16(b, v) 411 } 412 `, 413 pos: []string{"\tROLW\t\\$8,"}, 414 }, 415 { 416 fn: ` 417 func f17(b []byte, i int, v uint16) { 418 binary.BigEndian.PutUint16(b[i:], v) 419 } 420 `, 421 pos: []string{"\tROLW\t\\$8,"}, 422 }, 423 // Structure zeroing. See issue #18370. 424 { 425 fn: ` 426 type T1 struct { 427 a, b, c int 428 } 429 func $(t *T1) { 430 *t = T1{} 431 } 432 `, 433 pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)"}, 434 }, 435 // SSA-able composite literal initialization. Issue 18872. 436 { 437 fn: ` 438 type T18872 struct { 439 a, b, c, d int 440 } 441 442 func f18872(p *T18872) { 443 *p = T18872{1, 2, 3, 4} 444 } 445 `, 446 pos: []string{"\tMOVQ\t[$]1", "\tMOVQ\t[$]2", "\tMOVQ\t[$]3", "\tMOVQ\t[$]4"}, 447 }, 448 // Also test struct containing pointers (this was special because of write barriers). 449 { 450 fn: ` 451 type T2 struct { 452 a, b, c *int 453 } 454 func f19(t *T2) { 455 *t = T2{} 456 } 457 `, 458 pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)", "\tCALL\truntime\\.writebarrierptr\\(SB\\)"}, 459 }, 460 // Rotate tests 461 { 462 fn: ` 463 func f20(x uint64) uint64 { 464 return x<<7 | x>>57 465 } 466 `, 467 pos: []string{"\tROLQ\t[$]7,"}, 468 }, 469 { 470 fn: ` 471 func f21(x uint64) uint64 { 472 return x<<7 + x>>57 473 } 474 `, 475 pos: []string{"\tROLQ\t[$]7,"}, 476 }, 477 { 478 fn: ` 479 func f22(x uint64) uint64 { 480 return x<<7 ^ x>>57 481 } 482 `, 483 pos: []string{"\tROLQ\t[$]7,"}, 484 }, 485 { 486 fn: ` 487 func f23(x uint32) uint32 { 488 return x<<7 + x>>25 489 } 490 `, 491 pos: []string{"\tROLL\t[$]7,"}, 492 }, 493 { 494 fn: ` 495 func f24(x uint32) uint32 { 496 return x<<7 | x>>25 497 } 498 `, 499 pos: []string{"\tROLL\t[$]7,"}, 500 }, 501 { 502 fn: ` 503 func f25(x uint32) uint32 { 504 return x<<7 ^ x>>25 505 } 506 `, 507 pos: []string{"\tROLL\t[$]7,"}, 508 }, 509 { 510 fn: ` 511 func f26(x uint16) uint16 { 512 return x<<7 + x>>9 513 } 514 `, 515 pos: []string{"\tROLW\t[$]7,"}, 516 }, 517 { 518 fn: ` 519 func f27(x uint16) uint16 { 520 return x<<7 | x>>9 521 } 522 `, 523 pos: []string{"\tROLW\t[$]7,"}, 524 }, 525 { 526 fn: ` 527 func f28(x uint16) uint16 { 528 return x<<7 ^ x>>9 529 } 530 `, 531 pos: []string{"\tROLW\t[$]7,"}, 532 }, 533 { 534 fn: ` 535 func f29(x uint8) uint8 { 536 return x<<7 + x>>1 537 } 538 `, 539 pos: []string{"\tROLB\t[$]7,"}, 540 }, 541 { 542 fn: ` 543 func f30(x uint8) uint8 { 544 return x<<7 | x>>1 545 } 546 `, 547 pos: []string{"\tROLB\t[$]7,"}, 548 }, 549 { 550 fn: ` 551 func f31(x uint8) uint8 { 552 return x<<7 ^ x>>1 553 } 554 `, 555 pos: []string{"\tROLB\t[$]7,"}, 556 }, 557 // Rotate after inlining (see issue 18254). 558 { 559 fn: ` 560 func f32(x uint32) uint32 { 561 return g(x, 7) 562 } 563 func g(x uint32, k uint) uint32 { 564 return x<<k | x>>(32-k) 565 } 566 `, 567 pos: []string{"\tROLL\t[$]7,"}, 568 }, 569 { 570 fn: ` 571 func f33(m map[int]int) int { 572 return m[5] 573 } 574 `, 575 pos: []string{"\tMOVQ\t[$]5,"}, 576 }, 577 // Direct use of constants in fast map access calls. Issue 19015. 578 { 579 fn: ` 580 func f34(m map[int]int) bool { 581 _, ok := m[5] 582 return ok 583 } 584 `, 585 pos: []string{"\tMOVQ\t[$]5,"}, 586 }, 587 { 588 fn: ` 589 func f35(m map[string]int) int { 590 return m["abc"] 591 } 592 `, 593 pos: []string{"\"abc\""}, 594 }, 595 { 596 fn: ` 597 func f36(m map[string]int) bool { 598 _, ok := m["abc"] 599 return ok 600 } 601 `, 602 pos: []string{"\"abc\""}, 603 }, 604 // Bit test ops on amd64, issue 18943. 605 { 606 fn: ` 607 func f37(a, b uint64) int { 608 if a&(1<<(b&63)) != 0 { 609 return 1 610 } 611 return -1 612 } 613 `, 614 pos: []string{"\tBTQ\t"}, 615 }, 616 { 617 fn: ` 618 func f38(a, b uint64) bool { 619 return a&(1<<(b&63)) != 0 620 } 621 `, 622 pos: []string{"\tBTQ\t"}, 623 }, 624 { 625 fn: ` 626 func f39(a uint64) int { 627 if a&(1<<60) != 0 { 628 return 1 629 } 630 return -1 631 } 632 `, 633 pos: []string{"\tBTQ\t\\$60"}, 634 }, 635 { 636 fn: ` 637 func f40(a uint64) bool { 638 return a&(1<<60) != 0 639 } 640 `, 641 pos: []string{"\tBTQ\t\\$60"}, 642 }, 643 // Intrinsic tests for math/bits 644 { 645 fn: ` 646 func f41(a uint64) int { 647 return bits.TrailingZeros64(a) 648 } 649 `, 650 pos: []string{"\tBSFQ\t", "\tMOVL\t\\$64,", "\tCMOVQEQ\t"}, 651 }, 652 { 653 fn: ` 654 func f42(a uint32) int { 655 return bits.TrailingZeros32(a) 656 } 657 `, 658 pos: []string{"\tBSFQ\t", "\tORQ\t[^$]", "\tMOVQ\t\\$4294967296,"}, 659 }, 660 { 661 fn: ` 662 func f43(a uint16) int { 663 return bits.TrailingZeros16(a) 664 } 665 `, 666 pos: []string{"\tBSFQ\t", "\tORQ\t\\$65536,"}, 667 }, 668 { 669 fn: ` 670 func f44(a uint8) int { 671 return bits.TrailingZeros8(a) 672 } 673 `, 674 pos: []string{"\tBSFQ\t", "\tORQ\t\\$256,"}, 675 }, 676 { 677 fn: ` 678 func f45(a uint64) uint64 { 679 return bits.ReverseBytes64(a) 680 } 681 `, 682 pos: []string{"\tBSWAPQ\t"}, 683 }, 684 { 685 fn: ` 686 func f46(a uint32) uint32 { 687 return bits.ReverseBytes32(a) 688 } 689 `, 690 pos: []string{"\tBSWAPL\t"}, 691 }, 692 { 693 fn: ` 694 func f47(a uint16) uint16 { 695 return bits.ReverseBytes16(a) 696 } 697 `, 698 pos: []string{"\tROLW\t\\$8,"}, 699 }, 700 { 701 fn: ` 702 func f48(a uint64) int { 703 return bits.Len64(a) 704 } 705 `, 706 pos: []string{"\tBSRQ\t"}, 707 }, 708 { 709 fn: ` 710 func f49(a uint32) int { 711 return bits.Len32(a) 712 } 713 `, 714 pos: []string{"\tBSRQ\t"}, 715 }, 716 { 717 fn: ` 718 func f50(a uint16) int { 719 return bits.Len16(a) 720 } 721 `, 722 pos: []string{"\tBSRQ\t"}, 723 }, 724 /* see ssa.go 725 { 726 fn:` 727 func f51(a uint8) int { 728 return bits.Len8(a) 729 } 730 `, 731 pos:[]string{"\tBSRQ\t"}, 732 }, 733 */ 734 { 735 fn: ` 736 func f52(a uint) int { 737 return bits.Len(a) 738 } 739 `, 740 pos: []string{"\tBSRQ\t"}, 741 }, 742 { 743 fn: ` 744 func f53(a uint64) int { 745 return bits.LeadingZeros64(a) 746 } 747 `, 748 pos: []string{"\tBSRQ\t"}, 749 }, 750 { 751 fn: ` 752 func f54(a uint32) int { 753 return bits.LeadingZeros32(a) 754 } 755 `, 756 pos: []string{"\tBSRQ\t"}, 757 }, 758 { 759 fn: ` 760 func f55(a uint16) int { 761 return bits.LeadingZeros16(a) 762 } 763 `, 764 pos: []string{"\tBSRQ\t"}, 765 }, 766 /* see ssa.go 767 { 768 fn:` 769 func f56(a uint8) int { 770 return bits.LeadingZeros8(a) 771 } 772 `, 773 pos:[]string{"\tBSRQ\t"}, 774 }, 775 */ 776 { 777 fn: ` 778 func f57(a uint) int { 779 return bits.LeadingZeros(a) 780 } 781 `, 782 pos: []string{"\tBSRQ\t"}, 783 }, 784 { 785 fn: ` 786 func pop1(x uint64) int { 787 return bits.OnesCount64(x) 788 }`, 789 pos: []string{"\tPOPCNTQ\t", "support_popcnt"}, 790 }, 791 { 792 fn: ` 793 func pop2(x uint32) int { 794 return bits.OnesCount32(x) 795 }`, 796 pos: []string{"\tPOPCNTL\t", "support_popcnt"}, 797 }, 798 { 799 fn: ` 800 func pop3(x uint16) int { 801 return bits.OnesCount16(x) 802 }`, 803 pos: []string{"\tPOPCNTL\t", "support_popcnt"}, 804 }, 805 { 806 fn: ` 807 func pop4(x uint) int { 808 return bits.OnesCount(x) 809 }`, 810 pos: []string{"\tPOPCNTQ\t", "support_popcnt"}, 811 }, 812 // multiplication merging tests 813 { 814 fn: ` 815 func mul1(n int) int { 816 return 15*n + 31*n 817 }`, 818 pos: []string{"\tIMULQ\t[$]46"}, // 46*n 819 }, 820 { 821 fn: ` 822 func mul2(n int) int { 823 return 5*n + 7*(n+1) + 11*(n+2) 824 }`, 825 pos: []string{"\tIMULQ\t[$]23", "\tADDQ\t[$]29"}, // 23*n + 29 826 }, 827 { 828 fn: ` 829 func mul3(a, n int) int { 830 return a*n + 19*n 831 }`, 832 pos: []string{"\tADDQ\t[$]19", "\tIMULQ"}, // (a+19)*n 833 }, 834 835 // see issue 19595. 836 // We want to merge load+op in f58, but not in f59. 837 { 838 fn: ` 839 func f58(p, q *int) { 840 x := *p 841 *q += x 842 }`, 843 pos: []string{"\tADDQ\t\\("}, 844 }, 845 { 846 fn: ` 847 func f59(p, q *int) { 848 x := *p 849 for i := 0; i < 10; i++ { 850 *q += x 851 } 852 }`, 853 pos: []string{"\tADDQ\t[A-Z]"}, 854 }, 855 // Floating-point strength reduction 856 { 857 fn: ` 858 func f60(f float64) float64 { 859 return f * 2.0 860 }`, 861 pos: []string{"\tADDSD\t"}, 862 }, 863 { 864 fn: ` 865 func f62(f float64) float64 { 866 return f / 16.0 867 }`, 868 pos: []string{"\tMULSD\t"}, 869 }, 870 { 871 fn: ` 872 func f63(f float64) float64 { 873 return f / 0.125 874 }`, 875 pos: []string{"\tMULSD\t"}, 876 }, 877 { 878 fn: ` 879 func f64(f float64) float64 { 880 return f / 0.5 881 }`, 882 pos: []string{"\tADDSD\t"}, 883 }, 884 // Check that compare to constant string uses 2/4/8 byte compares 885 { 886 fn: ` 887 func f65(a string) bool { 888 return a == "xx" 889 }`, 890 pos: []string{"\tCMPW\t[A-Z]"}, 891 }, 892 { 893 fn: ` 894 func f66(a string) bool { 895 return a == "xxxx" 896 }`, 897 pos: []string{"\tCMPL\t[A-Z]"}, 898 }, 899 { 900 fn: ` 901 func f67(a string) bool { 902 return a == "xxxxxxxx" 903 }`, 904 pos: []string{"\tCMPQ\t[A-Z]"}, 905 }, 906 // Non-constant rotate 907 { 908 fn: `func rot64l(x uint64, y int) uint64 { 909 z := uint(y & 63) 910 return x << z | x >> (64-z) 911 }`, 912 pos: []string{"\tROLQ\t"}, 913 }, 914 { 915 fn: `func rot64r(x uint64, y int) uint64 { 916 z := uint(y & 63) 917 return x >> z | x << (64-z) 918 }`, 919 pos: []string{"\tRORQ\t"}, 920 }, 921 { 922 fn: `func rot32l(x uint32, y int) uint32 { 923 z := uint(y & 31) 924 return x << z | x >> (32-z) 925 }`, 926 pos: []string{"\tROLL\t"}, 927 }, 928 { 929 fn: `func rot32r(x uint32, y int) uint32 { 930 z := uint(y & 31) 931 return x >> z | x << (32-z) 932 }`, 933 pos: []string{"\tRORL\t"}, 934 }, 935 { 936 fn: `func rot16l(x uint16, y int) uint16 { 937 z := uint(y & 15) 938 return x << z | x >> (16-z) 939 }`, 940 pos: []string{"\tROLW\t"}, 941 }, 942 { 943 fn: `func rot16r(x uint16, y int) uint16 { 944 z := uint(y & 15) 945 return x >> z | x << (16-z) 946 }`, 947 pos: []string{"\tRORW\t"}, 948 }, 949 { 950 fn: `func rot8l(x uint8, y int) uint8 { 951 z := uint(y & 7) 952 return x << z | x >> (8-z) 953 }`, 954 pos: []string{"\tROLB\t"}, 955 }, 956 { 957 fn: `func rot8r(x uint8, y int) uint8 { 958 z := uint(y & 7) 959 return x >> z | x << (8-z) 960 }`, 961 pos: []string{"\tRORB\t"}, 962 }, 963 // Check that array compare uses 2/4/8 byte compares 964 { 965 fn: ` 966 func f68(a,b [2]byte) bool { 967 return a == b 968 }`, 969 pos: []string{"\tCMPW\t[A-Z]"}, 970 }, 971 { 972 fn: ` 973 func f69(a,b [3]uint16) bool { 974 return a == b 975 }`, 976 pos: []string{"\tCMPL\t[A-Z]"}, 977 }, 978 { 979 fn: ` 980 func f70(a,b [15]byte) bool { 981 return a == b 982 }`, 983 pos: []string{"\tCMPQ\t[A-Z]"}, 984 }, 985 { 986 fn: ` 987 func f71(a,b unsafe.Pointer) bool { // This was a TODO in mapaccess1_faststr 988 return *((*[4]byte)(a)) != *((*[4]byte)(b)) 989 }`, 990 pos: []string{"\tCMPL\t[A-Z]"}, 991 }, 992 { 993 // make sure assembly output has matching offset and base register. 994 fn: ` 995 func f72(a, b int) int { 996 //go:noinline 997 func() {_, _ = a, b} () // use some frame 998 return b 999 } 1000 `, 1001 pos: []string{"b\\+40\\(SP\\)"}, 1002 }, 1003 { 1004 // check load combining 1005 fn: ` 1006 func f73(a, b byte) (byte,byte) { 1007 return f73(f73(a,b)) 1008 } 1009 `, 1010 pos: []string{"\tMOVW\t"}, 1011 }, 1012 { 1013 fn: ` 1014 func f74(a, b uint16) (uint16,uint16) { 1015 return f74(f74(a,b)) 1016 } 1017 `, 1018 pos: []string{"\tMOVL\t"}, 1019 }, 1020 { 1021 fn: ` 1022 func f75(a, b uint32) (uint32,uint32) { 1023 return f75(f75(a,b)) 1024 } 1025 `, 1026 pos: []string{"\tMOVQ\t"}, 1027 }, 1028 { 1029 fn: ` 1030 func f76(a, b uint64) (uint64,uint64) { 1031 return f76(f76(a,b)) 1032 } 1033 `, 1034 pos: []string{"\tMOVUPS\t"}, 1035 }, 1036 // Make sure we don't put pointers in SSE registers across safe points. 1037 { 1038 fn: ` 1039 func $(p, q *[2]*int) { 1040 a, b := p[0], p[1] 1041 runtime.GC() 1042 q[0], q[1] = a, b 1043 } 1044 `, 1045 neg: []string{"MOVUPS"}, 1046 }, 1047 { 1048 // check that stack store is optimized away 1049 fn: ` 1050 func $() int { 1051 var x int 1052 return *(&x) 1053 } 1054 `, 1055 pos: []string{"TEXT\t.*, [$]0-8"}, 1056 }, 1057 // math.Abs using integer registers 1058 { 1059 fn: ` 1060 func $(x float64) float64 { 1061 return math.Abs(x) 1062 } 1063 `, 1064 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,"}, 1065 }, 1066 // math.Copysign using integer registers 1067 { 1068 fn: ` 1069 func $(x, y float64) float64 { 1070 return math.Copysign(x, y) 1071 } 1072 `, 1073 pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,", "\tSHRQ\t[$]63,", "\tSHLQ\t[$]63,", "\tORQ\t"}, 1074 }, 1075 // int <-> fp moves 1076 { 1077 fn: ` 1078 func $(x float64) uint64 { 1079 return math.Float64bits(x+1) + 1 1080 } 1081 `, 1082 pos: []string{"\tMOVQ\tX.*, [^X].*"}, 1083 }, 1084 { 1085 fn: ` 1086 func $(x float32) uint32 { 1087 return math.Float32bits(x+1) + 1 1088 } 1089 `, 1090 pos: []string{"\tMOVL\tX.*, [^X].*"}, 1091 }, 1092 { 1093 fn: ` 1094 func $(x uint64) float64 { 1095 return math.Float64frombits(x+1) + 1 1096 } 1097 `, 1098 pos: []string{"\tMOVQ\t[^X].*, X.*"}, 1099 }, 1100 { 1101 fn: ` 1102 func $(x uint32) float32 { 1103 return math.Float32frombits(x+1) + 1 1104 } 1105 `, 1106 pos: []string{"\tMOVL\t[^X].*, X.*"}, 1107 }, 1108 } 1109 1110 var linux386Tests = []*asmTest{ 1111 { 1112 fn: ` 1113 func f0(b []byte) uint32 { 1114 return binary.LittleEndian.Uint32(b) 1115 } 1116 `, 1117 pos: []string{"\tMOVL\t\\(.*\\),"}, 1118 }, 1119 { 1120 fn: ` 1121 func f1(b []byte, i int) uint32 { 1122 return binary.LittleEndian.Uint32(b[i:]) 1123 } 1124 `, 1125 pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"}, 1126 }, 1127 1128 // multiplication merging tests 1129 { 1130 fn: ` 1131 func $(n int) int { 1132 return 9*n + 14*n 1133 }`, 1134 pos: []string{"\tIMULL\t[$]23"}, // 23*n 1135 }, 1136 { 1137 fn: ` 1138 func $(a, n int) int { 1139 return 19*a + a*n 1140 }`, 1141 pos: []string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a 1142 }, 1143 { 1144 // check that stack store is optimized away 1145 fn: ` 1146 func $() int { 1147 var x int 1148 return *(&x) 1149 } 1150 `, 1151 pos: []string{"TEXT\t.*, [$]0-4"}, 1152 }, 1153 } 1154 1155 var linuxS390XTests = []*asmTest{ 1156 { 1157 fn: ` 1158 func f0(b []byte) uint32 { 1159 return binary.LittleEndian.Uint32(b) 1160 } 1161 `, 1162 pos: []string{"\tMOVWBR\t\\(.*\\),"}, 1163 }, 1164 { 1165 fn: ` 1166 func f1(b []byte, i int) uint32 { 1167 return binary.LittleEndian.Uint32(b[i:]) 1168 } 1169 `, 1170 pos: []string{"\tMOVWBR\t\\(.*\\)\\(.*\\*1\\),"}, 1171 }, 1172 { 1173 fn: ` 1174 func f2(b []byte) uint64 { 1175 return binary.LittleEndian.Uint64(b) 1176 } 1177 `, 1178 pos: []string{"\tMOVDBR\t\\(.*\\),"}, 1179 }, 1180 { 1181 fn: ` 1182 func f3(b []byte, i int) uint64 { 1183 return binary.LittleEndian.Uint64(b[i:]) 1184 } 1185 `, 1186 pos: []string{"\tMOVDBR\t\\(.*\\)\\(.*\\*1\\),"}, 1187 }, 1188 { 1189 fn: ` 1190 func f4(b []byte) uint32 { 1191 return binary.BigEndian.Uint32(b) 1192 } 1193 `, 1194 pos: []string{"\tMOVWZ\t\\(.*\\),"}, 1195 }, 1196 { 1197 fn: ` 1198 func f5(b []byte, i int) uint32 { 1199 return binary.BigEndian.Uint32(b[i:]) 1200 } 1201 `, 1202 pos: []string{"\tMOVWZ\t\\(.*\\)\\(.*\\*1\\),"}, 1203 }, 1204 { 1205 fn: ` 1206 func f6(b []byte) uint64 { 1207 return binary.BigEndian.Uint64(b) 1208 } 1209 `, 1210 pos: []string{"\tMOVD\t\\(.*\\),"}, 1211 }, 1212 { 1213 fn: ` 1214 func f7(b []byte, i int) uint64 { 1215 return binary.BigEndian.Uint64(b[i:]) 1216 } 1217 `, 1218 pos: []string{"\tMOVD\t\\(.*\\)\\(.*\\*1\\),"}, 1219 }, 1220 { 1221 fn: ` 1222 func f8(x uint64) uint64 { 1223 return x<<7 + x>>57 1224 } 1225 `, 1226 pos: []string{"\tRLLG\t[$]7,"}, 1227 }, 1228 { 1229 fn: ` 1230 func f9(x uint64) uint64 { 1231 return x<<7 | x>>57 1232 } 1233 `, 1234 pos: []string{"\tRLLG\t[$]7,"}, 1235 }, 1236 { 1237 fn: ` 1238 func f10(x uint64) uint64 { 1239 return x<<7 ^ x>>57 1240 } 1241 `, 1242 pos: []string{"\tRLLG\t[$]7,"}, 1243 }, 1244 { 1245 fn: ` 1246 func f11(x uint32) uint32 { 1247 return x<<7 + x>>25 1248 } 1249 `, 1250 pos: []string{"\tRLL\t[$]7,"}, 1251 }, 1252 { 1253 fn: ` 1254 func f12(x uint32) uint32 { 1255 return x<<7 | x>>25 1256 } 1257 `, 1258 pos: []string{"\tRLL\t[$]7,"}, 1259 }, 1260 { 1261 fn: ` 1262 func f13(x uint32) uint32 { 1263 return x<<7 ^ x>>25 1264 } 1265 `, 1266 pos: []string{"\tRLL\t[$]7,"}, 1267 }, 1268 // Fused multiply-add/sub instructions. 1269 { 1270 fn: ` 1271 func f14(x, y, z float64) float64 { 1272 return x * y + z 1273 } 1274 `, 1275 pos: []string{"\tFMADD\t"}, 1276 }, 1277 { 1278 fn: ` 1279 func f15(x, y, z float64) float64 { 1280 return x * y - z 1281 } 1282 `, 1283 pos: []string{"\tFMSUB\t"}, 1284 }, 1285 { 1286 fn: ` 1287 func f16(x, y, z float32) float32 { 1288 return x * y + z 1289 } 1290 `, 1291 pos: []string{"\tFMADDS\t"}, 1292 }, 1293 { 1294 fn: ` 1295 func f17(x, y, z float32) float32 { 1296 return x * y - z 1297 } 1298 `, 1299 pos: []string{"\tFMSUBS\t"}, 1300 }, 1301 // Intrinsic tests for math/bits 1302 { 1303 fn: ` 1304 func f18(a uint64) int { 1305 return bits.TrailingZeros64(a) 1306 } 1307 `, 1308 pos: []string{"\tFLOGR\t"}, 1309 }, 1310 { 1311 fn: ` 1312 func f19(a uint32) int { 1313 return bits.TrailingZeros32(a) 1314 } 1315 `, 1316 pos: []string{"\tFLOGR\t", "\tMOVWZ\t"}, 1317 }, 1318 { 1319 fn: ` 1320 func f20(a uint16) int { 1321 return bits.TrailingZeros16(a) 1322 } 1323 `, 1324 pos: []string{"\tFLOGR\t", "\tOR\t\\$65536,"}, 1325 }, 1326 { 1327 fn: ` 1328 func f21(a uint8) int { 1329 return bits.TrailingZeros8(a) 1330 } 1331 `, 1332 pos: []string{"\tFLOGR\t", "\tOR\t\\$256,"}, 1333 }, 1334 // Intrinsic tests for math/bits 1335 { 1336 fn: ` 1337 func f22(a uint64) uint64 { 1338 return bits.ReverseBytes64(a) 1339 } 1340 `, 1341 pos: []string{"\tMOVDBR\t"}, 1342 }, 1343 { 1344 fn: ` 1345 func f23(a uint32) uint32 { 1346 return bits.ReverseBytes32(a) 1347 } 1348 `, 1349 pos: []string{"\tMOVWBR\t"}, 1350 }, 1351 { 1352 fn: ` 1353 func f24(a uint64) int { 1354 return bits.Len64(a) 1355 } 1356 `, 1357 pos: []string{"\tFLOGR\t"}, 1358 }, 1359 { 1360 fn: ` 1361 func f25(a uint32) int { 1362 return bits.Len32(a) 1363 } 1364 `, 1365 pos: []string{"\tFLOGR\t"}, 1366 }, 1367 { 1368 fn: ` 1369 func f26(a uint16) int { 1370 return bits.Len16(a) 1371 } 1372 `, 1373 pos: []string{"\tFLOGR\t"}, 1374 }, 1375 { 1376 fn: ` 1377 func f27(a uint8) int { 1378 return bits.Len8(a) 1379 } 1380 `, 1381 pos: []string{"\tFLOGR\t"}, 1382 }, 1383 { 1384 fn: ` 1385 func f28(a uint) int { 1386 return bits.Len(a) 1387 } 1388 `, 1389 pos: []string{"\tFLOGR\t"}, 1390 }, 1391 { 1392 fn: ` 1393 func f29(a uint64) int { 1394 return bits.LeadingZeros64(a) 1395 } 1396 `, 1397 pos: []string{"\tFLOGR\t"}, 1398 }, 1399 { 1400 fn: ` 1401 func f30(a uint32) int { 1402 return bits.LeadingZeros32(a) 1403 } 1404 `, 1405 pos: []string{"\tFLOGR\t"}, 1406 }, 1407 { 1408 fn: ` 1409 func f31(a uint16) int { 1410 return bits.LeadingZeros16(a) 1411 } 1412 `, 1413 pos: []string{"\tFLOGR\t"}, 1414 }, 1415 { 1416 fn: ` 1417 func f32(a uint8) int { 1418 return bits.LeadingZeros8(a) 1419 } 1420 `, 1421 pos: []string{"\tFLOGR\t"}, 1422 }, 1423 { 1424 fn: ` 1425 func f33(a uint) int { 1426 return bits.LeadingZeros(a) 1427 } 1428 `, 1429 pos: []string{"\tFLOGR\t"}, 1430 }, 1431 { 1432 // check that stack store is optimized away 1433 fn: ` 1434 func $() int { 1435 var x int 1436 return *(&x) 1437 } 1438 `, 1439 pos: []string{"TEXT\t.*, [$]0-8"}, 1440 }, 1441 } 1442 1443 var linuxARMTests = []*asmTest{ 1444 { 1445 fn: ` 1446 func f0(x uint32) uint32 { 1447 return x<<7 + x>>25 1448 } 1449 `, 1450 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1451 }, 1452 { 1453 fn: ` 1454 func f1(x uint32) uint32 { 1455 return x<<7 | x>>25 1456 } 1457 `, 1458 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1459 }, 1460 { 1461 fn: ` 1462 func f2(x uint32) uint32 { 1463 return x<<7 ^ x>>25 1464 } 1465 `, 1466 pos: []string{"\tMOVW\tR[0-9]+@>25,"}, 1467 }, 1468 { 1469 fn: ` 1470 func f3(a uint64) int { 1471 return bits.Len64(a) 1472 } 1473 `, 1474 pos: []string{"\tCLZ\t"}, 1475 }, 1476 { 1477 fn: ` 1478 func f4(a uint32) int { 1479 return bits.Len32(a) 1480 } 1481 `, 1482 pos: []string{"\tCLZ\t"}, 1483 }, 1484 { 1485 fn: ` 1486 func f5(a uint16) int { 1487 return bits.Len16(a) 1488 } 1489 `, 1490 pos: []string{"\tCLZ\t"}, 1491 }, 1492 { 1493 fn: ` 1494 func f6(a uint8) int { 1495 return bits.Len8(a) 1496 } 1497 `, 1498 pos: []string{"\tCLZ\t"}, 1499 }, 1500 { 1501 fn: ` 1502 func f7(a uint) int { 1503 return bits.Len(a) 1504 } 1505 `, 1506 pos: []string{"\tCLZ\t"}, 1507 }, 1508 { 1509 fn: ` 1510 func f8(a uint64) int { 1511 return bits.LeadingZeros64(a) 1512 } 1513 `, 1514 pos: []string{"\tCLZ\t"}, 1515 }, 1516 { 1517 fn: ` 1518 func f9(a uint32) int { 1519 return bits.LeadingZeros32(a) 1520 } 1521 `, 1522 pos: []string{"\tCLZ\t"}, 1523 }, 1524 { 1525 fn: ` 1526 func f10(a uint16) int { 1527 return bits.LeadingZeros16(a) 1528 } 1529 `, 1530 pos: []string{"\tCLZ\t"}, 1531 }, 1532 { 1533 fn: ` 1534 func f11(a uint8) int { 1535 return bits.LeadingZeros8(a) 1536 } 1537 `, 1538 pos: []string{"\tCLZ\t"}, 1539 }, 1540 { 1541 fn: ` 1542 func f12(a uint) int { 1543 return bits.LeadingZeros(a) 1544 } 1545 `, 1546 pos: []string{"\tCLZ\t"}, 1547 }, 1548 { 1549 // make sure assembly output has matching offset and base register. 1550 fn: ` 1551 func f13(a, b int) int { 1552 //go:noinline 1553 func() {_, _ = a, b} () // use some frame 1554 return b 1555 } 1556 `, 1557 pos: []string{"b\\+4\\(FP\\)"}, 1558 }, 1559 { 1560 // check that stack store is optimized away 1561 fn: ` 1562 func $() int { 1563 var x int 1564 return *(&x) 1565 } 1566 `, 1567 pos: []string{"TEXT\t.*, [$]-4-4"}, 1568 }, 1569 } 1570 1571 var linuxARM64Tests = []*asmTest{ 1572 { 1573 fn: ` 1574 func f0(x uint64) uint64 { 1575 return x<<7 + x>>57 1576 } 1577 `, 1578 pos: []string{"\tROR\t[$]57,"}, 1579 }, 1580 { 1581 fn: ` 1582 func f1(x uint64) uint64 { 1583 return x<<7 | x>>57 1584 } 1585 `, 1586 pos: []string{"\tROR\t[$]57,"}, 1587 }, 1588 { 1589 fn: ` 1590 func f2(x uint64) uint64 { 1591 return x<<7 ^ x>>57 1592 } 1593 `, 1594 pos: []string{"\tROR\t[$]57,"}, 1595 }, 1596 { 1597 fn: ` 1598 func f3(x uint32) uint32 { 1599 return x<<7 + x>>25 1600 } 1601 `, 1602 pos: []string{"\tRORW\t[$]25,"}, 1603 }, 1604 { 1605 fn: ` 1606 func f4(x uint32) uint32 { 1607 return x<<7 | x>>25 1608 } 1609 `, 1610 pos: []string{"\tRORW\t[$]25,"}, 1611 }, 1612 { 1613 fn: ` 1614 func f5(x uint32) uint32 { 1615 return x<<7 ^ x>>25 1616 } 1617 `, 1618 pos: []string{"\tRORW\t[$]25,"}, 1619 }, 1620 { 1621 fn: ` 1622 func f22(a uint64) uint64 { 1623 return bits.ReverseBytes64(a) 1624 } 1625 `, 1626 pos: []string{"\tREV\t"}, 1627 }, 1628 { 1629 fn: ` 1630 func f23(a uint32) uint32 { 1631 return bits.ReverseBytes32(a) 1632 } 1633 `, 1634 pos: []string{"\tREVW\t"}, 1635 }, 1636 { 1637 fn: ` 1638 func f24(a uint64) int { 1639 return bits.Len64(a) 1640 } 1641 `, 1642 pos: []string{"\tCLZ\t"}, 1643 }, 1644 { 1645 fn: ` 1646 func f25(a uint32) int { 1647 return bits.Len32(a) 1648 } 1649 `, 1650 pos: []string{"\tCLZ\t"}, 1651 }, 1652 { 1653 fn: ` 1654 func f26(a uint16) int { 1655 return bits.Len16(a) 1656 } 1657 `, 1658 pos: []string{"\tCLZ\t"}, 1659 }, 1660 { 1661 fn: ` 1662 func f27(a uint8) int { 1663 return bits.Len8(a) 1664 } 1665 `, 1666 pos: []string{"\tCLZ\t"}, 1667 }, 1668 { 1669 fn: ` 1670 func f28(a uint) int { 1671 return bits.Len(a) 1672 } 1673 `, 1674 pos: []string{"\tCLZ\t"}, 1675 }, 1676 { 1677 fn: ` 1678 func f29(a uint64) int { 1679 return bits.LeadingZeros64(a) 1680 } 1681 `, 1682 pos: []string{"\tCLZ\t"}, 1683 }, 1684 { 1685 fn: ` 1686 func f30(a uint32) int { 1687 return bits.LeadingZeros32(a) 1688 } 1689 `, 1690 pos: []string{"\tCLZ\t"}, 1691 }, 1692 { 1693 fn: ` 1694 func f31(a uint16) int { 1695 return bits.LeadingZeros16(a) 1696 } 1697 `, 1698 pos: []string{"\tCLZ\t"}, 1699 }, 1700 { 1701 fn: ` 1702 func f32(a uint8) int { 1703 return bits.LeadingZeros8(a) 1704 } 1705 `, 1706 pos: []string{"\tCLZ\t"}, 1707 }, 1708 { 1709 fn: ` 1710 func f33(a uint) int { 1711 return bits.LeadingZeros(a) 1712 } 1713 `, 1714 pos: []string{"\tCLZ\t"}, 1715 }, 1716 { 1717 fn: ` 1718 func f34(a uint64) uint64 { 1719 return a & ((1<<63)-1) 1720 } 1721 `, 1722 pos: []string{"\tAND\t"}, 1723 }, 1724 { 1725 fn: ` 1726 func f35(a uint64) uint64 { 1727 return a & (1<<63) 1728 } 1729 `, 1730 pos: []string{"\tAND\t"}, 1731 }, 1732 { 1733 // make sure offsets are folded into load and store. 1734 fn: ` 1735 func f36(_, a [20]byte) (b [20]byte) { 1736 b = a 1737 return 1738 } 1739 `, 1740 pos: []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"}, 1741 }, 1742 { 1743 // check that stack store is optimized away 1744 fn: ` 1745 func $() int { 1746 var x int 1747 return *(&x) 1748 } 1749 `, 1750 pos: []string{"TEXT\t.*, [$]-8-8"}, 1751 }, 1752 { 1753 // check that we don't emit comparisons for constant shift 1754 fn: ` 1755 //go:nosplit 1756 func $(x int) int { 1757 return x << 17 1758 } 1759 `, 1760 pos: []string{"LSL\t\\$17"}, 1761 neg: []string{"CMP"}, 1762 }, 1763 } 1764 1765 var linuxMIPSTests = []*asmTest{ 1766 { 1767 fn: ` 1768 func f0(a uint64) int { 1769 return bits.Len64(a) 1770 } 1771 `, 1772 pos: []string{"\tCLZ\t"}, 1773 }, 1774 { 1775 fn: ` 1776 func f1(a uint32) int { 1777 return bits.Len32(a) 1778 } 1779 `, 1780 pos: []string{"\tCLZ\t"}, 1781 }, 1782 { 1783 fn: ` 1784 func f2(a uint16) int { 1785 return bits.Len16(a) 1786 } 1787 `, 1788 pos: []string{"\tCLZ\t"}, 1789 }, 1790 { 1791 fn: ` 1792 func f3(a uint8) int { 1793 return bits.Len8(a) 1794 } 1795 `, 1796 pos: []string{"\tCLZ\t"}, 1797 }, 1798 { 1799 fn: ` 1800 func f4(a uint) int { 1801 return bits.Len(a) 1802 } 1803 `, 1804 pos: []string{"\tCLZ\t"}, 1805 }, 1806 { 1807 fn: ` 1808 func f5(a uint64) int { 1809 return bits.LeadingZeros64(a) 1810 } 1811 `, 1812 pos: []string{"\tCLZ\t"}, 1813 }, 1814 { 1815 fn: ` 1816 func f6(a uint32) int { 1817 return bits.LeadingZeros32(a) 1818 } 1819 `, 1820 pos: []string{"\tCLZ\t"}, 1821 }, 1822 { 1823 fn: ` 1824 func f7(a uint16) int { 1825 return bits.LeadingZeros16(a) 1826 } 1827 `, 1828 pos: []string{"\tCLZ\t"}, 1829 }, 1830 { 1831 fn: ` 1832 func f8(a uint8) int { 1833 return bits.LeadingZeros8(a) 1834 } 1835 `, 1836 pos: []string{"\tCLZ\t"}, 1837 }, 1838 { 1839 fn: ` 1840 func f9(a uint) int { 1841 return bits.LeadingZeros(a) 1842 } 1843 `, 1844 pos: []string{"\tCLZ\t"}, 1845 }, 1846 { 1847 // check that stack store is optimized away 1848 fn: ` 1849 func $() int { 1850 var x int 1851 return *(&x) 1852 } 1853 `, 1854 pos: []string{"TEXT\t.*, [$]-4-4"}, 1855 }, 1856 } 1857 1858 var linuxMIPS64Tests = []*asmTest{ 1859 { 1860 // check that we don't emit comparisons for constant shift 1861 fn: ` 1862 func $(x int) int { 1863 return x << 17 1864 } 1865 `, 1866 pos: []string{"SLLV\t\\$17"}, 1867 neg: []string{"SGT"}, 1868 }, 1869 } 1870 1871 var linuxPPC64LETests = []*asmTest{ 1872 // Fused multiply-add/sub instructions. 1873 { 1874 fn: ` 1875 func f0(x, y, z float64) float64 { 1876 return x * y + z 1877 } 1878 `, 1879 pos: []string{"\tFMADD\t"}, 1880 }, 1881 { 1882 fn: ` 1883 func f1(x, y, z float64) float64 { 1884 return x * y - z 1885 } 1886 `, 1887 pos: []string{"\tFMSUB\t"}, 1888 }, 1889 { 1890 fn: ` 1891 func f2(x, y, z float32) float32 { 1892 return x * y + z 1893 } 1894 `, 1895 pos: []string{"\tFMADDS\t"}, 1896 }, 1897 { 1898 fn: ` 1899 func f3(x, y, z float32) float32 { 1900 return x * y - z 1901 } 1902 `, 1903 pos: []string{"\tFMSUBS\t"}, 1904 }, 1905 { 1906 fn: ` 1907 func f4(x uint32) uint32 { 1908 return x<<7 | x>>25 1909 } 1910 `, 1911 pos: []string{"\tROTLW\t"}, 1912 }, 1913 { 1914 fn: ` 1915 func f5(x uint32) uint32 { 1916 return x<<7 + x>>25 1917 } 1918 `, 1919 pos: []string{"\tROTLW\t"}, 1920 }, 1921 { 1922 fn: ` 1923 func f6(x uint32) uint32 { 1924 return x<<7 ^ x>>25 1925 } 1926 `, 1927 pos: []string{"\tROTLW\t"}, 1928 }, 1929 { 1930 fn: ` 1931 func f7(x uint64) uint64 { 1932 return x<<7 | x>>57 1933 } 1934 `, 1935 pos: []string{"\tROTL\t"}, 1936 }, 1937 { 1938 fn: ` 1939 func f8(x uint64) uint64 { 1940 return x<<7 + x>>57 1941 } 1942 `, 1943 pos: []string{"\tROTL\t"}, 1944 }, 1945 { 1946 fn: ` 1947 func f9(x uint64) uint64 { 1948 return x<<7 ^ x>>57 1949 } 1950 `, 1951 pos: []string{"\tROTL\t"}, 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.*, [$]0-8"}, 1962 }, 1963 } 1964 1965 var plan9AMD64Tests = []*asmTest{ 1966 // We should make sure that the compiler doesn't generate floating point 1967 // instructions for non-float operations on Plan 9, because floating point 1968 // operations are not allowed in the note handler. 1969 // Array zeroing. 1970 { 1971 fn: ` 1972 func $() [16]byte { 1973 var a [16]byte 1974 return a 1975 } 1976 `, 1977 pos: []string{"\tMOVQ\t\\$0, \"\""}, 1978 }, 1979 // Array copy. 1980 { 1981 fn: ` 1982 func $(a [16]byte) (b [16]byte) { 1983 b = a 1984 return 1985 } 1986 `, 1987 pos: []string{"\tMOVQ\t\"\"\\.a\\+[0-9]+\\(SP\\), (AX|CX)", "\tMOVQ\t(AX|CX), \"\"\\.b\\+[0-9]+\\(SP\\)"}, 1988 }, 1989 } 1990 1991 // TestLineNumber checks to make sure the generated assembly has line numbers 1992 // see issue #16214 1993 func TestLineNumber(t *testing.T) { 1994 testenv.MustHaveGoBuild(t) 1995 dir, err := ioutil.TempDir("", "TestLineNumber") 1996 if err != nil { 1997 t.Fatalf("could not create directory: %v", err) 1998 } 1999 defer os.RemoveAll(dir) 2000 2001 src := filepath.Join(dir, "x.go") 2002 err = ioutil.WriteFile(src, []byte(issue16214src), 0644) 2003 if err != nil { 2004 t.Fatalf("could not write file: %v", err) 2005 } 2006 2007 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) 2008 out, err := cmd.CombinedOutput() 2009 if err != nil { 2010 t.Fatalf("fail to run go tool compile: %v", err) 2011 } 2012 2013 if strings.Contains(string(out), "unknown line number") { 2014 t.Errorf("line number missing in assembly:\n%s", out) 2015 } 2016 } 2017 2018 var issue16214src = ` 2019 package main 2020 2021 func Mod32(x uint32) uint32 { 2022 return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos 2023 } 2024 `