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

View as plain text