// +build amd64 // errorcheck -0 -d=ssa/check_bce/debug=3 // Copyright 2016 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. // Test that the compiler does bounds check elimination as expected. // This avoids accidental regressions. package main func f0(a []int) { a[0] = 1 // ERROR "Found IsInBounds$" a[0] = 1 a[6] = 1 // ERROR "Found IsInBounds$" a[6] = 1 a[5] = 1 a[5] = 1 } func f1(a [256]int, i int) { var j int useInt(a[i]) // ERROR "Found IsInBounds$" j = i % 256 useInt(a[j]) // ERROR "Found IsInBounds$" j = i & 255 useInt(a[j]) j = i & 17 useInt(a[j]) if 4 <= i && i < len(a) { useInt(a[i]) useInt(a[i-1]) // ERROR "Found IsInBounds$" // TODO: 'if 4 <= i && i < len(a)' gets rewritten to 'if uint(i - 4) < 256 - 4', // which the bounds checker cannot yet use to infer that the next line doesn't need a bounds check. useInt(a[i-4]) } } func f2(a [256]int, i uint) { useInt(a[i]) // ERROR "Found IsInBounds$" j := i % 256 useInt(a[j]) j = i & 255 useInt(a[j]) j = i & 17 useInt(a[j]) } func f2a(a [35]int, i uint8) { useInt(a[i]) // ERROR "Found IsInBounds$" j := i & 34 useInt(a[j]) j = i & 17 useInt(a[j]) } func f2b(a [35]int, i uint16) { useInt(a[i]) // ERROR "Found IsInBounds$" j := i & 34 useInt(a[j]) j = i & 17 useInt(a[j]) } func f2c(a [35]int, i uint32) { useInt(a[i]) // ERROR "Found IsInBounds$" j := i & 34 useInt(a[j]) j = i & 17 useInt(a[j]) } func f3(a [256]int, i uint8) { useInt(a[i]) useInt(a[i+10]) useInt(a[i+14]) } func f4(a [27]int, i uint8) { useInt(a[i%15]) useInt(a[i%19]) useInt(a[i%27]) } func f5(a []int) { if len(a) > 5 { useInt(a[5]) useSlice(a[6:]) useSlice(a[:6]) } } func f6(a [32]int, b [64]int, i int) { useInt(a[uint32(i*0x07C4ACDD)>>27]) useInt(b[uint64(i*0x07C4ACDD)>>58]) useInt(a[uint(i*0x07C4ACDD)>>59]) // The following bounds should not be removed because they can overflow. useInt(a[uint32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" useInt(b[uint64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$" useInt(a[int32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" useInt(b[int64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$" } func g1(a []int) { for i := range a { a[i] = i useSlice(a[:i+1]) useSlice(a[:i]) } } func g2(a []int) { useInt(a[3]) // ERROR "Found IsInBounds$" useInt(a[2]) useInt(a[1]) useInt(a[0]) } func g3(a []int) { for i := range a[:256] { // ERROR "Found IsSliceInBounds$" useInt(a[i]) // ERROR "Found IsInBounds$" } b := a[:256] for i := range b { useInt(b[i]) } } func g4(a [100]int) { for i := 10; i < 50; i++ { useInt(a[i-10]) useInt(a[i]) useInt(a[i+25]) useInt(a[i+50]) // The following are out of bounds. useInt(a[i-11]) // ERROR "Found IsInBounds$" useInt(a[i+51]) // ERROR "Found IsInBounds$" } } func Uint64(b []byte) uint64 { _ = b[7] // ERROR "Found IsInBounds$" return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 } func decode1(data []byte) (x uint64) { for len(data) >= 32 { x += Uint64(data[:8]) x += Uint64(data[8:16]) x += Uint64(data[16:24]) x += Uint64(data[24:32]) data = data[32:] } return x } func decode2(data []byte) (x uint64) { // TODO(rasky): this should behave like decode1 and compile to no // boundchecks. We're currently not able to remove all of them. for len(data) >= 32 { x += Uint64(data) data = data[8:] x += Uint64(data) // ERROR "Found IsInBounds$" data = data[8:] x += Uint64(data) // ERROR "Found IsInBounds$" data = data[8:] x += Uint64(data) // ERROR "Found IsInBounds$" data = data[8:] } return x } //go:noinline func useInt(a int) { } //go:noinline func useSlice(a []int) { } func main() { }