github.com/euank/go@v0.0.0-20160829210321-495514729181/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_ssa.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  
    94  	w := new(bytes.Buffer)
    95  	fmt.Fprintf(w, "package main;\n")
    96  	fmt.Fprintf(w, "import \"fmt\"\n")
    97  
    98  	for _, sz := range []int{64, 32, 16, 8} {
    99  		fmt.Fprintf(w, "type utd%d struct {\n", sz)
   100  		fmt.Fprintf(w, "  a,b uint%d\n", sz)
   101  		fmt.Fprintf(w, "  add,sub,mul,div,mod uint%d\n", sz)
   102  		fmt.Fprintf(w, "}\n")
   103  
   104  		fmt.Fprintf(w, "type itd%d struct {\n", sz)
   105  		fmt.Fprintf(w, "  a,b int%d\n", sz)
   106  		fmt.Fprintf(w, "  add,sub,mul,div,mod int%d\n", sz)
   107  		fmt.Fprintf(w, "}\n")
   108  	}
   109  
   110  	// the function being tested
   111  	testFunc, err := template.New("testFunc").Parse(
   112  		`//go:noinline
   113  		func {{.Name}}_{{.Stype}}_ssa(a, b {{.Stype}}) {{.Stype}} {
   114  	return a {{.SymFirst}} b
   115  }
   116  `)
   117  	if err != nil {
   118  		panic(err)
   119  	}
   120  
   121  	// generate our functions to be tested
   122  	for _, s := range szs {
   123  		for _, o := range ops {
   124  			fd := tmplData{o.name, s.name, o.symbol}
   125  			err = testFunc.Execute(w, fd)
   126  			if err != nil {
   127  				panic(err)
   128  			}
   129  		}
   130  	}
   131  
   132  	// generate the test data
   133  	for _, s := range szs {
   134  		if len(s.u) > 0 {
   135  			fmt.Fprintf(w, "var %s_data []utd%s = []utd%s{", s.name, s.sn, s.sn)
   136  			for _, i := range s.u {
   137  				for _, j := range s.u {
   138  					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))
   139  					if j != 0 {
   140  						fmt.Fprintf(w, ", div: %d, mod: %d", ucast(i/j, s), ucast(i%j, s))
   141  					}
   142  					fmt.Fprint(w, "},\n")
   143  				}
   144  			}
   145  			fmt.Fprintf(w, "}\n")
   146  		} else {
   147  			// TODO: clean up this duplication
   148  			fmt.Fprintf(w, "var %s_data []itd%s = []itd%s{", s.name, s.sn, s.sn)
   149  			for _, i := range s.i {
   150  				for _, j := range s.i {
   151  					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))
   152  					if j != 0 {
   153  						fmt.Fprintf(w, ", div: %d, mod: %d", icast(i/j, s), icast(i%j, s))
   154  					}
   155  					fmt.Fprint(w, "},\n")
   156  				}
   157  			}
   158  			fmt.Fprintf(w, "}\n")
   159  		}
   160  	}
   161  
   162  	fmt.Fprintf(w, "var failed bool\n\n")
   163  	fmt.Fprintf(w, "func main() {\n\n")
   164  
   165  	verify, err := template.New("tst").Parse(
   166  		`if got := {{.Name}}_{{.Stype}}_ssa(v.a, v.b); got != v.{{.Name}} {
   167         fmt.Printf("{{.Name}}_{{.Stype}} %d{{.Symbol}}%d = %d, wanted %d\n",v.a,v.b,got,v.{{.Name}})
   168         failed = true
   169  }
   170  `)
   171  
   172  	for _, s := range szs {
   173  		fmt.Fprintf(w, "for _, v := range %s_data {\n", s.name)
   174  
   175  		for _, o := range ops {
   176  			// avoid generating tests that divide by zero
   177  			if o.name == "div" || o.name == "mod" {
   178  				fmt.Fprint(w, "if v.b != 0 {")
   179  			}
   180  
   181  			err = verify.Execute(w, tmplData{o.name, s.name, o.symbol})
   182  
   183  			if o.name == "div" || o.name == "mod" {
   184  				fmt.Fprint(w, "\n}\n")
   185  			}
   186  
   187  			if err != nil {
   188  				panic(err)
   189  			}
   190  
   191  		}
   192  		fmt.Fprint(w, "    }\n")
   193  	}
   194  
   195  	fmt.Fprintf(w, `if failed {
   196          panic("tests failed")
   197      }
   198  `)
   199  	fmt.Fprintf(w, "}\n")
   200  
   201  	// gofmt result
   202  	b := w.Bytes()
   203  	src, err := format.Source(b)
   204  	if err != nil {
   205  		fmt.Printf("%s\n", b)
   206  		panic(err)
   207  	}
   208  
   209  	// write to file
   210  	err = ioutil.WriteFile("../arithBoundary_ssa.go", src, 0666)
   211  	if err != nil {
   212  		log.Fatalf("can't write output: %v\n", err)
   213  	}
   214  }