compiler: implement deferring of interface calls
Этот коммит содержится в:
родитель
8f8942d763
коммит
a531caa2e9
3 изменённых файлов: 164 добавлений и 68 удалений
|
@ -41,29 +41,30 @@ type Config struct {
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
Config
|
Config
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
builder llvm.Builder
|
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
|
||||||
ditypes map[string]llvm.Metadata
|
ditypes map[string]llvm.Metadata
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
i8ptrType llvm.Type // for convenience
|
i8ptrType llvm.Type // for convenience
|
||||||
uintptrType llvm.Type
|
uintptrType llvm.Type
|
||||||
lenType llvm.Type
|
lenType llvm.Type
|
||||||
coroIdFunc llvm.Value
|
coroIdFunc llvm.Value
|
||||||
coroSizeFunc llvm.Value
|
coroSizeFunc llvm.Value
|
||||||
coroBeginFunc llvm.Value
|
coroBeginFunc llvm.Value
|
||||||
coroSuspendFunc llvm.Value
|
coroSuspendFunc llvm.Value
|
||||||
coroEndFunc llvm.Value
|
coroEndFunc llvm.Value
|
||||||
coroFreeFunc llvm.Value
|
coroFreeFunc llvm.Value
|
||||||
initFuncs []llvm.Value
|
initFuncs []llvm.Value
|
||||||
deferFuncs []*ir.Function
|
deferFuncs []*ir.Function
|
||||||
ctxDeferFuncs []ContextDeferFunction
|
deferInvokeFuncs []InvokeDeferFunction
|
||||||
ir *ir.Program
|
ctxDeferFuncs []ContextDeferFunction
|
||||||
|
ir *ir.Program
|
||||||
}
|
}
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
|
@ -93,6 +94,12 @@ type ContextDeferFunction struct {
|
||||||
signature *types.Signature
|
signature *types.Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A thunk for a defer that defers calling an interface method.
|
||||||
|
type InvokeDeferFunction struct {
|
||||||
|
method *types.Func
|
||||||
|
valueTypes []llvm.Type
|
||||||
|
}
|
||||||
|
|
||||||
func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
||||||
if config.Triple == "" {
|
if config.Triple == "" {
|
||||||
config.Triple = llvm.DefaultTargetTriple()
|
config.Triple = llvm.DefaultTargetTriple()
|
||||||
|
@ -373,6 +380,41 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||||
c.builder.CreateRetVoid()
|
c.builder.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create wrapper for deferred interface call.
|
||||||
|
for _, thunk := range c.deferInvokeFuncs {
|
||||||
|
// This function gets a single parameter which is a pointer to a struct
|
||||||
|
// (the defer frame).
|
||||||
|
// This struct starts with the values of runtime._defer, but after that
|
||||||
|
// follow the real function parameters.
|
||||||
|
// The job of this wrapper is to extract these parameters and to call
|
||||||
|
// the real function with them.
|
||||||
|
llvmFn := c.mod.NamedFunction(thunk.method.FullName() + "$defer")
|
||||||
|
llvmFn.SetLinkage(llvm.InternalLinkage)
|
||||||
|
llvmFn.SetUnnamedAddr(true)
|
||||||
|
entry := c.ctx.AddBasicBlock(llvmFn, "entry")
|
||||||
|
c.builder.SetInsertPointAtEnd(entry)
|
||||||
|
deferRawPtr := llvmFn.Param(0)
|
||||||
|
|
||||||
|
// Get the real param type and cast to it.
|
||||||
|
deferFrameType := c.ctx.StructType(thunk.valueTypes, false)
|
||||||
|
deferFramePtr := c.builder.CreateBitCast(deferRawPtr, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
|
|
||||||
|
// Extract the params from the struct.
|
||||||
|
forwardParams := []llvm.Value{}
|
||||||
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
|
for i := range thunk.valueTypes[3:] {
|
||||||
|
gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+3), false)}, "gep")
|
||||||
|
forwardParam := c.builder.CreateLoad(gep, "param")
|
||||||
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call real function (of which this is a wrapper).
|
||||||
|
fnGEP := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), 2, false)}, "fn.gep")
|
||||||
|
fn := c.builder.CreateLoad(fnGEP, "fn")
|
||||||
|
c.createCall(fn, forwardParams, "")
|
||||||
|
c.builder.CreateRetVoid()
|
||||||
|
}
|
||||||
|
|
||||||
// Create wrapper for deferred function pointer call.
|
// Create wrapper for deferred function pointer call.
|
||||||
for _, thunk := range c.ctxDeferFuncs {
|
for _, thunk := range c.ctxDeferFuncs {
|
||||||
// This function gets a single parameter which is a pointer to a struct
|
// This function gets a single parameter which is a pointer to a struct
|
||||||
|
@ -1563,7 +1605,37 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
|
|
||||||
var values []llvm.Value
|
var values []llvm.Value
|
||||||
var valueTypes []llvm.Type
|
var valueTypes []llvm.Type
|
||||||
if callee, ok := instr.Call.Value.(*ssa.Function); ok && !instr.Call.IsInvoke() {
|
if instr.Call.IsInvoke() {
|
||||||
|
// Function call on an interface.
|
||||||
|
fnPtr, args, err := c.getInvokeCall(frame, &instr.Call)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
valueTypes = []llvm.Type{llvm.PointerType(deferFuncType, 0), next.Type(), fnPtr.Type()}
|
||||||
|
for _, param := range args {
|
||||||
|
valueTypes = append(valueTypes, param.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a thunk.
|
||||||
|
deferName := instr.Call.Method.FullName() + "$defer"
|
||||||
|
callback := c.mod.NamedFunction(deferName)
|
||||||
|
if callback.IsNil() {
|
||||||
|
// Not found, have to add it.
|
||||||
|
callback = llvm.AddFunction(c.mod, deferName, deferFuncType)
|
||||||
|
thunk := InvokeDeferFunction{
|
||||||
|
method: instr.Call.Method,
|
||||||
|
valueTypes: valueTypes,
|
||||||
|
}
|
||||||
|
c.deferInvokeFuncs = append(c.deferInvokeFuncs, thunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all values to be put in the struct (starting with
|
||||||
|
// runtime._defer fields, followed by the function pointer to be
|
||||||
|
// called).
|
||||||
|
values = append([]llvm.Value{callback, next, fnPtr}, args...)
|
||||||
|
|
||||||
|
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
||||||
// Regular function call.
|
// Regular function call.
|
||||||
fn := c.ir.GetFunction(callee)
|
fn := c.ir.GetFunction(callee)
|
||||||
|
|
||||||
|
@ -1588,6 +1660,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
values = append(values, llvmParam)
|
values = append(values, llvmParam)
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
valueTypes = append(valueTypes, llvmParam.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
||||||
// Immediately applied function literal with free variables.
|
// Immediately applied function literal with free variables.
|
||||||
closure, err := c.parseExpr(frame, instr.Call.Value)
|
closure, err := c.parseExpr(frame, instr.Call.Value)
|
||||||
|
@ -1618,6 +1691,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
makeClosure.Fn.(*ssa.Function).Signature,
|
makeClosure.Fn.(*ssa.Function).Signature,
|
||||||
}
|
}
|
||||||
c.ctxDeferFuncs = append(c.ctxDeferFuncs, thunk)
|
c.ctxDeferFuncs = append(c.ctxDeferFuncs, thunk)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return errors.New("todo: defer on uncommon function call type")
|
return errors.New("todo: defer on uncommon function call type")
|
||||||
}
|
}
|
||||||
|
@ -2035,51 +2109,11 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
|
||||||
|
|
||||||
func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle llvm.Value) (llvm.Value, error) {
|
func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle llvm.Value) (llvm.Value, error) {
|
||||||
if instr.IsInvoke() {
|
if instr.IsInvoke() {
|
||||||
// Call an interface method with dynamic dispatch.
|
|
||||||
itf, err := c.parseExpr(frame, instr.Value) // interface
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
llvmFnType, err := c.getLLVMType(instr.Method.Type())
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
|
||||||
// This is somewhat of a hack.
|
|
||||||
// getLLVMType() has created a closure type for us, but we don't
|
|
||||||
// actually want a closure type as an interface call can never be a
|
|
||||||
// closure call. So extract the function pointer type from the
|
|
||||||
// closure.
|
|
||||||
// This happens because somewhere the same function signature is
|
|
||||||
// used in a closure or bound method.
|
|
||||||
llvmFnType = llvmFnType.Subtypes()[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
values := []llvm.Value{
|
|
||||||
itf,
|
|
||||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false),
|
|
||||||
}
|
|
||||||
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
|
||||||
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
|
|
||||||
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
|
||||||
|
|
||||||
args := []llvm.Value{receiverValue}
|
|
||||||
for _, arg := range instr.Args {
|
|
||||||
val, err := c.parseExpr(frame, arg)
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
args = append(args, val)
|
|
||||||
}
|
|
||||||
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
|
||||||
// This function takes an extra context parameter. An interface call
|
|
||||||
// cannot also be a closure but we have to supply the nil pointer
|
|
||||||
// anyway.
|
|
||||||
args = append(args, llvm.ConstPointerNull(c.i8ptrType))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: blocking methods (needs analysis)
|
// TODO: blocking methods (needs analysis)
|
||||||
|
fnCast, args, err := c.getInvokeCall(frame, instr)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
return c.createCall(fnCast, args, ""), nil
|
return c.createCall(fnCast, args, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2209,6 +2243,56 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getInvokeCall creates and returns the function pointer and parameters of an
|
||||||
|
// interface call. It can be used in a call or defer instruction.
|
||||||
|
func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value, error) {
|
||||||
|
// Call an interface method with dynamic dispatch.
|
||||||
|
itf, err := c.parseExpr(frame, instr.Value) // interface
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
llvmFnType, err := c.getLLVMType(instr.Method.Type())
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, nil, err
|
||||||
|
}
|
||||||
|
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
||||||
|
// This is somewhat of a hack.
|
||||||
|
// getLLVMType() has created a closure type for us, but we don't
|
||||||
|
// actually want a closure type as an interface call can never be a
|
||||||
|
// closure call. So extract the function pointer type from the
|
||||||
|
// closure.
|
||||||
|
// This happens because somewhere the same function signature is
|
||||||
|
// used in a closure or bound method.
|
||||||
|
llvmFnType = llvmFnType.Subtypes()[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
values := []llvm.Value{
|
||||||
|
itf,
|
||||||
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false),
|
||||||
|
}
|
||||||
|
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
||||||
|
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
|
||||||
|
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
|
|
||||||
|
args := []llvm.Value{receiverValue}
|
||||||
|
for _, arg := range instr.Args {
|
||||||
|
val, err := c.parseExpr(frame, arg)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, nil, err
|
||||||
|
}
|
||||||
|
args = append(args, val)
|
||||||
|
}
|
||||||
|
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
||||||
|
// This function takes an extra context parameter. An interface call
|
||||||
|
// cannot also be a closure but we have to supply the nil pointer
|
||||||
|
// anyway.
|
||||||
|
args = append(args, llvm.ConstPointerNull(c.i8ptrType))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fnCast, args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) {
|
func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) {
|
||||||
if frame.fn.IsNoBounds() {
|
if frame.fn.IsNoBounds() {
|
||||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||||
|
|
11
testdata/calls.go
предоставленный
11
testdata/calls.go
предоставленный
|
@ -8,6 +8,14 @@ func (t Thing) String() string {
|
||||||
return t.name
|
return t.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Thing) Print(arg string) {
|
||||||
|
println("Thing.Print:", t.name, "arg:", arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Printer interface {
|
||||||
|
Print(string)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
thing := &Thing{"foo"}
|
thing := &Thing{"foo"}
|
||||||
|
|
||||||
|
@ -49,6 +57,9 @@ func testDefer() {
|
||||||
defer deferred("...run as defer", i)
|
defer deferred("...run as defer", i)
|
||||||
i++
|
i++
|
||||||
|
|
||||||
|
var t Printer = &Thing{"foo"}
|
||||||
|
defer t.Print("bar")
|
||||||
|
|
||||||
println("deferring...")
|
println("deferring...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
testdata/calls.txt
предоставленный
1
testdata/calls.txt
предоставленный
|
@ -1,5 +1,6 @@
|
||||||
hello from function pointer: 5
|
hello from function pointer: 5
|
||||||
deferring...
|
deferring...
|
||||||
|
Thing.Print: foo arg: bar
|
||||||
...run as defer 3
|
...run as defer 3
|
||||||
...run closure deferred: 4
|
...run closure deferred: 4
|
||||||
...run as defer 1
|
...run as defer 1
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче