https://github.com/kuilpd updated 
https://github.com/llvm/llvm-project/pull/209768

>From 3e59893eb147ac92781b965914b1c2895a1141c3 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <[email protected]>
Date: Wed, 15 Jul 2026 18:24:08 +0500
Subject: [PATCH] [lldb] Add bitwise operators to DIL

---
 lldb/docs/dil-expr-lang.ebnf                  | 10 ++-
 lldb/include/lldb/ValueObject/DILAST.h        |  4 +
 lldb/include/lldb/ValueObject/DILEval.h       |  3 +
 lldb/include/lldb/ValueObject/DILLexer.h      |  3 +
 lldb/include/lldb/ValueObject/DILParser.h     |  3 +
 lldb/source/ValueObject/DILAST.cpp            |  6 ++
 lldb/source/ValueObject/DILEval.cpp           | 60 ++++++++++++++
 lldb/source/ValueObject/DILLexer.cpp          |  9 ++
 lldb/source/ValueObject/DILParser.cpp         | 83 ++++++++++++++++++-
 .../expr/Bitwise/TestFrameVarDILBitwise.py    | 57 +++++++++++++
 .../frame/var-dil/expr/Bitwise/main.cpp       |  9 ++
 11 files changed, 241 insertions(+), 6 deletions(-)

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
 }

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to