summaryrefslogtreecommitdiff
path: root/source/slang-record-replay/record
diff options
context:
space:
mode:
authorkaizhangNV <149626564+kaizhangNV@users.noreply.github.com>2024-07-23 10:45:26 -0500
committerGitHub <noreply@github.com>2024-07-23 08:45:26 -0700
commit986256ffb92ab7c8fc7cf9f2c424919a439a824f (patch)
tree260e37bd439275e3398d16fe238b20cd00d08cb7 /source/slang-record-replay/record
parentc28d8b6aec721fa3350fc52647f1572a353f6151 (diff)
Feature/capture (#4625)
* Add decoder * Add a replay executable to consume the decoded content Add file-processor.cpp/h where we implement the logic to process the captured file block by block. Each block is: function header + parameter buffer + function tailer + function output[optional]. After reading one block, the block of data is sent to decoder module to dispatch the corresponding API. Add slang-decoder.cpp/h where we implement the logic to dispatch the slang API according to the input block data. - Rename api_callId.h to capture-format.h - Renmae capture_utility.cpp to capture-utility.cpp - Renmae capture_utility.h to capture-utility.h - Change the #include file name accordingly. * Reorganize source files structure Move all the capture logic code into `capture` directory. - the capture code will be build with slang dll. Move all the replay logic code into `relay` directoy. - the replay code is not part of slang dll, it will be built as a stand alone binary and link against slang dll. Change the #include file names accordingly. Add tools/slang-replay/main.cpp for the slang-replay stand alone binary place holder. Will implement it later. Update premake5.lua accordingly. * Update cmake files Update cmake files to change the build process for capture and relay system. - capture component should be build with slang dll, so we should not include replay component. - replay component should be a separate executable tool, which should not include capture component. - In order to easy use our current cmake infrastructure, move the shared files to a `util` folder - change the header include path * Redesgin the interfaces of consumers Fix some issues in capture Finish implementing all slang-decoder functions * Fix the AppleClang build issue * Address few comments - Fix the weird indent issues. - Correct the function name for CreateGlobalSession() - Rename file-processor to captureFile-processor to be more specific. - Use Slang::List instead of std::vector * record/replay: name refactor change Refactor the naming. Change the name "encoder/capture" to "record".
Diffstat (limited to 'source/slang-record-replay/record')
-rw-r--r--source/slang-record-replay/record/output-stream.cpp52
-rw-r--r--source/slang-record-replay/record/output-stream.h46
-rw-r--r--source/slang-record-replay/record/parameter-recorder.cpp123
-rw-r--r--source/slang-record-replay/record/parameter-recorder.h94
-rw-r--r--source/slang-record-replay/record/record-manager.cpp97
-rw-r--r--source/slang-record-replay/record/record-manager.h35
-rw-r--r--source/slang-record-replay/record/slang-composite-component-type.cpp309
-rw-r--r--source/slang-record-replay/record/slang-composite-component-type.h76
-rw-r--r--source/slang-record-replay/record/slang-entrypoint.cpp313
-rw-r--r--source/slang-record-replay/record/slang-entrypoint.h77
-rw-r--r--source/slang-record-replay/record/slang-filesystem.cpp124
-rw-r--r--source/slang-record-replay/record/slang-filesystem.h71
-rw-r--r--source/slang-record-replay/record/slang-global-session.cpp452
-rw-r--r--source/slang-record-replay/record/slang-global-session.h85
-rw-r--r--source/slang-record-replay/record/slang-module.cpp496
-rw-r--r--source/slang-record-replay/record/slang-module.h102
-rw-r--r--source/slang-record-replay/record/slang-session.cpp517
-rw-r--r--source/slang-record-replay/record/slang-session.h110
-rw-r--r--source/slang-record-replay/record/slang-type-conformance.cpp310
-rw-r--r--source/slang-record-replay/record/slang-type-conformance.h77
20 files changed, 3566 insertions, 0 deletions
diff --git a/source/slang-record-replay/record/output-stream.cpp b/source/slang-record-replay/record/output-stream.cpp
new file mode 100644
index 000000000..8575d19d0
--- /dev/null
+++ b/source/slang-record-replay/record/output-stream.cpp
@@ -0,0 +1,52 @@
+#include "output-stream.h"
+#include "../util/record-utility.h"
+
+namespace SlangRecord
+{
+ FileOutputStream::FileOutputStream(const std::string& filename, bool append)
+ {
+ Slang::String path(filename.c_str());
+ Slang::FileMode fileMode = append ? Slang::FileMode::Append : Slang::FileMode::Create;
+ Slang::FileAccess fileAccess = Slang::FileAccess::Write;
+ Slang::FileShare fileShare = Slang::FileShare::None;
+
+ SlangResult res = m_fileStream.init(path, fileMode, fileAccess, fileShare);
+
+ if (res != SLANG_OK)
+ {
+ SlangRecord::slangRecordLog(SlangRecord::LogLevel::Error, "Failed to open file %s\n", filename.c_str());
+ std::abort();
+ }
+ }
+
+ FileOutputStream::~FileOutputStream()
+ {
+ m_fileStream.close();
+ }
+
+ void FileOutputStream::write(const void* data, size_t len)
+ {
+ SLANG_RECORD_CHECK(m_fileStream.write(data, len));
+ }
+
+ MemoryStream::MemoryStream()
+ : m_memoryStream(Slang::FileAccess::Write)
+ { }
+
+ void FileOutputStream::flush()
+ {
+ SLANG_RECORD_CHECK(m_fileStream.flush());
+ }
+
+ void MemoryStream::write(const void* data, size_t len)
+ {
+ SLANG_RECORD_CHECK(m_memoryStream.write(data, len));
+ }
+
+ void MemoryStream::flush()
+ {
+ // This call will reset the underlying buffer to size 0,
+ // and reset the write position to 0.
+ m_memoryStream.setContent(nullptr, 0);
+ }
+}
diff --git a/source/slang-record-replay/record/output-stream.h b/source/slang-record-replay/record/output-stream.h
new file mode 100644
index 000000000..770c2aeac
--- /dev/null
+++ b/source/slang-record-replay/record/output-stream.h
@@ -0,0 +1,46 @@
+#ifndef OUTPUT_STREAM_H
+#define OUTPUT_STREAM_H
+
+#include <string>
+#include "../../core/slang-stream.h"
+
+namespace SlangRecord
+{
+ class OutputStream
+ {
+ public:
+ virtual ~OutputStream() {}
+ virtual void write(const void* data, size_t len) = 0;
+ virtual void flush() {}
+ };
+
+ class FileOutputStream : public OutputStream
+ {
+ public:
+ FileOutputStream(const std::string& filename, bool append = false);
+ virtual ~FileOutputStream() override;
+ virtual void write(const void* data, size_t len) override;
+ virtual void flush() override;
+
+ private:
+ Slang::FileStream m_fileStream;
+ };
+
+ // The reason we inherit from OwnedMemoryStream instead of declaring it
+ // as a member is because OwnedMemoryStream lacks some of the functionality
+ // of operating on the underlying buffer directly.
+ class MemoryStream : public OutputStream
+ {
+ public:
+ MemoryStream();
+ virtual ~MemoryStream() { }
+ virtual void write(const void* data, size_t len) override;
+ virtual void flush() override;
+ const void* getData() { return m_memoryStream.getContents().getBuffer(); }
+ size_t getSizeInBytes() { return m_memoryStream.getContents().getCount(); }
+
+ private:
+ Slang::OwnedMemoryStream m_memoryStream;
+ };
+} // namespace SlangRecord
+#endif // OUTPUT_STREAM_H
diff --git a/source/slang-record-replay/record/parameter-recorder.cpp b/source/slang-record-replay/record/parameter-recorder.cpp
new file mode 100644
index 000000000..1c2fd9609
--- /dev/null
+++ b/source/slang-record-replay/record/parameter-recorder.cpp
@@ -0,0 +1,123 @@
+#include "parameter-recorder.h"
+
+namespace SlangRecord
+{
+ void ParameterRecorder::recordStruct(slang::SessionDesc const& desc)
+ {
+ recordUint64(desc.structureSize);
+ recordInt64(desc.targetCount);
+
+ for (SlangInt i = 0; i < desc.targetCount; i++)
+ {
+ recordStruct(desc.targets[i]);
+ }
+
+ recordUint32(desc.flags);
+ recordEnumValue(desc.defaultMatrixLayoutMode);
+ recordInt64(desc.searchPathCount);
+ for (SlangInt i = 0; i < desc.searchPathCount; i++)
+ {
+ recordString(desc.searchPaths[i]);
+ }
+
+ recordInt64(desc.preprocessorMacroCount);
+ for (SlangInt i = 0; i < desc.preprocessorMacroCount; i++)
+ {
+ recordStruct(desc.preprocessorMacros[i]);
+ }
+
+ recordBool(desc.enableEffectAnnotations);
+ recordBool(desc.allowGLSLSyntax);
+
+ recordUint32(desc.compilerOptionEntryCount);
+ for (uint32_t i = 0; i < desc.compilerOptionEntryCount; i++)
+ {
+ recordStruct(desc.compilerOptionEntries[i]);
+ }
+ }
+
+ void ParameterRecorder::recordStruct(slang::PreprocessorMacroDesc const& desc)
+ {
+ recordString(desc.name);
+ recordString(desc.value);
+ }
+
+ void ParameterRecorder::recordStruct(slang::CompilerOptionEntry const& entry)
+ {
+ recordEnumValue(entry.name);
+ recordStruct(entry.value);
+ }
+
+ void ParameterRecorder::recordStruct(slang::CompilerOptionValue const& value)
+ {
+ recordEnumValue(value.kind);
+ recordInt32(value.intValue0);
+ recordString(value.stringValue0);
+ recordString(value.stringValue1);
+ }
+
+ void ParameterRecorder::recordStruct(slang::TargetDesc const& targetDesc)
+ {
+ recordUint64(targetDesc.structureSize);
+ recordEnumValue(targetDesc.format);
+ recordEnumValue(targetDesc.profile);
+ recordEnumValue(targetDesc.flags);
+ recordEnumValue(targetDesc.floatingPointMode);
+ recordEnumValue(targetDesc.lineDirectiveMode);
+ recordBool(targetDesc.forceGLSLScalarBufferLayout);
+ recordUint32(targetDesc.compilerOptionEntryCount);
+ for (uint32_t i = 0; i < targetDesc.compilerOptionEntryCount; i++)
+ {
+ recordStruct(targetDesc.compilerOptionEntries[i]);
+ }
+ }
+
+ void ParameterRecorder::recordStruct(slang::SpecializationArg const& specializationArg)
+ {
+ recordEnumValue(specializationArg.kind);
+ recordAddress(specializationArg.type);
+ }
+
+ void ParameterRecorder::recordPointer(const void* value, bool omitData, size_t size)
+ {
+ recordAddress(value);
+ if (omitData)
+ {
+ recordUint64(0llu);
+ return;
+ }
+
+ recordUint64(size);
+ if (size)
+ {
+ m_stream->write(value, size);
+ }
+ }
+
+ void ParameterRecorder::recordPointer(ISlangBlob* blob)
+ {
+ recordAddress(static_cast<const void*>(blob));
+
+ if (blob)
+ {
+ size_t size = blob->getBufferSize();
+ const void* buffer = blob->getBufferPointer();
+ recordPointer(buffer, false, size);
+ }
+ }
+
+ // first 4-bytes is the length of the string
+ void ParameterRecorder::recordString(const char* value)
+ {
+ if (value == nullptr)
+ {
+ recordUint32(0);
+ }
+ else
+ {
+ uint32_t size = (uint32_t)strlen(value);
+ recordUint32(size);
+ m_stream->write(value, size);
+ }
+ }
+}
diff --git a/source/slang-record-replay/record/parameter-recorder.h b/source/slang-record-replay/record/parameter-recorder.h
new file mode 100644
index 000000000..29bf39fb3
--- /dev/null
+++ b/source/slang-record-replay/record/parameter-recorder.h
@@ -0,0 +1,94 @@
+#ifndef PARAMETER_ENCODER_H
+#define PARAMETER_ENCODER_H
+
+#include <cstdio>
+#include <cinttypes>
+#include <cstdint>
+
+#include "output-stream.h"
+#include "../util/record-format.h"
+
+namespace SlangRecord
+{
+ class ParameterRecorder
+ {
+ public:
+ ParameterRecorder(OutputStream* stream) : m_stream(stream) {};
+ void recordInt8(int8_t value) { recordValue(value); }
+ void recordUint8(uint8_t value) { recordValue(value); }
+ void recordInt16(int16_t value) { recordValue(value); }
+ void recordUint16(uint16_t value) { recordValue(value); }
+ void recordInt32(int32_t value) { recordValue(value); }
+ void recordUint32(uint32_t value) { recordValue(value); }
+ void recordInt64(int64_t value) { recordValue(value); }
+ void recordUint64(uint64_t value) { recordValue(value); }
+ void recordFloat(float value) { recordValue(value); }
+ void recordDouble(double value) { recordValue(value); }
+ void recordBool(bool value) { recordValue(value); }
+
+ template<typename T>
+ void recordEnumValue(T value) { recordValue(static_cast<uint32_t>(value)); }
+
+ void recordString(const char* value);
+ void recordPointer(const void* value, bool omitData = false, size_t size = 0);
+ void recordPointer(ISlangBlob* blob);
+ void recordAddress(const void* value) { recordValue(reinterpret_cast<SlangRecord::AddressFormat>(value)); }
+
+ void recordStruct(slang::SessionDesc const& desc);
+ void recordStruct(slang::PreprocessorMacroDesc const& desc);
+ void recordStruct(slang::CompilerOptionEntry const& entry);
+ void recordStruct(slang::CompilerOptionValue const& value);
+ void recordStruct(slang::TargetDesc const& targetDesc);
+ void recordStruct(slang::SpecializationArg const& specializationArg);
+
+ template <typename T>
+ void recordValueArray(const T* array, size_t count)
+ {
+ recordUint32((uint32_t)count);
+ for (size_t i = 0; i < count; ++i)
+ {
+ recordValue(array[i]);
+ }
+ }
+
+ void recordStringArray(const char* const* array, size_t count)
+ {
+ recordUint32((uint32_t)count);
+ for (size_t i = 0; i < count; ++i)
+ {
+ recordString(array[i]);
+ }
+ }
+
+ template <typename T>
+ void recordStructArray(T const* array, size_t count)
+ {
+ recordUint32((uint32_t)count);
+ for (size_t i = 0; i < count; ++i)
+ {
+ recordStruct(array[i]);
+ }
+ }
+
+ template <typename T>
+ void recordAddressArray(T* const* array, size_t count)
+ {
+ recordUint32((uint32_t)count);
+ for (size_t i = 0; i < count; ++i)
+ {
+ recordAddress(array[i]);
+ }
+ }
+
+
+ private:
+ template <typename T>
+ void recordValue(T value)
+ {
+ m_stream->write(&value, sizeof(T));
+ }
+ OutputStream* m_stream;
+ };
+} // namespace SlangRecord
+
+#endif // PARAMETER_ENCODER_H
diff --git a/source/slang-record-replay/record/record-manager.cpp b/source/slang-record-replay/record/record-manager.cpp
new file mode 100644
index 000000000..f10d2c704
--- /dev/null
+++ b/source/slang-record-replay/record/record-manager.cpp
@@ -0,0 +1,97 @@
+
+#include <string>
+#include <sstream>
+#include <thread>
+#include "../util/record-utility.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ RecordManager::RecordManager(uint64_t globalSessionHandle)
+ : m_recorder(&m_memoryStream)
+ {
+ std::stringstream ss;
+ ss << "gs-"<< globalSessionHandle <<"-t-"<<std::this_thread::get_id() << ".cap";
+
+ m_recordFileDirectory = m_recordFileDirectory / "slang-record";
+
+ if (!std::filesystem::exists(m_recordFileDirectory))
+ {
+ std::error_code ec;
+ if (!std::filesystem::create_directory(m_recordFileDirectory, ec))
+ {
+ slangRecordLog(LogLevel::Error, "Fail to create directory: %s, error (%d): %s\n",
+ m_recordFileDirectory.string().c_str(), ec.value(), ec.message().c_str());
+ }
+ }
+
+ std::filesystem::path recordFilePath = m_recordFileDirectory / ss.str();
+ m_fileStream = std::make_unique<FileOutputStream>(recordFilePath.string());
+ }
+
+ void RecordManager::clearWithHeader(const ApiCallId& callId, uint64_t handleId)
+ {
+ m_memoryStream.flush();
+ FunctionHeader header;
+ header.callId = callId;
+ header.handleId = handleId;
+
+ // write header to memory stream
+ m_memoryStream.write(&header, sizeof(FunctionHeader));
+ }
+
+ void RecordManager::clearWithTailer()
+ {
+ m_memoryStream.flush();
+ FunctionTailer tailer;
+
+ // write header to memory stream
+ m_memoryStream.write(&tailer, sizeof(FunctionTailer));
+ }
+
+ ParameterRecorder* RecordManager::beginMethodRecord(const ApiCallId& callId, uint64_t handleId)
+ {
+ clearWithHeader(callId, handleId);
+ return &m_recorder;
+ }
+
+ ParameterRecorder* RecordManager::endMethodRecord()
+ {
+ FunctionHeader* pHeader = const_cast<FunctionHeader*>(
+ reinterpret_cast<const FunctionHeader*>(m_memoryStream.getData()));
+
+ pHeader->dataSizeInBytes = m_memoryStream.getSizeInBytes() - sizeof(FunctionHeader);
+
+ std::hash<std::thread::id> hasher;
+ pHeader->threadId = hasher(std::this_thread::get_id());
+
+ // write record data to file
+ m_fileStream->write(m_memoryStream.getData(), m_memoryStream.getSizeInBytes());
+
+ // take effect of the write
+ m_fileStream->flush();
+
+ // clear the memory stream
+ m_memoryStream.flush();
+
+ clearWithTailer();
+ return &m_recorder;
+ }
+
+ void RecordManager::endMethodRecordAppendOutput()
+ {
+ FunctionTailer* pTailer = const_cast<FunctionTailer*>(
+ reinterpret_cast<const FunctionTailer*>(m_memoryStream.getData()));
+
+ pTailer->dataSizeInBytes = (uint32_t)(m_memoryStream.getSizeInBytes() - sizeof(FunctionTailer));
+
+ // write record data to file
+ m_fileStream->write(m_memoryStream.getData(), m_memoryStream.getSizeInBytes());
+
+ // take effect of the write
+ m_fileStream->flush();
+
+ // clear the memory stream
+ m_memoryStream.flush();
+ }
+}
diff --git a/source/slang-record-replay/record/record-manager.h b/source/slang-record-replay/record/record-manager.h
new file mode 100644
index 000000000..40992e8ad
--- /dev/null
+++ b/source/slang-record-replay/record/record-manager.h
@@ -0,0 +1,35 @@
+#ifndef RECORD_MANAGER_H
+#define RECORD_MANAGER_H
+
+#include <filesystem>
+#include "parameter-recorder.h"
+#include "../util/record-format.h"
+
+namespace SlangRecord
+{
+ class RecordManager
+ {
+ public:
+ RecordManager(uint64_t globalSessionHandle);
+
+ // Each method record has to start with a FunctionHeader
+ ParameterRecorder* beginMethodRecord(const ApiCallId& callId, uint64_t handleId);
+ ParameterRecorder* endMethodRecord();
+
+ // endMethodRecordAppendOutput is an optional call that can be used to append output to
+ // the end of the record. It has to start with a FunctionTailer
+ void endMethodRecordAppendOutput();
+
+ std::filesystem::path const& getRecordFileDirectory() const { return m_recordFileDirectory; }
+
+ private:
+ void clearWithHeader(const ApiCallId& callId, uint64_t handleId);
+ void clearWithTailer();
+
+ MemoryStream m_memoryStream;
+ std::unique_ptr<FileOutputStream> m_fileStream;
+ std::filesystem::path m_recordFileDirectory = std::filesystem::current_path();
+ ParameterRecorder m_recorder;
+ };
+} // namespace SlangRecord
+#endif // RECORD_MANAGER_H
diff --git a/source/slang-record-replay/record/slang-composite-component-type.cpp b/source/slang-record-replay/record/slang-composite-component-type.cpp
new file mode 100644
index 000000000..ef594a893
--- /dev/null
+++ b/source/slang-record-replay/record/slang-composite-component-type.cpp
@@ -0,0 +1,309 @@
+#include "../util/record-utility.h"
+#include "slang-composite-component-type.h"
+
+namespace SlangRecord
+{
+ CompositeComponentTypeRecorder::CompositeComponentTypeRecorder(
+ slang::IComponentType* componentType, RecordManager* recordManager)
+ : m_actualCompositeComponentType(componentType),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualCompositeComponentType != nullptr);
+ SLANG_RECORD_ASSERT(m_recordManager != nullptr);
+
+ m_compositeComponentHandle = reinterpret_cast<uint64_t>(m_actualCompositeComponentType.get());
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, componentType);
+ }
+
+ CompositeComponentTypeRecorder::~CompositeComponentTypeRecorder()
+ {
+ m_actualCompositeComponentType->release();
+ }
+
+ ISlangUnknown* CompositeComponentTypeRecorder::getInterface(const Guid& guid)
+ {
+ if (guid == IComponentType::getTypeGuid())
+ {
+ return static_cast<ISlangUnknown*>(this);
+ }
+ return nullptr;
+ }
+
+ SLANG_NO_THROW slang::ISession* CompositeComponentTypeRecorder::getSession()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getSession, m_compositeComponentHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ISession* res = m_actualCompositeComponentType->getSession();
+
+ {
+ recorder->recordAddress(res);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* CompositeComponentTypeRecorder::getLayout(
+ SlangInt targetIndex,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getLayout, m_compositeComponentHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ProgramLayout* programLayout = m_actualCompositeComponentType->getLayout(targetIndex, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(programLayout);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return programLayout;
+ }
+
+ SLANG_NO_THROW SlangInt CompositeComponentTypeRecorder::getSpecializationParamCount()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt res = m_actualCompositeComponentType->getSpecializationParamCount();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointCode, m_compositeComponentHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getTargetCode, m_compositeComponentHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->getTargetCode(targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getResultAsFileSystem, m_compositeComponentHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+
+ {
+ recorder->recordAddress(*outFileSystem);
+ }
+
+ // TODO: We might need to wrap the file system object.
+ return res;
+ }
+
+ SLANG_NO_THROW void CompositeComponentTypeRecorder::getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointHash, m_compositeComponentHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualCompositeComponentType->getEntryPointHash(entryPointIndex, targetIndex, outHash);
+
+ {
+ recorder->recordAddress(*outHash);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_specialize, m_compositeComponentHandle);
+ recorder->recordInt64(specializationArgCount);
+ recorder->recordStructArray(specializationArgs, specializationArgCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSpecializedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_link, m_compositeComponentHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->link(outLinkedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointHostCallable, m_compositeComponentHandle);
+ recorder->recordInt32(entryPointIndex);
+ recorder->recordInt32(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSharedLibrary);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_renameEntryPoint, m_compositeComponentHandle);
+ recorder->recordString(newName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->renameEntryPoint(newName, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_linkWithOptions, m_compositeComponentHandle);
+ recorder->recordUint32(compilerOptionEntryCount);
+ recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualCompositeComponentType->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+}
diff --git a/source/slang-record-replay/record/slang-composite-component-type.h b/source/slang-record-replay/record/slang-composite-component-type.h
new file mode 100644
index 000000000..758a59434
--- /dev/null
+++ b/source/slang-record-replay/record/slang-composite-component-type.h
@@ -0,0 +1,76 @@
+#ifndef SLANG_COMPOSITE_COMPONENT_TYPE_H
+#define SLANG_COMPOSITE_COMPONENT_TYPE_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "../../core/slang-dictionary.h"
+#include "../../slang/slang-compiler.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+ class CompositeComponentTypeRecorder: public slang::IComponentType, public RefObject
+ {
+ public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ explicit CompositeComponentTypeRecorder(slang::IComponentType* componentType, RecordManager* recordManager);
+ ~CompositeComponentTypeRecorder();
+
+ // Interfaces for `IComponentType`
+ virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override;
+ virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
+ SlangInt targetIndex = 0,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics = 0) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+
+ slang::IComponentType* getActualCompositeComponentType() const { return m_actualCompositeComponentType; }
+ private:
+ Slang::ComPtr<slang::IComponentType> m_actualCompositeComponentType;
+ uint64_t m_compositeComponentHandle = 0;
+ RecordManager* m_recordManager = nullptr;
+
+ };
+}
+#endif // SLANG_COMPOSITE_COMPONENT_TYPE_H
diff --git a/source/slang-record-replay/record/slang-entrypoint.cpp b/source/slang-record-replay/record/slang-entrypoint.cpp
new file mode 100644
index 000000000..d3bf3b47e
--- /dev/null
+++ b/source/slang-record-replay/record/slang-entrypoint.cpp
@@ -0,0 +1,313 @@
+#include "../util/record-utility.h"
+#include "slang-entrypoint.h"
+
+namespace SlangRecord
+{
+ EntryPointRecorder::EntryPointRecorder(slang::IEntryPoint* entryPoint, RecordManager* recordManager)
+ : m_actualEntryPoint(entryPoint),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualEntryPoint != nullptr);
+ SLANG_RECORD_ASSERT(m_recordManager != nullptr);
+
+ m_entryPointHandle = reinterpret_cast<uint64_t>(m_actualEntryPoint.get());
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, entryPoint);
+ }
+
+ EntryPointRecorder::~EntryPointRecorder()
+ {
+ m_actualEntryPoint->release();
+ }
+
+ ISlangUnknown* EntryPointRecorder::getInterface(const Guid& guid)
+ {
+ if(guid == EntryPointRecorder::getTypeGuid())
+ return static_cast<ISlangUnknown*>(this);
+ else
+ return nullptr;
+ }
+
+ SLANG_NO_THROW slang::ISession* EntryPointRecorder::getSession()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getSession, m_entryPointHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ISession* session = m_actualEntryPoint->getSession();
+
+ {
+ recorder->recordAddress(session);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return session;
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* EntryPointRecorder::getLayout(
+ SlangInt targetIndex,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getLayout, m_entryPointHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ProgramLayout* programLayout = m_actualEntryPoint->getLayout(targetIndex, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(programLayout);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return programLayout;
+ }
+
+ SLANG_NO_THROW SlangInt EntryPointRecorder::getSpecializationParamCount()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt res = m_actualEntryPoint->getSpecializationParamCount();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointCode, m_entryPointHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getTargetCode, m_entryPointHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->getTargetCode(targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getResultAsFileSystem, m_entryPointHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+
+ {
+ recorder->recordAddress(*outFileSystem);
+ }
+
+ // TODO: We might need to wrap the file system object.
+ return res;
+ }
+
+ SLANG_NO_THROW void EntryPointRecorder::getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointHash, m_entryPointHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualEntryPoint->getEntryPointHash(entryPointIndex, targetIndex, outHash);
+
+ {
+ recorder->recordAddress(*outHash);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_specialize, m_entryPointHandle);
+ recorder->recordInt64(specializationArgCount);
+ recorder->recordStructArray(specializationArgs, specializationArgCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSpecializedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_link, m_entryPointHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->link(outLinkedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointHostCallable, m_entryPointHandle);
+ recorder->recordInt32(entryPointIndex);
+ recorder->recordInt32(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSharedLibrary);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_renameEntryPoint, m_entryPointHandle);
+ recorder->recordString(newName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->renameEntryPoint(newName, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult EntryPointRecorder::linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_linkWithOptions, m_entryPointHandle);
+ recorder->recordUint32(compilerOptionEntryCount);
+ recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualEntryPoint->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW slang::FunctionReflection* EntryPointRecorder::getFunctionReflection()
+ {
+ return m_actualEntryPoint->getFunctionReflection();
+ }
+
+}
diff --git a/source/slang-record-replay/record/slang-entrypoint.h b/source/slang-record-replay/record/slang-entrypoint.h
new file mode 100644
index 000000000..17c6fab6f
--- /dev/null
+++ b/source/slang-record-replay/record/slang-entrypoint.h
@@ -0,0 +1,77 @@
+#ifndef SLANG_ENTRY_POINT_H
+#define SLANG_ENTRY_POINT_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "../../core/slang-dictionary.h"
+#include "../../slang/slang-compiler.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+ class EntryPointRecorder : public slang::IEntryPoint, public RefObject
+ {
+ public:
+ SLANG_COM_INTERFACE(0xf4c1e23d, 0xb321, 0x4931, { 0x8f, 0x37, 0xf1, 0x22, 0x6a, 0xf9, 0x20, 0x85 })
+
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ explicit EntryPointRecorder(slang::IEntryPoint* entryPoint, RecordManager* recordManager);
+ ~EntryPointRecorder();
+
+ // Interfaces for `IComponentType`
+ virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override;
+ virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
+ SlangInt targetIndex = 0,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics = 0) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW slang::FunctionReflection* SLANG_MCALL getFunctionReflection() override;
+ slang::IEntryPoint* getActualEntryPoint() const { return m_actualEntryPoint; }
+ private:
+ Slang::ComPtr<slang::IEntryPoint> m_actualEntryPoint;
+ uint64_t m_entryPointHandle = 0;
+ RecordManager* m_recordManager = nullptr;
+ };
+}
+#endif // SLANG_ENTRY_POINT_H
diff --git a/source/slang-record-replay/record/slang-filesystem.cpp b/source/slang-record-replay/record/slang-filesystem.cpp
new file mode 100644
index 000000000..1187a9aa6
--- /dev/null
+++ b/source/slang-record-replay/record/slang-filesystem.cpp
@@ -0,0 +1,124 @@
+#include "slang-filesystem.h"
+#include "../util/record-utility.h"
+#include "output-stream.h"
+
+namespace SlangRecord
+{
+ // We don't actually need to record the methods of ISlangFileSystemExt, we just want to record the file content
+ // and save them into disk.
+ FileSystemRecorder::FileSystemRecorder(ISlangFileSystemExt* fileSystem, RecordManager* recordManager)
+ : m_actualFileSystem(fileSystem),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualFileSystem);
+ SLANG_RECORD_ASSERT(m_recordManager);
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, m_actualFileSystem.get());
+ }
+
+ FileSystemRecorder::~FileSystemRecorder()
+ {
+ m_actualFileSystem->release();
+ }
+
+ void* FileSystemRecorder::castAs(const Slang::Guid& guid)
+ {
+ return getInterface(guid);
+ }
+
+ ISlangUnknown* FileSystemRecorder::getInterface(const Slang::Guid& guid)
+ {
+ if(guid == ISlangUnknown::getTypeGuid() || guid == ISlangFileSystem::getTypeGuid())
+ return static_cast<ISlangFileSystem*>(this);
+ return nullptr;
+ }
+
+ // TODO: There could be a potential issue that could not be able to dump the generated file content correctly.
+ // Details: https://github.com/shader-slang/slang/issues/4423.
+ SLANG_NO_THROW SlangResult FileSystemRecorder::loadFile(
+ char const* path,
+ ISlangBlob** outBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->loadFile(path, outBlob);
+
+ // Since the loadFile method could be implemented by client, we can't guarantee the result is always as expected,
+ // we will check every thing to make sure we won't crash at writing file.
+ //
+ // We can only dump the file content after this 'loadFile' call, no matter this call crashes or file is not
+ // found, we can't save the file anyway, so we don't need to pay special care to the crash recovery. We will
+ // know something wrong with the loadFile call if we can't find the file in the record directory.
+ if ((res == SLANG_OK) && (*outBlob != nullptr) && ((*outBlob)->getBufferSize() != 0))
+ {
+ std::filesystem::path filePath = m_recordManager->getRecordFileDirectory();
+ filePath = filePath / path;
+
+ FileOutputStream fileStream(filePath.string().c_str());
+
+ fileStream.write((*outBlob)->getBufferPointer(), (*outBlob)->getBufferSize());
+ fileStream.flush();
+ }
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult FileSystemRecorder::getFileUniqueIdentity(
+ const char* path,
+ ISlangBlob** outUniqueIdentity)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s :\"%s\"\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->getFileUniqueIdentity(path, outUniqueIdentity);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult FileSystemRecorder::calcCombinedPath(
+ SlangPathType fromPathType,
+ const char* fromPath,
+ const char* path,
+ ISlangBlob** pathOut)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->calcCombinedPath(fromPathType, fromPath, path, pathOut);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult FileSystemRecorder::getPathType(
+ const char* path,
+ SlangPathType* pathTypeOut)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->getPathType(path, pathTypeOut);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult FileSystemRecorder::getPath(
+ PathKind kind,
+ const char* path,
+ ISlangBlob** outPath)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->getPath(kind, path, outPath);
+ return res;
+ }
+
+ SLANG_NO_THROW void FileSystemRecorder::clearCache()
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__);
+ m_actualFileSystem->clearCache();
+ }
+
+ SLANG_NO_THROW SlangResult FileSystemRecorder::enumeratePathContents(
+ const char* path,
+ FileSystemContentsCallBack callback,
+ void* userData)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path);
+ SlangResult res = m_actualFileSystem->enumeratePathContents(path, callback, userData);
+ return res;
+ }
+
+ SLANG_NO_THROW OSPathKind FileSystemRecorder::getOSPathKind()
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__);
+ OSPathKind pathKind = m_actualFileSystem->getOSPathKind();
+ return pathKind;
+ }
+}
diff --git a/source/slang-record-replay/record/slang-filesystem.h b/source/slang-record-replay/record/slang-filesystem.h
new file mode 100644
index 000000000..ff7004fa1
--- /dev/null
+++ b/source/slang-record-replay/record/slang-filesystem.h
@@ -0,0 +1,71 @@
+#ifndef SLANG_FILE_SYSTEM_H
+#define SLANG_FILE_SYSTEM_H
+
+#include "slang-com-helper.h"
+#include "slang-com-ptr.h"
+#include "../../core/slang-com-object.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+
+ using namespace Slang;
+
+ // slang always requires ISlangFileSystemExt interface, even if user only provides ISlangFileSystem,
+ // slang will still wrap it with ISlangFileSystemExt. So we have to record ISlangFileSystemExt, even
+ // though we only need to record loadFile() function.
+ class FileSystemRecorder : public RefObject, public ISlangFileSystemExt
+ {
+ public:
+ explicit FileSystemRecorder(ISlangFileSystemExt* fileSystem, RecordManager* recordManager);
+ ~FileSystemRecorder();
+
+ // ISlangUnknown
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ ISlangUnknown* getInterface(const Slang::Guid& guid);
+
+ // ISlangCastable
+ virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Slang::Guid& guid) override;
+
+ // ISlangFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(
+ char const* path,
+ ISlangBlob** outBlob) override;
+
+ // ISlangFileSystemExt
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(
+ const char* path,
+ ISlangBlob** outUniqueIdentity) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath(
+ SlangPathType fromPathType,
+ const char* fromPath,
+ const char* path,
+ ISlangBlob** pathOut) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType(
+ const char* path,
+ SlangPathType* pathTypeOut) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPath(
+ PathKind kind,
+ const char* path,
+ ISlangBlob** outPath) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL clearCache() override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents(
+ const char* path,
+ FileSystemContentsCallBack callback,
+ void* userData) override;
+
+ virtual SLANG_NO_THROW OSPathKind SLANG_MCALL getOSPathKind() override;
+ private:
+ Slang::ComPtr<ISlangFileSystemExt> m_actualFileSystem;
+ RecordManager* m_recordManager = nullptr;
+};
+
+}
+#endif
+
diff --git a/source/slang-record-replay/record/slang-global-session.cpp b/source/slang-record-replay/record/slang-global-session.cpp
new file mode 100644
index 000000000..78fa8aaa2
--- /dev/null
+++ b/source/slang-record-replay/record/slang-global-session.cpp
@@ -0,0 +1,452 @@
+#include "slang-global-session.h"
+#include "slang-session.h"
+#include "slang-filesystem.h"
+#include "../../slang/slang-compiler.h"
+#include "../util/record-utility.h"
+
+namespace SlangRecord
+{
+ // constructor is called in slang_createGlobalSession
+ GlobalSessionRecorder::GlobalSessionRecorder(slang::IGlobalSession* session):
+ m_actualGlobalSession(session)
+ {
+ SLANG_RECORD_ASSERT(m_actualGlobalSession != nullptr);
+
+ m_globalSessionHandle = reinterpret_cast<SlangRecord::AddressFormat>(m_actualGlobalSession.get());
+ m_recordManager = std::make_unique<RecordManager>(m_globalSessionHandle);
+
+ // We will use the address of the global session as the filename for the record manager
+ // to make it unique for each global session.
+ // record slang::createGlobalSession
+ ParameterRecorder* recorder = m_recordManager->beginMethodRecord(ApiCallId::CreateGlobalSession, g_globalFunctionHandle);
+ recorder->recordAddress(m_actualGlobalSession);
+ m_recordManager->endMethodRecord();
+ }
+
+ GlobalSessionRecorder::~GlobalSessionRecorder()
+ {
+ m_actualGlobalSession->release();
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::queryInterface(SlangUUID const& uuid, void** outObject)
+ {
+ if (uuid == Session::getTypeGuid())
+ {
+ // no add-ref here, the query will cause the inner session to handle the add-ref.
+ this->m_actualGlobalSession->queryInterface(uuid, outObject);
+ return SLANG_OK;
+ }
+
+ if (uuid == ISlangUnknown::getTypeGuid() && uuid == IGlobalSession::getTypeGuid())
+ {
+ addReference();
+ *outObject = static_cast<slang::IGlobalSession*>(this);
+ return SLANG_OK;
+ }
+
+ return SLANG_E_NO_INTERFACE;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::createSession(slang::SessionDesc const& desc, slang::ISession** outSession)
+ {
+ setLogLevel();
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ slang::ISession* actualSession = nullptr;
+
+ ParameterRecorder* recorder{};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_createSession, m_globalSessionHandle);
+ recorder->recordStruct(desc);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->createSession(desc, &actualSession);
+
+ { // record output
+ recorder->recordAddress(actualSession);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (actualSession != nullptr)
+ {
+ // reset the file system to our record file system. After createSession() call,
+ // the Linkage will set to user provided file system or slang default file system.
+ // We need to reset it to our record file system
+ Slang::Linkage* linkage = static_cast<Linkage*>(actualSession);
+ FileSystemRecorder* fileSystemRecord = new FileSystemRecorder(linkage->getFileSystemExt(), m_recordManager.get());
+
+ Slang::ComPtr<FileSystemRecorder> resultFileSystemRecorder(fileSystemRecord);
+ linkage->setFileSystem(resultFileSystemRecorder.detach());
+
+ SessionRecorder* sessionRecord = new SessionRecorder(actualSession, m_recordManager.get());
+ Slang::ComPtr<SessionRecorder> result(sessionRecord);
+ *outSession = result.detach();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangProfileID SLANG_MCALL GlobalSessionRecorder::findProfile(char const* name)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_findProfile, m_globalSessionHandle);
+ recorder->recordString(name);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangProfileID profileId = m_actualGlobalSession->findProfile(name);
+ return profileId;
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerPath, m_globalSessionHandle);
+ recorder->recordEnumValue(passThrough);
+ recorder->recordString(path);
+ m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->setDownstreamCompilerPath(passThrough, path);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerPrelude, m_globalSessionHandle);
+ recorder->recordEnumValue(inPassThrough);
+ recorder->recordString(prelude);
+ m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->setDownstreamCompilerPrelude(inPassThrough, prelude);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getDownstreamCompilerPrelude, m_globalSessionHandle);
+ recorder->recordEnumValue(inPassThrough);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->getDownstreamCompilerPrelude(inPassThrough, outPrelude);
+
+ {
+ recorder->recordAddress(*outPrelude);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW const char* SLANG_MCALL GlobalSessionRecorder::getBuildTagString()
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ const char* resStr = m_actualGlobalSession->getBuildTagString();
+ return resStr;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::setDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage, SlangPassThrough defaultCompiler)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDefaultDownstreamCompiler, m_globalSessionHandle);
+ recorder->recordEnumValue(sourceLanguage);
+ recorder->recordEnumValue(defaultCompiler);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->setDefaultDownstreamCompiler(sourceLanguage, defaultCompiler);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL GlobalSessionRecorder::getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getDefaultDownstreamCompiler, m_globalSessionHandle);
+ recorder->recordEnumValue(sourceLanguage);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangPassThrough passThrough = m_actualGlobalSession->getDefaultDownstreamCompiler(sourceLanguage);
+ return passThrough;
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setLanguagePrelude, m_globalSessionHandle);
+ recorder->recordEnumValue(inSourceLanguage);
+ recorder->recordString(prelude);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->setLanguagePrelude(inSourceLanguage, prelude);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getLanguagePrelude, m_globalSessionHandle);
+ recorder->recordEnumValue(inSourceLanguage);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->getLanguagePrelude(inSourceLanguage, outPrelude);
+
+ {
+ recorder->recordAddress(*outPrelude);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::createCompileRequest(slang::ICompileRequest** outCompileRequest)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_createCompileRequest, m_globalSessionHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->createCompileRequest(outCompileRequest);
+
+ {
+ recorder->recordAddress(*outCompileRequest);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::addBuiltins(char const* sourcePath, char const* sourceString)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_addBuiltins, m_globalSessionHandle);
+ recorder->recordString(sourcePath);
+ recorder->recordString(sourceString);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->addBuiltins(sourcePath, sourceString);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setSharedLibraryLoader(ISlangSharedLibraryLoader* loader)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ // TODO: Not sure if we need to record this function. Because this functions is something like the file system
+ // override, it's provided by user code. So capturing it makes no sense. The only way is to wrapper this interface
+ // by our own implementation, and record it there.
+ m_actualGlobalSession->setSharedLibraryLoader(loader);
+ }
+
+ SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL GlobalSessionRecorder::getSharedLibraryLoader()
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getSharedLibraryLoader, m_globalSessionHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ ISlangSharedLibraryLoader* loader = m_actualGlobalSession->getSharedLibraryLoader();
+
+ {
+ recorder->recordAddress(loader);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ return loader;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::checkCompileTargetSupport(SlangCompileTarget target)
+ {
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ SlangResult res = m_actualGlobalSession->checkCompileTargetSupport(target);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::checkPassThroughSupport(SlangPassThrough passThrough)
+ {
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ SlangResult res = m_actualGlobalSession->checkPassThroughSupport(passThrough);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::compileStdLib(slang::CompileStdLibFlags flags)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_compileStdLib, m_globalSessionHandle);
+ recorder->recordEnumValue(flags);
+ m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->compileStdLib(flags);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::loadStdLib(const void* stdLib, size_t stdLibSizeInBytes)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_loadStdLib, m_globalSessionHandle);
+ recorder->recordPointer(stdLib, false, stdLibSizeInBytes);
+ m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->loadStdLib(stdLib, stdLibSizeInBytes);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::saveStdLib(SlangArchiveType archiveType, ISlangBlob** outBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_saveStdLib, m_globalSessionHandle);
+ recorder->recordEnumValue(archiveType);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->saveStdLib(archiveType, outBlob);
+
+ {
+ recorder->recordAddress(*outBlob);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ return res;
+ }
+
+ SLANG_NO_THROW SlangCapabilityID SLANG_MCALL GlobalSessionRecorder::findCapability(char const* name)
+ {
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ SlangCapabilityID capId = m_actualGlobalSession->findCapability(name);
+ return capId;
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target, SlangPassThrough compiler)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerForTransition, m_globalSessionHandle);
+ recorder->recordEnumValue(source);
+ recorder->recordEnumValue(target);
+ recorder->recordEnumValue(compiler);
+ m_recordManager->endMethodRecord();
+ }
+
+ m_actualGlobalSession->setDownstreamCompilerForTransition(source, target, compiler);
+ }
+
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL GlobalSessionRecorder::getDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target)
+ {
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ SlangPassThrough passThrough = m_actualGlobalSession->getDownstreamCompilerForTransition(source, target);
+ return passThrough;
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime)
+ {
+ // No need to record this function. It's just a query function and it won't impact the internal state.
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+ m_actualGlobalSession->getCompilerElapsedTime(outTotalTime, outDownstreamTime);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::setSPIRVCoreGrammar(char const* jsonPath)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setSPIRVCoreGrammar, m_globalSessionHandle);
+ recorder->recordString(jsonPath);
+ m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->setSPIRVCoreGrammar(jsonPath);
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::parseCommandLineArguments(
+ int argc, const char* const* argv, slang::SessionDesc* outSessionDesc, ISlangUnknown** outAllocation)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_parseCommandLineArguments, m_globalSessionHandle);
+ recorder->recordInt32(argc);
+ recorder->recordStringArray(argv, argc);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->parseCommandLineArguments(argc, argv, outSessionDesc, outAllocation);
+
+ {
+ recorder->recordAddress(outSessionDesc);
+ recorder->recordAddress(*outAllocation);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getSessionDescDigest, m_globalSessionHandle);
+ recorder->recordStruct(*sessionDesc);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualGlobalSession->getSessionDescDigest(sessionDesc, outBlob);
+
+ {
+ recorder->recordAddress(*outBlob);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ return res;
+ }
+}
diff --git a/source/slang-record-replay/record/slang-global-session.h b/source/slang-record-replay/record/slang-global-session.h
new file mode 100644
index 000000000..3fd584ef4
--- /dev/null
+++ b/source/slang-record-replay/record/slang-global-session.h
@@ -0,0 +1,85 @@
+#ifndef SLANG_GLOBAL_SESSION_H
+#define SLANG_GLOBAL_SESSION_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+
+ class GlobalSessionRecorder : public RefObject, public slang::IGlobalSession
+ {
+ public:
+ explicit GlobalSessionRecorder(slang::IGlobalSession* session);
+ virtual ~GlobalSessionRecorder();
+
+ SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
+ SLANG_REF_OBJECT_IUNKNOWN_RELEASE
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE;
+
+ // slang::IGlobalSession
+ SLANG_NO_THROW SlangResult SLANG_MCALL createSession(slang::SessionDesc const& desc, slang::ISession** outSession) override;
+ SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile(char const* name) override;
+ SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) override;
+ SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) override;
+ SLANG_NO_THROW void SLANG_MCALL getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) override;
+ SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL setDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage, SlangPassThrough defaultCompiler) override;
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) override;
+
+ SLANG_NO_THROW void SLANG_MCALL setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) override;
+ SLANG_NO_THROW void SLANG_MCALL getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest(slang::ICompileRequest** outCompileRequest) override;
+
+ SLANG_NO_THROW void SLANG_MCALL addBuiltins(char const* sourcePath, char const* sourceString) override;
+ SLANG_NO_THROW void SLANG_MCALL setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) override;
+ SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL getSharedLibraryLoader() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL checkCompileTargetSupport(SlangCompileTarget target) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL checkPassThroughSupport(SlangPassThrough passThrough) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL compileStdLib(slang::CompileStdLibFlags flags) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL loadStdLib(const void* stdLib, size_t stdLibSizeInBytes) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL saveStdLib(SlangArchiveType archiveType, ISlangBlob** outBlob) override;
+
+ SLANG_NO_THROW SlangCapabilityID SLANG_MCALL findCapability(char const* name) override;
+
+ SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target, SlangPassThrough compiler) override;
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target) override;
+ SLANG_NO_THROW void SLANG_MCALL getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL setSPIRVCoreGrammar(char const* jsonPath) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL parseCommandLineArguments(
+ int argc, const char* const* argv, slang::SessionDesc* outSessionDesc, ISlangUnknown** outAllocation) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) override;
+
+ RecordManager* getRecordManager() { return m_recordManager.get(); }
+
+ private:
+ SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(GlobalSessionRecorder* session)
+ {
+ return static_cast<slang::IGlobalSession*>(session);
+ }
+
+ Slang::ComPtr<slang::IGlobalSession> m_actualGlobalSession;
+
+ // we will create one record file per IGlobalSession.
+ // We don't try to reproduce the user application's threading model, because it requires lots of effort and it's not necessary.
+ // Instead, we record all the compilation jobs associated with the session in the same record file, so that during replay,
+ // those jobs will be executed sequentially. This might violate the user application's threading model, because those jobs might
+ // be executed in different threads. But it's not a big problem, because slang doesn't allow multiple threads to access the same
+ // session at the same time. So even if there is one session used by multiple threads, those threads will execute the compile jobs
+ // sequentially.
+ std::unique_ptr<RecordManager> m_recordManager;
+ uint64_t m_globalSessionHandle = 0;
+ };
+} // namespace Slang
+
+#endif
diff --git a/source/slang-record-replay/record/slang-module.cpp b/source/slang-record-replay/record/slang-module.cpp
new file mode 100644
index 000000000..1457e7cf1
--- /dev/null
+++ b/source/slang-record-replay/record/slang-module.cpp
@@ -0,0 +1,496 @@
+#include "../util/record-utility.h"
+#include "slang-module.h"
+
+namespace SlangRecord
+{
+ ModuleRecorder::ModuleRecorder(slang::IModule* module, RecordManager* recordManager)
+ : m_actualModule(module),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualModule != nullptr);
+ SLANG_RECORD_ASSERT(m_recordManager != nullptr);
+
+ m_moduleHandle = reinterpret_cast<uint64_t>(m_actualModule.get());
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, module);
+ }
+
+ ModuleRecorder::~ModuleRecorder()
+ {
+ m_actualModule->release();
+ }
+
+ ISlangUnknown* ModuleRecorder::getInterface(const Guid& guid)
+ {
+ if(guid == ModuleRecorder::getTypeGuid())
+ return static_cast<ISlangUnknown*>(this);
+ else
+ return nullptr;
+ }
+
+ SLANG_NO_THROW slang::DeclReflection* ModuleRecorder::getModuleReflection()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ slang::DeclReflection* res = (slang::DeclReflection*)m_actualModule->getModuleReflection();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::findEntryPointByName(
+ char const* name,
+ slang::IEntryPoint** outEntryPoint)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_findEntryPointByName, m_moduleHandle);
+ recorder->recordString(name);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->findEntryPointByName(name, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (SLANG_OK == res)
+ {
+ EntryPointRecorder* entryPointRecord = getEntryPointRecorder(*outEntryPoint);
+ *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord);
+ }
+ return res;
+ }
+
+ SLANG_NO_THROW SlangInt32 ModuleRecorder::getDefinedEntryPointCount()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt32 res = m_actualModule->getDefinedEntryPointCount();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint)
+ {
+ // This call is to find the existing entry point, so it has been created already. Therefore, we don't create a new one
+ // and assert the error if it is not found in our map.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getDefinedEntryPoint, m_moduleHandle);
+ recorder->recordInt32(index);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->getDefinedEntryPoint(index, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (*outEntryPoint)
+ {
+ EntryPointRecorder* entryPointRecord = m_mapEntryPointToRecord.tryGetValue(*outEntryPoint);
+ if (!entryPointRecord)
+ {
+ SLANG_RECORD_ASSERT(!"Entrypoint not found in mapEntryPointToRecord");
+ }
+ *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord);
+ }
+ else
+ *outEntryPoint = nullptr;
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::serialize(ISlangBlob** outSerializedBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_serialize, m_moduleHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->serialize(outSerializedBlob);
+
+ {
+ recorder->recordAddress(*outSerializedBlob);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::writeToFile(char const* fileName)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_writeToFile, m_moduleHandle);
+ recorder->recordString(fileName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->writeToFile(fileName);
+ return res;
+ }
+
+ SLANG_NO_THROW const char* ModuleRecorder::getName()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ const char* res = m_actualModule->getName();
+ return res;
+ }
+
+ SLANG_NO_THROW const char* ModuleRecorder::getFilePath()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ const char* res = m_actualModule->getFilePath();
+ return res;
+ }
+
+ SLANG_NO_THROW const char* ModuleRecorder::getUniqueIdentity()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ const char* res = m_actualModule->getUniqueIdentity();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::findAndCheckEntryPoint(
+ char const* name,
+ SlangStage stage,
+ slang::IEntryPoint** outEntryPoint,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_findAndCheckEntryPoint, m_moduleHandle);
+ recorder->recordString(name);
+ recorder->recordEnumValue(stage);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->findAndCheckEntryPoint(name, stage, outEntryPoint, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (SLANG_OK == res)
+ {
+ EntryPointRecorder* entryPointRecord = getEntryPointRecorder(*outEntryPoint);
+ *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord);
+ }
+ return res;
+ }
+
+ SLANG_NO_THROW SlangInt32 ModuleRecorder::getDependencyFileCount()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt32 res = m_actualModule->getDependencyFileCount();
+ return res;
+ }
+
+ SLANG_NO_THROW char const* ModuleRecorder::getDependencyFilePath(SlangInt32 index)
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ const char* res = m_actualModule->getDependencyFilePath(index);
+ return res;
+ }
+
+ SLANG_NO_THROW slang::ISession* ModuleRecorder::getSession()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ slang::ISession* session = m_actualModule->getSession();
+
+ return session;
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* ModuleRecorder::getLayout(
+ SlangInt targetIndex,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getLayout, m_moduleHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ProgramLayout* programLayout = m_actualModule->getLayout(targetIndex, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(programLayout);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return programLayout;
+ }
+
+ SLANG_NO_THROW SlangInt ModuleRecorder::getSpecializationParamCount()
+ {
+ // No need to record this call as it is just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt res = m_actualModule->getSpecializationParamCount();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointCode, m_moduleHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getTargetCode, m_moduleHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->getTargetCode(targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getResultAsFileSystem, m_moduleHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+
+ {
+ recorder->recordAddress(*outFileSystem);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ // TODO: We might need to wrap the file system object.
+ return res;
+ }
+
+ SLANG_NO_THROW void ModuleRecorder::getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointHash, m_moduleHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualModule->getEntryPointHash(entryPointIndex, targetIndex, outHash);
+
+ {
+ recorder->recordAddress(*outHash);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_specialize, m_moduleHandle);
+ recorder->recordInt64(specializationArgCount);
+ recorder->recordStructArray(specializationArgs, specializationArgCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSpecializedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::link(
+ IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_link, m_moduleHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->link(outLinkedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointHostCallable, m_moduleHandle);
+ recorder->recordInt32(entryPointIndex);
+ recorder->recordInt32(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSharedLibrary);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_renameEntryPoint, m_moduleHandle);
+ recorder->recordString(newName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->renameEntryPoint(newName, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult ModuleRecorder::linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_linkWithOptions, m_moduleHandle);
+ recorder->recordUint32(compilerOptionEntryCount);
+ recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualModule->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ EntryPointRecorder* ModuleRecorder::getEntryPointRecorder(slang::IEntryPoint* entryPoint)
+ {
+ EntryPointRecorder* entryPointRecord = nullptr;
+ entryPointRecord = m_mapEntryPointToRecord.tryGetValue(entryPoint);
+ if (!entryPointRecord)
+ {
+ entryPointRecord = new EntryPointRecorder(entryPoint, m_recordManager);
+ Slang::ComPtr<EntryPointRecorder> result(entryPointRecord);
+ m_mapEntryPointToRecord.add(entryPoint, *result.detach());
+ }
+ return entryPointRecord;
+ }
+}
diff --git a/source/slang-record-replay/record/slang-module.h b/source/slang-record-replay/record/slang-module.h
new file mode 100644
index 000000000..ca0403c2d
--- /dev/null
+++ b/source/slang-record-replay/record/slang-module.h
@@ -0,0 +1,102 @@
+#ifndef SLANG_MODULE_H
+#define SLANG_MODULE_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "../../slang/slang-compiler.h"
+#include "slang-entrypoint.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+ class ModuleRecorder : public slang::IModule, public RefObject
+ {
+ public:
+ SLANG_COM_INTERFACE(0xb1802991, 0x185a, 0x4a03, { 0xa7, 0x7e, 0x0c, 0x86, 0xe0, 0x68, 0x2a, 0xab })
+
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ explicit ModuleRecorder(slang::IModule* module, RecordManager* recordManager);
+ ~ModuleRecorder();
+
+ // Interfaces for `IModule`
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL findEntryPointByName(
+ char const* name, slang::IEntryPoint** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDefinedEntryPointCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL serialize(ISlangBlob** outSerializedBlob) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) override;
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getName() override;
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() override;
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL findAndCheckEntryPoint(
+ char const* name, SlangStage stage, slang::IEntryPoint** outEntryPoint, ISlangBlob** outDiagnostics) override;
+ virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override;
+ virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(
+ SlangInt32 index) override;
+
+ // Interfaces for `IComponentType`
+ virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override;
+ virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
+ SlangInt targetIndex = 0,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics = 0) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW slang::DeclReflection* getModuleReflection() override;
+
+ slang::IModule* getActualModule() const { return m_actualModule; }
+ private:
+ EntryPointRecorder* getEntryPointRecorder(slang::IEntryPoint* entryPoint);
+ Slang::ComPtr<slang::IModule> m_actualModule;
+ uint64_t m_moduleHandle = 0;
+ RecordManager* m_recordManager = nullptr;
+
+ // `IEntryPoint` can only be created from 'IModule', so we need to record it in
+ // this class, and create a map such that we don't create new `EntryPointRecorder`
+ // for the same `IEntryPoint`.
+ Dictionary<slang::IEntryPoint*, EntryPointRecorder> m_mapEntryPointToRecord;
+ };
+} // namespace SlangRecord
+
+#endif // SLANG_MODULE_H
diff --git a/source/slang-record-replay/record/slang-session.cpp b/source/slang-record-replay/record/slang-session.cpp
new file mode 100644
index 000000000..82c7a7479
--- /dev/null
+++ b/source/slang-record-replay/record/slang-session.cpp
@@ -0,0 +1,517 @@
+#include "../util/record-utility.h"
+#include "slang-session.h"
+#include "slang-entrypoint.h"
+#include "slang-composite-component-type.h"
+#include "slang-type-conformance.h"
+
+namespace SlangRecord
+{
+
+ SessionRecorder::SessionRecorder(slang::ISession* session, RecordManager* recordManager)
+ : m_actualSession(session),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualSession);
+ SLANG_RECORD_ASSERT(m_recordManager);
+ m_sessionHandle = reinterpret_cast<uint64_t>(m_actualSession.get());
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", "SessionRecorder create:", session);
+ }
+
+ SessionRecorder::~SessionRecorder()
+ {
+ m_actualSession->release();
+ }
+
+ ISlangUnknown* SessionRecorder::getInterface(const Guid& guid)
+ {
+ if(guid == ISlangUnknown::getTypeGuid() || guid == ISession::getTypeGuid())
+ return asExternal(this);
+
+ return nullptr;
+ }
+
+ SLANG_NO_THROW slang::IGlobalSession* SessionRecorder::getGlobalSession()
+ {
+ // No need to record this function.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ slang::IGlobalSession* pGlobalSession = m_actualSession->getGlobalSession();
+ return pGlobalSession;
+ }
+
+ SLANG_NO_THROW slang::IModule* SessionRecorder::loadModule(
+ const char* moduleName,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModule, m_sessionHandle);
+ recorder->recordString(moduleName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::IModule* pModule = m_actualSession->loadModule(moduleName, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pModule);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule);
+ return static_cast<slang::IModule*>(pModuleRecorder);
+ }
+
+ SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromIRBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromIRBlob, m_sessionHandle);
+ recorder->recordString(moduleName);
+ recorder->recordString(path);
+ recorder->recordPointer(source);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::IModule* pModule = m_actualSession->loadModuleFromIRBlob(moduleName, path, source, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pModule);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule);
+ return static_cast<slang::IModule*>(pModuleRecorder);
+ }
+
+ SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromSource(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromSource, m_sessionHandle);
+ recorder->recordString(moduleName);
+ recorder->recordString(path);
+ recorder->recordPointer(source);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::IModule* pModule = m_actualSession->loadModuleFromSource(moduleName, path, source, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pModule);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule);
+ return static_cast<slang::IModule*>(pModuleRecorder);
+ }
+
+ SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromSourceString(
+ const char* moduleName,
+ const char* path,
+ const char* string,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromSourceString, m_sessionHandle);
+ recorder->recordString(moduleName);
+ recorder->recordString(path);
+ recorder->recordString(string);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::IModule* pModule = m_actualSession->loadModuleFromSourceString(moduleName, path, string, outDiagnostics);
+
+ {
+ // TODO: Not sure if we need to record the diagnostics blob.
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pModule);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule);
+ return static_cast<slang::IModule*>(pModuleRecorder);
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::createCompositeComponentType(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ slang::IComponentType** outCompositeComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ Slang::List<slang::IComponentType*> componentTypeList;
+
+ // get the actual component types from our record wrappers
+ if(SLANG_OK != getActualComponentTypes(componentTypes, componentTypeCount, componentTypeList))
+ {
+ SLANG_RECORD_ASSERT(!"Failed to get actual component types");
+ }
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createCompositeComponentType, m_sessionHandle);
+ recorder->recordAddressArray(componentTypeList.getBuffer(), componentTypeCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->createCompositeComponentType(
+ componentTypeList.getBuffer(), componentTypeCount, outCompositeComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCompositeComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (SLANG_OK == result)
+ {
+ CompositeComponentTypeRecorder* compositeComponentTypeRecord =
+ new CompositeComponentTypeRecorder(*outCompositeComponentType, m_recordManager);
+ Slang::ComPtr<CompositeComponentTypeRecorder> resultRecord(compositeComponentTypeRecord);
+ *outCompositeComponentType = resultRecord.detach();
+ }
+
+ return result;
+ }
+
+ SLANG_NO_THROW slang::TypeReflection* SessionRecorder::specializeType(
+ slang::TypeReflection* type,
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_specializeType, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder->recordStructArray(specializationArgs, specializationArgCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::TypeReflection* pTypeReflection = m_actualSession->specializeType(type, specializationArgs, specializationArgCount, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pTypeReflection);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return pTypeReflection;
+ }
+
+ SLANG_NO_THROW slang::TypeLayoutReflection* SessionRecorder::getTypeLayout(
+ slang::TypeReflection* type,
+ SlangInt targetIndex,
+ slang::LayoutRules rules,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeLayout, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder->recordInt64(targetIndex);
+ recorder->recordEnumValue(rules);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::TypeLayoutReflection* pTypeLayoutReflection = m_actualSession->getTypeLayout(type, targetIndex, rules, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pTypeLayoutReflection);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return pTypeLayoutReflection;
+ }
+
+ SLANG_NO_THROW slang::TypeReflection* SessionRecorder::getContainerType(
+ slang::TypeReflection* elementType,
+ slang::ContainerType containerType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getContainerType, m_sessionHandle);
+ recorder->recordAddress(elementType);
+ recorder->recordEnumValue(containerType);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::TypeReflection* pTypeReflection = m_actualSession->getContainerType(elementType, containerType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(pTypeReflection);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return pTypeReflection;
+ }
+
+ SLANG_NO_THROW slang::TypeReflection* SessionRecorder::getDynamicType()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getDynamicType, m_sessionHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::TypeReflection* pTypeReflection = m_actualSession->getDynamicType();
+
+ {
+ recorder->recordAddress(pTypeReflection);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return pTypeReflection;
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::getTypeRTTIMangledName(
+ slang::TypeReflection* type,
+ ISlangBlob** outNameBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeRTTIMangledName, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->getTypeRTTIMangledName(type, outNameBlob);
+
+ {
+ recorder->recordAddress(outNameBlob);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return result;
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::getTypeConformanceWitnessMangledName(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ ISlangBlob** outNameBlob)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeConformanceWitnessMangledName, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder->recordAddress(interfaceType);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->getTypeConformanceWitnessMangledName(type, interfaceType, outNameBlob);
+
+ {
+ recorder->recordAddress(outNameBlob);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return result;
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::getTypeConformanceWitnessSequentialID(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outId)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeConformanceWitnessSequentialID, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder->recordAddress(interfaceType);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->getTypeConformanceWitnessSequentialID(type, interfaceType, outId);
+
+ // No need to record outId, it's not slang allocation
+ return result;
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::createTypeConformanceComponentType(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ slang::ITypeConformance** outConformance,
+ SlangInt conformanceIdOverride,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createTypeConformanceComponentType, m_sessionHandle);
+ recorder->recordAddress(type);
+ recorder->recordAddress(interfaceType);
+ recorder->recordInt64(conformanceIdOverride);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->createTypeConformanceComponentType(type, interfaceType, outConformance, conformanceIdOverride, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outConformance);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (SLANG_OK != result)
+ {
+ TypeConformanceRecorder* conformanceRecord = new TypeConformanceRecorder(*outConformance, m_recordManager);
+ Slang::ComPtr<TypeConformanceRecorder> resultRecord(conformanceRecord);
+ *outConformance = resultRecord.detach();
+ }
+
+ return result;
+ }
+
+ SLANG_NO_THROW SlangResult SessionRecorder::createCompileRequest(
+ SlangCompileRequest** outCompileRequest)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createCompileRequest, m_sessionHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult result = m_actualSession->createCompileRequest(outCompileRequest);
+
+ {
+ recorder->recordAddress(*outCompileRequest);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return result;
+ }
+
+ SLANG_NO_THROW SlangInt SessionRecorder::getLoadedModuleCount()
+ {
+ // No need to record this function, it's just a query.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt count = m_actualSession->getLoadedModuleCount();
+ return count;
+ }
+
+ SLANG_NO_THROW slang::IModule* SessionRecorder::getLoadedModule(SlangInt index)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getLoadedModule, m_sessionHandle);
+ recorder->recordInt64(index);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::IModule* pModule = m_actualSession->getLoadedModule(index);
+
+ {
+ recorder->recordAddress(pModule);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ if (pModule)
+ {
+ ModuleRecorder* moduleRecord = m_mapModuleToRecord.tryGetValue(pModule);
+ if (!moduleRecord)
+ {
+ SLANG_RECORD_ASSERT(!"Module not found in mapModuleToRecord");
+ }
+ return static_cast<slang::IModule*>(moduleRecord);
+ }
+
+ return pModule;
+ }
+
+ SLANG_NO_THROW bool SessionRecorder::isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob)
+ {
+ // No need to record this function, it's a query function and doesn't impact slang internal state.
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ bool result = m_actualSession->isBinaryModuleUpToDate(modulePath, binaryModuleBlob);
+ return result;
+ }
+
+ ModuleRecorder* SessionRecorder::getModuleRecorder(slang::IModule* module)
+ {
+ ModuleRecorder* moduleRecord = nullptr;
+ moduleRecord = m_mapModuleToRecord.tryGetValue(module);
+ if (!moduleRecord)
+ {
+ moduleRecord = new ModuleRecorder(module, m_recordManager);
+ Slang::ComPtr<ModuleRecorder> result(moduleRecord);
+ m_mapModuleToRecord.add(module, *result.detach());
+ }
+ return moduleRecord;
+ }
+
+ SlangResult SessionRecorder::getActualComponentTypes(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ List<slang::IComponentType*>& outActualComponentTypes)
+ {
+ for (SlangInt i = 0; i < componentTypeCount; i++)
+ {
+ slang::IComponentType* const& componentType = componentTypes[i];
+ void* outObj = nullptr;
+
+ if (componentType->queryInterface(ModuleRecorder::getTypeGuid(), &outObj) == SLANG_OK)
+ {
+ ModuleRecorder* moduleRecord = static_cast<ModuleRecorder*>(outObj);
+ outActualComponentTypes.add(moduleRecord->getActualModule());
+ }
+ else if (componentType->queryInterface(EntryPointRecorder::getTypeGuid(), &outObj) == SLANG_OK)
+ {
+ EntryPointRecorder* entrypointRecord = static_cast<EntryPointRecorder*>(outObj);
+ outActualComponentTypes.add(entrypointRecord->getActualEntryPoint());
+ }
+ // will fall back to the actual component type, it means that we didn't record this type.
+ else
+ {
+ outActualComponentTypes.add(componentType);
+ }
+ }
+
+ if (componentTypeCount == outActualComponentTypes.getCount())
+ {
+ return SLANG_OK;
+ }
+ return SLANG_FAIL;
+ }
+} // namespace SlangRecord
diff --git a/source/slang-record-replay/record/slang-session.h b/source/slang-record-replay/record/slang-session.h
new file mode 100644
index 000000000..ca06e25a0
--- /dev/null
+++ b/source/slang-record-replay/record/slang-session.h
@@ -0,0 +1,110 @@
+#ifndef SLANG_SESSION_H
+#define SLANG_SESSION_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "../../core/slang-dictionary.h"
+#include "../../slang/slang-compiler.h"
+#include "slang-module.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+ class SessionRecorder: public RefObject, public slang::ISession
+ {
+ public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ explicit SessionRecorder(slang::ISession* session, RecordManager* recordManager);
+ ~SessionRecorder();
+
+ SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule(
+ const char* moduleName,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromIRBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSourceString(
+ const char* moduleName,
+ const char* path,
+ const char* string,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL createCompositeComponentType(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ slang::IComponentType** outCompositeComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType(
+ slang::TypeReflection* type,
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout(
+ slang::TypeReflection* type,
+ SlangInt targetIndex = 0,
+ slang::LayoutRules rules = slang::LayoutRules::Default,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getContainerType(
+ slang::TypeReflection* elementType,
+ slang::ContainerType containerType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getDynamicType() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTypeRTTIMangledName(
+ slang::TypeReflection* type,
+ ISlangBlob** outNameBlob) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessMangledName(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ ISlangBlob** outNameBlob) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessSequentialID(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outId) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL createTypeConformanceComponentType(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ slang::ITypeConformance** outConformance,
+ SlangInt conformanceIdOverride,
+ ISlangBlob** outDiagnostics) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest(
+ SlangCompileRequest** outCompileRequest) override;
+ SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override;
+ SLANG_NO_THROW bool SLANG_MCALL isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) override;
+
+ private:
+ SLANG_FORCE_INLINE slang::ISession* asExternal(SessionRecorder* session)
+ {
+ return static_cast<slang::ISession*>(session);
+ }
+
+ // The IComponentType object is the record target, therefore `componentTypes` will not be
+ // the actual component types, we have to use the COM interface to get the actual objects.
+ SlangResult getActualComponentTypes(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ List<slang::IComponentType*>& outActualComponentTypes);
+
+ ModuleRecorder* getModuleRecorder(slang::IModule* module);
+
+ Slang::ComPtr<slang::ISession> m_actualSession;
+ uint64_t m_sessionHandle = 0;
+
+ Dictionary<slang::IModule*, ModuleRecorder> m_mapModuleToRecord;
+ RecordManager* m_recordManager = nullptr;
+ };
+}
+
+#endif // SLANG_SESSION_H
diff --git a/source/slang-record-replay/record/slang-type-conformance.cpp b/source/slang-record-replay/record/slang-type-conformance.cpp
new file mode 100644
index 000000000..b9b652e9e
--- /dev/null
+++ b/source/slang-record-replay/record/slang-type-conformance.cpp
@@ -0,0 +1,310 @@
+#include "../util/record-utility.h"
+#include "slang-type-conformance.h"
+
+namespace SlangRecord
+{
+ TypeConformanceRecorder::TypeConformanceRecorder(slang::ITypeConformance* typeConformance, RecordManager* recordManager)
+ : m_actualTypeConformance(typeConformance),
+ m_recordManager(recordManager)
+ {
+ SLANG_RECORD_ASSERT(m_actualTypeConformance != nullptr);
+ SLANG_RECORD_ASSERT(m_recordManager != nullptr);
+
+ m_typeConformanceHandle = reinterpret_cast<uint64_t>(m_actualTypeConformance.get());
+ slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, typeConformance);
+ }
+
+ TypeConformanceRecorder::~TypeConformanceRecorder()
+ {
+ m_actualTypeConformance->release();
+ }
+
+ ISlangUnknown* TypeConformanceRecorder::getInterface(const Guid& guid)
+ {
+ if (guid == TypeConformanceRecorder::getTypeGuid())
+ {
+ return static_cast<ISlangUnknown*>(this);
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ SLANG_NO_THROW slang::ISession* TypeConformanceRecorder::getSession()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getSession, m_typeConformanceHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ISession* res = m_actualTypeConformance->getSession();
+
+ {
+ recorder->recordAddress(res);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* TypeConformanceRecorder::getLayout(
+ SlangInt targetIndex,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getLayout, m_typeConformanceHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ slang::ProgramLayout* programLayout = m_actualTypeConformance->getLayout(targetIndex, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outDiagnostics);
+ recorder->recordAddress(programLayout);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return programLayout;
+ }
+
+ SLANG_NO_THROW SlangInt TypeConformanceRecorder::getSpecializationParamCount()
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+ SlangInt res = m_actualTypeConformance->getSpecializationParamCount();
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointCode, m_typeConformanceHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getTargetCode, m_typeConformanceHandle);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->getTargetCode(targetIndex, outCode, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outCode);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getResultAsFileSystem, m_typeConformanceHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+
+ {
+ recorder->recordAddress(*outFileSystem);
+ }
+
+ // TODO: We might need to wrap the file system object.
+ return res;
+ }
+
+ SLANG_NO_THROW void TypeConformanceRecorder::getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointHash, m_typeConformanceHandle);
+ recorder->recordInt64(entryPointIndex);
+ recorder->recordInt64(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ m_actualTypeConformance->getEntryPointHash(entryPointIndex, targetIndex, outHash);
+
+ {
+ recorder->recordAddress(*outHash);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_specialize, m_typeConformanceHandle);
+ recorder->recordInt64(specializationArgCount);
+ recorder->recordStructArray(specializationArgs, specializationArgCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSpecializedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_link, m_typeConformanceHandle);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->link(outLinkedComponentType, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointHostCallable, m_typeConformanceHandle);
+ recorder->recordInt32(entryPointIndex);
+ recorder->recordInt32(targetIndex);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outSharedLibrary);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_renameEntryPoint, m_typeConformanceHandle);
+ recorder->recordString(newName);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->renameEntryPoint(newName, outEntryPoint);
+
+ {
+ recorder->recordAddress(*outEntryPoint);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+
+ SLANG_NO_THROW SlangResult TypeConformanceRecorder::linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics)
+ {
+ slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
+
+ ParameterRecorder* recorder {};
+ {
+ recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_linkWithOptions, m_typeConformanceHandle);
+ recorder->recordUint32(compilerOptionEntryCount);
+ recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount);
+ recorder = m_recordManager->endMethodRecord();
+ }
+
+ SlangResult res = m_actualTypeConformance->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics);
+
+ {
+ recorder->recordAddress(*outLinkedComponentType);
+ recorder->recordAddress(*outDiagnostics);
+ m_recordManager->endMethodRecordAppendOutput();
+ }
+
+ return res;
+ }
+}
diff --git a/source/slang-record-replay/record/slang-type-conformance.h b/source/slang-record-replay/record/slang-type-conformance.h
new file mode 100644
index 000000000..7bcae0d15
--- /dev/null
+++ b/source/slang-record-replay/record/slang-type-conformance.h
@@ -0,0 +1,77 @@
+#ifndef SLANG_TYPE_CONFORMANCE_H
+#define SLANG_TYPE_CONFORMANCE_H
+
+#include "slang-com-ptr.h"
+#include "slang.h"
+#include "slang-com-helper.h"
+#include "../../core/slang-smart-pointer.h"
+#include "../../core/slang-dictionary.h"
+#include "../../slang/slang-compiler.h"
+#include "record-manager.h"
+
+namespace SlangRecord
+{
+ using namespace Slang;
+ class TypeConformanceRecorder: public slang::ITypeConformance, public RefObject
+ {
+ public:
+ SLANG_COM_INTERFACE(0x0e67d05d, 0xee0a, 0x41e1, { 0xb5, 0xa3, 0x23, 0xe3, 0xb0, 0xec, 0x33, 0xf1 })
+
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ explicit TypeConformanceRecorder(slang::ITypeConformance* typeConformance, RecordManager* recordManager);
+ ~TypeConformanceRecorder();
+
+ // Interfaces for `IComponentType`
+ virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override;
+ virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
+ SlangInt targetIndex = 0,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics = 0) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint(
+ const char* newName, IComponentType** outEntryPoint) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ IComponentType** outLinkedComponentType,
+ uint32_t compilerOptionEntryCount,
+ slang::CompilerOptionEntry* compilerOptionEntries,
+ ISlangBlob** outDiagnostics = nullptr) override;
+
+ slang::ITypeConformance* getActualTypeConformance() const { return m_actualTypeConformance; }
+ private:
+ Slang::ComPtr<slang::ITypeConformance> m_actualTypeConformance;
+ uint64_t m_typeConformanceHandle = 0;
+ RecordManager* m_recordManager = nullptr;
+ };
+}
+#endif // SLANG_TYPE_CONFORMANCE_H