From b654aa399acf8e935bc83d58d74f2269b74f36b7 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 10 May 2016 22:02:56 +0200 Subject: [PATCH] better debugging support for native debuggers; changed name mangling; fixes #3471 --- compiler/ccgtypes.nim | 129 +++++++++++++++++++++++------------------ compiler/debuginfo.nim | 89 ++++++++++++++++++++++++++++ compiler/extccomp.nim | 4 ++ 3 files changed, 164 insertions(+), 58 deletions(-) create mode 100644 compiler/debuginfo.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index d7a5debdca..da671940a9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2016 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -11,6 +11,8 @@ # ------------------------- Name Mangling -------------------------------- +import debuginfo + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -26,67 +28,66 @@ proc mangleField(name: PIdent): string = result[0] = result[0].toUpper # Mangling makes everything lowercase, # but some identifiers are C keywords +proc hashOwner(s: PSym): FilenameHash = + var m = s + while m.kind != skModule: m = m.owner + let p = m.owner + assert p.kind == skPackage + result = gDebugInfo.register(p.name.s, m.name.s) + proc mangleName(s: PSym): Rope = result = s.loc.r if result == nil: - when oKeepVariableNames: - let keepOrigName = s.kind in skLocalVars - {skForVar} and - {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and - not isKeyword(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 are now eliminated. - # - # Future notes: - # sfFromGeneric seems to be 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. - # 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.mangle.rope - else: - add(result, rope(mangle(s.name.s))) - add(result, ~"_") - add(result, rope(s.id)) + 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 + if keepOrigName: + result.add "0" else: - add(result, rope(mangle(s.name.s))) add(result, ~"_") add(result, rope(s.id)) + add(result, ~"_") + add(result, rope(hashOwner(s).BiggestInt)) s.loc.r = result proc typeName(typ: PType): Rope = @@ -570,15 +571,27 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = let t = if t.kind == tyRange: t.lastSon else: t result = getTypeName(t) idTablePut(m.typeCache, t, result) + var size: int if firstOrd(t) < 0: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) + size = 4 else: - case int(getSize(typ)) + size = int(getSize(t)) + case size of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result]) of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) - else: internalError(t.sym.info, "getTypeDescAux: " & $getSize(t)) + else: internalError(t.sym.info, "getTypeDescAux: enum") + let owner = hashOwner(t.sym) + if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner): + var vals: seq[(string, int)] = @[] + for i in countup(0, t.n.len - 1): + assert(t.n.sons[i].kind == nkSym) + let field = t.n.sons[i].sym + vals.add((field.name.s, field.position.int)) + gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id, + name: t.sym.name.s, values: vals)) of tyProc: result = getTypeName(t) idTablePut(m.typeCache, t, result) diff --git a/compiler/debuginfo.nim b/compiler/debuginfo.nim new file mode 100644 index 0000000000..f30de2893c --- /dev/null +++ b/compiler/debuginfo.nim @@ -0,0 +1,89 @@ +# +# +# The Nim Compiler +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code +## to Nim. The data structure has been designed to produce something useful with Nim's marshal +## module. + +type + FilenameHash* = uint32 + FilenameMapping* = object + package*, file*: string + mangled*: FilenameHash + EnumDesc* = object + size*: int + owner*: FilenameHash + id*: int + name*: string + values*: seq[(string, int)] + DebugInfo* = object + version*: int + files*: seq[FilenameMapping] + enums*: seq[EnumDesc] + conflicts*: bool + +{.experimental.} + +using + self: var DebugInfo + package, file: string + +{.this: self.} + +proc sdbmHash(hash: FilenameHash, c: char): FilenameHash {.inline.} = + return FilenameHash(c) + (hash shl 6) + (hash shl 16) - hash + +proc sdbmHash(package, file): FilenameHash = + template `&=`(x, c) = x = sdbmHash(x, c) + result = 0 + for i in 0..