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))
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) =
# we don't generate labels here as for example GCC would produce
# 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):
let label = assignLabel(p.blocks[p.breakIdx])
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:
# invoke at loop body exit:

View file

@ -46,7 +46,7 @@ proc InitDefines*() =
DefineSymbol("nimmixin")
DefineSymbol("nimeffects")
DefineSymbol("nimbabel")
DefineSymbol("nimsuperops")
DefineSymbol("nimcomputedgoto")
# add platform specific symbols:
case targetCPU

View file

@ -774,6 +774,9 @@ proc GlobalError*(info: TLineInfo, arg: string) =
proc LocalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing)
proc LocalError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doNothing)
proc Message*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing)

View file

@ -42,7 +42,7 @@ const
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd, wPatterns, wEffects, wNoForward}
wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
@ -446,9 +446,6 @@ proc PragmaUnroll(c: PContext, n: PNode) =
else:
invalidPragma(n)
proc PragmaLinearScanEnd(c: PContext, n: PNode) =
noVal(n)
proc PragmaLine(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr:
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)
of wEmit: PragmaEmit(c, it)
of wUnroll: PragmaUnroll(c, it)
of wLinearScanEnd: PragmaLinearScanEnd(c, it)
of wLinearScanEnd, wComputedGoto: noVal(it)
of wEffects:
# is later processed in effect analysis:
noVal(it)

View file

@ -54,8 +54,8 @@ proc bailOut(c: PCtx; tos: PStackFrame) =
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
c.currentExceptionA.sons[2].strVal)
when not defined(nimHasInterpreterLoop):
{.pragma: interpreterLoop.}
when not defined(nimComputedGoto):
{.pragma: computedGoto.}
template inc(pc: ptr TInstr, diff = 1) =
inc cast[TAddress](pc), TInstr.sizeof * diff
@ -237,7 +237,7 @@ proc execute(c: PCtx, start: int) =
var tos: PStackFrame
newSeq(regs, c.prc.maxSlots)
while true:
{.interpreterLoop.}
{.computedGoto.}
let instr = c.code[pc]
let ra = instr.regA
#echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra

View file

@ -59,7 +59,7 @@ type
wPassc, wPassl, wBorrow, wDiscardable,
wFieldChecks,
wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl,
@ -140,7 +140,7 @@ const
"compiletime", "noinit",
"passc", "passl", "borrow", "discardable", "fieldchecks",
"watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"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()