mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
137 lines
4.3 KiB
Go
137 lines
4.3 KiB
Go
package compiler
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
func (c *Compiler) checkType(t llvm.Type, checked map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) {
|
|
if t.IsNil() {
|
|
panic(t)
|
|
}
|
|
|
|
// prevent infinite recursion for self-referential types
|
|
if checked == nil {
|
|
// base of type-check stack
|
|
checked = map[llvm.Type]struct{}{}
|
|
}
|
|
if _, ok := checked[t]; ok {
|
|
return
|
|
}
|
|
checked[t] = struct{}{}
|
|
|
|
// check for any context mismatches
|
|
switch {
|
|
case t.Context() == c.ctx:
|
|
// this is correct
|
|
case t.Context() == llvm.GlobalContext():
|
|
// somewhere we accidentally used the global context instead of a real context
|
|
panic(fmt.Errorf("type %q uses global context", t.String()))
|
|
default:
|
|
// we used some other context by accident
|
|
panic(fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx))
|
|
}
|
|
|
|
// if this is a composite type, check the components of the type
|
|
switch t.TypeKind() {
|
|
case llvm.VoidTypeKind, llvm.LabelTypeKind, llvm.TokenTypeKind, llvm.MetadataTypeKind:
|
|
// there should only be one of any of these
|
|
if s, ok := specials[t.TypeKind()]; !ok {
|
|
specials[t.TypeKind()] = t
|
|
} else if s != t {
|
|
panic(fmt.Errorf("duplicate special type %q: %v and %v", t.TypeKind().String(), t, s))
|
|
}
|
|
case llvm.FloatTypeKind, llvm.DoubleTypeKind, llvm.X86_FP80TypeKind, llvm.FP128TypeKind, llvm.PPC_FP128TypeKind:
|
|
// floating point numbers are primitives - nothing to recurse
|
|
case llvm.IntegerTypeKind:
|
|
// integers are primitives - nothing to recurse
|
|
case llvm.FunctionTypeKind:
|
|
// check arguments and return(s)
|
|
for _, v := range t.ParamTypes() {
|
|
c.checkType(v, checked, specials)
|
|
}
|
|
c.checkType(t.ReturnType(), checked, specials)
|
|
case llvm.StructTypeKind:
|
|
// check all elements
|
|
for _, v := range t.StructElementTypes() {
|
|
c.checkType(v, checked, specials)
|
|
}
|
|
case llvm.ArrayTypeKind:
|
|
// check element type
|
|
c.checkType(t.ElementType(), checked, specials)
|
|
case llvm.PointerTypeKind:
|
|
// check underlying type
|
|
c.checkType(t.ElementType(), checked, specials)
|
|
case llvm.VectorTypeKind:
|
|
// check element type
|
|
c.checkType(t.ElementType(), checked, specials)
|
|
}
|
|
}
|
|
|
|
func (c *Compiler) checkValue(v llvm.Value, checked map[llvm.Value]struct{}, specials map[llvm.TypeKind]llvm.Type) {
|
|
// avoid infinite recursion by marking values that are being checked
|
|
// values are left in so that this also avoids checking a value twice
|
|
if _, ok := checked[v]; ok {
|
|
return
|
|
}
|
|
checked[v] = struct{}{}
|
|
|
|
// check type
|
|
c.checkType(v.Type(), nil, specials)
|
|
}
|
|
|
|
func (c *Compiler) checkInstruction(inst llvm.Value, checked map[llvm.Value]struct{}, specials map[llvm.TypeKind]llvm.Type) {
|
|
// check value properties
|
|
c.checkValue(inst, checked, specials)
|
|
|
|
// check operands
|
|
for i := 0; i < inst.OperandsCount(); i++ {
|
|
c.checkValue(inst.Operand(i), checked, specials)
|
|
}
|
|
}
|
|
|
|
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, checked map[llvm.Value]struct{}, specials map[llvm.TypeKind]llvm.Type) {
|
|
// check basic block value and type
|
|
c.checkValue(bb.AsValue(), checked, specials)
|
|
|
|
// check instructions
|
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
|
c.checkInstruction(inst, checked, specials)
|
|
}
|
|
}
|
|
|
|
func (c *Compiler) checkFunction(fn llvm.Value, checked map[llvm.Value]struct{}, specials map[llvm.TypeKind]llvm.Type) {
|
|
// check function value and type
|
|
c.checkValue(fn, checked, specials)
|
|
|
|
// check basic blocks
|
|
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
|
c.checkBasicBlock(bb, checked, specials)
|
|
}
|
|
}
|
|
|
|
func (c *Compiler) check() {
|
|
// check for any context mismatches
|
|
switch {
|
|
case c.mod.Context() == c.ctx:
|
|
// this is correct
|
|
case c.mod.Context() == llvm.GlobalContext():
|
|
// somewhere we accidentally used the global context instead of a real context
|
|
panic(errors.New("module uses global context"))
|
|
default:
|
|
// we used some other context by accident
|
|
panic(fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx))
|
|
}
|
|
|
|
checked := map[llvm.Value]struct{}{}
|
|
specials := map[llvm.TypeKind]llvm.Type{}
|
|
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
|
c.checkFunction(fn, checked, specials)
|
|
}
|
|
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
|
c.checkValue(g, checked, specials)
|
|
}
|
|
}
|