[lldb] Add bitwise operators to DIL#209768
Conversation
21fbec6 to
3e59893
Compare
|
@llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) ChangesAdd binary bitwise operators Full diff: https://github.com/llvm/llvm-project/pull/209768.diff 11 Files Affected:
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
}
|
| llvm::Expected<lldb::ValueObjectSP> conv_op = | ||
| UnaryConversion(operand, node.GetLocation()); |
There was a problem hiding this comment.
Should we dereference reference operands before calling UnaryConversion here? The existing unary + and unary - branches already handle this by checking IsReferenceType() and calling Dereference() before the unary conversion.
There was a problem hiding this comment.
Should we dereference reference operands before calling
UnaryConversionhere? The existing unary+and unary-branches already handle this by checkingIsReferenceType()and callingDereference()before the unary conversion.
You're right, thank you for noticing!
There was a problem hiding this comment.
add ~ to unary_operator
Add binary bitwise operators
|,^,&, and unary bitwise negation~.