linearScanEnd pragma; string case statement optimization

This commit is contained in:
Araq
2011-03-14 23:57:41 +01:00
parent 6850fb73c1
commit 8d734244b1
26 changed files with 537 additions and 252 deletions

View File

@@ -123,6 +123,10 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/procfind.c -o build/1_1/procfind.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/procfind.c -o build/1_1/procfind.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/pragmas.c -o build/1_1/pragmas.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/pragmas.c -o build/1_1/pragmas.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/semtypinst.c -o build/1_1/semtypinst.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/semtypinst.c -o build/1_1/semtypinst.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/sigmatch.c -o build/1_1/sigmatch.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/sigmatch.c -o build/1_1/sigmatch.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/suggest.c -o build/1_1/suggest.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/suggest.c -o build/1_1/suggest.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/docgen.c -o build/1_1/docgen.o
@@ -139,8 +143,6 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/cgmeth.c -o build/1_1/cgmeth.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/cgmeth.c -o build/1_1/cgmeth.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/ecmasgen.c -o build/1_1/ecmasgen.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/ecmasgen.c -o build/1_1/ecmasgen.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/interact.c -o build/1_1/interact.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/interact.c -o build/1_1/interact.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/passaux.c -o build/1_1/passaux.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/passaux.c -o build/1_1/passaux.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/depends.c -o build/1_1/depends.o
@@ -150,8 +152,8 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/transf.c -o build/1_1/transf.o
ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o
%CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o
ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o
%LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o
ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o
%LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o
ECHO SUCCESS

View File

@@ -2646,6 +2646,53 @@ hint pragma
-----------
The `hint`:idx: pragma is used to make the compiler output a hint message with
the given content. Compilation continues after the hint.
linearScanEnd pragma
--------------------
The `linearScanEnd`:idx: pragma can be used to tell the compiler how to
compile a Nimrod `case`:idx: statement. Syntactially it has to be used as a
statement:
.. code-block:: nimrod
case myInt
of 0:
echo "most common case"
of 1:
{.linearScanEnd.}
echo "second most common case"
of 2: echo "unlikely: use branch table"
else: echo "unlikely too: use branch table for ", myInt
In the example, the case branches ``0`` and ``1`` are much more common than
the other cases. Therefore the generated assembler code should test for these
values first, so that the CPU's branch predictor has a good chance to succeed
(avoiding an expensive CPU pipeline stall). The other cases might be put into a
jump table for O(1) overhead, but at the cost of a (very likely) pipeline
stall.
The ``linearScanEnd`` pragma should be put into the last branch that should be
tested against via linear scanning. If put into the last branch of the
whole ``case`` statement, the whole ``case`` statement uses linear scanning.
unroll pragma
-------------
The `unroll`:idx: pragma can be used to tell the compiler that it should unroll
a `for`:idx: or `while`:idx: loop for runtime efficiency:
.. code-block:: nimrod
proc searchChar(s: string, c: char): int =
for i in 0 .. s.high:
{.unroll: 4.}
if s[i] == c: return i
result = -1
In the above example, the search loop is unrolled by a factor 4. The unroll
factor can be left out too; the compiler then chooses an appropriate unroll
factor.
**Note**: Currently the compiler recognizes but ignores this pragma.
compilation option pragmas

View File

@@ -293,10 +293,26 @@ However it is not efficient to do:
.. code-block:: Nimrod
var s = varA # assignment has to copy the whole string into a new buffer!
..
String case statements are optimized too. A hashing scheme is used for them
if several different string constants are used. This is likely to be more
efficient than any hand-coded scheme.
The compiler optimizes string case statements: A hashing scheme is used for them
if several different string constants are used. So code like this is reasonably
efficient:
.. code-block:: Nimrod
case normalize(k.key)
of "name": c.name = v
of "displayname": c.displayName = v
of "version": c.version = v
of "os": c.oses = split(v, {';'})
of "cpu": c.cpus = split(v, {';'})
of "authors": c.authors = split(v, {';'})
of "description": c.description = v
of "app":
case normalize(v)
of "console": c.app = appConsole
of "gui": c.app = appGUI
else: quit(errorStr(p, "expected: console or gui"))
of "license": c.license = UnixToNativePath(k.value)
else: quit(errorStr(p, "unknown variable: " & k.key))
..

View File

@@ -88,8 +88,6 @@ if [ $# -eq 1 ] ; then
chmod 644 $docdir/abstypes.txt
cp doc/advopt.txt $docdir/advopt.txt || exit 1
chmod 644 $docdir/advopt.txt
cp doc/altsyn.txt $docdir/altsyn.txt || exit 1
chmod 644 $docdir/altsyn.txt
cp doc/apis.txt $docdir/apis.txt || exit 1
chmod 644 $docdir/apis.txt
cp doc/astspec.txt $docdir/astspec.txt || exit 1
@@ -330,6 +328,8 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/core/marshal.nim
cp lib/core/threads.nim $libdir/core/threads.nim || exit 1
chmod 644 $libdir/core/threads.nim
cp lib/pure/algorithm.nim $libdir/pure/algorithm.nim || exit 1
chmod 644 $libdir/pure/algorithm.nim
cp lib/pure/base64.nim $libdir/pure/base64.nim || exit 1
chmod 644 $libdir/pure/base64.nim
cp lib/pure/browsers.nim $libdir/pure/browsers.nim || exit 1
@@ -344,6 +344,8 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/pure/cookies.nim
cp lib/pure/dynlib.nim $libdir/pure/dynlib.nim || exit 1
chmod 644 $libdir/pure/dynlib.nim
cp lib/pure/gentabs.nim $libdir/pure/gentabs.nim || exit 1
chmod 644 $libdir/pure/gentabs.nim
cp lib/pure/hashes.nim $libdir/pure/hashes.nim || exit 1
chmod 644 $libdir/pure/hashes.nim
cp lib/pure/htmlparser.nim $libdir/pure/htmlparser.nim || exit 1

View File

@@ -406,14 +406,16 @@ proc ParseInt*(s: string): int {.noSideEffect, procvar,
## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised.
var L = parseutils.parseInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
if L != s.len or L == 0:
raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar,
rtl, extern: "nsuParseBiggestInt".} =
## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised.
var L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
if L != s.len or L == 0:
raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat*(s: string): float {.noSideEffect, procvar,
rtl, extern: "nsuParseFloat".} =
@@ -421,7 +423,8 @@ proc ParseFloat*(s: string): float {.noSideEffect, procvar,
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
var L = parseutils.parseFloat(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid float: " & s)
if L != s.len or L == 0:
raise newException(EInvalidValue, "invalid float: " & s)
proc ParseHexInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseHexInt".} =

View File

@@ -1198,12 +1198,12 @@ proc each*[T](data: var openArray[T], op: proc (x: var T)) =
# ----------------- GC interface ---------------------------------------------
proc GC_disable*() {.rtl.}
proc GC_disable*() {.rtl, inl.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enable*() {.rtl.}
proc GC_enable*() {.rtl, inl.}
## enables the GC again.
proc GC_fullCollect*() {.rtl.}

View File

@@ -7,9 +7,7 @@
# distribution, for details about the copyright.
#
#when defined(debugGC):
# {.define: logAssign.}
proc genericAssign(dest, src: Pointer, mt: PNimType) {.compilerProc.}
proc genericAssignAux(dest, src: Pointer, mt: PNimType)
proc genericAssignAux(dest, src: Pointer, n: ptr TNimNode) =
var
d = cast[TAddress](dest)
@@ -17,8 +15,8 @@ proc genericAssignAux(dest, src: Pointer, n: ptr TNimNode) =
case n.kind
of nkNone: assert(false)
of nkSlot:
genericAssign(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ)
genericAssignAux(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ)
of nkList:
for i in 0..n.len-1:
genericAssignAux(dest, src, n.sons[i])
@@ -28,7 +26,7 @@ proc genericAssignAux(dest, src: Pointer, n: ptr TNimNode) =
var m = selectBranch(src, n)
if m != nil: genericAssignAux(dest, src, m)
proc genericAssign(dest, src: Pointer, mt: PNimType) =
proc genericAssignAux(dest, src: Pointer, mt: PNimType) =
var
d = cast[TAddress](dest)
s = cast[TAddress](src)
@@ -47,7 +45,7 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
var dst = cast[taddress](cast[ppointer](dest)^)
for i in 0..seq.len-1:
genericAssign(
genericAssignAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[taddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
@@ -60,8 +58,8 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
genericAssignAux(dest, src, mt.node)
of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1:
genericAssign(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base)
genericAssignAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base)
of tyString: # a leaf
var s2 = cast[ppointer](s)^
if s2 != nil: # nil strings are possible!
@@ -75,6 +73,11 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
else:
copyMem(dest, src, mt.size) # copy raw bits
proc genericAssign(dest, src: Pointer, mt: PNimType) {.compilerProc.} =
GC_disable()
genericAssignAux(dest, src, mt)
GC_enable()
proc genericSeqAssign(dest, src: Pointer, mt: PNimType) {.compilerProc.} =
var src = src # ugly, but I like to stress the parser sometimes :-)
genericAssign(dest, addr(src), mt)

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -269,7 +269,8 @@ type
tfNoSideEffect, # procedure type does not allow side effects
tfFinal, # is the object final?
tfAcyclic, # type is acyclic (for GC optimization)
tfEnumHasWholes # enum cannot be mapped into a range
tfEnumHasWholes, # enum cannot be mapped into a range
tfShallow # type can be shallow copied on assignment
TTypeFlags* = set[TTypeFlag]

View File

@@ -215,6 +215,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
[addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)])
else:
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
of tyObject:
# XXX: check for subtyping?
if needsComplexAssignment(dest.t):
appcg(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
[addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)])
else:
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
of tyArray, tyArrayConstr:
if needsComplexAssignment(dest.t):
appcg(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
@@ -223,12 +230,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
appcg(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n",
[rdLoc(dest), rdLoc(src)])
of tyObject: # XXX: check for subtyping?
if needsComplexAssignment(dest.t):
appcg(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
[addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)])
else:
appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
of tyOpenArray:
# open arrays are always on the stack - really? What if a sequence is
# passed to an open array?

View File

@@ -10,6 +10,10 @@
const
RangeExpandLimit = 256 # do not generate ranges
# over 'RangeExpandLimit' elements
stringCaseThreshold = 8
# above X strings a hash-switch for strings is generated
# this version sets it too high to avoid hashing, because this has not
# been tested for a long time
proc genLineDir(p: BProc, t: PNode) =
var line = toLinenumber(t.info) # BUGFIX
@@ -229,13 +233,6 @@ proc genRaiseStmt(p: BProc, t: PNode) =
else:
appcg(p, cpsStmts, "#reraiseException();" & tnl)
const
stringCaseThreshold = 100000
# above X strings a hash-switch for strings is generated
# this version sets it too high to avoid hashing, because this has not
# been tested for a long time
# XXX test and enable this optimization!
proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: TFormatStr, labl: TLabel) =
var
@@ -251,90 +248,68 @@ proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
initLocExpr(p, b.sons[i], x)
appcg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
proc genCaseSecondPass(p: BProc, t: PNode, labId: int) =
proc genCaseSecondPass(p: BProc, t: PNode, labId, until: int): TLabel =
var Lend = getLabel(p)
for i in countup(1, sonsLen(t) - 1):
for i in 1..until:
appf(p.s[cpsStmts], "LA$1: ;$n", [toRope(labId + i)])
if t.sons[i].kind == nkOfBranch: # else statement
if t.sons[i].kind == nkOfBranch:
var length = sonsLen(t.sons[i])
genStmts(p, t.sons[i].sons[length - 1])
appf(p.s[cpsStmts], "goto $1;$n", [Lend])
else:
genStmts(p, t.sons[i].sons[0])
fixLabel(p, Lend)
result = Lend
proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) =
proc genIfForCaseUntil(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr,
until: int, a: TLoc): TLabel =
# generate a C-if statement for a Nimrod case statement
var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: generate ifs+goto:
var labId = p.labels
for i in countup(1, sonsLen(t) - 1):
for i in 1..until:
inc(p.labels)
if t.sons[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
con("LA", toRope(p.labels)))
else:
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.labels)])
genCaseSecondPass(p, t, labId)
if until < t.len-1:
inc(p.labels)
var gotoTarget = p.labels
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(gotoTarget)])
result = genCaseSecondPass(p, t, labId, until)
appf(p.s[cpsStmts], "LA$1: ;$n", [toRope(gotoTarget)])
else:
result = genCaseSecondPass(p, t, labId, until)
proc hashString(s: string): biggestInt =
var
a: int32
b: int64
if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth
# as the target CPU
b = 0
for i in countup(0, len(s) - 1):
b = b +% Ord(s[i])
b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6)
b = b +% `shl`(b, 3)
b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15)
result = b
else:
a = 0
for i in countup(0, len(s) - 1):
a = a +% int32(Ord(s[i]))
a = a +% `shl`(a, int32(10))
a = a xor `shr`(a, int32(6))
a = a +% `shl`(a, int32(3))
a = a xor `shr`(a, int32(11))
a = a +% `shl`(a, int32(15))
result = a
type
TRopeSeq = seq[PRope]
proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) =
var a: TLoc
initLocExpr(p, t.sons[0], a)
var Lend = genIfForCaseUntil(p, t, rangeFormat, eqFormat, sonsLen(t)-1, a)
fixLabel(p, Lend)
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
branches: var TRopeSeq) =
var
length, j: int
x: TLoc
length = sonsLen(b)
branches: var openArray[PRope]) =
var x: TLoc
var length = sonsLen(b)
for i in countup(0, length - 2):
assert(b.sons[i].kind != nkRange)
initLocExpr(p, b.sons[i], x)
assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit})
j = int(hashString(b.sons[i].strVal) and high(branches))
var j = int(hashString(b.sons[i].strVal) and high(branches))
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
[rdLoc(e), rdLoc(x), labl])
proc genStringCase(p: BProc, t: PNode) =
var
strings, bitMask, labId: int
a: TLoc
branches: TRopeSeq
# count how many constant strings there are in the case:
strings = 0
var strings = 0
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
if strings > stringCaseThreshold:
bitMask = math.nextPowerOfTwo(strings) - 1
var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[PRope]
newSeq(branches, bitMask + 1)
var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
labId = p.labels
var labId = p.labels
for i in countup(1, sonsLen(t) - 1):
inc(p.labels)
if t.sons[i].kind == nkOfBranch:
@@ -353,67 +328,72 @@ proc genStringCase(p: BProc, t: PNode) =
if t.sons[sonsLen(t) - 1].kind != nkOfBranch:
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.labels)])
# third pass: generate statements
genCaseSecondPass(p, t, labId)
var Lend = genCaseSecondPass(p, t, labId, sonsLen(t)-1)
fixLabel(p, Lend)
else:
genCaseGeneric(p, t, "", "if (#eqStrings($1, $2)) goto $3;$n")
proc branchHasTooBigRange(b: PNode): bool =
for i in countup(0, sonsLen(b) - 2):
for i in countup(0, sonsLen(b)-2):
# last son is block
if (b.sons[i].Kind == nkRange) and
b.sons[i].sons[1].intVal - b.sons[i].sons[0].intVal > RangeExpandLimit:
return true
result = false
proc genOrdinalCase(p: BProc, t: PNode) =
# We analyse if we have a too big switch range. If this is the case,
# we generate an ordinary if statement and rely on the C compiler
# to produce good code.
var
canGenerateSwitch, hasDefault: bool
length: int
a: TLoc
v: PNode
canGenerateSwitch = true
if not (hasSwitchRange in CC[ccompiler].props):
for i in countup(1, sonsLen(t) - 1):
if (t.sons[i].kind == nkOfBranch) and branchHasTooBigRange(t.sons[i]):
canGenerateSwitch = false
break
if canGenerateSwitch:
initLocExpr(p, t.sons[0], a)
proc IfSwitchSplitPoint(p: BProc, n: PNode): int =
for i in 1..n.len-1:
var branch = n[i]
var stmtBlock = lastSon(branch)
if stmtBlock.stmtsContainPragma(wLinearScanEnd):
result = i
elif hasSwitchRange notin CC[ccompiler].props:
if branch.kind == nkOfBranch and branchHasTooBigRange(branch):
result = i
proc genOrdinalCase(p: BProc, n: PNode) =
# analyse 'case' statement:
var splitPoint = IfSwitchSplitPoint(p, n)
# generate if part (might be empty):
var a: TLoc
initLocExpr(p, n.sons[0], a)
var Lend = if splitPoint > 0: genIfForCaseUntil(p, n,
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
eqFormat = "if ($1 == $2) goto $3;$n",
splitPoint, a) else: nil
# generate switch part (might be empty):
if splitPoint+1 < n.len:
appf(p.s[cpsStmts], "switch ($1) {$n", [rdCharLoc(a)])
hasDefault = false
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == nkOfBranch:
length = sonsLen(t.sons[i])
for j in countup(0, length - 2):
if t.sons[i].sons[j].kind == nkRange:
# a range
var hasDefault = false
for i in splitPoint+1 .. < n.len:
var branch = n[i]
if branch.kind == nkOfBranch:
var length = branch.len
for j in 0 .. length-2:
if branch[j].kind == nkRange:
if hasSwitchRange in CC[ccompiler].props:
appf(p.s[cpsStmts], "case $1 ... $2:$n", [
genLiteral(p, t.sons[i].sons[j].sons[0]),
genLiteral(p, t.sons[i].sons[j].sons[1])])
genLiteral(p, branch[j][0]),
genLiteral(p, branch[j][1])])
else:
v = copyNode(t.sons[i].sons[j].sons[0])
while (v.intVal <= t.sons[i].sons[j].sons[1].intVal):
var v = copyNode(branch[j][0])
while v.intVal <= branch[j][1].intVal:
appf(p.s[cpsStmts], "case $1:$n", [genLiteral(p, v)])
Inc(v.intVal)
else:
appf(p.s[cpsStmts], "case $1:$n", [genLiteral(p, t.sons[i].sons[j])])
genStmts(p, t.sons[i].sons[length - 1])
appf(p.s[cpsStmts], "case $1:$n", [genLiteral(p, branch[j])])
genStmts(p, branch[length-1])
else:
# else part of case statement:
app(p.s[cpsStmts], "default:" & tnl)
genStmts(p, t.sons[i].sons[0])
genStmts(p, branch[0])
hasDefault = true
app(p.s[cpsStmts], "break;" & tnl)
if (hasAssume in CC[ccompiler].props) and not hasDefault:
app(p.s[cpsStmts], "default: __assume(0);" & tnl)
app(p.s[cpsStmts], '}' & tnl)
else:
genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n")
if Lend != nil: fixLabel(p, Lend)
proc genCaseStmt(p: BProc, t: PNode) =
genLineDir(p, t)
@@ -424,7 +404,6 @@ proc genCaseStmt(p: BProc, t: PNode) =
genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n")
else:
# ordinal type: generate a switch statement
genOrdinalCase(p, t)
proc hasGeneralExceptSection(t: PNode): bool =
@@ -629,19 +608,17 @@ proc genBreakPoint(p: BProc, t: PNode) =
proc genPragma(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkIdent:
case whichKeyword(key.ident)
of wEmit:
genEmit(p, it)
of wBreakpoint:
genBreakPoint(p, it)
of wDeadCodeElim:
if not (optDeadCodeElim in gGlobalOptions):
# we need to keep track of ``deadCodeElim`` pragma
if (sfDeadCodeElim in p.module.module.flags):
addPendingModule(p.module)
else: nil
case whichPragma(it)
of wEmit:
genEmit(p, it)
of wBreakpoint:
genBreakPoint(p, it)
of wDeadCodeElim:
if not (optDeadCodeElim in gGlobalOptions):
# we need to keep track of ``deadCodeElim`` pragma
if (sfDeadCodeElim in p.module.module.flags):
addPendingModule(p.module)
else: nil
proc genAsgn(p: BProc, e: PNode) =
var a: TLoc

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,21 +10,58 @@
# This module declares some helpers for the C code generator.
import
ast, astalgo, ropes, lists, nhashes, strutils, types, msgs
ast, astalgo, ropes, lists, nhashes, strutils, types, msgs, wordrecg,
platform
proc toCChar*(c: Char): string
proc makeCString*(s: string): PRope
proc makeLLVMString*(s: string): PRope
proc TableGetType*(tab: TIdTable, key: PType): PObject
proc GetUniqueType*(key: PType): PType
# implementation
proc whichPragma*(n: PNode): TSpecialWord =
var key = if n.kind == nkExprColonExpr: n.sons[0] else: n
if key.kind == nkIdent: result = whichKeyword(key.ident)
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
for i in 0 .. < n.len:
result = getPragmaStmt(n[i], w)
if result != nil: break
of nkPragma:
for i in 0 .. < n.len:
if whichPragma(n[i]) == w: return n[i]
else: nil
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil
proc hashString*(s: string): biggestInt =
# has to be the same algorithm as system.hashString!
if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth
# as the target CPU
var b = 0'i64
for i in countup(0, len(s) - 1):
b = b +% Ord(s[i])
b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6)
b = b +% `shl`(b, 3)
b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15)
result = b
else:
var a = 0'i32
for i in countup(0, len(s) - 1):
a = a +% Ord(s[i]).int32
a = a +% `shl`(a, 10'i32)
a = a xor `shr`(a, 6'i32)
a = a +% `shl`(a, 3'i32)
a = a xor `shr`(a, 11'i32)
a = a +% `shl`(a, 15'i32)
result = a
var gTypeTable: array[TTypeKind, TIdTable]
proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i])
proc GetUniqueType(key: PType): PType =
proc GetUniqueType*(key: PType): PType =
var
t: PType
k: TTypeKind
@@ -32,33 +69,7 @@ proc GetUniqueType(key: PType): PType =
result = key
if key == nil: return
k = key.kind
case k #
# case key.Kind of
# tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
# tyInt..tyFloat128, tyProc, tyAnyEnum: begin end;
# tyNone, tyForward:
# InternalError('GetUniqueType: ' + typeToString(key));
# tyGenericParam, tyGeneric, tyAbstract, tySequence,
# tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyArrayConstr,
# tyArray, tyTuple, tyRange: begin
# // we have to do a slow linear search because types may need
# // to be compared by their structure:
# if IdTableHasObjectAsKey(gTypeTable, key) then exit;
# for h := 0 to high(gTypeTable.data) do begin
# t := PType(gTypeTable.data[h].key);
# if (t <> nil) and sameType(t, key) then begin result := t; exit end
# end;
# IdTablePut(gTypeTable, key, key);
# end;
# tyObject, tyEnum: begin
# result := PType(IdTableGet(gTypeTable, key));
# if result = nil then begin
# IdTablePut(gTypeTable, key, key);
# result := key;
# end
# end;
# tyGenericInst, tyAbstract: result := GetUniqueType(lastSon(key));
# end;
case k
of tyObject, tyEnum:
result = PType(IdTableGet(gTypeTable[k], key))
if result == nil:
@@ -78,7 +89,7 @@ proc GetUniqueType(key: PType): PType =
return t
IdTablePut(gTypeTable[k], key, key)
proc TableGetType(tab: TIdTable, key: PType): PObject =
proc TableGetType*(tab: TIdTable, key: PType): PObject =
var t: PType
# returns nil if we need to declare this type
result = IdTableGet(tab, key)
@@ -91,13 +102,13 @@ proc TableGetType(tab: TIdTable, key: PType): PObject =
if sameType(t, key):
return tab.data[h].val
proc toCChar(c: Char): string =
proc toCChar*(c: Char): string =
case c
of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c)
of '\'', '\"', '\\': result = '\\' & c
else: result = $(c)
proc makeCString(s: string): PRope =
proc makeCString*(s: string): PRope =
# BUGFIX: We have to split long strings into many ropes. Otherwise
# this could trigger an InternalError(). See the ropes module for
# further information.
@@ -117,9 +128,8 @@ proc makeCString(s: string): PRope =
add(res, '\"')
app(result, toRope(res))
proc makeLLVMString(s: string): PRope =
const
MaxLineLength = 64
proc makeLLVMString*(s: string): PRope =
const MaxLineLength = 64
var res: string
result = nil
res = "c\""
@@ -135,4 +145,4 @@ proc makeLLVMString(s: string): PRope =
add(res, "\\00\"")
app(result, toRope(res))
InitTypeTables()
InitTypeTables()

View File

@@ -308,14 +308,19 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
proc zeroTemp(p: BProc, loc: TLoc) =
if skipTypes(loc.t, abstractVarRange).Kind notin
{tyArray, tyArrayConstr, tySet, tyTuple, tyObject}:
var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.t, onStack)
nilLoc.r = toRope("NIM_NIL")
# puts ``unsureAsgnRef`` etc to ``p.s[cpsStmts]``:
genRefAssign(p, loc, nilLoc, {afSrcIsNil})
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)])
when false:
var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.t, onStack)
nilLoc.r = toRope("NIM_NIL")
# puts ``unsureAsgnRef`` etc to ``p.s[cpsStmts]``:
genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else:
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)])
appf(p.s[cpsStmts], "memset((void*)$1, 0, sizeof($2));$n",
[addrLoc(loc), rdLoc(loc)])
when false:
appcg(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
[addrLoc(loc), genTypeInfo(p.module, loc.t)])
proc initVariable(p: BProc, v: PSym) =
var b = containsGarbageCollectedRef(v.typ)

View File

@@ -193,23 +193,23 @@ proc MainCommand(cmd, filename: string) =
setID(100)
passes.gIncludeFile = syntaxes.parseFile
passes.gImportModule = importModule
case whichKeyword(cmd)
of wCompile, wCompileToC, wC, wCC:
case cmd.normalize
of "c", "cc", "compile", "compiletoc":
# compile means compileToC currently
gCmd = cmdCompileToC
wantFile(filename)
CommandCompileToC(filename)
of wCompileToCpp:
of "compiletocpp":
extccomp.cExt = ".cpp"
gCmd = cmdCompileToCpp
wantFile(filename)
CommandCompileToC(filename)
of wCompileToOC, wOC:
of "oc", "compiletooc":
extccomp.cExt = ".m"
gCmd = cmdCompileToOC
wantFile(filename)
CommandCompileToC(filename)
of wRun:
of "run":
gCmd = cmdRun
wantFile(filename)
when hasTinyCBackend:
@@ -217,61 +217,61 @@ proc MainCommand(cmd, filename: string) =
CommandCompileToC(filename)
else:
rawMessage(errInvalidCommandX, cmd)
of wCompileToEcmaScript, wJs:
of "js", "compiletoecmascript":
gCmd = cmdCompileToEcmaScript
wantFile(filename)
CommandCompileToEcmaScript(filename)
of wCompileToLLVM:
of "compiletollvm":
gCmd = cmdCompileToLLVM
wantFile(filename)
when has_LLVM_Backend:
CommandCompileToLLVM(filename)
else:
rawMessage(errInvalidCommandX, cmd)
of wPretty:
of "pretty":
gCmd = cmdPretty
wantFile(filename) #CommandExportSymbols(filename);
CommandPretty(filename)
of wDoc:
of "doc":
gCmd = cmdDoc
LoadSpecialConfig(DocConfig)
wantFile(filename)
CommandDoc(filename)
of wRst2html:
of "rst2html":
gCmd = cmdRst2html
LoadSpecialConfig(DocConfig)
wantFile(filename)
CommandRst2Html(filename)
of wRst2tex:
of "rst2tex":
gCmd = cmdRst2tex
LoadSpecialConfig(DocTexConfig)
wantFile(filename)
CommandRst2TeX(filename)
of wGenDepend:
of "gendepend":
gCmd = cmdGenDepend
wantFile(filename)
CommandGenDepend(filename)
of wDump:
of "dump":
gCmd = cmdDump
condsyms.ListSymbols()
for it in iterSearchPath(): MsgWriteln(it)
of wCheck:
of "check":
gCmd = cmdCheck
wantFile(filename)
CommandCheck(filename)
of wParse:
of "parse":
gCmd = cmdParse
wantFile(filename)
discard parseFile(addFileExt(filename, nimExt))
of wScan:
of "scan":
gCmd = cmdScan
wantFile(filename)
CommandScan(filename)
MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
of wI:
of "i":
gCmd = cmdInteractive
CommandInteractive()
of wIdeTools:
of "idetools":
gCmd = cmdIdeTools
wantFile(filename)
CommandSuggest(filename)

View File

@@ -34,12 +34,12 @@ const
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wBreakpoint,
wCheckpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated, wFloatChecks,
wInfChecks, wNanChecks, wPragma, wEmit}
wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wPure,
wDeprecated, wExtern}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern}
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern}
@@ -357,6 +357,19 @@ proc PragmaEmit(c: PContext, n: PNode) =
proc noVal(n: PNode) =
if n.kind == nkExprColonExpr: invalidPragma(n)
proc PragmaUnroll(c: PContext, n: PNode) =
if c.p.nestedLoopCounter <= 0:
invalidPragma(n)
elif n.kind == nkExprColonExpr:
var unrollFactor = expectIntLit(c, n)
if unrollFactor <% 32:
n.sons[1] = newIntNode(nkIntLit, unrollFactor)
else:
invalidPragma(n)
proc PragmaLinearScanEnd(c: PContext, n: PNode) =
noVal(n)
proc processPragma(c: PContext, n: PNode, i: int) =
var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n)
@@ -475,6 +488,10 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfAcyclic)
of wShallow:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfShallow)
of wTypeCheck:
noVal(it)
incl(sym.flags, sfTypeCheck)
@@ -509,6 +526,8 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if sym.typ == nil: invalidPragma(it)
sym.typ.callConv = wordToCallConv(k)
of wEmit: PragmaEmit(c, it)
of wUnroll: PragmaUnroll(c, it)
of wLinearScanEnd: PragmaLinearScanEnd(c, it)
else: invalidPragma(it)
else: invalidPragma(it)
else: processNote(c, it)

View File

@@ -188,7 +188,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
of '\x0D', '\x0A':
getIndent(L, tok)
of '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{',
'|', '}', '~':
getAdornment(L, tok)
if len(tok.symbol) <= 3: tok.kind = tkPunct
@@ -257,16 +257,16 @@ type
value*: PRstNode
TSharedState{.final.} = object
uLevel*, oLevel*: int # counters for the section levels
subs*: seq[TSubstitution] # substitutions
refs*: seq[TSubstitution] # references
uLevel*, oLevel*: int # counters for the section levels
subs*: seq[TSubstitution] # substitutions
refs*: seq[TSubstitution] # references
underlineToLevel*: TLevelMap # Saves for each possible title adornment
# character its level in the
# current document.
# This is for single underline adornments.
overlineToLevel*: TLevelMap # Saves for each possible title adornment
# character its level in the current document.
# This is for over-underline adornments.
overlineToLevel*: TLevelMap # Saves for each possible title adornment
# character its level in the current document.
# This is for over-underline adornments.
PSharedState = ref TSharedState
TRstParser = object of TObject

View File

@@ -410,10 +410,13 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
flags: TExprFlags): PNode =
if not (efWantIterator in flags):
result = semDirectCall(c, n, {skProc, skMethod, skConverter})
else:
result = semDirectCall(c, n, {skIterator})
var symflags = {skProc, skMethod, skConverter}
if efWantIterator in flags:
symflags = {skIterator}
elif efAllowType in flags:
# for ``type countup(1,3)``, see ``tests/ttoseq``.
symflags.incl(skIterator)
result = semDirectCall(c, n, symflags)
if result != nil:
if result.sons[0].kind != nkSym:
InternalError("semDirectCallAnalyseEffects")
@@ -1037,7 +1040,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of paNone: result = nil
of paTuplePositions: result = semTuplePositionsConstr(c, n)
of paTupleFields: result = semTupleFieldsConstr(c, n)
of paSingle: result = semExpr(c, n.sons[0])
of paSingle: result = semExpr(c, n.sons[0], flags)
of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n)
of nkLambda: result = semLambda(c, n)

View File

@@ -574,7 +574,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
case n.kind
of nkEmpty: nil
of nkTypeOfExpr:
result = semExprWithType(c, n, {efAllowType}).typ
checkSonsLen(n, 1)
result = semExprWithType(c, n.sons[0], {efAllowType}).typ
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: GlobalError(n.info, errTypeExpected)

View File

@@ -340,10 +340,6 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
#result = newTransNode(n.sons[0])
#result[1] = transform(c, m.sons[0])
#if skipTypes(n.sons[0].typ, abstractVar).kind == tyOpenArray:
# debug(result.pnode)
# liMessage(n.info, warnUser,
# "nkPassAsOpenArray introduced here " & renderTree(n))
else:
result = transformSons(c, n)
else:

View File

@@ -53,12 +53,8 @@ type
wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH,
wSymbolFiles, wFieldChecks, wX, wVersion, wAdvanced, wSkipcfg, wSkipProjCfg,
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar,
wAcyclic, wIndex,
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM,
wCompileToOC,
wPretty,
wDoc, wGenDepend, wDump, wCheck, wParse, wScan, wJs, wOC,
wRst2html, wRst2tex, wI,
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wIndex,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads,
wRecursivePath,
wStdout,
@@ -105,33 +101,25 @@ const
"cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v",
"help", "h", "symbolfiles", "fieldchecks", "x", "version", "advanced",
"skipcfg", "skipprojcfg", "cc", "genscript", "checkpoint", "checkpoints",
"nomain", "subschar", "acyclic", "index",
"compiletoc", "compiletocpp", "compiletoecmascript", "compiletollvm",
"compiletooc",
"pretty", "doc", "gendepend", "dump", "check", "parse", "scan",
"js", "oc", "rst2html", "rst2tex", "i",
"nomain", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"index",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
"threads", "recursivepath",
"stdout",
"idetools", "suggest", "track", "def", "context"]
proc whichKeyword*(id: PIdent): TSpecialWord
proc whichKeyword*(id: String): TSpecialWord
proc findStr*(a: openarray[string], s: string): int
# implementation
proc findStr(a: openarray[string], s: string): int =
proc findStr*(a: openarray[string], s: string): int =
for i in countup(low(a), high(a)):
if cmpIgnoreStyle(a[i], s) == 0:
return i
result = - 1
proc whichKeyword(id: String): TSpecialWord =
result = whichKeyword(getIdent(id))
proc whichKeyword(id: PIdent): TSpecialWord =
proc whichKeyword*(id: PIdent): TSpecialWord =
if id.id < 0: result = wInvalid
else: result = TSpecialWord(id.id)
proc whichKeyword*(id: String): TSpecialWord =
result = whichKeyword(getIdent(id))
proc initSpecials() =
# initialize the keywords:

View File

@@ -0,0 +1,11 @@
iterator fibonacci(): int =
var a = 0
var b = 1
while true:
yield a
var c = b
b = a
a = a + c

185
tests/accept/run/tsort.nim Normal file
View File

@@ -0,0 +1,185 @@
# design criteria:
# Generic code is expenisve wrt code size!
# So the implementation should be small.
# The sort should be stable.
#
proc sort[T](arr: var openArray[T], lo, hi: natural) =
var k = 0
if lo < hi:
var mid = (lo + hi) div 2
sort(arr, lo, mid)
inc(mid)
sort(arr, mid, hi)
while lo < mid and mid <= hi:
if arr[lo] < arr[mid]:
inc(lo)
else:
when swapIsExpensive(T):
var help = arr[mid]
for k in countdown(mid, succ(lo)):
arr[k] = arr[pred(k)]
arr[lo] = help
else:
for k in countdown(mid, succ(lo)):
swap(arr[k], arr[pred(k)])
inc(lo)
inc(mid)
type
TSortOrder* = enum
Descending = -1,
Ascending = 0
proc flip(x: int, order: TSortOrder): int {.inline.} =
result = x xor ord(order) - ord(order)
# We use a fixed size stack. This size is larger
# than can be overflowed on a 64-bit machine
const
stackSize = 66
minRunSize = 7
type
TRun = tuple[index, length: int]
TSortState[T] {.pure, final.} = object
storage: seq[T]
runs: array[0..stackSize-1, TRun]
stackHeight: int # The index of the first unwritten element of the stack.
partitionedUpTo, length: int
# We keep track of how far we've partitioned up
# to so we know where to start the next partition.
# The idea is that everything < partionedUpTo
# is on the stack, everything >= partionedUpTo
# is not yet on the stack. When partitionedUpTo == length
# we'll have put everything on the stack.
proc reverse[T](a: var openArray[T], first, last: int) =
for j in first .. < first+length div 2: swap(a[j], a[length-j-1])
proc insertionSort[T]( int xs[], int length) =
for i in 1.. < length:
# The array before i is sorted. Now insert xs[i] into it
var x = xs[i]
var j = i-1
# Move j down until it's either at the beginning or on
# something <= x, and everything to the right of it has
# been moved up one.
while j >= 0 and xs[j] > x:
xs[j+1] = xs[j]
dec j
xs[j+1] = x
proc boostRunLength(s: TSortState, run: var TRun) =
# Need to make sure we don't overshoot the end of the array
var length = min(s.length - run.index, minRunSize)
insertionSort(run.index, length)
run.length = length
proc nextPartition[T](a: var openarray[T], s: var TSortState): bool =
if s.partitionedUpTo >= s.length: return false
var startIndex = s.partitionedUpTo
# Find an increasing run starting from startIndex
var nextStartIndex = startIndex + 1
if nextStartIndex < s.length:
if a[nextStartIndex] < a[startIndex]:
# We have a decreasing sequence starting here.
while nextStartIndex < s.length:
if a[nextStartIndex] < a[nextStartIndex-1]: inc(nextStartIndex)
else: break
# Now reverse it in place.
reverse(a, startIndex, nextStartIndex)
else:
# We have an increasing sequence starting here.
while nextStartIndex < s.length:
if a[nextStartIndex] >= a[nextStartIndex-1]: inc(nextStartIndex)
else: break
# So now [startIndex, nextStartIndex) is an increasing run.
# Push it onto the stack.
var runToAdd: TRun = (startIndex, nextStartIndex - startIndex)
if runToAdd.length < minRunSize:
boostRunLength(s, runToAdd)
s.partitionedUpTo = startIndex + runToAdd.length
s.runs[s.stackHeight] = runToAdd
inc s.stackHeight
result = true
proc shouldCollapse(s: TSortState): bool =
if s.stackHeight > 2:
var h = s.stackHeight-1
var headLength = s.runs[h].length
var nextLength = s.runs[h-1].length
result = 2 * headLength > nextLength
proc merge(int target[], int p1[], int l1, int p2[], int l2, int storage[]) =
# Merge the sorted arrays p1, p2 of length l1, l2 into a single
# sorted array starting at target. target may overlap with either
# of p1 or p2 but must have enough space to store the array.
# Use the storage argument for temporary storage. It must have room for
# l1 + l2 ints.
int *merge_to = storage
# Current index into each of the two arrays we're writing
# from.
int i1, i2;
i1 = i2 = 0;
# The address to which we write the next element in the merge
int *next_merge_element = merge_to;
# Iterate over the two arrays, writing the least element at the
# current position to merge_to. When the two are equal we prefer
# the left one, because if we're merging left, right we want to
# ensure stability.
# Of course this doesn't matter for integers, but it's the thought
# that counts.
while i1 < l1 and i2 < l2:
if p1[i1] <= p2[i2]:
*next_merge_element = p1[i1];
i1++
else:
*next_merge_element = p2[i2];
i2++
next_merge_element++
# If we stopped short before the end of one of the arrays
# we now copy the rest over.
memcpy(next_merge_element, p1 + i1, sizeof(int) * (l1 - i1));
memcpy(next_merge_element, p2 + i2, sizeof(int) * (l2 - i2));
# We've now merged into our additional working space. Time
# to copy to the target.
memcpy(target, merge_to, sizeof(int) * (l1 + l2));
proc mergeCollapse(a: s: var TSortState) =
var X = s.runs[s.stackHeight-2]
var Y = s.runs[s.stackHeight-1]
merge(X.index, X.index, X.length, Y.index, Y.length, s.storage)
dec s.stackHeight
inc X.length, Y.length
s.runs[s.stackHeight-1] = X
proc sort[T](arr: var openArray[T], first, last: natural,
cmp: proc (x,y: T): int, order = TSortOrder.ascending) =
var s: TSortState
newSeq(s.storage, arr.len)
s.stackHeight = 0
s.partitionedUpTo = 0
s.length = arr.len
while nextPartition(s):
while shouldCollapse(s): mergeCollapse(s)
while s.stackHeight > 1: mergeCollapse(s)
proc sort[T](arr: var openArray[T], cmp: proc (x, y: T): int = cmp,
order = TSortOrder.ascending) =
sort(arr, 0, high(arr), order)

View File

@@ -0,0 +1,12 @@
discard """
output: "23456"
"""
template toSeq*(iter: expr): expr =
var result: seq[type(iter)] = @[]
for x in iter: add(result, x)
result
for x, y in items(toSeq(countup(2, 6))).withIndex:
stdout.write(x)

View File

@@ -1,5 +1,4 @@
- 'nimrod def': does not always work
- BUG: gcleak.nim
- thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch

View File

@@ -20,7 +20,8 @@ Bugfixes
- Bugfix: Multiple yield statements in iterators did not cause local vars to be
copied.
- Bugfix: The compiler does not emit very inaccurate floating point literals
anymore.
anymore.
- Lots of other bugfixes: Too many to list them all.
Changes affecting backwards compatibility
@@ -65,6 +66,9 @@ Additions
- Added ``math.floor``.
- The *interactive mode* (REPL) has been improved and documented for the
first time.
- Added the ``linearScanEnd`` and ``unroll`` pragmas.
- The compiler now might use a hashing for string case statements depending
on the number of string literals in the case statement.
2010-10-20 Version 0.8.10 released