summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-07-17 10:26:37 -0400
committerGitHub <noreply@github.com>2019-07-17 10:26:37 -0400
commit749634a2a6e03acf435c39f78b933a01b90a7440 (patch)
tree950203d3fc29610428b0ca03eb756911b9b11f47
parentf52f5cd4a7b5b71617b949fc62a78abe8c4822b3 (diff)
Slang -> C++ -> SharedLibrary -> Test (#999)
* WIP: Adding support for C/C++ compilation to slang API. * Removed BackEndType in test harness -> use SlangPassThrough to identify backends Only require stage for targets that require it. Detection of all different backends. * Windows/Unix create temporary filename. * WIP: Output CPU binaries. * Added a pass-through c/c++ test. * Compile C++/C and store in temporary file. * Read the binary back into memory. * Set debug info and optimization flags for C/C++. Make the CPPCompiler debug/optimization levels match slangs. * Handling of include paths and math precision. * Dumping c++/c source and exe/shared library. * Put hex dump into own util. * End to end pass through c compilation test. * WIP: Simple execute test working on Linux/Unix. * Fix typo on linux. * WIP: To compile slang to cpp shared library. Report backend compiler errors. * Compiles slang -> cpp and loads as shared library. * Fix problem on c-cross-compile test because prelude is now included with <> quotes. * Run slang generated cpp code - using hard coded data. * Added cpp-execute-simple, and test output. * Fix warning that broke win32 build. * Fix compilation problem on osx.
-rw-r--r--slang.h11
-rw-r--r--source/core/core.vcxproj2
-rw-r--r--source/core/core.vcxproj.filters6
-rw-r--r--source/core/slang-cpp-compiler.cpp46
-rw-r--r--source/core/slang-cpp-compiler.h53
-rw-r--r--source/core/slang-gcc-compiler-util.cpp77
-rw-r--r--source/core/slang-gcc-compiler-util.h5
-rw-r--r--source/core/slang-hex-dump-util.cpp183
-rw-r--r--source/core/slang-hex-dump-util.h28
-rw-r--r--source/core/slang-io.cpp96
-rw-r--r--source/core/slang-io.h24
-rw-r--r--source/core/slang-string.cpp6
-rw-r--r--source/core/slang-string.h3
-rw-r--r--source/core/slang-visual-studio-compiler-util.cpp91
-rw-r--r--source/core/slang-visual-studio-compiler-util.h4
-rw-r--r--source/core/slang-writer.cpp17
-rw-r--r--source/core/slang-writer.h8
-rw-r--r--source/core/windows/slang-win-visual-studio-util.cpp2
-rw-r--r--source/slang/slang-check.cpp11
-rw-r--r--source/slang/slang-compiler.cpp546
-rw-r--r--source/slang/slang-compiler.h21
-rw-r--r--source/slang/slang-diagnostic-defs.h6
-rw-r--r--source/slang/slang-emit-cpp.cpp5
-rw-r--r--source/slang/slang-options.cpp59
-rw-r--r--source/slang/slang-profile-defs.h4
-rw-r--r--source/slang/slang-profile.h2
-rw-r--r--source/slang/slang-type-layout.cpp2
-rw-r--r--tests/cpp-compiler/simple-c-compile.c10
-rw-r--r--tests/cpp-compiler/simple-c-compile.c.expected6
-rw-r--r--tests/cross-compile/cpp-execute-simple.slang12
-rw-r--r--tests/cross-compile/cpp-execute-simple.slang.expected1
-rw-r--r--tests/cross-compile/cpp-execute.slang106
-rw-r--r--tests/cross-compile/cpp-execute.slang.expected1
-rw-r--r--tests/cross-compile/slang-cpp-prelude.h204
-rw-r--r--tools/slang-test/slang-test-main.cpp301
-rw-r--r--tools/slang-test/test-context.h37
36 files changed, 1786 insertions, 210 deletions
diff --git a/slang.h b/slang.h
index 1fd92f440..55c454d0d 100644
--- a/slang.h
+++ b/slang.h
@@ -514,6 +514,8 @@ extern "C"
SLANG_DXIL_ASM,
SLANG_C_SOURCE, ///< The C language
SLANG_CPP_SOURCE, ///< The C++ language
+ SLANG_EXECUTABLE, ///< Executable (for hosting CPU/OS)
+ SLANG_SHARED_LIBRARY, ///< A shared library/Dll (for hosting CPU/OS)
};
/* A "container format" describes the way that the outputs
@@ -537,6 +539,11 @@ extern "C"
SLANG_PASS_THROUGH_FXC,
SLANG_PASS_THROUGH_DXC,
SLANG_PASS_THROUGH_GLSLANG,
+ SLANG_PASS_THROUGH_CLANG, ///< Clang C/C++ compiler
+ SLANG_PASS_THROUGH_VISUAL_STUDIO, ///< Visual studio C/C++ compiler
+ SLANG_PASS_THROUGH_GCC, ///< GCC C/C++ compiler
+ SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C or C++ compiler, which is decided by the source type
+ SLANG_PASS_THROUGH_COUNT_OF,
};
/*!
@@ -601,6 +608,8 @@ extern "C"
SLANG_SOURCE_LANGUAGE_SLANG,
SLANG_SOURCE_LANGUAGE_HLSL,
SLANG_SOURCE_LANGUAGE_GLSL,
+ SLANG_SOURCE_LANGUAGE_C,
+ SLANG_SOURCE_LANGUAGE_CPP,
};
typedef unsigned int SlangProfileID;
@@ -859,7 +868,7 @@ extern "C"
typedef void(*SlangFuncPtr)(void);
- /** An interface that can be used to encapsulate access to a shared library. An implementaion
+ /** An interface that can be used to encapsulate access to a shared library. An implementation
does not have to implement the library as a shared library.
*/
struct ISlangSharedLibrary: public ISlangUnknown
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj
index 9238cf058..a5a6c1b24 100644
--- a/source/core/core.vcxproj
+++ b/source/core/core.vcxproj
@@ -182,6 +182,7 @@
<ClInclude Include="slang-free-list.h" />
<ClInclude Include="slang-gcc-compiler-util.h" />
<ClInclude Include="slang-hash.h" />
+ <ClInclude Include="slang-hex-dump-util.h" />
<ClInclude Include="slang-io.h" />
<ClInclude Include="slang-list.h" />
<ClInclude Include="slang-math.h" />
@@ -213,6 +214,7 @@
<ClCompile Include="slang-cpp-compiler.cpp" />
<ClCompile Include="slang-free-list.cpp" />
<ClCompile Include="slang-gcc-compiler-util.cpp" />
+ <ClCompile Include="slang-hex-dump-util.cpp" />
<ClCompile Include="slang-io.cpp" />
<ClCompile Include="slang-memory-arena.cpp" />
<ClCompile Include="slang-object-scope-manager.cpp" />
diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters
index 3b3e429ba..fa4101506 100644
--- a/source/core/core.vcxproj.filters
+++ b/source/core/core.vcxproj.filters
@@ -45,6 +45,9 @@
<ClInclude Include="slang-hash.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-hex-dump-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="slang-io.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -134,6 +137,9 @@
<ClCompile Include="slang-gcc-compiler-util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="slang-hex-dump-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="slang-io.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/core/slang-cpp-compiler.cpp b/source/core/slang-cpp-compiler.cpp
index 0ed0a1ba8..54138ab27 100644
--- a/source/core/slang-cpp-compiler.cpp
+++ b/source/core/slang-cpp-compiler.cpp
@@ -19,6 +19,17 @@
namespace Slang
{
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler::Desc !!!!!!!!!!!!!!!!!!!!!!*/
+
+void CPPCompiler::Desc::appendAsText(StringBuilder& out) const
+{
+ out << getCompilerTypeAsText(type);
+ out << " ";
+ out << majorVersion;
+ out << ".";
+ out << minorVersion;
+}
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler::OutputMessage !!!!!!!!!!!!!!!!!!!!!!*/
/* static */UnownedStringSlice CPPCompiler::OutputMessage::getTypeText(OutputMessage::Type type)
@@ -33,6 +44,21 @@ namespace Slang
}
}
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
+
+/* static */UnownedStringSlice CPPCompiler::getCompilerTypeAsText(CompilerType type)
+{
+ switch (type)
+ {
+ default:
+ case CompilerType::Unknown: return UnownedStringSlice::fromLiteral("Unknown");
+ case CompilerType::VisualStudio:return UnownedStringSlice::fromLiteral("Visual Studio");
+ case CompilerType::GCC: return UnownedStringSlice::fromLiteral("GCC");
+ case CompilerType::Clang: return UnownedStringSlice::fromLiteral("Clang");
+ case CompilerType::SNC: return UnownedStringSlice::fromLiteral("SNC");
+ case CompilerType::GHS: return UnownedStringSlice::fromLiteral("GHS");
+ }
+}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler::Output !!!!!!!!!!!!!!!!!!!!!!*/
@@ -167,6 +193,11 @@ SlangResult GenericCPPCompiler::compile(const CompileOptions& options, Output& o
return m_parseOutputFunc(exeRes, outOutput);
}
+SlangResult GenericCPPCompiler::calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath)
+{
+ return m_calcModuleFilePathFunc(options, outPath);
+}
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/
static CPPCompiler::Desc _calcCompiledWithDesc()
@@ -308,7 +339,7 @@ static void _addGCCFamilyCompiler(const String& exeName, CPPCompilerSet* compile
CPPCompiler::Desc desc;
if (SLANG_SUCCEEDED(GCCCompilerUtil::calcVersion(exeName, desc)))
{
- RefPtr<CPPCompiler> compiler(new GenericCPPCompiler(desc, exeName, &GCCCompilerUtil::calcArgs, &GCCCompilerUtil::parseOutput));
+ RefPtr<CPPCompiler> compiler(new GenericCPPCompiler(desc, exeName, &GCCCompilerUtil::calcArgs, &GCCCompilerUtil::parseOutput, GCCCompilerUtil::calcModuleFilePath));
compilerSet->addCompiler(compiler);
}
}
@@ -377,6 +408,19 @@ void CPPCompilerSet::getCompilers(List<CPPCompiler*>& outCompilers) const
outCompilers.addRange((CPPCompiler*const*)m_compilers.begin(), m_compilers.getCount());
}
+bool CPPCompilerSet::hasCompiler(CPPCompiler::CompilerType compilerType) const
+{
+ for (CPPCompiler* compiler : m_compilers)
+ {
+ const auto& desc = compiler->getDesc();
+ if (desc.type == compilerType)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
void CPPCompilerSet::addCompiler(CPPCompiler* compiler)
{
const Index index = _findIndex(compiler->getDesc());
diff --git a/source/core/slang-cpp-compiler.h b/source/core/slang-cpp-compiler.h
index 60b534556..851975d72 100644
--- a/source/core/slang-cpp-compiler.h
+++ b/source/core/slang-cpp-compiler.h
@@ -40,6 +40,8 @@ public:
/// Get the version as a value
Int getVersionValue() const { return majorVersion * 100 + minorVersion; }
+ void appendAsText(StringBuilder& out) const;
+
/// Ctor
Desc(CompilerType inType = CompilerType::Unknown, Int inMajorVersion = 0, Int inMinorVersion = 0):type(inType), majorVersion(inMajorVersion), minorVersion(inMinorVersion) {}
@@ -50,16 +52,26 @@ public:
enum class OptimizationLevel
{
- Normal, ///< Normal optimization
- Debug, ///< General has no optimizations
+ None, ///< Don't optimize at all.
+ Default, ///< Default optimization level: balance code quality and compilation time.
+ High, ///< Optimize aggressively.
+ Maximal, ///< Include optimizations that may take a very long time, or may involve severe space-vs-speed tradeoffs
};
- enum DebugInfoType
+ enum class DebugInfoType
+ {
+ None, ///< Don't emit debug information at all.
+ Minimal, ///< Emit as little debug information as possible, while still supporting stack traces.
+ Standard, ///< Emit whatever is the standard level of debug information for each target.
+ Maximal, ///< Emit as much debug information as possible for each target.
+ };
+ enum class FloatingPointMode
{
- None, ///< Binary has no debug information
- Maximum, ///< Has maximum debug information
- Normal, ///< Has normal debug information
+ Default,
+ Fast,
+ Precise,
};
+
enum TargetType
{
Executable, ///< Produce an executable
@@ -84,10 +96,11 @@ public:
};
};
- OptimizationLevel optimizationLevel = OptimizationLevel::Debug;
- DebugInfoType debugInfoType = DebugInfoType::Normal;
+ OptimizationLevel optimizationLevel = OptimizationLevel::Default;
+ DebugInfoType debugInfoType = DebugInfoType::Standard;
TargetType targetType = TargetType::Executable;
SourceType sourceType = SourceType::CPP;
+ FloatingPointMode floatingPointMode = FloatingPointMode::Default;
Flags flags = Flag::EnableExceptionHandling;
@@ -163,6 +176,11 @@ public:
const Desc& getDesc() const { return m_desc; }
/// Compile using the specified options. The result is in resOut
virtual SlangResult compile(const CompileOptions& options, Output& outOutput) = 0;
+ /// Given the compilation options and the module name, determines the actual file name used for output
+ virtual SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath) = 0;
+
+ /// Return the compiler type as name
+ static UnownedStringSlice getCompilerTypeAsText(CompilerType type);
protected:
@@ -180,26 +198,31 @@ public:
typedef void(*CalcArgsFunc)(const CPPCompiler::CompileOptions& options, CommandLine& cmdLine);
typedef SlangResult(*ParseOutputFunc)(const ExecuteResult& exeResult, Output& output);
+ typedef SlangResult(*CalcModuleFilePathFunc)(const CPPCompiler::CompileOptions& options, StringBuilder& outPath);
virtual SlangResult compile(const CompileOptions& options, Output& outOutput) SLANG_OVERRIDE;
+ virtual SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath) SLANG_OVERRIDE;
- GenericCPPCompiler(const Desc& desc, const String& exeName, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc) :
+ GenericCPPCompiler(const Desc& desc, const String& exeName, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc, CalcModuleFilePathFunc calcModuleFilePathFunc) :
Super(desc),
m_calcArgsFunc(calcArgsFunc),
- m_parseOutputFunc(parseOutputFunc)
+ m_parseOutputFunc(parseOutputFunc),
+ m_calcModuleFilePathFunc(calcModuleFilePathFunc)
{
m_cmdLine.setExecutableFilename(exeName);
}
- GenericCPPCompiler(const Desc& desc, const CommandLine& cmdLine, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc) :
+ GenericCPPCompiler(const Desc& desc, const CommandLine& cmdLine, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc, CalcModuleFilePathFunc calcModuleFilePathFunc) :
Super(desc),
m_cmdLine(cmdLine),
m_calcArgsFunc(calcArgsFunc),
- m_parseOutputFunc(parseOutputFunc)
+ m_parseOutputFunc(parseOutputFunc),
+ m_calcModuleFilePathFunc(calcModuleFilePathFunc)
{}
CalcArgsFunc m_calcArgsFunc;
ParseOutputFunc m_parseOutputFunc;
+ CalcModuleFilePathFunc m_calcModuleFilePathFunc;
CommandLine m_cmdLine;
};
@@ -225,6 +248,9 @@ public:
/// Set the default compiler
void setDefaultCompiler(CPPCompiler* compiler) { m_defaultCompiler = compiler; }
+ /// True if has a compiler of the specified type
+ bool hasCompiler(CPPCompiler::CompilerType compilerType) const;
+
protected:
Index _findIndex(const CPPCompiler::Desc& desc) const;
@@ -263,6 +289,9 @@ struct CPPCompilerUtil
/// Given a set, registers compilers found through standard means and determines a reasonable default compiler if possible
static SlangResult initializeSet(CPPCompilerSet* set);
+
+
+
};
diff --git a/source/core/slang-gcc-compiler-util.cpp b/source/core/slang-gcc-compiler-util.cpp
index d210fa829..69d68e608 100644
--- a/source/core/slang-gcc-compiler-util.cpp
+++ b/source/core/slang-gcc-compiler-util.cpp
@@ -335,6 +335,34 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
return SLANG_OK;
}
+/* static */ SlangResult GCCCompilerUtil::calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath)
+{
+ outPath.Clear();
+
+ switch (options.targetType)
+ {
+ case TargetType::SharedLibrary:
+ {
+ outPath << SharedLibrary::calcPlatformPath(options.modulePath.getUnownedSlice());
+ return SLANG_OK;
+ }
+ case TargetType::Executable:
+ {
+ outPath << options.modulePath;
+ outPath << ProcessUtil::getExecutableSuffix();
+ return SLANG_OK;
+ }
+ case TargetType::Object:
+ {
+ // Will be .o for typical gcc targets
+ outPath << options.modulePath << ".o";
+ return SLANG_OK;
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
/* static */void GCCCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
{
cmdLine.addArg("-fvisibility=hidden");
@@ -349,17 +377,27 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
switch (options.optimizationLevel)
{
- case OptimizationLevel::Debug:
+ case OptimizationLevel::None:
{
// No optimization
cmdLine.addArg("-O0");
break;
}
- case OptimizationLevel::Normal:
+ case OptimizationLevel::Default:
{
cmdLine.addArg("-Os");
break;
}
+ case OptimizationLevel::High:
+ {
+ cmdLine.addArg("-O2");
+ break;
+ }
+ case OptimizationLevel::Maximal:
+ {
+ cmdLine.addArg("-O4");
+ break;
+ }
default: break;
}
@@ -368,6 +406,29 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
cmdLine.addArg("-g");
}
+ switch (options.floatingPointMode)
+ {
+ case FloatingPointMode::Default: break;
+ case FloatingPointMode::Precise:
+ {
+ //cmdLine.addArg("-fno-unsafe-math-optimizations");
+ break;
+ }
+ case FloatingPointMode::Fast:
+ {
+ // We could enable SSE with -mfpmath=sse
+ // But that would only make sense on a x64/x86 type processor and only if that feature is present (it is on all x64)
+ cmdLine.addArg("-ffast-math");
+ break;
+ }
+ }
+
+ StringBuilder moduleFilePath;
+ calcModuleFilePath(options, moduleFilePath);
+
+ cmdLine.addArg("-o");
+ cmdLine.addArg(moduleFilePath);
+
switch (options.targetType)
{
case TargetType::SharedLibrary:
@@ -376,22 +437,10 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
cmdLine.addArg("-shared");
// Position independent
cmdLine.addArg("-fPIC");
-
- String sharedLibraryPath = SharedLibrary::calcPlatformPath(options.modulePath.getUnownedSlice());
-
- cmdLine.addArg("-o");
- cmdLine.addArg(sharedLibraryPath);
break;
}
case TargetType::Executable:
{
- cmdLine.addArg("-o");
-
- StringBuilder builder;
- builder << options.modulePath;
- builder << ProcessUtil::getExecutableSuffix();
-
- cmdLine.addArg(options.modulePath);
break;
}
case TargetType::Object:
diff --git a/source/core/slang-gcc-compiler-util.h b/source/core/slang-gcc-compiler-util.h
index 29070fc61..a72080acc 100644
--- a/source/core/slang-gcc-compiler-util.h
+++ b/source/core/slang-gcc-compiler-util.h
@@ -14,6 +14,7 @@ struct GCCCompilerUtil
typedef CPPCompiler::TargetType TargetType;
typedef CPPCompiler::DebugInfoType DebugInfoType;
typedef CPPCompiler::SourceType SourceType;
+ typedef CPPCompiler::FloatingPointMode FloatingPointMode;
/// Extracts version number into desc from text (assumes gcc/clang -v layout with a line with version)
static SlangResult parseVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, CPPCompiler::Desc& outDesc);
@@ -26,6 +27,10 @@ struct GCCCompilerUtil
/// Parse ExecuteResult into Output
static SlangResult parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput);
+
+ /// Calculate the output module filename
+ static SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath);
+
};
}
diff --git a/source/core/slang-hex-dump-util.cpp b/source/core/slang-hex-dump-util.cpp
new file mode 100644
index 000000000..908f6c1b6
--- /dev/null
+++ b/source/core/slang-hex-dump-util.cpp
@@ -0,0 +1,183 @@
+// slang-hex-dump-util.cpp
+#include "slang-hex-dump-util.h"
+
+#include "slang-common.h"
+#include "slang-string-util.h"
+#include "slang-writer.h"
+
+#include "../../slang-com-helper.h"
+
+namespace Slang
+{
+
+static const UnownedStringSlice s_start = UnownedStringSlice::fromLiteral("--START--");
+static const UnownedStringSlice s_end = UnownedStringSlice::fromLiteral("--END--");
+
+/* static */SlangResult HexDumpUtil::dumpWithMarkers(const List<uint8_t>& data, int maxBytesPerLine, ISlangWriter* writer)
+{
+ WriterHelper helper(writer);
+ SLANG_RETURN_ON_FAIL(helper.write(s_start.begin(), s_start.size()));
+ SLANG_RETURN_ON_FAIL(helper.print(" (%zu)\n", size_t(data.getCount())));
+
+ SLANG_RETURN_ON_FAIL(dump(data, maxBytesPerLine, writer));
+
+ SLANG_RETURN_ON_FAIL(helper.write(s_end.begin(), s_end.size()));
+ SLANG_RETURN_ON_FAIL(helper.put("\n"));
+ return SLANG_OK;
+}
+
+/* static */SlangResult HexDumpUtil::dump(const List<uint8_t>& data, int maxBytesPerLine, ISlangWriter* writer)
+{
+ int maxCharsPerLine = 2 * maxBytesPerLine + 1 + maxBytesPerLine + 1;
+
+ const uint8_t* cur = data.begin();
+ const uint8_t* end = data.end();
+
+ static char s_hex[] = "0123456789abcdef";
+
+ while (cur < end)
+ {
+ size_t count = size_t(end - cur);
+ count = (count > size_t(maxBytesPerLine)) ? size_t(maxBytesPerLine) : count;
+
+ char* startDst = writer->beginAppendBuffer(maxCharsPerLine);
+ char* dst = startDst;
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ uint8_t byte = cur[i];
+ *dst++ = s_hex[byte >> 4];
+ *dst++ = s_hex[byte & 0xf];
+ }
+
+ *dst++ = ' ';
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ char c = char(cur[i]);
+
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 32 && (c & 0x80) == 0))
+ {
+ }
+ else
+ {
+ c = '.';
+ }
+ *dst++ = c;
+ }
+
+ *dst++ = '\n';
+ SLANG_ASSERT(dst <= startDst + maxCharsPerLine);
+
+ SLANG_RETURN_ON_FAIL(writer->endAppendBuffer(startDst, size_t(dst - startDst)));
+
+ cur += count;
+ }
+
+ return SLANG_OK;
+}
+
+static int _parseHexDigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ {
+ return c -'0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ return c - 'A' + 10;
+ }
+ return -1;
+}
+
+/* static */SlangResult HexDumpUtil::parse(const UnownedStringSlice& lines, List<uint8_t>& outBytes)
+{
+ outBytes.clear();
+
+ bool inHex = false;
+
+ LineParser lineParser(lines);
+ for (const auto& line : lineParser)
+ {
+ if (!inHex)
+ {
+ if (line.startsWith(s_start))
+ {
+ inHex = true;
+ continue;
+ }
+ }
+ else
+ {
+ if (line.startsWith(s_end))
+ {
+ break;
+ }
+ }
+
+ const char* cur = line.begin();
+ const char* end = line.end();
+
+ while(cur + 2 <= end)
+ {
+ const char c = cur[0];
+ if (c == ' ' || c== '\n' || c == '\r' || c == '\t')
+ {
+ // Skip to next line
+ break;
+ }
+
+ const int hi = _parseHexDigit(c);
+ const int lo = _parseHexDigit(cur[1]);
+ cur += 2;
+
+ if (hi < 0 || lo < 0)
+ {
+ return SLANG_FAIL;
+ }
+ outBytes.add(uint8_t((hi << 4) | lo));
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult HexDumpUtil::parseWithMarkers(const UnownedStringSlice& lines, List<uint8_t>& outBytes)
+{
+ UnownedStringSlice remaining(lines), line;
+
+ while(StringUtil::extractLine(remaining, line))
+ {
+ if (line.startsWith(s_start))
+ {
+ // Extract next line
+ if (!StringUtil::extractLine(remaining, line))
+ {
+ return SLANG_FAIL;
+ }
+ // It's the start line
+ UnownedStringSlice startLine = line;
+
+ // Look for the ending line
+ do
+ {
+ if (line.startsWith(s_end))
+ {
+ return parse(UnownedStringSlice(startLine.begin(), line.begin()), outBytes);
+ }
+ }
+ while ( StringUtil::extractLine(remaining, line));
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
+}
diff --git a/source/core/slang-hex-dump-util.h b/source/core/slang-hex-dump-util.h
new file mode 100644
index 000000000..580f5a120
--- /dev/null
+++ b/source/core/slang-hex-dump-util.h
@@ -0,0 +1,28 @@
+#ifndef SLANG_HEX_DUMP_UTIL_H
+#define SLANG_HEX_DUMP_UTIL_H
+
+#include "slang-common.h"
+#include "slang-string.h"
+
+#include "slang-list.h"
+#include "../../slang.h"
+
+namespace Slang
+{
+
+struct HexDumpUtil
+{
+ /// Dump data to writer, with lines starting with hex data
+ static SlangResult dump(const List<uint8_t>& data, int numBytesPerLine, ISlangWriter* writer);
+
+ static SlangResult dumpWithMarkers(const List<uint8_t>& data, int numBytesPerLine, ISlangWriter* writer);
+
+ /// Parses lines formatted by dump, back into bytes
+ static SlangResult parse(const UnownedStringSlice& lines, List<uint8_t>& outBytes);
+
+ static SlangResult parseWithMarkers(const UnownedStringSlice& lines, List<uint8_t>& outBytes);
+};
+
+}
+
+#endif
diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp
index 6cdd75c74..caa45c8f4 100644
--- a/source/core/slang-io.cpp
+++ b/source/core/slang-io.cpp
@@ -17,7 +17,7 @@
# include <Windows.h>
#endif
-#if defined(__linux__) || defined(__CYGWIN__)
+#if defined(__linux__) || defined(__CYGWIN__) || SLANG_APPLE_FAMILY
# include <unistd.h>
#endif
@@ -51,6 +51,99 @@ namespace Slang
#endif
}
+
+#ifdef _WIN32
+ /* static */SlangResult File::generateTemporary(const UnownedStringSlice& inPrefix, Slang::String& outFileName)
+ {
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/creating-and-using-a-temporary-file
+
+ String tempPath;
+ {
+ int count = MAX_PATH + 1;
+ while (true)
+ {
+ char* chars = tempPath.prepareForAppend(count);
+ // Gets the temp path env string (no guarantee it's a valid path).
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha
+ DWORD ret = ::GetTempPathA(count - 1, chars);
+ if (ret == 0)
+ {
+ return SLANG_FAIL;
+ }
+ if (ret > DWORD(count - 1))
+ {
+ count = ret + 1;
+ continue;
+ }
+ tempPath.appendInPlace(chars, count);
+ break;
+ }
+ }
+
+ if (!File::exists(tempPath))
+ {
+ return SLANG_FAIL;
+ }
+
+ const String prefix(inPrefix);
+ String tempFileName;
+
+ {
+ int count = MAX_PATH + 1;
+ char* chars = tempFileName.prepareForAppend(count);
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea
+ // Generates a temporary file name.
+ DWORD ret = ::GetTempFileNameA(tempPath.getBuffer(), prefix.getBuffer(), 0, chars);
+
+ if (ret == 0)
+ {
+ return SLANG_FAIL;
+ }
+ tempFileName.appendInPlace(chars, ::strlen(chars));
+ }
+
+ outFileName = tempFileName;
+ return SLANG_OK;
+ }
+#else
+ /* static */SlangResult File::generateTemporary(const UnownedStringSlice& inPrefix, Slang::String& outFileName)
+ {
+ StringBuilder builder;
+ builder << "/tmp/" << inPrefix << "-XXXXXX";
+
+ List<char> buffer;
+ buffer.setCount(builder.getLength() + 1);
+ ::memcpy(buffer.getBuffer(), builder.getBuffer(), builder.getLength());
+ buffer[builder.getLength()] = 0;
+
+ int handle = mkstemp(buffer.getBuffer());
+ if (handle == -1)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Close the handle..
+ close(handle);
+
+ outFileName = buffer.getBuffer();
+ return SLANG_OK;
+ }
+#endif
+
+ /* static */SlangResult File::makeExecutable(const String& fileName)
+ {
+#ifdef _WIN32
+ SLANG_UNUSED(fileName);
+ // As long as file extension is executable, it can be executed
+ return SLANG_OK;
+#else
+ const int ret = ::chmod(fileName.getBuffer(), S_IXUSR);
+ return (ret == 0) ? SLANG_OK : SLANG_FAIL;
+#endif
+ }
+
+
bool File::exists(const String& fileName)
{
#ifdef _WIN32
@@ -585,5 +678,6 @@ namespace Slang
writer.Write(text);
}
+
}
diff --git a/source/core/slang-io.h b/source/core/slang-io.h
index da63e9c20..e2935afe4 100644
--- a/source/core/slang-io.h
+++ b/source/core/slang-io.h
@@ -16,6 +16,10 @@ namespace Slang
static Slang::List<unsigned char> readAllBytes(const Slang::String& fileName);
static void writeAllText(const Slang::String& fileName, const Slang::String& text);
static SlangResult remove(const String& fileName);
+
+ static SlangResult makeExecutable(const String& fileName);
+
+ static SlangResult generateTemporary(const UnownedStringSlice& prefix, Slang::String& outFileName);
};
class Path
@@ -85,6 +89,26 @@ namespace Slang
/// @return The first element of the path, or empty
static UnownedStringSlice getFirstElement(const UnownedStringSlice& path);
};
+
+ // Helper class to clean up temporary files on dtor
+ struct TemporaryFileSet
+ {
+ void add(const String& path)
+ {
+ if (m_paths.indexOf(path) < 0)
+ {
+ m_paths.add(path);
+ }
+ }
+ ~TemporaryFileSet()
+ {
+ for (const auto& path : m_paths)
+ {
+ File::remove(path);
+ }
+ }
+ List<String> m_paths;
+ };
}
#endif
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index 8b2a35b3f..22049f82d 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -361,6 +361,12 @@ namespace Slang
append(&chr, &chr + 1);
}
+
+ void String::appendChar(char chr)
+ {
+ append(&chr, &chr + 1);
+ }
+
void String::append(String const& str)
{
if (!m_buffer)
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index cb2731cee..357eb726e 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -443,6 +443,9 @@ namespace Slang
void append(StringSlice const& slice);
void append(UnownedStringSlice const& slice);
+ /// Append a character (to remove ambiguity with other integral types)
+ void appendChar(char chr);
+
String(int32_t val, int radix = 10)
{
append(val, radix);
diff --git a/source/core/slang-visual-studio-compiler-util.cpp b/source/core/slang-visual-studio-compiler-util.cpp
index 20d655e78..765edc349 100644
--- a/source/core/slang-visual-studio-compiler-util.cpp
+++ b/source/core/slang-visual-studio-compiler-util.cpp
@@ -10,6 +10,33 @@
namespace Slang
{
+/* static */ SlangResult VisualStudioCompilerUtil::calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath)
+{
+ outPath.Clear();
+
+ switch (options.targetType)
+ {
+ case TargetType::SharedLibrary:
+ {
+ outPath << options.modulePath << ".dll";
+ return SLANG_OK;
+ }
+ case TargetType::Executable:
+ {
+ outPath << options.modulePath << ".exe";
+ return SLANG_OK;
+ }
+ case TargetType::Object:
+ {
+ outPath << options.modulePath << ".obj";
+ return SLANG_OK;
+ }
+ default: break;
+ }
+
+ return SLANG_FAIL;
+}
+
/* static */void VisualStudioCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
{
// https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=vs-2019
@@ -30,30 +57,72 @@ namespace Slang
}
}
+ switch (options.debugInfoType)
+ {
+ default:
+ {
+ // Multithreaded statically linked runtime library
+ cmdLine.addArg("/MD");
+ break;
+ }
+ case DebugInfoType::Maximal:
+ {
+ // Multithreaded statically linked *debug* runtime library
+ cmdLine.addArg("/MDd");
+ break;
+ }
+ }
+
+ // /Fd - followed by name of the pdb file
+ if (options.debugInfoType != DebugInfoType::None)
+ {
+ cmdLine.addPrefixPathArg("/Fd", options.modulePath, ".pdb");
+ }
+
switch (options.optimizationLevel)
{
- case OptimizationLevel::Debug:
+ case OptimizationLevel::None:
{
// No optimization
- cmdLine.addArg("/Od");
-
- cmdLine.addArg("/MDd");
+ cmdLine.addArg("/Od");
+ break;
+ }
+ case OptimizationLevel::Default:
+ {
break;
}
- case OptimizationLevel::Normal:
+ case OptimizationLevel::High:
{
cmdLine.addArg("/O2");
- // Multithreaded DLL
- cmdLine.addArg("/MD");
+ break;
+ }
+ case OptimizationLevel::Maximal:
+ {
+ cmdLine.addArg("/Ox");
break;
}
default: break;
}
- // /Fd - followed by name of the pdb file
- if (options.debugInfoType != DebugInfoType::None)
+ switch (options.floatingPointMode)
{
- cmdLine.addPrefixPathArg("/Fd", options.modulePath, ".pdb");
+ case FloatingPointMode::Default: break;
+ case FloatingPointMode::Precise:
+ {
+ // precise is default behavior, VS also has 'strict'
+ //
+ // ```/fp:strict has behavior similar to /fp:precise, that is, the compiler preserves the source ordering and rounding properties of floating-point code when
+ // it generates and optimizes object code for the target machine, and observes the standard when handling special values. In addition, the program may safely
+ // access or modify the floating-point environment at runtime.```
+
+ cmdLine.addArg("/fp:precise");
+ break;
+ }
+ case FloatingPointMode::Fast:
+ {
+ cmdLine.addArg("/fp:fast");
+ break;
+ }
}
switch (options.targetType)
@@ -61,7 +130,7 @@ namespace Slang
case TargetType::SharedLibrary:
{
// Create dynamic link library
- if (options.optimizationLevel == OptimizationLevel::Debug)
+ if (options.debugInfoType == DebugInfoType::None)
{
cmdLine.addArg("/LDd");
}
diff --git a/source/core/slang-visual-studio-compiler-util.h b/source/core/slang-visual-studio-compiler-util.h
index 86845b5af..cc34b49f5 100644
--- a/source/core/slang-visual-studio-compiler-util.h
+++ b/source/core/slang-visual-studio-compiler-util.h
@@ -14,11 +14,15 @@ struct VisualStudioCompilerUtil
typedef CPPCompiler::DebugInfoType DebugInfoType;
typedef CPPCompiler::SourceType SourceType;
typedef CPPCompiler::OutputMessage OutputMessage;
+ typedef CPPCompiler::FloatingPointMode FloatingPointMode;
/// Calculate Visual Studio family compilers cmdLine arguments from options
static void calcArgs(const CompileOptions& options, CommandLine& cmdLine);
/// Parse Visual Studio exeRes into CPPCompiler::Output
static SlangResult parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput);
+
+ static SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath);
+
};
}
diff --git a/source/core/slang-writer.cpp b/source/core/slang-writer.cpp
index 5b643fff8..2f694aeec 100644
--- a/source/core/slang-writer.cpp
+++ b/source/core/slang-writer.cpp
@@ -1,3 +1,5 @@
+#define _CRT_SECURE_NO_WARNINGS
+
#include "slang-writer.h"
#include "slang-platform.h"
@@ -166,6 +168,21 @@ SlangResult FileWriter::setMode(SlangWriterMode mode)
return SLANG_FAIL;
}
+/* static */SlangResult FileWriter::create(const char* filePath, const char* writeOptions, WriterFlags flags, ComPtr<ISlangWriter>& outWriter)
+{
+ flags &= ~WriterFlag::IsUnowned;
+
+ FILE* file = fopen(filePath, writeOptions);
+ if (!file)
+ {
+ return SLANG_E_CANNOT_OPEN;
+ }
+
+ outWriter = new FileWriter(file, flags);
+ return SLANG_OK;
+}
+
+
/* !!!!!!!!!!!!!!!!!!!!!!!!! StringWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
SLANG_NO_THROW char* SLANG_MCALL StringWriter::beginAppendBuffer(size_t maxNumChars)
diff --git a/source/core/slang-writer.h b/source/core/slang-writer.h
index cd5737b3d..759644fde 100644
--- a/source/core/slang-writer.h
+++ b/source/core/slang-writer.h
@@ -4,6 +4,8 @@
#include "slang-string.h"
#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
#include "slang-list.h"
namespace Slang
@@ -118,6 +120,12 @@ public:
m_file(file)
{}
+ ///
+ static SlangResult create(const char* filePath, const char* writeOptions, WriterFlags flags, ComPtr<ISlangWriter>& outWriter);
+
+ static SlangResult createBinary(const char* filePath, WriterFlags flags, ComPtr<ISlangWriter>& outWriter) { return create(filePath, "wb", flags, outWriter); }
+ static SlangResult createText(const char* filePath, WriterFlags flags, ComPtr<ISlangWriter>& outWriter) { return create(filePath, "w", flags, outWriter); }
+
/// Dtor
~FileWriter();
diff --git a/source/core/windows/slang-win-visual-studio-util.cpp b/source/core/windows/slang-win-visual-studio-util.cpp
index 7e683ee55..a406e168f 100644
--- a/source/core/windows/slang-win-visual-studio-util.cpp
+++ b/source/core/windows/slang-win-visual-studio-util.cpp
@@ -293,7 +293,7 @@ static SlangResult _find(int versionIndex, WinVisualStudioUtil::VersionPath& out
CommandLine cmdLine;
calcExecuteCompilerArgs(versionPath, cmdLine);
- RefPtr<GenericCPPCompiler> compiler = new GenericCPPCompiler(desc, cmdLine, &VisualStudioCompilerUtil::calcArgs, &VisualStudioCompilerUtil::parseOutput);
+ RefPtr<GenericCPPCompiler> compiler = new GenericCPPCompiler(desc, cmdLine, &VisualStudioCompilerUtil::calcArgs, &VisualStudioCompilerUtil::parseOutput, &VisualStudioCompilerUtil::calcModuleFilePath);
set->addCompiler(compiler);
}
}
diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp
index 83b75b964..a8900986b 100644
--- a/source/slang/slang-check.cpp
+++ b/source/slang/slang-check.cpp
@@ -381,6 +381,17 @@ namespace Slang
return func;
}
+ CPPCompilerSet* Session::requireCPPCompilerSet()
+ {
+ if (cppCompilerSet == nullptr)
+ {
+ cppCompilerSet = new CPPCompilerSet;
+ CPPCompilerUtil::initializeSet(cppCompilerSet);
+ }
+ SLANG_ASSERT(cppCompilerSet);
+ return cppCompilerSet;
+ }
+
enum class CheckingPhase
{
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index ff2facc26..64afef136 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -4,6 +4,7 @@
#include "../core/slang-platform.h"
#include "../core/slang-io.h"
#include "../core/slang-string-util.h"
+#include "../core/slang-hex-dump-util.h"
#include "slang-compiler.h"
#include "slang-lexer.h"
@@ -84,7 +85,9 @@ namespace Slang
x("dxil", DXIL) \
x("dxil-asm,dxil-assembly", DXILAssembly) \
x("c", CSource) \
- x("cpp", CPPSource)
+ x("cpp", CPPSource) \
+ x("exe,executable", Executable) \
+ x("sharedlib,sharedlibrary,dll", SharedLibrary)
#define SLANG_CODE_GEN_INFO(names, e) \
{ CodeGenTarget::e, UnownedStringSlice::fromLiteral(names) },
@@ -369,6 +372,22 @@ namespace Slang
return Stage::Unknown;
}
+ static UnownedStringSlice _getPassThroughAsText(PassThroughMode mode)
+ {
+ switch (mode)
+ {
+ case PassThroughMode::None: return UnownedStringSlice::fromLiteral("None");
+ case PassThroughMode::Dxc: return UnownedStringSlice::fromLiteral("Dxc");
+ case PassThroughMode::Fxc: return UnownedStringSlice::fromLiteral("Fxc");
+ case PassThroughMode::Glslang: return UnownedStringSlice::fromLiteral("Glslang");
+ case PassThroughMode::Clang: return UnownedStringSlice::fromLiteral("Clang");
+ case PassThroughMode::VisualStudio: return UnownedStringSlice::fromLiteral("VisualStudio");
+ case PassThroughMode::Gcc: return UnownedStringSlice::fromLiteral("GCC");
+ case PassThroughMode::GenericCCpp: return UnownedStringSlice::fromLiteral("Generic C/C++ Compiler");
+ default: return UnownedStringSlice::fromLiteral("Unknown");
+ }
+ }
+
SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough)
{
switch (passThrough)
@@ -378,7 +397,7 @@ namespace Slang
// If no pass through -> that will always work!
return SLANG_OK;
}
- case PassThroughMode::dxc:
+ case PassThroughMode::Dxc:
{
#if SLANG_ENABLE_DXIL_SUPPORT
// Must have dxc
@@ -386,7 +405,7 @@ namespace Slang
#endif
break;
}
- case PassThroughMode::fxc:
+ case PassThroughMode::Fxc:
{
#if SLANG_ENABLE_DXBC_SUPPORT
// Must have fxc
@@ -394,13 +413,32 @@ namespace Slang
#endif
break;
}
- case PassThroughMode::glslang:
+ case PassThroughMode::Glslang:
{
#if SLANG_ENABLE_GLSLANG_SUPPORT
return session->getOrLoadSharedLibrary(Slang::SharedLibraryType::Glslang, nullptr) ? SLANG_OK : SLANG_E_NOT_FOUND;
#endif
break;
}
+ case PassThroughMode::Clang:
+ {
+ return session->requireCPPCompilerSet()->hasCompiler(CPPCompiler::CompilerType::Clang) ? SLANG_OK: SLANG_E_NOT_FOUND;
+ }
+ case PassThroughMode::VisualStudio:
+ {
+ return session->requireCPPCompilerSet()->hasCompiler(CPPCompiler::CompilerType::VisualStudio) ? SLANG_OK: SLANG_E_NOT_FOUND;
+ }
+ case PassThroughMode::Gcc:
+ {
+ return session->requireCPPCompilerSet()->hasCompiler(CPPCompiler::CompilerType::GCC) ? SLANG_OK: SLANG_E_NOT_FOUND;
+ }
+ case PassThroughMode::GenericCCpp:
+ {
+ List<CPPCompiler::Desc> descs;
+ session->requireCPPCompilerSet()->getCompilerDescs(descs);
+
+ return descs.getCount() ? SLANG_OK: SLANG_E_NOT_FOUND;
+ }
}
return SLANG_E_NOT_IMPLEMENTED;
}
@@ -428,17 +466,17 @@ namespace Slang
case CodeGenTarget::SPIRVAssembly:
case CodeGenTarget::SPIRV:
{
- return PassThroughMode::glslang;
+ return PassThroughMode::Glslang;
}
case CodeGenTarget::DXBytecode:
case CodeGenTarget::DXBytecodeAssembly:
{
- return PassThroughMode::fxc;
+ return PassThroughMode::Fxc;
}
case CodeGenTarget::DXIL:
case CodeGenTarget::DXILAssembly:
{
- return PassThroughMode::dxc;
+ return PassThroughMode::Dxc;
}
case CodeGenTarget::CPPSource:
case CodeGenTarget::CSource:
@@ -446,6 +484,12 @@ namespace Slang
// Don't need an external compiler to output C and C++ code
return PassThroughMode::None;
}
+ case CodeGenTarget::SharedLibrary:
+ case CodeGenTarget::Executable:
+ {
+ // We need some C/C++ compiler
+ return PassThroughMode::GenericCCpp;
+ }
default: break;
}
@@ -485,6 +529,27 @@ namespace Slang
return translationUnit;
}
+ static void _appendEscapedPath(const UnownedStringSlice& path, StringBuilder& outBuilder)
+ {
+ for (auto c : path)
+ {
+ // TODO(JS): Probably want more sophisticated handling...
+ if (c == '\\')
+ {
+ outBuilder.appendChar(c);
+ }
+ outBuilder.appendChar(c);
+ }
+ }
+
+ static void _appendCodeWithPath(const UnownedStringSlice& filePath, const UnownedStringSlice& fileContent, StringBuilder& outCodeBuilder)
+ {
+ outCodeBuilder << "#line 1 \"";
+ _appendEscapedPath(filePath, outCodeBuilder);
+ outCodeBuilder << "\"\n";
+ outCodeBuilder << fileContent << "\n";
+ }
+
String emitHLSLForEntryPoint(
BackEndCompileRequest* compileRequest,
EntryPoint* entryPoint,
@@ -503,26 +568,7 @@ namespace Slang
StringBuilder codeBuilder;
for(auto sourceFile : translationUnit->getSourceFiles())
{
- codeBuilder << "#line 1 \"";
-
- const String& path = sourceFile->getPathInfo().foundPath;
-
- for(auto c : path)
- {
- char buffer[] = { c, 0 };
- switch(c)
- {
- default:
- codeBuilder << buffer;
- break;
-
- case '\\':
- codeBuilder << "\\\\";
- }
- }
- codeBuilder << "\"\n";
-
- codeBuilder << sourceFile->getContent() << "\n";
+ _appendCodeWithPath(sourceFile->getPathInfo().foundPath.getUnownedSlice(), sourceFile->getContent(), codeBuilder);
}
return codeBuilder.ProduceString();
@@ -537,6 +583,35 @@ namespace Slang
}
}
+ String emitCPPForEntryPoint(
+ BackEndCompileRequest* compileRequest,
+ EntryPoint* entryPoint,
+ Int entryPointIndex,
+ TargetRequest* targetReq,
+ EndToEndCompileRequest* endToEndReq)
+ {
+ if (auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex))
+ {
+ // Generate a string that includes the content of
+ // the source file(s), along with a line directive
+ // to ensure that we get reasonable messages
+ // from the downstream compiler when in pass-through
+ // mode.
+
+ StringBuilder codeBuilder;
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ _appendCodeWithPath(sourceFile->getPathInfo().foundPath.getUnownedSlice(), sourceFile->getContent(), codeBuilder);
+ }
+
+ return codeBuilder.ProduceString();
+ }
+ else
+ {
+ return emitEntryPoint(compileRequest, entryPoint, CodeGenTarget::CPPSource, targetReq);
+ }
+ }
+
String emitGLSLForEntryPoint(
BackEndCompileRequest* compileRequest,
EntryPoint* entryPoint,
@@ -711,10 +786,10 @@ namespace Slang
if(!translationUnitRequest)
return "slang-generated";
- auto sink = endToEndReq->getSink();
-
const auto& sourceFiles = translationUnitRequest->getSourceFiles();
+ auto sink = endToEndReq->getSink();
+
const Index numSourceFiles = sourceFiles.getCount();
switch (numSourceFiles)
@@ -1028,6 +1103,381 @@ SlangResult dissassembleDXILUsingDXC(
return SLANG_OK;
}
+ SlangResult emitCPUBinaryForEntryPoint(
+ BackEndCompileRequest* slangRequest,
+ EntryPoint* entryPoint,
+ Int entryPointIndex,
+ TargetRequest* targetReq,
+ EndToEndCompileRequest* endToEndReq,
+ List<uint8_t>& binOut)
+ {
+ auto sink = slangRequest->getSink();
+
+ const String originalSourcePath = calcSourcePathForEntryPoint(endToEndReq, entryPointIndex);
+
+ binOut.clear();
+
+ CPPCompilerSet* compilerSet = slangRequest->getSession()->requireCPPCompilerSet();
+
+ // Determine compiler to use
+ CPPCompiler* compiler = nullptr;
+ switch (endToEndReq->passThrough)
+ {
+ case PassThroughMode::None:
+ case PassThroughMode::GenericCCpp:
+ {
+ // If there is no pass through... still need a compiler
+ compiler = compilerSet->getDefaultCompiler();
+ break;
+ }
+ case PassThroughMode::Clang:
+ {
+ compiler = CPPCompilerUtil::findCompiler(compilerSet, CPPCompilerUtil::MatchType::Newest, CPPCompiler::Desc(CPPCompiler::CompilerType::Clang));
+ break;
+ }
+ case PassThroughMode::VisualStudio:
+ {
+ compiler = CPPCompilerUtil::findCompiler(compilerSet, CPPCompilerUtil::MatchType::Newest, CPPCompiler::Desc(CPPCompiler::CompilerType::VisualStudio));
+ break;
+ }
+ case PassThroughMode::Gcc:
+ {
+ compiler = CPPCompilerUtil::findCompiler(compilerSet, CPPCompilerUtil::MatchType::Newest, CPPCompiler::Desc(CPPCompiler::CompilerType::GCC));
+ break;
+ }
+ }
+
+ if (!compiler)
+ {
+ if (endToEndReq->passThrough != PassThroughMode::None)
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::passThroughCompilerNotFound, _getPassThroughAsText(endToEndReq->passThrough));
+ }
+ else
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::cppCompilerNotFound);
+ }
+ return SLANG_FAIL;
+ }
+
+ TemporaryFileSet temporaryFileSet;
+
+ bool useOriginalFile = false;
+
+ String compileSourcePath;
+ String sourceContents;
+
+ String rawSource;
+
+ SourceLanguage rawSourceLanguage = SourceLanguage::Unknown;
+
+ Dictionary<String, String> preprocessorDefinitions;
+ List<String> includePaths;
+
+ /* This is more convoluted than the other scenarios, because when we invoke C/C++ compiler we would ideally like
+ to use the original file. We want to do this because we want includes relative to the source file to work, and
+ for that to work most easily we want to use the original file, if there is one */
+ if (auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex))
+ {
+ // If it's pass through we accumulate the preprocessor definitions.
+ for (auto& define : translationUnit->compileRequest->preprocessorDefinitions)
+ {
+ preprocessorDefinitions.Add(define.Key, define.Value);
+ }
+ for (auto& define : translationUnit->preprocessorDefinitions)
+ {
+ preprocessorDefinitions.Add(define.Key, define.Value);
+ }
+
+ {
+ /* TODO(JS): Not totally clear what options should be set here. If we are using the pass through - then using say the defines/includes
+ all makes total sense. If we are generating C++ code from slang, then should we really be using these values -> aren't they what is
+ being set for the *slang* source, not for the C++ generated code. That being the case it implies that there needs to be a mechanism
+ (if there isn't already) to specify such information on a particular pass/pass through etc.
+
+ On invoking DXC for example include paths do not appear to be set at all (even with pass-through).
+ */
+
+ auto linkage = targetReq->getLinkage();
+
+ // Add all the search paths
+
+ const auto searchDirectories = linkage->getSearchDirectories();
+ const SearchDirectoryList* searchList = &searchDirectories;
+ while (searchList)
+ {
+ for (const auto& searchDirectory : searchList->searchDirectories)
+ {
+ includePaths.add(searchDirectory.path);
+ }
+ searchList = searchList->parent;
+ }
+ }
+
+ // We are just passing thru, so it's whatever it originally was
+ rawSourceLanguage = translationUnit->sourceLanguage;
+
+ const auto& sourceFiles = translationUnit->getSourceFiles();
+ if (sourceFiles.getCount() == 1)
+ {
+ const SourceFile* sourceFile = sourceFiles[0];
+ const PathInfo& pathInfo = sourceFile->getPathInfo();
+ if (pathInfo.type == PathInfo::Type::FoundPath || pathInfo.type == PathInfo::Type::Normal)
+ {
+ compileSourcePath = pathInfo.foundPath;
+ // We can see if we can load it
+ if (File::exists(compileSourcePath))
+ {
+ // Here we look for the file on the regular file system (as opposed to using the
+ // ISlangFileSystem. This is unfortunate but necessary - because when we call out
+ // to the CPP compiler all it is able to (currently) see are files on the file system.
+ //
+ // Note that it could be coincidence that the filesystem has a file that's identical in
+ // contents/name. That being the case though, any includes wouldn't work for a generated
+ // file either from some specialized ISlangFileSystem, so this is probably as good as it gets
+ // until we can integrate directly to a C/C++ compiler through say a shared library where we can control
+ // file system access.
+ try
+ {
+ String readContents = File::readAllText(compileSourcePath);
+ // We should see if they are the same
+ useOriginalFile = (sourceFile->getContent() == readContents.getUnownedSlice());
+ }
+ catch (const Slang::IOException&)
+ {
+ }
+ }
+ }
+ }
+
+ if (!useOriginalFile)
+ {
+ StringBuilder codeBuilder;
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ _appendCodeWithPath(sourceFile->getPathInfo().foundPath.getUnownedSlice(), sourceFile->getContent(), codeBuilder);
+ }
+ sourceContents = codeBuilder.ProduceString();
+ }
+ }
+ else
+ {
+ rawSource = emitCPPForEntryPoint(
+ slangRequest,
+ entryPoint,
+ entryPointIndex,
+ targetReq,
+ endToEndReq);
+
+ maybeDumpIntermediate(slangRequest, rawSource.getBuffer(), CodeGenTarget::CPPSource);
+
+ rawSourceLanguage = SourceLanguage::CPP;
+ }
+
+ List<String> tempFiles;
+
+ if (!useOriginalFile)
+ {
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice::fromLiteral("slang-generated"), compileSourcePath));
+
+ // Make the temporary filename have the appropriate extension.
+ // NOTE: Strictly speaking that may introduce a temp filename clash, but in practice is extraordinary unlikely
+ if (rawSourceLanguage == SourceLanguage::C)
+ {
+ compileSourcePath.append(".c");
+ }
+ else
+ {
+ compileSourcePath.append(".cpp");
+ }
+
+ // Delete this path at end of execution
+ temporaryFileSet.add(compileSourcePath);
+
+ try
+ {
+ File::writeAllText(compileSourcePath, rawSource);
+ }
+ catch (...)
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::unableToWriteFile, compileSourcePath);
+ return SLANG_FAIL;
+ }
+ }
+
+ CPPCompiler::CompileOptions options;
+
+ // Generate a path a temporary filename for output module
+ String modulePath;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice::fromLiteral("slang-generated"), modulePath));
+
+ options.modulePath = modulePath;
+ options.sourceFiles.add(compileSourcePath);
+
+ // Set what kind of target we should build
+ switch (targetReq->target)
+ {
+ case CodeGenTarget::SharedLibrary:
+ {
+ options.targetType = CPPCompiler::TargetType::SharedLibrary;
+ break;
+ }
+ case CodeGenTarget::Executable:
+ {
+ options.targetType = CPPCompiler::TargetType::Executable;
+ break;
+ }
+ default: break;
+ }
+
+ String moduleFilePath;
+
+ {
+ StringBuilder builder;
+ SLANG_RETURN_ON_FAIL(compiler->calcModuleFilePath(options, builder));
+ moduleFilePath = builder.ProduceString();
+ }
+
+ // Add so it's deleted at the end
+ temporaryFileSet.add(moduleFilePath);
+
+ // Need to configure for the compilation
+
+ {
+ auto linkage = targetReq->getLinkage();
+
+ switch (linkage->optimizationLevel)
+ {
+ case OptimizationLevel::None: options.optimizationLevel = CPPCompiler::OptimizationLevel::None; break;
+ case OptimizationLevel::Default: options.optimizationLevel = CPPCompiler::OptimizationLevel::Default; break;
+ case OptimizationLevel::High: options.optimizationLevel = CPPCompiler::OptimizationLevel::High; break;
+ case OptimizationLevel::Maximal: options.optimizationLevel = CPPCompiler::OptimizationLevel::Maximal; break;
+ default: SLANG_ASSERT(!"Unhandled optimization level"); break;
+ }
+
+ switch (linkage->debugInfoLevel)
+ {
+ case DebugInfoLevel::None: options.debugInfoType = CPPCompiler::DebugInfoType::None; break;
+ case DebugInfoLevel::Minimal: options.debugInfoType = CPPCompiler::DebugInfoType::Minimal; break;
+
+ case DebugInfoLevel::Standard: options.debugInfoType = CPPCompiler::DebugInfoType::Standard; break;
+ case DebugInfoLevel::Maximal: options.debugInfoType = CPPCompiler::DebugInfoType::Maximal; break;
+ default: SLANG_ASSERT(!"Unhandled debug level"); break;
+ }
+
+ switch( targetReq->floatingPointMode )
+ {
+ case FloatingPointMode::Default: options.floatingPointMode = CPPCompiler::FloatingPointMode::Default; break;
+ case FloatingPointMode::Precise: options.floatingPointMode = CPPCompiler::FloatingPointMode::Precise; break;
+ case FloatingPointMode::Fast: options.floatingPointMode = CPPCompiler::FloatingPointMode::Fast; break;
+ default: SLANG_ASSERT(!"Unhanlde floating point mode");
+ }
+
+ // Add all the search paths (as calculated earlier - they will only be set if this is a pass through else will be empty)
+ options.includePaths = includePaths;
+
+ // Add the specified defines (as calculated earlier - they will only be set if this is a pass through else will be empty)
+ {
+ for(auto& def : preprocessorDefinitions)
+ {
+ CPPCompiler::Define define;
+ define.nameWithSig = def.Key;
+ define.value = def.Value;
+
+ options.defines.add(define);
+ }
+ }
+ }
+
+ // TODO(JS): HACK! We need to include the prelude from somewhere, but where? The generated output
+ // is sitting in some temp directory.
+ // So here, we search all the 'sourceFiles', and try their paths for plausibility, and take the first
+ {
+ auto frontEndReq = endToEndReq->getFrontEndReq();
+ auto entryPointReq = frontEndReq->getEntryPointReq(entryPointIndex);
+ auto translationUnit = entryPointReq->getTranslationUnit();
+
+ for (SourceFile* sourceFile : translationUnit->m_sourceFiles)
+ {
+ const auto& pathInfo = sourceFile->getPathInfo();
+
+ if (pathInfo.type == PathInfo::Type::FoundPath ||
+ pathInfo.type == PathInfo::Type::Normal)
+ {
+ String originalSourceDirectory = Path::getParentDirectory(pathInfo.foundPath);
+
+ if (originalSourceDirectory.getLength() && File::exists(originalSourceDirectory))
+ {
+ // We can't use this path directly, so make canonical so it is absolute
+ StringBuilder canonicalPath;
+ if (SLANG_SUCCEEDED(Path::getCanonical(originalSourceDirectory, canonicalPath)))
+ {
+ options.includePaths.add(canonicalPath.ProduceString());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile
+ CPPCompiler::Output output;
+ SLANG_RETURN_ON_FAIL(compiler->compile(options, output));
+
+ {
+ StringBuilder compilerText;
+ compiler->getDesc().appendAsText(compilerText);
+
+ StringBuilder builder;
+
+ typedef CPPCompiler::OutputMessage OutputMessage;
+
+ for (const auto& msg : output.messages)
+ {
+ builder.Clear();
+
+ builder << msg.filePath << "(" << msg.fileLine <<"): ";
+
+ if (msg.stage == OutputMessage::Stage::Link)
+ {
+ builder << "link ";
+ }
+
+ switch (msg.type)
+ {
+ case OutputMessage::Type::Error: builder << "error"; break;
+ case OutputMessage::Type::Unknown: builder << "warning"; break;
+ case OutputMessage::Type::Info: builder << "info"; break;
+ default: break;
+ }
+
+ builder << " " << msg.code << ": " << msg.text;
+
+ reportExternalCompileError(compilerText.getBuffer(), SLANG_OK, builder.getUnownedSlice(), sink);
+ }
+ }
+
+ // If any errors are emitted, then we are done
+ if (output.has(CPPCompiler::OutputMessage::Type::Error))
+ {
+ return SLANG_FAIL;
+ }
+
+ // Read the binary
+ try
+ {
+ // Read the contents of the binary
+ binOut = File::readAllBytes(moduleFilePath);
+ }
+ catch (const Slang::IOException&)
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::unableToReadFile, moduleFilePath);
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+ }
+
SlangResult emitSPIRVForEntryPoint(
BackEndCompileRequest* slangRequest,
EntryPoint* entryPoint,
@@ -1106,6 +1556,23 @@ SlangResult dissassembleDXILUsingDXC(
switch (target)
{
+ case CodeGenTarget::SharedLibrary:
+ case CodeGenTarget::Executable:
+ {
+ List<uint8_t> code;
+ if (SLANG_SUCCEEDED(emitCPUBinaryForEntryPoint(
+ compileRequest,
+ entryPoint,
+ entryPointIndex,
+ targetReq,
+ endToEndReq,
+ code)))
+ {
+ maybeDumpIntermediate(compileRequest, code.getBuffer(), code.getCount(), target);
+ result = CompileResult(code);
+ }
+ }
+ break;
case CodeGenTarget::HLSL:
{
String code = emitHLSLForEntryPoint(
@@ -1444,6 +1911,11 @@ SlangResult dissassembleDXILUsingDXC(
}
break;
+ case CodeGenTarget::SharedLibrary:
+ case CodeGenTarget::Executable:
+ HexDumpUtil::dumpWithMarkers(data, 24, writer);
+ break;
+
default:
SLANG_UNEXPECTED("unhandled output format");
return;
@@ -1753,6 +2225,22 @@ SlangResult dissassembleDXILUsingDXC(
}
break;
#endif
+
+ case CodeGenTarget::CSource:
+ dumpIntermediateText(compileRequest, data, size, ".c");
+ break;
+ case CodeGenTarget::CPPSource:
+ dumpIntermediateText(compileRequest, data, size, ".cpp");
+ break;
+
+ case CodeGenTarget::Executable:
+ // What these should be called is target specific, but just use these exts to make clear for now
+ // for now
+ dumpIntermediateBinary(compileRequest, data, size, ".exe");
+ break;
+ case CodeGenTarget::SharedLibrary:
+ dumpIntermediateBinary(compileRequest, data, size, ".shared-lib");
+ break;
}
}
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 2b2c35845..b7f62ae5b 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -4,6 +4,8 @@
#include "../core/slang-basic.h"
#include "../core/slang-shared-library.h"
+#include "../core/slang-cpp-compiler.h"
+
#include "../../slang-com-ptr.h"
#include "slang-diagnostics.h"
@@ -57,6 +59,8 @@ namespace Slang
DXILAssembly = SLANG_DXIL_ASM,
CSource = SLANG_C_SOURCE,
CPPSource = SLANG_CPP_SOURCE,
+ Executable = SLANG_EXECUTABLE,
+ SharedLibrary = SLANG_SHARED_LIBRARY,
};
CodeGenTarget calcCodeGenTargetFromName(const UnownedStringSlice& name);
@@ -417,10 +421,14 @@ namespace Slang
enum class PassThroughMode : SlangPassThrough
{
- None = SLANG_PASS_THROUGH_NONE, // don't pass through: use Slang compiler
- fxc = SLANG_PASS_THROUGH_FXC, // pass through HLSL to `D3DCompile` API
- dxc = SLANG_PASS_THROUGH_DXC, // pass through HLSL to `IDxcCompiler` API
- glslang = SLANG_PASS_THROUGH_GLSLANG, // pass through GLSL to `glslang` library
+ None = SLANG_PASS_THROUGH_NONE, ///< don't pass through: use Slang compiler
+ Fxc = SLANG_PASS_THROUGH_FXC, ///< pass through HLSL to `D3DCompile` API
+ Dxc = SLANG_PASS_THROUGH_DXC, ///< pass through HLSL to `IDxcCompiler` API
+ Glslang = SLANG_PASS_THROUGH_GLSLANG, ///< pass through GLSL to `glslang` library
+ Clang = SLANG_PASS_THROUGH_CLANG, ///< Pass through clang compiler
+ Gcc = SLANG_PASS_THROUGH_GCC, ///< Gcc compiler
+ VisualStudio = SLANG_PASS_THROUGH_VISUAL_STUDIO, ///< Visual studio compiler
+ GenericCCpp = SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C/C++ compiler
};
class SourceFile;
@@ -1493,6 +1501,8 @@ namespace Slang
RefPtr<Type> stringType;
RefPtr<Type> enumTypeType;
+ RefPtr<CPPCompilerSet> cppCompilerSet; ///< Information about available C/C++ compilers. null unless information is requested (because slow)
+
ComPtr<ISlangSharedLibraryLoader> sharedLibraryLoader; ///< The shared library loader (never null)
ComPtr<ISlangSharedLibrary> sharedLibraries[int(SharedLibraryType::CountOf)]; ///< The loaded shared libraries
SlangFuncPtr sharedLibraryFunctions[int(SharedLibraryFuncType::CountOf)];
@@ -1567,6 +1577,9 @@ namespace Slang
SlangFuncPtr getSharedLibraryFunc(SharedLibraryFuncType type, DiagnosticSink* sink);
+ /// Finds out what compilers are present and caches the result
+ CPPCompilerSet* requireCPPCompilerSet();
+
Session();
void addBuiltinSource(
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index fc9cac3f3..04b7560d2 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -478,6 +478,12 @@ DIAGNOSTIC(52000, Error, multiLevelBreakUnsupported, "control flow appears to re
DIAGNOSTIC(52001, Warning, dxilNotFound, "dxil shared library not found, so 'dxc' output cannot be signed! Shader code will not be runnable in non-development environments.");
+DIAGNOSTIC(52002, Error, passThroughCompilerNotFound, "Could not find a suitable pass-through compiler for '$0'.");
+DIAGNOSTIC(52003, Error, cppCompilerNotFound, "Could not find a suitable C/C++ compiler.");
+
+DIAGNOSTIC(52004, Error, unableToWriteFile, "Unable to write file '$0'");
+DIAGNOSTIC(52005, Error, unableToReadFile, "Unable to read file '$0'");
+
// 99999 - Internal compiler errors, and not-yet-classified diagnostics.
DIAGNOSTIC(99999, Internal, unimplemented, "unimplemented feature in Slang compiler: $0")
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index e89268106..354a2d856 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -1399,7 +1399,7 @@ void CPPSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLa
default: break;
}
- m_writer->emit("SLANG_EXPORT\n");
+ m_writer->emit("SLANG_PRELUDE_EXPORT\n");
}
void CPPSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount)
@@ -1613,7 +1613,8 @@ void CPPSourceEmitter::emitPreprocessorDirectivesImpl()
SourceWriter* writer = getSourceWriter();
writer->emit("\n");
- writer->emit("#include \"slang-cpp-prelude.h\"\n\n");
+
+ writer->emit("#include <slang-cpp-prelude.h>\n\n");
// Emit the type definitions
for (const auto& keyValue : m_typeNameMap)
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 4b3a9f8f0..81fb468eb 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -36,6 +36,26 @@ SlangResult tryReadCommandLineArgument(DiagnosticSink* sink, char const* option,
return SLANG_OK;
}
+#define SLANG_PASS_THROUGH_TYPES(x) \
+ x(none, NONE) \
+ x(fxc, FXC) \
+ x(dxc, DXC) \
+ x(glslang, GLSLANG) \
+ x(vs, VISUAL_STUDIO) \
+ x(visualstudio, VISUAL_STUDIO) \
+ x(clang, CLANG) \
+ x(gcc, GCC) \
+ x(c, GENERIC_C_CPP) \
+ x(cpp, GENERIC_C_CPP)
+
+static SlangResult _parsePassThrough(const UnownedStringSlice& name, SlangPassThrough& outPassThrough)
+{
+#define SLANG_PASS_THROUGH_TYPE_CHECK(x, y) \
+ if (name == #x) { outPassThrough = SLANG_PASS_THROUGH_##y; return SLANG_OK; }
+ SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_TYPE_CHECK)
+ return SLANG_FAIL;
+}
+
struct OptionsParser
{
SlangSession* session = nullptr;
@@ -278,6 +298,9 @@ struct OptionsParser
{ ".tesc", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_HULL },
{ ".tese", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_DOMAIN },
{ ".comp", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_COMPUTE },
+
+ { ".c", SLANG_SOURCE_LANGUAGE_C, SLANG_STAGE_NONE },
+ { ".cpp", SLANG_SOURCE_LANGUAGE_CPP, SLANG_STAGE_NONE },
};
for (int i = 0; i < SLANG_COUNT_OF(entries); ++i)
@@ -360,8 +383,12 @@ struct OptionsParser
CASE(".spv", SPIRV);
CASE(".spv.asm", SPIRV_ASM);
- CASE(".c", C_SOURCE);
- CASE(".cpp", CPP_SOURCE);
+ CASE(".c", C_SOURCE);
+ CASE(".cpp", CPP_SOURCE);
+
+ CASE(".exe", EXECUTABLE);
+ CASE(".dll", SHARED_LIBRARY);
+ CASE(".so", SHARED_LIBRARY);
#undef CASE
@@ -422,6 +449,23 @@ struct OptionsParser
rawTarget->floatingPointMode = mode;
}
+ static bool _passThroughRequiresStage(PassThroughMode passThrough)
+ {
+ switch (passThrough)
+ {
+ case PassThroughMode::Glslang:
+ case PassThroughMode::Dxc:
+ case PassThroughMode::Fxc:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+
SlangResult parse(
int argc,
char const* const* argv)
@@ -561,18 +605,13 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name));
SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
- if (name == "fxc") { passThrough = SLANG_PASS_THROUGH_FXC; }
- else if (name == "dxc") { passThrough = SLANG_PASS_THROUGH_DXC; }
- else if (name == "glslang") { passThrough = SLANG_PASS_THROUGH_GLSLANG; }
- else
+ if (SLANG_FAILED(_parsePassThrough(name.getUnownedSlice(), passThrough)))
{
sink->diagnose(SourceLoc(), Diagnostics::unknownPassThroughTarget, name);
return SLANG_FAIL;
}
- spSetPassThrough(
- compileRequest,
- passThrough);
+ spSetPassThrough(compileRequest, passThrough);
}
else if (argStr == "-dxc-path")
{
@@ -938,7 +977,7 @@ struct OptionsParser
// because fxc/dxc/glslang don't have a facility for taking
// a named entry point and pulling its stage from an attribute.
//
- if( requestImpl->passThrough != PassThroughMode::None )
+ if(_passThroughRequiresStage(requestImpl->passThrough) )
{
for( auto& rawEntryPoint : rawEntryPoints )
{
diff --git a/source/slang/slang-profile-defs.h b/source/slang/slang-profile-defs.h
index 238621084..50e1750ac 100644
--- a/source/slang/slang-profile-defs.h
+++ b/source/slang/slang-profile-defs.h
@@ -3,7 +3,7 @@
// Define all the various language "profiles" we want to support.
#ifndef LANGUAGE
-#define LANGUAGE(TAG, NAME) /* emptry */
+#define LANGUAGE(TAG, NAME) /* empty */
#endif
#ifndef LANGUAGE_ALIAS
@@ -47,6 +47,8 @@ LANGUAGE(GLSL_ES, glsl_es)
LANGUAGE(GLSL_VK, glsl_vk)
LANGUAGE(SPIRV, spirv)
LANGUAGE(SPIRV_GL, spirv_gl)
+LANGUAGE(C, c)
+LANGUAGE(CPP, cpp)
LANGUAGE_ALIAS(GLSL, glsl_gl)
LANGUAGE_ALIAS(SPIRV, spirv_vk)
diff --git a/source/slang/slang-profile.h b/source/slang/slang-profile.h
index de471282d..f26b48866 100644
--- a/source/slang/slang-profile.h
+++ b/source/slang/slang-profile.h
@@ -13,6 +13,8 @@ namespace Slang
Slang = SLANG_SOURCE_LANGUAGE_SLANG,
HLSL = SLANG_SOURCE_LANGUAGE_HLSL,
GLSL = SLANG_SOURCE_LANGUAGE_GLSL,
+ C = SLANG_SOURCE_LANGUAGE_C,
+ CPP = SLANG_SOURCE_LANGUAGE_CPP,
};
// TODO(tfoley): This should merge with the above...
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index c3dbb4966..9b6571372 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -815,6 +815,8 @@ LayoutRulesFamilyImpl* getDefaultLayoutRulesFamilyForTarget(TargetRequest* targe
return &kGLSLLayoutRulesFamilyImpl;
+ case CodeGenTarget::Executable:
+ case CodeGenTarget::SharedLibrary:
case CodeGenTarget::CPPSource:
case CodeGenTarget::CSource:
{
diff --git a/tests/cpp-compiler/simple-c-compile.c b/tests/cpp-compiler/simple-c-compile.c
new file mode 100644
index 000000000..32b5561f1
--- /dev/null
+++ b/tests/cpp-compiler/simple-c-compile.c
@@ -0,0 +1,10 @@
+//TEST:SIMPLE: -entry main -target exe -pass-through c
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char** argv)
+{
+ printf("Hello World!\n");
+ return 0;
+}
diff --git a/tests/cpp-compiler/simple-c-compile.c.expected b/tests/cpp-compiler/simple-c-compile.c.expected
new file mode 100644
index 000000000..0e042b53b
--- /dev/null
+++ b/tests/cpp-compiler/simple-c-compile.c.expected
@@ -0,0 +1,6 @@
+result code = 0
+standard error = {
+}
+standard output = {
+Hello World!
+}
diff --git a/tests/cross-compile/cpp-execute-simple.slang b/tests/cross-compile/cpp-execute-simple.slang
new file mode 100644
index 000000000..74a3ec634
--- /dev/null
+++ b/tests/cross-compile/cpp-execute-simple.slang
@@ -0,0 +1,12 @@
+//TEST:CPU_EXECUTE: -profile cs_5_0 -entry computeMain -target sharedlib
+
+[numthreads(4, 1, 1)]
+void computeMain(
+ uint3 dispatchThreadID : SV_DispatchThreadID,
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
+ RWStructuredBuffer<int> outputBuffer)
+{
+ uint tid = dispatchThreadID.x;
+
+ outputBuffer[tid] = int(tid * tid);
+} \ No newline at end of file
diff --git a/tests/cross-compile/cpp-execute-simple.slang.expected b/tests/cross-compile/cpp-execute-simple.slang.expected
new file mode 100644
index 000000000..b84777e95
--- /dev/null
+++ b/tests/cross-compile/cpp-execute-simple.slang.expected
@@ -0,0 +1 @@
+0, 1, 4, 9
diff --git a/tests/cross-compile/cpp-execute.slang b/tests/cross-compile/cpp-execute.slang
new file mode 100644
index 000000000..1c90c8dd2
--- /dev/null
+++ b/tests/cross-compile/cpp-execute.slang
@@ -0,0 +1,106 @@
+//TEST:CPU_EXECUTE: -profile cs_5_0 -entry computeMain -target sharedlib
+
+enum Color
+{
+ Red,
+ Green = 2,
+ Blue,
+}
+
+int test(int val)
+{
+ Color c = Color.Red;
+
+ if(val > 1)
+ {
+ c = Color.Green;
+ }
+
+ if(c == Color.Red)
+ {
+ if(val & 1)
+ {
+ c = Color.Blue;
+ }
+ }
+
+ switch(c)
+ {
+ case Color.Red:
+ val = 1;
+ break;
+
+ case Color.Green:
+ val = 2;
+ break;
+
+ case Color.Blue:
+ val = 3;
+ break;
+
+ default:
+ val = -1;
+ break;
+ }
+
+ return (val << 4) + int(c);
+}
+
+float sum(float a[3])
+{
+ float total = a[0];
+ for (int i = 1; i < 3; ++i)
+ {
+ total += a[i];
+ }
+ return total;
+}
+
+struct Thing
+{
+ int a;
+ float b;
+};
+
+[numthreads(4, 1, 1)]
+void computeMain(
+ uint3 dispatchThreadID : SV_DispatchThreadID,
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
+ RWStructuredBuffer<int> outputBuffer)
+{
+ uint tid = dispatchThreadID.x;
+
+ Thing thing = { 10, -1.0 };
+
+ float array[3] = { thing.a, 2, 3};
+
+ float anotherArray[] = { 1, 2, 5 };
+
+ array[0] += anotherArray[1];
+
+ matrix<float, 2, 3> mat = { { sum(array), 1, 2 }, { 3, 4, 5} };
+ vector<float, 2> vec = { float(tid + 1), float(tid + 2) };
+
+ vector<float, 3> vec2 = max(sin(mul(vec, mat)), float3(1, 2, -1));
+ vector<float, 3> vec3 = mul(vec, mat);
+
+ float3 vec4 = lerp(vec2, vec3, float3(tid * (1.0f / 4), 1, 1));
+
+ float3 crossVec = normalize(cross(vec4, vec4));
+
+ vec2.x = fmod(crossVec.y, crossVec.x);
+
+ vec2 = fmod(vec2, crossVec);
+
+ vec2 += (-vec2.zyx) * 2 + crossVec * length(crossVec) + reflect(vec4, normalize(crossVec));
+
+ vector<bool, 3> z = vec2 > 0;
+
+ int val = (int(tid) + (any(z) ? 1 : 0) + (all(z) ? 2 : 0)) % 100;
+
+ val = asint(asfloat(asuint(asfloat(val))));
+
+ val = test(val);
+
+ outputBuffer[tid] = val + int(dot(vec2, vec4));
+} \ No newline at end of file
diff --git a/tests/cross-compile/cpp-execute.slang.expected b/tests/cross-compile/cpp-execute.slang.expected
new file mode 100644
index 000000000..65e3ed534
--- /dev/null
+++ b/tests/cross-compile/cpp-execute.slang.expected
@@ -0,0 +1 @@
+-2147483632, -2147483597, -2147483614, -2147483614
diff --git a/tests/cross-compile/slang-cpp-prelude.h b/tests/cross-compile/slang-cpp-prelude.h
index f60fc8518..f2635eb2c 100644
--- a/tests/cross-compile/slang-cpp-prelude.h
+++ b/tests/cross-compile/slang-cpp-prelude.h
@@ -1,3 +1,5 @@
+#ifndef SLANG_CPP_PRELUDE_H
+#define SLANG_CPP_PRELUDE_H
#include <inttypes.h>
#include <math.h>
@@ -6,24 +8,32 @@
#include <assert.h>
#include <stdlib.h>
-#ifndef M_PI
-# define M_PI 3.14159265358979323846
+#ifndef SLANG_FORCE_INLINE
+# define SLANG_FORCE_INLINE inline
+#endif
+
+#ifndef SLANG_PRELUDE_PI
+# define SLANG_PRELUDE_PI 3.14159265358979323846
#endif
#if defined(_MSC_VER)
-# define SLANG_SHARED_LIB_EXPORT __declspec(dllexport)
+# define SLANG_PRELUDE_SHARED_LIB_EXPORT __declspec(dllexport)
#else
-# define SLANG_SHARED_LIB_EXPORT __attribute__((__visibility__("default")))
-//# define SLANG_SHARED_LIB_EXPORT __attribute__ ((dllexport)) __attribute__((__visibility__("default")))
+# define SLANG_PRELUDE_SHARED_LIB_EXPORT __attribute__((__visibility__("default")))
+//# define SLANG_PRELUDE_SHARED_LIB_EXPORT __attribute__ ((dllexport)) __attribute__((__visibility__("default")))
#endif
#ifdef __cplusplus
-# define SLANG_EXTERN_C extern "C"
+# define SLANG_PRELUDE_EXTERN_C extern "C"
#else
-# define SLANG_EXTERN_C
+# define SLANG_PRELUDE_EXTERN_C
#endif
-#define SLANG_EXPORT SLANG_EXTERN_C SLANG_SHARED_LIB_EXPORT
+#define SLANG_PRELUDE_EXPORT SLANG_PRELUDE_EXTERN_C SLANG_PRELUDE_SHARED_LIB_EXPORT
+
+#ifdef SLANG_PRELUDE_NAMESPACE
+namespace SLANG_PRELUDE_NAMESPACE {
+#endif
template <typename T, size_t SIZE>
struct FixedArray
@@ -43,6 +53,39 @@ struct RWStructuredBuffer
size_t count;
};
+template <typename T, int COUNT>
+struct Vector;
+
+template <typename T>
+struct Vector<T, 1>
+{
+ T x;
+};
+
+template <typename T>
+struct Vector<T, 2>
+{
+ T x, y;
+};
+
+template <typename T>
+struct Vector<T, 3>
+{
+ T x, y, z;
+};
+
+template <typename T>
+struct Vector<T, 4>
+{
+ T x, y, z, w;
+};
+
+template <typename T, int ROWS, int COLS>
+struct Matrix
+{
+ Vector<T, COLS> rows[ROWS];
+};
+
// ----------------------------- F32 -----------------------------------------
union Union32
@@ -53,70 +96,125 @@ union Union32
};
// Helpers
-float F32_calcSafeRadians(float radians)
+SLANG_FORCE_INLINE float F32_calcSafeRadians(float radians)
{
- float a = radians * (1.0f / M_PI);
+ float a = radians * (1.0f / float(SLANG_PRELUDE_PI));
a = (a < 0.0f) ? (::ceilf(a) - a) : (a - ::floorf(a));
- return (a * M_PI);
+ return (a * float(SLANG_PRELUDE_PI));
}
// Unary
-float F32_ceil(float f) { return ::ceilf(f); }
-float F32_floor(float f) { return ::floorf(f); }
-float F32_sin(float f) { return ::sinf(F32_calcSafeRadians(f)); }
-float F32_cos(float f) { return ::cosf(F32_calcSafeRadians(f)); }
-float F32_tan(float f) { return ::tanf(f); }
-float F32_asin(float f) { return ::asinf(f); }
-float F32_acos(float f) { return ::acosf(f); }
-float F32_atan(float f) { return ::atanf(f); }
-float F32_log2(float f) { return ::log2f(f); }
-float F32_exp2(float f) { return ::exp2f(f); }
-float F32_exp(float f) { return ::expf(f); }
-float F32_abs(float f) { return ::fabsf(f); }
-float F32_trunc(float f) { return ::truncf(f); }
-float F32_sqrt(float f) { return ::sqrtf(f); }
-float F32_rsqrt(float f) { return 1.0f / F32_sqrt(f); }
-float F32_rcp(float f) { return 1.0f / f; }
-float F32_sign(float f) { return ( f == 0.0f) ? f : (( f < 0.0f) ? -1.0f : 1.0f); }
-float F32_saturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
-float F32_frac(float f) { return f - F32_floor(f); }
-float F32_radians(float f) { return f * 0.01745329222f; }
+SLANG_FORCE_INLINE float F32_ceil(float f) { return ::ceilf(f); }
+SLANG_FORCE_INLINE float F32_floor(float f) { return ::floorf(f); }
+SLANG_FORCE_INLINE float F32_sin(float f) { return ::sinf(F32_calcSafeRadians(f)); }
+SLANG_FORCE_INLINE float F32_cos(float f) { return ::cosf(F32_calcSafeRadians(f)); }
+SLANG_FORCE_INLINE float F32_tan(float f) { return ::tanf(f); }
+SLANG_FORCE_INLINE float F32_asin(float f) { return ::asinf(f); }
+SLANG_FORCE_INLINE float F32_acos(float f) { return ::acosf(f); }
+SLANG_FORCE_INLINE float F32_atan(float f) { return ::atanf(f); }
+SLANG_FORCE_INLINE float F32_log2(float f) { return ::log2f(f); }
+SLANG_FORCE_INLINE float F32_exp2(float f) { return ::exp2f(f); }
+SLANG_FORCE_INLINE float F32_exp(float f) { return ::expf(f); }
+SLANG_FORCE_INLINE float F32_abs(float f) { return ::fabsf(f); }
+SLANG_FORCE_INLINE float F32_trunc(float f) { return ::truncf(f); }
+SLANG_FORCE_INLINE float F32_sqrt(float f) { return ::sqrtf(f); }
+SLANG_FORCE_INLINE float F32_rsqrt(float f) { return 1.0f / F32_sqrt(f); }
+SLANG_FORCE_INLINE float F32_rcp(float f) { return 1.0f / f; }
+SLANG_FORCE_INLINE float F32_sign(float f) { return ( f == 0.0f) ? f : (( f < 0.0f) ? -1.0f : 1.0f); }
+SLANG_FORCE_INLINE float F32_saturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
+SLANG_FORCE_INLINE float F32_frac(float f) { return f - F32_floor(f); }
+SLANG_FORCE_INLINE float F32_radians(float f) { return f * 0.01745329222f; }
// Binary
-float F32_min(float a, float b) { return a < b ? a : b; }
-float F32_max(float a, float b) { return a > b ? a : b; }
-float F32_pow(float a, float b) { return ::powf(a, b); }
-float F32_fmod(float a, float b) { return ::fmodf(a, b); }
-float F32_step(float a, float b) { return float(a >= b); }
-float F32_atan2(float a, float b) { return float(atan2(a, b)); }
+SLANG_FORCE_INLINE float F32_min(float a, float b) { return a < b ? a : b; }
+SLANG_FORCE_INLINE float F32_max(float a, float b) { return a > b ? a : b; }
+SLANG_FORCE_INLINE float F32_pow(float a, float b) { return ::powf(a, b); }
+SLANG_FORCE_INLINE float F32_fmod(float a, float b) { return ::fmodf(a, b); }
+SLANG_FORCE_INLINE float F32_step(float a, float b) { return float(a >= b); }
+SLANG_FORCE_INLINE float F32_atan2(float a, float b) { return float(atan2(a, b)); }
// Ternary
-float F32_smoothstep(float min, float max, float x) { return x < min ? min : ((x > max) ? max : x / (max - min)); }
-float F32_lerp(float x, float y, float s) { return x + s * (y - x); }
-float F32_clamp(float x, float min, float max) { return ( x < min) ? min : ((x > max) ? max : x); }
-void F32_sincos(float f, float& outSin, float& outCos) { outSin = F32_sin(f); outCos = F32_cos(f); }
+SLANG_FORCE_INLINE float F32_smoothstep(float min, float max, float x) { return x < min ? min : ((x > max) ? max : x / (max - min)); }
+SLANG_FORCE_INLINE float F32_lerp(float x, float y, float s) { return x + s * (y - x); }
+SLANG_FORCE_INLINE float F32_clamp(float x, float min, float max) { return ( x < min) ? min : ((x > max) ? max : x); }
+SLANG_FORCE_INLINE void F32_sincos(float f, float& outSin, float& outCos) { outSin = F32_sin(f); outCos = F32_cos(f); }
+
+SLANG_FORCE_INLINE uint32_t F32_asuint(float f) { Union32 u; u.f = f; return u.u; }
+SLANG_FORCE_INLINE int32_t F32_asint(float f) { Union32 u; u.f = f; return u.i; }
-uint32_t F32_asuint(float f) { Union32 u; u.f = f; return u.u; }
-int32_t F32_asint(float f) { Union32 u; u.f = f; return u.i; }
+// ----------------------------- F64 -----------------------------------------
+
+SLANG_FORCE_INLINE double F64_calcSafeRadians(double radians)
+{
+ double a = radians * (1.0 / SLANG_PRELUDE_PI);
+ a = (a < 0.0) ? (::ceil(a) - a) : (a - ::floor(a));
+ return (a * SLANG_PRELUDE_PI);
+}
+
+// Unary
+SLANG_FORCE_INLINE double F64_ceil(double f) { return ::ceil(f); }
+SLANG_FORCE_INLINE double F64_floor(double f) { return ::floor(f); }
+SLANG_FORCE_INLINE double F64_sin(double f) { return ::sin(F64_calcSafeRadians(f)); }
+SLANG_FORCE_INLINE double F64_cos(double f) { return ::cos(F64_calcSafeRadians(f)); }
+SLANG_FORCE_INLINE double F64_tan(double f) { return ::tan(f); }
+SLANG_FORCE_INLINE double F64_asin(double f) { return ::asin(f); }
+SLANG_FORCE_INLINE double F64_acos(double f) { return ::acos(f); }
+SLANG_FORCE_INLINE double F64_atan(double f) { return ::atan(f); }
+SLANG_FORCE_INLINE double F64_log2(double f) { return ::log2(f); }
+SLANG_FORCE_INLINE double F64_exp2(double f) { return ::exp2(f); }
+SLANG_FORCE_INLINE double F64_exp(double f) { return ::exp(f); }
+SLANG_FORCE_INLINE double F64_abs(double f) { return ::fabs(f); }
+SLANG_FORCE_INLINE double F64_trunc(double f) { return ::trunc(f); }
+SLANG_FORCE_INLINE double F64_sqrt(double f) { return ::sqrt(f); }
+SLANG_FORCE_INLINE double F64_rsqrt(double f) { return 1.0 / F64_sqrt(f); }
+SLANG_FORCE_INLINE double F64_rcp(double f) { return 1.0 / f; }
+SLANG_FORCE_INLINE double F64_sign(double f) { return (f == 0.0) ? f : ((f < 0.0) ? -1.0 : 1.0); }
+SLANG_FORCE_INLINE double F64_saturate(double f) { return (f < 0.0) ? 0.0 : (f > 1.0) ? 1.0 : f; }
+SLANG_FORCE_INLINE double F64_frac(double f) { return f - F64_floor(f); }
+SLANG_FORCE_INLINE double F64_radians(double f) { return f * 0.01745329222; }
+
+// Binary
+SLANG_FORCE_INLINE double F64_min(double a, double b) { return a < b ? a : b; }
+SLANG_FORCE_INLINE double F64_max(double a, double b) { return a > b ? a : b; }
+SLANG_FORCE_INLINE double F64_pow(double a, double b) { return ::pow(a, b); }
+SLANG_FORCE_INLINE double F64_fmod(double a, double b) { return ::fmod(a, b); }
+SLANG_FORCE_INLINE double F64_step(double a, double b) { return double(a >= b); }
+SLANG_FORCE_INLINE double F64_atan2(double a, double b) { return atan2(a, b); }
+
+// Ternary
+SLANG_FORCE_INLINE double F64_smoothstep(double min, double max, double x) { return x < min ? min : ((x > max) ? max : x / (max - min)); }
+SLANG_FORCE_INLINE double F64_lerp(double x, double y, double s) { return x + s * (y - x); }
+SLANG_FORCE_INLINE double F64_clamp(double x, double min, double max) { return (x < min) ? min : ((x > max) ? max : x); }
+SLANG_FORCE_INLINE void F64_sincos(double f, double& outSin, double& outCos) { outSin = F64_sin(f); outCos = F64_cos(f); }
+
+// TODO!
+//uint32_t F64_asuint(float f);
+//int32_t F64_asint(float f);
// ----------------------------- I32 -----------------------------------------
-int32_t I32_abs(int32_t f) { return (f < 0) ? -f : f; }
+SLANG_FORCE_INLINE int32_t I32_abs(int32_t f) { return (f < 0) ? -f : f; }
-int32_t I32_min(int32_t a, int32_t b) { return a < b ? a : b; }
-int32_t I32_max(int32_t a, int32_t b) { return a > b ? a : b; }
+SLANG_FORCE_INLINE int32_t I32_min(int32_t a, int32_t b) { return a < b ? a : b; }
+SLANG_FORCE_INLINE int32_t I32_max(int32_t a, int32_t b) { return a > b ? a : b; }
-int32_t I32_clamp(int32_t x, int32_t min, int32_t max) { return ( x < min) ? min : ((x > max) ? max : x); }
+SLANG_FORCE_INLINE int32_t I32_clamp(int32_t x, int32_t min, int32_t max) { return ( x < min) ? min : ((x > max) ? max : x); }
-float I32_asfloat(int32_t x) { Union32 u; u.i = x; return u.f; }
+SLANG_FORCE_INLINE float I32_asfloat(int32_t x) { Union32 u; u.i = x; return u.f; }
// ----------------------------- U32 -----------------------------------------
-uint32_t U32_abs(uint32_t f) { return f; }
+SLANG_FORCE_INLINE uint32_t U32_abs(uint32_t f) { return f; }
-uint32_t U32_min(uint32_t a, uint32_t b) { return a < b ? a : b; }
-uint32_t U32_max(uint32_t a, uint32_t b) { return a > b ? a : b; }
+SLANG_FORCE_INLINE uint32_t U32_min(uint32_t a, uint32_t b) { return a < b ? a : b; }
+SLANG_FORCE_INLINE uint32_t U32_max(uint32_t a, uint32_t b) { return a > b ? a : b; }
-uint32_t U32_clamp(uint32_t x, uint32_t min, uint32_t max) { return ( x < min) ? min : ((x > max) ? max : x); }
+SLANG_FORCE_INLINE uint32_t U32_clamp(uint32_t x, uint32_t min, uint32_t max) { return ( x < min) ? min : ((x > max) ? max : x); }
-float U32_asfloat(uint32_t x) { Union32 u; u.u = x; return u.f; }
+SLANG_FORCE_INLINE float U32_asfloat(uint32_t x) { Union32 u; u.u = x; return u.f; }
+
+#ifdef SLANG_PRELUDE_NAMESPACE
+}
+#endif
+
+#endif
diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp
index b6c29b423..5e7a3d6f3 100644
--- a/tools/slang-test/slang-test-main.cpp
+++ b/tools/slang-test/slang-test-main.cpp
@@ -3,6 +3,7 @@
#include "../../source/core/slang-io.h"
#include "../../source/core/slang-token-reader.h"
#include "../../source/core/slang-std-writers.h"
+#include "../../source/core/slang-hex-dump-util.h"
#include "../../slang-com-helper.h"
@@ -30,6 +31,8 @@ using namespace Slang;
#include <stdlib.h>
#include <stdarg.h>
+#define SLANG_PRELUDE_NAMESPACE CPPPrelude
+#include "../../tests/cross-compile/slang-cpp-prelude.h"
// Options for a particular test
struct TestOptions
@@ -490,24 +493,41 @@ static bool _hasOption(const List<String>& args, const String& argName)
return args.indexOf(argName) != Index(-1);
}
-static BackendType _toBackendType(const UnownedStringSlice& slice)
+static SlangPassThrough _toPassThroughType(const UnownedStringSlice& slice)
{
if (slice == "dxc")
{
- return BackendType::Dxc;
+ return SLANG_PASS_THROUGH_DXC;
}
else if (slice == "fxc")
{
- return BackendType::Fxc;
+ return SLANG_PASS_THROUGH_FXC;
}
else if (slice == "glslang")
{
- return BackendType::Glslang;
+ return SLANG_PASS_THROUGH_GLSLANG;
}
- return BackendType::Unknown;
+ else if (slice == "c" || slice == "cpp")
+ {
+ return SLANG_PASS_THROUGH_GENERIC_C_CPP;
+ }
+ else if (slice == "clang")
+ {
+ return SLANG_PASS_THROUGH_CLANG;
+ }
+ else if (slice == "gcc")
+ {
+ return SLANG_PASS_THROUGH_GCC;
+ }
+ else if (slice == "vs" || slice == "visualstudio")
+ {
+ return SLANG_PASS_THROUGH_VISUAL_STUDIO;
+ }
+
+ return SLANG_PASS_THROUGH_NONE;
}
-static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
+static PassThroughFlags _getPassThroughFlagsForTarget(SlangCompileTarget target)
{
switch (target)
{
@@ -523,18 +543,25 @@ static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
case SLANG_DXBC:
case SLANG_DXBC_ASM:
{
- return BackendFlag::Fxc;
+ return PassThroughFlag::Fxc;
}
case SLANG_SPIRV:
case SLANG_SPIRV_ASM:
{
- return BackendFlag::Glslang;
+ return PassThroughFlag::Glslang;
}
case SLANG_DXIL:
case SLANG_DXIL_ASM:
{
- return BackendFlag::Dxc;
+ return PassThroughFlag::Dxc;
+ }
+
+ case SLANG_EXECUTABLE:
+ case SLANG_SHARED_LIBRARY:
+ {
+ return PassThroughFlag::Generic_C_CPP;
}
+
default:
{
SLANG_ASSERT(!"Unknown type");
@@ -543,17 +570,6 @@ static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
}
}
-static BackendType _toBackendTypeFromPassThroughType(SlangPassThrough passThru)
-{
- switch (passThru)
- {
- case SLANG_PASS_THROUGH_DXC: return BackendType::Dxc;
- case SLANG_PASS_THROUGH_FXC: return BackendType::Fxc;
- case SLANG_PASS_THROUGH_GLSLANG: return BackendType::Glslang;
- default: return BackendType::Unknown;
- }
-}
-
static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name)
{
#define CASE(NAME, TARGET) if(name == NAME) return SLANG_##TARGET;
@@ -571,6 +587,9 @@ static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name)
CASE("dxil-asm", DXIL_ASM)
CASE("c", C_SOURCE)
CASE("cpp", CPP_SOURCE)
+ CASE("exe", EXECUTABLE)
+ CASE("sharedlib", SHARED_LIBRARY)
+ CASE("dll", SHARED_LIBRARY)
#undef CASE
return SLANG_TARGET_UNKNOWN;
@@ -680,11 +699,11 @@ static SlangResult _extractRenderTestRequirements(const CommandLine& cmdLine, Te
if (passThru == SLANG_PASS_THROUGH_NONE)
{
// Work out backends needed based on the target
- ioRequirements->addUsedBackends(_getBackendFlagsForTarget(target));
+ ioRequirements->addUsedBackends(_getPassThroughFlagsForTarget(target));
}
else
{
- ioRequirements->addUsed(_toBackendTypeFromPassThroughType(passThru));
+ ioRequirements->addUsed(passThru);
}
// Add the render api used
@@ -701,7 +720,7 @@ static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, Te
String passThrough;
if (SLANG_SUCCEEDED(_extractArg(cmdLine, "-pass-through", passThrough)))
{
- ioRequirements->addUsed(_toBackendType(passThrough.getUnownedSlice()));
+ ioRequirements->addUsed(_toPassThroughType(passThrough.getUnownedSlice()));
}
}
@@ -711,7 +730,7 @@ static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, Te
if (SLANG_SUCCEEDED(_extractArg(cmdLine, "-target", targetName)))
{
const SlangCompileTarget target = _getCompileTarget(targetName.getUnownedSlice());
- ioRequirements->addUsedBackends(_getBackendFlagsForTarget(target));
+ ioRequirements->addUsedBackends(_getPassThroughFlagsForTarget(target));
}
}
return SLANG_OK;
@@ -921,17 +940,48 @@ TestResult asTestResult(ToolReturnCode code)
} \
}
+static SlangResult _executeBinary(const UnownedStringSlice& hexDump, ExecuteResult& outExeRes)
+{
+ // We need to extract the binary
+ List<uint8_t> data;
+ SLANG_RETURN_ON_FAIL(HexDumpUtil::parseWithMarkers(hexDump, data));
+
+ // Need to write this off to a temporary file
+ String fileName;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice("slang-test"), fileName));
+
+ fileName.append(ProcessUtil::getExecutableSuffix());
+
+ TemporaryFileSet temporaryFileSet;
+ temporaryFileSet.add(fileName);
+
+ {
+ ComPtr<ISlangWriter> writer;
+ SLANG_RETURN_ON_FAIL(FileWriter::createBinary(fileName.getBuffer(), 0, writer));
+
+ SLANG_RETURN_ON_FAIL(writer->write((const char*)data.getBuffer(), data.getCount()));
+ }
+
+ // Make executable... (for linux/unix like targets)
+ SLANG_RETURN_ON_FAIL(File::makeExecutable(fileName));
+
+ // Execute it
+ CommandLine cmdLine;
+ cmdLine.m_executable = fileName;
+ cmdLine.m_executableType = CommandLine::ExecutableType::Path;
+ return ProcessUtil::execute(cmdLine, outExeRes);
+}
+
+
TestResult runSimpleTest(TestContext* context, TestInput& input)
{
// need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect
-
- auto filePath999 = input.filePath;
auto outputStem = input.outputStem;
CommandLine cmdLine;
_initSlangCompiler(context, cmdLine);
- cmdLine.addArg(filePath999);
+ cmdLine.addArg(input.filePath);
for( auto arg : input.testOptions->args )
{
@@ -946,6 +996,28 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
return TestResult::Pass;
}
+ // See what kind of target it is
+ SlangCompileTarget target = SLANG_TARGET_UNKNOWN;
+ {
+ const auto& args = input.testOptions->args;
+ const Index targetIndex = args.indexOf("-target");
+ if (targetIndex != Index(-1) && targetIndex + 1 < args.getCount())
+ {
+ target = _getCompileTarget(args[targetIndex + 1].getUnownedSlice());
+ }
+ }
+
+ // If it's executable we run it and use it's output
+ if (target == SLANG_EXECUTABLE)
+ {
+ ExecuteResult runExeRes;
+ if (SLANG_FAILED(_executeBinary(exeRes.standardOutput.getUnownedSlice(), runExeRes)))
+ {
+ return TestResult::Fail;
+ }
+ exeRes = runExeRes;
+ }
+
String actualOutput = getOutput(exeRes);
String expectedOutputPath = outputStem + ".expected";
@@ -988,6 +1060,136 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
return result;
}
+static SlangResult _loadAsSharedLibrary(const UnownedStringSlice& hexDump, TemporaryFileSet& inOutTemporaryFileSet, SharedLibrary::Handle& outSharedLibrary)
+{
+ // We need to extract the binary
+ List<uint8_t> data;
+ SLANG_RETURN_ON_FAIL(HexDumpUtil::parseWithMarkers(hexDump, data));
+
+ // Need to write this off to a temporary file
+ String fileName;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice("slang-test"), fileName));
+
+ // Need to work out the dll name
+ String sharedLibraryName = SharedLibrary::calcPlatformPath(fileName.getUnownedSlice());
+ inOutTemporaryFileSet.add(sharedLibraryName);
+
+ {
+ ComPtr<ISlangWriter> writer;
+ SLANG_RETURN_ON_FAIL(FileWriter::createBinary(sharedLibraryName.getBuffer(), 0, writer));
+ SLANG_RETURN_ON_FAIL(writer->write((const char*)data.getBuffer(), data.getCount()));
+ }
+
+ // Make executable... (for linux/unix like targets)
+ //SLANG_RETURN_ON_FAIL(File::makeExecutable(fileName));
+
+ return SharedLibrary::loadWithPlatformPath(sharedLibraryName.getBuffer(), outSharedLibrary);
+}
+
+static void _writeBuffer(const CPPPrelude::RWStructuredBuffer<int32_t>& in, StringBuilder& out)
+{
+ for (size_t i = 0; i < in.count; ++i)
+ {
+ if (i > 0)
+ {
+ out << ", ";
+ }
+ out << in[i];
+ }
+ out << "\n";
+}
+
+TestResult runCPUExecuteTest(TestContext* context, TestInput& input)
+{
+ auto outputStem = input.outputStem;
+
+ CommandLine cmdLine;
+ _initSlangCompiler(context, cmdLine);
+
+ cmdLine.addArg(input.filePath);
+
+ for (auto arg : input.testOptions->args)
+ {
+ cmdLine.addArg(arg);
+ }
+
+ ExecuteResult exeRes;
+ TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes));
+
+ if (context->isCollectingRequirements())
+ {
+ return TestResult::Pass;
+ }
+
+ TemporaryFileSet temporaryFileSet;
+ SharedLibrary::Handle sharedLibrary = SharedLibrary::Handle(0);
+ if (SLANG_FAILED(_loadAsSharedLibrary(exeRes.standardOutput.getUnownedSlice(), temporaryFileSet, sharedLibrary)))
+ {
+ return TestResult::Fail;
+ }
+
+ StringBuilder actualOutput;
+
+ // TODO(JS): For moment just assume function name/data/paramters
+ {
+ SharedLibrary::FuncPtr func = SharedLibrary::findFuncByName(sharedLibrary, "computeMain");
+ if (!func)
+ {
+ SharedLibrary::unload(sharedLibrary);
+ return TestResult::Fail;
+ }
+
+ typedef void (*Func)(CPPPrelude::Vector<uint32_t,3> threadID, CPPPrelude::RWStructuredBuffer<int32_t> buffer);
+
+ Func runFunc = Func(func);
+ int32_t data[4] = { 0, 0, 0, 0};
+ CPPPrelude::RWStructuredBuffer<int32_t> buffer{data, 4};
+
+ for (Int i = 0; i < 4; ++i)
+ {
+ CPPPrelude::Vector<uint32_t, 3> threadID{ uint32_t(i), 0, 0};
+ runFunc(threadID, buffer);
+ }
+
+ SharedLibrary::unload(sharedLibrary);
+
+ // Write the data
+ _writeBuffer(buffer, actualOutput);
+ }
+
+ String expectedOutputPath = outputStem + ".expected";
+ String expectedOutput;
+ try
+ {
+ expectedOutput = Slang::File::readAllText(expectedOutputPath);
+ }
+ catch (Slang::IOException)
+ {
+ }
+
+ TestResult result = TestResult::Pass;
+
+ // Otherwise we compare to the expected output
+ if (actualOutput != expectedOutput)
+ {
+ context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
+ result = TestResult::Fail;
+ }
+
+ // If the test failed, then we write the actual output to a file
+ // so that we can easily diff it from the command line and
+ // diagnose the problem.
+ if (result == TestResult::Fail)
+ {
+ String actualOutputPath = outputStem + ".actual";
+ Slang::File::writeAllText(actualOutputPath, actualOutput);
+
+ context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
+ }
+
+ return result;
+}
+
TestResult runSimpleCompareCommandLineTest(TestContext* context, TestInput& input)
{
TestInput workInput(input);
@@ -1158,6 +1360,8 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input)
CPPCompiler::CompileOptions options;
options.sourceType = (targetExt == "c") ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP;
+ options.includePaths.add("tests/cross-compile");
+
// Create a filename to write this out to
String cppSource = modulePath + "." + targetExt;
Slang::File::writeAllText(cppSource, actualOutput);
@@ -2217,7 +2421,8 @@ static const TestCommandInfo s_testCommandInfos[] =
{ "CROSS_COMPILE", &runCrossCompilerTest},
{ "CPP_COMPILER_EXECUTE", &runCPPCompilerExecute},
{ "CPP_COMPILER_SHARED_LIBRARY", &runCPPCompilerSharedLibrary},
- { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile}
+ { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile},
+ { "CPU_EXECUTE", &runCPUExecuteTest},
};
TestResult runTest(
@@ -2624,44 +2829,42 @@ SlangResult innerMain(int argc, char** argv)
auto unixCatagory = categorySet.add("unix", fullTestCategory);
#endif
+ // An un-categorized test will always belong to the `full` category
+ categorySet.defaultCategory = fullTestCategory;
+
+
TestCategory* fxcCategory = nullptr;
TestCategory* dxcCategory = nullptr;
TestCategory* glslangCategory = nullptr;
- // Might be better if we had an API on slang so we could get what 'pass-through's are available
- // This works whilst these targets imply the pass-through/backends
+ // Work out what backends/pass-thrus are available
{
SlangSession* session = context.getSession();
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_FXC)))
+
+ for (int i = 0; i < SLANG_PASS_THROUGH_COUNT_OF; ++i)
+ {
+ SlangPassThrough passThru = SlangPassThrough(i);
+
+ if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, passThru)))
+ {
+ context.availableBackendFlags |= PassThroughFlags(1) << int(i);
+ }
+ }
+
+ if (context.availableBackendFlags & PassThroughFlag::Fxc)
{
fxcCategory = categorySet.add("fxc", fullTestCategory);
}
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_GLSLANG)))
+ if (context.availableBackendFlags & PassThroughFlag::Glslang)
{
glslangCategory = categorySet.add("glslang", fullTestCategory);
}
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_DXC)))
+ if (context.availableBackendFlags & PassThroughFlag::Dxc)
{
dxcCategory = categorySet.add("dxc", fullTestCategory);
}
}
- // An un-categorized test will always belong to the `full` category
- categorySet.defaultCategory = fullTestCategory;
-
- // Work out what backends are available
- {
- SlangSession* session = context.getSession();
- const SlangPassThrough passThrus[] = { SLANG_PASS_THROUGH_DXC, SLANG_PASS_THROUGH_FXC, SLANG_PASS_THROUGH_GLSLANG };
- for (auto passThru: passThrus)
- {
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, passThru)))
- {
- context.availableBackendFlags |= BackendFlags(1) << int(_toBackendTypeFromPassThroughType(passThru));
- }
- }
- }
-
// Working out what renderApis is worked on on demand through
// _getAvailableRenderApiFlags()
diff --git a/tools/slang-test/test-context.h b/tools/slang-test/test-context.h
index 895cb9c06..a66047687 100644
--- a/tools/slang-test/test-context.h
+++ b/tools/slang-test/test-context.h
@@ -13,23 +13,18 @@
#include "options.h"
-enum class BackendType
+typedef uint32_t PassThroughFlags;
+struct PassThroughFlag
{
- Unknown = -1,
- Dxc,
- Fxc,
- Glslang,
- CountOf,
-};
-
-typedef uint32_t BackendFlags;
-struct BackendFlag
-{
- enum Enum : uint32_t
+ enum Enum : PassThroughFlags
{
- Dxc = 1 << int(BackendType::Dxc),
- Fxc = 1 << int(BackendType::Fxc),
- Glslang = 1 << int(BackendType::Glslang),
+ Dxc = 1 << int(SLANG_PASS_THROUGH_DXC),
+ Fxc = 1 << int(SLANG_PASS_THROUGH_FXC),
+ Glslang = 1 << int(SLANG_PASS_THROUGH_GLSLANG),
+ VisualStudio = 1 << int(SLANG_PASS_THROUGH_VISUAL_STUDIO),
+ GCC = 1 << int(SLANG_PASS_THROUGH_GCC),
+ Clang = 1 << int(SLANG_PASS_THROUGH_CLANG),
+ Generic_C_CPP = 1 << int(SLANG_PASS_THROUGH_GENERIC_C_CPP),
};
};
@@ -37,11 +32,11 @@ struct BackendFlag
/// back-end availability
struct TestRequirements
{
- TestRequirements& addUsed(BackendType type)
+ TestRequirements& addUsed(SlangPassThrough type)
{
- if (type != BackendType::Unknown)
+ if (type != SLANG_PASS_THROUGH_NONE)
{
- usedBackendFlags |= BackendFlags(1) << int(type);
+ usedBackendFlags |= PassThroughFlags(1) << int(type);
}
return *this;
}
@@ -54,7 +49,7 @@ struct TestRequirements
}
return *this;
}
- TestRequirements& addUsedBackends(BackendFlags flags)
+ TestRequirements& addUsedBackends(PassThroughFlags flags)
{
usedBackendFlags |= flags;
return *this;
@@ -71,7 +66,7 @@ struct TestRequirements
}
Slang::RenderApiType explicitRenderApi = Slang::RenderApiType::Unknown; ///< The render api explicitly specified
- BackendFlags usedBackendFlags = 0; ///< Used backends
+ PassThroughFlags usedBackendFlags = 0; ///< Used backends
Slang::RenderApiFlags usedRenderApiFlags = 0; ///< Used render api flags (some might be implied)
};
@@ -111,7 +106,7 @@ class TestContext
/// If set then tests are not run, but their requirements are set
TestRequirements* testRequirements = nullptr;
- BackendFlags availableBackendFlags = 0;
+ PassThroughFlags availableBackendFlags = 0;
Slang::RenderApiFlags availableRenderApiFlags = 0;
bool isAvailableRenderApiFlagsValid = false;