diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index a6223c4d998a..da4479036c17 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -89,6 +89,13 @@ private: llvm::Expected ArithmeticConversion(lldb::ValueObjectSP &lhs, lldb::ValueObjectSP &rhs, uint32_t location); + /// Add or subtract the offset to the pointer according to the pointee type + /// byte size. + /// \returns A new `ValueObject` with a new pointer value. + llvm::Expected PointerOffset(lldb::ValueObjectSP ptr, + lldb::ValueObjectSP offset, + BinaryOpKind operation, + uint32_t location); llvm::Expected EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index ec9ea8b9618f..e1736e1dc008 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -538,6 +538,43 @@ Interpreter::Visit(const UnaryOpNode &node) { node.GetLocation()); } +llvm::Expected +Interpreter::PointerOffset(lldb::ValueObjectSP ptr, lldb::ValueObjectSP offset, + BinaryOpKind operation, uint32_t location) { + assert(operation == BinaryOpKind::Add || operation == BinaryOpKind::Sub); + if (ptr->GetCompilerType().IsPointerToVoid()) + return llvm::make_error( + m_expr, "arithmetic on a pointer to void", location); + if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0) + return llvm::make_error( + m_expr, "arithmetic on a nullptr is undefined", location); + + bool success; + int64_t offset_int = offset->GetValueAsSigned(0, &success); + if (!success) { + std::string errMsg = llvm::formatv("could not get the offset: {0}", + offset->GetError().AsCString()); + return llvm::make_error(m_expr, std::move(errMsg), + location); + } + + llvm::Expected byte_size = + ptr->GetCompilerType().GetPointeeType().GetByteSize( + m_exe_ctx_scope.get()); + if (!byte_size) + return byte_size.takeError(); + uint64_t ptr_addr = ptr->GetValueAsUnsigned(0); + if (operation == BinaryOpKind::Sub) + ptr_addr -= offset_int * (*byte_size); + else + ptr_addr += offset_int * (*byte_size); + + ExecutionContext exe_ctx(m_target.get(), false); + Scalar scalar(ptr_addr); + return ValueObject::CreateValueObjectFromScalar( + m_exe_ctx_scope, scalar, ptr->GetCompilerType(), "result"); +} + llvm::Expected Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, CompilerType result_type, @@ -569,7 +606,8 @@ llvm::Expected Interpreter::EvaluateBinaryAddition( lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { // Operation '+' works for: // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} - // TODO: Pointer arithmetics + // {integer,unscoped_enum} <-> pointer + // pointer <-> {integer,unscoped_enum} auto orig_lhs_type = lhs->GetCompilerType(); auto orig_rhs_type = rhs->GetCompilerType(); auto type_or_err = ArithmeticConversion(lhs, rhs, location); @@ -580,18 +618,34 @@ llvm::Expected Interpreter::EvaluateBinaryAddition( if (result_type.IsScalarType()) return EvaluateScalarOp(BinaryOpKind::Add, lhs, rhs, result_type, location); - std::string errMsg = - llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", - orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); - return llvm::make_error(m_expr, std::move(errMsg), - location); + // Check for pointer arithmetics. + // One of the operands must be a pointer and the other one an integer. + lldb::ValueObjectSP ptr, offset; + if (lhs->GetCompilerType().IsPointerType()) { + ptr = lhs; + offset = rhs; + } else if (rhs->GetCompilerType().IsPointerType()) { + ptr = rhs; + offset = lhs; + } + + if (!ptr || !offset->GetCompilerType().IsInteger()) { + std::string errMsg = + llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", + orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); + return llvm::make_error(m_expr, std::move(errMsg), + location); + } + + return PointerOffset(ptr, offset, BinaryOpKind::Add, location); } llvm::Expected Interpreter::EvaluateBinarySubtraction( lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { // Operation '-' works for: // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} - // TODO: Pointer arithmetics + // pointer <-> {integer,unscoped_enum} + // pointer <-> pointer (if pointee types are compatible) auto orig_lhs_type = lhs->GetCompilerType(); auto orig_rhs_type = rhs->GetCompilerType(); auto type_or_err = ArithmeticConversion(lhs, rhs, location); @@ -602,6 +656,60 @@ llvm::Expected Interpreter::EvaluateBinarySubtraction( if (result_type.IsScalarType()) return EvaluateScalarOp(BinaryOpKind::Sub, lhs, rhs, result_type, location); + auto lhs_type = lhs->GetCompilerType(); + auto rhs_type = rhs->GetCompilerType(); + + // "pointer - integer" operation. + if (lhs_type.IsPointerType() && rhs_type.IsInteger()) + return PointerOffset(lhs, rhs, BinaryOpKind::Sub, location); + + // "pointer - pointer" operation. + if (lhs_type.IsPointerType() && rhs_type.IsPointerType()) { + if (lhs_type.IsPointerToVoid() && rhs_type.IsPointerToVoid()) { + return llvm::make_error( + m_expr, "arithmetic on pointers to void", location); + } + // Compare canonical unqualified pointer types. + CompilerType lhs_unqualified_type = lhs_type.GetCanonicalType(); + CompilerType rhs_unqualified_type = rhs_type.GetCanonicalType(); + if (!lhs_unqualified_type.CompareTypes(rhs_unqualified_type)) { + std::string errMsg = llvm::formatv( + "'{0}' and '{1}' are not pointers to compatible types", + orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); + return llvm::make_error(m_expr, errMsg, location); + } + + llvm::Expected lhs_byte_size = + lhs_type.GetPointeeType().GetByteSize(m_exe_ctx_scope.get()); + if (!lhs_byte_size) + return lhs_byte_size.takeError(); + // Since pointers have compatible types, both have the same pointee size. + int64_t item_size = *lhs_byte_size; + int64_t diff = static_cast(lhs->GetValueAsUnsigned(0) - + rhs->GetValueAsUnsigned(0)); + assert(item_size > 0 && "Pointee size cannot be 0"); + if (diff % item_size != 0) { + // If address difference isn't divisible by pointee size then performing + // the operation is undefined behaviour. + return llvm::make_error( + m_expr, "undefined pointer arithmetic", location); + } + diff /= item_size; + + llvm::Expected type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + CompilerType ptrdiff_type = type_system.get()->GetPointerDiffType(true); + if (!ptrdiff_type) + return llvm::make_error( + m_expr, "unable to determine pointer diff type", location); + + Scalar scalar(diff); + return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar, + ptrdiff_type, "result"); + } + std::string errMsg = llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index 03075314ab9b..fb3b877209d1 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -102,15 +102,3 @@ class TestFrameVarDILArithmetic(TestBase): self.expect_var_path("my_ref - 1", value="1") self.expect_var_path("ref + my_ref", value="4") self.expect_var_path("ref - my_ref", value="0") - - # TODO: Pointer arithmetics - self.expect( - "frame var -- 'p + 1'", - error=True, - substrs=["invalid operands to binary expression ('int *' and 'int')"], - ) - self.expect( - "frame var -- 'p - 1'", - error=True, - substrs=["invalid operands to binary expression ('int *' and 'int')"], - ) diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py index 448cd5b1ec7e..5da79a29bad0 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py @@ -22,8 +22,139 @@ class TestFrameVarDILExprPointerArithmetic(TestBase): self.expect_var_path("+array", type="int *") self.expect_var_path("+array_ref", type="int *") self.expect_var_path("+p_int0", type="int *") + + # Binary operations + self.expect_var_path("p_char", type="const char *") + self.expect_var_path("p_char + 1", type="const char *") + self.expect_var_path("p_char + offset", type="const char *") + self.expect_var_path("p_char5 + -1", type="const char *") + self.expect_var_path("p_char5 - 1", type="const char *") + self.expect_var_path("p_char5 - offset", type="const char *") + + self.expect_var_path("my_p_char", type="my_char_ptr") + self.expect_var_path("my_p_char + 1", type="my_char_ptr") + self.expect_var_path("my_p_char - 1", type="my_char_ptr") + + self.expect_var_path("*(p_char + 0)", value="'h'") + self.expect_var_path("*(5 + p_char)", value="'!'") + self.expect_var_path("*(p_char5 + -5)", value="'h'") + self.expect_var_path("*(p_char5 - 5)", value="'h'") + self.expect_var_path("*(p_char - -5)", value="'!'") + self.expect_var_path("*(p_char5 - offset + 5)", value="'!'") + self.expect_var_path("*((p_char + offset) - 5)", value="'h'") + self.expect_var_path("*(p_char + (offset - 5))", value="'h'") + + self.expect_var_path("*p_int0", value="0") + self.expect_var_path("*cp_int5", value="5") + self.expect_var_path("*(&*(cp_int5 + 1) - 1)", value="5") + + self.expect_var_path("p_int0 - p_int0", value="0", type="__ptrdiff_t") + self.expect_var_path("cp_int5 - p_int0", value="5", type="__ptrdiff_t") + self.expect_var_path("cp_int5 - td_int_ptr0", value="5", type="__ptrdiff_t") + self.expect_var_path("td_int_ptr0 - cp_int5", value="-5", type="__ptrdiff_t") + + # Check arrays + self.expect_var_path("array + 1", type="int *") + self.expect_var_path("1 + array", type="int *") + self.expect_var_path("array_ref + 1", type="int *") + self.expect_var_path("1 + array_ref", type="int *") + self.expect_var_path("array - 1", type="int *") + self.expect_var_path("array_ref - 1", type="int *") + self.expect_var_path("array - array", value="0", type="__ptrdiff_t") + self.expect_var_path("array - array_ref", value="0", type="__ptrdiff_t") + self.expect_var_path("array_ref - array_ref", value="0", type="__ptrdiff_t") + + # Errors self.expect( "frame var -- '-p_int0'", error=True, substrs=["invalid argument type 'int *' to unary expression"], ) + self.expect( + "frame var -- 'cp_int5 - p_char'", + error=True, + substrs=[ + "'const int *' and 'const char *' are not pointers to compatible types" + ], + ) + self.expect( + "frame var -- 'p_int0 + cp_int5'", + error=True, + substrs=[ + "invalid operands to binary expression ('int *' and 'const int *')" + ], + ) + self.expect( + "frame var -- 'p_void + 1'", + error=True, + substrs=["arithmetic on a pointer to void"], + ) + self.expect( + "frame var -- 'p_void - 1'", + error=True, + substrs=["arithmetic on a pointer to void"], + ) + self.expect( + "frame var -- 'p_void - p_char'", + error=True, + substrs=[ + "'void *' and 'const char *' are not pointers to compatible types" + ], + ) + self.expect( + "frame var -- 'p_void - p_void'", + error=True, + substrs=["arithmetic on pointers to void"], + ) + self.expect( + "frame var -- 'pp_void0 - p_char'", + error=True, + substrs=[ + "'void **' and 'const char *' are not pointers to compatible types" + ], + ) + self.expect( + "frame var -- 'p_int0 - 1.0'", + error=True, + substrs=["invalid operands to binary expression ('int *' and 'double')"], + ) + self.expect( + "frame var -- '1.0f + p_int0'", + error=True, + substrs=["invalid operands to binary expression ('float' and 'int *')"], + ) + self.expect( + "frame var -- '1 - array'", + error=True, + substrs=["invalid operands to binary expression ('int' and 'int[10]')"], + ) + self.expect( + "frame var -- 'array + array'", + error=True, + substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"], + ) + self.expect( + "frame var -- 'array + array'", + error=True, + substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"], + ) + self.expect( + "frame var -- 'int_null + 1'", + error=True, + substrs=["arithmetic on a nullptr is undefined"], + ) + self.expect( + "frame var -- 'int_null - 1'", + error=True, + substrs=["arithmetic on a nullptr is undefined"], + ) + self.expect( + "frame var -- 'p_char + *((int*) 0)'", + error=True, + substrs=["could not get the offset: parent is NULL"], + ) + self.expect( + "frame var -- 'p_char - *((int*) 0)'", + error=True, + substrs=["could not get the offset: parent is NULL"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp index b4e0e88b1ffc..02754e846f75 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp @@ -1,11 +1,31 @@ void stop() {} int main(int argc, char **argv) { + int offset = 5; int array[10]; array[0] = 0; + array[offset] = offset; int (&array_ref)[10] = array; int *p_int0 = &array[0]; + const char *p_char = "hello!"; + const char *p_char5 = p_char + 5; + typedef const char *my_char_ptr; + my_char_ptr my_p_char = p_char; + + int **pp_int0 = &p_int0; + const int *cp_int0 = &array[0]; + const int *cp_int5 = &array[offset]; + + typedef int *td_int_ptr_t; + td_int_ptr_t td_int_ptr0 = &array[0]; + + void *p_void = (void *)p_char; + void **pp_void0 = &p_void; + void **pp_void1 = pp_void0 + 1; + + int *int_null = nullptr; + stop(); // Set a breakpoint here return 0; }