name mangling bugfixes; ndi file generation for debugger support

This commit is contained in:
Andreas Rumpf 2017-02-03 09:49:36 +01:00
commit 848676cec6
6 changed files with 71 additions and 65 deletions

View file

@ -64,7 +64,7 @@ proc genVarTuple(p: BProc, n: PNode) =
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else: else:
if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple") if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)] field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)]
putLocIntoDest(p, v.loc, field) putLocIntoDest(p, v.loc, field)
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false)

View file

@ -22,12 +22,10 @@ proc isKeyword(w: PIdent): bool =
ord(wInline): return true ord(wInline): return true
else: return false else: return false
proc mangleField(name: PIdent): string = proc mangleField(m: BModule; name: PIdent): string =
result = mangle(name.s) result = mangle(name.s)
if isKeyword(name): if isKeyword(name) or m.g.config.cppDefines.contains(result):
result[0] = result[0].toUpperAscii result.add "_0"
# Mangling makes everything lowercase,
# but some identifiers are C keywords
when false: when false:
proc hashOwner(s: PSym): SigHash = proc hashOwner(s: PSym): SigHash =
@ -67,54 +65,10 @@ proc idOrSig(m: BModule; s: PSym): Rope =
proc mangleName(m: BModule; s: PSym): Rope = proc mangleName(m: BModule; s: PSym): Rope =
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:
let keepOrigName = s.kind in skLocalVars - {skForVar} and
{sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
not isKeyword(s.name)
# Even with all these inefficient checks, the bootstrap
# time is actually improved. This is probably because so many
# rope concatenations are now eliminated.
#
# sfFromGeneric is needed in order to avoid multiple
# definitions of certain variables generated in transf with
# names such as:
# `r`, `res`
# I need to study where these come from.
#
# about sfShadowed:
# consider the following Nim 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:
# Nim variable names can be C keywords.
# We need to avoid such names in the generated code.
#
# 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
result = s.name.s.mangle.rope result = s.name.s.mangle.rope
if keepOrigName: add(result, m.idOrSig(s))
result.add "0"
else:
add(result, m.idOrSig(s))
s.loc.r = result s.loc.r = result
writeMangledName(m.ndi, s)
proc mangleParamName(m: BModule; s: PSym): Rope = proc mangleParamName(m: BModule; s: PSym): Rope =
## we cannot use 'sigConflicts' here since we have a BModule, not a BProc. ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
@ -122,10 +76,12 @@ proc mangleParamName(m: BModule; s: PSym): Rope =
## cause any trouble. ## cause any trouble.
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:
result = s.name.s.mangle.rope var res = s.name.s.mangle
if isKeyword(s.name) or m.g.config.cppDefines.contains(s.name.s): if isKeyword(s.name) or m.g.config.cppDefines.contains(res):
result.add "0" res.add "_0"
result = res.rope
s.loc.r = result s.loc.r = result
writeMangledName(m.ndi, s)
proc mangleLocalName(p: BProc; s: PSym): Rope = proc mangleLocalName(p: BProc; s: PSym): Rope =
assert s.kind in skLocalVars+{skTemp} assert s.kind in skLocalVars+{skTemp}
@ -139,10 +95,11 @@ proc mangleLocalName(p: BProc; s: PSym): Rope =
if s.kind == skTemp: if s.kind == skTemp:
# speed up conflict search for temps (these are quite common): # speed up conflict search for temps (these are quite common):
if counter != 0: result.add "_" & rope(counter+1) if counter != 0: result.add "_" & rope(counter+1)
elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(s.name.s): elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
result.add "_" & rope(counter+1) result.add "_" & rope(counter+1)
p.sigConflicts.inc(key) p.sigConflicts.inc(key)
s.loc.r = result s.loc.r = result
if s.kind != skTemp: writeMangledName(p.module.ndi, s)
proc scopeMangledParam(p: BProc; param: PSym) = proc scopeMangledParam(p: BProc; param: PSym) =
## parameter generation only takes BModule, not a BProc, so we have to ## parameter generation only takes BModule, not a BProc, so we have to
@ -472,12 +429,12 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
else: add(params, ")") else: add(params, ")")
params = "(" & params params = "(" & params
proc mangleRecFieldName(field: PSym, rectype: PType): Rope = proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope =
if (rectype.sym != nil) and if (rectype.sym != nil) and
({sfImportc, sfExportc} * rectype.sym.flags != {}): ({sfImportc, sfExportc} * rectype.sym.flags != {}):
result = field.loc.r result = field.loc.r
else: else:
result = rope(mangleField(field.name)) result = rope(mangleField(m, field.name))
if result == nil: internalError(field.info, "mangleRecFieldName") if result == nil: internalError(field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode, proc genRecordFieldsAux(m: BModule, n: PNode,
@ -516,7 +473,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
let field = n.sym let field = n.sym
if field.typ.kind == tyVoid: return if field.typ.kind == tyVoid: return
#assert(field.ast == nil) #assert(field.ast == nil)
let sname = mangleRecFieldName(field, rectype) let sname = mangleRecFieldName(m, field, rectype)
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname] let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
else: sname else: sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown) fillLoc(field.loc, locField, field.typ, ae, OnUnknown)

View file

@ -164,9 +164,6 @@ proc makeSingleLineCString*(s: string): string =
result.add('\"') result.add('\"')
proc mangle*(name: string): string = proc mangle*(name: string): string =
## Lowercases the given name and manges any non-alphanumeric characters
## so they are represented as `HEX____`. If the name starts with a number,
## `N` is prepended
result = newStringOfCap(name.len) result = newStringOfCap(name.len)
var start = 0 var start = 0
if name[0] in Digits: if name[0] in Digits:
@ -179,8 +176,15 @@ proc mangle*(name: string): string =
for i in start..(name.len-1): for i in start..(name.len-1):
let c = name[i] let c = name[i]
case c case c
of 'a'..'z', '0'..'9', 'A'..'Z', '_': of 'a'..'z', '0'..'9', 'A'..'Z':
add(result, c) add(result, c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i < name.len-1 and name[i] in Digits:
discard
else:
add(result, c)
of '$': special "dollar" of '$': special "dollar"
of '%': special "percent" of '%': special "percent"
of '&': special "amp" of '&': special "amp"

View file

@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, semparallel, tables, sets lowerings, semparallel, tables, sets, ndi
import strutils except `%` # collides with ropes.`%` import strutils except `%` # collides with ropes.`%`
@ -1152,6 +1152,9 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
incl result.flags, preventStackTrace incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace) excl(result.preInitProc.options, optStackTrace)
excl(result.postInitProc.options, optStackTrace) excl(result.postInitProc.options, optStackTrace)
let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi")
else: ""
open(result.ndi, ndiName)
proc nullify[T](arr: var T) = proc nullify[T](arr: var T) =
for i in low(arr)..high(arr): for i in low(arr)..high(arr):
@ -1343,6 +1346,7 @@ proc writeModule(m: BModule, pending: bool) =
var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached} if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
addFileToCompile(cf) addFileToCompile(cf)
close(m.ndi)
proc updateCachedModule(m: BModule) = proc updateCachedModule(m: BModule) =
let cfile = getCFile(m) let cfile = getCFile(m)

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, ropes, passes, options, intsets, lists, platform, sighashes, ast, astalgo, ropes, passes, options, intsets, lists, platform, sighashes,
tables tables, ndi
from msgs import TLineInfo from msgs import TLineInfo
@ -146,6 +146,7 @@ type
injectStmt*: Rope injectStmt*: Rope
sigConflicts*: CountTable[SigHash] sigConflicts*: CountTable[SigHash]
g*: BModuleList g*: BModuleList
ndi*: NdiFile
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} = proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block # section in the current block

40
compiler/ndi.nim Normal file
View file

@ -0,0 +1,40 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the generation of ``.ndi`` files for better debugging
## support of Nim code. "ndi" stands for "Nim debug info".
import ast, msgs, ropes
type
NdiFile* = object
enabled: bool
f: File
buf: string
proc doWrite(f: var NdiFile; s: PSym) =
f.buf.setLen 0
f.buf.add s.info.line.int
f.buf.add "\t"
f.buf.add s.info.col.int
f.f.write(s.name.s, "\t")
f.f.writeRope(s.loc.r)
f.f.writeLine("\t", s.info.toFullPath, "\t", f.buf)
template writeMangledName*(f: NdiFile; s: PSym) =
if f.enabled: doWrite(f, s)
proc open*(f: var NdiFile; filename: string) =
f.enabled = filename.len > 0
if f.enabled:
f.f = open(filename, fmWrite, 8000)
f.buf = newStringOfCap(20)
proc close*(f: var NdiFile) =
if f.enabled: close(f.f)