From 3edcdb5f0d057363b4270195f99165a3825cde48 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 25 May 2021 23:39:28 +0200 Subject: [PATCH] compiler: do not emit nil checks for loading closure variables Closure variables are allocated in a parent function and are thus never nil. Don't do a nil check before reading or modifying the value. This commit results in a slight reduction in code size in some test cases: calls.go, channel.go, goroutines.go, json.go, sort.go - presumably wherever closures are used. --- compiler/asserts.go | 4 ++++ compiler/compiler.go | 2 ++ compiler/testdata/goroutine-cortex-m-qemu.ll | 10 ---------- compiler/testdata/goroutine-wasm.ll | 10 ---------- transform/testdata/allocs2.go | 9 ++++++++- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/compiler/asserts.go b/compiler/asserts.go index 1559da76..01e7a402 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -162,6 +162,10 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str case *ssa.Alloc: // An alloc is never nil. return + case *ssa.FreeVar: + // A free variable is allocated in a parent function and is thus never + // nil. + return 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 diff --git a/compiler/compiler.go b/compiler/compiler.go index 9082d396..4a5092d0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -994,6 +994,8 @@ func getPos(val posser) token.Pos { switch val := val.(type) { case *ssa.MakeInterface: return getPos(val.X) + case *ssa.MakeClosure: + return val.Fn.(*ssa.Function).Pos() case *ssa.Return: syntax := val.Parent().Syntax() if syntax != nil { diff --git a/compiler/testdata/goroutine-cortex-m-qemu.ll b/compiler/testdata/goroutine-cortex-m-qemu.ll index 7ddfe569..3a263fec 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu.ll @@ -86,14 +86,6 @@ entry: define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr { entry: - %0 = icmp eq i8* %context, null - br i1 %0, label %store.throw, label %store.next - -store.throw: ; preds = %entry - call void @runtime.nilPanic(i8* undef, i8* null) - unreachable - -store.next: ; preds = %entry %unpack.ptr = bitcast i8* %context to i32* store i32 7, i32* %unpack.ptr, align 4 ret void @@ -112,8 +104,6 @@ entry: declare void @runtime.printint32(i32, i8*, i8*) -declare void @runtime.nilPanic(i8*, i8*) - define hidden void @main.funcGoroutine(i8* %fn.context, void (i32, i8*, i8*)* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr { entry: %0 = call i8* @runtime.alloc(i32 12, i8* undef, i8* null) diff --git a/compiler/testdata/goroutine-wasm.ll b/compiler/testdata/goroutine-wasm.ll index c9607185..212bd714 100644 --- a/compiler/testdata/goroutine-wasm.ll +++ b/compiler/testdata/goroutine-wasm.ll @@ -62,14 +62,6 @@ entry: define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr { entry: - %0 = icmp eq i8* %context, null - br i1 %0, label %store.throw, label %store.next - -store.throw: ; preds = %entry - call void @runtime.nilPanic(i8* undef, i8* null) - unreachable - -store.next: ; preds = %entry %unpack.ptr = bitcast i8* %context to i32* store i32 7, i32* %unpack.ptr, align 4 ret void @@ -77,8 +69,6 @@ store.next: ; preds = %entry declare void @runtime.printint32(i32, i8*, i8*) -declare void @runtime.nilPanic(i8*, i8*) - define hidden void @main.funcGoroutine(i8* %fn.context, i32 %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr { entry: %0 = call i32 @runtime.getFuncPtr(i8* %fn.context, i32 %fn.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) diff --git a/transform/testdata/allocs2.go b/transform/testdata/allocs2.go index aeddcf6b..bf39fdef 100644 --- a/transform/testdata/allocs2.go +++ b/transform/testdata/allocs2.go @@ -33,7 +33,7 @@ func main() { c1 := getComplex128() // OUT: object allocated on the heap: escapes at line 34 useInterface(c1) - n3 := 5 // OUT: object allocated on the heap: escapes at line 39 + n3 := 5 func() int { return n3 }() @@ -42,6 +42,13 @@ func main() { s8 := []int{3, 5, 8} // OUT: object allocated on the heap: escapes at line 44 callVariadic(s8...) + + n4 := 3 // OUT: object allocated on the heap: escapes at line 48 + n5 := 7 // OUT: object allocated on the heap: escapes at line 48 + func() { + n4 = n5 + }() + println(n4, n5) } func derefInt(x *int) int {