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.
|
// createChanClose closes the given channel.
|
||||||
func (b *builder) createChanClose(param ssa.Value) {
|
func (b *builder) createChanClose(ch llvm.Value) {
|
||||||
ch := b.getValue(param)
|
|
||||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,9 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ir.Function) *bui
|
||||||
}
|
}
|
||||||
|
|
||||||
type deferBuiltin struct {
|
type deferBuiltin struct {
|
||||||
funcName string
|
callName string
|
||||||
|
pos token.Pos
|
||||||
|
argTypes []types.Type
|
||||||
callback int
|
callback int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1196,11 +1198,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||||
|
|
||||||
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
||||||
// LLVM IR. It uses runtime calls for some builtins.
|
// 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 {
|
switch callName {
|
||||||
case "append":
|
case "append":
|
||||||
src := b.getValue(args[0])
|
src := argValues[0]
|
||||||
elems := b.getValue(args[1])
|
elems := argValues[1]
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
|
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
|
||||||
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
|
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
|
||||||
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
|
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, "")
|
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
|
||||||
return newSlice, nil
|
return newSlice, nil
|
||||||
case "cap":
|
case "cap":
|
||||||
value := b.getValue(args[0])
|
value := argValues[0]
|
||||||
var llvmCap llvm.Value
|
var llvmCap llvm.Value
|
||||||
switch args[0].Type().(type) {
|
switch argTypes[0].(type) {
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
|
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
|
@ -1236,12 +1238,12 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
||||||
}
|
}
|
||||||
return llvmCap, nil
|
return llvmCap, nil
|
||||||
case "close":
|
case "close":
|
||||||
b.createChanClose(args[0])
|
b.createChanClose(argValues[0])
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
case "complex":
|
case "complex":
|
||||||
r := b.getValue(args[0])
|
r := argValues[0]
|
||||||
i := b.getValue(args[1])
|
i := argValues[1]
|
||||||
t := args[0].Type().Underlying().(*types.Basic)
|
t := argTypes[0].Underlying().(*types.Basic)
|
||||||
var cplx llvm.Value
|
var cplx llvm.Value
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case types.Float32:
|
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, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx, nil
|
return cplx, nil
|
||||||
case "copy":
|
case "copy":
|
||||||
dst := b.getValue(args[0])
|
dst := argValues[0]
|
||||||
src := b.getValue(args[1])
|
src := argValues[1]
|
||||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
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)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||||
case "delete":
|
case "delete":
|
||||||
m := b.getValue(args[0])
|
m := argValues[0]
|
||||||
key := b.getValue(args[1])
|
key := argValues[1]
|
||||||
return llvm.Value{}, b.createMapDelete(args[1].Type(), m, key, pos)
|
return llvm.Value{}, b.createMapDelete(argTypes[1], m, key, pos)
|
||||||
case "imag":
|
case "imag":
|
||||||
cplx := b.getValue(args[0])
|
cplx := argValues[0]
|
||||||
return b.CreateExtractValue(cplx, 1, "imag"), nil
|
return b.CreateExtractValue(cplx, 1, "imag"), nil
|
||||||
case "len":
|
case "len":
|
||||||
value := b.getValue(args[0])
|
value := argValues[0]
|
||||||
var llvmLen llvm.Value
|
var llvmLen llvm.Value
|
||||||
switch args[0].Type().Underlying().(type) {
|
switch argTypes[0].Underlying().(type) {
|
||||||
case *types.Basic, *types.Slice:
|
case *types.Basic, *types.Slice:
|
||||||
// string or slice
|
// string or slice
|
||||||
llvmLen = b.CreateExtractValue(value, 1, "len")
|
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
|
return llvmLen, nil
|
||||||
case "print", "println":
|
case "print", "println":
|
||||||
for i, arg := range args {
|
for i, value := range argValues {
|
||||||
if i >= 1 && callName == "println" {
|
if i >= 1 && callName == "println" {
|
||||||
b.createRuntimeCall("printspace", nil, "")
|
b.createRuntimeCall("printspace", nil, "")
|
||||||
}
|
}
|
||||||
value := b.getValue(arg)
|
typ := argTypes[i].Underlying()
|
||||||
typ := arg.Type().Underlying()
|
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
switch typ.Kind() {
|
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
|
return llvm.Value{}, nil // print() or println() returns void
|
||||||
case "real":
|
case "real":
|
||||||
cplx := b.getValue(args[0])
|
cplx := argValues[0]
|
||||||
return b.CreateExtractValue(cplx, 0, "real"), nil
|
return b.CreateExtractValue(cplx, 0, "real"), nil
|
||||||
case "recover":
|
case "recover":
|
||||||
return b.createRuntimeCall("_recover", nil, ""), nil
|
return b.createRuntimeCall("_recover", nil, ""), nil
|
||||||
case "ssa:wrapnilchk":
|
case "ssa:wrapnilchk":
|
||||||
// TODO: do an actual nil check?
|
// TODO: do an actual nil check?
|
||||||
return b.getValue(args[0]), nil
|
return argValues[0], nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
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()
|
exported = targetFunc.IsExported()
|
||||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||||
// Builtin function (append, close, delete, etc.).)
|
// 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 {
|
} else {
|
||||||
// Function pointer.
|
// Function pointer.
|
||||||
value := b.getValue(instr.Value)
|
value := b.getValue(instr.Value)
|
||||||
|
|
|
@ -155,19 +155,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
valueTypes = append(valueTypes, context.Type())
|
valueTypes = append(valueTypes, context.Type())
|
||||||
|
|
||||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||||
var funcName string
|
var argTypes []types.Type
|
||||||
switch builtin.Name() {
|
var argValues []llvm.Value
|
||||||
case "close":
|
for _, arg := range instr.Call.Args {
|
||||||
funcName = "chanClose"
|
argTypes = append(argTypes, arg.Type())
|
||||||
default:
|
argValues = append(argValues, b.getValue(arg))
|
||||||
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
||||||
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
|
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
|
||||||
funcName,
|
callName: builtin.Name(),
|
||||||
len(b.allDeferFuncs),
|
pos: builtin.Pos(),
|
||||||
|
argTypes: argTypes,
|
||||||
|
callback: len(b.allDeferFuncs),
|
||||||
}
|
}
|
||||||
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
|
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
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields).
|
// runtime._defer fields).
|
||||||
values = []llvm.Value{callback, next}
|
values = []llvm.Value{callback, next}
|
||||||
for _, param := range instr.Call.Args {
|
for _, param := range argValues {
|
||||||
llvmParam := b.getValue(param)
|
values = append(values, param)
|
||||||
values = append(values, llvmParam)
|
valueTypes = append(valueTypes, param.Type())
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -426,15 +425,18 @@ func (b *builder) createRunDefers() {
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
var forwardParams []llvm.Value
|
var argValues []llvm.Value
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
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:
|
default:
|
||||||
panic("unknown deferred function type")
|
panic("unknown deferred function type")
|
||||||
}
|
}
|
||||||
|
|
32
testdata/calls.go
предоставленный
32
testdata/calls.go
предоставленный
|
@ -72,7 +72,8 @@ func main() {
|
||||||
regression1033()
|
regression1033()
|
||||||
|
|
||||||
//Test deferred builtins
|
//Test deferred builtins
|
||||||
testDeferBuiltin()
|
testDeferBuiltinClose()
|
||||||
|
testDeferBuiltinDelete()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFunc(f func(int), arg int) {
|
func runFunc(f func(int), arg int) {
|
||||||
|
@ -121,13 +122,30 @@ func testMultiFuncVar() {
|
||||||
defer f(1)
|
defer f(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDeferBuiltin() {
|
func testDeferBuiltinClose() {
|
||||||
i := make(chan int)
|
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 {
|
type dumb struct {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*dumb) Value(key interface{}) interface{} {
|
func (*dumb) Value(key interface{}) interface{} {
|
||||||
|
@ -144,17 +162,17 @@ func exportedDefer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func deferFunc() (int, func(int)) {
|
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) {
|
func multiFuncDefer() func(int) {
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
if 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) {
|
func testBound(f func() string) {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче