Author: Ilia Kuklin Date: 2025-05-22T17:29:24+05:00 New Revision: b2e110036df39bb33163b5cd6c8dacf9253b6b33
URL: https://github.com/llvm/llvm-project/commit/b2e110036df39bb33163b5cd6c8dacf9253b6b33 DIFF: https://github.com/llvm/llvm-project/commit/b2e110036df39bb33163b5cd6c8dacf9253b6b33.diff LOG: Revert "[LLDB] Add array subscription and integer parsing to DIL (#138551)" This reverts commit 491619a25003c499be16708562206f9a81bff5f7. Added: Modified: lldb/docs/dil-expr-lang.ebnf lldb/include/lldb/ValueObject/DILAST.h lldb/include/lldb/ValueObject/DILEval.h lldb/include/lldb/ValueObject/DILLexer.h lldb/include/lldb/ValueObject/DILParser.h lldb/source/ValueObject/DILAST.cpp lldb/source/ValueObject/DILEval.cpp lldb/source/ValueObject/DILLexer.cpp lldb/source/ValueObject/DILParser.cpp lldb/unittests/ValueObject/DILLexerTests.cpp Removed: lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp ################################################################################ diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index d54f65df15865..c8bf4231b3e4a 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,19 +6,16 @@ expression = unary_expression ; unary_expression = unary_operator expression - | postfix_expression ; + | primary_expression ; unary_operator = "*" | "&" ; -postfix_expression = primary_expression - | postfix_expression "[" integer_literal "]"; - primary_expression = id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -27,8 +24,6 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; -integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ; - register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index c53fafefe2a0a..fe3827ef0516a 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,10 +18,8 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { - eArraySubscriptNode, eErrorNode, eIdentifierNode, - eScalarLiteralNode, eUnaryOpNode, }; @@ -98,8 +96,8 @@ class UnaryOpNode : public ASTNode { llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; - UnaryOpKind GetKind() const { return m_kind; } - ASTNode *GetOperand() const { return m_operand.get(); } + UnaryOpKind kind() const { return m_kind; } + ASTNode *operand() const { return m_operand.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eUnaryOpNode; @@ -110,26 +108,6 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; -class ArraySubscriptNode : public ASTNode { -public: - ArraySubscriptNode(uint32_t location, ASTNodeUP base, int64_t index) - : ASTNode(location, NodeKind::eArraySubscriptNode), - m_base(std::move(base)), m_index(index) {} - - llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; - - ASTNode *GetBase() const { return m_base.get(); } - int64_t GetIndex() const { return m_index; } - - static bool classof(const ASTNode *node) { - return node->GetKind() == NodeKind::eArraySubscriptNode; - } - -private: - ASTNodeUP m_base; - int64_t m_index; -}; - /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -141,8 +119,6 @@ class Visitor { Visit(const IdentifierNode *node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) = 0; - virtual llvm::Expected<lldb::ValueObjectSP> - Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 03f869297c18f..b1dd3fdb49739 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -50,8 +50,6 @@ class Interpreter : Visitor { llvm::Expected<lldb::ValueObjectSP> Visit(const IdentifierNode *node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override; - llvm::Expected<lldb::ValueObjectSP> - Visit(const ArraySubscriptNode *node) override; // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 0176db73835e9..3508b8b7a85c6 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,10 +29,7 @@ class Token { eof, identifier, l_paren, - l_square, - numeric_constant, r_paren, - r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 2df9678173870..f5c00b1040ef4 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,14 +84,12 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); - ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); - std::optional<int64_t> ParseIntegerConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index 330b5a3f3c586..ea847587501ee 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -23,9 +23,4 @@ llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } -llvm::Expected<lldb::ValueObjectSP> -ArraySubscriptNode::Accept(Visitor *v) const { - return v->Visit(this); -} - } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index da3f9969fce2b..15a66d4866305 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -237,13 +237,13 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const UnaryOpNode *node) { Status error; - auto rhs_or_err = Evaluate(node->GetOperand()); + auto rhs_or_err = Evaluate(node->operand()); if (!rhs_or_err) return rhs_or_err; lldb::ValueObjectSP rhs = *rhs_or_err; - switch (node->GetKind()) { + switch (node->kind()) { case UnaryOpKind::Deref: { lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); if (dynamic_rhs) @@ -272,51 +272,4 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } -llvm::Expected<lldb::ValueObjectSP> -Interpreter::Visit(const ArraySubscriptNode *node) { - auto lhs_or_err = Evaluate(node->GetBase()); - if (!lhs_or_err) - return lhs_or_err; - lldb::ValueObjectSP base = *lhs_or_err; - - // Check to see if 'base' has a synthetic value; if so, try using that. - uint64_t child_idx = node->GetIndex(); - if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) { - llvm::Expected<uint32_t> num_children = - synthetic->GetNumChildren(child_idx + 1); - if (!num_children) - return llvm::make_error<DILDiagnosticError>( - m_expr, toString(num_children.takeError()), node->GetLocation()); - if (child_idx >= *num_children) { - std::string message = llvm::formatv( - "array index {0} is not valid for \"({1}) {2}\"", child_idx, - base->GetTypeName().AsCString("<invalid type>"), - base->GetName().AsCString()); - return llvm::make_error<DILDiagnosticError>(m_expr, message, - node->GetLocation()); - } - if (lldb::ValueObjectSP child_valobj_sp = - synthetic->GetChildAtIndex(child_idx)) - return child_valobj_sp; - } - - auto base_type = base->GetCompilerType().GetNonReferenceType(); - if (!base_type.IsPointerType() && !base_type.IsArrayType()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscripted value is not an array or pointer", - node->GetLocation()); - if (base_type.IsPointerToVoid()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscript of pointer to incomplete type 'void'", - node->GetLocation()); - - if (base_type.IsArrayType()) { - if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx)) - return child_valobj_sp; - } - - int64_t signed_child_idx = node->GetIndex(); - return base->GetSyntheticArrayMember(signed_child_idx, true); -} - } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 449feb7dee55b..b9c2e7971e3b4 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,7 +13,6 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" -#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -30,14 +29,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; - case Kind::l_square: - return "l_square"; - case Kind::numeric_constant: - return "numeric_constant"; case Kind::r_paren: return "r_paren"; - case Kind::r_square: - return "r_square"; case Token::star: return "star"; } @@ -64,18 +57,6 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr, return candidate; } -static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } - -static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr, - llvm::StringRef &remainder) { - if (IsDigit(remainder[0])) { - llvm::StringRef number = remainder.take_while(IsNumberBodyChar); - remainder = remainder.drop_front(number.size()); - return number; - } - return std::nullopt; -} - llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr) { std::vector<Token> tokens; llvm::StringRef remainder = expr; @@ -100,17 +81,13 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); - std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder); - if (maybe_number) - return Token(Token::numeric_constant, maybe_number->str(), position); std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair<Token::Kind, const char *> operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, - {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::r_paren, ")"}, {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -118,8 +95,7 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::make_error<DILDiagnosticError>(expr, "unrecognized token", - position); + return llvm::createStringError("Unable to lex input string"); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index f310538617589..2c78eae8cf6bf 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,42 +111,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePostfixExpression(); -} - -// Parse a postfix_expression. -// -// postfix_expression: -// primary_expression -// postfix_expression "[" integer_literal "]" -// -ASTNodeUP DILParser::ParsePostfixExpression() { - ASTNodeUP lhs = ParsePrimaryExpression(); - while (CurToken().Is(Token::l_square)) { - uint32_t loc = CurToken().GetLocation(); - Token token = CurToken(); - switch (token.GetKind()) { - case Token::l_square: { - m_dil_lexer.Advance(); - std::optional<int64_t> rhs = ParseIntegerConstant(); - if (!rhs) { - BailOut( - llvm::formatv("failed to parse integer constant: {0}", CurToken()), - CurToken().GetLocation(), CurToken().GetSpelling().length()); - return std::make_unique<ErrorNode>(); - } - Expect(Token::r_square); - m_dil_lexer.Advance(); - lhs = std::make_unique<ArraySubscriptNode>(loc, std::move(lhs), - std::move(*rhs)); - break; - } - default: - llvm_unreachable("invalid token"); - } - } - - return lhs; + return ParsePrimaryExpression(); } // Parse a primary_expression. @@ -315,23 +280,6 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } -// Parse a integer_literal. -// -// integer_literal: -// ? Integer constant ? -// -std::optional<int64_t> DILParser::ParseIntegerConstant() { - auto spelling = CurToken().GetSpelling(); - llvm::StringRef spelling_ref = spelling; - int64_t raw_value; - if (!spelling_ref.getAsInteger<int64_t>(0, raw_value)) { - m_dil_lexer.Advance(); - return raw_value; - } - - return std::nullopt; -} - void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile deleted file mode 100644 index 99998b20bcb05..0000000000000 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -CXX_SOURCES := main.cpp - -include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py deleted file mode 100644 index 9cf23c5aa0fd7..0000000000000 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Test DIL array subscript. -""" - -import lldb -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * -from lldbsuite.test import lldbutil - - -class TestFrameVarDILGlobalVariableLookup(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): - value_dil = super().expect_var_path(expr, value=value, type=type) - if compare_to_framevar: - self.runCmd("settings set target.experimental.use-DIL false") - value_frv = super().expect_var_path(expr, value=value, type=type) - self.runCmd("settings set target.experimental.use-DIL true") - self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) - - def test_dereference(self): - self.build() - lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") - ) - - self.runCmd("settings set target.experimental.use-DIL true") - - # Test int[] and int* - self.expect_var_path("int_arr[0]", True, value="1") - self.expect_var_path("int_ptr[1]", True, value="2") - self.expect("frame var 'int_arr[enum_one]'", error=True) - - # Test when base and index are references. - self.expect_var_path("int_arr[0]", True, value="1") - self.expect("frame var 'int_arr[idx_1_ref]'", error=True) - self.expect("frame var 'int_arr[enum_ref]'", error=True) - self.expect_var_path("int_arr_ref[0]", value="1") - self.expect("frame var 'int_arr_ref[idx_1_ref]'", error=True) - self.expect("frame var 'int_arr_ref[enum_ref]'", error=True) - - # Test when base and index are typedefs. - self.expect_var_path("td_int_arr[0]", True, value="1") - self.expect("frame var 'td_int_arr[td_int_idx_1]'", error=True) - self.expect("frame var 'td_int_arr[td_td_int_idx_2]'", error=True) - self.expect_var_path("td_int_ptr[0]", True, value="1") - self.expect("frame var 'td_int_ptr[td_int_idx_1]'", error=True) - self.expect("frame var 'td_int_ptr[td_td_int_idx_2]'", error=True) - - # Both typedefs and refs - self.expect("frame var 'td_int_arr_ref[td_int_idx_1_ref]'", error=True) - - # Test for index out of bounds. - self.expect_var_path("int_arr[42]", True, type="int") - self.expect_var_path("int_arr[100]", True, type="int") - - # Test address-of of the subscripted value. - self.expect_var_path("*(&int_arr[1])", value="2") - - # Test for negative index. - self.expect( - "frame var 'int_arr[-1]'", - error=True, - substrs=["unrecognized token"], - ) - - # Test synthetic value subscription - self.expect_var_path("vector[1]", value="2") - self.expect( - "frame var 'vector[100]'", - error=True, - substrs=["array index 100 is not valid"], - ) - - # Test for floating point index - self.expect( - "frame var 'int_arr[1.0]'", - error=True, - substrs=["unrecognized token"], - ) - - # Base should be a "pointer to T" and index should be of an integral type. - self.expect( - "frame var 'idx_1[0]'", - error=True, - substrs=["subscripted value is not an array or pointer"], - ) - self.expect( - "frame var 'idx_1_ref[0]'", - error=True, - substrs=["subscripted value is not an array or pointer"], - ) - self.expect( - "frame var 'int_arr[int_ptr]'", - error=True, - substrs=["failed to parse integer constant"], - ) - self.expect( - "frame var '1[2]'", - error=True, - substrs=["Unexpected token"], - ) - - # Base should not be a pointer to void - self.expect( - "frame var 'p_void[0]'", - error=True, - substrs=["subscript of pointer to incomplete type 'void'"], - ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp deleted file mode 100644 index 485666ae46c20..0000000000000 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include <vector> - -int main(int argc, char **argv) { - int int_arr[] = {1, 2, 3}; - int *int_ptr = int_arr; - int(&int_arr_ref)[3] = int_arr; - void *p_void = (void *)int_arr; - - int idx_1 = 1; - const int &idx_1_ref = idx_1; - - typedef int td_int_t; - typedef td_int_t td_td_int_t; - typedef int *td_int_ptr_t; - typedef int &td_int_ref_t; - - td_int_t td_int_idx_1 = 1; - td_td_int_t td_td_int_idx_2 = 2; - - td_int_t td_int_arr[3] = {1, 2, 3}; - td_int_ptr_t td_int_ptr = td_int_arr; - - td_int_ref_t td_int_idx_1_ref = td_int_idx_1; - td_int_t(&td_int_arr_ref)[3] = td_int_arr; - - enum Enum { kZero, kOne } enum_one = kOne; - Enum &enum_ref = enum_one; - - std::vector<int> vector = {1, 2, 3}; - - return 0; // Set a breakpoint here -} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index f65034c1dbea3..9afa957901ae7 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -121,11 +121,11 @@ TEST(DILLexerTests, IdentifiersTest) { "a_b", "this", "self", "a", "MyName", "namespace"}; // The lexer can lex these strings, but they should not be identifiers. - std::vector<std::string> invalid_identifiers = {"", "::", "(", ")", "0abc"}; + std::vector<std::string> invalid_identifiers = {"", "::", "(", ")"}; // The lexer is expected to fail attempting to lex these strings (it cannot // create valid tokens out of them). - std::vector<std::string> invalid_tok_strings = {"#include", "a@a"}; + std::vector<std::string> invalid_tok_strings = {"234", "2a", "2", "1MyName"}; // Verify that all of the valid identifiers come out as identifier tokens. for (auto &str : valid_identifiers) { @@ -150,32 +150,7 @@ TEST(DILLexerTests, IdentifiersTest) { DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); - EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, - Token::r_paren, Token::numeric_constant})); - } -} - -TEST(DILLexerTests, NumbersTest) { - // These strings should lex into number tokens. - std::vector<std::string> valid_numbers = {"123", "0x123", "0123", "0b101"}; - - // The lexer can lex these strings, but they should not be numbers. - std::vector<std::string> invalid_numbers = {"", "x123", "b123"}; - - for (auto &str : valid_numbers) { - SCOPED_TRACE(str); - EXPECT_THAT_EXPECTED(ExtractTokenData(str), - llvm::HasValue(testing::ElementsAre( - testing::Pair(Token::numeric_constant, str)))); - } - // Verify that none of the invalid numbers come out as numeric tokens. - for (auto &str : invalid_numbers) { - SCOPED_TRACE(str); - llvm::Expected<DILLexer> maybe_lexer = DILLexer::Create(str); - EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); - DILLexer lexer(*maybe_lexer); - Token token = lexer.GetCurrentToken(); - EXPECT_TRUE(token.IsNot(Token::numeric_constant)); - EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); + EXPECT_TRUE(token.IsOneOf( + {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren})); } } _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits