diff options
| -rw-r--r-- | CMakeLists.txt | 8 | ||||
| -rw-r--r-- | cmake/SlangTarget.cmake | 15 | ||||
| -rw-r--r-- | source/slang-stdlib/CMakeLists.txt | 125 | ||||
| -rw-r--r-- | source/slang-stdlib/slang-embedded-stdlib-source.cpp | 338 | ||||
| -rw-r--r-- | source/slang-stdlib/slang-embedded-stdlib.cpp (renamed from source/slang/slang-stdlib-api.cpp) | 19 | ||||
| -rw-r--r-- | source/slang/CMakeLists.txt | 188 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 330 |
7 files changed, 568 insertions, 455 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index da22ec19a..4e27a724e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,7 +334,12 @@ generator( source/slangc TARGET_NAME slang-bootstrap USE_FEWER_WARNINGS - LINK_WITH_PRIVATE prelude slang-no-embedded-stdlib slang-capability-lookup slang-lookup-tables Threads::Threads + LINK_WITH_PRIVATE + prelude + slang-without-embedded-stdlib + slang-capability-lookup + slang-lookup-tables + Threads::Threads ) # @@ -344,6 +349,7 @@ generator( # keep these non-trivial targets in their own directories so as not to clutter # this file add_subdirectory(prelude) +add_subdirectory(source/slang-stdlib) add_subdirectory(source/slang) if(SLANG_ENABLE_SLANGD) diff --git a/cmake/SlangTarget.cmake b/cmake/SlangTarget.cmake index 5387cd2ce..9e3fe11cd 100644 --- a/cmake/SlangTarget.cmake +++ b/cmake/SlangTarget.cmake @@ -18,6 +18,9 @@ function(slang_add_target dir type) WIN32_EXECUTABLE # Install this target for a non-component install INSTALL + # Don't include any source in this target, this is a complement to + # EXPLICIT_SOURCE, and doesn't interact with EXTRA_SOURCE_DIRS + NO_SOURCE ) set(single_value_args # Set the target name, useful for multiple targets from the same @@ -35,6 +38,10 @@ function(slang_add_target dir type) # ${EXPORT_MACRO_PREFIX}_DYNAMIC_EXPORT macros are set for using and # building respectively EXPORT_MACRO_PREFIX + # Ignore target type and use a particular style of export macro + # _DYNAMIC or _STATIC, this is useful when the target type is OBJECT + # pass in STATIC or SHARED + EXPORT_TYPE_AS # The folder in which to place this target for IDE-based generators (VS # and XCode) FOLDER @@ -115,7 +122,8 @@ function(slang_add_target dir type) # # Find the source for this target # - if(ARG_EXPLICIT_SOURCE) + if(ARG_NO_SOURCE) + elseif(ARG_EXPLICIT_SOURCE) list(APPEND source ${ARG_EXPLICIT_SOURCE}) else() slang_glob_sources(source ${dir}) @@ -269,6 +277,8 @@ function(slang_add_target dir type) if( target_type STREQUAL SHARED_LIBRARY OR target_type STREQUAL MODULE_LIBRARY + OR ARG_EXPORT_TYPE_AS STREQUAL SHARED + OR ARG_EXPORT_TYPE_AS STREQUAL MODULE ) target_compile_definitions( ${target} @@ -277,11 +287,14 @@ function(slang_add_target dir type) ) elseif( target_type STREQUAL STATIC_LIBRARY + OR ARG_EXPORT_TYPE_AS STREQUAL STATIC ) target_compile_definitions( ${target} PUBLIC "${ARG_EXPORT_MACRO_PREFIX}_STATIC" ) + else() + message(WARNING "unhandled case in slang_add_target while setting export macro") endif() endif() diff --git a/source/slang-stdlib/CMakeLists.txt b/source/slang-stdlib/CMakeLists.txt new file mode 100644 index 000000000..eca23859a --- /dev/null +++ b/source/slang-stdlib/CMakeLists.txt @@ -0,0 +1,125 @@ +# +# In this file, for stdlib source embed and stdlib embed itself, we define two +# targets, one which includes the embed and one which does not, these are +# linked in as needed to libslang and libslang-without-embedded-stdlib (for +# slang-bootstrap) +# +# If MSVC supported weak linking we could just have a single library for each +# and just not link it if we didn't want the embed... +# + +# +# Generate an embeddable stdlib +# + +set(stdlib_generated_header_dir ${CMAKE_CURRENT_BINARY_DIR}) +set(stdlib_generated_header + ${stdlib_generated_header_dir}/slang-stdlib-generated.h +) +add_custom_command( + OUTPUT ${stdlib_generated_header} + COMMAND + slang-bootstrap -archive-type riff-lz4 -save-stdlib-bin-source + ${stdlib_generated_header} + DEPENDS slang-bootstrap + VERBATIM +) + +set(stdlib_common_args + . + OBJECT + EXCLUDE_FROM_ALL + EXPORT_MACRO_PREFIX SLANG + EXPORT_TYPE_AS ${SLANG_LIB_TYPE} + FOLDER generated + LINK_WITH_PRIVATE core + USE_EXTRA_WARNINGS + EXPLICIT_SOURCE ./slang-embedded-stdlib.cpp +) + +slang_add_target( + ${stdlib_common_args} + TARGET_NAME slang-embedded-stdlib + EXPLICIT_SOURCE ${stdlib_generated_header} + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_EMBED_STDLIB + INCLUDE_DIRECTORIES_PRIVATE ${stdlib_generated_header_dir} +) + +slang_add_target( + ${stdlib_common_args} + TARGET_NAME slang-no-embedded-stdlib +) + +# +# Compiling the meta.slang files +# + +# List of *.meta.slang headers +set(stdlib_meta_source_dir "${slang_SOURCE_DIR}/source/slang") +glob_append(stdlib_meta_source "${stdlib_meta_source_dir}/*.meta.slang") + +set(stdlib_meta_output_dir "${CMAKE_CURRENT_BINARY_DIR}/stdlib-meta") + +# Generate the output file list +set(stdlib_meta_generated_headers) +foreach(meta_source ${stdlib_meta_source}) + file( + RELATIVE_PATH + meta_source_relative + "${stdlib_meta_source_dir}" + ${meta_source} + ) + list( + APPEND + stdlib_meta_generated_headers + "${stdlib_meta_output_dir}/${meta_source_relative}.h" + ) +endforeach() + +add_custom_command( + OUTPUT ${stdlib_meta_generated_headers} + COMMAND ${CMAKE_COMMAND} -E make_directory ${stdlib_meta_output_dir} + COMMAND + slang-generate ${stdlib_meta_source} --target-directory + ${stdlib_meta_output_dir} + DEPENDS ${stdlib_meta_source} slang-generate + WORKING_DIRECTORY "${stdlib_meta_source_dir}" + VERBATIM +) + +# +# Generate embedded stdlib source +# + +set(stdlib_source_common_args + . + OBJECT + EXCLUDE_FROM_ALL + USE_EXTRA_WARNINGS + LINK_WITH_PRIVATE + core + slang-capability-defs + slang-reflect-headers + SPIRV-Headers + INCLUDE_DIRECTORIES_PRIVATE ../slang + FOLDER generated + EXPLICIT_SOURCE + ./slang-embedded-stdlib-source.cpp + ${stdlib_meta_generated_headers} + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_EMBED_STDLIB_SOURCE + INCLUDE_DIRECTORIES_PRIVATE + ${stdlib_meta_output_dir} + EXPORT_MACRO_PREFIX SLANG + EXPORT_TYPE_AS ${SLANG_LIB_TYPE} +) + +slang_add_target( + ${stdlib_source_common_args} + TARGET_NAME slang-embedded-stdlib-source + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_EMBED_STDLIB_SOURCE +) + +slang_add_target( + ${stdlib_source_common_args} + TARGET_NAME slang-no-embedded-stdlib-source +) diff --git a/source/slang-stdlib/slang-embedded-stdlib-source.cpp b/source/slang-stdlib/slang-embedded-stdlib-source.cpp new file mode 100644 index 000000000..d77126c94 --- /dev/null +++ b/source/slang-stdlib/slang-embedded-stdlib-source.cpp @@ -0,0 +1,338 @@ +#include "slang-compiler.h" +#include "slang-ir.h" +#include "slang-ir-util.h" +#include "slang-stdlib-textures.h" + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define LINE_STRING STRINGIZE(__LINE__) + +namespace Slang +{ + // We are going to generate the stdlib source code from a more compact + // description. For example, we need to generate all the `operator` + // declarations for the basic unary and binary math operations on + // builtin types. To do this, we will make a big array of all these + // types, and associate them with data on their categories/capabilities + // so that we generate only the correct operations. + // + enum + { + SINT_MASK = 1 << 0, + FLOAT_MASK = 1 << 1, + BOOL_RESULT = 1 << 2, + BOOL_MASK = 1 << 3, + UINT_MASK = 1 << 4, + + INT_MASK = SINT_MASK | UINT_MASK, + ARITHMETIC_MASK = INT_MASK | FLOAT_MASK, + LOGICAL_MASK = INT_MASK | BOOL_MASK, + ANY_MASK = INT_MASK | FLOAT_MASK | BOOL_MASK, + }; + + // We are going to declare initializers that allow for conversion between + // all of our base types, and we need a way to priotize those conversion + // by giving them different costs. Rather than maintain a hard-coded table + // of N^2 costs for N basic types, we are going to try to do things a bit + // more systematically. + // + // Every base type will be given a "kind" and a "rank" for conversion. + // The kind will classify it as signed/unsigned/float, and the rank will + // classify it by its logical bit size (with a distinct rank for pointer-sized + // types that logically sits between 32- and 64-bit types). + // + enum BaseTypeConversionKind : uint8_t + { + kBaseTypeConversionKind_Signed, + kBaseTypeConversionKind_Unsigned, + kBaseTypeConversionKind_Float, + kBaseTypeConversionKind_Error, + }; + enum BaseTypeConversionRank : uint8_t + { + kBaseTypeConversionRank_Bool, + kBaseTypeConversionRank_Int8, + kBaseTypeConversionRank_Int16, + kBaseTypeConversionRank_Int32, + kBaseTypeConversionRank_IntPtr, + kBaseTypeConversionRank_Int64, + kBaseTypeConversionRank_Error, + }; + + // Here we declare the table of all our builtin types, so that we can generate all the relevant declarations. + // + struct BaseTypeConversionInfo + { + char const* name; + BaseType tag; + unsigned flags; + BaseTypeConversionKind conversionKind; + BaseTypeConversionRank conversionRank; + }; + static const BaseTypeConversionInfo kBaseTypes[] = { + // TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so differently across the board + { "void", BaseType::Void, 0, kBaseTypeConversionKind_Error, kBaseTypeConversionRank_Error}, + + { "bool", BaseType::Bool, BOOL_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Bool }, + + { "int8_t", BaseType::Int8, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int8}, + { "int16_t", BaseType::Int16, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int16}, + { "int", BaseType::Int, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int32}, + { "int64_t", BaseType::Int64, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int64}, + { "intptr_t", BaseType::IntPtr, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_IntPtr}, + + + { "half", BaseType::Half, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int16}, + { "float", BaseType::Float, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int32}, + { "double", BaseType::Double, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int64}, + + { "uint8_t", BaseType::UInt8, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int8}, + { "uint16_t", BaseType::UInt16, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int16}, + { "uint", BaseType::UInt, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int32}, + { "uint64_t", BaseType::UInt64, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int64}, + { "uintptr_t", BaseType::UIntPtr, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_IntPtr}, + + }; + + void Session::finalizeSharedASTBuilder() + { + // Force creation of all builtin types so we can make sure + // they are created by the builtin AST builder instead of + // some user linkage's ast builder. This avoid the problem + // of storing a reference to these global types that are + // owned by a user linkage that gets deleted with the linkage. + // + globalAstBuilder->getNoneType(); + globalAstBuilder->getNullPtrType(); + globalAstBuilder->getBottomType(); + globalAstBuilder->getErrorType(); + globalAstBuilder->getInitializerListType(); + globalAstBuilder->getOverloadedType(); + globalAstBuilder->getStringType(); + globalAstBuilder->getEnumTypeType(); + globalAstBuilder->getDiffInterfaceType(); + globalAstBuilder->getSharedASTBuilder()->getDynamicType(); + globalAstBuilder->getSharedASTBuilder()->getDiffInterfaceType(); + globalAstBuilder->getSharedASTBuilder()->getNativeStringType(); + for (auto& baseType : kBaseTypes) + globalAstBuilder->getBuiltinType(baseType.tag); + } + + + // Given two base types, we need to be able to compute the cost of converting between them. + ConversionCost getBaseTypeConversionCost( + BaseTypeConversionInfo const& toInfo, + BaseTypeConversionInfo const& fromInfo) + { + if(toInfo.conversionKind == fromInfo.conversionKind + && toInfo.conversionRank == fromInfo.conversionRank) + { + // Thse should represent the exact same type. + return kConversionCost_None; + } + + // Conversions within the same kind are easist to handle + if (toInfo.conversionKind == fromInfo.conversionKind) + { + // If we are converting to a "larger" type, then + // we are doing a lossless promotion, and otherwise + // we are doing a demotion. + if (toInfo.conversionRank > fromInfo.conversionRank) + return kConversionCost_RankPromotion; + else + return kConversionCost_GeneralConversion; + } + else if (fromInfo.tag == BaseType::Bool && toInfo.tag == BaseType::Int) + { + return kConversionCost_BoolToInt; + } + + // If we are converting from an unsigned integer type to + // a signed integer type that is guaranteed to be larger, + // then that is also a lossless promotion. + // + // There is one additional wrinkle here, which is that + // a conversion from a 32-bit unsigned integer to a + // "pointer-sized" signed integer should be treated + // as unsafe, because the pointer size might also be + // 32 bits. + // + // The same basic exemption applied when converting + // *from* a pointer-sized unsigned integer. + else if(toInfo.conversionKind == kBaseTypeConversionKind_Signed + && fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned + && toInfo.conversionRank > fromInfo.conversionRank + && toInfo.conversionRank != kBaseTypeConversionRank_IntPtr + && fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr) + { + return kConversionCost_UnsignedToSignedPromotion; + } + // Same-size unsigned to signed integer conversion. + else if (toInfo.conversionKind == kBaseTypeConversionKind_Signed + && fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned + && toInfo.conversionRank == fromInfo.conversionRank + && toInfo.conversionRank != kBaseTypeConversionRank_IntPtr + && fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr) + { + return kConversionCost_SameSizeUnsignedToSignedConversion; + } + + // Conversion from signed to unsigned is always lossy, + // but it is preferred over conversions from unsigned + // to signed, for same-size types. + else if(toInfo.conversionKind == kBaseTypeConversionKind_Unsigned + && fromInfo.conversionKind == kBaseTypeConversionKind_Signed + && toInfo.conversionRank >= fromInfo.conversionRank) + { + return kConversionCost_SignedToUnsignedConversion; + } + + // Conversion from an integer to a floating-point type + // is never considered a promotion (even when the value + // would fit in the available mantissa bits). + // If the destination type is at least 32 bits we consider + // this a reasonably good conversion, though. + // + // Note that this means we do *not* consider implicit + // conversion to `half` as a good conversion, even for small + // types. This makes sense because we relaly want to prefer + // conversion to `float` as the default. + else if (toInfo.conversionKind == kBaseTypeConversionKind_Float + && toInfo.conversionRank >= kBaseTypeConversionRank_Int32 + && fromInfo.conversionRank >= kBaseTypeConversionRank_Int8) + { + return kConversionCost_IntegerToFloatConversion; + } + else if (toInfo.conversionKind == kBaseTypeConversionKind_Float + && toInfo.conversionRank >= kBaseTypeConversionRank_Int16 + && fromInfo.conversionRank >= kBaseTypeConversionRank_Int8) + { + return kConversionCost_IntegerToHalfConversion; + } + // All other cases are considered as "general" conversions, + // where we don't consider any one conversion better than + // any others. + else + { + return kConversionCost_GeneralConversion; + } + } + + IROp getBaseTypeConversionOp( + BaseTypeConversionInfo const& toInfo, + BaseTypeConversionInfo const& fromInfo) + { + if (toInfo.tag == fromInfo.tag) + return kIROp_Nop; + + IROp intrinsicOpCode = kIROp_Nop; + auto toStyle = getTypeStyle(toInfo.tag); + auto fromStyle = getTypeStyle(fromInfo.tag); + if (toStyle == kIROp_BoolType) toStyle = kIROp_IntType; + if (fromStyle == kIROp_BoolType) fromStyle = kIROp_IntType; + if (toStyle == kIROp_IntType && fromStyle == kIROp_IntType) + intrinsicOpCode = kIROp_IntCast; + if (toStyle == kIROp_IntType && fromStyle == kIROp_FloatType) + intrinsicOpCode = kIROp_CastFloatToInt; + if (toStyle == kIROp_FloatType && fromStyle == kIROp_IntType) + intrinsicOpCode = kIROp_CastIntToFloat; + if (toStyle == kIROp_FloatType && fromStyle == kIROp_FloatType) + intrinsicOpCode = kIROp_FloatCast; + return intrinsicOpCode; + } + + struct IntrinsicOpInfo { IROp opCode; char const* funcName; char const* opName; char const* interface; unsigned flags; }; + + [[maybe_unused]] + static const IntrinsicOpInfo intrinsicUnaryOps[] = { + { kIROp_Neg, "neg", "-", "__BuiltinArithmeticType", ARITHMETIC_MASK }, + { kIROp_Not, "logicalNot", "!", nullptr, BOOL_MASK | BOOL_RESULT }, + { kIROp_BitNot, "not", "~", "__BuiltinLogicalType", INT_MASK }, + }; + + [[maybe_unused]] + static const IntrinsicOpInfo intrinsicBinaryOps[] = { + {kIROp_Add, "add", "+", "__BuiltinArithmeticType", ARITHMETIC_MASK}, + {kIROp_Sub, "sub", "-", "__BuiltinArithmeticType", ARITHMETIC_MASK}, + {kIROp_Mul, "mul", "*", "__BuiltinArithmeticType", ARITHMETIC_MASK}, + {kIROp_Div, "div", "/", "__BuiltinArithmeticType", ARITHMETIC_MASK}, + {kIROp_IRem, "irem", "%", "__BuiltinIntegerType", INT_MASK}, + {kIROp_FRem, "frem", "%", "__BuiltinFloatingPointType", FLOAT_MASK}, + {kIROp_And, "logicalAnd", "&&", nullptr, BOOL_MASK | BOOL_RESULT}, + {kIROp_Or, "logicalOr", "||", nullptr, BOOL_MASK | BOOL_RESULT}, + {kIROp_BitAnd, "and", "&", "__BuiltinLogicalType", LOGICAL_MASK}, + {kIROp_BitOr, "or", "|", "__BuiltinLogicalType", LOGICAL_MASK}, + {kIROp_BitXor, "xor", "^", "__BuiltinLogicalType", LOGICAL_MASK}, + {kIROp_Eql, "eql", "==", "__BuiltinType", ANY_MASK | BOOL_RESULT}, + {kIROp_Neq, "neq", "!=", "__BuiltinType", ANY_MASK | BOOL_RESULT}, + {kIROp_Greater, "greater", ">", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, + {kIROp_Less, "less", "<", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, + {kIROp_Geq, "geq", ">=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, + {kIROp_Leq, "leq", "<=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, + }; + + // Integer types that can be used in atomic operations in CUDA. + [[maybe_unused]] + static const char* kCudaAtomicIntegerTypes[] = { "int", "uint", "uint64_t", "int64_t" }; + + // Both the following functions use these macros. + // NOTE! They require a variable named path to emit the #line correctly if in source file. +#define SLANG_RAW(TEXT) sb << TEXT; +#define SLANG_SPLICE(EXPR) sb << (EXPR); + +#define EMIT_LINE_DIRECTIVE() sb << "#line " << (__LINE__+1) << " \"" << path << "\"\n" + + ComPtr<ISlangBlob> Session::getCoreLibraryCode() + { +#if SLANG_EMBED_STDLIB_SOURCE + if (!coreLibraryCode) + { + StringBuilder sb; + const String path = getStdlibPath(); + #include "core.meta.slang.h" + coreLibraryCode = StringBlob::moveCreate(sb); + } +#endif + return coreLibraryCode; + } + + ComPtr<ISlangBlob> Session::getHLSLLibraryCode() + { +#if SLANG_EMBED_STDLIB_SOURCE + if (!hlslLibraryCode) + { + const String path = getStdlibPath(); + StringBuilder sb; + #include "hlsl.meta.slang.h" + hlslLibraryCode = StringBlob::moveCreate(sb); + } +#endif + return hlslLibraryCode; + } + + ComPtr<ISlangBlob> Session::getAutodiffLibraryCode() + { +#if SLANG_EMBED_STDLIB_SOURCE + if (!autodiffLibraryCode) + { + const String path = getStdlibPath(); + StringBuilder sb; + #include "diff.meta.slang.h" + autodiffLibraryCode = StringBlob::moveCreate(sb); + } +#endif + return autodiffLibraryCode; + } + + ComPtr<ISlangBlob> Session::getGLSLLibraryCode() + { + if (!glslLibraryCode) + { + const String path = getStdlibPath(); + StringBuilder sb; + #include "glsl.meta.slang.h" + glslLibraryCode = StringBlob::moveCreate(sb); + } + return glslLibraryCode; + } +} diff --git a/source/slang/slang-stdlib-api.cpp b/source/slang-stdlib/slang-embedded-stdlib.cpp index e5934531a..87107fa50 100644 --- a/source/slang/slang-stdlib-api.cpp +++ b/source/slang-stdlib/slang-embedded-stdlib.cpp @@ -1,18 +1,8 @@ -// slang-stdlib-api.cpp - #include "../core/slang-basic.h" #include "../core/slang-array-view.h" - #include "../core/slang-blob.h" -#ifdef SLANG_WITHOUT_EMBEDDED_STD_LIB - -SLANG_API ISlangBlob* slang_getEmbeddedStdLib() -{ - return nullptr; -} - -#else +#ifdef SLANG_EMBED_STDLIB static const uint8_t g_stdLib[] = { @@ -26,4 +16,11 @@ SLANG_API ISlangBlob* slang_getEmbeddedStdLib() return &g_stdLibBlob; } +#else + +SLANG_API ISlangBlob* slang_getEmbeddedStdLib() +{ + return nullptr; +} + #endif diff --git a/source/slang/CMakeLists.txt b/source/slang/CMakeLists.txt index 78d2376a7..27600e3f8 100644 --- a/source/slang/CMakeLists.txt +++ b/source/slang/CMakeLists.txt @@ -1,50 +1,3 @@ -# -# Compiling the meta.slang files -# - -# List of *.meta.slang headers -glob_append(SLANG_STDLIB_META_SOURCE "*.meta.slang") - -set(SLANG_STDLIB_META_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/stdlib-meta") - -# Generate the output file list -set(SLANG_STDLIB_META_GENERATED_HEADERS) -foreach(meta_source ${SLANG_STDLIB_META_SOURCE}) - file( - RELATIVE_PATH - meta_source_relative - ${CMAKE_CURRENT_LIST_DIR} - ${meta_source} - ) - list( - APPEND - SLANG_STDLIB_META_GENERATED_HEADERS - "${SLANG_STDLIB_META_OUTPUT_DIR}/${meta_source_relative}.h" - ) -endforeach() - -add_custom_command( - OUTPUT ${SLANG_STDLIB_META_GENERATED_HEADERS} - COMMAND ${CMAKE_COMMAND} -E make_directory ${SLANG_STDLIB_META_OUTPUT_DIR} - COMMAND - slang-generate ${SLANG_STDLIB_META_SOURCE} --target-directory - ${SLANG_STDLIB_META_OUTPUT_DIR} - DEPENDS ${SLANG_STDLIB_META_SOURCE} slang-generate - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - VERBATIM -) - -add_library( - slang-meta-headers - INTERFACE - EXCLUDE_FROM_ALL - ${SLANG_STDLIB_META_GENERATED_HEADERS} -) -set_target_properties(slang-meta-headers PROPERTIES FOLDER generated) -target_include_directories( - slang-meta-headers - INTERFACE ${SLANG_STDLIB_META_OUTPUT_DIR} -) # # generate capability files @@ -211,33 +164,6 @@ slang_add_target( ) # -# Generate an embeddable stdlib -# - -set(SLANG_STDLIB_GENERATED_HEADER - ${CMAKE_CURRENT_BINARY_DIR}/slang-stdlib-generated.h -) -add_custom_command( - OUTPUT ${SLANG_STDLIB_GENERATED_HEADER} - COMMAND - slang-bootstrap -archive-type riff-lz4 -save-stdlib-bin-source - ${SLANG_STDLIB_GENERATED_HEADER} - DEPENDS slang-bootstrap - VERBATIM -) -add_library( - slang-stdlib-embed-headers - INTERFACE - EXCLUDE_FROM_ALL - ${SLANG_STDLIB_GENERATED_HEADER} -) -set_target_properties(slang-stdlib-embed-headers PROPERTIES FOLDER generated) -target_include_directories( - slang-stdlib-embed-headers - INTERFACE ${CMAKE_CURRENT_BINARY_DIR} -) - -# # Generate the version header # @@ -247,60 +173,98 @@ configure_file(${slang_SOURCE_DIR}/slang-tag-version.h.in slang-version-header/s # Slang itself # -set(slang_common_args - EXTRA_SOURCE_DIRS ${SLANG_RECORD_REPLAY_SYSTEM} +set(slang_build_args USE_EXTRA_WARNINGS + EXTRA_COMPILE_OPTIONS_PRIVATE + # a warning is disabled for a memset boundary check. + # everything looked fine and it is unclear why the checking fails + $<$<CXX_COMPILER_ID:GNU>:-Wno-error=stringop-overflow> + # slang.h is in the project root, so include that directory in the interface + # for slang + INCLUDE_DIRECTORIES_PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/slang-version-header + EXPORT_MACRO_PREFIX SLANG + EXPORT_TYPE_AS ${SLANG_LIB_TYPE} + EXTRA_SOURCE_DIRS ${SLANG_RECORD_REPLAY_SYSTEM} +) +set(slang_link_args LINK_WITH_PRIVATE core + prelude compiler-core slang-capability-defs slang-capability-lookup slang-reflect-headers slang-lookup-tables SPIRV-Headers - EXTRA_COMPILE_OPTIONS_PRIVATE - # a warning is disabled for a memset boundary check. - # everything looked fine and it is unclear why the checking fails - $<$<CXX_COMPILER_ID:GNU>:-Wno-error=stringop-overflow> - # slang.h is in the project root, so include that directory in the interface - # for slang +) +set(slang_interface_args INCLUDE_DIRECTORIES_PUBLIC ${slang_SOURCE_DIR} - INCLUDE_DIRECTORIES_PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/slang-version-header - EXPORT_MACRO_PREFIX SLANG +) +set(slang_public_lib_args + PUBLIC_HEADERS + ${slang_SOURCE_DIR}/slang*.h + ${CMAKE_CURRENT_BINARY_DIR}/slang-version-header/*.h + LINK_WITH_PRIVATE + $<IF:$<BOOL:${SLANG_EMBED_STDLIB}>,slang-embedded-stdlib,slang-no-embedded-stdlib> + $<IF:$<BOOL:${SLANG_EMBED_STDLIB_SOURCE}>,slang-embedded-stdlib-source,slang-no-embedded-stdlib-source> + INSTALL ) # # Minimal static slang used in generating the embedded stdlib # -slang_add_target( - . - OBJECT - ${slang_common_args} - TARGET_NAME slang-no-embedded-stdlib - EXCLUDE_FROM_ALL - LINK_WITH_PRIVATE slang-meta-headers - EXTRA_COMPILE_DEFINITIONS_PUBLIC SLANG_STATIC - EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_WITHOUT_EMBEDDED_STD_LIB - FOLDER generators -) - # -# Slang with whatever options +# Slang itself # -slang_add_target( - . - ${SLANG_LIB_TYPE} - ${slang_common_args} - LINK_WITH_PRIVATE - prelude - $<$<BOOL:${SLANG_EMBED_STDLIB}>:slang-stdlib-embed-headers> - $<$<BOOL:${SLANG_EMBED_STDLIB_SOURCE}>:slang-meta-headers> - EXTRA_COMPILE_DEFINITIONS_PRIVATE - $<$<NOT:$<BOOL:${SLANG_EMBED_STDLIB}>>:SLANG_WITHOUT_EMBEDDED_STD_LIB> - $<$<NOT:$<BOOL:${SLANG_EMBED_STDLIB_SOURCE}>>:SLANG_DISABLE_STDLIB_SOURCE> - EXPORT_MACRO_PREFIX SLANG - INSTALL - PUBLIC_HEADERS ${slang_SOURCE_DIR}/slang*.h ${CMAKE_CURRENT_BINARY_DIR}/slang-version-header/*.h -) +if(NOT SLANG_EMBED_STDLIB) + # If we're not embedding stdlib we can just do a normal build of slang, + # including all the options, this can also serve as our no embedded stdlib + # library for slang-bootstrap (not that we need that anyway) + slang_add_target( + . + ${SLANG_LIB_TYPE} + ${slang_build_args} + ${slang_link_args} + ${slang_interface_args} + ${slang_public_lib_args} + ) + add_library(slang-without-embedded-stdlib ALIAS slang) +else() + # However if we're embedding stdlib, we need to make two different + # libraries, one with the embedded stdlib and one without, so first define + # the shared objects as one target so we don't build them twice. + slang_add_target( + . + OBJECT + ${slang_build_args} + ${slang_link_args} + TARGET_NAME slang-common-objects + EXCLUDE_FROM_ALL + ) + slang_add_target( + . + ${SLANG_LIB_TYPE} + ${slang_link_args} + ${slang_interface_args} + NO_SOURCE + TARGET_NAME + slang-without-embedded-stdlib + EXCLUDE_FROM_ALL + LINK_WITH_PRIVATE + slang-common-objects + slang-no-embedded-stdlib + slang-embedded-stdlib-source + ) + slang_add_target( + . + ${SLANG_LIB_TYPE} + ${slang_link_args} + ${slang_interface_args} + ${slang_public_lib_args} + LINK_WITH_PRIVATE + slang-common-objects + NO_SOURCE + ) +endif() diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index dbc358c18..e28d95ad5 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -2,9 +2,6 @@ #include "slang-compiler.h" #include "slang-ir.h" -#include "slang-syntax.h" -#include "slang-ir-util.h" -#include "slang-stdlib-textures.h" #include "../core/slang-string-util.h" #define STRINGIZE(x) STRINGIZE2(x) @@ -27,331 +24,4 @@ namespace Slang } return stdlibPath; } - - // We are going to generate the stdlib source code from a more compact - // description. For example, we need to generate all the `operator` - // declarations for the basic unary and binary math operations on - // builtin types. To do this, we will make a big array of all these - // types, and associate them with data on their categories/capabilities - // so that we generate only the correct operations. - // - enum - { - SINT_MASK = 1 << 0, - FLOAT_MASK = 1 << 1, - BOOL_RESULT = 1 << 2, - BOOL_MASK = 1 << 3, - UINT_MASK = 1 << 4, - - INT_MASK = SINT_MASK | UINT_MASK, - ARITHMETIC_MASK = INT_MASK | FLOAT_MASK, - LOGICAL_MASK = INT_MASK | BOOL_MASK, - ANY_MASK = INT_MASK | FLOAT_MASK | BOOL_MASK, - }; - - // We are going to declare initializers that allow for conversion between - // all of our base types, and we need a way to priotize those conversion - // by giving them different costs. Rather than maintain a hard-coded table - // of N^2 costs for N basic types, we are going to try to do things a bit - // more systematically. - // - // Every base type will be given a "kind" and a "rank" for conversion. - // The kind will classify it as signed/unsigned/float, and the rank will - // classify it by its logical bit size (with a distinct rank for pointer-sized - // types that logically sits between 32- and 64-bit types). - // - enum BaseTypeConversionKind : uint8_t - { - kBaseTypeConversionKind_Signed, - kBaseTypeConversionKind_Unsigned, - kBaseTypeConversionKind_Float, - kBaseTypeConversionKind_Error, - }; - enum BaseTypeConversionRank : uint8_t - { - kBaseTypeConversionRank_Bool, - kBaseTypeConversionRank_Int8, - kBaseTypeConversionRank_Int16, - kBaseTypeConversionRank_Int32, - kBaseTypeConversionRank_IntPtr, - kBaseTypeConversionRank_Int64, - kBaseTypeConversionRank_Error, - }; - - // Here we declare the table of all our builtin types, so that we can generate all the relevant declarations. - // - struct BaseTypeConversionInfo - { - char const* name; - BaseType tag; - unsigned flags; - BaseTypeConversionKind conversionKind; - BaseTypeConversionRank conversionRank; - }; - static const BaseTypeConversionInfo kBaseTypes[] = { - // TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so differently across the board - { "void", BaseType::Void, 0, kBaseTypeConversionKind_Error, kBaseTypeConversionRank_Error}, - - { "bool", BaseType::Bool, BOOL_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Bool }, - - { "int8_t", BaseType::Int8, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int8}, - { "int16_t", BaseType::Int16, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int16}, - { "int", BaseType::Int, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int32}, - { "int64_t", BaseType::Int64, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_Int64}, - { "intptr_t", BaseType::IntPtr, SINT_MASK, kBaseTypeConversionKind_Signed, kBaseTypeConversionRank_IntPtr}, - - - { "half", BaseType::Half, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int16}, - { "float", BaseType::Float, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int32}, - { "double", BaseType::Double, FLOAT_MASK, kBaseTypeConversionKind_Float, kBaseTypeConversionRank_Int64}, - - { "uint8_t", BaseType::UInt8, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int8}, - { "uint16_t", BaseType::UInt16, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int16}, - { "uint", BaseType::UInt, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int32}, - { "uint64_t", BaseType::UInt64, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_Int64}, - { "uintptr_t", BaseType::UIntPtr, UINT_MASK, kBaseTypeConversionKind_Unsigned, kBaseTypeConversionRank_IntPtr}, - - }; - - void Session::finalizeSharedASTBuilder() - { - // Force creation of all builtin types so we can make sure - // they are created by the builtin AST builder instead of - // some user linkage's ast builder. This avoid the problem - // of storing a reference to these global types that are - // owned by a user linkage that gets deleted with the linkage. - // - globalAstBuilder->getNoneType(); - globalAstBuilder->getNullPtrType(); - globalAstBuilder->getBottomType(); - globalAstBuilder->getErrorType(); - globalAstBuilder->getInitializerListType(); - globalAstBuilder->getOverloadedType(); - globalAstBuilder->getStringType(); - globalAstBuilder->getEnumTypeType(); - globalAstBuilder->getDiffInterfaceType(); - globalAstBuilder->getSharedASTBuilder()->getDynamicType(); - globalAstBuilder->getSharedASTBuilder()->getDiffInterfaceType(); - globalAstBuilder->getSharedASTBuilder()->getNativeStringType(); - for (auto& baseType : kBaseTypes) - globalAstBuilder->getBuiltinType(baseType.tag); - } - - - // Given two base types, we need to be able to compute the cost of converting between them. - ConversionCost getBaseTypeConversionCost( - BaseTypeConversionInfo const& toInfo, - BaseTypeConversionInfo const& fromInfo) - { - if(toInfo.conversionKind == fromInfo.conversionKind - && toInfo.conversionRank == fromInfo.conversionRank) - { - // Thse should represent the exact same type. - return kConversionCost_None; - } - - // Conversions within the same kind are easist to handle - if (toInfo.conversionKind == fromInfo.conversionKind) - { - // If we are converting to a "larger" type, then - // we are doing a lossless promotion, and otherwise - // we are doing a demotion. - if (toInfo.conversionRank > fromInfo.conversionRank) - return kConversionCost_RankPromotion; - else - return kConversionCost_GeneralConversion; - } - else if (fromInfo.tag == BaseType::Bool && toInfo.tag == BaseType::Int) - { - return kConversionCost_BoolToInt; - } - - // If we are converting from an unsigned integer type to - // a signed integer type that is guaranteed to be larger, - // then that is also a lossless promotion. - // - // There is one additional wrinkle here, which is that - // a conversion from a 32-bit unsigned integer to a - // "pointer-sized" signed integer should be treated - // as unsafe, because the pointer size might also be - // 32 bits. - // - // The same basic exemption applied when converting - // *from* a pointer-sized unsigned integer. - else if(toInfo.conversionKind == kBaseTypeConversionKind_Signed - && fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned - && toInfo.conversionRank > fromInfo.conversionRank - && toInfo.conversionRank != kBaseTypeConversionRank_IntPtr - && fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr) - { - return kConversionCost_UnsignedToSignedPromotion; - } - // Same-size unsigned to signed integer conversion. - else if (toInfo.conversionKind == kBaseTypeConversionKind_Signed - && fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned - && toInfo.conversionRank == fromInfo.conversionRank - && toInfo.conversionRank != kBaseTypeConversionRank_IntPtr - && fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr) - { - return kConversionCost_SameSizeUnsignedToSignedConversion; - } - - // Conversion from signed to unsigned is always lossy, - // but it is preferred over conversions from unsigned - // to signed, for same-size types. - else if(toInfo.conversionKind == kBaseTypeConversionKind_Unsigned - && fromInfo.conversionKind == kBaseTypeConversionKind_Signed - && toInfo.conversionRank >= fromInfo.conversionRank) - { - return kConversionCost_SignedToUnsignedConversion; - } - - // Conversion from an integer to a floating-point type - // is never considered a promotion (even when the value - // would fit in the available mantissa bits). - // If the destination type is at least 32 bits we consider - // this a reasonably good conversion, though. - // - // Note that this means we do *not* consider implicit - // conversion to `half` as a good conversion, even for small - // types. This makes sense because we relaly want to prefer - // conversion to `float` as the default. - else if (toInfo.conversionKind == kBaseTypeConversionKind_Float - && toInfo.conversionRank >= kBaseTypeConversionRank_Int32 - && fromInfo.conversionRank >= kBaseTypeConversionRank_Int8) - { - return kConversionCost_IntegerToFloatConversion; - } - else if (toInfo.conversionKind == kBaseTypeConversionKind_Float - && toInfo.conversionRank >= kBaseTypeConversionRank_Int16 - && fromInfo.conversionRank >= kBaseTypeConversionRank_Int8) - { - return kConversionCost_IntegerToHalfConversion; - } - // All other cases are considered as "general" conversions, - // where we don't consider any one conversion better than - // any others. - else - { - return kConversionCost_GeneralConversion; - } - } - - IROp getBaseTypeConversionOp( - BaseTypeConversionInfo const& toInfo, - BaseTypeConversionInfo const& fromInfo) - { - if (toInfo.tag == fromInfo.tag) - return kIROp_Nop; - - IROp intrinsicOpCode = kIROp_Nop; - auto toStyle = getTypeStyle(toInfo.tag); - auto fromStyle = getTypeStyle(fromInfo.tag); - if (toStyle == kIROp_BoolType) toStyle = kIROp_IntType; - if (fromStyle == kIROp_BoolType) fromStyle = kIROp_IntType; - if (toStyle == kIROp_IntType && fromStyle == kIROp_IntType) - intrinsicOpCode = kIROp_IntCast; - if (toStyle == kIROp_IntType && fromStyle == kIROp_FloatType) - intrinsicOpCode = kIROp_CastFloatToInt; - if (toStyle == kIROp_FloatType && fromStyle == kIROp_IntType) - intrinsicOpCode = kIROp_CastIntToFloat; - if (toStyle == kIROp_FloatType && fromStyle == kIROp_FloatType) - intrinsicOpCode = kIROp_FloatCast; - return intrinsicOpCode; - } - - struct IntrinsicOpInfo { IROp opCode; char const* funcName; char const* opName; char const* interface; unsigned flags; }; - - [[maybe_unused]] - static const IntrinsicOpInfo intrinsicUnaryOps[] = { - { kIROp_Neg, "neg", "-", "__BuiltinArithmeticType", ARITHMETIC_MASK }, - { kIROp_Not, "logicalNot", "!", nullptr, BOOL_MASK | BOOL_RESULT }, - { kIROp_BitNot, "not", "~", "__BuiltinLogicalType", INT_MASK }, - }; - - [[maybe_unused]] - static const IntrinsicOpInfo intrinsicBinaryOps[] = { - {kIROp_Add, "add", "+", "__BuiltinArithmeticType", ARITHMETIC_MASK}, - {kIROp_Sub, "sub", "-", "__BuiltinArithmeticType", ARITHMETIC_MASK}, - {kIROp_Mul, "mul", "*", "__BuiltinArithmeticType", ARITHMETIC_MASK}, - {kIROp_Div, "div", "/", "__BuiltinArithmeticType", ARITHMETIC_MASK}, - {kIROp_IRem, "irem", "%", "__BuiltinIntegerType", INT_MASK}, - {kIROp_FRem, "frem", "%", "__BuiltinFloatingPointType", FLOAT_MASK}, - {kIROp_And, "logicalAnd", "&&", nullptr, BOOL_MASK | BOOL_RESULT}, - {kIROp_Or, "logicalOr", "||", nullptr, BOOL_MASK | BOOL_RESULT}, - {kIROp_BitAnd, "and", "&", "__BuiltinLogicalType", LOGICAL_MASK}, - {kIROp_BitOr, "or", "|", "__BuiltinLogicalType", LOGICAL_MASK}, - {kIROp_BitXor, "xor", "^", "__BuiltinLogicalType", LOGICAL_MASK}, - {kIROp_Eql, "eql", "==", "__BuiltinType", ANY_MASK | BOOL_RESULT}, - {kIROp_Neq, "neq", "!=", "__BuiltinType", ANY_MASK | BOOL_RESULT}, - {kIROp_Greater, "greater", ">", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, - {kIROp_Less, "less", "<", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, - {kIROp_Geq, "geq", ">=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, - {kIROp_Leq, "leq", "<=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT}, - }; - - // Integer types that can be used in atomic operations in CUDA. - [[maybe_unused]] - static const char* kCudaAtomicIntegerTypes[] = { "int", "uint", "uint64_t", "int64_t" }; - - // Both the following functions use these macros. - // NOTE! They require a variable named path to emit the #line correctly if in source file. -#define SLANG_RAW(TEXT) sb << TEXT; -#define SLANG_SPLICE(EXPR) sb << (EXPR); - -#define EMIT_LINE_DIRECTIVE() sb << "#line " << (__LINE__+1) << " \"" << path << "\"\n" - - ComPtr<ISlangBlob> Session::getCoreLibraryCode() - { -#if !defined(SLANG_DISABLE_STDLIB_SOURCE) - if (!coreLibraryCode) - { - StringBuilder sb; - const String path = getStdlibPath(); - #include "core.meta.slang.h" - coreLibraryCode = StringBlob::moveCreate(sb); - } -#endif - return coreLibraryCode; - } - - ComPtr<ISlangBlob> Session::getHLSLLibraryCode() - { -#if !defined(SLANG_DISABLE_STDLIB_SOURCE) - if (!hlslLibraryCode) - { - const String path = getStdlibPath(); - StringBuilder sb; - #include "hlsl.meta.slang.h" - hlslLibraryCode = StringBlob::moveCreate(sb); - } -#endif - return hlslLibraryCode; - } - - ComPtr<ISlangBlob> Session::getAutodiffLibraryCode() - { -#if !defined(SLANG_DISABLE_STDLIB_SOURCE) - if (!autodiffLibraryCode) - { - const String path = getStdlibPath(); - StringBuilder sb; - #include "diff.meta.slang.h" - autodiffLibraryCode = StringBlob::moveCreate(sb); - } -#endif - return autodiffLibraryCode; - } - - ComPtr<ISlangBlob> Session::getGLSLLibraryCode() - { - if (!glslLibraryCode) - { - const String path = getStdlibPath(); - StringBuilder sb; - #include "glsl.meta.slang.h" - glslLibraryCode = StringBlob::moveCreate(sb); - } - return glslLibraryCode; - } } |
