summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2024-05-29 18:01:11 -0700
committerGitHub <noreply@github.com>2024-05-29 18:01:11 -0700
commitefdbb954c57b89362e390f955d45f90e59d66878 (patch)
tree7b47d6e52d2de666af99f66a2fd3a5dc387ca5cc /source
parent83f176ba8a3bae5533470aed6a90663653f894b8 (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.cpp2
-rw-r--r--source/slang/hlsl.meta.slang2
-rw-r--r--source/slang/slang-artifact-output-util.cpp6
-rw-r--r--source/slang/slang-ast-support-types.h6
-rw-r--r--source/slang/slang-check-conversion.cpp74
-rw-r--r--source/slang/slang-check-decl.cpp150
-rw-r--r--source/slang/slang-check-impl.h48
-rw-r--r--source/slang/slang-check-inheritance.cpp18
-rw-r--r--source/slang/slang-emit.cpp9
-rw-r--r--source/slang/slang-ir-autodiff-fwd.cpp3
-rw-r--r--source/slang/slang-ir-ssa-simplification.cpp4
-rw-r--r--source/slang/slang-options.cpp676
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)
{