experimental support for preserving local variable names in the generated code

This commit is contained in:
Zahary Karadjov
2012-04-13 16:01:26 +03:00
parent caf7878009
commit f25c638dc4
6 changed files with 146 additions and 22 deletions

View File

@@ -244,12 +244,17 @@ const
sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
sfNoInit* = sfMainModule # don't generate code to init the variable
sfImmediate* = sfDeadCodeElim # macro or template is immediately expanded
# without considering any possible overloads
sfImmediate* = sfDeadCodeElim
# macro or template is immediately expanded
# without considering any possible overloads
sfAnon* = sfCompilerProc # symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfAnon* = sfDiscardable
# symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfShadowed* = sfInnerProc
# a variable that was shadowed in some inner scope
const
# getting ready for the future expr/stmt merge
@@ -647,6 +652,14 @@ const
resultPos* = 5
dispatcherPos* = 6 # caution: if method has no 'result' it can be position 5!
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit}
nkLambdaKinds* = {nkLambda, nkDo}
skLocalVars* = {skVar, skLet, skForVar, skParam}
# creator procs:
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym): PSym
proc NewType*(kind: TTypeKind, owner: PSym): PType
@@ -691,11 +704,6 @@ proc copyNode*(src: PNode): PNode
proc copyTree*(src: PNode): PNode
# does copy its sons!
const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit}
const nkLambdaKinds* = {nkLambda, nkDo}
proc isCallExpr*(n: PNode): bool =
result = n.kind in nkCallKinds

View File

@@ -32,6 +32,15 @@ proc mangle(name: string): string =
add(result, "HEX")
add(result, toHex(ord(name[i]), 2))
proc isCKeyword(w: PIdent): bool =
# nimrod and C++ share some keywords
# it's more efficient to test the whole nimrod keywords range
case w.id
of cppKeywordsLow..cppKeywordsHigh,
nimKeywordsLow..nimKeywordsHigh,
ord(wInline): return true
else: return false
proc mangleName(s: PSym): PRope =
result = s.loc.r
if result == nil:
@@ -45,9 +54,64 @@ proc mangleName(s: PSym): PRope =
of skTemp, skParam, skType, skEnumField, skModule:
result = toRope("%")
else: InternalError(s.info, "mangleName")
app(result, toRope(mangle(s.name.s)))
app(result, "_")
app(result, toRope(s.id))
when oKeepVariableNames:
let keepOrigName = s.kind in skLocalVars - {skForVar} and
{sfFromGeneric, sfGlobal, sfShadowed} * s.flags == {} and
not isCKeyword(s.name)
# XXX: This is still very experimental
#
# Even with all these inefficient checks, the bootstrap
# time is actually improved. This is probably because so many
# rope concatenations and are now eliminated.
#
# Future notes:
# sfFromGeneric seems to be needed in order to avoid multiple
# definitions of certain varialbes generated in transf with
# names such as:
# `r`, `res`
# I need to study where these come from.
#
# about sfShadowed:
# consider the following nimrod code:
# var x = 10
# block:
# var x = something(x)
# The generated C code will be:
# NI x;
# x = 10;
# {
# NI x;
# x = something(x); // Oops, x is already shadowed here
# }
# Right now, we work-around by not keeping the original name
# of the shadowed variable, but we can do better - we can
# create an alternative reference to it in the outer scope and
# use that in the inner scope.
#
# about isCKeyword:
# nimrod variable names can be C keywords.
# We need to avoid such names in the generated code.
# XXX: Study whether mangleName is called just once per variable.
# Otherwise, there might be better place to do this.
#
# about sfGlobal:
# This seems to be harder - a top level extern variable from
# another modules can have the same name as a local one.
# Maybe we should just implement sfShadowed for them too.
#
# about skForVar:
# These are not properly scoped now - we need to add blocks
# around for loops in transf
if keepOrigName:
result = s.name.s.toRope
else:
app(result, toRope(mangle(s.name.s)))
app(result, "_")
app(result, toRope(s.id))
else:
app(result, toRope(mangle(s.name.s)))
app(result, "_")
app(result, toRope(s.id))
s.loc.r = result
proc isCompileTimeOnly(t: PType): bool =

View File

@@ -113,7 +113,9 @@ var
gKeepComments*: bool = true # whether the parser needs to keep comments
implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
const oKeepVariableNames* = true
proc mainCommandArg*: string =
## This is intended for commands like check or parse
## which will work on the main project file unless

View File

@@ -15,6 +15,8 @@
# So we have to eval templates/macros right here so that symbol
# lookup can be accurate.
# included from sem.nim
type
TSemGenericFlag = enum
withinBind, withinTypeDesc

View File

@@ -216,7 +216,14 @@ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
result = result.sons[1]
elif not sameType(result.typ, typ):
changeType(result, typ)
proc findShadowedVar(c: PContext, v: PSym): PSym =
for i in countdown(c.tab.tos - 2, 0):
let shadowed = StrTableGet(c.tab.stack[i], v.name)
if shadowed != nil:
if shadowed.kind in skLocalVars:
return shadowed
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
if isTopLevel(c):
result = semIdentWithPragma(c, kind, n, {sfExported})
@@ -267,6 +274,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind)
addInterfaceDecl(c, v)
when oKeepVariableNames:
let shadowed = findShadowedVar(c, v)
if shadowed != nil: shadowed.flags.incl(sfShadowed)
if def != nil and def.kind != nkEmpty:
# this is only needed for the evaluation pass:
v.ast = def

View File

@@ -38,8 +38,8 @@ type
wMagic, wThread, wFinal, wProfiler, wObjChecks,
wImmediate, wImportCpp, wImportObjC,
wImportCompilerProc,
wImportc, wExportc, wExtern, wIncompleteStruct,
wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader,
wImportc, wExportc, wIncompleteStruct,
wAlign, wNodecl, wPure, wSideeffect, wHeader,
wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar,
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
@@ -58,13 +58,36 @@ type
wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
wImplicitStatic, wGlobal
wImplicitStatic, wGlobal,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
wExplicit, wExtern, wFalse, wFloat, wFriend,
wGoto, wInt, wLong, wMutable, wNamespace, wNew, wOperator,
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
wUnion, wUnsigned, wUsing, wVirtual, wVoid, wVolatile, wWchar_t,
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
TSpecialWords* = set[TSpecialWord]
const
oprLow* = ord(wColon)
oprHigh* = ord(wDotDot)
nimKeywordsLow* = ord(wAsm)
nimKeywordsHigh* = ord(wYield)
cppKeywordsLow* = ord(wAuto)
cppKeywordsHigh* = ord(wChar32_t)
cppNimSharedKeywords* = {
wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile }
specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["",
"addr", "and", "as", "asm", "atomic",
@@ -86,14 +109,14 @@ const
"magic", "thread", "final", "profiler", "objchecks",
"immediate", "importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "extern", "incompletestruct",
"align", "nodecl", "pure", "volatile", "register", "sideeffect",
"importcompilerproc", "importc", "exportc", "incompletestruct",
"align", "nodecl", "pure", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
"link", "compile", "linksys", "deprecated", "varargs",
"byref", "callconv", "breakpoint", "debugger", "nimcall", "stdcall",
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
"noconv", "on", "off", "checks", "rangechecks", "boundchecks",
"overflowchecks", "nilchecks",
"floatchecks", "nanchecks", "infchecks",
@@ -107,7 +130,22 @@ const
"watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
"nostackframe", "implicitstatic", "global"]
"nostackframe", "implicitstatic", "global",
"auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double",
"dynamic_cast", "explicit", "extern", "false",
"float", "friend", "goto", "int", "long", "mutable",
"namespace", "new", "operator",
"private", "protected", "public", "register", "reinterpret_cast",
"short", "signed", "sizeof", "static_cast", "struct", "switch",
"this", "throw", "true", "typedef", "typeid",
"typename", "union", "unsigned", "using", "virtual", "void", "volatile",
"wchar_t",
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
"thread_local", "static_assert", "char16_t", "char32_t",
]
proc findStr*(a: openarray[string], s: string): int =
for i in countup(low(a), high(a)):