implemented computed goto support

This commit is contained in:
Araq 2013-08-13 11:21:02 +02:00
commit d53f313599
7 changed files with 116 additions and 12 deletions

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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",

View 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()