Skip to content

[LLDB] Add more helper functions to ValueObject class. #87197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 13, 2024

Conversation

cmtice
Copy link
Contributor

@cmtice cmtice commented Mar 31, 2024

Create additional helper functions for the ValueObject class, for:

  • returning the value as an APSInt or APFloat
  • additional type casting options
  • additional ways to create ValueObjects from various types of data
  • dereferencing a ValueObject

These helper functions are needed for implementing the Data Inspection Language, described in
https://discourse.llvm.org/t/rfc-data-inspection-language/69893

Create additional helper functions for the ValueObject class, for:
  - returning the value as an APSInt or APFloat
  - additional type casting options
  - additional ways to create ValueObjects from various types of data
  - dereferencing a ValueObject

These helper functions are needed for implementing the Data Inspection
Language, described in
https://discourse.llvm.org/t/rfc-data-inspection-language/69893
@cmtice cmtice requested a review from JDevlieghere as a code owner March 31, 2024 18:02
@llvmbot llvmbot added the lldb label Mar 31, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 31, 2024

@llvm/pr-subscribers-lldb

Author: None (cmtice)

Changes

Create additional helper functions for the ValueObject class, for:

  • returning the value as an APSInt or APFloat
  • additional type casting options
  • additional ways to create ValueObjects from various types of data
  • dereferencing a ValueObject

These helper functions are needed for implementing the Data Inspection Language, described in
https://discourse.llvm.org/t/rfc-data-inspection-language/69893


Patch is 20.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/87197.diff

2 Files Affected:

  • (modified) lldb/include/lldb/Core/ValueObject.h (+61)
  • (modified) lldb/source/Core/ValueObject.cpp (+405)
diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h
index e7e35e2b2bffc0..0c8dbf384a326c 100644
--- a/lldb/include/lldb/Core/ValueObject.h
+++ b/lldb/include/lldb/Core/ValueObject.h
@@ -441,6 +441,19 @@ class ValueObject {
 
   virtual int64_t GetValueAsSigned(int64_t fail_value, bool *success = nullptr);
 
+  llvm::APSInt GetValueAsAPSInt();
+
+  llvm::APFloat GetValueAsFloat();
+
+  bool GetValueAsBool();
+
+  /// Update the value of the current object to be the integer in the 'value'
+  /// parameter.
+  void UpdateIntegerValue(const llvm::APInt &value);
+
+  /// Assign the integer value 'new_val_sp' to the current object.
+  void UpdateIntegerValue(lldb::ValueObjectSP new_val_sp);
+
   virtual bool SetValueFromCString(const char *value_str, Status &error);
 
   /// Return the module associated with this value object in case the value is
@@ -618,6 +631,24 @@ class ValueObject {
   virtual lldb::ValueObjectSP CastPointerType(const char *name,
                                               lldb::TypeSP &type_sp);
 
+  /// Return the target load address assocaited with this value object.
+  lldb::addr_t GetLoadAddress();
+
+  lldb::ValueObjectSP CastDerivedToBaseType(CompilerType type,
+                                            const std::vector<uint32_t> &idx);
+
+  lldb::ValueObjectSP CastBaseToDerivedType(CompilerType type, uint64_t offset);
+
+  lldb::ValueObjectSP CastScalarToBasicType(CompilerType type, Status &error);
+
+  lldb::ValueObjectSP CastEnumToBasicType(CompilerType type);
+
+  lldb::ValueObjectSP CastPointerToBasicType(CompilerType type);
+
+  lldb::ValueObjectSP CastIntegerOrEnumToEnumType(CompilerType type);
+
+  lldb::ValueObjectSP CastFloatToEnumType(CompilerType type, Status &error);
+
   /// If this object represents a C++ class with a vtable, return an object
   /// that represents the virtual function table. If the object isn't a class
   /// with a vtable, return a valid ValueObject with the error set correctly.
@@ -668,6 +699,32 @@ class ValueObject {
   CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data,
                             const ExecutionContext &exe_ctx, CompilerType type);
 
+  static lldb::ValueObjectSP
+  CreateValueObjectFromBytes(lldb::TargetSP target_sp, const void *bytes,
+                             CompilerType type);
+
+  static lldb::ValueObjectSP CreateValueObjectFromBytes(lldb::TargetSP target,
+                                                        const void *bytes,
+                                                        lldb::BasicType type);
+
+  static lldb::ValueObjectSP CreateValueObjectFromAPInt(lldb::TargetSP target,
+                                                        const llvm::APInt &v,
+                                                        CompilerType type);
+
+  static lldb::ValueObjectSP
+  CreateValueObjectFromAPFloat(lldb::TargetSP target, const llvm::APFloat &v,
+                               CompilerType type);
+
+  static lldb::ValueObjectSP CreateValueObjectFromPointer(lldb::TargetSP target,
+                                                          uintptr_t addr,
+                                                          CompilerType type);
+
+  static lldb::ValueObjectSP CreateValueObjectFromBool(lldb::TargetSP target,
+                                                       bool value);
+
+  static lldb::ValueObjectSP CreateValueObjectFromNullptr(lldb::TargetSP target,
+                                                          CompilerType type);
+
   lldb::ValueObjectSP Persist();
 
   /// Returns true if this is a char* or a char[] if it is a char* and
@@ -719,6 +776,10 @@ class ValueObject {
     ClearUserVisibleData(eClearUserVisibleDataItemsSummary);
   }
 
+  void SetDerefValobj(ValueObject *deref) { m_deref_valobj = deref; }
+
+  ValueObject *GetDerefValobj() { return m_deref_valobj; }
+
   void SetValueFormat(lldb::TypeFormatImplSP format) {
     m_type_format_sp = std::move(format);
     ClearUserVisibleData(eClearUserVisibleDataItemsValue);
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index f39bd07a255366..70cd3bdece8a40 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -1089,6 +1089,116 @@ int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) {
   return fail_value;
 }
 
+llvm::APSInt ValueObject::GetValueAsAPSInt() {
+  lldb::TargetSP target = GetTargetSP();
+  uint64_t byte_size = 0;
+  if (auto temp = GetCompilerType().GetByteSize(target.get()))
+    byte_size = temp.value();
+
+  unsigned bit_width = static_cast<unsigned>(byte_size * CHAR_BIT);
+  bool success = true;
+  uint64_t fail_value = 0;
+  uint64_t ret_val = GetValueAsUnsigned(fail_value, &success);
+  uint64_t new_value = fail_value;
+  if (success)
+    new_value = ret_val;
+  bool is_signed = GetCompilerType().IsSigned();
+
+  return llvm::APSInt(llvm::APInt(bit_width, new_value, is_signed), !is_signed);
+}
+
+llvm::APFloat ValueObject::GetValueAsFloat() {
+  lldb::BasicType basic_type =
+      GetCompilerType().GetCanonicalType().GetBasicTypeEnumeration();
+  lldb::DataExtractorSP data_sp(new DataExtractor());
+  Status error;
+
+  switch (basic_type) {
+  case lldb::eBasicTypeFloat: {
+    float v = 0;
+    GetData(*data_sp, error);
+    assert(error.Success() && "Unable to read float data from value");
+
+    lldb::offset_t offset = 0;
+    uint32_t old_offset = offset;
+    void *ok = nullptr;
+    ok = data_sp->GetU8(&offset, (void *)&v, sizeof(float));
+    assert(offset != old_offset && ok != nullptr && "unable to read data");
+
+    return llvm::APFloat(v);
+  }
+  case lldb::eBasicTypeDouble:
+    // No way to get more precision at the moment.
+  case lldb::eBasicTypeLongDouble: {
+    double v = 0;
+    GetData(*data_sp, error);
+    assert(error.Success() && "Unable to read long double data from value");
+
+    lldb::offset_t offset = 0;
+    uint32_t old_offset = offset;
+    void *ok = nullptr;
+    ok = data_sp->GetU8(&offset, (void *)&v, sizeof(double));
+    assert(offset != old_offset && ok != nullptr && "unable to read data");
+
+    return llvm::APFloat(v);
+  }
+  default:
+    return llvm::APFloat(NAN);
+  }
+}
+
+bool ValueObject::GetValueAsBool() {
+  CompilerType val_type = GetCompilerType();
+  if (val_type.IsInteger() || val_type.IsUnscopedEnumerationType() ||
+      val_type.IsPointerType()) {
+    return GetValueAsAPSInt().getBoolValue();
+  }
+  if (val_type.IsFloat()) {
+    return GetValueAsFloat().isNonZero();
+  }
+  if (val_type.IsArrayType()) {
+    lldb::ValueObjectSP new_val =
+        ValueObject::ValueObject::CreateValueObjectFromAddress(
+            GetName().GetStringRef(), GetAddressOf(), GetExecutionContextRef(),
+            val_type);
+    return new_val->GetValueAsUnsigned(0) != 0;
+  }
+  return false;
+}
+
+void ValueObject::UpdateIntegerValue(const llvm::APInt &value) {
+  lldb::TargetSP target = GetTargetSP();
+  uint64_t byte_size = 0;
+  if (auto temp = GetCompilerType().GetByteSize(target.get()))
+    byte_size = temp.value();
+
+  assert(value.getBitWidth() == byte_size * CHAR_BIT &&
+         "illegal argument: new value should be of the same size");
+
+  lldb::DataExtractorSP data_sp;
+  Status error;
+  data_sp->SetData(value.getRawData(), byte_size,
+                   target->GetArchitecture().GetByteOrder());
+  data_sp->SetAddressByteSize(
+      static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize()));
+  SetData(*data_sp, error);
+}
+
+void ValueObject::UpdateIntegerValue(lldb::ValueObjectSP new_val_sp) {
+  CompilerType new_val_type = new_val_sp->GetCompilerType();
+  assert((new_val_type.IsInteger() || new_val_type.IsFloat() ||
+          new_val_type.IsPointerType()) &&
+         "illegal argument: new value should be of the same size");
+
+  if (new_val_type.IsInteger()) {
+    UpdateIntegerValue(new_val_sp->GetValueAsAPSInt());
+  } else if (new_val_type.IsFloat()) {
+    UpdateIntegerValue(new_val_sp->GetValueAsFloat().bitcastToAPInt());
+  } else if (new_val_type.IsPointerType()) {
+    UpdateIntegerValue(llvm::APInt(64, new_val_sp->GetValueAsUnsigned(0)));
+  }
+}
+
 // if any more "special cases" are added to
 // ValueObject::DumpPrintableRepresentation() please keep this call up to date
 // by returning true for your new special cases. We will eventually move to
@@ -2809,6 +2919,243 @@ ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) {
   return valobj_sp;
 }
 
+lldb::addr_t ValueObject::GetLoadAddress() {
+  lldb::addr_t addr_value = LLDB_INVALID_ADDRESS;
+  lldb::TargetSP target_sp = GetTargetSP();
+  if (target_sp) {
+    const bool scalar_is_load_address = true;
+    AddressType addr_type;
+    addr_value = GetAddressOf(scalar_is_load_address, &addr_type);
+    if (addr_type == eAddressTypeFile) {
+      lldb::ModuleSP module_sp(GetModule());
+      if (!module_sp)
+        addr_value = LLDB_INVALID_ADDRESS;
+      else {
+        Address tmp_addr;
+        module_sp->ResolveFileAddress(addr_value, tmp_addr);
+        addr_value = tmp_addr.GetLoadAddress(target_sp.get());
+      }
+    } else if (addr_type == eAddressTypeHost || addr_type == eAddressTypeHost)
+      addr_value = LLDB_INVALID_ADDRESS;
+  }
+  return addr_value;
+}
+
+lldb::ValueObjectSP
+ValueObject::CastDerivedToBaseType(CompilerType type,
+                                   const std::vector<uint32_t> &idx) {
+
+  lldb::TargetSP target = GetTargetSP();
+  assert((type.IsPointerType() || type.IsReferenceType()) &&
+         "invalid ast: target type should be a pointer or a reference");
+  assert(!idx.empty() && "invalid ast: children sequence should be non-empty");
+
+  // The `value` can be a pointer, but GetChildAtIndex works for pointers too.
+  lldb::ValueObjectSP inner_value;
+
+  for (const uint32_t i : idx) {
+    // Force static value, otherwise we can end up with the "real" type.
+    inner_value = GetChildAtIndex(i, /*can_create_synthetic*/ false);
+  }
+
+  // At this point type of `inner_value` should be the dereferenced target type.
+  CompilerType inner_value_type = inner_value->GetCompilerType();
+  if (type.IsPointerType()) {
+    assert(inner_value_type.CompareTypes(type.GetPointeeType()) &&
+           "casted value doesn't match the desired type");
+
+    uintptr_t addr = inner_value->GetLoadAddress();
+    return ValueObject::CreateValueObjectFromPointer(target, addr, type);
+  }
+
+  // At this point the target type should be a reference.
+  assert(inner_value_type.CompareTypes(type.GetNonReferenceType()) &&
+         "casted value doesn't match the desired type");
+
+  return lldb::ValueObjectSP(inner_value->Cast(type.GetNonReferenceType()));
+}
+
+lldb::ValueObjectSP ValueObject::CastBaseToDerivedType(CompilerType type,
+                                                       uint64_t offset) {
+  lldb::TargetSP target = GetTargetSP();
+
+  assert((type.IsPointerType() || type.IsReferenceType()) &&
+         "invalid ast: target type should be a pointer or a reference");
+
+  auto pointer_type =
+      type.IsPointerType() ? type : type.GetNonReferenceType().GetPointerType();
+
+  uintptr_t addr =
+      type.IsPointerType() ? GetValueAsUnsigned(0) : GetLoadAddress();
+
+  lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromPointer(
+      target, addr - offset, pointer_type);
+
+  if (type.IsPointerType()) {
+    return value;
+  }
+
+  // At this point the target type is a reference. Since `value` is a pointer,
+  // it has to be dereferenced.
+  Status error;
+  return value->Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObject::CastScalarToBasicType(CompilerType type,
+                                                       Status &error) {
+  assert(type.IsScalarType() && "target type must be an scalar");
+  assert(GetCompilerType().IsScalarType() && "argument must be a scalar");
+
+  lldb::TargetSP target = GetTargetSP();
+  if (type.IsBoolean()) {
+    if (GetCompilerType().IsInteger()) {
+      return ValueObject::CreateValueObjectFromBool(target,
+                                                    GetValueAsUnsigned(0) != 0);
+    }
+    if (GetCompilerType().IsFloat()) {
+      return ValueObject::CreateValueObjectFromBool(
+          target, !GetValueAsFloat().isZero());
+    }
+  }
+  if (type.IsInteger()) {
+    if (GetCompilerType().IsInteger()) {
+      uint64_t byte_size = 0;
+      if (auto temp = type.GetByteSize(target.get()))
+        byte_size = temp.value();
+      llvm::APSInt ext = GetValueAsAPSInt().extOrTrunc(byte_size * CHAR_BIT);
+      return ValueObject::CreateValueObjectFromAPInt(target, ext, type);
+    }
+    if (GetCompilerType().IsFloat()) {
+      uint64_t byte_size = 0;
+      if (auto temp = type.GetByteSize(target.get()))
+        byte_size = temp.value();
+      llvm::APSInt integer(byte_size * CHAR_BIT, !type.IsSigned());
+      bool is_exact;
+      llvm::APFloatBase::opStatus status = GetValueAsFloat().convertToInteger(
+          integer, llvm::APFloat::rmTowardZero, &is_exact);
+
+      // Casting floating point values that are out of bounds of the target type
+      // is undefined behaviour.
+      if (status & llvm::APFloatBase::opInvalidOp) {
+        error.SetErrorString("invalid type cast detected");
+      }
+
+      return ValueObject::CreateValueObjectFromAPInt(target, integer, type);
+    }
+  }
+  if (type.IsFloat()) {
+    if (GetCompilerType().IsInteger()) {
+      Scalar scalar_int(GetValueAsAPSInt());
+      llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt(
+          type.GetCanonicalType().GetBasicTypeEnumeration());
+      return ValueObject::CreateValueObjectFromAPFloat(target, f, type);
+    }
+    if (GetCompilerType().IsFloat()) {
+      Scalar scalar_float(GetValueAsFloat());
+      llvm::APFloat f = scalar_float.CreateAPFloatFromAPFloat(
+          type.GetCanonicalType().GetBasicTypeEnumeration());
+      return ValueObject::CreateValueObjectFromAPFloat(target, f, type);
+    }
+  }
+  assert(false && "invalid target type: must be a scalar");
+  return lldb::ValueObjectSP();
+}
+
+lldb::ValueObjectSP ValueObject::CastEnumToBasicType(CompilerType type) {
+  lldb::TargetSP target = GetTargetSP();
+
+  assert(type.IsScalarType() && "target type must be a scalar");
+  assert(GetCompilerType().IsEnumerationType() && "argument must be an enum");
+
+  if (type.IsBoolean()) {
+    return ValueObject::CreateValueObjectFromBool(target,
+                                                  GetValueAsUnsigned(0) != 0);
+  }
+
+  uint64_t byte_size = 0;
+  if (auto temp = type.GetByteSize(target.get()))
+    byte_size = temp.value();
+  // Get the value as APSInt and extend or truncate it to the requested size.
+  llvm::APSInt ext = GetValueAsAPSInt().extOrTrunc(byte_size * CHAR_BIT);
+
+  if (type.IsInteger()) {
+    return ValueObject::CreateValueObjectFromAPInt(target, ext, type);
+  }
+  if (type.IsFloat()) {
+    Scalar scalar_int(ext);
+    llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt(
+        type.GetCanonicalType().GetBasicTypeEnumeration());
+    return ValueObject::CreateValueObjectFromAPFloat(target, f, type);
+  }
+  assert(false && "invalid target type: must be a scalar");
+  return lldb::ValueObjectSP();
+}
+
+lldb::ValueObjectSP ValueObject::CastPointerToBasicType(CompilerType type) {
+  lldb::TargetSP target = GetTargetSP();
+
+  uint64_t type_byte_size = 0;
+  uint64_t val_byte_size = 0;
+  if (auto temp = type.GetByteSize(target.get()))
+    type_byte_size = temp.value();
+  if (auto temp = GetCompilerType().GetByteSize(target.get()))
+    val_byte_size = temp.value();
+  assert(type.IsInteger() && "target type must be an integer");
+  assert((type.IsBoolean() || type_byte_size >= val_byte_size) &&
+         "target type cannot be smaller than the pointer type");
+
+  if (type.IsBoolean()) {
+    return ValueObject::CreateValueObjectFromBool(target,
+                                                  GetValueAsUnsigned(0) != 0);
+  }
+
+  // Get the value as APSInt and extend or truncate it to the requested size.
+  llvm::APSInt ext = GetValueAsAPSInt().extOrTrunc(type_byte_size * CHAR_BIT);
+  return ValueObject::CreateValueObjectFromAPInt(target, ext, type);
+}
+
+lldb::ValueObjectSP
+ValueObject::CastIntegerOrEnumToEnumType(CompilerType type) {
+  lldb::TargetSP target = GetTargetSP();
+
+  assert(type.IsEnumerationType() && "target type must be an enum");
+  assert((GetCompilerType().IsInteger() ||
+          GetCompilerType().IsEnumerationType()) &&
+         "argument must be an integer or an enum");
+  uint64_t byte_size = 0;
+  if (auto temp = type.GetByteSize(target.get()))
+    byte_size = temp.value();
+
+  // Get the value as APSInt and extend or truncate it to the requested size.
+  llvm::APSInt ext = GetValueAsAPSInt().extOrTrunc(byte_size * CHAR_BIT);
+  return ValueObject::CreateValueObjectFromAPInt(target, ext, type);
+}
+
+lldb::ValueObjectSP ValueObject::CastFloatToEnumType(CompilerType type,
+                                                     Status &error) {
+  lldb::TargetSP target = GetTargetSP();
+
+  assert(type.IsEnumerationType() && "target type must be an enum");
+  assert(GetCompilerType().IsFloat() && "argument must be a float");
+
+  uint64_t byte_size = 0;
+  if (auto temp = type.GetByteSize(target.get()))
+    byte_size = temp.value();
+  llvm::APSInt integer(byte_size * CHAR_BIT, !type.IsSigned());
+  bool is_exact;
+
+  llvm::APFloatBase::opStatus status = GetValueAsFloat().convertToInteger(
+      integer, llvm::APFloat::rmTowardZero, &is_exact);
+
+  // Casting floating point values that are out of bounds of the target type
+  // is undefined behaviour.
+  if (status & llvm::APFloatBase::opInvalidOp) {
+    error.SetErrorString("invalid type cast detected");
+  }
+
+  return ValueObject::CreateValueObjectFromAPInt(target, integer, type);
+}
+
 ValueObject::EvaluationPoint::EvaluationPoint() : m_mod_id(), m_exe_ctx_ref() {}
 
 ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope,
@@ -3031,6 +3378,64 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromData(
   return new_value_sp;
 }
 
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromBytes(lldb::TargetSP target_sp,
+                                        const void *bytes, CompilerType type) {
+  ExecutionContext exe_ctx(
+      ExecutionContextRef(ExecutionContext(target_sp.get(), false)));
+  uint64_t byte_size = 0;
+  if (auto temp = type.GetByteSize(target_sp.get()))
+    byte_size = temp.value();
+  lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
+      bytes, byte_size, target_sp->GetArchitecture().GetByteOrder(),
+      static_cast<uint8_t>(target_sp->GetArchitecture().GetAddressByteSize()));
+  lldb::ValueObjectSP value =
+      ValueObject::CreateValueObjectFromData("result", *data_sp, exe_ctx, type);
+  return value;
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromBytes(
+    lldb::TargetSP target, const void *bytes, lldb::BasicType type) {
+  CompilerType target_type;
+  if (target) {
+    for (auto type_system_sp : target->GetScratchTypeSystems())
+      if (auto compiler_type = type_system_sp->GetBasicTypeFromAST(type)) {
+        target_type = compiler_type;
+        break;
+      }
+  }
+  return CreateValueObjectFromBytes(target, bytes, target_type);
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPInt(
+    lldb::TargetSP target, const llvm::APInt &v, CompilerType type) {
+  return CreateValueObjectFromBytes(target, v.getRawData(), type);
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat(
+    lldb::TargetSP target, const llvm::APFloat &v, CompilerType type) {
+  return CreateValueObjectFromAPInt(target, v.bitcastToAPInt(), type);
+}
+
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromPointer(lldb::TargetSP target, uintptr_t addr,
+                                          CompilerType type) {
+  return CreateValueObjectFromBytes(target, &addr, type);
+}
+
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromBool(lldb::TargetSP target, bool value) {
+  return CreateValueObjectFromBytes(target, &value, lldb::eBasicTypeBool);
+}
+
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromNullptr(lldb::TargetSP target,
+                                          CompilerType type) {
+  assert(type.IsNullPtrType() && "target type must be nullptr");
+  uintptr_t z...
[truncated]

@@ -441,6 +441,19 @@ class ValueObject {

virtual int64_t GetValueAsSigned(int64_t fail_value, bool *success = nullptr);

llvm::APSInt GetValueAsAPSInt();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all SBValues are representable as Int's or Floats, Bool's etc. But I don't see any way to report errors with these new API's.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if these errors matter to users then llvm::Expected<llvm::APSInt> would be the right choice. Otherwise std::optional is the way to go.


lldb::ValueObjectSP CastIntegerOrEnumToEnumType(CompilerType type);

lldb::ValueObjectSP CastFloatToEnumType(CompilerType type, Status &error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need an error parameter for anything that returns a ValueObject. ValueObjects carry their errors in the ValueObject (ValueObject::GetError()).

@@ -668,6 +699,32 @@ class ValueObject {
CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data,
const ExecutionContext &exe_ctx, CompilerType type);

static lldb::ValueObjectSP
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given targets can be big or little-endian, I'm a little worried about a "const void *bytes" that says nothing about what is in the bytes. This is what DataBuffer's are for, why couldn't you use them?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on this, but why can't we just use the above CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type);? The DataExtractor in the mentioned existing API can own or share a pointer to data. Would the "const void *bytes" always be copied into the internal data in the ValueObject in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateValueObjectFromBytes was being called from CreateValueObjectfrom{APInt,Pointer,Bool,Nullptr}, receivng the pointer to the appropriate values in the input parameter, putting the bytes into a DataExtractor, then calling CreateValueObjectFromData with the DataExtractor. I thought that would handle things like correct endianness, etc. However, I will move the "putting the bytes into the DataExtractor" into the callers for CreateValueObjectFromBytes & call CreateValueObjectFromData directly from each of them. This leads to a bit of code duplication, but I can see that it's safer. (This eliminates the CreateValueObjectFromBytes functions altogether).

@@ -441,6 +441,19 @@ class ValueObject {

virtual int64_t GetValueAsSigned(int64_t fail_value, bool *success = nullptr);

llvm::APSInt GetValueAsAPSInt();

llvm::APFloat GetValueAsFloat();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


llvm::APFloat GetValueAsFloat();

bool GetValueAsBool();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

CreateValueObjectFromBytes(lldb::TargetSP target_sp, const void *bytes,
CompilerType type);

static lldb::ValueObjectSP CreateValueObjectFromBytes(lldb::TargetSP target,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions could all benefit from Doxygen comments. If we can use a StringRef, or DataExtractor/Buffer instead a raw pointer, that would likely also be safer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

CreateValueObjectFromAPFloat(lldb::TargetSP target, const llvm::APFloat &v,
CompilerType type);

static lldb::ValueObjectSP CreateValueObjectFromPointer(lldb::TargetSP target,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different from CreateValueObjectFromAddress?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we already have this API unless this is doing things differently. That other API allows a name to be specified and is used for synthetic children, so you should be able to use it and specify no name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"CreateValueObjectFromPointer" is different from "CreateValueObjectFromAddresss" In that the former creates a value object whose value is the pointer address; the latter creates a value object whose value is whatever the pointer points to (it dereferences the pointer). I can update CreateValueObjectFromAddress to take a parameter telling it whether to dereference the pointer or use the pointer address as the value, if you would prefer.

@@ -719,6 +776,10 @@ class ValueObject {
ClearUserVisibleData(eClearUserVisibleDataItemsSummary);
}

void SetDerefValobj(ValueObject *deref) { m_deref_valobj = deref; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthetic child providers also have a way to provide the deref ValueObject dynamically. How do these two ways of doing that job work together?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use the synthetic child providers way of getting the deref ValueObject to get the value I use for my call to SetDerefValobj (when I'm converting a ValueObject containing a SmartPointer into a ValueObject containing the pointer the SmartPointer was referencing).

Copy link
Member

@bulbazord bulbazord left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to all of Jim and Adrian's comments.

High level comments:

  1. I'm a little concerned about the use of assertions in this patch. I generally like assertions when they are used to assert internal consistency. It looks like you're using them as an error-checking and parameter-checking mechanism. This seems pretty fragile, especially since it will be difficult to enforce that these functions are called "correctly" in all cases from all contributors.

  2. There isn't a lot of documentation. It would be helpful to know the expected behavior for a lot of these methods (especially for people like me who aren't super familiar with the ValueObject class and all of its tendrils).

  3. Is there a way you can add some tests for the new functionality? I'm not sure how you could trigger these new methods from an API test, but a unit test might be possible?

new_value = ret_val;
bool is_signed = GetCompilerType().IsSigned();

return llvm::APSInt(llvm::APInt(bit_width, new_value, is_signed), !is_signed);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If GetValueAsUnsigned fails, this will give you an APSInt that looks like 0, meaning there's no way to distinguish between a ValueObject that represents 0 and a failure value.
Suggestion: Either return an invalid APSInt (via default constructor) or return a std::optional<APSInt> or llvm::Expected<APSInt> if you want an error message back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to GetValueAsUnsigned included a boolean, 'success' which would be set correctly if the call fails, and which I was checking upon return. But, if I use Greg's code from above then I omit the call to GetValueAsUnsigned, anyway. And yes, I am converting these GetValueAs... functions to return an llvm::Expected<> value.

return llvm::APFloat(v);
}
default:
return llvm::APFloat(NAN);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is NAN coming from? I don't see it in this file or the APFloat header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was coming from a stdlib definition; but I'm updating the code to set an error message here instead.

return llvm::APSInt(llvm::APInt(bit_width, new_value, is_signed), !is_signed);
}

llvm::APFloat ValueObject::GetValueAsFloat() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues and a small nitpick:
Nit: The name could be GetValueAsAPFloat to differentiate between it and a potential function that would return a float/double?

  1. I don't think assertions are the right way to make sure we have good data. GetData has error handling through its Status & out parameter, we should handle that appropriately instead of asserting that we got good data. What happens when we compile out the assertions for release builds? Reading data from the DataExtractor will fail and crash regardless.
  2. This function has no way of returning a value that would indicate failure.

Suggestion: Return std::optional<llvm::APFloat> or llvm::Expected<llvm::APFloat>. There's no way to return an invalid APFloat from looking at the header, so that doesn't appear to be an option. Then if any of the steps fail, you can return either std::nullopt or an appropriate llvm::Error.

if (auto temp = GetCompilerType().GetByteSize(target.get()))
byte_size = temp.value();

assert(value.getBitWidth() == byte_size * CHAR_BIT &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of asserting this, why not have this function return an llvm::Error? If this invariant is not met, this can return this exact error message.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, no asserts and add error propagation.

assert(type.IsEnumerationType() && "target type must be an enum");
assert((GetCompilerType().IsInteger() ||
GetCompilerType().IsEnumerationType()) &&
"argument must be an integer or an enum");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same w/ these assertions

lldb::TargetSP target = GetTargetSP();

assert(type.IsEnumerationType() && "target type must be an enum");
assert(GetCompilerType().IsFloat() && "argument must be a float");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same w/ these assertions

lldb::ValueObjectSP
ValueObject::CreateValueObjectFromNullptr(lldb::TargetSP target,
CompilerType type) {
assert(type.IsNullPtrType() && "target type must be nullptr");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this assertion


lldb::ValueObjectSP CastEnumToBasicType(CompilerType type);

lldb::ValueObjectSP CastPointerToBasicType(CompilerType type);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is BasicType in this context? I know there's lldb::BasicType, but these methods take CompilerTypes.

CreateValueObjectFromBytes(lldb::TargetSP target_sp, const void *bytes,
CompilerType type);

static lldb::ValueObjectSP CreateValueObjectFromBytes(lldb::TargetSP target,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


llvm::APFloat GetValueAsFloat();

bool GetValueAsBool();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need GetValueAsBool() or can we just use GetValueAsAPSInt() and make sure bool gets converted to a suitable integer?

Copy link
Contributor Author

@cmtice cmtice Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to have GetValueAsBool(). Otherwise I will need to do integer-to-bool conversions in the places where I call this function; also it makes the code more clear, readable and maintainable. Also, my function handles more object types than just integers. But if you feel very strongly about this, I can fall back on GetValueAsAPSInt.


/// Update the value of the current object to be the integer in the 'value'
/// parameter.
void UpdateIntegerValue(const llvm::APInt &value);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we name this similar to SetValueFromCString(...)? Something like: SetValue(const llvm::APInt &value) or SetValueFromInteger(const llvm::APInt &value)? Do we want an error to be returned in case this fails?

Comment on lines 454 to 455
/// Assign the integer value 'new_val_sp' to the current object.
void UpdateIntegerValue(lldb::ValueObjectSP new_val_sp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this function actually do? Are we wanting the current ValueObject to copy the integer value from lldb::ValueObjectSP new_val_sp? Seems like we should just be able to use the above void UpdateIntegerValue(const llvm::APInt &value);? Can you elaborate on why this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this assigns the value from new_val_sp to the current (integer) ValueObject. The main part of this function does call the preceding one. Because new_val_sp can actually contain an integer, a float or a pointer, there's some slightly different manipulations of the value that needs to happen before calling the preceding function (take a look at the code in ValueObject.cpp), which is why this function was written.

@@ -618,6 +631,24 @@ class ValueObject {
virtual lldb::ValueObjectSP CastPointerType(const char *name,
lldb::TypeSP &type_sp);

/// Return the target load address assocaited with this value object.
lldb::addr_t GetLoadAddress();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function just a copy of the contents of lldb::addr_t SBValue::GetLoadAddress()? If so, we should also fix that function to call this function. Might be nice to have this return std::optional<lldb::addr_t> for internal use as if the ValueObject is a variable that is in a register, this will need to return LLDB_INVALID_ADDRESS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is based on SBValue::GetLoadAddress; I'll update that one to call this one. As written, it already does return LLDB_INVALID_ADDRESS in certain cases...I'm not sure what you want me to do differently (probably better to discuss this at the implementation rather than the declaration).

lldb::addr_t GetLoadAddress();

lldb::ValueObjectSP CastDerivedToBaseType(CompilerType type,
const std::vector<uint32_t> &idx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use const llvm::ArrayRef<uint32_t> instead of const std::vector<uint32_t>? Some headerdoc on what this does might be nice. I would guess idx contains a base class index path? Maybe renamed idx to something more descriptive?

Comment on lines 1159 to 1165
if (val_type.IsArrayType()) {
lldb::ValueObjectSP new_val =
ValueObject::ValueObject::CreateValueObjectFromAddress(
GetName().GetStringRef(), GetAddressOf(), GetExecutionContextRef(),
val_type);
return new_val->GetValueAsUnsigned(0) != 0;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this attempting to do? It looks like this is taking an array type and returning an array pointer and trying to return true if the array is in memory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm...What I was trying to do (and I can see I didn't do it well here) is to check to see if the array had a real value(pointer address) or not. I'm updating the code to be cleaner and more clear.

if (auto temp = GetCompilerType().GetByteSize(target.get()))
byte_size = temp.value();

assert(value.getBitWidth() == byte_size * CHAR_BIT &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, no asserts and add error propagation.

return false;
}

void ValueObject::UpdateIntegerValue(const llvm::APInt &value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I look at this API, I would expect this function to change the integer value of any ValueObject. The value object might have a variable that lives in memory (on the stack or heap), or if it lives in a register (update the register) or if there is already a local buffer, replace the bytes in the local buffer.

This function seems to replace the integer value always with a local buffer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is to only call this function on ValueObjects that are intermediate objects in the new parser/evaluator, not ValueObjects that are associated with program variables. I'll add a check for that while I'm updating the other asserts & error checks. So no, this function should not update values in memory, heap or register, only in the ValueObject itself.

Comment on lines 1178 to 1181
lldb::DataExtractorSP data_sp;
Status error;
data_sp->SetData(value.getRawData(), byte_size,
target->GetArchitecture().GetByteOrder());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will crash, data_sp is never constructed or assigned. We also need to figure out if we are going to want the integer value to be replaced always with a local copy, or updated in the actual program memory, register, or local buffer.

I realize many of these APIs are probably only intended to be used in the new ValueObject based expression parser, but there are APIs being added to ValueObject so they should work regardless of where they come from or originate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the record: This call is not crashing for me; I am calling this code in my light-weight parser/evaluator, and it seems to work just fine.

I am ok with changing this code, if you really think it should be changed, but I'd appreciate suggestions on how to make this work "properly" -- I've been looking through ValueObject.cpp & ValueObject.h and not seeing much that looks helpful to me. This does seem to be the way SBValue::SetData works (which was the original basis for this function...)

module_sp->ResolveFileAddress(addr_value, tmp_addr);
addr_value = tmp_addr.GetLoadAddress(target_sp.get());
}
} else if (addr_type == eAddressTypeHost || addr_type == eAddressTypeHost)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check for eAddressTypeHost, I am guessing the second one was meant to be eAddressTypeInvalid. We should also change lldb::addr_t SBValue::GetLoadAddress() to call this function

@jimingham
Copy link
Collaborator

jimingham commented Apr 3, 2024 via email

@cmtice
Copy link
Contributor Author

cmtice commented Apr 3, 2024

Most of my error checking was being done in the code that calls these functions, rather than in the functions directly, but I see your point. I will update the functions to do the error checking & reporting directly themselves.

cmtice added 2 commits April 8, 2024 10:11
Fix various issues in  GetValueAsAPSInt, GetValueAsAPFloat,
GetValueAsBool, UpdateIntegerValue (renamed to SetValueFromInteger),
and GetLoadAddress:

- Added error checking & reporting.
- Removed asserts.
- Added doxygen comments.
- Renamed UpdateIntegerValue to SetValueFromInteger
- Renamed GetValueAsFloat to GetValueAsAPFloat
- Updated code in GetValue... functions to use existing Scalar values
  where possibled.
- Updated SBValue::GetLoadAddress to call ValueObject::GetLoadAddress.
@cmtice
Copy link
Contributor Author

cmtice commented Apr 8, 2024

I think I've addressed all the review comments for GetValueAsAPSInt, GetValueAsAPFloat, GetValueAsBool, UpdateIntegerValue (now SetValueFromInteger) and GetLoadAddress. I apologize if I missed anything. Please take another look at those functions.

I'm still working on fixing the issues with the other functions. :-)

cmtice added 2 commits April 22, 2024 21:33
- Update CreateValueObjectFromAddress to optionally not dereference the address
  before creating the value (i.e. create a value object containing the addres
  itself). This removes the need for CreateValueObjectFromPointer.
- Remove CreateValueObjectFromPointer
- Remove CreateValueObjectFromBytes functions, moving the DataExtractor into
  the callers & having them call CreateValueObjectFromData directly.
- Add 'name' parameter to the next CreateValueObjectFrom... functions.
- Remove assert stetement.
- Add Doxygen comments.
@cmtice
Copy link
Contributor Author

cmtice commented Apr 23, 2024

I've now addressed the comments for the "CreateValueObjectFrom..." functions (I think).

I will not be able to work on this for the next couple of weeks, but I'll get back to it and fix the rest as soon as I can.

cmtice added 2 commits May 19, 2024 12:37
Add doxygen comments for cast functions; reduce number of cast functions
by combining some; fix error reporting in cast functions and remove all
assertions.
@cmtice
Copy link
Contributor Author

cmtice commented May 22, 2024

I'm sorry this has taken me so long, but I believe I have addressed/fixed everyone's comments now; if I missed any please let me know. Please review this PR again.

Fix remaining issues wit 'cast' functions:
- Update parameter type checking
- Update error messages & error status checking
- Correctly update 'inner_value' in 'CastDerivedToBaseType'
@cmtice
Copy link
Contributor Author

cmtice commented Jun 5, 2024

I've updated with a few minor fixes. I've addressed all the review comments (I think). I've verified/tested that all the casting, etc. works properly. Please review/approve this. Thanks!


for (const uint32_t i : base_type_indices)
// Force static value, otherwise we can end up with the "real" type.
inner_value = GetChildAtIndex(i, /*can_create_synthetic*/ false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this change. How is getting this->GetSP()->GetChildAtIndex different from this->GetChildAtIndex?

The other change here seems to be to change can_create_synthetic from false to true. IIUC, the comment directly above this change says why it chose false here, though it is a somewhat confusing comment. Since you seem to disagree with that comment, you should probably change the comment to say why you think true is the right value here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is that now in each loop iteration the rhs is 'inner_loop->GetChildAtIndex...', which will be different each iteration through the loop; before it was just 'GetChildAtIndex', which did not change on each iteration.

re false->true: The original lldb-eval code on which that was based used 'false', but it was also making the call through the SB API interface (SBValues, SBTypes, etc). I found that the cast test cases passed with lldb-eval when this value was false, but failed in my translated, migrated version. Setting this to true allows those failing test cases to pass.

I don't have any easy way to add those test cases to this PR because they all use 'frame variable' (using my DIL implementation) for doing the casts...But I do have them, and I am running (and passing) them.

Copy link
Collaborator

@jimingham jimingham Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that I was looking at a diff against your original diff which didn't make sense. In context the code seems fine.

But then that also showed that you are the author of the "somewhat confusing comment". Can you make that comment more helpful? Suppressing the synthetic child production would force this child to get its type directly from the CompilerType, which I would call the "real" child. So that comment seems to make sense when we passed true but doesn't make sense when passing true.

You don't need to introduce tests for this at this stage if that's dependent on other changes, but this seems like a tricky point, so it should have a comment. But this one doesn't make sense to me. And it doesn't explain why getting the real type would be bad.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -3138,13 +3141,13 @@ lldb::ValueObjectSP ValueObject::CastToBasicType(CompilerType type) {
val_byte_size = temp.value();

if (is_pointer) {
if (type.IsBoolean() && type_byte_size < val_byte_size) {
if (!type.IsBoolean() && type_byte_size < val_byte_size) {
Copy link
Collaborator

@jimingham jimingham Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what the original code intended with that type.IsBoolean? Makes no sense we would only return the "too small" error when the incoming type is Boolean.

But with this code, if I pass 32 bit float (on a 64 bit pointers system), the error I'll get is that it's too small. OTOH, if I pass a 64 bit float, I'll get "must be integer". That's confusing. If you aren't planning to take anything but integers, it seems like you should check that first, then check the size.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

cmtice added 2 commits June 6, 2024 05:24
Re-order checks for input type and size in CastToBasicType.
if (!type.IsInteger()) {
m_error.SetErrorString("target type must be an integer");
return GetSP();
}
if (!type.IsBoolean() && type_byte_size < val_byte_size) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's better, but now the type.IsBoolean is entirely pointless, since you would never get to this code in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops! Good catch; I've fixed that now.

Fix type-checking error introduced with previous re-ordering fix.
uint64_t num_bits = 0;
if (auto temp = new_val_sp->GetCompilerType().GetBitSize(target.get()))
num_bits = temp.value();
// SetValueFromInteger(llvm::APInt(64, int_val), error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this leftover from some experiment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes. I thought I had found and removed all of those, but it looks like I missed one. I'll remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@jimingham
Copy link
Collaborator

There was one comment that should probably be removed. This looks okay to me at this stage.

Delete a commented-out line of code.
@cmtice
Copy link
Contributor Author

cmtice commented Jun 12, 2024

There was one comment that should probably be removed. This looks okay to me at this stage.

Jim, If this looks ok to you, could you officially mark it as approved please?

@cmtice
Copy link
Contributor Author

cmtice commented Jun 12, 2024

Review ping? Does this look ok to everyone now? I would really like to get this PR committed (I have more waiting behind it). Thanks in advance!

Copy link
Collaborator

@jimingham jimingham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM at this point.

@cmtice cmtice merged commit 9f70cd8 into llvm:main Jun 13, 2024
5 checks passed
EthanLuisMcDonough pushed a commit to EthanLuisMcDonough/llvm-project that referenced this pull request Aug 13, 2024
Create additional helper functions for the ValueObject class, for:
  - returning the value as an APSInt or APFloat
  - additional type casting options
  - additional ways to create ValueObjects from various types of data
  - dereferencing a ValueObject

These helper functions are needed for implementing the Data Inspection
Language, described in
https://discourse.llvm.org/t/rfc-data-inspection-language/69893

if (!type.IsScalarType()) {
m_error.SetErrorString("target type must be a scalar");
return GetSP();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @cmtice! I just came across this code and I don't think this API makes sense. Since CastToBasicType creates a new casted ValueObject I think this method should be const and not modify the error of the original value when the cast fails. I think it should return a ValueObjectConstResult with the error instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I have tried to do what I think you were asking me to do. Please see #117401

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants