Skip to content

Commit 9bd3659

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent d85ac4b commit 9bd3659

File tree

4 files changed

+75
-49
lines changed

4 files changed

+75
-49
lines changed

compiler/channel.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
7979
}
8080

8181
// createChanClose closes the given channel.
82-
func (b *builder) createChanClose(param ssa.Value) {
83-
ch := b.getValue(param)
82+
func (b *builder) createChanClose(ch llvm.Value) {
8483
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
8584
}
8685

compiler/compiler.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ir.Function) *bui
130130
}
131131

132132
type deferBuiltin struct {
133-
funcName string
133+
callName string
134+
pos token.Pos
135+
argTypes []types.Type
134136
callback int
135137
}
136138

@@ -1196,11 +1198,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
11961198

11971199
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
11981200
// LLVM IR. It uses runtime calls for some builtins.
1199-
func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos) (llvm.Value, error) {
1201+
func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
12001202
switch callName {
12011203
case "append":
1202-
src := b.getValue(args[0])
1203-
elems := b.getValue(args[1])
1204+
src := argValues[0]
1205+
elems := argValues[1]
12041206
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
12051207
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
12061208
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
@@ -1221,9 +1223,9 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
12211223
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
12221224
return newSlice, nil
12231225
case "cap":
1224-
value := b.getValue(args[0])
1226+
value := argValues[0]
12251227
var llvmCap llvm.Value
1226-
switch args[0].Type().(type) {
1228+
switch argTypes[0].(type) {
12271229
case *types.Chan:
12281230
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
12291231
case *types.Slice:
@@ -1236,12 +1238,12 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
12361238
}
12371239
return llvmCap, nil
12381240
case "close":
1239-
b.createChanClose(args[0])
1241+
b.createChanClose(argValues[0])
12401242
return llvm.Value{}, nil
12411243
case "complex":
1242-
r := b.getValue(args[0])
1243-
i := b.getValue(args[1])
1244-
t := args[0].Type().Underlying().(*types.Basic)
1244+
r := argValues[0]
1245+
i := argValues[1]
1246+
t := argTypes[0].Underlying().(*types.Basic)
12451247
var cplx llvm.Value
12461248
switch t.Kind() {
12471249
case types.Float32:
@@ -1255,8 +1257,8 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
12551257
cplx = b.CreateInsertValue(cplx, i, 1, "")
12561258
return cplx, nil
12571259
case "copy":
1258-
dst := b.getValue(args[0])
1259-
src := b.getValue(args[1])
1260+
dst := argValues[0]
1261+
src := argValues[1]
12601262
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
12611263
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
12621264
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
@@ -1267,16 +1269,16 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
12671269
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
12681270
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
12691271
case "delete":
1270-
m := b.getValue(args[0])
1271-
key := b.getValue(args[1])
1272-
return llvm.Value{}, b.createMapDelete(args[1].Type(), m, key, pos)
1272+
m := argValues[0]
1273+
key := argValues[1]
1274+
return llvm.Value{}, b.createMapDelete(argTypes[1], m, key, pos)
12731275
case "imag":
1274-
cplx := b.getValue(args[0])
1276+
cplx := argValues[0]
12751277
return b.CreateExtractValue(cplx, 1, "imag"), nil
12761278
case "len":
1277-
value := b.getValue(args[0])
1279+
value := argValues[0]
12781280
var llvmLen llvm.Value
1279-
switch args[0].Type().Underlying().(type) {
1281+
switch argTypes[0].Underlying().(type) {
12801282
case *types.Basic, *types.Slice:
12811283
// string or slice
12821284
llvmLen = b.CreateExtractValue(value, 1, "len")
@@ -1292,12 +1294,11 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
12921294
}
12931295
return llvmLen, nil
12941296
case "print", "println":
1295-
for i, arg := range args {
1297+
for i, value := range argValues {
12961298
if i >= 1 && callName == "println" {
12971299
b.createRuntimeCall("printspace", nil, "")
12981300
}
1299-
value := b.getValue(arg)
1300-
typ := arg.Type().Underlying()
1301+
typ := argTypes[i].Underlying()
13011302
switch typ := typ.(type) {
13021303
case *types.Basic:
13031304
switch typ.Kind() {
@@ -1349,13 +1350,13 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
13491350
}
13501351
return llvm.Value{}, nil // print() or println() returns void
13511352
case "real":
1352-
cplx := b.getValue(args[0])
1353+
cplx := argValues[0]
13531354
return b.CreateExtractValue(cplx, 0, "real"), nil
13541355
case "recover":
13551356
return b.createRuntimeCall("_recover", nil, ""), nil
13561357
case "ssa:wrapnilchk":
13571358
// TODO: do an actual nil check?
1358-
return b.getValue(args[0]), nil
1359+
return argValues[0], nil
13591360
default:
13601361
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
13611362
}
@@ -1432,7 +1433,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
14321433
exported = targetFunc.IsExported()
14331434
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
14341435
// Builtin function (append, close, delete, etc.).)
1435-
return b.createBuiltin(instr.Args, call.Name(), instr.Pos())
1436+
var argTypes []types.Type
1437+
var argValues []llvm.Value
1438+
for _, arg := range instr.Args {
1439+
argTypes = append(argTypes, arg.Type())
1440+
argValues = append(argValues, b.getValue(arg))
1441+
}
1442+
return b.createBuiltin(argTypes, argValues, call.Name(), instr.Pos())
14361443
} else {
14371444
// Function pointer.
14381445
value := b.getValue(instr.Value)

compiler/defer.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
155155
valueTypes = append(valueTypes, context.Type())
156156

157157
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
158-
var funcName string
159-
switch builtin.Name() {
160-
case "close":
161-
funcName = "chanClose"
162-
default:
163-
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
164-
return
158+
var argTypes []types.Type
159+
var argValues []llvm.Value
160+
for _, arg := range instr.Call.Args {
161+
argTypes = append(argTypes, arg.Type())
162+
argValues = append(argValues, b.getValue(arg))
165163
}
166164

167165
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
168166
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
169-
funcName,
170-
len(b.allDeferFuncs),
167+
callName: builtin.Name(),
168+
pos: builtin.Pos(),
169+
argTypes: argTypes,
170+
callback: len(b.allDeferFuncs),
171171
}
172172
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
173173
}
@@ -176,10 +176,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
176176
// Collect all values to be put in the struct (starting with
177177
// runtime._defer fields).
178178
values = []llvm.Value{callback, next}
179-
for _, param := range instr.Call.Args {
180-
llvmParam := b.getValue(param)
181-
values = append(values, llvmParam)
182-
valueTypes = append(valueTypes, llvmParam.Type())
179+
for _, param := range argValues {
180+
values = append(values, param)
181+
valueTypes = append(valueTypes, param.Type())
183182
}
184183

185184
} else {
@@ -426,15 +425,18 @@ func (b *builder) createRunDefers() {
426425
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
427426

428427
// Extract the params from the struct.
429-
var forwardParams []llvm.Value
428+
var argValues []llvm.Value
430429
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
431430
for i := 0; i < params.Len(); i++ {
432431
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
433432
forwardParam := b.CreateLoad(gep, "param")
434-
forwardParams = append(forwardParams, forwardParam)
433+
argValues = append(argValues, forwardParam)
435434
}
436435

437-
b.createRuntimeCall(db.funcName, forwardParams, "")
436+
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
437+
if err != nil {
438+
b.diagnostics = append(b.diagnostics, err)
439+
}
438440
default:
439441
panic("unknown deferred function type")
440442
}

testdata/calls.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ func main() {
7272
regression1033()
7373

7474
//Test deferred builtins
75-
testDeferBuiltin()
75+
testDeferBuiltinClose()
76+
testDeferBuiltinDelete()
7677
}
7778

7879
func runFunc(f func(int), arg int) {
@@ -121,13 +122,30 @@ func testMultiFuncVar() {
121122
defer f(1)
122123
}
123124

124-
func testDeferBuiltin() {
125+
func testDeferBuiltinClose() {
125126
i := make(chan int)
126-
defer close(i)
127+
func() {
128+
defer close(i)
129+
}()
130+
if n, ok := <-i; n != 0 || ok {
131+
println("expected to read 0 from closed channel")
132+
}
127133
}
128134

129-
type dumb struct {
135+
func testDeferBuiltinDelete() {
136+
m := map[int]int{3: 30, 5: 50}
137+
func() {
138+
defer delete(m, 3)
139+
if m[3] != 30 {
140+
println("expected m[3] to be 30")
141+
}
142+
}()
143+
if m[3] != 0 {
144+
println("expected m[3] to be 0")
145+
}
146+
}
130147

148+
type dumb struct {
131149
}
132150

133151
func (*dumb) Value(key interface{}) interface{} {
@@ -144,17 +162,17 @@ func exportedDefer() {
144162
}
145163

146164
func deferFunc() (int, func(int)) {
147-
return 0, func(i int){println("...extracted defer func ", i)}
165+
return 0, func(i int) { println("...extracted defer func ", i) }
148166
}
149167

150168
func multiFuncDefer() func(int) {
151169
i := 0
152170

153171
if i > 0 {
154-
return func(i int){println("Should not have gotten here. i = ", i)}
172+
return func(i int) { println("Should not have gotten here. i = ", i) }
155173
}
156174

157-
return func(i int){println("Called the correct function. i = ", i)}
175+
return func(i int) { println("Called the correct function. i = ", i) }
158176
}
159177

160178
func testBound(f func() string) {

0 commit comments

Comments
 (0)