Source file src/cmd/compile/internal/test/testdata/gen/arithConstGen.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  // This program generates a test to verify that the standard arithmetic
     6  // operators properly handle const cases. The test file should be
     7  // generated with a known working version of go.
     8  // launch with `go run arithConstGen.go` a file called arithConst.go
     9  // will be written into the parent directory containing the tests
    10  
    11  package main
    12  
    13  import (
    14  	"bytes"
    15  	"fmt"
    16  	"go/format"
    17  	"io/ioutil"
    18  	"log"
    19  	"strings"
    20  	"text/template"
    21  )
    22  
    23  type op struct {
    24  	name, symbol string
    25  }
    26  type szD struct {
    27  	name   string
    28  	sn     string
    29  	u      []uint64
    30  	i      []int64
    31  	oponly string
    32  }
    33  
    34  var szs = []szD{
    35  	{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0x8000000000000000, 0xffffFFFFffffFFFF}},
    36  	{name: "uint64", sn: "64", u: []uint64{3, 5, 7, 9, 10, 11, 13, 19, 21, 25, 27, 37, 41, 45, 73, 81}, oponly: "mul"},
    37  
    38  	{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF,
    39  		-4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}},
    40  	{name: "int64", sn: "64", i: []int64{-9, -5, -3, 3, 5, 7, 9, 10, 11, 13, 19, 21, 25, 27, 37, 41, 45, 73, 81}, oponly: "mul"},
    41  
    42  	{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}},
    43  	{name: "uint32", sn: "32", u: []uint64{3, 5, 7, 9, 10, 11, 13, 19, 21, 25, 27, 37, 41, 45, 73, 81}, oponly: "mul"},
    44  
    45  	{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0,
    46  		1, 0x7FFFFFFF}},
    47  	{name: "int32", sn: "32", i: []int64{-9, -5, -3, 3, 5, 7, 9, 10, 11, 13, 19, 21, 25, 27, 37, 41, 45, 73, 81}, oponly: "mul"},
    48  
    49  	{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}},
    50  	{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}},
    51  
    52  	{name: "uint8", sn: "8", u: []uint64{0, 1, 255}},
    53  	{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}},
    54  }
    55  
    56  var ops = []op{
    57  	{"add", "+"},
    58  	{"sub", "-"},
    59  	{"div", "/"},
    60  	{"mul", "*"},
    61  	{"lsh", "<<"},
    62  	{"rsh", ">>"},
    63  	{"mod", "%"},
    64  	{"and", "&"},
    65  	{"or", "|"},
    66  	{"xor", "^"},
    67  }
    68  
    69  // compute the result of i op j, cast as type t.
    70  func ansU(i, j uint64, t, op string) string {
    71  	var ans uint64
    72  	switch op {
    73  	case "+":
    74  		ans = i + j
    75  	case "-":
    76  		ans = i - j
    77  	case "*":
    78  		ans = i * j
    79  	case "/":
    80  		if j != 0 {
    81  			ans = i / j
    82  		}
    83  	case "%":
    84  		if j != 0 {
    85  			ans = i % j
    86  		}
    87  	case "<<":
    88  		ans = i << j
    89  	case ">>":
    90  		ans = i >> j
    91  	case "&":
    92  		ans = i & j
    93  	case "|":
    94  		ans = i | j
    95  	case "^":
    96  		ans = i ^ j
    97  	}
    98  	switch t {
    99  	case "uint32":
   100  		ans = uint64(uint32(ans))
   101  	case "uint16":
   102  		ans = uint64(uint16(ans))
   103  	case "uint8":
   104  		ans = uint64(uint8(ans))
   105  	}
   106  	return fmt.Sprintf("%d", ans)
   107  }
   108  
   109  // compute the result of i op j, cast as type t.
   110  func ansS(i, j int64, t, op string) string {
   111  	var ans int64
   112  	switch op {
   113  	case "+":
   114  		ans = i + j
   115  	case "-":
   116  		ans = i - j
   117  	case "*":
   118  		ans = i * j
   119  	case "/":
   120  		if j != 0 {
   121  			ans = i / j
   122  		}
   123  	case "%":
   124  		if j != 0 {
   125  			ans = i % j
   126  		}
   127  	case "<<":
   128  		ans = i << uint64(j)
   129  	case ">>":
   130  		ans = i >> uint64(j)
   131  	case "&":
   132  		ans = i & j
   133  	case "|":
   134  		ans = i | j
   135  	case "^":
   136  		ans = i ^ j
   137  	}
   138  	switch t {
   139  	case "int32":
   140  		ans = int64(int32(ans))
   141  	case "int16":
   142  		ans = int64(int16(ans))
   143  	case "int8":
   144  		ans = int64(int8(ans))
   145  	}
   146  	return fmt.Sprintf("%d", ans)
   147  }
   148  
   149  func main() {
   150  	w := new(bytes.Buffer)
   151  	fmt.Fprintf(w, "// Code generated by gen/arithConstGen.go. DO NOT EDIT.\n\n")
   152  	fmt.Fprintf(w, "package main;\n")
   153  	fmt.Fprintf(w, "import \"testing\"\n")
   154  
   155  	fncCnst1 := template.Must(template.New("fnc").Parse(
   156  		`//go:noinline
   157  func {{.Name}}_{{.Type_}}_{{.FNumber}}(a {{.Type_}}) {{.Type_}} { return a {{.Symbol}} {{.Number}} }
   158  `))
   159  	fncCnst2 := template.Must(template.New("fnc").Parse(
   160  		`//go:noinline
   161  func {{.Name}}_{{.FNumber}}_{{.Type_}}(a {{.Type_}}) {{.Type_}} { return {{.Number}} {{.Symbol}} a }
   162  `))
   163  
   164  	type fncData struct {
   165  		Name, Type_, Symbol, FNumber, Number string
   166  	}
   167  
   168  	for _, s := range szs {
   169  		for _, o := range ops {
   170  			if s.oponly != "" && s.oponly != o.name {
   171  				continue
   172  			}
   173  			fd := fncData{o.name, s.name, o.symbol, "", ""}
   174  
   175  			// unsigned test cases
   176  			if len(s.u) > 0 {
   177  				for _, i := range s.u {
   178  					fd.Number = fmt.Sprintf("%d", i)
   179  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   180  
   181  					// avoid division by zero
   182  					if o.name != "mod" && o.name != "div" || i != 0 {
   183  						// introduce uint64 cast for rhs shift operands
   184  						// if they are too large for default uint type
   185  						number := fd.Number
   186  						if (o.name == "lsh" || o.name == "rsh") && uint64(uint32(i)) != i {
   187  							fd.Number = fmt.Sprintf("uint64(%s)", number)
   188  						}
   189  						fncCnst1.Execute(w, fd)
   190  						fd.Number = number
   191  					}
   192  
   193  					fncCnst2.Execute(w, fd)
   194  				}
   195  			}
   196  
   197  			// signed test cases
   198  			if len(s.i) > 0 {
   199  				// don't generate tests for shifts by signed integers
   200  				if o.name == "lsh" || o.name == "rsh" {
   201  					continue
   202  				}
   203  				for _, i := range s.i {
   204  					fd.Number = fmt.Sprintf("%d", i)
   205  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   206  
   207  					// avoid division by zero
   208  					if o.name != "mod" && o.name != "div" || i != 0 {
   209  						fncCnst1.Execute(w, fd)
   210  					}
   211  					fncCnst2.Execute(w, fd)
   212  				}
   213  			}
   214  		}
   215  	}
   216  
   217  	vrf1 := template.Must(template.New("vrf1").Parse(`
   218  		test_{{.Size}}{fn: {{.Name}}_{{.FNumber}}_{{.Type_}}, fnname: "{{.Name}}_{{.FNumber}}_{{.Type_}}", in: {{.Input}}, want: {{.Ans}}},`))
   219  
   220  	vrf2 := template.Must(template.New("vrf2").Parse(`
   221  		test_{{.Size}}{fn: {{.Name}}_{{.Type_}}_{{.FNumber}}, fnname: "{{.Name}}_{{.Type_}}_{{.FNumber}}", in: {{.Input}}, want: {{.Ans}}},`))
   222  
   223  	type cfncData struct {
   224  		Size, Name, Type_, Symbol, FNumber, Number string
   225  		Ans, Input                                 string
   226  	}
   227  	for _, s := range szs {
   228  		fmt.Fprintf(w, `
   229  type test_%[1]s%[2]s struct {
   230  	fn func (%[1]s) %[1]s
   231  	fnname string
   232  	in %[1]s
   233  	want %[1]s
   234  }
   235  `, s.name, s.oponly)
   236  		fmt.Fprintf(w, "var tests_%[1]s%[2]s =[]test_%[1]s {\n\n", s.name, s.oponly)
   237  
   238  		if len(s.u) > 0 {
   239  			for _, o := range ops {
   240  				if s.oponly != "" && s.oponly != o.name {
   241  					continue
   242  				}
   243  				fd := cfncData{s.name, o.name, s.name, o.symbol, "", "", "", ""}
   244  				for _, i := range s.u {
   245  					fd.Number = fmt.Sprintf("%d", i)
   246  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   247  
   248  					// unsigned
   249  					for _, j := range s.u {
   250  
   251  						if o.name != "mod" && o.name != "div" || j != 0 {
   252  							fd.Ans = ansU(i, j, s.name, o.symbol)
   253  							fd.Input = fmt.Sprintf("%d", j)
   254  							if err := vrf1.Execute(w, fd); err != nil {
   255  								panic(err)
   256  							}
   257  						}
   258  
   259  						if o.name != "mod" && o.name != "div" || i != 0 {
   260  							fd.Ans = ansU(j, i, s.name, o.symbol)
   261  							fd.Input = fmt.Sprintf("%d", j)
   262  							if err := vrf2.Execute(w, fd); err != nil {
   263  								panic(err)
   264  							}
   265  						}
   266  
   267  					}
   268  				}
   269  
   270  			}
   271  		}
   272  
   273  		// signed
   274  		if len(s.i) > 0 {
   275  			for _, o := range ops {
   276  				if s.oponly != "" && s.oponly != o.name {
   277  					continue
   278  				}
   279  				// don't generate tests for shifts by signed integers
   280  				if o.name == "lsh" || o.name == "rsh" {
   281  					continue
   282  				}
   283  				fd := cfncData{s.name, o.name, s.name, o.symbol, "", "", "", ""}
   284  				for _, i := range s.i {
   285  					fd.Number = fmt.Sprintf("%d", i)
   286  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   287  					for _, j := range s.i {
   288  						if o.name != "mod" && o.name != "div" || j != 0 {
   289  							fd.Ans = ansS(i, j, s.name, o.symbol)
   290  							fd.Input = fmt.Sprintf("%d", j)
   291  							if err := vrf1.Execute(w, fd); err != nil {
   292  								panic(err)
   293  							}
   294  						}
   295  
   296  						if o.name != "mod" && o.name != "div" || i != 0 {
   297  							fd.Ans = ansS(j, i, s.name, o.symbol)
   298  							fd.Input = fmt.Sprintf("%d", j)
   299  							if err := vrf2.Execute(w, fd); err != nil {
   300  								panic(err)
   301  							}
   302  						}
   303  
   304  					}
   305  				}
   306  
   307  			}
   308  		}
   309  
   310  		fmt.Fprintf(w, "}\n\n")
   311  	}
   312  
   313  	fmt.Fprint(w, `
   314  
   315  // TestArithmeticConst tests results for arithmetic operations against constants.
   316  func TestArithmeticConst(t *testing.T) {
   317  `)
   318  
   319  	for _, s := range szs {
   320  		fmt.Fprintf(w, `for _, test := range tests_%s%s {`, s.name, s.oponly)
   321  		// Use WriteString here to avoid a vet warning about formatting directives.
   322  		w.WriteString(`if got := test.fn(test.in); got != test.want {
   323  			t.Errorf("%s(%d) = %d, want %d\n", test.fnname, test.in, got, test.want)
   324  		}
   325  	}
   326  `)
   327  	}
   328  
   329  	fmt.Fprint(w, `
   330  }
   331  `)
   332  
   333  	// gofmt result
   334  	b := w.Bytes()
   335  	src, err := format.Source(b)
   336  	if err != nil {
   337  		fmt.Printf("%s\n", b)
   338  		panic(err)
   339  	}
   340  
   341  	// write to file
   342  	err = ioutil.WriteFile("../arithConst_test.go", src, 0666)
   343  	if err != nil {
   344  		log.Fatalf("can't write output: %v\n", err)
   345  	}
   346  }
   347  

View as plain text