compiler: move IR checker to separate package
This is a preparation for moving the Optimize function to the transform package.
Этот коммит содержится в:
родитель
599670cef6
коммит
2f88c7aab4
3 изменённых файлов: 74 добавлений и 21 удалений
|
@ -1,7 +1,8 @@
|
||||||
package compiler
|
// 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 file implements a set of sanity checks for the IR that is generated.
|
// this is not a replacement for the LLVM verifier but does catch things that
|
||||||
// It can catch some mistakes that LLVM's verifier cannot.
|
// the LLVM verifier doesn't catch.
|
||||||
|
package ircheck
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -10,7 +11,11 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"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
|
// prevent infinite recursion for self-referential types
|
||||||
if _, ok := checked[t]; ok {
|
if _, ok := checked[t]; ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -81,7 +86,7 @@ func (c *Compiler) checkType(t llvm.Type, checked map[llvm.Type]struct{}, specia
|
||||||
return nil
|
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
|
// check type
|
||||||
if err := c.checkType(v.Type(), types, specials); err != nil {
|
if err := c.checkType(v.Type(), types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to verify type of value: %s", err.Error())
|
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
|
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
|
// check value properties
|
||||||
if err := c.checkValue(inst, types, specials); err != nil {
|
if err := c.checkValue(inst, types, specials); err != nil {
|
||||||
return errorAt(inst, err.Error())
|
return errorAt(inst, err.Error())
|
||||||
|
@ -129,7 +134,7 @@ func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{
|
||||||
return nil
|
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
|
// check basic block value and type
|
||||||
var errs []error
|
var errs []error
|
||||||
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
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
|
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
|
// check function value and type
|
||||||
var errs []error
|
var errs []error
|
||||||
if err := c.checkValue(fn, types, specials); err != nil {
|
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
|
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
|
// check for any context mismatches
|
||||||
var errs []error
|
var errs []error
|
||||||
switch {
|
c := checker{
|
||||||
case c.mod.Context() == c.ctx:
|
ctx: mod.Context(),
|
||||||
// this is correct
|
}
|
||||||
case c.mod.Context() == llvm.GlobalContext():
|
if c.ctx == llvm.GlobalContext() {
|
||||||
// somewhere we accidentally used the global context instead of a real context
|
// somewhere we accidentally used the global context instead of a real context
|
||||||
errs = append(errs, errors.New("module uses global 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{}{}
|
types := map[llvm.Type]struct{}{}
|
||||||
specials := map[llvm.TypeKind]llvm.Type{}
|
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)...)
|
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 {
|
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()))
|
errs = append(errs, fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error()))
|
||||||
}
|
}
|
48
compiler/ircheck/errors.go
Обычный файл
48
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{}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package compiler
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/ircheck"
|
||||||
"github.com/tinygo-org/tinygo/transform"
|
"github.com/tinygo-org/tinygo/transform"
|
||||||
"tinygo.org/x/go-llvm"
|
"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
|
// run a check of all of our code
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
errs := c.checkModule()
|
errs := ircheck.Module(c.mod)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -131,7 +132,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
if errs := c.checkModule(); errs != nil {
|
if errs := ircheck.Module(c.mod); errs != nil {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче