[lldb] Add pointer arithmetics for addition and subtraction to DIL (#184652)
This commit is contained in:
parent
dc5c6d008f
commit
9f4fbe86a5
@ -89,6 +89,13 @@ private:
|
||||
llvm::Expected<CompilerType> 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<lldb::ValueObjectSP> PointerOffset(lldb::ValueObjectSP ptr,
|
||||
lldb::ValueObjectSP offset,
|
||||
BinaryOpKind operation,
|
||||
uint32_t location);
|
||||
llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
|
||||
lldb::ValueObjectSP lhs,
|
||||
lldb::ValueObjectSP rhs,
|
||||
|
||||
@ -538,6 +538,43 @@ Interpreter::Visit(const UnaryOpNode &node) {
|
||||
node.GetLocation());
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
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<DILDiagnosticError>(
|
||||
m_expr, "arithmetic on a pointer to void", location);
|
||||
if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0)
|
||||
return llvm::make_error<DILDiagnosticError>(
|
||||
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<DILDiagnosticError>(m_expr, std::move(errMsg),
|
||||
location);
|
||||
}
|
||||
|
||||
llvm::Expected<uint64_t> 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<lldb::ValueObjectSP>
|
||||
Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
|
||||
lldb::ValueObjectSP rhs, CompilerType result_type,
|
||||
@ -569,7 +606,8 @@ llvm::Expected<lldb::ValueObjectSP> 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<lldb::ValueObjectSP> 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<DILDiagnosticError>(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<DILDiagnosticError>(m_expr, std::move(errMsg),
|
||||
location);
|
||||
}
|
||||
|
||||
return PointerOffset(ptr, offset, BinaryOpKind::Add, location);
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP> 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<lldb::ValueObjectSP> 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<DILDiagnosticError>(
|
||||
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<DILDiagnosticError>(m_expr, errMsg, location);
|
||||
}
|
||||
|
||||
llvm::Expected<uint64_t> 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<int64_t>(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<DILDiagnosticError>(
|
||||
m_expr, "undefined pointer arithmetic", location);
|
||||
}
|
||||
diff /= item_size;
|
||||
|
||||
llvm::Expected<lldb::TypeSystemSP> 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<DILDiagnosticError>(
|
||||
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());
|
||||
|
||||
@ -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')"],
|
||||
)
|
||||
|
||||
@ -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"],
|
||||
)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user