Skip to content
Permalink
Browse files

compiler: calculate max number of entries in slice at compile time

This avoids difficult multiply-with-overflow code and avoids a multiply
at runtime.
  • Loading branch information...
aykevl authored and deadprogram committed Feb 7, 2019
1 parent 26e7e93 commit b837c943661dc85dad219252976a0c2738d714a3
Showing with 39 additions and 28 deletions.
  1. +9 −7 compiler/compiler.go
  2. +4 −10 src/runtime/panic.go
  3. +26 −11 testdata/slice.go
@@ -1736,9 +1736,10 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
elemSize := c.targetData.TypeAllocSize(llvmElemType)
elemSizeValue := llvm.ConstInt(c.uintptrType, elemSize, false)

// Calculate ^uintptr(0)
maxSize := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
if elemSize > maxSize {
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
// uintptr if uintptr were signed.
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)), llvm.ConstInt(c.uintptrType, 1, false))
if elemSize > maxSize.ZExtValue() {
// This seems to be checked by the typechecker already, but let's
// check it again just to be sure.
return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
@@ -1770,10 +1771,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
sliceCap = c.builder.CreateSExt(sliceCap, biggestInt, "")
}
}
// Note: the max element size needs to be doubled to make sure it
// fits in an int for, for example, len().
elemSizeDoubled := c.builder.CreateMul(elemSizeValue, llvm.ConstInt(c.uintptrType, 2, false), "")
c.createRuntimeCall(checkFunc, []llvm.Value{sliceLen, sliceCap, elemSizeDoubled}, "")
maxSliceSize := maxSize
if elemSize != 0 { // avoid divide by zero
maxSliceSize = llvm.ConstSDiv(maxSize, llvm.ConstInt(c.uintptrType, elemSize, false))
}
c.createRuntimeCall(checkFunc, []llvm.Value{sliceLen, sliceCap, maxSliceSize}, "")
}

// Allocate the backing array.
@@ -57,21 +57,15 @@ func sliceBoundsCheck64(capacity uintptr, low, high uint64) {
}

// Check for bounds in *ssa.MakeSlice.
func sliceBoundsCheckMake(length, capacity uintptr, elementSizeDoubled uintptr) {
overflow := uint64(capacity*elementSizeDoubled) != uint64(capacity)*uint64(elementSizeDoubled)
if length > capacity || overflow {
func sliceBoundsCheckMake(length, capacity uintptr, max uintptr) {
if length > capacity || capacity > max {
runtimePanic("slice size out of range")
}
}

// Check for bounds in *ssa.MakeSlice. Supports 64-bit indexes.
func sliceBoundsCheckMake64(length, capacity uint64, elementSizeDoubled uintptr) {
// This function is only ever called on systems where uintptr is smaller
// than uint64 (thus must be 32-bit or less). So multiplying as uint64 will
// never overflow if we know that capacity fits in uintptr.
// That elementSizeDoubled fits in uintptr is checked by the compiler.
overflow := capacity != uint64(uintptr(capacity)) || capacity != uint64(uintptr(capacity*uint64(elementSizeDoubled)))
if length > capacity || overflow {
func sliceBoundsCheckMake64(length, capacity uint64, max uintptr) {
if length > capacity || capacity > uint64(max) {
runtimePanic("slice size out of range")
}
}
@@ -13,17 +13,17 @@ func main() {
println("sum foo:", sum(foo))

// creating a slice with uncommon len, cap types
assert(len(make([]int, int(2), int(3))) == 2)
assert(len(make([]int, int8(2), int8(3))) == 2)
assert(len(make([]int, int16(2), int16(3))) == 2)
assert(len(make([]int, int32(2), int32(3))) == 2)
assert(len(make([]int, int64(2), int64(3))) == 2)
assert(len(make([]int, uint(2), uint(3))) == 2)
assert(len(make([]int, uint8(2), uint8(3))) == 2)
assert(len(make([]int, uint16(2), uint16(3))) == 2)
assert(len(make([]int, uint32(2), uint32(3))) == 2)
assert(len(make([]int, uint64(2), uint64(3))) == 2)
assert(len(make([]int, uintptr(2), uintptr(3))) == 2)
assert(len(make([]int, makeInt(2), makeInt(3))) == 2)
assert(len(make([]int, makeInt8(2), makeInt8(3))) == 2)
assert(len(make([]int, makeInt16(2), makeInt16(3))) == 2)
assert(len(make([]int, makeInt32(2), makeInt32(3))) == 2)
assert(len(make([]int, makeInt64(2), makeInt64(3))) == 2)
assert(len(make([]int, makeUint(2), makeUint(3))) == 2)
assert(len(make([]int, makeUint8(2), makeUint8(3))) == 2)
assert(len(make([]int, makeUint16(2), makeUint16(3))) == 2)
assert(len(make([]int, makeUint32(2), makeUint32(3))) == 2)
assert(len(make([]int, makeUint64(2), makeUint64(3))) == 2)
assert(len(make([]int, makeUintptr(2), makeUintptr(3))) == 2)

// indexing into a slice with uncommon index types
assert(foo[int(2)] == 4)
@@ -120,3 +120,18 @@ func assert(ok bool) {
panic("assert failed")
}
}

// Helper functions used to hide const values from the compiler during IR
// construction.

func makeInt(x int) int { return x }
func makeInt8(x int8) int8 { return x }
func makeInt16(x int16) int16 { return x }
func makeInt32(x int32) int32 { return x }
func makeInt64(x int64) int64 { return x }
func makeUint(x uint) uint { return x }
func makeUint8(x uint8) uint8 { return x }
func makeUint16(x uint16) uint16 { return x }
func makeUint32(x uint32) uint32 { return x }
func makeUint64(x uint64) uint64 { return x }
func makeUintptr(x uintptr) uintptr { return x }

0 comments on commit b837c94

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.