@@ -1971,6 +1971,11 @@ func opLen4_31(op ssa.Op, t *types.Type) func(s *state, n *ir.CallExpr, args []*
19711971}
19721972
19731973func immJumpTable (s * state , idx * ssa.Value , intrinsicCall * ir.CallExpr , genOp func (* state , int )) * ssa.Value {
1974+ if base .Ctxt .Retpoline {
1975+ // Note spectre=all implies retpoline which requires binary search instead of table switch.
1976+ return branchTableImm8 (s , idx , intrinsicCall , genOp )
1977+ }
1978+
19741979 // Make blocks we'll need.
19751980 bEnd := s .f .NewBlock (ssa .BlockPlain )
19761981
@@ -1985,10 +1990,7 @@ func immJumpTable(s *state, idx *ssa.Value, intrinsicCall *ir.CallExpr, genOp fu
19851990 b := s .curBlock
19861991 b .Kind = ssa .BlockJumpTable
19871992 b .Pos = intrinsicCall .Pos ()
1988- if base .Flag .Cfg .SpectreIndex {
1989- // Potential Spectre vulnerability hardening?
1990- idx = s .newValue2 (ssa .OpSpectreSliceIndex , t , idx , s .uintptrConstant (255 ))
1991- }
1993+
19921994 b .SetControl (idx )
19931995 targets := [256 ]* ssa.Block {}
19941996 for i := range 256 {
@@ -2012,6 +2014,77 @@ func immJumpTable(s *state, idx *ssa.Value, intrinsicCall *ir.CallExpr, genOp fu
20122014 return ret
20132015}
20142016
2017+ func branchTableImm8 (s * state , idx * ssa.Value , intrinsicCall * ir.CallExpr , genOp func (* state , int )) * ssa.Value {
2018+ return branchTableN (s , idx , intrinsicCall , genOp , 256 , true )
2019+ }
2020+
2021+ func branchTableN (s * state , idx * ssa.Value , intrinsicCall * ir.CallExpr , genOp func (* state , int ), immLimit uint64 , preChecked bool ) * ssa.Value {
2022+ // Make blocks we'll need.
2023+ bEnd := s .f .NewBlock (ssa .BlockPlain )
2024+ bPanic := s .f .NewBlock (ssa .BlockPlain )
2025+
2026+ jt := s .f .NewBlock (ssa .BlockPlain )
2027+
2028+ t := types .Types [types .TUINTPTR ]
2029+ idx = s .conv (nil , idx , idx .Type , t )
2030+
2031+ if ! preChecked {
2032+ // Begin with a bounds check
2033+ width := s .uintptrConstant (immLimit )
2034+ cmp := s .newValue2 (s .ssaOp (ir .OLT , t ), types .Types [types .TBOOL ], idx , width )
2035+ bb := s .endBlock ()
2036+ bb .Kind = ssa .BlockIf
2037+ bb .SetControl (cmp )
2038+ bb .AddEdgeTo (jt ) // in range - use jump table
2039+ bb .AddEdgeTo (bPanic ) // out of range - panic
2040+ bb .Likely = ssa .BranchLikely // panic is unlikely
2041+
2042+ s .startBlock (bPanic )
2043+ s .rtcall (ir .Syms .PanicSimdImm , false , nil )
2044+ }
2045+
2046+ s .startBlock (jt )
2047+ jt .Kind = ssa .BlockPlain
2048+ jt .Pos = intrinsicCall .Pos ()
2049+
2050+ branchTableNInner (s , idx , 0 , immLimit , genOp , bEnd )
2051+
2052+ s .startBlock (bEnd )
2053+ ret := s .variable (intrinsicCall , intrinsicCall .Type ())
2054+ return ret
2055+ }
2056+
2057+ func branchTableNInner (s * state , idx * ssa.Value , lowInclusive , len uint64 , genOp func (* state , int ), bEnd * ssa.Block ) {
2058+ t := types .Types [types .TUINTPTR ]
2059+ if len == 0 {
2060+ panic ("empty branch table" )
2061+ }
2062+ if len == 1 {
2063+ genOp (s , int (lowInclusive + len - 1 ))
2064+ if s .curBlock != nil { // if genOp was "panic" then curBlock is already ended and nil
2065+ if s .curBlock .Kind != ssa .BlockExit {
2066+ s .curBlock .AddEdgeTo (bEnd )
2067+ }
2068+ s .endBlock ()
2069+ }
2070+ return
2071+ }
2072+
2073+ s .curBlock .Kind = ssa .BlockIf
2074+ cmp := s .newValue2 (s .ssaOp (ir .OLT , t ), types .Types [types .TBOOL ], idx , s .uintptrConstant (lowInclusive + len / 2 ))
2075+ bb := s .endBlock ()
2076+ bb .Kind = ssa .BlockIf
2077+ bb .SetControl (cmp )
2078+ bMatch := s .f .NewBlock (ssa .BlockPlain )
2079+ bNext := s .f .NewBlock (ssa .BlockPlain )
2080+ bb .AddEdgeTo (bMatch )
2081+ bb .AddEdgeTo (bNext )
2082+ s .startBlock (bMatch )
2083+ branchTableNInner (s , idx , lowInclusive , len / 2 , genOp , bEnd )
2084+ s .startBlock (bNext )
2085+ branchTableNInner (s , idx , lowInclusive + len / 2 , len - len / 2 , genOp , bEnd )
2086+ }
2087+
20152088func opLen1Imm8 (op ssa.Op , t * types.Type , offset int ) func (s * state , n * ir.CallExpr , args []* ssa.Value ) * ssa.Value {
20162089 return func (s * state , n * ir.CallExpr , args []* ssa.Value ) * ssa.Value {
20172090 if args [1 ].Op == ssa .OpConst8 {
0 commit comments