summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-05-22 17:22:22 -0400
committerGitHub <noreply@github.com>2023-05-22 17:22:22 -0400
commit972a931452c3f06a23a4f67ccfb655851df53fa4 (patch)
tree309a75c5239af163e47bc0e123d023683c1ec74a /source
parent33e15236c6fd8623bc516a194ca65e8810f1f855 (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.cpp2
-rw-r--r--source/compiler-core/slang-artifact-desc-util.h3
-rw-r--r--source/compiler-core/slang-dxc-compiler.cpp2
-rw-r--r--source/compiler-core/slang-fxc-compiler.cpp2
-rw-r--r--source/compiler-core/slang-glslang-compiler.cpp2
-rw-r--r--source/compiler-core/slang-nvrtc-compiler.cpp2
-rw-r--r--source/compiler-core/slang-source-embed-util.cpp399
-rw-r--r--source/compiler-core/slang-source-embed-util.h59
-rw-r--r--source/slang/slang-artifact-output-util.cpp4
-rw-r--r--source/slang/slang-compiler.cpp77
-rwxr-xr-xsource/slang/slang-compiler.h10
-rw-r--r--source/slang/slang-diagnostic-defs.h2
-rw-r--r--source/slang/slang-options.cpp46
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...