IDE configuration
heir-lsp
HEIR provides an LSP server that extends the MLIR LSP server with HEIR’s dialects.
Build the LSP binary, then move it to a location on your path or point your IDE
to bazel-bin/tools/heir-lsp
.
bazel build //tools:heir-lsp
cp bazel-bin/tools/heir-lsp /usr/local/bin
Note that if you change any HEIR dialects, or if HEIR’s dependency on MLIR
updates and the upstream MLIR has dialect changes (which happens roughly daily),
you need to rebuild heir-lsp
for it to recognize the changes.
clangd
Most IDE configured to use clangd can be powered from a file called
compile_commands.json
. To generate that for HEIR, run
bazel run @hedron_compile_commands//:refresh_all
This will need to be regenerated when there are major BUILD
file changes. If
you encounter errors like *.h.inc
not found, or syntax errors inside these
files, you may need to build those targets and then re-run the refresh_all
command above.
ibazel file watcher
ibazel
is a shell around
bazel
that watches a build target for file changes and automatically rebuilds.
ibazel build //tools:heir-opt
VS Code
While a wide variety of IDEs and editors can be used for HEIR development, we currently only provide support for VSCode.
Setup
For the best experience, we recommend following these steps:
Install and rename Buildifier:
You can download the latest Buildifier release, e.g., for linux-amd64 (see the Bazel Release Page for a list of available binaries):
wget -c https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier-linux-amd64 mv buildifier-linux-amd64 buildifier chmod +x buildifier
Just as with bazel, you will want to move this somewhere on your PATH, e.g.:
mkdir -p ~/bin echo 'export PATH=$PATH:~/bin' >> ~/.bashrc mv buildifier ~/bin/buildifier
VS Code should automatically detect buildifier. If this is not successful, you can manually set the “Buildifier Executable” setting for the Bazel extension (
bazel.buildifierExecutable
).Disable the C/C++ (aka ‘cpptools’) extension (either completely, or in the current workspace).
Add the following snippet to your VS Code user settings found in .vscode/settings.json to enable autocomplete based on the compile_commands.json file (see above).
"clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/", "--completion-style=detailed", "--query-driver=**" ],
It might be necessary to add the path to your buildifier to VSCode, though it should be auto-detected.
- Open the heir folder in VSCode
- Go to ‘Settings’ and set it on the ‘Workspace’
- Search for “Bazel Buildifier Executable”
- Once you find it, write
[home-directory]/bin/buildifier
for your specific [home-directory].
Building, Testing, Running and Debugging with VSCode
Building
- Open the “Explorer” (File Overview) in the left panel.
- Find “Bazel Build Targets” towards the bottom of the “Explorer” panel and click the dropdown button.
- Unfold the heir folder
- Right-click on “//tools” and click the “Build Package Recursively” option
Testing
- Open the “Explorer” (File Overview) in the left panel.
- Find “Bazel Build Targets” towards the bottom of the “Explorer” panel and click the dropdown button.
- Unfold the heir folder
- Right-click on “//test” and click the “Test Package Recursively” option
Running and Debugging
Create a
launch.json
file in the.vscode
folder, changing the"name"
and"args"
as required:{ "version": "0.2.0", "configurations": [ { "name": "Debug Secret->BGV", "preLaunchTask": "build", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/bazel-bin/tools/heir-opt", "args": [ "--secret-to-bgv", "--debug", "${workspaceFolder}/tests/secret_to_bgv/ops.mlir" ], "relativePathBase": "${workspaceFolder}", "sourceMap": { "proc/self/cwd": "${workspaceFolder}", "/proc/self/cwd": "${workspaceFolder}" } }, ] }
You can add as many different configurations as necessary.
Add Breakpoints to your program as desired.
Open the Run/Debug panel on the left, select the desired configuration and run/debug it.
- Note that you might have to hit “Enter” to proceed past the Bazel build. It might take several seconds between hitting “Enter” and the debug terminal opening.
Vim/Neovim
Misc config
Filetype detection
# ftdetect/mlir.vim
au BufRead,BufNewFile *.mlir setfiletype mlir
Buildifier integration
# ftplugin/bzl.vim
augroup AutoFormat
autocmd!
autocmd BufWritePre Neoformat buildifier
augroup END
LSP configuration (Neovim, using nvim-lspconfig)
nvim_lsp["mlir_lsp_server"].setup {
on_attach = on_attach,
capabilities = capabilities,
cmd = { "heir-lsp" },
}
Tree-sitter configuration for relevant project languages
require('nvim-treesitter.configs').setup {
ensure_installed = {
"markdown_inline", -- for markdown in tablegen
"mlir",
"tablegen",
"verilog", -- for yosys
},
-- <... other config options ...>
}
Telescope-alternate config (quickly jump between cc, header, and tablegen files)
require('telescope-alternate').setup({
mappings = {
{
pattern = '**/(.*).h',
targets = {
{ template = '**/[1].cc', label = 'cc', enable_new = false },
{ template = '**/[1].cpp', label = 'cpp', enable_new = false },
{ template = '**/[1]Test.cc', label = 'cc Test', enable_new = false },
{ template = '**/[1]Test.cpp', label = 'cpp Test', enable_new = false },
{ template = '**/[1].td', label = 'tablegen', enable_new = false },
}
},
{
pattern = '**/(.*).cc',
targets = {
{ template = '**/[1].h', label = 'header', enable_new = false },
{ template = '**/[1].cpp', label = 'cpp', enable_new = false },
{ template = '**/[1]Test.cc', label = 'cc Test', enable_new = false },
{ template = '**/[1]Test.cpp', label = 'cpp Test', enable_new = false },
{ template = '**/[1].td', label = 'tablegen', enable_new = false },
}
},
{
pattern = '**/(.*).cpp',
targets = {
{ template = '**/[1].h', label = 'header', enable_new = false },
{ template = '**/[1].cc', label = 'cc', enable_new = false },
{ template = '**/[1]Test.cc', label = 'test', enable_new = false },
{ template = '**/[1]Test.cpp', label = 'test', enable_new = false },
{ template = '**/[1].td', label = 'tablegen', enable_new = false },
}
},
{
pattern = '**/(.*).td',
targets = {
{ template = '**/[1].h', label = 'header', enable_new = false },
{ template = '**/[1].cc', label = 'cc', enable_new = false },
{ template = '**/[1].cpp', label = 'cpp', enable_new = false },
}
},
{
pattern = '(.*)Test.(.*)',
targets = {
{ template = '**/[1].[2]', label = 'implementation', enable_new = false },
}
},
},
open_only_one_with = 'vertical_split',
})
Useful mappings
Navigate to the bazel build target for current file
vim.keymap.set('n', '<leader>eb', function()
-- expand("%:p:h") gets the current filepath
local buildfile = vim.fn.expand("%:p:h") .. "/BUILD"
-- expand("%:t") gets the current filename with suffix.
local target = vim.fn.expand("%:t")
vim.api.nvim_command("botright vsplit " .. buildfile)
vim.cmd("normal /" .. target .. vim.api.nvim_replace_termcodes("<CR>", true, true, true))
vim.cmd("normal zz")
end,
{ noremap = true })
Set include guards according to HEIR style guide.
local function build_include_guard()
-- project relative filepath
local abs_path = vim.fn.expand("%")
local rel_path = vim.fn.fnamemodify(abs_path, ":~:.")
-- screaming case
local upper = string.upper(rel_path)
-- underscore separated
local underscored = string.gsub(upper, "[./]", "_")
-- trailing underscore
return underscored .. "_"
end
-- mnemonic: fi = fix include (guard)
vim.keymap.set('n', '<leader>fi', function()
local buf = vim.api.nvim_get_current_buf()
local include_guard = build_include_guard()
local ifndef = "#ifndef " .. include_guard
local define = "#define " .. include_guard
local endif = "#endif // " .. include_guard
vim.api.nvim_buf_set_lines(buf, 0, 2, false, { ifndef, define })
vim.api.nvim_buf_set_lines(buf, -2, -1, false, { endif })
end, { noremap = true })