CIRCT 23.0.0git
Loading...
Searching...
No Matches
ImportVerilog.cpp
Go to the documentation of this file.
1//===- ImportVerilog.cpp - Slang Verilog frontend integration -------------===//
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 implements bridging from the slang Verilog frontend to CIRCT dialects.
10//
11//===----------------------------------------------------------------------===//
12
19#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
20#include "mlir/IR/Diagnostics.h"
21#include "mlir/IR/Verifier.h"
22#include "mlir/Pass/PassManager.h"
23#include "mlir/Support/Timing.h"
24#include "mlir/Tools/mlir-translate/Translation.h"
25#include "mlir/Transforms/Passes.h"
26#include "llvm/ADT/Hashing.h"
27#include "llvm/Support/SourceMgr.h"
28
29#include "slang/analysis/AnalysisManager.h"
30#include "slang/diagnostics/DiagnosticClient.h"
31#include "slang/driver/Driver.h"
32#include "slang/parsing/Preprocessor.h"
33#include "slang/syntax/SyntaxPrinter.h"
34#include "slang/util/VersionInfo.h"
35
36using namespace mlir;
37using namespace circt;
38using namespace ImportVerilog;
39
40using llvm::SourceMgr;
41
43 std::string buffer;
44 llvm::raw_string_ostream os(buffer);
45 os << "slang version ";
46 os << slang::VersionInfo::getMajor() << ".";
47 os << slang::VersionInfo::getMinor() << ".";
48 os << slang::VersionInfo::getPatch() << "+";
49 os << slang::VersionInfo::getHash();
50 return buffer;
51}
52
53//===----------------------------------------------------------------------===//
54// Diagnostics
55//===----------------------------------------------------------------------===//
56
57/// Convert a slang `SourceLocation` to an MLIR `Location`.
58static Location convertLocation(MLIRContext *context,
59 const slang::SourceManager &sourceManager,
60 slang::SourceLocation loc) {
61 if (loc && loc.buffer() != slang::SourceLocation::NoLocation.buffer()) {
62 auto fileName = sourceManager.getFileName(loc);
63 auto line = sourceManager.getLineNumber(loc);
64 auto column = sourceManager.getColumnNumber(loc);
65 return FileLineColLoc::get(context, fileName, line, column);
66 }
67 return UnknownLoc::get(context);
68}
69
70/// Convert a slang `SourceRange` to an MLIR `Location`.
71static Location convertLocation(MLIRContext *context,
72 const slang::SourceManager &sourceManager,
73 slang::SourceRange range) {
74 auto start = range.start();
75 auto end = range.end();
76 if (start && start.buffer() != slang::SourceLocation::NoLocation.buffer()) {
77 auto fileName = sourceManager.getFileName(start);
78 auto startLine = sourceManager.getLineNumber(start);
79 auto startColumn = sourceManager.getColumnNumber(start);
80 if (end && end.buffer() == start.buffer()) {
81 auto endLine = sourceManager.getLineNumber(end);
82 auto endColumn = sourceManager.getColumnNumber(end);
83 return FileLineColRange::get(context, fileName, startLine, startColumn,
84 endLine, endColumn);
85 }
86 return FileLineColLoc::get(context, fileName, startLine, startColumn);
87 }
88 return UnknownLoc::get(context);
89}
90
91Location Context::convertLocation(slang::SourceLocation loc) {
92 return ::convertLocation(getContext(), sourceManager, loc);
93}
94
95Location Context::convertLocation(slang::SourceRange range) {
96 return ::convertLocation(getContext(), sourceManager, range);
97}
98
99namespace {
100/// A converter that can be plugged into a slang `DiagnosticEngine` as a client
101/// that will map slang diagnostics to their MLIR counterpart and emit them.
102class MlirDiagnosticClient : public slang::DiagnosticClient {
103public:
104 MlirDiagnosticClient(MLIRContext *context) : context(context) {}
105
106 void report(const slang::ReportedDiagnostic &diag) override {
107 // Generate the primary MLIR diagnostic.
108 auto &diagEngine = context->getDiagEngine();
109 Location loc = !diag.ranges.empty() ? convertLocation(diag.ranges[0])
110 : convertLocation(diag.location);
111
112 auto mlirDiag = diagEngine.emit(loc, getSeverity(diag.severity));
113 mlirDiag << diag.formattedMessage;
114
115 // Append the name of the option that can be used to control this
116 // diagnostic.
117 auto optionName = engine->getOptionName(diag.originalDiagnostic.code);
118 if (!optionName.empty())
119 mlirDiag << " [-W" << optionName << "]";
120
121 // Write out macro expansions, if we have any, in reverse order.
122 for (auto loc : std::views::reverse(diag.expansionLocs)) {
123 auto &note = mlirDiag.attachNote(
124 convertLocation(sourceManager->getFullyOriginalLoc(loc)));
125 auto macroName = sourceManager->getMacroName(loc);
126 if (macroName.empty())
127 note << "expanded from here";
128 else
129 note << "expanded from macro '" << macroName << "'";
130 }
131
132 // Write out the include stack.
133 slang::SmallVector<slang::SourceLocation> includeStack;
134 getIncludeStack(diag.location.buffer(), includeStack);
135 for (auto &loc : std::views::reverse(includeStack))
136 mlirDiag.attachNote(convertLocation(loc)) << "included from here";
137 }
138
139 /// Convert a slang `SourceLocation` to an MLIR `Location`.
140 Location convertLocation(slang::SourceLocation loc) const {
141 return ::convertLocation(context, *sourceManager, loc);
142 }
143
144 /// Convert a slang `SourceRange` to an MLIR `Location`.
145 Location convertLocation(slang::SourceRange range) const {
146 return ::convertLocation(context, *sourceManager, range);
147 }
148
149 static DiagnosticSeverity getSeverity(slang::DiagnosticSeverity severity) {
150 switch (severity) {
151 case slang::DiagnosticSeverity::Fatal:
152 case slang::DiagnosticSeverity::Error:
153 return DiagnosticSeverity::Error;
154 case slang::DiagnosticSeverity::Warning:
155 return DiagnosticSeverity::Warning;
156 case slang::DiagnosticSeverity::Ignored:
157 case slang::DiagnosticSeverity::Note:
158 return DiagnosticSeverity::Remark;
159 }
160 llvm_unreachable("all slang diagnostic severities should be handled");
161 return DiagnosticSeverity::Error;
162 }
163
164private:
165 MLIRContext *context;
166};
167} // namespace
168
169// Allow for `slang::BufferID` to be used as hash map keys.
170namespace llvm {
171template <>
172struct DenseMapInfo<slang::BufferID> {
173 static unsigned getHashValue(slang::BufferID id) {
174 return llvm::hash_value(id.getId());
175 }
176 static bool isEqual(slang::BufferID a, slang::BufferID b) { return a == b; }
177};
178} // namespace llvm
179
180//===----------------------------------------------------------------------===//
181// Driver
182//===----------------------------------------------------------------------===//
183
184namespace {
185const static ImportVerilogOptions defaultOptions;
186
187struct ImportDriver {
188 ImportDriver(MLIRContext *mlirContext, TimingScope &ts,
189 const ImportVerilogOptions *options)
190 : mlirContext(mlirContext), ts(ts),
191 options(options ? *options : defaultOptions) {}
192
193 LogicalResult prepareDriver(SourceMgr &sourceMgr);
194 LogicalResult importVerilog(ModuleOp module);
195 LogicalResult preprocessVerilog(llvm::raw_ostream &os);
196
197 MLIRContext *mlirContext;
198 TimingScope &ts;
199 const ImportVerilogOptions &options;
200
201 // Use slang's driver which conveniently packages a lot of the things we
202 // need for compilation.
203 slang::driver::Driver driver;
204};
205} // namespace
206
207/// Populate the Slang driver with source files from the given `sourceMgr`, and
208/// configure driver options based on the `ImportVerilogOptions` passed to the
209/// `ImportDriver` constructor.
210LogicalResult ImportDriver::prepareDriver(SourceMgr &sourceMgr) {
211 // Use slang's driver which conveniently packages a lot of the things we
212 // need for compilation.
213 auto diagClient = std::make_shared<MlirDiagnosticClient>(mlirContext);
214 driver.diagEngine.addClient(diagClient);
215
216 for (const auto &value : options.commandFiles)
217 if (!driver.processCommandFiles(value, /*makeRelative=*/true,
218 /*separateUnit=*/true))
219 return failure();
220
221 // Populate the source manager with the source files.
222 // NOTE: This is a bit ugly since we're essentially copying the Verilog
223 // source text in memory. At a later stage we'll want to extend slang's
224 // SourceManager such that it can contain non-owned buffers. This will do
225 // for now.
226 driver.sourceManager.setDisableProximatePaths(
227 !options.makeLocationPathsProximate);
228 DenseSet<StringRef> seenBuffers;
229 for (unsigned i = 0, e = sourceMgr.getNumBuffers(); i < e; ++i) {
230 const llvm::MemoryBuffer *mlirBuffer = sourceMgr.getMemoryBuffer(i + 1);
231 auto name = mlirBuffer->getBufferIdentifier();
232 if (!name.empty() && !seenBuffers.insert(name).second)
233 continue; // Slang doesn't like listing the same buffer twice
234 auto slangBuffer =
235 driver.sourceManager.assignText(name, mlirBuffer->getBuffer());
236 driver.sourceLoader.addBuffer(slangBuffer);
237 }
238
239 for (const auto &libDir : options.libDirs)
240 driver.sourceLoader.addSearchDirectories(libDir);
241
242 for (const auto &libExt : options.libExts)
243 driver.sourceLoader.addSearchExtension(libExt);
244
245 for (const auto &[i, f] : llvm::enumerate(options.libraryFiles)) {
246 // Include a space to avoid conflicts with explicitly-specified names.
247 auto libName = "library " + std::to_string(i);
248 driver.sourceLoader.addLibraryFiles(libName, f);
249 }
250
251 for (const auto &includeDir : options.includeDirs)
252 if (driver.sourceManager.addUserDirectories(includeDir))
253 return failure();
254
255 for (const auto &includeSystemDir : options.includeSystemDirs)
256 if (driver.sourceManager.addSystemDirectories(includeSystemDir))
257 return failure();
258
259 // Populate the driver options.
260 driver.addStandardArgs();
261
262 driver.options.excludeExts.insert(options.excludeExts.begin(),
263 options.excludeExts.end());
264 driver.options.ignoreDirectives = options.ignoreDirectives;
265
266 driver.options.maxIncludeDepth = options.maxIncludeDepth;
267 driver.options.defines = options.defines;
268 driver.options.undefines = options.undefines;
269 driver.options.librariesInheritMacros = options.librariesInheritMacros;
270
271 driver.options.timeScale = options.timeScale;
272 driver.options
273 .compilationFlags[slang::ast::CompilationFlags::AllowUseBeforeDeclare] =
274 options.allowUseBeforeDeclare;
275 driver.options
276 .compilationFlags[slang::ast::CompilationFlags::IgnoreUnknownModules] =
277 options.ignoreUnknownModules;
278 driver.options.compilationFlags[slang::ast::CompilationFlags::LintMode] =
280 driver.options
281 .compilationFlags[slang::ast::CompilationFlags::DisableInstanceCaching] =
282 false;
283 driver.options.topModules = options.topModules;
284 driver.options.paramOverrides = options.paramOverrides;
285
286 driver.options.errorLimit = options.errorLimit;
287 driver.options.warningOptions = options.warningOptions;
288
289 driver.options.singleUnit = options.singleUnit;
290
291 // Parse pass through options.
292 if (!options.slangArgs.empty()) {
293 SmallVector<const char *> slangArgs;
294 slangArgs.push_back("slang"); // dummy program name
295 for (const auto &arg : options.slangArgs)
296 slangArgs.push_back(arg.c_str());
297 if (!driver.parseCommandLine(slangArgs.size(), slangArgs.data()))
298 return failure();
299 }
300
301 return success(driver.processOptions());
302}
303
304/// Parse and elaborate the prepared source files, and populate the given MLIR
305/// `module` with corresponding operations.
306LogicalResult ImportDriver::importVerilog(ModuleOp module) {
307 // Parse the input.
308 auto parseTimer = ts.nest("Verilog parser");
309 bool parseSuccess = driver.parseAllSources();
310 parseTimer.stop();
311
312 // Elaborate the input.
313 auto compileTimer = ts.nest("Verilog elaboration");
314 auto compilation = driver.createCompilation();
315
316 // Semantic analysis
317 auto analysisTimer = ts.nest("Semantic analysis");
318 driver.runAnalysis(*compilation);
319
320 for (auto &diag : compilation->getAllDiagnostics())
321 driver.diagEngine.issue(diag);
322 if (!parseSuccess || driver.diagEngine.getNumErrors() > 0)
323 return failure();
324 compileTimer.stop();
325
326 // If we were only supposed to lint the input, return here. This leaves the
327 // module empty, but any Slang linting messages got reported as diagnostics.
328 if (options.mode == ImportVerilogOptions::Mode::OnlyLint)
329 return success();
330
331 // Traverse the parsed Verilog AST and map it to the equivalent CIRCT ops.
332 mlirContext
333 ->loadDialect<moore::MooreDialect, hw::HWDialect, cf::ControlFlowDialect,
334 func::FuncDialect, verif::VerifDialect, ltl::LTLDialect,
335 debug::DebugDialect>();
336 auto conversionTimer = ts.nest("Verilog to dialect mapping");
337 Context context(options, *compilation, module, driver.sourceManager);
338 if (failed(context.convertCompilation()))
339 return failure();
340 conversionTimer.stop();
341
342 // Run the verifier on the constructed module to ensure it is clean.
343 auto verifierTimer = ts.nest("Post-parse verification");
344 return verify(module);
345}
346
347/// Preprocess the prepared source files and print them to the given output
348/// stream.
349LogicalResult ImportDriver::preprocessVerilog(llvm::raw_ostream &os) {
350 auto parseTimer = ts.nest("Verilog preprocessing");
351
352 // Run the preprocessor to completion across all sources previously added with
353 // `pushSource`, report diagnostics, and print the output.
354 auto preprocessAndPrint = [&](slang::parsing::Preprocessor &preprocessor) {
355 slang::syntax::SyntaxPrinter output;
356 output.setIncludeComments(false);
357 while (true) {
358 slang::parsing::Token token = preprocessor.next();
359 output.print(token);
360 if (token.kind == slang::parsing::TokenKind::EndOfFile)
361 break;
362 }
363
364 for (auto &diag : preprocessor.getDiagnostics()) {
365 if (diag.isError()) {
366 driver.diagEngine.issue(diag);
367 return failure();
368 }
369 }
370 os << output.str();
371 return success();
372 };
373
374 // Depending on whether the single-unit option is set, either add all source
375 // files to a single preprocessor such that they share define macros and
376 // directives, or create a separate preprocessor for each, such that each
377 // source file is in its own compilation unit.
378 auto optionBag = driver.createOptionBag();
379 if (driver.options.singleUnit == true) {
380 slang::BumpAllocator alloc;
381 slang::Diagnostics diagnostics;
382 slang::parsing::Preprocessor preprocessor(driver.sourceManager, alloc,
383 diagnostics, optionBag);
384 // Sources have to be pushed in reverse, as they form a stack in the
385 // preprocessor. Last pushed source is processed first.
386 auto sources = driver.sourceLoader.loadSources();
387 for (auto &buffer : std::views::reverse(sources))
388 preprocessor.pushSource(buffer);
389 if (failed(preprocessAndPrint(preprocessor)))
390 return failure();
391 } else {
392 for (auto &buffer : driver.sourceLoader.loadSources()) {
393 slang::BumpAllocator alloc;
394 slang::Diagnostics diagnostics;
395 slang::parsing::Preprocessor preprocessor(driver.sourceManager, alloc,
396 diagnostics, optionBag);
397 preprocessor.pushSource(buffer);
398 if (failed(preprocessAndPrint(preprocessor)))
399 return failure();
400 }
401 }
402
403 return success();
404}
405
406//===----------------------------------------------------------------------===//
407// Entry Points
408//===----------------------------------------------------------------------===//
409
410/// Parse the specified Verilog inputs into the specified MLIR context.
411LogicalResult circt::importVerilog(SourceMgr &sourceMgr,
412 MLIRContext *mlirContext, TimingScope &ts,
413 ModuleOp module,
414 const ImportVerilogOptions *options) {
415 ImportDriver importDriver(mlirContext, ts, options);
416 if (failed(importDriver.prepareDriver(sourceMgr)))
417 return failure();
418 return importDriver.importVerilog(module);
419}
420
421/// Run the files in a source manager through Slang's Verilog preprocessor and
422/// emit the result to the given output stream.
423LogicalResult circt::preprocessVerilog(SourceMgr &sourceMgr,
424 MLIRContext *mlirContext,
425 TimingScope &ts, llvm::raw_ostream &os,
426 const ImportVerilogOptions *options) {
427 ImportDriver importDriver(mlirContext, ts, options);
428 if (failed(importDriver.prepareDriver(sourceMgr)))
429 return failure();
430 return importDriver.preprocessVerilog(os);
431}
432
433/// Entry point as an MLIR translation.
435 static TranslateToMLIRRegistration fromVerilog(
436 "import-verilog", "import Verilog or SystemVerilog",
437 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
438 TimingScope ts;
440 ModuleOp::create(UnknownLoc::get(context)));
441 ImportVerilogOptions options;
442 options.debugInfo = true;
443 options.warningOptions.push_back("no-missing-top");
444 if (failed(
445 importVerilog(sourceMgr, context, ts, module.get(), &options)))
446 module = {};
447 return module;
448 });
449}
450
451//===----------------------------------------------------------------------===//
452// Pass Pipeline
453//===----------------------------------------------------------------------===//
454
455/// Optimize and simplify the Moore dialect IR.
456void circt::populateVerilogToMoorePipeline(OpPassManager &pm) {
457 {
458 // Perform an initial cleanup and preprocessing across all
459 // modules/functions.
460 auto &anyPM = pm.nestAny();
461 anyPM.addPass(mlir::createCSEPass());
462 anyPM.addPass(mlir::createCanonicalizerPass());
463 }
464
465 pm.addPass(moore::createVTablesPass());
466
467 // Remove unused symbols.
468 pm.addPass(mlir::createSymbolDCEPass());
469
470 {
471 // Perform module-specific transformations.
472 auto &modulePM = pm.nest<moore::SVModuleOp>();
473 modulePM.addPass(moore::createSimplifyRefsPass());
474 // TODO: Enable the following once it not longer interferes with @(...)
475 // event control checks. The introduced dummy variables make the event
476 // control observe a static local variable that never changes, instead of
477 // observing a module-wide signal.
478 // modulePM.addPass(moore::createSimplifyProceduresPass());
479 modulePM.addPass(mlir::createSROA());
480 }
481
482 {
483 // Perform a final cleanup across all modules/functions.
484 auto &anyPM = pm.nestAny();
485 anyPM.addPass(mlir::createMem2Reg());
486 anyPM.addPass(mlir::createCSEPass());
487 anyPM.addPass(mlir::createCanonicalizerPass());
488 }
489}
490
491/// Convert Moore dialect IR into core dialect IR
492void circt::populateMooreToCorePipeline(OpPassManager &pm) {
493 // Perform the conversion.
494 pm.addPass(createConvertMooreToCorePass());
495
496 {
497 // Conversion to the core dialects likely uncovers new canonicalization
498 // opportunities.
499 auto &anyPM = pm.nestAny();
500 anyPM.addPass(mlir::createCSEPass());
501 anyPM.addPass(mlir::createCanonicalizerPass());
502 }
503}
504
505/// Convert LLHD dialect IR into core dialect IR
507 OpPassManager &pm, const LlhdToCorePipelineOptions &options) {
508 // Inline function calls and lower SCF to CF.
509 pm.addNestedPass<hw::HWModuleOp>(llhd::createWrapProceduralOpsPass());
510 pm.addPass(mlir::createSCFToControlFlowPass());
511 pm.addPass(llhd::createInlineCallsPass());
512 pm.addPass(mlir::createSymbolDCEPass());
513
514 // Simplify processes, replace signals with process results, and detect
515 // registers.
516 auto &modulePM = pm.nest<hw::HWModuleOp>();
517 // See https://github.com/llvm/circt/issues/8804.
518 if (options.sroa) {
519 modulePM.addPass(mlir::createSROA());
520 }
521 modulePM.addPass(llhd::createMem2RegPass());
522 modulePM.addPass(llhd::createHoistSignalsPass());
523 modulePM.addPass(llhd::createDeseqPass());
524 modulePM.addPass(llhd::createLowerProcessesPass());
525 modulePM.addPass(mlir::createCSEPass());
526 modulePM.addPass(mlir::createCanonicalizerPass());
527
528 // Unroll loops and remove control flow.
529 modulePM.addPass(llhd::createUnrollLoopsPass());
530 modulePM.addPass(mlir::createCSEPass());
531 modulePM.addPass(mlir::createCanonicalizerPass());
532 modulePM.addPass(llhd::createRemoveControlFlowPass());
533 modulePM.addPass(mlir::createCSEPass());
534 modulePM.addPass(mlir::createCanonicalizerPass());
535
536 // Convert `arith.select` generated by some of the control flow canonicalizers
537 // to `comb.mux`.
538 modulePM.addPass(createMapArithToCombPass(true));
539
540 // Simplify module-level signals.
541 modulePM.addPass(llhd::createCombineDrivesPass());
542 modulePM.addPass(llhd::createSig2Reg());
543 modulePM.addPass(mlir::createCSEPass());
544 modulePM.addPass(mlir::createCanonicalizerPass());
545
546 // Map `seq.firreg` with array type and `hw.array_inject` self-feedback to
547 // `seq.firmem` ops.
548 if (options.detectMemories) {
549 modulePM.addPass(seq::createRegOfVecToMem());
550 modulePM.addPass(mlir::createCSEPass());
551 modulePM.addPass(mlir::createCanonicalizerPass());
552 }
553}
static std::unique_ptr< Context > context
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
static llvm::lsp::DiagnosticSeverity getSeverity(slang::DiagnosticSeverity severity)
std::unique_ptr< mlir::Pass > createSimplifyRefsPass()
std::unique_ptr< mlir::Pass > createVTablesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createMapArithToCombPass(bool enableBestEffortLowering=false)
void populateVerilogToMoorePipeline(mlir::OpPassManager &pm)
Optimize and simplify the Moore dialect IR.
void populateMooreToCorePipeline(mlir::OpPassManager &pm)
Convert Moore dialect IR into core dialect IR.
void populateLlhdToCorePipeline(mlir::OpPassManager &pm, const LlhdToCorePipelineOptions &options)
std::string getSlangVersion()
Return a human-readable string describing the slang frontend version linked into CIRCT.
std::unique_ptr< OperationPass< ModuleOp > > createConvertMooreToCorePass()
Create an Moore to Comb/HW/LLHD conversion pass.
mlir::LogicalResult importVerilog(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, mlir::TimingScope &ts, mlir::ModuleOp module, const ImportVerilogOptions *options=nullptr)
Parse files in a source manager as Verilog source code and populate the given MLIR module with corres...
mlir::LogicalResult preprocessVerilog(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, mlir::TimingScope &ts, llvm::raw_ostream &os, const ImportVerilogOptions *options=nullptr)
Run the files in a source manager through Slang's Verilog preprocessor and emit the result to the giv...
void registerFromVerilogTranslation()
Register the import-verilog MLIR translation.
llvm::hash_code hash_value(const DenseSet< T > &set)
Options that control how Verilog input files are parsed and processed.
std::vector< std::string > warningOptions
A list of warning options that will be passed to the DiagnosticEngine.
@ OnlyLint
Only lint the input, without elaboration and lowering to CIRCT IR.
bool debugInfo
Generate debug information in the form of debug dialect ops in the IR.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
const slang::SourceManager & sourceManager
MLIRContext * getContext()
Return the MLIR context.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.
Convert LLHD dialect IR into core dialect IR.
static bool isEqual(slang::BufferID a, slang::BufferID b)
static unsigned getHashValue(slang::BufferID id)