Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ImportVerilog] Bump slang #7792

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -551,14 +551,17 @@ llvm_canonicalize_cmake_booleans(CIRCT_SLANG_BUILD_FROM_SOURCE)
if(CIRCT_SLANG_FRONTEND_ENABLED)
message(STATUS "slang Verilog frontend is enabled")
if(CIRCT_SLANG_BUILD_FROM_SOURCE)
# slang requires C++20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually needed? slang's CMakeLists already sets these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an error trying to build ImportVerilog due to public headers in slang requiring C++ 20 if I remove this, but perhaps there's a more principled way to solve this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, that makes sense. It's a little unfortunate that this is adding friction but I make extensive use of, e.g. std::span that would be hard to remove from the public headers. Hopefully LLVM / MLIR / CIRCT can upgrade to C++20 at some point across the board; it's coming up on 5 years old.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive by comment: in general, it's OK to require c++20 (or other non-llvm-standard things like exceptions) if they're optional and can be disabled. Since slang is an optional dependency, it's OK to require c++20 when slang is being used. We just need to be sure to run a ci build with c++17 and no slang.

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Build slang as part of CIRCT (see https://sv-lang.com/building.html)
message(STATUS "Building slang from source")
include(FetchContent)
FetchContent_Declare(
slang
GIT_REPOSITORY https://github.com/MikePopoloski/slang.git
GIT_TAG v3.0
GIT_SHALLOW ON)
GIT_TAG bad0d6ba3dec20355b0156c535bbddf2f926ab37)
set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE "NEVER")

# Force Slang to be built as a static library to avoid messing around with
Expand All @@ -573,6 +576,7 @@ if(CIRCT_SLANG_FRONTEND_ENABLED)
set(CMAKE_CXX_FLAGS "")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MSVC /EHsc is probably not needed now?

endif ()
set(BUILD_SHARED_LIBS OFF)
set(SLANG_USE_MIMALLOC OFF)
FetchContent_MakeAvailable(slang)

set(CMAKE_CXX_FLAGS ${ORIGINAL_CMAKE_CXX_FLAGS})
Expand All @@ -581,7 +585,6 @@ if(CIRCT_SLANG_FRONTEND_ENABLED)
if(BUILD_SHARED_LIBS)
set_target_properties(slang_slang PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(unordered_dense PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()

# The following feels *very* hacky, but CMake complains about the
Expand All @@ -590,17 +593,24 @@ if(CIRCT_SLANG_FRONTEND_ENABLED)
# statically link slang into the CIRCTImportVerilog library, but seems to be
# harder than it ought to be.
set_property(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it seems like all this install / export stuff shouldn't be needed but I'm not enough of a CMake expert to know offhand how to fix it.

GLOBAL APPEND PROPERTY CIRCT_EXPORTS slang_slang unordered_dense fmt)
GLOBAL APPEND PROPERTY CIRCT_EXPORTS slang_slang fmt)

# Disable the installation of headers coming from third-party libraries. We
# won't use those APIs directly. Just make them static libraries for the sake
# of running slang normally.
set_target_properties(fmt PROPERTIES PUBLIC_HEADER "")
set_target_properties(unordered_dense PROPERTIES PUBLIC_HEADER "")

install(TARGETS slang_slang unordered_dense fmt EXPORT CIRCTTargets)
install(TARGETS slang_slang fmt EXPORT CIRCTTargets)

# Match the behavior of slang_slang, which installs its own vendored
# boost_unordered if it does not a system-wide boost installation.
find_package(Boost 1.82.0 QUIET)
if(NOT Boost_FOUND)
set_property(GLOBAL APPEND PROPERTY CIRCT_EXPORTS boost_unordered)
install(TARGETS boost_unordered EXPORT CIRCTTargets)
endif()
else()
find_package(slang 3.0 REQUIRED)
find_package(slang 7.0 REQUIRED)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Ask slang to mint a release so that this version includes everything that we need, including MikePopoloski/slang#1164.

endif()
endif()

Expand Down
4 changes: 2 additions & 2 deletions include/circt/Support/FVInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,8 @@ inline FVInt operator-(uint64_t a, const FVInt &b) {

inline FVInt operator-(const APInt &a, const FVInt &b) { return FVInt(a) - b; }

inline bool operator==(uint64_t a, const FVInt &b) { return b == a; }
inline bool operator!=(uint64_t a, const FVInt &b) { return b != a; }
inline bool operator==(uint64_t a, const FVInt &b) { return b.operator==(a); }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this related to the slang version? Is it picking up some overzealous global operator in slang somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a consequence of building circt code with C++ 20 for the first time, where https://timsong-cpp.github.io/cppwp/n4868/over.match#oper-3.4.4 has come into play. If my analysis is correct, operator resolution of == in { return b == a; } where a: uint64_t and b: const FVInt &b resolved to inline bool operator==(uint64_t a, const FVInt &b) { return b == a; } , i.e. itself, leading to infinite recursion.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, just an artifact of C++20 and not anything slang is doing. Maybe you can just ifdef out these overloads when compiling in C++20 mode.

inline bool operator!=(uint64_t a, const FVInt &b) { return b.operator!=(a); }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any tips on how to best deal with https://timsong-cpp.github.io/cppwp/n4868/over.match#oper-3.4.4 in a codebase that still needs to support older standards?


inline raw_ostream &operator<<(raw_ostream &os, const FVInt &value) {
value.print(os);
Expand Down
7 changes: 0 additions & 7 deletions lib/Conversion/ImportVerilog/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# slang uses exceptions
set(LLVM_REQUIRES_EH ON)
set(LLVM_REQUIRES_RTTI ON)

# For ABI compatibility, define the DEBUG macro in debug builds. Slang sets this
# internally. If we don't set this here as well, header-defined things like the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really shouldn't be necessary; ideally adding the slang lib as a target should bring along its public compile definitions. Is there something I should be fixing on the slang side to do away with this here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for taking a look here @MikePopoloski, it's much appreciated. I'm not good enough at CMake to answer all of your comments, but I'm hoping that somebody more qualified can chime in where needed.

Expand All @@ -15,15 +13,10 @@ add_compile_definitions($<$<CONFIG:Debug>:DEBUG>)
if (MSVC)
# No idea what to put here
else ()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not specify slang include directories as being -isystem so that you don't need these warning suppressions?

# slang uses exceptions; we intercept these in ImportVerilog
add_compile_options(-fexceptions)
add_compile_options(-frtti)
# slang has some classes with virtual funcs but non-virtual destructor.
add_compile_options(-Wno-non-virtual-dtor)
# some other warnings we've seen
add_compile_options(-Wno-c++98-compat-extra-semi)
add_compile_options(-Wno-ctad-maybe-unsupported)
add_compile_options(-Wno-cast-qual)
# visitor switch statements cover all cases but have default
add_compile_options(-Wno-covered-switch-default)
endif ()
Expand Down
7 changes: 5 additions & 2 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "ImportVerilogInternals.h"
#include "slang/ast/EvalContext.h"
#include "slang/ast/SystemSubroutine.h"
#include "slang/syntax/AllSyntax.h"

Expand Down Expand Up @@ -526,7 +527,7 @@ struct RvalueExprVisitor {
// The open range list on the right-hand side of the inside operator is a
// comma-separated list of expressions or ranges.
if (const auto *openRange =
listExpr->as_if<slang::ast::OpenRangeExpression>()) {
listExpr->as_if<slang::ast::ValueRangeExpression>()) {
// Handle ranges.
auto lowBound = context.convertToSimpleBitVector(
context.convertRvalueExpression(openRange->left()));
Expand Down Expand Up @@ -1022,7 +1023,9 @@ slang::ConstantValue
Context::evaluateConstant(const slang::ast::Expression &expr) {
using slang::ast::EvalFlags;
slang::ast::EvalContext evalContext(
compilation, EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
slang::ast::ASTContext(compilation.getRoot(),
slang::ast::LookupLocation::max),
EvalFlags::CacheResults | EvalFlags::SpecparamsAllowed);
return expr.eval(evalContext);
}

Expand Down
8 changes: 4 additions & 4 deletions lib/Conversion/ImportVerilog/FormatStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
//===----------------------------------------------------------------------===//

#include "ImportVerilogInternals.h"
#include "slang/text/SFormat.h"
#include "slang/ast/SFormat.h"

using namespace mlir;
using namespace circt;
using namespace ImportVerilog;
using moore::IntAlign;
using moore::IntFormat;
using moore::IntPadding;
using slang::SFormat::FormatOptions;
using slang::ast::SFormat::FormatOptions;

namespace {
struct FormatStringParser {
Expand Down Expand Up @@ -87,7 +87,7 @@ struct FormatStringParser {
auto onError = [&](auto, auto, auto, auto) {
assert(false && "Slang should have already reported all errors");
};
slang::SFormat::parse(format, onText, onArg, onError);
slang::ast::SFormat::parse(format, onText, onArg, onError);
return failure(anyFailure);
}

Expand Down Expand Up @@ -191,7 +191,7 @@ struct FormatStringParser {
} // namespace

FailureOr<Value> Context::convertFormatString(
slang::span<const slang::ast::Expression *const> arguments, Location loc,
std::span<const slang::ast::Expression *const> arguments, Location loc,
IntFormat defaultFormat, bool appendNewline) {
FormatStringParser parser(*this, ArrayRef(arguments.data(), arguments.size()),
loc, defaultFormat);
Expand Down
87 changes: 42 additions & 45 deletions lib/Conversion/ImportVerilog/ImportVerilog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===//

#include "ImportVerilogInternals.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Verifier.h"
#include "mlir/Support/Timing.h"
Expand All @@ -23,7 +22,7 @@
#include "slang/driver/Driver.h"
#include "slang/parsing/Preprocessor.h"
#include "slang/syntax/SyntaxPrinter.h"
#include "slang/util/Version.h"
#include "slang/util/VersionInfo.h"

using namespace mlir;
using namespace circt;
Expand Down Expand Up @@ -92,11 +91,10 @@ class MlirDiagnosticClient : public slang::DiagnosticClient {
mlirDiag << " [-W" << optionName << "]";

// Write out macro expansions, if we have any, in reverse order.
for (auto it = diag.expansionLocs.rbegin(); it != diag.expansionLocs.rend();
it++) {
for (auto loc : std::views::reverse(diag.expansionLocs)) {
auto &note = mlirDiag.attachNote(
convertLocation(sourceManager->getFullyOriginalLoc(*it)));
auto macroName = sourceManager->getMacroName(*it);
convertLocation(sourceManager->getFullyOriginalLoc(loc)));
auto macroName = sourceManager->getMacroName(loc);
if (macroName.empty())
note << "expanded from here";
else
Expand Down Expand Up @@ -202,15 +200,29 @@ LogicalResult ImportDriver::prepareDriver(SourceMgr &sourceMgr) {
const llvm::MemoryBuffer *mlirBuffer = sourceMgr.getMemoryBuffer(i + 1);
auto slangBuffer = driver.sourceManager.assignText(
mlirBuffer->getBufferIdentifier(), mlirBuffer->getBuffer());
driver.buffers.push_back(slangBuffer);
driver.sourceLoader.addBuffer(slangBuffer);
bufferFilePaths.insert({slangBuffer.id, mlirBuffer->getBufferIdentifier()});
}

for (const auto &libDir : options.libDirs)
driver.sourceLoader.addSearchDirectories(libDir);

for (const auto &libExt : options.libExts)
driver.sourceLoader.addSearchExtension(libExt);

for (const auto &includeDir : options.includeDirs) {
if (driver.sourceManager.addUserDirectories(includeDir)) {
return failure();
}
}

for (const auto &includeDir : options.includeSystemDirs) {
if (driver.sourceManager.addSystemDirectories(includeDir)) {
return failure();
}
}

// Populate the driver options.
driver.options.includeDirs = options.includeDirs;
driver.options.includeSystemDirs = options.includeSystemDirs;
driver.options.libDirs = options.libDirs;
driver.options.libExts = options.libExts;
driver.options.excludeExts.insert(options.excludeExts.begin(),
options.excludeExts.end());
driver.options.ignoreDirectives = options.ignoreDirectives;
Expand All @@ -221,22 +233,22 @@ LogicalResult ImportDriver::prepareDriver(SourceMgr &sourceMgr) {
driver.options.librariesInheritMacros = options.librariesInheritMacros;

driver.options.timeScale = options.timeScale;
driver.options.allowUseBeforeDeclare = options.allowUseBeforeDeclare;
driver.options.ignoreUnknownModules = options.ignoreUnknownModules;
driver.options.onlyLint =
options.mode == ImportVerilogOptions::Mode::OnlyLint;
driver.options.compilationFlags.emplace(
slang::ast::CompilationFlags::AllowUseBeforeDeclare,
options.allowUseBeforeDeclare);
driver.options.compilationFlags.emplace(
slang::ast::CompilationFlags::IgnoreUnknownModules,
options.ignoreUnknownModules);
driver.options.compilationFlags.emplace(
slang::ast::CompilationFlags::LintMode,
options.mode == ImportVerilogOptions::Mode::OnlyLint);
driver.options.topModules = options.topModules;
driver.options.paramOverrides = options.paramOverrides;

driver.options.errorLimit = options.errorLimit;
driver.options.warningOptions = options.warningOptions;
driver.options.suppressWarningsPaths = options.suppressWarningsPaths;

driver.options.singleUnit = options.singleUnit;
driver.options.libraryFiles = options.libraryFiles;

for (auto &dir : sourceMgr.getIncludeDirs())
driver.options.includeDirs.push_back(dir);

return success(driver.processOptions());
}
Expand Down Expand Up @@ -318,12 +330,12 @@ LogicalResult ImportDriver::preprocessVerilog(llvm::raw_ostream &os) {
diagnostics, optionBag);
// Sources have to be pushed in reverse, as they form a stack in the
// preprocessor. Last pushed source is processed first.
for (auto &buffer : slang::make_reverse_range(driver.buffers))
for (auto &buffer : std::views::reverse(driver.sourceLoader.loadSources()))
preprocessor.pushSource(buffer);
if (failed(preprocessAndPrint(preprocessor)))
return failure();
} else {
for (auto &buffer : driver.buffers) {
for (auto &buffer : driver.sourceLoader.loadSources()) {
slang::BumpAllocator alloc;
slang::Diagnostics diagnostics;
slang::parsing::Preprocessor preprocessor(driver.sourceManager, alloc,
Expand All @@ -341,28 +353,15 @@ LogicalResult ImportDriver::preprocessVerilog(llvm::raw_ostream &os) {
// Entry Points
//===----------------------------------------------------------------------===//

/// Execute a callback and report any thrown exceptions as "internal slang
/// error" MLIR diagnostics.
static LogicalResult
catchExceptions(llvm::function_ref<LogicalResult()> callback) {
try {
return callback();
} catch (const std::exception &e) {
return emitError(UnknownLoc(), "internal slang error: ") << e.what();
}
}

/// Parse the specified Verilog inputs into the specified MLIR context.
LogicalResult circt::importVerilog(SourceMgr &sourceMgr,
MLIRContext *mlirContext, TimingScope &ts,
ModuleOp module,
const ImportVerilogOptions *options) {
return catchExceptions([&] {
ImportDriver importDriver(mlirContext, ts, options);
if (failed(importDriver.prepareDriver(sourceMgr)))
return failure();
return importDriver.importVerilog(module);
});
ImportDriver importDriver(mlirContext, ts, options);
if (failed(importDriver.prepareDriver(sourceMgr)))
return failure();
return importDriver.importVerilog(module);
}

/// Run the files in a source manager through Slang's Verilog preprocessor and
Expand All @@ -371,12 +370,10 @@ LogicalResult circt::preprocessVerilog(SourceMgr &sourceMgr,
MLIRContext *mlirContext,
TimingScope &ts, llvm::raw_ostream &os,
const ImportVerilogOptions *options) {
return catchExceptions([&] {
ImportDriver importDriver(mlirContext, ts, options);
if (failed(importDriver.prepareDriver(sourceMgr)))
return failure();
return importDriver.preprocessVerilog(os);
});
ImportDriver importDriver(mlirContext, ts, options);
if (failed(importDriver.prepareDriver(sourceMgr)))
return failure();
return importDriver.preprocessVerilog(os);
}

/// Entry point as an MLIR translation.
Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ struct Context {
/// failure if an error occurs. Returns a null value if the formatted string
/// is trivially empty. Otherwise returns the formatted string.
FailureOr<Value> convertFormatString(
slang::span<const slang::ast::Expression *const> arguments, Location loc,
std::span<const slang::ast::Expression *const> arguments, Location loc,
moore::IntFormat defaultFormat = moore::IntFormat::Decimal,
bool appendNewline = false);

Expand Down
4 changes: 2 additions & 2 deletions lib/Conversion/ImportVerilog/Structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,15 +602,15 @@ Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
using slang::ast::PortSymbol;
using slang::ast::TypeParameterSymbol;

auto parameters = module->parameters;
auto parameters = module->getParameters();
bool hasModuleSame = false;
// If there is already exist a module that has the same name with this
// module ,has the same parent scope and has the same parameters we can
// define this module is a duplicate module
for (auto const &existingModule : modules) {
if (module->getDeclaringDefinition() ==
existingModule.getFirst()->getDeclaringDefinition()) {
auto moduleParameters = existingModule.getFirst()->parameters;
auto moduleParameters = existingModule.getFirst()->getParameters();
hasModuleSame = true;
for (auto it1 = parameters.begin(), it2 = moduleParameters.begin();
it1 != parameters.end() && it2 != moduleParameters.end();
Expand Down
2 changes: 1 addition & 1 deletion test/circt-verilog/commandline.sv
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// REQUIRES: slang

// CHECK-HELP: OVERVIEW: Verilog and SystemVerilog frontend
// CHECK-VERSION: slang version 3.
// CHECK-VERSION: slang version 7.
2 changes: 1 addition & 1 deletion test/circt-verilog/preprocess-errors.sv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: circt-verilog %s -E --verify-diagnostics
// REQUIRES: slang

// expected-error @below {{could not find or open include file}}
// expected-error @below {{'unknown.sv': No such file or directory}}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this error message comes from the system / OS, so you may find this test fails on a different OS than the one you tested on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've got a sharp eye, thanks!

`include "unknown.sv"
Loading