[llvm] [llvm-lsp] LSP server for LLVM IR (PR #161969)

via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 8 00:59:02 PDT 2025


Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,Bertik23
 <39457484+Bertik23 at users.noreply.github.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>,
Albert =?utf-8?q?Havliček?= <ahavlicek at azul.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/161969 at github.com>


https://github.com/Bertik23 updated https://github.com/llvm/llvm-project/pull/161969

>From ecf591c76631957bea7f8d62191d4b99fe8fc4c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Tue, 29 Jul 2025 14:41:18 +0000
Subject: [PATCH 01/20] Update CurLineNum anc CurColNum in sync with movement
 in text

---
 llvm/include/llvm/AsmParser/LLLexer.h |  2 +
 llvm/lib/AsmParser/LLLexer.cpp        | 97 +++++++++++++++++----------
 2 files changed, 63 insertions(+), 36 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index 501a7aefccd7f..5f6e32a4bf5e1 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -94,6 +94,8 @@ namespace llvm {
     lltok::Kind LexToken();
 
     int getNextChar();
+    const char *skipNChars(unsigned N);
+    void advancePositionTo(const char *Ptr);
     void SkipLineComment();
     bool SkipCComment();
     lltok::Kind ReadString(lltok::Kind kind);
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 520c6a00a9c07..7cefd4f6b4935 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -190,6 +190,23 @@ int LLLexer::getNextChar() {
   }
 }
 
+const char *LLLexer::skipNChars(unsigned N) {
+  while (N--)
+    getNextChar();
+  return CurPtr;
+}
+
+void LLLexer::advancePositionTo(const char *Ptr) {
+  while (CurPtr != Ptr) {
+    // FIXME: Assumes that if moving back, we stay in that line
+    if (CurPtr > Ptr) {
+      --CurPtr;
+      --CurColNum;
+    } else
+      getNextChar();
+  }
+}
+
 lltok::Kind LLLexer::LexToken() {
   while (true) {
     TokStart = CurPtr;
@@ -216,12 +233,12 @@ lltok::Kind LLLexer::LexToken() {
     case '"': return LexQuote();
     case '.':
       if (const char *Ptr = isLabelTail(CurPtr)) {
-        CurPtr = Ptr;
+        advancePositionTo(Ptr);
         StrVal.assign(TokStart, CurPtr-1);
         return lltok::LabelStr;
       }
       if (CurPtr[0] == '.' && CurPtr[1] == '.') {
-        CurPtr += 2;
+        skipNChars(2);
         return lltok::dotdotdot;
       }
       return lltok::Error;
@@ -299,14 +316,14 @@ lltok::Kind LLLexer::LexAt() {
 
 lltok::Kind LLLexer::LexDollar() {
   if (const char *Ptr = isLabelTail(TokStart)) {
-    CurPtr = Ptr;
+    advancePositionTo(Ptr);
     StrVal.assign(TokStart, CurPtr - 1);
     return lltok::LabelStr;
   }
 
   // Handle DollarStringConstant: $\"[^\"]*\"
   if (CurPtr[0] == '"') {
-    ++CurPtr;
+    getNextChar();
 
     while (true) {
       int CurChar = getNextChar();
@@ -358,11 +375,11 @@ bool LLLexer::ReadVarName() {
   if (isalpha(static_cast<unsigned char>(CurPtr[0])) ||
       CurPtr[0] == '-' || CurPtr[0] == '$' ||
       CurPtr[0] == '.' || CurPtr[0] == '_') {
-    ++CurPtr;
+    getNextChar();
     while (isalnum(static_cast<unsigned char>(CurPtr[0])) ||
            CurPtr[0] == '-' || CurPtr[0] == '$' ||
            CurPtr[0] == '.' || CurPtr[0] == '_')
-      ++CurPtr;
+      getNextChar();
 
     StrVal.assign(NameStart, CurPtr);
     return true;
@@ -376,7 +393,8 @@ lltok::Kind LLLexer::LexUIntID(lltok::Kind Token) {
   if (!isdigit(static_cast<unsigned char>(CurPtr[0])))
     return lltok::Error;
 
-  for (++CurPtr; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (getNextChar(); isdigit(static_cast<unsigned char>(CurPtr[0]));
+       getNextChar())
     /*empty*/;
 
   uint64_t Val = atoull(TokStart + 1, CurPtr);
@@ -389,7 +407,7 @@ lltok::Kind LLLexer::LexUIntID(lltok::Kind Token) {
 lltok::Kind LLLexer::LexVar(lltok::Kind Var, lltok::Kind VarID) {
   // Handle StringConstant: \"[^\"]*\"
   if (CurPtr[0] == '"') {
-    ++CurPtr;
+    getNextChar();
 
     while (true) {
       int CurChar = getNextChar();
@@ -435,7 +453,7 @@ lltok::Kind LLLexer::LexQuote() {
     return kind;
 
   if (CurPtr[0] == ':') {
-    ++CurPtr;
+    getNextChar();
     if (StringRef(StrVal).contains(0)) {
       LexError("NUL character is not allowed in names");
       kind = lltok::Error;
@@ -455,11 +473,11 @@ lltok::Kind LLLexer::LexExclaim() {
   if (isalpha(static_cast<unsigned char>(CurPtr[0])) ||
       CurPtr[0] == '-' || CurPtr[0] == '$' ||
       CurPtr[0] == '.' || CurPtr[0] == '_' || CurPtr[0] == '\\') {
-    ++CurPtr;
+    getNextChar();
     while (isalnum(static_cast<unsigned char>(CurPtr[0])) ||
            CurPtr[0] == '-' || CurPtr[0] == '$' ||
            CurPtr[0] == '.' || CurPtr[0] == '_' || CurPtr[0] == '\\')
-      ++CurPtr;
+      getNextChar();
 
     StrVal.assign(TokStart+1, CurPtr);   // Skip !
     UnEscapeLexed(StrVal);
@@ -495,7 +513,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   const char *IntEnd = CurPtr[-1] == 'i' ? nullptr : StartChar;
   const char *KeywordEnd = nullptr;
 
-  for (; isLabelChar(*CurPtr); ++CurPtr) {
+  for (; isLabelChar(*CurPtr); getNextChar()) {
     // If we decide this is an integer, remember the end of the sequence.
     if (!IntEnd && !isdigit(static_cast<unsigned char>(*CurPtr)))
       IntEnd = CurPtr;
@@ -507,7 +525,8 @@ lltok::Kind LLLexer::LexIdentifier() {
   // If we stopped due to a colon, unless we were directed to ignore it,
   // this really is a label.
   if (!IgnoreColonInIdentifiers && *CurPtr == ':') {
-    StrVal.assign(StartChar-1, CurPtr++);
+    StrVal.assign(StartChar - 1, CurPtr);
+    getNextChar();
     return lltok::LabelStr;
   }
 
@@ -515,7 +534,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   // return it.
   if (!IntEnd) IntEnd = CurPtr;
   if (IntEnd != StartChar) {
-    CurPtr = IntEnd;
+    advancePositionTo(IntEnd);
     uint64_t NumBits = atoull(StartChar, CurPtr);
     if (NumBits < IntegerType::MIN_INT_BITS ||
         NumBits > IntegerType::MAX_INT_BITS) {
@@ -528,7 +547,7 @@ lltok::Kind LLLexer::LexIdentifier() {
 
   // Otherwise, this was a letter sequence.  See which keyword this is.
   if (!KeywordEnd) KeywordEnd = CurPtr;
-  CurPtr = KeywordEnd;
+  advancePositionTo(KeywordEnd);
   --StartChar;
   StringRef Keyword(StartChar, CurPtr - StartChar);
 
@@ -1042,7 +1061,7 @@ lltok::Kind LLLexer::LexIdentifier() {
     StringRef HexStr(TokStart + 3, len);
     if (!all_of(HexStr, isxdigit)) {
       // Bad token, return it as an error.
-      CurPtr = TokStart+3;
+      advancePositionTo(TokStart + 3);
       return lltok::Error;
     }
     APInt Tmp(bits, HexStr, 16);
@@ -1055,12 +1074,12 @@ lltok::Kind LLLexer::LexIdentifier() {
 
   // If this is "cc1234", return this as just "cc".
   if (TokStart[0] == 'c' && TokStart[1] == 'c') {
-    CurPtr = TokStart+2;
+    advancePositionTo(TokStart + 2);
     return lltok::kw_cc;
   }
 
   // Finally, if this isn't known, return an error.
-  CurPtr = TokStart+1;
+  advancePositionTo(TokStart + 1);
   return lltok::Error;
 }
 
@@ -1073,24 +1092,25 @@ lltok::Kind LLLexer::LexIdentifier() {
 ///    HexHalfConstant   0xH[0-9A-Fa-f]+
 ///    HexBFloatConstant 0xR[0-9A-Fa-f]+
 lltok::Kind LLLexer::Lex0x() {
-  CurPtr = TokStart + 2;
+  advancePositionTo(TokStart + 2);
 
   char Kind;
   if ((CurPtr[0] >= 'K' && CurPtr[0] <= 'M') || CurPtr[0] == 'H' ||
       CurPtr[0] == 'R') {
-    Kind = *CurPtr++;
+    Kind = *CurPtr;
+    getNextChar();
   } else {
     Kind = 'J';
   }
 
   if (!isxdigit(static_cast<unsigned char>(CurPtr[0]))) {
     // Bad token, return it as an error.
-    CurPtr = TokStart+1;
+    advancePositionTo(TokStart + 1);
     return lltok::Error;
   }
 
   while (isxdigit(static_cast<unsigned char>(CurPtr[0])))
-    ++CurPtr;
+    getNextChar();
 
   if (Kind == 'J') {
     // HexFPConstant - Floating point constant represented in IEEE format as a
@@ -1147,7 +1167,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
     // Okay, this is not a number after the -, it's probably a label.
     if (const char *End = isLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
-      CurPtr = End;
+      advancePositionTo(End);
       return lltok::LabelStr;
     }
 
@@ -1157,13 +1177,13 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
   // At this point, it is either a label, int or fp constant.
 
   // Skip digits, we have at least one.
-  for (; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (; isdigit(static_cast<unsigned char>(CurPtr[0])); getNextChar())
     /*empty*/;
 
   // Check if this is a fully-numeric label:
   if (isdigit(TokStart[0]) && CurPtr[0] == ':') {
     uint64_t Val = atoull(TokStart, CurPtr);
-    ++CurPtr; // Skip the colon.
+    getNextChar(); // Skip the colon.
     if ((unsigned)Val != Val)
       LexError("invalid value number (too large)");
     UIntVal = unsigned(Val);
@@ -1174,7 +1194,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
   if (isLabelChar(CurPtr[0]) || CurPtr[0] == ':') {
     if (const char *End = isLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
-      CurPtr = End;
+      advancePositionTo(End);
       return lltok::LabelStr;
     }
   }
@@ -1188,17 +1208,19 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
     return lltok::APSInt;
   }
 
-  ++CurPtr;
+  getNextChar();
 
   // Skip over [0-9]*([eE][-+]?[0-9]+)?
-  while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+  while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+    getNextChar();
 
   if (CurPtr[0] == 'e' || CurPtr[0] == 'E') {
     if (isdigit(static_cast<unsigned char>(CurPtr[1])) ||
         ((CurPtr[1] == '-' || CurPtr[1] == '+') &&
           isdigit(static_cast<unsigned char>(CurPtr[2])))) {
-      CurPtr += 2;
-      while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+      skipNChars(2);
+      while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+        getNextChar();
     }
   }
 
@@ -1216,26 +1238,29 @@ lltok::Kind LLLexer::LexPositive() {
     return lltok::Error;
 
   // Skip digits.
-  for (++CurPtr; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (getNextChar(); isdigit(static_cast<unsigned char>(CurPtr[0]));
+       getNextChar())
     /*empty*/;
 
   // At this point, we need a '.'.
   if (CurPtr[0] != '.') {
-    CurPtr = TokStart+1;
+    advancePositionTo(TokStart + 1);
     return lltok::Error;
   }
 
-  ++CurPtr;
+  getNextChar();
 
   // Skip over [0-9]*([eE][-+]?[0-9]+)?
-  while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+  while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+    getNextChar();
 
   if (CurPtr[0] == 'e' || CurPtr[0] == 'E') {
     if (isdigit(static_cast<unsigned char>(CurPtr[1])) ||
         ((CurPtr[1] == '-' || CurPtr[1] == '+') &&
         isdigit(static_cast<unsigned char>(CurPtr[2])))) {
-      CurPtr += 2;
-      while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+      skipNChars(2);
+      while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+        getNextChar();
     }
   }
 

>From 06926e9fa28e10abbec7803c0cd8c196ea09daf5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Mon, 4 Aug 2025 09:36:51 +0000
Subject: [PATCH 02/20] Remove remains from cherry pick from LSP branch

---
 llvm/lib/AsmParser/LLLexer.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 7cefd4f6b4935..db4079975ad40 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -198,10 +198,8 @@ const char *LLLexer::skipNChars(unsigned N) {
 
 void LLLexer::advancePositionTo(const char *Ptr) {
   while (CurPtr != Ptr) {
-    // FIXME: Assumes that if moving back, we stay in that line
     if (CurPtr > Ptr) {
       --CurPtr;
-      --CurColNum;
     } else
       getNextChar();
   }

>From 1fdf13c326ba2f7889b27de5789c0339190fa0f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Mon, 4 Aug 2025 09:49:32 +0000
Subject: [PATCH 03/20] Make isLabelTail more safe and rename it to better show
 what it does

---
 llvm/include/llvm/AsmParser/LLLexer.h |  3 ++
 llvm/lib/AsmParser/LLLexer.cpp        | 43 +++++++++++----------------
 2 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index 5f6e32a4bf5e1..d0d6f72c197da 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -93,6 +93,9 @@ namespace llvm {
   private:
     lltok::Kind LexToken();
 
+    // Return closes pointer after `Ptr` that is an end of a label.
+    // Returns nullptr if `Ptr` doesn't point into a label.
+    const char *getLabelTail(const char *Ptr);
     int getNextChar();
     const char *skipNChars(unsigned N);
     void advancePositionTo(const char *Ptr);
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index db4079975ad40..bbd6b690a97c0 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -155,15 +155,6 @@ static bool isLabelChar(char C) {
          C == '.' || C == '_';
 }
 
-/// isLabelTail - Return true if this pointer points to a valid end of a label.
-static const char *isLabelTail(const char *CurPtr) {
-  while (true) {
-    if (CurPtr[0] == ':') return CurPtr+1;
-    if (!isLabelChar(CurPtr[0])) return nullptr;
-    ++CurPtr;
-  }
-}
-
 //===----------------------------------------------------------------------===//
 // Lexer definition.
 //===----------------------------------------------------------------------===//
@@ -174,20 +165,22 @@ LLLexer::LLLexer(StringRef StartBuf, SourceMgr &SM, SMDiagnostic &Err,
   CurPtr = CurBuf.begin();
 }
 
+/// getLabelTail - Return true if this pointer points to a valid end of a label.
+const char *LLLexer::getLabelTail(const char *Ptr) {
+  while (Ptr != CurBuf.end()) {
+    if (Ptr[0] == ':')
+      return Ptr + 1;
+    if (!isLabelChar(Ptr[0]))
+      return nullptr;
+    ++Ptr;
+  }
+  return nullptr;
+}
+
 int LLLexer::getNextChar() {
-  char CurChar = *CurPtr++;
-  switch (CurChar) {
-  default: return (unsigned char)CurChar;
-  case 0:
-    // A nul character in the stream is either the end of the current buffer or
-    // a random nul in the file.  Disambiguate that here.
-    if (CurPtr-1 != CurBuf.end())
-      return 0;  // Just whitespace.
-
-    // Otherwise, return end of file.
-    --CurPtr;  // Another call to lex will return EOF again.
+  if (CurPtr == CurBuf.end())
     return EOF;
-  }
+  return *CurPtr++;
 }
 
 const char *LLLexer::skipNChars(unsigned N) {
@@ -230,7 +223,7 @@ lltok::Kind LLLexer::LexToken() {
     case '%': return LexPercent();
     case '"': return LexQuote();
     case '.':
-      if (const char *Ptr = isLabelTail(CurPtr)) {
+      if (const char *Ptr = getLabelTail(CurPtr)) {
         advancePositionTo(Ptr);
         StrVal.assign(TokStart, CurPtr-1);
         return lltok::LabelStr;
@@ -313,7 +306,7 @@ lltok::Kind LLLexer::LexAt() {
 }
 
 lltok::Kind LLLexer::LexDollar() {
-  if (const char *Ptr = isLabelTail(TokStart)) {
+  if (const char *Ptr = getLabelTail(TokStart)) {
     advancePositionTo(Ptr);
     StrVal.assign(TokStart, CurPtr - 1);
     return lltok::LabelStr;
@@ -1163,7 +1156,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
   if (!isdigit(static_cast<unsigned char>(TokStart[0])) &&
       !isdigit(static_cast<unsigned char>(CurPtr[0]))) {
     // Okay, this is not a number after the -, it's probably a label.
-    if (const char *End = isLabelTail(CurPtr)) {
+    if (const char *End = getLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
       advancePositionTo(End);
       return lltok::LabelStr;
@@ -1190,7 +1183,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
 
   // Check to see if this really is a string label, e.g. "-1:".
   if (isLabelChar(CurPtr[0]) || CurPtr[0] == ':') {
-    if (const char *End = isLabelTail(CurPtr)) {
+    if (const char *End = getLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
       advancePositionTo(End);
       return lltok::LabelStr;

>From 2772cd8f679124daed8bae737451dcf54b637694 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Mon, 4 Aug 2025 09:51:38 +0000
Subject: [PATCH 04/20] Remove dangling comment

---
 llvm/lib/AsmParser/LLLexer.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index bbd6b690a97c0..578ac851e38c5 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -165,7 +165,6 @@ LLLexer::LLLexer(StringRef StartBuf, SourceMgr &SM, SMDiagnostic &Err,
   CurPtr = CurBuf.begin();
 }
 
-/// getLabelTail - Return true if this pointer points to a valid end of a label.
 const char *LLLexer::getLabelTail(const char *Ptr) {
   while (Ptr != CurBuf.end()) {
     if (Ptr[0] == ':')

>From b05d11ac2ee3f8e14020a0fa4d2e7c602bb3d77b Mon Sep 17 00:00:00 2001
From: Bertik23 <39457484+Bertik23 at users.noreply.github.com>
Date: Tue, 12 Aug 2025 12:23:23 +0200
Subject: [PATCH 05/20] Fix typo

Co-authored-by: Nikita Popov <github at npopov.com>
---
 llvm/include/llvm/AsmParser/LLLexer.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index d0d6f72c197da..beb88a8c73305 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -93,7 +93,7 @@ namespace llvm {
   private:
     lltok::Kind LexToken();
 
-    // Return closes pointer after `Ptr` that is an end of a label.
+    // Return closest pointer after `Ptr` that is an end of a label.
     // Returns nullptr if `Ptr` doesn't point into a label.
     const char *getLabelTail(const char *Ptr);
     int getNextChar();

>From 458599b36cfdc15d7d79c7c7ff0fe770c4d0685d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Thu, 28 Aug 2025 07:57:04 +0000
Subject: [PATCH 06/20] Add location tracking to IR parser

---
 .../include/llvm/AsmParser/AsmParserContext.h |  53 +++++++
 llvm/include/llvm/AsmParser/LLLexer.h         |  35 ++++-
 llvm/include/llvm/AsmParser/LLParser.h        |   9 +-
 llvm/include/llvm/AsmParser/Parser.h          |  16 ++-
 llvm/include/llvm/IR/Value.h                  |  32 +++++
 llvm/include/llvm/IRReader/IRReader.h         |  17 +--
 llvm/lib/AsmParser/AsmParserContext.cpp       |  91 ++++++++++++
 llvm/lib/AsmParser/CMakeLists.txt             |   1 +
 llvm/lib/AsmParser/LLLexer.cpp                | 131 +++++++++++++-----
 llvm/lib/AsmParser/LLParser.cpp               |  27 +++-
 llvm/lib/AsmParser/Parser.cpp                 |  31 +++--
 llvm/lib/IRReader/IRReader.cpp                |  13 +-
 llvm/unittests/AsmParser/AsmParserTest.cpp    |  60 ++++++++
 13 files changed, 440 insertions(+), 76 deletions(-)
 create mode 100644 llvm/include/llvm/AsmParser/AsmParserContext.h
 create mode 100644 llvm/lib/AsmParser/AsmParserContext.cpp

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
new file mode 100644
index 0000000000000..bc4d93ef727ef
--- /dev/null
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -0,0 +1,53 @@
+//===-- AsmParserContext.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ASMPARSER_ASMPARSER_STATE_H
+#define LLVM_ASMPARSER_ASMPARSER_STATE_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/IR/Value.h"
+#include <optional>
+
+namespace llvm {
+
+/// Registry of file location information for LLVM IR constructs
+///
+/// This class provides access to the file location information
+/// for various LLVM IR constructs. Currently, it supports Function,
+/// BasicBlock and Instruction locations.
+///
+/// When available, it can answer queries about what is at a given
+/// file location, as well as where in a file a given IR construct
+/// is.
+/// 
+/// This information is optionally emitted by the LLParser while
+/// it reads LLVM textual IR.
+class AsmParserContext {
+public:
+  std::optional<FileLocRange> getFunctionLocation(const Function *) const;
+  std::optional<FileLocRange> getBlockLocation(const BasicBlock *) const;
+  std::optional<FileLocRange> getInstructionLocation(const Instruction *) const;
+  std::optional<Function *> getFunctionAtLocation(const FileLocRange &) const;
+  std::optional<Function *> getFunctionAtLocation(const FileLoc &) const;
+  std::optional<BasicBlock *> getBlockAtLocation(const FileLocRange &) const;
+  std::optional<BasicBlock *> getBlockAtLocation(const FileLoc &) const;
+  std::optional<Instruction *>
+  getInstructionAtLocation(const FileLocRange &) const;
+  std::optional<Instruction *> getInstructionAtLocation(const FileLoc &) const;
+  bool addFunctionLocation(Function *, const FileLocRange &);
+  bool addBlockLocation(BasicBlock *, const FileLocRange &);
+  bool addInstructionLocation(Instruction *, const FileLocRange &);
+
+private:
+  DenseMap<Function *, FileLocRange> Functions;
+  DenseMap<BasicBlock *, FileLocRange> Blocks;
+  DenseMap<Instruction *, FileLocRange> Instructions;
+};
+} // namespace llvm
+
+#endif
diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index 501a7aefccd7f..3d0e28ea9f5bc 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -29,6 +29,20 @@ namespace llvm {
     const char *CurPtr;
     StringRef CurBuf;
 
+    // The line number at `CurPtr-1`, zero-indexed
+    unsigned CurLineNum = 0;
+    // The column number at `CurPtr-1`, zero-indexed
+    unsigned CurColNum = -1;
+    // The line number of the start of the current token, zero-indexed
+    unsigned CurTokLineNum = 0;
+    // The column number of the start of the current token, zero-indexed
+    unsigned CurTokColNum = 0;
+    // The line number of the end of the current token, zero-indexed
+    unsigned PrevTokEndLineNum = -1;
+    // The column number of the end (exclusive) of the current token,
+    // zero-indexed
+    unsigned PrevTokEndColNum = -1;
+
     enum class ErrorPriority {
       None,   // No error message present.
       Parser, // Errors issued by parser.
@@ -62,9 +76,7 @@ namespace llvm {
     explicit LLLexer(StringRef StartBuf, SourceMgr &SM, SMDiagnostic &,
                      LLVMContext &C);
 
-    lltok::Kind Lex() {
-      return CurKind = LexToken();
-    }
+    lltok::Kind Lex() { return CurKind = LexToken(); }
 
     typedef SMLoc LocTy;
     LocTy getLoc() const { return SMLoc::getFromPointer(TokStart); }
@@ -79,6 +91,21 @@ namespace llvm {
       IgnoreColonInIdentifiers = val;
     }
 
+    // Get the current line number, zero-indexed
+    unsigned getLineNum() { return CurLineNum; }
+    // Get the current column number, zero-indexed
+    unsigned getColNum() { return CurColNum; }
+    // Get the line number of the start of the current token, zero-indexed
+    unsigned getTokLineNum() { return CurTokLineNum; }
+    // Get the column number of the start of the current token, zero-indexed
+    unsigned getTokColNum() { return CurTokColNum; }
+    // Get the line number of the end of the previous token, zero-indexed,
+    // exclusive
+    unsigned getPrevTokEndLineNum() { return PrevTokEndLineNum; }
+    // Get the column number of the end of the previous token, zero-indexed,
+    // exclusive
+    unsigned getPrevTokEndColNum() { return PrevTokEndColNum; }
+
     // This returns true as a convenience for the parser functions that return
     // true on error.
     bool ParseError(LocTy ErrorLoc, const Twine &Msg) {
@@ -94,6 +121,8 @@ namespace llvm {
     lltok::Kind LexToken();
 
     int getNextChar();
+    const char *skipNChars(unsigned N);
+    void advancePositionTo(const char *Ptr);
     void SkipLineComment();
     bool SkipCComment();
     lltok::Kind ReadString(lltok::Kind kind);
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index c01de4a289a69..02460e5e52203 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_ASMPARSER_LLPARSER_H
 #define LLVM_ASMPARSER_LLPARSER_H
 
+#include "AsmParserContext.h"
 #include "LLLexer.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/AsmParser/NumberedValues.h"
@@ -177,6 +178,9 @@ namespace llvm {
     // Map of module ID to path.
     std::map<unsigned, StringRef> ModuleIdMap;
 
+    /// Keeps track of source locations for Values, BasicBlocks, and Functions
+    AsmParserContext *ParserContext;
+
     /// Only the llvm-as tool may set this to false to bypass
     /// UpgradeDebuginfo so it can generate broken bitcode.
     bool UpgradeDebugInfo;
@@ -189,10 +193,11 @@ namespace llvm {
   public:
     LLParser(StringRef F, SourceMgr &SM, SMDiagnostic &Err, Module *M,
              ModuleSummaryIndex *Index, LLVMContext &Context,
-             SlotMapping *Slots = nullptr)
+             SlotMapping *Slots = nullptr,
+             AsmParserContext *ParserContext = nullptr)
         : Context(Context), OPLex(F, SM, Err, Context),
           Lex(F, SM, Err, Context), M(M), Index(Index), Slots(Slots),
-          BlockAddressPFS(nullptr) {}
+          BlockAddressPFS(nullptr), ParserContext(ParserContext) {}
     bool Run(
         bool UpgradeDebugInfo,
         DataLayoutCallbackTy DataLayoutCallback = [](StringRef, StringRef) {
diff --git a/llvm/include/llvm/AsmParser/Parser.h b/llvm/include/llvm/AsmParser/Parser.h
index c900b79665404..22b0881d92b53 100644
--- a/llvm/include/llvm/AsmParser/Parser.h
+++ b/llvm/include/llvm/AsmParser/Parser.h
@@ -15,6 +15,7 @@
 
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/AsmParser/AsmParserContext.h"
 #include "llvm/Support/Compiler.h"
 #include <memory>
 #include <optional>
@@ -62,7 +63,8 @@ parseAssemblyFile(StringRef Filename, SMDiagnostic &Err, LLVMContext &Context,
 ///              parsing.
 LLVM_ABI std::unique_ptr<Module>
 parseAssemblyString(StringRef AsmString, SMDiagnostic &Err,
-                    LLVMContext &Context, SlotMapping *Slots = nullptr);
+                    LLVMContext &Context, SlotMapping *Slots = nullptr,
+                    AsmParserContext *ParserContext = nullptr);
 
 /// Holds the Module and ModuleSummaryIndex returned by the interfaces
 /// that parse both.
@@ -128,9 +130,9 @@ parseSummaryIndexAssemblyString(StringRef AsmString, SMDiagnostic &Err);
 LLVM_ABI std::unique_ptr<Module> parseAssembly(
     MemoryBufferRef F, SMDiagnostic &Err, LLVMContext &Context,
     SlotMapping *Slots = nullptr,
-    DataLayoutCallbackTy DataLayoutCallback = [](StringRef, StringRef) {
-      return std::nullopt;
-    });
+    DataLayoutCallbackTy DataLayoutCallback =
+        [](StringRef, StringRef) { return std::nullopt; },
+    AsmParserContext *ParserContext = nullptr);
 
 /// Parse LLVM Assembly including the summary index from a MemoryBuffer.
 ///
@@ -169,9 +171,9 @@ parseSummaryIndexAssembly(MemoryBufferRef F, SMDiagnostic &Err);
 LLVM_ABI bool parseAssemblyInto(
     MemoryBufferRef F, Module *M, ModuleSummaryIndex *Index, SMDiagnostic &Err,
     SlotMapping *Slots = nullptr,
-    DataLayoutCallbackTy DataLayoutCallback = [](StringRef, StringRef) {
-      return std::nullopt;
-    });
+    DataLayoutCallbackTy DataLayoutCallback =
+        [](StringRef, StringRef) { return std::nullopt; },
+    AsmParserContext *ParserContext = nullptr);
 
 /// Parse a type and a constant value in the given string.
 ///
diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h
index 04d0391c04098..9e27797d151e2 100644
--- a/llvm/include/llvm/IR/Value.h
+++ b/llvm/include/llvm/IR/Value.h
@@ -55,6 +55,38 @@ class User;
 
 using ValueName = StringMapEntry<Value *>;
 
+struct FileLoc {
+  unsigned Line;
+  unsigned Col;
+
+  bool operator<=(const FileLoc &RHS) const {
+    return Line < RHS.Line || (Line == RHS.Line && Col <= RHS.Col);
+  }
+
+  bool operator<(const FileLoc &RHS) const {
+    return Line < RHS.Line || (Line == RHS.Line && Col < RHS.Col);
+  }
+
+  FileLoc(unsigned L, unsigned C) : Line(L), Col(C) {}
+};
+
+struct FileLocRange {
+  FileLoc Start;
+  FileLoc End;
+
+  FileLocRange() : Start(0, 0), End(0, 0) {}
+
+  FileLocRange(FileLoc S, FileLoc E) : Start(S), End(E) {
+    assert(Start <= End);
+  }
+
+  bool contains(FileLoc L) const { return Start <= L && L <= End; }
+
+  bool contains(FileLocRange LR) const {
+    return contains(LR.Start) && contains(LR.End);
+  }
+};
+
 //===----------------------------------------------------------------------===//
 //                                 Value Class
 //===----------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IRReader/IRReader.h b/llvm/include/llvm/IRReader/IRReader.h
index 790140f19934e..00cf12d342ae0 100644
--- a/llvm/include/llvm/IRReader/IRReader.h
+++ b/llvm/include/llvm/IRReader/IRReader.h
@@ -15,6 +15,7 @@
 #define LLVM_IRREADER_IRREADER_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/AsmParser/AsmParserContext.h"
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Support/Compiler.h"
 #include <memory>
@@ -50,19 +51,19 @@ getLazyIRFileModule(StringRef Filename, SMDiagnostic &Err, LLVMContext &Context,
 /// for it.  Otherwise, attempt to parse it as LLVM Assembly and return
 /// a Module for it.
 /// \param DataLayoutCallback Override datalayout in the llvm assembly.
-LLVM_ABI std::unique_ptr<Module> parseIR(MemoryBufferRef Buffer,
-                                         SMDiagnostic &Err,
-                                         LLVMContext &Context,
-                                         ParserCallbacks Callbacks = {});
+LLVM_ABI std::unique_ptr<Module>
+parseIR(MemoryBufferRef Buffer, SMDiagnostic &Err, LLVMContext &Context,
+        ParserCallbacks Callbacks = {},
+        AsmParserContext *ParserContext = nullptr);
 
 /// If the given file holds a bitcode image, return a Module for it.
 /// Otherwise, attempt to parse it as LLVM Assembly and return a Module
 /// for it.
 /// \param DataLayoutCallback Override datalayout in the llvm assembly.
-LLVM_ABI std::unique_ptr<Module> parseIRFile(StringRef Filename,
-                                             SMDiagnostic &Err,
-                                             LLVMContext &Context,
-                                             ParserCallbacks Callbacks = {});
+LLVM_ABI std::unique_ptr<Module>
+parseIRFile(StringRef Filename, SMDiagnostic &Err, LLVMContext &Context,
+            ParserCallbacks Callbacks = {},
+            AsmParserContext *ParserContext = nullptr);
 }
 
 #endif
diff --git a/llvm/lib/AsmParser/AsmParserContext.cpp b/llvm/lib/AsmParser/AsmParserContext.cpp
new file mode 100644
index 0000000000000..f5e3d83f5d346
--- /dev/null
+++ b/llvm/lib/AsmParser/AsmParserContext.cpp
@@ -0,0 +1,91 @@
+//===-- AsmParserContext.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/AsmParser/AsmParserContext.h"
+
+namespace llvm {
+
+std::optional<FileLocRange>
+AsmParserContext::getFunctionLocation(const Function *F) const {
+  if (!Functions.contains(F))
+    return std::nullopt;
+  return Functions.at(F);
+}
+
+std::optional<FileLocRange>
+AsmParserContext::getBlockLocation(const BasicBlock *BB) const {
+  if (!Blocks.contains(BB))
+    return std::nullopt;
+  return Blocks.at(BB);
+}
+
+std::optional<FileLocRange>
+AsmParserContext::getInstructionLocation(const Instruction *I) const {
+  if (!Instructions.contains(I))
+    return std::nullopt;
+  return Instructions.at(I);
+}
+
+std::optional<Function *>
+AsmParserContext::getFunctionAtLocation(const FileLocRange &Query) const {
+  for (auto &[F, Loc] : Functions) {
+    if (Loc.contains(Query))
+      return F;
+  }
+  return std::nullopt;
+}
+
+std::optional<Function *>
+AsmParserContext::getFunctionAtLocation(const FileLoc &Query) const {
+  return getFunctionAtLocation(FileLocRange(Query, Query));
+}
+
+std::optional<BasicBlock *>
+AsmParserContext::getBlockAtLocation(const FileLocRange &Query) const {
+  for (auto &[BB, Loc] : Blocks) {
+    if (Loc.contains(Query))
+      return BB;
+  }
+  return std::nullopt;
+}
+
+std::optional<BasicBlock *>
+AsmParserContext::getBlockAtLocation(const FileLoc &Query) const {
+  return getBlockAtLocation(FileLocRange(Query, Query));
+}
+
+std::optional<Instruction *>
+AsmParserContext::getInstructionAtLocation(const FileLocRange &Query) const {
+  for (auto &[I, Loc] : Instructions) {
+    if (Loc.contains(Query))
+      return I;
+  }
+  return std::nullopt;
+}
+
+std::optional<Instruction *>
+AsmParserContext::getInstructionAtLocation(const FileLoc &Query) const {
+  return getInstructionAtLocation(FileLocRange(Query, Query));
+}
+
+bool AsmParserContext::addFunctionLocation(Function *F,
+                                           const FileLocRange &Loc) {
+  return Functions.insert({F, Loc}).second;
+}
+
+bool AsmParserContext::addBlockLocation(BasicBlock *BB,
+                                        const FileLocRange &Loc) {
+  return Blocks.insert({BB, Loc}).second;
+}
+
+bool AsmParserContext::addInstructionLocation(Instruction *I,
+                                              const FileLocRange &Loc) {
+  return Instructions.insert({I, Loc}).second;
+}
+
+} // namespace llvm
diff --git a/llvm/lib/AsmParser/CMakeLists.txt b/llvm/lib/AsmParser/CMakeLists.txt
index 20d0c50a029ca..dcfcc06f093a7 100644
--- a/llvm/lib/AsmParser/CMakeLists.txt
+++ b/llvm/lib/AsmParser/CMakeLists.txt
@@ -1,5 +1,6 @@
 # AsmParser
 add_llvm_component_library(LLVMAsmParser
+  AsmParserContext.cpp
   LLLexer.cpp
   LLParser.cpp
   Parser.cpp
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 3d5bd6155536e..a209de05c39db 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -176,6 +176,14 @@ LLLexer::LLLexer(StringRef StartBuf, SourceMgr &SM, SMDiagnostic &Err,
 
 int LLLexer::getNextChar() {
   char CurChar = *CurPtr++;
+  // Increment line number if this is the first character after a newline
+  // CurPtr points to the char after CurChar, so two positions before that
+  if ((CurPtr - 2) >= CurBuf.begin() && *(CurPtr - 2) == '\n') {
+    CurLineNum++;
+    CurColNum = 0;
+  } else
+    CurColNum++;
+
   switch (CurChar) {
   default: return (unsigned char)CurChar;
   case 0:
@@ -190,11 +198,52 @@ int LLLexer::getNextChar() {
   }
 }
 
+const char *LLLexer::skipNChars(unsigned N) {
+  while (N--)
+    getNextChar();
+  return CurPtr;
+}
+
+void LLLexer::advancePositionTo(const char *Ptr) {
+  bool RecalculateColumn = false;
+  while (CurPtr != Ptr) {
+    if (CurPtr > Ptr) {
+      --CurPtr;
+      --CurColNum;
+      // Since CurPtr is one char ahead of the stored position, chech if the
+      // previous char is not a newline
+      if (CurPtr != CurBuf.begin() && *(CurPtr - 1) == '\n') {
+        --CurLineNum;
+        RecalculateColumn = true;
+      }
+    } else
+      getNextChar();
+  }
+  if (RecalculateColumn) {
+    CurColNum = 0;
+    // Count the number of chars to the previous newline or start of buffer
+    for (const char *Ptr = CurPtr; Ptr != CurBuf.begin() && *(Ptr - 1) != '\n';
+         --Ptr, ++CurColNum)
+      ;
+  }
+}
+
 lltok::Kind LLLexer::LexToken() {
+  // Set token end to next location, since the end is
+  // exclusive
+  if (CurPtr != CurBuf.begin() && *(CurPtr - 1) == '\n') {
+    PrevTokEndLineNum = CurLineNum + 1;
+    PrevTokEndColNum = 0;
+  } else {
+    PrevTokEndLineNum = CurLineNum;
+    PrevTokEndColNum = CurColNum + 1;
+  }
   while (true) {
     TokStart = CurPtr;
-
     int CurChar = getNextChar();
+    CurTokColNum = CurColNum;
+    CurTokLineNum = CurLineNum;
+
     switch (CurChar) {
     default:
       // Handle letters: [a-zA-Z_]
@@ -216,12 +265,12 @@ lltok::Kind LLLexer::LexToken() {
     case '"': return LexQuote();
     case '.':
       if (const char *Ptr = isLabelTail(CurPtr)) {
-        CurPtr = Ptr;
+        advancePositionTo(Ptr);
         StrVal.assign(TokStart, CurPtr-1);
         return lltok::LabelStr;
       }
       if (CurPtr[0] == '.' && CurPtr[1] == '.') {
-        CurPtr += 2;
+        skipNChars(2);
         return lltok::dotdotdot;
       }
       return lltok::Error;
@@ -299,14 +348,14 @@ lltok::Kind LLLexer::LexAt() {
 
 lltok::Kind LLLexer::LexDollar() {
   if (const char *Ptr = isLabelTail(TokStart)) {
-    CurPtr = Ptr;
+    advancePositionTo(Ptr);
     StrVal.assign(TokStart, CurPtr - 1);
     return lltok::LabelStr;
   }
 
   // Handle DollarStringConstant: $\"[^\"]*\"
   if (CurPtr[0] == '"') {
-    ++CurPtr;
+    getNextChar();
 
     while (true) {
       int CurChar = getNextChar();
@@ -358,11 +407,11 @@ bool LLLexer::ReadVarName() {
   if (isalpha(static_cast<unsigned char>(CurPtr[0])) ||
       CurPtr[0] == '-' || CurPtr[0] == '$' ||
       CurPtr[0] == '.' || CurPtr[0] == '_') {
-    ++CurPtr;
+    getNextChar();
     while (isalnum(static_cast<unsigned char>(CurPtr[0])) ||
            CurPtr[0] == '-' || CurPtr[0] == '$' ||
            CurPtr[0] == '.' || CurPtr[0] == '_')
-      ++CurPtr;
+      getNextChar();
 
     StrVal.assign(NameStart, CurPtr);
     return true;
@@ -376,7 +425,8 @@ lltok::Kind LLLexer::LexUIntID(lltok::Kind Token) {
   if (!isdigit(static_cast<unsigned char>(CurPtr[0])))
     return lltok::Error;
 
-  for (++CurPtr; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (getNextChar(); isdigit(static_cast<unsigned char>(CurPtr[0]));
+       getNextChar())
     /*empty*/;
 
   uint64_t Val = atoull(TokStart + 1, CurPtr);
@@ -389,7 +439,7 @@ lltok::Kind LLLexer::LexUIntID(lltok::Kind Token) {
 lltok::Kind LLLexer::LexVar(lltok::Kind Var, lltok::Kind VarID) {
   // Handle StringConstant: \"[^\"]*\"
   if (CurPtr[0] == '"') {
-    ++CurPtr;
+    getNextChar();
 
     while (true) {
       int CurChar = getNextChar();
@@ -435,7 +485,7 @@ lltok::Kind LLLexer::LexQuote() {
     return kind;
 
   if (CurPtr[0] == ':') {
-    ++CurPtr;
+    getNextChar();
     if (StringRef(StrVal).contains(0)) {
       LexError("NUL character is not allowed in names");
       kind = lltok::Error;
@@ -455,11 +505,11 @@ lltok::Kind LLLexer::LexExclaim() {
   if (isalpha(static_cast<unsigned char>(CurPtr[0])) ||
       CurPtr[0] == '-' || CurPtr[0] == '$' ||
       CurPtr[0] == '.' || CurPtr[0] == '_' || CurPtr[0] == '\\') {
-    ++CurPtr;
+    getNextChar();
     while (isalnum(static_cast<unsigned char>(CurPtr[0])) ||
            CurPtr[0] == '-' || CurPtr[0] == '$' ||
            CurPtr[0] == '.' || CurPtr[0] == '_' || CurPtr[0] == '\\')
-      ++CurPtr;
+      getNextChar();
 
     StrVal.assign(TokStart+1, CurPtr);   // Skip !
     UnEscapeLexed(StrVal);
@@ -495,7 +545,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   const char *IntEnd = CurPtr[-1] == 'i' ? nullptr : StartChar;
   const char *KeywordEnd = nullptr;
 
-  for (; isLabelChar(*CurPtr); ++CurPtr) {
+  for (; isLabelChar(*CurPtr); getNextChar()) {
     // If we decide this is an integer, remember the end of the sequence.
     if (!IntEnd && !isdigit(static_cast<unsigned char>(*CurPtr)))
       IntEnd = CurPtr;
@@ -507,7 +557,8 @@ lltok::Kind LLLexer::LexIdentifier() {
   // If we stopped due to a colon, unless we were directed to ignore it,
   // this really is a label.
   if (!IgnoreColonInIdentifiers && *CurPtr == ':') {
-    StrVal.assign(StartChar-1, CurPtr++);
+    StrVal.assign(StartChar - 1, CurPtr);
+    getNextChar();
     return lltok::LabelStr;
   }
 
@@ -515,7 +566,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   // return it.
   if (!IntEnd) IntEnd = CurPtr;
   if (IntEnd != StartChar) {
-    CurPtr = IntEnd;
+    advancePositionTo(IntEnd);
     uint64_t NumBits = atoull(StartChar, CurPtr);
     if (NumBits < IntegerType::MIN_INT_BITS ||
         NumBits > IntegerType::MAX_INT_BITS) {
@@ -528,7 +579,7 @@ lltok::Kind LLLexer::LexIdentifier() {
 
   // Otherwise, this was a letter sequence.  See which keyword this is.
   if (!KeywordEnd) KeywordEnd = CurPtr;
-  CurPtr = KeywordEnd;
+  advancePositionTo(KeywordEnd);
   --StartChar;
   StringRef Keyword(StartChar, CurPtr - StartChar);
 
@@ -1043,7 +1094,7 @@ lltok::Kind LLLexer::LexIdentifier() {
     StringRef HexStr(TokStart + 3, len);
     if (!all_of(HexStr, isxdigit)) {
       // Bad token, return it as an error.
-      CurPtr = TokStart+3;
+      advancePositionTo(TokStart + 3);
       return lltok::Error;
     }
     APInt Tmp(bits, HexStr, 16);
@@ -1056,12 +1107,12 @@ lltok::Kind LLLexer::LexIdentifier() {
 
   // If this is "cc1234", return this as just "cc".
   if (TokStart[0] == 'c' && TokStart[1] == 'c') {
-    CurPtr = TokStart+2;
+    advancePositionTo(TokStart + 2);
     return lltok::kw_cc;
   }
 
   // Finally, if this isn't known, return an error.
-  CurPtr = TokStart+1;
+  advancePositionTo(TokStart + 1);
   return lltok::Error;
 }
 
@@ -1074,24 +1125,25 @@ lltok::Kind LLLexer::LexIdentifier() {
 ///    HexHalfConstant   0xH[0-9A-Fa-f]+
 ///    HexBFloatConstant 0xR[0-9A-Fa-f]+
 lltok::Kind LLLexer::Lex0x() {
-  CurPtr = TokStart + 2;
+  advancePositionTo(TokStart + 2);
 
   char Kind;
   if ((CurPtr[0] >= 'K' && CurPtr[0] <= 'M') || CurPtr[0] == 'H' ||
       CurPtr[0] == 'R') {
-    Kind = *CurPtr++;
+    Kind = *CurPtr;
+    getNextChar();
   } else {
     Kind = 'J';
   }
 
   if (!isxdigit(static_cast<unsigned char>(CurPtr[0]))) {
     // Bad token, return it as an error.
-    CurPtr = TokStart+1;
+    advancePositionTo(TokStart + 1);
     return lltok::Error;
   }
 
   while (isxdigit(static_cast<unsigned char>(CurPtr[0])))
-    ++CurPtr;
+    getNextChar();
 
   if (Kind == 'J') {
     // HexFPConstant - Floating point constant represented in IEEE format as a
@@ -1148,7 +1200,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
     // Okay, this is not a number after the -, it's probably a label.
     if (const char *End = isLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
-      CurPtr = End;
+      advancePositionTo(End);
       return lltok::LabelStr;
     }
 
@@ -1158,13 +1210,13 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
   // At this point, it is either a label, int or fp constant.
 
   // Skip digits, we have at least one.
-  for (; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (; isdigit(static_cast<unsigned char>(CurPtr[0])); getNextChar())
     /*empty*/;
 
   // Check if this is a fully-numeric label:
   if (isdigit(TokStart[0]) && CurPtr[0] == ':') {
     uint64_t Val = atoull(TokStart, CurPtr);
-    ++CurPtr; // Skip the colon.
+    getNextChar(); // Skip the colon.
     if ((unsigned)Val != Val)
       LexError("invalid value number (too large)");
     UIntVal = unsigned(Val);
@@ -1175,7 +1227,7 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
   if (isLabelChar(CurPtr[0]) || CurPtr[0] == ':') {
     if (const char *End = isLabelTail(CurPtr)) {
       StrVal.assign(TokStart, End-1);
-      CurPtr = End;
+      advancePositionTo(End);
       return lltok::LabelStr;
     }
   }
@@ -1189,17 +1241,19 @@ lltok::Kind LLLexer::LexDigitOrNegative() {
     return lltok::APSInt;
   }
 
-  ++CurPtr;
+  getNextChar();
 
   // Skip over [0-9]*([eE][-+]?[0-9]+)?
-  while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+  while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+    getNextChar();
 
   if (CurPtr[0] == 'e' || CurPtr[0] == 'E') {
     if (isdigit(static_cast<unsigned char>(CurPtr[1])) ||
         ((CurPtr[1] == '-' || CurPtr[1] == '+') &&
           isdigit(static_cast<unsigned char>(CurPtr[2])))) {
-      CurPtr += 2;
-      while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+      skipNChars(2);
+      while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+        getNextChar();
     }
   }
 
@@ -1217,26 +1271,29 @@ lltok::Kind LLLexer::LexPositive() {
     return lltok::Error;
 
   // Skip digits.
-  for (++CurPtr; isdigit(static_cast<unsigned char>(CurPtr[0])); ++CurPtr)
+  for (getNextChar(); isdigit(static_cast<unsigned char>(CurPtr[0]));
+       getNextChar())
     /*empty*/;
 
   // At this point, we need a '.'.
   if (CurPtr[0] != '.') {
-    CurPtr = TokStart+1;
+    advancePositionTo(TokStart + 1);
     return lltok::Error;
   }
 
-  ++CurPtr;
+  getNextChar();
 
   // Skip over [0-9]*([eE][-+]?[0-9]+)?
-  while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+  while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+    getNextChar();
 
   if (CurPtr[0] == 'e' || CurPtr[0] == 'E') {
     if (isdigit(static_cast<unsigned char>(CurPtr[1])) ||
         ((CurPtr[1] == '-' || CurPtr[1] == '+') &&
         isdigit(static_cast<unsigned char>(CurPtr[2])))) {
-      CurPtr += 2;
-      while (isdigit(static_cast<unsigned char>(CurPtr[0]))) ++CurPtr;
+      skipNChars(2);
+      while (isdigit(static_cast<unsigned char>(CurPtr[0])))
+        getNextChar();
     }
   }
 
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 1bc2906f63b07..03fe1097f8612 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -740,14 +740,22 @@ bool LLParser::parseDeclare() {
 ///   ::= 'define' FunctionHeader (!dbg !56)* '{' ...
 bool LLParser::parseDefine() {
   assert(Lex.getKind() == lltok::kw_define);
+  FileLoc FunctionStart(Lex.getTokLineNum(), Lex.getTokColNum());
   Lex.Lex();
 
   Function *F;
   unsigned FunctionNumber = -1;
   SmallVector<unsigned> UnnamedArgNums;
-  return parseFunctionHeader(F, true, FunctionNumber, UnnamedArgNums) ||
-         parseOptionalFunctionMetadata(*F) ||
-         parseFunctionBody(*F, FunctionNumber, UnnamedArgNums);
+  bool RetValue =
+      parseFunctionHeader(F, true, FunctionNumber, UnnamedArgNums) ||
+      parseOptionalFunctionMetadata(*F) ||
+      parseFunctionBody(*F, FunctionNumber, UnnamedArgNums);
+  if (ParserContext)
+    ParserContext->addFunctionLocation(
+        F, FileLocRange(FunctionStart, {Lex.getPrevTokEndLineNum(),
+                                        Lex.getPrevTokEndColNum()}));
+
+  return RetValue;
 }
 
 /// parseGlobalType
@@ -6951,6 +6959,8 @@ bool LLParser::parseFunctionBody(Function &Fn, unsigned FunctionNumber,
 /// parseBasicBlock
 ///   ::= (LabelStr|LabelID)? Instruction*
 bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
+  FileLoc BBStart(Lex.getTokLineNum(), Lex.getTokColNum());
+
   // If this basic block starts out with a name, remember it.
   std::string Name;
   int NameID = -1;
@@ -6992,6 +7002,7 @@ bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
       TrailingDbgRecord.emplace_back(DR, DeleteDbgRecord);
     }
 
+    FileLoc InstStart(Lex.getTokLineNum(), Lex.getTokColNum());
     // This instruction may have three possibilities for a name: a) none
     // specified, b) name specified "%foo =", c) number specified: "%4 =".
     LocTy NameLoc = Lex.getLoc();
@@ -7041,8 +7052,18 @@ bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
     for (DbgRecordPtr &DR : TrailingDbgRecord)
       BB->insertDbgRecordBefore(DR.release(), Inst->getIterator());
     TrailingDbgRecord.clear();
+    if (ParserContext) {
+      ParserContext->addInstructionLocation(
+          Inst, FileLocRange(InstStart, {Lex.getPrevTokEndLineNum(),
+                                         Lex.getPrevTokEndColNum()}));
+    }
   } while (!Inst->isTerminator());
 
+  if (ParserContext)
+    ParserContext->addBlockLocation(
+        BB, FileLocRange(BBStart, {Lex.getPrevTokEndLineNum(),
+                                   Lex.getPrevTokEndColNum()}));
+
   assert(TrailingDbgRecord.empty() &&
          "All debug values should have been attached to an instruction.");
 
diff --git a/llvm/lib/AsmParser/Parser.cpp b/llvm/lib/AsmParser/Parser.cpp
index 07fdce981b084..c5346d0977314 100644
--- a/llvm/lib/AsmParser/Parser.cpp
+++ b/llvm/lib/AsmParser/Parser.cpp
@@ -24,33 +24,38 @@ using namespace llvm;
 static bool parseAssemblyInto(MemoryBufferRef F, Module *M,
                               ModuleSummaryIndex *Index, SMDiagnostic &Err,
                               SlotMapping *Slots, bool UpgradeDebugInfo,
-                              DataLayoutCallbackTy DataLayoutCallback) {
+                              DataLayoutCallbackTy DataLayoutCallback,
+                              AsmParserContext *ParserContext = nullptr) {
   SourceMgr SM;
   std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(F);
   SM.AddNewSourceBuffer(std::move(Buf), SMLoc());
 
   std::optional<LLVMContext> OptContext;
   return LLParser(F.getBuffer(), SM, Err, M, Index,
-                  M ? M->getContext() : OptContext.emplace(), Slots)
+                  M ? M->getContext() : OptContext.emplace(), Slots,
+                  ParserContext)
       .Run(UpgradeDebugInfo, DataLayoutCallback);
 }
 
 bool llvm::parseAssemblyInto(MemoryBufferRef F, Module *M,
                              ModuleSummaryIndex *Index, SMDiagnostic &Err,
                              SlotMapping *Slots,
-                             DataLayoutCallbackTy DataLayoutCallback) {
+                             DataLayoutCallbackTy DataLayoutCallback,
+                             AsmParserContext *ParserContext) {
   return ::parseAssemblyInto(F, M, Index, Err, Slots,
-                             /*UpgradeDebugInfo*/ true, DataLayoutCallback);
+                             /*UpgradeDebugInfo*/ true, DataLayoutCallback,
+                             ParserContext);
 }
 
 std::unique_ptr<Module>
 llvm::parseAssembly(MemoryBufferRef F, SMDiagnostic &Err, LLVMContext &Context,
-                    SlotMapping *Slots,
-                    DataLayoutCallbackTy DataLayoutCallback) {
+                    SlotMapping *Slots, DataLayoutCallbackTy DataLayoutCallback,
+                    AsmParserContext *ParserContext) {
   std::unique_ptr<Module> M =
       std::make_unique<Module>(F.getBufferIdentifier(), Context);
 
-  if (parseAssemblyInto(F, M.get(), nullptr, Err, Slots, DataLayoutCallback))
+  if (parseAssemblyInto(F, M.get(), nullptr, Err, Slots, DataLayoutCallback,
+                        ParserContext))
     return nullptr;
 
   return M;
@@ -133,12 +138,14 @@ ParsedModuleAndIndex llvm::parseAssemblyFileWithIndexNoUpgradeDebugInfo(
                                       DataLayoutCallback);
 }
 
-std::unique_ptr<Module> llvm::parseAssemblyString(StringRef AsmString,
-                                                  SMDiagnostic &Err,
-                                                  LLVMContext &Context,
-                                                  SlotMapping *Slots) {
+std::unique_ptr<Module>
+llvm::parseAssemblyString(StringRef AsmString, SMDiagnostic &Err,
+                          LLVMContext &Context, SlotMapping *Slots,
+                          AsmParserContext *ParserContext) {
   MemoryBufferRef F(AsmString, "<string>");
-  return parseAssembly(F, Err, Context, Slots);
+  return parseAssembly(
+      F, Err, Context, Slots, [](StringRef, StringRef) { return std::nullopt; },
+      ParserContext);
 }
 
 static bool parseSummaryIndexAssemblyInto(MemoryBufferRef F,
diff --git a/llvm/lib/IRReader/IRReader.cpp b/llvm/lib/IRReader/IRReader.cpp
index a7e7deee8aa91..c16871f081d1d 100644
--- a/llvm/lib/IRReader/IRReader.cpp
+++ b/llvm/lib/IRReader/IRReader.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/IRReader/IRReader.h"
 #include "llvm-c/IRReader.h"
+#include "llvm/AsmParser/AsmParserContext.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/IR/LLVMContext.h"
@@ -68,7 +69,8 @@ std::unique_ptr<Module> llvm::getLazyIRFileModule(StringRef Filename,
 
 std::unique_ptr<Module> llvm::parseIR(MemoryBufferRef Buffer, SMDiagnostic &Err,
                                       LLVMContext &Context,
-                                      ParserCallbacks Callbacks) {
+                                      ParserCallbacks Callbacks,
+                                      llvm::AsmParserContext *ParserContext) {
   NamedRegionTimer T(TimeIRParsingName, TimeIRParsingDescription,
                      TimeIRParsingGroupName, TimeIRParsingGroupDescription,
                      TimePassesIsEnabled);
@@ -88,12 +90,14 @@ std::unique_ptr<Module> llvm::parseIR(MemoryBufferRef Buffer, SMDiagnostic &Err,
 
   return parseAssembly(Buffer, Err, Context, nullptr,
                        Callbacks.DataLayout.value_or(
-                           [](StringRef, StringRef) { return std::nullopt; }));
+                           [](StringRef, StringRef) { return std::nullopt; }),
+                       ParserContext);
 }
 
 std::unique_ptr<Module> llvm::parseIRFile(StringRef Filename, SMDiagnostic &Err,
                                           LLVMContext &Context,
-                                          ParserCallbacks Callbacks) {
+                                          ParserCallbacks Callbacks,
+                                          AsmParserContext *ParserContext) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
       MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/true);
   if (std::error_code EC = FileOrErr.getError()) {
@@ -102,7 +106,8 @@ std::unique_ptr<Module> llvm::parseIRFile(StringRef Filename, SMDiagnostic &Err,
     return nullptr;
   }
 
-  return parseIR(FileOrErr.get()->getMemBufferRef(), Err, Context, Callbacks);
+  return parseIR(FileOrErr.get()->getMemBufferRef(), Err, Context, Callbacks,
+                 ParserContext);
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/unittests/AsmParser/AsmParserTest.cpp b/llvm/unittests/AsmParser/AsmParserTest.cpp
index ce226705068af..49f59696709f2 100644
--- a/llvm/unittests/AsmParser/AsmParserTest.cpp
+++ b/llvm/unittests/AsmParser/AsmParserTest.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/AsmParser/AsmParserContext.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/AsmParser/SlotMapping.h"
 #include "llvm/IR/Constants.h"
@@ -14,6 +16,8 @@
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
@@ -479,4 +483,60 @@ TEST(AsmParserTest, DIExpressionBodyAtBeginningWithSlotMappingParsing) {
   ASSERT_EQ(Mapping.MetadataNodes.size(), 0u);
 }
 
+#define ASSERT_EQ_LOC(Loc1, Loc2)                                              \
+  do {                                                                         \
+    bool AreLocsEqual = Loc1.contains(Loc2) && Loc2.contains(Loc1);            \
+    if (!AreLocsEqual) {                                                       \
+      dbgs() << #Loc1 " location: " << Loc1.Start.Line << ":"                  \
+             << Loc1.Start.Col << " - " << Loc1.End.Line << ":"                \
+             << Loc1.End.Col << "\n";                                          \
+      dbgs() << #Loc2 " location: " << Loc2.Start.Line << ":"                  \
+             << Loc2.Start.Col << " - " << Loc2.End.Line << ":"                \
+             << Loc2.End.Col << "\n";                                          \
+    }                                                                          \
+    ASSERT_TRUE(AreLocsEqual);                                                 \
+  } while (false)
+
+TEST(AsmParserTest, ParserObjectLocations) {
+  // Expected to fail with function location starting one character later, needs
+  // a fix
+  StringRef Source = "define i32 @main() {\n"
+                     "entry:\n"
+                     "    %a = add i32 1, 2\n"
+                     "    ret i32 %a\n"
+                     "}\n";
+  LLVMContext Ctx;
+  SMDiagnostic Error;
+  SlotMapping Mapping;
+  AsmParserContext ParserContext;
+  auto Mod = parseAssemblyString(Source, Error, Ctx, &Mapping, &ParserContext);
+
+  auto *MainFn = Mod->getFunction("main");
+  ASSERT_TRUE(MainFn != nullptr);
+
+  auto MaybeMainLoc = ParserContext.getFunctionLocation(MainFn);
+  ASSERT_TRUE(MaybeMainLoc.has_value());
+  auto MainLoc = MaybeMainLoc.value();
+  auto ExpectedMainLoc = FileLocRange(FileLoc{0, 0}, FileLoc{4, 1});
+  ASSERT_EQ_LOC(MainLoc, ExpectedMainLoc);
+
+  auto &EntryBB = MainFn->getEntryBlock();
+  auto MaybeEntryBBLoc = ParserContext.getBlockLocation(&EntryBB);
+  ASSERT_TRUE(MaybeEntryBBLoc.has_value());
+  auto EntryBBLoc = MaybeEntryBBLoc.value();
+  auto ExpectedEntryBBLoc = FileLocRange(FileLoc{1, 0}, FileLoc{3, 14});
+  ASSERT_EQ_LOC(EntryBBLoc, ExpectedEntryBBLoc);
+
+  SmallVector<FileLocRange> InstructionLocations = {
+      FileLocRange(FileLoc{2, 4}, FileLoc{2, 21}),
+      FileLocRange(FileLoc{3, 4}, FileLoc{3, 14})};
+
+  for (const auto &[Inst, ExpectedLoc] : zip(EntryBB, InstructionLocations)) {
+    auto MaybeInstLoc = ParserContext.getInstructionLocation(&Inst);
+    ASSERT_TRUE(MaybeMainLoc.has_value());
+    auto InstLoc = MaybeInstLoc.value();
+    ASSERT_EQ_LOC(InstLoc, ExpectedLoc);
+  }
+}
+
 } // end anonymous namespace

>From b0c5318d100ecb44684c5c31de1b19ab774b5549 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Thu, 28 Aug 2025 10:10:43 +0000
Subject: [PATCH 07/20] Fix clang format

---
 llvm/include/llvm/AsmParser/AsmParserContext.h | 2 +-
 llvm/lib/AsmParser/LLLexer.cpp                 | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
index bc4d93ef727ef..78ea32ac7ca08 100644
--- a/llvm/include/llvm/AsmParser/AsmParserContext.h
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -24,7 +24,7 @@ namespace llvm {
 /// When available, it can answer queries about what is at a given
 /// file location, as well as where in a file a given IR construct
 /// is.
-/// 
+///
 /// This information is optionally emitted by the LLParser while
 /// it reads LLVM textual IR.
 class AsmParserContext {
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index cd9d5b37d86e5..be5b2b9bce0ca 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -180,7 +180,7 @@ int LLLexer::getNextChar() {
   if (CurPtr == CurBuf.end())
     return EOF;
   // Increment line number if this is the first character after a newline
-  if (CurPtr > CurBuf.begin() && *(CurPtr-1) == '\n'){
+  if (CurPtr > CurBuf.begin() && *(CurPtr - 1) == '\n') {
     CurLineNum++;
     CurColNum = 0;
   } else

>From 416514e8eb5de149dffe5bd49035b7a91904d70c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Fri, 29 Aug 2025 07:49:27 +0000
Subject: [PATCH 08/20] Move private members to top of class definition

---
 llvm/include/llvm/AsmParser/AsmParserContext.h | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
index 78ea32ac7ca08..39c0e0d9df4de 100644
--- a/llvm/include/llvm/AsmParser/AsmParserContext.h
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -28,6 +28,10 @@ namespace llvm {
 /// This information is optionally emitted by the LLParser while
 /// it reads LLVM textual IR.
 class AsmParserContext {
+  DenseMap<Function *, FileLocRange> Functions;
+  DenseMap<BasicBlock *, FileLocRange> Blocks;
+  DenseMap<Instruction *, FileLocRange> Instructions;
+
 public:
   std::optional<FileLocRange> getFunctionLocation(const Function *) const;
   std::optional<FileLocRange> getBlockLocation(const BasicBlock *) const;
@@ -42,11 +46,6 @@ class AsmParserContext {
   bool addFunctionLocation(Function *, const FileLocRange &);
   bool addBlockLocation(BasicBlock *, const FileLocRange &);
   bool addInstructionLocation(Instruction *, const FileLocRange &);
-
-private:
-  DenseMap<Function *, FileLocRange> Functions;
-  DenseMap<BasicBlock *, FileLocRange> Blocks;
-  DenseMap<Instruction *, FileLocRange> Instructions;
 };
 } // namespace llvm
 

>From 35ca1a501ac7a7c969df05d8e40ea3530bad75b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Tue, 2 Sep 2025 12:12:51 +0000
Subject: [PATCH 09/20] Use SourceMgr to resolve Line:Column position

---
 llvm/include/llvm/AsmParser/LLLexer.h |  8 -----
 llvm/lib/AsmParser/LLLexer.cpp        | 45 +++++++--------------------
 2 files changed, 12 insertions(+), 41 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index 5008ef029f3ff..bacf124d07d20 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -29,10 +29,6 @@ namespace llvm {
     const char *CurPtr;
     StringRef CurBuf;
 
-    // The line number at `CurPtr-1`, zero-indexed
-    unsigned CurLineNum = 0;
-    // The column number at `CurPtr-1`, zero-indexed
-    unsigned CurColNum = -1;
     // The line number of the start of the current token, zero-indexed
     unsigned CurTokLineNum = 0;
     // The column number of the start of the current token, zero-indexed
@@ -91,10 +87,6 @@ namespace llvm {
       IgnoreColonInIdentifiers = val;
     }
 
-    // Get the current line number, zero-indexed
-    unsigned getLineNum() { return CurLineNum; }
-    // Get the current column number, zero-indexed
-    unsigned getColNum() { return CurColNum; }
     // Get the line number of the start of the current token, zero-indexed
     unsigned getTokLineNum() { return CurTokLineNum; }
     // Get the column number of the start of the current token, zero-indexed
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index be5b2b9bce0ca..0041cc5fd95fa 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -180,11 +180,6 @@ int LLLexer::getNextChar() {
   if (CurPtr == CurBuf.end())
     return EOF;
   // Increment line number if this is the first character after a newline
-  if (CurPtr > CurBuf.begin() && *(CurPtr - 1) == '\n') {
-    CurLineNum++;
-    CurColNum = 0;
-  } else
-    CurColNum++;
   return *CurPtr++;
 }
 
@@ -195,44 +190,28 @@ const char *LLLexer::skipNChars(unsigned N) {
 }
 
 void LLLexer::advancePositionTo(const char *Ptr) {
-  bool RecalculateColumn = false;
-  while (CurPtr != Ptr) {
-    if (CurPtr > Ptr) {
-      --CurPtr;
-      --CurColNum;
-      // Since CurPtr is one char ahead of the stored position, check if the
-      // previous char is not a newline
-      if (CurPtr != CurBuf.begin() && *(CurPtr - 1) == '\n') {
-        --CurLineNum;
-        RecalculateColumn = true;
-      }
-    } else
-      getNextChar();
+  if (CurBuf.begin() > Ptr) {
+    CurPtr = CurBuf.begin();
+    return;
   }
-  if (RecalculateColumn) {
-    CurColNum = 0;
-    // Count the number of chars to the previous newline or start of buffer
-    for (const char *Ptr = CurPtr; Ptr != CurBuf.begin() && *(Ptr - 1) != '\n';
-         --Ptr, ++CurColNum)
-      ;
+  if (CurBuf.end() < Ptr) {
+    CurPtr = CurBuf.end();
+    return;
   }
+
+  CurPtr = Ptr;
 }
 
 lltok::Kind LLLexer::LexToken() {
   // Set token end to next location, since the end is
   // exclusive
-  if (CurPtr != CurBuf.begin() && *(CurPtr - 1) == '\n') {
-    PrevTokEndLineNum = CurLineNum + 1;
-    PrevTokEndColNum = 0;
-  } else {
-    PrevTokEndLineNum = CurLineNum;
-    PrevTokEndColNum = CurColNum + 1;
-  }
+  std::tie(PrevTokEndLineNum, PrevTokEndColNum) =
+      SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
   while (true) {
     TokStart = CurPtr;
+    std::tie(CurTokLineNum, CurTokColNum) =
+        SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
     int CurChar = getNextChar();
-    CurTokColNum = CurColNum;
-    CurTokLineNum = CurLineNum;
 
     switch (CurChar) {
     default:

>From b3d8254fadec29bde061f89dd74ef85e758419e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Tue, 2 Sep 2025 13:44:38 +0000
Subject: [PATCH 10/20] Fix zeroindexing on token positions

---
 llvm/lib/AsmParser/LLLexer.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 0041cc5fd95fa..8ce963702f330 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -207,10 +207,14 @@ lltok::Kind LLLexer::LexToken() {
   // exclusive
   std::tie(PrevTokEndLineNum, PrevTokEndColNum) =
       SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
+  --PrevTokEndLineNum;
+  --PrevTokEndColNum;
   while (true) {
     TokStart = CurPtr;
     std::tie(CurTokLineNum, CurTokColNum) =
         SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
+    --CurTokLineNum;
+    --CurTokColNum;
     int CurChar = getNextChar();
 
     switch (CurChar) {

>From 23dcc6b4d05c58dc359c5334fcba0061f92499be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Wed, 3 Sep 2025 13:21:54 +0000
Subject: [PATCH 11/20] Replace Line:Column storage with Poiters and on demand
 conversion

---
 llvm/include/llvm/AsmParser/LLLexer.h | 39 +++++++++++++--------------
 llvm/include/llvm/IR/Value.h          |  1 +
 llvm/lib/AsmParser/LLLexer.cpp        |  9 +------
 llvm/lib/AsmParser/LLParser.cpp       | 15 +++++------
 4 files changed, 27 insertions(+), 37 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h
index bacf124d07d20..5e4d43ebbd4ed 100644
--- a/llvm/include/llvm/AsmParser/LLLexer.h
+++ b/llvm/include/llvm/AsmParser/LLLexer.h
@@ -17,27 +17,20 @@
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/SMLoc.h"
+#include "llvm/Support/SourceMgr.h"
 #include <string>
 
 namespace llvm {
   class Type;
   class SMDiagnostic;
-  class SourceMgr;
   class LLVMContext;
 
   class LLLexer {
     const char *CurPtr;
     StringRef CurBuf;
 
-    // The line number of the start of the current token, zero-indexed
-    unsigned CurTokLineNum = 0;
-    // The column number of the start of the current token, zero-indexed
-    unsigned CurTokColNum = 0;
-    // The line number of the end of the current token, zero-indexed
-    unsigned PrevTokEndLineNum = -1;
-    // The column number of the end (exclusive) of the current token,
-    // zero-indexed
-    unsigned PrevTokEndColNum = -1;
+    // The the end (exclusive) of the current token
+    const char *PrevTokEnd = nullptr;
 
     enum class ErrorPriority {
       None,   // No error message present.
@@ -87,16 +80,22 @@ namespace llvm {
       IgnoreColonInIdentifiers = val;
     }
 
-    // Get the line number of the start of the current token, zero-indexed
-    unsigned getTokLineNum() { return CurTokLineNum; }
-    // Get the column number of the start of the current token, zero-indexed
-    unsigned getTokColNum() { return CurTokColNum; }
-    // Get the line number of the end of the previous token, zero-indexed,
-    // exclusive
-    unsigned getPrevTokEndLineNum() { return PrevTokEndLineNum; }
-    // Get the column number of the end of the previous token, zero-indexed,
-    // exclusive
-    unsigned getPrevTokEndColNum() { return PrevTokEndColNum; }
+    // Get the line, column position of the start of the current token,
+    // zero-indexed
+    std::pair<unsigned, unsigned> getTokLineColumnPos() {
+      auto LC = SM.getLineAndColumn(SMLoc::getFromPointer(TokStart));
+      --LC.first;
+      --LC.second;
+      return LC;
+    }
+    // Get the line, column position of the end of the previous token,
+    // zero-indexed exclusive
+    std::pair<unsigned, unsigned> getPrevTokEndLineColumnPos() {
+      auto LC = SM.getLineAndColumn(SMLoc::getFromPointer(PrevTokEnd));
+      --LC.first;
+      --LC.second;
+      return LC;
+    }
 
     // This returns true as a convenience for the parser functions that return
     // true on error.
diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h
index 9e27797d151e2..2617981cc090c 100644
--- a/llvm/include/llvm/IR/Value.h
+++ b/llvm/include/llvm/IR/Value.h
@@ -68,6 +68,7 @@ struct FileLoc {
   }
 
   FileLoc(unsigned L, unsigned C) : Line(L), Col(C) {}
+  FileLoc(std::pair<unsigned, unsigned> LC) : Line(LC.first), Col(LC.second) {}
 };
 
 struct FileLocRange {
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 8ce963702f330..0e378bc81fd69 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -205,16 +205,9 @@ void LLLexer::advancePositionTo(const char *Ptr) {
 lltok::Kind LLLexer::LexToken() {
   // Set token end to next location, since the end is
   // exclusive
-  std::tie(PrevTokEndLineNum, PrevTokEndColNum) =
-      SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
-  --PrevTokEndLineNum;
-  --PrevTokEndColNum;
+  PrevTokEnd = CurPtr;
   while (true) {
     TokStart = CurPtr;
-    std::tie(CurTokLineNum, CurTokColNum) =
-        SM.getLineAndColumn(SMLoc::getFromPointer(CurPtr));
-    --CurTokLineNum;
-    --CurTokColNum;
     int CurChar = getNextChar();
 
     switch (CurChar) {
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 03fe1097f8612..65daaf5be318d 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -740,7 +740,7 @@ bool LLParser::parseDeclare() {
 ///   ::= 'define' FunctionHeader (!dbg !56)* '{' ...
 bool LLParser::parseDefine() {
   assert(Lex.getKind() == lltok::kw_define);
-  FileLoc FunctionStart(Lex.getTokLineNum(), Lex.getTokColNum());
+  FileLoc FunctionStart(Lex.getTokLineColumnPos());
   Lex.Lex();
 
   Function *F;
@@ -752,8 +752,7 @@ bool LLParser::parseDefine() {
       parseFunctionBody(*F, FunctionNumber, UnnamedArgNums);
   if (ParserContext)
     ParserContext->addFunctionLocation(
-        F, FileLocRange(FunctionStart, {Lex.getPrevTokEndLineNum(),
-                                        Lex.getPrevTokEndColNum()}));
+        F, FileLocRange(FunctionStart, Lex.getPrevTokEndLineColumnPos()));
 
   return RetValue;
 }
@@ -6959,7 +6958,7 @@ bool LLParser::parseFunctionBody(Function &Fn, unsigned FunctionNumber,
 /// parseBasicBlock
 ///   ::= (LabelStr|LabelID)? Instruction*
 bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
-  FileLoc BBStart(Lex.getTokLineNum(), Lex.getTokColNum());
+  FileLoc BBStart(Lex.getTokLineColumnPos());
 
   // If this basic block starts out with a name, remember it.
   std::string Name;
@@ -7002,7 +7001,7 @@ bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
       TrailingDbgRecord.emplace_back(DR, DeleteDbgRecord);
     }
 
-    FileLoc InstStart(Lex.getTokLineNum(), Lex.getTokColNum());
+    FileLoc InstStart(Lex.getTokLineColumnPos());
     // This instruction may have three possibilities for a name: a) none
     // specified, b) name specified "%foo =", c) number specified: "%4 =".
     LocTy NameLoc = Lex.getLoc();
@@ -7054,15 +7053,13 @@ bool LLParser::parseBasicBlock(PerFunctionState &PFS) {
     TrailingDbgRecord.clear();
     if (ParserContext) {
       ParserContext->addInstructionLocation(
-          Inst, FileLocRange(InstStart, {Lex.getPrevTokEndLineNum(),
-                                         Lex.getPrevTokEndColNum()}));
+          Inst, FileLocRange(InstStart, Lex.getPrevTokEndLineColumnPos()));
     }
   } while (!Inst->isTerminator());
 
   if (ParserContext)
     ParserContext->addBlockLocation(
-        BB, FileLocRange(BBStart, {Lex.getPrevTokEndLineNum(),
-                                   Lex.getPrevTokEndColNum()}));
+        BB, FileLocRange(BBStart, Lex.getPrevTokEndLineColumnPos()));
 
   assert(TrailingDbgRecord.empty() &&
          "All debug values should have been attached to an instruction.");

>From 06d526544de7973b5abf4779c08bc2e45c444983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Thu, 4 Sep 2025 09:11:51 +0000
Subject: [PATCH 12/20] Use nullptr as missing value

---
 .../include/llvm/AsmParser/AsmParserContext.h | 31 ++++++++++++++-----
 llvm/lib/AsmParser/AsmParserContext.cpp       | 20 ++++++------
 2 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
index 39c0e0d9df4de..092485d23437d 100644
--- a/llvm/include/llvm/AsmParser/AsmParserContext.h
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -36,13 +36,30 @@ class AsmParserContext {
   std::optional<FileLocRange> getFunctionLocation(const Function *) const;
   std::optional<FileLocRange> getBlockLocation(const BasicBlock *) const;
   std::optional<FileLocRange> getInstructionLocation(const Instruction *) const;
-  std::optional<Function *> getFunctionAtLocation(const FileLocRange &) const;
-  std::optional<Function *> getFunctionAtLocation(const FileLoc &) const;
-  std::optional<BasicBlock *> getBlockAtLocation(const FileLocRange &) const;
-  std::optional<BasicBlock *> getBlockAtLocation(const FileLoc &) const;
-  std::optional<Instruction *>
-  getInstructionAtLocation(const FileLocRange &) const;
-  std::optional<Instruction *> getInstructionAtLocation(const FileLoc &) const;
+  /// Get the function at the requested location range.
+  /// If no single function occupies the queried range, or the record is
+  /// missing, a nullptr is returned.
+  Function *getFunctionAtLocation(const FileLocRange &) const;
+  /// Get the function at the requested location.
+  /// If no function occupies the queried location, or the record is missing, a
+  /// nullptr is returned.
+  Function *getFunctionAtLocation(const FileLoc &) const;
+  /// Get the block at the requested location range.
+  /// If no single block occupies the queried range, or the record is missing, a
+  /// nullptr is returned.
+  BasicBlock *getBlockAtLocation(const FileLocRange &) const;
+  /// Get the block at the requested location.
+  /// If no block occupies the queried location, or the record is missing, a
+  /// nullptr is returned.
+  BasicBlock *getBlockAtLocation(const FileLoc &) const;
+  /// Get the instruction at the requested location range.
+  /// If no single instruction occupies the queried range, or the record is
+  /// missing, a nullptr is returned.
+  Instruction *getInstructionAtLocation(const FileLocRange &) const;
+  /// Get the instruction at the requested location.
+  /// If no instruction occupies the queried location, or the record is missing,
+  /// a nullptr is returned.
+  Instruction *getInstructionAtLocation(const FileLoc &) const;
   bool addFunctionLocation(Function *, const FileLocRange &);
   bool addBlockLocation(BasicBlock *, const FileLocRange &);
   bool addInstructionLocation(Instruction *, const FileLocRange &);
diff --git a/llvm/lib/AsmParser/AsmParserContext.cpp b/llvm/lib/AsmParser/AsmParserContext.cpp
index f5e3d83f5d346..7de2bfc67acfb 100644
--- a/llvm/lib/AsmParser/AsmParserContext.cpp
+++ b/llvm/lib/AsmParser/AsmParserContext.cpp
@@ -31,44 +31,42 @@ AsmParserContext::getInstructionLocation(const Instruction *I) const {
   return Instructions.at(I);
 }
 
-std::optional<Function *>
+Function *
 AsmParserContext::getFunctionAtLocation(const FileLocRange &Query) const {
   for (auto &[F, Loc] : Functions) {
     if (Loc.contains(Query))
       return F;
   }
-  return std::nullopt;
+  return nullptr;
 }
 
-std::optional<Function *>
-AsmParserContext::getFunctionAtLocation(const FileLoc &Query) const {
+Function *AsmParserContext::getFunctionAtLocation(const FileLoc &Query) const {
   return getFunctionAtLocation(FileLocRange(Query, Query));
 }
 
-std::optional<BasicBlock *>
+BasicBlock *
 AsmParserContext::getBlockAtLocation(const FileLocRange &Query) const {
   for (auto &[BB, Loc] : Blocks) {
     if (Loc.contains(Query))
       return BB;
   }
-  return std::nullopt;
+  return nullptr;
 }
 
-std::optional<BasicBlock *>
-AsmParserContext::getBlockAtLocation(const FileLoc &Query) const {
+BasicBlock *AsmParserContext::getBlockAtLocation(const FileLoc &Query) const {
   return getBlockAtLocation(FileLocRange(Query, Query));
 }
 
-std::optional<Instruction *>
+Instruction *
 AsmParserContext::getInstructionAtLocation(const FileLocRange &Query) const {
   for (auto &[I, Loc] : Instructions) {
     if (Loc.contains(Query))
       return I;
   }
-  return std::nullopt;
+  return nullptr;
 }
 
-std::optional<Instruction *>
+Instruction *
 AsmParserContext::getInstructionAtLocation(const FileLoc &Query) const {
   return getInstructionAtLocation(FileLocRange(Query, Query));
 }

>From 4e08921093d0031fa135f5ffff55ff6beabe9e3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Thu, 4 Sep 2025 09:12:32 +0000
Subject: [PATCH 13/20] Enclose debug prints of tests in LLVM_DEBUG

---
 llvm/unittests/AsmParser/AsmParserTest.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/unittests/AsmParser/AsmParserTest.cpp b/llvm/unittests/AsmParser/AsmParserTest.cpp
index 49f59696709f2..700864c2d12b9 100644
--- a/llvm/unittests/AsmParser/AsmParserTest.cpp
+++ b/llvm/unittests/AsmParser/AsmParserTest.cpp
@@ -22,6 +22,8 @@
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
 
+#define DEBUG_TYPE "Unittest-asm-parser-tests"
+
 using namespace llvm;
 
 namespace {
@@ -486,14 +488,14 @@ TEST(AsmParserTest, DIExpressionBodyAtBeginningWithSlotMappingParsing) {
 #define ASSERT_EQ_LOC(Loc1, Loc2)                                              \
   do {                                                                         \
     bool AreLocsEqual = Loc1.contains(Loc2) && Loc2.contains(Loc1);            \
-    if (!AreLocsEqual) {                                                       \
+    LLVM_DEBUG(if (!AreLocsEqual) {                                            \
       dbgs() << #Loc1 " location: " << Loc1.Start.Line << ":"                  \
              << Loc1.Start.Col << " - " << Loc1.End.Line << ":"                \
              << Loc1.End.Col << "\n";                                          \
       dbgs() << #Loc2 " location: " << Loc2.Start.Line << ":"                  \
              << Loc2.Start.Col << " - " << Loc2.End.Line << ":"                \
              << Loc2.End.Col << "\n";                                          \
-    }                                                                          \
+    });                                                                        \
     ASSERT_TRUE(AreLocsEqual);                                                 \
   } while (false)
 

>From 3da9e9db2de1dfcea062522eb2ebdcd7d2eba715 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Mon, 15 Sep 2025 11:42:06 +0000
Subject: [PATCH 14/20] Decapitalize DEBUG_TYPE

---
 llvm/unittests/AsmParser/AsmParserTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/AsmParser/AsmParserTest.cpp b/llvm/unittests/AsmParser/AsmParserTest.cpp
index 700864c2d12b9..0ca21eaea800f 100644
--- a/llvm/unittests/AsmParser/AsmParserTest.cpp
+++ b/llvm/unittests/AsmParser/AsmParserTest.cpp
@@ -22,7 +22,7 @@
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
 
-#define DEBUG_TYPE "Unittest-asm-parser-tests"
+#define DEBUG_TYPE "unittest-asm-parser-tests"
 
 using namespace llvm;
 

>From 4b3bc0ee923a1c402d1279696d191f1907c51d11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Fri, 26 Sep 2025 10:11:24 +0000
Subject: [PATCH 15/20] Move FileLoc from Value.h to FileLoc.h

---
 .../include/llvm/AsmParser/AsmParserContext.h |  1 +
 llvm/include/llvm/AsmParser/FileLoc.h         | 48 +++++++++++++++++++
 llvm/include/llvm/IR/Value.h                  | 33 -------------
 3 files changed, 49 insertions(+), 33 deletions(-)
 create mode 100644 llvm/include/llvm/AsmParser/FileLoc.h

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
index 092485d23437d..0bc383ff147fd 100644
--- a/llvm/include/llvm/AsmParser/AsmParserContext.h
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_ASMPARSER_ASMPARSER_STATE_H
 #define LLVM_ASMPARSER_ASMPARSER_STATE_H
 
+#include "FileLoc.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/Value.h"
 #include <optional>
diff --git a/llvm/include/llvm/AsmParser/FileLoc.h b/llvm/include/llvm/AsmParser/FileLoc.h
new file mode 100644
index 0000000000000..66fd4f21df9c0
--- /dev/null
+++ b/llvm/include/llvm/AsmParser/FileLoc.h
@@ -0,0 +1,48 @@
+//===-- FileLoc.h ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ASMPARSER_FILELOC_H
+#define LLVM_ASMPARSER_FILELOC_H
+
+#include <assert.h>
+#include <utility>
+
+struct FileLoc {
+  unsigned Line;
+  unsigned Col;
+
+  bool operator<=(const FileLoc &RHS) const {
+    return Line < RHS.Line || (Line == RHS.Line && Col <= RHS.Col);
+  }
+
+  bool operator<(const FileLoc &RHS) const {
+    return Line < RHS.Line || (Line == RHS.Line && Col < RHS.Col);
+  }
+
+  FileLoc(unsigned L, unsigned C) : Line(L), Col(C) {}
+  FileLoc(std::pair<unsigned, unsigned> LC) : Line(LC.first), Col(LC.second) {}
+};
+
+struct FileLocRange {
+  FileLoc Start;
+  FileLoc End;
+
+  FileLocRange() : Start(0, 0), End(0, 0) {}
+
+  FileLocRange(FileLoc S, FileLoc E) : Start(S), End(E) {
+    assert(Start <= End);
+  }
+
+  bool contains(FileLoc L) const { return Start <= L && L <= End; }
+
+  bool contains(FileLocRange LR) const {
+    return contains(LR.Start) && contains(LR.End);
+  }
+};
+
+#endif
diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h
index 2617981cc090c..04d0391c04098 100644
--- a/llvm/include/llvm/IR/Value.h
+++ b/llvm/include/llvm/IR/Value.h
@@ -55,39 +55,6 @@ class User;
 
 using ValueName = StringMapEntry<Value *>;
 
-struct FileLoc {
-  unsigned Line;
-  unsigned Col;
-
-  bool operator<=(const FileLoc &RHS) const {
-    return Line < RHS.Line || (Line == RHS.Line && Col <= RHS.Col);
-  }
-
-  bool operator<(const FileLoc &RHS) const {
-    return Line < RHS.Line || (Line == RHS.Line && Col < RHS.Col);
-  }
-
-  FileLoc(unsigned L, unsigned C) : Line(L), Col(C) {}
-  FileLoc(std::pair<unsigned, unsigned> LC) : Line(LC.first), Col(LC.second) {}
-};
-
-struct FileLocRange {
-  FileLoc Start;
-  FileLoc End;
-
-  FileLocRange() : Start(0, 0), End(0, 0) {}
-
-  FileLocRange(FileLoc S, FileLoc E) : Start(S), End(E) {
-    assert(Start <= End);
-  }
-
-  bool contains(FileLoc L) const { return Start <= L && L <= End; }
-
-  bool contains(FileLocRange LR) const {
-    return contains(LR.Start) && contains(LR.End);
-  }
-};
-
 //===----------------------------------------------------------------------===//
 //                                 Value Class
 //===----------------------------------------------------------------------===//

>From ed7a04a5c6ab05963bdde3285e663280a78434c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Fri, 26 Sep 2025 10:23:44 +0000
Subject: [PATCH 16/20] Rename include guard defines to reflext filename

---
 llvm/include/llvm/AsmParser/AsmParserContext.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/AsmParser/AsmParserContext.h b/llvm/include/llvm/AsmParser/AsmParserContext.h
index 0bc383ff147fd..eb4b9c4013b9a 100644
--- a/llvm/include/llvm/AsmParser/AsmParserContext.h
+++ b/llvm/include/llvm/AsmParser/AsmParserContext.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_ASMPARSER_ASMPARSER_STATE_H
-#define LLVM_ASMPARSER_ASMPARSER_STATE_H
+#ifndef LLVM_ASMPARSER_ASMPARSERCONTEXT_H
+#define LLVM_ASMPARSER_ASMPARSERCONTEXT_H
 
 #include "FileLoc.h"
 #include "llvm/ADT/DenseMap.h"

>From 4a749d2651eb4076deb5829b458d0790845cf81b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Wed, 1 Oct 2025 10:37:30 +0000
Subject: [PATCH 17/20] LSP server

---
 llvm/include/llvm/Analysis/CFGPrinter.h  |  51 ++-
 llvm/include/llvm/AsmParser/FileLoc.h    |   4 +
 llvm/include/llvm/Support/LSP/Protocol.h |  17 +
 llvm/lib/Analysis/CFGPrinter.cpp         |   6 +-
 llvm/lib/Support/LSP/Protocol.cpp        |  11 +
 llvm/tools/llvm-lsp/CMakeLists.txt       |  17 +
 llvm/tools/llvm-lsp/IRDocument.h         | 350 +++++++++++++++++++++
 llvm/tools/llvm-lsp/LLVMBuild.txt        |  26 ++
 llvm/tools/llvm-lsp/OptRunner.h          | 208 +++++++++++++
 llvm/tools/llvm-lsp/Protocol.cpp         |  58 ++++
 llvm/tools/llvm-lsp/Protocol.h           | 102 ++++++
 llvm/tools/llvm-lsp/README.md            | 183 +++++++++++
 llvm/tools/llvm-lsp/llvm-lsp-server.cpp  | 379 +++++++++++++++++++++++
 llvm/tools/llvm-lsp/llvm-lsp-server.h    | 127 ++++++++
 14 files changed, 1521 insertions(+), 18 deletions(-)
 create mode 100644 llvm/tools/llvm-lsp/CMakeLists.txt
 create mode 100644 llvm/tools/llvm-lsp/IRDocument.h
 create mode 100644 llvm/tools/llvm-lsp/LLVMBuild.txt
 create mode 100644 llvm/tools/llvm-lsp/OptRunner.h
 create mode 100644 llvm/tools/llvm-lsp/Protocol.cpp
 create mode 100644 llvm/tools/llvm-lsp/Protocol.h
 create mode 100644 llvm/tools/llvm-lsp/README.md
 create mode 100755 llvm/tools/llvm-lsp/llvm-lsp-server.cpp
 create mode 100644 llvm/tools/llvm-lsp/llvm-lsp-server.h

diff --git a/llvm/include/llvm/Analysis/CFGPrinter.h b/llvm/include/llvm/Analysis/CFGPrinter.h
index ec26da87eb916..c492ab8be1271 100644
--- a/llvm/include/llvm/Analysis/CFGPrinter.h
+++ b/llvm/include/llvm/Analysis/CFGPrinter.h
@@ -31,6 +31,9 @@
 #include "llvm/Support/DOTGraphTraits.h"
 #include "llvm/Support/FormatVariadic.h"
 
+#include <functional>
+#include <sstream>
+
 namespace llvm {
 class ModuleSlotTracker;
 
@@ -69,13 +72,19 @@ class DOTFuncInfo {
   bool ShowHeat;
   bool EdgeWeights;
   bool RawWeights;
+  using NodeIdFormatterTy =
+      std::function<std::optional<std::string>(const BasicBlock *)>;
+  std::optional<NodeIdFormatterTy> NodeIdFormatter;
 
 public:
-  DOTFuncInfo(const Function *F) : DOTFuncInfo(F, nullptr, nullptr, 0) {}
+  DOTFuncInfo(const Function *F)
+      : DOTFuncInfo(F, nullptr, nullptr, 0) {}
   LLVM_ABI ~DOTFuncInfo();
 
-  LLVM_ABI DOTFuncInfo(const Function *F, const BlockFrequencyInfo *BFI,
-                       const BranchProbabilityInfo *BPI, uint64_t MaxFreq);
+  LLVM_ABI
+  DOTFuncInfo(const Function *F, const BlockFrequencyInfo *BFI,
+              const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
+              std::optional<NodeIdFormatterTy> NodeIdFormatter = std::nullopt);
 
   const BlockFrequencyInfo *getBFI() const { return BFI; }
 
@@ -102,6 +111,10 @@ class DOTFuncInfo {
   void setEdgeWeights(bool EdgeWeights) { this->EdgeWeights = EdgeWeights; }
 
   bool showEdgeWeights() { return EdgeWeights; }
+
+  std::optional<NodeIdFormatterTy> getNodeIdFormatter() {
+    return NodeIdFormatter;
+  }
 };
 
 template <>
@@ -311,21 +324,27 @@ struct DOTGraphTraits<DOTFuncInfo *> : public DefaultDOTGraphTraits {
   }
 
   std::string getNodeAttributes(const BasicBlock *Node, DOTFuncInfo *CFGInfo) {
+    std::stringstream Attrs;
+
+    if (auto NodeIdFmt = CFGInfo->getNodeIdFormatter())
+      if (auto NodeId = (*NodeIdFmt)(Node))
+        Attrs << "id=\"" << *NodeId << "\"";
+
+    if (CFGInfo->showHeatColors()) {
+      uint64_t Freq = CFGInfo->getFreq(Node);
+      std::string Color = getHeatColor(Freq, CFGInfo->getMaxFreq());
+      std::string EdgeColor = (Freq <= (CFGInfo->getMaxFreq() / 2))
+                                  ? (getHeatColor(0))
+                                  : (getHeatColor(1));
+      if (!Attrs.str().empty())
+        Attrs << ",";
+      Attrs << "color=\"" << EdgeColor << "ff\",style=filled,"
+            << "fillcolor=\"" << Color << "70\"" << "fontname=\"Courier\"";
+    }
 
-    if (!CFGInfo->showHeatColors())
-      return "";
-
-    uint64_t Freq = CFGInfo->getFreq(Node);
-    std::string Color = getHeatColor(Freq, CFGInfo->getMaxFreq());
-    std::string EdgeColor = (Freq <= (CFGInfo->getMaxFreq() / 2))
-                                ? (getHeatColor(0))
-                                : (getHeatColor(1));
-
-    std::string Attrs = "color=\"" + EdgeColor + "ff\", style=filled," +
-                        " fillcolor=\"" + Color + "70\"" +
-                        " fontname=\"Courier\"";
-    return Attrs;
+    return Attrs.str();
   }
+
   LLVM_ABI bool isNodeHidden(const BasicBlock *Node,
                              const DOTFuncInfo *CFGInfo);
   LLVM_ABI void computeDeoptOrUnreachablePaths(const Function *F);
diff --git a/llvm/include/llvm/AsmParser/FileLoc.h b/llvm/include/llvm/AsmParser/FileLoc.h
index 66fd4f21df9c0..d209fd56f9bfb 100644
--- a/llvm/include/llvm/AsmParser/FileLoc.h
+++ b/llvm/include/llvm/AsmParser/FileLoc.h
@@ -12,6 +12,8 @@
 #include <assert.h>
 #include <utility>
 
+namespace llvm {
+
 struct FileLoc {
   unsigned Line;
   unsigned Col;
@@ -45,4 +47,6 @@ struct FileLocRange {
   }
 };
 
+} // namespace llvm
+
 #endif
diff --git a/llvm/include/llvm/Support/LSP/Protocol.h b/llvm/include/llvm/Support/LSP/Protocol.h
index 93b82f1e581f8..0437557ff646e 100644
--- a/llvm/include/llvm/Support/LSP/Protocol.h
+++ b/llvm/include/llvm/Support/LSP/Protocol.h
@@ -1244,6 +1244,23 @@ struct CodeAction {
 /// Add support for JSON serialization.
 llvm::json::Value toJSON(const CodeAction &);
 
+//===----------------------------------------------------------------------===//
+//  ShowMessageParams
+//===----------------------------------------------------------------------===//
+
+enum class MessageType { Error = 1, Warning = 2, Info = 3, Log = 4, Debug = 5 };
+
+struct ShowMessageParams {
+  ShowMessageParams(MessageType Type, std::string Message)
+      : type(Type), message(Message) {}
+  MessageType type;
+  /// The actual message.
+  std::string message;
+};
+
+/// Add support for JSON serialization.
+llvm::json::Value toJSON(const ShowMessageParams &Params);
+
 } // namespace lsp
 } // namespace llvm
 
diff --git a/llvm/lib/Analysis/CFGPrinter.cpp b/llvm/lib/Analysis/CFGPrinter.cpp
index 38aad849755be..39108a906f081 100644
--- a/llvm/lib/Analysis/CFGPrinter.cpp
+++ b/llvm/lib/Analysis/CFGPrinter.cpp
@@ -92,8 +92,10 @@ static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,
 }
 
 DOTFuncInfo::DOTFuncInfo(const Function *F, const BlockFrequencyInfo *BFI,
-                         const BranchProbabilityInfo *BPI, uint64_t MaxFreq)
-    : F(F), BFI(BFI), BPI(BPI), MaxFreq(MaxFreq) {
+                         const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
+                         std::optional<NodeIdFormatterTy> NodeIdFormatter)
+    : F(F), BFI(BFI), BPI(BPI), MaxFreq(MaxFreq),
+      NodeIdFormatter(NodeIdFormatter) {
   ShowHeat = false;
   EdgeWeights = !!BPI; // Print EdgeWeights when BPI is available.
   RawWeights = !!BFI;  // Print RawWeights when BFI is available.
diff --git a/llvm/lib/Support/LSP/Protocol.cpp b/llvm/lib/Support/LSP/Protocol.cpp
index f22126345a435..10ee6cd02c789 100644
--- a/llvm/lib/Support/LSP/Protocol.cpp
+++ b/llvm/lib/Support/LSP/Protocol.cpp
@@ -1041,3 +1041,14 @@ llvm::json::Value llvm::lsp::toJSON(const CodeAction &Value) {
     CodeAction["edit"] = *Value.edit;
   return std::move(CodeAction);
 }
+
+//===----------------------------------------------------------------------===//
+// ShowMessageParams
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value llvm::lsp::toJSON(const ShowMessageParams &Params) {
+  return llvm::json::Object{
+      {"type", static_cast<int>(Params.type)},
+      {"message", Params.message},
+  };
+}
diff --git a/llvm/tools/llvm-lsp/CMakeLists.txt b/llvm/tools/llvm-lsp/CMakeLists.txt
new file mode 100644
index 0000000000000..2a1bc3fb307d4
--- /dev/null
+++ b/llvm/tools/llvm-lsp/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+  Core
+  IRReader
+  Support
+  Analysis
+  Passes
+  SupportLSP
+  TransformUtils
+  AsmParser
+)
+
+add_llvm_tool(llvm-lsp-server
+  llvm-lsp-server.cpp
+  Protocol.cpp
+)
+
+target_compile_features(llvm-lsp-server PRIVATE cxx_std_23)
diff --git a/llvm/tools/llvm-lsp/IRDocument.h b/llvm/tools/llvm-lsp/IRDocument.h
new file mode 100644
index 0000000000000..a9c83bb2aa142
--- /dev/null
+++ b/llvm/tools/llvm-lsp/IRDocument.h
@@ -0,0 +1,350 @@
+//===-- IRDocument.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_LSP_IRDOCUMENT_H
+#define LLVM_TOOLS_LLVM_LSP_IRDOCUMENT_H
+
+#include "OptRunner.h"
+#include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/Analysis/CFGPrinter.h"
+#include "llvm/AsmParser/AsmParserContext.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/SourceMgr.h"
+
+#include <filesystem>
+#include <memory>
+#include <string>
+
+namespace {
+
+class IRDocumentHelpers {
+public:
+  static std::optional<std::string>
+  basicBlockIdFormatter(const llvm::BasicBlock *BB,
+                        const llvm::AsmParserContext &ParserContext) {
+    return ParserContext.getBlockLocation(BB).transform([](const auto &Loc) {
+      return llvm::formatv("range_{0}_{1}_{2}_{3}", Loc.Start.Line,
+                           Loc.Start.Col, Loc.End.Line, Loc.End.Col);
+    });
+  }
+
+  static std::optional<llvm::FileLocRange>
+  basicBlockIdParser(std::string BBId) {
+    unsigned StartLine, StartCol, EndLine, EndCol;
+    auto [part0, rest0] = llvm::StringRef{BBId}.split('_');
+    if (part0 != "range")
+      return std::nullopt;
+    auto [part1, rest1] = rest0.split('_');
+    if (part1.getAsInteger(10, StartLine))
+      return std::nullopt;
+    auto [part2, rest2] = rest1.split('_');
+    if (part2.getAsInteger(10, StartCol))
+      return std::nullopt;
+    auto [part3, rest3] = rest2.split('_');
+    if (part3.getAsInteger(10, EndLine))
+      return std::nullopt;
+    if (rest3.contains('_') || rest3.getAsInteger(10, EndCol))
+      return std::nullopt;
+    if (part1.empty() || part2.empty() || part3.empty() || rest3.empty())
+      return std::nullopt;
+    return llvm::FileLocRange{llvm::FileLoc{StartLine, StartCol},
+                              llvm::FileLoc{EndLine, EndCol}};
+  }
+};
+
+} // namespace
+
+namespace llvm {
+// Tracks and Manages the Cache of all Artifacts for a given IR.
+class IRArtifacts {
+  const Module &IR;
+  std::filesystem::path ArtifactsFolderPath;
+
+  // FIXME: Can perhaps maintain a single list of only SVG/Dot files
+  DenseMap<Function *, std::filesystem::path> DotFileList;
+  DenseMap<Function *, std::filesystem::path> SVGFileList;
+  DenseMap<unsigned, std::filesystem::path> IntermediateIRDirectories;
+
+  // TODO: Add support to store locations of Intermediate IR file locations
+
+public:
+  IRArtifacts(StringRef Filepath, Module &M) : IR(M) {
+    // Make Artifacts folder, if it does not exist
+    lsp::Logger::info("Creating IRArtifacts Directory for {}", Filepath.str());
+    std::filesystem::path FilepathObj(Filepath.str());
+    ArtifactsFolderPath = FilepathObj.parent_path().string() + "/Artifacts-" +
+                          FilepathObj.stem().string();
+    if (!std::filesystem::exists(ArtifactsFolderPath)) {
+      std::filesystem::create_directory(ArtifactsFolderPath);
+      lsp::Logger::info("Finished creating IR Artifacts Directory {} for {}",
+                        ArtifactsFolderPath.string(), Filepath.str());
+    } else
+      lsp::Logger::info("Directory {} already exists",
+                        ArtifactsFolderPath.string());
+  }
+
+  void generateGraphs(const AsmParserContext &ParserContext) {
+    for (auto &F : IR.getFunctionList())
+      if (!F.isDeclaration())
+        generateGraphsForFunc(F.getName(), ParserContext);
+  }
+
+  void generateGraphsForFunc(StringRef FuncName,
+                             const AsmParserContext &ParserContext) {
+    Function *F = IR.getFunction(FuncName);
+    assert(F && "Function does not exist to generate Dot file");
+
+    // Generate Dot file
+    std::filesystem::path DotFilePath =
+        ArtifactsFolderPath / std::filesystem::path(FuncName.str() + ".dot");
+    if (!std::filesystem::exists(DotFilePath)) {
+      PassBuilder PB;
+      FunctionAnalysisManager FAM;
+      PB.registerFunctionAnalyses(FAM);
+      auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(*F);
+      auto &BPI = FAM.getResult<BranchProbabilityAnalysis>(*F);
+      DOTFuncInfo DFI(
+          F, &BFI, &BPI, getMaxFreq(*F, &BFI), [&](const BasicBlock *BB) {
+            return IRDocumentHelpers::basicBlockIdFormatter(BB, ParserContext);
+          });
+      DFI.setHeatColors(true);
+      DFI.setEdgeWeights(true);
+      DFI.setRawEdgeWeights(false);
+      // FIXME: I think this dumps something to the stdout (or stderr?) that in
+      // any case gets
+      //   sent to the client and shows in the trace log, eg. I see messages
+      //   like this: "writing to the newly created file
+      //   /remote-home/jjecmen/irviz-2.0/test/Artifacts-foo/main.dot" We should
+      //   prevent that.
+      WriteGraph(&DFI, FuncName, false, "CFG for " + FuncName.str(),
+                 DotFilePath.string());
+    }
+
+    // Generate SVG file
+    generateSVGFromDot(DotFilePath, F);
+
+    DotFileList[F] = DotFilePath;
+  }
+
+  void addIntermediateIR(Module &M, unsigned PassNum, StringRef PassName) {
+    auto IRFolder =
+        ArtifactsFolderPath / (std::to_string(PassNum) + "-" + PassName.str());
+    if (!std::filesystem::exists(IRFolder))
+      std::filesystem::create_directory(IRFolder);
+    IntermediateIRDirectories[PassNum] = IRFolder;
+    lsp::Logger::info("Created directory for intermediate IR artifacts!");
+
+    auto IRFilepath = IRFolder / "ir.ll";
+    if (!std::filesystem::exists(IRFilepath)) {
+      lsp::Logger::info("Creating new file to store Intermediate IR: {}",
+                        IRFilepath.string());
+      std::error_code EC;
+      raw_fd_ostream OutFile(IRFilepath.string(), EC, sys::fs::OF_None);
+      M.print(OutFile, nullptr);
+      OutFile.flush();
+      OutFile.close();
+      lsp::Logger::info("Finished creating IR file");
+    } else {
+      lsp::Logger::info("IR File path already exists: {}", IRFilepath.string());
+    }
+  }
+
+  std::optional<std::string> getIRAfterPassNumber(unsigned N) {
+    if (!IntermediateIRDirectories.contains(N)) {
+      lsp::Logger::info("Did not find IR Directory!");
+      return std::nullopt;
+    }
+    return IntermediateIRDirectories[N].string() + "/ir.ll";
+  }
+
+  std::optional<std::string> getDotFilePath(Function *F) {
+    if (DotFileList.contains(F)) {
+      return DotFileList[F].string();
+    }
+    return std::nullopt;
+  }
+
+  std::optional<std::string> getSVGFilePath(Function *F) {
+    if (SVGFileList.contains(F)) {
+      return SVGFileList[F].string();
+    }
+    return std::nullopt;
+  }
+
+private:
+  void generateSVGFromDot(std::filesystem::path Dotpath, Function *F) {
+    std::filesystem::path SVGFilePath =
+        std::filesystem::path(Dotpath).replace_extension(".svg");
+    std::string Cmd =
+        "dot -Tsvg " + Dotpath.string() + " -o " + SVGFilePath.string();
+    lsp::Logger::info("Running command: {}", Cmd);
+    int Result = std::system(Cmd.c_str());
+
+    if (Result == 0) {
+      lsp::Logger::info("SVG Generated : {}", SVGFilePath.string());
+      SVGFileList[F] = SVGFilePath;
+    } else
+      lsp::Logger::info("Failed to generate SVG!");
+  }
+};
+
+// LSP Server will use this class to query details about the IR file.
+class IRDocument {
+  LLVMContext C;
+  std::unique_ptr<Module> ParsedModule;
+  StringRef Filepath;
+
+  std::unique_ptr<OptRunner> Optimizer;
+  std::unique_ptr<IRArtifacts> IRA;
+
+public:
+  IRDocument(StringRef PathToIRFile) : Filepath(PathToIRFile) {
+    ParsedModule = loadModuleFromIR(PathToIRFile, C);
+    IRA = std::make_unique<IRArtifacts>(PathToIRFile, *ParsedModule);
+    Optimizer = std::make_unique<OptRunner>(*ParsedModule);
+
+    // Eagerly generate all CFG for all functions in the IRDocument.
+    IRA->generateGraphs(ParserContext);
+    lsp::Logger::info("Finished setting up IR Document: {}",
+                      PathToIRFile.str());
+  }
+
+  // ---------------- APIs that the Language Server can use  -----------------
+
+  std::string getNodeId(const BasicBlock *BB) {
+    if (auto Id = IRDocumentHelpers::basicBlockIdFormatter(BB, ParserContext))
+      return *Id;
+    return "";
+  }
+
+  FileLocRange parseNodeId(std::string BBId) {
+    if (auto FLR = IRDocumentHelpers::basicBlockIdParser(BBId))
+      return *FLR;
+    return FileLocRange{};
+  }
+
+  Function *getFirstFunction() {
+    return &ParsedModule->getFunctionList().front();
+  }
+
+  std::optional<std::string> getPathForSVGFile(Function *F) {
+    return IRA->getSVGFilePath(F);
+  }
+
+  auto &getFunctions() { return ParsedModule->getFunctionList(); }
+
+  Function *getFunctionAtLocation(unsigned Line, unsigned Col) {
+    FileLoc FL(Line, Col);
+    if (auto *MaybeF = ParserContext.getFunctionAtLocation(FL))
+      return MaybeF;
+    return nullptr;
+  }
+
+  BasicBlock *getBlockAtLocation(unsigned Line, unsigned Col) {
+    FileLoc FL(Line, Col);
+    if (auto *MaybeBB = ParserContext.getBlockAtLocation(FL))
+      return MaybeBB;
+    return nullptr;
+  }
+
+  Instruction *getInstructionAtLocation(unsigned Line, unsigned Col) {
+    FileLoc FL(Line, Col);
+    if (auto *MaybeI = ParserContext.getInstructionAtLocation(FL))
+      return MaybeI;
+    return nullptr;
+  }
+
+  // N is 1-Indexed here, but IRA expects 0-Indexed
+  llvm::Expected<std::string> getIRAfterPassNumber(const std::string &Pipeline,
+                                                   unsigned N) {
+    auto ExistingIR = IRA->getIRAfterPassNumber(N);
+    if (ExistingIR) {
+      lsp::Logger::info("Found Existing IR");
+      return *ExistingIR;
+    }
+    auto PassNameResult = Optimizer->getPassName(Pipeline, N);
+    if (!PassNameResult)
+      return PassNameResult.takeError();
+    auto PassName = PassNameResult.get();
+    lsp::Logger::info("Found Pass name for pass number {} as {}",
+                      std::to_string(N), PassName);
+
+    auto IntermediateIR = Optimizer->getModuleAfterPass(Pipeline, N);
+    if (!IntermediateIR) {
+      lsp::Logger::info("Error while getting intermediate IR");
+      return IntermediateIR.takeError();
+    }
+    lsp::Logger::info(
+        "Got intermediate IR. Storing it in Artifacts Directory!");
+    IRA->addIntermediateIR(*IntermediateIR.get(), N, PassName);
+    lsp::Logger::info("Finished storing in Artifacts directory!");
+    return *IRA->getIRAfterPassNumber(N);
+  }
+
+  // FIXME: We are doing some redundant work here in below functions, which can
+  // be fused together.
+  llvm::Expected<SmallVector<std::string, 256>>
+  getPassList(const std::string &Pipeline) {
+    SmallVector<std::string, 256> PassList;
+    auto PassNameAndDescriptionListResult =
+        Optimizer->getPassListAndDescription(Pipeline);
+
+    if (!PassNameAndDescriptionListResult) {
+      lsp::Logger::info("Handling error in getPassList()");
+      return PassNameAndDescriptionListResult.takeError();
+    }
+
+    for (auto &P : PassNameAndDescriptionListResult.get())
+      PassList.push_back(P.first);
+
+    return PassList;
+  }
+  llvm::Expected<SmallVector<std::string, 256>>
+  getPassDescriptions(const std::string &Pipeline) {
+    SmallVector<std::string, 256> PassDesc;
+    auto PassNameAndDescriptionListResult =
+        Optimizer->getPassListAndDescription(Pipeline);
+
+    if (!PassNameAndDescriptionListResult)
+      return PassNameAndDescriptionListResult.takeError();
+
+    for (auto &P : PassNameAndDescriptionListResult.get())
+      PassDesc.push_back(P.second);
+
+    return PassDesc;
+  }
+
+  AsmParserContext ParserContext;
+
+private:
+  std::unique_ptr<Module> loadModuleFromIR(StringRef Filepath, LLVMContext &C) {
+    SMDiagnostic Err;
+    // Try to parse as textual IR
+    auto M = parseIRFile(Filepath, Err, C, {}, &ParserContext);
+    if (!M) {
+      // If parsing failed, print the error and crash
+      lsp::Logger::error("Failed parsing IR file: {}", Err.getMessage().str());
+      return nullptr;
+    }
+    return M;
+  }
+};
+
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_LSP_IRDOCUMENT_H
diff --git a/llvm/tools/llvm-lsp/LLVMBuild.txt b/llvm/tools/llvm-lsp/LLVMBuild.txt
new file mode 100644
index 0000000000000..c686e51e2a2e6
--- /dev/null
+++ b/llvm/tools/llvm-lsp/LLVMBuild.txt
@@ -0,0 +1,26 @@
+;===- ./tools/llvm-lsp/LLVMBuild.txt -------------------------*- Conf -*--===;
+;
+; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+; See https://llvm.org/LICENSE.txt for license information.
+; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+;   http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-lsp-server
+parent = Tools
+required_libraries =
+ Core
+ IRReader
+ Support
+ Analysis
+ Passes
diff --git a/llvm/tools/llvm-lsp/OptRunner.h b/llvm/tools/llvm-lsp/OptRunner.h
new file mode 100644
index 0000000000000..b4c1da37cb665
--- /dev/null
+++ b/llvm/tools/llvm-lsp/OptRunner.h
@@ -0,0 +1,208 @@
+//===-- OptRunner.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_LSP_OPTRUNNER_H
+#define LLVM_TOOLS_LLVM_LSP_OPTRUNNER_H
+
+#include "Protocol.h"
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/LSP/Logging.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include <memory>
+#include <string>
+
+namespace llvm {
+
+// FIXME: Maybe a better name?
+class OptRunner {
+  LLVMContext Context;
+  const Module &InitialIR;
+
+  SmallVector<std::unique_ptr<Module>, 256> IntermediateIRList;
+
+public:
+  OptRunner(Module &IIR) : InitialIR(IIR) {}
+
+  llvm::Expected<SmallVector<std::pair<std::string, std::string>, 256>>
+  getPassListAndDescription(const std::string PipelineText) {
+    // First is Passname, Second is Pass Description.
+    SmallVector<std::pair<std::string, std::string>, 256>
+        PassListAndDescription;
+    unsigned PassNumber = 0;
+    // FIXME: Should we only consider passes that modify the IR?
+    std::function<void(const StringRef, Any, const PreservedAnalyses)>
+        RecordPassNamesAndDescription = [&PassListAndDescription, &PassNumber](
+                                            const StringRef PassName, Any IR,
+                                            const PreservedAnalyses &PA) {
+          PassNumber++;
+          std::string PassNameStr =
+              (std::to_string(PassNumber) + "-" + PassName.str());
+          std::string PassDescStr = [&IR, &PassName]() -> std::string {
+            if (auto *M = any_cast<const Module *>(&IR))
+              return "Module Pass on \"" + (**M).getName().str() + "\"";
+            if (auto *F = any_cast<const Function *>(&IR))
+              return "Function Pass on \"" + (**F).getName().str() + "\"";
+            if (auto *L = any_cast<const Loop *>(&IR)) {
+              Function *F = (*L)->getHeader()->getParent();
+              std::string Desc = "Loop Pass in Function \"" +
+                                 F->getName().str() +
+                                 "\" on loop with Header \"" +
+                                 (*L)->getHeader()->getName().str() + "\"";
+              return Desc;
+            }
+            if (auto *SCC = any_cast<const LazyCallGraph::SCC *>(&IR)) {
+              Function &F = (**SCC).begin()->getFunction();
+              std::string Desc =
+                  "CGSCC Pass on Function \"" + F.getName().str() + "\"";
+              return Desc;
+            }
+            lsp::Logger::error("Unknown Pass Type \"{}\"!", PassName.str());
+            return "";
+          }();
+
+          PassListAndDescription.push_back({PassNameStr, PassDescStr});
+        };
+
+    auto RunOptResult = runOpt(PipelineText, RecordPassNamesAndDescription);
+    if (!RunOptResult) {
+      lsp::Logger::info("Handling error in getPassListAndDescription()");
+      return RunOptResult.takeError();
+    }
+    return PassListAndDescription;
+  }
+
+  llvm::Expected<std::unique_ptr<Module>>
+  runOpt(const std::string PipelineText,
+         std::function<void(const StringRef, Any, const PreservedAnalyses)>
+             &AfterPassCallback) {
+    // Analysis Managers
+    LoopAnalysisManager LAM;
+    FunctionAnalysisManager FAM;
+    CGSCCAnalysisManager CGAM;
+    ModuleAnalysisManager MAM;
+
+    PassInstrumentationCallbacks PIC;
+
+    ModulePassManager MPM;
+    PassBuilder PB;
+
+    // Callback that redirects to a custom callback.
+    PIC.registerAfterPassCallback(AfterPassCallback);
+
+    PB = PassBuilder(nullptr, PipelineTuningOptions(), std::nullopt, &PIC);
+    PB.registerModuleAnalyses(MAM);
+    PB.registerCGSCCAnalyses(CGAM);
+    PB.registerFunctionAnalyses(FAM);
+    PB.registerLoopAnalyses(LAM);
+    PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+    // Parse Pipeline text
+    auto ParseError = PB.parsePassPipeline(MPM, PipelineText);
+    if (ParseError) {
+      lsp::Logger::info("Error parsing pipeline text!");
+      return llvm::createStringError(toString(std::move(ParseError)).c_str());
+    }
+
+    // Run Opt on a copy of the original IR, so that we dont modify the original
+    // IR.
+    auto FinalIR = CloneModule(InitialIR);
+    MPM.run(*FinalIR, MAM);
+    return FinalIR;
+  }
+
+  // TODO: Check if N lies with in bounds for below methods. And to verify that
+  // they are populated.
+  // N is 1-Indexed
+  llvm::Expected<std::unique_ptr<Module>>
+  getModuleAfterPass(const std::string PipelineText, unsigned N) {
+    unsigned PassNumber = 0;
+    std::unique_ptr<Module> IntermediateIR = nullptr;
+    std::function<void(const StringRef, Any, const PreservedAnalyses)>
+        RecordIRAfterPass = [&PassNumber, &N,
+                             &IntermediateIR](const StringRef PassName, Any IR,
+                                              const PreservedAnalyses &PA) {
+          PassNumber++;
+          if (PassNumber == N) {
+            IntermediateIR = [&IR, &PassName]() -> std::unique_ptr<Module> {
+              if (auto *M = any_cast<const Module *>(&IR))
+                return CloneModule(**M);
+              if (auto *F = any_cast<const Function *>(&IR))
+                return CloneModule(*(**F).getParent());
+              if (auto *L = any_cast<const Loop *>(&IR))
+                return CloneModule(
+                    *((*L)->getHeader()->getParent())->getParent());
+              if (auto *SCC = any_cast<const LazyCallGraph::SCC *>(&IR))
+                return CloneModule(
+                    *((**SCC).begin()->getFunction()).getParent());
+
+              lsp::Logger::error("Unknown Pass Type \"{}\"!", PassName.str());
+              return nullptr;
+            }();
+          }
+        };
+
+    auto RunOptResult = runOpt(PipelineText, RecordIRAfterPass);
+    if (!RunOptResult) {
+      return RunOptResult.takeError();
+    }
+
+    if (!IntermediateIR) {
+      lsp::Logger::error("Unrecognized Pass Number {}!", std::to_string(N));
+      return make_error<lsp::LSPError>(
+          formatv("Unrecognized pass number {}!", N),
+          lsp::ErrorCode::InvalidParams);
+    }
+
+    return IntermediateIR;
+  }
+
+  llvm::Expected<std::unique_ptr<Module>>
+  getFinalModule(const std::string PipelineText) {
+    std::function<void(const StringRef, Any, const PreservedAnalyses)>
+        EmptyCallback = [](const StringRef, Any, const PreservedAnalyses &) {};
+    return runOpt(PipelineText, EmptyCallback);
+  }
+
+  llvm::Expected<std::string> getPassName(std::string PipelineText,
+                                          unsigned N) {
+    unsigned PassNumber = 0;
+    std::string IntermediatePassName = "";
+    std::function<void(const StringRef, Any, const PreservedAnalyses)>
+        RecordNameAfterPass =
+            [&PassNumber, &N, &IntermediatePassName](
+                const StringRef PassName, Any IR, const PreservedAnalyses &PA) {
+              PassNumber++;
+              if (PassNumber == N)
+                IntermediatePassName = PassName.str();
+            };
+
+    auto RunOptResult = runOpt(PipelineText, RecordNameAfterPass);
+    if (!RunOptResult) {
+      return RunOptResult.takeError();
+    }
+
+    if (IntermediatePassName == "") {
+      lsp::Logger::error("Unrecognized Pass Number {}!", std::to_string(N));
+      return make_error<lsp::LSPError>(
+          formatv("Unrecognized pass number {}!", N),
+          lsp::ErrorCode::InvalidParams);
+    }
+
+    return IntermediatePassName;
+  }
+};
+
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_LSP_OPTRUNNER_H
diff --git a/llvm/tools/llvm-lsp/Protocol.cpp b/llvm/tools/llvm-lsp/Protocol.cpp
new file mode 100644
index 0000000000000..ff56c063ddb2c
--- /dev/null
+++ b/llvm/tools/llvm-lsp/Protocol.cpp
@@ -0,0 +1,58 @@
+//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol.h"
+
+using namespace llvm;
+using namespace llvm::lsp;
+
+bool llvm::lsp::fromJSON(const llvm::json::Value &Value, GetCfgParams &Result,
+                         llvm::json::Path Path) {
+  llvm::json::ObjectMapper O(Value, Path);
+  return O && O.map("uri", Result.uri) &&
+         O.map("position", Result.position);
+}
+
+llvm::json::Value llvm::lsp::toJSON(const CFG &Value) {
+  return llvm::json::Object{{"uri", Value.uri},
+                            {"node_id", Value.node_id},
+                            {"function", Value.function}};
+}
+
+bool llvm::lsp::fromJSON(const llvm::json::Value &Value,
+                         BbLocationParams &Result, llvm::json::Path Path) {
+  llvm::json::ObjectMapper O(Value, Path);
+  return O && O.map("uri", Result.uri) && O.map("node_id", Result.node_id);
+}
+
+llvm::json::Value llvm::lsp::toJSON(const BbLocation &Value) {
+  return llvm::json::Object{{"uri", Value.uri}, {"range", Value.range}};
+}
+
+bool llvm::lsp::fromJSON(const llvm::json::Value &Value,
+                         GetPassListParams &Result, llvm::json::Path Path) {
+  llvm::json::ObjectMapper O(Value, Path);
+  return O && O.map("uri", Result.uri) && O.map("pipeline", Result.pipeline);
+}
+
+llvm::json::Value llvm::lsp::toJSON(const PassList &Value) {
+  return llvm::json::Object{
+      {"list", llvm::json::Array(Value.list)},
+      {"descriptions", llvm::json::Array(Value.descriptions)}};
+}
+
+bool llvm::lsp::fromJSON(const llvm::json::Value &Value,
+                         GetIRAfterPassParams &Result, llvm::json::Path Path) {
+  llvm::json::ObjectMapper O(Value, Path);
+  return O && O.map("uri", Result.uri) && O.map("pipeline", Result.pipeline) &&
+         O.map("passnumber", Result.passnumber);
+}
+
+llvm::json::Value llvm::lsp::toJSON(const IR &Value) {
+  return llvm::json::Object{{"uri", Value.uri}};
+}
diff --git a/llvm/tools/llvm-lsp/Protocol.h b/llvm/tools/llvm-lsp/Protocol.h
new file mode 100644
index 0000000000000..fe76f0318aaa3
--- /dev/null
+++ b/llvm/tools/llvm-lsp/Protocol.h
@@ -0,0 +1,102 @@
+//===--- Protocol.h - Language Server Protocol Implementation -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMLSP_PROTOCOL_H
+#define LLVM_TOOLS_LLVMLSP_PROTOCOL_H
+
+#include "llvm/Support/LSP/Protocol.h"
+
+// This file is using the LSP syntax for identifier names which is different
+// from the LLVM coding standard. To avoid the clang-tidy warnings, we're
+// disabling one check here.
+// NOLINTBEGIN(readability-identifier-naming)
+
+namespace llvm {
+namespace lsp {
+struct GetCfgParams {
+  URIForFile uri;
+  Position position;
+};
+
+/// Add support for JSON serialization.
+bool fromJSON(const llvm::json::Value &Value, GetCfgParams &Result,
+              llvm::json::Path Path);
+
+struct CFG {
+  URIForFile uri;
+  std::string node_id;
+  std::string function;
+};
+
+llvm::json::Value toJSON(const CFG &Value);
+
+struct BbLocationParams {
+  /// The URI of the SVG file containing the CFG.
+  URIForFile uri;
+  /// The ID of the node representing the basic block.
+  std::string node_id;
+};
+
+/// Add support for JSON serialization.
+bool fromJSON(const llvm::json::Value &Value, BbLocationParams &Result,
+              llvm::json::Path Path);
+
+struct BbLocation {
+  /// The URI of the `.ll` file containing the basic block.
+  URIForFile uri;
+  /// The range of the basic block corresponding to the node ID.
+  Range range;
+};
+
+llvm::json::Value toJSON(const BbLocation &Value);
+
+struct GetPassListParams {
+  /// The URI of the `.ll` file for which the pass list is requested.
+  URIForFile uri;
+  /// The optimization pipeline string, in the format passed to the `opt` tool.
+  std::string pipeline;
+};
+
+/// Add support for JSON serialization.
+bool fromJSON(const llvm::json::Value &Value, GetPassListParams &Result,
+              llvm::json::Path Path);
+
+struct PassList {
+  /// A list of passes in the pipeline, formatted as `<number>-<name>`.
+  std::vector<std::string> list;
+  /// A list of descriptions corresponding to each pass.
+  std::vector<std::string> descriptions;
+};
+
+llvm::json::Value toJSON(const PassList &Value);
+
+struct GetIRAfterPassParams {
+  /// The URI of the `.ll` file for which the intermediate IR is requested.
+  URIForFile uri;
+  /// The optimization pipeline string, in the format passed to the `opt` tool.
+  std::string pipeline;
+  /// The number of the pass in the pipeline after which to return the IR.
+  unsigned passnumber;
+};
+
+bool fromJSON(const llvm::json::Value &Value, GetIRAfterPassParams &Result,
+              llvm::json::Path Path);
+
+struct IR {
+  /// The URI of the `.ll` file containing the generated intermediate IR.
+  URIForFile uri;
+};
+
+llvm::json::Value toJSON(const IR &Value);
+
+} // namespace lsp
+} // namespace llvm
+
+// NOLINTEND(readability-identifier-naming)
+
+#endif // LLVM_TOOLS_LLVMLSP_PROTOCOL_H
diff --git a/llvm/tools/llvm-lsp/README.md b/llvm/tools/llvm-lsp/README.md
new file mode 100644
index 0000000000000..d956fcdd4c331
--- /dev/null
+++ b/llvm/tools/llvm-lsp/README.md
@@ -0,0 +1,183 @@
+# LLVM LSP server
+
+## Build
+
+```bash
+cmake -S llvm -B buildR -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
+ninja -j 6 -C buildR llvm-lsp-server
+```
+Or
+```bash
+cmake -S llvm -B buildRA -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
+ninja -j 6 -C buildRA llvm-lsp-server
+```
+
+## Features
+
+This LSP server is built to the Language Server Protocol Specification 3.17. It provides several standard and custom features to enhance the development experience.
+
+---
+
+### Standard Capabilities
+
+The server supports the following standard LSP capabilities:
+
+* `textDocumentSync.openClose`: Synchronizes document content with the server.
+* `referencesProvider`: Finds all references to a symbol.
+* `codeActionProvider`: Provides code actions, such as quick fixes and refactorings. This server uses it to provide CFG views.
+
+---
+
+### Custom Methods
+
+In addition to the standard capabilities, the server exposes several custom methods tailored for LLVM development.
+
+#### `llvm/getCfg`
+
+This method generates and returns an SVG representation of the Control Flow Graph (CFG) for the function at a specified position.
+
+**Parameters**
+
+```ts
+interface GetCfgParams {
+    /**
+     * The URI of the file for which the CFG is requested.
+     */
+    uri: string;
+    /**
+     * The cursor's position. The CFG is generated for the function where the cursor is located.
+     */
+    position: Position;
+}
+```
+
+**Response**
+
+```ts
+interface CFG {
+    /**
+     * URI of the SVG file containing the CFG.
+     */
+    uri: string;
+    /**
+     * The ID of the node corresponding to the basic block where the cursor was located.
+     */
+    node_id: string;
+    /**
+     * The name of the function for which the CFG was generated.
+     */
+    function: string;
+}
+```
+
+---
+
+#### `llvm/bbLocation`
+
+This method retrieves the location of a basic block within the source code, identified by its node ID from a generated CFG.
+
+**Parameters**
+
+```ts
+interface BbLocationParams {
+    /**
+     * The URI of the SVG file containing the CFG.
+     */
+    uri: string;
+    /**
+     * The ID of the node representing the basic block.
+     */
+    node_id: string;
+}
+```
+
+**Response**
+
+```ts
+interface BbLocation {
+    /**
+     * The URI of the `.ll` file containing the basic block.
+     */
+    uri: string;
+    /**
+     * The range of the basic block corresponding to the node ID.
+     */
+    range: Range;
+}
+```
+
+---
+
+#### `llvm/getPassList`
+
+This method returns a list of optimization passes that would be applied by a given optimization pipeline.
+
+**Parameters**
+
+```ts
+interface GetPassListParams {
+    /**
+     * The URI of the `.ll` file for which the pass list is requested.
+     */
+    uri: string;
+    /**
+     * The optimization pipeline string, in the format passed to the `opt` tool.
+     */
+    pipeline: string;
+}
+```
+
+**Response**
+
+```ts
+interface PassList {
+    /**
+     * A list of passes in the pipeline, formatted as `<number>-<name>`.
+     */
+    list: string[];
+    /**
+     * A list of descriptions corresponding to each pass.
+     */
+    descriptions: string[];
+    /**
+     * A status indicator for the request.
+     */
+    status: string = "success";
+}
+```
+
+---
+
+#### `llvm/getIRAfterPass`
+
+This method retrieves the Intermediate Representation (IR) of the code after a specific optimization pass in a pipeline has been applied.
+
+**Parameters**
+
+```ts
+interface GetIRAfterPassParams {
+    /**
+     * The URI of the `.ll` file for which the intermediate IR is requested.
+     */
+    uri: string;
+    /**
+     * The optimization pipeline string, in the format passed to the `opt` tool.
+     */
+    pipeline: string;
+    /**
+     * The number of the pass in the pipeline after which to return the IR.
+     */
+    passnumber: uinteger;
+}
+```
+
+**Response**
+
+```ts
+interface IR {
+    /**
+     * The URI of the `.ll` file containing the generated intermediate IR.
+     */
+    uri: string;
+}
+```
diff --git a/llvm/tools/llvm-lsp/llvm-lsp-server.cpp b/llvm/tools/llvm-lsp/llvm-lsp-server.cpp
new file mode 100755
index 0000000000000..2c5355bb0ebea
--- /dev/null
+++ b/llvm/tools/llvm-lsp/llvm-lsp-server.cpp
@@ -0,0 +1,379 @@
+//===-- llvm-lsp-server.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Program.h"
+
+#include "IRDocument.h"
+#include "Protocol.h"
+#include "llvm-lsp-server.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace llvm;
+
+static cl::OptionCategory LlvmLspServerCategory("llvm-lsp-server options");
+static cl::opt<std::string> LogFilePath("log-file",
+                                        cl::desc("Path to log file"),
+                                        cl::init("/tmp/llvm-lsp-server.log"),
+                                        cl::cat(LlvmLspServerCategory));
+
+static lsp::Position llvmFileLocToLspPosition(const FileLoc &Pos) {
+  return lsp::Position(Pos.Line, Pos.Col);
+}
+
+static lsp::Range llvmFileLocRangeToLspRange(const FileLocRange &Range) {
+  return lsp::Range(llvmFileLocToLspPosition(Range.Start),
+                    llvmFileLocToLspPosition(Range.End));
+}
+
+llvm::Error LspServer::run() {
+  registerMessageHandlers();
+  return Transport.run(MessageHandler);
+}
+
+void LspServer::sendInfo(const std::string &Message) {
+  ShowMessageSender(lsp::ShowMessageParams(lsp::MessageType::Info, Message));
+}
+
+void LspServer::sendError(const std::string &Message) {
+  ShowMessageSender(lsp::ShowMessageParams(lsp::MessageType::Error, Message));
+}
+
+void LspServer::handleRequestInitialize(
+    const lsp::InitializeParams &Params,
+    lsp::Callback<llvm::json::Value> Reply) {
+  lsp::Logger::info("Received Initialize Message!");
+  sendInfo("Hello! Welcome to LLVM IR Language Server!");
+
+  // clang-format off
+  json::Object ResponseParams{
+    {"capabilities",
+      json::Object{
+          {"textDocumentSync",
+          json::Object{
+              {"openClose", true},
+              {"change", 0}, // We dont want to sync the documents.
+          }
+        },
+        {"referencesProvider", true},
+        {"codeActionProvider", true},
+        {"documentSymbolProvider", true},
+      }
+    }
+  };
+  // clang-format on
+  Reply(json::Value(std::move(ResponseParams)));
+}
+
+void LspServer::handleNotificationTextDocumentDidOpen(
+    const lsp::DidOpenTextDocumentParams &Params) {
+  lsp::Logger::info("Received didOpen Message!");
+  StringRef Filepath = Params.textDocument.uri.file();
+  sendInfo("LLVM Language Server Recognized that you opened " + Filepath.str());
+
+  // Prepare IRDocument for Queries
+  lsp::Logger::info("Creating IRDocument for {}", Filepath.str());
+  OpenDocuments[Filepath.str()] = std::make_unique<IRDocument>(Filepath.str());
+}
+
+void LspServer::handleRequestGetReferences(
+    const lsp::ReferenceParams &Params,
+    lsp::Callback<std::vector<lsp::Location>> Reply) {
+  auto Filepath = Params.textDocument.uri.file();
+  auto Line = Params.position.line;
+  auto Character = Params.position.character;
+  assert(Line >= 0);
+  assert(Character >= 0);
+  std::stringstream SS;
+  std::vector<lsp::Location> Result;
+  const auto &Doc = OpenDocuments[Filepath.str()];
+  if (Instruction *MaybeI = Doc->getInstructionAtLocation(Line, Character)) {
+    auto TryAddReference = [&Result, &Params, &Doc](Instruction *I) {
+      // FIXME: very hacky way to remove the newline from the reference...
+      //   we need to have the parser set the proper end
+      auto MaybeInstLocation = Doc->ParserContext.getInstructionLocation(I);
+      if (!MaybeInstLocation)
+        return;
+      Result.emplace_back(
+          lsp::Location(Params.textDocument.uri,
+                        llvmFileLocRangeToLspRange(MaybeInstLocation.value())));
+    };
+    TryAddReference(MaybeI);
+    for (User *U : MaybeI->users()) {
+      if (auto *UserInst = dyn_cast<Instruction>(U)) {
+        TryAddReference(UserInst);
+      }
+    }
+  }
+
+  Reply(std::move(Result));
+}
+
+void LspServer::handleRequestTextDocumentDocumentSymbol(
+    const lsp::DocumentSymbolParams &Params,
+    lsp::Callback<std::vector<lsp::DocumentSymbol>> Reply) {
+  if (!OpenDocuments.contains(Params.textDocument.uri.file().str())) {
+    lsp::Logger::error(
+        "Document in textDocument/documentSymbol request not open: {}",
+        Params.textDocument.uri.file());
+    return Reply(
+        make_error<lsp::LSPError>(formatv("Did not open file previously {}",
+                                          Params.textDocument.uri.file()),
+                                  lsp::ErrorCode::InvalidParams));
+  }
+  auto &Doc = OpenDocuments[Params.textDocument.uri.file().str()];
+  std::vector<lsp::DocumentSymbol> Result;
+  for (const auto &Fn : Doc->getFunctions()) {
+    lsp::DocumentSymbol Func;
+    Func.name = Fn.getNameOrAsOperand();
+    Func.kind = lsp::SymbolKind::Function;
+    auto MaybeLoc = Doc->ParserContext.getFunctionLocation(&Fn);
+    if (!MaybeLoc)
+      continue;
+    Func.range = llvmFileLocRangeToLspRange(*MaybeLoc);
+    Func.selectionRange = Func.range;
+    for (const auto &BB : Fn) {
+      lsp::DocumentSymbol Block;
+      Block.name = BB.getNameOrAsOperand();
+      Block.kind =
+          lsp::SymbolKind::Namespace; // Using namespace as there is no block
+                                      // kind, and namespace is the closest
+      Block.detail = "basic block";
+      auto MaybeLoc = Doc->ParserContext.getBlockLocation(&BB);
+      if (!MaybeLoc)
+        continue;
+      Block.range = llvmFileLocRangeToLspRange(*MaybeLoc);
+      Block.selectionRange = Block.range;
+      for (const auto &I : BB) {
+        lsp::DocumentSymbol Inst;
+        Inst.name = I.getNameOrAsOperand();
+        Inst.kind = lsp::SymbolKind::Variable;
+        {
+          raw_string_ostream Ss(Inst.detail);
+          I.print(Ss);
+        }
+        auto MaybeLoc = Doc->ParserContext.getInstructionLocation(&I);
+        if (!MaybeLoc)
+          continue;
+        Inst.range = llvmFileLocRangeToLspRange(*MaybeLoc);
+        Inst.selectionRange = Inst.range;
+        Block.children.emplace_back(std::move(Inst));
+      }
+      Func.children.emplace_back(std::move(Block));
+    }
+    Result.emplace_back(std::move(Func));
+  }
+  Reply(std::move(Result));
+}
+
+void LspServer::handleRequestCodeAction(const lsp::CodeActionParams &Params,
+                                        lsp::Callback<json::Value> Reply) {
+  Reply(json::Array{
+      json::Object{{"title", "Open CFG Preview"}, {"command", "llvm.cfg"}}});
+}
+
+void LspServer::handleRequestGetCFG(const lsp::GetCfgParams &Params,
+                                    lsp::Callback<lsp::CFG> Reply) {
+  // TODO: have a flag to force regenerating the artifacts
+  std::string Filepath = Params.uri.file().str();
+  auto Line = Params.position.line;
+  auto Character = Params.position.character;
+
+  for (const auto &[K, _] : OpenDocuments) {
+    lsp::Logger::debug("OpenDocuments: {}", K);
+  }
+  if (OpenDocuments.find(Filepath) == OpenDocuments.end()) {
+    lsp::Logger::error("Did not open file previously {}", Filepath);
+    return Reply(make_error<lsp::LSPError>(
+        formatv("Did not open file previously {}", Filepath),
+        lsp::ErrorCode::InvalidParams));
+  }
+  IRDocument &Doc = *OpenDocuments[Filepath];
+
+  Function *F = nullptr;
+  BasicBlock *BB = nullptr;
+  if (Instruction *MaybeI =
+          OpenDocuments[Filepath]->getInstructionAtLocation(Line, Character)) {
+    BB = MaybeI->getParent();
+    F = BB->getParent();
+  } else {
+    F = Doc.getFirstFunction();
+    BB = &F->getEntryBlock();
+  }
+
+  auto PathOpt = Doc.getPathForSVGFile(F);
+  if (!PathOpt)
+    lsp::Logger::info("Did not find Path for SVG file for {}", Filepath);
+
+  lsp::CFG Result;
+  auto MaybeURI = lsp::URIForFile::fromFile(*PathOpt);
+  if (!MaybeURI) {
+    Reply(MaybeURI.takeError());
+    return;
+  }
+  Result.uri = *MaybeURI;
+  Result.node_id = Doc.getNodeId(Doc.getBlockAtLocation(Line, Character));
+  Result.function = F->getName();
+
+  Reply(Result);
+
+  SVGToIRMap[*PathOpt] = Filepath;
+}
+
+void LspServer::handleRequestBBLocation(const lsp::BbLocationParams &Params,
+                                        lsp::Callback<lsp::BbLocation> Reply) {
+  auto Filepath = Params.uri.file();
+  auto NodeIDStr = Params.node_id;
+
+  // We assume the query to SVGToIRMap would not fail.
+  auto IR = SVGToIRMap[Filepath.str()];
+  IRDocument &Doc = *OpenDocuments[IR];
+  lsp::BbLocation Result;
+  Result.range = llvmFileLocRangeToLspRange(Doc.parseNodeId(NodeIDStr));
+  auto MaybeURI = lsp::URIForFile::fromFile(IR);
+  if (!MaybeURI)
+    return Reply(MaybeURI.takeError());
+  Result.uri = *MaybeURI;
+  return Reply(Result);
+}
+
+void LspServer::handleRequestGetPassList(const lsp::GetPassListParams &Params,
+                                         lsp::Callback<lsp::PassList> Reply) {
+
+  StringRef Filepath = Params.uri.file();
+  std::string Pipeline = Params.pipeline;
+
+  if (OpenDocuments.find(Filepath.str()) == OpenDocuments.end()) {
+    lsp::Logger::error("Did not open file previously {}", Filepath.str());
+    return Reply(make_error<lsp::LSPError>(
+        formatv("Did not open file previously {}", Filepath.str()),
+        lsp::ErrorCode::InvalidParams));
+  }
+  IRDocument &Doc = *OpenDocuments[Filepath.str()];
+
+  lsp::Logger::info("Opened IR file to get pass list {}", Filepath.str());
+
+  auto PassListResult = Doc.getPassList(Pipeline);
+
+  if (!PassListResult) {
+    return Reply(PassListResult.takeError());
+  }
+
+  auto PassList = PassListResult.get();
+
+  auto PassDescriptionsResult = Doc.getPassDescriptions(Pipeline);
+
+  if (!PassDescriptionsResult) {
+    return Reply(PassDescriptionsResult.takeError());
+  }
+
+  auto PassDescriptions = PassDescriptionsResult.get();
+
+  if (PassList.size() != PassDescriptions.size()) {
+    lsp::Logger::error("Size mismatch between the objects!");
+    return Reply(make_error<lsp::LSPError>("Size mismatch between the objects!",
+                                           lsp::ErrorCode::InvalidParams));
+  }
+
+  // Build the response object
+  lsp::PassList ResponseParams;
+  ResponseParams.list.insert(ResponseParams.list.begin(), PassList.begin(),
+                             PassList.end());
+  ResponseParams.descriptions.insert(ResponseParams.descriptions.begin(),
+                                     PassDescriptions.begin(),
+                                     PassDescriptions.end());
+
+  Reply(ResponseParams);
+}
+
+void LspServer::handleRequestGetIRAfterPass(
+    const lsp::GetIRAfterPassParams &Params, lsp::Callback<lsp::IR> Reply) {
+  StringRef Filepath = Params.uri.file();
+  std::string Pipeline = Params.pipeline;
+
+  if (OpenDocuments.find(Filepath.str()) == OpenDocuments.end()) {
+    lsp::Logger::error("Did not open file previously {}", Filepath.str());
+    return Reply(make_error<lsp::LSPError>(
+        formatv("Did not open file previously {}", Filepath.str()),
+        lsp::ErrorCode::InvalidParams));
+  }
+  IRDocument &Doc = *OpenDocuments[Filepath.str()];
+
+  unsigned PassNum = Params.passnumber;
+  auto IRFilePathResult = Doc.getIRAfterPassNumber(Pipeline, PassNum);
+
+  if (!IRFilePathResult) {
+    return Reply(IRFilePathResult.takeError());
+  }
+
+  auto IRFilePath = IRFilePathResult.get();
+
+  if (auto MaybeIRUri = lsp::URIForFile::fromFile(IRFilePath)) {
+    lsp::IR Return;
+    Return.uri = *MaybeIRUri;
+    Reply(Return);
+  } else {
+    return Reply(MaybeIRUri.takeError());
+  }
+
+  return;
+}
+
+bool LspServer::registerMessageHandlers() {
+  MessageHandler.method("initialize", this,
+                        &LspServer::handleRequestInitialize);
+
+  MessageHandler.notification(
+      "textDocument/didOpen", this,
+      &LspServer::handleNotificationTextDocumentDidOpen);
+  MessageHandler.method("textDocument/references", this,
+                        &LspServer::handleRequestGetReferences);
+  MessageHandler.method("textDocument/documentSymbol", this,
+                        &LspServer::handleRequestTextDocumentDocumentSymbol);
+  MessageHandler.method("textDocument/codeAction", this,
+                        &LspServer::handleRequestCodeAction);
+  // Custom messages
+  MessageHandler.method("llvm/getCfg", this, &LspServer::handleRequestGetCFG);
+  MessageHandler.method("llvm/bbLocation", this,
+                        &LspServer::handleRequestBBLocation);
+  MessageHandler.method("llvm/getPassList", this,
+                        &LspServer::handleRequestGetPassList);
+  MessageHandler.method("llvm/getIRAfterPass", this,
+                        &LspServer::handleRequestGetIRAfterPass);
+
+  ShowMessageSender =
+      MessageHandler.outgoingNotification<lsp::ShowMessageParams>(
+          "window/showMessage");
+
+  // Return true to indicate handlers were registered successfully
+  return true;
+}
+
+int main(int argc, char **argv) {
+  cl::HideUnrelatedOptions(LlvmLspServerCategory);
+  cl::ParseCommandLineOptions(argc, argv, "LLVM LSP Language Server");
+
+  llvm::sys::ChangeStdinToBinary();
+  lsp::JSONTransport Transport(stdin, llvm::outs());
+
+  LspServer LS(Transport);
+
+  lsp::Logger::setLogLevel(lsp::Logger::Level::Debug);
+
+  auto LSResult = LS.run();
+  if (!LSResult)
+    lsp::Logger::error("Error while running Language Server: {}", LSResult);
+
+  return LS.getExitCode();
+}
diff --git a/llvm/tools/llvm-lsp/llvm-lsp-server.h b/llvm/tools/llvm-lsp/llvm-lsp-server.h
new file mode 100644
index 0000000000000..89b5df95189c0
--- /dev/null
+++ b/llvm/tools/llvm-lsp/llvm-lsp-server.h
@@ -0,0 +1,127 @@
+//===-- llvm-lsp-server.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_LSP_SERVER_H
+#define LLVM_TOOLS_LLVM_LSP_SERVER_H
+
+#include <sstream>
+
+#include "IRDocument.h"
+#include "Protocol.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/LSP/Transport.h"
+
+namespace llvm {
+
+class LspServer {
+  lsp::MessageHandler MessageHandler;
+
+  enum class LspServerState {
+    Starting,
+    Initializing,
+    Ready,
+    ShuttingDown, // Received 'shutdown' message
+    Exitted,      // Received 'exit' message
+  } State = LspServerState::Starting;
+
+  std::string stateToString(LspServerState S) {
+    switch (S) {
+    case LspServerState::Starting:
+      return "Starting";
+    case LspServerState::Initializing:
+      return "Initializing";
+    case LspServerState::Ready:
+      return "Ready";
+    case LspServerState::ShuttingDown:
+      return "ShuttingDown";
+    case LspServerState::Exitted:
+      return "Exitted";
+    }
+    return "<UNKNOWN STATE>";
+  }
+
+  void switchToState(LspServerState NewState) {
+    std::stringstream SS;
+    SS << "Changing State from " << stateToString(State) << " to "
+       << stateToString(NewState);
+    lsp::Logger::info("{}", SS.str());
+    State = NewState;
+  }
+
+  std::unordered_map<std::string, std::unique_ptr<IRDocument>> OpenDocuments;
+  std::unordered_map<std::string, std::string> SVGToIRMap;
+
+public:
+  LspServer(lsp::JSONTransport &Transport)
+      : MessageHandler(Transport), Transport(Transport) {
+    lsp::Logger::info("Starting LLVM LSP Server");
+  }
+
+  // Runs LSP server
+  llvm::Error run();
+
+  // Sends a message to client as INFO notification
+  void sendInfo(const std::string &Message);
+
+  // Sends a message to client as ERROR notification
+  void sendError(const std::string &Message);
+
+  // The process exit code, should be success only if the State is Exitted
+  int getExitCode() { return State == LspServerState::Exitted ? 0 : 1; }
+
+private:
+  // ---------- Functions to handle various RPC calls -----------------------
+
+  // initialize
+  void handleRequestInitialize(const lsp::InitializeParams &Params,
+                               lsp::Callback<llvm::json::Value> Reply);
+  // textDocument/didOpen
+  void handleNotificationTextDocumentDidOpen(
+      const lsp::DidOpenTextDocumentParams &Params);
+
+  // textDocument/references
+  void
+  handleRequestGetReferences(const lsp::ReferenceParams &Params,
+                             lsp::Callback<std::vector<lsp::Location>> Reply);
+
+  // textDocument/documentSymbol
+  void handleRequestTextDocumentDocumentSymbol(
+      const lsp::DocumentSymbolParams &Params,
+      lsp::Callback<std::vector<lsp::DocumentSymbol>> Reply);
+
+  // textDocument/codeAction
+  void handleRequestCodeAction(const lsp::CodeActionParams &Params,
+                               lsp::Callback<json::Value> Reply);
+
+  // llvm/getCfg
+  void handleRequestGetCFG(const lsp::GetCfgParams &Params,
+                           lsp::Callback<lsp::CFG> Reply);
+
+  // llvm/bbLocation
+  void handleRequestBBLocation(const lsp::BbLocationParams &Params,
+                               lsp::Callback<lsp::BbLocation> Reply);
+
+  // llvm/getPassList
+  void handleRequestGetPassList(const lsp::GetPassListParams &Params,
+                                lsp::Callback<lsp::PassList> Reply);
+
+  // llvm/getIRAfterPAss
+  void handleRequestGetIRAfterPass(const lsp::GetIRAfterPassParams &Params,
+                                   lsp::Callback<lsp::IR> Reply);
+
+  // Identifies RPC Call and dispatches the handling to other methods
+  bool registerMessageHandlers();
+
+  lsp::OutgoingNotification<lsp::ShowMessageParams> ShowMessageSender;
+
+  lsp::JSONTransport &Transport;
+};
+
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_LSP_SERVER_H

>From d870571c30ea96fe6b8523fe41c788905e6de0e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Wed, 1 Oct 2025 10:52:07 +0000
Subject: [PATCH 18/20] Update LLVMBuild.txt in accordance to CMakeLists.txt

---
 llvm/tools/llvm-lsp/LLVMBuild.txt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/tools/llvm-lsp/LLVMBuild.txt b/llvm/tools/llvm-lsp/LLVMBuild.txt
index c686e51e2a2e6..c29d0099b6b07 100644
--- a/llvm/tools/llvm-lsp/LLVMBuild.txt
+++ b/llvm/tools/llvm-lsp/LLVMBuild.txt
@@ -24,3 +24,6 @@ required_libraries =
  Support
  Analysis
  Passes
+ SupportLSP
+ TransformUtils
+ AsmParser

>From 53bf014c07b897d3ffc4525148b77a010e241073 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Wed, 1 Oct 2025 11:03:39 +0000
Subject: [PATCH 19/20] Update LSP capabilities in README

---
 llvm/tools/llvm-lsp/README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/tools/llvm-lsp/README.md b/llvm/tools/llvm-lsp/README.md
index d956fcdd4c331..0753bf7549372 100644
--- a/llvm/tools/llvm-lsp/README.md
+++ b/llvm/tools/llvm-lsp/README.md
@@ -25,6 +25,7 @@ The server supports the following standard LSP capabilities:
 * `textDocumentSync.openClose`: Synchronizes document content with the server.
 * `referencesProvider`: Finds all references to a symbol.
 * `codeActionProvider`: Provides code actions, such as quick fixes and refactorings. This server uses it to provide CFG views.
+* `documentsSymbolProvider`: Provides a tree of document symbols, enables breadcrums navigation in the editor.
 
 ---
 

>From 5aef2dbc255162c7e06e850f2b2110eb30e80bab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <ahavlicek at azul.com>
Date: Wed, 8 Oct 2025 07:57:42 +0000
Subject: [PATCH 20/20] Fix clang-format

---
 llvm/include/llvm/Analysis/CFGPrinter.h |  7 ++-
 llvm/tools/llvm-lsp/OptRunner.h         | 62 ++++++++++++-------------
 llvm/tools/llvm-lsp/Protocol.cpp        |  3 +-
 3 files changed, 35 insertions(+), 37 deletions(-)

diff --git a/llvm/include/llvm/Analysis/CFGPrinter.h b/llvm/include/llvm/Analysis/CFGPrinter.h
index c492ab8be1271..8d284e0cf669c 100644
--- a/llvm/include/llvm/Analysis/CFGPrinter.h
+++ b/llvm/include/llvm/Analysis/CFGPrinter.h
@@ -77,8 +77,7 @@ class DOTFuncInfo {
   std::optional<NodeIdFormatterTy> NodeIdFormatter;
 
 public:
-  DOTFuncInfo(const Function *F)
-      : DOTFuncInfo(F, nullptr, nullptr, 0) {}
+  DOTFuncInfo(const Function *F) : DOTFuncInfo(F, nullptr, nullptr, 0) {}
   LLVM_ABI ~DOTFuncInfo();
 
   LLVM_ABI
@@ -338,8 +337,8 @@ struct DOTGraphTraits<DOTFuncInfo *> : public DefaultDOTGraphTraits {
                                   : (getHeatColor(1));
       if (!Attrs.str().empty())
         Attrs << ",";
-      Attrs << "color=\"" << EdgeColor << "ff\",style=filled,"
-            << "fillcolor=\"" << Color << "70\"" << "fontname=\"Courier\"";
+      Attrs << "color=\"" << EdgeColor << "ff\",style=filled," << "fillcolor=\""
+            << Color << "70\"" << "fontname=\"Courier\"";
     }
 
     return Attrs.str();
diff --git a/llvm/tools/llvm-lsp/OptRunner.h b/llvm/tools/llvm-lsp/OptRunner.h
index b4c1da37cb665..e6d0cb8394acf 100644
--- a/llvm/tools/llvm-lsp/OptRunner.h
+++ b/llvm/tools/llvm-lsp/OptRunner.h
@@ -42,37 +42,37 @@ class OptRunner {
     unsigned PassNumber = 0;
     // FIXME: Should we only consider passes that modify the IR?
     std::function<void(const StringRef, Any, const PreservedAnalyses)>
-        RecordPassNamesAndDescription = [&PassListAndDescription, &PassNumber](
-                                            const StringRef PassName, Any IR,
-                                            const PreservedAnalyses &PA) {
-          PassNumber++;
-          std::string PassNameStr =
-              (std::to_string(PassNumber) + "-" + PassName.str());
-          std::string PassDescStr = [&IR, &PassName]() -> std::string {
-            if (auto *M = any_cast<const Module *>(&IR))
-              return "Module Pass on \"" + (**M).getName().str() + "\"";
-            if (auto *F = any_cast<const Function *>(&IR))
-              return "Function Pass on \"" + (**F).getName().str() + "\"";
-            if (auto *L = any_cast<const Loop *>(&IR)) {
-              Function *F = (*L)->getHeader()->getParent();
-              std::string Desc = "Loop Pass in Function \"" +
-                                 F->getName().str() +
-                                 "\" on loop with Header \"" +
-                                 (*L)->getHeader()->getName().str() + "\"";
-              return Desc;
-            }
-            if (auto *SCC = any_cast<const LazyCallGraph::SCC *>(&IR)) {
-              Function &F = (**SCC).begin()->getFunction();
-              std::string Desc =
-                  "CGSCC Pass on Function \"" + F.getName().str() + "\"";
-              return Desc;
-            }
-            lsp::Logger::error("Unknown Pass Type \"{}\"!", PassName.str());
-            return "";
-          }();
-
-          PassListAndDescription.push_back({PassNameStr, PassDescStr});
-        };
+        RecordPassNamesAndDescription =
+            [&PassListAndDescription, &PassNumber](
+                const StringRef PassName, Any IR, const PreservedAnalyses &PA) {
+              PassNumber++;
+              std::string PassNameStr =
+                  (std::to_string(PassNumber) + "-" + PassName.str());
+              std::string PassDescStr = [&IR, &PassName]() -> std::string {
+                if (auto *M = any_cast<const Module *>(&IR))
+                  return "Module Pass on \"" + (**M).getName().str() + "\"";
+                if (auto *F = any_cast<const Function *>(&IR))
+                  return "Function Pass on \"" + (**F).getName().str() + "\"";
+                if (auto *L = any_cast<const Loop *>(&IR)) {
+                  Function *F = (*L)->getHeader()->getParent();
+                  std::string Desc = "Loop Pass in Function \"" +
+                                     F->getName().str() +
+                                     "\" on loop with Header \"" +
+                                     (*L)->getHeader()->getName().str() + "\"";
+                  return Desc;
+                }
+                if (auto *SCC = any_cast<const LazyCallGraph::SCC *>(&IR)) {
+                  Function &F = (**SCC).begin()->getFunction();
+                  std::string Desc =
+                      "CGSCC Pass on Function \"" + F.getName().str() + "\"";
+                  return Desc;
+                }
+                lsp::Logger::error("Unknown Pass Type \"{}\"!", PassName.str());
+                return "";
+              }();
+
+              PassListAndDescription.push_back({PassNameStr, PassDescStr});
+            };
 
     auto RunOptResult = runOpt(PipelineText, RecordPassNamesAndDescription);
     if (!RunOptResult) {
diff --git a/llvm/tools/llvm-lsp/Protocol.cpp b/llvm/tools/llvm-lsp/Protocol.cpp
index ff56c063ddb2c..f7858ea5845df 100644
--- a/llvm/tools/llvm-lsp/Protocol.cpp
+++ b/llvm/tools/llvm-lsp/Protocol.cpp
@@ -14,8 +14,7 @@ using namespace llvm::lsp;
 bool llvm::lsp::fromJSON(const llvm::json::Value &Value, GetCfgParams &Result,
                          llvm::json::Path Path) {
   llvm::json::ObjectMapper O(Value, Path);
-  return O && O.map("uri", Result.uri) &&
-         O.map("position", Result.position);
+  return O && O.map("uri", Result.uri) && O.map("position", Result.position);
 }
 
 llvm::json::Value llvm::lsp::toJSON(const CFG &Value) {



More information about the llvm-commits mailing list