summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-capabilities.capdef13
-rw-r--r--source/slang/slang-capability.cpp38
-rw-r--r--source/slang/slang-capability.h18
-rw-r--r--source/slang/slang-check-shader.cpp4
-rwxr-xr-xsource/slang/slang-compiler.h2
-rw-r--r--source/slang/slang-diagnostic-defs.h3
-rw-r--r--source/slang/slang-emit-spirv-ops.h5
-rw-r--r--source/slang/slang-emit-spirv.cpp83
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp45
-rw-r--r--source/slang/slang-ir-inst-defs.h5
-rw-r--r--source/slang/slang-ir-insts.h17
-rw-r--r--source/slang/slang-ir-restructure.cpp1
-rw-r--r--source/slang/slang-ir-spirv-legalize.cpp122
-rw-r--r--source/slang/slang-ir-spirv-legalize.h21
-rw-r--r--source/slang/slang-ir-synthesize-active-mask.cpp19
-rw-r--r--source/slang/slang-ir.cpp2
-rw-r--r--source/slang/slang-lower-to-ir.cpp16
-rw-r--r--source/slang/slang-profile-defs.h16
-rw-r--r--source/slang/slang-spirv-val.cpp2
-rw-r--r--source/slang/slang.cpp50
20 files changed, 426 insertions, 56 deletions
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef
index 7567dd617..6d4e5b3f4 100644
--- a/source/slang/slang-capabilities.capdef
+++ b/source/slang/slang-capabilities.capdef
@@ -129,6 +129,9 @@ def _GLSL_460 : _GLSL_450;
// metal versions
def metallib_2_3 : metal;
def metallib_2_4 : metallib_2_3;
+def metallib_3_0 : metallib_2_4;
+def metallib_3_1 : metallib_3_0;
+alias metallib_latest = metallib_3_1;
// hlsl versions
def _sm_4_0 : hlsl;
@@ -221,6 +224,7 @@ def SPV_KHR_shader_clock : spirv_1_0;
def SPV_NV_shader_image_footprint : spirv_1_0;
def SPV_GOOGLE_user_type : spirv_1_0;
def SPV_NV_compute_shader_derivatives : spirv_1_0;
+def SPV_EXT_demote_to_helper_invocation : spirv_1_4;
// SPIRV Capabilities.
@@ -252,6 +256,7 @@ def spvRayQueryPositionFetchKHR : SPV_KHR_ray_tracing_position_fetch;
def spvShaderInvocationReorderNV : SPV_NV_shader_invocation_reorder;
def spvShaderClockKHR : SPV_KHR_shader_clock;
def spvShaderNonUniform : spirv_1_5;
+def spvDemoteToHelperInvocationEXT : SPV_EXT_demote_to_helper_invocation;
// The following capabilities all pertain to how ray tracing shaders are translated
// to GLSL, where there are two different extensions that can provide the core
@@ -593,6 +598,14 @@ alias DX_6_7 = sm_6_7;
alias METAL_2_3 = metallib_2_3;
alias METAL_2_4 = metallib_2_4;
+alias SPIRV_1_0 = spirv_1_0;
+alias SPIRV_1_1 = spirv_1_1;
+alias SPIRV_1_2 = spirv_1_2;
+alias SPIRV_1_3 = spirv_1_3;
+alias SPIRV_1_4 = spirv_1_4;
+alias SPIRV_1_5 = spirv_1_5;
+alias SPIRV_1_6 = spirv_1_6;
+
alias sm_2_0_GLSL_140 = _GLSL_140 + sm_4_0 | sm_4_0;
alias sm_2_0_GLSL_400 = _GLSL_400 + sm_4_0 | sm_4_0;
alias appendstructuredbuffer = sm_5_0 + raytracingstages_compute_fragment;
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp
index e77901b5b..c717a0ab9 100644
--- a/source/slang/slang-capability.cpp
+++ b/source/slang/slang-capability.cpp
@@ -112,6 +112,20 @@ bool isDirectChildOfAbstractAtom(CapabilityAtom name)
return _getInfo(name).abstractBase != CapabilityName::Invalid;
}
+bool isTargetVersionAtom(CapabilityName name)
+{
+ if (name >= CapabilityName::spirv_1_0 && name <= getLatestSpirvAtom())
+ return true;
+ if (name >= CapabilityName::metallib_2_3 && name <= getLatestMetalAtom())
+ return true;
+ return false;
+}
+
+bool isSpirvExtensionAtom(CapabilityName name)
+{
+ return UnownedStringSlice(_getInfo(name).name).startsWith("SPV_");
+}
+
bool lookupCapabilityName(const UnownedStringSlice& str, CapabilityName& value);
CapabilityName findCapabilityName(UnownedStringSlice const& name)
@@ -122,6 +136,30 @@ CapabilityName findCapabilityName(UnownedStringSlice const& name)
return result;
}
+CapabilityName getLatestSpirvAtom()
+{
+ static CapabilityName result = CapabilityName::Invalid;
+ if (result == CapabilityName::Invalid)
+ {
+ CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest);
+ auto latestSpirvCapSetElements = latestSpirvCapSet.getAtomSets()->getElements<CapabilityAtom>();
+ result = (CapabilityName)latestSpirvCapSetElements[latestSpirvCapSetElements.getCount() - 2]; //-1 gets shader stage
+ }
+ return result;
+}
+
+CapabilityName getLatestMetalAtom()
+{
+ static CapabilityName result = CapabilityName::Invalid;
+ if (result == CapabilityName::Invalid)
+ {
+ CapabilitySet latestMetalCapSet = CapabilitySet(CapabilityName::metallib_latest);
+ auto latestMetalCapSetElements = latestMetalCapSet.getAtomSets()->getElements<CapabilityAtom>();
+ result = (CapabilityName)latestMetalCapSetElements[latestMetalCapSetElements.getCount() - 2]; //-1 gets shader stage
+ }
+ return result;
+}
+
bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base)
{
if (atom == base)
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h
index 8bd03147e..5ee79670b 100644
--- a/source/slang/slang-capability.h
+++ b/source/slang/slang-capability.h
@@ -191,7 +191,7 @@ public:
const CapabilityTargetSets* context;
CapabilityTargetSets::ConstIterator targetNode{};
CapabilityStageSets::ConstIterator stageNode{};
- const std::optional<CapabilityAtomSet>* atomSetNode;
+ const std::optional<CapabilityAtomSet>* atomSetNode = {};
public:
operator bool() const
@@ -258,10 +258,12 @@ public:
if (tmp.targetNode == this->context->end())
return tmp;
tmp.stageNode = (*tmp.targetNode).second.shaderStageSets.begin();
- if (tmp.stageNode == (*tmp.targetNode).second.shaderStageSets.end())
+ while (tmp.stageNode == (*tmp.targetNode).second.shaderStageSets.end())
{
- tmp++;
- return tmp;
+ tmp.targetNode++;
+ if (tmp.targetNode == this->context->end())
+ return end();
+ tmp.stageNode = (*tmp.targetNode).second.shaderStageSets.begin();
}
tmp.atomSetNode = &(*tmp.stageNode).second.atomSet;
if (!tmp.atomSetNode->has_value())
@@ -298,6 +300,9 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base);
/// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid.
CapabilityName findCapabilityName(UnownedStringSlice const& name);
+CapabilityName getLatestSpirvAtom();
+CapabilityName getLatestMetalAtom();
+
/// Gets the capability names.
void getCapabilityNames(List<UnownedStringSlice>& ioNames);
@@ -305,6 +310,11 @@ UnownedStringSlice capabilityNameToString(CapabilityName name);
bool isDirectChildOfAbstractAtom(CapabilityAtom name);
+
+ /// Return true if `name` represents an atom for a target version, e.g. spirv_1_5.
+bool isTargetVersionAtom(CapabilityName name);
+bool isSpirvExtensionAtom(CapabilityName name);
+
void printDiagnosticArg(StringBuilder& sb, CapabilityAtom atom);
void printDiagnosticArg(StringBuilder& sb, CapabilityName name);
diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp
index 7a6deed5c..43f8b55e8 100644
--- a/source/slang/slang-check-shader.cpp
+++ b/source/slang/slang-check-shader.cpp
@@ -526,8 +526,8 @@ namespace Slang
// TODO: provedence should have a way to filter out for provenance that are missing X capabilitySet from their caps, else in big functions we get junk errors
// This is specifically a problem for when a function is missing a target but otherwise has identical capabilities.
- const auto& interredCapConjunctions = entryPointFuncDecl->inferredCapabilityRequirements.getAtomSets();
- const auto& compileCaps = targetCaps.getAtomSets();
+ const auto interredCapConjunctions = entryPointFuncDecl->inferredCapabilityRequirements.getAtomSets();
+ const auto compileCaps = targetCaps.getAtomSets();
if (compileCaps && interredCapConjunctions)
{
for (auto inferredAtom : *interredCapConjunctions.begin())
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index ef8bc45d5..fd12a71a3 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1739,6 +1739,8 @@ namespace Slang
CapabilitySet getTargetCaps();
+ void setTargetCaps(CapabilitySet capSet);
+
HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions();
private:
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 9ba1e4724..7385d5e33 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -763,7 +763,8 @@ DIAGNOSTIC(41300, Error, byteAddressBufferUnaligned, "invalid alignment `$0` spe
//
DIAGNOSTIC(50010, Internal, missingExistentialBindingsForParameter, "missing argument for existential parameter slot")
-
+DIAGNOSTIC(50011, Warning, spirvVersionNotSupported, "Slang's SPIR-V backend only supports SPIR-V version 1.3 and later."
+ " Use `-emit-spirv-via-glsl` option to produce SPIR-V 1.0 through 1.2.")
DIAGNOSTIC(50020, Error, invalidTessCoordType, "TessCoord must have vec2 or vec3 type.")
DIAGNOSTIC(50020, Error, invalidFragCoordType, "FragCoord must be a vec4.")
DIAGNOSTIC(50020, Error, invalidInvocationIdType, "InvocationId must have int type.")
diff --git a/source/slang/slang-emit-spirv-ops.h b/source/slang/slang-emit-spirv-ops.h
index d24153aa5..fa5fc7cd1 100644
--- a/source/slang/slang-emit-spirv-ops.h
+++ b/source/slang/slang-emit-spirv-ops.h
@@ -2235,6 +2235,11 @@ SpvInst* emitOpKill(SpvInstParent* parent, IRInst* inst)
return emitInst(parent, inst, SpvOpKill);
}
+SpvInst* emitOpDemoteToHelperInvocation(SpvInstParent* parent, IRInst* inst)
+{
+ return emitInst(parent, inst, SpvOpDemoteToHelperInvocation);
+}
+
// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpReturn
SpvInst* emitOpReturn(SpvInstParent* parent, IRInst* inst)
{
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 9ad8513fc..d3d0b16c5 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -521,11 +521,7 @@ struct SPIRVEmitContext
// > Version nuumber
//
-
- // We are targeting SPIRV 1.5 for now.
-
- static const uint32_t spvVersion1_5_0 = 0x00010500;
- m_words.add(spvVersion1_5_0);
+ m_words.add(m_spvVersion);
// > Generator's magic number.
//
@@ -1280,6 +1276,27 @@ struct SPIRVEmitContext
return result;
}
+ SpvInst* ensureExtensionDeclarationBeforeSpv14(UnownedStringSlice name)
+ {
+ if (isSpirv14OrLater())
+ return nullptr;
+ return ensureExtensionDeclaration(name);
+ }
+
+ SpvInst* ensureExtensionDeclarationBeforeSpv15(UnownedStringSlice name)
+ {
+ if (isSpirv15OrLater())
+ return nullptr;
+ return ensureExtensionDeclaration(name);
+ }
+
+ SpvInst* ensureExtensionDeclarationBeforeSpv16(UnownedStringSlice name)
+ {
+ if (isSpirv16OrLater())
+ return nullptr;
+ return ensureExtensionDeclaration(name);
+ }
+
bool hasExtensionDeclaration(const UnownedStringSlice& name)
{
return m_extensionInsts.containsKey(name);
@@ -1403,6 +1420,7 @@ struct SPIRVEmitContext
auto spvValueType = ensureInst(valueType);
valueTypeId = getID(spvValueType);
}
+
auto resultSpvType = emitOpTypePointer(
inst,
storageClass,
@@ -1463,8 +1481,11 @@ struct SPIRVEmitContext
if (structSize >= (uint64_t)IRSizeAndAlignment::kIndeterminateSize)
{
IRBuilder builder(inst);
- auto decoration = builder.addDecoration(inst, kIROp_SPIRVBlockDecoration);
- emitDecoration(getID(spvStructType), decoration);
+ if (isSpirv14OrLater() || !inst->findDecorationImpl(kIROp_SPIRVBufferBlockDecoration))
+ {
+ auto decoration = builder.addDecoration(inst, kIROp_SPIRVBlockDecoration);
+ emitDecoration(getID(spvStructType), decoration);
+ }
}
emitLayoutDecorations(as<IRStructType>(inst), getID(spvStructType));
return spvStructType;
@@ -2248,7 +2269,7 @@ struct SPIRVEmitContext
emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), decor, varInst, SpvDecorationPerPrimitiveEXT);
break;
case kIROp_RequireSPIRVDescriptorIndexingExtensionDecoration:
- ensureExtensionDeclaration(UnownedStringSlice("SPV_EXT_descriptor_indexing"));
+ ensureExtensionDeclarationBeforeSpv15(UnownedStringSlice("SPV_EXT_descriptor_indexing"));
requireSPIRVCapability(SpvCapabilityRuntimeDescriptorArray);
break;
}
@@ -2540,6 +2561,12 @@ struct SPIRVEmitContext
emitLocalInst(spvBlock, irInst);
if (irInst->getOp() == kIROp_loop)
pendingLoopInsts.add(as<IRLoop>(irInst));
+ if (irInst->getOp() == kIROp_discard && !shouldEmitDiscardAsDemote())
+ {
+ // If we emitted OpKill for discard, we should stop emitting anything
+ // after this inst in the block, because OpKill is a terminator inst.
+ break;
+ }
}
}
@@ -2586,6 +2613,11 @@ struct SPIRVEmitContext
return false;
}
+ bool shouldEmitDiscardAsDemote()
+ {
+ return (isSpirv16OrLater() || m_useDemoteToHelperInvocationExtension);
+ }
+
// The instructions that appear inside the basic blocks of
// functions are what we will call "local" instructions.
//
@@ -2785,7 +2817,16 @@ struct SPIRVEmitContext
result = emitOpReturnValue(parent, inst, as<IRReturn>(inst)->getVal());
break;
case kIROp_discard:
- result = emitOpKill(parent, inst);
+ if (shouldEmitDiscardAsDemote())
+ {
+ ensureExtensionDeclarationBeforeSpv16(toSlice("SPV_EXT_demote_to_helper_invocation"));
+ requireSPIRVCapability(SpvCapabilityDemoteToHelperInvocation);
+ result = emitOpDemoteToHelperInvocation(parent, inst);
+ }
+ else
+ {
+ result = emitOpKill(parent, inst);
+ }
break;
case kIROp_BeginFragmentShaderInterlock:
ensureExtensionDeclaration(UnownedStringSlice("SPV_EXT_fragment_shader_interlock"));
@@ -3224,6 +3265,17 @@ struct SPIRVEmitContext
auto refSet = m_referencingEntryPoints.tryGetValue(globalInst);
if (refSet && refSet->contains(entryPoint))
{
+ if (!isSpirv14OrLater())
+ {
+ // Prior to SPIRV 1.4, we can only reference In and Out variables
+ // in the interface part.
+ if (auto ptrType = as<IRPtrTypeBase>(globalInst->getDataType()))
+ {
+ auto addrSpace = ptrType->getAddressSpace();
+ if (addrSpace != SpvStorageClassInput && addrSpace != SpvStorageClassOutput)
+ continue;
+ }
+ }
paramsSet.add(spvGlobalInst);
params.add(spvGlobalInst);
referencedBuiltinIRVars.add(globalInst);
@@ -3406,6 +3458,8 @@ struct SPIRVEmitContext
case kIROp_SPIRVNonUniformResourceDecoration:
{
+ ensureExtensionDeclarationBeforeSpv15(toSlice("SPV_EXT_descriptor_indexing"));
+
requireSPIRVCapability(SpvCapabilityShaderNonUniform);
emitOpDecorate(
getSection(SpvLogicalSectionID::Annotations),
@@ -3548,6 +3602,7 @@ struct SPIRVEmitContext
break;
case kIROp_SemanticDecoration:
{
+ ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1"));
emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations),
decoration,
dstID,
@@ -3558,6 +3613,7 @@ struct SPIRVEmitContext
case kIROp_UserTypeNameDecoration:
{
ensureExtensionDeclaration(toSlice("SPV_GOOGLE_user_type"));
+ ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1"));
emitOpDecorateString(getSection(SpvLogicalSectionID::Annotations),
decoration,
dstID,
@@ -3676,6 +3732,7 @@ struct SPIRVEmitContext
{
if (shouldEmitSPIRVReflectionInfo())
{
+ ensureExtensionDeclarationBeforeSpv14(toSlice("SPV_GOOGLE_hlsl_functionality1"));
emitOpMemberDecorateString(
getSection(SpvLogicalSectionID::Annotations),
nullptr,
@@ -3980,7 +4037,10 @@ struct SPIRVEmitContext
}
else if (semanticName == "sv_rendertargetarrayindex")
{
- requireSPIRVCapability(SpvCapabilityShaderLayer);
+ if (isSpirv14OrLater())
+ requireSPIRVCapability(SpvCapabilityShaderLayer);
+ else
+ requireSPIRVCapability(SpvCapabilityGeometry);
return getBuiltinGlobalVar(inst->getFullType(), SpvBuiltInLayer);
}
else if (semanticName == "sv_sampleindex")
@@ -4840,11 +4900,12 @@ struct SPIRVEmitContext
{
//"%addr = OpAccessChain resultType*StorageBuffer resultId _0 const(int, 0) _1;"
IRBuilder builder(inst);
+ auto storageClass = isSpirv14OrLater()? SpvStorageClassStorageBuffer : SpvStorageClassUniform;
return emitOpAccessChain(
parent,
inst,
// Make sure the resulting pointer has the correct storage class
- getPtrTypeWithAddressSpace(cast<IRPtrTypeBase>(inst->getDataType()), SpvStorageClassStorageBuffer),
+ getPtrTypeWithAddressSpace(cast<IRPtrTypeBase>(inst->getDataType()), storageClass),
inst->getOperand(0),
makeArray(emitIntConstant(0, builder.getIntType()), ensureInst(inst->getOperand(1)))
);
diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp
index 7a970589e..eb5905dfe 100644
--- a/source/slang/slang-ir-glsl-legalize.cpp
+++ b/source/slang/slang-ir-glsl-legalize.cpp
@@ -808,6 +808,7 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo(
default:
context->requireGLSLVersion(ProfileVersion::GLSL_450);
+ context->requireSPIRVVersion(SemanticVersion(1, 5, 0));
context->requireGLSLExtension(UnownedStringSlice::fromLiteral("GL_ARB_shader_viewport_layer_array"));
break;
}
@@ -2945,6 +2946,23 @@ void legalizeEntryPointForGLSL(
// and as an ordinary function.
SLANG_ASSERT(!func->firstUse);
+ // Require SPIRV version based on the stage.
+ switch (stage)
+ {
+ case Stage::Mesh:
+ case Stage::Amplification:
+ glslExtensionTracker->requireSPIRVVersion(SemanticVersion(1, 4, 0));
+ break;
+ case Stage::AnyHit:
+ case Stage::Callable:
+ case Stage::Miss:
+ case Stage::RayGeneration:
+ case Stage::Intersection:
+ case Stage::ClosestHit:
+ glslExtensionTracker->requireSPIRVVersion(SemanticVersion(1, 4, 0));
+ break;
+ }
+
// We create a dummy IR builder, since some of
// the functions require it.
//
@@ -3137,6 +3155,31 @@ void legalizeEntryPointForGLSL(
}
}
+void decorateModuleWithSPIRVVersion(IRModule* module, SemanticVersion spirvVersion)
+{
+ CapabilityName atom = CapabilityName::spirv_1_0;
+ switch (spirvVersion.m_major)
+ {
+ case 1:
+ {
+ switch (spirvVersion.m_minor)
+ {
+ case 0: atom = CapabilityName::spirv_1_0; break;
+ case 1: atom = CapabilityName::spirv_1_1; break;
+ case 2: atom = CapabilityName::spirv_1_2; break;
+ case 3: atom = CapabilityName::spirv_1_3; break;
+ case 4: atom = CapabilityName::spirv_1_4; break;
+ case 5: atom = CapabilityName::spirv_1_5; break;
+ case 6: atom = CapabilityName::spirv_1_6; break;
+ default: SLANG_UNEXPECTED("Unknown SPIRV version");
+ }
+ break;
+ }
+ }
+ IRBuilder builder(module);
+ builder.addRequireCapabilityAtomDecoration(module->getModuleInst(), atom);
+}
+
void legalizeEntryPointsForGLSL(
Session* session,
IRModule* module,
@@ -3150,6 +3193,8 @@ void legalizeEntryPointsForGLSL(
}
assignRayPayloadHitObjectAttributeLocations(module);
+
+ decorateModuleWithSPIRVVersion(module, glslExtensionTracker->getSPIRVVersion());
}
void legalizeConstantBufferLoadForGLSL(IRModule* module)
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index b7873b8bd..9ab8e9a02 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -585,8 +585,6 @@ INST(TargetSwitch, targetSwitch, 1, 0)
// A generic asm inst has an return semantics that terminates the control flow.
INST(GenericAsm, GenericAsm, 1, 0)
-INST(discard, discard, 0, 0)
-
/* IRUnreachable */
INST(MissingReturn, missingReturn, 0, 0)
INST(Unreachable, unreachable, 0, 0)
@@ -594,6 +592,8 @@ INST_RANGE(Unreachable, MissingReturn, Unreachable)
INST_RANGE(TerminatorInst, Return, Unreachable)
+INST(discard, discard, 0, 0)
+
INST(RequirePrelude, RequirePrelude, 1, 0)
INST(RequireGLSLExtension, RequireGLSLExtension, 1, 0)
INST(RequireComputeDerivative, RequireComputeDerivative, 0, 0)
@@ -735,6 +735,7 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace)
INST(RequireGLSLVersionDecoration, requireGLSLVersion, 1, 0)
INST(RequireGLSLExtensionDecoration, requireGLSLExtension, 1, 0)
INST(RequireCUDASMVersionDecoration, requireCUDASMVersion, 1, 0)
+ INST(RequireCapabilityAtomDecoration, requireCapabilityAtom, 1, 0)
INST(HasExplicitHLSLBindingDecoration, HasExplicitHLSLBinding, 0, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 8d1880366..93f3e1227 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -351,6 +351,18 @@ struct IRRequireSPIRVVersionDecoration : IRDecoration
}
};
+struct IRRequireCapabilityAtomDecoration : IRDecoration
+{
+ enum { kOp = kIROp_RequireCapabilityAtomDecoration };
+ IR_LEAF_ISA(RequireCapabilityAtomDecoration)
+
+ IRConstant* getCapabilityAtomOperand() { return cast<IRConstant>(getOperand(0)); }
+ CapabilityName getAtom()
+ {
+ return (CapabilityName)getCapabilityAtomOperand()->value.intVal;
+ }
+};
+
struct IRRequireCUDASMVersionDecoration : IRDecoration
{
enum { kOp = kIROp_RequireCUDASMVersionDecoration };
@@ -4529,6 +4541,11 @@ public:
addDecoration(value, kIROp_RequireCUDASMVersionDecoration, getIntValue(getBasicType(BaseType::UInt64), intValue));
}
+ void addRequireCapabilityAtomDecoration(IRInst* value, CapabilityName atom)
+ {
+ addDecoration(value, kIROp_RequireCapabilityAtomDecoration, getIntValue(getUIntType(), IRIntegerValue(atom)));
+ }
+
void addPatchConstantFuncDecoration(IRInst* value, IRInst* patchConstantFunc)
{
addDecoration(value, kIROp_PatchConstantFuncDecoration, patchConstantFunc);
diff --git a/source/slang/slang-ir-restructure.cpp b/source/slang/slang-ir-restructure.cpp
index cfd1d4597..68be41fdc 100644
--- a/source/slang/slang-ir-restructure.cpp
+++ b/source/slang/slang-ir-restructure.cpp
@@ -255,7 +255,6 @@ namespace Slang
case kIROp_Unreachable:
case kIROp_MissingReturn:
case kIROp_Return:
- case kIROp_discard:
case kIROp_GenericAsm:
// These cases are all simple terminators that can be handled as-is
// without needing to construct a separate `Region` to encapsulate them.
diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp
index c023537b9..0965377c8 100644
--- a/source/slang/slang-ir-spirv-legalize.cpp
+++ b/source/slang/slang-ir-spirv-legalize.cpp
@@ -136,7 +136,10 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
break;
}
builder.addNameHintDecoration(structType, nameSb.getUnownedSlice());
- builder.addDecoration(structType, kIROp_SPIRVBlockDecoration);
+ if (m_sharedContext->isSpirv14OrLater())
+ builder.addDecoration(structType, kIROp_SPIRVBlockDecoration);
+ else
+ builder.addDecoration(structType, kIROp_SPIRVBufferBlockDecoration);
result.structType = structType;
result.arrayKey = arrayKey;
@@ -628,7 +631,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
else if (auto structuredBufferType = as<IRHLSLStructuredBufferTypeBase>(innerType))
{
innerType = lowerStructuredBufferType(structuredBufferType).structType;
- storageClass = SpvStorageClassStorageBuffer;
+ storageClass = getStorageBufferStorageClass();
needLoad = false;
auto memoryFlags = MemoryQualifierSetModifier::Flags::kNone;
@@ -645,8 +648,16 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
else if (auto glslShaderStorageBufferType = as<IRGLSLShaderStorageBufferType>(innerType))
{
innerType = glslShaderStorageBufferType->getElementType();
- builder.addDecoration(innerType, kIROp_SPIRVBlockDecoration);
- storageClass = SpvStorageClassStorageBuffer;
+ if (m_sharedContext->isSpirv14OrLater())
+ {
+ builder.addDecoration(innerType, kIROp_SPIRVBlockDecoration);
+ storageClass = SpvStorageClassStorageBuffer;
+ }
+ else
+ {
+ builder.addDecoration(innerType, kIROp_SPIRVBufferBlockDecoration);
+ storageClass = SpvStorageClassUniform;
+ }
needLoad = false;
}
@@ -711,7 +722,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
break;
case LayoutResourceKind::ShaderResource:
case LayoutResourceKind::UnorderedAccess:
- storageClass = SpvStorageClassStorageBuffer;
+ storageClass = getStorageBufferStorageClass();
break;
case LayoutResourceKind::PushConstantBuffer:
storageClass = SpvStorageClassPushConstant;
@@ -1144,6 +1155,11 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
}
}
+ SpvStorageClass getStorageBufferStorageClass()
+ {
+ return m_sharedContext->isSpirv14OrLater() ? SpvStorageClassStorageBuffer : SpvStorageClassUniform;
+ }
+
void processStructuredBufferLoad(IRInst* loadInst)
{
auto sb = loadInst->getOperand(0);
@@ -1152,7 +1168,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
builder.setInsertBefore(loadInst);
IRInst* args[] = { sb, index };
auto addrInst = builder.emitIntrinsicInst(
- builder.getPtrType(kIROp_PtrType, translateToStorageBufferPointer(loadInst->getFullType()), SpvStorageClassStorageBuffer),
+ builder.getPtrType(kIROp_PtrType, translateToStorageBufferPointer(loadInst->getFullType()), getStorageBufferStorageClass()),
kIROp_RWStructuredBufferGetElementPtr,
2,
args);
@@ -1171,7 +1187,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
builder.setInsertBefore(storeInst);
IRInst* args[] = { sb, index };
auto addrInst = builder.emitIntrinsicInst(
- builder.getPtrType(kIROp_PtrType, value->getFullType(), SpvStorageClassStorageBuffer),
+ builder.getPtrType(kIROp_PtrType, value->getFullType(), getStorageBufferStorageClass()),
kIROp_RWStructuredBufferGetElementPtr,
2,
args);
@@ -2078,8 +2094,95 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
builder.setInsertBefore(beforeInst);
}
+ void determineSpirvVersion()
+ {
+ // Determine minimum spirv version from target request.
+ auto targetCaps = m_sharedContext->m_targetProgram->getTargetReq()->getTargetCaps();
+ for (auto targetAtomSet : targetCaps.getAtomSets())
+ {
+ for (auto atom : targetAtomSet)
+ {
+ auto spirvAtom = ((CapabilityName)atom);
+ switch (spirvAtom)
+ {
+ case CapabilityName::spirv_1_0:
+ m_sharedContext->requireSpirvVersion(0x10000);
+ break;
+ case CapabilityName::spirv_1_1:
+ m_sharedContext->requireSpirvVersion(0x10100);
+ break;
+ case CapabilityName::spirv_1_2:
+ m_sharedContext->requireSpirvVersion(0x10200);
+ break;
+ case CapabilityName::spirv_1_3:
+ m_sharedContext->requireSpirvVersion(0x10300);
+ break;
+ case CapabilityName::spirv_1_4:
+ m_sharedContext->requireSpirvVersion(0x10400);
+ break;
+ case CapabilityName::spirv_1_5:
+ m_sharedContext->requireSpirvVersion(0x10500);
+ break;
+ case CapabilityName::spirv_1_6:
+ m_sharedContext->requireSpirvVersion(0x10600);
+ break;
+ case CapabilityName::SPV_EXT_demote_to_helper_invocation:
+ m_sharedContext->m_useDemoteToHelperInvocationExtension = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // Scan through the entry points and find the max version required.
+ auto processInst = [&](IRInst* globalInst)
+ {
+ for (auto decor : globalInst->getDecorations())
+ {
+ switch (decor->getOp())
+ {
+ case kIROp_RequireCapabilityAtomDecoration:
+ {
+ auto atomDecor = as<IRRequireCapabilityAtomDecoration>(decor);
+ switch (atomDecor->getAtom())
+ {
+ case CapabilityName::spirv_1_3:
+ m_sharedContext->requireSpirvVersion(0X10300);
+ break;
+ case CapabilityName::spirv_1_4:
+ m_sharedContext->requireSpirvVersion(0X10400);
+ break;
+ case CapabilityName::spirv_1_5:
+ m_sharedContext->requireSpirvVersion(0X10500);
+ break;
+ case CapabilityName::spirv_1_6:
+ m_sharedContext->requireSpirvVersion(0X10600);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ processInst(m_module->getModuleInst());
+ for (auto globalInst : m_module->getGlobalInsts())
+ {
+ processInst(globalInst);
+ }
+
+ if (m_sharedContext->m_spvVersion < 0x10300)
+ {
+ // Direct SPIRV backend does not support generating SPIRV before 1.3,
+ // we will issue an error message here.
+ m_sharedContext->m_sink->diagnose(SourceLoc(), Diagnostics::spirvVersionNotSupported);
+ }
+ }
+
void processModule()
{
+ determineSpirvVersion();
+
// Process global params before anything else, so we don't generate inefficient
// array marhalling code for array-typed global params.
for (auto globalInst : m_module->getGlobalInsts())
@@ -2118,7 +2221,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
auto lowered = lowerStructuredBufferType(t);
IRBuilder builder(t);
builder.setInsertBefore(t);
- t->replaceUsesWith(builder.getPtrType(kIROp_PtrType, lowered.structType, SpvStorageClassStorageBuffer));
+ t->replaceUsesWith(builder.getPtrType(kIROp_PtrType, lowered.structType, getStorageBufferStorageClass()));
}
for (auto t : textureFootprintTypes)
{
@@ -2374,7 +2477,8 @@ void insertFragmentShaderInterlock(SPIRVEmitSharedContext* context, IRModule* mo
{
if (auto inst = block->getTerminator())
{
- if (inst->getOp() == kIROp_Return || inst->getOp() == kIROp_discard)
+ if (inst->getOp() == kIROp_Return ||
+ !context->isSpirv16OrLater() && inst->getOp() == kIROp_discard)
{
builder.setInsertBefore(inst);
builder.emitEndFragmentShaderInterlock();
diff --git a/source/slang/slang-ir-spirv-legalize.h b/source/slang/slang-ir-spirv-legalize.h
index 431c9627d..2aa55bb12 100644
--- a/source/slang/slang-ir-spirv-legalize.h
+++ b/source/slang/slang-ir-spirv-legalize.h
@@ -26,6 +26,27 @@ struct SPIRVEmitSharedContext
const SPIRVCoreGrammarInfo* m_grammarInfo;
IRInst* m_voidType;
+ unsigned int m_spvVersion = 0x10000;
+ bool m_useDemoteToHelperInvocationExtension = false;
+
+ bool isSpirv14OrLater()
+ {
+ return m_spvVersion >= 0x10400;
+ }
+ bool isSpirv15OrLater()
+ {
+ return m_spvVersion >= 0x10500;
+ }
+ bool isSpirv16OrLater()
+ {
+ return m_spvVersion >= 0x10600;
+ }
+
+ void requireSpirvVersion(unsigned int version)
+ {
+ m_spvVersion = Math::Max(m_spvVersion, version);
+ }
+
SPIRVEmitSharedContext(IRModule* module, TargetProgram* program, DiagnosticSink* sink)
: m_irModule(module),
m_targetProgram(program),
diff --git a/source/slang/slang-ir-synthesize-active-mask.cpp b/source/slang/slang-ir-synthesize-active-mask.cpp
index 60e13b418..c3d772615 100644
--- a/source/slang/slang-ir-synthesize-active-mask.cpp
+++ b/source/slang/slang-ir-synthesize-active-mask.cpp
@@ -1047,25 +1047,6 @@ struct SynthesizeActiveMaskForFunctionContext
//
case kIROp_conditionalBranch:
//
- // A `discard` instruction can only appear in fragment shaders,
- // and this pass is currently only being applied for the CUDA
- // target (which doesn't support rasterization stages).
- //
- // The difficulty with `discard` is that a `discard` operation
- // in a subroutine can affect the active mask seen by code
- // after a call to that subroutine. Thus, the `discard` operation
- // would violate our assumption that the active mask cannot change
- // inside of a single basic block.
- //
- // Devising a solution to synthesize the active mask in the presence
- // of `discard` would seem to require a different approach to this
- // pass *or* some new constraints on the front-end representation
- // (e.g., a `discard` is really more akin to a `throw` or other
- // error-propagation operation, and might need to be explicit in
- // the signature of any function that may `discard`).
- //
- case kIROp_discard:
- //
// Finally, we also don't handle any control-flow op we might not
// have introduced at the time this pass was created.
//
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index cc095a0cb..3e96d61eb 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -558,7 +558,6 @@ namespace Slang
case kIROp_Return:
case kIROp_Unreachable:
case kIROp_MissingReturn:
- case kIROp_discard:
case kIROp_GenericAsm:
break;
@@ -854,7 +853,6 @@ namespace Slang
case kIROp_conditionalBranch:
case kIROp_loop:
case kIROp_ifElse:
- case kIROp_discard:
case kIROp_Switch:
case kIROp_Unreachable:
case kIROp_MissingReturn:
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 80e73fe2b..403e2a921 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -11599,6 +11599,9 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink)
builder->addLayoutDecoration(irModule->getModuleInst(), irGlobalScopeVarLayout);
+ auto latestSpirvAtom = getLatestSpirvAtom();
+ auto latestMetalAtom = getLatestMetalAtom();
+
for( auto entryPointLayout : programLayout->entryPoints )
{
auto funcDeclRef = entryPointLayout->entryPoint;
@@ -11617,6 +11620,19 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink)
builder->addImportDecoration(irFunc, getMangledName(astBuilder, funcDeclRef).getUnownedSlice());
}
+ for (auto atomSet : as<FuncDecl>(funcDeclRef.getDecl())->inferredCapabilityRequirements.getAtomSets())
+ {
+ for (auto atomVal : atomSet)
+ {
+ auto atom = (CapabilityName)atomVal;
+ if (atom >= CapabilityName::spirv_1_0 && atom <= latestSpirvAtom ||
+ atom >= CapabilityName::metallib_2_3 && atom <= latestMetalAtom)
+ {
+ builder->addRequireCapabilityAtomDecoration(irFunc, atom);
+ }
+ }
+ }
+
auto irEntryPointLayout = lowerEntryPointLayout(context, entryPointLayout);
builder->addLayoutDecoration(irFunc, irEntryPointLayout);
diff --git a/source/slang/slang-profile-defs.h b/source/slang/slang-profile-defs.h
index 826555d1b..3b9ee27f5 100644
--- a/source/slang/slang-profile-defs.h
+++ b/source/slang/slang-profile-defs.h
@@ -87,6 +87,7 @@ PROFILE_STAGE_ALIAS(Fragment, fragment, Pixel)
PROFILE_FAMILY(DX)
PROFILE_FAMILY(GLSL)
PROFILE_FAMILY(METAL)
+PROFILE_FAMILY(SPIRV)
// Profile versions
PROFILE_VERSION(DX_4_0, DX)
@@ -115,6 +116,13 @@ PROFILE_VERSION(GLSL_460, GLSL)
PROFILE_VERSION(METAL_2_3, METAL)
PROFILE_VERSION(METAL_2_4, METAL)
+PROFILE_VERSION(SPIRV_1_0, SPIRV)
+PROFILE_VERSION(SPIRV_1_1, SPIRV)
+PROFILE_VERSION(SPIRV_1_2, SPIRV)
+PROFILE_VERSION(SPIRV_1_3, SPIRV)
+PROFILE_VERSION(SPIRV_1_4, SPIRV)
+PROFILE_VERSION(SPIRV_1_5, SPIRV)
+PROFILE_VERSION(SPIRV_1_6, SPIRV)
// Specific profiles
PROFILE(DX_Compute_4_0, cs_4_0, Compute, DX_4_0)
@@ -236,6 +244,14 @@ PROFILE_ALIAS(DX_None_6_7, DX_Lib_6_7, sm_6_7)
PROFILE(METAL_LIB_2_3, metallib_2_3, Unknown, METAL_2_3)
PROFILE(METAL_LIB_2_4, metallib_2_4, Unknown, METAL_2_4)
+PROFILE(SPIRV_LIB_1_0, spirv_1_0, Unknown, SPIRV_1_0)
+PROFILE(SPIRV_LIB_1_1, spirv_1_1, Unknown, SPIRV_1_1)
+PROFILE(SPIRV_LIB_1_2, spirv_1_2, Unknown, SPIRV_1_2)
+PROFILE(SPIRV_LIB_1_3, spirv_1_3, Unknown, SPIRV_1_3)
+PROFILE(SPIRV_LIB_1_4, spirv_1_4, Unknown, SPIRV_1_4)
+PROFILE(SPIRV_LIB_1_5, spirv_1_5, Unknown, SPIRV_1_5)
+PROFILE(SPIRV_LIB_1_6, spirv_1_6, Unknown, SPIRV_1_6)
+
// Define all the GLSL profiles
PROFILE(GLSL_None_150, glsl_150, Unknown, GLSL_150)
diff --git a/source/slang/slang-spirv-val.cpp b/source/slang/slang-spirv-val.cpp
index 8b638da94..faacb9c59 100644
--- a/source/slang/slang-spirv-val.cpp
+++ b/source/slang/slang-spirv-val.cpp
@@ -44,7 +44,7 @@ SlangResult debugValidateSPIRV(const List<uint8_t>& spirv)
CommandLine commandLine;
commandLine.m_executableLocation.setName("spirv-val");
commandLine.addArg("--target-env");
- commandLine.addArg("vulkan1.2");
+ commandLine.addArg("vulkan1.3");
commandLine.addArg("--scalar-block-layout");
RefPtr<Process> p;
const auto createResult = Process::create(commandLine, 0, p);
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 8b0012f1d..66e75a282 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -1666,6 +1666,12 @@ HLSLToVulkanLayoutOptions* TargetRequest::getHLSLToVulkanLayoutOptions()
return hlslToVulkanOptions.get();
}
+void TargetRequest::setTargetCaps(CapabilitySet capSet)
+{
+ cookedCapabilities = capSet;
+
+}
+
CapabilitySet TargetRequest::getTargetCaps()
{
if(!cookedCapabilities.isEmpty())
@@ -1691,6 +1697,11 @@ CapabilitySet TargetRequest::getTargetCaps()
// are available where can be directly encoded on the declarations.
List<CapabilityName> atoms;
+
+ // If the user specified a explicit profile, we should pull
+ // a corresponding atom representing the target version from the profile.
+ CapabilitySet profileCaps = CapabilitySet(optionSet.getProfile().getCapabilityName());
+
bool isGLSLTarget = false;
switch(getTarget())
{
@@ -1704,7 +1715,40 @@ CapabilitySet TargetRequest::getTargetCaps()
case CodeGenTarget::SPIRVAssembly:
if (getOptionSet().shouldEmitSPIRVDirectly())
{
- atoms.add(CapabilityName::spirv_1_5);
+ // Default to SPIRV 1.5 if the user has not specified a target version.
+ bool hasTargetVersionAtom = false;
+ if (!profileCaps.isEmpty())
+ {
+ profileCaps.join(CapabilitySet(CapabilityName::spirv_1_0));
+ for (auto profileCapAtomSet : profileCaps.getAtomSets())
+ {
+ for (auto atom : profileCapAtomSet)
+ {
+ if (isTargetVersionAtom((CapabilityName)atom))
+ {
+ atoms.add((CapabilityName)atom);
+ hasTargetVersionAtom = true;
+ }
+ }
+ }
+ }
+ if (!hasTargetVersionAtom)
+ {
+ atoms.add(CapabilityName::spirv_1_5);
+ }
+ // If the user specified any SPIR-V extensions in the profile,
+ // pull them in.
+ for (auto profileCapAtomSet : profileCaps.getAtomSets())
+ {
+ for (auto atom : profileCapAtomSet)
+ {
+ if (isSpirvExtensionAtom((CapabilityName)atom))
+ {
+ atoms.add((CapabilityName)atom);
+ hasTargetVersionAtom = true;
+ }
+ }
+ }
}
else
{
@@ -1752,9 +1796,7 @@ CapabilitySet TargetRequest::getTargetCaps()
CapabilitySet targetCap = CapabilitySet(atoms);
- CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest);
- auto latestSpirvCapSetElements = latestSpirvCapSet.getAtomSets()->getElements<CapabilityAtom>();
- CapabilityName latestSpirvAtom = (CapabilityName)latestSpirvCapSetElements[latestSpirvCapSetElements.getCount()-2]; //-1 gets shader stage
+ CapabilityName latestSpirvAtom = getLatestSpirvAtom();
for (auto atomVal : optionSet.getArray(CompilerOptionName::Capability))
{