[LLVM][CodeView] Add S_REGREL32_INDIR (#183172)

This adds `RegRelativeIndirSym` (`S_REGREL32_INDIR`) as a record, so we
can emit and dump it (#34392). It encodes a variable at the location
`*($Register+ Offset) + OffsetInUdt` and is used by MSVC in C++ 20
coroutines and C++ 17 structured bindings. Clang also needs this for
coroutines (for `__promise` which has the location `DW_OP_deref,
DW_OP_plus_uconst, 16`).

For example:

```cpp
struct Foo { int a, b; };

void fn() {
  Foo f = {1, 2};
  //  ╰─ S_REGREL32{ reg = rsp, offset = 0 }
  auto &[x, y] = f;
  //     │  ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 4, type = int }
  //     ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 0, type = int }
}
```

The `S_REGREL32_INDIR` for `y` from above looks like this:

```
│ 08000000 │ 74000000 │ 04000000 │ 4F01 │ 7900 │ 
│ Offset   │ Type     │ OffInUdt │ Reg. │ Name │
```

I prototyped support for this in LLDB's native PDB parser to check the
assumption about the location (in a followup PR).

I was wrong in #182743, thinking that the location was just `$Register +
Offset + OffsetInUdt`. That could've been encoded as a `S_REGREL32`.
Presumably, `S_BPREL32_INDIR` works similar, but I can't get MSVC to
generate this.
This commit is contained in:
Nerixyz 2026-03-12 14:04:46 +01:00 committed by GitHub
parent 5e887716b0
commit cacf225ff3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 177 additions and 2 deletions

View File

@ -247,6 +247,42 @@ S_FASTLINK (0x1167)
S_INLINEES (0x1168)
^^^^^^^^^^^^^^^^^^^
S_REGREL32_INDIR (0x1171)
^^^^^^^^^^^^^^^^^^^^^^^^^
This encodes a variable at the location ``*($Register + Offset) + OffsetInUdt``.
It's equivalent to the following DWARF location expression:
.. code::
DW_OP_breg{corresponding DWARF register} {Offset}
DW_OP_deref
DW_OP_plus_uconst {OffsetInUdt}
It's used in C++ 17 structured bindings for example:
.. code:: cpp
struct Foo { int a, b; };
void fn() {
Foo f = {1, 2};
// ╰─ S_REGREL32{ reg = rsp, offset = 0 }
auto &[x, y] = f;
// │ ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 4, type = int }
// ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 0, type = int }
}
The ``S_REGREL32_INDIR`` symbol for ``y`` from above looks like this:
============ ============ ============ ======== ========
Offset Type OffsetInUdt Register Name
============ ============ ============ ======== ========
``08000000`` ``74000000`` ``04000000`` ``4F01`` ``7900``
8 int 4 RSP "a"
============ ============ ============ ======== ========
.. _module_and_global_symbols:
Symbols which can go in either/both of the module info stream & global stream

View File

@ -185,7 +185,7 @@ CV_SYMBOL(S_FASTLINK, 0x1167) // Undocumented (also known to as S_REF_MINIPDB2)
SYMBOL_RECORD_ALIAS(S_INLINEES, 0x1168, InlineesSym, CallerSym) // Undocumented
CV_SYMBOL(S_BPREL32_INDIR, 0x1170)
CV_SYMBOL(S_REGREL32_INDIR, 0x1171)
SYMBOL_RECORD(S_REGREL32_INDIR, 0x1171, RegRelativeIndirSym)
CV_SYMBOL(S_GPROC32EX, 0x1172)
CV_SYMBOL(S_LPROC32EX, 0x1173)
CV_SYMBOL(S_GPROC32EX_ID, 0x1174)

View File

@ -949,6 +949,26 @@ public:
uint32_t RecordOffset = 0;
};
/// S_REGREL32_INDIR
///
/// \p Name is located at `*($Register + Offset) + OffsetInUDT` with type
/// \p Type.
class RegRelativeIndirSym : public SymbolRecord {
public:
explicit RegRelativeIndirSym(SymbolRecordKind Kind) : SymbolRecord(Kind) {}
explicit RegRelativeIndirSym(uint32_t RecordOffset)
: SymbolRecord(SymbolRecordKind::RegRelativeIndirSym),
RecordOffset(RecordOffset) {}
uint32_t Offset = 0;
TypeIndex Type;
uint32_t OffsetInUdt = 0;
RegisterId Register;
StringRef Name;
uint32_t RecordOffset = 0;
};
// S_CONSTANT, S_MANCONSTANT
class ConstantSym : public SymbolRecord {
public:

View File

@ -220,6 +220,7 @@ public:
Error visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) override;
Error visitKnownRecord(CVSymbol &Record, ProcSym &Proc) override;
Error visitKnownRecord(CVSymbol &Record, RegRelativeSym &Local) override;
Error visitKnownRecord(CVSymbol &Record, RegRelativeIndirSym &Local) override;
Error visitKnownRecord(CVSymbol &Record, ScopeEndSym &ScopeEnd) override;
Error visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) override;
Error visitKnownRecord(CVSymbol &Record, UDTSym &UDT) override;

View File

@ -278,8 +278,9 @@ static int getSymbolNameOffset(CVSymbol Sym) {
// See SectionSym
case SymbolKind::S_SECTION:
return 16;
// See CoffGroupSym
// See CoffGroupSym, RegRelativeIndirSym
case SymbolKind::S_COFFGROUP:
case SymbolKind::S_REGREL32_INDIR:
return 14;
// See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym
case SymbolKind::S_PUB32:

View File

@ -620,6 +620,17 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR,
return Error::success();
}
Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR,
RegRelativeIndirSym &RegRelIndir) {
W.printHex("Offset", RegRelIndir.Offset);
printTypeIndex("Type", RegRelIndir.Type);
W.printEnum("Register", uint16_t(RegRelIndir.Register),
getRegisterNames(CompilationCPUType));
W.printHex("OffsetInUdt", RegRelIndir.OffsetInUdt);
W.printString("VarName", RegRelIndir.Name);
return Error::success();
}
Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR,
ThreadLocalDataSym &Data) {
StringRef LinkageName;

View File

@ -444,6 +444,17 @@ Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
return Error::success();
}
Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
RegRelativeIndirSym &RegRelIndir) {
error(IO.mapInteger(RegRelIndir.Offset));
error(IO.mapInteger(RegRelIndir.Type));
error(IO.mapInteger(RegRelIndir.OffsetInUdt));
error(IO.mapEnum(RegRelIndir.Register));
error(IO.mapStringZ(RegRelIndir.Name));
return Error::success();
}
Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
ThreadLocalDataSym &Data) {

View File

@ -402,6 +402,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind,
break;
case SymbolKind::S_BPREL32:
case SymbolKind::S_REGREL32:
case SymbolKind::S_REGREL32_INDIR:
Refs.push_back({TiRefKind::TypeRef, 4, 1}); // Type
break;
case SymbolKind::S_CALLSITEINFO:

View File

@ -906,6 +906,57 @@ Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
return Error::success();
}
// S_REGREL32_INDIR
Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
RegRelativeIndirSym &Local) {
LLVM_DEBUG({
printTypeIndex("Type", Local.Type);
W.printNumber("Offset", Local.Offset);
W.printNumber("OffsetInUdt", Local.OffsetInUdt);
W.printString("VarName", Local.Name);
});
if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
Symbol->setName(Local.Name);
// Symbol was created as 'variable'; determine its real kind.
Symbol->resetIsVariable();
// Check for the 'this' symbol.
if (Local.Name == "this") {
Symbol->setIsArtificial();
Symbol->setIsParameter();
} else {
// Determine symbol kind.
determineSymbolKind(Symbol, Local.Register);
}
// Update correct debug information tag.
if (Symbol->getIsParameter())
Symbol->setTag(dwarf::DW_TAG_formal_parameter);
LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type);
if (Element && Element->getIsScoped()) {
// We have a local type. Find its parent function.
LVScope *Parent = Symbol->getFunctionParent();
// The element representing the type has been already finalized. If
// the type is an aggregate type, its members have been already added.
// As the type is local, its level will be changed.
// FIXME: Currently the algorithm used to scope lambda functions is
// incorrect. Before we allocate the type at this scope, check if is
// already allocated in other scope.
if (!Element->getParentScope()) {
Parent->addElement(Element);
Element->updateLevel(Parent);
}
}
Symbol->setType(Element);
}
return Error::success();
}
// S_BUILDINFO
Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR,
BuildInfoSym &BuildInfo) {
@ -3079,6 +3130,7 @@ LVElement *LVLogicalVisitor::createElement(SymbolKind Kind) {
case SymbolKind::S_BPREL32:
case SymbolKind::S_REGREL32:
case SymbolKind::S_REGREL32_INDIR:
case SymbolKind::S_GDATA32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_LOCAL:

View File

@ -555,6 +555,14 @@ template <> void SymbolRecordImpl<RegRelativeSym>::map(IO &IO) {
IO.mapRequired("VarName", Symbol.Name);
}
template <> void SymbolRecordImpl<RegRelativeIndirSym>::map(IO &IO) {
IO.mapRequired("Offset", Symbol.Offset);
IO.mapRequired("Type", Symbol.Type);
IO.mapRequired("Register", Symbol.Register);
IO.mapRequired("OffsetInUdt", Symbol.OffsetInUdt);
IO.mapRequired("VarName", Symbol.Name);
}
template <> void SymbolRecordImpl<ConstantSym>::map(IO &IO) {
IO.mapRequired("Type", Symbol.Type);
IO.mapRequired("Value", Symbol.Value);

View File

@ -18,4 +18,11 @@ DbiStream:
Type: 4494
Register: RSP
VarName: this
- Kind: S_REGREL32_INDIR
RegRelativeIndirSym:
Offset: 64
Type: 116
Register: RSP
OffsetInUdt: 4
VarName: a
...

View File

@ -9,6 +9,8 @@ CHECK_YAML2PDB: ============================================================
CHECK_YAML2PDB: Mod 0000 | `/tmp/test.obj`:
CHECK_YAML2PDB: 4 | S_REGREL32 [size = 20] `this`
CHECK_YAML2PDB: type = 0x118E (<unknown UDT>), register = RSP, offset = 56
CHECK_YAML2PDB: 24 | S_REGREL32_INDIR [size = 20] `a`
CHECK_YAML2PDB: type = 0x0074 (int), register = RSP, offset = 64, offset-in-udt = 4
CHECK_PDB2YAML: - Kind: S_REGREL32
CHECK_PDB2YAML: RegRelativeSym:
@ -16,3 +18,10 @@ CHECK_PDB2YAML: Offset: 56
CHECK_PDB2YAML: Type: 4494
CHECK_PDB2YAML: Register: RSP
CHECK_PDB2YAML: VarName: this
CHECK_PDB2YAML: - Kind: S_REGREL32_INDIR
CHECK_PDB2YAML: RegRelativeIndirSym:
CHECK_PDB2YAML: Offset: 64
CHECK_PDB2YAML: Type: 116
CHECK_PDB2YAML: Register: RSP
CHECK_PDB2YAML: OffsetInUdt: 4
CHECK_PDB2YAML: VarName: a

View File

@ -920,6 +920,17 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
return Error::success();
}
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
RegRelativeIndirSym &RegRelIndir) {
P.format(" `{0}`", RegRelIndir.Name);
AutoIndent Indent(P, 7);
P.formatLine("type = {0}, register = {1}, offset = {2}, offset-in-udt = {3}",
typeIndex(RegRelIndir.Type),
formatRegisterId(RegRelIndir.Register, CompilationCPU),
RegRelIndir.Offset, RegRelIndir.OffsetInUdt);
return Error::success();
}
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
ThreadLocalDataSym &Data) {
P.format(" `{0}`", Data.Name);

View File

@ -606,3 +606,10 @@ TEST_F(TypeIndexIteratorTest, UsingNamespace) {
writeSymbolRecords(UN);
checkTypeReferences(0);
}
TEST_F(TypeIndexIteratorTest, RegRelativeIndir) {
RegRelativeIndirSym RR(SymbolRecordKind::RegRelativeIndirSym);
RR.Type = TypeIndex::Int32();
writeSymbolRecords(RR);
checkTypeReferences(0, RR.Type);
}