Source file src/cmd/internal/obj/loong64/asm_test.go

     1  // Copyright 2023 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 loong64
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  	"runtime"
    15  	"testing"
    16  )
    17  
    18  const genBufSize = (1024 * 1024 * 32) // 32MB
    19  
    20  // TestLargeBranch generates a large function with a very far conditional
    21  // branch, in order to ensure that it assembles successfully.
    22  func TestLargeBranch(t *testing.T) {
    23  	if testing.Short() {
    24  		t.Skip("Skipping test in short mode")
    25  	}
    26  	testenv.MustHaveGoBuild(t)
    27  
    28  	dir := t.TempDir()
    29  
    30  	// Generate a very large function.
    31  	buf := bytes.NewBuffer(make([]byte, 0, genBufSize))
    32  	genLargeBranch(buf)
    33  
    34  	tmpfile := filepath.Join(dir, "x.s")
    35  	if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
    36  		t.Fatalf("Failed to write file: %v", err)
    37  	}
    38  
    39  	// Assemble generated file.
    40  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
    41  	cmd.Env = append(os.Environ(), "GOARCH=loong64", "GOOS=linux")
    42  	out, err := cmd.CombinedOutput()
    43  	if err != nil {
    44  		t.Errorf("Build failed: %v, output: %s", err, out)
    45  	}
    46  }
    47  
    48  func genLargeBranch(buf *bytes.Buffer) {
    49  	genSize1 := (1 << 16) + 16
    50  	genSize2 := (1 << 21) + 16
    51  
    52  	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
    53  	fmt.Fprintln(buf, "BEQ R5, R6, label18")
    54  	fmt.Fprintln(buf, "BNE R5, R6, label18")
    55  	fmt.Fprintln(buf, "BGE R5, R6, label18")
    56  
    57  	fmt.Fprintln(buf, "BGEU R5, R6, label18")
    58  	fmt.Fprintln(buf, "BLTU R5, R6, label18")
    59  
    60  	fmt.Fprintln(buf, "BLEZ R5, label18")
    61  	fmt.Fprintln(buf, "BGEZ R5, label18")
    62  	fmt.Fprintln(buf, "BLTZ R5, label18")
    63  	fmt.Fprintln(buf, "BGTZ R5, label18")
    64  
    65  	fmt.Fprintln(buf, "BFPT label23")
    66  	fmt.Fprintln(buf, "BFPF label23")
    67  
    68  	fmt.Fprintln(buf, "BEQ R5, label23")
    69  	fmt.Fprintln(buf, "BNE R5, label23")
    70  
    71  	for i := 0; i <= genSize1; i++ {
    72  		fmt.Fprintln(buf, "ADDV $0, R0, R0")
    73  	}
    74  
    75  	fmt.Fprintln(buf, "label18:")
    76  	for i := 0; i <= (genSize2 - genSize1); i++ {
    77  		fmt.Fprintln(buf, "ADDV $0, R0, R0")
    78  	}
    79  
    80  	fmt.Fprintln(buf, "label23:")
    81  	fmt.Fprintln(buf, "ADDV $0, R0, R0")
    82  	fmt.Fprintln(buf, "RET")
    83  }
    84  
    85  // TestPCALIGN verifies the correctness of the PCALIGN by checking if the
    86  // code can be aligned to the alignment value.
    87  func TestPCALIGN(t *testing.T) {
    88  	testenv.MustHaveGoBuild(t)
    89  	dir := t.TempDir()
    90  	tmpfile := filepath.Join(dir, "testpcalign.s")
    91  	tmpout := filepath.Join(dir, "testpcalign.o")
    92  
    93  	code1 := []byte("TEXT ·foo(SB),$0-0\nMOVW $0, R0\nPCALIGN $8\nADDV $8, R0\nRET\n")
    94  	code2 := []byte("TEXT ·foo(SB),$0-0\nMOVW $0, R0\nPCALIGN $16\nADDV $16, R0\nRET\n")
    95  	code3 := []byte("TEXT ·foo(SB),$0-0\nMOVW $0, R0\nPCALIGN $32\nADDV $32, R0\nRET\n")
    96  	out1 := `0x0008\s00008\s\(.*\)\s*ADDV\s\$8,\sR0`
    97  	out2 := `0x0010\s00016\s\(.*\)\s*ADDV\s\$16,\sR0`
    98  	out3 := `0x0020\s00032\s\(.*\)\s*ADDV\s\$32,\sR0`
    99  	var testCases = []struct {
   100  		name   string
   101  		source []byte
   102  		want   string
   103  	}{
   104  		{"pcalign8", code1, out1},
   105  		{"pcalign16", code2, out2},
   106  		{"pcalign32", code3, out3},
   107  	}
   108  	for _, test := range testCases {
   109  		if err := os.WriteFile(tmpfile, test.source, 0644); err != nil {
   110  			t.Fatal(err)
   111  		}
   112  		cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
   113  		cmd.Env = append(os.Environ(), "GOARCH=loong64", "GOOS=linux")
   114  		out, err := cmd.CombinedOutput()
   115  		if err != nil {
   116  			t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
   117  			continue
   118  		}
   119  		matched, err := regexp.MatchString(test.want, string(out))
   120  		if err != nil {
   121  			t.Fatal(err)
   122  		}
   123  		if !matched {
   124  			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.source, out)
   125  		}
   126  	}
   127  }
   128  
   129  func TestNoRet(t *testing.T) {
   130  	dir := t.TempDir()
   131  	tmpfile := filepath.Join(dir, "testnoret.s")
   132  	tmpout := filepath.Join(dir, "testnoret.o")
   133  	if err := os.WriteFile(tmpfile, []byte("TEXT ·foo(SB),$0-0\nNOP\n"), 0644); err != nil {
   134  		t.Fatal(err)
   135  	}
   136  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", tmpout, tmpfile)
   137  	cmd.Env = append(os.Environ(), "GOARCH=loong64", "GOOS=linux")
   138  	if out, err := cmd.CombinedOutput(); err != nil {
   139  		t.Errorf("%v\n%s", err, out)
   140  	}
   141  }
   142  
   143  func TestLargeCall(t *testing.T) {
   144  	if testing.Short() {
   145  		t.Skip("Skipping test in short mode")
   146  	}
   147  	if runtime.GOARCH != "loong64" {
   148  		t.Skip("Require loong64 to run")
   149  	}
   150  	testenv.MustHaveGoBuild(t)
   151  
   152  	dir := t.TempDir()
   153  
   154  	if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largecall"), 0644); err != nil {
   155  		t.Fatalf("Failed to write file: %v\n", err)
   156  	}
   157  	main := `package main
   158  
   159  func main() {
   160          a()
   161  }
   162  
   163  func a()
   164  `
   165  	if err := os.WriteFile(filepath.Join(dir, "largecall.go"), []byte(main), 0644); err != nil {
   166  		t.Fatalf("failed to write main: %v\n", err)
   167  	}
   168  
   169  	// Generate a very large call instruction.
   170  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   171  	genLargeCall(buf)
   172  
   173  	if err := os.WriteFile(filepath.Join(dir, "largecall.s"), buf.Bytes(), 0644); err != nil {
   174  		t.Fatalf("Failed to write file: %v\n", err)
   175  	}
   176  
   177  	// Build generated files.
   178  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build")
   179  	cmd.Dir = dir
   180  	out, err := cmd.CombinedOutput()
   181  	if err != nil {
   182  		t.Errorf("Build failed: %v, output: %s", err, out)
   183  	}
   184  }
   185  
   186  func genLargeCall(buf *bytes.Buffer) {
   187  	fmt.Fprintln(buf, "TEXT main·a(SB),0,$0-8")
   188  	fmt.Fprintln(buf, "CALL b(SB)")
   189  	for i := 0; i <= ((1 << 26) + 26); i++ {
   190  		fmt.Fprintln(buf, "ADDV $0, R0, R0")
   191  	}
   192  	fmt.Fprintln(buf, "RET")
   193  	fmt.Fprintln(buf, "TEXT b(SB),0,$0-8")
   194  	fmt.Fprintln(buf, "ADDV $0, R0, R0")
   195  	fmt.Fprintln(buf, "RET")
   196  }
   197  

View as plain text