compiler: implement full slice expression
This feature was introduced in Go 1.2 and is used by some standard library packages.
Этот коммит содержится в:
родитель
54169c714f
коммит
4688664b41
3 изменённых файлов: 75 добавлений и 16 удалений
|
@ -56,7 +56,7 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
|
||||||
// normal meaning) and for creating a new slice, where 'capacity' means the
|
// normal meaning) and for creating a new slice, where 'capacity' means the
|
||||||
// biggest possible slice capacity, 'low' means len and 'high' means cap. The
|
// biggest possible slice capacity, 'low' means len and 'high' means cap. The
|
||||||
// logic is the same in both cases.
|
// logic is the same in both cases.
|
||||||
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value, lowType, highType *types.Basic) {
|
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) {
|
||||||
if frame.fn.IsNoBounds() {
|
if frame.fn.IsNoBounds() {
|
||||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||||
// checking.
|
// checking.
|
||||||
|
@ -71,6 +71,9 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
||||||
if high.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
|
if high.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
|
||||||
capacityType = high.Type()
|
capacityType = high.Type()
|
||||||
}
|
}
|
||||||
|
if max.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
|
||||||
|
capacityType = max.Type()
|
||||||
|
}
|
||||||
if capacityType != capacity.Type() {
|
if capacityType != capacity.Type() {
|
||||||
capacity = c.builder.CreateZExt(capacity, capacityType, "")
|
capacity = c.builder.CreateZExt(capacity, capacityType, "")
|
||||||
}
|
}
|
||||||
|
@ -90,6 +93,13 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
||||||
high = c.builder.CreateSExt(high, capacityType, "")
|
high = c.builder.CreateSExt(high, capacityType, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
|
||||||
|
if maxType.Info()&types.IsUnsigned != 0 {
|
||||||
|
max = c.builder.CreateZExt(max, capacityType, "")
|
||||||
|
} else {
|
||||||
|
max = c.builder.CreateSExt(max, capacityType, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.outofbounds")
|
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.outofbounds")
|
||||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.next")
|
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.next")
|
||||||
|
@ -97,8 +107,10 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
||||||
|
|
||||||
// Now do the bounds check: low > high || high > capacity
|
// Now do the bounds check: low > high || high > capacity
|
||||||
outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
|
outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
|
||||||
outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, capacity, "slice.highcap")
|
outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, max, "slice.highmax")
|
||||||
outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.outofbounds")
|
outOfBounds3 := c.builder.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap")
|
||||||
|
outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax")
|
||||||
|
outOfBounds = c.builder.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap")
|
||||||
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
|
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
|
||||||
|
|
||||||
// Fail: this is a nil pointer, exit with a panic.
|
// Fail: this is a nil pointer, exit with a panic.
|
||||||
|
|
|
@ -1650,7 +1650,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds checking.
|
// Bounds checking.
|
||||||
c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, expr.Len.Type().(*types.Basic), expr.Cap.Type().(*types.Basic))
|
lenType := expr.Len.Type().(*types.Basic)
|
||||||
|
capType := expr.Cap.Type().(*types.Basic)
|
||||||
|
c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||||
|
|
||||||
// Allocate the backing array.
|
// Allocate the backing array.
|
||||||
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||||
|
@ -1724,13 +1726,10 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
case *ssa.Select:
|
case *ssa.Select:
|
||||||
return c.emitSelect(frame, expr), nil
|
return c.emitSelect(frame, expr), nil
|
||||||
case *ssa.Slice:
|
case *ssa.Slice:
|
||||||
if expr.Max != nil {
|
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
|
||||||
}
|
|
||||||
value := c.getValue(frame, expr.X)
|
value := c.getValue(frame, expr.X)
|
||||||
|
|
||||||
var lowType, highType *types.Basic
|
var lowType, highType, maxType *types.Basic
|
||||||
var low, high llvm.Value
|
var low, high, max llvm.Value
|
||||||
|
|
||||||
if expr.Low != nil {
|
if expr.Low != nil {
|
||||||
lowType = expr.Low.Type().Underlying().(*types.Basic)
|
lowType = expr.Low.Type().Underlying().(*types.Basic)
|
||||||
|
@ -1761,33 +1760,56 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
highType = types.Typ[types.Uintptr]
|
highType = types.Typ[types.Uintptr]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if expr.Max != nil {
|
||||||
|
maxType = expr.Max.Type().Underlying().(*types.Basic)
|
||||||
|
max = c.getValue(frame, expr.Max)
|
||||||
|
if max.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() {
|
||||||
|
if maxType.Info()&types.IsUnsigned != 0 {
|
||||||
|
max = c.builder.CreateZExt(max, c.uintptrType, "")
|
||||||
|
} else {
|
||||||
|
max = c.builder.CreateSExt(max, c.uintptrType, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxType = types.Typ[types.Uintptr]
|
||||||
|
}
|
||||||
|
|
||||||
switch typ := expr.X.Type().Underlying().(type) {
|
switch typ := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Pointer: // pointer to array
|
case *types.Pointer: // pointer to array
|
||||||
|
if expr.Max != nil {
|
||||||
|
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
||||||
|
}
|
||||||
// slice an array
|
// slice an array
|
||||||
length := typ.Elem().Underlying().(*types.Array).Len()
|
length := typ.Elem().Underlying().(*types.Array).Len()
|
||||||
llvmLen := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
llvmLen := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||||
if high.IsNil() {
|
if high.IsNil() {
|
||||||
high = llvmLen
|
high = llvmLen
|
||||||
}
|
}
|
||||||
|
if max.IsNil() {
|
||||||
|
max = llvmLen
|
||||||
|
}
|
||||||
indices := []llvm.Value{
|
indices := []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
low,
|
low,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emitSliceBoundsCheck(frame, llvmLen, low, high, lowType, highType)
|
c.emitSliceBoundsCheck(frame, llvmLen, low, high, max, lowType, highType, maxType)
|
||||||
|
|
||||||
// Truncate ints bigger than uintptr. This is after the bounds
|
// Truncate ints bigger than uintptr. This is after the bounds
|
||||||
// check so it's safe.
|
// check so it's safe.
|
||||||
|
if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||||
|
low = c.builder.CreateTrunc(low, c.uintptrType, "")
|
||||||
|
}
|
||||||
if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||||
high = c.builder.CreateTrunc(high, c.uintptrType, "")
|
high = c.builder.CreateTrunc(high, c.uintptrType, "")
|
||||||
}
|
}
|
||||||
if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
if c.targetData.TypeAllocSize(max.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||||
low = c.builder.CreateTrunc(low, c.uintptrType, "")
|
max = c.builder.CreateTrunc(max, c.uintptrType, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
sliceLen := c.builder.CreateSub(high, low, "slice.len")
|
sliceLen := c.builder.CreateSub(high, low, "slice.len")
|
||||||
slicePtr := c.builder.CreateInBoundsGEP(value, indices, "slice.ptr")
|
slicePtr := c.builder.CreateInBoundsGEP(value, indices, "slice.ptr")
|
||||||
sliceCap := c.builder.CreateSub(llvmLen, low, "slice.cap")
|
sliceCap := c.builder.CreateSub(max, low, "slice.cap")
|
||||||
|
|
||||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.Undef(slicePtr.Type()),
|
llvm.Undef(slicePtr.Type()),
|
||||||
|
@ -1807,8 +1829,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
if high.IsNil() {
|
if high.IsNil() {
|
||||||
high = oldLen
|
high = oldLen
|
||||||
}
|
}
|
||||||
|
if max.IsNil() {
|
||||||
|
max = oldCap
|
||||||
|
}
|
||||||
|
|
||||||
c.emitSliceBoundsCheck(frame, oldCap, low, high, lowType, highType)
|
c.emitSliceBoundsCheck(frame, oldCap, low, high, max, lowType, highType, maxType)
|
||||||
|
|
||||||
// Truncate ints bigger than uintptr. This is after the bounds
|
// Truncate ints bigger than uintptr. This is after the bounds
|
||||||
// check so it's safe.
|
// check so it's safe.
|
||||||
|
@ -1818,10 +1843,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||||
high = c.builder.CreateTrunc(high, c.uintptrType, "")
|
high = c.builder.CreateTrunc(high, c.uintptrType, "")
|
||||||
}
|
}
|
||||||
|
if c.targetData.TypeAllocSize(max.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
|
||||||
|
max = c.builder.CreateTrunc(max, c.uintptrType, "")
|
||||||
|
}
|
||||||
|
|
||||||
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
||||||
newLen := c.builder.CreateSub(high, low, "")
|
newLen := c.builder.CreateSub(high, low, "")
|
||||||
newCap := c.builder.CreateSub(oldCap, low, "")
|
newCap := c.builder.CreateSub(max, low, "")
|
||||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.Undef(newPtr.Type()),
|
llvm.Undef(newPtr.Type()),
|
||||||
llvm.Undef(c.uintptrType),
|
llvm.Undef(c.uintptrType),
|
||||||
|
@ -1837,13 +1865,18 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||||
}
|
}
|
||||||
// slice a string
|
// slice a string
|
||||||
|
if expr.Max != nil {
|
||||||
|
// This might as well be a panic, as the frontend should have
|
||||||
|
// handled this already.
|
||||||
|
return llvm.Value{}, c.makeError(expr.Pos(), "slicing a string with a max parameter is not allowed by the spec")
|
||||||
|
}
|
||||||
oldPtr := c.builder.CreateExtractValue(value, 0, "")
|
oldPtr := c.builder.CreateExtractValue(value, 0, "")
|
||||||
oldLen := c.builder.CreateExtractValue(value, 1, "")
|
oldLen := c.builder.CreateExtractValue(value, 1, "")
|
||||||
if high.IsNil() {
|
if high.IsNil() {
|
||||||
high = oldLen
|
high = oldLen
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emitSliceBoundsCheck(frame, oldLen, low, high, lowType, highType)
|
c.emitSliceBoundsCheck(frame, oldLen, low, high, high, lowType, highType, maxType)
|
||||||
|
|
||||||
// Truncate ints bigger than uintptr. This is after the bounds
|
// Truncate ints bigger than uintptr. This is after the bounds
|
||||||
// check so it's safe.
|
// check so it's safe.
|
||||||
|
|
14
testdata/slice.go
предоставленный
14
testdata/slice.go
предоставленный
|
@ -65,6 +65,20 @@ func main() {
|
||||||
assert(len(arr[uint64(1):uint64(3)]) == 2)
|
assert(len(arr[uint64(1):uint64(3)]) == 2)
|
||||||
assert(len(arr[uintptr(1):uintptr(3)]) == 2)
|
assert(len(arr[uintptr(1):uintptr(3)]) == 2)
|
||||||
|
|
||||||
|
// slicing with max parameter (added in Go 1.2)
|
||||||
|
longfoo := []int{1, 2, 4, 5, 10, 11}
|
||||||
|
assert(cap(longfoo[int(1):int(3):int(5)]) == 4)
|
||||||
|
assert(cap(longfoo[int8(1):int8(3):int8(5)]) == 4)
|
||||||
|
assert(cap(longfoo[int16(1):int16(3):int16(5)]) == 4)
|
||||||
|
assert(cap(longfoo[int32(1):int32(3):int32(5)]) == 4)
|
||||||
|
assert(cap(longfoo[int64(1):int64(3):int64(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uint(1):uint(3):uint(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uint8(1):uint8(3):uint8(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uint16(1):uint16(3):uint16(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uint32(1):uint32(3):uint32(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uint64(1):uint64(3):uint64(5)]) == 4)
|
||||||
|
assert(cap(longfoo[uintptr(1):uintptr(3):uintptr(5)]) == 4)
|
||||||
|
|
||||||
// copy
|
// copy
|
||||||
println("copy foo -> bar:", copy(bar, foo))
|
println("copy foo -> bar:", copy(bar, foo))
|
||||||
printslice("bar", bar)
|
printslice("bar", bar)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче