llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) <details> <summary>Changes</summary> Add binary bitwise operators `|`, `^`, `&`, and unary bitwise negation `~`. --- Full diff: https://github.com/llvm/llvm-project/pull/209768.diff 11 Files Affected: - (modified) lldb/docs/dil-expr-lang.ebnf (+8-2) - (modified) lldb/include/lldb/ValueObject/DILAST.h (+4) - (modified) lldb/include/lldb/ValueObject/DILEval.h (+3) - (modified) lldb/include/lldb/ValueObject/DILLexer.h (+3) - (modified) lldb/include/lldb/ValueObject/DILParser.h (+3) - (modified) lldb/source/ValueObject/DILAST.cpp (+6) - (modified) lldb/source/ValueObject/DILEval.cpp (+60) - (modified) lldb/source/ValueObject/DILLexer.cpp (+9) - (modified) lldb/source/ValueObject/DILParser.cpp (+79-4) - (modified) lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py (+57) - (modified) lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp (+9) ``````````diff diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index fce8f4461b46f..ec4687ab05389 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -5,13 +5,19 @@ expression = assignment_expression ; -assignment_expression = shift_expression - | shift_expression assignment_operator assignment_expression ; +assignment_expression = inclusive_or_expression + | inclusive_or_expression assignment_operator assignment_expression ; assignment_operator = "=" | "+=" | "-=" ; +inclusive_or_expression = exclusive_or_expression {"|" exclusive_or_expression} ; + +exclusive_or_expression = and_expression {"^" and_expression} ; + +and_expression = shift_expression {"&" shift_expression} ; + shift_expression = additive_expression {"<<" additive_expression} | additive_expression {">>" additive_expression} ; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 93310a91a15bb..a2b02bb4a9da8 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -38,6 +38,7 @@ enum class UnaryOpKind { Deref, ///< "*" Minus, ///< "-" Plus, ///< "+" + Not, ///< "~" }; /// The binary operators recognized by DIL. @@ -48,6 +49,9 @@ enum class BinaryOpKind { Div, ///< "/" Mul, ///< "*" Rem, ///< "%" + And, ///< "&" + Xor, ///< "^" + Or, ///< "|" Shl, ///< "<<" Shr, ///< ">>" Sub, ///< "-" diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 35784ea9987f9..59fe465b2dd88 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -126,6 +126,9 @@ class Interpreter : Visitor { llvm::Expected<lldb::ValueObjectSP> EvaluateBinaryRemainder(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location); + llvm::Expected<lldb::ValueObjectSP> + EvaluateBinaryBitwise(BinaryOpKind kind, lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, uint32_t location); llvm::Expected<CompilerType> PickIntegerType(lldb::TypeSystemSP type_system, ExecutionContextScope &ctx, const IntegerLiteralNode &literal); diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index f9f42dc59b311..8b58bbcc84547 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -27,6 +27,7 @@ class Token { enum Kind { amp, arrow, + caret, colon, coloncolon, eof, @@ -44,12 +45,14 @@ class Token { minusequal, percent, period, + pipe, plus, plusequal, r_paren, r_square, slash, star, + tilde, }; Token(Kind kind, std::string spelling, uint32_t start) diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9e2bbff4b6614..3e58620875a6a 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,6 +84,9 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseAssignmentExpression(); + ASTNodeUP ParseInclusiveOrExpression(); + ASTNodeUP ParseExclusiveOrExpression(); + ASTNodeUP ParseAndExpression(); ASTNodeUP ParseShiftExpression(); ASTNodeUP ParseAdditiveExpression(); ASTNodeUP ParseMultiplicativeExpression(); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index 40bf07bdd5aab..bb018712a1ba4 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -29,6 +29,12 @@ BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) { return BinaryOpKind::Div; case Token::percent: return BinaryOpKind::Rem; + case Token::amp: + return BinaryOpKind::And; + case Token::caret: + return BinaryOpKind::Xor; + case Token::pipe: + return BinaryOpKind::Or; case Token::lessless: return BinaryOpKind::Shl; case Token::greatergreater: diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 5fc2376be0e75..5d4d520d233f4 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -559,6 +559,34 @@ Interpreter::Visit(const UnaryOpNode &node) { } return operand; } + case UnaryOpKind::Not: { + llvm::Expected<lldb::ValueObjectSP> conv_op = + UnaryConversion(operand, node.GetLocation()); + if (!conv_op) + return conv_op; + operand = *conv_op; + CompilerType operand_type = operand->GetCompilerType(); + if (!operand_type.IsInteger()) { + std::string errMsg = + llvm::formatv("invalid argument type '{0}' to unary expression", + operand_type.GetTypeName()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, + node.GetLocation()); + } + Scalar scalar; + bool resolved = operand->ResolveValue(scalar); + if (!resolved) { + std::string errMsg = llvm::formatv("invalid operand value: {0}", + operand->GetError().AsCString()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, + node.GetLocation()); + } + + bool flipped = scalar.OnesComplement(); + if (flipped) + return ValueObject::CreateValueObjectFromScalar( + m_stack_frame, scalar, operand->GetCompilerType(), "result"); + } } return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation", node.GetLocation()); @@ -634,6 +662,12 @@ Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, return value_object(l / r); case BinaryOpKind::Rem: return value_object(l % r); + case BinaryOpKind::And: + return value_object(l & r); + case BinaryOpKind::Xor: + return value_object(l ^ r); + case BinaryOpKind::Or: + return value_object(l | r); case BinaryOpKind::Shl: return value_object(l << r); case BinaryOpKind::Shr: @@ -891,6 +925,28 @@ Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs, return lhs; } +llvm::Expected<lldb::ValueObjectSP> +Interpreter::EvaluateBinaryBitwise(BinaryOpKind kind, lldb::ValueObjectSP lhs, + lldb::ValueObjectSP rhs, uint32_t location) { + // Operations {'&', '|', '^'} work for: + // {integer,unscoped_enum} <-> {integer,unscoped_enum} + 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.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, errMsg, location); + } + + return EvaluateScalarOp(kind, lhs, rhs, result_type, location); +} + llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { @@ -977,6 +1033,10 @@ Interpreter::Visit(const BinaryOpNode &node) { return EvaluateBinaryDivision(lhs, rhs, node.GetLocation()); case BinaryOpKind::Rem: return EvaluateBinaryRemainder(lhs, rhs, node.GetLocation()); + case BinaryOpKind::And: + case BinaryOpKind::Xor: + case BinaryOpKind::Or: + return EvaluateBinaryBitwise(node.GetKind(), lhs, rhs, node.GetLocation()); case BinaryOpKind::Shl: case BinaryOpKind::Shr: return EvaluateBinaryShift(node.GetKind(), lhs, rhs, node.GetLocation()); diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 997ba6b09f872..813ab3bbcab3e 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -24,6 +24,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "amp"; case Kind::arrow: return "arrow"; + case Kind::caret: + return "caret"; case Kind::colon: return "colon"; case Kind::coloncolon: @@ -58,6 +60,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "percent"; case Kind::period: return "period"; + case Kind::pipe: + return "pipe"; case Kind::plus: return "plus"; case Kind::plusequal: @@ -70,6 +74,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "slash"; case Token::star: return "star"; + case Token::tilde: + return "tilde"; } llvm_unreachable("Unknown token name"); } @@ -205,6 +211,7 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, {Token::minusequal, "-="}, {Token::plusequal, "+="}, {Token::amp, "&"}, + {Token::caret, "^"}, {Token::colon, ":"}, {Token::equal, "="}, {Token::l_paren, "("}, @@ -212,11 +219,13 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, {Token::minus, "-"}, {Token::percent, "%"}, {Token::period, "."}, + {Token::pipe, "|"}, {Token::plus, "+"}, {Token::r_paren, ")"}, {Token::r_square, "]"}, {Token::slash, "/"}, {Token::star, "*"}, + {Token::tilde, "~"}, }; 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 b55b12a2bc42a..3af14d31c2e8b 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -134,8 +134,8 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); } // Parse an assignment_expression // // assignment_expression -// shift_expression -// shift_expression assignment_operator assignment_expression +// inclusive_or_expression +// inclusive_or_expression assignment_operator assignment_expression // // assignment_operator: // "=" @@ -143,7 +143,7 @@ ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); } // "-=" // ASTNodeUP DILParser::ParseAssignmentExpression() { - auto lhs = ParseShiftExpression(); + auto lhs = ParseInclusiveOrExpression(); assert(lhs && "ASTNodeUP must not contain a nullptr"); // Check if it's an assignment expression. @@ -160,6 +160,77 @@ ASTNodeUP DILParser::ParseAssignmentExpression() { return lhs; } +// Parse an inclusive_or_expression. +// +// inclusive_or_expression: +// exclusive_or_expression {"|" exclusive_or_expression} +// +ASTNodeUP DILParser::ParseInclusiveOrExpression() { + auto lhs = ParseExclusiveOrExpression(); + assert(lhs && "ASTNodeUP must not contain a nullptr"); + + while (CurToken().Is(Token::pipe)) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto rhs = ParseExclusiveOrExpression(); + assert(rhs && "ASTNodeUP must not contain a nullptr"); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} + +// Parse an exclusive_or_expression. +// +// exclusive_or_expression: +// and_expression {"^" and_expression} +// +ASTNodeUP DILParser::ParseExclusiveOrExpression() { + auto lhs = ParseAndExpression(); + assert(lhs && "ASTNodeUP must not contain a nullptr"); + + while (CurToken().Is(Token::caret)) { + Token token = CurToken(); + m_dil_lexer.Advance(); + auto rhs = ParseAndExpression(); + assert(rhs && "ASTNodeUP must not contain a nullptr"); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} + +// Parse an and_expression. +// +// and_expression: +// shift_expression {"&" shift_expression} +// +ASTNodeUP DILParser::ParseAndExpression() { + auto lhs = ParseShiftExpression(); + assert(lhs && "ASTNodeUP must not contain a nullptr"); + + while (CurToken().Is(Token::amp)) { + Token token = CurToken(); + if (token.Is(Token::amp) && m_mode != lldb::eDILModeFull) { + BailOut("bitwise and (&) is allowed only in DIL full mode", + token.GetLocation(), token.GetSpelling().length()); + return std::make_unique<ErrorNode>(); + } + m_dil_lexer.Advance(); + auto rhs = ParseShiftExpression(); + assert(rhs && "ASTNodeUP must not contain a nullptr"); + lhs = std::make_unique<BinaryOpNode>( + token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()), + std::move(lhs), std::move(rhs)); + } + + return lhs; +} + // Parse a shift_expression. // // shift_expression: @@ -293,10 +364,11 @@ ASTNodeUP DILParser::ParseCastExpression() { // "*" // "+" // "-" +// "~" // ASTNodeUP DILParser::ParseUnaryExpression() { if (CurToken().IsOneOf( - {Token::amp, Token::star, Token::minus, Token::plus})) { + {Token::amp, Token::star, Token::minus, Token::plus, Token::tilde})) { Token token = CurToken(); uint32_t loc = token.GetLocation(); m_dil_lexer.Advance(); @@ -315,6 +387,9 @@ ASTNodeUP DILParser::ParseUnaryExpression() { case Token::plus: return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Plus, std::move(rhs)); + case Token::tilde: + return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Not, + std::move(rhs)); default: llvm_unreachable("invalid token kind"); } diff --git a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py index 150a63d1ca975..a81b676070f45 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py @@ -19,6 +19,20 @@ def test_bitwise(self): self.runCmd("settings set target.experimental.use-DIL true") + # Check unary negation + self.expect_var_path("~(-1)", value="0") + self.expect_var_path("~~0", value="0") + self.expect_var_path("~0", value="-1") + self.expect_var_path("~1", value="-2") + self.expect_var_path("~0LL", value="-1") + self.expect_var_path("~1LL", value="-2") + self.expect_var_path("~true", value="-2") + self.expect_var_path("~false", value="-1") + self.expect_var_path("~var_true", value="-2") + self.expect_var_path("~var_false", value="-1") + self.expect_var_path("~ull_max", value="0") + self.expect_var_path("~0b1011", value="-12") + # Check bitwise shifts self.expect_var_path("(1 << 5)", value="32") self.expect_var_path("(32 >> 2)", value="8") @@ -33,7 +47,41 @@ def test_bitwise(self): self.expect_var_path("2 >> enum_one", value="1") self.expect_var_path("i64 << 63", type="uint64_t") + # Check And, Xor, Or + self.expect_var_path("0b1011 & 0xFF", value="11") + self.expect_var_path("0b1011 & mask_ff", value="11") + self.expect_var_path("0b1011 & 0b0111", value="3") + self.expect_var_path("0b1011 | 0b0111", value="15") + self.expect_var_path("-0b1011 | 0xFF", value="-1") + self.expect_var_path("-0b1011 | 0xFFu", value="4294967295") + self.expect_var_path("0b1011 ^ 0b0111", value="12") + # Check errors + self.expect( + "frame var -- '~1.0'", + error=True, + substrs=["invalid argument type 'double' to unary expression"], + ) + self.expect( + "frame var -- '~s'", + error=True, + substrs=["invalid argument type 'S' to unary expression"], + ) + self.expect( + "frame var -- 's & 1.0'", + error=True, + substrs=["invalid operands to binary expression ('S' and 'double')"], + ) + self.expect( + "frame var -- '1 ^ s'", + error=True, + substrs=["invalid operands to binary expression ('int' and 'S')"], + ) + self.expect( + "frame var -- '1 | 1.0'", + error=True, + substrs=["invalid operands to binary expression ('int' and 'double')"], + ) self.expect( "frame var -- '1 << 1.0'", error=True, @@ -54,3 +102,12 @@ def test_bitwise(self): error=True, substrs=["invalid shift amount"], ) + + # Check that bitwise & is allowed only in full mode + frame = thread.GetFrameAtIndex(0) + simple = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeLegacy) + full = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeFull) + self.assertFailure(simple.GetError()) + self.assertFailure(legacy.GetError()) + self.assertSuccess(full.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp index 2978946b757b9..38236606db5b6 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp @@ -1,12 +1,21 @@ #include <cstdint> +#include <limits> + enum UnscopedEnum { kZero, kOne }; int main(int argc, char **argv) { + bool var_true = true; + bool var_false = false; + + unsigned long long ull_max = std::numeric_limits<unsigned long long>::max(); + unsigned long long ull_zero = 0; + struct S { } s; auto enum_one = UnscopedEnum::kOne; uint64_t i64 = 1; + uint32_t mask_ff = 0xFF; return 0; // Set a breakpoint here } `````````` </details> https://github.com/llvm/llvm-project/pull/209768 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
