[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 Apr 16 11:26:51 PDT 2026
https://github.com/kuilpd created https://github.com/llvm/llvm-project/pull/192506
This patch add bitwise shift operators and fixes the Scalar's sign inside of the literals with signed types. The only way to test this fix, for now, is to use these literals in an arithmetic right shift that does sign extension, so this is included in this patch.
>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] [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
+}
More information about the lldb-commits
mailing list