diff options
author | Ayke van Laethem <[email protected]> | 2020-03-18 17:38:24 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2020-03-21 15:45:25 +0100 |
commit | 2f88c7aab4f4b85213a575017b055eb3d86562cd (patch) | |
tree | 2a4c0108fb83d4128ce9ae1f2e7d0002c1501e80 /compiler | |
parent | 599670cef6d2fce10969b9e8aec3d6344e5865b8 (diff) | |
download | tinygo-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.go | 48 | ||||
-rw-r--r-- | compiler/optimizer.go | 5 |
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 } } |