diff --git a/compiler/channel.go b/compiler/channel.go index 6e2d4319..a036fbf4 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -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}, "") } diff --git a/compiler/compiler.go b/compiler/compiler.go index d9a5ed4e..d31d2077 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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) diff --git a/compiler/defer.go b/compiler/defer.go index db2bed32..eb35ab23 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -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") } diff --git a/testdata/calls.go b/testdata/calls.go index 9f6ba69f..d1c80dd0 100644 --- a/testdata/calls.go +++ b/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) {