diff --git a/compiler/asserts.go b/compiler/asserts.go index 6b254781..7aeb0fe0 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -8,6 +8,7 @@ import ( "go/token" "go/types" + "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) @@ -151,12 +152,20 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, // 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 (b *builder) createNilCheck(ptr llvm.Value, blockPrefix string) { +func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix string) { // Check whether we need to emit this check at all. if !ptr.IsAGlobalValue().IsNil() { return } + switch inst.(type) { + case *ssa.IndexAddr: + // This pointer is the result of an index operation into a slice or + // array. Such slices/arrays are already bounds checked so the pointer + // must be a valid (non-nil) pointer. No nil checking is necessary. + return + } + // Compare against nil. var isnil llvm.Value if ptr.Type().PointerAddressSpace() == 0 { diff --git a/compiler/compiler.go b/compiler/compiler.go index 6a17da79..46dc70d8 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1154,7 +1154,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) { case *ssa.Store: llvmAddr := b.getValue(instr.Addr) llvmVal := b.getValue(instr.Val) - b.createNilCheck(llvmAddr, "store") + b.createNilCheck(instr.Addr, llvmAddr, "store") if b.targetData.TypeAllocSize(llvmVal.Type()) == 0 { // nothing to store return @@ -1402,7 +1402,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) // This is a func value, which cannot be called directly. We have to // extract the function pointer and context first from the func value. callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) - b.createNilCheck(callee, "fpcall") + b.createNilCheck(instr.Value, callee, "fpcall") } var params []llvm.Value @@ -1548,7 +1548,7 @@ func (b *builder) createExpr(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. - b.createNilCheck(val, "gep") + b.createNilCheck(expr.X, val, "gep") // Do a GEP on the pointer to get the field address. indices := []llvm.Value{ llvm.ConstInt(b.ctx.Int32Type(), 0, false), @@ -1596,7 +1596,7 @@ func (b *builder) createExpr(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. - b.createNilCheck(bufptr, "gep") + b.createNilCheck(expr.X, bufptr, "gep") default: return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+typ.String()) } @@ -2588,7 +2588,7 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) { } return b.CreateBitCast(fn, b.i8ptrType, ""), nil } else { - b.createNilCheck(x, "deref") + b.createNilCheck(unop.X, x, "deref") load := b.CreateLoad(x, "") return load, nil } diff --git a/compiler/volatile.go b/compiler/volatile.go index d2e51c84..0c945874 100644 --- a/compiler/volatile.go +++ b/compiler/volatile.go @@ -12,7 +12,7 @@ import ( // runtime/volatile.LoadT(). func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) { addr := b.getValue(instr.Args[0]) - b.createNilCheck(addr, "deref") + b.createNilCheck(instr.Args[0], addr, "deref") val := b.CreateLoad(addr, "") val.SetVolatile(true) return val, nil @@ -23,7 +23,7 @@ func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) { addr := b.getValue(instr.Args[0]) val := b.getValue(instr.Args[1]) - b.createNilCheck(addr, "deref") + b.createNilCheck(instr.Args[0], addr, "deref") store := b.CreateStore(val, addr) store.SetVolatile(true) return llvm.Value{}, nil