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 | ||||
| // biggest possible slice capacity, 'low' means len and 'high' means cap. The | ||||
| // 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() { | ||||
| 		// The //go:nobounds pragma was added to the function to avoid bounds | ||||
| 		// checking. | ||||
|  | @ -71,6 +71,9 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V | |||
| 	if high.Type().IntTypeWidth() > capacityType.IntTypeWidth() { | ||||
| 		capacityType = high.Type() | ||||
| 	} | ||||
| 	if max.Type().IntTypeWidth() > capacityType.IntTypeWidth() { | ||||
| 		capacityType = max.Type() | ||||
| 	} | ||||
| 	if capacityType != capacity.Type() { | ||||
| 		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, "") | ||||
| 		} | ||||
| 	} | ||||
| 	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") | ||||
| 	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 | ||||
| 	outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh") | ||||
| 	outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, capacity, "slice.highcap") | ||||
| 	outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.outofbounds") | ||||
| 	outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, max, "slice.highmax") | ||||
| 	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) | ||||
| 
 | ||||
| 	// 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. | ||||
| 		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. | ||||
| 		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: | ||||
| 		return c.emitSelect(frame, expr), nil | ||||
| 	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) | ||||
| 
 | ||||
| 		var lowType, highType *types.Basic | ||||
| 		var low, high llvm.Value | ||||
| 		var lowType, highType, maxType *types.Basic | ||||
| 		var low, high, max llvm.Value | ||||
| 
 | ||||
| 		if expr.Low != nil { | ||||
| 			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] | ||||
| 		} | ||||
| 
 | ||||
| 		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) { | ||||
| 		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 | ||||
| 			length := typ.Elem().Underlying().(*types.Array).Len() | ||||
| 			llvmLen := llvm.ConstInt(c.uintptrType, uint64(length), false) | ||||
| 			if high.IsNil() { | ||||
| 				high = llvmLen | ||||
| 			} | ||||
| 			if max.IsNil() { | ||||
| 				max = llvmLen | ||||
| 			} | ||||
| 			indices := []llvm.Value{ | ||||
| 				llvm.ConstInt(c.ctx.Int32Type(), 0, false), | ||||
| 				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 | ||||
| 			// 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) { | ||||
| 				high = c.builder.CreateTrunc(high, c.uintptrType, "") | ||||
| 			} | ||||
| 			if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { | ||||
| 				low = c.builder.CreateTrunc(low, c.uintptrType, "") | ||||
| 			if c.targetData.TypeAllocSize(max.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { | ||||
| 				max = c.builder.CreateTrunc(max, c.uintptrType, "") | ||||
| 			} | ||||
| 
 | ||||
| 			sliceLen := c.builder.CreateSub(high, low, "slice.len") | ||||
| 			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{ | ||||
| 				llvm.Undef(slicePtr.Type()), | ||||
|  | @ -1807,8 +1829,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 			if high.IsNil() { | ||||
| 				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 | ||||
| 			// 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) { | ||||
| 				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}, "") | ||||
| 			newLen := c.builder.CreateSub(high, low, "") | ||||
| 			newCap := c.builder.CreateSub(oldCap, low, "") | ||||
| 			newCap := c.builder.CreateSub(max, low, "") | ||||
| 			slice := c.ctx.ConstStruct([]llvm.Value{ | ||||
| 				llvm.Undef(newPtr.Type()), | ||||
| 				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()) | ||||
| 			} | ||||
| 			// 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, "") | ||||
| 			oldLen := c.builder.CreateExtractValue(value, 1, "") | ||||
| 			if high.IsNil() { | ||||
| 				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 | ||||
| 			// 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[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 | ||||
| 	println("copy foo -> bar:", copy(bar, foo)) | ||||
| 	printslice("bar", bar) | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem