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.
Этот коммит содержится в:
Ayke van Laethem 2021-01-21 17:10:19 +01:00 коммит произвёл Ron Evans
родитель 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 предоставленный
Просмотреть файл

@ -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) {