code generator supports constant sequences; more consistent compile time evaluation

This commit is contained in:
Araq 2011-10-07 09:02:08 +02:00
commit 42516c0086
18 changed files with 180 additions and 137 deletions

View file

@ -559,9 +559,11 @@ const
tyBool, tyChar, tyEnum, tyArray, tyObject,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc,
tyPointer,
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128}
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple}
ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}

View file

@ -1831,6 +1831,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
if sym.loc.r == nil or sym.loc.t == nil:
InternalError(e.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of nkMetaNode: expr(p, e.sons[0], d)
else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
proc genNamedConstExpr(p: BProc, n: PNode): PRope =
@ -1845,6 +1846,24 @@ proc genConstSimpleList(p: BProc, n: PNode): PRope =
if length > 0: app(result, genNamedConstExpr(p, n.sons[length - 1]))
appf(result, "}$n")
proc genConstSeq(p: BProc, n: PNode, t: PType): PRope =
var data = ropef("{{$1, $1}", n.len.toRope)
for i in countup(0, n.len - 1):
appf(data, ",$1$n", [genConstExpr(p, n.sons[i])])
data.app("}")
inc(p.labels)
result = con("CNSTSEQ", p.labels.toRope)
appcg(p.module, cfsData,
"NIM_CONST struct {$n" &
" #TGenericSeq Sup;$n" &
" $1 data[$2];$n" &
"} $3 = $4;$n", [
getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data])
result = ropef("(($1)&$2)", [getTypeDesc(p.module, t), result])
proc genConstExpr(p: BProc, n: PNode): PRope =
case n.Kind
of nkHiddenStdConv, nkHiddenSubConv:
@ -1855,7 +1874,11 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
result = genRawSetData(cs, int(getSize(n.typ)))
of nkBracket, nkPar:
# XXX: tySequence!
result = genConstSimpleList(p, n)
var t = skipTypes(n.typ, abstractInst)
if t.kind == tySequence:
result = genConstSeq(p, n, t)
else:
result = genConstSimpleList(p, n)
else:
var d: TLoc
initLocExpr(p, n, d)

View file

@ -67,7 +67,8 @@ proc genConstStmt(p: BProc, t: PNode) =
var c = it.sons[0].sym
if sfFakeConst in c.flags:
genSingleVar(p, it)
elif c.typ.kind in ConstantDataTypes and not (lfNoDecl in c.loc.flags):
elif c.typ.kind in ConstantDataTypes and not (lfNoDecl in c.loc.flags) and
c.ast.len != 0:
# generate the data:
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
if sfImportc in c.flags:

View file

@ -1426,6 +1426,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
of nkCStringToString: convCStrToStr(p, n, r)
of nkStmtListExpr: genStmtListExpr(p, n, r)
of nkEmpty: nil
of nkMetaNode: gen(p, n.sons[0], r)
else: InternalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals

View file

@ -43,7 +43,7 @@ const
evalMaxIterations = 500_000 # max iterations of all loops
evalMaxRecDepth = 10_000 # max recursion depth for evaluation
# Much better: use a timeout! -> Wether code compiles depends on the machine
# other idea: use a timeout! -> Wether code compiles depends on the machine
# the compiler runs on then! Bad idea!
proc newStackFrame*(): PStackFrame =
@ -754,17 +754,15 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
if isSpecial(result): return
result = newStrNodeT(renderTree(result, {renderNoComments}), n)
proc isEmpty(n: PNode): bool =
result = (n != nil) and (n.kind == nkEmpty)
proc isEmpty(n: PNode): bool =
result = n != nil and n.kind == nkEmpty
# The lexer marks multi-line strings as residing at the line where they
# are closed. This function returns the line where the string begins
# Maybe the lexer should mark both the beginning and the end of expressions,
# then this function could be removed.
proc stringStartingLine(s: PNode): int =
var totalLines = 0
for ln in splitLines(s.strVal): inc totalLines
result = s.info.line - totalLines
result = s.info.line - countLines(s.strVal)
proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
var code = evalAux(c, n.sons[1], {})
@ -1069,9 +1067,6 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
dec(gNestedEvals)
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
case n.kind # atoms:
of nkMetaNode:
result = copyTree(n.sons[0])
result.typ = n.typ
of nkEmpty: result = n
of nkSym: result = evalSym(c, n, flags)
of nkType..nkNilLit: result = copyNode(n) # end of atoms
@ -1131,6 +1126,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef,
nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt:
nil
of nkMetaNode:
result = copyTree(n.sons[0])
result.typ = n.typ
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambda, nkContinueStmt, nkIdent:
stackTrace(c, n, errCannotInterpretNodeX, $n.kind)

View file

@ -55,25 +55,26 @@ proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
GlobalError(typ.n.info, errXisNoType, typeToString(typ))
proc semConstExpr(c: PContext, n: PNode): PNode =
result = semExprWithType(c, n)
if result == nil:
GlobalError(n.info, errConstExprExpected)
return
result = getConstExpr(c.module, result)
if result == nil: GlobalError(n.info, errConstExprExpected)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil:
GlobalError(n.info, errConstExprExpected)
return nil
result = getConstExpr(c.module, e)
if result == nil:
#writeln(output, renderTree(n));
if result == nil:
result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty:
GlobalError(n.info, errConstExprExpected)
when false:
result = semExprWithType(c, n)
if result == nil:
GlobalError(n.info, errConstExprExpected)
return
result = getConstExpr(c.module, result)
if result == nil: GlobalError(n.info, errConstExprExpected)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
result = semConstExpr(c, n)
include seminst, semcall
proc typeMismatch(n: PNode, formal, actual: PType) =

View file

@ -9,10 +9,6 @@
# this module does the semantic checking for expressions
const
ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyArrayConstr, tyTuple, tySet}
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
markUsed(n, s)
pushInfoContext(n.info)
@ -49,6 +45,11 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s)
proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
result = copyTree(s.ast)
result.typ = s.typ
result.info = n.info
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
case s.kind
of skProc, skMethod, skIterator, skConverter:
@ -56,24 +57,25 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
getModule(s).id != c.module.id:
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
result = symChoice(c, n, s)
of skConst:
#
# Consider::
# const x = []
# proc p(a: openarray[int])
# proc q(a: openarray[char])
# p(x)
# q(x)
#
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
#
of skConst:
markUsed(n, s)
if s.typ.kind in ConstAbstractTypes:
result = copyTree(s.ast)
result.typ = s.typ
result.info = n.info
case skipTypes(s.typ, abstractInst).kind
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyTuple, tySet, tyUInt..tyUInt64:
result = inlineConst(n, s)
of tyArrayConstr, tySequence:
# Consider::
# const x = []
# proc p(a: openarray[int])
# proc q(a: openarray[char])
# p(x)
# q(x)
#
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
if s.ast.len == 0: result = inlineConst(n, s)
else: result = newSymNode(s, n.info)
else:
result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, s)

View file

@ -181,10 +181,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
of mInSet: result = newIntNodeT(Ord(inSet(a, b)), n)
of mRepr:
# BUGFIX: we cannot eval mRepr here. But this means that it is not
# available for interpretation. I don't know how to fix this.
#result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
of mRepr:
# BUGFIX: we cannot eval mRepr here for reasons that I forgot.
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n)
@ -324,7 +322,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
var s = n.sym
if s.kind == skEnumField:
result = newIntNodeT(s.position, n)
elif (s.kind == skConst):
elif s.kind == skConst:
case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
@ -370,10 +368,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh:
if not (skipTypes(n.sons[1].typ, abstractVar).kind in
{tyOpenArray, tySequence, tyString}):
result = newIntNodeT(lastOrd(skipTypes(n.sons[1].typ, abstractVar)),
n)
if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tyOpenArray, tySequence, tyString}:
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
else:
var a = 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:

View file

@ -280,22 +280,33 @@ proc semConst(c: PContext, n: PNode): PNode =
var typ: PType = nil
if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
var e = semExprWithType(c, a.sons[2])
if e == nil: GlobalError(a.sons[2].info, errConstExprExpected)
var def = getConstExpr(c.module, e)
if def == nil:
v.flags.incl(sfFakeConst)
def = evalConstExpr(c.module, e)
if def == nil or def.kind == nkEmpty: def = e
# check type compatibility between def.typ and typ:
if typ != nil:
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
v.flags.incl(sfFakeConst)
if not typeAllowed(typ, skVar):
when true:
var def = semConstExpr(c, a.sons[2])
if def == nil: GlobalError(a.sons[2].info, errConstExprExpected)
# check type compatibility between def.typ and typ:
if typ != nil:
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
GlobalError(a.info, errXisNoType, typeToString(typ))
else:
var e = semExprWithType(c, a.sons[2])
if e == nil: GlobalError(a.sons[2].info, errConstExprExpected)
var def = getConstExpr(c.module, e)
if def == nil:
v.flags.incl(sfFakeConst)
def = evalConstExpr(c.module, e)
if def == nil or def.kind == nkEmpty: def = e
# check type compatibility between def.typ and typ:
if typ != nil:
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
v.flags.incl(sfFakeConst)
if not typeAllowed(typ, skVar):
GlobalError(a.info, errXisNoType, typeToString(typ))
v.typ = typ
v.ast = def # no need to copy
addInterfaceDecl(c, v)

View file

@ -161,7 +161,6 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
proc transformSymAux(c: PTransf, n: PNode): PNode =
var b: PNode
if (n.kind != nkSym): internalError(n.info, "transformSym")
var tc = c.transCon
if sfBorrow in n.sym.flags:
# simply exchange the symbol:
@ -176,14 +175,15 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
if result != nil: return
tc = tc.next
result = b
case b.sym.kind
of skConst, skEnumField:
if sfFakeConst notin b.sym.flags:
if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes:
result = getConstExpr(c.module, b)
if result == nil: InternalError(b.info, "transformSym: const")
else:
nil
when false:
case b.sym.kind
of skConst, skEnumField:
if sfFakeConst notin b.sym.flags:
if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes:
result = getConstExpr(c.module, b)
if result == nil: InternalError(b.info, "transformSym: const")
else:
nil
proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n))
@ -360,7 +360,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool:
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt..tyUInt64:
if not isOrdinalType(source):
# XXX int64 -> float conversion?
result = transformSons(c, n)
@ -659,7 +659,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
return transformSym(c, n)
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
@ -719,7 +719,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
else:
result = transformSons(c, n)
var cnst = getConstExpr(c.module, PNode(result))
if cnst != nil:
if cnst != nil and (cnst.kind != nkBracket or cnst.len == 0):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(context: PPassContext, n: PNode): PNode =

View file

@ -762,7 +762,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
of tyOpenArray, tyVarargs:
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
of tySequence:
result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or
result = typeAllowedAux(marker, t.sons[0], skVar) or
t.sons[0].kind == tyEmpty
of tyArray:
result = typeAllowedAux(marker, t.sons[1], skVar) or