Skip to content

Commit d752e66

Browse files
aykevldeadprogram
authored andcommitted
compiler: refactor function calling
1 parent d46934d commit d752e66

File tree

6 files changed

+177
-171
lines changed

6 files changed

+177
-171
lines changed

compiler/compiler.go

Lines changed: 55 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
10661066
// A goroutine call on a func value, but the callee is trivial to find. For
10671067
// example: immediately applied functions.
10681068
funcValue := frame.getValue(value)
1069-
context = c.extractFuncContext(funcValue)
1069+
context = frame.extractFuncContext(funcValue)
10701070
default:
10711071
panic("StaticCallee returned an unexpected value")
10721072
}
@@ -1078,7 +1078,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
10781078
// goroutine:
10791079
// * The function context, for closures.
10801080
// * The function pointer (for tasks).
1081-
funcPtr, context := c.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
1081+
funcPtr, context := frame.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
10821082
params = append(params, context) // context parameter
10831083
switch c.Scheduler() {
10841084
case "none", "coroutines":
@@ -1315,86 +1315,90 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
13151315
}
13161316
}
13171317

1318-
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, exported bool) llvm.Value {
1319-
var params []llvm.Value
1320-
for _, param := range args {
1321-
params = append(params, frame.getValue(param))
1322-
}
1323-
1324-
if !exported {
1325-
// This function takes a context parameter.
1326-
// Add it to the end of the parameter list.
1327-
params = append(params, context)
1328-
1329-
// Parent coroutine handle.
1330-
params = append(params, llvm.Undef(c.i8ptrType))
1331-
}
1332-
1333-
return c.createCall(llvmFn, params, "")
1334-
}
1335-
1336-
func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
1318+
// createFunctionCall lowers a Go SSA call instruction (to a simple function,
1319+
// closure, function pointer, builtin, method, etc.) to LLVM IR, usually a call
1320+
// instruction.
1321+
//
1322+
// This is also where compiler intrinsics are implemented.
1323+
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
13371324
if instr.IsInvoke() {
1338-
fnCast, args := frame.getInvokeCall(instr)
1339-
return c.createCall(fnCast, args, ""), nil
1325+
fnCast, args := b.getInvokeCall(instr)
1326+
return b.createCall(fnCast, args, ""), nil
13401327
}
13411328

13421329
// Try to call the function directly for trivially static calls.
1330+
var callee, context llvm.Value
1331+
exported := false
13431332
if fn := instr.StaticCallee(); fn != nil {
1333+
// Direct function call, either to a named or anonymous (directly
1334+
// applied) function call. If it is anonymous, it may be a closure.
13441335
name := fn.RelString(nil)
13451336
switch {
13461337
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
1347-
return c.emitReadRegister(name, instr.Args)
1338+
return b.createReadRegister(name, instr.Args)
13481339
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
1349-
return c.emitAsm(instr.Args)
1340+
return b.createInlineAsm(instr.Args)
13501341
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
1351-
return c.emitAsmFull(frame, instr)
1342+
return b.createInlineAsmFull(instr)
13521343
case strings.HasPrefix(name, "device/arm.SVCall"):
1353-
return c.emitSVCall(frame, instr.Args)
1344+
return b.emitSVCall(instr.Args)
13541345
case strings.HasPrefix(name, "(device/riscv.CSR)."):
1355-
return c.emitCSROperation(frame, instr)
1346+
return b.emitCSROperation(instr)
13561347
case strings.HasPrefix(name, "syscall.Syscall"):
1357-
return c.emitSyscall(frame, instr)
1348+
return b.createSyscall(instr)
13581349
case strings.HasPrefix(name, "runtime/volatile.Load"):
1359-
return c.emitVolatileLoad(frame, instr)
1350+
return b.createVolatileLoad(instr)
13601351
case strings.HasPrefix(name, "runtime/volatile.Store"):
1361-
return c.emitVolatileStore(frame, instr)
1352+
return b.createVolatileStore(instr)
13621353
case name == "runtime/interrupt.New":
1363-
return c.emitInterruptGlobal(frame, instr)
1354+
return b.createInterruptGlobal(instr)
13641355
}
13651356

1366-
targetFunc := c.ir.GetFunction(fn)
1357+
targetFunc := b.ir.GetFunction(fn)
13671358
if targetFunc.LLVMFn.IsNil() {
1368-
return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
1359+
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
13691360
}
1370-
var context llvm.Value
13711361
switch value := instr.Value.(type) {
13721362
case *ssa.Function:
13731363
// Regular function call. No context is necessary.
1374-
context = llvm.Undef(c.i8ptrType)
1364+
context = llvm.Undef(b.i8ptrType)
13751365
case *ssa.MakeClosure:
13761366
// A call on a func value, but the callee is trivial to find. For
13771367
// example: immediately applied functions.
1378-
funcValue := frame.getValue(value)
1379-
context = c.extractFuncContext(funcValue)
1368+
funcValue := b.getValue(value)
1369+
context = b.extractFuncContext(funcValue)
13801370
default:
13811371
panic("StaticCallee returned an unexpected value")
13821372
}
1383-
return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, targetFunc.IsExported()), nil
1384-
}
1385-
1386-
// Builtin or function pointer.
1387-
switch call := instr.Value.(type) {
1388-
case *ssa.Builtin:
1389-
return frame.createBuiltin(instr.Args, call.Name(), instr.Pos())
1390-
default: // function pointer
1391-
value := frame.getValue(instr.Value)
1373+
callee = targetFunc.LLVMFn
1374+
exported = targetFunc.IsExported()
1375+
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
1376+
// Builtin function (append, close, delete, etc.).)
1377+
return b.createBuiltin(instr.Args, call.Name(), instr.Pos())
1378+
} else {
1379+
// Function pointer.
1380+
value := b.getValue(instr.Value)
13921381
// This is a func value, which cannot be called directly. We have to
13931382
// extract the function pointer and context first from the func value.
1394-
funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
1395-
frame.createNilCheck(funcPtr, "fpcall")
1396-
return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil
1383+
callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
1384+
b.createNilCheck(callee, "fpcall")
1385+
}
1386+
1387+
var params []llvm.Value
1388+
for _, param := range instr.Args {
1389+
params = append(params, b.getValue(param))
1390+
}
1391+
1392+
if !exported {
1393+
// This function takes a context parameter.
1394+
// Add it to the end of the parameter list.
1395+
params = append(params, context)
1396+
1397+
// Parent coroutine handle.
1398+
params = append(params, llvm.Undef(b.i8ptrType))
13971399
}
1400+
1401+
return b.createCall(callee, params, ""), nil
13981402
}
13991403

14001404
// getValue returns the LLVM value of a constant, function value, global, or
@@ -1462,9 +1466,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
14621466
y := frame.getValue(expr.Y)
14631467
return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
14641468
case *ssa.Call:
1465-
// Passing the current task here to the subroutine. It is only used when
1466-
// the subroutine is blocking.
1467-
return c.parseCall(frame, expr.Common())
1469+
return frame.createFunctionCall(expr.Common())
14681470
case *ssa.ChangeInterface:
14691471
// Do not change between interface types: always use the underlying
14701472
// (concrete) type in the type number of the interface. Every method

compiler/func.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,22 @@ func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
7171

7272
// extractFuncContext extracts the context pointer from this function value. It
7373
// is a cheap operation.
74-
func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
75-
return c.builder.CreateExtractValue(funcValue, 0, "")
74+
func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
75+
return b.CreateExtractValue(funcValue, 0, "")
7676
}
7777

7878
// decodeFuncValue extracts the context and the function pointer from this func
7979
// value. This may be an expensive operation.
80-
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
81-
context = c.builder.CreateExtractValue(funcValue, 0, "")
82-
switch c.FuncImplementation() {
80+
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
81+
context = b.CreateExtractValue(funcValue, 0, "")
82+
switch b.FuncImplementation() {
8383
case compileopts.FuncValueDoubleword:
84-
funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
84+
funcPtr = b.CreateExtractValue(funcValue, 1, "")
8585
case compileopts.FuncValueSwitch:
86-
llvmSig := c.getRawFuncType(sig)
87-
sigGlobal := c.getTypeCode(sig)
88-
funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
89-
funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
86+
llvmSig := b.getRawFuncType(sig)
87+
sigGlobal := b.getTypeCode(sig)
88+
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
89+
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
9090
default:
9191
panic("unimplemented func value variant")
9292
}

compiler/inlineasm.go

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import (
1818
// func ReadRegister(name string) uintptr
1919
//
2020
// The register name must be a constant, for example "sp".
21-
func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
22-
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
21+
func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
22+
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false)
2323
regname := constant.StringVal(args[0].(*ssa.Const).Value)
2424
var asm string
2525
switch name {
@@ -31,7 +31,7 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
3131
panic("unknown architecture")
3232
}
3333
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
34-
return c.builder.CreateCall(target, nil, ""), nil
34+
return b.CreateCall(target, nil, ""), nil
3535
}
3636

3737
// This is a compiler builtin, which emits a piece of inline assembly with no
@@ -41,12 +41,12 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
4141
// func Asm(asm string)
4242
//
4343
// The provided assembly must be a constant.
44-
func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
44+
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
4545
// Magic function: insert inline assembly instead of calling it.
46-
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
46+
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
4747
asm := constant.StringVal(args[0].(*ssa.Const).Value)
4848
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
49-
return c.builder.CreateCall(target, nil, ""), nil
49+
return b.CreateCall(target, nil, ""), nil
5050
}
5151

5252
// This is a compiler builtin, which allows assembly to be called in a flexible
@@ -63,7 +63,7 @@ func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
6363
// "value": 1
6464
// "result": &dest,
6565
// })
66-
func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
66+
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
6767
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
6868
registers := map[string]llvm.Value{}
6969
registerMap := instr.Args[1].(*ssa.MakeMap)
@@ -73,17 +73,17 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
7373
// ignore
7474
case *ssa.MapUpdate:
7575
if r.Block() != registerMap.Block() {
76-
return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block")
76+
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
7777
}
7878
key := constant.StringVal(r.Key.(*ssa.Const).Value)
7979
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
80-
registers[key] = frame.getValue(r.Value.(*ssa.MakeInterface).X)
80+
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
8181
case *ssa.Call:
8282
if r.Common() == instr {
8383
break
8484
}
8585
default:
86-
return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
86+
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
8787
}
8888
}
8989
// TODO: handle dollar signs in asm string
@@ -98,7 +98,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
9898
name := s[1 : len(s)-1]
9999
if _, ok := registers[name]; !ok {
100100
if err == nil {
101-
err = c.makeError(instr.Pos(), "unknown register name: "+name)
101+
err = b.makeError(instr.Pos(), "unknown register name: "+name)
102102
}
103103
return s
104104
}
@@ -112,7 +112,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
112112
case llvm.PointerTypeKind:
113113
constraints = append(constraints, "*m")
114114
default:
115-
err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
115+
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
116116
return s
117117
}
118118
}
@@ -121,9 +121,9 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
121121
if err != nil {
122122
return llvm.Value{}, err
123123
}
124-
fnType := llvm.FunctionType(c.ctx.VoidType(), argTypes, false)
124+
fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false)
125125
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
126-
return c.builder.CreateCall(target, args, ""), nil
126+
return b.CreateCall(target, args, ""), nil
127127
}
128128

129129
// This is a compiler builtin which emits an inline SVCall instruction. It can
@@ -137,7 +137,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
137137
//
138138
// The num parameter must be a constant. All other parameters may be any scalar
139139
// value supported by LLVM inline assembly.
140-
func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error) {
140+
func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
141141
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
142142
llvmArgs := []llvm.Value{}
143143
argTypes := []llvm.Type{}
@@ -150,17 +150,17 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
150150
} else {
151151
constraints += ",{r" + strconv.Itoa(i) + "}"
152152
}
153-
llvmValue := frame.getValue(arg)
153+
llvmValue := b.getValue(arg)
154154
llvmArgs = append(llvmArgs, llvmValue)
155155
argTypes = append(argTypes, llvmValue.Type())
156156
}
157157
// Implement the ARM calling convention by marking r1-r3 as
158158
// clobbered. r0 is used as an output register so doesn't have to be
159159
// marked as clobbered.
160160
constraints += ",~{r1},~{r2},~{r3}"
161-
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
161+
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
162162
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
163-
return c.builder.CreateCall(target, llvmArgs, ""), nil
163+
return b.CreateCall(target, llvmArgs, ""), nil
164164
}
165165

166166
// This is a compiler builtin which emits CSR instructions. It can be one of:
@@ -172,38 +172,38 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
172172
//
173173
// The csr parameter (method receiver) must be a constant. Other parameter can
174174
// be any value.
175-
func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
175+
func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
176176
csrConst, ok := call.Args[0].(*ssa.Const)
177177
if !ok {
178-
return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant")
178+
return llvm.Value{}, b.makeError(call.Pos(), "CSR must be constant")
179179
}
180180
csr := csrConst.Uint64()
181181
switch name := call.StaticCallee().Name(); name {
182182
case "Get":
183183
// Note that this instruction may have side effects, and thus must be
184184
// marked as such.
185-
fnType := llvm.FunctionType(c.uintptrType, nil, false)
185+
fnType := llvm.FunctionType(b.uintptrType, nil, false)
186186
asm := fmt.Sprintf("csrr $0, %d", csr)
187187
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
188-
return c.builder.CreateCall(target, nil, ""), nil
188+
return b.CreateCall(target, nil, ""), nil
189189
case "Set":
190-
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false)
190+
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
191191
asm := fmt.Sprintf("csrw %d, $0", csr)
192192
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
193-
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
193+
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
194194
case "SetBits":
195195
// Note: it may be possible to optimize this to csrrsi in many cases.
196-
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
196+
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
197197
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
198198
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
199-
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
199+
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
200200
case "ClearBits":
201201
// Note: it may be possible to optimize this to csrrci in many cases.
202-
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
202+
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
203203
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
204204
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
205-
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
205+
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
206206
default:
207-
return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name)
207+
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
208208
}
209209
}

0 commit comments

Comments
 (0)