llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) <details> <summary>Changes</summary> A draft to discuss scalar literal node implementation --- Patch is 26.08 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/147064.diff 8 Files Affected: - (modified) lldb/include/lldb/ValueObject/DILAST.h (+58) - (modified) lldb/include/lldb/ValueObject/DILEval.h (+15) - (modified) lldb/include/lldb/ValueObject/DILLexer.h (+1) - (modified) lldb/include/lldb/ValueObject/DILParser.h (+3) - (modified) lldb/source/ValueObject/DILAST.cpp (+21) - (modified) lldb/source/ValueObject/DILEval.cpp (+405) - (modified) lldb/source/ValueObject/DILLexer.cpp (+6-4) - (modified) lldb/source/ValueObject/DILParser.cpp (+72-2) ``````````diff 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 = + lh... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/147064 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits