github.com/stingnevermore/go@v0.0.0-20180120041312-3810f5bfed72/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go (about)

     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  }
    32  
    33  var szs = []szD{
    34  	{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0x8000000000000000, 0xffffFFFFffffFFFF}},
    35  	{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF,
    36  		-4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}},
    37  
    38  	{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}},
    39  	{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0,
    40  		1, 0x7FFFFFFF}},
    41  
    42  	{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}},
    43  	{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}},
    44  
    45  	{name: "uint8", sn: "8", u: []uint64{0, 1, 255}},
    46  	{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}},
    47  }
    48  
    49  var ops = []op{
    50  	{"add", "+"},
    51  	{"sub", "-"},
    52  	{"div", "/"},
    53  	{"mul", "*"},
    54  	{"lsh", "<<"},
    55  	{"rsh", ">>"},
    56  	{"mod", "%"},
    57  	{"and", "&"},
    58  	{"or", "|"},
    59  	{"xor", "^"},
    60  }
    61  
    62  // compute the result of i op j, cast as type t.
    63  func ansU(i, j uint64, t, op string) string {
    64  	var ans uint64
    65  	switch op {
    66  	case "+":
    67  		ans = i + j
    68  	case "-":
    69  		ans = i - j
    70  	case "*":
    71  		ans = i * j
    72  	case "/":
    73  		if j != 0 {
    74  			ans = i / j
    75  		}
    76  	case "%":
    77  		if j != 0 {
    78  			ans = i % j
    79  		}
    80  	case "<<":
    81  		ans = i << j
    82  	case ">>":
    83  		ans = i >> j
    84  	case "&":
    85  		ans = i & j
    86  	case "|":
    87  		ans = i | j
    88  	case "^":
    89  		ans = i ^ j
    90  	}
    91  	switch t {
    92  	case "uint32":
    93  		ans = uint64(uint32(ans))
    94  	case "uint16":
    95  		ans = uint64(uint16(ans))
    96  	case "uint8":
    97  		ans = uint64(uint8(ans))
    98  	}
    99  	return fmt.Sprintf("%d", ans)
   100  }
   101  
   102  // compute the result of i op j, cast as type t.
   103  func ansS(i, j int64, t, op string) string {
   104  	var ans int64
   105  	switch op {
   106  	case "+":
   107  		ans = i + j
   108  	case "-":
   109  		ans = i - j
   110  	case "*":
   111  		ans = i * j
   112  	case "/":
   113  		if j != 0 {
   114  			ans = i / j
   115  		}
   116  	case "%":
   117  		if j != 0 {
   118  			ans = i % j
   119  		}
   120  	case "<<":
   121  		ans = i << uint64(j)
   122  	case ">>":
   123  		ans = i >> uint64(j)
   124  	case "&":
   125  		ans = i & j
   126  	case "|":
   127  		ans = i | j
   128  	case "^":
   129  		ans = i ^ j
   130  	}
   131  	switch t {
   132  	case "int32":
   133  		ans = int64(int32(ans))
   134  	case "int16":
   135  		ans = int64(int16(ans))
   136  	case "int8":
   137  		ans = int64(int8(ans))
   138  	}
   139  	return fmt.Sprintf("%d", ans)
   140  }
   141  
   142  func main() {
   143  	w := new(bytes.Buffer)
   144  	fmt.Fprintf(w, "// run\n")
   145  	fmt.Fprintf(w, "// Code generated by gen/arithConstGen.go. DO NOT EDIT.\n\n")
   146  	fmt.Fprintf(w, "package main;\n")
   147  	fmt.Fprintf(w, "import \"fmt\"\n")
   148  
   149  	fncCnst1, err := template.New("fnc").Parse(
   150  		`//go:noinline
   151  		func {{.Name}}_{{.Type_}}_{{.FNumber}}_ssa(a {{.Type_}}) {{.Type_}} {
   152  	return a {{.Symbol}} {{.Number}}
   153  }
   154  `)
   155  	if err != nil {
   156  		panic(err)
   157  	}
   158  	fncCnst2, err := template.New("fnc").Parse(
   159  		`//go:noinline
   160  		func {{.Name}}_{{.FNumber}}_{{.Type_}}_ssa(a {{.Type_}}) {{.Type_}} {
   161  	return {{.Number}} {{.Symbol}} a
   162  }
   163  
   164  `)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  
   169  	type fncData struct {
   170  		Name, Type_, Symbol, FNumber, Number string
   171  	}
   172  
   173  	for _, s := range szs {
   174  		for _, o := range ops {
   175  			fd := fncData{o.name, s.name, o.symbol, "", ""}
   176  
   177  			// unsigned test cases
   178  			if len(s.u) > 0 {
   179  				for _, i := range s.u {
   180  					fd.Number = fmt.Sprintf("%d", i)
   181  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   182  
   183  					// avoid division by zero
   184  					if o.name != "mod" && o.name != "div" || i != 0 {
   185  						// introduce uint64 cast for rhs shift operands
   186  						// if they are too large for default uint type
   187  						number := fd.Number
   188  						if (o.name == "lsh" || o.name == "rsh") && uint64(uint32(i)) != i {
   189  							fd.Number = fmt.Sprintf("uint64(%s)", number)
   190  						}
   191  						fncCnst1.Execute(w, fd)
   192  						fd.Number = number
   193  					}
   194  
   195  					fncCnst2.Execute(w, fd)
   196  				}
   197  			}
   198  
   199  			// signed test cases
   200  			if len(s.i) > 0 {
   201  				// don't generate tests for shifts by signed integers
   202  				if o.name == "lsh" || o.name == "rsh" {
   203  					continue
   204  				}
   205  				for _, i := range s.i {
   206  					fd.Number = fmt.Sprintf("%d", i)
   207  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   208  
   209  					// avoid division by zero
   210  					if o.name != "mod" && o.name != "div" || i != 0 {
   211  						fncCnst1.Execute(w, fd)
   212  					}
   213  					fncCnst2.Execute(w, fd)
   214  				}
   215  			}
   216  		}
   217  	}
   218  
   219  	fmt.Fprintf(w, "var failed bool\n\n")
   220  	fmt.Fprintf(w, "func main() {\n\n")
   221  
   222  	vrf1, _ := template.New("vrf1").Parse(`
   223    if got := {{.Name}}_{{.FNumber}}_{{.Type_}}_ssa({{.Input}}); got != {{.Ans}} {
   224    	fmt.Printf("{{.Name}}_{{.Type_}} {{.Number}}%s{{.Input}} = %d, wanted {{.Ans}}\n", ` + "`{{.Symbol}}`" + `, got)
   225    	failed = true
   226    }
   227  `)
   228  
   229  	vrf2, _ := template.New("vrf2").Parse(`
   230    if got := {{.Name}}_{{.Type_}}_{{.FNumber}}_ssa({{.Input}}); got != {{.Ans}} {
   231      fmt.Printf("{{.Name}}_{{.Type_}} {{.Input}}%s{{.Number}} = %d, wanted {{.Ans}}\n", ` + "`{{.Symbol}}`" + `, got)
   232      failed = true
   233    }
   234  `)
   235  
   236  	type cfncData struct {
   237  		Name, Type_, Symbol, FNumber, Number string
   238  		Ans, Input                           string
   239  	}
   240  	for _, s := range szs {
   241  		if len(s.u) > 0 {
   242  			for _, o := range ops {
   243  				fd := cfncData{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  							err = vrf1.Execute(w, fd)
   255  							if err != nil {
   256  								panic(err)
   257  							}
   258  						}
   259  
   260  						if o.name != "mod" && o.name != "div" || i != 0 {
   261  							fd.Ans = ansU(j, i, s.name, o.symbol)
   262  							fd.Input = fmt.Sprintf("%d", j)
   263  							err = vrf2.Execute(w, fd)
   264  							if err != nil {
   265  								panic(err)
   266  							}
   267  						}
   268  
   269  					}
   270  				}
   271  
   272  			}
   273  		}
   274  
   275  		// signed
   276  		if len(s.i) > 0 {
   277  			for _, o := range ops {
   278  				// don't generate tests for shifts by signed integers
   279  				if o.name == "lsh" || o.name == "rsh" {
   280  					continue
   281  				}
   282  				fd := cfncData{o.name, s.name, o.symbol, "", "", "", ""}
   283  				for _, i := range s.i {
   284  					fd.Number = fmt.Sprintf("%d", i)
   285  					fd.FNumber = strings.Replace(fd.Number, "-", "Neg", -1)
   286  					for _, j := range s.i {
   287  						if o.name != "mod" && o.name != "div" || j != 0 {
   288  							fd.Ans = ansS(i, j, s.name, o.symbol)
   289  							fd.Input = fmt.Sprintf("%d", j)
   290  							err = vrf1.Execute(w, fd)
   291  							if 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  							err = vrf2.Execute(w, fd)
   300  							if err != nil {
   301  								panic(err)
   302  							}
   303  						}
   304  
   305  					}
   306  				}
   307  
   308  			}
   309  		}
   310  	}
   311  
   312  	fmt.Fprintf(w, `if failed {
   313          panic("tests failed")
   314      }
   315  `)
   316  	fmt.Fprintf(w, "}\n")
   317  
   318  	// gofmt result
   319  	b := w.Bytes()
   320  	src, err := format.Source(b)
   321  	if err != nil {
   322  		fmt.Printf("%s\n", b)
   323  		panic(err)
   324  	}
   325  
   326  	// write to file
   327  	err = ioutil.WriteFile("../arithConst.go", src, 0666)
   328  	if err != nil {
   329  		log.Fatalf("can't write output: %v\n", err)
   330  	}
   331  }