summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-11-06 18:43:33 -0800
committerGitHub <noreply@github.com>2019-11-06 18:43:33 -0800
commitfedda2e5342d3bfbdbbdd3ca232b3f69fff81ef7 (patch)
treee6edb884d601c5f71ac70370904b2b04274a518f /source/slang
parent7a877c380c3c84d03326e6fb982f85547d0bd338 (diff)
Add basic support for entry points in `.slang-lib` files. (#1112)
* Add basic support for entry points in `.slang-lib` files. The basic idea here is that when writing out a `.slang-lib` file based on a compile request, we include new sections in the generated RIFF that represent the entry points that were requested. The entry-point information is serialized in an entirely ad hoc fashion (a future change might clean it up to use the `OffsetContainer` machinery), and contains the name, profile, and mangled symbol name of an entry point. When deserializing this information, we create a list of "extra" entry points that gets attached to the front-end compile requests. These "extra" entry points get turned into `EntryPoint` objects at the same place in the code that entry points specified on the command line or via API would be checked, but the extra entry points bypass the semantic checking and just create "dummy" `EntryPoint` objects. Aside: the ability for a compile request to end up with entry points that weren't originally specified via API or command-line is not new. We already had support for compiling a translation unit with entry points entirely specified via `[shader(...)]` attributes, and this new support tries to function similarly. Because the "dummy" entry points don't retain AST-level information, several parts of the code have been modified to defensively check for `EntryPoint` objects without a matching AST declaration, and skip over them. The main place where this creates a problem is paramete binding, where ignoring the dummy entry point is appropriate since we currently assume linked-in library code has been laid out manually. One small cleanup here is that the `-r` command-line flag and the `spAddLibraryReference` API functio now bottleneck through a common routine to do their work, so that they both gain the new behavior without needing copy-paste programming. In order to keep the existing test case for library linking with entry points working, I had to add a flag to the `render-test` tool so that it can skip specifying entry point names as part of the compile request it creates. In that case it must instead assume that the entry points will be added to the compile request via other means. This logic is a bit magical, and hints that we should be looking for other ways to expose the library linking functionality over time. * fixup: remove alignment assertion
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/slang-check-shader.cpp47
-rw-r--r--source/slang/slang-compiler.cpp50
-rw-r--r--source/slang/slang-compiler.h21
-rw-r--r--source/slang/slang-ir-serialize.cpp32
-rw-r--r--source/slang/slang-ir-serialize.h4
-rw-r--r--source/slang/slang-lower-to-ir.cpp6
-rw-r--r--source/slang/slang-options.cpp14
-rw-r--r--source/slang/slang-parameter-binding.cpp39
-rw-r--r--source/slang/slang.cpp39
9 files changed, 204 insertions, 48 deletions
diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp
index 4917cc067..2ac449e83 100644
--- a/source/slang/slang-check-shader.cpp
+++ b/source/slang/slang-check-shader.cpp
@@ -1382,6 +1382,19 @@ static bool doesParameterMatch(
}
}
+ // Also consider entry points that were introduced via adding
+ // a library reference...
+ //
+ for( auto extraEntryPoint : compileRequest->m_extraEntryPoints )
+ {
+ auto entryPoint = EntryPoint::createDummyForDeserialize(
+ linkage,
+ extraEntryPoint.name,
+ extraEntryPoint.profile,
+ extraEntryPoint.mangledName);
+ allComponentTypes.add(entryPoint);
+ }
+
if(allComponentTypes.getCount() > 1)
{
auto composite = CompositeComponentType::create(
@@ -1998,9 +2011,29 @@ static bool doesParameterMatch(
allComponentTypes.add(specializedGlobalComponentType);
auto unspecializedGlobalAndEntryPointsComponentType = endToEndReq->getUnspecializedGlobalAndEntryPointsComponentType();
- auto entryPointCount = unspecializedGlobalAndEntryPointsComponentType->getEntryPointCount();
- for(Index ii = 0; ii < entryPointCount; ++ii)
+ // It is possible that there were entry points other than those specified
+ // vai the original end-to-end compile request. In particular:
+ //
+ // * It is possible to compile with *no* entry points specified, in which
+ // case the current compiler behavior is to use any entry points marked
+ // via `[shader(...)]` attributes in the AST.
+ //
+ // * It is possible for entry points to come into play via serialized libraries
+ // loaded with `-r` on the command line (or the equivalent API).
+ //
+ // We will thus draw a distinction between the "specified" entry points,
+ // and the "found" entry points.
+ //
+ auto specifiedEntryPointCount = endToEndReq->entryPoints.getCount();
+ auto foundEntryPointCount = unspecializedGlobalAndEntryPointsComponentType->getEntryPointCount();
+
+ SLANG_ASSERT(foundEntryPointCount >= specifiedEntryPointCount);
+
+ // For any entry points that were specified, we can use the specialization
+ // argument information provided via API or command line.
+ //
+ for(Index ii = 0; ii < specifiedEntryPointCount; ++ii)
{
auto& entryPointInfo = endToEndReq->entryPoints[ii];
auto unspecializedEntryPoint = unspecializedGlobalAndEntryPointsComponentType->getEntryPoint(ii);
@@ -2011,6 +2044,16 @@ static bool doesParameterMatch(
outSpecializedEntryPoints.add(specializedEntryPoint);
}
+ // Any entry points beyond those that were specified up front will be
+ // assumed to not need/want specialization.
+ //
+ for( Index ii = specifiedEntryPointCount; ii < foundEntryPointCount; ++ii )
+ {
+ auto unspecializedEntryPoint = unspecializedGlobalAndEntryPointsComponentType->getEntryPoint(ii);
+ allComponentTypes.add(unspecializedEntryPoint);
+ outSpecializedEntryPoints.add(unspecializedEntryPoint);
+ }
+
RefPtr<ComponentType> composed = CompositeComponentType::create(endToEndReq->getLinkage(), allComponentTypes);
return composed;
}
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 74075bb51..ba2ad1dd8 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -250,6 +250,7 @@ namespace Slang
funcDeclRef.GetName(),
profile,
funcDeclRef);
+ entryPoint->m_mangledName = getMangledName(funcDeclRef);
return entryPoint;
}
@@ -266,6 +267,21 @@ namespace Slang
return entryPoint;
}
+ RefPtr<EntryPoint> EntryPoint::createDummyForDeserialize(
+ Linkage* linkage,
+ Name* name,
+ Profile profile,
+ String mangledName)
+ {
+ RefPtr<EntryPoint> entryPoint = new EntryPoint(
+ linkage,
+ name,
+ profile,
+ DeclRef<FuncDecl>());
+ entryPoint->m_mangledName = mangledName;
+ return entryPoint;
+ }
+
EntryPoint::EntryPoint(
Linkage* linkage,
Name* name,
@@ -338,16 +354,7 @@ namespace Slang
SLANG_UNUSED(index);
SLANG_ASSERT(index == 0);
- // Note: this routine might get called on the "dummy"
- // `EntryPoint` objects we create when doing pass-through
- // compilation, in which case there won't be any
- // function decl to be referenced and thus have
- // its mangled name computed.
- //
- if(auto funcDeclRef = getFuncDeclRef())
- return getMangledName(funcDeclRef);
- else
- return String();
+ return m_mangledName;
}
void EntryPoint::acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
@@ -2255,6 +2262,29 @@ SlangResult dissassembleDXILUsingDXC(
SLANG_RETURN_ON_FAIL(writer.write(irModule, sourceManager, optionFlags, &serialData));
SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, compressionType, &container));
}
+
+ auto entryPointCount = program->getEntryPointCount();
+ for( Index ii = 0; ii < entryPointCount; ++ii )
+ {
+ auto entryPoint = program->getEntryPoint(ii);
+ auto entryPointMangledName = program->getEntryPointMangledName(ii);
+
+ RiffContainer::ScopeChunk entryPointScope(&container, RiffContainer::Chunk::Kind::Data, IRSerialBinary::kEntryPointFourCc);
+
+ auto writeString = [&](String const& str)
+ {
+ uint32_t length = (uint32_t) str.getLength();
+ container.write(&length, sizeof(length));
+ container.write(str.getBuffer(), length+1);
+ };
+
+ writeString(entryPoint->getName()->text);
+
+ Profile profile = entryPoint->getProfile();
+ container.write(&profile, sizeof(profile));
+
+ writeString(entryPointMangledName);
+ }
}
// We now write the RiffContainer to the stream
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 2819269b5..e3fbf57f6 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -695,6 +695,13 @@ namespace Slang
Name* name,
Profile profile);
+ /// Create a dummy `EntryPoint` that stands in for a serialized entry point
+ static RefPtr<EntryPoint> createDummyForDeserialize(
+ Linkage* linkage,
+ Name* name,
+ Profile profile,
+ String mangledName);
+
/// Get the number of existential type parameters for the entry point.
Index getSpecializationParamCount() SLANG_OVERRIDE;
@@ -752,6 +759,9 @@ namespace Slang
//
DeclRef<FuncDecl> m_funcDeclRef;
+ /// The mangled name of the entry point function
+ String m_mangledName;
+
SpecializationParams m_genericSpecializationParams;
SpecializationParams m_existentialSpecializationParams;
@@ -1445,6 +1455,17 @@ namespace Slang
Name* m_defaultModuleName = nullptr;
+ /// An "extra" entry point that was added via a library reference
+ struct ExtraEntryPointInfo
+ {
+ Name* name;
+ Profile profile;
+ String mangledName;
+ };
+
+ /// A list of "extra" entry points added via a library reference
+ List<ExtraEntryPointInfo> m_extraEntryPoints;
+
private:
/// A component type that includes only the global scopes of the translation unit(s) that were compiled.
RefPtr<ComponentType> m_globalComponentType;
diff --git a/source/slang/slang-ir-serialize.cpp b/source/slang/slang-ir-serialize.cpp
index 5c16f70f3..fcde8dcda 100644
--- a/source/slang/slang-ir-serialize.cpp
+++ b/source/slang/slang-ir-serialize.cpp
@@ -1357,19 +1357,25 @@ static int _calcFixSourceLoc(const IRSerialData::DebugSourceInfo& info, SourceVi
return int(sourceView->getRange().begin.getRaw()) - int(info.m_startSourceLoc);
}
-/* static */Result IRSerialReader::readStreamModules(Stream* stream, Session* session, SourceManager* sourceManager, List<RefPtr<IRModule>>& outModules)
+// TODO: The following function isn't really part of the IR serialization system, but rather
+// a layered "container" format, and as such probably belongs in a higher-level system that
+// simply calls into the `IRSerialReader` rather than being part of it...
+//
+/* static */Result IRSerialReader::readStreamModules(Stream* stream, Session* session, SourceManager* sourceManager, List<RefPtr<IRModule>>& outModules, List<FrontEndCompileRequest::ExtraEntryPointInfo>& outEntryPoints)
{
// Load up the module
RiffContainer container;
SLANG_RETURN_ON_FAIL(RiffUtil::read(stream, container));
List<RiffContainer::ListChunk*> moduleChunks;
+ List<RiffContainer::DataChunk*> entryPointChunks;
// First try to find a list
{
RiffContainer::ListChunk* listChunk = container.getRoot()->findListRec(IRSerialBinary::kSlangModuleListFourCc);
if (listChunk)
{
listChunk->findContained(IRSerialBinary::kSlangModuleFourCc, moduleChunks);
+ listChunk->findContained(IRSerialBinary::kEntryPointFourCc, entryPointChunks);
}
else
{
@@ -1399,6 +1405,30 @@ static int _calcFixSourceLoc(const IRSerialData::DebugSourceInfo& info, SourceVi
outModules.add(irModule);
}
+ for( auto entryPointChunk : entryPointChunks )
+ {
+ auto reader = entryPointChunk->asReadHelper();
+
+ auto readString = [&]()
+ {
+ uint32_t length = 0;
+ reader.read(length);
+
+ char* begin = (char*) reader.getData();
+ reader.skip(length+1);
+
+ return UnownedStringSlice(begin, begin + length);
+ };
+
+ FrontEndCompileRequest::ExtraEntryPointInfo entryPointInfo;
+
+ entryPointInfo.name = session->getNamePool()->getName(readString());
+ reader.read(entryPointInfo.profile);
+ entryPointInfo.mangledName = readString();
+
+ outEntryPoints.add(entryPointInfo);
+ }
+
return SLANG_OK;
}
diff --git a/source/slang/slang-ir-serialize.h b/source/slang/slang-ir-serialize.h
index d1f78d5f8..644e533ae 100644
--- a/source/slang/slang-ir-serialize.h
+++ b/source/slang/slang-ir-serialize.h
@@ -383,6 +383,8 @@ struct IRSerialBinary
static const FourCC kDebugSourceInfoFourCc = SLANG_FOUR_CC('S', 'd', 's', 'o');
static const FourCC kDebugSourceLocRunFourCc = SLANG_FOUR_CC('S', 'd', 's', 'r');
+ static const FourCC kEntryPointFourCc = SLANG_FOUR_CC('E', 'P', 'n', 't');
+
struct ModuleHeader
{
uint32_t compressionType; ///< Holds the compression type used (if used at all)
@@ -504,7 +506,7 @@ struct IRSerialReader
static Result readStream(Stream* stream, IRSerialData* dataOut);
/// Read potentially multiple modules from a stream
- static Result readStreamModules(Stream* stream, Session* session, SourceManager* manager, List<RefPtr<IRModule>>& outModules);
+ static Result readStreamModules(Stream* stream, Session* session, SourceManager* manager, List<RefPtr<IRModule>>& outModules, List<FrontEndCompileRequest::ExtraEntryPointInfo>& outEntryPoints);
/// Read a stream to fill in dataOut IRSerialData
static Result readContainer(RiffContainer::ListChunk* module, IRSerialData* outData);
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index f50bd8be1..f9d39993a 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -7265,6 +7265,12 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink)
{
auto funcDeclRef = entryPointLayout->entryPoint;
+ // HACK: skip over entry points that came from deserialization,
+ // and thus don't have AST-level information for us to work with.
+ //
+ if(!funcDeclRef)
+ continue;
+
auto irFuncType = lowerType(context, getFuncType(session, funcDeclRef));
auto irFunc = getSimpleVal(context, emitDeclRef(context, funcDeclRef, irFuncType));
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 915bea726..082973947 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -19,6 +19,8 @@
namespace Slang {
+SlangResult _addLibraryReference(EndToEndCompileRequest* req, Stream* stream);
+
SlangResult tryReadCommandLineArgumentRaw(DiagnosticSink* sink, char const* option, char const* const**ioCursor, char const* const*end, char const** argOut)
{
*argOut = nullptr;
@@ -917,17 +919,9 @@ struct OptionsParser
// We need to deserialize and add the modules
FileStream fileStream(referenceModuleName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite);
- List<RefPtr<IRModule>> irModules;
- if (SLANG_FAILED(IRSerialReader::readStreamModules(&fileStream, asInternal(session), requestImpl->getFrontEndReq()->getSourceManager(), irModules)))
- {
- sink->diagnose(SourceLoc(), Diagnostics::unableToReadModuleContainer, referenceModuleName);
- return SLANG_FAIL;
- }
+ // TODO: probalby near an error when we can't open the file?
- // TODO(JS): May be better to have a ITypeComponent that encapsulates a collection of modules
- // For now just add to the linkage
- auto linkage = requestImpl->getLinkage();
- linkage->m_libModules.addRange(irModules.getBuffer(), irModules.getCount());
+ _addLibraryReference(requestImpl, &fileStream);
}
else if (argStr == "-v")
{
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index a2353824c..33ad32918 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -2161,8 +2161,37 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
EntryPoint* entryPoint,
EntryPoint::EntryPointSpecializationInfo* specializationInfo)
{
+ // We will take responsibility for creating and filling in
+ // the `EntryPointLayout` object here.
+ //
+ RefPtr<EntryPointLayout> entryPointLayout = new EntryPointLayout();
+ entryPointLayout->profile = entryPoint->getProfile();
+
+ // The entry point layout must be added to the output
+ // program layout so that it can be accessed by reflection.
+ //
+ context->shared->programLayout->entryPoints.add(entryPointLayout);
+
DeclRef<FuncDecl> entryPointFuncDeclRef = entryPoint->getFuncDeclRef();
+ // HACK: We might have an `EntryPoint` that has been deserialized, in
+ // which case we don't currently have access to its AST-level information,
+ // and as a result we cannot collect parameter information from it.
+ //
+ if( !entryPointFuncDeclRef )
+ {
+ // TODO: figure out what fields we absolutely need to fill in.
+
+ RefPtr<StructTypeLayout> paramsTypeLayout = new StructTypeLayout();
+
+ RefPtr<VarLayout> paramsLayout = new VarLayout();
+ paramsLayout->typeLayout = paramsTypeLayout;
+
+ entryPointLayout->parametersLayout = paramsLayout;
+
+ return entryPointLayout;
+ }
+
// If specialization was applied to the entry point, then the side-band
// information that was generated will have a more specialized reference
// to the entry point with generic parameters filled in. We should
@@ -2173,18 +2202,8 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
auto entryPointType = DeclRefType::Create(context->getLinkage()->getSessionImpl(), entryPointFuncDeclRef);
- // We will take responsibility for creating and filling in
- // the `EntryPointLayout` object here.
- //
- RefPtr<EntryPointLayout> entryPointLayout = new EntryPointLayout();
- entryPointLayout->profile = entryPoint->getProfile();
entryPointLayout->entryPoint = entryPointFuncDeclRef;
- // The entry point layout must be added to the output
- // program layout so that it can be accessed by reflection.
- //
- context->shared->programLayout->entryPoints.add(entryPointLayout);
-
// For the duration of our parameter collection work we will
// establish this entry point as the current one in the context.
//
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index ab1b0e489..6632f2fa3 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -3041,21 +3041,14 @@ SLANG_API void spSetDefaultModuleName(
frontEndReq->m_defaultModuleName = namePool->getName(defaultModuleName);
}
-
-SLANG_API SlangResult spAddLibraryReference(
- SlangCompileRequest* request,
- const void* libData,
- size_t libDataSize)
+namespace Slang
+{
+SlangResult _addLibraryReference(EndToEndCompileRequest* req, Stream* stream)
{
- using namespace Slang;
- auto req = Slang::asInternal(request);
-
- // We need to deserialize and add the modules
- MemoryStreamBase fileStream(FileAccess::Read, libData, libDataSize);
-
// Read all of the contained modules
List<RefPtr<IRModule>> irModules;
- if (SLANG_FAILED(IRSerialReader::readStreamModules(&fileStream, req->getSession(), req->getFrontEndReq()->getSourceManager(), irModules)))
+ List<FrontEndCompileRequest::ExtraEntryPointInfo> entryPointMangledNames;
+ if (SLANG_FAILED(IRSerialReader::readStreamModules(stream, req->getSession(), req->getFrontEndReq()->getSourceManager(), irModules, entryPointMangledNames)))
{
req->getSink()->diagnose(SourceLoc(), Diagnostics::unableToAddReferenceToModuleContainer);
return SLANG_FAIL;
@@ -3064,10 +3057,28 @@ SLANG_API SlangResult spAddLibraryReference(
// TODO(JS): May be better to have a ITypeComponent that encapsulates a collection of modules
// For now just add to the linkage
auto linkage = req->getLinkage();
- linkage->m_libModules.addRange(irModules.getBuffer(), irModules.getCount());
+ linkage->m_libModules.addRange(irModules);
+
+ req->getFrontEndReq()->m_extraEntryPoints.addRange(entryPointMangledNames);
return SLANG_OK;
}
+}
+
+
+SLANG_API SlangResult spAddLibraryReference(
+ SlangCompileRequest* request,
+ const void* libData,
+ size_t libDataSize)
+{
+ using namespace Slang;
+ auto req = Slang::asInternal(request);
+
+ // We need to deserialize and add the modules
+ MemoryStreamBase fileStream(FileAccess::Read, libData, libDataSize);
+
+ return _addLibraryReference(req, &fileStream);
+}
SLANG_API void spTranslationUnit_addPreprocessorDefine(
SlangCompileRequest* request,
@@ -3417,7 +3428,7 @@ SLANG_API void const* spGetEntryPointCode(
if(entryPointIndex < 0) return nullptr;
- if(Index(entryPointIndex) >= req->entryPoints.getCount()) return nullptr;
+ if(Index(entryPointIndex) >= program->getEntryPointCount()) return nullptr;
auto entryPoint = program->getEntryPoint(entryPointIndex);
auto targetProgram = program->getTargetProgram(targetReq);