diff --git a/cross-project-tests/debuginfo-tests/dexter-tests/nrvo.cpp b/cross-project-tests/debuginfo-tests/dexter-tests/nrvo.cpp index 42e038e7d41a..bcb6d9b0b1d1 100644 --- a/cross-project-tests/debuginfo-tests/dexter-tests/nrvo.cpp +++ b/cross-project-tests/debuginfo-tests/dexter-tests/nrvo.cpp @@ -1,7 +1,7 @@ // This ensures that DW_OP_deref is inserted when necessary, such as when NRVO // of a string object occurs in C++. // -// REQUIRES: system-windows +// REQUIRES: system-windows, dbgeng-10-19041 // // RUN: %clang_cl /Z7 /Zi %s -o %t // RUN: %dexter --fail-lt 1.0 -w --binary %t --debugger 'dbgeng' -- %s @@ -36,5 +36,5 @@ int main() { get_string2(); } -// DexExpectWatchValue('result->i', 3, on_line=ref('readresult1')) -// DexExpectWatchValue('result->i', 5, on_line=ref('readresult2')) +// DexExpectWatchValue('result.i', 3, on_line=ref('readresult1')) +// DexExpectWatchValue('result.i', 5, on_line=ref('readresult2')) diff --git a/cross-project-tests/lit.cfg.py b/cross-project-tests/lit.cfg.py index 845965beec21..c8d961aaac8d 100644 --- a/cross-project-tests/lit.cfg.py +++ b/cross-project-tests/lit.cfg.py @@ -401,3 +401,41 @@ llvm_config.feature_config([("--build-mode", {"Debug|RelWithDebInfo": "debug-inf # Allow 'REQUIRES: XXX-registered-target' in tests. for arch in config.targets_to_build: config.available_features.add(arch.lower() + "-registered-target") + + +def find_dbgeng(): + if platform.system() != "Windows": + return None + + for path in os.environ.get("PATH", "").split(os.pathsep): + p = os.path.join(path, "dbgeng.dll") + if os.path.exists(p) and not os.path.isdir(p): + return os.path.abspath(p) + + return None + + +def get_dbgeng_version(): + dbgeng = find_dbgeng() + if not dbgeng: + return None + + try: + import win32api + except: + return None + + info = win32api.GetFileVersionInfo(dbgeng, "\\") + ms = info["FileVersionMS"] + ls = info["FileVersionLS"] + return ( + win32api.HIWORD(ms), + win32api.LOWORD(ms), + win32api.HIWORD(ls), + win32api.LOWORD(ls), + ) + + +dbgeng_version = get_dbgeng_version() +if dbgeng_version and dbgeng_version >= (10, 0, 19041, 0): + config.available_features.add("dbgeng-10-19041") diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index e28b47d87d4e..24619d44f6d3 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -3662,6 +3662,10 @@ public: LLVM_ABI static void appendOffset(SmallVectorImpl &Ops, int64_t Offset); + LLVM_ABI static bool + extractLeadingOffset(ArrayRef Ops, int64_t &OffsetInBytes, + SmallVectorImpl &RemainingOps); + /// If this is a constant offset, extract it. If there is no expression, /// return true with an offset of zero. LLVM_ABI bool extractIfOffset(int64_t &Offset) const; diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 254220f0e4f9..0a27eb7b104d 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -62,6 +62,7 @@ struct DefRangeRegisterRelHeader; struct DefRangeSubfieldRegisterHeader; struct DefRangeRegisterHeader; struct DefRangeFramePointerRelHeader; +struct DefRangeRegisterRelIndirHeader; } using MCSectionSubPair = std::pair; @@ -978,6 +979,10 @@ public: ArrayRef> Ranges, codeview::DefRangeFramePointerRelHeader DRHdr); + virtual void emitCVDefRangeDirective( + ArrayRef> Ranges, + codeview::DefRangeRegisterRelIndirHeader DRHdr); + /// This implements the CodeView '.cv_stringtable' assembler directive. virtual void emitCVStringTableDirective() {} diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 4b6b76c48dcb..dc35405d92a2 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -1274,7 +1274,8 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV, } CodeViewDebug::LocalVarDef -CodeViewDebug::createDefRangeMem(uint16_t CVRegister, int Offset) { +CodeViewDebug::createDefRangeMem(uint16_t CVRegister, int Offset, + int32_t DerefOffset) { LocalVarDef DR; DR.InMemory = -1; DR.DataOffset = Offset; @@ -1282,6 +1283,7 @@ CodeViewDebug::createDefRangeMem(uint16_t CVRegister, int Offset) { DR.IsSubfield = 0; DR.StructOffset = 0; DR.CVRegister = CVRegister; + DR.DerefOffset = DerefOffset; return DR; } @@ -1307,16 +1309,23 @@ void CodeViewDebug::collectVariableInfoFromMFTable( continue; // If the variable has an attached offset expression, extract it. - // FIXME: Try to handle DW_OP_deref as well. int64_t ExprOffset = 0; - bool Deref = false; + int64_t DerefOffset = LocalVarDef::NoDeref; if (VI.Expr) { - // If there is one DW_OP_deref element, use offset of 0 and keep going. - if (VI.Expr->getNumElements() == 1 && - VI.Expr->getElement(0) == llvm::dwarf::DW_OP_deref) - Deref = true; - else if (!VI.Expr->extractIfOffset(ExprOffset)) + SmallVector FirstRemaining; + if (!VI.Expr->extractLeadingOffset(ExprOffset, FirstRemaining)) continue; + if (!FirstRemaining.empty()) { + if (FirstRemaining.front() != dwarf::DW_OP_deref) + continue; + SmallVector LastRemaining; + if (!DIExpression::extractLeadingOffset( + ArrayRef(FirstRemaining).drop_front(), DerefOffset, + LastRemaining)) + continue; + if (!LastRemaining.empty()) + continue; + } } // Get the frame register used and the offset. @@ -1329,10 +1338,13 @@ void CodeViewDebug::collectVariableInfoFromMFTable( // No encoding currently exists for scalable offsets; bail out. continue; } + if (DerefOffset < INT32_MIN || DerefOffset > INT32_MAX) + continue; // Calculate the label ranges. LocalVarDef DefRange = - createDefRangeMem(CVReg, FrameOffset.getFixed() + ExprOffset); + createDefRangeMem(CVReg, FrameOffset.getFixed() + ExprOffset, + static_cast(DerefOffset)); LocalVariable Var; Var.DIVar = VI.Var; @@ -1344,21 +1356,10 @@ void CodeViewDebug::collectVariableInfoFromMFTable( Var.DefRanges[DefRange].emplace_back(Begin, End); } - if (Deref) - Var.UseReferenceType = true; - recordLocalVariable(std::move(Var), Scope); } } -static bool canUseReferenceType(const DbgVariableLocation &Loc) { - return !Loc.LoadChain.empty() && Loc.LoadChain.back() == 0; -} - -static bool needsReferenceType(const DbgVariableLocation &Loc) { - return Loc.LoadChain.size() == 2 && Loc.LoadChain.back() == 0; -} - void CodeViewDebug::calculateRanges( LocalVariable &Var, const DbgValueHistoryMap::Entries &Entries) { const TargetRegisterInfo *TRI = Asm->MF->getSubtarget().getRegisterInfo(); @@ -1387,30 +1388,9 @@ void CodeViewDebug::calculateRanges( continue; } - // CodeView can only express variables in register and variables in memory - // at a constant offset from a register. However, for variables passed - // indirectly by pointer, it is common for that pointer to be spilled to a - // stack location. For the special case of one offseted load followed by a - // zero offset load (a pointer spilled to the stack), we change the type of - // the local variable from a value type to a reference type. This tricks the - // debugger into doing the load for us. - if (Var.UseReferenceType) { - // We're using a reference type. Drop the last zero offset load. - if (canUseReferenceType(*Location)) - Location->LoadChain.pop_back(); - else - continue; - } else if (needsReferenceType(*Location)) { - // This location can't be expressed without switching to a reference type. - // Start over using that. - Var.UseReferenceType = true; - Var.DefRanges.clear(); - calculateRanges(Var, Entries); - return; - } - - // We can only handle a register or an offseted load of a register. - if (!Location->Register || Location->LoadChain.size() > 1) + // We can only handle a register, an offsetted load of a register, or an + // indirect offsetted load. + if (!Location->Register || Location->LoadChain.size() > 2) continue; // Codeview can only express byte-aligned offsets, ensure that we have a @@ -1427,8 +1407,13 @@ void CodeViewDebug::calculateRanges( LocalVarDef DR; DR.CVRegister = TRI->getCodeViewRegNum(Location->Register); DR.InMemory = !Location->LoadChain.empty(); - DR.DataOffset = - !Location->LoadChain.empty() ? Location->LoadChain.back() : 0; + DR.DataOffset = 0; + DR.DerefOffset = LocalVarDef::NoDeref; + if (!Location->LoadChain.empty()) { + DR.DataOffset = Location->LoadChain[0]; + if (Location->LoadChain.size() >= 2) + DR.DerefOffset = Location->LoadChain[1]; + } if (Location->FragmentInfo) { DR.IsSubfield = true; DR.StructOffset = Location->FragmentInfo->OffsetInBits / 8; @@ -2743,15 +2728,6 @@ CodeViewDebug::getTypeIndexForThisPtr(const DIDerivedType *PtrTy, return recordTypeIndexForDINode(PtrTy, TI, SubroutineTy); } -TypeIndex CodeViewDebug::getTypeIndexForReferenceTo(const DIType *Ty) { - PointerRecord PR(getTypeIndex(Ty), - getPointerSizeInBytes() == 8 ? PointerKind::Near64 - : PointerKind::Near32, - PointerMode::LValueReference, PointerOptions::None, - Ty->getSizeInBits() / 8); - return TypeTable.writeLeafType(PR); -} - TypeIndex CodeViewDebug::getCompleteTypeIndex(const DIType *Ty) { // The null DIType is the void type. Don't try to hash it. if (!Ty) @@ -2877,9 +2853,7 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI, Flags |= LocalSymFlags::IsOptimizedOut; OS.AddComment("TypeIndex"); - TypeIndex TI = Var.UseReferenceType - ? getTypeIndexForReferenceTo(Var.DIVar->getType()) - : getCompleteTypeIndex(Var.DIVar->getType()); + TypeIndex TI = getCompleteTypeIndex(Var.DIVar->getType()); OS.emitInt32(TI.getIndex()); OS.AddComment("Flags"); OS.emitInt16(static_cast(Flags)); @@ -2907,14 +2881,28 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI, Offset += FI.OffsetAdjustment; } - // If we can use the chosen frame pointer for the frame and this isn't a - // sliced aggregate, use the smaller S_DEFRANGE_FRAMEPOINTER_REL record. - // Otherwise, use S_DEFRANGE_REGISTER_REL. EncodedFramePtrReg EncFP = encodeFramePtrReg(RegisterId(Reg), TheCPU); - if (!DefRange.IsSubfield && EncFP != EncodedFramePtrReg::None && - (bool(Flags & LocalSymFlags::IsParameter) - ? (EncFP == FI.EncodedParamFramePtrReg) - : (EncFP == FI.EncodedLocalFramePtrReg))) { + + if (DefRange.DerefOffset != LocalVarDef::NoDeref) { + uint16_t RegRelFlags = 0; + if (DefRange.IsSubfield) { + RegRelFlags = DefRangeRegisterRelSym::IsSubfieldFlag | + (DefRange.StructOffset + << DefRangeRegisterRelSym::OffsetInParentShift); + } + DefRangeRegisterRelIndirHeader DRHdr; + DRHdr.Register = Reg; + DRHdr.Flags = RegRelFlags; + DRHdr.BasePointerOffset = Offset; + DRHdr.OffsetInUdt = DefRange.DerefOffset; + OS.emitCVDefRangeDirective(Ranges, DRHdr); + } else if (!DefRange.IsSubfield && EncFP != EncodedFramePtrReg::None && + (bool(Flags & LocalSymFlags::IsParameter) + ? (EncFP == FI.EncodedParamFramePtrReg) + : (EncFP == FI.EncodedLocalFramePtrReg))) { + // If we can use the chosen frame pointer for the frame and this isn't a + // sliced aggregate, use the smaller S_DEFRANGE_FRAMEPOINTER_REL record. + // Otherwise, use S_DEFRANGE_REGISTER_REL. DefRangeFramePointerRelHeader DRHdr; DRHdr.Offset = Offset; OS.emitCVDefRangeDirective(Ranges, DRHdr); @@ -2932,7 +2920,9 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI, OS.emitCVDefRangeDirective(Ranges, DRHdr); } } else { - assert(DefRange.DataOffset == 0 && "unexpected offset into register"); + assert(DefRange.DataOffset == 0 && + DefRange.DerefOffset == LocalVarDef::NoDeref && + "unexpected offset into register"); if (DefRange.IsSubfield) { DefRangeSubfieldRegisterHeader DRHdr; DRHdr.Register = DefRange.CVRegister; diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h index 7fd2cec8c74f..b4dcadde18e8 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -72,21 +72,43 @@ public: /// location containing the data. uint32_t CVRegister : 16; - uint64_t static toOpaqueValue(const LocalVarDef DR) { - uint64_t Val = 0; - std::memcpy(&Val, &DR, sizeof(Val)); - return Val; + /// Value for `DerefOffset` indicating this is not an indirect load. + constexpr static int32_t NoDeref = INT32_MIN; + + /// Offset to add after dereferencing `CVRegister + DataOffset` for + /// indirect loads. If this is not an indirect load, it's set to NoDeref. + int32_t DerefOffset = NoDeref; + + static LocalVarDef emptyValue() { + LocalVarDef V; + std::memset(&V, 0xff, sizeof(LocalVarDef)); + return V; } - LocalVarDef static createFromOpaqueValue(uint64_t Val) { - LocalVarDef DR; - std::memcpy(&DR, &Val, sizeof(Val)); - return DR; + static LocalVarDef tombstoneValue() { + LocalVarDef V; + std::memset(&V, 0xff, sizeof(LocalVarDef)); + V.InMemory = 0; + return V; + } + + unsigned hashValue() const { + uint64_t H = 0; + std::memcpy(&H, this, sizeof(uint64_t)); + static_assert(sizeof(LocalVarDef) == 8 + 4 && + offsetof(LocalVarDef, DerefOffset) == 8); + H = hash_combine(H, DerefOffset); + return H; + } + + bool operator==(const LocalVarDef &Other) const { + return InMemory == Other.InMemory && DataOffset == Other.DataOffset && + IsSubfield == Other.IsSubfield && + StructOffset == Other.StructOffset && + CVRegister == Other.CVRegister && DerefOffset == Other.DerefOffset; } }; - static_assert(sizeof(uint64_t) == sizeof(LocalVarDef)); - private: MCStreamer &OS; BumpPtrAllocator Allocator; @@ -104,7 +126,8 @@ private: /// info is being emitted, DebugHandlerBase::Asm may be null. AsmPrinter *CompilerInfoAsm = nullptr; - static LocalVarDef createDefRangeMem(uint16_t CVRegister, int Offset); + static LocalVarDef createDefRangeMem(uint16_t CVRegister, int Offset, + int32_t DerefOffset); /// Similar to DbgVariable in DwarfDebug, but not dwarf-specific. struct LocalVariable { @@ -112,7 +135,6 @@ private: MapVector, 1>> DefRanges; - bool UseReferenceType = false; std::optional ConstantValue; }; @@ -431,8 +453,6 @@ private: getTypeIndexForThisPtr(const DIDerivedType *PtrTy, const DISubroutineType *SubroutineTy); - codeview::TypeIndex getTypeIndexForReferenceTo(const DIType *Ty); - codeview::TypeIndex getMemberFunctionType(const DISubprogram *SP, const DICompositeType *Class); @@ -536,21 +556,20 @@ public: template <> struct DenseMapInfo { static inline CodeViewDebug::LocalVarDef getEmptyKey() { - return CodeViewDebug::LocalVarDef::createFromOpaqueValue(~0ULL); + return CodeViewDebug::LocalVarDef::emptyValue(); } static inline CodeViewDebug::LocalVarDef getTombstoneKey() { - return CodeViewDebug::LocalVarDef::createFromOpaqueValue(~0ULL - 1ULL); + return CodeViewDebug::LocalVarDef::tombstoneValue(); } static unsigned getHashValue(const CodeViewDebug::LocalVarDef &DR) { - return CodeViewDebug::LocalVarDef::toOpaqueValue(DR) * 37ULL; + return DR.hashValue(); } static bool isEqual(const CodeViewDebug::LocalVarDef &LHS, const CodeViewDebug::LocalVarDef &RHS) { - return CodeViewDebug::LocalVarDef::toOpaqueValue(LHS) == - CodeViewDebug::LocalVarDef::toOpaqueValue(RHS); + return LHS == RHS; } }; diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index 5db2c86aafc3..e32c68b602a2 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -2115,16 +2115,13 @@ bool DIExpression::extractIfOffset(int64_t &Offset) const { } bool DIExpression::extractLeadingOffset( - int64_t &OffsetInBytes, SmallVectorImpl &RemainingOps) const { + ArrayRef Ops, int64_t &OffsetInBytes, + SmallVectorImpl &RemainingOps) { OffsetInBytes = 0; RemainingOps.clear(); - auto SingleLocEltsOpt = getSingleLocationExpressionElements(); - if (!SingleLocEltsOpt) - return false; - - auto ExprOpEnd = expr_op_iterator(SingleLocEltsOpt->end()); - auto ExprOpIt = expr_op_iterator(SingleLocEltsOpt->begin()); + auto ExprOpEnd = expr_op_iterator(Ops.end()); + auto ExprOpIt = expr_op_iterator(Ops.begin()); while (ExprOpIt != ExprOpEnd) { uint64_t Op = ExprOpIt->getOp(); if (Op == dwarf::DW_OP_deref || Op == dwarf::DW_OP_deref_size || @@ -2153,6 +2150,18 @@ bool DIExpression::extractLeadingOffset( return true; } +bool DIExpression::extractLeadingOffset( + int64_t &OffsetInBytes, SmallVectorImpl &RemainingOps) const { + auto SingleLocEltsOpt = getSingleLocationExpressionElements(); + if (!SingleLocEltsOpt) { + OffsetInBytes = 0; + RemainingOps.clear(); + return false; + } + + return extractLeadingOffset(*SingleLocEltsOpt, OffsetInBytes, RemainingOps); +} + bool DIExpression::hasAllLocationOps(unsigned N) const { SmallDenseSet SeenOps; for (auto ExprOp : expr_ops()) diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 6b8caa046c7b..67d7fab513de 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -360,6 +360,10 @@ public: ArrayRef> Ranges, codeview::DefRangeFramePointerRelHeader DRHdr) override; + void emitCVDefRangeDirective( + ArrayRef> Ranges, + codeview::DefRangeRegisterRelIndirHeader DRHdr) override; + void emitCVStringTableDirective() override; void emitCVFileChecksumsDirective() override; void emitCVFileChecksumOffsetDirective(unsigned FileNo) override; @@ -1944,6 +1948,16 @@ void MCAsmStreamer::emitCVDefRangeDirective( EmitEOL(); } +void MCAsmStreamer::emitCVDefRangeDirective( + ArrayRef> Ranges, + codeview::DefRangeRegisterRelIndirHeader DRHdr) { + PrintCVDefRangePrefix(Ranges); + OS << ", reg_rel_indir, "; + OS << DRHdr.Register << ", " << DRHdr.Flags << ", " << DRHdr.BasePointerOffset + << ", " << DRHdr.OffsetInUdt; + EmitEOL(); +} + void MCAsmStreamer::emitCVStringTableDirective() { OS << "\t.cv_stringtable"; EmitEOL(); diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 938eac7b05fb..e2b70c3e7dd3 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -545,7 +545,8 @@ private: CVDR_DEFRANGE_REGISTER, CVDR_DEFRANGE_FRAMEPOINTER_REL, CVDR_DEFRANGE_SUBFIELD_REGISTER, - CVDR_DEFRANGE_REGISTER_REL + CVDR_DEFRANGE_REGISTER_REL, + CVDR_DEFRANGE_REGISTER_REL_INDIR }; /// Maps Codeview def_range types --> CVDefRangeType enum, for @@ -3977,6 +3978,7 @@ void AsmParser::initializeCVDefRangeTypeMap() { CVDefRangeTypeMap["frame_ptr_rel"] = CVDR_DEFRANGE_FRAMEPOINTER_REL; CVDefRangeTypeMap["subfield_reg"] = CVDR_DEFRANGE_SUBFIELD_REGISTER; CVDefRangeTypeMap["reg_rel"] = CVDR_DEFRANGE_REGISTER_REL; + CVDefRangeTypeMap["reg_rel_indir"] = CVDR_DEFRANGE_REGISTER_REL_INDIR; } /// parseDirectiveCVDefRange @@ -4080,6 +4082,37 @@ bool AsmParser::parseDirectiveCVDefRange() { getStreamer().emitCVDefRangeDirective(Ranges, DRHdr); break; } + case CVDR_DEFRANGE_REGISTER_REL_INDIR: { + int64_t DRRegister; + int64_t DRFlags; + int64_t DRBasePointerOffset; + int64_t DROffsetInUdt; + if (parseToken(AsmToken::Comma, "expected comma before register number in " + ".cv_def_range directive") || + parseAbsoluteExpression(DRRegister)) + return Error(Loc, "expected register value"); + if (parseToken( + AsmToken::Comma, + "expected comma before flag value in .cv_def_range directive") || + parseAbsoluteExpression(DRFlags)) + return Error(Loc, "expected flag value"); + if (parseToken(AsmToken::Comma, "expected comma before base pointer offset " + "in .cv_def_range directive") || + parseAbsoluteExpression(DRBasePointerOffset)) + return Error(Loc, "expected base pointer offset value"); + if (parseToken(AsmToken::Comma, "expected comma before offset in UDT " + "in .cv_def_range directive") || + parseAbsoluteExpression(DROffsetInUdt)) + return Error(Loc, "expected offset in UDT value"); + + codeview::DefRangeRegisterRelIndirHeader DRHdr; + DRHdr.Register = DRRegister; + DRHdr.Flags = DRFlags; + DRHdr.BasePointerOffset = DRBasePointerOffset; + DRHdr.OffsetInUdt = DROffsetInUdt; + getStreamer().emitCVDefRangeDirective(Ranges, DRHdr); + break; + } default: return Error(Loc, "unexpected def_range type in .cv_def_range directive"); } diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 0ee6f490b4be..4131d027f63f 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -382,6 +382,15 @@ void MCStreamer::emitCVDefRangeDirective( emitCVDefRangeDirective(Ranges, BytePrefix); } +void MCStreamer::emitCVDefRangeDirective( + ArrayRef> Ranges, + codeview::DefRangeRegisterRelIndirHeader DRHdr) { + SmallString<20> BytePrefix; + copyBytesForDefRange(BytePrefix, codeview::S_DEFRANGE_REGISTER_REL_INDIR, + DRHdr); + emitCVDefRangeDirective(Ranges, BytePrefix); +} + void MCStreamer::emitEHSymAttributes(const MCSymbol *Symbol, MCSymbol *EHSymbol) { } diff --git a/llvm/test/CodeGen/MIR/X86/diexpr-win32.mir b/llvm/test/CodeGen/MIR/X86/diexpr-win32.mir index 054c34996d03..4fc981b8b77a 100644 --- a/llvm/test/CodeGen/MIR/X86/diexpr-win32.mir +++ b/llvm/test/CodeGen/MIR/X86/diexpr-win32.mir @@ -25,20 +25,23 @@ # (DW_OP_plus_uconst, 8, DW_OP_deref) # CHECK: LocalSym { # CHECK-NEXT: Kind: S_LOCAL (0x113E) -# CHECK-NEXT: Type: string& (0x +# CHECK-NEXT: Type: string (0x # CHECK-NEXT: Flags [ (0x0) # CHECK-NEXT: ] # CHECK-NEXT: VarName: Result # CHECK-NEXT: } -# CHECK-NEXT: DefRangeFramePointerRelSym { -# CHECK-NEXT: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142) -# CHECK-NEXT: Offset: 8 +# CHECK-NEXT: DefRangeRegisterRelIndirSym { +# CHECK-NEXT: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +# CHECK-NEXT: BaseRegister: VFRAME (0x +# CHECK-NEXT: HasSpilledUDTMember: No +# CHECK-NEXT: OffsetInParent: 0 +# CHECK-NEXT: BasePointerOffset: 8 +# CHECK-NEXT: OffsetInUDT: 0 # CHECK-NEXT: LocalVariableAddrRange { # CHECK-NEXT: OffsetStart: .text+0x5 # CHECK-NEXT: ISectStart: 0x0 # CHECK-NEXT: Range: 0x18 # CHECK-NEXT: } -# CHECK-NEXT: } # (DW_OP_constu, 4, DW_OP_minus) # CHECK: LocalSym { # CHECK-NEXT: Kind: S_LOCAL (0x113E) diff --git a/llvm/test/DebugInfo/COFF/indirect-local.ll b/llvm/test/DebugInfo/COFF/indirect-local.ll new file mode 100644 index 000000000000..0cc3aa8429d3 --- /dev/null +++ b/llvm/test/DebugInfo/COFF/indirect-local.ll @@ -0,0 +1,212 @@ +; RUN: llc < %s | FileCheck %s --check-prefix=ASM +; RUN: llc < %s -filetype=obj | llvm-readobj - --codeview | FileCheck %s --check-prefix=OBJ + + +; C++ source to regenerate: +; struct Foo { +; int a; +; int b; +; int c; +; int d; +; }; +; +; int main() { +; Foo f{1, 2, 3, 4}; +; auto &[a, b, c, d] = f; +; return a; +; } +; $ clang t.cpp -S -emit-llvm -g -o t.ll -std=c++17 + +; ASM-LABEL: .long 241 # Symbol subsection for main +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM-NEXT: .long 4101 # TypeIndex +; ASM-NEXT: .short 0 # Flags +; ASM-NEXT: .asciz "f" +; ASM-NEXT: .p2align 2, 0x0 +; ASM-NEXT: .Ltmp{{.*}}: +; ASM-NEXT: .cv_def_range .Ltmp0 .Ltmp1, frame_ptr_rel, 16 + +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM-NEXT: .long 116 # TypeIndex +; ASM-NEXT: .short 0 # Flags +; ASM-NEXT: .asciz "a" +; ASM-NEXT: .p2align 2, 0x0 +; ASM-NEXT: .Ltmp{{.*}}: +; ASM-NEXT: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, reg_rel_indir, 335, 0, 0, 0 + +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM-NEXT: .long 116 # TypeIndex +; ASM-NEXT: .short 0 # Flags +; ASM-NEXT: .asciz "b" +; ASM-NEXT: .p2align 2, 0x0 +; ASM-NEXT: .Ltmp{{.*}}: +; ASM-NEXT: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, reg_rel_indir, 335, 0, 0, 4 + +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM-NEXT: .long 116 # TypeIndex +; ASM-NEXT: .short 0 # Flags +; ASM-NEXT: .asciz "c" +; ASM-NEXT: .p2align 2, 0x0 +; ASM-NEXT: .Ltmp{{.*}}: +; ASM-NEXT: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, reg_rel_indir, 335, 0, 0, 8 + +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM-NEXT: .long 116 # TypeIndex +; ASM-NEXT: .short 0 # Flags +; ASM-NEXT: .asciz "d" +; ASM-NEXT: .p2align 2, 0x0 +; ASM-NEXT: .Ltmp{{.*}}: +; ASM-NEXT: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, reg_rel_indir, 335, 0, 0, 12 + +; OBJ: Subsection [ +; OBJ: SubSectionType: Symbols (0xF1) +; OBJ: GlobalProcIdSym { +; OBJ: FunctionType: main (0x1002) +; OBJ: } +; OBJ: LocalSym { +; OBJ: Kind: S_LOCAL (0x113E) +; OBJ: Type: Foo (0x1005) +; OBJ: VarName: f +; OBJ: } +; OBJ: DefRangeFramePointerRelSym { +; OBJ: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142) +; OBJ: Offset: 16 +; OBJ: LocalVariableAddrRange { +; OBJ: } +; OBJ: } +; OBJ: LocalSym { +; OBJ: Kind: S_LOCAL (0x113E) +; OBJ: Type: int (0x74) +; OBJ: Flags [ (0x0) +; OBJ: ] +; OBJ: VarName: a +; OBJ: } +; OBJ: DefRangeRegisterRelIndirSym { +; OBJ: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +; OBJ: BaseRegister: RSP (0x14F) +; OBJ: HasSpilledUDTMember: No +; OBJ: OffsetInParent: 0 +; OBJ: BasePointerOffset: 0 +; OBJ: OffsetInUDT: 0 +; OBJ: LocalVariableAddrRange { +; OBJ: } +; OBJ: } +; OBJ: LocalSym { +; OBJ: Kind: S_LOCAL (0x113E) +; OBJ: Type: int (0x74) +; OBJ: Flags [ (0x0) +; OBJ: ] +; OBJ: VarName: b +; OBJ: } +; OBJ: DefRangeRegisterRelIndirSym { +; OBJ: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +; OBJ: BaseRegister: RSP (0x14F) +; OBJ: HasSpilledUDTMember: No +; OBJ: OffsetInParent: 0 +; OBJ: BasePointerOffset: 0 +; OBJ: OffsetInUDT: 4 +; OBJ: LocalVariableAddrRange { +; OBJ: } +; OBJ: } +; OBJ: LocalSym { +; OBJ: Kind: S_LOCAL (0x113E) +; OBJ: Type: int (0x74) +; OBJ: Flags [ (0x0) +; OBJ: ] +; OBJ: VarName: c +; OBJ: } +; OBJ: DefRangeRegisterRelIndirSym { +; OBJ: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +; OBJ: BaseRegister: RSP (0x14F) +; OBJ: HasSpilledUDTMember: No +; OBJ: OffsetInParent: 0 +; OBJ: BasePointerOffset: 0 +; OBJ: OffsetInUDT: 8 +; OBJ: LocalVariableAddrRange { +; OBJ: } +; OBJ: } +; OBJ: LocalSym { +; OBJ: Kind: S_LOCAL (0x113E) +; OBJ: Type: int (0x74) +; OBJ: Flags [ (0x0) +; OBJ: ] +; OBJ: VarName: d +; OBJ: } +; OBJ: DefRangeRegisterRelIndirSym { +; OBJ: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +; OBJ: BaseRegister: RSP (0x14F) +; OBJ: HasSpilledUDTMember: No +; OBJ: OffsetInParent: 0 +; OBJ: BasePointerOffset: 0 +; OBJ: OffsetInUDT: 12 +; OBJ: LocalVariableAddrRange { +; OBJ: } +; OBJ: } + +; ModuleID = 't.cpp' +source_filename = "t.cpp" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.50.35725" + +%struct.Foo = type { i32, i32, i32, i32 } + +@__const.main.f = private unnamed_addr constant %struct.Foo { i32 1, i32 2, i32 3, i32 4 }, align 4 + +; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable +define dso_local noundef i32 @main() #0 !dbg !9 { + %1 = alloca i32, align 4 + %2 = alloca %struct.Foo, align 4 + %3 = alloca ptr, align 8 + store i32 0, ptr %1, align 4 + #dbg_declare(ptr %2, !14, !DIExpression(), !21) + call void @llvm.memcpy.p0.p0.i64(ptr align 4 %2, ptr align 4 @__const.main.f, i64 16, i1 false), !dbg !21 + #dbg_declare(ptr %3, !22, !DIExpression(DW_OP_deref), !23) + #dbg_declare(ptr %3, !24, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4), !23) + #dbg_declare(ptr %3, !25, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 8), !23) + #dbg_declare(ptr %3, !26, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 12), !23) + store ptr %2, ptr %3, align 8, !dbg !23 + %4 = load ptr, ptr %3, align 8, !dbg !27, !nonnull !13, !align !28 + %5 = getelementptr inbounds nuw %struct.Foo, ptr %4, i32 0, i32 0, !dbg !27 + %6 = load i32, ptr %5, align 4, !dbg !27 + ret i32 %6, !dbg !27 +} + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 + +attributes #0 = { mustprogress noinline norecurse nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 21.1.8", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "t.cpp", directory: "F:\\Dev\\dummy", checksumkind: CSK_MD5, checksum: "e257352cbba404e4548bc4500877ceb0") +!2 = !{i32 2, !"CodeView", i32 1} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 2} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"uwtable", i32 2} +!7 = !{i32 1, !"MaxTLSAlign", i32 65536} +!8 = !{!"clang version 21.1.8"} +!9 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 8, type: !10, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !13) +!10 = !DISubroutineType(types: !11) +!11 = !{!12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{} +!14 = !DILocalVariable(name: "f", scope: !9, file: !1, line: 9, type: !15) +!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 1, size: 128, flags: DIFlagTypePassByValue, elements: !16, identifier: ".?AUFoo@@") +!16 = !{!17, !18, !19, !20} +!17 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !15, file: !1, line: 2, baseType: !12, size: 32) +!18 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !15, file: !1, line: 3, baseType: !12, size: 32, offset: 32) +!19 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !15, file: !1, line: 4, baseType: !12, size: 32, offset: 64) +!20 = !DIDerivedType(tag: DW_TAG_member, name: "d", scope: !15, file: !1, line: 5, baseType: !12, size: 32, offset: 96) +!21 = !DILocation(line: 9, scope: !9) +!22 = !DILocalVariable(name: "a", scope: !9, file: !1, line: 10, type: !12) +!23 = !DILocation(line: 10, scope: !9) +!24 = !DILocalVariable(name: "b", scope: !9, file: !1, line: 10, type: !12) +!25 = !DILocalVariable(name: "c", scope: !9, file: !1, line: 10, type: !12) +!26 = !DILocalVariable(name: "d", scope: !9, file: !1, line: 10, type: !12) +!27 = !DILocation(line: 11, scope: !9) +!28 = !{i64 4} diff --git a/llvm/test/DebugInfo/COFF/nrvo.ll b/llvm/test/DebugInfo/COFF/nrvo.ll index 6e254773cd18..93c2c0e0d3e9 100644 --- a/llvm/test/DebugInfo/COFF/nrvo.ll +++ b/llvm/test/DebugInfo/COFF/nrvo.ll @@ -24,29 +24,34 @@ ; ASM-LABEL: .long 241 # Symbol subsection for GetFoo ; ASM: .short 4414 # Record kind: S_LOCAL -; ASM-NEXT: .long 4113 # TypeIndex +; ASM-NEXT: .long 4109 # TypeIndex ; ASM-NEXT: .short 0 # Flags ; ASM-NEXT: .asciz "foo" ; ASM-NEXT: .p2align 2 ; ASM-NEXT: .Ltmp -; ASM: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, frame_ptr_rel, 40 +; ASM: .cv_def_range .Ltmp{{.*}} .Ltmp{{.*}}, reg_rel_indir, 335, 0, 40, 0 ; OBJ: Subsection [ ; OBJ: SubSectionType: Symbols (0xF1) ; OBJ: LocalSym { ; OBJ: Kind: S_LOCAL (0x113E) -; OBJ: Type: Foo& (0x1011) +; OBJ: Type: Foo (0x100D) ; OBJ: Flags [ (0x0) ; OBJ: ] ; OBJ: VarName: foo ; OBJ: } -; OBJ: DefRangeFramePointerRelSym { -; OBJ: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142) -; OBJ: Offset: 40 +; OBJ: DefRangeRegisterRelIndirSym { +; OBJ: Kind: S_DEFRANGE_REGISTER_REL_INDIR (0x1177) +; OBJ: BaseRegister: RSP (0x14F) +; OBJ: HasSpilledUDTMember: No +; OBJ: OffsetInParent: 0 +; OBJ: BasePointerOffset: 40 +; OBJ: OffsetInUDT: 0 ; OBJ: LocalVariableAddrRange { ; OBJ: OffsetStart: .text+0x1D ; OBJ: ISectStart: 0x0 ; OBJ: Range: 0x16 +; OBJ: } ; OBJ: } ; ModuleID = 't.cpp' diff --git a/llvm/test/DebugInfo/COFF/pieces.ll b/llvm/test/DebugInfo/COFF/pieces.ll index 88c3b7d45a0b..8a86a9bacd5a 100644 --- a/llvm/test/DebugInfo/COFF/pieces.ll +++ b/llvm/test/DebugInfo/COFF/pieces.ll @@ -180,8 +180,9 @@ ; ASM-LABEL: .short 4423 # Record kind: S_GPROC32_ID ; ASM: .asciz "nested" # Function name ; ASM: .short 4414 # Record kind: S_LOCAL +; ASM: .long 4123 # TypeIndex ; ASM: .asciz "o" -; ASM: .cv_def_range .Lfunc_begin3 .Lfunc_end3, reg_rel, 330, 0, 0 +; ASM: .cv_def_range .Lfunc_begin3 .Lfunc_end3, reg_rel_indir, 330, 0, 0, 0 ; ASM: .short 4414 # Record kind: S_LOCAL ; ASM: .asciz "p" ; ASM: .cv_def_range [[p_start]] .Lfunc_end3, subfield_reg, 17, 4 @@ -191,14 +192,15 @@ ; OBJ: DisplayName: nested ; OBJ: } ; OBJ: LocalSym { -; OBJ: Type: Nested& +; OBJ: Type: Nested ; OBJ: VarName: o ; OBJ: } -; OBJ: DefRangeRegisterRelSym { +; OBJ: DefRangeRegisterRelIndirSym { ; OBJ: BaseRegister: RCX (0x14A) ; OBJ: HasSpilledUDTMember: No ; OBJ: OffsetInParent: 0 ; OBJ: BasePointerOffset: 0 +; OBJ: OffsetInUDT: 0 ; OBJ: LocalVariableAddrRange { ; OBJ: } ; OBJ: } diff --git a/llvm/test/DebugInfo/COFF/types-array-advanced.ll b/llvm/test/DebugInfo/COFF/types-array-advanced.ll index f44510813372..b7bc466de7c2 100644 --- a/llvm/test/DebugInfo/COFF/types-array-advanced.ll +++ b/llvm/test/DebugInfo/COFF/types-array-advanced.ll @@ -49,41 +49,30 @@ ; CHECK: ElementType: int (0x74) ; CHECK: IndexType: unsigned long (0x22) ; CHECK: SizeOf: 0 -; CHECK: Name: +; CHECK: Name: ; CHECK: } -; CHECK: Pointer (0x1004) { -; CHECK: TypeLeafKind: LF_POINTER (0x1002) -; CHECK: PointeeType: 0x1003 -; CHECK: PtrType: Near32 (0xA) -; CHECK: PtrMode: LValueReference (0x1) -; CHECK: IsFlat: 0 -; CHECK: IsConst: 0 -; CHECK: IsVolatile: 0 -; CHECK: IsUnaligned: 0 -; CHECK: SizeOf: 0 -; CHECK: } -; CHECK: Array (0x1005) { +; CHECK: Array (0x1004) { ; CHECK: TypeLeafKind: LF_ARRAY (0x1503) ; CHECK: ElementType: char (0x70) ; CHECK: IndexType: unsigned long (0x22) ; CHECK: SizeOf: 7 -; CHECK: Name: +; CHECK: Name: +; CHECK: } +; CHECK: Array (0x1005) { +; CHECK: TypeLeafKind: LF_ARRAY (0x1503) +; CHECK: ElementType: 0x1004 +; CHECK: IndexType: unsigned long (0x22) +; CHECK: SizeOf: 35 +; CHECK: Name: ; CHECK: } ; CHECK: Array (0x1006) { ; CHECK: TypeLeafKind: LF_ARRAY (0x1503) ; CHECK: ElementType: 0x1005 ; CHECK: IndexType: unsigned long (0x22) -; CHECK: SizeOf: 35 -; CHECK: Name: -; CHECK: } -; CHECK: Array (0x1007) { -; CHECK: TypeLeafKind: LF_ARRAY (0x1503) -; CHECK: ElementType: 0x1006 -; CHECK: IndexType: unsigned long (0x22) ; CHECK: SizeOf: 70 -; CHECK: Name: +; CHECK: Name: ; CHECK: } -; CHECK: Struct (0x1008) { +; CHECK: Struct (0x1007) { ; CHECK: TypeLeafKind: LF_STRUCTURE (0x1505) ; CHECK: MemberCount: 0 ; CHECK: Properties [ (0x280) @@ -97,16 +86,16 @@ ; CHECK: Name: incomplete_struct ; CHECK: LinkageName: .?AUincomplete_struct@@ ; CHECK: } -; CHECK: Array (0x1009) { +; CHECK: Array (0x1008) { ; CHECK: TypeLeafKind: LF_ARRAY (0x1503) -; CHECK: ElementType: incomplete_struct (0x1008) +; CHECK: ElementType: incomplete_struct (0x1007) ; CHECK: IndexType: unsigned long (0x22) ; CHECK: SizeOf: 12 -; CHECK: Name: +; CHECK: Name: ; CHECK: } -; CHECK: Pointer (0x100A) { +; CHECK: Pointer (0x1009) { ; CHECK: TypeLeafKind: LF_POINTER (0x1002) -; CHECK: PointeeType: 0x1009 +; CHECK: PointeeType: 0x1008 ; CHECK: PtrType: Near32 (0xA) ; CHECK: PtrMode: Pointer (0x0) ; CHECK: IsFlat: 0 @@ -115,7 +104,7 @@ ; CHECK: IsUnaligned: 0 ; CHECK: SizeOf: 4 ; CHECK: } -; CHECK: FieldList (0x100B) { +; CHECK: FieldList (0x100A) { ; CHECK: TypeLeafKind: LF_FIELDLIST (0x1203) ; CHECK: DataMember { ; CHECK: TypeLeafKind: LF_MEMBER (0x150D) @@ -125,31 +114,31 @@ ; CHECK: Name: s1 ; CHECK: } ; CHECK: } -; CHECK: Struct (0x100C) { +; CHECK: Struct (0x100B) { ; CHECK: TypeLeafKind: LF_STRUCTURE (0x1505) ; CHECK: MemberCount: 1 ; CHECK: Properties [ (0x200) ; CHECK: HasUniqueName (0x200) ; CHECK: ] -; CHECK: FieldList: (0x100B) +; CHECK: FieldList: (0x100A) ; CHECK: DerivedFrom: 0x0 ; CHECK: VShape: 0x0 ; CHECK: SizeOf: 4 ; CHECK: Name: incomplete_struct ; CHECK: LinkageName: .?AUincomplete_struct@@ ; CHECK: } -; CHECK: StringId (0x100D) { +; CHECK: StringId (0x100C) { ; CHECK: TypeLeafKind: LF_STRING_ID (0x1605) ; CHECK: Id: 0x0 ; CHECK: StringData: /t.cpp ; CHECK: } -; CHECK: UdtSourceLine (0x100E) { +; CHECK: UdtSourceLine (0x100D) { ; CHECK: TypeLeafKind: LF_UDT_SRC_LINE (0x1606) -; CHECK: UDT: incomplete_struct (0x100C) -; CHECK: SourceFile: /t.cpp (0x100D) +; CHECK: UDT: incomplete_struct (0x100B) +; CHECK: SourceFile: /t.cpp (0x100C) ; CHECK: LineNumber: 4 ; CHECK: } -; CHECK: Modifier (0x100F) { +; CHECK: Modifier (0x100E) { ; CHECK: TypeLeafKind: LF_MODIFIER (0x1001) ; CHECK: ModifiedType: int (0x74) ; CHECK: Modifiers [ (0x3) @@ -157,12 +146,12 @@ ; CHECK: Volatile (0x2) ; CHECK: ] ; CHECK: } -; CHECK: Array (0x1010) { +; CHECK: Array (0x100F) { ; CHECK: TypeLeafKind: LF_ARRAY (0x1503) -; CHECK: ElementType: const volatile int (0x100F) +; CHECK: ElementType: const volatile int (0x100E) ; CHECK: IndexType: unsigned long (0x22) ; CHECK: SizeOf: 16 -; CHECK: Name: +; CHECK: Name: ; CHECK: } ; CHECK: ]