forked from OSchip/llvm-project
676 lines
21 KiB
C++
676 lines
21 KiB
C++
//===-- LVLocation.cpp ----------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This implements the LVOperation and LVLocation classes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
|
|
#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
|
|
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
|
|
#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::logicalview;
|
|
|
|
#define DEBUG_TYPE "Location"
|
|
|
|
void LVOperation::print(raw_ostream &OS, bool Full) const {}
|
|
|
|
// Identify the most common type of operations and print them using a high
|
|
// level format, trying to isolate the DWARF complexity.
|
|
std::string LVOperation::getOperandsDWARFInfo() {
|
|
std::string String;
|
|
raw_string_ostream Stream(String);
|
|
|
|
auto PrintRegisterInfo = [&](LVSmall Code) {
|
|
//-----------------------------------------------------------------------
|
|
// 2.5.1.1 Literal encodings.
|
|
//-----------------------------------------------------------------------
|
|
if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) {
|
|
Stream << format("lit%d", Code - dwarf::DW_OP_lit0);
|
|
return;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// 2.5.1.2 Register values.
|
|
//-----------------------------------------------------------------------
|
|
if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) {
|
|
std::string RegisterName(getReader().getRegisterName(Code, Operands));
|
|
Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0],
|
|
RegisterName.c_str());
|
|
return;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// 2.6.1.1.3 Register location descriptions.
|
|
//-----------------------------------------------------------------------
|
|
if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) {
|
|
std::string RegisterName(getReader().getRegisterName(Code, Operands));
|
|
Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0,
|
|
RegisterName.c_str());
|
|
return;
|
|
}
|
|
|
|
Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " "
|
|
<< hexString(Operands[1]) << "#";
|
|
};
|
|
|
|
switch (Opcode) {
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.1 Literal encodings.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_addr:
|
|
Stream << "addr " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_constu:
|
|
case dwarf::DW_OP_const1u:
|
|
case dwarf::DW_OP_const2u:
|
|
case dwarf::DW_OP_const4u:
|
|
case dwarf::DW_OP_const8u:
|
|
Stream << "const_u " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_consts:
|
|
case dwarf::DW_OP_const1s:
|
|
case dwarf::DW_OP_const2s:
|
|
case dwarf::DW_OP_const4s:
|
|
case dwarf::DW_OP_const8s:
|
|
Stream << "const_s " << int(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_addrx:
|
|
Stream << "addrx " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_constx:
|
|
Stream << "constx " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_const_type:
|
|
Stream << "TODO: DW_OP_const_type";
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.2 Register values.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_fbreg:
|
|
Stream << "fbreg " << int(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_bregx: {
|
|
std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
|
|
Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(),
|
|
unsigned(Operands[1]));
|
|
break;
|
|
}
|
|
case dwarf::DW_OP_regval_type: {
|
|
std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
|
|
Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(),
|
|
unsigned(Operands[1]));
|
|
break;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.3 Stack operations.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_dup:
|
|
Stream << "dup";
|
|
break;
|
|
case dwarf::DW_OP_drop:
|
|
Stream << "drop";
|
|
break;
|
|
case dwarf::DW_OP_pick:
|
|
Stream << "pick " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_over:
|
|
Stream << "over";
|
|
break;
|
|
case dwarf::DW_OP_swap:
|
|
Stream << "swap";
|
|
break;
|
|
case dwarf::DW_OP_rot:
|
|
Stream << "rot";
|
|
break;
|
|
case dwarf::DW_OP_deref:
|
|
Stream << "deref";
|
|
break;
|
|
case dwarf::DW_OP_deref_size:
|
|
Stream << "deref_size " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_deref_type:
|
|
Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset "
|
|
<< hexString(Operands[1]);
|
|
break;
|
|
case dwarf::DW_OP_xderef:
|
|
Stream << "xderef";
|
|
break;
|
|
case dwarf::DW_OP_xderef_size:
|
|
Stream << "xderef_size " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_xderef_type:
|
|
Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset "
|
|
<< hexString(Operands[1]);
|
|
break;
|
|
case dwarf::DW_OP_push_object_address:
|
|
Stream << "push_object_address";
|
|
break;
|
|
case dwarf::DW_OP_form_tls_address:
|
|
Stream << "form_tls_address " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_call_frame_cfa:
|
|
Stream << "call_frame_cfa";
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.4 Arithmetic and Logical Operations.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_abs:
|
|
Stream << "abs";
|
|
break;
|
|
case dwarf::DW_OP_and:
|
|
Stream << "and";
|
|
break;
|
|
case dwarf::DW_OP_div:
|
|
Stream << "div";
|
|
break;
|
|
case dwarf::DW_OP_minus:
|
|
Stream << "minus";
|
|
break;
|
|
case dwarf::DW_OP_mod:
|
|
Stream << "mod";
|
|
break;
|
|
case dwarf::DW_OP_mul:
|
|
Stream << "mul";
|
|
break;
|
|
case dwarf::DW_OP_neg:
|
|
Stream << "neg";
|
|
break;
|
|
case dwarf::DW_OP_not:
|
|
Stream << "not";
|
|
break;
|
|
case dwarf::DW_OP_or:
|
|
Stream << "or";
|
|
break;
|
|
case dwarf::DW_OP_plus:
|
|
Stream << "plus";
|
|
break;
|
|
case dwarf::DW_OP_plus_uconst:
|
|
Stream << "plus_uconst " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_shl:
|
|
Stream << "shl";
|
|
break;
|
|
case dwarf::DW_OP_shr:
|
|
Stream << "shr";
|
|
break;
|
|
case dwarf::DW_OP_shra:
|
|
Stream << "shra";
|
|
break;
|
|
case dwarf::DW_OP_xor:
|
|
Stream << "xor";
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.5 Control Flow Operations.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_le:
|
|
Stream << "le";
|
|
break;
|
|
case dwarf::DW_OP_ge:
|
|
Stream << "ge";
|
|
break;
|
|
case dwarf::DW_OP_eq:
|
|
Stream << "eq";
|
|
break;
|
|
case dwarf::DW_OP_lt:
|
|
Stream << "lt";
|
|
break;
|
|
case dwarf::DW_OP_gt:
|
|
Stream << "gt";
|
|
break;
|
|
case dwarf::DW_OP_ne:
|
|
Stream << "ne";
|
|
break;
|
|
case dwarf::DW_OP_skip:
|
|
Stream << "skip " << signed(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_bra:
|
|
Stream << "bra " << signed(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_call2:
|
|
Stream << "call2 DIE offset " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_call4:
|
|
Stream << "call4 DIE offset " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_call_ref:
|
|
Stream << "call_ref DIE offset " << hexString(Operands[0]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.6 Type Conversions.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_convert:
|
|
Stream << "convert DIE offset " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_reinterpret:
|
|
Stream << "reinterpret DIE offset " << hexString(Operands[0]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.5.1.7 Special Operations.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_nop:
|
|
Stream << "nop";
|
|
break;
|
|
case dwarf::DW_OP_entry_value:
|
|
Stream << "TODO: DW_OP_entry_value";
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.6.1.1.3 Register location descriptions.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_regx:
|
|
Stream << "regx" << getReader().getRegisterName(Opcode, Operands);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.6.1.1.4 Implicit location descriptions.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_stack_value:
|
|
Stream << "stack_value";
|
|
break;
|
|
case dwarf::DW_OP_implicit_value:
|
|
Stream << "TODO: DW_OP_implicit_value";
|
|
break;
|
|
case dwarf::DW_OP_implicit_pointer:
|
|
Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " "
|
|
<< int(Operands[1]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 2.6.1.2 Composite location descriptions.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_piece:
|
|
Stream << "piece " << int(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_bit_piece:
|
|
Stream << "bit_piece " << int(Operands[0]) << " offset "
|
|
<< int(Operands[1]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// GNU extensions.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_GNU_entry_value:
|
|
Stream << "gnu_entry_value ";
|
|
PrintRegisterInfo(dwarf::DW_OP_reg0);
|
|
break;
|
|
case dwarf::DW_OP_GNU_push_tls_address:
|
|
Stream << "gnu_push_tls_address " << hexString(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_GNU_addr_index:
|
|
Stream << "gnu_addr_index " << unsigned(Operands[0]);
|
|
break;
|
|
case dwarf::DW_OP_GNU_const_index:
|
|
Stream << "gnu_const_index " << unsigned(Operands[0]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Member location.
|
|
//-------------------------------------------------------------------------
|
|
case LVLocationMemberOffset:
|
|
Stream << "offset " << int(Operands[0]);
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Missing location.
|
|
//-------------------------------------------------------------------------
|
|
case dwarf::DW_OP_hi_user:
|
|
Stream << "missing";
|
|
break;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Register values.
|
|
//-------------------------------------------------------------------------
|
|
default:
|
|
PrintRegisterInfo(Opcode);
|
|
break;
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
// Identify the most common type of operations and print them using a high
|
|
// level format, trying to isolate the CodeView complexity.
|
|
std::string LVOperation::getOperandsCodeViewInfo() {
|
|
std::string String;
|
|
raw_string_ostream Stream(String);
|
|
|
|
// Get original CodeView operation code.
|
|
uint16_t OperationCode = getCodeViewOperationCode(Opcode);
|
|
|
|
switch (OperationCode) {
|
|
// Operands: [Offset, 0].
|
|
case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL:
|
|
Stream << "frame_pointer_rel " << int(Operands[0]);
|
|
break;
|
|
case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
|
|
Stream << "frame_pointer_rel_full_scope " << int(Operands[0]);
|
|
break;
|
|
|
|
// Operands: [Register, 0].
|
|
case codeview::SymbolKind::S_DEFRANGE_REGISTER:
|
|
Stream << "register " << getReader().getRegisterName(Opcode, Operands);
|
|
break;
|
|
case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER:
|
|
Stream << "subfield_register "
|
|
<< getReader().getRegisterName(Opcode, Operands);
|
|
break;
|
|
|
|
// Operands: [Register, Offset].
|
|
case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL:
|
|
Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands)
|
|
<< " offset " << int(Operands[1]);
|
|
break;
|
|
|
|
// Operands: [Program, 0].
|
|
case codeview::SymbolKind::S_DEFRANGE:
|
|
Stream << "frame " << int(Operands[0]);
|
|
break;
|
|
case codeview::SymbolKind::S_DEFRANGE_SUBFIELD:
|
|
Stream << "subfield " << int(Operands[0]);
|
|
break;
|
|
|
|
default:
|
|
Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " "
|
|
<< hexString(Operands[1]) << "#";
|
|
break;
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
namespace {
|
|
const char *const KindBaseClassOffset = "BaseClassOffset";
|
|
const char *const KindBaseClassStep = "BaseClassStep";
|
|
const char *const KindClassOffset = "ClassOffset";
|
|
const char *const KindFixedAddress = "FixedAddress";
|
|
const char *const KindMissingInfo = "Missing";
|
|
const char *const KindOperation = "Operation";
|
|
const char *const KindOperationList = "OperationList";
|
|
const char *const KindRegister = "Register";
|
|
const char *const KindUndefined = "Undefined";
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DWARF location information.
|
|
//===----------------------------------------------------------------------===//
|
|
const char *LVLocation::kind() const {
|
|
const char *Kind = KindUndefined;
|
|
if (getIsBaseClassOffset())
|
|
Kind = KindBaseClassOffset;
|
|
else if (getIsBaseClassStep())
|
|
Kind = KindBaseClassStep;
|
|
else if (getIsClassOffset())
|
|
Kind = KindClassOffset;
|
|
else if (getIsFixedAddress())
|
|
Kind = KindFixedAddress;
|
|
else if (getIsGapEntry())
|
|
Kind = KindMissingInfo;
|
|
else if (getIsOperation())
|
|
Kind = KindOperation;
|
|
else if (getIsOperationList())
|
|
Kind = KindOperationList;
|
|
else if (getIsRegister())
|
|
Kind = KindRegister;
|
|
return Kind;
|
|
}
|
|
|
|
std::string LVLocation::getIntervalInfo() const {
|
|
static const char *const Question = "?";
|
|
std::string String;
|
|
raw_string_ostream Stream(String);
|
|
if (getIsAddressRange())
|
|
Stream << "{Range}";
|
|
|
|
auto PrintLine = [&](const LVLine *Line) {
|
|
if (Line) {
|
|
std::string TheLine;
|
|
TheLine = Line->lineNumberAsStringStripped();
|
|
Stream << TheLine.c_str();
|
|
} else {
|
|
Stream << Question;
|
|
}
|
|
};
|
|
|
|
Stream << " Lines ";
|
|
PrintLine(getLowerLine());
|
|
Stream << ":";
|
|
PrintLine(getUpperLine());
|
|
|
|
if (options().getAttributeOffset())
|
|
// Print the active range (low pc and high pc).
|
|
Stream << " [" << hexString(getLowerAddress()) << ":"
|
|
<< hexString(getUpperAddress()) << "]";
|
|
|
|
return String;
|
|
}
|
|
|
|
// Validate the ranges associated with the location.
|
|
bool LVLocation::validateRanges() {
|
|
// Traverse the locations and validate them against the address to line
|
|
// mapping in the current compile unit. Record those invalid ranges.
|
|
// A valid range must meet the following conditions:
|
|
// a) line(lopc) <= line(hipc)
|
|
// b) line(lopc) and line(hipc) are valid.
|
|
|
|
if (!hasAssociatedRange())
|
|
return true;
|
|
|
|
LVLineRange Range = getReaderCompileUnit()->lineRange(this);
|
|
LVLine *LowLine = Range.first;
|
|
LVLine *HighLine = Range.second;
|
|
if (LowLine)
|
|
setLowerLine(LowLine);
|
|
else {
|
|
setIsInvalidLower();
|
|
return false;
|
|
}
|
|
if (HighLine)
|
|
setUpperLine(HighLine);
|
|
else {
|
|
setIsInvalidUpper();
|
|
return false;
|
|
}
|
|
// Check for a valid interval.
|
|
if (LowLine->getLineNumber() > HighLine->getLineNumber()) {
|
|
setIsInvalidRange();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor,
|
|
float &Percentage) {
|
|
if (!options().getAttributeCoverage() && !Locations)
|
|
return false;
|
|
|
|
// Calculate the coverage depending on the kind of location. We have
|
|
// the simple and composed locations.
|
|
if (Locations->size() == 1) {
|
|
// Simple: fixed address, class offset, stack offset.
|
|
LVLocation *Location = Locations->front();
|
|
// Some types of locations do not have specific kind. Now is the time
|
|
// to set those types, depending on the operation type.
|
|
Location->updateKind();
|
|
if (Location->getIsLocationSimple()) {
|
|
Factor = 100;
|
|
Percentage = 100;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Composed locations.
|
|
LVAddress LowerAddress = 0;
|
|
LVAddress UpperAddress = 0;
|
|
for (const LVLocation *Location : *Locations)
|
|
// Do not include locations representing a gap.
|
|
if (!Location->getIsGapEntry()) {
|
|
LowerAddress = Location->getLowerAddress();
|
|
UpperAddress = Location->getUpperAddress();
|
|
Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress
|
|
: LowerAddress - UpperAddress;
|
|
}
|
|
|
|
Percentage = 0;
|
|
return false;
|
|
}
|
|
|
|
void LVLocation::printRaw(raw_ostream &OS, bool Full) const {
|
|
// Print the active range (low pc and high pc).
|
|
OS << " [" << hexString(getLowerAddress()) << ":"
|
|
<< hexString(getUpperAddress()) << "]\n";
|
|
// Print any DWARF operations.
|
|
printRawExtra(OS, Full);
|
|
}
|
|
|
|
void LVLocation::printInterval(raw_ostream &OS, bool Full) const {
|
|
if (hasAssociatedRange())
|
|
OS << getIntervalInfo();
|
|
}
|
|
|
|
void LVLocation::print(raw_ostream &OS, bool Full) const {
|
|
if (getReader().doPrintLocation(this)) {
|
|
LVObject::print(OS, Full);
|
|
printExtra(OS, Full);
|
|
}
|
|
}
|
|
|
|
void LVLocation::printExtra(raw_ostream &OS, bool Full) const {
|
|
printInterval(OS, Full);
|
|
OS << "\n";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DWARF location for a symbol.
|
|
//===----------------------------------------------------------------------===//
|
|
// Add a Location Entry.
|
|
void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC,
|
|
LVUnsigned SectionOffset,
|
|
uint64_t LocDescOffset) {
|
|
setLowerAddress(LowPC);
|
|
setUpperAddress(HighPC);
|
|
|
|
// Record the offset where the location information begins.
|
|
setOffset(LocDescOffset ? LocDescOffset : SectionOffset);
|
|
|
|
// A -1 HighPC value, indicates no range.
|
|
if (HighPC == LVAddress(UINT64_MAX))
|
|
setIsDiscardedRange();
|
|
|
|
// Update the location kind, using the DWARF attribute.
|
|
setKind();
|
|
}
|
|
|
|
// Add a Location Record.
|
|
void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1,
|
|
LVUnsigned Operand2) {
|
|
if (!Entries)
|
|
Entries = new LVAutoOperations();
|
|
Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2));
|
|
}
|
|
|
|
// Based on the DWARF attribute, define the location kind.
|
|
void LVLocation::setKind() {
|
|
switch (getAttr()) {
|
|
case dwarf::DW_AT_data_member_location:
|
|
setIsClassOffset();
|
|
break;
|
|
case dwarf::DW_AT_location:
|
|
// Depending on the operand, we have a fixed address.
|
|
setIsFixedAddress();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// For those symbols with absolute location information, ignore any
|
|
// gaps in their location description; that is the case with absolute
|
|
// memory addresses and members located at specific offsets.
|
|
if (hasAssociatedRange())
|
|
getParentSymbol()->setFillGaps();
|
|
}
|
|
|
|
void LVLocationSymbol::updateKind() {
|
|
// Update the location type for simple ones.
|
|
if (Entries && Entries->size() == 1) {
|
|
LVOperation *Operation = Entries->front();
|
|
if (dwarf::DW_OP_fbreg == Operation->getOpcode())
|
|
setIsStackOffset();
|
|
}
|
|
}
|
|
|
|
void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const {
|
|
if (Entries)
|
|
for (const LVOperation *Operation : *Entries)
|
|
Operation->print(OS, Full);
|
|
}
|
|
|
|
// Print location (formatted version).
|
|
void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) {
|
|
if (!Locations || Locations->empty())
|
|
return;
|
|
|
|
// Print the symbol coverage.
|
|
if (options().getAttributeCoverage()) {
|
|
// The location entries are contained within a symbol. Get a location,
|
|
// to access basic information about indentation, parent, etc.
|
|
LVLocation *Location = Locations->front();
|
|
LVSymbol *Symbol = Location->getParentSymbol();
|
|
float Percentage = Symbol->getCoveragePercentage();
|
|
|
|
// The coverage is dependent on the kind of location.
|
|
std::string String;
|
|
raw_string_ostream Stream(String);
|
|
Stream << format("%.2f%%", Percentage);
|
|
if (!Location->getIsLocationSimple())
|
|
Stream << format(" (%d/%d)", Symbol->getCoverageFactor(),
|
|
Symbol->getParentScope()->getCoverageFactor());
|
|
Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String),
|
|
/*UseQuotes=*/false,
|
|
/*PrintRef=*/false);
|
|
}
|
|
|
|
// Print the symbol location, including the missing entries.
|
|
if (getReader().doPrintLocation(/*Location=*/nullptr))
|
|
for (const LVLocation *Location : *Locations)
|
|
Location->print(OS, Full);
|
|
}
|
|
|
|
void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
|
|
OS << "{Location}";
|
|
if (getIsCallSite())
|
|
OS << " -> CallSite";
|
|
printInterval(OS, Full);
|
|
OS << "\n";
|
|
|
|
// Print location entries.
|
|
if (Full && Entries) {
|
|
bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation();
|
|
std::stringstream Stream;
|
|
std::string Leading = "";
|
|
for (LVOperation *Operation : *Entries) {
|
|
Stream << Leading
|
|
<< (CodeViewLocation ? Operation->getOperandsCodeViewInfo()
|
|
: Operation->getOperandsDWARFInfo());
|
|
Leading = ", ";
|
|
}
|
|
printAttributes(OS, Full, "{Entry} ", const_cast<LVLocationSymbol *>(this),
|
|
StringRef(Stream.str()),
|
|
/*UseQuotes=*/false,
|
|
/*PrintRef=*/false);
|
|
}
|
|
}
|