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