compiler: support all kinds of deferred builtins
This change extends defer support to all supported builitin functions. Not all of them make sense (such as len, cap, real, imag, etc) but this change for example adds support for `defer(delete(m, key))` which is used in the Go 1.15 encoding/json package.
Этот коммит содержится в:
родитель
d85ac4b3cc
коммит
9bd36597d6
4 изменённых файлов: 75 добавлений и 49 удалений
|
@ -79,8 +79,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||
}
|
||||
|
||||
// createChanClose closes the given channel.
|
||||
func (b *builder) createChanClose(param ssa.Value) {
|
||||
ch := b.getValue(param)
|
||||
func (b *builder) createChanClose(ch llvm.Value) {
|
||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ir.Function) *bui
|
|||
}
|
||||
|
||||
type deferBuiltin struct {
|
||||
funcName string
|
||||
callName string
|
||||
pos token.Pos
|
||||
argTypes []types.Type
|
||||
callback int
|
||||
}
|
||||
|
||||
|
@ -1196,11 +1198,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||
|
||||
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
||||
// LLVM IR. It uses runtime calls for some builtins.
|
||||
func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos) (llvm.Value, error) {
|
||||
func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
|
||||
switch callName {
|
||||
case "append":
|
||||
src := b.getValue(args[0])
|
||||
elems := b.getValue(args[1])
|
||||
src := argValues[0]
|
||||
elems := argValues[1]
|
||||
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
|
||||
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
|
||||
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
|
||||
|
@ -1221,9 +1223,9 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
|
||||
return newSlice, nil
|
||||
case "cap":
|
||||
value := b.getValue(args[0])
|
||||
value := argValues[0]
|
||||
var llvmCap llvm.Value
|
||||
switch args[0].Type().(type) {
|
||||
switch argTypes[0].(type) {
|
||||
case *types.Chan:
|
||||
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
|
||||
case *types.Slice:
|
||||
|
@ -1236,12 +1238,12 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
}
|
||||
return llvmCap, nil
|
||||
case "close":
|
||||
b.createChanClose(args[0])
|
||||
b.createChanClose(argValues[0])
|
||||
return llvm.Value{}, nil
|
||||
case "complex":
|
||||
r := b.getValue(args[0])
|
||||
i := b.getValue(args[1])
|
||||
t := args[0].Type().Underlying().(*types.Basic)
|
||||
r := argValues[0]
|
||||
i := argValues[1]
|
||||
t := argTypes[0].Underlying().(*types.Basic)
|
||||
var cplx llvm.Value
|
||||
switch t.Kind() {
|
||||
case types.Float32:
|
||||
|
@ -1255,8 +1257,8 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case "copy":
|
||||
dst := b.getValue(args[0])
|
||||
src := b.getValue(args[1])
|
||||
dst := argValues[0]
|
||||
src := argValues[1]
|
||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||
|
@ -1267,16 +1269,16 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||
case "delete":
|
||||
m := b.getValue(args[0])
|
||||
key := b.getValue(args[1])
|
||||
return llvm.Value{}, b.createMapDelete(args[1].Type(), m, key, pos)
|
||||
m := argValues[0]
|
||||
key := argValues[1]
|
||||
return llvm.Value{}, b.createMapDelete(argTypes[1], m, key, pos)
|
||||
case "imag":
|
||||
cplx := b.getValue(args[0])
|
||||
cplx := argValues[0]
|
||||
return b.CreateExtractValue(cplx, 1, "imag"), nil
|
||||
case "len":
|
||||
value := b.getValue(args[0])
|
||||
value := argValues[0]
|
||||
var llvmLen llvm.Value
|
||||
switch args[0].Type().Underlying().(type) {
|
||||
switch argTypes[0].Underlying().(type) {
|
||||
case *types.Basic, *types.Slice:
|
||||
// string or slice
|
||||
llvmLen = b.CreateExtractValue(value, 1, "len")
|
||||
|
@ -1292,12 +1294,11 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
}
|
||||
return llvmLen, nil
|
||||
case "print", "println":
|
||||
for i, arg := range args {
|
||||
for i, value := range argValues {
|
||||
if i >= 1 && callName == "println" {
|
||||
b.createRuntimeCall("printspace", nil, "")
|
||||
}
|
||||
value := b.getValue(arg)
|
||||
typ := arg.Type().Underlying()
|
||||
typ := argTypes[i].Underlying()
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
|
@ -1349,13 +1350,13 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
|||
}
|
||||
return llvm.Value{}, nil // print() or println() returns void
|
||||
case "real":
|
||||
cplx := b.getValue(args[0])
|
||||
cplx := argValues[0]
|
||||
return b.CreateExtractValue(cplx, 0, "real"), nil
|
||||
case "recover":
|
||||
return b.createRuntimeCall("_recover", nil, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
return b.getValue(args[0]), nil
|
||||
return argValues[0], nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||
}
|
||||
|
@ -1432,7 +1433,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||
exported = targetFunc.IsExported()
|
||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||
// Builtin function (append, close, delete, etc.).)
|
||||
return b.createBuiltin(instr.Args, call.Name(), instr.Pos())
|
||||
var argTypes []types.Type
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
}
|
||||
return b.createBuiltin(argTypes, argValues, call.Name(), instr.Pos())
|
||||
} else {
|
||||
// Function pointer.
|
||||
value := b.getValue(instr.Value)
|
||||
|
|
|
@ -155,19 +155,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||
valueTypes = append(valueTypes, context.Type())
|
||||
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
var funcName string
|
||||
switch builtin.Name() {
|
||||
case "close":
|
||||
funcName = "chanClose"
|
||||
default:
|
||||
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
|
||||
return
|
||||
var argTypes []types.Type
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Call.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
}
|
||||
|
||||
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
||||
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
|
||||
funcName,
|
||||
len(b.allDeferFuncs),
|
||||
callName: builtin.Name(),
|
||||
pos: builtin.Pos(),
|
||||
argTypes: argTypes,
|
||||
callback: len(b.allDeferFuncs),
|
||||
}
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
|
||||
}
|
||||
|
@ -176,10 +176,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param)
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
for _, param := range argValues {
|
||||
values = append(values, param)
|
||||
valueTypes = append(valueTypes, param.Type())
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -426,15 +425,18 @@ func (b *builder) createRunDefers() {
|
|||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
|
||||
// Extract the params from the struct.
|
||||
var forwardParams []llvm.Value
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
argValues = append(argValues, forwardParam)
|
||||
}
|
||||
|
||||
b.createRuntimeCall(db.funcName, forwardParams, "")
|
||||
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
||||
if err != nil {
|
||||
b.diagnostics = append(b.diagnostics, err)
|
||||
}
|
||||
default:
|
||||
panic("unknown deferred function type")
|
||||
}
|
||||
|
|
32
testdata/calls.go
предоставленный
32
testdata/calls.go
предоставленный
|
@ -72,7 +72,8 @@ func main() {
|
|||
regression1033()
|
||||
|
||||
//Test deferred builtins
|
||||
testDeferBuiltin()
|
||||
testDeferBuiltinClose()
|
||||
testDeferBuiltinDelete()
|
||||
}
|
||||
|
||||
func runFunc(f func(int), arg int) {
|
||||
|
@ -121,13 +122,30 @@ func testMultiFuncVar() {
|
|||
defer f(1)
|
||||
}
|
||||
|
||||
func testDeferBuiltin() {
|
||||
func testDeferBuiltinClose() {
|
||||
i := make(chan int)
|
||||
defer close(i)
|
||||
func() {
|
||||
defer close(i)
|
||||
}()
|
||||
if n, ok := <-i; n != 0 || ok {
|
||||
println("expected to read 0 from closed channel")
|
||||
}
|
||||
}
|
||||
|
||||
func testDeferBuiltinDelete() {
|
||||
m := map[int]int{3: 30, 5: 50}
|
||||
func() {
|
||||
defer delete(m, 3)
|
||||
if m[3] != 30 {
|
||||
println("expected m[3] to be 30")
|
||||
}
|
||||
}()
|
||||
if m[3] != 0 {
|
||||
println("expected m[3] to be 0")
|
||||
}
|
||||
}
|
||||
|
||||
type dumb struct {
|
||||
|
||||
}
|
||||
|
||||
func (*dumb) Value(key interface{}) interface{} {
|
||||
|
@ -144,17 +162,17 @@ func exportedDefer() {
|
|||
}
|
||||
|
||||
func deferFunc() (int, func(int)) {
|
||||
return 0, func(i int){println("...extracted defer func ", i)}
|
||||
return 0, func(i int) { println("...extracted defer func ", i) }
|
||||
}
|
||||
|
||||
func multiFuncDefer() func(int) {
|
||||
i := 0
|
||||
|
||||
if i > 0 {
|
||||
return func(i int){println("Should not have gotten here. i = ", i)}
|
||||
return func(i int) { println("Should not have gotten here. i = ", i) }
|
||||
}
|
||||
|
||||
return func(i int){println("Called the correct function. i = ", i)}
|
||||
return func(i int) { println("Called the correct function. i = ", i) }
|
||||
}
|
||||
|
||||
func testBound(f func() string) {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче