diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ced463411..d15050f9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: # Black, the python code formatter - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black exclude: ^(docs) diff --git a/CMakeLists.txt b/CMakeLists.txt index a904730dd..c97968229 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) # target_ specific commands. if(CMAKE_SYSTEM_NAME MATCHES "Windows") add_compile_definitions(WIN32 _WINDOWS NTDDI_VERSION=0x06010000 - _WIN32_WINNT=0x0601 UNICODE _UNICODE) + _WIN32_WINNT=0x0601) add_compile_definitions(_SCL_SECURE_NO_WARNINGS _CRT_SECURE_NO_WARNINGS) add_compile_definitions(_CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_WARNINGS) endif() diff --git a/bindings/python/ASTBindings.cpp b/bindings/python/ASTBindings.cpp index 2f39b767a..2b41abd04 100644 --- a/bindings/python/ASTBindings.cpp +++ b/bindings/python/ASTBindings.cpp @@ -429,7 +429,8 @@ void registerAST(py::module_& m) { m, "StreamingConcatenationExpression"); streamConcatExpr.def_readonly("sliceSize", &StreamingConcatenationExpression::sliceSize) .def_property_readonly("isFixedSize", &StreamingConcatenationExpression::isFixedSize) - .def_property_readonly("bitstreamWidth", &StreamingConcatenationExpression::bitstreamWidth) + .def_property_readonly("bitstreamWidth", + &StreamingConcatenationExpression::getBitstreamWidth) .def_property_readonly("streams", &StreamingConcatenationExpression::streams); py::class_(streamConcatExpr, diff --git a/bindings/python/TypeBindings.cpp b/bindings/python/TypeBindings.cpp index 57fda77d4..6b8c8c744 100644 --- a/bindings/python/TypeBindings.cpp +++ b/bindings/python/TypeBindings.cpp @@ -22,7 +22,7 @@ void registerTypes(py::module_& m) { py::class_(m, "Type") .def_property_readonly("canonicalType", &Type::getCanonicalType) .def_property_readonly("bitWidth", &Type::getBitWidth) - .def_property_readonly("bitstreamWidth", &Type::bitstreamWidth) + .def_property_readonly("bitstreamWidth", &Type::getBitstreamWidth) .def_property_readonly("selectableWidth", &Type::getSelectableWidth) .def_property_readonly("isSigned", &Type::isSigned) .def_property_readonly("isFourState", &Type::isFourState) diff --git a/include/slang/ast/expressions/OperatorExpressions.h b/include/slang/ast/expressions/OperatorExpressions.h index 12645260a..48e7df832 100644 --- a/include/slang/ast/expressions/OperatorExpressions.h +++ b/include/slang/ast/expressions/OperatorExpressions.h @@ -253,7 +253,7 @@ class SLANG_EXPORT StreamingConcatenationExpression : public Expression { public: /// The size of the blocks to slice and reorder: if 0, this is a left-to-right /// concatenation. Otherwise, it's a right-to-left concatenation. - const size_t sliceSize; + const uint32_t sliceSize; struct StreamExpression { not_null operand; @@ -261,14 +261,14 @@ class SLANG_EXPORT StreamingConcatenationExpression : public Expression { std::optional constantWithWidth; }; - StreamingConcatenationExpression(const Type& type, size_t sliceSize, + StreamingConcatenationExpression(const Type& type, uint32_t sliceSize, uint32_t bitstreamWidth, std::span streams, SourceRange sourceRange) : Expression(ExpressionKind::Streaming, type, sourceRange), - sliceSize(sliceSize), streams_(streams) {} + sliceSize(sliceSize), streams_(streams), bitstreamWidth(bitstreamWidth) {} bool isFixedSize() const; - size_t bitstreamWidth() const; + uint32_t getBitstreamWidth() const { return bitstreamWidth; } std::span streams() const { return streams_; } @@ -293,6 +293,7 @@ class SLANG_EXPORT StreamingConcatenationExpression : public Expression { private: std::span streams_; + uint32_t bitstreamWidth; }; /// Denotes a range of values by providing expressions for the lower and upper diff --git a/include/slang/ast/symbols/ClassSymbols.h b/include/slang/ast/symbols/ClassSymbols.h index 972a9b66d..8931c0967 100644 --- a/include/slang/ast/symbols/ClassSymbols.h +++ b/include/slang/ast/symbols/ClassSymbols.h @@ -86,6 +86,13 @@ class SLANG_EXPORT ClassType : public Type, public Scope { /// invoke that method. Otherwise returns nullptr. const Expression* getBaseConstructorCall() const; + /// Gets $bits of the type. Returns zero if the type does not have a statically known size. + uint32_t getBitstreamWidth() const { + if (!cachedBitstreamWidth) + computeSize(); + return *cachedBitstreamWidth; + } + static const Symbol& fromSyntax(const Scope& scope, const syntax::ClassDeclarationSyntax& syntax); @@ -113,13 +120,15 @@ class SLANG_EXPORT ClassType : public Type, public Scope { void handleImplements(const syntax::ImplementsClauseSyntax& implementsClause, const ASTContext& context, function_ref insertCB) const; + void computeSize() const; mutable const Type* baseClass = nullptr; mutable const Symbol* baseConstructor = nullptr; - mutable std::optional baseConstructorCall; mutable const ForwardingTypedefSymbol* firstForward = nullptr; mutable std::span implementsIfaces; mutable std::span declaredIfaces; + mutable std::optional baseConstructorCall; + mutable std::optional cachedBitstreamWidth; SymbolIndex headerIndex; }; diff --git a/include/slang/ast/types/AllTypes.h b/include/slang/ast/types/AllTypes.h index ce296c84e..c20b781b8 100644 --- a/include/slang/ast/types/AllTypes.h +++ b/include/slang/ast/types/AllTypes.h @@ -164,9 +164,10 @@ class SLANG_EXPORT FixedSizeUnpackedArrayType : public Type { const Type& elementType; ConstantRange range; uint32_t selectableWidth; + uint32_t bitstreamWidth; FixedSizeUnpackedArrayType(const Type& elementType, ConstantRange range, - uint32_t selectableWidth); + uint32_t selectableWidth, uint32_t bitstreamWidth); static const Type& fromDims(const Scope& scope, const Type& elementType, std::span dimensions, @@ -253,6 +254,7 @@ class SLANG_EXPORT UnpackedStructType : public Type, public Scope { public: std::span fields; uint32_t selectableWidth = 0; + uint32_t bitstreamWidth = 0; int systemId; UnpackedStructType(Compilation& compilation, SourceLocation loc, const ASTContext& context); @@ -287,6 +289,7 @@ class SLANG_EXPORT UnpackedUnionType : public Type, public Scope { public: std::span fields; uint32_t selectableWidth = 0; + uint32_t bitstreamWidth = 0; int systemId; bool isTagged; diff --git a/include/slang/ast/types/Type.h b/include/slang/ast/types/Type.h index 6e823500c..1afcb6f08 100644 --- a/include/slang/ast/types/Type.h +++ b/include/slang/ast/types/Type.h @@ -47,6 +47,9 @@ SLANG_BITMASK(IntegralFlags, Reg) /// class SLANG_EXPORT Type : public Symbol { public: + /// The maximum size in bits of any fixed size type. + static constexpr uint32_t MaxBitWidth = uint32_t(INT32_MAX); + /// Gets the canonical type for this type, which involves unwrapping any type aliases. const Type& getCanonicalType() const { if (!canonical) @@ -59,7 +62,7 @@ class SLANG_EXPORT Type : public Symbol { bitwidth_t getBitWidth() const; /// Gets $bits of the type. Returns zero if the type does not have a statically known size. - size_t bitstreamWidth() const; + uint32_t getBitstreamWidth() const; /// Gets the "selectable" width of the type. This is the size of the object when determining /// whether assignments to the static portions overlap with each other. Dynamically sized diff --git a/include/slang/util/CommandLine.h b/include/slang/util/CommandLine.h index 41c5d8b1b..b67bb63c7 100644 --- a/include/slang/util/CommandLine.h +++ b/include/slang/util/CommandLine.h @@ -290,12 +290,6 @@ class SLANG_EXPORT CommandLine { /// @return true on success, false if an errors occurs. bool parse(int argc, const char* const argv[]); -#if defined(_WIN32) - /// Parse the provided command line (MSVC wchar-style). - /// @return true on success, false if an errors occurs. - bool parse(int argc, const wchar_t* const argv[]); -#endif - /// Contains various options to control parsing of command flags. struct ParseOptions { /// If set to true, comments will be parsed and ignored. diff --git a/include/slang/util/OS.h b/include/slang/util/OS.h index 940ae44b8..aca1e7437 100644 --- a/include/slang/util/OS.h +++ b/include/slang/util/OS.h @@ -19,6 +19,9 @@ namespace slang { /// A collection of various OS-specific utility functions. class SLANG_EXPORT OS { public: + /// Does initial one-time setup of OS console. + static void setupConsole(); + /// Tries to enable color output support for stdout and stderr. /// @return true if successful and false otherwise. static bool tryEnableColors(); @@ -39,6 +42,9 @@ class SLANG_EXPORT OS { /// Note that the buffer will be null-terminated. static std::error_code readFile(const std::filesystem::path& path, SmallVector& buffer); + /// Writes the given contents to the specified file. + static void writeFile(const std::filesystem::path& path, std::string_view contents); + /// Prints text to stdout. static void print(std::string_view text); diff --git a/include/slang/util/SmallVector.h b/include/slang/util/SmallVector.h index e23e7444d..a8609465f 100644 --- a/include/slang/util/SmallVector.h +++ b/include/slang/util/SmallVector.h @@ -453,7 +453,7 @@ class SmallVectorBase { /// Indicates whether we are still "small", which means we are still on the stack. [[nodiscard]] constexpr bool isSmall() const noexcept { - return (void*)data_ == (void*)firstElement; + return (const void*)data_ == (const void*)firstElement; } protected: diff --git a/include/slang/util/String.h b/include/slang/util/String.h index 993f307fc..c33d88c2d 100644 --- a/include/slang/util/String.h +++ b/include/slang/util/String.h @@ -75,28 +75,18 @@ inline char charToLower(char c) { int editDistance(std::string_view left, std::string_view right, bool allowReplacements = true, int maxDistance = 0); -/// C++20 is dumb and provides no way to get a std::string with the UTF-8 -/// contents of a fs::path, so we have to use this method to copy the chars :( -std::string getU8Str(const std::filesystem::path& path); - #if defined(_WIN32) -/// Widens the provided UTF8 string into UTF16 wchars. -SLANG_EXPORT std::wstring widen(std::string_view str); - -/// Narrows the provided UTF16 string into UTF8. -SLANG_EXPORT std::string narrow(std::wstring_view str); +/// Gets a string representation of the given path, in UTF-8 encoding. +inline std::string getU8Str(const std::filesystem::path& path) { + return path.string(); +} #else -/// Widens the provided UTF8 string into UTF16 wchars. -inline std::string_view widen(std::string_view str) { - return str; -} - -/// Narrows the provided UTF16 string into UTF8. -inline std::string_view narrow(std::string_view str) { - return str; +/// Gets a string representation of the given path, in UTF-8 encoding. +inline const std::string& getU8Str(const std::filesystem::path& path) { + return path.native(); } #endif diff --git a/scripts/diagnostics.txt b/scripts/diagnostics.txt index 868d1e9e4..d8ef14fe7 100644 --- a/scripts/diagnostics.txt +++ b/scripts/diagnostics.txt @@ -793,7 +793,7 @@ error InvalidAssociativeIndexType "index type cannot be or contain floating poin error PackedArrayNotIntegral "packed array elements must be of integral type (not {})" error PackedTypeTooLarge "packed type of {} bits is too large; maximum width is {} bits" error ArrayDimTooLarge "array dimension of {} is too large; maximum number of elements is {}" -error ObjectTooLarge "object size of {} bits exceeds implementation limit of {} bits" +error ObjectTooLarge "object size exceeds implementation limit of {} bits" error InvalidUnionMember "untagged unions cannot have members of type {}" error VirtualInterfaceUnionMember "virtual interfaces cannot be used as members of unions" error InvalidArrayElemType "cannot declare an array with elements of type {}" @@ -926,7 +926,6 @@ subsystem ConstEval note NoteInCallTo "in call to '{}'" note NoteSkippingFrames "(skipping {} calls in backtrace; use --constexpr-backtrace-limit=0 to see all)" error ConstEvalNonConstVariable "reference to non-constant variable '{}' is not allowed in a constant expression" -error ConstEvalBitsNotFixedSize "$bits argument type {} is not fixed-size in a constant expression" error ConstEvalBitstreamCastSize "bit-stream casting argument size {} cannot fit casting type {} in a constant expression" error ConstEvalReplicationCountInvalid "string replication count {} is invalid in a constant expression" error ConstEvalHierarchicalName "reference to '{}' by hierarchical name is not allowed in a constant expression" diff --git a/scripts/win32.manifest b/scripts/win32.manifest new file mode 100644 index 000000000..324aa6d6a --- /dev/null +++ b/scripts/win32.manifest @@ -0,0 +1,9 @@ + + + + + + UTF-8 + + + diff --git a/source/ast/Bitstream.cpp b/source/ast/Bitstream.cpp index 00d5593ea..2a7adb93c 100644 --- a/source/ast/Bitstream.cpp +++ b/source/ast/Bitstream.cpp @@ -98,7 +98,7 @@ static std::pair dynamicBitstreamSize(const Type& type, Bitstrea static std::pair dynamicBitstreamSize( const StreamingConcatenationExpression& concat, BitstreamSizeMode mode) { if (concat.isFixedSize()) - return {0, concat.bitstreamWidth()}; + return {0, concat.getBitstreamWidth()}; size_t multiplier = 0, fixedSize = 0; for (auto& stream : concat.streams()) { @@ -117,7 +117,6 @@ static std::pair dynamicBitstreamSize( } else { multiplierStream = fixedSizeElem; - fixedSizeStream = fixedSizeElem; } } else { @@ -197,7 +196,7 @@ bool Bitstream::dynamicSizesMatch(const T1& destination, const T2& source) { template static size_t bitstreamCastRemainingSize(const T& destination, size_t srcSize) { if (destination.isFixedSize()) { - auto destSize = destination.bitstreamWidth(); + auto destSize = destination.getBitstreamWidth(); if (destSize != srcSize) return srcSize + 1; // cannot fit @@ -375,7 +374,7 @@ static ConstantValue unpackBitstream(const Type& type, PackIterator& iter, // empty. if (dynamicSize > 0) { auto elemWidth = type.getArrayElementType()->isFixedSize() - ? type.getArrayElementType()->bitstreamWidth() + ? type.getArrayElementType()->getBitstreamWidth() : dynamicSize; // If element is dynamically sized, num = 1 @@ -428,7 +427,7 @@ ConstantValue Bitstream::evaluateCast(const Type& type, ConstantValue&& value, } } else { // implicit streaming concatenation conversion - auto targetWidth = type.bitstreamWidth(); + auto targetWidth = type.getBitstreamWidth(); if (targetWidth < srcSize) { if (type.isFixedSize()) { context.addDiag(diag::BadStreamSize, sourceRange) << targetWidth << srcSize; @@ -488,7 +487,7 @@ bool Bitstream::canBeTarget(const StreamingConcatenationExpression& lhs, const E if (context.inUnevaluatedBranch()) return true; // No size check in an unevaluated conditional branch - size_t targetWidth = lhs.bitstreamWidth(); + size_t targetWidth = lhs.getBitstreamWidth(); size_t sourceWidth; bool good = true; @@ -496,12 +495,12 @@ bool Bitstream::canBeTarget(const StreamingConcatenationExpression& lhs, const E if (!rhs.type->isFixedSize()) return true; // Sizes checked at constant evaluation or runtime - sourceWidth = rhs.type->bitstreamWidth(); + sourceWidth = rhs.type->getBitstreamWidth(); good = targetWidth <= sourceWidth; } else { auto& source = rhs.as(); - sourceWidth = source.bitstreamWidth(); + sourceWidth = source.getBitstreamWidth(); if (lhs.isFixedSize() && source.isFixedSize()) good = targetWidth == sourceWidth; else @@ -538,8 +537,8 @@ bool Bitstream::canBeSource(const Type& target, const StreamingConcatenationExpr if (!target.isFixedSize()) return true; // Sizes checked at constant evaluation or runtime - auto targetWidth = target.bitstreamWidth(); - auto sourceWidth = rhs.bitstreamWidth(); + auto targetWidth = target.getBitstreamWidth(); + auto sourceWidth = rhs.getBitstreamWidth(); if (targetWidth < sourceWidth) { auto& diag = context.addDiag(diag::BadStreamSize, assignLoc) << targetWidth << sourceWidth; diag << rhs.sourceRange; @@ -554,7 +553,7 @@ bool Bitstream::isBitstreamCast(const Type& type, const StreamingConcatenationEx return false; if (type.isFixedSize() && arg.isFixedSize()) - return type.bitstreamWidth() == arg.bitstreamWidth(); + return type.getBitstreamWidth() == arg.getBitstreamWidth(); return dynamicSizesMatch(type, arg); } @@ -705,9 +704,9 @@ static bool unpackConcatenation(const StreamingConcatenationExpression& lhs, Pac auto elemType = arrayType.getArrayElementType(); SLANG_ASSERT(elemType); + // TODO: overflow + auto withSize = elemType->getBitstreamWidth() * with.width(); if (dynamicSize > 0 && !stream.constantWithWidth) { - // TODO: overflow - auto withSize = elemType->bitstreamWidth() * with.width(); if (withSize >= dynamicSize) dynamicSize = 0; else @@ -721,7 +720,8 @@ static bool unpackConcatenation(const StreamingConcatenationExpression& lhs, Pac // We already checked for overflow earlier so it's fine to create this // temporary array result type as-is. FixedSizeUnpackedArrayType rvalueType( - *elemType, with, elemType->getSelectableWidth() * with.width()); + *elemType, with, elemType->getSelectableWidth() * with.width(), + (uint32_t)withSize); rvalue = unpackBitstream(rvalueType, iter, iterEnd, bitOffset, dynamicSize); } @@ -777,7 +777,7 @@ ConstantValue Bitstream::evaluateTarget(const StreamingConcatenationExpression& return nullptr; auto srcSize = rvalue.bitstreamWidth(); - auto targetWidth = lhs.bitstreamWidth(); + auto targetWidth = lhs.getBitstreamWidth(); size_t dynamicSize = 0; if (rhs.kind == ExpressionKind::Streaming) { diff --git a/source/ast/ElabVisitors.h b/source/ast/ElabVisitors.h index cb0bccec8..2e2a1ce14 100644 --- a/source/ast/ElabVisitors.h +++ b/source/ast/ElabVisitors.h @@ -159,6 +159,7 @@ struct DiagnosticVisitor : public ASTVisitor { return; symbol.getBaseConstructorCall(); + symbol.getBitstreamWidth(); } void handle(const CovergroupType& symbol) { @@ -385,7 +386,7 @@ struct DiagnosticVisitor : public ASTVisitor { // Once everything has been visited, go back over and check things that might // have been influenced by visiting later symbols. Unfortunately visiting // a specialization can trigger more specializations to be made for the - // same or other generic classs, so we need to be careful here when iterating. + // same or other generic class, so we need to be careful here when iterating. SmallSet visitedSpecs; SmallVector toVisit; bool didSomething; diff --git a/source/ast/builtins/QueryFuncs.cpp b/source/ast/builtins/QueryFuncs.cpp index 856f875e5..553f6f91b 100644 --- a/source/ast/builtins/QueryFuncs.cpp +++ b/source/ast/builtins/QueryFuncs.cpp @@ -52,12 +52,7 @@ class BitsFunction : public SystemSubroutine { size_t width; if (args[0]->type->isFixedSize()) { - width = args[0]->type->bitstreamWidth(); - } - else if (args[0]->kind == ExpressionKind::DataType) { - auto& diag = context.addDiag(diag::ConstEvalBitsNotFixedSize, args[0]->sourceRange); - diag << *args[0]->type; - return nullptr; + width = args[0]->type->getBitstreamWidth(); } else { ConstantValue cv = args[0]->eval(context); diff --git a/source/ast/expressions/OperatorExpressions.cpp b/source/ast/expressions/OperatorExpressions.cpp index 4b72b71f9..20a908ec4 100644 --- a/source/ast/expressions/OperatorExpressions.cpp +++ b/source/ast/expressions/OperatorExpressions.cpp @@ -20,6 +20,8 @@ #include "slang/ast/types/AllTypes.h" #include "slang/diagnostics/ConstEvalDiags.h" #include "slang/diagnostics/ExpressionsDiags.h" +#include "slang/diagnostics/TypesDiags.h" +#include "slang/numeric/MathUtils.h" #include "slang/parsing/LexerFacts.h" #include "slang/syntax/AllSyntax.h" @@ -359,8 +361,6 @@ Expression& UnaryExpression::fromSyntax(Compilation& compilation, // Supported for both integral and real types. Result is same as input type. // The operand must also be an assignable lvalue. - // TODO: detect and warn for multiple reads and writes of a single variable in an - // expression good = type->isNumeric(); result->type = type; if (!operand.requireLValue(context, syntax.operatorToken.location())) { @@ -1259,7 +1259,6 @@ Expression& ConcatenationExpression::fromSyntax(Compilation& compilation, return badExpr(compilation, nullptr); } - // TODO: check overflow handling bool bad = false; bool anyDynamic = false; size_t totalElems = 0; @@ -1674,13 +1673,12 @@ Expression& StreamingConcatenationExpression::fromSyntax( // Streaming concatenation is self-determined and its size/type should not be affected // by assignmentTarget. const bool isDestination = !assignmentTarget; - const bool isRightToLeft = syntax.operatorToken.kind == TokenKind::LeftShift; - size_t sliceSize = 0; + uint32_t sliceSize = 0; auto badResult = [&]() -> Expression& { return badExpr(comp, comp.emplace( - comp.getErrorType(), sliceSize, + comp.getErrorType(), sliceSize, 0u, std::span(), syntax.sourceRange())); }; @@ -1703,14 +1701,14 @@ Expression& StreamingConcatenationExpression::fromSyntax( diag.addNote(diag::NoteDeclarationHere, sliceExpr.type->location); return badResult(); } - sliceSize = sliceExpr.type->bitstreamWidth(); + sliceSize = sliceExpr.type->getBitstreamWidth(); } else { // It shall be an error for the value to be zero or negative. std::optional count = context.evalInteger(sliceExpr); if (!context.requireGtZero(count, sliceExpr.sourceRange)) return badResult(); - sliceSize = static_cast(*count); + sliceSize = static_cast(*count); } if (!isRightToLeft) { @@ -1725,6 +1723,7 @@ Expression& StreamingConcatenationExpression::fromSyntax( sliceSize = 1; } + uint64_t bitstreamWidth = 0; SmallVector buffer; for (const auto argSyntax : syntax.expressions) { Expression* arg; @@ -1741,15 +1740,14 @@ Expression& StreamingConcatenationExpression::fromSyntax( if (arg->bad()) return badResult(); + const Type* argType = arg->type; const Expression* withExpr = nullptr; std::optional constantWithWidth; if (argSyntax->withRange) { // The expression before the with can be any one-dimensional unpacked array - // (including a queue). Interpreted as fixed-sized unpacked arrays, - // dynamic arrays, or queues of fixed-size elements. - auto& arrayType = *arg->type; - if (!arrayType.isUnpackedArray() || arrayType.isAssociativeArray() || - !arrayType.getArrayElementType()->isFixedSize()) { + // (including a queue). + if (!argType->isUnpackedArray() || argType->isAssociativeArray() || + !argType->getArrayElementType()->isFixedSize()) { context.addDiag(diag::BadStreamWithType, arg->sourceRange); return badResult(); } @@ -1760,6 +1758,8 @@ Expression& StreamingConcatenationExpression::fromSyntax( return badResult(); // Try to get the bounds of the selection, if they are constant. + // If they are constant, we've already done bounds checking and + // max size checking on them. EvalContext evalCtx(context); auto range = withExpr->evalSelector(evalCtx); if (range) @@ -1768,26 +1768,53 @@ Expression& StreamingConcatenationExpression::fromSyntax( if (argSyntax->expression->kind != SyntaxKind::StreamingConcatenationExpression) { // Unpacked unions get "unwrapped" to their first member when streaming. - const Type* type = arg->type; - if (type->isUnpackedUnion() && !type->isTaggedUnion()) { - auto& uu = type->getCanonicalType().as(); + if (argType->isUnpackedUnion() && !argType->isTaggedUnion()) { + auto& uu = argType->getCanonicalType().as(); auto members = uu.members(); if (members.begin() != members.end()) - type = &members.begin()->as().getType(); + argType = &members.begin()->as().getType(); } - if (!type->isBitstreamType(isDestination)) { + if (!argType->isBitstreamType(isDestination)) { context.addDiag(diag::BadStreamExprType, arg->sourceRange) << *arg->type; return badResult(); } } + uint32_t argWidth = 0; + if (arg->kind == ExpressionKind::Streaming) { + argWidth = arg->as().getBitstreamWidth(); + } + else if (withExpr) { + if (constantWithWidth) { + auto rangeBits = checkedMulU32(argType->getArrayElementType()->getBitstreamWidth(), + *constantWithWidth); + if (!rangeBits || rangeBits > Type::MaxBitWidth) { + context.addDiag(diag::ObjectTooLarge, withExpr->sourceRange) + << Type::MaxBitWidth; + return badResult(); + } + + argWidth = *rangeBits; + } + } + else { + argWidth = argType->getBitstreamWidth(); + } + + bitstreamWidth += argWidth; + if (bitstreamWidth > Type::MaxBitWidth) { + context.addDiag(diag::ObjectTooLarge, syntax.sourceRange()) << Type::MaxBitWidth; + return badResult(); + } + buffer.push_back({arg, withExpr, constantWithWidth}); } // Streaming concatenation has no explicit type. Use void to prevent problems when // its type is passed to context-determined expressions. return *comp.emplace(comp.getVoidType(), sliceSize, + (uint32_t)bitstreamWidth, buffer.ccopy(comp), syntax.sourceRange()); } @@ -1855,31 +1882,6 @@ bool StreamingConcatenationExpression::isFixedSize() const { return true; } -size_t StreamingConcatenationExpression::bitstreamWidth() const { - size_t width = 0; - for (auto& stream : streams()) { - auto& operand = *stream.operand; - if (operand.kind == ExpressionKind::Streaming) { - width += operand.as().bitstreamWidth(); - } - else if (stream.withExpr) { - // TODO: overflow - size_t count = stream.constantWithWidth.value_or(1); - width += count * operand.type->getArrayElementType()->bitstreamWidth(); - } - else if (operand.type->isUnpackedUnion()) { - auto& uu = operand.type->getCanonicalType().as(); - auto members = uu.members(); - if (members.begin() != members.end()) - width += members.begin()->as().getType().bitstreamWidth(); - } - else { - width += operand.type->bitstreamWidth(); - } - } - return width; -} - Expression& OpenRangeExpression::fromSyntax(Compilation& comp, const OpenRangeExpressionSyntax& syntax, const ASTContext& context) { diff --git a/source/ast/symbols/ClassSymbols.cpp b/source/ast/symbols/ClassSymbols.cpp index 35450ee2e..920606dc8 100644 --- a/source/ast/symbols/ClassSymbols.cpp +++ b/source/ast/symbols/ClassSymbols.cpp @@ -710,6 +710,32 @@ void ClassType::handleImplements(const ImplementsClauseSyntax& implementsClause, implementsIfaces = implementsIfacesBuf.copy(comp); } +void ClassType::computeSize() const { + ensureElaborated(); + cachedBitstreamWidth = 0; + if (isInterface) + return; + + ASTContext context(*this, LookupLocation(this, uint32_t(headerIndex))); + + uint64_t totalWidth = 0; + bool hasDynamic = false; + for (auto& prop : membersOfType()) { + uint32_t width = prop.getType().getBitstreamWidth(); + if (width == 0) + hasDynamic = true; + + totalWidth += width; + if (totalWidth > MaxBitWidth) { + context.addDiag(diag::ObjectTooLarge, location) << MaxBitWidth; + return; + } + } + + if (!hasDynamic) + cachedBitstreamWidth = uint32_t(totalWidth); +} + void ClassType::serializeTo(ASTSerializer& serializer) const { serializer.write("isAbstract", isAbstract); serializer.write("isInterface", isInterface); diff --git a/source/ast/types/AllTypes.cpp b/source/ast/types/AllTypes.cpp index 150ad427f..f119bb80b 100644 --- a/source/ast/types/AllTypes.cpp +++ b/source/ast/types/AllTypes.cpp @@ -667,9 +667,11 @@ const Type& PackedArrayType::fromDim(const Scope& scope, const Type& elementType } FixedSizeUnpackedArrayType::FixedSizeUnpackedArrayType(const Type& elementType, ConstantRange range, - uint32_t selectableWidth) : + uint32_t selectableWidth, + uint32_t bitstreamWidth) : Type(SymbolKind::FixedSizeUnpackedArrayType, "", SourceLocation()), - elementType(elementType), range(range), selectableWidth(selectableWidth) { + elementType(elementType), range(range), selectableWidth(selectableWidth), + bitstreamWidth(bitstreamWidth) { } const Type& FixedSizeUnpackedArrayType::fromDims(const Scope& scope, const Type& elementType, @@ -690,15 +692,17 @@ const Type& FixedSizeUnpackedArrayType::fromDim(const Scope& scope, const Type& return elementType; auto& comp = scope.getCompilation(); - auto width = checkedMulU32(elementType.getSelectableWidth(), dim.width()); - const uint32_t maxWidth = uint32_t(INT32_MAX) + 1; - if (!width || width > maxWidth) { - uint64_t fullWidth = uint64_t(elementType.getSelectableWidth()) * dim.width(); - scope.addDiag(diag::ObjectTooLarge, sourceRange.get()) << fullWidth << maxWidth; + auto selectableWidth = checkedMulU32(elementType.getSelectableWidth(), dim.width()); + auto bitstreamWidth = checkedMulU32(elementType.getBitstreamWidth(), dim.width()); + + if (!selectableWidth || selectableWidth > MaxBitWidth || !bitstreamWidth || + bitstreamWidth > MaxBitWidth) { + scope.addDiag(diag::ObjectTooLarge, sourceRange.get()) << MaxBitWidth; return comp.getErrorType(); } - auto result = comp.emplace(elementType, dim, *width); + auto result = comp.emplace(elementType, dim, *selectableWidth, + *bitstreamWidth); if (auto syntax = sourceRange.syntax()) result->setSyntax(*syntax); @@ -858,6 +862,7 @@ const Type& UnpackedStructType::fromSyntax(const ASTContext& context, auto result = comp.emplace(comp, syntax.keyword.location(), context); uint32_t bitOffset = 0; + uint64_t bitstreamWidth = 0; SmallVector fields; for (auto member : syntax.members) { RandMode randMode = RandMode::None; @@ -887,16 +892,16 @@ const Type& UnpackedStructType::fromSyntax(const ASTContext& context, fields.push_back(field); bitOffset += field->getType().getSelectableWidth(); - const uint32_t maxWidth = uint32_t(INT32_MAX) + 1; - if (bitOffset > maxWidth) { - context.addDiag(diag::ObjectTooLarge, syntax.sourceRange()) - << bitOffset << maxWidth; + bitstreamWidth += field->getType().getBitstreamWidth(); + if (bitOffset > MaxBitWidth || bitstreamWidth > MaxBitWidth) { + context.addDiag(diag::ObjectTooLarge, syntax.sourceRange()) << MaxBitWidth; return comp.getErrorType(); } } } result->selectableWidth = bitOffset; + result->bitstreamWidth = (uint32_t)bitstreamWidth; result->fields = fields.copy(comp); for (auto field : result->fields) { // Force resolution of the initializer right away, otherwise nothing @@ -1047,6 +1052,8 @@ const Type& UnpackedUnionType::fromSyntax(const ASTContext& context, result->selectableWidth = std::max(result->selectableWidth, field->getType().getSelectableWidth()); + result->bitstreamWidth = std::max(result->bitstreamWidth, + field->getType().getBitstreamWidth()); } } diff --git a/source/ast/types/Type.cpp b/source/ast/types/Type.cpp index 890ad1fa6..e9af6849f 100644 --- a/source/ast/types/Type.cpp +++ b/source/ast/types/Type.cpp @@ -96,48 +96,20 @@ bitwidth_t Type::getBitWidth() const { return 0; } -size_t Type::bitstreamWidth() const { +uint32_t Type::getBitstreamWidth() const { auto& ct = getCanonicalType(); - size_t width = ct.getBitWidth(); - if (width > 0) - return width; - - // TODO: check for overflow - if (ct.isUnpackedArray()) { - if (ct.kind != SymbolKind::FixedSizeUnpackedArrayType) - return 0; - - auto& fsa = ct.as(); - return fsa.elementType.bitstreamWidth() * fsa.range.width(); - } - - if (ct.isUnpackedStruct()) { - auto& us = ct.as(); - for (auto field : us.fields) - width += field->getType().bitstreamWidth(); - return width; - } - - if (ct.isUnpackedUnion()) { - // Unpacked unions are not bitstream types but we support - // getting a bit width out of them anyway. - auto& us = ct.as(); - for (auto field : us.fields) - width = std::max(width, field->getType().bitstreamWidth()); - return width; - } - - if (ct.isClass()) { - auto& classType = ct.as(); - if (classType.isInterface) - return 0; - - for (auto& prop : classType.membersOfType()) - width += prop.getType().bitstreamWidth(); - return width; + switch (ct.kind) { + case SymbolKind::FixedSizeUnpackedArrayType: + return ct.as().bitstreamWidth; + case SymbolKind::UnpackedStructType: + return ct.as().bitstreamWidth; + case SymbolKind::UnpackedUnionType: + return ct.as().bitstreamWidth; + case SymbolKind::ClassType: + return ct.as().getBitstreamWidth(); + default: + return ct.getBitWidth(); } - - return 0; } uint32_t Type::getSelectableWidth() const { @@ -332,17 +304,8 @@ bool Type::isFixedSize() const { return true; } - if (ct.isClass()) { - auto& classType = ct.as(); - if (classType.isInterface) - return false; - - for (auto& prop : classType.membersOfType()) { - if (!prop.getType().isFixedSize()) - return false; - } - return true; - } + if (ct.isClass()) + return ct.as().getBitstreamWidth() > 0; return false; } @@ -683,7 +646,7 @@ bool Type::isBitstreamCastable(const Type& rhs) const { const Type* r = &rhs.getCanonicalType(); if (l->isBitstreamType(true) && r->isBitstreamType()) { if (l->isFixedSize() && r->isFixedSize()) - return l->bitstreamWidth() == r->bitstreamWidth(); + return l->getBitstreamWidth() == r->getBitstreamWidth(); else return Bitstream::dynamicSizesMatch(*l, *r); } diff --git a/source/driver/SourceLoader.cpp b/source/driver/SourceLoader.cpp index 66cb28d32..85c6402af 100644 --- a/source/driver/SourceLoader.cpp +++ b/source/driver/SourceLoader.cpp @@ -27,7 +27,7 @@ SourceLoader::SourceLoader(SourceManager& sourceManager) : sourceManager(sourceM uniqueExtensions.emplace(".v"sv); uniqueExtensions.emplace(".sv"sv); for (auto ext : uniqueExtensions) - searchExtensions.emplace_back(widen(ext)); + searchExtensions.emplace_back(ext); } void SourceLoader::addFiles(std::string_view pattern) { @@ -45,7 +45,7 @@ void SourceLoader::addSearchDirectories(std::string_view pattern) { std::error_code ec; svGlob({}, pattern, GlobMode::Directories, directories, /* expandEnvVars */ false, ec); if (ec) { - addError(widen(pattern), ec); + addError(pattern, ec); return; } @@ -54,7 +54,7 @@ void SourceLoader::addSearchDirectories(std::string_view pattern) { void SourceLoader::addSearchExtension(std::string_view extension) { if (uniqueExtensions.emplace(extension).second) - searchExtensions.emplace_back(widen(extension)); + searchExtensions.emplace_back(extension); } static std::string_view getPathFromSpec(const FilePathSpecSyntax& syntax) { @@ -79,7 +79,7 @@ void SourceLoader::addLibraryMapsInternal(std::string_view pattern, const fs::pa svGlob(basePath, pattern, GlobMode::Files, files, expandEnvVars, ec); if (ec) { - addError(widen(pattern), ec); + addError(pattern, ec); return; } @@ -368,7 +368,7 @@ void SourceLoader::addFilesInternal(std::string_view pattern, const fs::path& ba std::error_code ec; auto rank = svGlob(basePath, pattern, GlobMode::Files, files, expandEnvVars, ec); if (ec) { - addError(widen(pattern), ec); + addError(pattern, ec); return; } @@ -425,7 +425,7 @@ void SourceLoader::createLibrary(const LibraryDeclarationSyntax& syntax, const f /* expandEnvVars */ true, ec); if (ec) { - addError(widen(spec), ec); + addError(spec, ec); } else { auto& lid = library->includeDirs; diff --git a/source/text/Glob.cpp b/source/text/Glob.cpp index 842a34eac..e3345aafe 100644 --- a/source/text/Glob.cpp +++ b/source/text/Glob.cpp @@ -88,7 +88,7 @@ static void globDir(const fs::path& path, std::string_view pattern, SmallVector< results.reserve(results.size() + local.size()); for (auto&& p : local) { - if (matches(narrow(p.filename().native()), pattern)) + if (matches(getU8Str(p.filename()), pattern)) results.emplace_back(std::move(p)); } } @@ -220,10 +220,10 @@ SLANG_EXPORT GlobRank svGlob(const fs::path& basePath, std::string_view pattern, patternStr.push_back(c); } - patternPath = fs::path(widen(patternStr)); + patternPath = fs::path(patternStr); } else { - patternPath = fs::path(widen(pattern)); + patternPath = fs::path(pattern); } // Normalize the path to remove duplicate separators, figure out @@ -234,11 +234,11 @@ SLANG_EXPORT GlobRank svGlob(const fs::path& basePath, std::string_view pattern, GlobRank rank; bool anyWildcards = false; if (patternPath.has_root_path()) { - rank = svGlobInternal(patternPath.root_path(), narrow(patternPath.relative_path().native()), - mode, local, anyWildcards); + rank = svGlobInternal(patternPath.root_path(), getU8Str(patternPath.relative_path()), mode, + local, anyWildcards); } else { - rank = svGlobInternal(basePath, narrow(patternPath.native()), mode, local, anyWildcards); + rank = svGlobInternal(basePath, getU8Str(patternPath), mode, local, anyWildcards); } // Results paths are always made canonical. diff --git a/source/text/SourceManager.cpp b/source/text/SourceManager.cpp index 9f32be49b..1c3fed60f 100644 --- a/source/text/SourceManager.cpp +++ b/source/text/SourceManager.cpp @@ -257,7 +257,7 @@ SourceBuffer SourceManager::assignBuffer(std::string_view bufferPath, SmallVecto SourceLocation includedFrom, const SourceLibrary* library) { // first see if we have this file cached - fs::path path(widen(bufferPath)); + fs::path path(bufferPath); auto pathStr = getU8Str(path); { std::shared_lock lock(mutex); @@ -283,7 +283,7 @@ SourceManager::BufferOrError SourceManager::readHeader(std::string_view path, bool isSystemPath) { // if the header is specified as an absolute path, just do a straight lookup SLANG_ASSERT(!path.empty()); - fs::path p = widen(path); + fs::path p = path; if (p.is_absolute()) return openCached(p, includedFrom, library); @@ -345,15 +345,15 @@ void SourceManager::addLineDirective(SourceLocation location, size_t lineNum, st return; fs::path full; - fs::path linePath = widen(name); + fs::path linePath = name; std::error_code ec; if (!disableProximatePaths && linePath.has_relative_path()) full = linePath.lexically_proximate(fs::current_path(ec)); else - full = fs::path(widen(info->data->name)).replace_filename(linePath); + full = fs::path(info->data->name).replace_filename(linePath); size_t sourceLineNum = getRawLineNumber(fileLocation, lock); - info->lineDirectives.emplace_back(getU8Str(full), sourceLineNum, lineNum, level); + info->lineDirectives.emplace_back(std::string(getU8Str(full)), sourceLineNum, lineNum, level); } void SourceManager::addDiagnosticDirective(SourceLocation location, std::string_view name, diff --git a/source/util/CommandLine.cpp b/source/util/CommandLine.cpp index 49544cedc..9f63f4de6 100644 --- a/source/util/CommandLine.cpp +++ b/source/util/CommandLine.cpp @@ -167,21 +167,6 @@ bool CommandLine::parse(int argc, const char* const argv[]) { return parse(args); } -#if defined(_WIN32) - -bool CommandLine::parse(int argc, const wchar_t* const argv[]) { - SmallVector storage{size_t(argc), UninitializedTag()}; - SmallVector args{size_t(argc), UninitializedTag()}; - for (int i = 0; i < argc; i++) { - storage.push_back(narrow(argv[i])); - args.push_back(storage.back()); - } - - return parse(args); -} - -#endif - bool CommandLine::parse(std::string_view argList, ParseOptions options) { bool hasArg = false; std::string current; @@ -316,7 +301,7 @@ bool CommandLine::parse(std::span args, ParseOptions opt if (args.empty()) SLANG_THROW(std::runtime_error("Expected at least one argument")); - programName = getU8Str(fs::path(widen(args[0])).filename()); + programName = getU8Str(fs::path(args[0]).filename()); args = args.subspan(1); } @@ -641,7 +626,7 @@ std::string CommandLine::Option::set(std::string_view name, std::string_view val std::string pathMem; if (flags.has(CommandLineFlags::FilePath) && !value.empty() && value != "-") { std::error_code ec; - fs::path path = fs::weakly_canonical(widen(value), ec); + fs::path path = fs::weakly_canonical(value, ec); if (!ec) { pathMem = getU8Str(path); value = pathMem; diff --git a/source/util/OS.cpp b/source/util/OS.cpp index 795abeea1..c13d485ae 100644 --- a/source/util/OS.cpp +++ b/source/util/OS.cpp @@ -7,6 +7,8 @@ //------------------------------------------------------------------------------ #include "slang/util/OS.h" +#include + #include "slang/text/CharInfo.h" #if defined(_WIN32) @@ -16,6 +18,9 @@ # ifndef NOMINMAX # define NOMINMAX # endif +# ifndef STRICT +# define STRICT +# endif # include # include # include @@ -34,6 +39,16 @@ namespace slang { #if defined(_WIN32) +void OS::setupConsole() { + // The application needs to be built with a manifest + // specifying the ActiveCodePage as UTF-8. + SLANG_ASSERT(GetACP() == 65001); + + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + setvbuf(stdout, nullptr, _IOFBF, 1000); +} + bool OS::tryEnableColors() { auto tryEnable = [](DWORD handle) { HANDLE hOut = GetStdHandle(handle); @@ -164,6 +179,10 @@ std::error_code OS::readFile(const fs::path& path, SmallVector& buffer) { #else +void OS::setupConsole() { + // Nothing to do. +} + bool OS::tryEnableColors() { return true; } @@ -267,6 +286,19 @@ std::error_code OS::readFile(const fs::path& path, SmallVector& buffer) { #endif +void OS::writeFile(const fs::path& path, std::string_view contents) { + if (path == "-") { + std::cout.write(contents.data(), (std::streamsize)contents.size()); + std::cout.flush(); + } + else { + std::ofstream file(path); + file.exceptions(std::ios::failbit | std::ios::badbit); + file.write(contents.data(), (std::streamsize)contents.size()); + file.flush(); + } +} + void OS::print(std::string_view text) { if (capturingOutput) capturedStdout += text; diff --git a/source/util/String.cpp b/source/util/String.cpp index c0248d08c..4dc0fd1b2 100644 --- a/source/util/String.cpp +++ b/source/util/String.cpp @@ -12,16 +12,6 @@ #include "slang/util/SmallVector.h" -#if defined(_WIN32) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -#endif - namespace slang { template @@ -128,42 +118,4 @@ int editDistance(std::string_view left, std::string_view right, bool allowReplac return row[n]; } -std::string getU8Str(const std::filesystem::path& path) { - return std::string(narrow(path.native())); -} - -#if defined(_WIN32) - -std::wstring widen(std::string_view str) { - if (str.empty()) - return L""; - - int sz = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), NULL, 0); - if (sz <= 0) - SLANG_THROW(std::runtime_error("Failed to convert string to UTF16")); - - std::wstring result; - result.resize(sz); - - MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.length(), result.data(), sz); - return result; -} - -std::string narrow(std::wstring_view str) { - if (str.empty()) - return ""; - - int sz = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), NULL, 0, NULL, NULL); - if (sz <= 0) - SLANG_THROW(std::runtime_error("Failed to convert string to UTF8")); - - std::string result; - result.resize(sz); - - WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), result.data(), sz, NULL, NULL); - return result; -} - -#endif - } // namespace slang diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index 0a14c7837..35a15e77f 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -48,6 +48,10 @@ if(SLANG_CI_BUILD) target_compile_definitions(unittests PRIVATE CI_BUILD) endif() +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(unittests PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() + # Copy the data directory for running tests from the build folder. add_custom_command( TARGET unittests diff --git a/tests/unittests/ast/ExpressionTests.cpp b/tests/unittests/ast/ExpressionTests.cpp index 32e48a563..b00c7422b 100644 --- a/tests/unittests/ast/ExpressionTests.cpp +++ b/tests/unittests/ast/ExpressionTests.cpp @@ -2971,3 +2971,26 @@ endmodule auto& p = compilation.getRoot().lookupName("m.p"); CHECK(p.getType().toString() == "logic[39:0]"); } + +TEST_CASE("Stream operator size overflow") { + auto tree = SyntaxTree::fromText(R"( +module m; + int i[]; + int j[9999999]; + assign i = {<< {j, j, j with [0+:50000000]}}; + + int k[$]; + int l[]; + always_comb l = {<< {k with [0+:999999999]}}; +endmodule +)"); + + Compilation compilation; + compilation.addSyntaxTree(tree); + + auto& diags = compilation.getAllDiagnostics(); + REQUIRE(diags.size() == 3); + CHECK(diags[0].code == diag::ObjectTooLarge); + CHECK(diags[1].code == diag::RangeOOB); + CHECK(diags[2].code == diag::ObjectTooLarge); +} diff --git a/tests/unittests/ast/TypeTests.cpp b/tests/unittests/ast/TypeTests.cpp index 862277251..2762c9442 100644 --- a/tests/unittests/ast/TypeTests.cpp +++ b/tests/unittests/ast/TypeTests.cpp @@ -2021,19 +2021,36 @@ module m; $display(bar[-2147483647:2147483647]); end endmodule + +class C; + logic a[2147483647]; + logic b[2147483647]; +endclass + +class D; + int foo[9999999]; +endclass + +module n; + D d[1000]; + struct { int a; D d; } asdf [1000]; +endmodule )"); Compilation compilation; compilation.addSyntaxTree(tree); auto& diags = compilation.getAllDiagnostics(); - REQUIRE(diags.size() == 6); + REQUIRE(diags.size() == 9); CHECK(diags[0].code == diag::ObjectTooLarge); CHECK(diags[1].code == diag::ObjectTooLarge); CHECK(diags[2].code == diag::PackedTypeTooLarge); CHECK(diags[3].code == diag::RangeOOB); CHECK(diags[4].code == diag::PackedTypeTooLarge); CHECK(diags[5].code == diag::ObjectTooLarge); + CHECK(diags[6].code == diag::ObjectTooLarge); + CHECK(diags[7].code == diag::ObjectTooLarge); + CHECK(diags[8].code == diag::ObjectTooLarge); } TEST_CASE("Giant string literal overflow") { diff --git a/tests/unittests/main.cpp b/tests/unittests/main.cpp index 3a34ae12d..bc1f2fd74 100644 --- a/tests/unittests/main.cpp +++ b/tests/unittests/main.cpp @@ -7,10 +7,7 @@ #include "slang/syntax/SyntaxTree.h" #include "slang/text/SourceManager.h" #include "slang/util/BumpAllocator.h" - -#if defined(_WIN32) -# include -#endif +#include "slang/util/OS.h" namespace slang { @@ -20,10 +17,7 @@ Diagnostics diagnostics; } // namespace slang int main(int argc, char* argv[]) { -#if defined(_WIN32) - SetConsoleOutputCP(CP_UTF8); - setvbuf(stdout, nullptr, _IOFBF, 1000); -#endif + slang::OS::setupConsole(); slang::syntax::SyntaxTree::getDefaultSourceManager().setDisableProximatePaths(true); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index bcf656727..3ca587573 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -24,6 +24,12 @@ if(SLANG_INCLUDE_INSTALL) install(TARGETS slang_driver RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(slang_driver + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) + target_sources(rewriter PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() + add_subdirectory(netlist) add_subdirectory(tidy) add_subdirectory(reflect) diff --git a/tools/driver/slang_main.cpp b/tools/driver/slang_main.cpp index 83161a2ba..858267e4d 100644 --- a/tools/driver/slang_main.cpp +++ b/tools/driver/slang_main.cpp @@ -24,8 +24,6 @@ using namespace slang; using namespace slang::ast; using namespace slang::driver; -void writeToFile(std::string_view fileName, std::string_view contents); - void printJson(Compilation& compilation, const std::string& fileName, const std::vector& scopes) { JsonWriter writer; @@ -43,12 +41,13 @@ void printJson(Compilation& compilation, const std::string& fileName, } } - writeToFile(fileName, writer.view()); + OS::writeFile(fileName, writer.view()); } template int driverMain(int argc, TArgs argv) { SLANG_TRY { + OS::setupConsole(); OS::tryEnableColors(); Driver driver; @@ -166,11 +165,7 @@ int driverMain(int argc, TArgs argv) { } if (timeTrace) { -#if defined(_WIN32) - std::ofstream file(widen(*timeTrace)); -#else std::ofstream file(*timeTrace); -#endif TimeTrace::write(file); if (!file.flush()) { SLANG_THROW(std::runtime_error( @@ -188,54 +183,15 @@ int driverMain(int argc, TArgs argv) { } } -template -void writeToFile(Stream& os, std::string_view fileName, String contents) { - os.write(contents.data(), contents.size()); - os.flush(); - if (!os) - SLANG_THROW(std::runtime_error(fmt::format("Unable to write AST to '{}'", fileName))); -} - -#if defined(_WIN32) - -void writeToFile(std::string_view fileName, std::string_view contents) { - if (fileName == "-") { - writeToFile(std::wcout, "stdout", widen(contents)); - } - else { - std::ofstream file(widen(fileName)); - writeToFile(file, fileName, contents); - } -} +#ifndef FUZZ_TARGET -# ifndef FUZZ_TARGET -int wmain(int argc, wchar_t** argv) { - return driverMain(argc, argv); -} -# endif - -#else - -void writeToFile(std::string_view fileName, std::string_view contents) { - if (fileName == "-") { - writeToFile(std::cout, "stdout", contents); - } - else { - std::ofstream file{std::string(fileName)}; - writeToFile(file, fileName, contents); - } -} - -# ifndef FUZZ_TARGET int main(int argc, char** argv) { return driverMain(argc, argv); } -# endif -#endif +#else // When fuzzing with libFuzzer, this is the entry point. -#ifdef FUZZ_TARGET extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { auto& sourceManager = SyntaxTree::getDefaultSourceManager(); @@ -253,4 +209,5 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } + #endif diff --git a/tools/netlist/CMakeLists.txt b/tools/netlist/CMakeLists.txt index 80efb72c2..9add88f87 100644 --- a/tools/netlist/CMakeLists.txt +++ b/tools/netlist/CMakeLists.txt @@ -13,6 +13,11 @@ target_link_libraries( target_include_directories(slang_netlist PRIVATE include ../../include) set_target_properties(slang_netlist PROPERTIES OUTPUT_NAME "slang-netlist") +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(slang_netlist + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() + if(SLANG_INCLUDE_INSTALL) install(TARGETS slang_netlist RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() diff --git a/tools/netlist/netlist.cpp b/tools/netlist/netlist.cpp index eb5827a9a..f52b54c77 100644 --- a/tools/netlist/netlist.cpp +++ b/tools/netlist/netlist.cpp @@ -42,8 +42,6 @@ inline constexpr DiagCode VariableReference(DiagSubsystem::Netlist, 0); } // namespace slang::diag -void writeToFile(std::string_view fileName, std::string_view contents); - void printJson(Compilation& compilation, const std::string& fileName, const std::vector& scopes) { JsonWriter writer; @@ -60,7 +58,7 @@ void printJson(Compilation& compilation, const std::string& fileName, } } } - writeToFile(fileName, writer.view()); + OS::writeFile(fileName, writer.view()); } void printDOT(const Netlist& netlist, const std::string& fileName) { @@ -105,44 +103,9 @@ void printDOT(const Netlist& netlist, const std::string& fileName) { } } buffer.append("}\n"); - writeToFile(fileName, buffer.data()); -} - -template -void writeToFile(Stream& os, std::string_view fileName, String contents) { - os.write(contents.data(), contents.size()); - os.flush(); - if (!os) { - SLANG_THROW(std::runtime_error(fmt::format("Unable to write to '{}'", fileName))); - } -} - -#if defined(_WIN32) - -void writeToFile(std::string_view fileName, std::string_view contents) { - if (fileName == "-") { - writeToFile(std::wcout, "stdout", widen(contents)); - } - else { - std::ofstream file(widen(fileName)); - writeToFile(file, fileName, contents); - } + OS::writeFile(fileName, buffer.data()); } -#else - -void writeToFile(std::string_view fileName, std::string_view contents) { - if (fileName == "-") { - writeToFile(std::cout, "stdout", contents); - } - else { - std::ofstream file{std::string(fileName)}; - writeToFile(file, fileName, contents); - } -} - -#endif - void reportPath(Compilation& compilation, const NetlistPath& path) { DiagnosticEngine diagEngine(*compilation.getSourceManager()); diagEngine.setMessage(diag::VariableReference, "variable {}"); @@ -174,6 +137,7 @@ void reportPath(Compilation& compilation, const NetlistPath& path) { } int main(int argc, char** argv) { + OS::setupConsole(); driver::Driver driver; driver.addStandardArgs(); diff --git a/tools/netlist/tests/CMakeLists.txt b/tools/netlist/tests/CMakeLists.txt index f0b38c2ac..48268113b 100644 --- a/tools/netlist/tests/CMakeLists.txt +++ b/tools/netlist/tests/CMakeLists.txt @@ -22,3 +22,8 @@ target_include_directories( ../../../tests/unittests) add_test(NAME netlist_unittests COMMAND netlist_unittests) + +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(netlist_unittests + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() diff --git a/tools/reflect/CMakeLists.txt b/tools/reflect/CMakeLists.txt index cb01925ba..ad27122d0 100644 --- a/tools/reflect/CMakeLists.txt +++ b/tools/reflect/CMakeLists.txt @@ -15,6 +15,11 @@ add_executable(slang::reflect ALIAS slang_reflect) target_link_libraries(slang_reflect PRIVATE slang_reflect_obj_lib) set_target_properties(slang_reflect PROPERTIES OUTPUT_NAME "slang-reflect") +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(slang_reflect + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() + if(SLANG_INCLUDE_INSTALL) install(TARGETS slang_reflect RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() diff --git a/tools/reflect/include/SvLocalParam.h b/tools/reflect/include/SvLocalParam.h index b892bca1e..db5dab278 100644 --- a/tools/reflect/include/SvLocalParam.h +++ b/tools/reflect/include/SvLocalParam.h @@ -23,7 +23,7 @@ class SvLocalParam : public SvGeneric { : std::string(parameter.name); hppFile.addWithIndent( fmt::format("static constexpr {} {} = {};\n", - toString(CppType::fromSize(parameter.getType().bitstreamWidth())), + toString(CppType::fromSize(parameter.getType().getBitstreamWidth())), parameterName, *parameter.getValue().integer().getRawPtr())); } diff --git a/tools/reflect/src/SvEnum.cpp b/tools/reflect/src/SvEnum.cpp index 1a94d8345..72959f8e8 100644 --- a/tools/reflect/src/SvEnum.cpp +++ b/tools/reflect/src/SvEnum.cpp @@ -8,13 +8,13 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const { auto underlyingType = [&]() { - if (type.bitstreamWidth() <= 8) + if (type.getBitstreamWidth() <= 8) return "uint8_t"sv; - if (type.bitstreamWidth() <= 16) + if (type.getBitstreamWidth() <= 16) return "uint16_t"sv; - if (type.bitstreamWidth() <= 32) + if (type.getBitstreamWidth() <= 32) return "uint32_t"sv; - if (type.bitstreamWidth() <= 64) + if (type.getBitstreamWidth() <= 64) return "uint64_t"sv; else SLANG_THROW( @@ -49,7 +49,7 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) c //** SIZE **/ hppFile.addWithIndent( - fmt::format("static constexpr size_t _size = {};\n\n", type.bitstreamWidth())); + fmt::format("static constexpr size_t _size = {};\n\n", type.getBitstreamWidth())); //** LOCAL **// hppFile.addWithIndent("Type type;\n"); diff --git a/tools/reflect/src/SvStruct.cpp b/tools/reflect/src/SvStruct.cpp index 8b588b604..dc90ccd2b 100644 --- a/tools/reflect/src/SvStruct.cpp +++ b/tools/reflect/src/SvStruct.cpp @@ -18,12 +18,7 @@ void SvStruct::toCpp(HppFile& hppFile, std::string_view _namespace, const SvAlia std::vector> members; - size_t structSize = 0; - if (type.isUnpackedStruct()) - structSize = type.getCanonicalType().as().bitstreamWidth(); - else - structSize = type.getCanonicalType().as().bitstreamWidth(); - + size_t structSize = type.getBitstreamWidth(); auto cppType = CppType::fromSize(structSize); if (cppType == CppType::SC_BV && noSystemC) { @@ -121,7 +116,7 @@ void SvStruct::toCpp(HppFile& hppFile, std::string_view _namespace, const SvAlia // sc_bv if (!noSystemC && structSize <= 64) { hppFile.addWithIndent( - fmt::format("{}(const sc_bv<{}>& data) {{\n", structName, type.bitstreamWidth())); + fmt::format("{}(const sc_bv<{}>& data) {{\n", structName, type.getBitstreamWidth())); hppFile.increaseIndent(); diff --git a/tools/reflect/src/SvType.cpp b/tools/reflect/src/SvType.cpp index 7820aef66..07bf7bc52 100644 --- a/tools/reflect/src/SvType.cpp +++ b/tools/reflect/src/SvType.cpp @@ -12,7 +12,7 @@ using namespace slang::ast; SvType::SvType(const Type& type) { - size = type.bitstreamWidth(); + size = type.getBitstreamWidth(); name = type.name; if (type.isScalar() || type.isArray()) cppType = CppType::fromSize(size); diff --git a/tools/reflect/src/reflect.cpp b/tools/reflect/src/reflect.cpp index 9d53f6630..ea7dac24f 100644 --- a/tools/reflect/src/reflect.cpp +++ b/tools/reflect/src/reflect.cpp @@ -17,6 +17,8 @@ using namespace slang; int main(int argc, char** argv) { + OS::setupConsole(); + driver::Driver driver; driver.addStandardArgs(); std::optional showHelp; diff --git a/tools/rewriter/rewriter.cpp b/tools/rewriter/rewriter.cpp index df209ebb3..c38d12d13 100644 --- a/tools/rewriter/rewriter.cpp +++ b/tools/rewriter/rewriter.cpp @@ -17,11 +17,14 @@ #include "slang/syntax/SyntaxPrinter.h" #include "slang/syntax/SyntaxTree.h" +#include "slang/util/OS.h" using namespace slang; using namespace slang::syntax; int main(int argc, char** argv) { + OS::setupConsole(); + SLANG_TRY { if (argc != 2) { fmt::print(stderr, "usage: rewriter file\n"); diff --git a/tools/tidy/CMakeLists.txt b/tools/tidy/CMakeLists.txt index d71951c8d..b14993b61 100644 --- a/tools/tidy/CMakeLists.txt +++ b/tools/tidy/CMakeLists.txt @@ -29,6 +29,11 @@ add_executable(slang::tidy ALIAS slang_tidy) target_link_libraries(slang_tidy PRIVATE slang_tidy_obj_lib) set_target_properties(slang_tidy PROPERTIES OUTPUT_NAME "slang-tidy") +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(slang_tidy + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif() + if(SLANG_INCLUDE_INSTALL) install(TARGETS slang_tidy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() diff --git a/tools/tidy/src/tidy.cpp b/tools/tidy/src/tidy.cpp index e2d5cd2b2..69f40eec1 100644 --- a/tools/tidy/src/tidy.cpp +++ b/tools/tidy/src/tidy.cpp @@ -24,6 +24,8 @@ std::optional project_slang_tidy_config(); using namespace slang; int main(int argc, char** argv) { + OS::setupConsole(); + driver::Driver driver; driver.addStandardArgs(); diff --git a/tools/tidy/tests/CMakeLists.txt b/tools/tidy/tests/CMakeLists.txt index c8da81309..84daacafb 100644 --- a/tools/tidy/tests/CMakeLists.txt +++ b/tools/tidy/tests/CMakeLists.txt @@ -25,3 +25,8 @@ target_compile_definitions(tidy_unittests PRIVATE UNITTESTS) target_include_directories(tidy_unittests PRIVATE ../../../tests/unittests) add_test(NAME tidy_unittests COMMAND tidy_unittests) + +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + target_sources(tidy_unittests + PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) +endif()