[dev.ssa] cmd/compile: implement OSLICESTR
Add a new function and generic operation to handle
bounds checking for slices. Unlike the index
bounds checking the index can be equal to the upper
bound.
Do gc-friendly slicing that generates proper code for
0-length result slices.
This is a takeover of Alexandru's original change,
(https://go-review.googlesource.com/#/c/12764/)
submittable now that the decompose phase is in.
Change-Id: I17d164cf42ed7839f84ca949c6ad3289269c9160
Reviewed-on: https://go-review.googlesource.com/13903
Reviewed-by: David Chase <drchase@google.com>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 676de23..ce20e7b 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -1465,6 +1465,71 @@
a := s.expr(n.Left)
return s.newValue1(ssa.OpITab, n.Type, a)
+ case OSLICESTR:
+ // Evaluate the string once.
+ str := s.expr(n.Left)
+ ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), str)
+ len := s.newValue1(ssa.OpStringLen, Types[TINT], str)
+ zero := s.constInt(Types[TINT], 0)
+
+ // Evaluate the slice indexes.
+ var low, high *ssa.Value
+ if n.Right.Left == nil {
+ low = zero
+ } else {
+ low = s.expr(n.Right.Left)
+ }
+ if n.Right.Right == nil {
+ high = len
+ } else {
+ high = s.expr(n.Right.Right)
+ }
+
+ // Panic if slice indices are not in bounds.
+ s.sliceBoundsCheck(low, high)
+ s.sliceBoundsCheck(high, len)
+
+ // Generate the following code assuming that indexes are in bounds.
+ // The conditional is to make sure that we don't generate a string
+ // that points to the next object in memory.
+ // rlen = (SubPtr high low)
+ // p = ptr
+ // if rlen != 0 {
+ // p = (AddPtr ptr low)
+ // }
+ // result = (StringMake p size)
+ rlen := s.newValue2(ssa.OpSubPtr, Types[TINT], high, low)
+
+ // Use n as the "variable" for p.
+ s.vars[n] = ptr
+
+ // Generate code to test the resulting slice length.
+ var cmp *ssa.Value
+ if s.config.IntSize == 8 {
+ cmp = s.newValue2(ssa.OpNeq64, Types[TBOOL], rlen, zero)
+ } else {
+ cmp = s.newValue2(ssa.OpNeq32, Types[TBOOL], rlen, zero)
+ }
+
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Likely = ssa.BranchLikely
+ b.Control = cmp
+
+ // Generate code for non-zero length slice case.
+ nz := s.f.NewBlock(ssa.BlockPlain)
+ addEdge(b, nz)
+ s.startBlock(nz)
+ s.vars[n] = s.newValue2(ssa.OpAddPtr, Ptrto(Types[TUINT8]), ptr, low)
+ s.endBlock()
+
+ // All done.
+ merge := s.f.NewBlock(ssa.BlockPlain)
+ addEdge(b, merge)
+ addEdge(nz, merge)
+ s.startBlock(merge)
+ return s.newValue2(ssa.OpStringMake, Types[TSTRING], s.variable(n, Ptrto(Types[TUINT8])), rlen)
+
case OCALLFUNC, OCALLMETH:
left := n.Left
static := left.Op == ONAME && left.Class == PFUNC
@@ -1782,6 +1847,25 @@
// bounds check
cmp := s.newValue2(ssa.OpIsInBounds, Types[TBOOL], idx, len)
+ s.check(cmp, ssa.OpPanicIndexCheck)
+}
+
+// sliceBoundsCheck generates slice bounds checking code. Checks if 0 <= idx <= len, branches to exit if not.
+// Starts a new block on return.
+func (s *state) sliceBoundsCheck(idx, len *ssa.Value) {
+ if Debug['B'] != 0 {
+ return
+ }
+ // TODO: convert index to full width?
+ // TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.
+
+ // bounds check
+ cmp := s.newValue2(ssa.OpIsSliceInBounds, Types[TBOOL], idx, len)
+ s.check(cmp, ssa.OpPanicSliceCheck)
+}
+
+// If cmp (a bool) is true, panic using the given op.
+func (s *state) check(cmp *ssa.Value, panicOp ssa.Op) {
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Control = cmp
@@ -1794,7 +1878,7 @@
s.startBlock(bPanic)
// The panic check takes/returns memory to ensure that the right
// memory state is observed if the panic happens.
- s.vars[&memvar] = s.newValue1(ssa.OpPanicIndexCheck, ssa.TypeMem, s.mem())
+ s.vars[&memvar] = s.newValue1(panicOp, ssa.TypeMem, s.mem())
s.endBlock()
s.startBlock(bNext)
}