Skip to content

Commit 3ea7345

Browse files
committed
Improve Printing Support for OCaml Values (#9)
1 parent 34028aa commit 3ea7345

File tree

18 files changed

+734
-66
lines changed

18 files changed

+734
-66
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,7 @@ pythonenv*
7070
/clang/utils/analyzer/projects/*/RefScanBuildResults
7171
# automodapi puts generated documentation files here.
7272
/lldb/docs/python_api/
73+
74+
75+
_install/
76+
build/

clang/include/clang/AST/Decl.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3149,6 +3149,22 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
31493149
// Implement isa/cast/dyncast/etc.
31503150
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
31513151
static bool classofKind(Kind K) { return K >= firstField && K <= lastField; }
3152+
3153+
private:
3154+
uint64_t m_variant_discr_value = UINT64_MAX;
3155+
3156+
public:
3157+
void setVariantDiscrValue(uint64_t value) {
3158+
m_variant_discr_value = value;
3159+
}
3160+
3161+
uint64_t getVariantDiscrValue() {
3162+
return m_variant_discr_value;
3163+
}
3164+
3165+
bool hasVariantDiscrValue() {
3166+
return getVariantDiscrValue() != UINT64_MAX;
3167+
}
31523168
};
31533169

31543170
/// An instance of this object exists for each enum constant

clang/include/clang/AST/DeclCXX.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,27 @@ class CXXRecordDecl : public RecordDecl {
18351835
return K >= firstCXXRecord && K <= lastCXXRecord;
18361836
}
18371837
void markAbstract() { data().Abstract = true; }
1838+
1839+
private:
1840+
bool m_is_variant = false;
1841+
int64_t m_offset_record_from_pointer = 0;
1842+
1843+
public:
1844+
bool isVariant() const {
1845+
return m_is_variant;
1846+
}
1847+
1848+
void setVariant(bool is_variant) {
1849+
m_is_variant = is_variant;
1850+
}
1851+
1852+
int64_t getOffsetRecordFromPointer() const {
1853+
return m_offset_record_from_pointer;
1854+
}
1855+
1856+
void setOffsetRecordFromPointer(int64_t offset) {
1857+
m_offset_record_from_pointer = offset;
1858+
}
18381859
};
18391860

18401861
/// Store information needed for an explicit specifier.

clang/lib/AST/TypePrinter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,14 @@ void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T,
544544

545545
void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T,
546546
raw_ostream &OS) {
547-
OS << "[]";
547+
// CR sspies: If we stick with this, we should find a way to propagate the
548+
// language information here. Once we add a type def with an explicit name
549+
// for the top-level type, custom printing will no longer be needed.
550+
bool is_ocaml = true;
551+
552+
if (is_ocaml) OS << " array";
553+
else OS << "[]";
554+
548555
printAfter(T->getElementType(), OS);
549556
}
550557

lldb/include/lldb/Core/ValueObject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ class ValueObject {
361361

362362
virtual lldb::ValueType GetValueType() const = 0;
363363

364+
// XXX mshinwell: why don't any of the following three functions get
365+
// called for displaying the type names in parameter lists and
366+
// "frame var"?
367+
364368
// Subclasses can implement the functions below.
365369
virtual ConstString GetTypeName() { return GetCompilerType().GetTypeName(); }
366370

lldb/include/lldb/Symbol/SymbolFile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class SymbolFile : public PluginInterface {
205205
/// To support variable-length array types, this function takes an
206206
/// optional \p ExecutionContext. If \c exe_ctx is non-null, the
207207
/// dynamic characteristics for that context are returned.
208+
// CR mshinwell: maybe we could use this?
208209
virtual std::optional<ArrayInfo>
209210
GetDynamicArrayInfoForUID(lldb::user_id_t type_uid,
210211
const lldb_private::ExecutionContext *exe_ctx) = 0;

lldb/include/lldb/Utility/DataExtractor.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,14 @@ class DataExtractor {
991991
uint8_t(GetAddressByteSize())};
992992
}
993993

994+
void setDataOriginalSource(lldb::addr_t ptr) {
995+
m_data_original_source = ptr;
996+
}
997+
998+
lldb::addr_t getDataOriginalSource() const {
999+
return m_data_original_source;
1000+
}
1001+
9941002
protected:
9951003
template <typename T> T Get(lldb::offset_t *offset_ptr, T fail_value) const {
9961004
constexpr size_t src_size = sizeof(T);
@@ -1018,6 +1026,8 @@ class DataExtractor {
10181026
lldb::DataBufferSP m_data_sp;
10191027
/// Making it const would require implementation of move assignment operator.
10201028
uint32_t m_target_byte_size = 1;
1029+
/// Temporary hack for reading more data from the source than is currently available.
1030+
lldb::addr_t m_data_original_source = 0;
10211031
};
10221032

10231033
} // namespace lldb_private

lldb/include/lldb/lldb-enumerations.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,10 @@ FLAGS_ENUM(TypeFlags){
10801080
eTypeIsVector = (1u << 16), eTypeIsScalar = (1u << 17),
10811081
eTypeIsInteger = (1u << 18), eTypeIsFloat = (1u << 19),
10821082
eTypeIsComplex = (1u << 20), eTypeIsSigned = (1u << 21),
1083-
eTypeInstanceIsPointer = (1u << 22)};
1083+
eTypeInstanceIsPointer = (1u << 22),eTypeIsOCaml = (1u << 23)};
1084+
1085+
// FIXME: The info eTypeIsOCaml is currently unused. If this information never
1086+
// becomes useful, remove the flag from the enum above.
10841087

10851088
FLAGS_ENUM(CommandFlags){
10861089
/// eCommandRequiresTarget

lldb/source/Core/DumpDataExtractor.cpp

Lines changed: 129 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,92 @@ static const llvm::fltSemantics &GetFloatSemantics(const TargetSP &target_sp,
338338
return llvm::APFloat::Bogus();
339339
}
340340

341+
342+
static std::optional<llvm::APInt> ReadAPIntFromMemory(const DataExtractor &data, Process * process,
343+
lldb::addr_t load_addr,
344+
size_t byte_size) {
345+
if (byte_size == 0)
346+
return std::nullopt;
347+
348+
llvm::SmallVector<uint64_t, 2> uint64_array;
349+
size_t bytes_left = byte_size;
350+
uint64_t u64;
351+
Status error;
352+
const lldb::ByteOrder byte_order = data.GetByteOrder();
353+
if (byte_order == lldb::eByteOrderLittle) {
354+
while (bytes_left > 0) {
355+
if (bytes_left >= 8) {
356+
u64 = process->ReadUnsignedIntegerFromMemory(load_addr, 8, 0, error);
357+
bytes_left -= 8;
358+
load_addr += 8;
359+
if (error.Fail())
360+
return std::nullopt;
361+
} else {
362+
u64 = process->ReadUnsignedIntegerFromMemory(load_addr, bytes_left, 0, error);
363+
if (error.Fail())
364+
return std::nullopt;
365+
bytes_left = 0;
366+
}
367+
uint64_array.push_back(u64);
368+
}
369+
return llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
370+
}
371+
// CR sspies: big endian not supported
372+
return std::nullopt;
373+
}
374+
375+
376+
377+
void PrintAPIntAsFloat(Stream *s, llvm::APInt apint,
378+
const llvm::fltSemantics &semantics,
379+
std::optional<unsigned> format_max_padding,
380+
std::string prefix = "", std::string suffix = "") {
381+
382+
llvm::APFloat apfloat(semantics, apint);
383+
llvm::SmallVector<char, 256> sv;
384+
if (format_max_padding)
385+
apfloat.toPossiblyShortString(sv, *format_max_padding);
386+
else
387+
apfloat.toPossiblyShortString(sv);
388+
389+
s->AsRawOstream() << prefix;
390+
s->AsRawOstream() << sv;
391+
// OCaml Specific:
392+
// Following OCaml conventions, print the trailing "." to
393+
// identify that the integer is in fact a float, but don't
394+
// print any trailing zeros.
395+
bool print_trailing_dot = true;
396+
for (char c : sv) {
397+
switch (c) {
398+
case '-':
399+
case '0':
400+
case '1':
401+
case '2':
402+
case '3':
403+
case '4':
404+
case '5':
405+
case '6':
406+
case '7':
407+
case '8':
408+
case '9':
409+
continue;
410+
default:
411+
// if we find something that is not a number such as 'e' or 'E' or '.'
412+
// there is no need to print the trailing ".".
413+
print_trailing_dot = false;
414+
}
415+
break; // we found something that is not a number, so we will not print
416+
// the trailing "."
417+
}
418+
if (print_trailing_dot){
419+
s->AsRawOstream() << ".";
420+
}
421+
422+
s->AsRawOstream() << suffix;
423+
424+
}
425+
426+
341427
static offset_t FormatOCamlValue(const DataExtractor &DE, Stream *s,
342428
offset_t start_offset, uint64_t base_addr,
343429
ExecutionContextScope *exe_ctx_scope,
@@ -365,6 +451,16 @@ static offset_t FormatOCamlValue(const DataExtractor &DE, Stream *s,
365451
exe_ctx_scope->CalculateExecutionContext(exe_ctx);
366452
Process *process = exe_ctx.GetProcessPtr();
367453

454+
// max padding for floating point numbers
455+
TargetSP target_sp;
456+
if (exe_ctx_scope)
457+
target_sp = exe_ctx_scope->CalculateTarget();
458+
459+
std::optional<unsigned> format_max_padding;
460+
if (target_sp)
461+
format_max_padding = target_sp->GetMaxZeroPaddingInFloatFormat();
462+
463+
368464
if (process) {
369465
Status error;
370466
lldb::addr_t header = process->ReadPointerFromMemory(value - 8, error);
@@ -497,51 +593,40 @@ static offset_t FormatOCamlValue(const DataExtractor &DE, Stream *s,
497593
}
498594

499595
case 253: { // Double_tag
500-
union {
501-
double f;
502-
uint64_t i;
503-
} u;
504-
u.i = process->ReadUnsignedIntegerFromMemory(value, 8, 0, error);
505-
if (error.Fail()) {
596+
std::optional<llvm::APInt> apint = ReadAPIntFromMemory(DE, process, value, 8);
597+
if (!apint) {
506598
s->Printf("<could not read float>@");
507-
} else {
508-
// CR mshinwell: should probably use proper float printing code
509-
// elsewhere in this file
510-
s->Printf("%g", u.f);
511-
print_default = false;
599+
break;
512600
}
601+
const llvm::fltSemantics &semantics = llvm::APFloat::IEEEdouble();
602+
PrintAPIntAsFloat(s, *apint, semantics, format_max_padding);
603+
print_default = false;
513604
break;
514605
}
515606

516607
case 254: { // Double_array_tag
517608
// N.B. Empty float arrays have tag zero
518609
uint64_t wosize_to_print = wosize <= 10 ? wosize : 10;
519-
s->Printf("[|");
610+
print_default = false; // we still print the default if one of the fields fails
611+
s->Printf("[| ");
520612
for (uint64_t field = 0; field < wosize_to_print; field++) {
521-
union {
522-
double f;
523-
uint64_t i;
524-
} u;
525-
u.i = process->ReadUnsignedIntegerFromMemory(value, 8, 0, error);
526-
if (error.Fail()) {
613+
std::optional<llvm::APInt> apint = ReadAPIntFromMemory(DE, process, value + field * 8, 8);
614+
if (!apint) {
527615
s->Printf("<could not read floatarray field %" PRIu64 ">", field);
616+
print_default = true;
528617
} else {
529-
// CR mshinwell: should probably use proper float printing code
530-
// elsewhere in this file
531-
s->Printf("%g", u.f);
618+
const llvm::fltSemantics &semantics = llvm::APFloat::IEEEdouble();
619+
PrintAPIntAsFloat(s, *apint, semantics, format_max_padding);
532620
}
533621

534622
if (field < wosize_to_print - 1)
535-
s->Printf(", ");
623+
s->Printf("; ");
536624
}
537625
if (wosize_to_print < wosize) {
538-
s->Printf(", <%" PRIu64 " more elements in floatarray>",
626+
s->Printf("; <%" PRIu64 " more elements in floatarray>",
539627
wosize - wosize_to_print);
540628
}
541-
s->Printf("|]");
542-
543-
if (!error.Fail())
544-
print_default = false;
629+
s->Printf(" |]");
545630
break;
546631
}
547632

@@ -612,7 +697,17 @@ static offset_t FormatOCamlValue(const DataExtractor &DE, Stream *s,
612697
(void *)bigarray_data_ptr);
613698
print_default = false;
614699
}
615-
} else {
700+
} else if (identifier_str == "_f32") {
701+
std::optional<llvm::APInt> apint = ReadAPIntFromMemory(DE, process, value + 8, 4);
702+
if (!apint) {
703+
s->Printf("<could not read float32>@");
704+
break;
705+
}
706+
const llvm::fltSemantics &semantics = llvm::APFloat::IEEEsingle();
707+
PrintAPIntAsFloat(s, *apint, semantics, format_max_padding, "", "s");
708+
print_default = false;
709+
}
710+
else {
616711
// CR mshinwell: check about converting Address.t to (void*)
617712
s->Printf("<custom|\"%s\")>@", identifier_str.c_str());
618713
}
@@ -987,9 +1082,6 @@ lldb::offset_t lldb_private::DumpDataExtractor(
9871082
if (target_sp)
9881083
format_max_padding = target_sp->GetMaxZeroPaddingInFloatFormat();
9891084

990-
// Show full precision when printing float values
991-
const unsigned format_precision = 0;
992-
9931085
const llvm::fltSemantics &semantics =
9941086
GetFloatSemantics(target_sp, item_byte_size);
9951087

@@ -1001,13 +1093,12 @@ lldb::offset_t lldb_private::DumpDataExtractor(
10011093
std::optional<llvm::APInt> apint =
10021094
GetAPInt(DE, &offset, semantics_byte_size);
10031095
if (apint) {
1004-
llvm::APFloat apfloat(semantics, *apint);
1005-
llvm::SmallVector<char, 256> sv;
1006-
if (format_max_padding)
1007-
apfloat.toString(sv, format_precision, *format_max_padding);
1008-
else
1009-
apfloat.toString(sv, format_precision);
1010-
s->AsRawOstream() << sv;
1096+
std::string suffix = "";
1097+
if (semantics_byte_size == 4){
1098+
suffix = "s";
1099+
}
1100+
PrintAPIntAsFloat(s, *apint, semantics,
1101+
format_max_padding, "#", suffix);
10111102
} else {
10121103
s->Format("error: unsupported byte size ({0}) for float format",
10131104
item_byte_size);

0 commit comments

Comments
 (0)