[RFC] IR Visualization with VS Code Extension Using an LSP Server

LLVM IR can be difficult to read, especially for large compilations. Internally, we have been using a visualizer tool that provides interactive views of IR, CFG, various analyses, and pass pipelines. This is useful for debugging, analysis of optimization passes (why something happens or doesn’t), looking for optimization opportunities, and interactive exploration (what happens when I run pass X here? And then pass Y on the result, etc.)

We want to upstream the capabilities of our internal IR visualizer as part of the LLVM VS Code extension. LLVM currently provides a minimal extension. We’d like to integrate selected visualization features into it. To enable communication between VS Code and LLVM, we plan to introduce an LSP server for LLVM IR.

The enhanced extension will support the following:

  • Provide common LSP functionality (navigation, reference tracking, etc.)

  • Display the CFG and IR side by side with cross-navigation

  • Run any optimization pass or analysis on the given IR and display the results

  • Run a custom optimization pipeline on the given IR

  • Support large compilations

    • E.g. do lazy evaluation of IRs at different pipeline stages. Doing print-after-all for large compilations can take a really long time.
  • Run entirely offline and locally

LSP

We plan to use LSP as a protocol to enable interaction between the VS Code extension and LLVM. LSP provides a lot of useful functionality “out-of-the-box” - navigation between symbols, tracking value uses, etc. Additionally, we can use LSP to run custom commands for the extension. For example, we are going to use it to request SVG files of the CFG from LLVM.

An alternative approach would be to invoke LLVM tools (e.g., opt) directly with command-line arguments. This is how our internal visualizer interacts with LLVM. It works for simple requests, but has limitations for more complicated queries. This approach requires exposing all the necessary functionality via a command-line interface. This gets cumbersome for complicated queries, such as inspecting what a specific analysis understands about an SSA value at a given instruction.

Integration Plan

  • LSP server for LLVM IR

    • Add support for tracking source location for LLVM IR constructions (Values, BasicBlocks, Functions). This is a prerequisite for many of the LSP features.

    • Add a simple LSP server as a new tool in LLVM

      • Support some basic LSP commands

      • Add a custom command to generate a CFG for a given IR

      • Add a custom command to run an arbitrary opt pass for a given IR

  • Extend the existing LLVM VS Code extension

    • Connect to the LSP server and enable some basic LSP functionality

    • Introduce a command to request the CFG from the LSP server

    • Introduce cross-navigation between the CFG and IR

    • Introduce a command to run a custom opt pass/opt pipeline

    • Highlight changes between optimization passes

Future work

Value Analysis

  • Show where a value originates and how it propagates through the IR.

  • Support interactive queries on a specific SSA value (e.g., which passes modified it, what analyses know about it).

Graph Visualization

  • Add dominator tree (DT) visualization similar to the existing CFG view.

  • Provide simplified CFG/DT views that omit instruction detail (block-only visualization).

  • Show visual diffs in the CFG view between optimization passes (e.g., added/removed edges or blocks).

Demo

The initial prototype of the proposed LSP server and the extended VS Code extension can be found here: GitHub - JanJecmen/llvm-project at vscode-irviz

The LSP server is located in llvm/tools/llvm-lsp and the VS Code extension in llvm/utils/vscode/llvm.

Easiest way to run the extension is to open src/extension.ts in VS Code and press F5. The compile target for the LSP server is llvm-lsp-server.

Screenshots

5 Likes

Link is not working, so I think you should add space between link and “The”.

Thanks, fixed

Sounds like a great idea! I support this RFC. (But note that I never touched the LLVM extension, so take my vote with a salt of grain).

I have been thinking about writing an LLVM-IR LSP server myself already for a while. In fact, I did write a LSP for a very similar, SSA-based, LLVM-inspired language the other day: GitHub - salesforce-misc/hyper-ir-lsp

A couple of functionalities which I found useful in my language server:

  • The control flow graph:
    • Triggered using a CodeLens action (in contrast to your screenshot which seems to use a Code Action?)
    • Relies on the “Graphviz Interactive Preview” - is your VSCode extension packaging its own graphviz rendering, or are you also relying on an existing GraphViz rendering extension?
  • Renaming / renumbering
    • of variable names, function names, etc.
    • In particular, if the new name starts with + or -, this is interpreted as a “renumbering request”. E.g., if I rename %x.5 to +6, then %x.5 will be renamed to %x.11, %6 will be renamed to %12 and so on.
    • Those renumberings turned out to be particularly useful when comparing two IR dumps side-by-side, to eliminate noisy diffs due to different variable numbering

Not sure if that’s useful - maybe you might also find that additional functionality useful :slight_smile:

The current prototype generates a SVG in the LSP server and the VS Code extension just displays it and facilitates navigation. With our current future plans, I think that rendering will have to be packaged into the LLVM extension.

Yeah, that might be a better workflow.

Our current roadmap is implement everything our internal tool does, while adding the possibility to diff two IRs side by side without the need of human intervention. So I hope we won’t need the renumbering functionality. But we’ll keep that in mind as a potential nice to have.

Great idea! I’m not too well versed in LSPs, but could the LSP server integrate with other editors, such as Neovim?

Yes, the LSP server should support standard LSP messages. So it should be plug and play for most IDEs for standard LSP features. The only issue would be with most of the IR visualization, as that will have to be implemented as a custom message and thus require additional support.

2 Likes

There’s precedence in LLVM already with an MLIR and TableGen LSP server already (as well as a CIR LSP server built on top of MLIR). I see no reason why you couldn’t just put up a PR for that component. cc @River707 as the author of MLIR/TableGen’s LSP server.

That brings me to a question. MLIR has several LSP servers, they share a common JRPC transport layer in lsp-server-support, afaik these files have mostly been taken from clangd. We plan on using this existing framework as well. I’d prefer not to copy those files into the LLVM IR LSP server, as that would mean a third copy of them in the monorepo. Including them doesn’t seem to be an option, as that would make LLVM depend on MLIR.

Would you be interested in moving this into LLVM? Since MLIR already depends on LLVM this shouldn’t be such an issue.

cc @mehdi_amini @modocache as you seem to be active MLIR LSP contributors

If you’re referring to factoring out lsp-server-support from clangd and moving it into LLVM, I think it will be useful and I am supportive of that.

1 Like

Yes, move the common code, that handles the LSP JRPC from clangd and MLIR into LLVM, so that it can be used by anyone.

1 Like