aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-03-18 17:38:24 +0100
committerRon Evans <[email protected]>2020-03-21 15:45:25 +0100
commit2f88c7aab4f4b85213a575017b055eb3d86562cd (patch)
tree2a4c0108fb83d4128ce9ae1f2e7d0002c1501e80 /compiler
parent599670cef6d2fce10969b9e8aec3d6344e5865b8 (diff)
downloadtinygo-2f88c7aab4f4b85213a575017b055eb3d86562cd.tar.gz
tinygo-2f88c7aab4f4b85213a575017b055eb3d86562cd.zip
compiler: move IR checker to separate package
This is a preparation for moving the Optimize function to the transform package.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ircheck/check.go (renamed from compiler/check.go)42
-rw-r--r--compiler/ircheck/errors.go48
-rw-r--r--compiler/optimizer.go5
3 files changed, 74 insertions, 21 deletions
diff --git a/compiler/check.go b/compiler/ircheck/check.go
index 5372b1efc..bbedc6273 100644
--- a/compiler/check.go
+++ b/compiler/ircheck/check.go
@@ -1,7 +1,8 @@
-package compiler
-
-// This file implements a set of sanity checks for the IR that is generated.
-// It can catch some mistakes that LLVM's verifier cannot.
+// Package ircheck implements a checker for LLVM IR, that goes a bit further
+// than the regular LLVM IR verifier. Note that it checks different things, so
+// this is not a replacement for the LLVM verifier but does catch things that
+// the LLVM verifier doesn't catch.
+package ircheck
import (
"errors"
@@ -10,7 +11,11 @@ import (
"tinygo.org/x/go-llvm"
)
-func (c *Compiler) checkType(t llvm.Type, checked map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
+type checker struct {
+ ctx llvm.Context
+}
+
+func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
// prevent infinite recursion for self-referential types
if _, ok := checked[t]; ok {
return nil
@@ -81,7 +86,7 @@ func (c *Compiler) checkType(t llvm.Type, checked map[llvm.Type]struct{}, specia
return nil
}
-func (c *Compiler) checkValue(v llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
+func (c *checker) checkValue(v llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
// check type
if err := c.checkType(v.Type(), types, specials); err != nil {
return fmt.Errorf("failed to verify type of value: %s", err.Error())
@@ -95,7 +100,7 @@ func (c *Compiler) checkValue(v llvm.Value, types map[llvm.Type]struct{}, specia
return nil
}
-func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
+func (c *checker) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
// check value properties
if err := c.checkValue(inst, types, specials); err != nil {
return errorAt(inst, err.Error())
@@ -129,7 +134,7 @@ func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{
return nil
}
-func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
+func (c *checker) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
// check basic block value and type
var errs []error
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
@@ -146,7 +151,7 @@ func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struc
return errs
}
-func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
+func (c *checker) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
// check function value and type
var errs []error
if err := c.checkValue(fn, types, specials); err != nil {
@@ -161,26 +166,25 @@ func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, sp
return errs
}
-func (c *Compiler) checkModule() []error {
+// Module checks the given module and returns a slice of error, if there are
+// any.
+func Module(mod llvm.Module) []error {
// check for any context mismatches
var errs []error
- switch {
- case c.mod.Context() == c.ctx:
- // this is correct
- case c.mod.Context() == llvm.GlobalContext():
+ c := checker{
+ ctx: mod.Context(),
+ }
+ if c.ctx == llvm.GlobalContext() {
// somewhere we accidentally used the global context instead of a real context
errs = append(errs, errors.New("module uses global context"))
- default:
- // we used some other context by accident
- errs = append(errs, fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx))
}
types := map[llvm.Type]struct{}{}
specials := map[llvm.TypeKind]llvm.Type{}
- for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
+ for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
errs = append(errs, c.checkFunction(fn, types, specials)...)
}
- for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
+ for g := mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
if err := c.checkValue(g, types, specials); err != nil {
errs = append(errs, fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error()))
}
diff --git a/compiler/ircheck/errors.go b/compiler/ircheck/errors.go
new file mode 100644
index 000000000..d2a013933
--- /dev/null
+++ b/compiler/ircheck/errors.go
@@ -0,0 +1,48 @@
+package ircheck
+
+import (
+ "go/scanner"
+ "go/token"
+ "path/filepath"
+
+ "tinygo.org/x/go-llvm"
+)
+
+// errorAt returns an error value at the location of the instruction.
+// The location information may not be complete as it depends on debug
+// information in the IR.
+func errorAt(inst llvm.Value, msg string) scanner.Error {
+ return scanner.Error{
+ Pos: getPosition(inst),
+ Msg: msg,
+ }
+}
+
+// getPosition returns the position information for the given value, as far as
+// it is available.
+func getPosition(val llvm.Value) token.Position {
+ if !val.IsAInstruction().IsNil() {
+ loc := val.InstructionDebugLoc()
+ if loc.IsNil() {
+ return token.Position{}
+ }
+ file := loc.LocationScope().ScopeFile()
+ return token.Position{
+ Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
+ Line: int(loc.LocationLine()),
+ Column: int(loc.LocationColumn()),
+ }
+ } else if !val.IsAFunction().IsNil() {
+ loc := val.Subprogram()
+ if loc.IsNil() {
+ return token.Position{}
+ }
+ file := loc.ScopeFile()
+ return token.Position{
+ Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
+ Line: int(loc.SubprogramLine()),
+ }
+ } else {
+ return token.Position{}
+ }
+}
diff --git a/compiler/optimizer.go b/compiler/optimizer.go
index 5402fa18d..35438b4e3 100644
--- a/compiler/optimizer.go
+++ b/compiler/optimizer.go
@@ -3,6 +3,7 @@ package compiler
import (
"errors"
+ "github.com/tinygo-org/tinygo/compiler/ircheck"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
)
@@ -25,7 +26,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
// run a check of all of our code
if c.VerifyIR() {
- errs := c.checkModule()
+ errs := ircheck.Module(c.mod)
if errs != nil {
return errs
}
@@ -131,7 +132,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
}
if c.VerifyIR() {
- if errs := c.checkModule(); errs != nil {
+ if errs := ircheck.Module(c.mod); errs != nil {
return errs
}
}