Skip to content

Commit fb1db3b

Browse files
authored
optimized closures & implements defer (#1186)
1 parent cccba5b commit fb1db3b

File tree

12 files changed

+331
-41
lines changed

12 files changed

+331
-41
lines changed

src/hexer/destroyer.nim

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,11 @@ proc trTry(c: var Context; n: var Cursor) =
342342
takeTree c.dest, n # `E as e`
343343
trNestedScope c, n, Other, fin
344344
if n.substructureKind == FinU:
345-
copyInto(c.dest, n):
346-
trNestedScope c, n
345+
if hasExcept:
346+
copyInto(c.dest, n):
347+
trNestedScope c, n
348+
else:
349+
skip n
347350

348351
proc tr(c: var Context; n: var Cursor) =
349352
if isAtom(n) or isDeclarative(n):

src/hexer/lambdalifting.nim

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type
5151
s: SymId
5252
typ: SymId
5353
mode: EnvMode
54+
needsHeap: bool
5455

5556
EnvField = object
5657
objType: SymId
@@ -62,7 +63,7 @@ type
6263
typeCache: TypeCache
6364
thisModuleSuffix: string
6465
procStack: seq[SymId]
65-
closureProcs, createsEnv: HashSet[SymId]
66+
closureProcs, createsEnv, escapes: HashSet[SymId]
6667
localToEnv: Table[SymId, EnvField]
6768
env: CurrentEnv
6869

@@ -80,19 +81,21 @@ proc trLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
8081
tr(c, dest, n)
8182

8283
proc trProc(c: var Context; dest: var TokenBuf; n: var Cursor) =
83-
c.typeCache.openScope(ProcScope)
84+
#c.typeCache.openScope(ProcScope)
8485
copyInto dest, n:
8586
let symId = n.symId
8687
c.procStack.add(symId)
8788
var isConcrete = true # assume it is concrete
8889
for i in 0..<BodyPos:
8990
if i == ParamsPos:
91+
c.typeCache.openProcScope(symId, n)
9092
c.typeCache.registerParams(symId, n)
9193
elif i == TypeVarsPos:
9294
isConcrete = n.substructureKind != TypevarsU
9395
elif i == ProcPragmasPos:
9496
if hasPragma(n, ClosureP):
9597
c.closureProcs.incl symId
98+
c.escapes.incl symId
9699
takeTree dest, n
97100
if isConcrete:
98101
tr(c, dest, n)
@@ -119,6 +122,17 @@ proc localToField(c: var Context; n: Cursor; local, typ: SymId): SymId =
119122
result = pool.syms.getOrIncl(name)
120123
c.localToEnv[local] = EnvField(objType: typ, field: result, typ: c.typeCache.getType(n))
121124

125+
proc trCall(c: var Context; dest: var TokenBuf; n: var Cursor) =
126+
dest.add n
127+
inc n
128+
if n.kind == Symbol:
129+
# if a closure proc is called, we don't want to see it as "escaping".
130+
dest.add n
131+
inc n
132+
while n.kind != ParRi:
133+
tr(c, dest, n)
134+
dest.takeParRi(n)
135+
122136
proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
123137
case n.kind
124138
of DotToken, UnknownToken, EofToken, Ident, SymbolDef,
@@ -158,6 +172,11 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
158172
inc n
159173
else:
160174
takeTree dest, n
175+
elif loc.kind in {ProcY, FuncY, IteratorY, ConverterY, MethodY}:
176+
# usage of a closure proc not within a call? --> The closure does escape:
177+
if c.procStack.len > 0:
178+
c.escapes.incl n.symId
179+
takeTree dest, n
161180
else:
162181
takeTree dest, n
163182
of ParLe:
@@ -176,7 +195,10 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
176195
trSons(c, dest, n)
177196
c.typeCache.closeScope()
178197
else:
179-
if n.exprKind == TypeofX:
198+
case n.exprKind
199+
of CallKinds:
200+
trCall c, dest, n
201+
of TypeofX:
180202
takeTree dest, n
181203
else:
182204
trSons(c, dest, n)
@@ -214,8 +236,11 @@ proc untypedEnv(dest: var TokenBuf; info: PackedLineInfo; env: CurrentEnv) =
214236
assert env.s != SymId(0)
215237
case env.mode
216238
of EnvIsLocal:
217-
dest.copyIntoKind CastX, info:
218-
dest.addRootRef info
239+
if env.needsHeap:
240+
dest.copyIntoKind CastX, info:
241+
dest.addRootRef info
242+
dest.addSymUse env.s, info
243+
else:
219244
dest.addSymUse env.s, info
220245
of EnvIsParam:
221246
# the parameter already has the erased type:
@@ -228,10 +253,13 @@ proc typedEnv(dest: var TokenBuf; info: PackedLineInfo; env: CurrentEnv) =
228253
# the local already has the full type:
229254
dest.addSymUse env.s, info
230255
of EnvIsParam:
231-
# the parameter has the erased type:
232-
dest.copyIntoKind CastX, info:
233-
dest.copyIntoKind RefT, info:
234-
dest.addSymUse env.typ, info
256+
if env.needsHeap:
257+
# the parameter has the erased type:
258+
dest.copyIntoKind CastX, info:
259+
dest.copyIntoKind RefT, info:
260+
dest.addSymUse env.typ, info
261+
dest.addSymUse env.s, info
262+
else:
235263
dest.addSymUse env.s, info
236264

237265
proc tre(c: var Context; dest: var TokenBuf; n: var Cursor)
@@ -258,7 +286,10 @@ proc treLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
258286
# generate an assignment:
259287
dest.copyIntoKind AsgnS, info:
260288
dest.copyIntoKind DotX, info:
261-
dest.copyIntoKind DerefX, info:
289+
if c.env.needsHeap:
290+
dest.copyIntoKind DerefX, info:
291+
dest.typedEnv info, c.env
292+
else:
262293
dest.typedEnv info, c.env
263294
dest.addSymUse fld.field, info
264295
tre c, dest, n # value
@@ -273,16 +304,20 @@ proc treLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
273304
tre c, dest, n # type (might grow an environment parameter)
274305
tre c, dest, n # value
275306

276-
proc addEnvParam(dest: var TokenBuf; info: PackedLineInfo) =
307+
proc addEnvParam(dest: var TokenBuf; info: PackedLineInfo; envTyp: SymId) =
277308
dest.copyIntoKind ParamU, info:
278309
dest.addSymDef pool.syms.getOrIncl(EnvParamName), info
279310
dest.addDotToken() # no export marker
280311
dest.addDotToken() # no pragmas
281-
dest.copyIntoKind RefT, info:
282-
dest.addSymUse pool.syms.getOrIncl(RootObjName), info
312+
if envTyp == SymId(0):
313+
dest.copyIntoKind RefT, info:
314+
dest.addSymUse pool.syms.getOrIncl(RootObjName), info
315+
else:
316+
dest.copyIntoKind PtrT, info:
317+
dest.addSymUse envTyp, info
283318
dest.addDotToken() # no default value
284319

285-
proc treParams(c: var Context; dest, init: var TokenBuf; n: var Cursor; addEnvParam: bool) =
320+
proc treParams(c: var Context; dest, init: var TokenBuf; n: var Cursor; doAddEnvParam: bool; envTyp: SymId) =
286321
copyInto dest, n:
287322
while n.kind != ParRi:
288323
assert n.substructureKind == ParamU
@@ -301,43 +336,54 @@ proc treParams(c: var Context; dest, init: var TokenBuf; n: var Cursor; addEnvPa
301336
# XXX Check here for memory safety violations: Cannot capture a `var T` parameter
302337
init.copyIntoKind AsgnS, n.info:
303338
init.copyIntoKind DotX, n.info:
304-
init.copyIntoKind DerefX, n.info:
339+
if envTyp != SymId(0):
340+
init.copyIntoKind DerefX, n.info:
341+
init.typedEnv n.info, c.env
342+
else:
305343
init.typedEnv n.info, c.env
306344
init.addSymUse fld.field, n.info
307345
init.addSymUse name, n.info
308346

309-
if addEnvParam:
310-
addEnvParam dest, n.info
347+
if doAddEnvParam:
348+
addEnvParam dest, n.info, envTyp
311349

312-
proc treProcBody(c: var Context; dest, init: var TokenBuf; n: var Cursor; sym: SymId) =
350+
proc treProcBody(c: var Context; dest, init: var TokenBuf; n: var Cursor; sym: SymId; needsHeap: bool) =
313351
if n.stmtKind == StmtsS:
314352
copyInto dest, n:
315353
let oldEnv = c.env
316354
if c.createsEnv.contains(sym):
317-
c.env = CurrentEnv(s: pool.syms.getOrIncl(EnvLocalName), mode: EnvIsLocal, typ: c.envTypeForProc(sym))
355+
let envTyp = c.envTypeForProc(sym)
356+
c.env = CurrentEnv(s: pool.syms.getOrIncl(EnvLocalName), mode: EnvIsLocal, typ: envTyp)
318357
dest.copyIntoKind VarS, NoLineInfo:
319358
dest.addSymDef c.env.s, NoLineInfo
320359
dest.addDotToken() # no export marker
321360
dest.addDotToken() # no pragmas
322-
dest.copyIntoKind RefT, NoLineInfo:
323-
dest.addSymUse c.env.typ, NoLineInfo
324-
dest.copyIntoKind NewobjX, NoLineInfo:
361+
if needsHeap:
325362
dest.copyIntoKind RefT, NoLineInfo:
326363
dest.addSymUse c.env.typ, NoLineInfo
364+
dest.copyIntoKind NewobjX, NoLineInfo:
365+
dest.copyIntoKind RefT, NoLineInfo:
366+
dest.addSymUse c.env.typ, NoLineInfo
367+
else:
368+
dest.addSymUse c.env.typ, NoLineInfo
369+
dest.addDotToken() # no default value
327370
# init the environment via the `=wasMoved` hooks:
328371
for _, field in c.localToEnv:
329372
if field.objType == c.env.typ:
330373
dest.copyIntoKind WasmovedX, NoLineInfo:
331374
dest.copyIntoKind HaddrX, NoLineInfo:
332375
dest.copyIntoKind DotX, NoLineInfo:
333-
dest.copyIntoKind DerefX, NoLineInfo:
376+
if needsHeap:
377+
dest.copyIntoKind DerefX, NoLineInfo:
378+
dest.addSymUse c.env.s, NoLineInfo
379+
else:
334380
dest.addSymUse c.env.s, NoLineInfo
335381
dest.addSymUse field.field, NoLineInfo
336382

337383
elif c.closureProcs.contains(sym):
338-
c.env = CurrentEnv(s: pool.syms.getOrIncl(EnvParamName), mode: EnvIsParam, typ: c.envTypeForProc(sym))
384+
c.env = CurrentEnv(s: pool.syms.getOrIncl(EnvParamName), mode: EnvIsParam, typ: c.envTypeForProc(sym), needsHeap: needsHeap)
339385
else:
340-
c.env = CurrentEnv(s: SymId(0), mode: EnvIsParam, typ: SymId(0))
386+
c.env = CurrentEnv(s: SymId(0), mode: EnvIsParam, typ: SymId(0), needsHeap: needsHeap)
341387
dest.add init
342388
while n.kind != ParRi:
343389
tre(c, dest, n)
@@ -350,26 +396,31 @@ proc treProc(c: var Context; dest: var TokenBuf; n: var Cursor) =
350396
copyInto dest, n:
351397
var isConcrete = true # assume it is concrete
352398
let sym = n.symId
399+
c.procStack.add(sym)
400+
let closureOwner = c.procStack[0]
401+
let needsHeap = c.escapes.contains(closureOwner)
353402
for i in 0..<BodyPos:
354403
if i == ParamsPos:
355404
c.typeCache.openProcScope(sym, n)
356-
treParams c, dest, init, n, c.closureProcs.contains(sym)
405+
let envType = if needsHeap: SymId(0) else: c.envTypeForProc(closureOwner)
406+
treParams c, dest, init, n, c.closureProcs.contains(sym), envType
357407
else:
358408
if i == TypeVarsPos:
359409
isConcrete = n.substructureKind != TypevarsU
360410
takeTree dest, n
361411

362412
if isConcrete:
363-
treProcBody(c, dest, init, n, sym)
413+
treProcBody(c, dest, init, n, sym, needsHeap)
364414
else:
365415
takeTree dest, n
416+
discard c.procStack.pop()
366417
c.typeCache.closeScope()
367418

368419
proc treParamsWithEnv(c: var Context; dest: var TokenBuf; n: var Cursor) =
369420
copyInto dest, n:
370421
while n.kind != ParRi:
371422
tre(c, dest, n)
372-
addEnvParam dest, NoLineInfo
423+
addEnvParam dest, NoLineInfo, SymId(0)
373424

374425
proc isStaticCall(c: var Context;s: SymId): bool =
375426
let res = tryLoadSym(s)
@@ -419,8 +470,12 @@ proc genCall(c: var Context; dest: var TokenBuf; n: var Cursor) =
419470
if wantsEnv:
420471
if isStatic:
421472
if c.env.s != SymId(0):
422-
# use the current environment as the last parameter:
423-
untypedEnv dest, info, c.env
473+
if c.env.needsHeap:
474+
# use the current environment as the last parameter:
475+
untypedEnv dest, info, c.env
476+
else:
477+
dest.copyIntoKind AddrX, info:
478+
untypedEnv dest, info, c.env
424479
else:
425480
# can happen for toplevel closures that have been declared .closure for interop
426481
# We have no environment here, so pass `nil` instead:
@@ -444,14 +499,14 @@ proc treProcType(c: var Context; dest: var TokenBuf; n: var Cursor) =
444499
let usesWrapper = n.typeKind == ProctypeT
445500
if usesWrapper:
446501
inc n
447-
for i in 1..4: dest.takeTree n
502+
for i in 1..4: skip n
448503
if n.typeKind == ParamsT:
449504
treParamsWithEnv(c, dest, n)
450505
else:
451506
assert n.kind == DotToken
452507
inc n
453508
dest.addParLe ParamsT, info
454-
addEnvParam dest, info
509+
addEnvParam dest, info, SymId(0)
455510
dest.addParRi()
456511
dest.takeTree n # return type
457512
# pragmas:
@@ -517,9 +572,13 @@ proc tre(c: var Context; dest: var TokenBuf; n: var Cursor) =
517572
inc n
518573
dest.copyIntoKind DotX, info:
519574
dest.copyIntoKind DerefX, info:
520-
dest.copyIntoKind CastX, info:
521-
dest.copyIntoKind RefT, info:
522-
dest.takeTree n # type
575+
if c.env.needsHeap:
576+
dest.copyIntoKind CastX, info:
577+
dest.copyIntoKind RefT, info:
578+
dest.takeTree n # type
579+
dest.addSymUse c.env.s, info
580+
else:
581+
skip n # type
523582
dest.addSymUse c.env.s, info
524583
assert n.kind == Symbol
525584
dest.takeTree n # the symbol

0 commit comments

Comments
 (0)