Source file test/codegen/switch.go

     1  // asmcheck
     2  
     3  // Copyright 2019 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // These tests check code generation of switch statements.
     8  
     9  package codegen
    10  
    11  // see issue 33934
    12  func f(x string) int {
    13  	// amd64:-`cmpstring`
    14  	switch x {
    15  	case "":
    16  		return -1
    17  	case "1", "2", "3":
    18  		return -2
    19  	default:
    20  		return -3
    21  	}
    22  }
    23  
    24  // use jump tables for 8+ string cases
    25  // Using multiple return values prevent lookup tables.
    26  func squareJump(x int) (int, int) {
    27  	// amd64:`JMP \(.*\)\(.*\)$`
    28  	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
    29  	// loong64: `ALSLV` `MOVV` `JMP`
    30  	switch x {
    31  	case 1:
    32  		return 1, 1
    33  	case 2:
    34  		return 4, 2
    35  	case 3:
    36  		return 9, 3
    37  	case 4:
    38  		return 16, 4
    39  	case 5:
    40  		return 25, 5
    41  	case 6:
    42  		return 36, 6
    43  	case 7:
    44  		return 49, 7
    45  	case 8:
    46  		return 64, 8
    47  	default:
    48  		return x * x, x
    49  	}
    50  }
    51  
    52  // use lookup tables for 8+ int cases returning constants
    53  func squareLookup(x int) int {
    54  	// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
    55  	// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
    56  	// loong64:`SLLV` `MOVV \(R.*\)\(R.*\)` -`ALSLV`
    57  	switch x {
    58  	case 1:
    59  		return 1
    60  	case 2:
    61  		return 4
    62  	case 3:
    63  		return 9
    64  	case 4:
    65  		return 16
    66  	case 5:
    67  		return 25
    68  	case 6:
    69  		return 36
    70  	case 7:
    71  		return 49
    72  	case 8:
    73  		return 64
    74  	default:
    75  		return x * x
    76  	}
    77  }
    78  
    79  // lookup tables work even when some cases use fallthrough,
    80  // as long as enough non-fallthrough cases return constants.
    81  func fallthroughLookup(x int) int {
    82  	// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
    83  	// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
    84  	switch x {
    85  	case 1:
    86  		return 1
    87  	case 2:
    88  		return 2
    89  	case 3:
    90  		fallthrough
    91  	case 4:
    92  		return 40
    93  	case 5:
    94  		return 5
    95  	case 6:
    96  		return 6
    97  	case 7:
    98  		return 7
    99  	case 8:
   100  		return 8
   101  	case 9:
   102  		fallthrough
   103  	default:
   104  		return x * x
   105  	}
   106  }
   107  
   108  // use lookup tables for 8+ bool-returning cases
   109  func boolLookup(x int) bool {
   110  	// amd64:`LEAQ .*\(SB\)` `MOVBLZX .*\(.*\)` -`JMP \(.*\)\(.*\)$`
   111  	// arm64:`MOVBU \(R.*\)\(R.*\)` -`JMP \(R.*\)$`
   112  	// loong64:`MOVBU \(R.*\)\(R.*\)` -`ALSLV`
   113  	switch x {
   114  	case 1:
   115  		return true
   116  	case 2:
   117  		return false
   118  	case 3:
   119  		return true
   120  	case 4:
   121  		return false
   122  	case 5:
   123  		return true
   124  	case 6:
   125  		return false
   126  	case 7:
   127  		return true
   128  	case 8:
   129  		return false
   130  	default:
   131  		return x > 0
   132  	}
   133  }
   134  
   135  // use lookup tables for 8+ float64-returning cases
   136  func floatLookup(x int) float64 {
   137  	// amd64:`LEAQ .*\(SB\)` `MOVSD .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
   138  	// arm64:`FMOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
   139  	// loong64:`SLLV` `MOVD \(R.*\)\(R.*\)` -`ALSLV`
   140  	switch x {
   141  	case 1:
   142  		return 1.5
   143  	case 2:
   144  		return 2.5
   145  	case 3:
   146  		return 3.5
   147  	case 4:
   148  		return 4.5
   149  	case 5:
   150  		return 5.5
   151  	case 6:
   152  		return 6.5
   153  	case 7:
   154  		return 7.5
   155  	case 8:
   156  		return 8.5
   157  	default:
   158  		return 0.0
   159  	}
   160  }
   161  
   162  // use lookup tables for 8+ string-returning cases
   163  func stringLookup(x int) string {
   164  	// amd64:`LEAQ .*\(SB\)` -`JMP \(.*\)\(.*\)$`
   165  	// arm64:-`JMP \(R.*\)$`
   166  	switch x {
   167  	case 1:
   168  		return "a"
   169  	case 2:
   170  		return "b"
   171  	case 3:
   172  		return "c"
   173  	case 4:
   174  		return "d"
   175  	case 5:
   176  		return "e"
   177  	case 6:
   178  		return "f"
   179  	case 7:
   180  		return "g"
   181  	case 8:
   182  		return "h"
   183  	default:
   184  		return ""
   185  	}
   186  }
   187  
   188  // use lookup tables for 8+ complex128-returning cases
   189  func complexLookup(x int) complex128 {
   190  	// amd64:`LEAQ .*\(SB\)` -`JMP \(.*\)\(.*\)$`
   191  	// arm64:-`JMP \(R.*\)$`
   192  	switch x {
   193  	case 1:
   194  		return 1 + 2i
   195  	case 2:
   196  		return 3 + 4i
   197  	case 3:
   198  		return 5 + 6i
   199  	case 4:
   200  		return 7 + 8i
   201  	case 5:
   202  		return 9 + 10i
   203  	case 6:
   204  		return 11 + 12i
   205  	case 7:
   206  		return 13 + 14i
   207  	case 8:
   208  		return 15 + 16i
   209  	default:
   210  		return 0
   211  	}
   212  }
   213  
   214  // use jump tables for 8+ string lengths
   215  func length(x string) int {
   216  	// amd64:`JMP \(.*\)\(.*\)$`
   217  	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
   218  	// loong64:`ALSLV` `MOVV` `JMP`
   219  	switch x {
   220  	case "a":
   221  		return 1
   222  	case "bb":
   223  		return 2
   224  	case "ccc":
   225  		return 3
   226  	case "dddd":
   227  		return 4
   228  	case "eeeee":
   229  		return 5
   230  	case "ffffff":
   231  		return 6
   232  	case "ggggggg":
   233  		return 7
   234  	case "hhhhhhhh":
   235  		return 8
   236  	default:
   237  		return len(x)
   238  	}
   239  }
   240  
   241  // Use single-byte ordered comparisons for binary searching strings.
   242  // See issue 53333.
   243  func mimetype(ext string) string {
   244  	// amd64: `CMPB 1\(.*\), \$104$` -`cmpstring`
   245  	// arm64: `MOVB 1\(R.*\), R.*$` `CMPW \$104, R.*$` -`cmpstring`
   246  	switch ext {
   247  	// amd64: `CMPL \(.*\), \$1836345390$`
   248  	// arm64: `MOVD \$1836345390` `CMPW R.*, R.*$`
   249  	case ".htm":
   250  		return "A"
   251  	// amd64: `CMPL \(.*\), \$1953457454$`
   252  	// arm64: `MOVD \$1953457454` `CMPW R.*, R.*$`
   253  	case ".eot":
   254  		return "B"
   255  	// amd64: `CMPL \(.*\), \$1735815982$`
   256  	// arm64: `MOVD \$1735815982` `CMPW R.*, R.*$`
   257  	case ".svg":
   258  		return "C"
   259  	// amd64: `CMPL \(.*\), \$1718907950$`
   260  	// arm64: `MOVD \$1718907950` `CMPW R.*, R.*$`
   261  	case ".ttf":
   262  		return "D"
   263  	default:
   264  		return ""
   265  	}
   266  }
   267  
   268  // use jump tables for type switches to concrete types.
   269  func typeSwitch(x any) int {
   270  	// amd64:`JMP \(.*\)\(.*\)$`
   271  	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
   272  	switch x.(type) {
   273  	case int:
   274  		return 0
   275  	case int8:
   276  		return 1
   277  	case int16:
   278  		return 2
   279  	case int32:
   280  		return 3
   281  	case int64:
   282  		return 4
   283  	}
   284  	return 7
   285  }
   286  
   287  type I interface {
   288  	foo()
   289  }
   290  type J interface {
   291  	bar()
   292  }
   293  type IJ interface {
   294  	I
   295  	J
   296  }
   297  type K interface {
   298  	baz()
   299  }
   300  
   301  // use a runtime call for type switches to interface types.
   302  func interfaceSwitch(x any) int {
   303  	// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
   304  	// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
   305  	switch x.(type) {
   306  	case I:
   307  		return 1
   308  	case J:
   309  		return 2
   310  	default:
   311  		return 3
   312  	}
   313  }
   314  
   315  func interfaceSwitch2(x K) int {
   316  	// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
   317  	// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
   318  	switch x.(type) {
   319  	case I:
   320  		return 1
   321  	case J:
   322  		return 2
   323  	default:
   324  		return 3
   325  	}
   326  }
   327  
   328  func interfaceCast(x any) int {
   329  	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
   330  	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
   331  	if _, ok := x.(I); ok {
   332  		return 3
   333  	}
   334  	return 5
   335  }
   336  
   337  func interfaceCast2(x K) int {
   338  	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
   339  	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
   340  	if _, ok := x.(I); ok {
   341  		return 3
   342  	}
   343  	return 5
   344  }
   345  
   346  func interfaceConv(x IJ) I {
   347  	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
   348  	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
   349  	return x
   350  }
   351  
   352  // Make sure we can constant fold after inlining. See issue 71699.
   353  func stringSwitchInlineable(s string) {
   354  	switch s {
   355  	case "foo", "bar", "baz", "goo":
   356  	default:
   357  		println("no")
   358  	}
   359  }
   360  func stringSwitch() {
   361  	// amd64:-"CMP" -"CALL"
   362  	// arm64:-"CMP" -"CALL"
   363  	stringSwitchInlineable("foo")
   364  }
   365  

View as plain text