summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorEllie Hermaszewska <ellieh@nvidia.com>2023-12-08 19:24:34 +0800
committerGitHub <noreply@github.com>2023-12-08 19:24:34 +0800
commit4321929879c1ed5b87ff95a99ca7da91e28d18fd (patch)
treeae1460dcb652981468e6fa4897e87b697f2bda33 /source
parent9903688ccc0793259d43f95cae88bd1a8e178824 (diff)
WIP: CMake (#3326)
* More robust input and output selection in generator tools * Add cmake build system * Get slang-test running with cmake * Bump lz4 and miniz dependencies * Make cmake build more declarative * Correct preprocessor logic in slang.h * Add cuda test to compute/simple * Remove empty cmake files * output placement for cmake, and commenting * Correct include paths in spirv-embed-generator * Format cmake with gersemi * Make cmake build clerer * Neaten header generation Also work around https://gitlab.kitware.com/cmake/cmake/-/issues/18399 by introducing correct_generated_properties to set the GENERATED flag in the correct scope * remove unused files * use 3.20 to set GENERATOR property properly * spelling * more flexible linker arg setting * replace slang-static with obj collection * Set rpath and linker path correctly * neaten generated file generation * tests working with cmake build * fix premake5 build * comment and neaten cmake * remove unnecessary dependency * Build aftermath example only when aftermath is enabled * Add slang-llvm and other dependencies * Put modules alongside binaries * Find slang-glslang correctly * Better option handling * comments * add llvm build test * Better option handling * cmake wobble * use UNICODE and _UNICODE * remove other workflows * use ccache * neaten * limit parallel for llvm build * use ninja for build * Windows and Darwin slang-llvm builds * cache key * verbose llvm build * cl on windows * sccache and cl.exe * use cl.exe * Correct package detection * less verbosity * Simplify miniz inclusion * fix build with sccache * Neaten llvm building * neaten * Neaten slang-llvm fetching * more surgical workarounds * Add ci action * Get version from git * better variable naming * add missing include * clean up after premake in cmake * more docs on cmake build * ci wobble * add imgui target * more selective source * do not download swiftshader * Some missing dependencies * only build llvm on dispatch * Disable /Zi in CI where sccache is present * simplify * set PIC for miniz * set policies before project * reengage workaround * more runs on ci * Add cmake presets * Add cpack * move iterator debug level to preset * Correct lib flag * simplify action * Neaten cmake init * Add todo * Add simple test wrapper * Add tests to workflow presets * rename packing preset * Correctly set definitions * docs * correct preset names * Make slang-test depend on test-server/test-process * neaten * use workflow in actions * install docs * Correct module install dir * debug dist workflow * Install headers * neaten header globbing * Neaten dependency handling * make lib and bin variables * Do not set compiler for vs builds, unnecessary * docs * allow setting explicit source for target * maintain archive subdir * cmake docs * install headers * place targets into folders * cmake docs * nest external projects in folder * remove name clash * Neater external packages * meta targets in folder structure * cleaner slang-glslang dll * Add missing static directive to slang-no-embedded-stdlib * more robust module copying * make slang-test the startup project * folder tweak * Make FETCH_BINARY the default on all platforms * Set DEBUG_DIR * add natvis files to source * skip spirv tests * remove test step from debug dist * Add build to .gitignore * redo warnings to be more like premake * Update imgui * clean more premake files * Disable PCH for glslang, gcc throws a warning * Add /MP for msvc builds * warning wobble * Add script to build llvm * Add slang-llvm and generators components * Build slang-llvm in ci * comments * fetch llvm with git * better abi approximation for cache * better sccache key * formatting * Correct logic around disabling problematic debug info for ccache * exclude gcc and clang from windows ci * Make dist workflows use system llvm * naming * restore normal dist builds * formatting * run tests in ci * Correct slang-llvm url setting * Rely on the system to find the test tool library * actions matrix wiggle * cope with OSX ancient bash * Correct compilers on windows * more ci debugging * Correct rpath handling on OSX * neaten * correct path to slang-llvm * Correct rpath separator on osx * Find slang-llvm correctly * smoke tests only on osx * ci wobble * Give MacOS module a dylib suffix * get swiftshader correctly * cope with bsd cp * remove debug output * full tests on osx * ci wobble * Add some vk tests to expected failures * simplify ci * ci wobble * exclude dx12 tests from github ci * remove cmake code for building llvm * warnings * warnings as errors for cl * spirv-tools in path * add aarch64 ci build * Add SLANG_GENERATORS_PATH option for prebuilt generators * neaten * Correct generator target name * remove yaml anchors because github actions does not support them * Demote CMake in docs Also add info on cross compiling * Restore premake CI * use minimal ci for cmake * Write miniz_export for premake build and .gitignore it * Mention build config tool options in docs * Remove redefined macro for miniz * regenerate vs project
Diffstat (limited to 'source')
-rw-r--r--source/compiler-core/slang-gcc-compiler-util.cpp5
-rw-r--r--source/core/slang-deflate-compression-system.cpp5
-rw-r--r--source/core/slang-zip-file-system.cpp6
-rw-r--r--source/slang-llvm/slang-llvm-filecheck.cpp188
-rw-r--r--source/slang-llvm/slang-llvm.cpp1066
-rw-r--r--source/slang/CMakeLists.txt187
-rw-r--r--source/slang/slang-compiler.cpp1
7 files changed, 1448 insertions, 10 deletions
diff --git a/source/compiler-core/slang-gcc-compiler-util.cpp b/source/compiler-core/slang-gcc-compiler-util.cpp
index d8df9714a..79aefc71d 100644
--- a/source/compiler-core/slang-gcc-compiler-util.cpp
+++ b/source/compiler-core/slang-gcc-compiler-util.cpp
@@ -632,7 +632,10 @@ static SlangResult _parseGCCFamilyLine(SliceAllocator& allocator, const UnownedS
if (options.libraryPaths.count && options.targetType == SLANG_HOST_EXECUTABLE)
{
- cmdLine.addArg("-Wl,-rpath,$ORIGIN");
+ if(PlatformUtil::isFamily(PlatformFamily::Apple, platformKind))
+ cmdLine.addArg("-Wl,-rpath,@loader_path,-rpath,@loader_path/../lib");
+ else
+ cmdLine.addArg("-Wl,-rpath,$ORIGIN,-rpath,$ORIGIN/../lib");
}
StringSlicePool libPathPool(StringSlicePool::Style::Default);
diff --git a/source/core/slang-deflate-compression-system.cpp b/source/core/slang-deflate-compression-system.cpp
index 6c75de9e4..a8e6302cf 100644
--- a/source/core/slang-deflate-compression-system.cpp
+++ b/source/core/slang-deflate-compression-system.cpp
@@ -6,10 +6,7 @@
// We don't want compress #define to clash
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES 1
-#include "../../external/miniz/miniz.h"
-#include "../../external/miniz/miniz_common.h"
-#include "../../external/miniz/miniz_tdef.h"
-#include "../../external/miniz/miniz_tinfl.h"
+#include <miniz.h>
#include "slang-blob.h"
diff --git a/source/core/slang-zip-file-system.cpp b/source/core/slang-zip-file-system.cpp
index 011fd2aab..9944bd9cf 100644
--- a/source/core/slang-zip-file-system.cpp
+++ b/source/core/slang-zip-file-system.cpp
@@ -12,11 +12,7 @@
#include "slang-implicit-directory-collector.h"
-#include "../../external/miniz/miniz.h"
-#include "../../external/miniz/miniz_common.h"
-#include "../../external/miniz/miniz_tdef.h"
-#include "../../external/miniz/miniz_tinfl.h"
-#include "../../external/miniz/miniz_zip.h"
+#include <miniz.h>
namespace Slang
{
diff --git a/source/slang-llvm/slang-llvm-filecheck.cpp b/source/slang-llvm/slang-llvm-filecheck.cpp
new file mode 100644
index 000000000..bd71eac05
--- /dev/null
+++ b/source/slang-llvm/slang-llvm-filecheck.cpp
@@ -0,0 +1,188 @@
+// This file contains a definition of LLVMFileCheck, an implementaion for
+// IFileCheck.
+
+#include <llvm/ADT/SmallString.h>
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/FileCheck/FileCheck.h>
+
+#include <slang.h>
+#include <slang-com-helper.h>
+#include <slang-com-ptr.h>
+#include <core/slang-com-object.h>
+#include <tools/slang-test/filecheck.h>
+
+namespace slang_llvm
+{
+
+using namespace llvm;
+using namespace Slang;
+
+class LLVMFileCheck : IFileCheck, ComBaseObject
+{
+public:
+ // ICastable
+ virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) override;
+
+ // IUnknown
+ SLANG_COM_BASE_IUNKNOWN_ALL
+ void* getInterface(const Guid& guid);
+ void* getObject(const Guid& guid);
+
+ // IFileCheck
+ virtual TestResult SLANG_MCALL performTest(
+ const char* programName,
+ const char* rulesFilePath,
+ const char* fileCheckPrefix,
+ const char* stringToCheck,
+ const char* stringToCheckName,
+ ReportDiagnostic testReporter,
+ void* reporterData,
+ bool colorDiagnosticOutput) noexcept override;
+
+private:
+ // Everything we need to pass through LLVM back to our diagnostic handler
+ struct ReporterData
+ {
+ ReportDiagnostic reportFun;
+ // User data from the caller of performTest
+ void* data;
+ bool colorDiagnosticOutput;
+ const char* programName;
+ TestMessageType testMessageType;
+ };
+
+ static void fileCheckDiagHandler(const SMDiagnostic& diag, void* reporterData);
+};
+
+class DisplayedStringOStream : public raw_string_ostream
+{
+public:
+ DisplayedStringOStream(std::string& s): raw_string_ostream(s){}
+ virtual bool is_displayed() const override { return true; };
+};
+
+void LLVMFileCheck::fileCheckDiagHandler(const SMDiagnostic& diag, void* dataPtr)
+{
+ const ReporterData& reporterData = *reinterpret_cast<ReporterData*>(dataPtr);
+ std::string s;
+ DisplayedStringOStream o(s);
+ o.enable_colors(reporterData.colorDiagnosticOutput);
+ diag.print(reporterData.programName, o);
+ reporterData.reportFun(reporterData.data, TestMessageType::TestFailure, s.c_str());
+}
+
+TestResult LLVMFileCheck::performTest(
+ const char* const programName,
+ const char* const rulesFilePath,
+ const char* const fileCheckPrefix,
+ const char* const stringToCheck,
+ const char* const stringToCheckName,
+ const ReportDiagnostic testReporter,
+ void* const userReporterData,
+ const bool colorDiagnosticOutput) noexcept
+{
+ //
+ // Set up our FileCheck session
+ //
+ FileCheckRequest fcReq;
+ fcReq.CheckPrefixes = {fileCheckPrefix};
+ FileCheck fc(fcReq);
+
+ //
+ // Set up the LLVM source manager for diagnostic output from our input buffers
+ //
+ SourceMgr sourceManager;
+ auto rulesTextOrError = MemoryBuffer::getFile(rulesFilePath, true);
+ if(std::error_code err = rulesTextOrError.getError())
+ {
+ const std::string message = "Unable to load FileCheck rules file: " + err.message();
+ testReporter(userReporterData, TestMessageType::RunError, message.c_str());
+ return TestResult::Fail;
+ }
+ SmallString<4096> rulesBuffer;
+ StringRef rulesStringRef = fc.CanonicalizeFile(*rulesTextOrError.get(), rulesBuffer);
+ sourceManager.AddNewSourceBuffer(
+ MemoryBuffer::getMemBuffer(rulesStringRef, rulesFilePath),
+ SMLoc());
+
+ SmallString<4096> inputBuffer;
+ const auto inputStringMB = MemoryBuffer::getMemBuffer(
+ StringRef(stringToCheck),
+ stringToCheckName,
+ false);
+ const StringRef inputStringRef = fc.CanonicalizeFile(*inputStringMB.get(), inputBuffer);
+ sourceManager.AddNewSourceBuffer(
+ MemoryBuffer::getMemBuffer(inputStringRef, stringToCheckName),
+ SMLoc());
+
+ // Initialize this with a 'RunError' failure type. We'll "downgrade" this to
+ // 'TestFailure' once we've done the FileCheck setup.
+ ReporterData reporterData{
+ testReporter,
+ userReporterData,
+ colorDiagnosticOutput,
+ programName,
+ TestMessageType::RunError};
+ sourceManager.setDiagHandler(fileCheckDiagHandler, static_cast<void*>(&reporterData));
+
+ auto checkPrefix = fc.buildCheckPrefixRegex();
+ if(fc.readCheckFile(sourceManager, rulesStringRef, checkPrefix))
+ {
+ // FileCheck failed to find or understand any FileCheck rules in
+ // the input file, automatic fail, and reported to the diag handler .
+ return TestResult::Fail;
+ }
+
+ // We've done the FileCheck setup, so make sure that any diagnostics
+ // reported on from here are just a regular test failure.
+ reporterData.testMessageType = TestMessageType::TestFailure;
+ if(!fc.checkInput(sourceManager, inputStringRef))
+ {
+ // An ordinary failure, the FileCheck rules didn't match
+ return TestResult::Fail;
+ }
+
+ return TestResult::Pass;
+}
+
+void* LLVMFileCheck::castAs(const Guid& guid)
+{
+ if (auto ptr = getInterface(guid))
+ {
+ return ptr;
+ }
+ return getObject(guid);
+}
+
+void* LLVMFileCheck::getInterface(const Guid& guid)
+{
+ if (guid == ISlangUnknown::getTypeGuid() ||
+ guid == ICastable::getTypeGuid() ||
+ guid == IFileCheck::getTypeGuid())
+ {
+ return static_cast<IFileCheck*>(this);
+ }
+ return nullptr;
+}
+
+void* LLVMFileCheck::getObject(const Guid& guid)
+{
+ SLANG_UNUSED(guid);
+ return nullptr;
+}
+
+} // namespace slang_llvm
+
+extern "C" SLANG_DLL_EXPORT SlangResult createLLVMFileCheck_V1(const SlangUUID& intfGuid, void** out)
+{
+ Slang::ComPtr<slang_llvm::LLVMFileCheck> fileCheck(new slang_llvm::LLVMFileCheck);
+
+ if (auto ptr = fileCheck->castAs(intfGuid))
+ {
+ fileCheck.detach();
+ *out = ptr;
+ return SLANG_OK;
+ }
+
+ return SLANG_E_NO_INTERFACE;
+}
diff --git a/source/slang-llvm/slang-llvm.cpp b/source/slang-llvm/slang-llvm.cpp
new file mode 100644
index 000000000..8ea88f09c
--- /dev/null
+++ b/source/slang-llvm/slang-llvm.cpp
@@ -0,0 +1,1066 @@
+
+#include "clang/Basic/Stack.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Config/config.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/FrontendTool/Utils.h"
+
+#include "clang/Lex/PreprocessorOptions.h"
+
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/Basic/Version.h"
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/LinkAllPasses.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/BuryPointer.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/Timer.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+#include "llvm/Target/TargetMachine.h"
+
+// Jit
+#include "llvm/ExecutionEngine/JITEventListener.h"
+#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
+
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+
+#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
+
+#include "llvm/ExecutionEngine/JITSymbol.h"
+
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IRReader/IRReader.h"
+
+// Slang
+
+#include <slang.h>
+#include <slang-com-helper.h>
+#include <slang-com-ptr.h>
+
+#include <core/slang-list.h>
+#include <core/slang-string.h>
+
+#include <core/slang-hash.h>
+#include <core/slang-com-object.h>
+#include <core/slang-string-util.h>
+#include <core/slang-shared-library.h>
+
+#include <compiler-core/slang-downstream-compiler.h>
+#include <compiler-core/slang-artifact-associated-impl.h>
+#include <compiler-core/slang-artifact-desc-util.h>
+#include <compiler-core/slang-slice-allocator.h>
+
+#include <stdio.h>
+
+// We want to make math functions available to the JIT
+#if SLANG_GCC_FAMILY && __GNUC__ < 6
+# include <cmath>
+# define SLANG_LLVM_STD std::
+#else
+# include <math.h>
+# define SLANG_LLVM_STD
+#endif
+
+#if SLANG_OSX
+// For memset_pattern functions
+// https://www.unix.com/man-page/osx/3/memset_pattern16/
+# include <string.h>
+#endif
+
+#if SLANG_WINDOWS_FAMILY
+
+/*
+It's not clear if this function is needed for ARM WIN targets, but we'll assume it does for now.
+
+https://learn.microsoft.com/en-us/windows/win32/devnotes/-win32-chkstk
+https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/100775
+https://codywu2010.wordpress.com/2010/10/04/__chkstk-and-stack-overflow/
+*/
+
+# if SLANG_PROCESSOR_X86
+extern "C" void /* __declspec(naked)*/ __cdecl _chkstk();
+# else
+extern "C" void /* __declspec(naked)*/ __cdecl __chkstk();
+# endif
+#endif
+
+// Predeclare. We'll use this symbol to lookup timestamp, if we don't have a hash.
+extern "C" SLANG_DLL_EXPORT SlangResult createLLVMDownstreamCompiler_V4(const SlangUUID& intfGuid, Slang::IDownstreamCompiler** out);
+
+namespace slang_llvm {
+
+using namespace clang;
+
+using namespace llvm::opt;
+using namespace llvm;
+using namespace llvm::orc;
+
+using namespace Slang;
+
+class LLVMDownstreamCompiler : public IDownstreamCompiler, ComBaseObject
+{
+public:
+ typedef ComBaseObject Super;
+
+ // IUnknown
+ SLANG_COM_BASE_IUNKNOWN_ALL
+
+ // ICastable
+ virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
+
+ // IDownstreamCompiler
+ virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() SLANG_OVERRIDE { return m_desc; }
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL compile(const CompileOptions& options, IArtifact** outArtifact) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW bool SLANG_MCALL canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return false; }
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString(slang::IBlob** outVersionString) SLANG_OVERRIDE;
+
+ LLVMDownstreamCompiler():
+ m_desc(SLANG_PASS_THROUGH_LLVM, SemanticVersion(LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH))
+ {
+ }
+
+ void* getInterface(const Guid& guid);
+ void* getObject(const Guid& guid);
+
+ Desc m_desc;
+};
+
+
+/* !!!!!!!!!!!!!!!!!!!!! LLVMJITSharedLibrary !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* This implementation uses atomic ref counting to ensure the shared libraries lifetime can outlive the
+LLVMDownstreamCompileResult and the compilation that created it */
+class LLVMJITSharedLibrary : public ISlangSharedLibrary, public ComBaseObject
+{
+public:
+ // ISlangUnknown
+ SLANG_COM_BASE_IUNKNOWN_ALL
+
+ /// ICastable
+ virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
+
+ // ISlangSharedLibrary impl
+ virtual SLANG_NO_THROW void* SLANG_MCALL findSymbolAddressByName(char const* name) SLANG_OVERRIDE;
+
+ LLVMJITSharedLibrary(std::unique_ptr<llvm::orc::LLJIT> jit) :
+ m_jit(std::move(jit))
+ {
+ }
+
+protected:
+ ISlangUnknown* getInterface(const SlangUUID& uuid);
+ void* getObject(const SlangUUID& uuid);
+
+ std::unique_ptr<llvm::orc::LLJIT> m_jit;
+};
+
+ISlangUnknown* LLVMJITSharedLibrary::getInterface(const SlangUUID& guid)
+{
+ if (guid == ISlangUnknown::getTypeGuid() ||
+ guid == ISlangCastable::getTypeGuid() ||
+ guid == ISlangSharedLibrary::getTypeGuid())
+ {
+ return static_cast<ISlangSharedLibrary*>(this);
+ }
+ return nullptr;
+}
+
+void* LLVMJITSharedLibrary::getObject(const SlangUUID& uuid)
+{
+ SLANG_UNUSED(uuid);
+ return nullptr;
+}
+
+void* LLVMJITSharedLibrary::castAs(const Guid& guid)
+{
+ if (auto ptr = getInterface(guid))
+ {
+ return ptr;
+ }
+ return getObject(guid);
+}
+
+void* LLVMJITSharedLibrary::findSymbolAddressByName(char const* name)
+{
+ auto fnExpected = m_jit->lookup(name);
+ if (fnExpected)
+ {
+ auto fn = std::move(*fnExpected);
+ return (void*)fn.getAddress();
+ }
+ return nullptr;
+}
+
+
+static void _ensureSufficientStack() {}
+
+static void _llvmErrorHandler(void* userData, const std::string& message, bool genCrashDiag)
+{
+ //DiagnosticsEngine& diags = *static_cast<DiagnosticsEngine*>(userData);
+ //diags.Report(diag::err_fe_error_backend) << message;
+
+ printf("Clang/LLVM fatal error: %s\n", message.c_str());
+
+ // Run the interrupt handlers to make sure any special cleanups get done, in
+ // particular that we remove files registered with RemoveFileOnSignal.
+ llvm::sys::RunInterruptHandlers();
+
+ // We cannot recover from llvm errors. (!)
+ //
+ // Returning nothing, will still cause LLVM to exit the process.
+}
+
+static Slang::ArtifactDiagnostic::Severity _getSeverity(DiagnosticsEngine::Level level)
+{
+ typedef ArtifactDiagnostic::Severity Severity;
+ typedef DiagnosticsEngine::Level Level;
+ switch (level)
+ {
+ default:
+ case Level::Ignored:
+ case Level::Note:
+ case Level::Remark:
+ {
+ return Severity::Info;
+ }
+ case Level::Warning:
+ {
+ return Severity::Warning;
+ }
+ case Level::Error:
+ case Level::Fatal:
+ {
+ return Severity::Error;
+ }
+ }
+}
+
+class BufferedDiagnosticConsumer : public clang::DiagnosticConsumer
+{
+public:
+
+ BufferedDiagnosticConsumer(IArtifactDiagnostics* diagnostics):
+ m_diagnostics(diagnostics)
+ {
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic& info) override
+ {
+ SmallString<100> text;
+ info.FormatDiagnostic(text);
+
+ ArtifactDiagnostic diagnostic;
+ diagnostic.severity = _getSeverity(level);
+ diagnostic.stage = ArtifactDiagnostic::Stage::Compile;
+ diagnostic.text = TerminatedCharSlice(text.c_str(), Count(text.size()));
+
+ auto location = info.getLocation();
+
+ // Work out what the location is
+ auto& sourceManager = info.getSourceManager();
+
+ // Gets the file/line number
+ const bool useLineDirectives = true;
+ const PresumedLoc presumedLoc = sourceManager.getPresumedLoc(location, useLineDirectives);
+
+ diagnostic.location.line = presumedLoc.getLine();
+ diagnostic.filePath = TerminatedCharSlice(presumedLoc.getFilename());
+
+ m_diagnostics->add(diagnostic);
+ }
+
+ bool hasError() const { return m_diagnostics->getCountAtLeastSeverity(ArtifactDiagnostic::Severity::Error) > 0; }
+
+ ComPtr<IArtifactDiagnostics> m_diagnostics;
+};
+
+/*
+* A question is how to make the prototypes available for these functions. They would need to be defined before the
+* the prelude - or potentially in the prelude.
+*
+* I could just define the prototypes in the prelude, and only impl, if needed. Here though I require that all the functions
+* implemented here, use C style names (ie unmanagled) to simplify lookup.
+*/
+
+struct NameAndFunc
+{
+ typedef void (*Func)();
+
+ const char* name;
+ Func func;
+};
+
+#define SLANG_LLVM_EXPAND(x) x
+
+#define SLANG_LLVM_FUNC(name, cppName, retType, paramTypes) NameAndFunc{ #name, (NameAndFunc::Func)static_cast<retType (*) paramTypes>(&SLANG_LLVM_EXPAND(cppName)) },
+
+// Implementations of maths functions available to JIT
+static float F32_frexp(float x, int* e)
+{
+ float m = ::frexpf(x, e);
+ return m;
+}
+
+static double F64_frexp(double x, int* e)
+{
+ double m = ::frexp(x, e);
+ return m;
+}
+
+static void assertFailed(const char* msg)
+{
+ printf("Assert failed: %s\n", msg);
+ SLANG_BREAKPOINT(0);
+}
+
+#if SLANG_OSX
+
+namespace OSXSpecific
+{
+
+static void bzero(void* dst, size_t size)
+{
+ ::memset(dst, 0, size);
+}
+
+} // OSXSpecific
+#endif
+
+#if SLANG_VC && SLANG_PTR_IS_32
+
+namespace WinSpecific {
+
+// NOTE! These are functions used in 32 bit windows to enable 64 bit maths. This set is probably *not* complete.
+// Check:
+
+// https://source.winehq.org/source/dlls/ntdll/large_int.c
+
+static int64_t __stdcall _alldiv(int64_t a, int64_t b)
+{
+ return a / b;
+}
+
+static int64_t __stdcall _allrem(int64_t a, int64_t b)
+{
+ return a % b;
+}
+
+static uint64_t __stdcall _aullrem(uint64_t a, uint64_t b)
+{
+ return a % b;
+}
+
+static uint64_t __stdcall _aulldiv(uint64_t a, uint64_t b)
+{
+ return a / b;
+}
+
+} // WinSpecific
+
+#endif
+
+// These are only the functions that cannot be implemented with 'reasonable performance' in the prelude.
+// It is assumed that calling from JIT to C function whilst not super expensive, is an issue.
+
+// name, cppName, retType, paramTypes
+#define SLANG_LLVM_FUNCS(x) \
+ x(F64_ceil, ceil, double, (double)) \
+ x(F64_floor, floor, double, (double)) \
+ x(F64_round, round, double, (double)) \
+ x(F64_abs, fabs, double, (double)) \
+ x(F64_sin, sin, double, (double)) \
+ x(F64_cos, cos, double, (double)) \
+ x(F64_tan, tan, double, (double)) \
+ x(F64_asin, asin, double, (double)) \
+ x(F64_acos, acos, double, (double)) \
+ x(F64_atan, atan, double, (double)) \
+ x(F64_sinh, sinh, double, (double)) \
+ x(F64_cosh, cosh, double, (double)) \
+ x(F64_tanh, tanh, double, (double)) \
+ x(F64_log2, log2, double, (double)) \
+ x(F64_log, log, double, (double)) \
+ x(F64_log10, log10, double, (double)) \
+ x(F64_exp2, exp2, double, (double)) \
+ x(F64_exp, exp, double, (double)) \
+ x(F64_fabs, fabs, double, (double)) \
+ x(F64_trunc, trunc, double, (double)) \
+ x(F64_sqrt, sqrt, double, (double)) \
+ \
+ x(F64_isnan, SLANG_LLVM_STD isnan, bool, (double)) \
+ x(F64_isfinite, SLANG_LLVM_STD isfinite, bool, (double)) \
+ x(F64_isinf, SLANG_LLVM_STD isinf, bool, (double)) \
+ \
+ x(F64_atan2, atan2, double, (double, double)) \
+ \
+ x(F64_frexp, F64_frexp, double, (double, int*)) \
+ x(F64_pow, pow, double, (double, double)) \
+ \
+ x(F64_modf, modf, double, (double, double*)) \
+ x(F64_fmod, fmod, double, (double, double)) \
+ x(F64_remainder, remainder, double, (double, double)) \
+ \
+ x(F32_ceil, ceilf, float, (float)) \
+ x(F32_floor, floorf, float, (float)) \
+ x(F32_round, roundf, float, (float)) \
+ x(F32_abs, fabsf, float, (float)) \
+ x(F32_sin, sinf, float, (float)) \
+ x(F32_cos, cosf, float, (float)) \
+ x(F32_tan, tanf, float, (float)) \
+ x(F32_asin, asinf, float, (float)) \
+ x(F32_acos, acosf, float, (float)) \
+ x(F32_atan, atanf, float, (float)) \
+ x(F32_sinh, sinhf, float, (float)) \
+ x(F32_cosh, coshf, float, (float)) \
+ x(F32_tanh, tanhf, float, (float)) \
+ x(F32_log2, log2f, float, (float)) \
+ x(F32_log, logf, float, (float)) \
+ x(F32_log10, log10f, float, (float)) \
+ x(F32_exp2, exp2f, float, (float)) \
+ x(F32_exp, expf, float, (float)) \
+ x(F32_fabs, fabsf, float, (float)) \
+ x(F32_trunc, truncf, float, (float)) \
+ x(F32_sqrt, sqrtf, float, (float)) \
+ \
+ x(F32_isnan, SLANG_LLVM_STD isnan, bool, (float)) \
+ x(F32_isfinite, SLANG_LLVM_STD isfinite, bool, (float)) \
+ x(F32_isinf, SLANG_LLVM_STD isinf, bool, (float)) \
+ \
+ x(F32_atan2, atan2f, float, (float, float)) \
+ \
+ x(F32_frexp, F32_frexp, float, (float, int*)) \
+ x(F32_pow, powf, float, (float, float)) \
+ \
+ x(F32_modf, modff, float, (float, float*)) \
+ x(F32_fmod, fmodf, float, (float, float)) \
+ x(F32_remainder, remainderf, float, (float, float)) \
+ \
+ x(assertFailed, assertFailed, void, (const char*)) \
+ \
+ x(memcpy, memcpy, void*, (void*, const void*, size_t)) \
+ x(memmove, memmove, void*, (void*, const void*, size_t)) \
+ x(memcmp, memcmp, int, (const void*, const void*, size_t)) \
+ x(memset, memset, void*, (void*, int, size_t))
+
+#if SLANG_OSX
+# define SLANG_PLATFORM_FUNCS(x) \
+ x(memset_pattern4, memset_pattern4, void, (void*, const void*, size_t)) \
+ x(memset_pattern8, memset_pattern8, void, (void*, const void*, size_t)) \
+ x(memset_pattern16, memset_pattern16, void, (void*, const void*, size_t)) \
+ \
+ x(__bzero, OSXSpecific::bzero, void, (void*, size_t))
+#endif
+
+#if SLANG_WINDOWS_FAMILY
+# if SLANG_PROCESSOR_X86
+# define SLANG_PLATFORM_FUNCS(x) \
+ x(_chkstk, _chkstk, void, ())
+# else
+# define SLANG_PLATFORM_FUNCS(x) \
+ x(__chkstk, __chkstk, void, ())
+# endif
+#endif
+
+#ifndef SLANG_PLATFORM_FUNCS
+# define SLANG_PLATFORM_FUNCS(x)
+#endif
+
+static int _getOptimizationLevel(DownstreamCompileOptions::OptimizationLevel level)
+{
+ typedef DownstreamCompileOptions::OptimizationLevel OptimizationLevel;
+ switch (level)
+ {
+ case OptimizationLevel::None: return 0;
+ default:
+ case OptimizationLevel::Default: return 1;
+ case OptimizationLevel::High: return 2;
+ case OptimizationLevel::Maximal: return 3;
+ }
+}
+
+static SlangResult _initLLVM()
+{
+ // Initialize targets first, so that --version shows registered targets.
+#if 0
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmPrinters();
+ llvm::InitializeAllAsmParsers();
+#else
+ // Just initialize items needed for this target.
+
+ llvm::InitializeNativeTarget();
+ llvm::InitializeNativeTargetAsmPrinter();
+ llvm::InitializeNativeTargetAsmParser();
+
+ llvm::InitializeNativeTargetDisassembler();
+#endif
+
+ // Set an error handler, so that any LLVM backend diagnostics go through our
+ // error handler.
+ //llvm::install_fatal_error_handler(_llvmErrorHandler, static_cast<void*>(&clang->getDiagnostics()));
+ // NOTE! Can only be set once.
+ llvm::install_fatal_error_handler(_llvmErrorHandler, nullptr);
+
+ return SLANG_OK;
+}
+
+
+bool LLVMDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to)
+{
+ return false;
+}
+
+SlangResult LLVMDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact)
+{
+ return SLANG_E_NOT_IMPLEMENTED;
+}
+
+SlangResult LLVMDownstreamCompiler::getVersionString(slang::IBlob** outVersionString)
+{
+ StringBuilder versionString;
+ // Append the version
+ m_desc.version.append(versionString);
+
+ // Really we should have a hash to identify the specific version.
+ // For now we'll fall back to just using the timestamp
+
+ {
+ // If we don't have the commitHash, we use the library timestamp, to uniquely identify.
+ versionString << " " << SharedLibraryUtils::getSharedLibraryTimestamp((void*)createLLVMDownstreamCompiler_V4);
+ }
+
+ *outVersionString = StringBlob::moveCreate(versionString).detach();
+ return SLANG_OK;
+}
+
+void* LLVMDownstreamCompiler::castAs(const Guid& guid)
+{
+ if (auto ptr = getInterface(guid))
+ {
+ return ptr;
+ }
+ return getObject(guid);
+}
+
+void* LLVMDownstreamCompiler::getInterface(const Guid& guid)
+{
+ if (guid == ISlangUnknown::getTypeGuid() ||
+ guid == ICastable::getTypeGuid() ||
+ guid == IDownstreamCompiler::getTypeGuid())
+ {
+ return static_cast<IDownstreamCompiler*>(this);
+ }
+ return nullptr;
+}
+
+void* LLVMDownstreamCompiler::getObject(const Guid& guid)
+{
+ SLANG_UNUSED(guid);
+ return nullptr;
+}
+
+SlangResult LLVMDownstreamCompiler::compile(const CompileOptions& inOptions, IArtifact** outArtifact)
+{
+ if (!isVersionCompatible(inOptions))
+ {
+ // Not possible to compile with this version of the interface.
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+
+ CompileOptions options = getCompatibleVersion(&inOptions);
+
+ // Currently supports single source file
+ if (options.sourceArtifacts.count != 1)
+ {
+ return SLANG_FAIL;
+ }
+ IArtifact* sourceArtifact = options.sourceArtifacts[0];
+
+ _ensureSufficientStack();
+
+ static const SlangResult initLLVMResult = _initLLVM();
+ SLANG_RETURN_ON_FAIL(initLLVMResult);
+
+ std::unique_ptr<CompilerInstance> clang(new CompilerInstance());
+ IntrusiveRefCntPtr<DiagnosticIDs> diagID(new DiagnosticIDs());
+
+ // Register the support for object-file-wrapped Clang modules.
+ auto pchOps = clang->getPCHContainerOperations();
+ pchOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
+ pchOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
+
+ IntrusiveRefCntPtr<DiagnosticOptions> diagOpts = new DiagnosticOptions();
+
+ ComPtr<IArtifactDiagnostics> diagnostics(new ArtifactDiagnostics);
+
+
+ // TODO(JS): We might just want this to talk directly to the listener.
+ // For now we just buffer up.
+ BufferedDiagnosticConsumer diagsBuffer(diagnostics);
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> diags = new DiagnosticsEngine(diagID, diagOpts, &diagsBuffer, false);
+
+ ComPtr<ISlangBlob> sourceBlob;
+ SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
+
+ const auto sourceSlice = StringUtil::getSlice(sourceBlob);
+ StringRef sourceStringRef(sourceSlice.begin(), sourceSlice.getLength());
+
+ auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer(sourceStringRef);
+
+ auto& invocation = clang->getInvocation();
+
+ std::string verboseOutputString;
+
+ // Capture all of the verbose output into a buffer, so not writen to stdout
+ clang->setVerboseOutputStream(std::make_unique<llvm::raw_string_ostream>(verboseOutputString));
+
+ SmallVector<char> output;
+ clang->setOutputStream(std::make_unique<llvm::raw_svector_ostream>(output));
+
+ frontend::ActionKind action = frontend::ActionKind::EmitLLVMOnly;
+
+ // EmitCodeGenOnly doesn't appear to actually emit anything
+ // EmitLLVM outputs LLVM assembly
+ // EmitLLVMOnly doesn't 'emit' anything, but the IR that is produced is accessible, from the 'action'.
+
+ action = frontend::ActionKind::EmitLLVMOnly;
+
+ //action = frontend::ActionKind::EmitBC;
+ //action = frontend::ActionKind::EmitLLVM;
+ //
+ //action = frontend::ActionKind::EmitCodeGenOnly;
+ //action = frontend::ActionKind::EmitObj;
+ //action = frontend::ActionKind::EmitAssembly;
+
+ Language language;
+ LangStandard::Kind langStd;
+ switch (options.sourceLanguage)
+ {
+ case SLANG_SOURCE_LANGUAGE_CPP:
+ {
+ language = Language::CXX;
+ langStd = LangStandard::Kind::lang_cxx17;
+ break;
+ }
+ case SLANG_SOURCE_LANGUAGE_C:
+ {
+ language = Language::C;
+ langStd = LangStandard::Kind::lang_c17;
+ break;
+ }
+ default:
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+ }
+
+ const InputKind inputKind(language, InputKind::Format::Source);
+
+ {
+ auto& opts = invocation.getFrontendOpts();
+
+ // Add the source
+ // TODO(JS): For the moment this kind of include does *NOT* show a input source filename
+ // not super surprising as one isn't set, but it's not clear how one would be set when the input is a memory buffer.
+ // For Slang usage, this probably isn't an issue, because it's *output* typically holds #line directives.
+ {
+
+ FrontendInputFile inputFile(*sourceBuffer, inputKind);
+ opts.Inputs.push_back(inputFile);
+ }
+
+ opts.ProgramAction = action;
+ }
+
+ {
+ auto& opts = invocation.getPreprocessorOpts();
+
+ // Add definition so that 'LLVM/Clang' compilations can be recognized
+ opts.addMacroDef("SLANG_LLVM");
+
+ for (const auto& define : options.defines)
+ {
+ const Index index = asStringSlice(define.nameWithSig).indexOf('(');
+ if (index >= 0)
+ {
+ // Interface does not support having a signature.
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+ // TODO(JS): NOTE! The options do not support setting a *value* just that a macro is defined.
+ // So strictly speaking, we should probably have a warning/error if the value is not appropriate
+ opts.addMacroDef(define.nameWithSig.begin());
+ }
+ }
+
+
+ llvm::Triple targetTriple;
+ {
+ auto& opts = invocation.getTargetOpts();
+
+ opts.Triple = LLVM_DEFAULT_TARGET_TRIPLE;
+
+ // A code model isn't set by default, "default" seems to fit the bill here
+ opts.CodeModel = "default";
+
+ targetTriple = llvm::Triple(opts.Triple);
+ }
+
+ {
+ auto opts = invocation.getLangOpts();
+
+ std::vector<std::string> includes;
+ for (const auto& includePath : options.includePaths)
+ {
+ includes.push_back(includePath.begin());
+ }
+
+ clang::CompilerInvocation::setLangDefaults(*opts, inputKind, targetTriple, includes, langStd);
+
+ if (options.floatingPointMode == DownstreamCompileOptions::FloatingPointMode::Fast)
+ {
+ opts->FastMath = true;
+ }
+ }
+
+ {
+ auto& opts = invocation.getHeaderSearchOpts();
+
+ // These only work if the resource directory is setup (or a virtual file system points to it)
+ opts.UseBuiltinIncludes = true;
+ opts.UseStandardSystemIncludes = true;
+ opts.UseStandardCXXIncludes = true;
+
+ /// Use libc++ instead of the default libstdc++.
+ //opts.UseLibcxx = true;
+ }
+
+
+ {
+ auto& opts = invocation.getCodeGenOpts();
+
+ // Set to -O optimization level
+ opts.OptimizationLevel = _getOptimizationLevel(options.optimizationLevel);
+
+ // Copy over the targets CodeModel
+ opts.CodeModel = invocation.getTargetOpts().CodeModel;
+ }
+
+ //const llvm::opt::OptTable& opts = clang::driver::getDriverOptTable();
+
+ // TODO(JS): Need a way to find in system search paths, for now we just don't bother
+ //
+ // The system search paths are for includes for compiler intrinsics it seems.
+ // Infer the builtin include path if unspecified.
+#if 0
+ {
+ auto& searchOpts = clang->getHeaderSearchOpts();
+ if (searchOpts.UseBuiltinIncludes && searchOpts.ResourceDir.empty())
+ {
+ // TODO(JS): Hack - hard coded path such that we can test out the
+ // resource directory functionality.
+
+ StringRef binaryPath = "F:/dev/llvm-12.0/llvm-project-llvmorg-12.0.1/build.vs/Release/bin";
+
+ // Dir is bin/ or lib/, depending on where BinaryPath is.
+
+ // On Windows, libclang.dll is in bin/.
+ // On non-Windows, libclang.so/.dylib is in lib/.
+ // With a static-library build of libclang, LibClangPath will contain the
+ // path of the embedding binary, which for LLVM binaries will be in bin/.
+ // ../lib gets us to lib/ in both cases.
+ SmallString<128> path = llvm::sys::path::parent_path(binaryPath);
+ llvm::sys::path::append(path, Twine("lib") + CLANG_LIBDIR_SUFFIX, "clang", CLANG_VERSION_STRING);
+
+ searchOpts.ResourceDir = path.c_str();
+ }
+ }
+#endif
+
+ // Create the actual diagnostics engine.
+ clang->createDiagnostics();
+ clang->setDiagnostics(diags.get());
+
+ if (!clang->hasDiagnostics())
+ return SLANG_FAIL;
+
+ //
+ clang->createFileManager();
+ clang->createSourceManager(clang->getFileManager());
+
+
+ std::unique_ptr<LLVMContext> llvmContext = std::make_unique<LLVMContext>();
+
+ clang::CodeGenAction* codeGenAction = nullptr;
+ std::unique_ptr<FrontendAction> act;
+
+ {
+ // If we are going to just emit IR, we need to have access to the underlying type
+ if (action == frontend::ActionKind::EmitLLVMOnly)
+ {
+ EmitLLVMOnlyAction* llvmOnlyAction = new EmitLLVMOnlyAction(llvmContext.get());
+ codeGenAction = llvmOnlyAction;
+ // Make act the owning ptr
+ act = std::unique_ptr<FrontendAction>(llvmOnlyAction);
+ }
+ else
+ {
+ act = CreateFrontendAction(*clang);
+ }
+
+ if (!act)
+ {
+ return SLANG_FAIL;
+ }
+
+ const bool compileSucceeded = clang->ExecuteAction(*act);
+
+ // If the compilation failed make sure, we have an error
+ if (!compileSucceeded)
+ {
+ diagnostics->requireErrorDiagnostic();
+ }
+
+ if (!compileSucceeded || diagsBuffer.hasError())
+ {
+ diagnostics->setResult(SLANG_FAIL);
+
+ auto artifact = ArtifactUtil::createArtifact(ArtifactDesc::make(ArtifactKind::None, ArtifactPayload::None));
+ ArtifactUtil::addAssociated(artifact, diagnostics);
+
+ *outArtifact = artifact.detach();
+ return SLANG_OK;
+ }
+ }
+
+ std::unique_ptr<llvm::Module> module;
+
+ switch (action)
+ {
+ case frontend::ActionKind::EmitLLVM:
+ {
+ // LLVM output is text, that must be zero terminated
+ output.push_back(char(0));
+
+ StringRef identifier;
+ StringRef data(output.begin(), output.size() - 1);
+
+ MemoryBufferRef memoryBufferRef(data, identifier);
+
+ SMDiagnostic err;
+ module = llvm::parseIR(memoryBufferRef, err, *llvmContext);
+ break;
+ }
+ case frontend::ActionKind::EmitBC:
+ {
+ StringRef identifier;
+ StringRef data(output.begin(), output.size());
+
+ MemoryBufferRef memoryBufferRef(data, identifier);
+
+ SMDiagnostic err;
+ module = llvm::parseIR(memoryBufferRef, err, *llvmContext);
+ break;
+ }
+ case frontend::ActionKind::EmitLLVMOnly:
+ {
+ // Get the module produced by the action
+ module = codeGenAction->takeModule();
+ break;
+ }
+ }
+
+ switch (options.targetType)
+ {
+ // TODO(JS): Shared library may not be appropriate, but as long as the 'shared library' is never accessed as a blob
+ // all is good.
+ case SLANG_SHADER_SHARED_LIBRARY:
+
+ // TODO(JS):
+ // Hmm. What does this even mean?
+ // I guess the idea is it's 'SHADER' style, but is runnable on the host.
+ case SLANG_SHADER_HOST_CALLABLE:
+ {
+ // Try running something in the module on the JIT
+ std::unique_ptr<llvm::orc::LLJIT> jit;
+ {
+ // Create the JIT
+
+ LLJITBuilder jitBuilder;
+
+ Expected<std::unique_ptr< llvm::orc::LLJIT>> expectJit = jitBuilder.create();
+ if (!expectJit)
+ {
+ /* JS: NOTE!
+
+ It is worth saying there can be some odd issues around creating the JIT - if LLVM-C is linked against.
+
+ If it is then LLVM will likely startup saying LLVM-C isn't found.
+ BUT if you have LLVM *installed* on your system (as is reasonable to do from a LLVM distro, then
+ at startup it *MIGHT* find a LLVM-C dll in that installation (ie nothing to do with the version of LLVM
+ linked with). This will likely lead to an odd error saying the 'triple can't be found' and that no
+ targets are registered.
+
+ Also note that the behavior *may* be different with Debug/Release - because of how the linked resolves symbols
+ that are multiply defined.
+
+ If there are problems creating the JIT, check that LLVM-C is not linked against (it should be disabled in the premake).
+ */
+
+ auto err = expectJit.takeError();
+
+ std::string jitErrorString;
+ llvm::raw_string_ostream jitErrorStream(jitErrorString);
+
+ jitErrorStream << err;
+
+ ArtifactDiagnostic diagnostic;
+
+ StringBuilder buf;
+ buf << "Unable to create JIT engine: " << jitErrorString.c_str();
+
+ diagnostic.severity = ArtifactDiagnostic::Severity::Error;
+ diagnostic.stage = ArtifactDiagnostic::Stage::Link;
+ diagnostic.text = TerminatedCharSlice(buf.getBuffer(), buf.getLength());
+
+ // Add the error
+ diagnostics->add(diagnostic);
+ diagnostics->setResult(SLANG_FAIL);
+
+ auto artifact = ArtifactUtil::createArtifact(ArtifactDesc::make(ArtifactKind::None, ArtifactPayload::None));
+ ArtifactUtil::addAssociated(artifact, diagnostics);
+
+ *outArtifact = artifact.detach();
+ return SLANG_OK;
+ }
+ jit = std::move(*expectJit);
+ }
+
+ // Used the following link to test this out
+ // https://www.llvm.org/docs/ORCv2.html
+ // https://www.llvm.org/docs/ORCv2.html#processandlibrarysymbols
+
+ {
+ auto& es = jit->getExecutionSession();
+
+ const DataLayout& dl = jit->getDataLayout();
+ MangleAndInterner mangler(es, dl);
+
+ // The name of the lib must be unique. Should be here as we are only thing adding libs
+ auto stdcLibExpected = es.createJITDylib("stdc");
+
+ if (stdcLibExpected)
+ {
+ auto& stdcLib = *stdcLibExpected;
+
+ // Add all the symbolmap
+ SymbolMap symbolMap;
+
+ //symbolMap.insert(std::make_pair(mangler("sin"), JITEvaluatedSymbol::fromPointer(static_cast<double (*)(double)>(&sin))));
+
+ {
+ static const NameAndFunc funcs[] =
+ {
+ SLANG_LLVM_FUNCS(SLANG_LLVM_FUNC)
+ SLANG_PLATFORM_FUNCS(SLANG_LLVM_FUNC)
+ };
+
+ for (auto& func : funcs)
+ {
+ symbolMap.insert(std::make_pair(mangler(func.name), JITEvaluatedSymbol::fromPointer(func.func)));
+ }
+ }
+
+#if SLANG_PTR_IS_32 && SLANG_VC
+ {
+ // https://docs.microsoft.com/en-us/windows/win32/devnotes/-win32-alldiv
+ symbolMap.insert(std::make_pair(mangler("_alldiv"), JITEvaluatedSymbol::fromPointer(WinSpecific::_alldiv)));
+ symbolMap.insert(std::make_pair(mangler("_allrem"), JITEvaluatedSymbol::fromPointer(WinSpecific::_allrem)));
+ symbolMap.insert(std::make_pair(mangler("_aullrem"), JITEvaluatedSymbol::fromPointer(WinSpecific::_aullrem)));
+ symbolMap.insert(std::make_pair(mangler("_aulldiv"), JITEvaluatedSymbol::fromPointer(WinSpecific::_aulldiv)));
+ }
+#endif
+
+ if (auto err = stdcLib.define(absoluteSymbols(symbolMap)))
+ {
+ return SLANG_FAIL;
+ }
+
+ // Required or the symbols won't be found
+ jit->getMainJITDylib().addToLinkOrder(stdcLib);
+ }
+ }
+
+ ThreadSafeModule threadSafeModule(std::move(module), std::move(llvmContext));
+
+ if (auto err = jit->addIRModule(std::move(threadSafeModule)))
+ {
+ return SLANG_FAIL;
+ }
+
+ if (auto err = jit->initialize(jit->getMainJITDylib()))
+ {
+ return SLANG_FAIL;
+ }
+
+ // Create the shared library
+ ComPtr<ISlangSharedLibrary> sharedLibrary(new LLVMJITSharedLibrary(std::move(jit)));
+
+ // Work out the ArtifactDesc
+ const auto targetDesc = ArtifactDescUtil::makeDescForCompileTarget(options.targetType);
+
+ auto artifact = ArtifactUtil::createArtifact(targetDesc);
+ ArtifactUtil::addAssociated(artifact, diagnostics);
+
+ artifact->addRepresentation(sharedLibrary);
+
+ *outArtifact = artifact.detach();
+ return SLANG_OK;
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
+} // namespace slang_llvm
+
+extern "C" SLANG_DLL_EXPORT SlangResult createLLVMDownstreamCompiler_V4(const SlangUUID& intfGuid, Slang::IDownstreamCompiler** out)
+{
+ Slang::ComPtr<slang_llvm::LLVMDownstreamCompiler> compiler(new slang_llvm::LLVMDownstreamCompiler);
+
+ if (auto ptr = compiler->castAs(intfGuid))
+ {
+ compiler.detach();
+ *out = (Slang::IDownstreamCompiler*)ptr;
+ return SLANG_OK;
+ }
+
+ return SLANG_E_NO_INTERFACE;
+}
diff --git a/source/slang/CMakeLists.txt b/source/slang/CMakeLists.txt
new file mode 100644
index 000000000..c3ea18433
--- /dev/null
+++ b/source/slang/CMakeLists.txt
@@ -0,0 +1,187 @@
+#
+# Compiling the meta.slang files
+#
+
+# List of *.meta.slang headers
+glob_append(SLANG_STDLIB_META_SOURCE "*.meta.slang")
+
+set(SLANG_STDLIB_META_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/stdlib-meta")
+
+# Generate the output file list
+set(SLANG_STDLIB_META_GENERATED_HEADERS)
+foreach(meta_source ${SLANG_STDLIB_META_SOURCE})
+ file(
+ RELATIVE_PATH
+ meta_source_relative
+ ${CMAKE_CURRENT_LIST_DIR}
+ ${meta_source}
+ )
+ list(
+ APPEND
+ SLANG_STDLIB_META_GENERATED_HEADERS
+ "${SLANG_STDLIB_META_OUTPUT_DIR}/${meta_source_relative}.h"
+ )
+endforeach()
+
+add_custom_command(
+ OUTPUT ${SLANG_STDLIB_META_GENERATED_HEADERS}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${SLANG_STDLIB_META_OUTPUT_DIR}
+ COMMAND
+ slang-generate ${SLANG_STDLIB_META_SOURCE} --target-directory
+ ${SLANG_STDLIB_META_OUTPUT_DIR}
+ DEPENDS ${SLANG_STDLIB_META_SOURCE}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+ VERBATIM
+)
+
+add_library(
+ slang-meta-headers
+ INTERFACE
+ EXCLUDE_FROM_ALL
+ ${SLANG_STDLIB_META_GENERATED_HEADERS}
+)
+set_target_properties(slang-meta-headers PROPERTIES FOLDER generated)
+target_include_directories(
+ slang-meta-headers
+ INTERFACE ${SLANG_STDLIB_META_OUTPUT_DIR}
+)
+
+#
+# generated headers for reflection
+#
+
+set(SLANG_REFLECT_INPUT
+ slang-ast-support-types.h
+ slang-ast-base.h
+ slang-ast-decl.h
+ slang-ast-expr.h
+ slang-ast-modifier.h
+ slang-ast-stmt.h
+ slang-ast-type.h
+ slang-ast-val.h
+)
+# Make them absolute
+list(TRANSFORM SLANG_REFLECT_INPUT PREPEND "${CMAKE_CURRENT_LIST_DIR}/")
+
+set(SLANG_REFLECT_GENERATED_HEADERS
+ slang-generated-obj.h
+ slang-generated-obj-macro.h
+ slang-generated-ast.h
+ slang-generated-ast-macro.h
+ slang-generated-value.h
+ slang-generated-value-macro.h
+)
+set(SLANG_REFLECT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ast-reflect")
+list(
+ TRANSFORM SLANG_REFLECT_GENERATED_HEADERS
+ PREPEND "${SLANG_REFLECT_OUTPUT_DIR}/"
+)
+
+add_custom_command(
+ OUTPUT ${SLANG_REFLECT_GENERATED_HEADERS}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${SLANG_REFLECT_OUTPUT_DIR}
+ COMMAND
+ slang-cpp-extractor ${SLANG_REFLECT_INPUT} -strip-prefix slang- -o
+ ${SLANG_REFLECT_OUTPUT_DIR}/slang-generated -output-fields -mark-suffix
+ _CLASS
+ DEPENDS ${SLANG_REFLECT_INPUT}
+ VERBATIM
+)
+
+add_library(
+ slang-reflect-headers
+ INTERFACE
+ EXCLUDE_FROM_ALL
+ ${SLANG_REFLECT_GENERATED_HEADERS}
+)
+set_target_properties(slang-reflect-headers PROPERTIES FOLDER generated)
+target_include_directories(
+ slang-reflect-headers
+ INTERFACE ${SLANG_REFLECT_OUTPUT_DIR}
+)
+
+#
+# Slang static
+#
+
+# TODO: Avoid compiling everything in this directory twice (once here and again
+# for the shared library). This will be easier once premake is gone and we can
+# move slang-stdlib-api.cpp to the build directory. It's basically instant with
+# ccache, but that's not available everywhere.
+slang_add_target(
+ .
+ OBJECT
+ TARGET_NAME slang-no-embedded-stdlib
+ EXCLUDE_FROM_ALL
+ EXTRA_COMPILE_DEFINITIONS_PUBLIC SLANG_STATIC
+ LINK_WITH_PRIVATE
+ core
+ compiler-core
+ # Bundle the source unconditionally
+ slang-meta-headers
+ slang-reflect-headers
+ SPIRV-Headers
+ FOLDER generators
+)
+target_compile_definitions(
+ slang-no-embedded-stdlib
+ PRIVATE SLANG_WITHOUT_EMBEDDED_STD_LIB
+)
+
+#
+# Generate an embeddable stdlib
+#
+
+set(SLANG_STDLIB_GENERATED_HEADER
+ ${CMAKE_CURRENT_BINARY_DIR}/slang-stdlib-generated.h
+)
+add_custom_command(
+ OUTPUT ${SLANG_STDLIB_GENERATED_HEADER}
+ COMMAND
+ slang-bootstrap -archive-type riff-lz4 -save-stdlib-bin-source
+ ${SLANG_STDLIB_GENERATED_HEADER}
+ VERBATIM
+)
+add_library(
+ slang-stdlib-embed-headers
+ INTERFACE
+ EXCLUDE_FROM_ALL
+ ${SLANG_STDLIB_GENERATED_HEADER}
+)
+set_target_properties(slang-stdlib-embed-headers PROPERTIES FOLDER generated)
+target_include_directories(
+ slang-stdlib-embed-headers
+ INTERFACE ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+#
+# Slang itself
+#
+slang_add_target(
+ .
+ SHARED
+ LINK_WITH_PRIVATE
+ core
+ compiler-core
+ prelude
+ slang-reflect-headers
+ SPIRV-Headers
+ # slang.h is in the project root, so include that directory in the interface
+ # for slang
+ INCLUDE_DIRECTORIES_PUBLIC ${slang_SOURCE_DIR}
+ EXPORT_MACRO_PREFIX SLANG
+ INSTALL
+ PUBLIC_HEADERS ${slang_SOURCE_DIR}/slang*.h
+)
+
+if(SLANG_EMBED_STDLIB_SOURCE)
+ target_link_libraries(slang PRIVATE slang-meta-headers)
+else()
+ target_compile_definitions(slang PRIVATE SLANG_DISABLE_STDLIB_SOURCE)
+endif()
+
+if(SLANG_EMBED_STDLIB)
+ target_link_libraries(slang PRIVATE slang-stdlib-embed-headers)
+else()
+ target_compile_definitions(slang PRIVATE SLANG_WITHOUT_EMBEDDED_STD_LIB)
+endif()
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 8956663cc..c2bb515f6 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1354,6 +1354,7 @@ namespace Slang
if (_isCPUHostTarget(target))
{
libraryPaths.add(Path::getParentDirectory(Path::getExecutablePath()));
+ libraryPaths.add(Path::combine(Path::getParentDirectory(Path::getExecutablePath()), "../lib"));
// Set up the library artifact
auto artifact = Artifact::create(ArtifactDesc::make(ArtifactKind::Library, Artifact::Payload::HostCPU), toSlice("slang-rt"));