Skip to content

[lldb][DataFormatter] VectorType: fix format for arrays with size not a power-of-2 #68907

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 5 commits into from
Oct 13, 2023

Conversation

Michael137
Copy link
Member

@Michael137 Michael137 commented Oct 12, 2023

To get the number of children for a VectorType (i.e.,
a type declared with a vector_size/ext_vector_type attribute)
LLDB previously did following calculation:

  1. Get byte-size of the vector container from Clang (getTypeInfo).
  2. Get byte-size of the element type we want to interpret the array as.
    (e.g., sometimes we want to interpret an unsigned char vec[16]
    as a float32[]).
  3. numChildren = containerSize / reinterpretedElementSize

However, for step 1, clang will return us the aligned container byte-size.
So for a type such as float __attribute__((ext_vector_type(3)))
(which is an array of 3 4-byte floats), clang will round up the
byte-width of the array to 16.
(see here)

This means that for vectors where the size isn't a power-of-2, LLDB
will miscalculate the number of elements.

Solution

This patch changes step 1 such that we calculate the container size
as numElementsInSource * byteSizeOfElement.

…unction

In preparation for changing this function I cleaned
it up so the conditions under which the function
returns what are clearer.
To get the number of children for a VectorType (i.e.,
a type declared with a `vector_size`/`ext_vector_type` attribute)
LLDB previously did following calculation:
1. Get byte-size of the vector container from Clang (`getTypeInfo`).
2. Get byte-size of the element type we want to interpret the array as.
   (e.g., sometimes we want to interpret an `unsigned char vec[16]`
   as a `float32[]`).
3. `numChildren = containerSize / reinterpretedElementSize`

However, for step 1, clang will return us the *aligned* container byte-size.
So for a type such as `float __attribute__((ext_vector_type(3)))`
(which is an array of 3 4-byte floats), clang will round up the
bit-width of the array to `16`.
(see [here](https://github.com/llvm/llvm-project/blob/ab6a66dbec61654d0962f6abf6d6c5b776937584/clang/lib/AST/ASTContext.cpp#L1987-L1992))

This means that for vectors where the size isn't a power-of-2, LLDB
will miscalculate the number of elements.

**Solution**

This patch changes step 1 such that we calculate the container size
as `numElementsInSource * byteSizeOfElement`. This mimicks the typical
`sizeof(array) / sizeof(array[0])` calculation in C.
@llvmbot
Copy link
Member

llvmbot commented Oct 12, 2023

@llvm/pr-subscribers-lldb

Author: Michael Buch (Michael137)

Changes

To get the number of children for a VectorType (i.e.,
a type declared with a vector_size/ext_vector_type attribute)
LLDB previously did following calculation:

  1. Get byte-size of the vector container from Clang (getTypeInfo).
  2. Get byte-size of the element type we want to interpret the array as.
    (e.g., sometimes we want to interpret an unsigned char vec[16]
    as a float32[]).
  3. numChildren = containerSize / reinterpretedElementSize

However, for step 1, clang will return us the aligned container byte-size.
So for a type such as float __attribute__((ext_vector_type(3)))
(which is an array of 3 4-byte floats), clang will round up the
bit-width of the array to 16.
(see here)

This means that for vectors where the size isn't a power-of-2, LLDB
will miscalculate the number of elements.

Solution

This patch changes step 1 such that we calculate the container size
as numElementsInSource * byteSizeOfElement. This mimicks the typical
sizeof(array) / sizeof(array[0]) calculation in C.


Full diff: https://github.com/llvm/llvm-project/pull/68907.diff

3 Files Affected:

  • (modified) lldb/source/DataFormatters/VectorType.cpp (+47-17)
  • (modified) lldb/test/API/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py (+7)
  • (modified) lldb/test/API/functionalities/data-formatter/vector-types/main.cpp (+3-1)
diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp
index 4afcfa2e8e490e0..93eea9507242a9a 100644
--- a/lldb/source/DataFormatters/VectorType.cpp
+++ b/lldb/source/DataFormatters/VectorType.cpp
@@ -169,21 +169,49 @@ static lldb::Format GetItemFormatForFormat(lldb::Format format,
   }
 }
 
-static size_t CalculateNumChildren(
-    CompilerType container_type, CompilerType element_type,
-    lldb_private::ExecutionContextScope *exe_scope =
-        nullptr // does not matter here because all we trade in are basic types
-    ) {
-  std::optional<uint64_t> container_size =
-      container_type.GetByteSize(exe_scope);
-  std::optional<uint64_t> element_size = element_type.GetByteSize(exe_scope);
-
-  if (container_size && element_size && *element_size) {
-    if (*container_size % *element_size)
-      return 0;
-    return *container_size / *element_size;
-  }
-  return 0;
+/// \brief Returns the number of elements stored in a container
+/// (with element type 'container_elem_type') as if it had elements
+/// of type 'element_type'.
+///
+/// For example, a container of type
+/// `uint8_t __attribute__((vector_size(16)))` has 16 elements.
+/// But calling `CalculateNumChildren` with an 'element_type'
+/// of `float` (4-bytes) will return `4` because we are interpreting
+/// the byte-array as a `float32[]`.
+///
+/// \param[in] container_elem_type The type of the elements stored
+/// in the container we are calculating the children of.
+///
+/// \param[in] num_elements Number of 'container_elem_type's our
+/// container stores.
+///
+/// \param[in] element_type The type of elements we interpret
+/// container_type to contain for the purposes of calculating
+/// the number of children.
+///
+/// If size of the container is not a multiple of 'element_type'
+/// returns 0.
+///
+/// On error, returns 0.
+static size_t CalculateNumChildren(CompilerType container_elem_type,
+                                   uint64_t num_elements,
+                                   CompilerType element_type) {
+  std::optional<uint64_t> container_elem_size =
+      container_elem_type.GetByteSize(/* exe_scope */ nullptr);
+  if (!container_elem_size)
+    return 0;
+
+  auto container_size = *container_elem_size * num_elements;
+
+  std::optional<uint64_t> element_size =
+      element_type.GetByteSize(/* exe_scope */ nullptr);
+  if (!element_size || !*element_size)
+    return 0;
+
+  if (container_size % *element_size)
+    return 0;
+
+  return container_size / *element_size;
 }
 
 namespace lldb_private {
@@ -221,11 +249,13 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
     m_parent_format = m_backend.GetFormat();
     CompilerType parent_type(m_backend.GetCompilerType());
     CompilerType element_type;
-    parent_type.IsVectorType(&element_type);
+    uint64_t num_elements;
+    parent_type.IsVectorType(&element_type, &num_elements);
     m_child_type = ::GetCompilerTypeForFormat(
         m_parent_format, element_type,
         parent_type.GetTypeSystem().GetSharedPointer());
-    m_num_children = ::CalculateNumChildren(parent_type, m_child_type);
+    m_num_children =
+        ::CalculateNumChildren(element_type, num_elements, m_child_type);
     m_item_format = GetItemFormatForFormat(m_parent_format, m_child_type);
     return false;
   }
diff --git a/lldb/test/API/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py b/lldb/test/API/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py
index 4103d62878c70f3..1839c28aeb29fc6 100644
--- a/lldb/test/API/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py
+++ b/lldb/test/API/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py
@@ -86,3 +86,10 @@ def cleanup():
         v.SetFormat(lldb.eFormatVectorOfFloat32)
         oldValueAgain = v.GetChildAtIndex(0).GetValue()
         self.assertEqual(oldValue, oldValueAgain, "same format but different values")
+
+        # Test formatter for vector types whose size is not a power-of-2
+        f3 = self.frame().FindVariable("f3")
+        self.assertEqual(f3.GetNumChildren(), 3)
+        self.assertEqual(f3.GetChildAtIndex(0).GetData().float[0], 1.25)
+        self.assertEqual(f3.GetChildAtIndex(1).GetData().float[0], 2.50)
+        self.assertEqual(f3.GetChildAtIndex(2).GetData().float[0], 2.50)
diff --git a/lldb/test/API/functionalities/data-formatter/vector-types/main.cpp b/lldb/test/API/functionalities/data-formatter/vector-types/main.cpp
index ef0a227560bc253..7f2309e776bc27e 100644
--- a/lldb/test/API/functionalities/data-formatter/vector-types/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/vector-types/main.cpp
@@ -1,8 +1,10 @@
 typedef float float4 __attribute__((ext_vector_type(4)));
-typedef  unsigned char vec __attribute__((ext_vector_type(16)));
+typedef unsigned char vec __attribute__((ext_vector_type(16)));
+typedef float float3 __attribute__((ext_vector_type(3)));
 
 int main() {
     float4 f4 = {1.25, 1.25, 2.50, 2.50};
     vec v = (vec)f4;
+    float3 f3 = f4.gba;
     return 0; // break here
 }

@Michael137 Michael137 changed the title [lldb][DataFormatter] Fix GetNumChildren for VectorType formatter [lldb][DataFormatter] VectorType: fix format for arrays with size not a power-of-2 Oct 12, 2023
/// of `float` (4-bytes) will return `4` because we are interpreting
/// the byte-array as a `float32[]`.
///
/// \param[in] container_type The type of the container We
Copy link
Member

Choose a reason for hiding this comment

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

nit: We -> we

/// container_type to contain for the purposes of calculating
/// the number of children.
///
/// If size of the container is not a multiple of 'element_type'
Copy link
Member

Choose a reason for hiding this comment

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

nit: You could use \return block here

@Michael137 Michael137 requested a review from clayborg October 13, 2023 09:41
return *container_size / *element_size;
}
return 0;
/// \brief Returns the number of elements stored in a container
Copy link
Collaborator

Choose a reason for hiding this comment

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

We run Doxygen with autobrief, so this \brief should be redundant.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

/// returns 0.
///
/// On error, returns 0.
static size_t CalculateNumChildren(CompilerType container_elem_type,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you, just out of principle, return optional<size_t> here even if you the use it with getValueOr(0) at the call site?

Copy link
Member Author

Choose a reason for hiding this comment

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

yup looks better!

@github-actions
Copy link

github-actions bot commented Oct 13, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@Michael137 Michael137 merged commit 0dfcfb5 into llvm:main Oct 13, 2023
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Oct 13, 2023
… a power-of-2 (llvm#68907)

To get the number of children for a VectorType (i.e.,
a type declared with a `vector_size`/`ext_vector_type` attribute)
LLDB previously did following calculation:
1. Get byte-size of the vector container from Clang (`getTypeInfo`).
2. Get byte-size of the element type we want to interpret the array as.
   (e.g., sometimes we want to interpret an `unsigned char vec[16]`
   as a `float32[]`).
3. `numChildren = containerSize / reinterpretedElementSize`

However, for step 1, clang will return us the *aligned* container
byte-size.
So for a type such as `float __attribute__((ext_vector_type(3)))`
(which is an array of 3 4-byte floats), clang will round up the
byte-width of the array to `16`.
(see
[here](https://github.com/llvm/llvm-project/blob/ab6a66dbec61654d0962f6abf6d6c5b776937584/clang/lib/AST/ASTContext.cpp#L1987-L1992))

This means that for vectors where the size isn't a power-of-2, LLDB
will miscalculate the number of elements.

**Solution**

This patch changes step 1 such that we calculate the container size
as `numElementsInSource * byteSizeOfElement`.

(cherry picked from commit 0dfcfb5)
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Oct 13, 2023
… a power-of-2 (llvm#68907)

To get the number of children for a VectorType (i.e.,
a type declared with a `vector_size`/`ext_vector_type` attribute)
LLDB previously did following calculation:
1. Get byte-size of the vector container from Clang (`getTypeInfo`).
2. Get byte-size of the element type we want to interpret the array as.
   (e.g., sometimes we want to interpret an `unsigned char vec[16]`
   as a `float32[]`).
3. `numChildren = containerSize / reinterpretedElementSize`

However, for step 1, clang will return us the *aligned* container
byte-size.
So for a type such as `float __attribute__((ext_vector_type(3)))`
(which is an array of 3 4-byte floats), clang will round up the
byte-width of the array to `16`.
(see
[here](https://github.com/llvm/llvm-project/blob/ab6a66dbec61654d0962f6abf6d6c5b776937584/clang/lib/AST/ASTContext.cpp#L1987-L1992))

This means that for vectors where the size isn't a power-of-2, LLDB
will miscalculate the number of elements.

**Solution**

This patch changes step 1 such that we calculate the container size
as `numElementsInSource * byteSizeOfElement`.

(cherry picked from commit 0dfcfb5)
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.

4 participants