compiler: refactor IR generation
This is the first commit in a series to refactor the compiler. The intention is to make sure every function to be compiled eventually has its own IR builder. This will make it much easier to do other refactorings in the future: * Most code won't depend (directly) on the central Compiler object, perhaps making it possible to eliminate it in the future. Right now it's embedded in the `builder` struct but individual fields from the `Compiler` can easily be moved into the `builder` object. * Some functions are not directly exposed in Go SSA, they are wrapper functions for something. At the moment they are included in the list of functions to be compiled with the reachability analysis (SimpleDCE) in the ir package, but eventually this reachability analys will be removed. At that point, it would be very convenient to be able to simply build a function with a new IR builder. The `compilerContext` struct makes sure that it is not possible for `builder` methods to accidentally use global state such as the global IR builder. It is a transitional mechanism and may be removed when finished.
Этот коммит содержится в:
родитель
8c86eae924
коммит
b5e29bf0c1
5 изменённых файлов: 115 добавлений и 77 удалений
|
@ -43,10 +43,10 @@ func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llv
|
||||||
|
|
||||||
// Expand an argument type to a list that can be used in a function call
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// parameter list.
|
||||||
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
func expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := c.flattenAggregateType(t)
|
fields := flattenAggregateType(t)
|
||||||
if len(fields) <= MaxFieldsPerParam {
|
if len(fields) <= MaxFieldsPerParam {
|
||||||
return fields
|
return fields
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,7 +82,7 @@ func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
||||||
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldTypes := c.flattenAggregateType(v.Type())
|
fieldTypes := flattenAggregateType(v.Type())
|
||||||
if len(fieldTypes) <= MaxFieldsPerParam {
|
if len(fieldTypes) <= MaxFieldsPerParam {
|
||||||
fields := c.flattenAggregate(v)
|
fields := c.flattenAggregate(v)
|
||||||
if len(fields) != len(fieldTypes) {
|
if len(fields) != len(fieldTypes) {
|
||||||
|
@ -101,12 +101,12 @@ func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
|
|
||||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||||
// with the passed in type if this is not possible.
|
// with the passed in type if this is not possible.
|
||||||
func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
|
func flattenAggregateType(t llvm.Type) []llvm.Type {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
|
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
|
||||||
for _, subfield := range t.StructElementTypes() {
|
for _, subfield := range t.StructElementTypes() {
|
||||||
subfields := c.flattenAggregateType(subfield)
|
subfields := flattenAggregateType(subfield)
|
||||||
fields = append(fields, subfields...)
|
fields = append(fields, subfields...)
|
||||||
}
|
}
|
||||||
return fields
|
return fields
|
||||||
|
@ -166,7 +166,7 @@ func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Va
|
||||||
func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
|
if len(flattenAggregateType(t)) <= MaxFieldsPerParam {
|
||||||
value := llvm.ConstNull(t)
|
value := llvm.ConstNull(t)
|
||||||
for i, subtyp := range t.StructElementTypes() {
|
for i, subtyp := range t.StructElementTypes() {
|
||||||
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
|
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
|
||||||
|
|
|
@ -34,11 +34,13 @@ func init() {
|
||||||
// The TinyGo import path.
|
// The TinyGo import path.
|
||||||
const tinygoPath = "github.com/tinygo-org/tinygo"
|
const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||||
|
|
||||||
type Compiler struct {
|
// compilerContext contains function-independent data that should still be
|
||||||
|
// available while compiling every function. It is not strictly read-only, but
|
||||||
|
// must not contain function-dependent data such as an IR builder.
|
||||||
|
type compilerContext struct {
|
||||||
*compileopts.Config
|
*compileopts.Config
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
builder llvm.Builder
|
|
||||||
dibuilder *llvm.DIBuilder
|
dibuilder *llvm.DIBuilder
|
||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
|
@ -49,14 +51,26 @@ type Compiler struct {
|
||||||
i8ptrType llvm.Type // for convenience
|
i8ptrType llvm.Type // for convenience
|
||||||
funcPtrAddrSpace int
|
funcPtrAddrSpace int
|
||||||
uintptrType llvm.Type
|
uintptrType llvm.Type
|
||||||
initFuncs []llvm.Value
|
|
||||||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
|
||||||
ir *ir.Program
|
ir *ir.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Compiler struct {
|
||||||
|
compilerContext
|
||||||
|
builder llvm.Builder
|
||||||
|
initFuncs []llvm.Value
|
||||||
|
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
// builder contains all information relevant to build a single function.
|
||||||
|
type builder struct {
|
||||||
|
*compilerContext
|
||||||
|
llvm.Builder
|
||||||
fn *ir.Function
|
fn *ir.Function
|
||||||
locals map[ssa.Value]llvm.Value // local variables
|
locals map[ssa.Value]llvm.Value // local variables
|
||||||
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
||||||
|
@ -81,9 +95,11 @@ type Phi struct {
|
||||||
|
|
||||||
func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) {
|
func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) {
|
||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
|
compilerContext: compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[types.Type]llvm.Metadata),
|
ditypes: make(map[types.Type]llvm.Metadata),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := llvm.GetTargetFromTriple(config.Triple())
|
target, err := llvm.GetTargetFromTriple(config.Triple())
|
||||||
|
@ -252,6 +268,20 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
|
|
||||||
c.loadASTComments(lprogram)
|
c.loadASTComments(lprogram)
|
||||||
|
|
||||||
|
// Declare runtime types.
|
||||||
|
// TODO: lazily create runtime types in getLLVMRuntimeType when they are
|
||||||
|
// needed. Eventually this will be required anyway, when packages are
|
||||||
|
// compiled independently (and the runtime types are not available).
|
||||||
|
for _, member := range c.ir.Program.ImportedPackage("runtime").Members {
|
||||||
|
if member, ok := member.(*ssa.Type); ok {
|
||||||
|
if typ, ok := member.Type().(*types.Named); ok {
|
||||||
|
if _, ok := typ.Underlying().(*types.Struct); ok {
|
||||||
|
c.getLLVMType(typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Declare all functions.
|
// Declare all functions.
|
||||||
for _, f := range c.ir.Functions {
|
for _, f := range c.ir.Functions {
|
||||||
frames = append(frames, c.parseFuncDecl(f))
|
frames = append(frames, c.parseFuncDecl(f))
|
||||||
|
@ -367,23 +397,23 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
return c.diagnostics
|
return c.diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRuntimeType obtains a named type from the runtime package and returns it
|
|
||||||
// as a Go type.
|
|
||||||
func (c *Compiler) getRuntimeType(name string) types.Type {
|
|
||||||
return c.ir.Program.ImportedPackage("runtime").Type(name).Type()
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
||||||
// getLLVMType(getRuntimeType(name)).
|
// getLLVMType(getRuntimeType(name)).
|
||||||
func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type {
|
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||||
return c.getLLVMType(c.getRuntimeType(name))
|
fullName := "runtime." + name
|
||||||
|
typ := c.mod.GetTypeByName(fullName)
|
||||||
|
if typ.IsNil() {
|
||||||
|
println(c.mod.String())
|
||||||
|
panic("could not find runtime type: " + fullName)
|
||||||
|
}
|
||||||
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||||
// named struct types (or Go types implemented as named LLVM structs such as
|
// named struct types (or Go types implemented as named LLVM structs such as
|
||||||
// strings) it also creates it first if necessary.
|
// strings) it also creates it first if necessary.
|
||||||
func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
|
@ -705,11 +735,15 @@ func (c *Compiler) getLocalVariable(frame *Frame, variable *types.Var) llvm.Meta
|
||||||
|
|
||||||
func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame {
|
func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame {
|
||||||
frame := &Frame{
|
frame := &Frame{
|
||||||
|
builder: builder{
|
||||||
|
compilerContext: &c.compilerContext,
|
||||||
|
Builder: c.builder, // TODO: use a separate builder per function
|
||||||
fn: f,
|
fn: f,
|
||||||
locals: make(map[ssa.Value]llvm.Value),
|
locals: make(map[ssa.Value]llvm.Value),
|
||||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var retType llvm.Type
|
var retType llvm.Type
|
||||||
|
@ -728,7 +762,7 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame {
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
for _, param := range f.Params {
|
for _, param := range f.Params {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramTypeFragments := c.expandFormalParamType(paramType)
|
paramTypeFragments := expandFormalParamType(paramType)
|
||||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
paramTypes = append(paramTypes, paramTypeFragments...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,7 +905,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
for _, param := range frame.fn.Params {
|
for _, param := range frame.fn.Params {
|
||||||
llvmType := c.getLLVMType(param.Type())
|
llvmType := c.getLLVMType(param.Type())
|
||||||
fields := make([]llvm.Value, 0, 1)
|
fields := make([]llvm.Value, 0, 1)
|
||||||
for range c.expandFormalParamType(llvmType) {
|
for range expandFormalParamType(llvmType) {
|
||||||
fields = append(fields, frame.fn.LLVMFn.Param(llvmParamIndex))
|
fields = append(fields, frame.fn.LLVMFn.Param(llvmParamIndex))
|
||||||
llvmParamIndex++
|
llvmParamIndex++
|
||||||
}
|
}
|
||||||
|
@ -1368,7 +1402,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||||
func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value {
|
func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value {
|
||||||
switch expr := expr.(type) {
|
switch expr := expr.(type) {
|
||||||
case *ssa.Const:
|
case *ssa.Const:
|
||||||
return c.parseConst(frame.fn.LinkName(), expr)
|
return frame.createConst(frame.fn.LinkName(), expr)
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
fn := c.ir.GetFunction(expr)
|
fn := c.ir.GetFunction(expr)
|
||||||
if fn.IsExported() {
|
if fn.IsExported() {
|
||||||
|
@ -2211,10 +2245,11 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
// createConst creates a LLVM constant value from a Go constant.
|
||||||
|
func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||||
switch typ := expr.Type().Underlying().(type) {
|
switch typ := expr.Type().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
llvmType := c.getLLVMType(typ)
|
llvmType := b.getLLVMType(typ)
|
||||||
if typ.Info()&types.IsBoolean != 0 {
|
if typ.Info()&types.IsBoolean != 0 {
|
||||||
b := constant.BoolVal(expr.Value)
|
b := constant.BoolVal(expr.Value)
|
||||||
n := uint64(0)
|
n := uint64(0)
|
||||||
|
@ -2224,23 +2259,23 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
} else if typ.Info()&types.IsString != 0 {
|
} else if typ.Info()&types.IsString != 0 {
|
||||||
str := constant.StringVal(expr.Value)
|
str := constant.StringVal(expr.Value)
|
||||||
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
|
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||||
objname := prefix + "$string"
|
objname := prefix + "$string"
|
||||||
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||||
global.SetInitializer(c.ctx.ConstString(str, false))
|
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
return strObj
|
return strObj
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
value, _ := constant.Uint64Val(expr.Value)
|
value, _ := constant.Uint64Val(expr.Value)
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(c.i8ptrType)
|
return llvm.ConstNull(b.i8ptrType)
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
n, _ := constant.Uint64Val(expr.Value)
|
n, _ := constant.Uint64Val(expr.Value)
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
|
@ -2251,18 +2286,18 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||||
n, _ := constant.Float64Val(expr.Value)
|
n, _ := constant.Float64Val(expr.Value)
|
||||||
return llvm.ConstFloat(llvmType, n)
|
return llvm.ConstFloat(llvmType, n)
|
||||||
} else if typ.Kind() == types.Complex64 {
|
} else if typ.Kind() == types.Complex64 {
|
||||||
r := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||||
i := c.parseConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
||||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx
|
return cplx
|
||||||
} else if typ.Kind() == types.Complex128 {
|
} else if typ.Kind() == types.Complex128 {
|
||||||
r := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||||
i := c.parseConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
||||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx
|
return cplx
|
||||||
} else {
|
} else {
|
||||||
panic("unknown constant of basic type: " + expr.String())
|
panic("unknown constant of basic type: " + expr.String())
|
||||||
|
@ -2271,35 +2306,35 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil chan constant")
|
panic("expected nil chan constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil signature constant")
|
panic("expected nil signature constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil interface constant")
|
panic("expected nil interface constant")
|
||||||
}
|
}
|
||||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||||
fields := []llvm.Value{
|
fields := []llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(b.uintptrType, 0, false),
|
||||||
llvm.ConstPointerNull(c.i8ptrType),
|
llvm.ConstPointerNull(b.i8ptrType),
|
||||||
}
|
}
|
||||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil pointer constant")
|
panic("expected nil pointer constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil slice constant")
|
panic("expected nil slice constant")
|
||||||
}
|
}
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := b.getLLVMType(typ.Elem())
|
||||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||||
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||||
llvmPtr, // backing array
|
llvmPtr, // backing array
|
||||||
llvmLen, // len
|
llvmLen, // len
|
||||||
llvmLen, // cap
|
llvmLen, // cap
|
||||||
|
@ -2310,7 +2345,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||||
// I believe this is not allowed by the Go spec.
|
// I believe this is not allowed by the Go spec.
|
||||||
panic("non-nil map constant")
|
panic("non-nil map constant")
|
||||||
}
|
}
|
||||||
llvmType := c.getLLVMType(typ)
|
llvmType := b.getLLVMType(typ)
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType)
|
||||||
default:
|
default:
|
||||||
panic("unknown constant: " + expr.String())
|
panic("unknown constant: " + expr.String())
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
|
// This file contains some utility functions related to error handling.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
@ -9,7 +11,8 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
// makeError makes it easy to create an error from a token.Pos with a message.
|
||||||
|
func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
|
||||||
return types.Error{
|
return types.Error{
|
||||||
Fset: c.ir.Program.Fset,
|
Fset: c.ir.Program.Fset,
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
|
|
|
@ -76,7 +76,7 @@ func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFuncType returns the type of a func value given a signature.
|
// getFuncType returns the type of a func value given a signature.
|
||||||
func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
switch c.FuncImplementation() {
|
switch c.FuncImplementation() {
|
||||||
case compileopts.FuncValueDoubleword:
|
case compileopts.FuncValueDoubleword:
|
||||||
rawPtr := c.getRawFuncType(typ)
|
rawPtr := c.getRawFuncType(typ)
|
||||||
|
@ -89,7 +89,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
||||||
func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||||
// Get the return type.
|
// Get the return type.
|
||||||
var returnType llvm.Type
|
var returnType llvm.Type
|
||||||
switch typ.Results().Len() {
|
switch typ.Results().Len() {
|
||||||
|
@ -119,11 +119,11 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||||
// The receiver is not an interface, but a i8* type.
|
// The receiver is not an interface, but a i8* type.
|
||||||
recv = c.i8ptrType
|
recv = c.i8ptrType
|
||||||
}
|
}
|
||||||
paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
|
paramTypes = append(paramTypes, expandFormalParamType(recv)...)
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for i := 0; i < typ.Params().Len(); i++ {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||||
paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
|
paramTypes = append(paramTypes, expandFormalParamType(subType)...)
|
||||||
}
|
}
|
||||||
// All functions take these parameters at the end.
|
// All functions take these parameters at the end.
|
||||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||||
|
|
|
@ -445,7 +445,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType := c.getLLVMType(f.Params[0].Type())
|
receiverType := c.getLLVMType(f.Params[0].Type())
|
||||||
expandedReceiverType := c.expandFormalParamType(receiverType)
|
expandedReceiverType := expandFormalParamType(receiverType)
|
||||||
|
|
||||||
// Does this method even need any wrapping?
|
// Does this method even need any wrapping?
|
||||||
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче