diff options
author | Araq <rumpf_a@web.de> | 2011-03-14 23:57:41 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-03-14 23:57:41 +0100 |
commit | 8d734244b14e09c97267432468ac20fcf8ff82eb (patch) | |
tree | 253eae61df789c42efe49b7c30cf38c66a3c208e | |
parent | 6850fb73c1b9ae8d3f56a61ee37922c3cb3788d6 (diff) | |
download | Nim-8d734244b14e09c97267432468ac20fcf8ff82eb.tar.gz |
linearScanEnd pragma; string case statement optimization
-rwxr-xr-x | build.bat | 10 | ||||
-rwxr-xr-x | doc/manual.txt | 47 | ||||
-rwxr-xr-x | doc/nimrodc.txt | 24 | ||||
-rwxr-xr-x | install.sh | 6 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 9 | ||||
-rwxr-xr-x | lib/system.nim | 4 | ||||
-rwxr-xr-x | lib/system/assign.nim | 21 | ||||
-rwxr-xr-x | lib/system/inclrtl.nim | 2 | ||||
-rwxr-xr-x | lib/system/sysstr.nim | 2 | ||||
-rwxr-xr-x | rod/ast.nim | 3 | ||||
-rwxr-xr-x | rod/ccgexprs.nim | 13 | ||||
-rwxr-xr-x | rod/ccgstmts.nim | 195 | ||||
-rwxr-xr-x | rod/ccgutils.nim | 96 | ||||
-rwxr-xr-x | rod/cgen.nim | 19 | ||||
-rwxr-xr-x | rod/main.nim | 36 | ||||
-rwxr-xr-x | rod/pragmas.nim | 23 | ||||
-rwxr-xr-x | rod/rst.nim | 14 | ||||
-rwxr-xr-x | rod/semexprs.nim | 13 | ||||
-rwxr-xr-x | rod/semtypes.nim | 3 | ||||
-rwxr-xr-x | rod/transf.nim | 4 | ||||
-rwxr-xr-x | rod/wordrecg.nim | 30 | ||||
-rw-r--r-- | tests/accept/compile/tfib.nim | 11 | ||||
-rw-r--r-- | tests/accept/run/tsort.nim | 185 | ||||
-rw-r--r-- | tests/accept/run/ttoseq.nim | 12 | ||||
-rwxr-xr-x | todo.txt | 1 | ||||
-rwxr-xr-x | web/news.txt | 6 |
26 files changed, 537 insertions, 252 deletions
diff --git a/build.bat b/build.bat index b4e46d3f0..34086269c 100755 --- a/build.bat +++ b/build.bat @@ -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 diff --git a/doc/manual.txt b/doc/manual.txt index 49311fb43..975094a56 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index c8cbbaa73..b30071b4e 100755 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -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)) .. diff --git a/install.sh b/install.sh index 16d646f56..60ec70ca1 100755 --- a/install.sh +++ b/install.sh @@ -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 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 435f522eb..de555917c 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -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".} = diff --git a/lib/system.nim b/lib/system.nim index 6909185e6..043275302 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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.} diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 9f0afb363..24d688ca9 100755 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -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) diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 3898355c8..23cecfbc1 100755 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -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. diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 6c5435724..2afb91d7b 100755 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -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. diff --git a/rod/ast.nim b/rod/ast.nim index 2b0fc6d38..c7c0fa7d0 100755 --- a/rod/ast.nim +++ b/rod/ast.nim @@ -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] diff --git a/rod/ccgexprs.nim b/rod/ccgexprs.nim index c8404fef6..65cc33fd4 100755 --- a/rod/ccgexprs.nim +++ b/rod/ccgexprs.nim @@ -215,20 +215,21 @@ 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 tyArray, tyArrayConstr: + 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, - "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n", - [rdLoc(dest), rdLoc(src)]) - of tyObject: # XXX: check for subtyping? + 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", [addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)]) else: - appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) + appcg(p, cpsStmts, + "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$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? diff --git a/rod/ccgstmts.nim b/rod/ccgstmts.nim index 572b60143..e87305065 100755 --- a/rod/ccgstmts.nim +++ b/rod/ccgstmts.nim @@ -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) - -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 + 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) -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 diff --git a/rod/ccgutils.nim b/rod/ccgutils.nim index c7733c5ff..f1d66ca94 100755 --- a/rod/ccgutils.nim +++ b/rod/ccgutils.nim @@ -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() \ No newline at end of file +InitTypeTables() diff --git a/rod/cgen.nim b/rod/cgen.nim index 503cb8586..7df9f3d11 100755 --- a/rod/cgen.nim +++ b/rod/cgen.nim @@ -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) diff --git a/rod/main.nim b/rod/main.nim index 977a4ff1b..79059a874 100755 --- a/rod/main.nim +++ b/rod/main.nim @@ -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) diff --git a/rod/pragmas.nim b/rod/pragmas.nim index 45543eb74..d7bda4099 100755 --- a/rod/pragmas.nim +++ b/rod/pragmas.nim @@ -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) diff --git a/rod/rst.nim b/rod/rst.nim index efda9bd9a..dace43a44 100755 --- a/rod/rst.nim +++ b/rod/rst.nim @@ -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 diff --git a/rod/semexprs.nim b/rod/semexprs.nim index 1d20e5253..7a14b931a 100755 --- a/rod/semexprs.nim +++ b/rod/semexprs.nim @@ -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) diff --git a/rod/semtypes.nim b/rod/semtypes.nim index 4a676e00a..8dae5c27b 100755 --- a/rod/semtypes.nim +++ b/rod/semtypes.nim @@ -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) diff --git a/rod/transf.nim b/rod/transf.nim index 5b62c7f16..c7c4e3db8 100755 --- a/rod/transf.nim +++ b/rod/transf.nim @@ -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: diff --git a/rod/wordrecg.nim b/rod/wordrecg.nim index f9ea37158..8376fa01b 100755 --- a/rod/wordrecg.nim +++ b/rod/wordrecg.nim @@ -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: diff --git a/tests/accept/compile/tfib.nim b/tests/accept/compile/tfib.nim new file mode 100644 index 000000000..09a4d5038 --- /dev/null +++ b/tests/accept/compile/tfib.nim @@ -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 + + diff --git a/tests/accept/run/tsort.nim b/tests/accept/run/tsort.nim new file mode 100644 index 000000000..306e7e360 --- /dev/null +++ b/tests/accept/run/tsort.nim @@ -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) + diff --git a/tests/accept/run/ttoseq.nim b/tests/accept/run/ttoseq.nim new file mode 100644 index 000000000..d3332cce6 --- /dev/null +++ b/tests/accept/run/ttoseq.nim @@ -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) + diff --git a/todo.txt b/todo.txt index 5fa136e1f..ab892de8c 100755 --- a/todo.txt +++ b/todo.txt @@ -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 diff --git a/web/news.txt b/web/news.txt index 172630467..805c9b200 100755 --- a/web/news.txt +++ b/web/news.txt @@ -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 |