Source file src/cmd/internal/obj/arm64/asm_arm64_test.go

     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 arm64
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  	"testing"
    15  )
    16  
    17  func TestSplitImm24uScaled(t *testing.T) {
    18  	tests := []struct {
    19  		v       int32
    20  		shift   int
    21  		wantErr bool
    22  		wantHi  int32
    23  		wantLo  int32
    24  	}{
    25  		{
    26  			v:      0,
    27  			shift:  0,
    28  			wantHi: 0,
    29  			wantLo: 0,
    30  		},
    31  		{
    32  			v:      0x1001,
    33  			shift:  0,
    34  			wantHi: 0x1000,
    35  			wantLo: 0x1,
    36  		},
    37  		{
    38  			v:      0xffffff,
    39  			shift:  0,
    40  			wantHi: 0xfff000,
    41  			wantLo: 0xfff,
    42  		},
    43  		{
    44  			v:       0xffffff,
    45  			shift:   1,
    46  			wantErr: true,
    47  		},
    48  		{
    49  			v:      0xfe,
    50  			shift:  1,
    51  			wantHi: 0x0,
    52  			wantLo: 0x7f,
    53  		},
    54  		{
    55  			v:      0x10fe,
    56  			shift:  1,
    57  			wantHi: 0x0,
    58  			wantLo: 0x87f,
    59  		},
    60  		{
    61  			v:      0x2002,
    62  			shift:  1,
    63  			wantHi: 0x2000,
    64  			wantLo: 0x1,
    65  		},
    66  		{
    67  			v:      0xfffffe,
    68  			shift:  1,
    69  			wantHi: 0xffe000,
    70  			wantLo: 0xfff,
    71  		},
    72  		{
    73  			v:      0x1000ffe,
    74  			shift:  1,
    75  			wantHi: 0xfff000,
    76  			wantLo: 0xfff,
    77  		},
    78  		{
    79  			v:       0x1001000,
    80  			shift:   1,
    81  			wantErr: true,
    82  		},
    83  		{
    84  			v:       0xfffffe,
    85  			shift:   2,
    86  			wantErr: true,
    87  		},
    88  		{
    89  			v:      0x4004,
    90  			shift:  2,
    91  			wantHi: 0x4000,
    92  			wantLo: 0x1,
    93  		},
    94  		{
    95  			v:      0xfffffc,
    96  			shift:  2,
    97  			wantHi: 0xffc000,
    98  			wantLo: 0xfff,
    99  		},
   100  		{
   101  			v:      0x1002ffc,
   102  			shift:  2,
   103  			wantHi: 0xfff000,
   104  			wantLo: 0xfff,
   105  		},
   106  		{
   107  			v:       0x1003000,
   108  			shift:   2,
   109  			wantErr: true,
   110  		},
   111  		{
   112  			v:       0xfffffe,
   113  			shift:   3,
   114  			wantErr: true,
   115  		},
   116  		{
   117  			v:      0x8008,
   118  			shift:  3,
   119  			wantHi: 0x8000,
   120  			wantLo: 0x1,
   121  		},
   122  		{
   123  			v:      0xfffff8,
   124  			shift:  3,
   125  			wantHi: 0xff8000,
   126  			wantLo: 0xfff,
   127  		},
   128  		{
   129  			v:      0x1006ff8,
   130  			shift:  3,
   131  			wantHi: 0xfff000,
   132  			wantLo: 0xfff,
   133  		},
   134  		{
   135  			v:       0x1007000,
   136  			shift:   3,
   137  			wantErr: true,
   138  		},
   139  	}
   140  	for _, test := range tests {
   141  		hi, lo, err := splitImm24uScaled(test.v, test.shift)
   142  		switch {
   143  		case err == nil && test.wantErr:
   144  			t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift)
   145  		case err != nil && !test.wantErr:
   146  			t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err)
   147  		case !test.wantErr:
   148  			if got, want := hi, test.wantHi; got != want {
   149  				t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want)
   150  			}
   151  			if got, want := lo, test.wantLo; got != want {
   152  				t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want)
   153  			}
   154  		}
   155  	}
   156  	for shift := 0; shift <= 3; shift++ {
   157  		for v := int32(0); v < 0xfff000+0xfff<<shift; v = v + 1<<shift {
   158  			hi, lo, err := splitImm24uScaled(v, shift)
   159  			if err != nil {
   160  				t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
   161  			}
   162  			if hi+lo<<shift != v {
   163  				t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
   164  			}
   165  		}
   166  	}
   167  }
   168  
   169  // TestLarge generates a very large file to verify that large
   170  // program builds successfully, in particular, too-far
   171  // conditional branches are fixed, and also verify that the
   172  // instruction's pc can be correctly aligned even when branches
   173  // need to be fixed.
   174  func TestLarge(t *testing.T) {
   175  	if testing.Short() {
   176  		t.Skip("Skip in short mode")
   177  	}
   178  	testenv.MustHaveGoBuild(t)
   179  
   180  	dir, err := os.MkdirTemp("", "testlarge")
   181  	if err != nil {
   182  		t.Fatalf("could not create directory: %v", err)
   183  	}
   184  	defer os.RemoveAll(dir)
   185  
   186  	// generate a very large function
   187  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   188  	gen(buf)
   189  
   190  	tmpfile := filepath.Join(dir, "x.s")
   191  	err = os.WriteFile(tmpfile, buf.Bytes(), 0644)
   192  	if err != nil {
   193  		t.Fatalf("can't write output: %v\n", err)
   194  	}
   195  
   196  	pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
   197  
   198  	// assemble generated file
   199  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
   200  	cmd.Env = append(os.Environ(), "GOOS=linux")
   201  	out, err := cmd.CombinedOutput()
   202  	if err != nil {
   203  		t.Errorf("Assemble failed: %v, output: %s", err, out)
   204  	}
   205  	matched, err := regexp.MatchString(pattern, string(out))
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	if !matched {
   210  		t.Errorf("The alignment is not correct: %t, output:%s\n", matched, out)
   211  	}
   212  
   213  	// build generated file
   214  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
   215  	cmd.Env = append(os.Environ(), "GOOS=linux")
   216  	out, err = cmd.CombinedOutput()
   217  	if err != nil {
   218  		t.Errorf("Build failed: %v, output: %s", err, out)
   219  	}
   220  }
   221  
   222  // gen generates a very large program, with a very far conditional branch.
   223  func gen(buf *bytes.Buffer) {
   224  	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
   225  	fmt.Fprintln(buf, "TBZ $5, R0, label")
   226  	fmt.Fprintln(buf, "CBZ R0, label")
   227  	fmt.Fprintln(buf, "BEQ label")
   228  	fmt.Fprintln(buf, "PCALIGN $128")
   229  	fmt.Fprintln(buf, "MOVD $3, R3")
   230  	for i := 0; i < 1<<19; i++ {
   231  		fmt.Fprintln(buf, "MOVD R0, R1")
   232  	}
   233  	fmt.Fprintln(buf, "label:")
   234  	fmt.Fprintln(buf, "RET")
   235  }
   236  
   237  // Issue 20348.
   238  func TestNoRet(t *testing.T) {
   239  	dir, err := os.MkdirTemp("", "testnoret")
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	defer os.RemoveAll(dir)
   244  	tmpfile := filepath.Join(dir, "x.s")
   245  	if err := os.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
   249  	cmd.Env = append(os.Environ(), "GOOS=linux")
   250  	if out, err := cmd.CombinedOutput(); err != nil {
   251  		t.Errorf("%v\n%s", err, out)
   252  	}
   253  }
   254  
   255  // TestPCALIGN verifies the correctness of the PCALIGN by checking if the
   256  // code can be aligned to the alignment value.
   257  func TestPCALIGN(t *testing.T) {
   258  	testenv.MustHaveGoBuild(t)
   259  	dir, err := os.MkdirTemp("", "testpcalign")
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	defer os.RemoveAll(dir)
   264  	tmpfile := filepath.Join(dir, "test.s")
   265  	tmpout := filepath.Join(dir, "test.o")
   266  
   267  	code1 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n")
   268  	code2 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n")
   269  	// If the output contains this pattern, the pc-offsite of "MOVD $1, R1" is 8 bytes aligned.
   270  	out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
   271  	// If the output contains this pattern, the pc-offsite of "MOVD $2, R2" is 16 bytes aligned.
   272  	out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
   273  	var testCases = []struct {
   274  		name string
   275  		code []byte
   276  		out  string
   277  	}{
   278  		{"8-byte alignment", code1, out1},
   279  		{"16-byte alignment", code2, out2},
   280  	}
   281  
   282  	for _, test := range testCases {
   283  		if err := os.WriteFile(tmpfile, test.code, 0644); err != nil {
   284  			t.Fatal(err)
   285  		}
   286  		cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
   287  		cmd.Env = append(os.Environ(), "GOOS=linux")
   288  		out, err := cmd.CombinedOutput()
   289  		if err != nil {
   290  			t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
   291  			continue
   292  		}
   293  
   294  		matched, err := regexp.MatchString(test.out, string(out))
   295  		if err != nil {
   296  			t.Fatal(err)
   297  		}
   298  		if !matched {
   299  			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
   300  		}
   301  	}
   302  }
   303  
   304  func testvmovs() (r1, r2 uint64)
   305  func testvmovd() (r1, r2 uint64)
   306  func testvmovq() (r1, r2 uint64)
   307  
   308  func TestVMOV(t *testing.T) {
   309  	tests := []struct {
   310  		op           string
   311  		vmovFunc     func() (uint64, uint64)
   312  		wantA, wantB uint64
   313  	}{
   314  		{"VMOVS", testvmovs, 0x80402010, 0},
   315  		{"VMOVD", testvmovd, 0x7040201008040201, 0},
   316  		{"VMOVQ", testvmovq, 0x7040201008040201, 0x3040201008040201},
   317  	}
   318  	for _, test := range tests {
   319  		gotA, gotB := test.vmovFunc()
   320  		if gotA != test.wantA || gotB != test.wantB {
   321  			t.Errorf("%v: got: a=0x%x, b=0x%x, want: a=0x%x, b=0x%x", test.op, gotA, gotB, test.wantA, test.wantB)
   322  		}
   323  	}
   324  }
   325  
   326  func testmovk() uint64
   327  
   328  // TestMOVK makes sure MOVK with a very large constant works. See issue 52261.
   329  func TestMOVK(t *testing.T) {
   330  	x := testmovk()
   331  	want := uint64(40000 << 48)
   332  	if x != want {
   333  		t.Errorf("Got %x want %x\n", x, want)
   334  	}
   335  }
   336  

View as plain text