aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler/asserts.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-03-07 13:33:46 +0100
committerRon Evans <[email protected]>2019-03-08 17:36:53 +0100
commit622d0ebde671a4c71fc771ef8f9210ff433c9fbe (patch)
tree00724eda34c158ffe776d5ec1829159425834ec1 /compiler/asserts.go
parentb7cdf8cd0c1f66da9e6c4392ce5acbab911892c5 (diff)
downloadtinygo-622d0ebde671a4c71fc771ef8f9210ff433c9fbe.tar.gz
tinygo-622d0ebde671a4c71fc771ef8f9210ff433c9fbe.zip
compiler: implement nil checks
This commit implements nil checks for all platforms. These nil checks can be optimized on systems with a MMU, but since a major target is systems without MMU, keep it this way for now. It implements three checks: * Nil checks before dereferencing a pointer. * Nil checks before calculating an address (*ssa.FieldAddr and *ssa.IndexAddr) * Nil checks before calling a function pointer. The first check has by far the biggest impact, with around 5% increase in code size. The other checks only trigger in only some test cases and have a minimal impact on code size. This first nil check is also the one that is easiest to avoid on systems with MMU, if necessary.
Diffstat (limited to 'compiler/asserts.go')
-rw-r--r--compiler/asserts.go31
1 files changed, 31 insertions, 0 deletions
diff --git a/compiler/asserts.go b/compiler/asserts.go
new file mode 100644
index 000000000..4c09264ae
--- /dev/null
+++ b/compiler/asserts.go
@@ -0,0 +1,31 @@
+package compiler
+
+// This file implements functions that do certain safety checks that are
+// required by the Go programming language.
+
+import (
+ "tinygo.org/x/go-llvm"
+)
+
+// 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
+// pointer dereferences exist in valid Go code.
+func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
+ // Check whether this is a nil pointer.
+ faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
+ nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
+ frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
+
+ // Compare against nil.
+ nilptr := llvm.ConstPointerNull(ptr.Type())
+ isnil := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
+ c.builder.CreateCondBr(isnil, faultBlock, nextBlock)
+
+ // Fail: this is a nil pointer, exit with a panic.
+ c.builder.SetInsertPointAtEnd(faultBlock)
+ c.createRuntimeCall("nilpanic", nil, "")
+ c.builder.CreateUnreachable()
+
+ // Ok: this is a valid pointer.
+ c.builder.SetInsertPointAtEnd(nextBlock)
+}