mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 07:23:39 +00:00
compiler: refactor slice related asserts
Move these asserts into compiler/asserts.go, to keep them together. The make([]T) asserts aren't moved yet because that code is (still!) quite ugly and in need of some clean up.
This commit is contained in:
committed by
Ron Evans
parent
09e85b7859
commit
051ad07755
@@ -4,9 +4,82 @@ package compiler
|
||||
// required by the Go programming language.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitLookupBoundsCheck emits a bounds check before doing a lookup into a
|
||||
// slice. This is required by the Go language spec: an index out of bounds must
|
||||
// cause a panic.
|
||||
func (c *Compiler) emitLookupBoundsCheck(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.
|
||||
if index.IsConstant() && arrayLen.IsConstant() && !arrayLen.IsUndef() {
|
||||
index := index.SExtValue()
|
||||
arrayLen := arrayLen.SExtValue()
|
||||
if index >= 0 && index < arrayLen {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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}, "")
|
||||
}
|
||||
}
|
||||
|
||||
// emitSliceBoundsCheck emits a bounds check before a slicing operation to make
|
||||
// sure it is within bounds.
|
||||
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value, lowType, highType *types.Basic) {
|
||||
if frame.fn.IsNoBounds() {
|
||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||
// checking.
|
||||
return
|
||||
}
|
||||
|
||||
uintptrWidth := c.uintptrType.IntTypeWidth()
|
||||
if low.Type().IntTypeWidth() > uintptrWidth || high.Type().IntTypeWidth() > uintptrWidth {
|
||||
if low.Type().IntTypeWidth() < 64 {
|
||||
if lowType.Info()&types.IsUnsigned != 0 {
|
||||
low = c.builder.CreateZExt(low, c.ctx.Int64Type(), "")
|
||||
} else {
|
||||
low = c.builder.CreateSExt(low, c.ctx.Int64Type(), "")
|
||||
}
|
||||
}
|
||||
if high.Type().IntTypeWidth() < 64 {
|
||||
if highType.Info()&types.IsUnsigned != 0 {
|
||||
high = c.builder.CreateZExt(high, c.ctx.Int64Type(), "")
|
||||
} else {
|
||||
high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "")
|
||||
}
|
||||
}
|
||||
// TODO: 32-bit or even 16-bit slice bounds checks for 8-bit platforms
|
||||
c.createRuntimeCall("sliceBoundsCheck64", []llvm.Value{capacity, low, high}, "")
|
||||
} else {
|
||||
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{capacity, low, high}, "")
|
||||
}
|
||||
}
|
||||
|
||||
// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
|
||||
// has no effect in well-behaved programs, but makes sure no uncaught nil
|
||||
// pointer dereferences exist in valid Go code.
|
||||
|
||||
Reference in New Issue
Block a user