Skip to content

Commit

Permalink
MangleProcs following the Itanium spec so they are demangled in the d…
Browse files Browse the repository at this point in the history
…ebugger call stack (nim-lang#23260)

![image](https://github.com/nim-lang/Nim/assets/1496571/0d796c5b-0bbf-4bb4-8c95-c3e3cce22f15)

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
  • Loading branch information
jmgomez and Araq authored Feb 9, 2024
1 parent c234a2a commit a45f43d
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 6 deletions.
28 changes: 23 additions & 5 deletions compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,31 @@ proc mangleField(m: BModule; name: PIdent): string =
if isKeyword(name):
result.add "_0"

proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
result = "_Z" # Common prefix in Itanium ABI
result.add encodeSym(m, s, makeUnique)
if s.typ.len > 1: #we dont care about the return param
for i in 1..<s.typ.len:
if s.typ[i].isNil: continue
result.add encodeType(m, s.typ[i])

if result in m.g.mangledPrcs:
result = mangleProc(m, s, true)
else:
m.g.mangledPrcs.incl(result)

proc fillBackendName(m: BModule; s: PSym) =
if s.loc.r == "":
var result = s.name.s.mangle.rope
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.addInt s.itemId.item # s.disamb #
var result: Rope
if s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
m.g.config.symbolFiles == disabledSf:
result = mangleProc(m, s, false).rope
else:
result = s.name.s.mangle.rope
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.addInt s.itemId.item # s.disamb #
if m.hcrOn:
result.add '_'
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
Expand Down
61 changes: 60 additions & 1 deletion compiler/ccgutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata

import std/[hashes, strutils]
import std/[hashes, strutils, formatFloat]

when defined(nimPreviewSlimSystem):
import std/assertions
Expand Down Expand Up @@ -153,3 +153,62 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
pt.kind == tySet and mapSetType(conf, pt) == ctArray)

proc encodeName*(name: string): string =
result = mangle(name)
result = $result.len & result

proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
result = if name == "": s.name.s else: name
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.add $s.itemId.item

proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
#Module::Type
var name = s.name.s
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.owner.name.s) & encodeName(name) & "E"

proc encodeType*(m: BModule; t: PType): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
case t.kind
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
result = encodeSym(m, t.sym)
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i])
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
else: encodeName(kindName)
result.add "I"
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s)
result.add "E"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
val.addFloat t.n[0].floatVal
val.add "_"
val.addFloat t.n[1].floatVal
else:
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
result = encodeName(val)
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType)
else:
assert false, "encodeType " & $t.kind

1 change: 1 addition & 0 deletions compiler/cgendata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ type
# unconditionally...
# nimtvDeps is VERY hard to cache because it's
# not a list of IDs nor can it be made to be one.
mangledPrcs*: HashSet[string]

TCGen = object of PPassContext # represents a C source file
s*: TCFileSections # sections of the C file
Expand Down
192 changes: 192 additions & 0 deletions tests/codegen/titaniummangle.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
discard """
targets: "c cpp"
matrix: "--debugger:native"
ccodecheck: "'_ZN14titaniummangle8testFuncE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE6stringN14titaniummangle3FooE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3int7varargsI6stringE'"
ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle3BooE'"
ccodecheck: "'_ZN8testFunc8testFuncE8typeDescIN14titaniummangle17EnumAnotherSampleEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI14uncheckedArrayI3intEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3setIN14titaniummangle10EnumSampleEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE4procI6string6stringE'"
ccodecheck: "'_ZN8testFunc8testFuncE3intN10Comparable10ComparableE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI3int3intE7cstring'"
ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI5float5floatE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3intE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrIN14titaniummangle3FooEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3ptrI3intEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3refIN14titaniummangle3FooEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3varIN14titaniummangle3FooEE5int325int323refIN14titaniummangle3FooEE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE3varI3intE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE9openArrayI6stringE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE5arrayI7range013intE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI3intE'"
ccodecheck: "'_ZN14titaniummangle8testFuncE10Container2I5int325int32E'"
ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI10Container2I5int325int32EE'"
"""

#When debugging this notice that if one check fails, it can be due to any of the above.

type
Comparable = concept x, y
(x < y) is bool

type
Foo = object
a: int32
b: int32

FooTuple = tuple
a: int
b: int
Container[T] = object
data: T
Container2[T, T2] = object
data: T
data2: T2

Boo = distinct Foo

Coo = Foo

Doo = Boo | Foo

TestProc = proc(a:string): string

type EnumSample = enum
a, b, c

type EnumAnotherSample = enum
a, b, c

proc testFunc(a: set[EnumSample]) =
echo $a

proc testFunc(a: typedesc) =
echo $a

proc testFunc(a: ptr Foo) =
echo repr a

proc testFunc(s: string, a: Coo) =
echo repr a

proc testFunc(s: int, a: Comparable) =
echo repr a

proc testFunc(a: TestProc) =
let b = ""
echo repr a("")

proc testFunc(a: ref Foo) =
echo repr a

proc testFunc(b: Boo) =
echo repr b

proc testFunc(a: ptr UncheckedArray[int]) =
echo repr a

proc testFunc(a: ptr int) =
echo repr a

proc testFunc(a: ptr ptr int) =
echo repr a

proc testFunc(e: FooTuple, str: cstring) =
echo e

proc testFunc(e: (float, float)) =
echo e

proc testFunc(e: EnumSample) =
echo e

proc testFunc(e: var int) =
echo e

proc testFunc(e: var Foo, a, b: int32, refFoo: ref Foo) =
echo e

proc testFunc(xs: Container[int]) =
let a = 2
echo xs

proc testFunc(xs: Container2[int32, int32]) =
let a = 2
echo xs

proc testFunc(xs: Container[Container2[int32, int32]]) =
let a = 2
echo xs

proc testFunc(xs: seq[int]) =
let a = 2
echo xs

proc testFunc(xs: openArray[string]) =
let a = 2
echo xs

proc testFunc(xs: array[2, int]) =
let a = 2
echo xs

proc testFunc(e: EnumAnotherSample) =
echo e

proc testFunc(a, b: int) =
echo "hola"
discard

proc testFunc(a: int, xs: varargs[string]) =
let a = 10
for x in xs:
echo x

proc testFunc() =
var a = 2
var aPtr = a.addr
var foo = Foo()
let refFoo : ref Foo = new(Foo)
let b = Foo().Boo()
let d: Doo = Foo()
testFunc("", Coo())
testFunc(1, )
testFunc(b)
testFunc(EnumAnotherSample)
var t = [1, 2]
let uArr = cast[ptr UncheckedArray[int]](t.addr)
testFunc(uArr)
testFunc({})
testFunc(proc(s:string): string = "test")
testFunc(20, a.int32)
testFunc(20, 2)
testFunc(EnumSample.c)
testFunc(EnumAnotherSample.c)
testFunc((2, 1), "adios")
testFunc((22.1, 1.2))
testFunc(a.addr)
testFunc(foo.addr)
testFunc(aPtr.addr)
testFunc(refFoo)
testFunc(foo, 2, 1, refFoo)
testFunc(a)
testFunc(@[2, 1, 2])
testFunc(@["hola"])
testFunc(2, "hola", "adios")
let arr: array[2, int] = [2, 1]
testFunc(arr)
testFunc(Container[int](data: 10))
let c2 = Container2[int32, int32](data: 10, data2: 20)
testFunc(c2)
testFunc(Container[Container2[int32, int32]](data: c2))


testFunc()

0 comments on commit a45f43d

Please sign in to comment.