CIRCT 22.0.0git
Loading...
Searching...
No Matches
VerilogServer.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the VerilogServer class, which is responsible for
10// managing the state of the Verilog server. VerilogServer keeps track of the
11// contents of all open text documents, and each document has a slang
12// compilation result.
13//
14//===----------------------------------------------------------------------===//
15#include "VerilogServer.h"
16#include "../Utils/LSPUtils.h"
17
18#include "circt/Support/LLVM.h"
20#include "mlir/Tools/lsp-server-support/Logging.h"
21#include "mlir/Tools/lsp-server-support/Protocol.h"
22#include "slang/ast/Compilation.h"
23#include "slang/diagnostics/DiagnosticClient.h"
24#include "slang/diagnostics/Diagnostics.h"
25#include "slang/driver/Driver.h"
26#include "slang/syntax/SyntaxTree.h"
27#include "slang/text/SourceLocation.h"
28#include "slang/text/SourceManager.h"
29#include "llvm/ADT/SmallString.h"
30#include "llvm/ADT/StringMap.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Path.h"
33#include "llvm/Support/SourceMgr.h"
34
35#include <memory>
36#include <optional>
37
38using namespace mlir;
39using namespace mlir::lsp;
40
41using namespace circt::lsp;
42using namespace circt;
43
44static mlir::lsp::DiagnosticSeverity
45getSeverity(slang::DiagnosticSeverity severity) {
46 switch (severity) {
47 case slang::DiagnosticSeverity::Fatal:
48 case slang::DiagnosticSeverity::Error:
49 return mlir::lsp::DiagnosticSeverity::Error;
50 case slang::DiagnosticSeverity::Warning:
51 return mlir::lsp::DiagnosticSeverity::Warning;
52 case slang::DiagnosticSeverity::Ignored:
53 case slang::DiagnosticSeverity::Note:
54 return mlir::lsp::DiagnosticSeverity::Information;
55 }
56 llvm_unreachable("all slang diagnostic severities should be handled");
57 return mlir::lsp::DiagnosticSeverity::Error;
58}
59namespace {
60
61// A global context carried around by the server.
62struct VerilogServerContext {
63 VerilogServerContext(const VerilogServerOptions &options)
64 : options(options) {}
65 const VerilogServerOptions &options;
66};
67
68//===----------------------------------------------------------------------===//
69// VerilogDocument
70//===----------------------------------------------------------------------===//
71
72/// This class represents all of the information pertaining to a specific
73/// Verilog document.
74class LSPDiagnosticClient;
75struct VerilogDocument {
76 VerilogDocument(VerilogServerContext &globalContext,
77 const mlir::lsp::URIForFile &uri, StringRef contents,
78 std::vector<mlir::lsp::Diagnostic> &diagnostics);
79 VerilogDocument(const VerilogDocument &) = delete;
80 VerilogDocument &operator=(const VerilogDocument &) = delete;
81
82 const mlir::lsp::URIForFile &getURI() const { return uri; }
83
84 llvm::SourceMgr &getSourceMgr() { return sourceMgr; }
86 return bufferIDMap;
87 }
88
89 const slang::SourceManager &getSlangSourceManager() const {
90 return driver.sourceManager;
91 }
92
93 // Return LSP location from slang location.
94 mlir::lsp::Location getLspLocation(slang::SourceLocation loc) const;
95
96private:
97 // A map from slang buffer ID to the corresponding buffer ID in the LLVM
98 // source manager.
100
101 // The compilation result.
102 FailureOr<std::unique_ptr<slang::ast::Compilation>> compilation;
103
104 // The slang driver.
105 slang::driver::Driver driver;
106
107 // The LLVM source manager.
108 llvm::SourceMgr sourceMgr;
109
110 // The URI of the document.
111 mlir::lsp::URIForFile uri;
112};
113
114} // namespace
115
116//===----------------------------------------------------------------------===//
117// LSPDiagnosticClient
118//===----------------------------------------------------------------------===//
119
120namespace {
121/// A converter that can be plugged into a slang `DiagnosticEngine` as a
122/// client that will map slang diagnostics to LSP diagnostics.
123class LSPDiagnosticClient : public slang::DiagnosticClient {
124 const VerilogDocument &document;
125 std::vector<mlir::lsp::Diagnostic> &diags;
126
127public:
128 LSPDiagnosticClient(const VerilogDocument &document,
129 std::vector<mlir::lsp::Diagnostic> &diags)
130 : document(document), diags(diags) {}
131
132 void report(const slang::ReportedDiagnostic &slangDiag) override;
133};
134} // namespace
135
136void LSPDiagnosticClient::report(const slang::ReportedDiagnostic &slangDiag) {
137 auto loc = document.getLspLocation(slangDiag.location);
138 // Show only the diagnostics in the current file.
139 if (loc.uri != document.getURI())
140 return;
141 auto &mlirDiag = diags.emplace_back();
142 mlirDiag.severity = getSeverity(slangDiag.severity);
143 mlirDiag.range = loc.range;
144 mlirDiag.source = "slang";
145 mlirDiag.message = slangDiag.formattedMessage;
146}
147
148//===----------------------------------------------------------------------===//
149// VerilogDocument
150//===----------------------------------------------------------------------===//
151
152VerilogDocument::VerilogDocument(
153 VerilogServerContext &context, const mlir::lsp::URIForFile &uri,
154 StringRef contents, std::vector<mlir::lsp::Diagnostic> &diagnostics)
155 : uri(uri) {
156 unsigned int bufferId;
157 if (auto memBufferOwn =
158 llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file())) {
159
160 bufferId = sourceMgr.AddNewSourceBuffer(std::move(memBufferOwn), SMLoc());
161 } else {
163 Twine("Failed to create memory buffer for file ") + uri.file());
164 return;
165 }
166
167 // Build the set of include directories for this file.
168 llvm::SmallString<32> uriDirectory(uri.file());
169 llvm::sys::path::remove_filename(uriDirectory);
170
171 std::vector<std::string> libDirs;
172 libDirs.push_back(uriDirectory.str().str());
173 libDirs.insert(libDirs.end(), context.options.libDirs.begin(),
174 context.options.libDirs.end());
175
176 // Populate source managers.
177 const llvm::MemoryBuffer *memBuffer = sourceMgr.getMemoryBuffer(bufferId);
178
179 for (const auto &libDir : libDirs) {
180 driver.sourceLoader.addSearchDirectories(libDir);
181 }
182
183 // Assign text to slang.
184 auto slangBuffer =
185 driver.sourceManager.assignText(uri.file(), memBuffer->getBuffer());
186 driver.sourceLoader.addBuffer(slangBuffer);
187 bufferIDMap[slangBuffer.id.getId()] = bufferId;
188
189 auto diagClient = std::make_shared<LSPDiagnosticClient>(*this, diagnostics);
190 driver.diagEngine.addClient(diagClient);
191
192 driver.options.compilationFlags.emplace(
193 slang::ast::CompilationFlags::LintMode, false);
194 driver.options.compilationFlags.emplace(
195 slang::ast::CompilationFlags::DisableInstanceCaching, false);
196
197 if (!driver.processOptions()) {
198 return;
199 }
200
201 driver.diagEngine.setIgnoreAllWarnings(false);
202
203 if (!driver.parseAllSources()) {
204 circt::lsp::Logger::error(Twine("Failed to parse Verilog file ") +
205 uri.file());
206 return;
207 }
208
209 compilation = driver.createCompilation();
210 for (auto &diag : (*compilation)->getAllDiagnostics())
211 driver.diagEngine.issue(diag);
212}
213
214mlir::lsp::Location
215VerilogDocument::getLspLocation(slang::SourceLocation loc) const {
216 if (loc && loc.buffer() != slang::SourceLocation::NoLocation.buffer()) {
217 const auto &slangSourceManager = getSlangSourceManager();
218 auto line = slangSourceManager.getLineNumber(loc) - 1;
219 auto column = slangSourceManager.getColumnNumber(loc) - 1;
220 auto it = bufferIDMap.find(loc.buffer().getId());
221 // Check if the current buffer is the main file.
222 if (it != bufferIDMap.end() && it->second == sourceMgr.getMainFileID())
223 return mlir::lsp::Location(uri, mlir::lsp::Range(Position(line, column)));
224
225 // Otherwise, construct URI from slang source manager.
226 auto fileName = slangSourceManager.getFileName(loc);
227 auto loc = mlir::lsp::URIForFile::fromFile(fileName);
228 if (auto e = loc.takeError())
229 return mlir::lsp::Location();
230 return mlir::lsp::Location(loc.get(),
231 mlir::lsp::Range(Position(line, column)));
232 }
233
234 return mlir::lsp::Location();
235}
236
237//===----------------------------------------------------------------------===//
238// VerilogTextFile
239//===----------------------------------------------------------------------===//
240
241namespace {
242/// This class represents a text file containing one or more Verilog
243/// documents.
244class VerilogTextFile {
245public:
246 VerilogTextFile(VerilogServerContext &globalContext,
247 const mlir::lsp::URIForFile &uri, StringRef fileContents,
248 int64_t version,
249 std::vector<mlir::lsp::Diagnostic> &diagnostics);
250
251 /// Return the current version of this text file.
252 int64_t getVersion() const { return version; }
253
254 /// Update the file to the new version using the provided set of content
255 /// changes. Returns failure if the update was unsuccessful.
256 LogicalResult
257 update(const mlir::lsp::URIForFile &uri, int64_t newVersion,
258 ArrayRef<mlir::lsp::TextDocumentContentChangeEvent> changes,
259 std::vector<mlir::lsp::Diagnostic> &diagnostics);
260
261private:
262 /// Initialize the text file from the given file contents.
263 void initialize(const mlir::lsp::URIForFile &uri, int64_t newVersion,
264 std::vector<mlir::lsp::Diagnostic> &diagnostics);
265
266 VerilogServerContext &context;
267
268 /// The full string contents of the file.
269 std::string contents;
270
271 /// The version of this file.
272 int64_t version = 0;
273
274 /// The chunks of this file. The order of these chunks is the order in which
275 /// they appear in the text file.
276 std::unique_ptr<VerilogDocument> document;
277};
278} // namespace
279
280VerilogTextFile::VerilogTextFile(
281 VerilogServerContext &context, const mlir::lsp::URIForFile &uri,
282 StringRef fileContents, int64_t version,
283 std::vector<mlir::lsp::Diagnostic> &diagnostics)
284 : context(context), contents(fileContents.str()) {
285 initialize(uri, version, diagnostics);
286}
287
288LogicalResult VerilogTextFile::update(
289 const mlir::lsp::URIForFile &uri, int64_t newVersion,
290 ArrayRef<mlir::lsp::TextDocumentContentChangeEvent> changes,
291 std::vector<mlir::lsp::Diagnostic> &diagnostics) {
292 if (failed(mlir::lsp::TextDocumentContentChangeEvent::applyTo(changes,
293 contents))) {
294 circt::lsp::Logger::error(Twine("Failed to update contents of ") +
295 uri.file());
296 return failure();
297 }
298
299 // If the file contents were properly changed, reinitialize the text file.
300 initialize(uri, newVersion, diagnostics);
301 return success();
302}
303
304void VerilogTextFile::initialize(
305 const mlir::lsp::URIForFile &uri, int64_t newVersion,
306 std::vector<mlir::lsp::Diagnostic> &diagnostics) {
307 version = newVersion;
308 document =
309 std::make_unique<VerilogDocument>(context, uri, contents, diagnostics);
310}
311
312//===----------------------------------------------------------------------===//
313// VerilogServer::Impl
314//===----------------------------------------------------------------------===//
315
317 explicit Impl(const VerilogServerOptions &options) : context(options) {}
318
319 /// The files held by the server, mapped by their URI file name.
320 llvm::StringMap<std::unique_ptr<VerilogTextFile>> files;
321
322 VerilogServerContext context;
323};
324
325//===----------------------------------------------------------------------===//
326// VerilogServer
327//===----------------------------------------------------------------------===//
328
330 : impl(std::make_unique<Impl>(options)) {}
332
334 const URIForFile &uri, StringRef contents, int64_t version,
335 std::vector<mlir::lsp::Diagnostic> &diagnostics) {
336 impl->files[uri.file()] = std::make_unique<VerilogTextFile>(
337 impl->context, uri, contents, version, diagnostics);
338}
339
341 const URIForFile &uri,
342 ArrayRef<mlir::lsp::TextDocumentContentChangeEvent> changes,
343 int64_t version, std::vector<mlir::lsp::Diagnostic> &diagnostics) {
344 // Check that we actually have a document for this uri.
345 auto it = impl->files.find(uri.file());
346 if (it == impl->files.end())
347 return;
348
349 // Try to update the document. If we fail, erase the file from the server. A
350 // failed updated generally means we've fallen out of sync somewhere.
351 if (failed(it->second->update(uri, version, changes, diagnostics)))
352 impl->files.erase(it);
353}
354
355std::optional<int64_t>
357 auto it = impl->files.find(uri.file());
358 if (it == impl->files.end())
359 return std::nullopt;
360
361 int64_t version = it->second->getVersion();
362 impl->files.erase(it);
363 return version;
364}
static mlir::lsp::DiagnosticSeverity getSeverity(slang::DiagnosticSeverity severity)
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
void updateDocument(const URIForFile &uri, llvm::ArrayRef< TextDocumentContentChangeEvent > changes, int64_t version, std::vector< Diagnostic > &diagnostics)
Update the document, with the provided version, at the given URI.
VerilogServer(const circt::lsp::VerilogServerOptions &options)
void addDocument(const URIForFile &uri, llvm::StringRef contents, int64_t version, std::vector< Diagnostic > &diagnostics)
Add the document, with the provided version, at the given URI.
void error(Twine message)
Definition LSPUtils.cpp:16
mlir::lsp::URIForFile URIForFile
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::StringMap< std::unique_ptr< VerilogTextFile > > files
The files held by the server, mapped by their URI file name.
Impl(const VerilogServerOptions &options)