// asmcheck

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// These tests check code generation of switch statements.

package codegen

// see issue 33934
func f(x string) int {
	// amd64:-`cmpstring`
	switch x {
	case "":
		return -1
	case "1", "2", "3":
		return -2
	default:
		return -3
	}
}

// use jump tables for 8+ string cases
// Using multiple return values prevent lookup tables.
func squareJump(x int) (int, int) {
	// amd64:`JMP \(.*\)\(.*\)$`
	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
	// loong64: `ALSLV` `MOVV` `JMP`
	switch x {
	case 1:
		return 1, 1
	case 2:
		return 4, 2
	case 3:
		return 9, 3
	case 4:
		return 16, 4
	case 5:
		return 25, 5
	case 6:
		return 36, 6
	case 7:
		return 49, 7
	case 8:
		return 64, 8
	default:
		return x * x, x
	}
}

// use lookup tables for 8+ int cases returning constants
func squareLookup(x int) int {
	// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
	// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
	// loong64:`SLLV` `MOVV \(R.*\)\(R.*\)` -`ALSLV`
	switch x {
	case 1:
		return 1
	case 2:
		return 4
	case 3:
		return 9
	case 4:
		return 16
	case 5:
		return 25
	case 6:
		return 36
	case 7:
		return 49
	case 8:
		return 64
	default:
		return x * x
	}
}

// lookup tables work even when some cases use fallthrough,
// as long as enough non-fallthrough cases return constants.
func fallthroughLookup(x int) int {
	// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
	// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
	switch x {
	case 1:
		return 1
	case 2:
		return 2
	case 3:
		fallthrough
	case 4:
		return 40
	case 5:
		return 5
	case 6:
		return 6
	case 7:
		return 7
	case 8:
		return 8
	case 9:
		fallthrough
	default:
		return x * x
	}
}

// use lookup tables for 8+ bool-returning cases
func boolLookup(x int) bool {
	// amd64:`LEAQ .*\(SB\)` `MOVBLZX .*\(.*\)` -`JMP \(.*\)\(.*\)$`
	// arm64:`MOVBU \(R.*\)\(R.*\)` -`JMP \(R.*\)$`
	// loong64:`MOVBU \(R.*\)\(R.*\)` -`ALSLV`
	switch x {
	case 1:
		return true
	case 2:
		return false
	case 3:
		return true
	case 4:
		return false
	case 5:
		return true
	case 6:
		return false
	case 7:
		return true
	case 8:
		return false
	default:
		return x > 0
	}
}

// use lookup tables for 8+ float64-returning cases
func floatLookup(x int) float64 {
	// amd64:`LEAQ .*\(SB\)` `MOVSD .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
	// arm64:`FMOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
	// loong64:`SLLV` `MOVD \(R.*\)\(R.*\)` -`ALSLV`
	switch x {
	case 1:
		return 1.5
	case 2:
		return 2.5
	case 3:
		return 3.5
	case 4:
		return 4.5
	case 5:
		return 5.5
	case 6:
		return 6.5
	case 7:
		return 7.5
	case 8:
		return 8.5
	default:
		return 0.0
	}
}

// use lookup tables for 8+ string-returning cases
func stringLookup(x int) string {
	// amd64:`LEAQ .*\(SB\)` -`JMP \(.*\)\(.*\)$`
	// arm64:-`JMP \(R.*\)$`
	switch x {
	case 1:
		return "a"
	case 2:
		return "b"
	case 3:
		return "c"
	case 4:
		return "d"
	case 5:
		return "e"
	case 6:
		return "f"
	case 7:
		return "g"
	case 8:
		return "h"
	default:
		return ""
	}
}

// use lookup tables for 8+ complex128-returning cases
func complexLookup(x int) complex128 {
	// amd64:`LEAQ .*\(SB\)` -`JMP \(.*\)\(.*\)$`
	// arm64:-`JMP \(R.*\)$`
	switch x {
	case 1:
		return 1 + 2i
	case 2:
		return 3 + 4i
	case 3:
		return 5 + 6i
	case 4:
		return 7 + 8i
	case 5:
		return 9 + 10i
	case 6:
		return 11 + 12i
	case 7:
		return 13 + 14i
	case 8:
		return 15 + 16i
	default:
		return 0
	}
}

// use jump tables for 8+ string lengths
func length(x string) int {
	// amd64:`JMP \(.*\)\(.*\)$`
	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
	// loong64:`ALSLV` `MOVV` `JMP`
	switch x {
	case "a":
		return 1
	case "bb":
		return 2
	case "ccc":
		return 3
	case "dddd":
		return 4
	case "eeeee":
		return 5
	case "ffffff":
		return 6
	case "ggggggg":
		return 7
	case "hhhhhhhh":
		return 8
	default:
		return len(x)
	}
}

// Use single-byte ordered comparisons for binary searching strings.
// See issue 53333.
func mimetype(ext string) string {
	// amd64: `CMPB 1\(.*\), \$104$` -`cmpstring`
	// arm64: `MOVB 1\(R.*\), R.*$` `CMPW \$104, R.*$` -`cmpstring`
	switch ext {
	// amd64: `CMPL \(.*\), \$1836345390$`
	// arm64: `MOVD \$1836345390` `CMPW R.*, R.*$`
	case ".htm":
		return "A"
	// amd64: `CMPL \(.*\), \$1953457454$`
	// arm64: `MOVD \$1953457454` `CMPW R.*, R.*$`
	case ".eot":
		return "B"
	// amd64: `CMPL \(.*\), \$1735815982$`
	// arm64: `MOVD \$1735815982` `CMPW R.*, R.*$`
	case ".svg":
		return "C"
	// amd64: `CMPL \(.*\), \$1718907950$`
	// arm64: `MOVD \$1718907950` `CMPW R.*, R.*$`
	case ".ttf":
		return "D"
	default:
		return ""
	}
}

// use jump tables for type switches to concrete types.
func typeSwitch(x any) int {
	// amd64:`JMP \(.*\)\(.*\)$`
	// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
	switch x.(type) {
	case int:
		return 0
	case int8:
		return 1
	case int16:
		return 2
	case int32:
		return 3
	case int64:
		return 4
	}
	return 7
}

type I interface {
	foo()
}
type J interface {
	bar()
}
type IJ interface {
	I
	J
}
type K interface {
	baz()
}

// use a runtime call for type switches to interface types.
func interfaceSwitch(x any) int {
	// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
	// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
	switch x.(type) {
	case I:
		return 1
	case J:
		return 2
	default:
		return 3
	}
}

func interfaceSwitch2(x K) int {
	// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
	// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
	switch x.(type) {
	case I:
		return 1
	case J:
		return 2
	default:
		return 3
	}
}

func interfaceCast(x any) int {
	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
	if _, ok := x.(I); ok {
		return 3
	}
	return 5
}

func interfaceCast2(x K) int {
	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
	if _, ok := x.(I); ok {
		return 3
	}
	return 5
}

func interfaceConv(x IJ) I {
	// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
	// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
	return x
}

// Make sure we can constant fold after inlining. See issue 71699.
func stringSwitchInlineable(s string) {
	switch s {
	case "foo", "bar", "baz", "goo":
	default:
		println("no")
	}
}
func stringSwitch() {
	// amd64:-"CMP" -"CALL"
	// arm64:-"CMP" -"CALL"
	stringSwitchInlineable("foo")
}
