Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/std/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ proc afterYield*(): Continuation {.semantics: "afterYield".} =
## Do not use unless you know what you are doing.
result = Continuation(fn: nil, env: nil)

proc delay*(x: untyped): Continuation {.magic: "Delay".}
proc delay*(x: typed): Continuation {.magic: "Delay".}
## Delays the execution of a `.passive` proc and returns a continuation representation
## this call. Think of it as a `toTask` builtin.

Expand Down
131 changes: 97 additions & 34 deletions src/hexer/cps.nim
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ proc trPassiveCall(c: var Context; dest: var TokenBuf; n: var Cursor; sym: SymId
proc trDelay(c: var Context; dest: var TokenBuf; n: var Cursor) =
inc n
skip n # skip type; it is `Continuation` and uninteresting here
const nested = 1

if n.exprKind in CallKinds and n.firstSon.kind == Symbol:
let fn = n.firstSon.symId
Expand All @@ -325,8 +324,7 @@ proc trDelay(c: var Context; dest: var TokenBuf; n: var Cursor) =
dest.copyIntoKind ErrT, n.info:
dest.addStrLit "`delay` takes a call expression"
skip n
for i in 0..<nested:
skipParRi n
skipParRi n

proc passiveCallFn(c: var Context; n: Cursor): SymId =
if n.exprKind notin CallKinds: return SymId(0)
Expand Down Expand Up @@ -462,7 +460,8 @@ proc returnValue(c: var Context; dest: var TokenBuf; n: var Cursor; info: Packed
inc n # yield/return
if n.kind == DotToken or (n.kind == Symbol and n.symId == c.currentProc.resultSym):
inc n
elif isVoidType(getType(c.typeCache, n)):
elif isVoidType(getType(c.typeCache, n)) and n.kind != Symbol:
# void type for Symbol can happen for `raise` statements:
tr c, dest, n
else:
dest.copyIntoKind AsgnS, info:
Expand Down Expand Up @@ -491,17 +490,19 @@ proc trYield(c: var Context; dest: var TokenBuf; n: var Cursor) =
c.currentProc.upcomingState = oldState

proc trReturn(c: var Context; dest: var TokenBuf; n: var Cursor) =
# return x -->
# return/raise x -->
# this.res[] = x
# return this.caller
let info = n.info
# return/raise this.caller
let head = n.load()
let info = head.info
returnValue(c, dest, n, info)
dest.copyIntoKind RetS, info:
dest.copyIntoKind DotX, info:
dest.copyIntoKind DerefX, info:
dest.addSymUse pool.syms.getOrIncl(EnvParamName), info
dest.addSymUse pool.syms.getOrIncl(CallerFieldName), info
dest.addIntLit 1, info # field is in superclass
dest.add head
dest.copyIntoKind DotX, info:
dest.copyIntoKind DerefX, info:
dest.addSymUse pool.syms.getOrIncl(EnvParamName), info
dest.addSymUse pool.syms.getOrIncl(CallerFieldName), info
dest.addIntLit 1, info # field is in superclass
dest.addParRi()

proc escapingLocals(c: var Context; n: Cursor) =
if n.kind == DotToken: return
Expand Down Expand Up @@ -559,39 +560,98 @@ proc isPassiveCall(c: var Context; n: PackedToken): bool =
return true
return false

proc findDelayX(c: var Context; n: Cursor): Cursor =
var nested = 0
var n = n
while true:
case n.kind
of ParLe:
if n.exprKind == DelayX:
inc n
skip n # type
return n
inc nested
of ParRi:
dec nested
else:
discard
if nested == 0:
break
inc n
return default(Cursor)

proc skipButHandleGoto(c: var Context; n: var Cursor; nextLabel: var int) =
if n.kind == ParLe:
var nested = 0
while true:
inc n
case n.kind
of ParRi:
if nested == 0: break
dec nested
of ParLe: inc nested
of GotoInstr:
let diff = n.getInt28
let i = cursorToPosition(c.currentProc.cf, n)
if i+diff > 0 and i+diff < c.currentProc.cf.len and c.currentProc.reachable[i+diff]:
c.currentProc.labels[i+diff] = nextLabel
inc nextLabel
else:
discard
inc n

proc treIteratorBody(c: var Context; dest: var TokenBuf; init: TokenBuf; iter: Cursor; sym: SymId) =
c.currentProc.cf = toControlflow(iter, keepReturns = true)
c.currentProc.reachable = eliminateDeadInstructions(c.currentProc.cf)

# Now compute basic blocks considering only reachable instructions
c.currentProc.labels = initTable[int, int]()

var nextLabel = 0
for i in 0..<c.currentProc.cf.len:
if c.currentProc.reachable[i]:
if c.currentProc.cf[i].kind == GotoInstr:
let diff = c.currentProc.cf[i].getInt28

var n = beginRead(c.currentProc.cf)
inc n # ProcS
for i in 0..<BodyPos: skip n
var nextStmtIsLabel = false
assert n.stmtKind == StmtsS
inc n
while n.kind != ParRi:
if nextStmtIsLabel:
nextStmtIsLabel = false
let i = cursorToPosition(c.currentProc.cf, n)
c.currentProc.labels[i] = nextLabel
inc nextLabel

if n.kind == GotoInstr:
let i = cursorToPosition(c.currentProc.cf, n)
if c.currentProc.reachable[i]:
let diff = n.getInt28
if i+diff > 0 and i+diff < c.currentProc.cf.len and c.currentProc.reachable[i+diff]:
c.currentProc.labels[i+diff] = nextLabel
inc nextLabel
elif c.currentProc.cf[i].stmtKind == YldS or
(c.currentProc.cf[i].exprKind in CallKinds and isPassiveCall(c, c.currentProc.cf[i+1])):
elif n.stmtKind == YldS or
(n.exprKind in CallKinds and isPassiveCall(c, n.firstSon.load)):
let i = cursorToPosition(c.currentProc.cf, n)
if c.currentProc.reachable[i]:
# after a yield we also have a suspension point (a label):
var nested = 1
c.currentProc.yieldConts[i] = nextLabel
for j in i+1..<c.currentProc.cf.len:
case c.currentProc.cf[j].kind
of ParLe: inc nested
of ParRi:
dec nested
if nested == 0:
c.currentProc.labels[j+1] = nextLabel
inc nextLabel
break
else:
discard
nextStmtIsLabel = true
else:
let d = findDelayX(c, n)
if not cursorIsNil(d):
let i = cursorToPosition(c.currentProc.cf, n)
if c.currentProc.reachable[i]:
# after a yield we also have a suspension point (a label):
c.currentProc.yieldConts[i] = nextLabel
let j = cursorToPosition(c.currentProc.cf, d)
c.currentProc.yieldConts[j] = nextLabel
nextStmtIsLabel = true
skipButHandleGoto(c, n, nextLabel)

endRead c.currentProc.cf

# analyze which locals are used across basic blocks:
var n = beginRead(c.currentProc.cf)
n = beginRead(c.currentProc.cf)
inc n # ProcS
for i in 0..<BodyPos: skip n
escapingLocals(c, n)
Expand Down Expand Up @@ -841,8 +901,11 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
takeTree dest, n
of YldS:
trYield c, dest, n
of RetS:
trReturn c, dest, n
of RetS, RaiseS:
if c.currentProc.kind == IsNormal:
trSons(c, dest, n)
else:
trReturn c, dest, n
of AsgnS:
trAsgn c, dest, n
of ScopeS:
Expand Down
2 changes: 1 addition & 1 deletion src/hexer/eraiser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ proc trCall(c: var Context; dest: var TokenBuf; n: var Cursor; inhibit: bool) =
c.needsXelim = true
let isVoid = retType.kind == DotToken or retType.typeKind == VoidT
if not isVoid:
dest.addParLe(ExprX, info)
dest.addParLe(ExprX, info)
copyIntoKind dest, StmtsS, info:
let symId = pool.syms.getOrIncl("`canRaise." & $c.tmpCounter)
inc c.tmpCounter
Expand Down
20 changes: 10 additions & 10 deletions src/hexer/pipeline.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,9 @@ proc transform*(c: var EContext; n: Cursor; moduleSuffix: string): TokenBuf =
var desugaredBuf = desugar(desugarReader, moduleSuffix, c.activeChecks)
endRead(initialBuf)

var cpsReader = beginRead(desugaredBuf)
var cpsBuf = transformToCps(cpsReader, moduleSuffix)
endRead(desugaredBuf)

var lambdaLiftingReader = beginRead(cpsBuf)
var lambdaLiftingReader = beginRead(desugaredBuf)
var lambdaLiftedBuf = elimLambdas(lambdaLiftingReader, moduleSuffix)
endRead(cpsBuf)
endRead(desugaredBuf)

var lowerExprsReader1 = beginRead(lambdaLiftedBuf)
var nx = lowerExprs(lowerExprsReader1, moduleSuffix)
Expand All @@ -66,9 +62,9 @@ proc transform*(c: var EContext; n: Cursor; moduleSuffix: string): TokenBuf =
var needsXelimIgnored = false
var withRaises = injectRaisingCalls(raisesReader, c.bits div 8, needsXelimIgnored)
endRead(duplicatedBuf)
var withRaisesReader = beginRead(withRaises)

var loweredBuf = lowerExprs(withRaisesReader, moduleSuffix)
var loweredReader = beginRead(withRaises)
var loweredBuf = lowerExprs(loweredReader, moduleSuffix)
endRead(withRaises)

var destructorReader = beginRead(loweredBuf)
Expand All @@ -87,11 +83,15 @@ proc transform*(c: var EContext; n: Cursor; moduleSuffix: string): TokenBuf =
destructorBuf.add move(c.liftingCtx[].dest)
destructorBuf.addParRi()

var cpsReader = beginRead(destructorBuf)
var cpsBuf = transformToCps(cpsReader, moduleSuffix)
endRead(destructorBuf)

var needsXelimAgain = false

var vtableReader = beginRead(destructorBuf)
var vtableReader = beginRead(cpsBuf)
var nwithvtables = transformVTables(vtableReader, moduleSuffix, needsXelimAgain)
endRead(destructorBuf)
endRead(cpsBuf)

var constParamReader = beginRead(nwithvtables)
var constParamBuf = injectConstParamDerefs(constParamReader, c.bits div 8, needsXelimAgain)
Expand Down
19 changes: 18 additions & 1 deletion src/nimony/controlflow.nim
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,21 @@ proc trIfCaseTryBlockExpr(c: var ControlFlow; n: var Cursor; kind: ControlFlowAs
trBlock c, n, aa
tar.t.addSymUse temp, info

proc trDelay(c: var ControlFlow; n: var Cursor; tar: var Target) =
if tar.m == IsEmpty:
tar.m = IsAppend
else:
assert tar.m == IsAppend, toString(n, false) & " " & $tar.m
tar.t.takeToken n # the `delay` tag
tar.t.takeTree n # the type
assert n.exprKind in CallKinds, toString(n, false)
# do not perform `trCall` here:
tar.t.takeToken n
while n.kind != ParRi:
trExpr c, n, tar
tar.t.addParRi()
tar.t.addParRi()

proc trExpr(c: var ControlFlow; n: var Cursor; tar: var Target) =
case n.kind
of Symbol, SymbolDef, IntLit, UIntLit, FloatLit, StringLit, CharLit,
Expand Down Expand Up @@ -586,8 +601,10 @@ proc trExpr(c: var ControlFlow; n: var Cursor; tar: var Target) =
MulSetX, XorSetX, EqSetX, LeSetX, LtSetX, InSetX, CardX, EmoveX,
DestroyX, DupX, CopyX, WasMovedX, SinkhX, TraceX,
BracketX, CurlyX, TupX, OvfX, InstanceofX, ProccallX, InternalFieldPairsX,
FailedX, IsX, EnvpX, DelayX:
FailedX, IsX, EnvpX:
trExprLoop c, n, tar
of DelayX:
trDelay c, n, tar
of PragmaxX:
bug "pragmax should be handled in trStmt"
of CompilesX, DeclaredX, DefinedX, AstToStrX, HighX, LowX, TypeofX, SizeofX, AlignofX, OffsetofX, InternalTypeNameX:
Expand Down
2 changes: 1 addition & 1 deletion src/nimony/sizeof.nim
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ proc typeSectionMode(n: Cursor): PragmaKind =

proc passByConstRef*(typ, pragmas: Cursor; ptrSize: int): bool =
let k = typ.typeKind
if k in {SinkT, MutT, OutT, TypeKindT, UntypedT, VarargsT, TypedescT, StaticT}:
if k in {SinkT, MutT, OutT, TypeKindT, UntypedT, TypedT, VarargsT, TypedescT, StaticT}:
result = false
elif typeIsBig(typ, ptrSize):
result = not hasPragma(pragmas, BycopyP) and typeSectionMode(typ) != BycopyP
Expand Down
14 changes: 14 additions & 0 deletions tests/nimony/cps/tfirstpassive.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

import std / [syncio]

proc myecho(x: string) {.passive.} =
echo x

proc passiveProc(x: string) {.passive.} =
for i in 0..<10:
myecho x & "xyz"

proc main {.passive.} =
passiveProc("abc")

main()
10 changes: 10 additions & 0 deletions tests/nimony/cps/tfirstpassive.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
abcxyz
Loading