From 96f74ec153dad8ce422f049131986b435c80b1ba Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 23 Oct 2018 14:07:27 +0200 Subject: [PATCH] compiler: support 64-bit numbers in bounds check --- compiler/compiler.go | 27 ++++++++++++++++++++++----- src/runtime/panic.go | 8 ++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index abfa2ac8..1da298b5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -2197,12 +2197,23 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l } } -func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value) { +func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) { if frame.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return } + + // Sometimes, the index can be e.g. an uint8 or int8, and we have to + // correctly extend that type. + if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() { + if indexType.(*types.Basic).Info()&types.IsUnsigned == 0 { + index = c.builder.CreateZExt(index, arrayLen.Type(), "") + } else { + index = c.builder.CreateSExt(index, arrayLen.Type(), "") + } + } + // Optimize away trivial cases. // LLVM would do this anyway with interprocedural optimizations, but it // helps to see cases where bounds check elimination would really help. @@ -2213,7 +2224,13 @@ func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value) { return } } - c.createRuntimeCall("lookupBoundsCheck", []llvm.Value{arrayLen, index}, "") + + if index.Type().IntTypeWidth() > c.intType.IntTypeWidth() { + // Index is too big for the regular bounds check. Use the one for int64. + c.createRuntimeCall("lookupBoundsCheckLong", []llvm.Value{arrayLen, index}, "") + } else { + c.createRuntimeCall("lookupBoundsCheck", []llvm.Value{arrayLen, index}, "") + } } func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Value) { @@ -2371,7 +2388,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Check bounds. arrayLen := expr.X.Type().(*types.Array).Len() arrayLenLLVM := llvm.ConstInt(c.lenType, uint64(arrayLen), false) - c.emitBoundsCheck(frame, arrayLenLLVM, index) + c.emitBoundsCheck(frame, arrayLenLLVM, index, expr.Index.Type()) // Can't load directly from array (as index is non-constant), so have to // do it using an alloca+gep+load. @@ -2411,7 +2428,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Bounds check. // LLVM optimizes this away in most cases. - c.emitBoundsCheck(frame, buflen, index) + c.emitBoundsCheck(frame, buflen, index, expr.Index.Type()) switch expr.X.Type().Underlying().(type) { case *types.Pointer: @@ -2447,7 +2464,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if err != nil { return llvm.Value{}, err // shouldn't happen } - c.emitBoundsCheck(frame, length, index) + c.emitBoundsCheck(frame, length, index, expr.Index.Type()) // Lookup byte buf := c.builder.CreateExtractValue(value, 0, "") diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 2754efd4..53ea81d1 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -29,6 +29,14 @@ func lookupBoundsCheck(length lenType, index int) { } } +// Check for bounds in *ssa.Index, *ssa.IndexAddr and *ssa.Lookup. +// Supports 64-bit indexes. +func lookupBoundsCheckLong(length lenType, index int64) { + if index < 0 || index >= int64(length) { + runtimePanic("index out of range") + } +} + // Check for bounds in *ssa.Slice. func sliceBoundsCheck(length lenType, low, high uint) { if !(0 <= low && low <= high && high <= uint(length)) {