diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-05-22 17:22:22 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-22 17:22:22 -0400 |
| commit | 972a931452c3f06a23a4f67ccfb655851df53fa4 (patch) | |
| tree | 309a75c5239af163e47bc0e123d023683c1ec74a /source | |
| parent | 33e15236c6fd8623bc516a194ca65e8810f1f855 (diff) | |
Source embedding for output (#2889)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Fix typo.
* Add options for source embedding.
* Small improvements.
* Working with tests.
* Add check for supported language types for embedding.
* Try and remove assume warning.
* Fix warning on MacOSX.
* Some extra checking around Style::Text.
* Some small improvements to docs/handling for headers extensions.
* Fix md issue.
* Small fixes around zeroing partial last element.
* Another small fix....
* Small improvement in hex conversion.
* Add an assert for unsignedness.
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-artifact-desc-util.cpp | 2 | ||||
| -rw-r--r-- | source/compiler-core/slang-artifact-desc-util.h | 3 | ||||
| -rw-r--r-- | source/compiler-core/slang-dxc-compiler.cpp | 2 | ||||
| -rw-r--r-- | source/compiler-core/slang-fxc-compiler.cpp | 2 | ||||
| -rw-r--r-- | source/compiler-core/slang-glslang-compiler.cpp | 2 | ||||
| -rw-r--r-- | source/compiler-core/slang-nvrtc-compiler.cpp | 2 | ||||
| -rw-r--r-- | source/compiler-core/slang-source-embed-util.cpp | 399 | ||||
| -rw-r--r-- | source/compiler-core/slang-source-embed-util.h | 59 | ||||
| -rw-r--r-- | source/slang/slang-artifact-output-util.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 77 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 46 |
13 files changed, 582 insertions, 28 deletions
diff --git a/source/compiler-core/slang-artifact-desc-util.cpp b/source/compiler-core/slang-artifact-desc-util.cpp index 18558eab9..559a1b4ca 100644 --- a/source/compiler-core/slang-artifact-desc-util.cpp +++ b/source/compiler-core/slang-artifact-desc-util.cpp @@ -899,7 +899,7 @@ SlangResult ArtifactDescUtil::appendDefaultExtension(const ArtifactDesc& desc, S } } -/* static */bool ArtifactDescUtil::isDissassembly(const ArtifactDesc& from, const ArtifactDesc& to) +/* static */bool ArtifactDescUtil::isDisassembly(const ArtifactDesc& from, const ArtifactDesc& to) { // From must be a binary like type if (!isDerivedFrom(from.kind, ArtifactKind::CompileBinary)) diff --git a/source/compiler-core/slang-artifact-desc-util.h b/source/compiler-core/slang-artifact-desc-util.h index 1e8b853e6..879732bb1 100644 --- a/source/compiler-core/slang-artifact-desc-util.h +++ b/source/compiler-core/slang-artifact-desc-util.h @@ -100,11 +100,12 @@ struct ArtifactDescUtil static bool isDescDerivedFrom(const ArtifactDesc& desc, const ArtifactDesc& from); /// True if `to` is disassembly of `from` - static bool isDissassembly(const ArtifactDesc& from, const ArtifactDesc& to); + static bool isDisassembly(const ArtifactDesc& from, const ArtifactDesc& to); /// Append the desc as text to out static void appendText(const ArtifactDesc& desc, StringBuilder& out); + /// Given an artifact desc return a description as a string static String getText(const ArtifactDesc& desc); }; diff --git a/source/compiler-core/slang-dxc-compiler.cpp b/source/compiler-core/slang-dxc-compiler.cpp index 832c5809c..9428d0d26 100644 --- a/source/compiler-core/slang-dxc-compiler.cpp +++ b/source/compiler-core/slang-dxc-compiler.cpp @@ -701,7 +701,7 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt bool DXCDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to) { - return ArtifactDescUtil::isDissassembly(from, to) && from.payload == ArtifactPayload::DXIL; + return ArtifactDescUtil::isDisassembly(from, to) && from.payload == ArtifactPayload::DXIL; } SlangResult DXCDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) diff --git a/source/compiler-core/slang-fxc-compiler.cpp b/source/compiler-core/slang-fxc-compiler.cpp index b3b952796..ed92cc3f8 100644 --- a/source/compiler-core/slang-fxc-compiler.cpp +++ b/source/compiler-core/slang-fxc-compiler.cpp @@ -335,7 +335,7 @@ SlangResult FXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt bool FXCDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to) { // Can only disassemble blobs that are DXBC - return ArtifactDescUtil::isDissassembly(from, to) && from.payload == ArtifactPayload::DXBC; + return ArtifactDescUtil::isDisassembly(from, to) && from.payload == ArtifactPayload::DXBC; } SlangResult FXCDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) diff --git a/source/compiler-core/slang-glslang-compiler.cpp b/source/compiler-core/slang-glslang-compiler.cpp index c1053692f..3c5f7a2b8 100644 --- a/source/compiler-core/slang-glslang-compiler.cpp +++ b/source/compiler-core/slang-glslang-compiler.cpp @@ -251,7 +251,7 @@ SlangResult GlslangDownstreamCompiler::compile(const CompileOptions& inOptions, bool GlslangDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to) { // Can only disassemble blobs that are SPIR-V - return ArtifactDescUtil::isDissassembly(from, to) && from.payload == ArtifactPayload::SPIRV; + return ArtifactDescUtil::isDisassembly(from, to) && from.payload == ArtifactPayload::SPIRV; } SlangResult GlslangDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) diff --git a/source/compiler-core/slang-nvrtc-compiler.cpp b/source/compiler-core/slang-nvrtc-compiler.cpp index cc8a72dce..e2f3e678c 100644 --- a/source/compiler-core/slang-nvrtc-compiler.cpp +++ b/source/compiler-core/slang-nvrtc-compiler.cpp @@ -1036,7 +1036,7 @@ SlangResult NVRTCDownstreamCompiler::compile(const DownstreamCompileOptions& inO bool NVRTCDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to) { - return ArtifactDescUtil::isDissassembly(from, to) || ArtifactDescUtil::isDissassembly(to, from); + return ArtifactDescUtil::isDisassembly(from, to) || ArtifactDescUtil::isDisassembly(to, from); } SlangResult NVRTCDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) diff --git a/source/compiler-core/slang-source-embed-util.cpp b/source/compiler-core/slang-source-embed-util.cpp new file mode 100644 index 000000000..ec0c005cf --- /dev/null +++ b/source/compiler-core/slang-source-embed-util.cpp @@ -0,0 +1,399 @@ +#include "slang-source-embed-util.h" + +// Artifact +#include "../compiler-core/slang-artifact-desc-util.h" +#include "../compiler-core/slang-artifact-util.h" + +#include "../core/slang-string-util.h" +#include "../core/slang-char-util.h" + +#include "../core/slang-string-escape-util.h" + +#include "../core/slang-blob.h" +#include "../core/slang-io.h" + +namespace Slang +{ + +namespace { // anonymous +typedef SourceEmbedUtil::Style Style; +} // anonymous + +static const NamesDescriptionValue kSourceEmbedStyleInfos[] = +{ + { ValueInt(Style::None), "none", "No source level embedding" }, + { ValueInt(Style::Default), "default", "The default embedding for the type to be embedded"}, + { ValueInt(Style::Text), "text", "Embed as text. May change line endings. If output isn't text will use 'default'. Size will *not* contain terminating 0." }, + { ValueInt(Style::BinaryText), "binary-text", "Embed as text assuming contents is binary. "}, + { ValueInt(Style::U8), "u8", "Embed as unsigned bytes."}, + { ValueInt(Style::U16), "u16", "Embed as uint16_t."}, + { ValueInt(Style::U32), "u32", "Embed as uint32_t."}, + { ValueInt(Style::U64), "u64", "Embed as uint64_t."}, +}; + +/* static */ConstArrayView<NamesDescriptionValue> SourceEmbedUtil::getStyleInfos() +{ + return makeConstArrayView(kSourceEmbedStyleInfos); +} + +/* static */ bool SourceEmbedUtil::isSupported(SlangSourceLanguage lang) +{ + return lang == SLANG_SOURCE_LANGUAGE_CPP || lang == SLANG_SOURCE_LANGUAGE_C; +} + +static bool _isHeaderExtension(const UnownedStringSlice& in) +{ + // Some "typical" header extensions + return in == toSlice("h") || + in == toSlice("hpp") || + in == toSlice("hxx") || + in == toSlice("h++") || + in == toSlice("hh"); +} + +/* static */String SourceEmbedUtil::getPath(const String& path, const Options& options) +{ + if (!isSupported(options.language)) + { + return String(); + } + + if (!path.getLength()) + { + return path; + } + + const auto ext = Path::getPathExt(path); + + if (_isHeaderExtension(ext.getUnownedSlice())) + { + return path; + } + + // Assume it's a header, and just use the .h extension + StringBuilder buf; + buf << path << toSlice(".h"); + return buf; +} + +/* static */SourceEmbedUtil::Style SourceEmbedUtil::getDefaultStyle(const ArtifactDesc& desc) +{ + if (ArtifactDescUtil::isText(desc)) + { + return Style::Text; + } + + if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary)) + { + // SPIR-V is encoded as U32 + if (isDerivedFrom(desc.payload, ArtifactPayload::SPIRV)) + { + return Style::U32; + } + } + + // When in doube encode as U8 bytes. + // The problem is on some compilers there are limits on how long a U8 based binary can be. + return Style::U8; +} + +// True if we need to copy into a buffer. Necessary if there is an alignement +// issue or if there is a partial entry +static bool _needsCopy(const uint8_t* cur, Count bytesPerElement, Count bytesPerLine) +{ + return ((size_t(bytesPerLine) | size_t(cur)) & size_t(bytesPerElement - 1)) != 0; +} + +// NOTE! Assumes T is an unsigned type. Behavior will be incorrect if it is not. +template <typename T> +static void _appendHex(const T* in, ArrayView<char> elementWork, char* dst, size_t bytesForLine, StringBuilder& out) +{ + // Check that T is unsigned + SLANG_COMPILE_TIME_ASSERT((T(~T(0))) > T(0)); + + // Make sure dst seems plausible + SLANG_ASSERT(dst >= elementWork.begin() && dst <= elementWork.end()); + // Check the alignment + SLANG_ASSERT((size_t(in) & (sizeof(T) - 1)) == 0); + + // Calculate the amount of elements for this line. + const size_t elementsCount = (bytesForLine + sizeof(T) - 1) / sizeof(T); + + // The amount of hex digits needed, is 2 per byte + const Count numHexDigits = sizeof(T) * 2; + + // Shift to get top nybble + const Index shift = (numHexDigits - 1) * 4; + + for (size_t i = 0; i < elementsCount; ++i) + { + T value = in[i]; + + for (Index j = 0; j < numHexDigits; j++, value <<= 4) + { + dst[j] = CharUtil::getHexChar(Index(value >> shift) & 0xf); + } + + out.append(elementWork.getBuffer(), elementWork.getCount()); + } +} + +static SlangResult _append(const SourceEmbedUtil::Options& options, ConstArrayView<uint8_t> data, StringBuilder& buf) +{ + const uint8_t* cur = data.begin(); + + const auto prefix = toSlice("0x"); + const auto suffix = toSlice(", "); + UnownedStringSlice literalSuffix; + + UnownedStringSlice elementType; + + Count bytesPerElement; + + switch (options.style) + { + case Style::U8: + { + elementType = toSlice("unsigned char"); + bytesPerElement = 1; + break; + } + case Style::U16: + { + elementType = toSlice("uint16_t"); + bytesPerElement = 2; + break; + } + case Style::U32: + { + elementType = toSlice("uint32_t"); + bytesPerElement = 4; + break; + } + case Style::U64: + { + elementType = toSlice("uint64_t"); + bytesPerElement = 8; + // On testing on GCC/CLANG/Recent VS, there is no warning/error without suffix, so + // will leave off for now. + // literalSuffix = toSlice("ULL"); + break; + } + default: return SLANG_FAIL; + } + + // Output the variable + + buf << "const " << elementType << " " << options.variableName << "[] = \n"; + buf << "{\n"; + + // Work out the element work + char work[80]; + Count elementSizeInChars; + { + StringBuilder workBuf; + workBuf << prefix; + workBuf.appendRepeatedChar('N', 2 * bytesPerElement); + workBuf << literalSuffix; + workBuf << suffix; + + elementSizeInChars = workBuf.getLength(); + ::memcpy(work, workBuf.getBuffer(), elementSizeInChars); + } + + auto workView = makeArrayView(work, elementSizeInChars); + char* dstChars = work + prefix.getLength(); + + Count elementsPerLine = (options.lineLength - options.indent.getLength()) / elementSizeInChars; + elementsPerLine = (elementsPerLine <= 0) ? 1 : elementsPerLine; + + // Maximum bytes output per line + const size_t bytesPerLine = elementsPerLine * bytesPerElement; + + List<uint64_t> alignedElements; + alignedElements.setCount(Count((bytesPerLine / sizeof(uint64_t)) + 2)); + uint8_t* alignedDst = (uint8_t*)alignedElements.getBuffer(); + + size_t bytesRemaining = data.getCount(); + + while (bytesRemaining > 0) + { + const size_t bytesForLine = bytesRemaining > bytesPerLine ? bytesPerLine : bytesRemaining; + bytesRemaining -= bytesForLine; + + const uint8_t* lineBytes = cur; + cur += bytesForLine; + + // We copy if we want alignment of if we hit a partial at the end + if (_needsCopy(lineBytes, bytesPerElement, bytesForLine)) + { + // Make sure the last element is zeroed, before copying + // Needed if the last element is partial. + alignedElements[Index(bytesForLine / sizeof(uint64_t))] = 0; + + // Copy the bytes over + ::memcpy(alignedDst, lineBytes, bytesForLine); + + // Use the aligned buffer for the line + lineBytes = alignedDst; + } + + buf << options.indent; + + switch (bytesPerElement) + { + case 1: _appendHex<uint8_t>(lineBytes, workView, dstChars, bytesForLine, buf); break; + case 2: _appendHex<uint16_t>((const uint16_t*)lineBytes, workView, dstChars, bytesForLine, buf); break; + case 4: _appendHex<uint32_t>((const uint32_t*)lineBytes, workView, dstChars, bytesForLine, buf); break; + case 8: _appendHex<uint64_t>((const uint64_t*)lineBytes, workView, dstChars, bytesForLine, buf); break; + } + + buf << "\n"; + } + + buf << "};\n\n"; + + return SLANG_OK; +} + +/* static */SlangResult SourceEmbedUtil::createEmbedded(IArtifact* artifact, const Options& inOptions, ComPtr<IArtifact>& outArtifact) +{ + if (!isSupported(inOptions.language)) + { + return SLANG_E_NOT_IMPLEMENTED; + } + + ComPtr<ISlangBlob> blob; + SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::No, blob.writeRef())); + + const auto desc = artifact->getDesc(); + + Options options(inOptions); + + // If the style is text, but the artifact *isn't* a text type, we'll + // use 'default' for the type + if (options.style == Style::Text && + !ArtifactDescUtil::isText(desc)) + { + options.style = Style::Default; + } + + if (options.style == Style::Default) + { + options.style = getDefaultStyle(desc); + } + + // If there is no style there is nothing to do + if (options.style == Style::None) + { + return SLANG_OK; + } + + if (options.variableName.getLength() <= 0) + { + options.variableName = "data"; + } + + StringBuilder buf; + + ConstArrayView<uint8_t> data((const uint8_t*)blob->getBufferPointer(), blob->getBufferSize()); + + size_t totalSizeInBytes = data.getCount(); + + switch (options.style) + { + case Style::Text: + { + totalSizeInBytes = 0; + + auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp); + + buf << "const char " << options.variableName << "[] = \n"; + + // Split into lines + // We dont worry about splitting lines in this impl... + UnownedStringSlice text((const char*)data.begin(), data.getCount()); + + for (auto line : LineParser(text)) + { + buf << options.indent; + buf << "\""; + + handler->appendEscaped(line, buf); + + // Work out the total size, taking into account we may encode line endings and \0 differently + // The +1 is for \n + totalSizeInBytes += line.getLength() + 1; + + buf << "\\n\"\n"; + } + + buf << ";\n"; + break; + } + case Style::BinaryText: + { + auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp); + + buf << "const char " << options.variableName << "[] = \n"; + + // We could encode everything and then split + // but if we do that we probably want to not split across an escaped character, + // although that may be handled correctly. + + // The other way to this is incrementally, so that's what we will do here + UnownedStringSlice text((const char*)data.begin(), data.getCount()); + + auto cur = text.begin(); + auto end = text.end(); + + while (cur < end) + { + const auto startOffset = buf.getLength(); + + buf << options.indent; + buf << "\""; + + do + { + handler->appendEscaped(UnownedStringSlice(cur, 1), buf); + cur++; + } + while (buf.getLength() - startOffset < options.lineLength - 1); + + buf << "\"\n"; + } + + buf << ";\n"; + break; + } + case Style::U8: + case Style::U16: + case Style::U32: + case Style::U64: + { + SLANG_RETURN_ON_FAIL(_append(options, data, buf)); + break; + } + default: + { + return SLANG_E_NOT_IMPLEMENTED; + } + } + + buf << "const size_t " << options.variableName << "_sizeInBytes = " << uint64_t(totalSizeInBytes) << ";\n\n"; + + // Make into an artifact + ArtifactPayload payload = options.language == SLANG_SOURCE_LANGUAGE_C ? ArtifactPayload::C : ArtifactPayload::Cpp; + auto dstDesc = ArtifactDesc::make(ArtifactKind::Source, payload); + + auto dstArtifact = ArtifactUtil::createArtifact(dstDesc); + + auto dstBlob = StringBlob::moveCreate(buf); + dstArtifact->addRepresentationUnknown(dstBlob); + + outArtifact = dstArtifact; + return SLANG_OK; +} + +} // namespace Slang diff --git a/source/compiler-core/slang-source-embed-util.h b/source/compiler-core/slang-source-embed-util.h new file mode 100644 index 000000000..70389cfe1 --- /dev/null +++ b/source/compiler-core/slang-source-embed-util.h @@ -0,0 +1,59 @@ +#ifndef SLANG_SOURCE_EMBED_UTIL_H +#define SLANG_SOURCE_EMBED_UTIL_H + +#include "../core/slang-basic.h" + +#include "slang-artifact.h" +#include "slang-diagnostic-sink.h" + +#include "../../slang-com-ptr.h" + +#include "../core/slang-name-value.h" + +namespace Slang +{ + + +struct SourceEmbedUtil +{ + enum class Style : uint32_t + { + None, ///< No embedding + Default, ///< Default embedding for the type + Text, ///< Embed as text. May change line endings. If output isn't text will use 'default'. Size will *not* contain terminating 0 + BinaryText, ///< Embed as text assuming contents is binary. + U8, ///< Embed as unsigned bytes + U16, ///< Embed as uint16_t + U32, ///< Embed as uint32_t + U64, ///< Embed as uint64_t + CountOf, + }; + + struct Options + { + Style style = Style::Default; ///< Style of embedding + Count lineLength = 120; ///< The line length, lines can be larger for some styles, but will aim to keep within range + SlangSourceLanguage language = SLANG_SOURCE_LANGUAGE_C; ///< The language to output for + String variableName; ///< The name to give the variable + String indent = " "; ///< Indenting + }; + + /// Get the style infos + static ConstArrayView<NamesDescriptionValue> getStyleInfos(); + + /// Given an artifact and + static SlangResult createEmbedded(IArtifact* artifact, const Options& options, ComPtr<IArtifact>& outArtifact); + + /// Returns the default style for the desc + static Style getDefaultStyle(const ArtifactDesc& desc); + + /// Returns true if supports the specified language for embedding + static bool isSupported(SlangSourceLanguage lang); + + /// Given the path return the output path. If no path is available return the empty string + static String getPath(const String& path, const Options& options); +}; + +} + +#endif diff --git a/source/slang/slang-artifact-output-util.cpp b/source/slang/slang-artifact-output-util.cpp index 80c11a466..32aff9f43 100644 --- a/source/slang/slang-artifact-output-util.cpp +++ b/source/slang/slang-artifact-output-util.cpp @@ -25,7 +25,7 @@ namespace Slang assemblyDesc.kind = ArtifactKind::Assembly; // Check it seems like a plausbile disassembly - if (!ArtifactDescUtil::isDissassembly(desc, assemblyDesc)) + if (!ArtifactDescUtil::isDisassembly(desc, assemblyDesc)) { if (sink) { @@ -77,7 +77,7 @@ SlangResult ArtifactOutputUtil::maybeDisassemble(Session* session, IArtifact* ar toDesc.kind = ArtifactKind::Assembly; // If this likes a playsible disassebly conversion - if (ArtifactDescUtil::isDissassembly(desc, toDesc)) + if (ArtifactDescUtil::isDisassembly(desc, toDesc)) { ComPtr<IArtifact> disassemblyArtifact; diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 4ca76c53a..8426b7eec 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -1648,24 +1648,52 @@ namespace Slang return String(); } + SlangResult EndToEndCompileRequest::_writeArtifact(const String& path, IArtifact* artifact) + { + if (path.getLength() > 0) + { + SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::writeToFile(artifact, getSink(), path)); + } + else if (m_containerFormat == ContainerFormat::None) + { + // If we aren't writing to a container and we didn't write to a file, we can output to standard output + writeArtifactToStandardOutput(artifact, getSink()); + } + return SLANG_OK; + } + SlangResult EndToEndCompileRequest::_maybeWriteArtifact(const String& path, IArtifact* artifact) { // We don't have to do anything if there is no artifact - if (artifact) + if (!artifact) { - if (path.getLength()) - { - SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::writeToFile(artifact, getSink(), path)); - return SLANG_OK; - } + return SLANG_OK; + } + + // If embedding is enabled... + if (m_sourceEmbedStyle != SourceEmbedUtil::Style::None) + { + SourceEmbedUtil::Options options; - // If we aren't writing to a container and we didn't write to a file, we can output to - if (m_containerFormat == ContainerFormat::None) + options.style = m_sourceEmbedStyle; + options.variableName = m_sourceEmbedName; + options.language = (SlangSourceLanguage)m_sourceEmbedLanguage; + + ComPtr<IArtifact> embeddedArtifact; + SLANG_RETURN_ON_FAIL(SourceEmbedUtil::createEmbedded(artifact, options, embeddedArtifact)); + + if (!embeddedArtifact) { - writeArtifactToStandardOutput(artifact, getSink()); + return SLANG_FAIL; } + SLANG_RETURN_ON_FAIL(_writeArtifact(SourceEmbedUtil::getPath(path, options), embeddedArtifact)); + return SLANG_OK; } - + else + { + SLANG_RETURN_ON_FAIL(_writeArtifact(path, artifact)); + } + return SLANG_OK; } @@ -2118,14 +2146,16 @@ namespace Slang } } + void EndToEndCompileRequest::generateOutput() { generateOutput(getSpecializedGlobalAndEntryPointsComponentType()); - + // If we are in command-line mode, we might be expected to actually // write output to one or more files here. - if (m_isCommandLineCompile) + if (m_isCommandLineCompile && + m_containerFormat == ContainerFormat::None) { auto linkage = getLinkage(); auto program = getSpecializedGlobalAndEntryPointsComponentType(); @@ -2136,23 +2166,27 @@ namespace Slang if (targetReq->isWholeProgramRequest()) { - const auto path = _getWholeProgramPath(targetReq); - const auto artifact = targetProgram->getExistingWholeProgramResult(); + if (const auto artifact = targetProgram->getExistingWholeProgramResult()) + { + const auto path = _getWholeProgramPath(targetReq); - _maybeWriteArtifact(path, artifact); + _maybeWriteArtifact(path, artifact); + } } else { Index entryPointCount = program->getEntryPointCount(); for (Index ee = 0; ee < entryPointCount; ++ee) { - const auto path = _getEntryPointPath(targetReq, ee); - const auto artifact = targetProgram->getExistingEntryPointResult(ee); - - _maybeWriteArtifact(path, artifact); + if (const auto artifact = targetProgram->getExistingEntryPointResult(ee)) + { + const auto path = _getEntryPointPath(targetReq, ee); + + _maybeWriteArtifact(path, artifact); + } } } - } + } } // Maybe create the container @@ -2161,6 +2195,9 @@ namespace Slang // If it's a command line compile we may need to write the container to a file if (m_isCommandLineCompile) { + // TODO(JS): + // We could write the container into a source embedded format potentially + maybeWriteContainer(m_containerOutputPath); _writeDependencyFile(this); diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 1386b37e8..4d3df5433 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -12,6 +12,8 @@ #include "../compiler-core/slang-include-system.h" #include "../compiler-core/slang-command-line-args.h" +#include "../compiler-core/slang-source-embed-util.h" + #include "../core/slang-std-writers.h" #include "../core/slang-command-options.h" @@ -2667,6 +2669,13 @@ namespace Slang // Should we just pass the input to another compiler? PassThroughMode m_passThrough = PassThroughMode::None; + /// If output should be source embedded, define the style of the embedding + SourceEmbedUtil::Style m_sourceEmbedStyle = SourceEmbedUtil::Style::None; + /// The language to be used for source embedding + SourceLanguage m_sourceEmbedLanguage = SourceLanguage::C; + /// Source embed variable name. Note may be used as a basis for names if multiple items written + String m_sourceEmbedName; + /// Source code for the specialization arguments to use for the global specialization parameters of the program. List<String> m_globalSpecializationArgStrings; @@ -2802,6 +2811,7 @@ namespace Slang /// Maybe write the artifact to the path (if set), or stdout (if there is no container or path) SlangResult _maybeWriteArtifact(const String& path, IArtifact* artifact); + SlangResult _writeArtifact(const String& path, IArtifact* artifact); ISlangUnknown* getInterface(const Guid& guid); diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 25c86fa05..722f25843 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -98,6 +98,8 @@ DIAGNOSTIC( 43, Error, targetFlagsIgnoredBecauseBeforeAllTargets, "when using DIAGNOSTIC( 50, Error, duplicateTargets, "the target '$0' has been specified more than once") +DIAGNOSTIC( 51, Error, unhandledLanguageForSourceEmbedding, "unhandled source language for source embedding") + DIAGNOSTIC( 60, Error, cannotDeduceOutputFormatFromPath, "cannot infer an output format from the output path '$0'") DIAGNOSTIC( 61, Error, cannotMatchOutputFileToTarget, "no specified '-target' option matches the output path '$0', which implies the '$1' format") diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index f69e0ec3a..09c46b7ef 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -29,6 +29,7 @@ #include "../compiler-core/slang-command-line-args.h" #include "../compiler-core/slang-artifact-desc-util.h" #include "../compiler-core/slang-core-diagnostics.h" +#include "../compiler-core/slang-source-embed-util.h" #include "../core/slang-string-slice-pool.h" #include "../core/slang-char-util.h" @@ -72,6 +73,10 @@ enum class OptionKind EmitIr, ReportDownstreamTime, + SourceEmbedStyle, + SourceEmbedName, + SourceEmbedLanguage, + // Target Capability, @@ -167,6 +172,7 @@ enum class ValueCategory DebugLevel, FileSystemType, VulkanShift, + SourceEmbedStyle, CountOf, }; @@ -184,6 +190,8 @@ SLANG_GET_VALUE_CATEGORY(FileSystemType, TypeTextUtil::FileSystemType) SLANG_GET_VALUE_CATEGORY(HelpStyle, CommandOptionsWriter::Style) SLANG_GET_VALUE_CATEGORY(OptimizationLevel, SlangOptimizationLevel) SLANG_GET_VALUE_CATEGORY(VulkanShift, HLSLToVulkanLayoutOptions::Kind) +SLANG_GET_VALUE_CATEGORY(SourceEmbedStyle, SourceEmbedUtil::Style) +SLANG_GET_VALUE_CATEGORY(Language, SourceLanguage) } // anonymous @@ -242,6 +250,9 @@ void initCommandOptions(CommandOptions& options) options.addCategory(CategoryKind::Value, "file-system-type", "File System Type", UserValue(ValueCategory::FileSystemType)); options.addValues(TypeTextUtil::getFileSystemTypeInfos()); + + options.addCategory(CategoryKind::Value, "source-embed-style", "Source Embed Style", UserValue(ValueCategory::SourceEmbedStyle)); + options.addValues(SourceEmbedUtil::getStyleInfos()); } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -416,6 +427,17 @@ void initCommandOptions(CommandOptions& options) { OptionKind::DumpWarningDiagnostics, "-dump-warning-diagnostics", nullptr, "Dump to output list of warning diagnostic numeric and name ids." }, { OptionKind::InputFilesRemain, "--", nullptr, "Treat the rest of the command line as input files."}, { OptionKind::ReportDownstreamTime, "-report-downstream-time", nullptr, "Reports the time spent in the downstream compiler." }, + { OptionKind::SourceEmbedStyle, "-source-embed-style", "-source-embed-style <source-embed-style>", + "If source embedding is enabled, defines the style used. When enabled (with any style other than `none`), " + "will write compile results into embeddable source for the target language. " + "If no output file is specified, the output is written to stdout. If an output file is specified " + "it is written either to that file directly (if it is appropriate for the target language), " + "or it will be output to the filename with an appropriate extension.\n\n" + "Note for C/C++ with u16/u32/u64 types it is necessary to have \"#include <stdint.h>\" before the generated file.\n" }, + { OptionKind::SourceEmbedName, "-source-embed-name", "-source-embed-name <name>", + "The name used as the basis for variables output for source embedding."}, + { OptionKind::SourceEmbedLanguage, "-source-embed-language", "-source-embed-language <language>", + "The language to be used for source embedding. Defaults to C/C++. Currently only C/C++ are supported"}, }; _addOptions(makeConstArrayView(generalOpts), options); @@ -2287,6 +2309,30 @@ SlangResult OptionsParser::_parse( } break; } + case OptionKind::SourceEmbedStyle: + { + SLANG_RETURN_ON_FAIL(_expectValue(m_requestImpl->m_sourceEmbedStyle)); + break; + } + case OptionKind::SourceEmbedName: + { + CommandLineArg name; + SLANG_RETURN_ON_FAIL(m_reader.expectArg(name)); + m_requestImpl->m_sourceEmbedName = name.value; + break; + } + case OptionKind::SourceEmbedLanguage: + { + SLANG_RETURN_ON_FAIL(_expectValue(m_requestImpl->m_sourceEmbedLanguage)); + + if (!SourceEmbedUtil::isSupported((SlangSourceLanguage)m_requestImpl->m_sourceEmbedLanguage)) + { + m_sink->diagnose(arg.loc, Diagnostics::unhandledLanguageForSourceEmbedding); + return SLANG_FAIL; + } + + break; + } default: { // Hmmm, we looked up and produced a valid enum, but it wasn't handled in the switch... |
