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