From 0a06c6014df131e021fa46942e37b95cde5b44c3 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 20 Oct 2018 18:26:10 +0200 Subject: [PATCH] compiler: special slice bounds check for 64-bit numbers It is allowed to index with an int64 even on a 32-bit platform, so we have to handle that case. But make sure the normal case isn't penalized by using 32-bit numbers when possible. --- compiler/compiler.go | 32 +++++++++++++++++++++++--------- src/runtime/panic.go | 7 +++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index db931a25..d99622c3 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -2062,6 +2062,26 @@ func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value) { c.createRuntimeCall("lookupBoundsCheck", []llvm.Value{arrayLen, index}, "") } +func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Value) { + if frame.fn.IsNoBounds() { + // The //go:nobounds pragma was added to the function to avoid bounds + // checking. + return + } + + if low.Type().IntTypeWidth() > 32 || high.Type().IntTypeWidth() > 32 { + if low.Type().IntTypeWidth() < 64 { + low = c.builder.CreateSExt(low, c.ctx.Int64Type(), "") + } + if high.Type().IntTypeWidth() < 64 { + high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "") + } + c.createRuntimeCall("sliceBoundsCheckLong", []llvm.Value{length, low, high}, "") + } else { + c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{length, low, high}, "") + } +} + func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if value, ok := frame.locals[expr]; ok { // Value is a local variable that has already been computed. @@ -2453,9 +2473,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { sliceCap := c.builder.CreateSub(llvmLenInt, low, "slice.cap") // This check is optimized away in most cases. - if !frame.fn.IsNoBounds() { - c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{llvmLen, low, high}, "") - } + c.emitSliceBoundsCheck(frame, llvmLen, low, high) if c.targetData.TypeAllocSize(sliceLen.Type()) > c.targetData.TypeAllocSize(c.lenType) { sliceLen = c.builder.CreateTrunc(sliceLen, c.lenType, "") @@ -2481,9 +2499,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = oldLen } - if !frame.fn.IsNoBounds() { - c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{oldLen, low, high}, "") - } + c.emitSliceBoundsCheck(frame, oldLen, low, high) if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.lenType) { low = c.builder.CreateTrunc(low, c.lenType, "") @@ -2516,9 +2532,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = oldLen } - if !frame.fn.IsNoBounds() { - c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{oldLen, low, high}, "") - } + c.emitSliceBoundsCheck(frame, oldLen, low, high) newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "") newLen := c.builder.CreateSub(high, low, "") diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 4d07bfb7..2754efd4 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -36,6 +36,13 @@ func sliceBoundsCheck(length lenType, low, high uint) { } } +// Check for bounds in *ssa.Slice. Supports 64-bit indexes. +func sliceBoundsCheckLong(length lenType, low, high uint64) { + if !(0 <= low && low <= high && high <= uint64(length)) { + runtimePanic("slice out of range") + } +} + // Check for bounds in *ssa.MakeSlice. func sliceBoundsCheckMake(length, capacity uint) { if !(0 <= length && length <= capacity) {