Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/dmd.dwarf-enum-members.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DWARF debug info now includes enum members

The DMD compiler now generates `DW_TAG_enumerator` entries for D enum members in DWARF debug info.
Previously, enum types were always emitted as forward references with no member information.

Debuggers can now inspect enum member names and values for `int`-based enums.
21 changes: 13 additions & 8 deletions compiler/src/dmd/backend/dcgcv.d
Original file line number Diff line number Diff line change
Expand Up @@ -959,14 +959,17 @@ idx_t cv4_struct(Classsym* s,int flags)
return s.Stypidx;
}

/* Write a forward-reference enum record for the linker to fold with
* the full definition emitted by cv4_Denum (via toDebug).
* Params:
* s = enum tag symbol
* bty = CV base type code for the enum's integer base type
*/
@trusted
private uint cv4_fwdenum(type* t)
private uint cv4_fwdenum(Symbol* s, uint bty)
{
Symbol* s = t.Ttag;

// write a forward reference enum record that is enough for the linker to
// fold with original definition from EnumDeclaration
uint bty = dttab4[tybasic(t.Tnext.Tty)];
if (s.Stypidx)
return s.Stypidx;
const id = prettyident(s);
uint len = config.fulltypes == CV8 ? 14 : 10;
debtyp_t* d = debtyp_alloc(len + cv_stringbytes(id));
Expand All @@ -981,7 +984,7 @@ private uint cv4_fwdenum(type* t)
break;

case CV4:
TOWORD(d.data.ptr,LF_ENUM);
TOWORD(d.data.ptr, LF_ENUM);
TOWORD(d.data.ptr + 2, 0); // count
TOWORD(d.data.ptr + 4, bty); // memtype
TOLONG(d.data.ptr + 6, 0); // fieldlist
Expand Down Expand Up @@ -1055,6 +1058,8 @@ uint cv4_typidx(type* t)
if (!t)
return dttab4[TYint]; // assume int
type_debug(t);
if (t.Tflags & TF.denum)
return cv4_fwdenum(t.Ttag, dttab4[tybasic(t.Tty)]);
next = cv4_typidx(t.Tnext);
tycv = t.Tty;
tym = tybasic(tycv);
Expand Down Expand Up @@ -1425,7 +1430,7 @@ else
{
}
else
typidx = cv4_fwdenum(t);
typidx = cv4_fwdenum(t.Ttag, dttab4[tybasic(t.Tnext.Tty)]);
break;

case TYref:
Expand Down
109 changes: 63 additions & 46 deletions compiler/src/dmd/backend/dwarfdbginf.d
Original file line number Diff line number Diff line change
Expand Up @@ -2214,6 +2214,64 @@ static if (1)

/* ======================= Type Index ============================== */

/* Emit DW_TAG_enumeration_type with DW_TAG_enumerator children for the given enum symbol.
* Params:
* s = enum Classsym with SEenumlist populated
* base_type = the integral base type determining size and signedness
* Returns: DWARF type index of the emitted DW_TAG_enumeration_type
*/
uint dwarf_enum_typidx(Symbol* s, type* base_type)
{
if (s.Stypidx)
return s.Stypidx;

uint sz = cast(uint)type_size(base_type);

uint code = DWARFAbbrev.write!([
DW_TAG_enumeration_type, DW_CHILDREN_yes,
DW_AT_name, DW_FORM_string,
DW_AT_byte_size, DW_FORM_data1,
]);

OutBuffer abuf;
abuf.writeByte(DW_TAG_enumerator);
abuf.writeByte(DW_CHILDREN_no);
abuf.writeByte(DW_AT_name);
abuf.writeByte(DW_FORM_string);
abuf.writeByte(DW_AT_const_value);
if (tyuns(base_type.Tty))
abuf.writeByte(DW_FORM_udata);
else
abuf.writeByte(DW_FORM_sdata);
abuf.writeByte(0);
abuf.writeByte(0);
uint membercode = dwarf_abbrev_code(abuf.buf, abuf.length());

uint idx = cast(uint)debug_info.buf.length();
debug_info.buf.writeuLEB128(code);
debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name
debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size

foreach (sl2; ListRange(s.Senum.SEenumlist))
{
Symbol* sf = cast(Symbol*)list_ptr(sl2);
const value = cast(uint)el_tolong(sf.Svalue);

debug_info.buf.writeuLEB128(membercode);
debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name
if (tyuns(base_type.Tty))
debug_info.buf.writeuLEB128(value);
else
debug_info.buf.writesLEB128(value);
}

debug_info.buf.writeByte(0); // no more children

s.Stypidx = idx;
resetSyms.push(s);
return idx;
}

uint dwarf_typidx(type* t, Symbol* sym = null)
{
uint idx = 0;
Expand Down Expand Up @@ -2340,6 +2398,10 @@ static if (1)
}
}

// D enum: stored as base type for ABI, but has enum members attached for debug info
if (t.Tflags & TF.denum)
return dwarf_enum_typidx(t.Ttag, t);

immutable tym_t ty = tybasic(t.Tty);
// use cached basic type if it's not TYdarray or TYdelegate
if (!(t.Tnext && (ty == TYdarray || ty == TYdelegate)))
Expand Down Expand Up @@ -2988,8 +3050,6 @@ static if (1)
Symbol* s = t.Ttag;
enum_t* se = s.Senum;
type* tbase2 = s.Stype.Tnext;
uint sz = cast(uint)type_size(tbase2);
symlist_t sl;

if (s.Stypidx)
return s.Stypidx;
Expand All @@ -3008,50 +3068,7 @@ static if (1)
break; // don't set Stypidx
}

code = DWARFAbbrev.write!([
DW_TAG_enumeration_type, DW_CHILDREN_yes, // child (the subrange type)
DW_AT_name, DW_FORM_string,
DW_AT_byte_size, DW_FORM_data1,
]);

uint membercode;
OutBuffer abuf;
abuf.writeByte(DW_TAG_enumerator);
abuf.writeByte(DW_CHILDREN_no);
abuf.writeByte(DW_AT_name);
abuf.writeByte(DW_FORM_string);
abuf.writeByte(DW_AT_const_value);
if (tyuns(tbase2.Tty))
abuf.writeByte(DW_FORM_udata);
else
abuf.writeByte(DW_FORM_sdata);
abuf.writeByte(0);
abuf.writeByte(0);
membercode = dwarf_abbrev_code(abuf.buf, abuf.length());

idx = cast(uint)debug_info.buf.length();
debug_info.buf.writeuLEB128(code);
debug_info.buf.writeStringz(getSymName(s)); // DW_AT_name
debug_info.buf.writeByte(cast(ubyte)sz); // DW_AT_byte_size

foreach (sl2; ListRange(s.Senum.SEenumlist))
{
Symbol* sf = cast(Symbol*)list_ptr(sl2);
const value = cast(uint)el_tolong(sf.Svalue);

debug_info.buf.writeuLEB128(membercode);
debug_info.buf.writeStringz(getSymName(sf)); // DW_AT_name
if (tyuns(tbase2.Tty))
debug_info.buf.writeuLEB128(value);
else
debug_info.buf.writesLEB128(value);
}

debug_info.buf.writeByte(0); // no more children

s.Stypidx = idx;
resetSyms.push(s);
return idx; // no need to cache it
return dwarf_enum_typidx(s, tbase2);
}

default:
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/backend/type.d
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum TF : ushort
static_ = 0x40, // TYarray: static dimension
vla = 0x80, // TYarray: variable length array
emptyexc = 0x100, // tyfunc(): empty exception specification
denum = 0x200, // D enum: base type for ABI but Ttag points to enum Classsym for debug info
}

public import dmd.backend.symbol : symbol_struct_addField, symbol_struct_addBitField, symbol_struct_hasBitFields, symbol_struct_addBaseClass;
Expand Down
48 changes: 42 additions & 6 deletions compiler/src/dmd/glue/toctype.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ module dmd.glue.toctype;
import core.stdc.stdio;
import core.stdc.stdlib;

import dmd.backend.cc : Classsym, Symbol;
import dmd.backend.cc : Classsym, enum_t, Symbol;
import dmd.backend.cdef : SC;
import dmd.backend.dlist : list_append;
import dmd.backend.el : el_long;
import dmd.backend.mem : mem_calloc;
import dmd.backend.symbol : symbol_calloc, symbol_name;
import dmd.backend.ty;
import dmd.backend.type;

Expand Down Expand Up @@ -253,9 +258,39 @@ type* Type_toCtype(Type t)
(cast(type*)t.ctype).Tcount++;
return cast(type*)t.ctype;
}
else if (symMemtype.toBasetype().ty == Tint32)
else if (symMemtype.isIntegral())
{
t.ctype = type_enum(sym.toPrettyChars(true), Type_toCtype(symMemtype));
type* basectype = Type_toCtype(symMemtype);
if (driverParams.symdebug && sym.members)
{
// Use the base type for ABI (avoids TYenum hardcoded-to-4-bytes issues),
// but attach a D enum Classsym via TF.denum so the DWARF backend can
// emit DW_TAG_enumeration_type with member names and values.
type* enumctype = type_alloc(tybasic(basectype.Tty));
enumctype.Tcount++;
enumctype.Tflags = cast(TF)(basectype.Tflags | TF.denum);
import core.stdc.string : strlen;
const name = sym.toPrettyChars(true);
Symbol* s = symbol_calloc(name[0 .. strlen(name)]);
s.Senum = cast(enum_t*)mem_calloc(enum_t.sizeof);
s.Sclass = SC.enum_;
s.Stype = enumctype;
enumctype.Ttag = s;
foreach (m; *sym.members)
{
EnumMember em = m.isEnumMember();
if (!em)
continue;
Symbol* sf = symbol_name(em.ident.toString(), SC.enum_, basectype);
sf.Svalue = el_long(totym(symMemtype.toBasetype()), em.value().toInteger());
list_append(&s.Senum.SEenumlist, sf);
}
t.ctype = enumctype;
}
else
{
t.ctype = basectype;
}
}
else
{
Expand All @@ -270,12 +305,13 @@ type* Type_toCtype(Type t)

// Copy mutable version of backend type and add modifiers
type* mctype = Type_toCtype(t.castMod(0));
if (tybasic(mctype.Tty) == TYenum)
if (mctype.Tflags & TF.denum)
{
Classsym* s = mctype.Ttag;
assert(s);
type* tr = type_allocn(TYenum, mctype.Tnext);
tr.Ttag = s; // enum tag name
type* tr = type_alloc(tybasic(mctype.Tty));
tr.Tflags = mctype.Tflags;
tr.Ttag = s;
tr.Tcount++;
tr.Tty |= modToTym(t.mod);
return tr;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
DW_TAG_enumeration_type
DW_AT_name : issue20004.Color
DW_TAG_enumerator
DW_AT_name : red
DW_AT_const_value : 0
DW_AT_name : green
DW_AT_const_value : 1
DW_AT_name : blue
DW_AT_const_value : 2
DW_AT_name : issue20004.Small
DW_AT_byte_size : 1
DW_AT_name : a
DW_AT_const_value : 10
DW_AT_name : b
DW_AT_const_value : 20
DW_AT_name : issue20004.Big
DW_AT_byte_size : 8
DW_AT_name : x
DW_AT_const_value : 1
DW_AT_name : y
DW_AT_const_value : 1000000
8 changes: 8 additions & 0 deletions compiler/test/dshell/extra-files/dwarf/issue20004.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enum Color { red, green, blue }
enum Small : ubyte { a = 10, b = 20 }
enum Big : ulong { x = 1, y = 1000000 }
void main() {
Color c = Color.red;
Small s = Small.a;
Big b = Big.y;
}
Loading