diff options
| author | Yong He <yonghe@outlook.com> | 2024-05-29 18:01:11 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-29 18:01:11 -0700 |
| commit | efdbb954c57b89362e390f955d45f90e59d66878 (patch) | |
| tree | 7b47d6e52d2de666af99f66a2fd3a5dc387ca5cc /source | |
| parent | 83f176ba8a3bae5533470aed6a90663653f894b8 (diff) | |
Improve compile time performance. (#3857)
* Handle type check cache update on extensions more gracefully.
* Correctness fix.
* Cache implcit cast overload resolution results.
* Fix.
* More optimizations.
* Cache implicit default ctor resolution.
* Disable redundancy removal.
* Fix.
* Fix test.
* Fix.
* Correctness fix.
* Fix.
* Fix,
* Fix test.
* Small tweak.
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-diagnostic-sink.cpp | 2 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 2 | ||||
| -rw-r--r-- | source/slang/slang-artifact-output-util.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-check-conversion.cpp | 74 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 150 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 48 | ||||
| -rw-r--r-- | source/slang/slang-check-inheritance.cpp | 18 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff-fwd.cpp | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-ssa-simplification.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 676 |
12 files changed, 609 insertions, 389 deletions
diff --git a/source/compiler-core/slang-diagnostic-sink.cpp b/source/compiler-core/slang-diagnostic-sink.cpp index d077ef90f..7e3c286eb 100644 --- a/source/compiler-core/slang-diagnostic-sink.cpp +++ b/source/compiler-core/slang-diagnostic-sink.cpp @@ -592,7 +592,7 @@ Severity DiagnosticSink::getEffectiveMessageSeverity(DiagnosticInfo const& info) if (effectiveSeverity < Severity::Error || *pSeverityOverride >= effectiveSeverity) effectiveSeverity = *pSeverityOverride; } - + if (isFlagSet(Flag::TreatWarningsAsErrors) && effectiveSeverity == Severity::Warning) effectiveSeverity = Severity::Error; diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 2b2de85bd..1458467b9 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -2153,7 +2153,7 @@ ${{{{ { }}}} __intrinsic_op($(kIROp_CombinedTextureSamplerGetTexture)) - __TextureImpl<T, Shape, isArray, isMS, sampleCount, 0, isShadow, 0, format> __getTexture(); + __TextureImpl<T, Shape, isArray, 0, sampleCount, 0, isShadow, 0, format> __getTexture(); __intrinsic_op($(kIROp_CombinedTextureSamplerGetSampler)) SamplerState __getSampler(); diff --git a/source/slang/slang-artifact-output-util.cpp b/source/slang/slang-artifact-output-util.cpp index 2c560e236..3cb0e6c93 100644 --- a/source/slang/slang-artifact-output-util.cpp +++ b/source/slang/slang-artifact-output-util.cpp @@ -58,9 +58,11 @@ namespace Slang } return SLANG_FAIL; } - + auto downstreamStartTime = std::chrono::high_resolution_clock::now(); SLANG_RETURN_ON_FAIL(compiler->convert(artifact, assemblyDesc, outArtifact)); - + auto downstreamElapsedTime = + (std::chrono::high_resolution_clock::now() - downstreamStartTime).count() * 0.000000001; + session->addDownstreamCompileTime(downstreamElapsedTime); return SLANG_OK; } diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 938e7bfbe..2d238d717 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -551,13 +551,11 @@ namespace Slang SLANG_VALUE_CLASS(QualType) Type* type = nullptr; - bool isLeftValue; + bool isLeftValue = false; bool hasReadOnlyOnTarget = false; bool isWriteOnly = false; - QualType() - : isLeftValue(false) - {} + QualType() = default; QualType(Type* type); diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp index 7dcc8975c..16f413055 100644 --- a/source/slang/slang-check-conversion.cpp +++ b/source/slang/slang-check-conversion.cpp @@ -1097,7 +1097,40 @@ namespace Slang overloadContext.baseExpr = nullptr; overloadContext.mode = OverloadResolveContext::Mode::JustTrying; - AddTypeOverloadCandidates(toType, overloadContext); + // Since the lookup and resolution of all possible implicit conversions + // can be very costly, we use a cache to store the checking results. + ImplicitCastMethodKey implicitCastKey = ImplicitCastMethodKey(fromType, toType, fromExpr); + ImplicitCastMethod* cachedMethod = getShared()->tryGetImplicitCastMethod(implicitCastKey); + + if (cachedMethod) + { + if (cachedMethod->conversionFuncOverloadCandidate.status != OverloadCandidate::Status::Applicable) + { + return _failedCoercion(toType, outToExpr, fromExpr); + } + overloadContext.bestCandidateStorage = cachedMethod->conversionFuncOverloadCandidate; + overloadContext.bestCandidate = &overloadContext.bestCandidateStorage; + if (!outToExpr) + { + // If we are not requesting to create an expression, we can return early. + if (outCost) + *outCost = cachedMethod->cost; + return true; + } + else + { + if (cachedMethod->isAmbiguous) + { + overloadContext.bestCandidate = nullptr; + overloadContext.bestCandidates.add(cachedMethod->conversionFuncOverloadCandidate); + } + } + } + + if (!overloadContext.bestCandidate) + { + AddTypeOverloadCandidates(toType, overloadContext); + } // After all of the overload candidates have been added // to the context and processed, we need to see whether @@ -1111,8 +1144,14 @@ namespace Slang // even applicable, because if not, then we shouldn't // consider the conversion as possible. // - if(overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Applicable) + if (overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Applicable) + { + if (!cachedMethod) + { + getShared()->cacheImplicitCastMethod(implicitCastKey, ImplicitCastMethod{}); + } return _failedCoercion(toType, outToExpr, fromExpr); + } // If all of the candidates in `bestCandidates` are applicable, // then we have an ambiguity. @@ -1121,13 +1160,16 @@ namespace Slang // all the conversions available. // ConversionCost bestCost = kConversionCost_Explicit; + ImplicitCastMethod method; for(auto candidate : overloadContext.bestCandidates) { ConversionCost candidateCost = getImplicitConversionCostWithKnownArg( candidate.item.declRef.getDecl(), toType, fromExpr); - - if(candidateCost < bestCost) + if (candidateCost < bestCost) + { + method.conversionFuncOverloadCandidate = candidate; bestCost = candidateCost; + } } // Conceptually, we want to treat the conversion as @@ -1141,9 +1183,15 @@ namespace Slang *outToExpr = CreateErrorExpr(fromExpr); } + if (!cachedMethod) + { + method.isAmbiguous = true; + method.cost = bestCost; + getShared()->cacheImplicitCastMethod(implicitCastKey, method); + } + if(outCost) *outCost = bestCost; - return true; } else if(overloadContext.bestCandidate) @@ -1155,8 +1203,14 @@ namespace Slang // but it wasn't actually usable, so we will check for // that case first. // - if(overloadContext.bestCandidate->status != OverloadCandidate::Status::Applicable) + if (overloadContext.bestCandidate->status != OverloadCandidate::Status::Applicable) + { + if (!cachedMethod) + { + getShared()->cacheImplicitCastMethod(implicitCastKey, ImplicitCastMethod{}); + } return _failedCoercion(toType, outToExpr, fromExpr); + } // Next, we need to look at the implicit conversion // cost associated with the initializer we are invoking. @@ -1265,10 +1319,14 @@ namespace Slang castExpr->arguments.clear(); castExpr->arguments.add(fromExpr); } - + if (!cachedMethod) + getShared()->cacheImplicitCastMethod(implicitCastKey, ImplicitCastMethod{ *overloadContext.bestCandidate, cost }); return true; } - + if (!cachedMethod) + { + getShared()->cacheImplicitCastMethod(implicitCastKey, ImplicitCastMethod{}); + } return _failedCoercion(toType, outToExpr, fromExpr); } diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 0fcf642f8..95528b185 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1997,13 +1997,23 @@ namespace Slang // and filtering them to ones that are applicable // to our "call site" with zero arguments. // - auto type = varDecl->getType(); - OverloadResolveContext overloadContext; overloadContext.loc = varDecl->nameAndLoc.loc; overloadContext.mode = OverloadResolveContext::Mode::JustTrying; overloadContext.sourceScope = m_outerScope; - AddTypeOverloadCandidates(type, overloadContext); + + auto type = varDecl->getType(); + ImplicitCastMethodKey key = ImplicitCastMethodKey(QualType(), type, nullptr); + auto ctorMethod = getShared()->tryGetImplicitCastMethod(key); + if (ctorMethod) + { + overloadContext.bestCandidateStorage = ctorMethod->conversionFuncOverloadCandidate; + overloadContext.bestCandidate = &overloadContext.bestCandidateStorage; + } + else + { + AddTypeOverloadCandidates(type, overloadContext); + } if(overloadContext.bestCandidates.getCount() != 0) { @@ -2015,8 +2025,11 @@ namespace Slang // because if they aren't then we actually just have // an uninitialized varaible. // - if(overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Applicable) + if (overloadContext.bestCandidates[0].status != OverloadCandidate::Status::Applicable) + { + getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{}); return; + } getSink()->diagnose(varDecl, Diagnostics::ambiguousDefaultInitializerForType, type); } @@ -2028,8 +2041,11 @@ namespace Slang // of a type that *doesn't* have a default initializer // isn't actually an error. // - if(overloadContext.bestCandidate->status != OverloadCandidate::Status::Applicable) + if (overloadContext.bestCandidate->status != OverloadCandidate::Status::Applicable) + { + getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{}); return; + } // If we had a single best candidate *and* it was applicable, // then we use it to construct a new initial-value expression @@ -2037,6 +2053,7 @@ namespace Slang // code generation. // varDecl->initExpr = CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate); + getShared()->cacheImplicitCastMethod(key, ImplicitCastMethod{ *overloadContext.bestCandidate, 0}); } } @@ -8847,17 +8864,120 @@ namespace Slang // extensions, and force it to rebuild its cache to include the // new extension we just added. // - // TODO: We should probably just go ahead and add `extDecl` directly - // into the appropriate entry here, and do a similar step on each - // `import`. - // - m_candidateExtensionListsBuilt = false; - m_mapTypeDeclToCandidateExtensions.clear(); + _getCandidateExtensionList(typeDecl, m_mapTypeDeclToCandidateExtensions).add(extDecl); + + // Remove the cached inheritanceInfo about typeDecl, if `extDecl` inherits new types. + bool invalidateSubtypes = false; + if (as<InterfaceDecl>(typeDecl)) + { + // If we are extending an interface, we are effectively extending all types + // that inherits the interface. So we need to remove all inheritance info + // that is related to the interface. + invalidateSubtypes = true; + } + bool hasInheritanceMember = false; + bool hasImplicitCastMember = false; + for (auto member : extDecl->members) + { + if (as<InheritanceDecl>(member)) + { + hasInheritanceMember = true; + } + else if (auto ctorDecl = as<ConstructorDecl>(member)) + { + if (ctorDecl->hasModifier<ImplicitConversionModifier>()) + hasImplicitCastMember = true; + } + } + auto isTypeUpToDate = [this](Type* type) + { + if (auto declRefType = as<DeclRefType>(type)) + { + return m_mapDeclRefToInheritanceInfo.containsKey(declRefType->getDeclRef()); + } + return m_mapTypeToInheritanceInfo.containsKey(type); + }; + auto isInheritanceInfoAffected = [typeDecl](InheritanceInfo& info) + { + for (auto f : info.facets) + if (f.getImpl()->getDeclRef().getDecl() == typeDecl) + { + return true; + } + return false; + }; + if (invalidateSubtypes) + { + decltype(m_mapTypeToInheritanceInfo) newMapTypeToInheritanceInfo; + for (auto& kv : m_mapTypeToInheritanceInfo) + { + if (!isInheritanceInfoAffected(kv.second)) + { + newMapTypeToInheritanceInfo.add(kv.first, kv.second); + } + } + m_mapTypeToInheritanceInfo = _Move(newMapTypeToInheritanceInfo); + } + + ShortList<DeclRef<Decl>, 16> keysToRemove; + for (auto& kv : m_mapDeclRefToInheritanceInfo) + { + // We can confirm the type is affected by the new extension, + // if the declref type points to typeDecl. + if (kv.first.getDecl() == typeDecl) + { + keysToRemove.add(kv.first); + continue; + } + + // If we are extending interface types (and in the future any struct type + // if we decide to have full inheritance support), + // we also need to account for conformant that implements the interface. + if (invalidateSubtypes && isInheritanceInfoAffected(kv.second)) + { + keysToRemove.add(kv.first); + } + } + for (auto& key : keysToRemove) + { + m_mapDeclRefToInheritanceInfo.remove(key); + } + + if (hasInheritanceMember || invalidateSubtypes) + { + ShortList<TypePair, 16> typePairsToRemove; + for (auto& kv : m_mapTypePairToSubtypeWitness) + { + if (!isTypeUpToDate(kv.first.type0) || !isTypeUpToDate(kv.first.type1)) + { + typePairsToRemove.add(kv.first); + } + } + for (auto& key : typePairsToRemove) + { + m_mapTypePairToSubtypeWitness.remove(key); + } + } - // Invalidate the cached inheritanceInfo. - m_mapTypeToInheritanceInfo.clear(); - m_mapDeclRefToInheritanceInfo.clear(); - m_mapTypePairToSubtypeWitness.clear(); + if (hasImplicitCastMember) + { + ShortList<ImplicitCastMethodKey> entriesToRemove; + for (auto& kv : m_mapTypePairToImplicitCastMethod) + { + // Since implicit casts are defined as constructors on the toType, + // we only need to check if the toType is affected by the new extension. + auto declRefType = as<DeclRefType>(kv.first.toType); + + if (!declRefType || declRefType->getDeclRef().getDecl() == typeDecl) + { + entriesToRemove.add(kv.first); + } + } + for (auto& key : entriesToRemove) + { + m_mapTypePairToImplicitCastMethod.remove(key); + } + } } void SharedSemanticsContext::_addCandidateExtensionsFromModule(ModuleDecl* moduleDecl) diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 569e27e7c..1bb450a6a 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -549,6 +549,45 @@ namespace Slang FacetList facets; }; + /// Cached information about how to convert between two types. + struct ImplicitCastMethod + { + OverloadCandidate conversionFuncOverloadCandidate; + ConversionCost cost = kConversionCost_Impossible; + bool isAmbiguous = false; + }; + + struct ImplicitCastMethodKey + { + Type* fromType; // nullptr means default construct. + bool isLValue; + Type* toType; + uint64_t constantVal; + bool isConstant; + HashCode getHashCode() const + { + return combineHash(Slang::getHashCode(fromType), Slang::getHashCode(toType), Slang::getHashCode(constantVal), (HashCode32)isConstant, (HashCode32)isLValue); + } + bool operator == (const ImplicitCastMethodKey& other) const + { + return fromType == other.fromType && toType == other.toType && isConstant == other.isConstant && constantVal == other.constantVal && isLValue == other.isLValue; + } + ImplicitCastMethodKey() = default; + ImplicitCastMethodKey(QualType fromType, Type* toType, Expr* fromExpr) + : fromType(fromType) + , toType(toType) + , constantVal(0) + , isConstant(false) + , isLValue(fromType.isLeftValue) + { + if (auto constInt = as<IntegerLiteralExpr>(fromExpr)) + { + constantVal = constInt->value; + isConstant = true; + } + } + }; + /// Shared state for a semantics-checking session. struct SharedSemanticsContext { @@ -657,6 +696,14 @@ namespace Slang auto pair = TypePair{ sub, sup }; m_mapTypePairToSubtypeWitness[pair] = outWitness; } + ImplicitCastMethod* tryGetImplicitCastMethod(ImplicitCastMethodKey key) + { + return m_mapTypePairToImplicitCastMethod.tryGetValue(key); + } + void cacheImplicitCastMethod(ImplicitCastMethodKey key, ImplicitCastMethod candidate) + { + m_mapTypePairToImplicitCastMethod[key] = candidate; + } private: /// Mapping from type declarations to the known extensiosn that apply to them @@ -776,6 +823,7 @@ namespace Slang Dictionary<Type*, InheritanceInfo> m_mapTypeToInheritanceInfo; Dictionary<DeclRef<Decl>, InheritanceInfo> m_mapDeclRefToInheritanceInfo; Dictionary<TypePair, SubtypeWitness*> m_mapTypePairToSubtypeWitness; + Dictionary<ImplicitCastMethodKey, ImplicitCastMethod> m_mapTypePairToImplicitCastMethod; }; /// Local/scoped state of the semantic-checking system diff --git a/source/slang/slang-check-inheritance.cpp b/source/slang/slang-check-inheritance.cpp index 18624424c..f1dbaef5c 100644 --- a/source/slang/slang-check-inheritance.cpp +++ b/source/slang/slang-check-inheritance.cpp @@ -5,11 +5,6 @@ // related to computing linearized inheritance // information for types and decalrations. -//#include "slang-lookup.h" -//#include "slang-syntax.h" -//#include "slang-ast-synthesis.h" -//#include <limits> - namespace Slang { InheritanceInfo SharedSemanticsContext::getInheritanceInfo(Type* type) @@ -17,7 +12,12 @@ namespace Slang // We cache the computed inheritance information for types, // and re-use that information whenever possible. - if(auto found = m_mapTypeToInheritanceInfo.tryGetValue(type)) + // DeclRefTypes will have their inheritance info cached in m_mapDeclRefToInheritanceInfo. + if (auto declRefType = as<DeclRefType>(type)) + return _getInheritanceInfo(declRefType->getDeclRef(), declRefType); + + // Non ordinary types are cached on m_mapTypeToInheritanceInfo. + if (auto found = m_mapTypeToInheritanceInfo.tryGetValue(type)) return *found; // Note: we install a null pointer into the dictionary to act @@ -32,9 +32,6 @@ namespace Slang auto info = _calcInheritanceInfo(type); m_mapTypeToInheritanceInfo[type] = info; - getSession()->m_typeDictionarySize = Math::Max( - getSession()->m_typeDictionarySize, (int)m_mapTypeToInheritanceInfo.getCount()); - return info; } @@ -68,6 +65,9 @@ namespace Slang auto info = _calcInheritanceInfo(declRef, declRefType); m_mapDeclRefToInheritanceInfo[declRef] = info; + getSession()->m_typeDictionarySize = Math::Max( + getSession()->m_typeDictionarySize, (int)m_mapDeclRefToInheritanceInfo.getCount()); + return info; } diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 428c9bf66..02dab6dfa 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -598,13 +598,12 @@ Result linkAndOptimizeIR( { applySparseConditionalConstantPropagation(irModule, codeGenContext->getSink()); } - eliminateDeadCode(irModule, deadCodeEliminationOptions); - validateIRModuleIfEnabled(codeGenContext, irModule); // Inline calls to any functions marked with [__unsafeInlineEarly] again, // since we may be missing out cases prevented by the functions that we just specialzied. performMandatoryEarlyInlining(irModule); + eliminateDeadCode(irModule, deadCodeEliminationOptions); // Unroll loops. if (!fastIRSimplificationOptions.minimalOptimization) @@ -691,11 +690,7 @@ Result linkAndOptimizeIR( // do unnecessary work to lower them. unpinWitnessTables(irModule); - if (fastIRSimplificationOptions.minimalOptimization) - { - eliminateDeadCode(irModule, deadCodeEliminationOptions); - } - else + if (!fastIRSimplificationOptions.minimalOptimization) { simplifyIR(targetProgram, irModule, fastIRSimplificationOptions, sink); } diff --git a/source/slang/slang-ir-autodiff-fwd.cpp b/source/slang/slang-ir-autodiff-fwd.cpp index 69e2832d3..299f4655b 100644 --- a/source/slang/slang-ir-autodiff-fwd.cpp +++ b/source/slang/slang-ir-autodiff-fwd.cpp @@ -1655,7 +1655,8 @@ SlangResult ForwardDiffTranscriber::prepareFuncForForwardDiff(IRFunc* func) if (SLANG_SUCCEEDED(result)) { disableIRValidationAtInsert(); - simplifyFunc(autoDiffSharedContext->targetProgram, func, IRSimplificationOptions::getDefault(autoDiffSharedContext->targetProgram)); + auto simplifyOptions = IRSimplificationOptions::getDefault(nullptr); + simplifyFunc(autoDiffSharedContext->targetProgram, func, simplifyOptions); enableIRValidationAtInsert(); } return result; diff --git a/source/slang/slang-ir-ssa-simplification.cpp b/source/slang/slang-ir-ssa-simplification.cpp index 6c02734b5..2a9c0da43 100644 --- a/source/slang/slang-ir-ssa-simplification.cpp +++ b/source/slang/slang-ir-ssa-simplification.cpp @@ -18,7 +18,7 @@ namespace Slang IRSimplificationOptions IRSimplificationOptions::getDefault(TargetProgram* targetProgram) { IRSimplificationOptions result; - result.minimalOptimization = targetProgram->getOptionSet().shouldPerformMinimumOptimizations(); + result.minimalOptimization = targetProgram ? targetProgram->getOptionSet().shouldPerformMinimumOptimizations() : false; if (result.minimalOptimization) result.cfgOptions = CFGSimplificationOptions::getFast(); else @@ -30,7 +30,7 @@ namespace Slang IRSimplificationOptions IRSimplificationOptions::getFast(TargetProgram* targetProgram) { IRSimplificationOptions result; - result.minimalOptimization = targetProgram->getOptionSet().shouldPerformMinimumOptimizations(); + result.minimalOptimization = targetProgram ? targetProgram->getOptionSet().shouldPerformMinimumOptimizations() : false; result.cfgOptions = CFGSimplificationOptions::getFast(); result.peepholeOptions = PeepholeOptimizationOptions(); return result; diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 2d3f35557..dcc0f521d 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -2334,199 +2334,196 @@ SlangResult OptionsParser::_parse( // If a repro has been loaded, then many of the following options will overwrite // what was set up. So for now they are ignored, and only parameters set as part // of the loop work if they are after -load-repro - if (m_hasLoadedRepro) + if (!m_hasLoadedRepro) { - return SLANG_OK; - } - - // As a compatability feature, if the user didn't list any explicit entry - // point names, *and* they are compiling a single translation unit, *and* they - // have either specified a stage, or we can assume one from the naming - // of the translation unit, then we assume they wanted to compile a single - // entry point named `main`. - // - if (m_rawEntryPoints.getCount() == 0 - && m_rawTranslationUnits.getCount() == 1 - && (m_defaultEntryPoint.stage != Stage::Unknown - || m_rawTranslationUnits[0].impliedStage != Stage::Unknown)) - { - RawEntryPoint entry; - entry.name = "main"; - entry.translationUnitIndex = 0; - m_rawEntryPoints.add(entry); - } - - // If the user (manually or implicitly) specified only a single entry point, - // then we allow the associated stage to be specified either before or after - // the entry point. This means that if there is a stage attached - // to the "default" entry point, we should copy it over to the - // explicit one. - // - if (m_rawEntryPoints.getCount() == 1) - { - if (m_defaultEntryPoint.stage != Stage::Unknown) - { - setStage(getCurrentEntryPoint(), m_defaultEntryPoint.stage); - } - - if (m_defaultEntryPoint.redundantStageSet) - getCurrentEntryPoint()->redundantStageSet = true; - if (m_defaultEntryPoint.conflictingStagesSet) - getCurrentEntryPoint()->conflictingStagesSet = true; - } - else - { - // If the "default" entry point has had a stage (or - // other state, if we add other per-entry-point state) - // specified, but there is more than one entry point, - // then that state doesn't apply to anything and we - // should issue an error to tell the user something - // funky is going on. + // As a compatability feature, if the user didn't list any explicit entry + // point names, *and* they are compiling a single translation unit, *and* they + // have either specified a stage, or we can assume one from the naming + // of the translation unit, then we assume they wanted to compile a single + // entry point named `main`. // - if (m_defaultEntryPoint.stage != Stage::Unknown) + if (m_rawEntryPoints.getCount() == 0 + && m_rawTranslationUnits.getCount() == 1 + && (m_defaultEntryPoint.stage != Stage::Unknown + || m_rawTranslationUnits[0].impliedStage != Stage::Unknown)) + { + RawEntryPoint entry; + entry.name = "main"; + entry.translationUnitIndex = 0; + m_rawEntryPoints.add(entry); + } + + // If the user (manually or implicitly) specified only a single entry point, + // then we allow the associated stage to be specified either before or after + // the entry point. This means that if there is a stage attached + // to the "default" entry point, we should copy it over to the + // explicit one. + // + if (m_rawEntryPoints.getCount() == 1) { - if (m_rawEntryPoints.getCount() == 0) + if (m_defaultEntryPoint.stage != Stage::Unknown) { - m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseNoEntryPoints); + setStage(getCurrentEntryPoint(), m_defaultEntryPoint.stage); } - else + + if (m_defaultEntryPoint.redundantStageSet) + getCurrentEntryPoint()->redundantStageSet = true; + if (m_defaultEntryPoint.conflictingStagesSet) + getCurrentEntryPoint()->conflictingStagesSet = true; + } + else + { + // If the "default" entry point has had a stage (or + // other state, if we add other per-entry-point state) + // specified, but there is more than one entry point, + // then that state doesn't apply to anything and we + // should issue an error to tell the user something + // funky is going on. + // + if (m_defaultEntryPoint.stage != Stage::Unknown) { - m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseBeforeAllEntryPoints); + if (m_rawEntryPoints.getCount() == 0) + { + m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseNoEntryPoints); + } + else + { + m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseBeforeAllEntryPoints); + } } } - } - // Slang requires that every explicit entry point indicate the translation - // unit it comes from. If there is only one translation unit specified, - // then implicitly all entry points come from it. - // - if (m_translationUnitCount == 1) - { - for (auto& entryPoint : m_rawEntryPoints) + // Slang requires that every explicit entry point indicate the translation + // unit it comes from. If there is only one translation unit specified, + // then implicitly all entry points come from it. + // + if (m_translationUnitCount == 1) { - entryPoint.translationUnitIndex = 0; + for (auto& entryPoint : m_rawEntryPoints) + { + entryPoint.translationUnitIndex = 0; + } } - } - else if (m_frontEndReq->additionalLoadedModules && - m_frontEndReq->additionalLoadedModules->getCount() == 0) - { - // Otherwise, we require that all entry points be specified after - // the translation unit to which tye belong. - bool anyEntryPointWithoutTranslationUnit = false; - for (auto& entryPoint : m_rawEntryPoints) + else if (m_frontEndReq->additionalLoadedModules && + m_frontEndReq->additionalLoadedModules->getCount() == 0) { - // Skip entry points that are already associated with a translation unit... - if (entryPoint.translationUnitIndex != -1) - continue; + // Otherwise, we require that all entry points be specified after + // the translation unit to which tye belong. + bool anyEntryPointWithoutTranslationUnit = false; + for (auto& entryPoint : m_rawEntryPoints) + { + // Skip entry points that are already associated with a translation unit... + if (entryPoint.translationUnitIndex != -1) + continue; - anyEntryPointWithoutTranslationUnit = true; - } - if (anyEntryPointWithoutTranslationUnit) - { - m_sink->diagnose(SourceLoc(), Diagnostics::entryPointsNeedToBeAssociatedWithTranslationUnits); - return SLANG_FAIL; + anyEntryPointWithoutTranslationUnit = true; + } + if (anyEntryPointWithoutTranslationUnit) + { + m_sink->diagnose(SourceLoc(), Diagnostics::entryPointsNeedToBeAssociatedWithTranslationUnits); + return SLANG_FAIL; + } } - } - // Now that entry points are associated with translation units, - // we can make one additional pass where if an entry point has - // no specified stage, but the nameing of its translation unit - // implies a stage, we will use that (a manual `-stage` annotation - // will always win out in such a case). - // - for (auto& rawEntryPoint : m_rawEntryPoints) - { - // Skip entry points that already have a stage. - if (rawEntryPoint.stage != Stage::Unknown) - continue; + // Now that entry points are associated with translation units, + // we can make one additional pass where if an entry point has + // no specified stage, but the nameing of its translation unit + // implies a stage, we will use that (a manual `-stage` annotation + // will always win out in such a case). + // + for (auto& rawEntryPoint : m_rawEntryPoints) + { + // Skip entry points that already have a stage. + if (rawEntryPoint.stage != Stage::Unknown) + continue; - // Sanity check: don't process entry points with no associated translation unit. - if (rawEntryPoint.translationUnitIndex == -1) - continue; + // Sanity check: don't process entry points with no associated translation unit. + if (rawEntryPoint.translationUnitIndex == -1) + continue; - auto impliedStage = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].impliedStage; - if (impliedStage != Stage::Unknown) - rawEntryPoint.stage = impliedStage; - } + auto impliedStage = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].impliedStage; + if (impliedStage != Stage::Unknown) + rawEntryPoint.stage = impliedStage; + } - // Note: it is possible that some entry points still won't have associated - // stages at this point, but we don't want to error out here, because - // those entry points might get stages later, as part of semantic checking, - // if the corresponding function has a `[shader("...")]` attribute. + // Note: it is possible that some entry points still won't have associated + // stages at this point, but we don't want to error out here, because + // those entry points might get stages later, as part of semantic checking, + // if the corresponding function has a `[shader("...")]` attribute. - // Now that we've tried to establish stages for entry points, we can - // issue diagnostics for cases where stages were set redundantly or - // in conflicting ways. - // - for (auto& rawEntryPoint : m_rawEntryPoints) - { - if (rawEntryPoint.conflictingStagesSet) - { - m_sink->diagnose(SourceLoc(), Diagnostics::conflictingStagesForEntryPoint, rawEntryPoint.name); - } - else if (rawEntryPoint.redundantStageSet) - { - m_sink->diagnose(SourceLoc(), Diagnostics::sameStageSpecifiedMoreThanOnce, rawEntryPoint.stage, rawEntryPoint.name); - } - else if (rawEntryPoint.translationUnitIndex != -1) + // Now that we've tried to establish stages for entry points, we can + // issue diagnostics for cases where stages were set redundantly or + // in conflicting ways. + // + for (auto& rawEntryPoint : m_rawEntryPoints) { - // As a quality-of-life feature, if the file name implies a particular - // stage, but the user manually specified something different for - // their entry point, give a warning in case they made a mistake. - - auto& rawTranslationUnit = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex]; - if (rawTranslationUnit.impliedStage != Stage::Unknown - && rawEntryPoint.stage != Stage::Unknown - && rawTranslationUnit.impliedStage != rawEntryPoint.stage) + if (rawEntryPoint.conflictingStagesSet) + { + m_sink->diagnose(SourceLoc(), Diagnostics::conflictingStagesForEntryPoint, rawEntryPoint.name); + } + else if (rawEntryPoint.redundantStageSet) { - m_sink->diagnose(SourceLoc(), Diagnostics::explicitStageDoesntMatchImpliedStage, rawEntryPoint.name, rawEntryPoint.stage, rawTranslationUnit.impliedStage); + m_sink->diagnose(SourceLoc(), Diagnostics::sameStageSpecifiedMoreThanOnce, rawEntryPoint.stage, rawEntryPoint.name); + } + else if (rawEntryPoint.translationUnitIndex != -1) + { + // As a quality-of-life feature, if the file name implies a particular + // stage, but the user manually specified something different for + // their entry point, give a warning in case they made a mistake. + + auto& rawTranslationUnit = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex]; + if (rawTranslationUnit.impliedStage != Stage::Unknown + && rawEntryPoint.stage != Stage::Unknown + && rawTranslationUnit.impliedStage != rawEntryPoint.stage) + { + m_sink->diagnose(SourceLoc(), Diagnostics::explicitStageDoesntMatchImpliedStage, rawEntryPoint.name, rawEntryPoint.stage, rawTranslationUnit.impliedStage); + } } } - } - // If the user is requesting code generation via pass-through, - // then any entry points they specify need to have a stage set, - // because fxc/dxc/glslang don't have a facility for taking - // a named entry point and pulling its stage from an attribute. - // - if (_passThroughRequiresStage(m_requestImpl->m_passThrough)) - { - for (auto& rawEntryPoint : m_rawEntryPoints) + // If the user is requesting code generation via pass-through, + // then any entry points they specify need to have a stage set, + // because fxc/dxc/glslang don't have a facility for taking + // a named entry point and pulling its stage from an attribute. + // + if (_passThroughRequiresStage(m_requestImpl->m_passThrough)) { - if (rawEntryPoint.stage == Stage::Unknown) + for (auto& rawEntryPoint : m_rawEntryPoints) { - m_sink->diagnose(SourceLoc(), Diagnostics::noStageSpecifiedInPassThroughMode, rawEntryPoint.name); + if (rawEntryPoint.stage == Stage::Unknown) + { + m_sink->diagnose(SourceLoc(), Diagnostics::noStageSpecifiedInPassThroughMode, rawEntryPoint.name); + } } } - } - // We now have inferred enough information to add the - // entry points to our compile request. - // - for (auto& rawEntryPoint : m_rawEntryPoints) - { - if (rawEntryPoint.translationUnitIndex < 0) - continue; + // We now have inferred enough information to add the + // entry points to our compile request. + // + for (auto& rawEntryPoint : m_rawEntryPoints) + { + if (rawEntryPoint.translationUnitIndex < 0) + continue; - auto translationUnitID = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].translationUnitID; + auto translationUnitID = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].translationUnitID; - List<const char*> specializationArgs; - for (auto& arg : rawEntryPoint.specializationArgs) - specializationArgs.add(arg.getBuffer()); + List<const char*> specializationArgs; + for (auto& arg : rawEntryPoint.specializationArgs) + specializationArgs.add(arg.getBuffer()); - int entryPointID = m_compileRequest->addEntryPointEx( - translationUnitID, - rawEntryPoint.name.begin(), - SlangStage(rawEntryPoint.stage), - (int)specializationArgs.getCount(), - specializationArgs.getBuffer()); + int entryPointID = m_compileRequest->addEntryPointEx( + translationUnitID, + rawEntryPoint.name.begin(), + SlangStage(rawEntryPoint.stage), + (int)specializationArgs.getCount(), + specializationArgs.getBuffer()); - rawEntryPoint.entryPointID = entryPointID; - } + rawEntryPoint.entryPointID = entryPointID; + } - // We are going to build a mapping from target formats to the - // target that handles that format. - Dictionary<CodeGenTarget, int> mapFormatToTargetIndex; + // We are going to build a mapping from target formats to the + // target that handles that format. + Dictionary<CodeGenTarget, int> mapFormatToTargetIndex; // If there was no explicit `-target` specified, then we will look // at the `-o` options to see what we can infer. @@ -2547,60 +2544,60 @@ SlangResult OptionsParser::_parse( if (impliedFormat == CodeGenTarget::Unknown) continue; - int targetIndex = 0; - if (!mapFormatToTargetIndex.tryGetValue(impliedFormat, targetIndex)) - { - targetIndex = (int)m_rawTargets.getCount(); + int targetIndex = 0; + if (!mapFormatToTargetIndex.tryGetValue(impliedFormat, targetIndex)) + { + targetIndex = (int)m_rawTargets.getCount(); - RawTarget rawTarget; - rawTarget.format = impliedFormat; - m_rawTargets.add(rawTarget); + RawTarget rawTarget; + rawTarget.format = impliedFormat; + m_rawTargets.add(rawTarget); - mapFormatToTargetIndex[impliedFormat] = targetIndex; - } + mapFormatToTargetIndex[impliedFormat] = targetIndex; + } - rawOutput.targetIndex = targetIndex; + rawOutput.targetIndex = targetIndex; + } } } - } - else - { - // If there were explicit targets, then we will use those, but still - // build up our mapping. We should object if the same target format - // is specified more than once (just because of the ambiguities - // it will create). - // - int targetCount = (int)m_rawTargets.getCount(); - for (int targetIndex = 0; targetIndex < targetCount; ++targetIndex) + else { - auto format = m_rawTargets[targetIndex].format; - - if (mapFormatToTargetIndex.containsKey(format)) - { - m_sink->diagnose(SourceLoc(), Diagnostics::duplicateTargets, format); - } - else + // If there were explicit targets, then we will use those, but still + // build up our mapping. We should object if the same target format + // is specified more than once (just because of the ambiguities + // it will create). + // + int targetCount = (int)m_rawTargets.getCount(); + for (int targetIndex = 0; targetIndex < targetCount; ++targetIndex) { - mapFormatToTargetIndex[format] = targetIndex; + auto format = m_rawTargets[targetIndex].format; + + if (mapFormatToTargetIndex.containsKey(format)) + { + m_sink->diagnose(SourceLoc(), Diagnostics::duplicateTargets, format); + } + else + { + mapFormatToTargetIndex[format] = targetIndex; + } } } - } - // If we weren't able to infer any targets from output paths (perhaps - // because there were no output paths), but there was a profile specified, - // then we can try to infer a target from the profile. - // - if (m_rawTargets.getCount() == 0 - && m_defaultTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown - && !m_defaultTarget.conflictingProfilesSet) - { - // Let's see if the chosen profile allows us to infer - // the code gen target format that the user probably meant. + // If we weren't able to infer any targets from output paths (perhaps + // because there were no output paths), but there was a profile specified, + // then we can try to infer a target from the profile. // - CodeGenTarget inferredFormat = CodeGenTarget::Unknown; - auto profileVersion = m_defaultTarget.optionSet.getProfileVersion(); - switch (Profile(profileVersion).getFamily()) + if (m_rawTargets.getCount() == 0 + && m_defaultTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown + && !m_defaultTarget.conflictingProfilesSet) { + // Let's see if the chosen profile allows us to infer + // the code gen target format that the user probably meant. + // + CodeGenTarget inferredFormat = CodeGenTarget::Unknown; + auto profileVersion = m_defaultTarget.optionSet.getProfileVersion(); + switch (Profile(profileVersion).getFamily()) + { default: break; @@ -2630,128 +2627,128 @@ SlangResult OptionsParser::_parse( inferredFormat = CodeGenTarget::DXBytecode; } break; - } + } - if (inferredFormat != CodeGenTarget::Unknown) - { - RawTarget rawTarget; - rawTarget.format = inferredFormat; - m_rawTargets.add(rawTarget); + if (inferredFormat != CodeGenTarget::Unknown) + { + RawTarget rawTarget; + rawTarget.format = inferredFormat; + m_rawTargets.add(rawTarget); + } } - } - // Similar to the case for entry points, if there is a single target, - // then we allow some of its options to come from the "default" - // target state. - auto defaultTargetFloatingPointMode = m_defaultTarget.optionSet.getEnumOption<FloatingPointMode>(CompilerOptionName::FloatingPointMode); + // Similar to the case for entry points, if there is a single target, + // then we allow some of its options to come from the "default" + // target state. + auto defaultTargetFloatingPointMode = m_defaultTarget.optionSet.getEnumOption<FloatingPointMode>(CompilerOptionName::FloatingPointMode); - if (m_rawTargets.getCount() == 1) - { - m_rawTargets[0].optionSet.overrideWith(m_defaultTarget.optionSet); - } - else - { - // If the "default" target has had a profile (or other state) - // specified, but there is != 1 taget, then that state doesn't - // apply to anythign and we should give the user an error. - // - if (m_defaultTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown) + if (m_rawTargets.getCount() == 1) + { + m_rawTargets[0].optionSet.overrideWith(m_defaultTarget.optionSet); + } + else { - if (m_rawTargets.getCount() == 0) + // If the "default" target has had a profile (or other state) + // specified, but there is != 1 taget, then that state doesn't + // apply to anythign and we should give the user an error. + // + if (m_defaultTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown) { - // This should only happen if there were multiple `-profile` options, - // so we didn't try to infer a target, or if the `-profile` option - // somehow didn't imply a target. - // - m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseNoTargets); + if (m_rawTargets.getCount() == 0) + { + // This should only happen if there were multiple `-profile` options, + // so we didn't try to infer a target, or if the `-profile` option + // somehow didn't imply a target. + // + m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseNoTargets); + } + else + { + m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseBeforeAllTargets); + } } - else + + if (defaultTargetFloatingPointMode != FloatingPointMode::Default) { - m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseBeforeAllTargets); + if (m_rawTargets.getCount() == 0) + { + m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets); + } + else + { + m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets); + } } - } - if (defaultTargetFloatingPointMode != FloatingPointMode::Default) + } + for (auto& rawTarget : m_rawTargets) { - if (m_rawTargets.getCount() == 0) + if (rawTarget.conflictingProfilesSet) { - m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets); + m_sink->diagnose(SourceLoc(), Diagnostics::conflictingProfilesSpecifiedForTarget, rawTarget.format); } - else + else if (rawTarget.redundantProfileSet) { - m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets); + m_sink->diagnose(SourceLoc(), Diagnostics::sameProfileSpecifiedMoreThanOnce, rawTarget.optionSet.getProfileVersion(), rawTarget.format); } } - } - for (auto& rawTarget : m_rawTargets) - { - if (rawTarget.conflictingProfilesSet) - { - m_sink->diagnose(SourceLoc(), Diagnostics::conflictingProfilesSpecifiedForTarget, rawTarget.format); - } - else if (rawTarget.redundantProfileSet) - { - m_sink->diagnose(SourceLoc(), Diagnostics::sameProfileSpecifiedMoreThanOnce, rawTarget.optionSet.getProfileVersion(), rawTarget.format); - } - } - - // TODO: do we need to require that a target must have a profile specified, - // or will we continue to allow the profile to be inferred from the target? - - // We now have enough information to go ahead and declare the targets - // through the Slang API: - // - for (auto& rawTarget : m_rawTargets) - { - int targetID = m_compileRequest->addCodeGenTarget(SlangCompileTarget(rawTarget.format)); - rawTarget.targetID = targetID; + // TODO: do we need to require that a target must have a profile specified, + // or will we continue to allow the profile to be inferred from the target? - if (rawTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown) - { - m_compileRequest->setTargetProfile(targetID, SlangProfileID(Profile(rawTarget.optionSet.getProfileVersion()).raw)); - } - for (auto atom : rawTarget.optionSet.getArray(CompilerOptionName::Capability)) + // We now have enough information to go ahead and declare the targets + // through the Slang API: + // + for (auto& rawTarget : m_rawTargets) { - m_requestImpl->addTargetCapability(targetID, SlangCapabilityID(atom.intValue)); - } + int targetID = m_compileRequest->addCodeGenTarget(SlangCompileTarget(rawTarget.format)); + rawTarget.targetID = targetID; - auto floatingPointMode = rawTarget.optionSet.getEnumOption<FloatingPointMode>(CompilerOptionName::FloatingPointMode); - if (floatingPointMode != FloatingPointMode::Default) - { - m_compileRequest->setTargetFloatingPointMode(targetID, SlangFloatingPointMode(floatingPointMode)); - } + if (rawTarget.optionSet.getProfileVersion() != ProfileVersion::Unknown) + { + m_compileRequest->setTargetProfile(targetID, SlangProfileID(Profile(rawTarget.optionSet.getProfileVersion()).raw)); + } + for (auto atom : rawTarget.optionSet.getArray(CompilerOptionName::Capability)) + { + m_requestImpl->addTargetCapability(targetID, SlangCapabilityID(atom.intValue)); + } - if (rawTarget.optionSet.shouldUseScalarLayout()) - { - m_compileRequest->setTargetForceGLSLScalarBufferLayout(targetID, true); + auto floatingPointMode = rawTarget.optionSet.getEnumOption<FloatingPointMode>(CompilerOptionName::FloatingPointMode); + if (floatingPointMode != FloatingPointMode::Default) + { + m_compileRequest->setTargetFloatingPointMode(targetID, SlangFloatingPointMode(floatingPointMode)); + } + + if (rawTarget.optionSet.shouldUseScalarLayout()) + { + m_compileRequest->setTargetForceGLSLScalarBufferLayout(targetID, true); + } } - } - // Next we need to sort out the output files specified with `-o`, and - // figure out which entry point and/or target they apply to. - // - // If there is only a single entry point, then that is automatically - // the entry point that should be associated with all outputs. - // - if (m_rawEntryPoints.getCount() == 1) - { - for (auto& rawOutput : m_rawOutputs) + // Next we need to sort out the output files specified with `-o`, and + // figure out which entry point and/or target they apply to. + // + // If there is only a single entry point, then that is automatically + // the entry point that should be associated with all outputs. + // + if (m_rawEntryPoints.getCount() == 1) { - rawOutput.entryPointIndex = 0; + for (auto& rawOutput : m_rawOutputs) + { + rawOutput.entryPointIndex = 0; + } } - } - // - // Similarly, if there is only one target, then all outputs must - // implicitly appertain to that target. - // - if (m_rawTargets.getCount() == 1) - { - for (auto& rawOutput : m_rawOutputs) + // + // Similarly, if there is only one target, then all outputs must + // implicitly appertain to that target. + // + if (m_rawTargets.getCount() == 1) { - rawOutput.targetIndex = 0; + for (auto& rawOutput : m_rawOutputs) + { + rawOutput.targetIndex = 0; + } } - } // If we don't have any raw outputs but do have a raw target, // add an empty' rawOutput for certain targets where the expected behavior is obvious. @@ -2773,55 +2770,55 @@ SlangResult OptionsParser::_parse( m_rawOutputs.add(rawOutput); } - // Consider the output files specified via `-o` and try to figure - // out how to deal with them. - // - for (auto& rawOutput : m_rawOutputs) - { - // For now, most output formats need to be tightly bound to - // both a target and an entry point. - - // If an output doesn't have a target associated with - // it, then search for the target with the matching format. - if (rawOutput.targetIndex == -1) + // Consider the output files specified via `-o` and try to figure + // out how to deal with them. + // + for (auto& rawOutput : m_rawOutputs) { - auto impliedFormat = rawOutput.impliedFormat; - int targetIndex = -1; + // For now, most output formats need to be tightly bound to + // both a target and an entry point. - if (impliedFormat == CodeGenTarget::Unknown) - { - - // If we hit this case, then it means that we need to pick the - // target to assocaite with this output based on its implied - // format, but the file path doesn't direclty imply a format - // (it doesn't have a suffix like `.spv` that tells us what to write). - // - m_sink->diagnose(SourceLoc(), Diagnostics::cannotDeduceOutputFormatFromPath, rawOutput.path); - } - else if (mapFormatToTargetIndex.tryGetValue(rawOutput.impliedFormat, targetIndex)) + // If an output doesn't have a target associated with + // it, then search for the target with the matching format. + if (rawOutput.targetIndex == -1) { - rawOutput.targetIndex = targetIndex; - } - else - { - m_sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToTarget, rawOutput.path, rawOutput.impliedFormat); + auto impliedFormat = rawOutput.impliedFormat; + int targetIndex = -1; + + if (impliedFormat == CodeGenTarget::Unknown) + { + + // If we hit this case, then it means that we need to pick the + // target to assocaite with this output based on its implied + // format, but the file path doesn't direclty imply a format + // (it doesn't have a suffix like `.spv` that tells us what to write). + // + m_sink->diagnose(SourceLoc(), Diagnostics::cannotDeduceOutputFormatFromPath, rawOutput.path); + } + else if (mapFormatToTargetIndex.tryGetValue(rawOutput.impliedFormat, targetIndex)) + { + rawOutput.targetIndex = targetIndex; + } + else + { + m_sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToTarget, rawOutput.path, rawOutput.impliedFormat); + } } - } - // We won't do any searching to match an output file - // with an entry point, since the case of a single entry - // point was handled above, and the user is expected to - // follow the ordering rules when using multiple entry points. - if (rawOutput.entryPointIndex == -1) - { - if (rawOutput.targetIndex != -1) + // We won't do any searching to match an output file + // with an entry point, since the case of a single entry + // point was handled above, and the user is expected to + // follow the ordering rules when using multiple entry points. + if (rawOutput.entryPointIndex == -1) { - auto outputFormat = m_rawTargets[rawOutput.targetIndex].format; - // Here we check whether the given output format supports multiple entry points - // When we add targets with support for multiple entry points, - // we should update this switch with those new formats - switch (outputFormat) + if (rawOutput.targetIndex != -1) { + auto outputFormat = m_rawTargets[rawOutput.targetIndex].format; + // Here we check whether the given output format supports multiple entry points + // When we add targets with support for multiple entry points, + // we should update this switch with those new formats + switch (outputFormat) + { case CodeGenTarget::CPPSource: case CodeGenTarget::PTX: case CodeGenTarget::CUDASource: @@ -2857,12 +2854,12 @@ SlangResult OptionsParser::_parse( m_sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToEntryPoint, rawOutput.path); } break; + } } } } } - // Now that we've diagnosed the output paths, we can add them // to the compile request at the appropriate locations. // @@ -2918,6 +2915,7 @@ SlangResult OptionsParser::_parse( } } + // Copy all settings from linkage to targets. for (auto target : linkage->targets) { |
