Permalink
Browse files

llgo: Represent functions as a pair of pointers

This commit changes function representation to be a
pair of pointers: (fnptr, context). The first pointer
is a raw function pointer; the second pointer is a
"context" pointer, which is non-nil for closures. For
a plain old function, the function value will be
constant, with the second value being const-null.

In addition, "receiver" has been removed from LLVMValue,
and we overload this pair-of-pointers representation to
enable passing bound methods around internally. In this
case, the second item in the pair may have some arbitrary
non-pointer type (i.e. the type of the method receiver).

Closures no longer use trampolines, instead setting the
function pair's context value directly.

The most complex change is in VisitCallExpr: to avoid
generating additional code for plain old functions, it
first checks if the context value is const-null, and if
so, calls the function pointer as it previously would
have. If the context is not const-null, then code is
generated to check if it is non-null at runtime, and if
so, call the function with an additional argument
(the context) prepended to the argument list; otherwise
call the function pointer as it previoulst would have.

There are some problems remaining with the runtime
where functions are assumed to occupy one word, and
also in the use of intrinsics (namely llvm.trap).
  • Loading branch information...
axw committed Jan 12, 2013
1 parent 41a97fc commit e26ff1f4a7ea661ff77b12a4cb7273f770d5a928
Showing with 231 additions and 139 deletions.
  1. +1 −0 attribute.go
  2. +13 −11 compiler.go
  3. +15 −13 decl.go
  4. +90 −34 expr.go
  5. +1 −0 interfaces.go
  6. +30 −34 literals.go
  7. +1 −1 llgo/function_test.go
  8. +2 −1 llgo/literals_test.go
  9. +8 −4 pkg/runtime/main.go
  10. +10 −0 pkg/runtime/main.ll
  11. +5 −2 pkg/runtime/unsafe.go
  12. +3 −3 runtime.go
  13. +45 −33 typemap.go
  14. +7 −3 value.go
View
@@ -116,6 +116,7 @@ type nameAttribute string
func (a nameAttribute) Apply(v Value) {
if _, isfunc := v.Type().(*types.Signature); isfunc {
fn := v.LLVMValue()
fn = llvm.ConstExtractValue(fn, []uint32{0})
fn.SetName(string(a))
} else {
global := v.(*LLVMValue).pointer.value
View
@@ -70,8 +70,8 @@ type compiler struct {
functions functionStack
breakblocks []llvm.BasicBlock
continueblocks []llvm.BasicBlock
initfuncs []Value
varinitfuncs []Value
initfuncs []llvm.Value
varinitfuncs []llvm.Value
pkg *ast.Package
importpath string
fileset *token.FileSet
@@ -368,22 +368,22 @@ func (compiler *compiler) Compile(fset *token.FileSet,
// At program initialisation, the runtime initialisation
// function (runtime.main) will invoke the constructors
// in reverse order.
var initfuncs [][]Value
var initfuncs [][]llvm.Value
if compiler.varinitfuncs != nil {
initfuncs = append(initfuncs, compiler.varinitfuncs)
}
if compiler.initfuncs != nil {
initfuncs = append(initfuncs, compiler.initfuncs)
}
if initfuncs != nil {
ctortype := llvm.PointerType(llvm.FunctionType(llvm.VoidType(), nil, false), 0)
ctortype := llvm.PointerType(llvm.Int8Type(), 0)
var ctors []llvm.Value
var index int = 0
for _, initfuncs := range initfuncs {
for _, fn := range initfuncs {
fnval := fn.LLVMValue()
fnval.SetName("__llgo.ctor." + compiler.importpath + strconv.Itoa(index))
ctors = append(ctors, fnval)
for _, fnptr := range initfuncs {
fnptr.SetName("__llgo.ctor." + compiler.importpath + strconv.Itoa(index))
fnptr = llvm.ConstBitCast(fnptr, ctortype)
ctors = append(ctors, fnptr)
index++
}
}
@@ -429,12 +429,14 @@ func (c *compiler) createMainFunction() error {
mainMain = c.module.NamedFunction("main.main")
}
// runtime.main is called by main, with argc, argv,
// and a pointer to main.main.
runtimeMain := c.NamedFunction("runtime.main", "func f(int32, **byte, **byte, func()) int32")
// runtime.main is called by main, with argc, argv, argp,
// and a pointer to main.main, which must be a niladic
// function with no result.
runtimeMain := c.NamedFunction("runtime.main", "func f(int32, **byte, **byte, *int8) int32")
main := c.NamedFunction("main", "func f(int32, **byte, **byte) int32")
entry := llvm.AddBasicBlock(main, "entry")
c.builder.SetInsertPointAtEnd(entry)
mainMain = c.builder.CreateBitCast(mainMain, runtimeMain.Type().ElementType().ParamTypes()[3], "")
args := []llvm.Value{main.Param(0), main.Param(1), main.Param(2), mainMain}
result := c.builder.CreateCall(runtimeMain, args, "")
c.builder.CreateRet(result)
View
28 decl.go
@@ -70,23 +70,26 @@ func (c *compiler) VisitFuncProtoDecl(f *ast.FuncDecl) *LLVMValue {
}
// gcimporter may produce multiple AST objects for the same function.
llvmftyp := c.types.ToLLVM(ftyp)
fn := c.module.Module.NamedFunction(fname)
if fn.IsNil() {
llvmftyp := c.types.ToLLVM(ftyp).ElementType()
fn = llvm.AddFunction(c.module.Module, fname, llvmftyp)
llvmfptrtyp := llvmftyp.StructElementTypes()[0].ElementType()
fn = llvm.AddFunction(c.module.Module, fname, llvmfptrtyp)
if ftyp.Recv != nil {
// Create an interface function if the receiver is
// not a pointer type.
recvtyp := ftyp.Recv.Type.(types.Type)
if _, ptr := recvtyp.(*types.Pointer); !ptr {
returntyp := llvmftyp.ReturnType()
paramtypes := llvmftyp.ParamTypes()
returntyp := llvmfptrtyp.ReturnType()
paramtypes := llvmfptrtyp.ParamTypes()
paramtypes[0] = llvm.PointerType(paramtypes[0], 0)
ifntyp := llvm.FunctionType(returntyp, paramtypes, false)
llvm.AddFunction(c.module.Module, "*"+fname, ifntyp)
}
}
}
fn = llvm.ConstInsertValue(llvm.ConstNull(llvmftyp), fn, []uint32{0})
result := c.NewValue(fn, ftyp)
if f.Name.Obj != nil {
f.Name.Obj.Data = result
@@ -116,7 +119,8 @@ func (stackvar *LLVMValue) promoteStackVar() {
// buildFunction takes a function Value, a list of parameters, and a body,
// and generates code for the function.
func (c *compiler) buildFunction(f *LLVMValue, params, results []*ast.Object, body *ast.BlockStmt) {
llvm_fn := f.LLVMValue()
defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock())
llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0})
entry := llvm.AddBasicBlock(llvm_fn, "entry")
c.builder.SetInsertPointAtEnd(entry)
@@ -205,19 +209,20 @@ func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value {
paramObjects = append(recvObjects, paramObjects...)
}
resultObjects := fieldListObjects(f.Type.Results)
c.buildFunction(fn, paramObjects, resultObjects, f.Body)
if f.Recv != nil {
// Create a shim function if the receiver is not
// a pointer type.
recvtyp := ftyp.Recv.Type.(types.Type)
if _, ptr := recvtyp.(*types.Pointer); !ptr {
c.buildPtrRecvFunction(fn.value)
fnptr := llvm.ConstExtractValue(fn.value, []uint32{0})
c.buildPtrRecvFunction(fnptr)
}
} else if f.Name.Name == "init" {
// Is it an 'init' function? Then record it.
c.initfuncs = append(c.initfuncs, fn)
fnptr := llvm.ConstExtractValue(fn.value, []uint32{0})
c.initfuncs = append(c.initfuncs, fnptr)
}
return fn
}
@@ -274,8 +279,7 @@ func (c *compiler) createGlobals(idents []*ast.Ident, values []ast.Expr, pkg str
if block := c.builder.GetInsertBlock(); !block.IsNil() {
defer c.builder.SetInsertPointAtEnd(block)
}
fntype := &types.Signature{}
llvmfntype := c.types.ToLLVM(fntype).ElementType()
llvmfntype := llvm.FunctionType(llvm.VoidType(), nil, false)
fn := llvm.AddFunction(c.module.Module, "", llvmfntype)
entry := llvm.AddBasicBlock(fn, "entry")
c.builder.SetInsertPointAtEnd(entry)
@@ -305,10 +309,8 @@ func (c *compiler) createGlobals(idents []*ast.Ident, values []ast.Expr, pkg str
}
}
}
c.builder.CreateRetVoid()
fnvalue := c.NewValue(fn, fntype)
c.varinitfuncs = append(c.varinitfuncs, fnvalue)
c.varinitfuncs = append(c.varinitfuncs, fn)
}
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec) {
View
124 expr.go
@@ -168,22 +168,7 @@ func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value {
fn := lhs.(*LLVMValue)
fn_type := underlyingType(fn.Type()).(*types.Signature)
// Convert untyped argument types.
for i, p := range fn_type.Params {
arginfo := c.types.expr[expr.Args[i]]
if isUntyped(arginfo.Type) {
arginfo.Type = p.Type.(types.Type)
c.types.expr[expr.Args[i]] = arginfo
}
}
args := make([]llvm.Value, 0)
if fn.receiver != nil {
// Don't dereference the receiver here. It'll have been worked out in
// the selector.
receiver := fn.receiver
args = append(args, receiver.LLVMValue())
}
var args []llvm.Value
if nparams := len(fn_type.Params); nparams > 0 {
if fn_type.IsVariadic {
nparams--
@@ -221,10 +206,72 @@ func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value {
result_type = &types.Result{Values: fn_type.Results}
}
// After calling the function, we must bitcast to the computed LLVM
// type. This is a no-op, and exists just to satisfy LLVM's type
// comparisons.
result := c.builder.CreateCall(fn.LLVMValue(), args, "")
var fnptr llvm.Value
fnval := fn.LLVMValue()
if fnval.Type().TypeKind() == llvm.PointerTypeKind {
fnptr = fnval
} else {
fnptr = c.builder.CreateExtractValue(fnval, 0, "")
context := c.builder.CreateExtractValue(fnval, 1, "")
fntyp := fnptr.Type().ElementType()
paramTypes := fntyp.ParamTypes()
// If the context is not a constant null, and we're not
// dealing with a method (where we don't care about the value
// of the receiver), then we must conditionally call the
// function with the additional receiver/closure.
if !context.IsNull() {
var nullctxblock llvm.BasicBlock
var nonnullctxblock llvm.BasicBlock
var endblock llvm.BasicBlock
var nullctxresult llvm.Value
if !context.IsConstant() && len(paramTypes) == len(args) {
currblock := c.builder.GetInsertBlock()
endblock = llvm.AddBasicBlock(currblock.Parent(), "")
endblock.MoveAfter(currblock)
nonnullctxblock = llvm.InsertBasicBlock(endblock, "")
nullctxblock = llvm.InsertBasicBlock(nonnullctxblock, "")
nullctx := c.builder.CreateIsNull(context, "")
c.builder.CreateCondBr(nullctx, nullctxblock, nonnullctxblock)
// null context case.
c.builder.SetInsertPointAtEnd(nullctxblock)
nullctxresult = c.builder.CreateCall(fnptr, args, "")
c.builder.CreateBr(endblock)
c.builder.SetInsertPointAtEnd(nonnullctxblock)
}
// non-null context case.
var result llvm.Value
args := append([]llvm.Value{context}, args...)
if len(paramTypes) < len(args) {
returnType := fntyp.ReturnType()
ctxType := context.Type()
paramTypes := append([]llvm.Type{ctxType}, paramTypes...)
vararg := fntyp.IsFunctionVarArg()
fntyp := llvm.FunctionType(returnType, paramTypes, vararg)
fnptrtyp := llvm.PointerType(fntyp, 0)
fnptr = c.builder.CreateBitCast(fnptr, fnptrtyp, "")
}
result = c.builder.CreateCall(fnptr, args, "")
// If the return type is not void, create a
// PHI node to select which value to return.
if !nullctxresult.IsNil() {
c.builder.CreateBr(endblock)
c.builder.SetInsertPointAtEnd(endblock)
if result.Type().TypeKind() != llvm.VoidTypeKind {
phiresult := c.builder.CreatePHI(result.Type(), "")
values := []llvm.Value{nullctxresult, result}
blocks := []llvm.BasicBlock{nullctxblock, nonnullctxblock}
phiresult.AddIncoming(values, blocks)
result = phiresult
}
}
return c.NewValue(result, result_type)
}
}
result := c.builder.CreateCall(fnptr, args, "")
return c.NewValue(result, result_type)
}
@@ -326,14 +373,13 @@ func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value {
structValue := lhs.LLVMValue()
receiver := c.builder.CreateExtractValue(structValue, 1, "")
f := c.builder.CreateExtractValue(structValue, i+2, "")
i8ptr := &types.Pointer{Base: types.Typ[types.Int8]}
ftype := iface.Methods[i].Type
ftype.Recv = &types.Var{Type: i8ptr}
f = c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), "")
ftype.Recv = nil
method := c.NewValue(f, ftype)
method.receiver = c.NewValue(receiver, i8ptr)
return method
types := []llvm.Type{f.Type(), receiver.Type()}
llvmStructType := llvm.StructType(types, false)
structValue = llvm.Undef(llvmStructType)
structValue = c.builder.CreateInsertValue(structValue, f, 0, "")
structValue = c.builder.CreateInsertValue(structValue, receiver, 1, "")
return c.NewValue(structValue, ftype)
}
// Otherwise, search for field/method,
@@ -418,14 +464,24 @@ func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value {
method := c.Resolve(method).(*LLVMValue)
methodType := method.Type().(*types.Signature)
receiverType := methodType.Recv.Type.(types.Type)
if isIdentical(recvValue.Type(), receiverType) {
method.receiver = recvValue
} else if isIdentical(&types.Pointer{Base: recvValue.Type()}, receiverType) {
method.receiver = recvValue.pointer
} else {
method.receiver = recvValue.makePointee()
var receiver Value
switch {
case isIdentical(recvValue.Type(), receiverType):
receiver = recvValue
case isIdentical(&types.Pointer{Base: recvValue.Type()}, receiverType):
receiver = recvValue.pointer
default:
receiver = recvValue.makePointee()
}
return method
methodValue := method.LLVMValue()
methodValue = c.builder.CreateExtractValue(methodValue, 0, "")
receiverValue := receiver.LLVMValue()
types := []llvm.Type{methodValue.Type(), receiverValue.Type()}
structType := llvm.StructType(types, false)
value := llvm.Undef(structType)
value = c.builder.CreateInsertValue(value, methodValue, 0, "")
value = c.builder.CreateInsertValue(value, receiverValue, 1, "")
return c.NewValue(value, methodType)
} else {
if isIdentical(recvValue.Type(), result.Type) {
// no-op
View
@@ -128,6 +128,7 @@ func (v *LLVMValue) convertV2I(iface *types.Interface) Value {
}
method := v.compiler.Resolve(methodobj).(*LLVMValue)
llvm_value := method.LLVMValue()
llvm_value = builder.CreateExtractValue(llvm_value, 0, "")
// If we have a receiver wider than a word, or a pointer
// receiver value and non-pointer receiver method, then
Oops, something went wrong.

0 comments on commit e26ff1f

Please sign in to comment.