llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) <details> <summary>Changes</summary> --- Full diff: https://github.com/llvm/llvm-project/pull/177208.diff 9 Files Affected: - (modified) lldb/docs/dil-expr-lang.ebnf (+3-1) - (modified) lldb/include/lldb/ValueObject/DILAST.h (+35) - (modified) lldb/include/lldb/ValueObject/DILEval.h (+16) - (modified) lldb/include/lldb/ValueObject/DILParser.h (+1) - (modified) lldb/source/ValueObject/DILAST.cpp (+14) - (modified) lldb/source/ValueObject/DILEval.cpp (+155) - (modified) lldb/source/ValueObject/DILParser.cpp (+21-1) - (modified) lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py (+34-1) - (modified) lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp (+17) ``````````diff diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 99a8b0fcaa006..127c1d6337efa 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,7 +3,9 @@ (* This is currently a subset of the final DIL Language, matching the current DIL implementation. *) -expression = cast_expression; +expression = additive_expression ; + +additive_expression = cast_expression {"+" cast_expression} ; cast_expression = unary_expression | "(" type_id ")" cast_expression; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index da7659959093a..226f80ac70c5e 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -9,6 +9,7 @@ #ifndef LLDB_VALUEOBJECT_DILAST_H #define LLDB_VALUEOBJECT_DILAST_H +#include "lldb/ValueObject/DILLexer.h" #include "lldb/ValueObject/ValueObject.h" #include "llvm/Support/Error.h" #include <cstdint> @@ -19,6 +20,7 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { eArraySubscriptNode, + eBinaryOpNode, eBitExtractionNode, eBooleanLiteralNode, eCastNode, @@ -38,6 +40,14 @@ enum class UnaryOpKind { Plus, // "+" }; +/// The binary operators recognized by DIL. +enum class BinaryOpKind { + Add, // "+" +}; + +/// Translates DIL tokens to BinaryOpKind. +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind); + /// The type casts allowed by DIL. enum class CastKind { eArithmetic, ///< Casting to a scalar. @@ -148,6 +158,29 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class BinaryOpNode : public ASTNode { +public: + BinaryOpNode(uint32_t location, BinaryOpKind kind, ASTNodeUP lhs, + ASTNodeUP rhs) + : ASTNode(location, NodeKind::eBinaryOpNode), m_kind(kind), + m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + BinaryOpKind GetKind() const { return m_kind; } + ASTNode &GetLHS() const { return *m_lhs; } + ASTNode &GetRHS() const { return *m_rhs; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eBinaryOpNode; + } + +private: + BinaryOpKind m_kind; + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + class ArraySubscriptNode : public ASTNode { public: ArraySubscriptNode(uint32_t location, ASTNodeUP base, ASTNodeUP index) @@ -292,6 +325,8 @@ class Visitor { virtual llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode &node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const BinaryOpNode &node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> Visit(const ArraySubscriptNode &node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode &node) = 0; diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 550f5083b1dc6..6807a6616b846 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -57,6 +57,7 @@ class Interpreter : Visitor { Visit(const IdentifierNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const MemberOfNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode &node) override; + llvm::Expected<lldb::ValueObjectSP> Visit(const BinaryOpNode &node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const ArraySubscriptNode &node) override; llvm::Expected<lldb::ValueObjectSP> @@ -73,6 +74,21 @@ class Interpreter : Visitor { /// includes array-to-pointer and integral promotion for eligible types. llvm::Expected<lldb::ValueObjectSP> UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location); + + /// Perform an arithmetic conversion on two values from an arithmetic + /// operation. + /// \returns The result type of an arithmetic operation. + llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + uint32_t location); + llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind, + lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, + CompilerType result_type, + uint32_t location); + llvm::Expected<lldb::ValueObjectSP> + EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, + uint32_t location); llvm::Expected<CompilerType> PickIntegerType(lldb::TypeSystemSP type_system, std::shared_ptr<ExecutionContextScope> ctx, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index bd2fc373cd9b5..d36e9084dfcb5 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -87,6 +87,7 @@ class DILParser { ASTNodeUP Run(); ASTNodeUP ParseExpression(); + ASTNodeUP ParseAdditiveExpression(); ASTNodeUP ParseUnaryExpression(); ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index d8a714d33712d..180708f79c269 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -11,6 +11,16 @@ namespace lldb_private::dil { +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) { + switch (token_kind) { + case Token::plus: + return BinaryOpKind::Add; + default: + break; + } + llvm_unreachable("Unknown binary operator kind."); +} + llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } @@ -27,6 +37,10 @@ llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const { return v->Visit(*this); } +llvm::Expected<lldb::ValueObjectSP> BinaryOpNode::Accept(Visitor *v) const { + return v->Visit(*this); +} + llvm::Expected<lldb::ValueObjectSP> ArraySubscriptNode::Accept(Visitor *v) const { return v->Visit(*this); diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 68a2a228962fb..e6f701b54c7f4 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -139,6 +139,85 @@ Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { return valobj; } +static size_t IntegerConversionRank(CompilerType type) { + switch (type.GetCanonicalType().GetBasicTypeEnumeration()) { + case lldb::eBasicTypeBool: + return 1; + case lldb::eBasicTypeChar: + case lldb::eBasicTypeSignedChar: + case lldb::eBasicTypeUnsignedChar: + return 2; + case lldb::eBasicTypeShort: + case lldb::eBasicTypeUnsignedShort: + return 3; + case lldb::eBasicTypeInt: + case lldb::eBasicTypeUnsignedInt: + return 4; + case lldb::eBasicTypeLong: + case lldb::eBasicTypeUnsignedLong: + return 5; + case lldb::eBasicTypeLongLong: + case lldb::eBasicTypeUnsignedLongLong: + return 6; + case lldb::eBasicTypeInt128: + case lldb::eBasicTypeUnsignedInt128: + return 7; + default: + break; + } + return 0; +} + +llvm::Expected<CompilerType> +Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, uint32_t location) { + // Apply unary conversion for both operands. + auto lhs_or_err = UnaryConversion(lhs, location); + if (!lhs_or_err) + return lhs_or_err.takeError(); + lhs = *lhs_or_err; + auto rhs_or_err = UnaryConversion(rhs, location); + if (!rhs_or_err) + return rhs_or_err.takeError(); + rhs = *rhs_or_err; + + CompilerType lhs_type = lhs->GetCompilerType(); + CompilerType rhs_type = rhs->GetCompilerType(); + + if (lhs_type.CompareTypes(rhs_type)) + return lhs_type; + + // If either of the operands is not arithmetic (e.g. pointer), we're done. + if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) + return CompilerType(); + + // Handle conversions for floating types (float, double). + if (lhs_type.IsFloat() || rhs_type.IsFloat()) { + // If both are floats, convert the smaller operand to the bigger. + if (lhs_type.IsFloat() && rhs_type.IsFloat()) { + if (lhs_type.GetBasicTypeEnumeration() > + rhs_type.GetBasicTypeEnumeration()) + return lhs_type; + return rhs_type; + } + if (lhs_type.IsFloat() && rhs_type.IsInteger()) + return lhs_type; + return rhs_type; + } + + if (lhs_type.IsInteger() && rhs_type.IsInteger()) { + using Rank = std::tuple<size_t, bool>; + Rank l_rank = {IntegerConversionRank(lhs_type), !lhs_type.IsSigned()}; + Rank r_rank = {IntegerConversionRank(rhs_type), !rhs_type.IsSigned()}; + + if (l_rank < r_rank) + return rhs_type; + if (l_rank > r_rank) + return lhs_type; + } + return rhs_type; +} + static lldb::VariableSP DILFindVariable(ConstString name, VariableList &variable_list) { lldb::VariableSP exact_match; @@ -405,6 +484,82 @@ Interpreter::Visit(const UnaryOpNode &node) { node.GetLocation()); } +llvm::Expected<lldb::ValueObjectSP> +Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, CompilerType result_type, + uint32_t location) { + Scalar l, r; + bool l_resolved = lhs->ResolveValue(l); + bool r_resolved = rhs->ResolveValue(r); + + if (!l_resolved || !r_resolved) + return llvm::make_error<DILDiagnosticError>(m_expr, "invalid scalar value", + location); + + auto value_object = [this, result_type](Scalar scalar) { + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, + result_type, "result"); + }; + + switch (kind) { + case BinaryOpKind::Add: + return value_object(l + r); + } + return llvm::make_error<DILDiagnosticError>( + m_expr, "invalid arithmetic operation", location); +} + +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 + auto orig_lhs_type = lhs->GetCompilerType(); + auto orig_rhs_type = rhs->GetCompilerType(); + auto type_or_err = ArithmeticConversion(lhs, rhs, location); + if (!type_or_err) + return type_or_err.takeError(); + CompilerType result_type = *type_or_err; + + 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, errMsg, location); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const BinaryOpNode &node) { + auto lhs_or_err = EvaluateAndDereference(node.GetLHS()); + if (!lhs_or_err) + return lhs_or_err; + lldb::ValueObjectSP lhs = *lhs_or_err; + auto rhs_or_err = EvaluateAndDereference(node.GetRHS()); + if (!rhs_or_err) + return rhs_or_err; + lldb::ValueObjectSP rhs = *rhs_or_err; + + lldb::TypeSystemSP lhs_system = + lhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); + lldb::TypeSystemSP rhs_system = + rhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); + if (lhs_system->GetPluginName() != rhs_system->GetPluginName()) { + // TODO: Attempt to convert values to current CU's type system + return llvm::make_error<DILDiagnosticError>( + m_expr, "operands have different type systems", node.GetLocation()); + } + + switch (node.GetKind()) { + case BinaryOpKind::Add: + return EvaluateBinaryAddition(lhs, rhs, node.GetLocation()); + } + + return llvm::make_error<DILDiagnosticError>( + m_expr, "unimplemented binary operation", node.GetLocation()); +} + llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const MemberOfNode &node) { auto base_or_err = Evaluate(node.GetBase()); diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 7dc284c8e070e..2283e96ff95e2 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -85,7 +85,27 @@ ASTNodeUP DILParser::Run() { // expression: // cast_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseCastExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); } + +// Parse an additive_expression. +// +// additive_expression: +// cast_expression {"+" cast_expression} +// +ASTNodeUP DILParser::ParseAdditiveExpression() { + auto lhs = ParseCastExpression(); + + while (CurToken().Is(Token::plus)) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto rhs = ParseCastExpression(); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} // Parse a cast_expression. // 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 53a85fed303f4..fe8ee99835311 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 @@ -13,7 +13,7 @@ class TestFrameVarDILArithmetic(TestBase): def test_arithmetic(self): self.build() - lldbutil.run_to_source_breakpoint( + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") ) @@ -44,3 +44,36 @@ def test_arithmetic(self): self.expect_var_path("+bitfield.b", value="2", type="int") self.expect_var_path("+bitfield.c", value="3", type="unsigned int") self.expect_var_path("+bitfield.d", value="4", type="uint64_t") + + # Check basic math and resulting types + self.expect_var_path("1 + 2", value="3", type="int") + self.expect_var_path("1 + true", value="2", type="int") + self.expect_var_path("1L + wchar", value="2", type="long") + self.expect_var_path("1L + char16", value="3", type="long") + self.expect_var_path("1LL + char32", value="4", type="long long") + self.expect_var_path("1UL + 1L", value="2", type="unsigned long") + self.expect_var_path("s + x", value="12", type="int") + self.expect_var_path("s + l", value="15", type="long") + self.expect_var_path("1.0 + 2.5", value="3.5", type="double") + self.expect_var_path("1 + 2.5f", value="3.5", type="float") + self.expect_var_path("2. + .5", value="2.5", type="double") + self.expect_var_path("2.f + .5f", value="2.5", type="float") + self.expect_var_path("f + d", value="3.5", type="double") + + # Check limits and overflows + frame = thread.GetFrameAtIndex(0) + int_min = frame.GetValueForVariablePath("int_min").GetValue() + int_max = frame.GetValueForVariablePath("int_max").GetValue() + uint_max = frame.GetValueForVariablePath("uint_max").GetValue() + ll_max = frame.GetValueForVariablePath("ll_max").GetValue() + ll_min = frame.GetValueForVariablePath("ll_min").GetValue() + ull_max = frame.GetValueForVariablePath("ull_max").GetValue() + self.expect_var_path("int_max + 1", value=int_min) + self.expect_var_path("uint_max + 1", value="0") + self.expect_var_path("ll_max + 1", value=ll_min) + self.expect_var_path("ull_max + 1", value="0") + + # Check references and typedefs + self.expect_var_path("ref + 1", value="3") + self.expect_var_path("my_ref + 1", value="3") + self.expect_var_path("ref + my_ref", value="4") diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp index 2c70e93433f5f..129a4214a42c4 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp @@ -1,11 +1,19 @@ #include <cstdint> +#include <limits> int main(int argc, char **argv) { short s = 10; unsigned short us = 1; + long l = 5; + float f = 1.0f; + double d = 2.5; int x = 2; int &ref = x; + int *p = &x; + typedef int &myref; + myref my_ref = x; + enum Enum { kZero, kOne } enum_one = kOne; wchar_t wchar = 1; char16_t char16 = 2; @@ -19,5 +27,14 @@ int main(int argc, char **argv) { }; BitFieldStruct bitfield = {1, 2, 3, 4}; + int int_max = std::numeric_limits<int>::max(); + int int_min = std::numeric_limits<int>::min(); + unsigned int uint_max = std::numeric_limits<unsigned int>::max(); + unsigned int uint_zero = 0; + long long ll_max = std::numeric_limits<long long>::max(); + long long ll_min = std::numeric_limits<long long>::min(); + unsigned long long ull_max = std::numeric_limits<unsigned long long>::max(); + unsigned long long ull_zero = 0; + return 0; // Set a breakpoint here } `````````` </details> https://github.com/llvm/llvm-project/pull/177208 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
