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
|
||||
// parameter list.
|
||||
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||
func expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := c.flattenAggregateType(t)
|
||||
fields := flattenAggregateType(t)
|
||||
if len(fields) <= MaxFieldsPerParam {
|
||||
return fields
|
||||
} else {
|
||||
|
@ -82,7 +82,7 @@ func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
|||
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
switch v.Type().TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fieldTypes := c.flattenAggregateType(v.Type())
|
||||
fieldTypes := flattenAggregateType(v.Type())
|
||||
if len(fieldTypes) <= MaxFieldsPerParam {
|
||||
fields := c.flattenAggregate(v)
|
||||
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
|
||||
// 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() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
|
||||
for _, subfield := range t.StructElementTypes() {
|
||||
subfields := c.flattenAggregateType(subfield)
|
||||
subfields := flattenAggregateType(subfield)
|
||||
fields = append(fields, subfields...)
|
||||
}
|
||||
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) {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
|
||||
if len(flattenAggregateType(t)) <= MaxFieldsPerParam {
|
||||
value := llvm.ConstNull(t)
|
||||
for i, subtyp := range t.StructElementTypes() {
|
||||
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
|
||||
|
|
|
@ -34,29 +34,43 @@ func init() {
|
|||
// The TinyGo import path.
|
||||
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
|
||||
mod llvm.Module
|
||||
ctx llvm.Context
|
||||
mod llvm.Module
|
||||
ctx llvm.Context
|
||||
dibuilder *llvm.DIBuilder
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[types.Type]llvm.Metadata
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
i8ptrType llvm.Type // for convenience
|
||||
funcPtrAddrSpace int
|
||||
uintptrType llvm.Type
|
||||
ir *ir.Program
|
||||
diagnostics []error
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
compilerContext
|
||||
builder llvm.Builder
|
||||
dibuilder *llvm.DIBuilder
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[types.Type]llvm.Metadata
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
i8ptrType llvm.Type // for convenience
|
||||
funcPtrAddrSpace int
|
||||
uintptrType llvm.Type
|
||||
initFuncs []llvm.Value
|
||||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||
ir *ir.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
builder
|
||||
}
|
||||
|
||||
// builder contains all information relevant to build a single function.
|
||||
type builder struct {
|
||||
*compilerContext
|
||||
llvm.Builder
|
||||
fn *ir.Function
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
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) {
|
||||
c := &Compiler{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
compilerContext: compilerContext{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
},
|
||||
}
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(config.Triple())
|
||||
|
@ -252,6 +268,20 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
|
||||
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.
|
||||
for _, f := range c.ir.Functions {
|
||||
frames = append(frames, c.parseFuncDecl(f))
|
||||
|
@ -367,23 +397,23 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
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
|
||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
||||
// getLLVMType(getRuntimeType(name)).
|
||||
func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type {
|
||||
return c.getLLVMType(c.getRuntimeType(name))
|
||||
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||
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
|
||||
// named struct types (or Go types implemented as named LLVM structs such as
|
||||
// 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) {
|
||||
case *types.Array:
|
||||
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 {
|
||||
frame := &Frame{
|
||||
fn: f,
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
builder: builder{
|
||||
compilerContext: &c.compilerContext,
|
||||
Builder: c.builder, // TODO: use a separate builder per function
|
||||
fn: f,
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
},
|
||||
}
|
||||
|
||||
var retType llvm.Type
|
||||
|
@ -728,7 +762,7 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame {
|
|||
var paramTypes []llvm.Type
|
||||
for _, param := range f.Params {
|
||||
paramType := c.getLLVMType(param.Type())
|
||||
paramTypeFragments := c.expandFormalParamType(paramType)
|
||||
paramTypeFragments := expandFormalParamType(paramType)
|
||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
||||
}
|
||||
|
||||
|
@ -871,7 +905,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||
for _, param := range frame.fn.Params {
|
||||
llvmType := c.getLLVMType(param.Type())
|
||||
fields := make([]llvm.Value, 0, 1)
|
||||
for range c.expandFormalParamType(llvmType) {
|
||||
for range expandFormalParamType(llvmType) {
|
||||
fields = append(fields, frame.fn.LLVMFn.Param(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 {
|
||||
switch expr := expr.(type) {
|
||||
case *ssa.Const:
|
||||
return c.parseConst(frame.fn.LinkName(), expr)
|
||||
return frame.createConst(frame.fn.LinkName(), expr)
|
||||
case *ssa.Function:
|
||||
fn := c.ir.GetFunction(expr)
|
||||
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) {
|
||||
case *types.Basic:
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmType := b.getLLVMType(typ)
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
b := constant.BoolVal(expr.Value)
|
||||
n := uint64(0)
|
||||
|
@ -2224,23 +2259,23 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||
return llvm.ConstInt(llvmType, n, false)
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
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"
|
||||
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
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 {
|
||||
n, _ := constant.Uint64Val(expr.Value)
|
||||
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)
|
||||
return llvm.ConstFloat(llvmType, n)
|
||||
} else if typ.Kind() == types.Complex64 {
|
||||
r := c.parseConst(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]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx
|
||||
} else if typ.Kind() == types.Complex128 {
|
||||
r := c.parseConst(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]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx
|
||||
} else {
|
||||
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 {
|
||||
panic("expected nil chan constant")
|
||||
}
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
case *types.Signature:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil signature constant")
|
||||
}
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
case *types.Interface:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil interface constant")
|
||||
}
|
||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||
fields := []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(c.i8ptrType),
|
||||
llvm.ConstInt(b.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(b.i8ptrType),
|
||||
}
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
||||
case *types.Pointer:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil pointer constant")
|
||||
}
|
||||
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
||||
case *types.Slice:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil slice constant")
|
||||
}
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
elemType := b.getLLVMType(typ.Elem())
|
||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||
llvmPtr, // backing array
|
||||
llvmLen, // len
|
||||
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.
|
||||
panic("non-nil map constant")
|
||||
}
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmType := b.getLLVMType(typ)
|
||||
return llvm.ConstNull(llvmType)
|
||||
default:
|
||||
panic("unknown constant: " + expr.String())
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// This file contains some utility functions related to error handling.
|
||||
|
||||
import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
|
@ -9,7 +11,8 @@ import (
|
|||
"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{
|
||||
Fset: c.ir.Program.Fset,
|
||||
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.
|
||||
func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
switch c.FuncImplementation() {
|
||||
case compileopts.FuncValueDoubleword:
|
||||
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.
|
||||
func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
// Get the return type.
|
||||
var returnType llvm.Type
|
||||
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.
|
||||
recv = c.i8ptrType
|
||||
}
|
||||
paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
|
||||
paramTypes = append(paramTypes, expandFormalParamType(recv)...)
|
||||
}
|
||||
for i := 0; i < typ.Params().Len(); i++ {
|
||||
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.
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||
|
|
|
@ -445,7 +445,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
|||
|
||||
// Get the expanded receiver type.
|
||||
receiverType := c.getLLVMType(f.Params[0].Type())
|
||||
expandedReceiverType := c.expandFormalParamType(receiverType)
|
||||
expandedReceiverType := expandFormalParamType(receiverType)
|
||||
|
||||
// Does this method even need any wrapping?
|
||||
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче