summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2023-09-26 23:56:06 -0700
committerGitHub <noreply@github.com>2023-09-27 14:56:06 +0800
commitebe8ddefc48478307d5f206cd3e40c41d28a36e3 (patch)
tree8e13977979909a26394eea532d8b95cd5ad0f6d1 /source
parentc5c8cfbb360d9a763f549df48636effde839eacd (diff)
Various SPIRV fixes. (#3231)
* Various SPIRV fixes. - Geometry shader support (WIP). - Fix texture get dimension and load. - Fold global GetElement(MakeArray/MakeVector) insts. - Call spvopt to inline all functions. - Translate OpImageSubscript. - Emit struct member names and global variable names. - Fix lowering of OpBitNot -> OpNot, instead of OpBitReverse. * Fix test. * Fix geometry shader. * Fix geometry shader emit. * Add atomic Image access test. * Fix tests. * don't fail if spirv-opt fails. * Update comments. * Fix test. * Cleanups. * indentation --------- Co-authored-by: Yong He <yhe@nvidia.com> Co-authored-by: Ellie Hermaszewska <ellieh@nvidia.com>
Diffstat (limited to 'source')
-rw-r--r--source/slang/hlsl.meta.slang74
-rw-r--r--source/slang/slang-diagnostic-defs.h17
-rw-r--r--source/slang/slang-emit-spirv-ops.h61
-rw-r--r--source/slang/slang-emit-spirv.cpp296
-rw-r--r--source/slang/slang-emit.cpp22
-rw-r--r--source/slang/slang-ir-array-reg-to-mem.cpp87
-rw-r--r--source/slang/slang-ir-array-reg-to-mem.h16
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp204
-rw-r--r--source/slang/slang-ir-insts.h1
-rw-r--r--source/slang/slang-ir-lower-buffer-element-type.cpp19
-rw-r--r--source/slang/slang-ir-peephole.cpp41
-rw-r--r--source/slang/slang-ir-spirv-legalize.cpp255
-rw-r--r--source/slang/slang-ir-util.cpp8
-rw-r--r--source/slang/slang-ir-util.h2
-rw-r--r--source/slang/slang-ir.cpp16
-rw-r--r--source/slang/slang-ir.h2
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
-rw-r--r--source/slang/slang-spirv-opt.cpp68
-rw-r--r--source/slang/slang-spirv-opt.h10
-rw-r--r--source/slang/slang-spirv-val.cpp1
-rw-r--r--source/slang/slang-stdlib-textures.cpp321
21 files changed, 1254 insertions, 271 deletions
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index d95c1e4d6..da729f67a 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -1167,11 +1167,27 @@ __magic_type(HLSLPointStreamType)
__intrinsic_type($(kIROp_HLSLPointStreamType))
struct PointStream
{
- __target_intrinsic(glsl, "EmitVertex()")
- void Append(T value);
+ [KnownBuiltin("GeometryStreamAppend")]
+ void Append(T value)
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EmitVertex()";
+ case hlsl: __intrinsic_asm ".Append";
+ case spirv: spirv_asm { OpEmitVertex; };
+ }
+ }
- __target_intrinsic(glsl, "EndPrimitive()")
- void RestartStrip();
+ [KnownBuiltin("GeometryStreamRestart")]
+ void RestartStrip()
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EndPrimitive()";
+ case hlsl: __intrinsic_asm ".RestartStrip";
+ case spirv: spirv_asm { OpEndPrimitive; };
+ }
+ }
};
__generic<T>
@@ -1179,11 +1195,27 @@ __magic_type(HLSLLineStreamType)
__intrinsic_type($(kIROp_HLSLLineStreamType))
struct LineStream
{
- __target_intrinsic(glsl, "EmitVertex()")
- void Append(T value);
+ [KnownBuiltin("GeometryStreamAppend")]
+ void Append(T value)
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EmitVertex()";
+ case hlsl: __intrinsic_asm ".Append";
+ case spirv: spirv_asm { OpEmitVertex; };
+ }
+ }
- __target_intrinsic(glsl, "EndPrimitive()")
- void RestartStrip();
+ [KnownBuiltin("GeometryStreamRestart")]
+ void RestartStrip()
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EndPrimitive()";
+ case hlsl: __intrinsic_asm ".RestartStrip";
+ case spirv: spirv_asm { OpEndPrimitive; };
+ }
+ }
};
__generic<T>
@@ -1191,11 +1223,27 @@ __magic_type(HLSLTriangleStreamType)
__intrinsic_type($(kIROp_HLSLTriangleStreamType))
struct TriangleStream
{
- __target_intrinsic(glsl, "EmitVertex()")
- void Append(T value);
+ [KnownBuiltin("GeometryStreamAppend")]
+ void Append(T value)
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EmitVertex()";
+ case hlsl: __intrinsic_asm ".Append";
+ case spirv: spirv_asm { OpEmitVertex; };
+ }
+ }
- __target_intrinsic(glsl, "EndPrimitive()")
- void RestartStrip();
+ [KnownBuiltin("GeometryStreamRestart")]
+ void RestartStrip()
+ {
+ __target_switch
+ {
+ case glsl: __intrinsic_asm "EndPrimitive()";
+ case hlsl: __intrinsic_asm ".RestartStrip";
+ case spirv: spirv_asm { OpEndPrimitive; };
+ }
+ }
};
#define VECTOR_MAP_UNARY(TYPE, COUNT, FUNC, VALUE) \
@@ -7994,7 +8042,7 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE>
uint iCandidateOrCommitted = 0;
return spirv_asm
{
- %rr:$$bool = OpRayQueryGetIntersectionCandidateAABBOpaqueKHR &this $iCandidateOrCommitted;
+ %rr:$$bool = OpRayQueryGetIntersectionCandidateAABBOpaqueKHR &this;
result:$$bool = OpLogicalNot %rr;
};
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 14f45e802..183a9bc28 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -667,12 +667,12 @@ DIAGNOSTIC(42001, Error, invalidUseOfTorchTensorTypeInDeviceFunc, "invalid use o
DIAGNOSTIC(50010, Internal, missingExistentialBindingsForParameter, "missing argument for existential parameter slot")
-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.")
-DIAGNOSTIC(50020, Error, invalidThreadIdType, "ThreadId must have int type.")
-DIAGNOSTIC(50020, Error, invalidPrimitiveIdType, "PrimitiveId must have int type.")
-DIAGNOSTIC(50020, Error, invalidPatchVertexCountType, "PatchVertexCount must have int type.")
+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.")
+DIAGNOSTIC(50020, Error, invalidThreadIdType, "ThreadId must have int type.")
+DIAGNOSTIC(50020, Error, invalidPrimitiveIdType, "PrimitiveId must have int type.")
+DIAGNOSTIC(50020, Error, invalidPatchVertexCountType, "PatchVertexCount must have int type.")
DIAGNOSTIC(50022, Error, worldIsNotDefined, "world '$0' is not defined.")
DIAGNOSTIC(50023, Error, stageShouldProvideWorldAttribute, "'$0' should provide 'World' attribute.")
DIAGNOSTIC(50040, Error, componentHasInvalidTypeForPositionOutput, "'$0': component used as 'loc' output must be of vec4 type.")
@@ -693,7 +693,7 @@ DIAGNOSTIC(50052, Error, hullShaderRequiresTessLevelOuter, "'HullShader' require
DIAGNOSTIC(50053, Error, invalidTessellationDomian, "'Domain' should be either 'triangles' or 'quads'.")
DIAGNOSTIC(50053, Error, invalidTessellationOutputTopology, "'OutputTopology' must be one of: 'point', 'line', 'triangle_cw', or 'triangle_ccw'.")
DIAGNOSTIC(50053, Error, invalidTessellationPartitioning, "'Partitioning' must be one of: 'integer', 'pow2', 'fractional_even', or 'fractional_odd'.")
-DIAGNOSTIC(50053, Error, invalidTessellationDomain, "'Domain' should be either 'triangles' or 'quads'.")
+DIAGNOSTIC(50053, Error, invalidTessellationDomain, "'Domain' should be either 'triangles' or 'quads'.")
DIAGNOSTIC(50082, Error, importingFromPackedBufferUnsupported, "importing type '$0' from PackedBuffer is not supported by the GLSL backend.")
DIAGNOSTIC(51090, Error, cannotGenerateCodeForExternComponentType, "cannot generate code for extern component type '$0'.")
@@ -716,7 +716,7 @@ DIAGNOSTIC(52006, Error, compilerNotDefinedForTransition, "compiler not defined
DIAGNOSTIC(52007, Error, typeCannotBeUsedInDynamicDispatch, "failed to generate dynamic dispatch code for type '$0'.")
DIAGNOSTIC(52008, Error, dynamicDispatchOnSpecializeOnlyInterface, "type '$0' is marked for specialization only, but dynamic dispatch is needed for the call.")
-DIAGNOSTIC(53001,Error, invalidTypeMarshallingForImportedDLLSymbol, "invalid type marshalling in imported func $0.")
+DIAGNOSTIC(53001, Error, invalidTypeMarshallingForImportedDLLSymbol, "invalid type marshalling in imported func $0.")
DIAGNOSTIC(54001, Error, meshOutputMustBeOut, "Mesh shader outputs must be declared with 'out'.")
DIAGNOSTIC(54002, Error, meshOutputMustBeArray, "HLSL style mesh shader outputs must be arrays")
@@ -728,6 +728,7 @@ DIAGNOSTIC(55102, Error, invalidTorchKernelParamType, "'$0' is not a valid param
DIAGNOSTIC(56001, Error, unableToAutoMapCUDATypeToHostType, "Could not automatically map '$0' to a host type. Automatic binding generation failed for '$1'")
+DIAGNOSTIC(57001, Warning, spirvOptFailed, "spirv-opt failed. $0")
//
// 8xxxx - Issues specific to a particular library/technology/platform/etc.
//
diff --git a/source/slang/slang-emit-spirv-ops.h b/source/slang/slang-emit-spirv-ops.h
index 9c4ec290d..bc53b70ae 100644
--- a/source/slang/slang-emit-spirv-ops.h
+++ b/source/slang/slang-emit-spirv-ops.h
@@ -20,6 +20,20 @@ SpvInst* emitOpName(
return emitInst(parent, inst, SpvOpName, target, name);
}
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpMemberName
+template<typename T>
+SpvInst* emitOpMemberName(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T& target,
+ int index,
+ const UnownedStringSlice& name
+)
+{
+ static_assert(isSingular<T>);
+ return emitInst(parent, inst, SpvOpMemberName, target, SpvLiteralInteger::from32(index), name);
+}
+
// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpExtension
SpvInst* emitOpExtension(SpvInstParent* parent, IRInst* inst, const UnownedStringSlice& name)
{
@@ -109,6 +123,36 @@ SpvInst* emitOpExecutionModeLocalSizeId(
);
}
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpExecutionMode
+template<typename T>
+SpvInst* emitOpExecutionModeOutputVertices(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T& entryPoint,
+ const SpvLiteralInteger& vertexCount
+)
+{
+ static_assert(isSingular<T>);
+ return emitInst(
+ parent, inst, SpvOpExecutionMode, entryPoint, SpvExecutionModeOutputVertices, vertexCount
+ );
+}
+
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpExecutionMode
+template<typename T>
+SpvInst* emitOpExecutionModeInvocations(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T& entryPoint,
+ const SpvLiteralInteger& invocations
+)
+{
+ static_assert(isSingular<T>);
+ return emitInst(
+ parent, inst, SpvOpExecutionMode, entryPoint, SpvExecutionModeInvocations, invocations
+ );
+}
+
// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpCapability
SpvInst* emitOpCapability(SpvInstParent* parent, IRInst* inst, SpvCapability capability)
{
@@ -1111,6 +1155,23 @@ SpvInst* emitOpCompositeExtract(
);
}
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpVectorExtractDynamic
+template<typename T1, typename T2, typename T3>
+SpvInst* emitOpVectorExtractDynamic(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T1& idResultType,
+ const T2& composite,
+ const T3& index)
+{
+ static_assert(isSingular<T1>);
+ static_assert(isSingular<T2>);
+ static_assert(isSingular<T3>);
+ return emitInst(
+ parent, inst, SpvOpVectorExtractDynamic, idResultType, kResultID, composite, index
+ );
+}
+
// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpCopyObject
template<typename T1, typename T2>
SpvInst* emitOpCopyObject(
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 0aa4d4c60..14fe2e17b 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -872,7 +872,10 @@ struct SPIRVEmitContext
key.type = type;
SpvInst* result = nullptr;
if (m_spvIntConstants.tryGetValue(key, result))
+ {
+ m_mapIRInstToSpvInst[inst] = result;
return result;
+ }
switch (type->getOp())
{
case kIROp_Int64Type:
@@ -900,6 +903,7 @@ struct SPIRVEmitContext
}
}
m_spvIntConstants[key] = result;
+ m_mapIRInstToSpvInst[inst] = result;
return result;
}
SpvInst* emitFloatConstant(IRFloatingPointValue val, IRType* type, IRInst* inst = nullptr)
@@ -909,7 +913,10 @@ struct SPIRVEmitContext
key.type = type;
SpvInst* result = nullptr;
if (m_spvFloatConstants.tryGetValue(key, result))
+ {
+ m_mapIRInstToSpvInst[inst] = result;
return result;
+ }
if (type->getOp() == kIROp_DoubleType)
{
result = emitOpConstant(
@@ -935,6 +942,7 @@ struct SPIRVEmitContext
{
SLANG_UNEXPECTED("missing case in SPIR-V emitFloatConstant");
}
+ m_mapIRInstToSpvInst[inst] = result;
m_spvFloatConstants[key] = result;
return result;
}
@@ -1404,7 +1412,9 @@ struct SPIRVEmitContext
case kIROp_IntLit:
case kIROp_FloatLit:
case kIROp_StringLit:
+ {
return emitLit(inst);
+ }
case kIROp_MakeVectorFromScalar:
{
const auto scalar = inst->getOperand(0);
@@ -1479,6 +1489,10 @@ struct SPIRVEmitContext
return emitGetStringHash(inst);
case kIROp_AllocateOpaqueHandle:
return nullptr;
+ case kIROp_HLSLTriangleStreamType:
+ case kIROp_HLSLLineStreamType:
+ case kIROp_HLSLPointStreamType:
+ return nullptr;
default:
{
if (as<IRSPIRVAsmOperand>(inst))
@@ -1490,6 +1504,84 @@ struct SPIRVEmitContext
}
}
+ static SpvImageFormat getSpvImageFormat(IRTextureTypeBase* type)
+ {
+ ImageFormat imageFormat = type->hasFormat() ? (ImageFormat)type->getFormat() : ImageFormat::unknown;
+ switch (imageFormat)
+ {
+ case ImageFormat::unknown: return SpvImageFormatUnknown;
+ case ImageFormat::rgba32f: return SpvImageFormatRgba32f;
+ case ImageFormat::rgba16f: return SpvImageFormatRgba16f;
+ case ImageFormat::rg32f: return SpvImageFormatRg32f;
+ case ImageFormat::rg16f: return SpvImageFormatRg16f;
+ case ImageFormat::r11f_g11f_b10f: return SpvImageFormatR11fG11fB10f;
+ case ImageFormat::r32f: return SpvImageFormatR32f;
+ case ImageFormat::r16f: return SpvImageFormatR16f;
+ case ImageFormat::rgba16: return SpvImageFormatRgba16;
+ case ImageFormat::rgb10_a2: return SpvImageFormatRgb10A2;
+ case ImageFormat::rgba8: return SpvImageFormatRgba8;
+ case ImageFormat::rg16: return SpvImageFormatRg16;
+ case ImageFormat::rg8: return SpvImageFormatRg8;
+ case ImageFormat::r16: return SpvImageFormatR16;
+ case ImageFormat::r8: return SpvImageFormatR8;
+ case ImageFormat::rgba16_snorm: return SpvImageFormatRgba16Snorm;
+ case ImageFormat::rgba8_snorm: return SpvImageFormatRgba8Snorm;
+ case ImageFormat::rg16_snorm: return SpvImageFormatRg16Snorm;
+ case ImageFormat::rg8_snorm: return SpvImageFormatRg8Snorm;
+ case ImageFormat::r16_snorm: return SpvImageFormatR16Snorm;
+ case ImageFormat::r8_snorm: return SpvImageFormatR8Snorm;
+ case ImageFormat::rgba32i: return SpvImageFormatRgba32i;
+ case ImageFormat::rgba16i: return SpvImageFormatRgba16i;
+ case ImageFormat::rgba8i: return SpvImageFormatRgba8i;
+ case ImageFormat::rg32i: return SpvImageFormatRg32i;
+ case ImageFormat::rg16i: return SpvImageFormatRg16i;
+ case ImageFormat::rg8i: return SpvImageFormatRg8i;
+ case ImageFormat::r32i: return SpvImageFormatR32i;
+ case ImageFormat::r16i: return SpvImageFormatR16i;
+ case ImageFormat::r8i: return SpvImageFormatR8i;
+ case ImageFormat::rgba32ui: return SpvImageFormatRgba32ui;
+ case ImageFormat::rgba16ui: return SpvImageFormatRgba16ui;
+ case ImageFormat::rgb10_a2ui: return SpvImageFormatRgb10a2ui;
+ case ImageFormat::rgba8ui: return SpvImageFormatRgba8ui;
+ case ImageFormat::rg32ui: return SpvImageFormatRg32ui;
+ case ImageFormat::rg16ui: return SpvImageFormatRg16ui;
+ case ImageFormat::rg8ui: return SpvImageFormatRg8ui;
+ case ImageFormat::r32ui: return SpvImageFormatR32ui;
+ case ImageFormat::r16ui: return SpvImageFormatR16ui;
+ case ImageFormat::r8ui: return SpvImageFormatR8ui;
+ case ImageFormat::r64ui: return SpvImageFormatR64ui;
+ case ImageFormat::r64i: return SpvImageFormatR64i;
+ default: SLANG_UNIMPLEMENTED_X("unknown image format for spirv emit");
+ }
+ }
+
+ SpvCapability getImageFormatCapability(SpvImageFormat format)
+ {
+ switch (format)
+ {
+ case SpvImageFormatUnknown:
+ case SpvImageFormatRgba32f:
+ case SpvImageFormatRgba16f:
+ case SpvImageFormatR32f:
+ case SpvImageFormatRgba8:
+ case SpvImageFormatRgba8Snorm:
+ case SpvImageFormatRgba32i:
+ case SpvImageFormatRgba16i:
+ case SpvImageFormatRgba8i:
+ case SpvImageFormatR32i:
+ case SpvImageFormatRgba32ui:
+ case SpvImageFormatRgba16ui:
+ case SpvImageFormatRgba8ui:
+ case SpvImageFormatR32ui:
+ return SpvCapabilityShader;
+ case SpvImageFormatR64ui:
+ case SpvImageFormatR64i:
+ return SpvCapabilityInt64ImageEXT;
+ default:
+ return SpvCapabilityStorageImageExtendedFormats;
+ }
+ }
+
SpvInst* ensureTextureType(IRInst* assignee, IRTextureTypeBase* inst)
{
// Some untyped constants from OpTypeImage
@@ -1565,9 +1657,7 @@ struct SPIRVEmitContext
break;
}
- // TODO: we need to do as _emitGLSLImageFormatModifier does,
- // take a guess at the image format
- SpvImageFormat format = SpvImageFormatUnknown;
+ SpvImageFormat format = getSpvImageFormat(inst);
//
// Capabilities, according to section 3.8
@@ -1611,6 +1701,10 @@ struct SPIRVEmitContext
requireSPIRVCapability(SpvCapabilityStorageImageReadWithoutFormat);
requireSPIRVCapability(SpvCapabilityStorageImageWriteWithoutFormat);
}
+
+ auto formatCapability = getImageFormatCapability(format);
+ if (formatCapability != SpvCapabilityShader)
+ requireSPIRVCapability(formatCapability);
//
// The op itself
@@ -1648,10 +1742,34 @@ struct SPIRVEmitContext
return result;
}
- void emitVarLayout(SpvInst* varInst, IRVarLayout* layout)
+ bool _maybeEmitInterpolationModifierDecoration(IRInterpolationMode mode, SpvInst* varInst)
+ {
+ switch (mode)
+ {
+ case IRInterpolationMode::NoInterpolation:
+ emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), nullptr, varInst, SpvDecorationFlat);
+ return true;
+ case IRInterpolationMode::NoPerspective:
+ emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), nullptr, varInst, SpvDecorationNoPerspective);
+ return true;
+ case IRInterpolationMode::Linear:
+ return true;
+ case IRInterpolationMode::Sample:
+ emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), nullptr, varInst, SpvDecorationSample);
+ return true;
+ case IRInterpolationMode::Centroid:
+ emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), nullptr, varInst, SpvDecorationCentroid);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void emitVarLayout(IRInst* var, SpvInst* varInst, IRVarLayout* layout)
{
bool needDefaultSetBindingDecoration = false;
bool hasExplicitSetBinding = false;
+ bool isInput = false;
for (auto rr : layout->getOffsetAttrs())
{
UInt index = rr->getOffset();
@@ -1668,6 +1786,7 @@ struct SPIRVEmitContext
varInst,
SpvLiteralInteger::from32(int32_t(index))
);
+ isInput = true;
break;
case LayoutResourceKind::VaryingOutput:
emitOpDecorateLocation(
@@ -1730,7 +1849,43 @@ struct SPIRVEmitContext
varInst,
SpvLiteralInteger::from32(int32_t(0)));
}
+
+ bool anyModifiers = false;
+ for (auto dd : var->getDecorations())
+ {
+ if (dd->getOp() != kIROp_InterpolationModeDecoration)
+ continue;
+
+ auto decoration = (IRInterpolationModeDecoration*)dd;
+
+ anyModifiers |= _maybeEmitInterpolationModifierDecoration(decoration->getMode(), varInst);
+ }
+
+ // If the user didn't explicitly qualify a varying
+ // with integer type, then we need to explicitly
+ // add the `flat` modifier for GLSL.
+ if (!anyModifiers)
+ {
+ // Only emit a default `flat` for fragment
+ // stage varying inputs.
+ if (layout
+ && layout->getStage() == Stage::Fragment
+ && layout->usesResourceKind(LayoutResourceKind::VaryingInput))
+ {
+ if (isIntegralScalarOrCompositeType(var->getDataType()))
+ emitOpDecorate(getSection(SpvLogicalSectionID::Annotations), nullptr, varInst, SpvDecorationFlat);
+ }
+ }
}
+
+ void maybeEmitName(SpvInst* spvInst, IRInst* irInst)
+ {
+ if (auto nameDecor = irInst->findDecoration<IRNameHintDecoration>())
+ {
+ emitOpName(getSection(SpvLogicalSectionID::DebugNames), nullptr, spvInst, nameDecor->getName());
+ }
+ }
+
/// Emit a global parameter definition.
SpvInst* emitGlobalParam(IRGlobalParam* param)
{
@@ -1752,8 +1907,9 @@ struct SPIRVEmitContext
storageClass
);
if (auto layout = getVarLayout(param))
- emitVarLayout(varInst, layout);
- return varInst;
+ emitVarLayout(param, varInst, layout);
+ maybeEmitName(varInst, param);
+ return varInst;
}
/// Emit a global variable definition.
@@ -1773,7 +1929,8 @@ struct SPIRVEmitContext
storageClass
);
if(layout)
- emitVarLayout(varInst, layout);
+ emitVarLayout(globalVar, varInst, layout);
+ maybeEmitName(varInst, globalVar);
return varInst;
}
@@ -2195,6 +2352,8 @@ struct SPIRVEmitContext
return emitImageLoad(parent, as<IRImageLoad>(inst));
case kIROp_ImageStore:
return emitImageStore(parent, as<IRImageStore>(inst));
+ case kIROp_ImageSubscript:
+ return emitImageSubscript(parent, as<IRImageSubscript>(inst));
}
}
@@ -2208,6 +2367,13 @@ struct SPIRVEmitContext
return emitInst(parent, store, SpvOpImageWrite, store->getImage(), store->getCoord(), store->getValue());
}
+ SpvInst* emitImageSubscript(SpvInstParent* parent, IRImageSubscript* subscript)
+ {
+ IRBuilder builder(subscript);
+ builder.setInsertBefore(subscript);
+ return emitInst(parent, subscript, SpvOpImageTexelPointer, subscript->getDataType(), kResultID, subscript->getImage(), subscript->getCoord(), builder.getIntValue(builder.getIntType(), 0));
+ }
+
SpvInst* emitGetStringHash(IRInst* inst)
{
auto getStringHashInst = as<IRGetStringHash>(inst);
@@ -2244,20 +2410,23 @@ struct SPIRVEmitContext
}
case kIROp_BoolLit:
{
+ SpvInst* spvInst = nullptr;
if (cast<IRBoolLit>(inst)->getValue())
{
- return emitOpConstantTrue(
+ spvInst = emitOpConstantTrue(
inst,
inst->getDataType()
);
}
else
{
- return emitOpConstantFalse(
+ spvInst = emitOpConstantFalse(
inst,
inst->getDataType()
);
}
+ m_mapIRInstToSpvInst[inst] = spvInst;
+ return spvInst;
}
case kIROp_StringLit:
{
@@ -2396,13 +2565,16 @@ struct SPIRVEmitContext
params
);
- // Stage specific execution mode declarations.
+ // Stage specific execution mode and capability declarations.
switch (entryPointDecor->getProfile().getStage())
{
case Stage::Fragment:
//OpExecutionMode %main OriginUpperLeft
emitInst(getSection(SpvLogicalSectionID::ExecutionModes), nullptr, SpvOpExecutionMode, dstID, SpvExecutionModeOriginUpperLeft);
break;
+ case Stage::Geometry:
+ requireSPIRVCapability(SpvCapabilityGeometry);
+ break;
default:
break;
}
@@ -2436,7 +2608,61 @@ struct SPIRVEmitContext
);
}
break;
+ case kIROp_MaxVertexCountDecoration:
+ {
+ auto section = getSection(SpvLogicalSectionID::ExecutionModes);
+ auto maxVertexCount = cast<IRMaxVertexCountDecoration>(decoration);
+ emitOpExecutionModeOutputVertices(
+ section,
+ decoration,
+ dstID,
+ SpvLiteralInteger::from32(int32_t(getIntVal(maxVertexCount->getCount())))
+ );
+ }
+ break;
+ case kIROp_InstanceDecoration:
+ {
+ auto decor = as<IRInstanceDecoration>(decoration);
+ auto count = int32_t(getIntVal(decor->getCount()));
+ auto section = getSection(SpvLogicalSectionID::ExecutionModes);
+ emitOpExecutionModeInvocations(section, decoration, dstID, SpvLiteralInteger::from32(count));
+ }
+ break;
+ case kIROp_TriangleInputPrimitiveTypeDecoration:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeTriangles);
+ break;
+ case kIROp_LineInputPrimitiveTypeDecoration:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeInputLines);
+ break;
+ case kIROp_LineAdjInputPrimitiveTypeDecoration:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeInputLinesAdjacency);
+ break;
+ case kIROp_PointInputPrimitiveTypeDecoration:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeInputPoints);
+ break;
+ case kIROp_TriangleAdjInputPrimitiveTypeDecoration:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeInputTrianglesAdjacency);
+ break;
+ case kIROp_StreamOutputTypeDecoration:
+ {
+ auto decor = as<IRStreamOutputTypeDecoration>(decoration);
+ IRType* type = decor->getStreamType();
+ switch (type->getOp())
+ {
+ case kIROp_HLSLPointStreamType:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeOutputPoints);
+ break;
+ case kIROp_HLSLLineStreamType:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeOutputLineStrip);
+ break;
+ case kIROp_HLSLTriangleStreamType:
+ emitOpExecutionMode(getSection(SpvLogicalSectionID::ExecutionModes), decoration, dstID, SpvExecutionModeOutputTriangleStrip);
+ break;
+ default: SLANG_ASSERT(!"Unknown stream out type");
+ }
+ }
+ break;
case kIROp_SPIRVBufferBlockDecoration:
{
emitOpDecorate(
@@ -2491,6 +2717,16 @@ struct SPIRVEmitContext
int32_t id = 0;
for (auto field : structType->getFields())
{
+ if (auto fieldNameDecor = field->getKey()->findDecoration<IRNameHintDecoration>())
+ {
+ emitOpMemberName(
+ getSection(SpvLogicalSectionID::DebugNames),
+ nullptr,
+ spvStructID,
+ id,
+ fieldNameDecor->getName());
+ }
+
IRIntegerValue offset = 0;
if (auto offsetDecor = field->getKey()->findDecoration<IRPackOffsetDecoration>())
{
@@ -2807,7 +3043,9 @@ struct SPIRVEmitContext
SpvInst* emitParam(SpvInstParent* parent, IRInst* inst)
{
- return emitOpFunctionParameter(parent, inst, inst->getFullType());
+ auto paramSpvInst = emitOpFunctionParameter(parent, inst, inst->getFullType());
+ maybeEmitName(paramSpvInst, inst);
+ return paramSpvInst;
}
SpvInst* emitVar(SpvInstParent* parent, IRInst* inst)
@@ -2819,7 +3057,9 @@ struct SPIRVEmitContext
{
storageClass = (SpvStorageClass)ptrType->getAddressSpace();
}
- return emitOpVariable(parent, inst, inst->getFullType(), storageClass);
+ auto varSpvInst = emitOpVariable(parent, inst, inst->getFullType(), storageClass);
+ maybeEmitName(varSpvInst, inst);
+ return varSpvInst;
}
/// Cached `IRParam` indices in an `IRBlock`. For use in `getParamIndexInBlock`.
@@ -2948,7 +3188,7 @@ struct SPIRVEmitContext
int paramIndex = getParamIndexInBlock(block, inst);
// Emit a Phi instruction.
- return emitInstCustomOperandFunc(parent, inst, SpvOpPhi, [&]() {
+ auto phiSpvInst = emitInstCustomOperandFunc(parent, inst, SpvOpPhi, [&]() {
emitOperand(inst->getFullType());
emitOperand(kResultID);
// Find phi arguments from incoming branch instructions that target `block`.
@@ -2979,6 +3219,9 @@ struct SPIRVEmitContext
emitOperand(getIRInstSpvID(sourceBlock));
}
});
+
+ maybeEmitName(phiSpvInst, inst);
+ return phiSpvInst;
}
SpvInst* emitCall(SpvInstParent* parent, IRCall* inst)
@@ -3389,15 +3632,22 @@ struct SPIRVEmitContext
IRBuilder builder(m_irModule);
builder.setInsertBefore(inst);
- auto index = getIntVal(inst->getIndex());
-
- return emitOpCompositeExtract(
- parent,
- inst,
- inst->getFullType(),
- inst->getBase(),
- makeArray(SpvLiteralInteger::from32((int32_t)index))
- );
+ if (auto index = as<IRIntLit>(inst->getIndex()))
+ {
+ return emitOpCompositeExtract(
+ parent,
+ inst,
+ inst->getFullType(),
+ inst->getBase(),
+ makeArray(SpvLiteralInteger::from32((int32_t)index->getValue()))
+ );
+ }
+ else
+ {
+ SLANG_ASSERT(as<IRVectorType>(baseTy));
+ // SPIRV Only allows dynamic element extract on vector types.
+ return emitOpVectorExtractDynamic(parent, inst, inst->getFullType(), inst->getBase(), inst->getIndex());
+ }
}
SpvInst* emitLoad(SpvInstParent* parent, IRLoad* inst)
@@ -3910,7 +4160,7 @@ struct SPIRVEmitContext
if (isBool)
opCode = SpvOpLogicalNot;
else
- opCode = SpvOpBitReverse;
+ opCode = SpvOpNot;
break;
case kIROp_Rsh:
opCode = isSigned ? SpvOpShiftRightArithmetic : SpvOpShiftRightLogical;
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index f2906dfc3..15ce25c3c 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -72,7 +72,7 @@
#include "slang-legalize-types.h"
#include "slang-lower-to-ir.h"
#include "slang-mangle.h"
-
+#include "slang-spirv-opt.h"
#include "slang-syntax.h"
#include "slang-type-layout.h"
#include "slang-visitor.h"
@@ -773,15 +773,20 @@ Result linkAndOptimizeIR(
switch (target)
{
case CodeGenTarget::GLSL:
+ case CodeGenTarget::SPIRV:
+ case CodeGenTarget::SPIRVAssembly:
{
- auto glslExtensionTracker = as<GLSLExtensionTracker>(options.sourceEmitter->getExtensionTracker());
+ GLSLExtensionTracker glslExtensionTracker;
+ GLSLExtensionTracker* glslExtensionTrackerPtr = options.sourceEmitter
+ ? as<GLSLExtensionTracker>(options.sourceEmitter->getExtensionTracker())
+ : &glslExtensionTracker;
legalizeEntryPointsForGLSL(
session,
irModule,
irEntryPoints,
codeGenContext,
- glslExtensionTracker);
+ glslExtensionTrackerPtr);
#if 0
dumpIRIfEnabled(codeGenContext, irModule, "GLSL LEGALIZED");
@@ -1259,11 +1264,18 @@ SlangResult emitSPIRVForEntryPointsDirectly(
auto irModule = linkedIR.module;
auto irEntryPoints = linkedIR.entryPoints;
- List<uint8_t> spirv;
+ List<uint8_t> spirv, outSpirv;
emitSPIRVFromIR(codeGenContext, irModule, irEntryPoints, spirv);
+ String optErr;
+ if (SLANG_FAILED(optimizeSPIRV(spirv, optErr, outSpirv)))
+ {
+ codeGenContext->getSink()->diagnose(SourceLoc(), Diagnostics::spirvOptFailed, optErr);
+ outSpirv = _Move(spirv);
+ }
+
auto artifact = ArtifactUtil::createArtifactForCompileTarget(asExternal(codeGenContext->getTargetFormat()));
- artifact->addRepresentationUnknown(ListBlob::moveCreate(spirv));
+ artifact->addRepresentationUnknown(ListBlob::moveCreate(outSpirv));
ArtifactUtil::addAssociated(artifact, linkedIR.metadata);
diff --git a/source/slang/slang-ir-array-reg-to-mem.cpp b/source/slang/slang-ir-array-reg-to-mem.cpp
new file mode 100644
index 000000000..6f749f242
--- /dev/null
+++ b/source/slang/slang-ir-array-reg-to-mem.cpp
@@ -0,0 +1,87 @@
+#include "slang-ir-array-reg-to-mem.h"
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+#include "slang-ir-util.h"
+
+namespace Slang
+{
+ bool eliminateArrayTypeParameters(IRFunc* func)
+ {
+ IRBuilder builder(func);
+ bool changed = false;
+ List<UInt> arrayParamIds;
+ UInt idx = 0;
+ List<IRParam*> paramWorkList;
+ for (auto param : func->getParams())
+ {
+ if (auto arrayType = as<IRArrayTypeBase>(param->getFullType()))
+ {
+ paramWorkList.add(param);
+ arrayParamIds.add(idx);
+ }
+ idx++;
+ }
+ for (auto param : paramWorkList)
+ {
+ // We have an array type parameter, so we need to replace it with a pointer to the array
+ // type.
+ //
+ // We will also need to insert a `load` instruction at the start of the function body
+ // to load the actual pointer value from the parameter.
+ //
+ if (auto arrayType = as<IRArrayTypeBase>(param->getFullType()))
+ {
+ changed = true;
+ builder.setInsertBefore(param);
+ auto ptrArrayType = builder.getPtrType(arrayType);
+ auto newParam = builder.emitParam(ptrArrayType);
+ setInsertAfterOrdinaryInst(&builder, param);
+ auto regVal = builder.emitLoad(newParam);
+ param->replaceUsesWith(regVal);
+ param->removeAndDeallocate();
+ }
+ }
+ if (changed)
+ {
+ // The function is modified, we need to also update its type.
+ List<IRType*> paramTypes;
+ for (auto param : func->getParams())
+ {
+ paramTypes.add(param->getFullType());
+ }
+ auto newFuncType = builder.getFuncType((UInt)paramTypes.getCount(), paramTypes.getBuffer(), func->getResultType());
+ func->setFullType(newFuncType);
+
+ // Update all the call sites to pass the arrays by pointer.
+ traverseUses(func, [&](IRUse* use)
+ {
+ if (const auto call = as<IRCall>(use->getUser()))
+ {
+ builder.setInsertBefore(call);
+ for (auto paramId : arrayParamIds)
+ {
+ auto arg = call->getArg(paramId);
+ auto var = builder.emitVar(as<IRPtrTypeBase>(paramTypes[paramId])->getValueType());
+ builder.emitStore(var, arg);
+ call->setArg(paramId, var);
+ }
+ }
+ });
+ }
+ return changed;
+ }
+
+ bool eliminateArrayTypeSSARegisters(IRModule* module)
+ {
+ bool changed = false;
+ for (auto inst : module->getGlobalInsts())
+ {
+ if (auto func = as<IRFunc>(inst))
+ {
+ changed |= eliminateArrayTypeParameters(func);
+ }
+ }
+ return changed;
+ }
+}
diff --git a/source/slang/slang-ir-array-reg-to-mem.h b/source/slang/slang-ir-array-reg-to-mem.h
new file mode 100644
index 000000000..941125e1c
--- /dev/null
+++ b/source/slang/slang-ir-array-reg-to-mem.h
@@ -0,0 +1,16 @@
+// slang-ir-array-reg-to-mem.h
+#pragma once
+
+namespace Slang
+{
+ struct IRModule;
+ struct IRCall;
+ struct IRInst;
+ struct IRFunc;
+
+ /// Eliminate SSA registers and IRParams of array type and turn them into pointers to memory objects.
+ bool eliminateArrayTypeSSARegisters(IRModule* module);
+
+ bool eliminateArrayTypeParameters(IRFunc* func);
+
+}
diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp
index 590404bf3..eb808bc97 100644
--- a/source/slang/slang-ir-glsl-legalize.cpp
+++ b/source/slang/slang-ir-glsl-legalize.cpp
@@ -7,6 +7,7 @@
#include "slang-ir-insts.h"
#include "slang-ir-inst-pass-base.h"
#include "slang-ir-specialize-function-call.h"
+#include "slang-ir-util.h"
#include "slang-glsl-extension-tracker.h"
#include "../../external/spirv-headers/include/spirv/unified1/spirv.h"
@@ -2377,135 +2378,128 @@ void legalizeEntryPointParameterForGLSL(
// will differ a bit between the pointer and non-pointer
// cases.
auto paramType = pp->getDataType();
-
+ auto valueType = paramType;
// First we will special-case stage input/outputs that
// don't fit into the standard varying model.
// - Geometry shader output streams
// - Mesh shader outputs
// - Mesh shader payload input
- if( auto paramPtrType = as<IROutTypeBase>(paramType) )
+ if (auto paramPtrType = as<IROutTypeBase>(paramType))
{
- auto valueType = paramPtrType->getValueType();
- if( const auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) )
- {
- // An output stream type like `TriangleStream<Foo>` should
- // more or less translate into `out Foo` (plus scalarization).
+ valueType = paramPtrType->getValueType();
+ }
+ if( const auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) )
+ {
+ // An output stream type like `TriangleStream<Foo>` should
+ // more or less translate into `out Foo` (plus scalarization).
- auto globalOutputVal = createGLSLGlobalVaryings(
- context,
- codeGenContext,
- builder,
- valueType,
- paramLayout,
- LayoutResourceKind::VaryingOutput,
- stage,
- pp);
+ auto globalOutputVal = createGLSLGlobalVaryings(
+ context,
+ codeGenContext,
+ builder,
+ valueType,
+ paramLayout,
+ LayoutResourceKind::VaryingOutput,
+ stage,
+ pp);
- // TODO: a GS output stream might be passed into other
- // functions, so that we should really be modifying
- // any function that has one of these in its parameter
- // list (and in the limit we should be leagalizing any
- // type that nests these...).
- //
- // For now we will just try to deal with `Append` calls
- // directly in this function.
+ // TODO: a GS output stream might be passed into other
+ // functions, so that we should really be modifying
+ // any function that has one of these in its parameter
+ // list (and in the limit we should be leagalizing any
+ // type that nests these...).
+ //
+ // For now we will just try to deal with `Append` calls
+ // directly in this function.
- for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
{
- for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
- {
- // Is it a call?
- if(ii->getOp() != kIROp_Call)
- continue;
+ // Is it a call?
+ if(ii->getOp() != kIROp_Call)
+ continue;
- // Is it calling the append operation?
- auto callee = ii->getOperand(0);
- for(;;)
+ // Is it calling the append operation?
+ auto callee = ii->getOperand(0);
+ for(;;)
+ {
+ // If the instruction is `specialize(X,...)` then
+ // we want to look at `X`, and if it is `generic { ... return R; }`
+ // then we want to look at `R`. We handle this
+ // iteratively here.
+ //
+ // TODO: This idiom seems to come up enough that we
+ // should probably have a dedicated convenience routine
+ // for this.
+ //
+ // Alternatively, we could switch the IR encoding so
+ // that decorations are added to the generic instead of the
+ // value it returns.
+ //
+ switch(callee->getOp())
{
- // If the instruction is `specialize(X,...)` then
- // we want to look at `X`, and if it is `generic { ... return R; }`
- // then we want to look at `R`. We handle this
- // iteratively here.
- //
- // TODO: This idiom seems to come up enough that we
- // should probably have a dedicated convenience routine
- // for this.
- //
- // Alternatively, we could switch the IR encoding so
- // that decorations are added to the generic instead of the
- // value it returns.
- //
- switch(callee->getOp())
+ case kIROp_Specialize:
{
- case kIROp_Specialize:
- {
- callee = cast<IRSpecialize>(callee)->getOperand(0);
- continue;
- }
+ callee = cast<IRSpecialize>(callee)->getOperand(0);
+ continue;
+ }
- case kIROp_Generic:
+ case kIROp_Generic:
+ {
+ auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee));
+ if(genericResult)
{
- auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee));
- if(genericResult)
- {
- callee = genericResult;
- continue;
- }
+ callee = genericResult;
+ continue;
}
-
- default:
- break;
}
+
+ default:
break;
}
- if(callee->getOp() != kIROp_Func)
- continue;
-
- // HACK: we will identify the operation based
- // on the target-intrinsic definition that was
- // given to it.
- auto decoration = as<IRTargetIntrinsicDecoration>(findBestTargetDecoration(callee, CapabilityAtom::GLSL));
- if(!decoration)
- continue;
+ break;
+ }
+ if(callee->getOp() != kIROp_Func)
+ continue;
- if(decoration->getDefinition() != UnownedStringSlice::fromLiteral("EmitVertex()"))
- {
- continue;
- }
+ if (getBuiltinFuncName(callee) != UnownedStringSlice::fromLiteral("GeometryStreamAppend"))
+ {
+ continue;
+ }
- // Okay, we have a declaration, and we want to modify it!
+ // Okay, we have a declaration, and we want to modify it!
- builder->setInsertBefore(ii);
+ builder->setInsertBefore(ii);
- assign(builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2)));
- }
+ assign(builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2)));
}
+ }
- // We will still have references to the parameter coming
- // from the `EmitVertex` calls, so we need to replace it
- // with something. There isn't anything reasonable to
- // replace it with that would have the right type, so
- // we will replace it with an undefined value, knowing
- // that the emitted code will not actually reference it.
- //
- // TODO: This approach to generating geometry shader code
- // is not ideal, and we should strive to find a better
- // approach that involes coding the `EmitVertex` operation
- // directly in the stdlib, similar to how ray-tracing
- // operations like `TraceRay` are handled.
- //
- builder->setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst());
- auto undefinedVal = builder->emitUndefined(pp->getFullType());
- pp->replaceUsesWith(undefinedVal);
+ // We will still have references to the parameter coming
+ // from the `EmitVertex` calls, so we need to replace it
+ // with something. There isn't anything reasonable to
+ // replace it with that would have the right type, so
+ // we will replace it with an undefined value, knowing
+ // that the emitted code will not actually reference it.
+ //
+ // TODO: This approach to generating geometry shader code
+ // is not ideal, and we should strive to find a better
+ // approach that involes coding the `EmitVertex` operation
+ // directly in the stdlib, similar to how ray-tracing
+ // operations like `TraceRay` are handled.
+ //
+ builder->setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst());
+ auto undefinedVal = builder->emitUndefined(pp->getFullType());
+ pp->replaceUsesWith(undefinedVal);
- return;
- }
- if( auto meshOutputType = as<IRMeshOutputType>(valueType) )
- {
- return legalizeMeshOutputParam(context, codeGenContext, func, pp, paramLayout, meshOutputType);
- }
+ return;
}
- else if(pp->findDecoration<IRHLSLMeshPayloadDecoration>())
+ if( auto meshOutputType = as<IRMeshOutputType>(valueType) )
+ {
+ return legalizeMeshOutputParam(context, codeGenContext, func, pp, paramLayout, meshOutputType);
+ }
+ if(pp->findDecoration<IRHLSLMeshPayloadDecoration>())
{
return legalizeMeshPayloadInputParam(context, codeGenContext, pp);
}
@@ -2543,7 +2537,7 @@ void legalizeEntryPointParameterForGLSL(
// Is the parameter type a special pointer type
// that indicates the parameter is used for `out`
// or `inout` access?
- if(auto paramPtrType = as<IROutTypeBase>(paramType) )
+ if( as<IROutTypeBase>(paramType) )
{
// Okay, we have the more interesting case here,
// where the parameter was being passed by reference.
@@ -2551,12 +2545,10 @@ void legalizeEntryPointParameterForGLSL(
// type, which will replace the parameter, along with
// one or more global variables for the actual input/output.
- auto valueType = paramPtrType->getValueType();
-
auto localVariable = builder->emitVar(valueType);
auto localVal = ScalarizedVal::address(localVariable);
- if( const auto inOutType = as<IRInOutType>(paramPtrType) )
+ if( const auto inOutType = as<IRInOutType>(paramType) )
{
// In the `in out` case we need to declare two
// sets of global variables: one for the `in`
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 04f993838..37d4e7e18 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -2129,6 +2129,7 @@ struct IRCall : IRInst
return IROperandList<IRInst>(getOperands() + 1, getOperands() + getOperandCount());
}
IRInst* getArg(UInt index) { return getOperand(index + 1); }
+ void setArg(UInt index, IRInst* arg) { setOperand(index + 1, arg); }
};
struct IRLoad : IRInst
diff --git a/source/slang/slang-ir-lower-buffer-element-type.cpp b/source/slang/slang-ir-lower-buffer-element-type.cpp
index 1c02af82a..a5227ed68 100644
--- a/source/slang/slang-ir-lower-buffer-element-type.cpp
+++ b/source/slang/slang-ir-lower-buffer-element-type.cpp
@@ -184,7 +184,7 @@ namespace Slang
IRBuilder builder(structType);
builder.setInsertAfter(structType);
auto func = builder.createFunc();
- auto funcType = builder.getFuncType(1, (IRType**)&structType, arrayType);
+ auto funcType = builder.getFuncType(1, (IRType**)&arrayType, structType);
func->setFullType(funcType);
builder.addNameHintDecoration(func, UnownedStringSlice("packStorage"));
builder.setInsertInto(func);
@@ -638,6 +638,23 @@ namespace Slang
break;
case kIROp_StructuredBufferGetDimensions:
break;
+ case kIROp_Call:
+ {
+ // If we are calling a function with an l-value pointer from buffer access,
+ // we need to materialize the object as a local variable, and pass the address
+ // of the local variable to the function.
+ builder.setInsertBefore(user);
+ auto newLoad = builder.emitLoad(loweredElementTypeInfo.loweredType, ptrVal);
+ auto unpackedVal = builder.emitCallInst((IRType*)originalElementType, loweredElementTypeInfo.convertLoweredToOriginal, 1, &newLoad);
+ auto var = builder.emitVar((IRType*)originalElementType);
+ builder.emitStore(var, unpackedVal);
+ use->set(var);
+ builder.setInsertAfter(user);
+ auto newVal = builder.emitLoad(var);
+ auto packedVal = builder.emitCallInst((IRType*)loweredElementTypeInfo.loweredType, loweredElementTypeInfo.convertOriginalToLowered, 1, &newVal);
+ builder.emitStore(ptrVal, packedVal);
+ }
+ break;
default:
SLANG_UNREACHABLE("unhandled inst of a buffer/pointer value that needs storage lowering.");
break;
diff --git a/source/slang/slang-ir-peephole.cpp b/source/slang/slang-ir-peephole.cpp
index d344590b1..a7e89eb4f 100644
--- a/source/slang/slang-ir-peephole.cpp
+++ b/source/slang/slang-ir-peephole.cpp
@@ -334,7 +334,46 @@ struct PeepholeContext : InstPassBase
changed = true;
}
}
- else if (inst->getOperand(0)->getOp() == kIROp_MakeArrayFromElement)
+ else if (inst->getOperand(0)->getOp() == kIROp_MakeVector)
+ {
+ auto index = as<IRIntLit>(as<IRGetElement>(inst)->getIndex());
+ if (!index)
+ break;
+ auto opCount = inst->getOperand(0)->getOperandCount();
+ IRIntegerValue startIndex = 0;
+ for (UInt i = 0; i < opCount; i++)
+ {
+ auto element = inst->getOperand(0)->getOperand(i);
+ if (auto elementVecType = as<IRVectorType>(element->getDataType()))
+ {
+ auto vecSize = as<IRIntLit>(elementVecType->getElementCount());
+ if (!vecSize)
+ break;
+ if (index->getValue() >= startIndex && index->getValue() < startIndex + vecSize->getValue())
+ {
+ IRBuilder builder(module);
+ builder.setInsertBefore(inst);
+ auto newElement = builder.emitElementExtract(element, builder.getIntValue(builder.getIntType(), index->getValue() - startIndex));
+ inst->replaceUsesWith(newElement);
+ maybeRemoveOldInst(inst);
+ changed = true;
+ break;
+ }
+ }
+ else
+ {
+ if (startIndex == index->getValue())
+ {
+ inst->replaceUsesWith(element);
+ maybeRemoveOldInst(inst);
+ changed = true;
+ break;
+ }
+ startIndex++;
+ }
+ }
+ }
+ else if (inst->getOperand(0)->getOp() == kIROp_MakeArrayFromElement || inst->getOperand(0)->getOp() == kIROp_MakeVectorFromScalar)
{
inst->replaceUsesWith(inst->getOperand(0)->getOperand(0));
maybeRemoveOldInst(inst);
diff --git a/source/slang/slang-ir-spirv-legalize.cpp b/source/slang/slang-ir-spirv-legalize.cpp
index 309fc5e38..c45378fa3 100644
--- a/source/slang/slang-ir-spirv-legalize.cpp
+++ b/source/slang/slang-ir-spirv-legalize.cpp
@@ -12,6 +12,12 @@
#include "slang-ir-layout.h"
#include "slang-ir-util.h"
#include "slang-ir-dominators.h"
+#include "slang-ir-array-reg-to-mem.h"
+#include "slang-ir-sccp.h"
+#include "slang-ir-dce.h"
+#include "slang-ir-simplify-cfg.h"
+#include "slang-ir-peephole.h"
+#include "slang-ir-redundancy-removal.h"
namespace Slang
{
@@ -255,8 +261,121 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
return false;
}
+ void inferTextureFormat(IRInst* textureInst, IRTextureTypeBase* textureType)
+ {
+ ImageFormat format = ImageFormat::unknown;
+ if (auto decor = textureInst->findDecoration<IRFormatDecoration>())
+ {
+ format = decor->getFormat();
+ }
+ if (format == ImageFormat::unknown)
+ {
+ // If the texture has no format decoration, try to infer it from the type.
+ auto elementType = textureType->getElementType();
+ Int vectorWidth = 1;
+ if (auto elementVecType = as<IRVectorType>(elementType))
+ {
+ if (auto intLitVal = as<IRIntLit>(elementVecType->getElementCount()))
+ {
+ vectorWidth = (Int)intLitVal->getValue();
+ }
+ else
+ {
+ vectorWidth = 0;
+ }
+ elementType = elementVecType->getElementType();
+ }
+ switch (elementType->getOp())
+ {
+ case kIROp_UIntType:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r32ui; break;
+ case 2: format = ImageFormat::rg32ui; break;
+ case 4: format = ImageFormat::rgba32ui; break;
+ }
+ break;
+ case kIROp_IntType:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r32i; break;
+ case 2: format = ImageFormat::rg32i; break;
+ case 4: format = ImageFormat::rgba32i; break;
+ }
+ break;
+ case kIROp_UInt16Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r16ui; break;
+ case 2: format = ImageFormat::rg16ui; break;
+ case 4: format = ImageFormat::rgba16ui; break;
+ }
+ break;
+ case kIROp_Int16Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r16i; break;
+ case 2: format = ImageFormat::rg16i; break;
+ case 4: format = ImageFormat::rgba16i; break;
+ }
+ break;
+ case kIROp_UInt8Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r8ui; break;
+ case 2: format = ImageFormat::rg8ui; break;
+ case 4: format = ImageFormat::rgba8ui; break;
+ }
+ break;
+ case kIROp_Int8Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r8i; break;
+ case 2: format = ImageFormat::rg8i; break;
+ case 4: format = ImageFormat::rgba8i; break;
+ }
+ break;
+ case kIROp_Int64Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r64i; break;
+ default: break;
+ }
+ break;
+ case kIROp_UInt64Type:
+ switch (vectorWidth)
+ {
+ case 1: format = ImageFormat::r64ui; break;
+ default: break;
+ }
+ break;
+ }
+ }
+ if (format != ImageFormat::unknown)
+ {
+ IRBuilder builder(textureInst->getModule());
+ builder.setInsertBefore(textureInst);
+ List<IRInst*> args;
+ args.add(textureType->getOperand(0));
+ if (textureType->getOperandCount() >= 2)
+ args.add(textureType->getOperand(1));
+ else
+ args.add(builder.getIntValue(builder.getUIntType(), 0));
+ args.add(builder.getIntValue(builder.getUIntType(), IRIntegerValue(format)));
+
+ auto newType = (IRType*)builder.emitIntrinsicInst(builder.getTypeKind(), textureType->getOp(), 3, args.getBuffer());
+ textureInst->setFullType(newType);
+ }
+ }
+
void processGlobalParam(IRGlobalParam* inst)
{
+ // If the param is a texture, infer its format.
+ if (auto textureType = as<IRTextureTypeBase>(inst->getDataType()))
+ {
+ inferTextureFormat(inst, textureType);
+ }
+
// If the global param is not a pointer type, make it so and insert explicit load insts.
auto ptrType = as<IRPtrTypeBase>(inst->getDataType());
if (!ptrType)
@@ -724,6 +843,33 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
addUsersToWorkList(newStore);
}
+ void processImageSubscript(IRImageSubscript* subscript)
+ {
+ if (auto ptrType = as<IRPtrTypeBase>(subscript->getDataType()))
+ {
+ if (ptrType->hasAddressSpace())
+ return;
+ IRBuilder builder(m_sharedContext->m_irModule);
+ builder.setInsertBefore(subscript);
+ auto newPtrType = builder.getPtrType(
+ ptrType->getOp(),
+ ptrType->getValueType(),
+ SpvStorageClassImage);
+ subscript->setFullType(newPtrType);
+
+ // HACK: assumes the image operand is a load and replace it with
+ // the pointer to satisfy SPIRV requirements.
+ // We should consider changing the front-end to pass `this` by ref
+ // for the __ref accessor so we will be guaranteed to have a pointer
+ // image operand here.
+ auto image = subscript->getImage();
+ if (auto load = as<IRLoad>(image))
+ subscript->setOperand(0, load->getPtr());
+
+ addUsersToWorkList(subscript);
+ }
+ }
+
void processFieldAddress(IRFieldAddress* inst)
{
if (auto ptrType = as<IRPtrTypeBase>(inst->getBase()->getDataType()))
@@ -942,7 +1088,7 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
}
}
- void processConstructor(IRInst* inst)
+ void maybeHoistConstructInstToGlobalScope(IRInst* inst)
{
// If all of the operands to this instruction are global, we can hoist
// this constructor to be a global too. This is important to make sure
@@ -950,11 +1096,47 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
// constant vectors (using OpConstantComposite).
UIndex opIndex = 0;
for (auto operand = inst->getOperands(); opIndex < inst->getOperandCount(); operand++, opIndex++)
- if(operand->get()->getParent() != m_module->getModuleInst())
+ if (operand->get()->getParent() != m_module->getModuleInst())
return;
inst->insertAtEnd(m_module->getModuleInst());
}
+ void processConstructor(IRInst* inst)
+ {
+ maybeHoistConstructInstToGlobalScope(inst);
+
+ if (inst->getOp() == kIROp_MakeVector
+ && inst->getParent()->getOp() == kIROp_Module
+ && inst->getOperandCount() != (UInt)getIntVal(as<IRVectorType>(inst->getDataType())->getElementCount()))
+ {
+ // SPIRV's OpConstantComposite inst requires the number of operands to
+ // exactly match the number of elements of the composite, so the general
+ // form of vector construction will not work, and we need to convert it.
+ //
+ List<IRInst*> args;
+ IRBuilder builder(inst);
+ builder.setInsertBefore(inst);
+ for (UInt i = 0; i < inst->getOperandCount(); i++)
+ {
+ auto operand = inst->getOperand(i);
+ if (auto operandVecType = as<IRVectorType>(operand->getDataType()))
+ {
+ auto operandVecSize = getIntVal(operandVecType->getElementCount());
+ for (IRIntegerValue j = 0; j < operandVecSize; j++)
+ {
+ args.add(builder.emitElementExtract(operand, j));
+ }
+ }
+ else
+ {
+ args.add(operand);
+ }
+ }
+ auto newMakeVector = builder.emitMakeVector(inst->getDataType(), args);
+ inst->replaceUsesWith(newMakeVector);
+ }
+ }
+
static bool isAsmInst(IRInst* inst)
{
return (as<IRSPIRVAsmInst>(inst) || as<IRSPIRVAsmOperand>(inst));
@@ -970,6 +1152,18 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
}
}
+ void legalizeSPIRVEntryPoint(IRFunc* func, IREntryPointDecoration* entryPointDecor)
+ {
+ if (entryPointDecor->getProfile().getStage() == Stage::Geometry)
+ {
+ if (!func->findDecoration<IRInstanceDecoration>())
+ {
+ IRBuilder builder(func);
+ builder.addDecoration(func, kIROp_InstanceDecoration, builder.getIntValue(builder.getUIntType(), 1));
+ }
+ }
+ }
+
void processModule()
{
// Process global params before anything else, so we don't generate inefficient
@@ -1010,6 +1204,9 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
case kIROp_FieldAddress:
processFieldAddress(as<IRFieldAddress>(inst));
break;
+ case kIROp_ImageSubscript:
+ processImageSubscript(as<IRImageSubscript>(inst));
+ break;
case kIROp_RWStructuredBufferGetElementPtr:
processRWStructuredBufferGetElementPtr(as<IRRWStructuredBufferGetElementPtr>(inst));
break;
@@ -1078,13 +1275,20 @@ struct SPIRVLegalizationContext : public SourceEmitterBase
t->replaceUsesWith(builder.getPtrType(kIROp_PtrType, lowered.structType, SpvStorageClassStorageBuffer));
}
- // SPIRV requires a dominator block to appear before dominated blocks.
- // After legalizing the control flow, we need to sort our blocks to ensure this is true.
for (auto globalInst : m_module->getGlobalInsts())
{
- if (auto func = as<IRGlobalValueWithCode>(globalInst))
+ if (auto func = as<IRFunc>(globalInst))
+ {
+ if (auto entryPointDecor = func->findDecoration<IREntryPointDecoration>())
+ {
+ legalizeSPIRVEntryPoint(func, entryPointDecor);
+ }
+ // SPIRV requires a dominator block to appear before dominated blocks.
+ // After legalizing the control flow, we need to sort our blocks to ensure this is true.
sortBlocksInFunc(func);
+ }
}
+
}
};
@@ -1205,15 +1409,52 @@ void buildEntryPointReferenceGraph(SPIRVEmitSharedContext* context, IRModule* mo
visit(workList[i].entryPoint, workList[i].inst);
}
+void simplifyIRForSpirvLegalization(DiagnosticSink* sink, IRModule* module)
+{
+ bool changed = true;
+ const int kMaxIterations = 8;
+ const int kMaxFuncIterations = 16;
+ int iterationCounter = 0;
+
+ while (changed && iterationCounter < kMaxIterations)
+ {
+ if (sink && sink->getErrorCount())
+ break;
+
+ changed = false;
+
+ changed |= applySparseConditionalConstantPropagationForGlobalScope(module, sink);
+ changed |= peepholeOptimizeGlobalScope(module);
+
+ for (auto inst : module->getGlobalInsts())
+ {
+ auto func = as<IRGlobalValueWithCode>(inst);
+ if (!func)
+ continue;
+ bool funcChanged = true;
+ int funcIterationCount = 0;
+ while (funcChanged && funcIterationCount < kMaxFuncIterations)
+ {
+ funcChanged = false;
+ funcChanged |= applySparseConditionalConstantPropagation(func, sink);
+ funcChanged |= peepholeOptimize(func);
+ funcChanged |= removeRedundancyInFunc(func);
+ funcChanged |= simplifyCFG(func);
+ eliminateDeadCode(func);
+ }
+ }
+ }
+}
+
void legalizeIRForSPIRV(
SPIRVEmitSharedContext* context,
IRModule* module,
const List<IRFunc*>& entryPoints,
CodeGenContext* codeGenContext)
{
- GLSLExtensionTracker extensionTracker;
- legalizeEntryPointsForGLSL(module->getSession(), module, entryPoints, codeGenContext, &extensionTracker);
+ SLANG_UNUSED(entryPoints);
legalizeSPIRV(context, module);
+ simplifyIRForSpirvLegalization(codeGenContext->getSink(), module);
buildEntryPointReferenceGraph(context, module);
}
diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp
index 4a92d263e..dd6cd8892 100644
--- a/source/slang/slang-ir-util.cpp
+++ b/source/slang/slang-ir-util.cpp
@@ -1128,6 +1128,14 @@ IRVarLayout* findVarLayout(IRInst* value)
}
+UnownedStringSlice getBuiltinFuncName(IRInst* callee)
+{
+ auto decor = getResolvedInstForDecorations(callee)->findDecoration<IRKnownBuiltinDecoration>();
+ if (!decor)
+ return UnownedStringSlice();
+ return decor->getName();
+}
+
UnownedStringSlice getBasicTypeNameHint(IRType* basicType)
{
switch (basicType->getOp())
diff --git a/source/slang/slang-ir-util.h b/source/slang/slang-ir-util.h
index 20bac0cbf..8af4f7536 100644
--- a/source/slang/slang-ir-util.h
+++ b/source/slang/slang-ir-util.h
@@ -257,6 +257,8 @@ HashSet<IRBlock*> getParentBreakBlockSet(IRDominatorTree* dom, IRBlock* block);
IRVarLayout* findVarLayout(IRInst* value);
+UnownedStringSlice getBuiltinFuncName(IRInst* callee);
+
// Run an operation over every block in a module
template<typename F>
static void overAllBlocks(IRModule* module, F f)
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index ff6c8c39e..5fa558e15 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -4708,7 +4708,12 @@ namespace Slang
IRInst* base,
IRInst* index)
{
- auto inst = createInst<IRFieldAddress>(
+ if (auto vectorFromScalar = as<IRMakeVectorFromScalar>(base))
+ return vectorFromScalar->getOperand(0);
+ if (base->getOp() == kIROp_MakeArrayFromElement)
+ return base->getOperand(0);
+
+ auto inst = createInst<IRGetElement>(
this,
kIROp_GetElement,
type,
@@ -4737,15 +4742,8 @@ namespace Slang
type = getVectorType(matrixType->getElementType(), matrixType->getColumnCount());
}
SLANG_RELEASE_ASSERT(type);
- auto inst = createInst<IRGetElement>(
- this,
- kIROp_GetElement,
- type,
- base,
- index);
- addInst(inst);
- return inst;
+ return emitElementExtract(type, base, index);
}
IRInst* IRBuilder::emitElementExtract(
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index ef803b642..a24e2e5c7 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -1388,6 +1388,8 @@ struct IRResourceType : IRResourceTypeBase
{
IRType* getElementType() { return (IRType*)getOperand(0); }
IRType* getSampleCount() { return (IRType*)getOperand(1); }
+ bool hasFormat() { return getOperandCount() >= 2; }
+ IRIntegerValue getFormat() { return getIntVal(getOperand(2)); }
IR_PARENT_ISA(ResourceType)
};
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index b3a309d5a..452b24c30 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -1364,12 +1364,12 @@ static void addLinkageDecoration(
? pyExportModifier->name.getUnownedSlice()
: decl->getName()->text.getUnownedSlice());
}
- else if (as<KnownBuiltinAttribute>(modifier))
+ else if (auto knownBuiltinModifier = as<KnownBuiltinAttribute>(modifier))
{
// We add this to the internal instruction, like other name-like
// decorations, for instance "nameHint". This prevents it becoming
// lost during specialization.
- builder->addKnownBuiltinDecoration(inInst, decl->getName()->text.getUnownedSlice());
+ builder->addKnownBuiltinDecoration(inInst, knownBuiltinModifier->name.getUnownedSlice());
}
}
if (as<InterfaceDecl>(decl->parentDecl) &&
diff --git a/source/slang/slang-spirv-opt.cpp b/source/slang/slang-spirv-opt.cpp
new file mode 100644
index 000000000..34c87db9f
--- /dev/null
+++ b/source/slang/slang-spirv-opt.cpp
@@ -0,0 +1,68 @@
+#include "slang-spirv-opt.h"
+#include "slang-spirv-val.h"
+
+namespace Slang
+{
+
+struct RemoveFileRAII
+{
+ String fileName;
+
+ RemoveFileRAII(String inFileName)
+ :fileName(inFileName)
+ {}
+
+ ~RemoveFileRAII()
+ {
+ File::remove(fileName);
+ }
+};
+
+SlangResult optimizeSPIRV(const List<uint8_t>& spirv, String& outErr, List<uint8_t>& outSpv)
+{
+ // Set up our process
+ CommandLine commandLine;
+ commandLine.m_executableLocation.setName("spirv-opt");
+ commandLine.addArg("--merge-return");
+ commandLine.addArg("--inline-entry-points-exhaustive");
+ commandLine.addArg("--eliminate-dead-functions");
+ commandLine.addArg("--eliminate-local-single-block");
+ commandLine.addArg("--eliminate-local-single-store");
+ commandLine.addArg("--eliminate-dead-code-aggressive");
+
+ commandLine.addArg("-o");
+ String outFileName;
+ File::generateTemporary(UnownedStringSlice("out_spv"), outFileName);
+ RemoveFileRAII removeFile(outFileName);
+
+ commandLine.addArg(outFileName);
+
+ RefPtr<Process> p;
+
+ // If we failed to even start the process, then spirv-opt isn't available
+ SLANG_RETURN_ON_FAIL(Process::create(commandLine, 0, p));
+ const auto in = p->getStream(StdStreamType::In);
+ const auto out = p->getStream(StdStreamType::Out);
+ const auto err = p->getStream(StdStreamType::ErrorOut);
+
+ List<Byte> outErrData;
+ SLANG_RETURN_ON_FAIL(StreamUtil::readAndWrite(in, spirv.getArrayView(), out, outSpv, err, outErrData));
+
+ outSpv.clear();
+ File::readAllBytes(outFileName, outSpv);
+
+ SLANG_RETURN_ON_FAIL(p->waitForTermination(3600000));
+
+ outErr = String(
+ reinterpret_cast<const char*>(outErrData.begin()),
+ reinterpret_cast<const char*>(outErrData.end())
+ );
+
+ const auto ret = p->getReturnValue();
+ if (ret != 0)
+ return SLANG_FAIL;
+
+ return debugValidateSPIRV(outSpv);
+}
+
+}
diff --git a/source/slang/slang-spirv-opt.h b/source/slang/slang-spirv-opt.h
new file mode 100644
index 000000000..c7dfde157
--- /dev/null
+++ b/source/slang/slang-spirv-opt.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <cstdint>
+#include "slang-compiler.h"
+
+namespace Slang
+{
+SlangResult optimizeSPIRV(const List<uint8_t>& spirv, String& outErr, List<uint8_t>& outSpv);
+}
+
diff --git a/source/slang/slang-spirv-val.cpp b/source/slang/slang-spirv-val.cpp
index a6bc29306..b5a02f3fe 100644
--- a/source/slang/slang-spirv-val.cpp
+++ b/source/slang/slang-spirv-val.cpp
@@ -55,6 +55,7 @@ SlangResult debugValidateSPIRV(const List<uint8_t>& spirv)
commandLine.m_executableLocation.setName("spirv-val");
commandLine.addArg("--target-env");
commandLine.addArg("vulkan1.2");
+ commandLine.addArg("--scalar-block-layout");
RefPtr<Process> p;
const auto createResult = Process::create(commandLine, 0, p);
// If we failed to even start the process, then validation isn't available
diff --git a/source/slang/slang-stdlib-textures.cpp b/source/slang/slang-stdlib-textures.cpp
index 76ca51380..9254bf966 100644
--- a/source/slang/slang-stdlib-textures.cpp
+++ b/source/slang/slang-stdlib-textures.cpp
@@ -1,4 +1,5 @@
#include "slang-stdlib-textures.h"
+#include <spirv/unified1/spirv.h>
#define EMIT_LINE_DIRECTIVE() sb << "#line " << (__LINE__+1) << " \"slang-stdlib-textures.cpp\"\n"
@@ -256,138 +257,231 @@ void TextureTypeInfo::writeQueryFunctions()
// `GetDimensions`
const char* dimParamTypes[] = {"out float ", "out int ", "out uint "};
- for(auto t : dimParamTypes)
- for(int includeMipInfo = 0; includeMipInfo < 2; ++includeMipInfo)
+ const char* dimParamTypesInner[] = { "float", "int", "uint" };
+ for (int tid = 0; tid < 3; tid++)
{
- StringBuilder glsl;
- {
-
- glsl << "(";
+ auto t = dimParamTypes[tid];
+ auto rawT = dimParamTypesInner[tid];
- int aa = 1;
- String lodStr = ", 0";
+ for (int includeMipInfo = 0; includeMipInfo < 2; ++includeMipInfo)
+ {
+ int sizeDimCount = 0;
+ StringBuilder params;
if (includeMipInfo)
- {
- int mipLevelArg = aa++;
- lodStr = ", int($";
- lodStr.append(mipLevelArg);
- lodStr.append(")");
- }
+ params << "uint mipLevel, ";
- String opStr = " = textureSize($0" + lodStr;
- switch( access )
- {
- case SLANG_RESOURCE_ACCESS_READ_WRITE:
- case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
- opStr = " = imageSize($0";
- break;
-
- default:
- break;
- }
-
-
- int cc = 0;
- switch(baseShape)
+ switch (baseShape)
{
case TextureFlavor::Shape::Shape1D:
- glsl << "($" << aa++ << opStr << ")";
- if (isArray)
- {
- glsl << ".x";
- }
- glsl << ")";
- cc = 1;
+ params << t << "width";
+ sizeDimCount = 1;
break;
case TextureFlavor::Shape::Shape2D:
case TextureFlavor::Shape::ShapeCube:
- glsl << "($" << aa++ << opStr << ").x)";
- glsl << ", ($" << aa++ << opStr << ").y)";
- cc = 2;
+ params << t << "width,";
+ params << t << "height";
+ sizeDimCount = 2;
break;
case TextureFlavor::Shape::Shape3D:
- glsl << "($" << aa++ << opStr << ").x)";
- glsl << ", ($" << aa++ << opStr << ").y)";
- glsl << ", ($" << aa++ << opStr << ").z)";
- cc = 3;
+ params << t << "width,";
+ params << t << "height,";
+ params << t << "depth";
+ sizeDimCount = 3;
break;
default:
- SLANG_UNEXPECTED("unhandled resource shape");
+ assert(!"unexpected");
break;
}
- if(isArray)
+ if (isArray)
{
- glsl << ", ($" << aa++ << opStr << ")." << kComponentNames[cc] << ")";
+ params << ", " << t << "elements";
+ sizeDimCount++;
}
- if(isMultisample)
+ if (isMultisample)
{
- glsl << ", ($" << aa++ << " = textureSamples($0))";
+ params << ", " << t << "sampleCount";
}
if (includeMipInfo)
+ params << ", " << t << "numberOfLevels";
+
+
+ StringBuilder glsl;
{
- glsl << ", ($" << aa++ << " = textureQueryLevels($0))";
- }
+ glsl << "(";
+ int aa = 1;
+ String lodStr = ", 0";
+ if (includeMipInfo)
+ {
+ int mipLevelArg = aa++;
+ lodStr = ", int($";
+ lodStr.append(mipLevelArg);
+ lodStr.append(")");
+ }
- glsl << ")";
- }
+ String opStr = " = textureSize($0" + lodStr;
+ switch (access)
+ {
+ case SLANG_RESOURCE_ACCESS_READ_WRITE:
+ case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
+ opStr = " = imageSize($0";
+ break;
- StringBuilder params;
- if(includeMipInfo)
- params << "uint mipLevel, ";
+ default:
+ break;
+ }
- switch(baseShape)
- {
- case TextureFlavor::Shape::Shape1D:
- params << t << "width";
- break;
+ int cc = 0;
+ switch (baseShape)
+ {
+ case TextureFlavor::Shape::Shape1D:
+ glsl << "($" << aa++ << opStr << ")";
+ if (isArray)
+ {
+ glsl << ".x";
+ }
+ glsl << ")";
+ cc = 1;
+ break;
+
+ case TextureFlavor::Shape::Shape2D:
+ case TextureFlavor::Shape::ShapeCube:
+ glsl << "($" << aa++ << opStr << ").x)";
+ glsl << ", ($" << aa++ << opStr << ").y)";
+ cc = 2;
+ break;
+
+ case TextureFlavor::Shape::Shape3D:
+ glsl << "($" << aa++ << opStr << ").x)";
+ glsl << ", ($" << aa++ << opStr << ").y)";
+ glsl << ", ($" << aa++ << opStr << ").z)";
+ cc = 3;
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unhandled resource shape");
+ break;
+ }
- case TextureFlavor::Shape::Shape2D:
- case TextureFlavor::Shape::ShapeCube:
- params << t << "width,";
- params << t << "height";
- break;
+ if (isArray)
+ {
+ glsl << ", ($" << aa++ << opStr << ")." << kComponentNames[cc] << ")";
+ }
- case TextureFlavor::Shape::Shape3D:
- params << t << "width,";
- params << t << "height,";
- params << t << "depth";
- break;
+ if (isMultisample)
+ {
+ glsl << ", ($" << aa++ << " = textureSamples($0))";
+ }
- default:
- assert(!"unexpected");
- break;
- }
+ if (includeMipInfo)
+ {
+ glsl << ", ($" << aa++ << " = textureQueryLevels($0))";
+ }
- if(isArray)
- {
- params << ", " << t << "elements";
- }
- if(isMultisample)
- {
- params << ", " << t << "sampleCount";
- }
+ glsl << ")";
+ }
- if(includeMipInfo)
- params << ", " << t << "numberOfLevels";
+ StringBuilder spirv;
+ {
+ spirv << "OpCapability ImageQuery; ";
+ spirv << "%vecSize:$$uint";
+ if (sizeDimCount > 1) spirv << sizeDimCount;
+ spirv << " = ";
+ if (isMultisample)
+ spirv << "OpImageQuerySize $this;";
+ else
+ spirv << "OpImageQuerySizeLod $this $0;";
+ auto convertAndStore = [&](UnownedStringSlice uintSourceVal, const char* destParam)
+ {
+ if (UnownedStringSlice(rawT) == "uint")
+ {
+ spirv << "OpStore &" << destParam << " %" << uintSourceVal << ";";
+ }
+ else
+ {
+ if (UnownedStringSlice(rawT) == "int")
+ {
+ spirv << "%c_" << uintSourceVal << " : $$" << rawT << " = OpBitcast %" << uintSourceVal << "; ";
+ }
+ else
+ {
+ spirv << "%c_" << uintSourceVal << " : $$" << rawT << " = OpConvertUToF %" << uintSourceVal << "; ";
+ }
+ spirv << "OpStore &" << destParam << "%c_" << uintSourceVal << ";";
+ }
+ };
+ auto extractSizeComponent = [&](int componentId, const char* destParam)
+ {
+ String elementVal = String("_") + destParam;
+ if (sizeDimCount == 1)
+ {
+ spirv << "%" << elementVal << " : $$uint = OpCopyObject %vecSize; ";
+ }
+ else
+ {
+ spirv << "%" << elementVal << " : $$uint = OpCompositeExtract %vecSize " << componentId << "; ";
+ }
+ convertAndStore(elementVal.getUnownedSlice(), destParam);
+ };
+ switch (baseShape)
+ {
+ case TextureFlavor::Shape::Shape1D:
+ extractSizeComponent(0, "width");
+ break;
+
+ case TextureFlavor::Shape::Shape2D:
+ case TextureFlavor::Shape::ShapeCube:
+ extractSizeComponent(0, "width");
+ extractSizeComponent(1, "height");
+ break;
+
+ case TextureFlavor::Shape::Shape3D:
+ extractSizeComponent(0, "width");
+ extractSizeComponent(1, "height");
+ extractSizeComponent(2, "depth");
+ break;
+
+ default:
+ assert(!"unexpected");
+ break;
+ }
- sb << " __glsl_version(450)\n";
- sb << " __glsl_extension(GL_EXT_samplerless_texture_functions)\n";
- writeFunc(
- "void",
- "GetDimensions",
- params,
- glsl,
- "",
- "",
- ReadNoneMode::Always);
+ if (isArray)
+ {
+ extractSizeComponent(sizeDimCount - 1, "elements");
+ }
+
+ if (isMultisample)
+ {
+ spirv << "%_sampleCount : $$uint = OpImageQuerySamples $this;";
+ convertAndStore(UnownedStringSlice("_sampleCount"), "sampleCount");
+ }
+
+ if (includeMipInfo)
+ {
+ spirv << "%_levelCount : $$uint = OpImageQueryLevels $this;";
+ convertAndStore(UnownedStringSlice("_levelCount"), "numberOfLevels");
+ }
+ }
+
+ sb << " __glsl_version(450)\n";
+ sb << " __glsl_extension(GL_EXT_samplerless_texture_functions)\n";
+ writeFunc(
+ "void",
+ "GetDimensions",
+ params,
+ glsl,
+ spirv,
+ "",
+ ReadNoneMode::Always);
+ }
}
// `GetSamplePosition()`
@@ -493,6 +587,40 @@ void TextureTypeInfo::writeQueryFunctions()
}
}
+ // SPIRV
+ auto getSpirvIntrinsic = [&](bool hasSampleIndex, bool hasOffset)
+ {
+ StringBuilder spirv;
+ spirv << "%lod:$$int = OpCompositeExtract $location " << base.coordCount + isArray << "; ";
+ spirv << "%coord:$$int" << base.coordCount + isArray << " = OpVectorShuffle $location $location ";
+ for (int i = 0; i < base.coordCount + isArray; i++)
+ spirv << " " << i;
+ spirv << "; ";
+ spirv << "%sampled:__sampledType(T) = ";
+ if (access == SLANG_RESOURCE_ACCESS_READ_WRITE)
+ spirv << "OpImageRead";
+ else
+ spirv << "OpImageFetch";
+ spirv << " $this %coord ";
+ uint32_t operandMask = 0;
+ if (!hasSampleIndex)
+ operandMask |= SpvImageOperandsLodMask;
+ if (hasOffset)
+ operandMask |= SpvImageOperandsConstOffsetMask;
+ if (hasSampleIndex)
+ operandMask |= SpvImageOperandsSampleMask;
+ spirv << operandMask << " ";
+ if (operandMask & SpvImageOperandsLodMask)
+ spirv << " %lod";
+ if (operandMask & SpvImageOperandsConstOffsetMask)
+ spirv << " $offset";
+ if (operandMask & SpvImageOperandsSampleMask)
+ spirv << " $sampleIndex";
+ spirv << ";";
+ spirv << i << "__truncate $$T result __sampledType(T) %sampled;";
+ return spirv.produceString();
+ };
+
sb << i << "__glsl_extension(GL_EXT_samplerless_texture_functions)";
writeFunc(
"T",
@@ -508,7 +636,7 @@ void TextureTypeInfo::writeQueryFunctions()
kGLSLLoadLODSwizzle[loadCoordCount],
")$z")
: cat("$c", glslFuncName, "($0, $1)$z"),
- "",
+ getSpirvIntrinsic(isMultisample, false),
cudaBuilder
);
@@ -527,7 +655,8 @@ void TextureTypeInfo::writeQueryFunctions()
"$c", glslFuncName, "($0, ($1).", kGLSLLoadCoordsSwizzle[loadCoordCount],
", ($1).", kGLSLLoadLODSwizzle[loadCoordCount],
", $2)$z")
- : cat("$c", glslFuncName, "($0, $1, 0, $2)$z")
+ : cat("$c", glslFuncName, "($0, $1, 0, $2)$z"),
+ getSpirvIntrinsic(isMultisample, true)
);
writeFunc(