From 2962ca7890160e20796292320a3f56eccd00cc90 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 8 Dec 2011 23:21:08 +0100 Subject: [PATCH] alias analysis as required for the code gen and the HLO --- compiler/aliases.nim | 182 ++++++++++++++++++++++++++++++++++++++++++ compiler/ast.nim | 33 +------- compiler/astalgo.nim | 26 ++++++ compiler/ccgexprs.nim | 7 -- compiler/ecmasgen.nim | 8 -- compiler/evals.nim | 9 +-- compiler/sem.nim | 2 +- compiler/semexprs.nim | 34 +++----- compiler/semfold.nim | 2 +- compiler/semmagic.nim | 36 +++++++++ compiler/types.nim | 1 - todo.txt | 2 +- 12 files changed, 262 insertions(+), 80 deletions(-) create mode 100644 compiler/aliases.nim create mode 100644 compiler/semmagic.nim diff --git a/compiler/aliases.nim b/compiler/aliases.nim new file mode 100644 index 000000000..e5f58356e --- /dev/null +++ b/compiler/aliases.nim @@ -0,0 +1,182 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Simple alias analysis for the HLO and the code generators. + +import + ast, astalgo, types, trees, intsets, msgs + +type + TAnalysisResult* = enum + arNo, arMaybe, arYes + +proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult + +proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult = + result = arNo + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + result = isPartOfAux(n.sons[i], b, marker) + if result == arYes: return + of nkRecCase: + assert(n.sons[0].kind == nkSym) + result = isPartOfAux(n.sons[0], b, marker) + if result == arYes: return + for i in countup(1, sonsLen(n) - 1): + case n.sons[i].kind + of nkOfBranch, nkElse: + result = isPartOfAux(lastSon(n.sons[i]), b, marker) + if result == arYes: return + else: internalError("isPartOfAux(record case branch)") + of nkSym: + result = isPartOfAux(n.sym.typ, b, marker) + else: internalError(n.info, "isPartOfAux()") + +proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult = + result = arNo + if a == nil: return + if ContainsOrIncl(marker, a.id): return + if compareTypes(a, b, dcEqIgnoreDistinct): return arYes + case a.kind + of tyObject: + result = isPartOfAux(a.sons[0], b, marker) + if result == arNo: result = isPartOfAux(a.n, b, marker) + of tyGenericInst, tyDistinct: + result = isPartOfAux(lastSon(a), b, marker) + of tyArray, tyArrayConstr, tySet, tyTuple: + for i in countup(0, sonsLen(a) - 1): + result = isPartOfAux(a.sons[i], b, marker) + if result == arYes: return + else: nil + +proc isPartOf(a, b: PType): TAnalysisResult = + ## checks iff 'a' can be part of 'b'. Iterates over VALUE types! + var marker = InitIntSet() + # watch out: parameters reversed because I'm too lazy to change the code... + result = isPartOfAux(b, a, marker) + +proc isPartOf*(a, b: PNode): TAnalysisResult = + ## checks if location `a` can be part of location `b`. We treat seqs and + ## strings as pointers because the code gen often just passes them as such. + ## + ## Note: `a` can only be part of `b`, if `a`'s type can be part of `b`'s + ## type. Since however type analysis is more expensive, we perform it only + ## if necessary. + ## + ## cases: + ## + ## YES-cases: + ## x <| x # for general trees + ## x[] <| x + ## x[i] <| x + ## x.f <| x + ## + ## NO-cases: + ## x !<| y # depending on type and symbol kind + ## x[constA] !<| x[constB] + ## x.f !<| x.g + ## x.f !<| y.f iff x !<= y + ## + ## MAYBE-cases: + ## + ## x[] ?<| y[] iff compatible type + ## + ## + ## x[] ?<| y depending on type + ## + if a.kind == b.kind: + case a.kind + of nkSym: + const varKinds = {skVar, skTemp, skProc} + # same symbol: aliasing: + if a.sym.id == b.sym.id: result = arYes + elif a.sym.kind in varKinds or b.sym.kind in varKinds: + # actually, a param could alias a var but we know that cannot happen + # here. XXX make this more generic + result = arNo + else: + # use expensive type check: + if isPartOf(a.sym.typ, b.sym.typ) != arNo: + result = arMaybe + of nkBracketExpr: + result = isPartOf(a[0], b[0]) + if len(a) >= 2 and len(b) >= 2: + # array accesses: + if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]): + # we know it's the same array and we have 2 constant indexes; + # if they are + var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1] + var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1] + + if SameValue(x, y): result = arYes + else: result = arNo + # else: maybe and no are accurate + else: + # pointer derefs: + if result != arYes: + if isPartOf(a.typ, b.typ) != arNo: result = arMaybe + + of nkDotExpr: + result = isPartOf(a[0], b[0]) + if result != arNo: + # if the fields are different, it's not the same location + if a[1].sym.id != b[1].sym.id: + result = arNo + + of nkHiddenDeref, nkDerefExpr: + result = isPartOf(a[0], b[0]) + # weaken because of indirection: + if result != arYes: + if isPartOf(a.typ, b.typ) != arNo: result = arMaybe + + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + result = isPartOf(a[1], b[1]) + of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + result = isPartOf(a[0], b[0]) + else: nil + # Calls return a new location, so a default of ``arNo`` is fine. + else: + # go down recursively; this is quite demanding: + const + Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, + nkCheckedFieldExpr} + Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv} + DerefKinds = {nkHiddenDeref, nkDerefExpr} + case b.kind + of Ix0Kinds: + # a* !<| b.f iff a* !<| b + result = isPartOf(a, b[0]) + + of DerefKinds: + # a* !<| b[] iff + if isPartOf(a.typ, b.typ) != arNo: + result = isPartOf(a, b[0]) + if result == arNo: result = arMaybe + + of Ix1Kinds: + # a* !<| T(b) iff a* !<| b + result = isPartOf(a, b[1]) + + of nkSym: + # b is an atom, so we have to check a: + case a.kind + of Ix0Kinds: + # a.f !<| b* iff a.f !<| b* + result = isPartOf(a[0], b) + of Ix1Kinds: + result = isPartOf(a[1], b) + + of DerefKinds: + if isPartOf(a.typ, b.typ) != arNo: + result = isPartOf(a[0], b) + if result == arNo: result = arMaybe + else: nil + else: nil + diff --git a/compiler/ast.nim b/compiler/ast.nim index 2b84c415f..419d57562 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -371,7 +371,7 @@ type mFields, mFieldPairs, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, - mAssert, mAstToStr, mRand, + mIsPartOf, mAstToStr, mRand, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mReset, @@ -669,36 +669,7 @@ const # for all kind of hash tables: GrowthFactor* = 2 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0 -proc SameValue*(a, b: PNode): bool - # a, b are literals -proc leValue*(a, b: PNode): bool - # a <= b? a, b are literals -proc ValueToString*(a: PNode): string - -proc leValue(a, b: PNode): bool = - # a <= b? - result = false - case a.kind - of nkCharLit..nkInt64Lit: - if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal - of nkFloatLit..nkFloat64Lit: - if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal - of nkStrLit..nkTripleStrLit: - if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal - else: InternalError(a.info, "leValue") - -proc SameValue(a, b: PNode): bool = - result = false - case a.kind - of nkCharLit..nkInt64Lit: - if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal - of nkFloatLit..nkFloat64Lit: - if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal - of nkStrLit..nkTripleStrLit: - if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal - else: InternalError(a.info, "SameValue") - -proc ValueToString(a: PNode): string = +proc ValueToString*(a: PNode): string = case a.kind of nkCharLit..nkInt64Lit: result = $(a.intVal) of nkFloatLit, nkFloat32Lit, nkFloat64Lit: result = $(a.floatVal) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 472a8d803..d405bd34c 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -146,6 +146,32 @@ proc IITablePut*(t: var TIITable, key, val: int) # implementation +proc SameValue*(a, b: PNode): bool = + result = false + case a.kind + of nkCharLit..nkInt64Lit: + if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal + of nkFloatLit..nkFloat64Lit: + if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal + of nkStrLit..nkTripleStrLit: + if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal + else: + debug a + debug b + InternalError(a.info, "SameValue") + +proc leValue*(a, b: PNode): bool = + # a <= b? + result = false + case a.kind + of nkCharLit..nkInt64Lit: + if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal + of nkFloatLit..nkFloat64Lit: + if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal + of nkStrLit..nkTripleStrLit: + if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal + else: InternalError(a.info, "leValue") + proc lookupInRecord(n: PNode, field: PIdent): PSym = result = nil case n.kind diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 13d9637ad..3a2c5965b 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1378,13 +1378,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr: expr(p, e.sons[1], d) of mEnumToStr: genRepr(p, e, d) - of mAssert: - if optAssert in p.Options: - expr(p, e.sons[1], d) - line = toRope(toLinenumber(e.info)) - filen = makeCString(ToFilename(e.info)) - appcg(p, cpsStmts, "#internalAssert($1, $2, $3);$n", - [filen, line, rdLoc(d)]) of mOf: genOf(p, e, d) of mNew: genNew(p, e) of mNewFinalize: genNewFinalize(p, e) diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 56749a139..76d4dde89 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -1132,14 +1132,6 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: unaryExpr(p, n, r, "", "$1 == null") of mEnumToStr: genRepr(p, n, r) - of mAssert: - if (optAssert in p.Options): - useMagic(p, "internalAssert") - gen(p, n.sons[1], a) - line = toRope(toLinenumber(n.info)) - filen = makeCString(ToFilename(n.info)) - appf(r.com, "if (!($3)) internalAssert($1, $2)", - [filen, line, mergeExpr(a)]) of mNew, mNewFinalize: genNew(p, n, r) of mSizeOf: r.res = toRope(getSize(n.sons[1].typ)) of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do diff --git a/compiler/evals.nim b/compiler/evals.nim index fff58b7f4..2ca0f3faf 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -695,13 +695,7 @@ proc evalNewSeq(c: PEvalContext, n: PNode): PNode = for i in countup(0, L-1): a.sons[i] = getNullValue(t.sons[0], n.info) result = emptyNode - -proc evalAssert(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - if getOrdValue(result) != 0: result = emptyNode - else: stackTrace(c, n, errAssertionFailed) - + proc evalIncl(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return @@ -876,7 +870,6 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mOf: result = evalOf(c, n) of mSizeOf: result = raiseCannotEval(c, n) of mHigh: result = evalHigh(c, n) - of mAssert: result = evalAssert(c, n) of mExit: result = evalExit(c, n) of mNew, mNewFinalize: result = evalNew(c, n) of mNewSeq: result = evalNewSeq(c, n) diff --git a/compiler/sem.nim b/compiler/sem.nim index a4a0ba1bc..d38a547c2 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, magicsys, parser, nversion, semdata, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest, - semthreads, intsets, transf, evals, idgen + semthreads, intsets, transf, evals, idgen, aliases proc semPass*(): TPass # implementation diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7bb6f26e0..a08e9c875 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -453,6 +453,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = skipTypes(t.sons[i], abstractInst).kind == tyVar: n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) + +proc expectStringArg(c: PContext, n: PNode, i: int): PNode = + result = c.semAndEvalConstExpr(n.sons[i+1]) + if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: + GlobalError(result.info, errStringLiteralExpected) + +include semmagic + proc semDirectCallAnalyseEffects(c: PContext, n: PNode, flags: TExprFlags): PNode = if efWantIterator in flags: @@ -520,6 +528,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = renderTree(n, {renderNoComments})) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) + if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone: + result = magicsAfterOverloadResolution(c, result, flags) proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! @@ -530,6 +540,8 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n)) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) + if result.sons[0].sym.magic != mNone: + result = magicsAfterOverloadResolution(c, result, flags) proc buildStringify(c: PContext, arg: PNode): PNode = if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString: @@ -914,12 +926,6 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info -proc expectStringArg(c: PContext, n: PNode, i: int): PNode = - result = c.semAndEvalConstExpr(n.sons[i+1]) - - if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: - GlobalError(result.info, errStringLiteralExpected) - proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = ## The argument to the proc should be nkCall(...) or similar ## Returns the macro/template symbol @@ -954,21 +960,6 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, else: result = semDirectOp(c, n, flags) -proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = - if sonsLen(n) == 2: - var a = expectStringArg(c, n, 0) - try: - var filename = a.strVal.FindFile - var content = readFile(filename) - result = newStrNode(nkStrLit, content) - result.typ = getSysType(tyString) - result.info = n.info - c.slurpedFiles.add(filename) - except EIO: - GlobalError(a.info, errCannotOpenFile, a.strVal) - else: - result = semDirectOp(c, n, flags) - proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! result = n @@ -991,7 +982,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = semAsgn(c, result) else: result = semDirectOp(c, n, flags) - of mSlurp: result = semSlurp(c, n, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mAstToStr: if sonsLen(n) == 2: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index b2e2f2277..0eb05a055 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -204,7 +204,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newIntNodeT(Ord( testCompileOptionArg(getStr(a), getStr(b), n.info)), n) of mNewString, mNewStringOfCap, - mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh, + mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mNLen..mNError, mEqRef: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim new file mode 100644 index 000000000..da1101729 --- /dev/null +++ b/compiler/semmagic.nim @@ -0,0 +1,36 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This include file implements the semantic checking for magics. + +proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = + var r = isPartOf(n[1], n[2]) + result = newIntNodeT(ord(r), n) + +proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = + assert sonsLen(n) == 2 + var a = expectStringArg(c, n, 0) + try: + var filename = a.strVal.FindFile + var content = readFile(filename) + result = newStrNode(nkStrLit, content) + result.typ = getSysType(tyString) + result.info = n.info + c.slurpedFiles.add(filename) + except EIO: + GlobalError(a.info, errCannotOpenFile, a.strVal) + +proc magicsAfterOverloadResolution(c: PContext, n: PNode, + flags: TExprFlags): PNode = + case n[0].sym.magic + of mSlurp: result = semSlurp(c, n, flags) + of mIsPartOf: result = semIsPartOf(c, n, flags) + else: result = n + + diff --git a/compiler/types.nim b/compiler/types.nim index 6ce4c6e48..d3f2bd1b5 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1024,7 +1024,6 @@ proc computeSize(typ: PType): biggestInt = proc getReturnType*(s: PSym): PType = # Obtains the return type of a iterator/proc/macro/template assert s.kind in {skProc, skTemplate, skMacro, skIterator} - # XXX ask Zahary if this change broke something result = s.typ.sons[0] proc getSize(typ: PType): biggestInt = diff --git a/todo.txt b/todo.txt index 0ff5967c0..ba7fed751 100755 --- a/todo.txt +++ b/todo.txt @@ -2,6 +2,7 @@ version 0.8.14 ============== - find a proper bugfix for C compilers optimizing away stack roots +- cleanup file path handling in the compiler - warning for implicit openArray -> varargs convention - implement explicit varargs; **but** ``len(varargs)`` problem remains! --> solve by implicit conversion from varargs to openarray @@ -124,7 +125,6 @@ Low priority need to recompile clients; er ... what about templates, macros or anything that has inlining semantics? - codegen should use "NIM_CAST" macro and respect aliasing rules for GCC -- remove unused mAssert magic Version 2 =========