diff --git a/compiler/asserts.go b/compiler/asserts.go index daeecadb..6b254781 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -11,11 +11,11 @@ import ( "tinygo.org/x/go-llvm" ) -// emitLookupBoundsCheck emits a bounds check before doing a lookup into a +// createLookupBoundsCheck 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() { +func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -25,29 +25,29 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu // Sometimes, the index can be e.g. an uint8 or int8, and we have to // correctly extend that type. if indexType.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 { - index = c.builder.CreateZExt(index, arrayLen.Type(), "") + index = b.CreateZExt(index, arrayLen.Type(), "") } else { - index = c.builder.CreateSExt(index, arrayLen.Type(), "") + index = b.CreateSExt(index, arrayLen.Type(), "") } } else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() { // The index is bigger than the array length type, so extend it. - arrayLen = c.builder.CreateZExt(arrayLen, index.Type(), "") + arrayLen = b.CreateZExt(arrayLen, index.Type(), "") } // Now do the bounds check: index >= arrayLen - outOfBounds := c.builder.CreateICmp(llvm.IntUGE, index, arrayLen, "") - c.createRuntimeAssert(frame, outOfBounds, "lookup", "lookupPanic") + outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "") + b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic") } -// emitSliceBoundsCheck emits a bounds check before a slicing operation to make +// createSliceBoundsCheck emits a bounds check before a slicing operation to make // sure it is within bounds. // // This function is both used for slicing a slice (low and high have their // 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, max llvm.Value, lowType, highType, maxType *types.Basic) { - if frame.fn.IsNoBounds() { +func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -65,45 +65,45 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high, max l capacityType = max.Type() } if capacityType != capacity.Type() { - capacity = c.builder.CreateZExt(capacity, capacityType, "") + capacity = b.CreateZExt(capacity, capacityType, "") } // Extend low and high to be the same size as capacity. if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if lowType.Info()&types.IsUnsigned != 0 { - low = c.builder.CreateZExt(low, capacityType, "") + low = b.CreateZExt(low, capacityType, "") } else { - low = c.builder.CreateSExt(low, capacityType, "") + low = b.CreateSExt(low, capacityType, "") } } if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if highType.Info()&types.IsUnsigned != 0 { - high = c.builder.CreateZExt(high, capacityType, "") + high = b.CreateZExt(high, capacityType, "") } else { - high = c.builder.CreateSExt(high, capacityType, "") + high = b.CreateSExt(high, capacityType, "") } } if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if maxType.Info()&types.IsUnsigned != 0 { - max = c.builder.CreateZExt(max, capacityType, "") + max = b.CreateZExt(max, capacityType, "") } else { - max = c.builder.CreateSExt(max, capacityType, "") + max = b.CreateSExt(max, capacityType, "") } } // 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, 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.createRuntimeAssert(frame, outOfBounds, "slice", "slicePanic") + outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh") + outOfBounds2 := b.CreateICmp(llvm.IntUGT, high, max, "slice.highmax") + outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap") + outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax") + outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap") + b.createRuntimeAssert(outOfBounds, "slice", "slicePanic") } -// emitChanBoundsCheck emits a bounds check before creating a new channel to +// createChanBoundsCheck creates a bounds check before creating a new channel to // check that the value is not too big for runtime.chanMake. -func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) { - if frame.fn.IsNoBounds() { +func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -111,23 +111,23 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize // Check whether the bufSize parameter must be cast to a wider integer for // comparison. - if bufSize.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { + if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if bufSizeType.Info()&types.IsUnsigned != 0 { // Unsigned, so zero-extend to uint type. bufSizeType = types.Typ[types.Uint] - bufSize = c.builder.CreateZExt(bufSize, c.intType, "") + bufSize = b.CreateZExt(bufSize, b.intType, "") } else { // Signed, so sign-extend to int type. bufSizeType = types.Typ[types.Int] - bufSize = c.builder.CreateSExt(bufSize, c.intType, "") + bufSize = b.CreateSExt(bufSize, b.intType, "") } } // Calculate (^uintptr(0)) >> 1, which is the max value that fits in an // uintptr if uintptrs were signed. - maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)), llvm.ConstInt(c.uintptrType, 1, false)) + maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false)) if elementSize > maxBufSize.ZExtValue() { - c.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize)) + b.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize)) return } // Avoid divide-by-zero. @@ -136,7 +136,7 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize } // Make the maxBufSize actually the maximum allowed value (in number of // elements in the channel buffer). - maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(c.uintptrType, elementSize, false)) + maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false)) // Make sure maxBufSize has the same type as bufSize. if maxBufSize.Type() != bufSize.Type() { @@ -144,14 +144,14 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize } // Do the check for a too large (or negative) buffer size. - bufSizeTooBig := c.builder.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "") - c.createRuntimeAssert(frame, bufSizeTooBig, "chan", "chanMakePanic") + bufSizeTooBig := b.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "") + b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic") } -// 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 +// createNilCheck 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. -func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) { +func (b *builder) createNilCheck(ptr llvm.Value, blockPrefix string) { // Check whether we need to emit this check at all. if !ptr.IsAGlobalValue().IsNil() { return @@ -167,22 +167,22 @@ func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string // https://reviews.llvm.org/D60047 for details. Pointer capturing // unfortunately breaks escape analysis, so we use this trick to let the // functionattr pass know that this pointer doesn't really escape. - ptr = c.builder.CreateBitCast(ptr, c.i8ptrType, "") - isnil = c.createRuntimeCall("isnil", []llvm.Value{ptr}, "") + ptr = b.CreateBitCast(ptr, b.i8ptrType, "") + isnil = b.createRuntimeCall("isnil", []llvm.Value{ptr}, "") } else { // Do the nil check using a regular icmp. This can happen with function // pointers on AVR, which don't benefit from escape analysis anyway. nilptr := llvm.ConstPointerNull(ptr.Type()) - isnil = c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "") + isnil = b.CreateICmp(llvm.IntEQ, ptr, nilptr, "") } // Emit the nil check in IR. - c.createRuntimeAssert(frame, isnil, blockPrefix, "nilPanic") + b.createRuntimeAssert(isnil, blockPrefix, "nilPanic") } // createRuntimeAssert is a common function to create a new branch on an assert // bool, calling an assert func if the assert value is true (1). -func (c *Compiler) createRuntimeAssert(frame *Frame, assert llvm.Value, blockPrefix, assertFunc string) { +func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) { // Check whether we can resolve this check at compile time. if !assert.IsAConstantInt().IsNil() { val := assert.ZExtValue() @@ -193,18 +193,18 @@ func (c *Compiler) createRuntimeAssert(frame *Frame, assert llvm.Value, blockPre } } - faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".throw") - nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next") - frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes + faultBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".throw") + nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".next") + b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes // Now branch to the out-of-bounds or the regular block. - c.builder.CreateCondBr(assert, faultBlock, nextBlock) + b.CreateCondBr(assert, faultBlock, nextBlock) // Fail: the assert triggered so panic. - c.builder.SetInsertPointAtEnd(faultBlock) - c.createRuntimeCall(assertFunc, nil, "") - c.builder.CreateUnreachable() + b.SetInsertPointAtEnd(faultBlock) + b.createRuntimeCall(assertFunc, nil, "") + b.CreateUnreachable() // Ok: assert didn't trigger so continue normally. - c.builder.SetInsertPointAtEnd(nextBlock) + b.SetInsertPointAtEnd(nextBlock) } diff --git a/compiler/channel.go b/compiler/channel.go index bdfdf9b9..2e802f0f 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -15,7 +15,7 @@ func (c *Compiler) emitMakeChan(frame *Frame, expr *ssa.MakeChan) llvm.Value { elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem())) elementSizeValue := llvm.ConstInt(c.uintptrType, elementSize, false) bufSize := frame.getValue(expr.Size) - c.emitChanBoundsCheck(frame, elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) + frame.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) if bufSize.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { bufSize = c.builder.CreateZExt(bufSize, c.uintptrType, "") } else if bufSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() { diff --git a/compiler/compiler.go b/compiler/compiler.go index 8608e2ea..1ad55453 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -347,7 +347,7 @@ func (c *Compiler) Compile(mainPath string) []error { c.mod.NamedFunction("runtime.alloc").AddAttributeAtIndex(0, getAttr(attrName)) } - // See emitNilCheck in asserts.go. + // See createNilCheck in asserts.go. c.mod.NamedFunction("runtime.isnil").AddAttributeAtIndex(1, nocapture) // On *nix systems, the "abort" functuion in libc is used to handle fatal panics. @@ -1135,7 +1135,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { case *ssa.Store: llvmAddr := frame.getValue(instr.Addr) llvmVal := frame.getValue(instr.Val) - c.emitNilCheck(frame, llvmAddr, "store") + frame.createNilCheck(llvmAddr, "store") if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 { // nothing to store return @@ -1392,7 +1392,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e // This is a func value, which cannot be called directly. We have to // extract the function pointer and context first from the func value. funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) - c.emitNilCheck(frame, funcPtr, "fpcall") + frame.createNilCheck(funcPtr, "fpcall") return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil } } @@ -1524,7 +1524,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // > For an operand x of type T, the address operation &x generates a // > pointer of type *T to x. [...] If the evaluation of x would cause a // > run-time panic, then the evaluation of &x does too. - c.emitNilCheck(frame, val, "gep") + frame.createNilCheck(val, "gep") // Do a GEP on the pointer to get the field address. indices := []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), @@ -1542,7 +1542,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.uintptrType, uint64(arrayLen), false) - c.emitLookupBoundsCheck(frame, arrayLenLLVM, index, expr.Index.Type()) + frame.createLookupBoundsCheck(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. @@ -1572,7 +1572,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // > generates a pointer of type *T to x. [...] If the // > evaluation of x would cause a run-time panic, then the // > evaluation of &x does too. - c.emitNilCheck(frame, bufptr, "gep") + frame.createNilCheck(bufptr, "gep") default: return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+typ.String()) } @@ -1584,7 +1584,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } // Bounds check. - c.emitLookupBoundsCheck(frame, buflen, index, expr.Index.Type()) + frame.createLookupBoundsCheck(buflen, index, expr.Index.Type()) switch expr.X.Type().Underlying().(type) { case *types.Pointer: @@ -1610,7 +1610,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Bounds check. length := c.builder.CreateExtractValue(value, 1, "len") - c.emitLookupBoundsCheck(frame, length, index, expr.Index.Type()) + frame.createLookupBoundsCheck(length, index, expr.Index.Type()) // Lookup byte buf := c.builder.CreateExtractValue(value, 0, "") @@ -1654,7 +1654,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Bounds checking. lenType := expr.Len.Type().(*types.Basic) capType := expr.Cap.Type().(*types.Basic) - c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType) + frame.createSliceBoundsCheck(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()) @@ -1792,7 +1792,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { low, } - c.emitSliceBoundsCheck(frame, llvmLen, low, high, max, lowType, highType, maxType) + frame.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. @@ -1832,7 +1832,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { max = oldCap } - c.emitSliceBoundsCheck(frame, oldCap, low, high, max, lowType, highType, maxType) + frame.createSliceBoundsCheck(oldCap, low, high, max, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. @@ -1875,7 +1875,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = oldLen } - c.emitSliceBoundsCheck(frame, oldLen, low, high, high, lowType, highType, maxType) + frame.createSliceBoundsCheck(oldLen, low, high, high, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. @@ -2559,7 +2559,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { } return c.builder.CreateBitCast(fn, c.i8ptrType, ""), nil } else { - c.emitNilCheck(frame, x, "deref") + frame.createNilCheck(x, "deref") load := c.builder.CreateLoad(x, "") return load, nil } diff --git a/compiler/volatile.go b/compiler/volatile.go index ef957d9a..237d5546 100644 --- a/compiler/volatile.go +++ b/compiler/volatile.go @@ -10,7 +10,7 @@ import ( func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { addr := frame.getValue(instr.Args[0]) - c.emitNilCheck(frame, addr, "deref") + frame.createNilCheck(addr, "deref") val := c.builder.CreateLoad(addr, "") val.SetVolatile(true) return val, nil @@ -19,7 +19,7 @@ func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.V func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { addr := frame.getValue(instr.Args[0]) val := frame.getValue(instr.Args[1]) - c.emitNilCheck(frame, addr, "deref") + frame.createNilCheck(addr, "deref") store := c.builder.CreateStore(val, addr) store.SetVolatile(true) return llvm.Value{}, nil