implemented computed goto support
This commit is contained in:
parent
1633e22aec
commit
d53f313599
7 changed files with 116 additions and 12 deletions
|
|
@ -292,6 +292,57 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
blockLeaveActions(p, min(1, p.nestedTryStmts.len))
|
blockLeaveActions(p, min(1, p.nestedTryStmts.len))
|
||||||
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
||||||
|
|
||||||
|
proc genComputedGoto(p: BProc; n: PNode) =
|
||||||
|
# first pass: Generate array of computed labels:
|
||||||
|
var casePos = -1
|
||||||
|
var arraySize: Int
|
||||||
|
for i in 0 .. <n.len:
|
||||||
|
let it = n.sons[i]
|
||||||
|
if it.kind == nkCaseStmt:
|
||||||
|
if lastSon(it).kind != nkOfBranch:
|
||||||
|
localError(it.info,
|
||||||
|
"case statement must be exhaustive for computed goto"); return
|
||||||
|
casePos = i
|
||||||
|
let aSize = lengthOrd(it.sons[0].typ)
|
||||||
|
if aSize > 10_000:
|
||||||
|
localError(it.info,
|
||||||
|
"case statement has too many cases for computed goto"); return
|
||||||
|
arraySize = aSize.int
|
||||||
|
if firstOrd(it.sons[0].typ) != 0:
|
||||||
|
localError(it.info,
|
||||||
|
"case statement has to start at 0 for computed goto"); return
|
||||||
|
if casePos < 0:
|
||||||
|
localError(n.info, "no case statement found for computed goto"); return
|
||||||
|
var id = p.labels+1
|
||||||
|
inc p.labels, arraySize+1
|
||||||
|
let tmp = ropef("TMP$1", id.toRope)
|
||||||
|
var gotoArray = ropef("static void* $#[$#] = {", tmp, arraySize.toRope)
|
||||||
|
for i in 1..arraySize-1:
|
||||||
|
gotoArray.appf("&&TMP$#, ", (id+i).toRope)
|
||||||
|
gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope)
|
||||||
|
line(p, cpsLocals, gotoArray)
|
||||||
|
|
||||||
|
let caseStmt = n.sons[casePos]
|
||||||
|
var a: TLoc
|
||||||
|
initLocExpr(p, caseStmt.sons[0], a)
|
||||||
|
# first goto:
|
||||||
|
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc)
|
||||||
|
|
||||||
|
for i in 1 .. <caseStmt.len:
|
||||||
|
let it = caseStmt.sons[i]
|
||||||
|
for j in 0 .. it.len-2:
|
||||||
|
if it.sons[j].kind == nkRange:
|
||||||
|
localError(it.info, "range notation not available for computed goto")
|
||||||
|
return
|
||||||
|
let val = getOrdValue(it.sons[j])
|
||||||
|
lineF(p, cpsStmts, "TMP$#:$n", intLiteral(val+id+1))
|
||||||
|
for j in 0 .. casePos-1: genStmts(p, n.sons[j])
|
||||||
|
genStmts(p, it.lastSon)
|
||||||
|
for j in casePos+1 .. <n.len: genStmts(p, n.sons[j])
|
||||||
|
var a: TLoc
|
||||||
|
initLocExpr(p, caseStmt.sons[0], a)
|
||||||
|
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc)
|
||||||
|
|
||||||
proc genWhileStmt(p: BProc, t: PNode) =
|
proc genWhileStmt(p: BProc, t: PNode) =
|
||||||
# we don't generate labels here as for example GCC would produce
|
# we don't generate labels here as for example GCC would produce
|
||||||
# significantly worse code
|
# significantly worse code
|
||||||
|
|
@ -309,7 +360,15 @@ proc genWhileStmt(p: BProc, t: PNode) =
|
||||||
if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
|
if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
|
||||||
let label = assignLabel(p.blocks[p.breakIdx])
|
let label = assignLabel(p.blocks[p.breakIdx])
|
||||||
lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
|
lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
|
||||||
genStmts(p, t.sons[1])
|
var loopBody = t.sons[1]
|
||||||
|
if loopBody.stmtsContainPragma(wComputedGoto) and
|
||||||
|
hasComputedGoto in CC[ccompiler].props:
|
||||||
|
# for closure support weird loop bodies are generated:
|
||||||
|
if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty:
|
||||||
|
loopBody = loopBody.sons[1]
|
||||||
|
genComputedGoto(p, loopBody)
|
||||||
|
else:
|
||||||
|
genStmts(p, loopBody)
|
||||||
|
|
||||||
if optProfiler in p.options:
|
if optProfiler in p.options:
|
||||||
# invoke at loop body exit:
|
# invoke at loop body exit:
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ proc InitDefines*() =
|
||||||
DefineSymbol("nimmixin")
|
DefineSymbol("nimmixin")
|
||||||
DefineSymbol("nimeffects")
|
DefineSymbol("nimeffects")
|
||||||
DefineSymbol("nimbabel")
|
DefineSymbol("nimbabel")
|
||||||
DefineSymbol("nimsuperops")
|
DefineSymbol("nimcomputedgoto")
|
||||||
|
|
||||||
# add platform specific symbols:
|
# add platform specific symbols:
|
||||||
case targetCPU
|
case targetCPU
|
||||||
|
|
|
||||||
|
|
@ -774,6 +774,9 @@ proc GlobalError*(info: TLineInfo, arg: string) =
|
||||||
proc LocalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
proc LocalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||||
liMessage(info, msg, arg, doNothing)
|
liMessage(info, msg, arg, doNothing)
|
||||||
|
|
||||||
|
proc LocalError*(info: TLineInfo, arg: string) =
|
||||||
|
liMessage(info, errGenerated, arg, doNothing)
|
||||||
|
|
||||||
proc Message*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
proc Message*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||||
liMessage(info, msg, arg, doNothing)
|
liMessage(info, msg, arg, doNothing)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const
|
||||||
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
|
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
|
||||||
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
|
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
|
||||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||||
wLinearScanEnd, wPatterns, wEffects, wNoForward}
|
wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto}
|
||||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||||
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
||||||
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
|
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
|
||||||
|
|
@ -446,9 +446,6 @@ proc PragmaUnroll(c: PContext, n: PNode) =
|
||||||
else:
|
else:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
proc PragmaLinearScanEnd(c: PContext, n: PNode) =
|
|
||||||
noVal(n)
|
|
||||||
|
|
||||||
proc PragmaLine(c: PContext, n: PNode) =
|
proc PragmaLine(c: PContext, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind == nkExprColonExpr:
|
||||||
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
||||||
|
|
@ -691,7 +688,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
else: sym.typ.callConv = wordToCallConv(k)
|
else: sym.typ.callConv = wordToCallConv(k)
|
||||||
of wEmit: PragmaEmit(c, it)
|
of wEmit: PragmaEmit(c, it)
|
||||||
of wUnroll: PragmaUnroll(c, it)
|
of wUnroll: PragmaUnroll(c, it)
|
||||||
of wLinearScanEnd: PragmaLinearScanEnd(c, it)
|
of wLinearScanEnd, wComputedGoto: noVal(it)
|
||||||
of wEffects:
|
of wEffects:
|
||||||
# is later processed in effect analysis:
|
# is later processed in effect analysis:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ proc bailOut(c: PCtx; tos: PStackFrame) =
|
||||||
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
|
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
|
||||||
c.currentExceptionA.sons[2].strVal)
|
c.currentExceptionA.sons[2].strVal)
|
||||||
|
|
||||||
when not defined(nimHasInterpreterLoop):
|
when not defined(nimComputedGoto):
|
||||||
{.pragma: interpreterLoop.}
|
{.pragma: computedGoto.}
|
||||||
|
|
||||||
template inc(pc: ptr TInstr, diff = 1) =
|
template inc(pc: ptr TInstr, diff = 1) =
|
||||||
inc cast[TAddress](pc), TInstr.sizeof * diff
|
inc cast[TAddress](pc), TInstr.sizeof * diff
|
||||||
|
|
@ -237,7 +237,7 @@ proc execute(c: PCtx, start: int) =
|
||||||
var tos: PStackFrame
|
var tos: PStackFrame
|
||||||
newSeq(regs, c.prc.maxSlots)
|
newSeq(regs, c.prc.maxSlots)
|
||||||
while true:
|
while true:
|
||||||
{.interpreterLoop.}
|
{.computedGoto.}
|
||||||
let instr = c.code[pc]
|
let instr = c.code[pc]
|
||||||
let ra = instr.regA
|
let ra = instr.regA
|
||||||
#echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
|
#echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ type
|
||||||
wPassc, wPassl, wBorrow, wDiscardable,
|
wPassc, wPassl, wBorrow, wDiscardable,
|
||||||
wFieldChecks,
|
wFieldChecks,
|
||||||
wWatchPoint, wSubsChar,
|
wWatchPoint, wSubsChar,
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
|
||||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||||
wNoStackFrame,
|
wNoStackFrame,
|
||||||
wImplicitStatic, wGlobal, wCodegenDecl,
|
wImplicitStatic, wGlobal, wCodegenDecl,
|
||||||
|
|
@ -140,7 +140,7 @@ const
|
||||||
"compiletime", "noinit",
|
"compiletime", "noinit",
|
||||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||||
"watchpoint",
|
"watchpoint",
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto",
|
||||||
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
||||||
"nostackframe", "implicitstatic", "global", "codegendecl",
|
"nostackframe", "implicitstatic", "global", "codegendecl",
|
||||||
|
|
||||||
|
|
|
||||||
45
tests/compile/tcomputedgoto.nim
Normal file
45
tests/compile/tcomputedgoto.nim
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
discard """
|
||||||
|
output: '''yeah A
|
||||||
|
yeah A
|
||||||
|
yeah CD
|
||||||
|
yeah CD
|
||||||
|
yeah A
|
||||||
|
yeah CD
|
||||||
|
yeah CD
|
||||||
|
yeah A
|
||||||
|
yeah B
|
||||||
|
yeah A
|
||||||
|
yeah A
|
||||||
|
yeah A'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
MyEnum = enum
|
||||||
|
enumA, enumB, enumC, enumD, enumE
|
||||||
|
|
||||||
|
proc vm() =
|
||||||
|
var instructions: array [0..100, MyEnum]
|
||||||
|
instructions[2] = enumC
|
||||||
|
instructions[3] = enumD
|
||||||
|
instructions[4] = enumA
|
||||||
|
instructions[5] = enumD
|
||||||
|
instructions[6] = enumC
|
||||||
|
instructions[7] = enumA
|
||||||
|
instructions[8] = enumB
|
||||||
|
|
||||||
|
instructions[12] = enumE
|
||||||
|
var pc = 0
|
||||||
|
while true:
|
||||||
|
{.computedGoto.}
|
||||||
|
case instructions[pc]
|
||||||
|
of enumA:
|
||||||
|
echo "yeah A"
|
||||||
|
of enumC, enumD:
|
||||||
|
echo "yeah CD"
|
||||||
|
of enumB:
|
||||||
|
echo "yeah B"
|
||||||
|
of enumE:
|
||||||
|
return
|
||||||
|
inc(pc)
|
||||||
|
|
||||||
|
vm()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue