Skip to content

Commit e26ff1f

Browse files
committed
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).
1 parent 41a97fc commit e26ff1f

File tree

14 files changed

+231
-139
lines changed

14 files changed

+231
-139
lines changed

attribute.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ type nameAttribute string
116116
func (a nameAttribute) Apply(v Value) {
117117
if _, isfunc := v.Type().(*types.Signature); isfunc {
118118
fn := v.LLVMValue()
119+
fn = llvm.ConstExtractValue(fn, []uint32{0})
119120
fn.SetName(string(a))
120121
} else {
121122
global := v.(*LLVMValue).pointer.value

compiler.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ type compiler struct {
7070
functions functionStack
7171
breakblocks []llvm.BasicBlock
7272
continueblocks []llvm.BasicBlock
73-
initfuncs []Value
74-
varinitfuncs []Value
73+
initfuncs []llvm.Value
74+
varinitfuncs []llvm.Value
7575
pkg *ast.Package
7676
importpath string
7777
fileset *token.FileSet
@@ -368,22 +368,22 @@ func (compiler *compiler) Compile(fset *token.FileSet,
368368
// At program initialisation, the runtime initialisation
369369
// function (runtime.main) will invoke the constructors
370370
// in reverse order.
371-
var initfuncs [][]Value
371+
var initfuncs [][]llvm.Value
372372
if compiler.varinitfuncs != nil {
373373
initfuncs = append(initfuncs, compiler.varinitfuncs)
374374
}
375375
if compiler.initfuncs != nil {
376376
initfuncs = append(initfuncs, compiler.initfuncs)
377377
}
378378
if initfuncs != nil {
379-
ctortype := llvm.PointerType(llvm.FunctionType(llvm.VoidType(), nil, false), 0)
379+
ctortype := llvm.PointerType(llvm.Int8Type(), 0)
380380
var ctors []llvm.Value
381381
var index int = 0
382382
for _, initfuncs := range initfuncs {
383-
for _, fn := range initfuncs {
384-
fnval := fn.LLVMValue()
385-
fnval.SetName("__llgo.ctor." + compiler.importpath + strconv.Itoa(index))
386-
ctors = append(ctors, fnval)
383+
for _, fnptr := range initfuncs {
384+
fnptr.SetName("__llgo.ctor." + compiler.importpath + strconv.Itoa(index))
385+
fnptr = llvm.ConstBitCast(fnptr, ctortype)
386+
ctors = append(ctors, fnptr)
387387
index++
388388
}
389389
}
@@ -429,12 +429,14 @@ func (c *compiler) createMainFunction() error {
429429
mainMain = c.module.NamedFunction("main.main")
430430
}
431431

432-
// runtime.main is called by main, with argc, argv,
433-
// and a pointer to main.main.
434-
runtimeMain := c.NamedFunction("runtime.main", "func f(int32, **byte, **byte, func()) int32")
432+
// runtime.main is called by main, with argc, argv, argp,
433+
// and a pointer to main.main, which must be a niladic
434+
// function with no result.
435+
runtimeMain := c.NamedFunction("runtime.main", "func f(int32, **byte, **byte, *int8) int32")
435436
main := c.NamedFunction("main", "func f(int32, **byte, **byte) int32")
436437
entry := llvm.AddBasicBlock(main, "entry")
437438
c.builder.SetInsertPointAtEnd(entry)
439+
mainMain = c.builder.CreateBitCast(mainMain, runtimeMain.Type().ElementType().ParamTypes()[3], "")
438440
args := []llvm.Value{main.Param(0), main.Param(1), main.Param(2), mainMain}
439441
result := c.builder.CreateCall(runtimeMain, args, "")
440442
c.builder.CreateRet(result)

decl.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,26 @@ func (c *compiler) VisitFuncProtoDecl(f *ast.FuncDecl) *LLVMValue {
7070
}
7171

7272
// gcimporter may produce multiple AST objects for the same function.
73+
llvmftyp := c.types.ToLLVM(ftyp)
7374
fn := c.module.Module.NamedFunction(fname)
7475
if fn.IsNil() {
75-
llvmftyp := c.types.ToLLVM(ftyp).ElementType()
76-
fn = llvm.AddFunction(c.module.Module, fname, llvmftyp)
76+
llvmfptrtyp := llvmftyp.StructElementTypes()[0].ElementType()
77+
fn = llvm.AddFunction(c.module.Module, fname, llvmfptrtyp)
7778
if ftyp.Recv != nil {
7879
// Create an interface function if the receiver is
7980
// not a pointer type.
8081
recvtyp := ftyp.Recv.Type.(types.Type)
8182
if _, ptr := recvtyp.(*types.Pointer); !ptr {
82-
returntyp := llvmftyp.ReturnType()
83-
paramtypes := llvmftyp.ParamTypes()
83+
returntyp := llvmfptrtyp.ReturnType()
84+
paramtypes := llvmfptrtyp.ParamTypes()
8485
paramtypes[0] = llvm.PointerType(paramtypes[0], 0)
8586
ifntyp := llvm.FunctionType(returntyp, paramtypes, false)
8687
llvm.AddFunction(c.module.Module, "*"+fname, ifntyp)
8788
}
8889
}
8990
}
91+
92+
fn = llvm.ConstInsertValue(llvm.ConstNull(llvmftyp), fn, []uint32{0})
9093
result := c.NewValue(fn, ftyp)
9194
if f.Name.Obj != nil {
9295
f.Name.Obj.Data = result
@@ -116,7 +119,8 @@ func (stackvar *LLVMValue) promoteStackVar() {
116119
// buildFunction takes a function Value, a list of parameters, and a body,
117120
// and generates code for the function.
118121
func (c *compiler) buildFunction(f *LLVMValue, params, results []*ast.Object, body *ast.BlockStmt) {
119-
llvm_fn := f.LLVMValue()
122+
defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock())
123+
llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0})
120124
entry := llvm.AddBasicBlock(llvm_fn, "entry")
121125
c.builder.SetInsertPointAtEnd(entry)
122126

@@ -205,19 +209,20 @@ func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value {
205209
paramObjects = append(recvObjects, paramObjects...)
206210
}
207211
resultObjects := fieldListObjects(f.Type.Results)
208-
209212
c.buildFunction(fn, paramObjects, resultObjects, f.Body)
210213

211214
if f.Recv != nil {
212215
// Create a shim function if the receiver is not
213216
// a pointer type.
214217
recvtyp := ftyp.Recv.Type.(types.Type)
215218
if _, ptr := recvtyp.(*types.Pointer); !ptr {
216-
c.buildPtrRecvFunction(fn.value)
219+
fnptr := llvm.ConstExtractValue(fn.value, []uint32{0})
220+
c.buildPtrRecvFunction(fnptr)
217221
}
218222
} else if f.Name.Name == "init" {
219223
// Is it an 'init' function? Then record it.
220-
c.initfuncs = append(c.initfuncs, fn)
224+
fnptr := llvm.ConstExtractValue(fn.value, []uint32{0})
225+
c.initfuncs = append(c.initfuncs, fnptr)
221226
}
222227
return fn
223228
}
@@ -274,8 +279,7 @@ func (c *compiler) createGlobals(idents []*ast.Ident, values []ast.Expr, pkg str
274279
if block := c.builder.GetInsertBlock(); !block.IsNil() {
275280
defer c.builder.SetInsertPointAtEnd(block)
276281
}
277-
fntype := &types.Signature{}
278-
llvmfntype := c.types.ToLLVM(fntype).ElementType()
282+
llvmfntype := llvm.FunctionType(llvm.VoidType(), nil, false)
279283
fn := llvm.AddFunction(c.module.Module, "", llvmfntype)
280284
entry := llvm.AddBasicBlock(fn, "entry")
281285
c.builder.SetInsertPointAtEnd(entry)
@@ -305,10 +309,8 @@ func (c *compiler) createGlobals(idents []*ast.Ident, values []ast.Expr, pkg str
305309
}
306310
}
307311
}
308-
309312
c.builder.CreateRetVoid()
310-
fnvalue := c.NewValue(fn, fntype)
311-
c.varinitfuncs = append(c.varinitfuncs, fnvalue)
313+
c.varinitfuncs = append(c.varinitfuncs, fn)
312314
}
313315

314316
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec) {

expr.go

Lines changed: 90 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -168,22 +168,7 @@ func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value {
168168
fn := lhs.(*LLVMValue)
169169
fn_type := underlyingType(fn.Type()).(*types.Signature)
170170

171-
// Convert untyped argument types.
172-
for i, p := range fn_type.Params {
173-
arginfo := c.types.expr[expr.Args[i]]
174-
if isUntyped(arginfo.Type) {
175-
arginfo.Type = p.Type.(types.Type)
176-
c.types.expr[expr.Args[i]] = arginfo
177-
}
178-
}
179-
180-
args := make([]llvm.Value, 0)
181-
if fn.receiver != nil {
182-
// Don't dereference the receiver here. It'll have been worked out in
183-
// the selector.
184-
receiver := fn.receiver
185-
args = append(args, receiver.LLVMValue())
186-
}
171+
var args []llvm.Value
187172
if nparams := len(fn_type.Params); nparams > 0 {
188173
if fn_type.IsVariadic {
189174
nparams--
@@ -221,10 +206,72 @@ func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value {
221206
result_type = &types.Result{Values: fn_type.Results}
222207
}
223208

224-
// After calling the function, we must bitcast to the computed LLVM
225-
// type. This is a no-op, and exists just to satisfy LLVM's type
226-
// comparisons.
227-
result := c.builder.CreateCall(fn.LLVMValue(), args, "")
209+
var fnptr llvm.Value
210+
fnval := fn.LLVMValue()
211+
if fnval.Type().TypeKind() == llvm.PointerTypeKind {
212+
fnptr = fnval
213+
} else {
214+
fnptr = c.builder.CreateExtractValue(fnval, 0, "")
215+
context := c.builder.CreateExtractValue(fnval, 1, "")
216+
fntyp := fnptr.Type().ElementType()
217+
paramTypes := fntyp.ParamTypes()
218+
219+
// If the context is not a constant null, and we're not
220+
// dealing with a method (where we don't care about the value
221+
// of the receiver), then we must conditionally call the
222+
// function with the additional receiver/closure.
223+
if !context.IsNull() {
224+
var nullctxblock llvm.BasicBlock
225+
var nonnullctxblock llvm.BasicBlock
226+
var endblock llvm.BasicBlock
227+
var nullctxresult llvm.Value
228+
if !context.IsConstant() && len(paramTypes) == len(args) {
229+
currblock := c.builder.GetInsertBlock()
230+
endblock = llvm.AddBasicBlock(currblock.Parent(), "")
231+
endblock.MoveAfter(currblock)
232+
nonnullctxblock = llvm.InsertBasicBlock(endblock, "")
233+
nullctxblock = llvm.InsertBasicBlock(nonnullctxblock, "")
234+
nullctx := c.builder.CreateIsNull(context, "")
235+
c.builder.CreateCondBr(nullctx, nullctxblock, nonnullctxblock)
236+
237+
// null context case.
238+
c.builder.SetInsertPointAtEnd(nullctxblock)
239+
nullctxresult = c.builder.CreateCall(fnptr, args, "")
240+
c.builder.CreateBr(endblock)
241+
c.builder.SetInsertPointAtEnd(nonnullctxblock)
242+
}
243+
244+
// non-null context case.
245+
var result llvm.Value
246+
args := append([]llvm.Value{context}, args...)
247+
if len(paramTypes) < len(args) {
248+
returnType := fntyp.ReturnType()
249+
ctxType := context.Type()
250+
paramTypes := append([]llvm.Type{ctxType}, paramTypes...)
251+
vararg := fntyp.IsFunctionVarArg()
252+
fntyp := llvm.FunctionType(returnType, paramTypes, vararg)
253+
fnptrtyp := llvm.PointerType(fntyp, 0)
254+
fnptr = c.builder.CreateBitCast(fnptr, fnptrtyp, "")
255+
}
256+
result = c.builder.CreateCall(fnptr, args, "")
257+
258+
// If the return type is not void, create a
259+
// PHI node to select which value to return.
260+
if !nullctxresult.IsNil() {
261+
c.builder.CreateBr(endblock)
262+
c.builder.SetInsertPointAtEnd(endblock)
263+
if result.Type().TypeKind() != llvm.VoidTypeKind {
264+
phiresult := c.builder.CreatePHI(result.Type(), "")
265+
values := []llvm.Value{nullctxresult, result}
266+
blocks := []llvm.BasicBlock{nullctxblock, nonnullctxblock}
267+
phiresult.AddIncoming(values, blocks)
268+
result = phiresult
269+
}
270+
}
271+
return c.NewValue(result, result_type)
272+
}
273+
}
274+
result := c.builder.CreateCall(fnptr, args, "")
228275
return c.NewValue(result, result_type)
229276
}
230277

@@ -326,14 +373,13 @@ func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value {
326373
structValue := lhs.LLVMValue()
327374
receiver := c.builder.CreateExtractValue(structValue, 1, "")
328375
f := c.builder.CreateExtractValue(structValue, i+2, "")
329-
i8ptr := &types.Pointer{Base: types.Typ[types.Int8]}
330376
ftype := iface.Methods[i].Type
331-
ftype.Recv = &types.Var{Type: i8ptr}
332-
f = c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), "")
333-
ftype.Recv = nil
334-
method := c.NewValue(f, ftype)
335-
method.receiver = c.NewValue(receiver, i8ptr)
336-
return method
377+
types := []llvm.Type{f.Type(), receiver.Type()}
378+
llvmStructType := llvm.StructType(types, false)
379+
structValue = llvm.Undef(llvmStructType)
380+
structValue = c.builder.CreateInsertValue(structValue, f, 0, "")
381+
structValue = c.builder.CreateInsertValue(structValue, receiver, 1, "")
382+
return c.NewValue(structValue, ftype)
337383
}
338384

339385
// Otherwise, search for field/method,
@@ -418,14 +464,24 @@ func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value {
418464
method := c.Resolve(method).(*LLVMValue)
419465
methodType := method.Type().(*types.Signature)
420466
receiverType := methodType.Recv.Type.(types.Type)
421-
if isIdentical(recvValue.Type(), receiverType) {
422-
method.receiver = recvValue
423-
} else if isIdentical(&types.Pointer{Base: recvValue.Type()}, receiverType) {
424-
method.receiver = recvValue.pointer
425-
} else {
426-
method.receiver = recvValue.makePointee()
467+
var receiver Value
468+
switch {
469+
case isIdentical(recvValue.Type(), receiverType):
470+
receiver = recvValue
471+
case isIdentical(&types.Pointer{Base: recvValue.Type()}, receiverType):
472+
receiver = recvValue.pointer
473+
default:
474+
receiver = recvValue.makePointee()
427475
}
428-
return method
476+
methodValue := method.LLVMValue()
477+
methodValue = c.builder.CreateExtractValue(methodValue, 0, "")
478+
receiverValue := receiver.LLVMValue()
479+
types := []llvm.Type{methodValue.Type(), receiverValue.Type()}
480+
structType := llvm.StructType(types, false)
481+
value := llvm.Undef(structType)
482+
value = c.builder.CreateInsertValue(value, methodValue, 0, "")
483+
value = c.builder.CreateInsertValue(value, receiverValue, 1, "")
484+
return c.NewValue(value, methodType)
429485
} else {
430486
if isIdentical(recvValue.Type(), result.Type) {
431487
// no-op

interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ func (v *LLVMValue) convertV2I(iface *types.Interface) Value {
128128
}
129129
method := v.compiler.Resolve(methodobj).(*LLVMValue)
130130
llvm_value := method.LLVMValue()
131+
llvm_value = builder.CreateExtractValue(llvm_value, 0, "")
131132

132133
// If we have a receiver wider than a word, or a pointer
133134
// receiver value and non-pointer receiver method, then

0 commit comments

Comments
 (0)