Skip to content

Commit 8cf5719

Browse files
committed
cmd/compile: refactor handling of switch binary search
DO NOT REVIEW [There’s no real point doing this until we generate better equality checks for composite literals, and that’s on hold for a while. See CL 22277.] The mechanics for binary search in expression switch statements was spread out across the code generation. Refactor it out and document it. This will make it easier to extend binary search to more types, using isStaticCompositeLiteral. Passes toolstash -cmp. (TODO) Update golang#15164. Change-Id: I8d8f0213781e36484e179396a21e45fe3058281f
1 parent f3d5478 commit 8cf5719

File tree

1 file changed

+65
-36
lines changed
  • src/cmd/compile/internal/gc

1 file changed

+65
-36
lines changed

src/cmd/compile/internal/gc/swt.go

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -323,15 +323,7 @@ func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
323323
half := len(cc) / 2
324324
a := Nod(OIF, nil, nil)
325325
mid := cc[half-1].node.Left
326-
le := Nod(OLE, s.exprname, mid)
327-
if Isconst(mid, CTSTR) {
328-
// Search by length and then by value; see exprcmp.
329-
lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
330-
leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
331-
a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
332-
} else {
333-
a.Left = le
334-
}
326+
a.Left = caseLessEq(s.exprname, mid)
335327
a.Left = typecheck(a.Left, Erv)
336328
a.Nbody.Set1(s.walkCases(cc[:half]))
337329
a.Rlist.Set1(s.walkCases(cc[half:]))
@@ -463,10 +455,9 @@ func caseClauses(sw *Node, kind int) []*caseClause {
463455
}
464456
} else {
465457
// expression switch
466-
switch consttype(n.Left) {
467-
case CTFLT, CTINT, CTRUNE, CTSTR:
458+
if caseIsConst(n.Left) {
468459
c.typ = caseKindExprConst
469-
default:
460+
} else {
470461
c.typ = caseKindExprVar
471462
}
472463
}
@@ -799,7 +790,67 @@ func exprcmp(c1, c2 *caseClause) int {
799790
}
800791
}
801792

793+
if !caseIsConst(n1) {
794+
return 0
795+
}
796+
802797
// sort by constant value to enable binary search
798+
return caseConstCompare(n1, n2)
799+
}
800+
801+
type caseClauseByType []*caseClause
802+
803+
func (x caseClauseByType) Len() int { return len(x) }
804+
func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
805+
func (x caseClauseByType) Less(i, j int) bool {
806+
c1, c2 := x[i], x[j]
807+
switch {
808+
// sort non-constants last
809+
case c1.typ != caseKindTypeConst:
810+
return false
811+
case c2.typ != caseKindTypeConst:
812+
return true
813+
814+
// sort by hash code
815+
case c1.hash != c2.hash:
816+
return c1.hash < c2.hash
817+
}
818+
819+
// sort by ordinal
820+
return c1.ordinal < c2.ordinal
821+
}
822+
823+
// caseIsConst reports whether n can be treated as a constant case in an expression switch.
824+
// This includes regular Go constants, but it could also include expressions
825+
// whose value is fixed at compile time, such as [3]array{1, 2, 3}.
826+
func caseIsConst(n *Node) bool {
827+
switch consttype(n) {
828+
case CTFLT, CTINT, CTRUNE, CTSTR:
829+
return true
830+
}
831+
return false
832+
}
833+
834+
// caseLessEq generates a Node representing "expr <= mid" for a binary search over constant cases.
835+
// mid is guaranteed to be a constant case, as defined by caseIsConst.
836+
// caseConstCompare and caseLessEq must agree on the sort order.
837+
func caseLessEq(expr, mid *Node) *Node {
838+
le := Nod(OLE, expr, mid)
839+
if Isconst(mid, CTSTR) {
840+
// Search by length and then by value; see caseConstCompare.
841+
lenlt := Nod(OLT, Nod(OLEN, expr, nil), Nod(OLEN, mid, nil))
842+
leneq := Nod(OEQ, Nod(OLEN, expr, nil), Nod(OLEN, mid, nil))
843+
le = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
844+
}
845+
return le
846+
}
847+
848+
// caseConstCompare provides a sort indicator (+1, 0, -1) for expressions n1 and n2.
849+
// n1 and n2 will be constants, as defined by caseIsConst.
850+
// This sort indicator will be used to sort the cases for binary searching.
851+
// caseConstCompare and caseLessEq must agree on the sort order.
852+
func caseConstCompare(n1, n2 *Node) int {
853+
ct := n1.Val().Ctype()
803854
switch ct {
804855
case CTFLT:
805856
return n1.Val().U.(*Mpflt).Cmp(n2.Val().U.(*Mpflt))
@@ -809,7 +860,7 @@ func exprcmp(c1, c2 *caseClause) int {
809860
// Sort strings by length and then by value.
810861
// It is much cheaper to compare lengths than values,
811862
// and all we need here is consistency.
812-
// We respect this sorting in exprSwitch.walkCases.
863+
// We respect this sorting in caseLessEq.
813864
a := n1.Val().U.(string)
814865
b := n2.Val().U.(string)
815866
if len(a) < len(b) {
@@ -826,28 +877,6 @@ func exprcmp(c1, c2 *caseClause) int {
826877
}
827878
return +1
828879
}
829-
880+
Fatalf("caseConstCompare non-const nodes %v / %v", n1, n2)
830881
return 0
831882
}
832-
833-
type caseClauseByType []*caseClause
834-
835-
func (x caseClauseByType) Len() int { return len(x) }
836-
func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
837-
func (x caseClauseByType) Less(i, j int) bool {
838-
c1, c2 := x[i], x[j]
839-
switch {
840-
// sort non-constants last
841-
case c1.typ != caseKindTypeConst:
842-
return false
843-
case c2.typ != caseKindTypeConst:
844-
return true
845-
846-
// sort by hash code
847-
case c1.hash != c2.hash:
848-
return c1.hash < c2.hash
849-
}
850-
851-
// sort by ordinal
852-
return c1.ordinal < c2.ordinal
853-
}

0 commit comments

Comments
 (0)