Source file src/cmd/internal/obj/arm64/asm_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 runAssembler(t *testing.T, srcdata string) []byte {
    18  	dir := t.TempDir()
    19  	defer os.RemoveAll(dir)
    20  	srcfile := filepath.Join(dir, "testdata.s")
    21  	outfile := filepath.Join(dir, "testdata.o")
    22  	os.WriteFile(srcfile, []byte(srcdata), 0644)
    23  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", outfile, srcfile)
    24  	cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=arm64")
    25  	out, err := cmd.CombinedOutput()
    26  	if err != nil {
    27  		t.Errorf("The build failed: %v, output:\n%s", err, out)
    28  	}
    29  	return out
    30  }
    31  
    32  func TestSplitImm24uScaled(t *testing.T) {
    33  	tests := []struct {
    34  		v       int32
    35  		shift   int
    36  		wantErr bool
    37  		wantHi  int32
    38  		wantLo  int32
    39  	}{
    40  		{
    41  			v:      0,
    42  			shift:  0,
    43  			wantHi: 0,
    44  			wantLo: 0,
    45  		},
    46  		{
    47  			v:      0x1001,
    48  			shift:  0,
    49  			wantHi: 0x1000,
    50  			wantLo: 0x1,
    51  		},
    52  		{
    53  			v:      0xffffff,
    54  			shift:  0,
    55  			wantHi: 0xfff000,
    56  			wantLo: 0xfff,
    57  		},
    58  		{
    59  			v:       0xffffff,
    60  			shift:   1,
    61  			wantErr: true,
    62  		},
    63  		{
    64  			v:      0xfe,
    65  			shift:  1,
    66  			wantHi: 0x0,
    67  			wantLo: 0x7f,
    68  		},
    69  		{
    70  			v:      0x10fe,
    71  			shift:  1,
    72  			wantHi: 0x0,
    73  			wantLo: 0x87f,
    74  		},
    75  		{
    76  			v:      0x2002,
    77  			shift:  1,
    78  			wantHi: 0x2000,
    79  			wantLo: 0x1,
    80  		},
    81  		{
    82  			v:      0xfffffe,
    83  			shift:  1,
    84  			wantHi: 0xffe000,
    85  			wantLo: 0xfff,
    86  		},
    87  		{
    88  			v:      0x1000ffe,
    89  			shift:  1,
    90  			wantHi: 0xfff000,
    91  			wantLo: 0xfff,
    92  		},
    93  		{
    94  			v:       0x1001000,
    95  			shift:   1,
    96  			wantErr: true,
    97  		},
    98  		{
    99  			v:       0xfffffe,
   100  			shift:   2,
   101  			wantErr: true,
   102  		},
   103  		{
   104  			v:      0x4004,
   105  			shift:  2,
   106  			wantHi: 0x4000,
   107  			wantLo: 0x1,
   108  		},
   109  		{
   110  			v:      0xfffffc,
   111  			shift:  2,
   112  			wantHi: 0xffc000,
   113  			wantLo: 0xfff,
   114  		},
   115  		{
   116  			v:      0x1002ffc,
   117  			shift:  2,
   118  			wantHi: 0xfff000,
   119  			wantLo: 0xfff,
   120  		},
   121  		{
   122  			v:       0x1003000,
   123  			shift:   2,
   124  			wantErr: true,
   125  		},
   126  		{
   127  			v:       0xfffffe,
   128  			shift:   3,
   129  			wantErr: true,
   130  		},
   131  		{
   132  			v:      0x8008,
   133  			shift:  3,
   134  			wantHi: 0x8000,
   135  			wantLo: 0x1,
   136  		},
   137  		{
   138  			v:      0xfffff8,
   139  			shift:  3,
   140  			wantHi: 0xff8000,
   141  			wantLo: 0xfff,
   142  		},
   143  		{
   144  			v:      0x1006ff8,
   145  			shift:  3,
   146  			wantHi: 0xfff000,
   147  			wantLo: 0xfff,
   148  		},
   149  		{
   150  			v:       0x1007000,
   151  			shift:   3,
   152  			wantErr: true,
   153  		},
   154  	}
   155  	for _, test := range tests {
   156  		hi, lo, err := splitImm24uScaled(test.v, test.shift)
   157  		switch {
   158  		case err == nil && test.wantErr:
   159  			t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift)
   160  		case err != nil && !test.wantErr:
   161  			t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err)
   162  		case !test.wantErr:
   163  			if got, want := hi, test.wantHi; got != want {
   164  				t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want)
   165  			}
   166  			if got, want := lo, test.wantLo; got != want {
   167  				t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want)
   168  			}
   169  		}
   170  	}
   171  	for shift := 0; shift <= 3; shift++ {
   172  		for v := int32(0); v < 0xfff000+0xfff<<shift; v = v + 1<<shift {
   173  			hi, lo, err := splitImm24uScaled(v, shift)
   174  			if err != nil {
   175  				t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
   176  			}
   177  			if hi+lo<<shift != v {
   178  				t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
   179  			}
   180  		}
   181  	}
   182  }
   183  
   184  // TestLarge generates a very large file to verify that large
   185  // program builds successfully, in particular, too-far
   186  // conditional branches are fixed, and also verify that the
   187  // instruction's pc can be correctly aligned even when branches
   188  // need to be fixed.
   189  func TestLarge(t *testing.T) {
   190  	if testing.Short() {
   191  		t.Skip("Skip in short mode")
   192  	}
   193  	testenv.MustHaveGoBuild(t)
   194  
   195  	// generate a very large function
   196  	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   197  	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
   198  	fmt.Fprintln(buf, "TBZ $5, R0, label")
   199  	fmt.Fprintln(buf, "CBZ R0, label")
   200  	fmt.Fprintln(buf, "BEQ label")
   201  	fmt.Fprintln(buf, "PCALIGN $128")
   202  	fmt.Fprintln(buf, "MOVD $3, R3")
   203  	for i := 0; i < 1<<19; i++ {
   204  		fmt.Fprintln(buf, "MOVD R0, R1")
   205  	}
   206  	fmt.Fprintln(buf, "label:")
   207  	fmt.Fprintln(buf, "RET")
   208  
   209  	// assemble generated file
   210  	out := runAssembler(t, buf.String())
   211  
   212  	pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
   213  	matched, err := regexp.MatchString(pattern, string(out))
   214  
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	if !matched {
   219  		t.Errorf("The alignment is not correct: %t\n", matched)
   220  	}
   221  }
   222  
   223  // Issue 20348.
   224  func TestNoRet(t *testing.T) {
   225  	runAssembler(t, "TEXT ·stub(SB),$0-0\nNOP\n")
   226  }
   227  
   228  // TestPCALIGN verifies the correctness of the PCALIGN by checking if the
   229  // code can be aligned to the alignment value.
   230  func TestPCALIGN(t *testing.T) {
   231  	testenv.MustHaveGoBuild(t)
   232  
   233  	code1 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n"
   234  	code2 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n"
   235  	// If the output contains this pattern, the pc-offset of "MOVD $1, R1" is 8 bytes aligned.
   236  	out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
   237  	// If the output contains this pattern, the pc-offset of "MOVD $2, R2" is 16 bytes aligned.
   238  	out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
   239  	var testCases = []struct {
   240  		name string
   241  		code string
   242  		out  string
   243  	}{
   244  		{"8-byte alignment", code1, out1},
   245  		{"16-byte alignment", code2, out2},
   246  	}
   247  
   248  	for _, test := range testCases {
   249  		out := runAssembler(t, test.code)
   250  		matched, err := regexp.MatchString(test.out, string(out))
   251  		if err != nil {
   252  			t.Fatal(err)
   253  		}
   254  		if !matched {
   255  			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
   256  		}
   257  	}
   258  }
   259  

View as plain text