[Lldb-commits] [lldb] [lldb] Add bitwise shift operators and fix literals' sign in DIL (PR #192506)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu May 14 11:51:46 PDT 2026
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/192506
>From 847e3c13c16377b24127f31767caeb10e8e43dec Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 16 Apr 2026 17:36:51 +0500
Subject: [PATCH 1/2] [lldb] Add bitwise shift operators and fix literals' sign
in DIL
---
lldb/docs/dil-expr-lang.ebnf | 5 +-
lldb/include/lldb/ValueObject/DILAST.h | 2 +
lldb/include/lldb/ValueObject/DILEval.h | 3 +
lldb/include/lldb/ValueObject/DILLexer.h | 2 +
lldb/include/lldb/ValueObject/DILParser.h | 1 +
lldb/source/ValueObject/DILAST.cpp | 4 ++
lldb/source/ValueObject/DILEval.cpp | 52 +++++++++++++++++
lldb/source/ValueObject/DILLexer.cpp | 25 +++++++--
lldb/source/ValueObject/DILParser.cpp | 28 +++++++++-
.../frame/var-dil/expr/Bitwise/Makefile | 3 +
.../expr/Bitwise/TestFrameVarDILBitwise.py | 56 +++++++++++++++++++
.../frame/var-dil/expr/Bitwise/main.cpp | 12 ++++
12 files changed, 185 insertions(+), 8 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Bitwise/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index b80533c015c95..7c4a187c9cf52 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -3,7 +3,10 @@
(* This is currently a subset of the final DIL Language, matching the current
DIL implementation. *)
-expression = additive_expression ;
+expression = shift_expression ;
+
+shift_expression = additive_expression {"<<" additive_expression}
+ | additive_expression {">>" additive_expression} ;
additive_expression = multiplicative_expression {"+" multiplicative_expression}
multiplicative_expression {"-" multiplicative_expression} ;
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 50530d687c41f..3235887c27f2c 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -47,6 +47,8 @@ enum class BinaryOpKind {
Mul, // "*"
Div, // "/"
Rem, // "%"
+ Shl, // "<<"
+ Shr, // ">>"
};
/// Translates DIL tokens to BinaryOpKind.
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index c5f41b5a0bc76..4def783520c0a 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -101,6 +101,9 @@ class Interpreter : Visitor {
CompilerType result_type,
uint32_t location);
llvm::Expected<lldb::ValueObjectSP>
+ EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs,
+ lldb::ValueObjectSP rhs, uint32_t location);
+ llvm::Expected<lldb::ValueObjectSP>
EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
uint32_t location);
llvm::Expected<lldb::ValueObjectSP>
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 1672e52b2adb0..82fbecd81f13b 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -31,12 +31,14 @@ class Token {
coloncolon,
eof,
float_constant,
+ greatergreater,
identifier,
integer_constant,
kw_false,
kw_true,
l_paren,
l_square,
+ lessless,
minus,
percent,
period,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 351eea6d0d268..b9cd32d0807d4 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -93,6 +93,7 @@ class DILParser {
ASTNodeUP Run();
ASTNodeUP ParseExpression();
+ ASTNodeUP ParseShiftExpression();
ASTNodeUP ParseAdditiveExpression();
ASTNodeUP ParseMultiplicativeExpression();
ASTNodeUP ParseUnaryExpression();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index a454cacbf494f..333c427a4bfa4 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -23,6 +23,10 @@ BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) {
return BinaryOpKind::Div;
case Token::percent:
return BinaryOpKind::Rem;
+ case Token::lessless:
+ return BinaryOpKind::Shl;
+ case Token::greatergreater:
+ return BinaryOpKind::Shr;
default:
break;
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 7801b9225f19e..0d7182a4dd772 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -615,6 +615,10 @@ Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
return value_object(l / r);
case BinaryOpKind::Rem:
return value_object(l % r);
+ case BinaryOpKind::Shl:
+ return value_object(l << r);
+ case BinaryOpKind::Shr:
+ return value_object(l >> r);
}
return llvm::make_error<DILDiagnosticError>(
m_expr, "invalid arithmetic operation", location);
@@ -812,6 +816,47 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryRemainder(
return EvaluateScalarOp(BinaryOpKind::Rem, lhs, rhs, result_type, location);
}
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::EvaluateBinaryShift(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 lhs_or_err = UnaryConversion(lhs, location);
+ if (!lhs_or_err)
+ return lhs_or_err.takeError();
+ lhs = *lhs_or_err;
+ auto rhs_or_err = UnaryConversion(rhs, location);
+ if (!rhs_or_err)
+ return rhs_or_err.takeError();
+ rhs = *rhs_or_err;
+
+ CompilerType lhs_type = lhs->GetCompilerType();
+ CompilerType rhs_type = rhs->GetCompilerType();
+ if (!lhs_type.IsInteger() || !rhs_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);
+ }
+
+ bool success;
+ uint64_t amount = rhs->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "could not get the shift amount as an integer", location);
+ llvm::Expected<uint64_t> lhs_size =
+ lhs_type.GetBitSize(m_exe_ctx_scope.get());
+ if (!lhs_size)
+ return lhs_size.takeError();
+ if (amount >= *lhs_size)
+ return llvm::make_error<DILDiagnosticError>(m_expr, "invalid shift amount",
+ location);
+
+ return EvaluateScalarOp(kind, lhs, rhs, lhs_type, location);
+}
+
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const BinaryOpNode &node) {
auto lhs_or_err = EvaluateAndDereference(node.GetLHS());
@@ -844,6 +889,9 @@ Interpreter::Visit(const BinaryOpNode &node) {
return EvaluateBinaryDivision(lhs, rhs, node.GetLocation());
case BinaryOpKind::Rem:
return EvaluateBinaryRemainder(lhs, rhs, node.GetLocation());
+ case BinaryOpKind::Shl:
+ case BinaryOpKind::Shr:
+ return EvaluateBinaryShift(node.GetKind(), lhs, rhs, node.GetLocation());
}
return llvm::make_error<DILDiagnosticError>(
@@ -1218,7 +1266,11 @@ Interpreter::Visit(const IntegerLiteralNode &node) {
type->GetBitSize(m_exe_ctx_scope.get());
if (!type_bitsize)
return type_bitsize.takeError();
+ // Literal itself cannot be a negative value, so we do an unsigned extension.
scalar.TruncOrExtendTo(*type_bitsize, false);
+ // If the picked compiler type is signed, make the scalar signed as well.
+ if (type->IsSigned())
+ scalar.MakeSigned();
return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar,
*type, "result");
}
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index c9848484851a7..eebac7179053d 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -32,6 +32,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "eof";
case Kind::float_constant:
return "float_constant";
+ case Kind::greatergreater:
+ return "greatergreater";
case Kind::identifier:
return "identifier";
case Kind::integer_constant:
@@ -44,6 +46,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "l_paren";
case Kind::l_square:
return "l_square";
+ case Kind::lessless:
+ return "lessless";
case Kind::minus:
return "minus";
case Token::percent:
@@ -185,11 +189,22 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
}
constexpr std::pair<Token::Kind, const char *> operators[] = {
- {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
- {Token::colon, ":"}, {Token::l_paren, "("}, {Token::l_square, "["},
- {Token::minus, "-"}, {Token::percent, "%"}, {Token::period, "."},
- {Token::plus, "+"}, {Token::r_paren, ")"}, {Token::r_square, "]"},
- {Token::slash, "/"}, {Token::star, "*"},
+ {Token::arrow, "->"},
+ {Token::coloncolon, "::"},
+ {Token::greatergreater, ">>"},
+ {Token::lessless, "<<"},
+ {Token::amp, "&"},
+ {Token::colon, ":"},
+ {Token::l_paren, "("},
+ {Token::l_square, "["},
+ {Token::minus, "-"},
+ {Token::percent, "%"},
+ {Token::period, "."},
+ {Token::plus, "+"},
+ {Token::r_paren, ")"},
+ {Token::r_square, "]"},
+ {Token::slash, "/"},
+ {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 51bdffd5087ca..e56abc6ca545e 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -148,14 +148,38 @@ ASTNodeUP DILParser::Run() {
// Parse an expression.
//
// expression:
-// cast_expression
+// shift_expression
//
-ASTNodeUP DILParser::ParseExpression() { return ParseAdditiveExpression(); }
+ASTNodeUP DILParser::ParseExpression() { return ParseShiftExpression(); }
+
+// Parse a shift_expression.
+//
+// shift_expression:
+// additive_expression {"<<" additive_expression}
+// additive_expression {">>" additive_expression}
+//
+ASTNodeUP DILParser::ParseShiftExpression() {
+ auto lhs = ParseAdditiveExpression();
+ assert(lhs && "ASTNodeUP must not contain a nullptr");
+
+ while (CurToken().IsOneOf({Token::lessless, Token::greatergreater})) {
+ Token token = CurToken();
+ m_dil_lexer.Advance();
+ auto rhs = ParseAdditiveExpression();
+ 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 additive_expression.
//
// additive_expression:
// multiplicative_expression {"+" multiplicative_expression}
+// multiplicative_expression {"-" multiplicative_expression}
//
ASTNodeUP DILParser::ParseAdditiveExpression() {
auto lhs = ParseMultiplicativeExpression();
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
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
new file mode 100644
index 0000000000000..150a63d1ca975
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py
@@ -0,0 +1,56 @@
+"""
+Test DIL bitwise operators.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILBitwise(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_bitwise(self):
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+
+ # Check bitwise shifts
+ self.expect_var_path("(1 << 5)", value="32")
+ self.expect_var_path("(32 >> 2)", value="8")
+ self.expect_var_path("(-1 >> 10)", value="-1")
+ self.expect_var_path("(-100 >> 5)", value="-4")
+ self.expect_var_path("(-3 << 6)", value="-192")
+ self.expect_var_path("(-1 >> 1U)", value="-1")
+ self.expect_var_path("(0xFFFFFFFFu>>31)", value="1")
+ self.expect_var_path("(char)1 << 16", value="65536")
+ self.expect_var_path("(signed char)-123 >> 8", value="-1")
+ self.expect_var_path("enum_one << enum_one", value="2")
+ self.expect_var_path("2 >> enum_one", value="1")
+ self.expect_var_path("i64 << 63", type="uint64_t")
+
+ # Check errors
+ self.expect(
+ "frame var -- '1 << 1.0'",
+ error=True,
+ substrs=["invalid operands to binary expression ('int' and 'double')"],
+ )
+ self.expect(
+ "frame var -- 's << 1'",
+ error=True,
+ substrs=["invalid operands to binary expression ('S' and 'int')"],
+ )
+ self.expect(
+ "frame var -- '1 >> -1'",
+ error=True,
+ substrs=["invalid shift amount"],
+ )
+ self.expect(
+ "frame var -- 'i64 << 64'",
+ error=True,
+ substrs=["invalid shift amount"],
+ )
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
new file mode 100644
index 0000000000000..2978946b757b9
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/main.cpp
@@ -0,0 +1,12 @@
+#include <cstdint>
+enum UnscopedEnum { kZero, kOne };
+
+int main(int argc, char **argv) {
+ struct S {
+ } s;
+
+ auto enum_one = UnscopedEnum::kOne;
+ uint64_t i64 = 1;
+
+ return 0; // Set a breakpoint here
+}
>From 3a507891fd7bc5c852426370d39c06d04c3b1f81 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 14 May 2026 23:51:37 +0500
Subject: [PATCH 2/2] Update lldb/source/ValueObject/DILEval.cpp
Co-authored-by: Adrian Prantl <adrian.prantl at gmail.com>
---
lldb/source/ValueObject/DILEval.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 0d7182a4dd772..e942210d17426 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -821,7 +821,7 @@ Interpreter::EvaluateBinaryShift(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();
+ CompilerType orig_lhs_type = lhs->GetCompilerType();
auto orig_rhs_type = rhs->GetCompilerType();
auto lhs_or_err = UnaryConversion(lhs, location);
if (!lhs_or_err)
More information about the lldb-commits
mailing list