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

     1  // Copyright 2019 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 riscv
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  // TestLargeBranch generates a large function with a very far conditional
    20  // branch, in order to ensure that it assembles successfully.
    21  func TestLargeBranch(t *testing.T) {
    22  	if testing.Short() {
    23  		t.Skip("Skipping test in short mode")
    24  	}
    25  	testenv.MustHaveGoBuild(t)
    26  
    27  	dir, err := os.MkdirTemp("", "testlargebranch")
    28  	if err != nil {
    29  		t.Fatalf("Could not create directory: %v", err)
    30  	}
    31  	defer os.RemoveAll(dir)
    32  
    33  	// Generate a very large function.
    34  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
    35  	genLargeBranch(buf)
    36  
    37  	tmpfile := filepath.Join(dir, "x.s")
    38  	if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
    39  		t.Fatalf("Failed to write file: %v", err)
    40  	}
    41  
    42  	// Assemble generated file.
    43  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
    44  	cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
    45  	out, err := cmd.CombinedOutput()
    46  	if err != nil {
    47  		t.Errorf("Build failed: %v, output: %s", err, out)
    48  	}
    49  }
    50  
    51  func genLargeBranch(buf *bytes.Buffer) {
    52  	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
    53  	fmt.Fprintln(buf, "BEQ X0, X0, label")
    54  	for i := 0; i < 1<<19; i++ {
    55  		fmt.Fprintln(buf, "ADD $0, X0, X0")
    56  	}
    57  	fmt.Fprintln(buf, "label:")
    58  	fmt.Fprintln(buf, "ADD $0, X0, X0")
    59  }
    60  
    61  // TestLargeCall generates a large function (>1MB of text) with a call to
    62  // a following function, in order to ensure that it assembles and links
    63  // correctly.
    64  func TestLargeCall(t *testing.T) {
    65  	if testing.Short() {
    66  		t.Skip("Skipping test in short mode")
    67  	}
    68  	testenv.MustHaveGoBuild(t)
    69  
    70  	dir, err := os.MkdirTemp("", "testlargecall")
    71  	if err != nil {
    72  		t.Fatalf("could not create directory: %v", err)
    73  	}
    74  	defer os.RemoveAll(dir)
    75  
    76  	if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largecall"), 0644); err != nil {
    77  		t.Fatalf("Failed to write file: %v\n", err)
    78  	}
    79  	main := `package main
    80  func main() {
    81          x()
    82  }
    83  
    84  func x()
    85  func y()
    86  `
    87  	if err := os.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
    88  		t.Fatalf("failed to write main: %v\n", err)
    89  	}
    90  
    91  	// Generate a very large function with call.
    92  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
    93  	genLargeCall(buf)
    94  
    95  	if err := os.WriteFile(filepath.Join(dir, "x.s"), buf.Bytes(), 0644); err != nil {
    96  		t.Fatalf("Failed to write file: %v\n", err)
    97  	}
    98  
    99  	// Build generated files.
   100  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal")
   101  	cmd.Dir = dir
   102  	cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
   103  	out, err := cmd.CombinedOutput()
   104  	if err != nil {
   105  		t.Errorf("Build failed: %v, output: %s", err, out)
   106  	}
   107  
   108  	if runtime.GOARCH == "riscv64" && testenv.HasCGO() {
   109  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=external")
   110  		cmd.Dir = dir
   111  		cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
   112  		out, err := cmd.CombinedOutput()
   113  		if err != nil {
   114  			t.Errorf("Build failed: %v, output: %s", err, out)
   115  		}
   116  	}
   117  }
   118  
   119  func genLargeCall(buf *bytes.Buffer) {
   120  	fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-0")
   121  	fmt.Fprintln(buf, "CALL ·y(SB)")
   122  	for i := 0; i < 1<<19; i++ {
   123  		fmt.Fprintln(buf, "ADD $0, X0, X0")
   124  	}
   125  	fmt.Fprintln(buf, "RET")
   126  	fmt.Fprintln(buf, "TEXT ·y(SB),0,$0-0")
   127  	fmt.Fprintln(buf, "ADD $0, X0, X0")
   128  	fmt.Fprintln(buf, "RET")
   129  }
   130  
   131  // TestLargeJump generates a large jump (>1MB of text) with a JMP to the
   132  // end of the function, in order to ensure that it assembles correctly.
   133  func TestLargeJump(t *testing.T) {
   134  	if testing.Short() {
   135  		t.Skip("Skipping test in short mode")
   136  	}
   137  	if runtime.GOARCH != "riscv64" {
   138  		t.Skip("Require riscv64 to run")
   139  	}
   140  	testenv.MustHaveGoBuild(t)
   141  
   142  	dir := t.TempDir()
   143  
   144  	if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largejump"), 0644); err != nil {
   145  		t.Fatalf("Failed to write file: %v\n", err)
   146  	}
   147  	main := `package main
   148  
   149  import "fmt"
   150  
   151  func main() {
   152          fmt.Print(x())
   153  }
   154  
   155  func x() uint64
   156  `
   157  	if err := os.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
   158  		t.Fatalf("failed to write main: %v\n", err)
   159  	}
   160  
   161  	// Generate a very large jump instruction.
   162  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   163  	genLargeJump(buf)
   164  
   165  	if err := os.WriteFile(filepath.Join(dir, "x.s"), buf.Bytes(), 0644); err != nil {
   166  		t.Fatalf("Failed to write file: %v\n", err)
   167  	}
   168  
   169  	// Build generated files.
   170  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "x.exe")
   171  	cmd.Dir = dir
   172  	out, err := cmd.CombinedOutput()
   173  	if err != nil {
   174  		t.Errorf("Build failed: %v, output: %s", err, out)
   175  	}
   176  
   177  	cmd = testenv.Command(t, filepath.Join(dir, "x.exe"))
   178  	out, err = cmd.CombinedOutput()
   179  	if string(out) != "1" {
   180  		t.Errorf(`Got test output %q, want "1"`, string(out))
   181  	}
   182  }
   183  
   184  func genLargeJump(buf *bytes.Buffer) {
   185  	fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-8")
   186  	fmt.Fprintln(buf, "MOV  X0, X10")
   187  	fmt.Fprintln(buf, "JMP end")
   188  	for i := 0; i < 1<<18; i++ {
   189  		fmt.Fprintln(buf, "ADD $1, X10, X10")
   190  	}
   191  	fmt.Fprintln(buf, "end:")
   192  	fmt.Fprintln(buf, "ADD $1, X10, X10")
   193  	fmt.Fprintln(buf, "MOV X10, r+0(FP)")
   194  	fmt.Fprintln(buf, "RET")
   195  }
   196  
   197  // Issue 20348.
   198  func TestNoRet(t *testing.T) {
   199  	dir, err := os.MkdirTemp("", "testnoret")
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	defer os.RemoveAll(dir)
   204  	tmpfile := filepath.Join(dir, "x.s")
   205  	if err := os.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
   209  	cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
   210  	if out, err := cmd.CombinedOutput(); err != nil {
   211  		t.Errorf("%v\n%s", err, out)
   212  	}
   213  }
   214  
   215  func TestImmediateSplitting(t *testing.T) {
   216  	dir, err := os.MkdirTemp("", "testimmsplit")
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  	defer os.RemoveAll(dir)
   221  	tmpfile := filepath.Join(dir, "x.s")
   222  	asm := `
   223  TEXT _stub(SB),$0-0
   224  	LB	4096(X5), X6
   225  	LH	4096(X5), X6
   226  	LW	4096(X5), X6
   227  	LD	4096(X5), X6
   228  	LBU	4096(X5), X6
   229  	LHU	4096(X5), X6
   230  	LWU	4096(X5), X6
   231  	SB	X6, 4096(X5)
   232  	SH	X6, 4096(X5)
   233  	SW	X6, 4096(X5)
   234  	SD	X6, 4096(X5)
   235  
   236  	FLW	4096(X5), F6
   237  	FLD	4096(X5), F6
   238  	FSW	F6, 4096(X5)
   239  	FSD	F6, 4096(X5)
   240  
   241  	MOVB	4096(X5), X6
   242  	MOVH	4096(X5), X6
   243  	MOVW	4096(X5), X6
   244  	MOV	4096(X5), X6
   245  	MOVBU	4096(X5), X6
   246  	MOVHU	4096(X5), X6
   247  	MOVWU	4096(X5), X6
   248  
   249  	MOVB	X6, 4096(X5)
   250  	MOVH	X6, 4096(X5)
   251  	MOVW	X6, 4096(X5)
   252  	MOV	X6, 4096(X5)
   253  
   254  	MOVF	4096(X5), F6
   255  	MOVD	4096(X5), F6
   256  	MOVF	F6, 4096(X5)
   257  	MOVD	F6, 4096(X5)
   258  `
   259  	if err := os.WriteFile(tmpfile, []byte(asm), 0644); err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
   263  	cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
   264  	if out, err := cmd.CombinedOutput(); err != nil {
   265  		t.Errorf("%v\n%s", err, out)
   266  	}
   267  }
   268  
   269  func TestBranch(t *testing.T) {
   270  	if runtime.GOARCH != "riscv64" {
   271  		t.Skip("Requires riscv64 to run")
   272  	}
   273  
   274  	testenv.MustHaveGoBuild(t)
   275  
   276  	cmd := testenv.Command(t, testenv.GoToolPath(t), "test")
   277  	cmd.Dir = "testdata/testbranch"
   278  	if out, err := testenv.CleanCmdEnv(cmd).CombinedOutput(); err != nil {
   279  		t.Errorf("Branch test failed: %v\n%s", err, out)
   280  	}
   281  }
   282  
   283  func TestPCAlign(t *testing.T) {
   284  	dir := t.TempDir()
   285  	tmpfile := filepath.Join(dir, "x.s")
   286  	asm := `
   287  TEXT _stub(SB),$0-0
   288  	FENCE
   289  	PCALIGN	$8
   290  	FENCE
   291  	RET
   292  `
   293  	if err := os.WriteFile(tmpfile, []byte(asm), 0644); err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
   297  	cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
   298  	out, err := cmd.CombinedOutput()
   299  	if err != nil {
   300  		t.Errorf("Failed to assemble: %v\n%s", err, out)
   301  	}
   302  	// The expected instruction sequence after alignment:
   303  	//	FENCE
   304  	//	NOP
   305  	//	FENCE
   306  	//	RET
   307  	want := "0f 00 f0 0f 13 00 00 00 0f 00 f0 0f 67 80 00 00"
   308  	if !strings.Contains(string(out), want) {
   309  		t.Errorf("PCALIGN test failed - got %s\nwant %s", out, want)
   310  	}
   311  }
   312  

View as plain text