github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/test/testdata/gen/arithBoundaryGen.go (about)

     1  // Copyright 2015 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 some special cases. The test file should be
     7  // generated with a known working version of go.
     8  // launch with `go run arithBoundaryGen.go` a file called arithBoundary.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  	"log"
    18  	"text/template"
    19  )
    20  
    21  // used for interpolation in a text template
    22  type tmplData struct {
    23  	Name, Stype, Symbol string
    24  }
    25  
    26  // used to work around an issue with the mod symbol being
    27  // interpreted as part of a format string
    28  func (s tmplData) SymFirst() string {
    29  	return string(s.Symbol[0])
    30  }
    31  
    32  // ucast casts an unsigned int to the size in s
    33  func ucast(i uint64, s sizedTestData) uint64 {
    34  	switch s.name {
    35  	case "uint32":
    36  		return uint64(uint32(i))
    37  	case "uint16":
    38  		return uint64(uint16(i))
    39  	case "uint8":
    40  		return uint64(uint8(i))
    41  	}
    42  	return i
    43  }
    44  
    45  // icast casts a signed int to the size in s
    46  func icast(i int64, s sizedTestData) int64 {
    47  	switch s.name {
    48  	case "int32":
    49  		return int64(int32(i))
    50  	case "int16":
    51  		return int64(int16(i))
    52  	case "int8":
    53  		return int64(int8(i))
    54  	}
    55  	return i
    56  }
    57  
    58  type sizedTestData struct {
    59  	name string
    60  	sn   string
    61  	u    []uint64
    62  	i    []int64
    63  }
    64  
    65  // values to generate tests. these should include the smallest and largest values, along
    66  // with any other values that might cause issues. we generate n^2 tests for each size to
    67  // cover all cases.
    68  var szs = []sizedTestData{
    69  	sizedTestData{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0xffffFFFFffffFFFF}},
    70  	sizedTestData{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF,
    71  		-4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}},
    72  
    73  	sizedTestData{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}},
    74  	sizedTestData{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0,
    75  		1, 0x7FFFFFFF}},
    76  
    77  	sizedTestData{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}},
    78  	sizedTestData{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}},
    79  
    80  	sizedTestData{name: "uint8", sn: "8", u: []uint64{0, 1, 255}},
    81  	sizedTestData{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}},
    82  }
    83  
    84  type op struct {
    85  	name, symbol string
    86  }
    87  
    88  // ops that we will be generating tests for
    89  var ops = []op{op{"add", "+"}, op{"sub", "-"}, op{"div", "/"}, op{"mod", "%%"}, op{"mul", "*"}}
    90  
    91  func main() {
    92  	w := new(bytes.Buffer)
    93  	fmt.Fprintf(w, "// Code generated by gen/arithBoundaryGen.go. DO NOT EDIT.\n\n")
    94  	fmt.Fprintf(w, "package main;\n")
    95  	fmt.Fprintf(w, "import \"testing\"\n")
    96  
    97  	for _, sz := range []int{64, 32, 16, 8} {
    98  		fmt.Fprintf(w, "type utd%d struct {\n", sz)
    99  		fmt.Fprintf(w, "  a,b uint%d\n", sz)
   100  		fmt.Fprintf(w, "  add,sub,mul,div,mod uint%d\n", sz)
   101  		fmt.Fprintf(w, "}\n")
   102  
   103  		fmt.Fprintf(w, "type itd%d struct {\n", sz)
   104  		fmt.Fprintf(w, "  a,b int%d\n", sz)
   105  		fmt.Fprintf(w, "  add,sub,mul,div,mod int%d\n", sz)
   106  		fmt.Fprintf(w, "}\n")
   107  	}
   108  
   109  	// the function being tested
   110  	testFunc, err := template.New("testFunc").Parse(
   111  		`//go:noinline
   112  		func {{.Name}}_{{.Stype}}_ssa(a, b {{.Stype}}) {{.Stype}} {
   113  	return a {{.SymFirst}} b
   114  }
   115  `)
   116  	if err != nil {
   117  		panic(err)
   118  	}
   119  
   120  	// generate our functions to be tested
   121  	for _, s := range szs {
   122  		for _, o := range ops {
   123  			fd := tmplData{o.name, s.name, o.symbol}
   124  			err = testFunc.Execute(w, fd)
   125  			if err != nil {
   126  				panic(err)
   127  			}
   128  		}
   129  	}
   130  
   131  	// generate the test data
   132  	for _, s := range szs {
   133  		if len(s.u) > 0 {
   134  			fmt.Fprintf(w, "var %s_data []utd%s = []utd%s{", s.name, s.sn, s.sn)
   135  			for _, i := range s.u {
   136  				for _, j := range s.u {
   137  					fmt.Fprintf(w, "utd%s{a: %d, b: %d, add: %d, sub: %d, mul: %d", s.sn, i, j, ucast(i+j, s), ucast(i-j, s), ucast(i*j, s))
   138  					if j != 0 {
   139  						fmt.Fprintf(w, ", div: %d, mod: %d", ucast(i/j, s), ucast(i%j, s))
   140  					}
   141  					fmt.Fprint(w, "},\n")
   142  				}
   143  			}
   144  			fmt.Fprintf(w, "}\n")
   145  		} else {
   146  			// TODO: clean up this duplication
   147  			fmt.Fprintf(w, "var %s_data []itd%s = []itd%s{", s.name, s.sn, s.sn)
   148  			for _, i := range s.i {
   149  				for _, j := range s.i {
   150  					fmt.Fprintf(w, "itd%s{a: %d, b: %d, add: %d, sub: %d, mul: %d", s.sn, i, j, icast(i+j, s), icast(i-j, s), icast(i*j, s))
   151  					if j != 0 {
   152  						fmt.Fprintf(w, ", div: %d, mod: %d", icast(i/j, s), icast(i%j, s))
   153  					}
   154  					fmt.Fprint(w, "},\n")
   155  				}
   156  			}
   157  			fmt.Fprintf(w, "}\n")
   158  		}
   159  	}
   160  
   161  	fmt.Fprintf(w, "//TestArithmeticBoundary tests boundary results for arithmetic operations.\n")
   162  	fmt.Fprintf(w, "func TestArithmeticBoundary(t *testing.T) {\n\n")
   163  
   164  	verify, err := template.New("tst").Parse(
   165  		`if got := {{.Name}}_{{.Stype}}_ssa(v.a, v.b); got != v.{{.Name}} {
   166         t.Errorf("{{.Name}}_{{.Stype}} %d{{.Symbol}}%d = %d, wanted %d\n",v.a,v.b,got,v.{{.Name}})
   167  }
   168  `)
   169  
   170  	for _, s := range szs {
   171  		fmt.Fprintf(w, "for _, v := range %s_data {\n", s.name)
   172  
   173  		for _, o := range ops {
   174  			// avoid generating tests that divide by zero
   175  			if o.name == "div" || o.name == "mod" {
   176  				fmt.Fprint(w, "if v.b != 0 {")
   177  			}
   178  
   179  			err = verify.Execute(w, tmplData{o.name, s.name, o.symbol})
   180  
   181  			if o.name == "div" || o.name == "mod" {
   182  				fmt.Fprint(w, "\n}\n")
   183  			}
   184  
   185  			if err != nil {
   186  				panic(err)
   187  			}
   188  
   189  		}
   190  		fmt.Fprint(w, "    }\n")
   191  	}
   192  
   193  	fmt.Fprintf(w, "}\n")
   194  
   195  	// gofmt result
   196  	b := w.Bytes()
   197  	src, err := format.Source(b)
   198  	if err != nil {
   199  		fmt.Printf("%s\n", b)
   200  		panic(err)
   201  	}
   202  
   203  	// write to file
   204  	err = os.WriteFile("../arithBoundary_test.go", src, 0666)
   205  	if err != nil {
   206  		log.Fatalf("can't write output: %v\n", err)
   207  	}
   208  }