1
0
mirror of https://github.com/golang/go synced 2024-11-17 00:54:49 -07:00
go/test/sliceopt.go
Keith Randall deb4177cf0 cmd/compile: use masks instead of branches for slicing
When we do

  var x []byte = ...
  y := x[i:]

We can't just use y.ptr = x.ptr + i, as the new pointer may point to the
next object in memory after the backing array.
We used to fix this by doing:

  y.cap = x.cap - i
  delta := i
  if y.cap == 0 {
    delta = 0
  }
  y.ptr = x.ptr + delta

That generates a branch in what is otherwise straight-line code.

Better to do:

  y.cap = x.cap - i
  mask := (y.cap - 1) >> 63 // -1 if y.cap==0, 0 otherwise
  y.ptr = x.ptr + i &^ mask

It's about the same number of instructions (~4, depending on what
parts are constant, and the target architecture), but it is all
inline. It plays nicely with CSE, and the mask can be computed
in parallel with the index (in cases where a multiply is required).

It is a minor win in both speed and space.

Change-Id: Ied60465a0b8abb683c02208402e5bb7ac0e8370f
Reviewed-on: https://go-review.googlesource.com/32022
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2016-10-27 20:22:49 +00:00

72 lines
2.4 KiB
Go

// errorcheck -0 -d=append,slice,ssa/prove/debug=1
// Copyright 2015 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.
// Check optimization results for append and slicing.
package main
func a1(x []int, y int) []int {
x = append(x, y) // ERROR "append: len-only update \(in local slice\)$"
return x
}
func a2(x []int, y int) []int {
return append(x, y)
}
func a3(x *[]int, y int) {
*x = append(*x, y) // ERROR "append: len-only update$"
}
// s1_if_false_then_anything
func s1_if_false_then_anything(x **[]int, xs **string, i, j int) {
z := (**x)[0:i]
z = z[i : i+1]
println(z) // if we get here, then we have proven that i==i+1 (this cannot happen, but the program is still being analyzed...)
zs := (**xs)[0:i] // since i=i+1 is proven, i+1 is "in bounds", ha-ha
zs = zs[i : i+1] // ERROR "Proved boolean IsSliceInBounds$"
println(zs)
}
func s1(x **[]int, xs **string, i, j int) {
var z []int
z = (**x)[2:]
z = (**x)[2:len(**x)] // ERROR "Proved boolean IsSliceInBounds$"
z = (**x)[2:cap(**x)] // ERROR "Proved IsSliceInBounds$"
z = (**x)[i:i] // -ERROR "Proved IsSliceInBounds"
z = (**x)[1:i:i] // ERROR "Proved boolean IsSliceInBounds$"
z = (**x)[i:j:0]
z = (**x)[i:0:j] // ERROR "Disproved IsSliceInBounds$"
z = (**x)[0:i:j] // ERROR "Proved boolean IsSliceInBounds$"
z = (**x)[0:] // ERROR "slice: omit slice operation$"
z = (**x)[2:8] // ERROR "Proved slicemask not needed$"
println(z)
z = (**x)[2:2]
z = (**x)[0:i]
z = (**x)[2:i:8] // ERROR "Disproved IsSliceInBounds$" "Proved IsSliceInBounds$"
z = (**x)[i:2:i] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
z = z[0:i] // ERROR "Proved boolean IsSliceInBounds"
z = z[0:i : i+1]
z = z[i : i+1] // ERROR "Proved boolean IsSliceInBounds$"
println(z)
var zs string
zs = (**xs)[2:]
zs = (**xs)[2:len(**xs)] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
zs = (**xs)[i:i] // -ERROR "Proved boolean IsSliceInBounds"
zs = (**xs)[0:] // ERROR "slice: omit slice operation$"
zs = (**xs)[2:8]
zs = (**xs)[2:2] // ERROR "Proved boolean IsSliceInBounds$"
zs = (**xs)[0:i] // ERROR "Proved boolean IsSliceInBounds$"
zs = zs[0:i] // See s1_if_false_then_anything above to explain the counterfactual bounds check result below
zs = zs[i : i+1] // ERROR "Proved boolean IsSliceInBounds$"
println(zs)
}