https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/147064
>From cf1f908360399ac51770d9fb7e1dac03eceab0e9 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <ikuk...@accesssoftek.com> Date: Thu, 3 Jul 2025 19:34:17 +0500 Subject: [PATCH 1/2] [LLDB] *WIP* Add scalar literal node and binary addition --- lldb/include/lldb/ValueObject/DILAST.h | 58 ++++ lldb/include/lldb/ValueObject/DILEval.h | 15 + lldb/include/lldb/ValueObject/DILLexer.h | 1 + lldb/include/lldb/ValueObject/DILParser.h | 3 + lldb/source/ValueObject/DILAST.cpp | 21 ++ lldb/source/ValueObject/DILEval.cpp | 405 ++++++++++++++++++++++ lldb/source/ValueObject/DILLexer.cpp | 10 +- lldb/source/ValueObject/DILParser.cpp | 74 +++- 8 files changed, 581 insertions(+), 6 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 709f0639135f1..a9d244031d55f 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,10 +20,12 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { eArraySubscriptNode, + eBinaryOpNode, eBitExtractionNode, eErrorNode, eIdentifierNode, eMemberOfNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -32,6 +35,14 @@ enum class UnaryOpKind { Deref, // "*" }; +enum class BinaryOpKind { + Add, // "+" + Sub, // "-" +}; + +/// Translates DIL tokens to BinaryOpKind. +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind); + /// Forward declaration, for use in DIL AST nodes. Definition is at the very /// end of this file. class Visitor; @@ -178,6 +189,49 @@ class BitFieldExtractionNode : public ASTNode { int64_t m_last_index; }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + +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.get(); } + ASTNode *GetRHS() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eBinaryOpNode; + } + +private: + BinaryOpKind m_kind; + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -195,6 +249,10 @@ class Visitor { Visit(const ArraySubscriptNode *node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const BinaryOpNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 45e29b3ddcd7b..9e9028f6122fd 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -54,6 +54,21 @@ class Interpreter : Visitor { Visit(const ArraySubscriptNode *node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode *node) override; + llvm::Expected<lldb::ValueObjectSP> + Visit(const ScalarLiteralNode *node) override; + llvm::Expected<lldb::ValueObjectSP> Visit(const BinaryOpNode *node) override; + + lldb::ValueObjectSP + ConvertValueObjectToTypeSystem(lldb::ValueObjectSP valobj, + lldb::TypeSystemSP type_system); + + llvm::Error PrepareBinaryAddition(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + uint32_t location); + + llvm::Expected<lldb::ValueObjectSP> + EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, + uint32_t location); // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 9c1ba97680253..58059657bf3a5 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -34,6 +34,7 @@ class Token { minus, numeric_constant, period, + plus, r_paren, r_square, star, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9eda7bac4a364..c57ef0cf28022 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(); @@ -96,6 +97,8 @@ class DILParser { std::string ParseIdExpression(); std::string ParseUnqualifiedId(); std::optional<int64_t> ParseIntegerConstant(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index b1cd824c2299e..7aee692f2b28a 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -11,6 +11,18 @@ namespace lldb_private::dil { +BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) { + switch (token_kind) { + case Token::minus: + return BinaryOpKind::Sub; + 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."); } @@ -37,4 +49,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected<lldb::ValueObjectSP> +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + +llvm::Expected<lldb::ValueObjectSP> BinaryOpNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 8ca9b4215985d..2e42b57e93042 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILEval.h" +#include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.h" #include "lldb/ValueObject/DILAST.h" @@ -401,4 +403,407 @@ Interpreter::Visit(const BitFieldExtractionNode *node) { return child_valobj_sp; } +static CompilerType GetBasicTypeFromCU(std::shared_ptr<StackFrame> ctx, + lldb::BasicType basic_type) { + SymbolContext symbol_context = + ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); + auto language = symbol_context.comp_unit->GetLanguage(); + + symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); + auto type_system = + symbol_context.module_sp->GetTypeSystemForLanguage(language); + + if (type_system) + if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type)) + return compiler_type; + + CompilerType empty_type; + return empty_type; +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = + GetBasicTypeFromCU(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + // Scalar later could be float or bool + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + +static CompilerType GetBasicType(lldb::TypeSystemSP type_system, + lldb::BasicType basic_type) { + if (type_system) + if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type)) + return compiler_type; + + CompilerType empty_type; + return empty_type; +} + +static CompilerType DoIntegralPromotion(CompilerType from, + std::shared_ptr<StackFrame> ctx) { + if (!from.IsInteger() && !from.IsUnscopedEnumerationType()) + return from; + + if (!from.IsPromotableIntegerType()) + return from; + + if (from.IsUnscopedEnumerationType()) + return DoIntegralPromotion(from.GetEnumerationIntegerType(), ctx); + lldb::BasicType builtin_type = + from.GetCanonicalType().GetBasicTypeEnumeration(); + lldb::TypeSystemSP type_system = from.GetTypeSystem().GetSharedPointer(); + + uint64_t from_size = 0; + if (builtin_type == lldb::eBasicTypeWChar || + builtin_type == lldb::eBasicTypeSignedWChar || + builtin_type == lldb::eBasicTypeUnsignedWChar || + builtin_type == lldb::eBasicTypeChar16 || + builtin_type == lldb::eBasicTypeChar32) { + // Find the type that can hold the entire range of values for our type. + bool is_signed = from.IsSigned(); + if (auto temp = from.GetByteSize(ctx.get())) + from_size = *temp; + + CompilerType promote_types[] = { + GetBasicType(type_system, lldb::eBasicTypeInt), + GetBasicType(type_system, lldb::eBasicTypeUnsignedInt), + GetBasicType(type_system, lldb::eBasicTypeLong), + GetBasicType(type_system, lldb::eBasicTypeUnsignedLong), + GetBasicType(type_system, lldb::eBasicTypeLongLong), + GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong), + }; + for (auto &type : promote_types) { + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(ctx.get())) + byte_size = *temp; + if (from_size < byte_size || + (from_size == byte_size && + is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) { + return type; + } + } + + llvm_unreachable("char type should fit into long long"); + } + + // Here we can promote only to "int" or "unsigned int". + CompilerType int_type = GetBasicType(type_system, lldb::eBasicTypeInt); + uint64_t int_byte_size = 0; + if (auto temp = int_type.GetByteSize(ctx.get())) + int_byte_size = *temp; + + // Signed integer types can be safely promoted to "int". + if (from.IsSigned()) { + return int_type; + } + // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold + // their entire value range. + return (from_size == int_byte_size) + ? GetBasicType(type_system, lldb::eBasicTypeUnsignedInt) + : int_type; +} + +static lldb::ValueObjectSP UnaryConversion(lldb::ValueObjectSP valobj, + std::shared_ptr<StackFrame> ctx) { + // Perform usual conversions for unary operators. + CompilerType in_type = valobj->GetCompilerType(); + CompilerType result_type; + + if (valobj->GetCompilerType().IsInteger() || + valobj->GetCompilerType().IsUnscopedEnumerationType()) { + CompilerType promoted_type = + DoIntegralPromotion(valobj->GetCompilerType(), ctx); + if (!promoted_type.CompareTypes(valobj->GetCompilerType())) + return valobj->CastToBasicType(promoted_type); + } + + return valobj; +} + +static size_t ConversionRank(CompilerType type) { + // Get integer conversion rank + // https://eel.is/c++draft/conv.rank + 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; + + // The ranks of char16_t, char32_t, and wchar_t are equal to the + // ranks of their underlying types. + case lldb::eBasicTypeWChar: + case lldb::eBasicTypeSignedWChar: + case lldb::eBasicTypeUnsignedWChar: + return 3; + case lldb::eBasicTypeChar16: + return 3; + case lldb::eBasicTypeChar32: + return 4; + + default: + break; + } + return 0; +} + +static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) { + switch (basic_type) { + case lldb::eBasicTypeInt: + return lldb::eBasicTypeUnsignedInt; + case lldb::eBasicTypeLong: + return lldb::eBasicTypeUnsignedLong; + case lldb::eBasicTypeLongLong: + return lldb::eBasicTypeUnsignedLongLong; + default: + return basic_type; + } +} + +static void PerformIntegerConversions(std::shared_ptr<StackFrame> ctx, + lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + bool convert_lhs, bool convert_rhs) { + CompilerType l_type = lhs->GetCompilerType(); + CompilerType r_type = rhs->GetCompilerType(); + if (r_type.IsSigned() && !l_type.IsSigned()) { + uint64_t l_size = 0; + uint64_t r_size = 0; + if (auto temp = l_type.GetByteSize(ctx.get())) + l_size = *temp; + ; + if (auto temp = r_type.GetByteSize(ctx.get())) + r_size = *temp; + if (l_size <= r_size) { + if (r_size == l_size) { + lldb::TypeSystemSP type_system = + l_type.GetTypeSystem().GetSharedPointer(); + auto r_type_unsigned = GetBasicType( + type_system, + BasicTypeToUnsigned( + r_type.GetCanonicalType().GetBasicTypeEnumeration())); + if (convert_rhs) + rhs = rhs->CastToBasicType(r_type_unsigned); + } + } + } + if (convert_lhs) + lhs = lhs->CastToBasicType(rhs->GetCompilerType()); +} + +static CompilerType ArithmeticConversions(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + std::shared_ptr<StackFrame> ctx) { + // Apply unary conversion (e.g. intergal promotion) for both operands. + lhs = UnaryConversion(lhs, ctx); + rhs = UnaryConversion(rhs, ctx); + + 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()) { + CompilerType bad_type; + return bad_type; + } + + // Removed floating point conversions + if (lhs_type.IsFloat() || rhs_type.IsFloat()) { + CompilerType bad_type; + return bad_type; + } + + if (lhs_type.IsInteger() && rhs_type.IsInteger()) { + using Rank = std::tuple<size_t, bool>; + Rank l_rank = {ConversionRank(lhs_type), !lhs_type.IsSigned()}; + Rank r_rank = {ConversionRank(rhs_type), !rhs_type.IsSigned()}; + + if (l_rank < r_rank) { + PerformIntegerConversions(ctx, lhs, rhs, true, true); + } else if (l_rank > r_rank) { + PerformIntegerConversions(ctx, rhs, lhs, true, true); + } + } + + return rhs_type; +} + +llvm::Error Interpreter::PrepareBinaryAddition(lldb::ValueObjectSP &lhs, + lldb::ValueObjectSP &rhs, + uint32_t location) { + // Operation '+' works for: + // + // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} + // {integer,unscoped_enum} <-> pointer + // pointer <-> {integer,unscoped_enum} + auto orig_lhs_type = lhs->GetCompilerType(); + auto orig_rhs_type = rhs->GetCompilerType(); + auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope); + + if (result_type.IsScalarType()) + return llvm::Error::success(); + + // Removed pointer arithmetics + return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented", + location); +} + +static lldb::ValueObjectSP EvaluateArithmeticOpInteger(lldb::TargetSP target, + BinaryOpKind kind, + lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, + CompilerType rtype) { + assert(lhs->GetCompilerType().IsInteger() && + rhs->GetCompilerType().IsInteger() && + "invalid ast: both operands must be integers"); + + auto wrap = [target, rtype](auto value) { + return ValueObject::CreateValueObjectFromAPInt(target, value, rtype, + "result"); + }; + + llvm::Expected<llvm::APSInt> l_value = lhs->GetValueAsAPSInt(); + llvm::Expected<llvm::APSInt> r_value = rhs->GetValueAsAPSInt(); + + if (l_value && r_value) { + llvm::APSInt l = *l_value; + llvm::APSInt r = *r_value; + + switch (kind) { + case BinaryOpKind::Add: + return wrap(l + r); + + default: + assert(false && "invalid ast: invalid arithmetic operation"); + return lldb::ValueObjectSP(); + } + } else { + return lldb::ValueObjectSP(); + } +} + +static lldb::ValueObjectSP EvaluateArithmeticOp(lldb::TargetSP target, + BinaryOpKind kind, + lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, + CompilerType rtype) { + assert((rtype.IsInteger() || rtype.IsFloat()) && + "invalid ast: result type must either integer or floating point"); + + // Evaluate arithmetic operation for two integral values. + if (rtype.IsInteger()) { + return EvaluateArithmeticOpInteger(target, kind, lhs, rhs, rtype); + } + + // Removed floating point arithmetics + + return lldb::ValueObjectSP(); +} + +llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition( + lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { + // Addition of two arithmetic types. + if (lhs->GetCompilerType().IsScalarType() && + rhs->GetCompilerType().IsScalarType()) { + return EvaluateArithmeticOp(m_target, BinaryOpKind::Add, lhs, rhs, + lhs->GetCompilerType().GetCanonicalType()); + } + + // Removed pointer arithmetics + return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented", + location); +} + +lldb::ValueObjectSP +Interpreter::ConvertValueObjectToTypeSystem(lldb::ValueObjectSP valobj, + lldb::TypeSystemSP type_system) { + auto apsint = valobj->GetValueAsAPSInt(); + if (apsint) { + llvm::APInt value = *apsint; + if (type_system) { + lldb::BasicType basic_type = valobj->GetCompilerType() + .GetCanonicalType() + .GetBasicTypeEnumeration(); + if (auto compiler_type = + type_system.get()->GetBasicTypeFromAST(basic_type)) { + valobj->GetValue().SetCompilerType(compiler_type); + return ValueObject::CreateValueObjectFromAPInt(m_target, value, + compiler_type, "result"); + } + } + } + + return lldb::ValueObjectSP(); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const BinaryOpNode *node) { + auto lhs_or_err = Evaluate(node->GetLHS()); + if (!lhs_or_err) + return lhs_or_err; + lldb::ValueObjectSP lhs = *lhs_or_err; + auto rhs_or_err = Evaluate(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(); + + // Is this a correct way to check if type systems are the same? + if (lhs_system != rhs_system) { + // If one of the nodes is a scalar const, convert it to the + // type system of another one + if (node->GetLHS()->GetKind() == NodeKind::eScalarLiteralNode) + lhs = ConvertValueObjectToTypeSystem(lhs, rhs_system); + else if (node->GetRHS()->GetKind() == NodeKind::eScalarLiteralNode) + rhs = ConvertValueObjectToTypeSystem(rhs, lhs_system); + else + return llvm::make_error<DILDiagnosticError>( + m_expr, "incompatible type systems", node->GetLocation()); + } + + switch (node->GetKind()) { + case BinaryOpKind::Add: + if (auto err = PrepareBinaryAddition(lhs, rhs, node->GetLocation())) + return err; + return EvaluateBinaryAddition(lhs, rhs, node->GetLocation()); + + // Other ops + + default: + break; + } + + return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented", + node->GetLocation()); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index eaefaf484bc18..2031bf677d6d7 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -40,6 +40,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "numeric_constant"; case Kind::period: return "period"; + case Kind::plus: + return "plus"; case Kind::r_paren: return "r_paren"; case Kind::r_square: @@ -114,10 +116,10 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair<Token::Kind, const char *> operators[] = { - {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, - {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, - {Token::period, "."}, {Token::r_paren, ")"}, {Token::r_square, "]"}, - {Token::star, "*"}, + {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, + {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, + {Token::period, "."}, {Token::plus, "+"}, {Token::r_paren, ")"}, + {Token::r_square, "]"}, {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index eac41fab90763..6de54eabc8ca1 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -80,9 +80,30 @@ ASTNodeUP DILParser::Run() { // Parse an expression. // // expression: -// unary_expression +// additive_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); } + +// Parse an additive_expression. +// +// additive_expression: +// unary_expression {"+" unary_expression} +// unary_expression {"-" unary_expression} +// +ASTNodeUP DILParser::ParseAdditiveExpression() { + auto lhs = ParseUnaryExpression(); + + while (CurToken().IsOneOf({Token::plus, Token::minus})) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto rhs = ParseUnaryExpression(); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} // Parse an unary_expression. // @@ -183,6 +204,8 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf( {Token::coloncolon, Token::identifier, Token::l_paren})) { // Save the source location for the diagnostics message. @@ -351,6 +374,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, // integer_literal: // ? Integer constant ? // +// Haven't removed this yet in order to not rewrite the array subscription std::optional<int64_t> DILParser::ParseIntegerConstant() { std::string number_spelling; if (CurToken().GetKind() == Token::minus) { @@ -370,6 +394,52 @@ std::optional<int64_t> DILParser::ParseIntegerConstant() { return std::nullopt; } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair<const char *, lldb::BasicType> type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique<ScalarLiteralNode>(token.GetLocation(), type, + scalar_value); + } + return std::make_unique<ErrorNode>(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), >From 81404fb9fd2eeab5f3d11a00f1ab3074262eccdc Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <ikuk...@accesssoftek.com> Date: Thu, 17 Jul 2025 00:28:10 +0500 Subject: [PATCH 2/2] Use Scalar for arithmetic operations --- lldb/include/lldb/ValueObject/DILEval.h | 6 +- lldb/source/ValueObject/DILEval.cpp | 338 ++++++------------------ 2 files changed, 78 insertions(+), 266 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 9e9028f6122fd..d9ac16cd41d16 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -62,13 +62,9 @@ class Interpreter : Visitor { ConvertValueObjectToTypeSystem(lldb::ValueObjectSP valobj, lldb::TypeSystemSP type_system); - llvm::Error PrepareBinaryAddition(lldb::ValueObjectSP &lhs, - lldb::ValueObjectSP &rhs, - uint32_t location); - llvm::Expected<lldb::ValueObjectSP> EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, - uint32_t location); + uint32_t location, std::shared_ptr<StackFrame> ctx); // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 2e42b57e93042..42d09686d729b 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -448,290 +448,107 @@ static CompilerType GetBasicType(lldb::TypeSystemSP type_system, return empty_type; } -static CompilerType DoIntegralPromotion(CompilerType from, - std::shared_ptr<StackFrame> ctx) { - if (!from.IsInteger() && !from.IsUnscopedEnumerationType()) - return from; - - if (!from.IsPromotableIntegerType()) - return from; - - if (from.IsUnscopedEnumerationType()) - return DoIntegralPromotion(from.GetEnumerationIntegerType(), ctx); - lldb::BasicType builtin_type = - from.GetCanonicalType().GetBasicTypeEnumeration(); - lldb::TypeSystemSP type_system = from.GetTypeSystem().GetSharedPointer(); - - uint64_t from_size = 0; - if (builtin_type == lldb::eBasicTypeWChar || - builtin_type == lldb::eBasicTypeSignedWChar || - builtin_type == lldb::eBasicTypeUnsignedWChar || - builtin_type == lldb::eBasicTypeChar16 || - builtin_type == lldb::eBasicTypeChar32) { - // Find the type that can hold the entire range of values for our type. - bool is_signed = from.IsSigned(); - if (auto temp = from.GetByteSize(ctx.get())) - from_size = *temp; - - CompilerType promote_types[] = { - GetBasicType(type_system, lldb::eBasicTypeInt), - GetBasicType(type_system, lldb::eBasicTypeUnsignedInt), - GetBasicType(type_system, lldb::eBasicTypeLong), - GetBasicType(type_system, lldb::eBasicTypeUnsignedLong), - GetBasicType(type_system, lldb::eBasicTypeLongLong), - GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong), - }; - for (auto &type : promote_types) { - uint64_t byte_size = 0; - if (auto temp = type.GetByteSize(ctx.get())) - byte_size = *temp; - if (from_size < byte_size || - (from_size == byte_size && - is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) { - return type; - } +static CompilerType GetIntCompilerTypeForSize(uint32_t size, bool is_signed, + lldb::TypeSystemSP type_system, + std::shared_ptr<StackFrame> ctx) { + lldb::BasicType promote_types[] = { + lldb::eBasicTypeChar, lldb::eBasicTypeUnsignedChar, + lldb::eBasicTypeShort, lldb::eBasicTypeUnsignedShort, + lldb::eBasicTypeInt, lldb::eBasicTypeUnsignedInt, + lldb::eBasicTypeLong, lldb::eBasicTypeUnsignedLong, + lldb::eBasicTypeLongLong, lldb::eBasicTypeUnsignedLongLong, + }; + for (auto &basic_type : promote_types) { + uint64_t byte_size = 0; + CompilerType type = GetBasicType(type_system, basic_type); + if (auto temp = type.GetByteSize(ctx.get())) + byte_size = *temp; + if (size < byte_size || + (size == byte_size && + is_signed == (bool)(type.GetTypeInfo() & lldb::eTypeIsSigned))) { + return type; } - - llvm_unreachable("char type should fit into long long"); } - // Here we can promote only to "int" or "unsigned int". - CompilerType int_type = GetBasicType(type_system, lldb::eBasicTypeInt); - uint64_t int_byte_size = 0; - if (auto temp = int_type.GetByteSize(ctx.get())) - int_byte_size = *temp; - - // Signed integer types can be safely promoted to "int". - if (from.IsSigned()) { - return int_type; - } - // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold - // their entire value range. - return (from_size == int_byte_size) - ? GetBasicType(type_system, lldb::eBasicTypeUnsignedInt) - : int_type; + llvm_unreachable("size could not fit into long long"); + return CompilerType(); } -static lldb::ValueObjectSP UnaryConversion(lldb::ValueObjectSP valobj, - std::shared_ptr<StackFrame> ctx) { - // Perform usual conversions for unary operators. - CompilerType in_type = valobj->GetCompilerType(); - CompilerType result_type; - - if (valobj->GetCompilerType().IsInteger() || - valobj->GetCompilerType().IsUnscopedEnumerationType()) { - CompilerType promoted_type = - DoIntegralPromotion(valobj->GetCompilerType(), ctx); - if (!promoted_type.CompareTypes(valobj->GetCompilerType())) - return valobj->CastToBasicType(promoted_type); +static lldb::ValueObjectSP +CreateValueObjectFromScalar(Scalar scalar, lldb::TargetSP target, + lldb::TypeSystemSP type_system, + std::shared_ptr<StackFrame> ctx) { + switch (scalar.GetType()) { + case Scalar::e_int: { + llvm::APSInt apsint = scalar.GetAPSInt(); + auto ret_type = GetIntCompilerTypeForSize( + scalar.GetByteSize(), scalar.IsSigned(), type_system, ctx); + return ValueObject::CreateValueObjectFromAPInt(target, apsint, ret_type, + "result"); } - - return valobj; -} - -static size_t ConversionRank(CompilerType type) { - // Get integer conversion rank - // https://eel.is/c++draft/conv.rank - 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; - - // The ranks of char16_t, char32_t, and wchar_t are equal to the - // ranks of their underlying types. - case lldb::eBasicTypeWChar: - case lldb::eBasicTypeSignedWChar: - case lldb::eBasicTypeUnsignedWChar: - return 3; - case lldb::eBasicTypeChar16: - return 3; - case lldb::eBasicTypeChar32: - return 4; - - default: - break; + case Scalar::e_float: { + llvm::APFloat apfloat = scalar.GetAPFloat(); + auto ret_type = + scalar.GetByteSize() <= 4 + ? type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat) + : type_system->GetBasicTypeFromAST(lldb::eBasicTypeDouble); + return ValueObject::CreateValueObjectFromAPFloat(target, apfloat, ret_type, + "result"); } - return 0; -} - -static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) { - switch (basic_type) { - case lldb::eBasicTypeInt: - return lldb::eBasicTypeUnsignedInt; - case lldb::eBasicTypeLong: - return lldb::eBasicTypeUnsignedLong; - case lldb::eBasicTypeLongLong: - return lldb::eBasicTypeUnsignedLongLong; default: - return basic_type; + return lldb::ValueObjectSP(); } } -static void PerformIntegerConversions(std::shared_ptr<StackFrame> ctx, - lldb::ValueObjectSP &lhs, - lldb::ValueObjectSP &rhs, - bool convert_lhs, bool convert_rhs) { - CompilerType l_type = lhs->GetCompilerType(); - CompilerType r_type = rhs->GetCompilerType(); - if (r_type.IsSigned() && !l_type.IsSigned()) { - uint64_t l_size = 0; - uint64_t r_size = 0; - if (auto temp = l_type.GetByteSize(ctx.get())) - l_size = *temp; - ; - if (auto temp = r_type.GetByteSize(ctx.get())) - r_size = *temp; - if (l_size <= r_size) { - if (r_size == l_size) { - lldb::TypeSystemSP type_system = - l_type.GetTypeSystem().GetSharedPointer(); - auto r_type_unsigned = GetBasicType( - type_system, - BasicTypeToUnsigned( - r_type.GetCanonicalType().GetBasicTypeEnumeration())); - if (convert_rhs) - rhs = rhs->CastToBasicType(r_type_unsigned); +static Scalar GetScalarFromValueObject(lldb::ValueObjectSP valobj, + std::shared_ptr<StackFrame> ctx) { + if (valobj->GetCompilerType().IsInteger()) { + llvm::Expected<llvm::APSInt> value = valobj->GetValueAsAPSInt(); + if (value) { + auto type_size = valobj->GetCompilerType().GetBitSize(ctx.get()); + if (type_size) { + llvm::APInt adjusted(*type_size, value->getExtValue(), + value->isSigned()); + return Scalar(adjusted); } } + } else { + llvm::Expected<llvm::APFloat> l_value = valobj->GetValueAsAPFloat(); + if (l_value) + return Scalar(*l_value); } - if (convert_lhs) - lhs = lhs->CastToBasicType(rhs->GetCompilerType()); -} - -static CompilerType ArithmeticConversions(lldb::ValueObjectSP &lhs, - lldb::ValueObjectSP &rhs, - std::shared_ptr<StackFrame> ctx) { - // Apply unary conversion (e.g. intergal promotion) for both operands. - lhs = UnaryConversion(lhs, ctx); - rhs = UnaryConversion(rhs, ctx); - - 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()) { - CompilerType bad_type; - return bad_type; - } - - // Removed floating point conversions - if (lhs_type.IsFloat() || rhs_type.IsFloat()) { - CompilerType bad_type; - return bad_type; - } - - if (lhs_type.IsInteger() && rhs_type.IsInteger()) { - using Rank = std::tuple<size_t, bool>; - Rank l_rank = {ConversionRank(lhs_type), !lhs_type.IsSigned()}; - Rank r_rank = {ConversionRank(rhs_type), !rhs_type.IsSigned()}; - - if (l_rank < r_rank) { - PerformIntegerConversions(ctx, lhs, rhs, true, true); - } else if (l_rank > r_rank) { - PerformIntegerConversions(ctx, rhs, lhs, true, true); - } - } - - return rhs_type; + return Scalar(); } -llvm::Error Interpreter::PrepareBinaryAddition(lldb::ValueObjectSP &lhs, - lldb::ValueObjectSP &rhs, - uint32_t location) { - // Operation '+' works for: - // - // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} - // {integer,unscoped_enum} <-> pointer - // pointer <-> {integer,unscoped_enum} - auto orig_lhs_type = lhs->GetCompilerType(); - auto orig_rhs_type = rhs->GetCompilerType(); - auto result_type = ArithmeticConversions(lhs, rhs, m_exe_ctx_scope); - - if (result_type.IsScalarType()) - return llvm::Error::success(); - - // Removed pointer arithmetics - return llvm::make_error<DILDiagnosticError>(m_expr, "unimplemented", - location); -} - -static lldb::ValueObjectSP EvaluateArithmeticOpInteger(lldb::TargetSP target, - BinaryOpKind kind, - lldb::ValueObjectSP lhs, - lldb::ValueObjectSP rhs, - CompilerType rtype) { - assert(lhs->GetCompilerType().IsInteger() && - rhs->GetCompilerType().IsInteger() && - "invalid ast: both operands must be integers"); - - auto wrap = [target, rtype](auto value) { - return ValueObject::CreateValueObjectFromAPInt(target, value, rtype, - "result"); - }; - - llvm::Expected<llvm::APSInt> l_value = lhs->GetValueAsAPSInt(); - llvm::Expected<llvm::APSInt> r_value = rhs->GetValueAsAPSInt(); +static lldb::ValueObjectSP +EvaluateArithmeticOp(lldb::TargetSP target, BinaryOpKind kind, + lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, + std::shared_ptr<StackFrame> ctx) { + auto type_system = lhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); + Scalar l = GetScalarFromValueObject(lhs, ctx); + Scalar r = GetScalarFromValueObject(rhs, ctx); - if (l_value && r_value) { - llvm::APSInt l = *l_value; - llvm::APSInt r = *r_value; + if (!l.IsValid() || !r.IsValid()) + return lldb::ValueObjectSP(); - switch (kind) { - case BinaryOpKind::Add: - return wrap(l + r); + switch (kind) { + case BinaryOpKind::Add: + return CreateValueObjectFromScalar(l + r, target, type_system, ctx); - default: - assert(false && "invalid ast: invalid arithmetic operation"); - return lldb::ValueObjectSP(); - } - } else { + default: + assert(false && "invalid ast: invalid arithmetic operation"); return lldb::ValueObjectSP(); } } -static lldb::ValueObjectSP EvaluateArithmeticOp(lldb::TargetSP target, - BinaryOpKind kind, - lldb::ValueObjectSP lhs, - lldb::ValueObjectSP rhs, - CompilerType rtype) { - assert((rtype.IsInteger() || rtype.IsFloat()) && - "invalid ast: result type must either integer or floating point"); - - // Evaluate arithmetic operation for two integral values. - if (rtype.IsInteger()) { - return EvaluateArithmeticOpInteger(target, kind, lhs, rhs, rtype); - } - - // Removed floating point arithmetics - - return lldb::ValueObjectSP(); -} - -llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition( - lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { +llvm::Expected<lldb::ValueObjectSP> +Interpreter::EvaluateBinaryAddition(lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, uint32_t location, + std::shared_ptr<StackFrame> ctx) { // Addition of two arithmetic types. if (lhs->GetCompilerType().IsScalarType() && rhs->GetCompilerType().IsScalarType()) { - return EvaluateArithmeticOp(m_target, BinaryOpKind::Add, lhs, rhs, - lhs->GetCompilerType().GetCanonicalType()); + return EvaluateArithmeticOp(m_target, BinaryOpKind::Add, lhs, rhs, ctx); } // Removed pointer arithmetics @@ -792,9 +609,8 @@ Interpreter::Visit(const BinaryOpNode *node) { switch (node->GetKind()) { case BinaryOpKind::Add: - if (auto err = PrepareBinaryAddition(lhs, rhs, node->GetLocation())) - return err; - return EvaluateBinaryAddition(lhs, rhs, node->GetLocation()); + return EvaluateBinaryAddition(lhs, rhs, node->GetLocation(), + m_exe_ctx_scope); // Other ops _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits