diff options
| author | Theresa Foley <10618364+tangent-vector@users.noreply.github.com> | 2025-07-24 12:59:58 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-24 19:59:58 +0000 |
| commit | 8ccd495d5eaa82cb831378c28dd190e657b6c999 (patch) | |
| tree | c39dc364d95f78984fd4ba2776d259ff944d2596 /source/slang/slang-linkable.cpp | |
| parent | 2d23a962766a97cbb11bcee5483a66aec923da49 (diff) | |
Organize code better by splitting some big files (#7890)
* Organize code better by splitting some big files
The basic change here is that the majority of the declarations in `slang-compiler.h` have been split out into a set of smaller and more focused files.
As a result, the implement of those declarations have been moved from `slang-compiler.cpp` and `slang.cpp` over to those new files when the proper home for code is obvious.
I have tried as much as possible to *not* make any edits to the code along the way, and just copy-paste declarations from one place to another as-is.
The exceptions I am aware of are:
* In some cases a function that used to be file-scope `static` was used by code that landed in two or more different `.cpp` files. In these cases, I changed the function to be non-`static` (removing the `_` prefix from its name, if it had one, per our naming conventions), and put a declaration for the function into the most appropriate header I could identify.
* I added a few comments in places where I saw ugly or unfortunate things in the code I was moving, and wanted to tag them with `TODO`s so we can hopefully get to them in the fullness of time.
* I added top-level comments to each of the new `.h` files that was introduced to try to explain the logic for what goes into that file.
* In cases where one of the new header files mostly existed to declare a single type, I sometimes added more detail to the doc comment on that type, to better explain the type and its role in the compiler (this is text that otherwise might have gone into the comment at the top leve lof the file, but I figured that the doc comment would have higher discoverability).
I expect that the most contentious choice here is that the `Session` class lands in `slang-global-session.h` while `slang-session.h` holds the `Linkage` class.
The names used in this change are consistent with how the relevant concepts in the public Slang API are named, and are consistent with how we *intend* to rename the classes themselves in time.
* format code
* fixup
---------
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-linkable.cpp')
| -rw-r--r-- | source/slang/slang-linkable.cpp | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/source/slang/slang-linkable.cpp b/source/slang/slang-linkable.cpp new file mode 100644 index 000000000..da4cec823 --- /dev/null +++ b/source/slang/slang-linkable.cpp @@ -0,0 +1,1037 @@ +// slang-linkable.cpp +#include "slang-linkable.h" + +#include "compiler-core/slang-artifact-container-util.h" +#include "compiler-core/slang-artifact-desc-util.h" +#include "compiler-core/slang-artifact-impl.h" +#include "core/slang-char-util.h" +#include "core/slang-memory-file-system.h" +#include "slang-check-impl.h" +#include "slang-compiler.h" +#include "slang-mangle.h" + +namespace Slang +{ + +// +// ModuleDependencyList +// + +void ModuleDependencyList::addDependency(Module* module) +{ + // If we depend on a module, then we depend on everything it depends on. + // + // Note: We are processing these sub-depenencies before adding the + // `module` itself, so that in the common case a module will always + // appear *after* everything it depends on. + // + // However, this rule is being violated in the compiler right now because + // the modules for hte top-level translation units of a compile request + // will be added to the list first (using `addLeafDependency`) to + // maintain compatibility with old behavior. This may be fixed later. + // + for (auto subDependency : module->getModuleDependencyList()) + { + _addDependency(subDependency); + } + _addDependency(module); +} + +void ModuleDependencyList::addLeafDependency(Module* module) +{ + _addDependency(module); +} + +void ModuleDependencyList::_addDependency(Module* module) +{ + if (m_moduleSet.contains(module)) + return; + + m_moduleList.add(module); + m_moduleSet.add(module); +} + +// +// FileDependencyList +// + +void FileDependencyList::addDependency(SourceFile* sourceFile) +{ + if (m_fileSet.contains(sourceFile)) + return; + + m_fileList.add(sourceFile); + m_fileSet.add(sourceFile); +} + +void FileDependencyList::addDependency(Module* module) +{ + for (SourceFile* sourceFile : module->getFileDependencyList()) + { + addDependency(sourceFile); + } +} + +// +// ComponentType +// + +ComponentType::ComponentType(Linkage* linkage) + : m_linkage(linkage) +{ +} + +ComponentType* asInternal(slang::IComponentType* inComponentType) +{ + // Note: we use a `queryInterface` here instead of just a `static_cast` + // to ensure that the `IComponentType` we get is the preferred/canonical + // one, which shares its address with the `ComponentType`. + // + // TODO: An alternative choice here would be to have a "magic" IID that + // we pass into `queryInterface` that returns the `ComponentType` directly + // (without even `addRef`-ing it). + // + ComPtr<slang::IComponentType> componentType; + inComponentType->queryInterface(SLANG_IID_PPV_ARGS(componentType.writeRef())); + return static_cast<ComponentType*>(componentType.get()); +} + +ISlangUnknown* ComponentType::getInterface(Guid const& guid) +{ + if (guid == ISlangUnknown::getTypeGuid() || guid == slang::IComponentType::getTypeGuid()) + { + return static_cast<slang::IComponentType*>(this); + } + if (guid == IModulePrecompileService_Experimental::getTypeGuid()) + return static_cast<slang::IModulePrecompileService_Experimental*>(this); + if (guid == IComponentType2::getTypeGuid()) + return static_cast<slang::IComponentType2*>(this); + return nullptr; +} + +SLANG_NO_THROW slang::ISession* SLANG_MCALL ComponentType::getSession() +{ + return m_linkage; +} + +SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL +ComponentType::getLayout(Int targetIndex, slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return nullptr; + auto target = linkage->targets[targetIndex]; + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + auto programLayout = getTargetProgram(target)->getOrCreateLayout(&sink); + sink.getBlobIfNeeded(outDiagnostics); + + return asExternal(programLayout); +} + +static ICastable* _findDiagnosticRepresentation(IArtifact* artifact) +{ + if (auto rep = findAssociatedRepresentation<IArtifactDiagnostics>(artifact)) + { + return rep; + } + + for (auto associated : artifact->getAssociated()) + { + if (isDerivedFrom(associated->getDesc().payload, ArtifactPayload::Diagnostics)) + { + return associated; + } + } + return nullptr; +} + +static IArtifact* _findObfuscatedSourceMap(IArtifact* artifact) +{ + // If we find any obfuscated source maps, we are done + for (auto associated : artifact->getAssociated()) + { + const auto desc = associated->getDesc(); + + if (isDerivedFrom(desc.payload, ArtifactPayload::SourceMap) && + isDerivedFrom(desc.style, ArtifactStyle::Obfuscated)) + { + return associated; + } + } + return nullptr; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getResultAsFileSystem( + SlangInt entryPointIndex, + Int targetIndex, + ISlangMutableFileSystem** outFileSystem) +{ + ComPtr<ISlangBlob> diagnostics; + ComPtr<ISlangBlob> code; + + SLANG_RETURN_ON_FAIL( + getEntryPointCode(entryPointIndex, targetIndex, diagnostics.writeRef(), code.writeRef())); + + auto linkage = getLinkage(); + + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex); + + // Add diagnostics id needs be... + if (diagnostics && !_findDiagnosticRepresentation(artifact)) + { + // Add as an associated + + auto diagnosticsArtifact = Artifact::create( + ArtifactDesc::make(Artifact::Kind::HumanText, ArtifactPayload::Diagnostics)); + diagnosticsArtifact->addRepresentationUnknown(diagnostics); + + artifact->addAssociated(diagnosticsArtifact); + + SLANG_ASSERT(diagnosticsArtifact == _findDiagnosticRepresentation(artifact)); + } + + // Add obfuscated source maps + if (!_findObfuscatedSourceMap(artifact)) + { + List<IRModule*> irModules; + enumerateIRModules([&](IRModule* irModule) -> void { irModules.add(irModule); }); + + for (auto irModule : irModules) + { + if (auto obfuscatedSourceMap = irModule->getObfuscatedSourceMap()) + { + auto artifactDesc = ArtifactDesc::make( + ArtifactKind::Json, + ArtifactPayload::SourceMap, + ArtifactStyle::Obfuscated); + + // Create the source map artifact + auto sourceMapArtifact = Artifact::create( + artifactDesc, + obfuscatedSourceMap->get().m_file.getUnownedSlice()); + + sourceMapArtifact->addRepresentation(obfuscatedSourceMap); + + // associate with the artifact + artifact->addAssociated(sourceMapArtifact); + } + } + } + + // Turn into a file system and return + ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem); + + // Filter the containerArtifact into things that can be written + ComPtr<IArtifact> writeArtifact; + SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(artifact, writeArtifact)); + SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem)); + + *outFileSystem = fileSystem.detach(); + + return SLANG_OK; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCode( + SlangInt entryPointIndex, + Int targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return SLANG_E_INVALID_ARG; + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet); + applySettingsToDiagnosticSink(&sink, &sink, m_optionSet); + + IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink); + sink.getBlobIfNeeded(outDiagnostics); + + if (artifact == nullptr) + return SLANG_FAIL; + + return artifact->loadBlob(ArtifactKeep::Yes, outCode); +} + +SLANG_NO_THROW void SLANG_MCALL ComponentType::getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) +{ + DigestBuilder<SHA1> builder; + + // A note on enums that may be hashed in as part of the following two function calls: + // + // While enums are not guaranteed to be encoded the same way across all versions of + // the compiler, part of hashing the linkage is hashing in the compiler version. + // Consequently, any encoding differences as a result of different compiler versions + // will already be reflected in the resulting hash. + getLinkage()->buildHash(builder, targetIndex); + + buildHash(builder); + + // Add the name and name override for the specified entry point to the hash. + auto entryPoint = getEntryPoint(entryPointIndex); + if (entryPoint) + { + auto entryPointName = entryPoint->getName()->text; + builder.append(entryPointName); + auto entryPointMangledName = getEntryPointMangledName(entryPointIndex); + builder.append(entryPointMangledName); + auto entryPointNameOverride = getEntryPointNameOverride(entryPointIndex); + builder.append(entryPointNameOverride); + } + + auto hash = builder.finalize().toBlob(); + *outHash = hash.detach(); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return SLANG_E_INVALID_ARG; + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + applySettingsToDiagnosticSink(&sink, &sink, m_optionSet); + + IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink); + sink.getBlobIfNeeded(outDiagnostics); + + if (artifact == nullptr) + return SLANG_FAIL; + + return artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointMetadata( + SlangInt entryPointIndex, + Int targetIndex, + slang::IMetadata** outMetadata, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return SLANG_E_INVALID_ARG; + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet); + applySettingsToDiagnosticSink(&sink, &sink, m_optionSet); + + IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink); + sink.getBlobIfNeeded(outDiagnostics); + + if (artifact == nullptr) + return SLANG_E_NOT_AVAILABLE; + + auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact); + if (!metadata) + return SLANG_E_NOT_AVAILABLE; + + *outMetadata = static_cast<slang::IMetadata*>(metadata); + (*outMetadata)->addRef(); + return SLANG_OK; +} + +RefPtr<ComponentType> ComponentType::specialize( + SpecializationArg const* inSpecializationArgs, + SlangInt specializationArgCount, + DiagnosticSink* sink) +{ + if (specializationArgCount == 0) + { + return this; + } + + List<SpecializationArg> specializationArgs; + specializationArgs.addRange(inSpecializationArgs, specializationArgCount); + + // We next need to validate that the specialization arguments + // make sense, and also expand them to include any derived data + // (e.g., interface conformance witnesses) that doesn't get + // passed explicitly through the API interface. + // + RefPtr<SpecializationInfo> specializationInfo = + _validateSpecializationArgs(specializationArgs.getBuffer(), specializationArgCount, sink); + + return new SpecializedComponentType(this, specializationInfo, specializationArgs, sink); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics) +{ + DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer); + + // First let's check if the number of arguments given matches + // the number of parameters that are present on this component type. + // + auto specializationParamCount = getSpecializationParamCount(); + if (specializationArgCount != specializationParamCount) + { + sink.diagnose( + SourceLoc(), + Diagnostics::mismatchSpecializationArguments, + specializationParamCount, + specializationArgCount); + sink.getBlobIfNeeded(outDiagnostics); + return SLANG_FAIL; + } + + List<SpecializationArg> expandedArgs; + for (Int aa = 0; aa < specializationArgCount; ++aa) + { + auto apiArg = specializationArgs[aa]; + + SpecializationArg expandedArg; + switch (apiArg.kind) + { + case slang::SpecializationArg::Kind::Type: + expandedArg.val = asInternal(apiArg.type); + break; + + default: + sink.getBlobIfNeeded(outDiagnostics); + return SLANG_FAIL; + } + expandedArgs.add(expandedArg); + } + + auto specializedComponentType = + specialize(expandedArgs.getBuffer(), expandedArgs.getCount(), &sink); + + sink.getBlobIfNeeded(outDiagnostics); + + *outSpecializedComponentType = specializedComponentType.detach(); + + return SLANG_OK; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL +ComponentType::renameEntryPoint(const char* newName, IComponentType** outEntryPoint) +{ + RefPtr<RenamedEntryPointComponentType> result = + new RenamedEntryPointComponentType(this, newName); + *outEntryPoint = result.detach(); + return SLANG_OK; +} + +RefPtr<ComponentType> fillRequirements(ComponentType* inComponentType); + +SLANG_NO_THROW SlangResult SLANG_MCALL +ComponentType::link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) +{ + // TODO: It should be possible for `fillRequirements` to fail, + // in cases where we have a dependency that can't be automatically + // resolved. + // + SLANG_UNUSED(outDiagnostics); + + DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer); + + try + { + auto linked = fillRequirements(this); + if (!linked) + return SLANG_FAIL; + + *outLinkedComponentType = ComPtr<slang::IComponentType>(linked).detach(); + return SLANG_OK; + } + catch (const AbortCompilationException& e) + { + outputExceptionDiagnostic(e, sink, outDiagnostics); + return SLANG_FAIL; + } + catch (const Exception& e) + { + outputExceptionDiagnostic(e, sink, outDiagnostics); + return SLANG_FAIL; + } + catch (...) + { + outputExceptionDiagnostic(sink, outDiagnostics); + return SLANG_FAIL; + } +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::linkWithOptions( + slang::IComponentType** outLinkedComponentType, + uint32_t count, + slang::CompilerOptionEntry* entries, + ISlangBlob** outDiagnostics) +{ + SLANG_RETURN_ON_FAIL(link(outLinkedComponentType, outDiagnostics)); + + auto linked = *outLinkedComponentType; + + if (linked) + { + static_cast<ComponentType*>(linked)->getOptionSet().load(count, entries); + } + + return SLANG_OK; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCompileResult( + SlangInt entryPointIndex, + Int targetIndex, + slang::ICompileResult** outCompileResult, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return SLANG_E_INVALID_ARG; + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet); + applySettingsToDiagnosticSink(&sink, &sink, m_optionSet); + + IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink); + sink.getBlobIfNeeded(outDiagnostics); + if (artifact == nullptr) + return SLANG_E_NOT_AVAILABLE; + + *outCompileResult = static_cast<slang::ICompileResult*>(artifact); + (*outCompileResult)->addRef(); + return SLANG_OK; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetCompileResult( + Int targetIndex, + slang::ICompileResult** outCompileResult, + slang::IBlob** outDiagnostics) +{ + IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics); + if (artifact == nullptr) + return SLANG_E_NOT_AVAILABLE; + + *outCompileResult = static_cast<slang::ICompileResult*>(artifact); + (*outCompileResult)->addRef(); + return SLANG_OK; +} + +/// Visitor used by `ComponentType::enumerateModules` +struct EnumerateModulesVisitor : ComponentTypeVisitor +{ + EnumerateModulesVisitor(ComponentType::EnumerateModulesCallback callback, void* userData) + : m_callback(callback), m_userData(userData) + { + } + + ComponentType::EnumerateModulesCallback m_callback; + void* m_userData; + + void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {} + + void visitRenamedEntryPoint( + RenamedEntryPointComponentType* entryPoint, + EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { + entryPoint->getBase()->acceptVisitor(this, specializationInfo); + } + + void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE + { + m_callback(module, m_userData); + } + + void visitComposite( + CompositeComponentType* composite, + CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { + visitChildren(composite, specializationInfo); + } + + void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE + { + visitChildren(specialized); + } + + void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE + { + SLANG_UNUSED(conformance); + } +}; + + +void ComponentType::enumerateModules(EnumerateModulesCallback callback, void* userData) +{ + EnumerateModulesVisitor visitor(callback, userData); + acceptVisitor(&visitor, nullptr); +} + +/// Visitor used by `ComponentType::enumerateIRModules` +struct EnumerateIRModulesVisitor : ComponentTypeVisitor +{ + EnumerateIRModulesVisitor(ComponentType::EnumerateIRModulesCallback callback, void* userData) + : m_callback(callback), m_userData(userData) + { + } + + ComponentType::EnumerateIRModulesCallback m_callback; + void* m_userData; + + void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {} + + void visitRenamedEntryPoint( + RenamedEntryPointComponentType* entryPoint, + EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { + entryPoint->getBase()->acceptVisitor(this, specializationInfo); + } + + void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE + { + m_callback(module->getIRModule(), m_userData); + } + + void visitComposite( + CompositeComponentType* composite, + CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { + visitChildren(composite, specializationInfo); + } + + void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE + { + visitChildren(specialized); + + m_callback(specialized->getIRModule(), m_userData); + } + + void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE + { + m_callback(conformance->getIRModule(), m_userData); + } +}; + +void ComponentType::enumerateIRModules(EnumerateIRModulesCallback callback, void* userData) +{ + EnumerateIRModulesVisitor visitor(callback, userData); + acceptVisitor(&visitor, nullptr); +} + +IArtifact* ComponentType::getTargetArtifact(Int targetIndex, slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkage(); + if (targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return nullptr; + ComPtr<IArtifact> artifact; + if (m_targetArtifacts.tryGetValue(targetIndex, artifact)) + { + return artifact.get(); + } + + // If the user hasn't specified any entry points, then we should + // discover all entrypoints that are defined in linked modules, and + // include all of them in the compile. + // + if (getEntryPointCount() == 0) + { + List<Module*> modules; + this->enumerateModules([&](Module* module) { modules.add(module); }); + List<RefPtr<ComponentType>> components; + components.add(this); + bool entryPointsDiscovered = false; + for (auto module : modules) + { + for (auto entryPoint : module->getEntryPoints()) + { + components.add(entryPoint); + entryPointsDiscovered = true; + } + } + + // If any entry points were discovered, then we should emit the program with entrypoints + // linked. + if (entryPointsDiscovered) + { + RefPtr<CompositeComponentType> composite = + new CompositeComponentType(linkage, components); + ComPtr<IComponentType> linkedComponentType; + SLANG_RETURN_NULL_ON_FAIL( + composite->link(linkedComponentType.writeRef(), outDiagnostics)); + auto targetArtifact = static_cast<ComponentType*>(linkedComponentType.get()) + ->getTargetArtifact(targetIndex, outDiagnostics); + if (targetArtifact) + { + m_targetArtifacts[targetIndex] = targetArtifact; + } + return targetArtifact; + } + } + + auto target = linkage->targets[targetIndex]; + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); + applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet); + applySettingsToDiagnosticSink(&sink, &sink, m_optionSet); + + IArtifact* targetArtifact = targetProgram->getOrCreateWholeProgramResult(&sink); + sink.getBlobIfNeeded(outDiagnostics); + m_targetArtifacts[targetIndex] = ComPtr<IArtifact>(targetArtifact); + return targetArtifact; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL +ComponentType::getTargetCode(Int targetIndex, slang::IBlob** outCode, slang::IBlob** outDiagnostics) +{ + IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics); + + if (artifact == nullptr) + return SLANG_FAIL; + + return artifact->loadBlob(ArtifactKeep::Yes, outCode); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetMetadata( + Int targetIndex, + slang::IMetadata** outMetadata, + slang::IBlob** outDiagnostics) +{ + IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics); + + if (artifact == nullptr) + return SLANG_FAIL; + + auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact); + if (!metadata) + return SLANG_E_NOT_AVAILABLE; + *outMetadata = static_cast<slang::IMetadata*>(metadata); + (*outMetadata)->addRef(); + return SLANG_OK; +} + +Type* ComponentType::getTypeFromString(String const& typeStr, DiagnosticSink* sink) +{ + // If we've looked up this type name before, + // then we can re-use it. + // + Type* type = nullptr; + if (m_types.tryGetValue(typeStr, type)) + return type; + + + // TODO(JS): For now just used the linkages ASTBuilder to keep on scope + // + // The parseTermString uses the linkage ASTBuilder for it's parsing. + // + // It might be possible to just create a temporary ASTBuilder - the worry though is + // that the parsing sets a member variable in AST node to one of these scopes, and then + // it become a dangling pointer. So for now we go with the linkages. + auto astBuilder = getLinkage()->getASTBuilder(); + + // Otherwise, we need to start looking in + // the modules that were directly or + // indirectly referenced. + // + Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder); + + auto linkage = getLinkage(); + + SLANG_AST_BUILDER_RAII(linkage->getASTBuilder()); + + Expr* typeExpr = linkage->parseTermString(typeStr, scope); + SharedSemanticsContext sharedSemanticsContext(linkage, nullptr, sink); + SemanticsVisitor visitor(&sharedSemanticsContext); + type = visitor.TranslateTypeNode(typeExpr); + auto typeOut = visitor.tryCoerceToProperType(TypeExp(type)); + if (typeOut.type) + type = typeOut.type; + + if (type) + { + m_types[typeStr] = type; + } + return type; +} + +Expr* ComponentType::findDeclFromString(String const& name, DiagnosticSink* sink) +{ + // If we've looked up this type name before, + // then we can re-use it. + // + Expr* result = nullptr; + if (m_decls.tryGetValue(name, result)) + return result; + + + // TODO(JS): For now just used the linkages ASTBuilder to keep on scope + // + // The parseTermString uses the linkage ASTBuilder for it's parsing. + // + // It might be possible to just create a temporary ASTBuilder - the worry though is + // that the parsing sets a member variable in AST node to one of these scopes, and then + // it become a dangling pointer. So for now we go with the linkages. + auto astBuilder = getLinkage()->getASTBuilder(); + + // Otherwise, we need to start looking in + // the modules that were directly or + // indirectly referenced. + // + Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder); + + auto linkage = getLinkage(); + + SLANG_AST_BUILDER_RAII(linkage->getASTBuilder()); + + Expr* expr = linkage->parseTermString(name, scope); + + SemanticsContext context(linkage->getSemanticsForReflection()); + context = context.allowStaticReferenceToNonStaticMember().withSink(sink); + + SemanticsVisitor visitor(context); + + auto checkedExpr = visitor.CheckTerm(expr); + + if (as<DeclRefExpr>(checkedExpr) || as<OverloadedExpr>(checkedExpr)) + { + result = checkedExpr; + } + + m_decls[name] = result; + return result; +} + +static bool _isSimpleName(String const& name) +{ + for (char c : name) + { + if (!CharUtil::isAlphaOrDigit(c) && c != '_' && c != '$') + return false; + } + return true; +} + +Expr* ComponentType::findDeclFromStringInType( + Type* type, + String const& name, + LookupMask mask, + DiagnosticSink* sink) +{ + // Only look up in the type if it is a DeclRefType + if (!as<DeclRefType>(type)) + return nullptr; + + // TODO(JS): For now just used the linkages ASTBuilder to keep on scope + // + // The parseTermString uses the linkage ASTBuilder for it's parsing. + // + // It might be possible to just create a temporary ASTBuilder - the worry though is + // that the parsing sets a member variable in AST node to one of these scopes, and then + // it become a dangling pointer. So for now we go with the linkages. + auto astBuilder = getLinkage()->getASTBuilder(); + + // Otherwise, we need to start looking in + // the modules that were directly or + // indirectly referenced. + // + Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder); + + auto linkage = getLinkage(); + + SLANG_AST_BUILDER_RAII(linkage->getASTBuilder()); + + Expr* expr = nullptr; + + if (_isSimpleName(name)) + { + auto varExpr = astBuilder->create<VarExpr>(); + varExpr->scope = scope; + varExpr->name = getLinkage()->getNamePool()->getName(name); + expr = varExpr; + } + else + { + expr = linkage->parseTermString(name, scope); + } + SemanticsContext context(linkage->getSemanticsForReflection()); + context = context.allowStaticReferenceToNonStaticMember().withSink(sink); + + SemanticsVisitor visitor(context); + + GenericAppExpr* genericOuterExpr = nullptr; + if (as<GenericAppExpr>(expr)) + { + // Unwrap the generic application, and re-wrap it around the static-member expr + genericOuterExpr = as<GenericAppExpr>(expr); + expr = genericOuterExpr->functionExpr; + } + + if (!as<VarExpr>(expr)) + return nullptr; + + auto rs = astBuilder->create<StaticMemberExpr>(); + auto typeExpr = astBuilder->create<SharedTypeExpr>(); + auto typetype = astBuilder->getOrCreate<TypeType>(type); + typeExpr->type = typetype; + rs->baseExpression = typeExpr; + rs->name = as<VarExpr>(expr)->name; + + expr = rs; + + // If we have a generic-app expression, re-wrap the static-member expr + if (genericOuterExpr) + { + genericOuterExpr->functionExpr = expr; + expr = genericOuterExpr; + } + + auto checkedTerm = visitor.CheckTerm(expr); + + // Check if checkedTerm is overloaded functions and avoid resolving if so + // to preserve all function overloads with different signatures + Expr* resolvedTerm = checkedTerm; + if (auto overloadedExpr = as<OverloadedExpr>(checkedTerm)) + { + // Check if all candidates are function references + bool allAreFunctions = true; + for (auto item : overloadedExpr->lookupResult2.items) + { + if (!as<FunctionDeclBase>(item.declRef.getDecl())) + { + allAreFunctions = false; + break; + } + } + + // If not all are functions, resolve the overload as usual + if (!allAreFunctions) + { + resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink); + } + } + else + { + // Not overloaded, resolve as usual + resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink); + } + + if (auto overloadedExpr = as<OverloadedExpr>(resolvedTerm)) + { + return overloadedExpr; + } + if (auto declRefExpr = as<DeclRefExpr>(resolvedTerm)) + { + return declRefExpr; + } + + return nullptr; +} + +bool ComponentType::isSubType(Type* subType, Type* superType) +{ + SemanticsContext context(getLinkage()->getSemanticsForReflection()); + SemanticsVisitor visitor(context); + + return (visitor.isSubtype(subType, superType, IsSubTypeOptions::None) != nullptr); +} + +static void collectExportedConstantInContainer( + Dictionary<String, IntVal*>& dict, + ASTBuilder* builder, + ContainerDecl* containerDecl) +{ + for (auto varMember : containerDecl->getDirectMemberDeclsOfType<VarDeclBase>()) + { + if (!varMember->val) + continue; + bool isExported = false; + bool isConst = false; + bool isExtern = false; + for (auto modifier : varMember->modifiers) + { + if (as<HLSLExportModifier>(modifier)) + isExported = true; + if (as<ExternAttribute>(modifier) || as<ExternModifier>(modifier)) + { + isExtern = true; + isExported = true; + } + if (as<ConstModifier>(modifier)) + isConst = true; + } + if (isExported && isConst) + { + auto mangledName = getMangledName(builder, varMember); + if (isExtern && dict.containsKey(mangledName)) + continue; + dict[mangledName] = varMember->val; + } + } + + for (auto member : containerDecl->getDirectMemberDecls()) + { + if (as<NamespaceDecl>(member) || as<FileDecl>(member)) + { + collectExportedConstantInContainer(dict, builder, (ContainerDecl*)member); + } + } +} + +Dictionary<String, IntVal*>& ComponentType::getMangledNameToIntValMap() +{ + if (m_mapMangledNameToIntVal) + { + return *m_mapMangledNameToIntVal; + } + m_mapMangledNameToIntVal = std::make_unique<Dictionary<String, IntVal*>>(); + auto astBuilder = getLinkage()->getASTBuilder(); + SLANG_AST_BUILDER_RAII(astBuilder); + Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder); + for (; scope; scope = scope->nextSibling) + { + if (scope->containerDecl) + collectExportedConstantInContainer( + *m_mapMangledNameToIntVal, + astBuilder, + scope->containerDecl); + } + return *m_mapMangledNameToIntVal; +} + +ConstantIntVal* ComponentType::tryFoldIntVal(IntVal* intVal) +{ + auto astBuilder = getLinkage()->getASTBuilder(); + SLANG_AST_BUILDER_RAII(astBuilder); + return as<ConstantIntVal>(intVal->linkTimeResolve(getMangledNameToIntValMap())); +} + +TargetProgram* ComponentType::getTargetProgram(TargetRequest* target) +{ + RefPtr<TargetProgram> targetProgram; + if (!m_targetPrograms.tryGetValue(target, targetProgram)) + { + targetProgram = new TargetProgram(this, target); + m_targetPrograms[target] = targetProgram; + } + return targetProgram; +} + +} // namespace Slang |
