From 299390a585a1f626c51b932484fa3929102896d7 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Oct 2011 23:54:50 +0200 Subject: [PATCH] constant folding for cnst[i] and cnst.attr --- compiler/ast.nim | 4 ++- compiler/cgen.nim | 2 +- compiler/semfold.nim | 73 ++++++++++++++++++++++++++++++++++++++------ compiler/transf.nim | 4 ++- todo.txt | 44 +++++--------------------- 5 files changed, 78 insertions(+), 49 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 30989f392..550bda122 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -281,7 +281,9 @@ type nfBase2, # nfBase10 is default, so not needed nfBase8, nfBase16, - nfAllConst, # used to mark complex expressions constant + nfAllConst, # used to mark complex expressions constant; easy to get rid of + # but unfortunately it has measurable impact for compilation + # efficiency nfTransf, # node has been transformed nfSem # node has been checked for semantics diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a5343e3f7..7e16042f8 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,7 +14,7 @@ import options, intsets, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, - rodutils, renderer, idgen, cgendata, ccgmerge + rodutils, renderer, idgen, cgendata, ccgmerge, semfold when options.hasTinyCBackend: import tccgen diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d51f69bc1..f317340da 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -155,9 +155,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) of mLeU, mLeU64: result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) - of mBitandI, mBitandI64, mAnd: result = newIntNodeT(getInt(a) and getInt(b), n) + of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) - of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(getInt(a) xor getInt(b), n) + of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) of mAddU, mAddU64: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU, mSubU64: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU, mMulU64: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) @@ -199,7 +199,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = copyTree(a) result.typ = n.typ of mCompileOption: - result = newIntNodeT(Ord(commands.testCompileOption(getStr(a), n.info)), n) + result = newIntNodeT(Ord(commands.testCompileOption(a.getStr, n.info)), n) of mCompileOptionArg: result = newIntNodeT(Ord( testCompileOptionArg(getStr(a), getStr(b), n.info)), n) @@ -315,6 +315,59 @@ proc foldConv*(n, a: PNode): PNode = result = a result.typ = n.typ +proc getArrayConstr(m: PSym, n: PNode): PNode = + if n.kind == nkBracket: + result = n + else: + result = getConstExpr(m, n) + if result == nil: result = n + +proc foldArrayAccess(m: PSym, n: PNode): PNode = + var x = getConstExpr(m, n.sons[0]) + if x == nil: return + + var y = getConstExpr(m, n.sons[1]) + if y == nil: return + + var idx = getOrdValue(y) + case x.kind + of nkPar: + if (idx >= 0) and (idx < sonsLen(x)): + result = x.sons[int(idx)] + if result.kind == nkExprColonExpr: result = result.sons[1] + else: + LocalError(n.info, errIndexOutOfBounds) + of nkBracket, nkMetaNode: + if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] + else: LocalError(n.info, errIndexOutOfBounds) + of nkStrLit..nkTripleStrLit: + result = newNodeIT(nkCharLit, x.info, n.typ) + if (idx >= 0) and (idx < len(x.strVal)): + result.intVal = ord(x.strVal[int(idx)]) + elif idx == len(x.strVal): + nil + else: + LocalError(n.info, errIndexOutOfBounds) + else: nil + +proc foldFieldAccess(m: PSym, n: PNode): PNode = + # a real field access; proc calls have already been transformed + var x = getConstExpr(m, n.sons[0]) + if x == nil or x.kind != nkPar: return + + var field = n.sons[1].sym + for i in countup(0, sonsLen(x) - 1): + var it = x.sons[i] + if it.kind != nkExprColonExpr: + # lookup per index: + result = x.sons[field.position] + if result.kind == nkExprColonExpr: result = result.sons[1] + return + if it.sons[0].sym.name.id == field.name.id: + result = x.sons[i].sons[1] + return + localError(n.info, errFieldXNotFound, field.name.s) + proc getConstExpr(m: PSym, n: PNode): PNode = result = nil case n.kind @@ -333,7 +386,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mNimrodPatch: result = newIntNodeT(VersionPatch, n) of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) of mHostOS: result = newStrNodeT(toLower(platform.OS[targetOS].name), n) - of mHostCPU: result = newStrNodeT(toLower(platform.CPU[targetCPU].name), n) + of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLower, n) of mAppType: result = getAppType(n) of mNaN: result = newFloatNodeT(NaN, n) of mInf: result = newFloatNodeT(Inf, n) @@ -346,7 +399,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = copyNode(n) of nkIfExpr: result = getConstIfExpr(m, n) - of nkCall, nkCommand, nkCallStrLit: + of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix: if n.sons[0].kind != nkSym: return var s = n.sons[0].sym if s.kind != skProc: return @@ -372,13 +425,13 @@ proc getConstExpr(m: PSym, n: PNode): PNode = {tyOpenArray, tySequence, tyString}: result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) else: - var a = n.sons[1] + var a = getArrayConstr(m, n.sons[1]) if a.kind == nkBracket: # we can optimize it away: result = newIntNodeT(sonsLen(a)-1, n) of mLengthOpenArray: - var a = n.sons[1] - if a.kind == nkBracket: + var a = getArrayConstr(m, n.sons[1]) + if a.kind == nkBracket: # we can optimize it away! This fixes the bug ``len(134)``. result = newIntNodeT(sonsLen(a), n) else: @@ -449,5 +502,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = var a = getConstExpr(m, n.sons[1]) if a == nil: return result = foldConv(n, a) - else: + of nkBracketExpr: result = foldArrayAccess(m, n) + of nkDotExpr: result = foldFieldAccess(m, n) + else: nil diff --git a/compiler/transf.nim b/compiler/transf.nim index f8475f853..81c25ecb2 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -719,7 +719,9 @@ proc transform(c: PTransf, n: PNode): PTransNode = else: result = transformSons(c, n) var cnst = getConstExpr(c.module, PNode(result)) - if cnst != nil and (cnst.kind != nkBracket or cnst.len == 0): + # we inline constants if they are not complex constants: + if cnst != nil and (cnst.kind notin {nkCurly, nkPar, nkBracket} or + cnst.len == 0): result = PTransNode(cnst) # do not miss an optimization proc processTransf(context: PPassContext, n: PNode): PNode = diff --git a/todo.txt b/todo.txt index de27c6fa1..7f24b99a8 100755 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,8 @@ Version 0.8.14 ============== -- optimize unused constants away (affected by HLO); get rid of nfAllConst -- 'let x = y' -- fix actors.nim -- make threadvar efficient again on linux after testing +- optimize unused constants away (affected by HLO) +- fix actors.nim; test with different thread var implementations - dead code elim for JS backend @@ -16,42 +14,14 @@ incremental compilation - implement lib/pure/memfiles properly -Destructors ------------ - -A destructor is bound to a proc instead of a type. The proc is then called -an *init proc*. A destructor is called at -scope exit for the local variables (declared with ``var`` or ``let``) -that are initialized with its corresponding init proc: - -.. code-block:: nimrod - proc autoOpen(filename: string): TFile {.destructor: close.} = - result = open(filename) - - var f = autoOpen("abc.txt") - -A template ``atScopeExit`` can easily be defined with this mechanism. However -unfortunately the regex problem is not solved at all: - -.. code-block:: nimrod - if x =~ re"abc": - ... - -We really need some form of escape analysis; ``var`` or ``let`` is not enough! -Hm, but what if the procs declare that property: - -.. code-block:: nimrod - proc `=~` (s: string, pattern: TRegex{.noEscape.}): bool - -So it's bound to a type again? - - version 0.9.0 ============= - test the sort implementation again +- 'let x = y' - const ptr/ref -- unsigned ints and bignums; requires abstract integer literal type +- unsigned ints and bignums; requires abstract integer literal type: + use tyInt+node for that - implement the high level optimizer - warning for implicit openArray -> varargs convention - implement explicit varargs; **but** ``len(varargs)`` problem remains! @@ -64,7 +34,6 @@ version 0.9.0 - 'const' objects including case objects - os module should use Windows Unicode versions - 64bit build for Windows -- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC - optional indentation for 'case' statement Bugs @@ -83,7 +52,7 @@ Bugs - bug: DLL generation is broken; write at least a basic test - bug: stress testing basic method example (eval example) - without ``-d:relase`` leaks memory; good way to figure out how a + without ``-d:release`` leaks memory; good way to figure out how a fixed amount of stack can hold an arbitrary number of GC roots! @@ -149,6 +118,7 @@ Low priority is allocated - timeout for locks - adapt thread var emulation to care about the new merge operation +- codegen should use "NIM_CAST" macro and respect aliasing rules for GCC Version 2