mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-03 18:34:43 +00:00
better debugging support for native debuggers; changed name mangling; fixes #3471
This commit is contained in:
@@ -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)
|
||||
|
||||
89
compiler/debuginfo.nim
Normal file
89
compiler/debuginfo.nim
Normal file
@@ -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..<package.len:
|
||||
result &= package[i]
|
||||
result &= '.'
|
||||
for i in 0..<file.len:
|
||||
result &= file[i]
|
||||
|
||||
proc register*(self; package, file): FilenameHash =
|
||||
result = sdbmHash(package, file)
|
||||
for f in files:
|
||||
if f.mangled == result:
|
||||
if f.package == package and f.file == file: return
|
||||
conflicts = true
|
||||
break
|
||||
files.add(FilenameMapping(package: package, file: file, mangled: result))
|
||||
|
||||
proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: FilenameHash): bool =
|
||||
for en in enums:
|
||||
if en.owner == owner and en.name == ename and en.id == id: return true
|
||||
|
||||
proc registerEnum*(self; ed: EnumDesc) =
|
||||
enums.add ed
|
||||
|
||||
proc init*(self) =
|
||||
version = 1
|
||||
files = @[]
|
||||
enums = @[]
|
||||
|
||||
var gDebugInfo*: DebugInfo
|
||||
debuginfo.init gDebugInfo
|
||||
|
||||
import marshal, streams
|
||||
|
||||
proc writeDebugInfo*(self; file) =
|
||||
let s = newFileStream(file, fmWrite)
|
||||
store(s, self)
|
||||
s.close
|
||||
|
||||
proc writeDebugInfo*(file) = writeDebugInfo(gDebugInfo, file)
|
||||
|
||||
proc loadDebugInfo*(self; file) =
|
||||
let s = newFileStream(file, fmRead)
|
||||
load(s, self)
|
||||
s.close
|
||||
|
||||
proc loadDebugInfo*(file) = loadDebugInfo(gDebugInfo, file)
|
||||
@@ -16,6 +16,8 @@ import
|
||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
|
||||
securehash, streams
|
||||
|
||||
from debuginfo import writeDebugInfo
|
||||
|
||||
type
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||
@@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) =
|
||||
if not noAbsolutePaths():
|
||||
if not exefile.isAbsolute():
|
||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||
if optCDebug in gGlobalOptions:
|
||||
writeDebugInfo(exefile.changeFileExt("ndb"))
|
||||
exefile = quoteShell(exefile)
|
||||
let linkOptions = getLinkOptions() & " " &
|
||||
getConfigVar(cCompiler, ".options.linker")
|
||||
|
||||
Reference in New Issue
Block a user