summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-12-20 14:01:33 -0500
committerGitHub <noreply@github.com>2019-12-20 14:01:33 -0500
commit79b52bb8ac2a6059f5bbdc17be22725400b74aad (patch)
tree41569fe825ecae951ff0881c11f356fb1b71dfb8 /source
parent9f0e9d6ba431d8deb000b4fe6ff03c879d662f45 (diff)
HLSLIntrinsicSet (#1159)
* CPPCompiler -> DownstreamCompiler * Added DownstreamCompileResult to start abstraction such that we don't need files. * * Split out slang-blob.cpp * Made CompileResult hold a DownstreamCompileResult - for access to binary or ISlangSharedLibrary * Keep temporary files in scope. * Add a hash to the hex dump stream. * Move all file tracking into DownstreamCompiler. * WIP support for nvrtc. * WIP: Adding support for nvrtc compiler. Adding enum types, wiring up the nvrtc into slang. * Fix remaining CPPCompiler references. * Fix order issue on target string matching. * Use ISlangSharedLibrary for nvrtc. * Use DownstreamCompiler for nvrtc. * WIP first pass at compilation win nvrtc. * Added testing if file is on file system into CommandLineDownstreamCompiler. Added sourceContentsPath. * Make test cuda-compile.cu work by just compiling not comparing output. * Genearlize DownstreamCompiler usage. * Fix warning on clang. * Remove CompilerType from DownstreamCompiler. * Use DownstreamCompiler interface for all compilers. NOTE for FXC, DXC and GLSLANG this doesn't mean using 'compile' - it's still extracting functions from shared library. * Replace DownstreamCompiler::SourceType -> SlangSourceLanguage * Replace _canCompile with something data driven. * Fix compiling on gcc/clang for DownstreamCompiler. * Moved some text conversions into DownstreamCompiler. * Fix problem on non-vc builds with not having return on locateCompilers for VS. * Change so no warning for code not reachable on locateCompilers for vs. * WIP: CUDA code generation - currently just using CPU layout and HLSL. * emitXXXForEntryPoint -> emitEntryPointSource emitSourceForEntryPoint -> emitEntryPointSourceFromIR Fix up generating cuda to get PTX. * WIP emitting cuda for IR. * Small improvements to CUDA ouput. * Disable the CUDA emit test, as output not currently compilable. * Split out IRTypeSet to simplify CPPSourceEmitter and other Emitters that rely on determining unique use of type and/or need to generate types in order to output code. * First pass at HLSLIntrinsicSet. * Small improvements to HLSLIntrinsicSet. * Use HLSLIntrinsicSet in CPPSourceEmitter. * Small improvements to checking of HLSLIntrinsic construction. * Deallocate intrinsic copy if a match was found.
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-emit-cpp.cpp498
-rw-r--r--source/slang/slang-emit-cpp.h213
-rw-r--r--source/slang/slang-hlsl-intrinsic-set.cpp481
-rw-r--r--source/slang/slang-hlsl-intrinsic-set.h261
-rw-r--r--source/slang/slang-ir-insts.h1
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters6
7 files changed, 919 insertions, 543 deletions
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index 46eeee5bd..0d5d1d293 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -168,12 +168,6 @@ static UnownedStringSlice _getCTypeVecPostFix(IROp op)
/* !!!!!!!!!!!!!!!!!!!!!!!! CPPEmitHandler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-static const CPPSourceEmitter::OperationInfo s_operationInfos[] =
-{
-#define SLANG_CPP_INTRINSIC_OP_INFO(x, funcName, numOperands) { UnownedStringSlice::fromLiteral(#x), UnownedStringSlice::fromLiteral(funcName), int8_t(numOperands) },
- SLANG_CPP_INTRINSIC_OP(SLANG_CPP_INTRINSIC_OP_INFO)
-};
-
/* static */ UnownedStringSlice CPPSourceEmitter::getBuiltinTypeName(IROp op)
{
switch (op)
@@ -201,62 +195,6 @@ static const CPPSourceEmitter::OperationInfo s_operationInfos[] =
}
}
-
-/* static */const CPPSourceEmitter::OperationInfo& CPPSourceEmitter::getOperationInfo(IntrinsicOp op)
-{
- return s_operationInfos[int(op)];
-}
-
-/* static */CPPSourceEmitter::IntrinsicOp CPPSourceEmitter::getOperation(IROp op)
-{
- switch (op)
- {
- case kIROp_Add: return IntrinsicOp::Add;
- case kIROp_Mul: return IntrinsicOp::Mul;
- case kIROp_Sub: return IntrinsicOp::Sub;
- case kIROp_Div: return IntrinsicOp::Div;
- case kIROp_Lsh: return IntrinsicOp::Lsh;
- case kIROp_Rsh: return IntrinsicOp::Rsh;
- case kIROp_IRem: return IntrinsicOp::IRem;
- case kIROp_FRem: return IntrinsicOp::FRem;
-
- case kIROp_Eql: return IntrinsicOp::Eql;
- case kIROp_Neq: return IntrinsicOp::Neq;
- case kIROp_Greater: return IntrinsicOp::Greater;
- case kIROp_Less: return IntrinsicOp::Less;
- case kIROp_Geq: return IntrinsicOp::Geq;
- case kIROp_Leq: return IntrinsicOp::Leq;
-
- case kIROp_BitAnd: return IntrinsicOp::BitAnd;
- case kIROp_BitXor: return IntrinsicOp::BitXor;
- case kIROp_BitOr: return IntrinsicOp::BitOr;
-
- case kIROp_And: return IntrinsicOp::And;
- case kIROp_Or: return IntrinsicOp::Or;
-
- case kIROp_Neg: return IntrinsicOp::Neg;
- case kIROp_Not: return IntrinsicOp::Not;
- case kIROp_BitNot: return IntrinsicOp::BitNot;
-
- default: return IntrinsicOp::Invalid;
- }
-}
-
-CPPSourceEmitter::IntrinsicOp CPPSourceEmitter::getOperationByName(const UnownedStringSlice& slice)
-{
- Index index = m_slicePool.findIndex(slice);
- if (index >= 0 && index < m_intrinsicOpMap.getCount())
- {
- IntrinsicOp op = m_intrinsicOpMap[index];
- if (op != IntrinsicOp::Invalid)
- {
- return op;
- }
- }
-
- return IntrinsicOp::Invalid;
-}
-
void CPPSourceEmitter::emitTypeDefinition(IRType* inType)
{
if (m_target == CodeGenTarget::CPPSource)
@@ -704,9 +642,9 @@ static bool _isOperator(const UnownedStringSlice& funcName)
return false;
}
-void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitAryDefinition(const HLSLIntrinsic* specOp)
{
- auto info = getOperationInfo(specOp.op);
+ auto info = HLSLIntrinsic::getInfo(specOp->op);
auto funcName = info.funcName;
SLANG_ASSERT(funcName.size() > 0);
@@ -714,7 +652,7 @@ void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
const int numParams = int(funcType->getParamCount());
SLANG_ASSERT(numParams <= 3);
@@ -732,7 +670,7 @@ void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
return;
}
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
TypeDimension retDim = _getTypeDimension(retType, false);
UnownedStringSlice scalarFuncName(funcName);
@@ -745,7 +683,7 @@ void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
}
else
{
- scalarFuncName = _getScalarFuncName(specOp.op, _getElementType(funcType->getParamType(0)));
+ scalarFuncName = _getScalarFuncName(specOp->op, _getElementType(funcType->getParamType(0)));
_emitSignature(funcName, specOp);
}
@@ -808,9 +746,9 @@ void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 1);
IRType* paramType0 = funcType->getParamType(0);
@@ -818,7 +756,7 @@ void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName,
IRType* elementType = _getElementType(paramType0);
SLANG_ASSERT(elementType);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
auto retTypeName = _getTypeName(retType);
IROp style = _getTypeStyle(elementType->op);
@@ -837,7 +775,7 @@ void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName,
{
if (i > 0 || j > 0)
{
- if (specOp.op == IntrinsicOp::All)
+ if (specOp->op == HLSLIntrinsic::Op::All)
{
writer->emit(" && ");
}
@@ -878,11 +816,11 @@ void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName,
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitSignature(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
const int paramsCount = int(funcType->getParamCount());
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
SourceWriter* writer = getSourceWriter();
@@ -924,13 +862,13 @@ void CPPSourceEmitter::_emitSignature(const UnownedStringSlice& funcName, const
writer->emit(")");
}
-void CPPSourceEmitter::_emitVecMatMulDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitVecMatMulDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 2);
IRType* paramType0 = funcType->getParamType(0);
IRType* paramType1 = funcType->getParamType(1);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
SourceWriter* writer = getSourceWriter();
@@ -974,7 +912,7 @@ void CPPSourceEmitter::_emitVecMatMulDefinition(const UnownedStringSlice& funcNa
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitCrossDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitCrossDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
_emitSignature(funcName, specOp);
@@ -984,32 +922,27 @@ void CPPSourceEmitter::_emitCrossDefinition(const UnownedStringSlice& funcName,
writer->indent();
writer->emit("return ");
- emitType(specOp.returnType);
+ emitType(specOp->returnType);
writer->emit("{ a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; \n");
writer->dedent();
writer->emit("}\n\n");
}
-UnownedStringSlice CPPSourceEmitter::_getAndEmitSpecializedOperationDefinition(IntrinsicOp op, IRType*const* argTypes, Int argCount, IRType* retType)
+UnownedStringSlice CPPSourceEmitter::_getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op op, IRType*const* argTypes, Int argCount, IRType* retType)
{
- // Use the type sets builder...
- IRBuilder& builder = m_typeSet.getBuilder();
-
- SpecializedIntrinsic specOp;
- specOp.op = op;
- specOp.returnType = retType;
- specOp.signatureType = builder.getFuncType(argCount, argTypes, builder.getVoidType());
-
+ HLSLIntrinsic intrinsic;
+ m_intrinsicSet.calcIntrinsic(op, retType, argTypes, argCount, intrinsic);
+ auto specOp = m_intrinsicSet.add(intrinsic);
emitSpecializedOperationDefinition(specOp);
return _getFuncName(specOp);
}
-void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 1);
IRType* paramType0 = funcType->getParamType(0);
@@ -1018,9 +951,9 @@ void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName,
IRBasicType* elementType = as<IRBasicType>(static_cast<IRVectorType*>(paramType0)->getElementType());
IRType* dotArgs[] = { paramType0, paramType0 };
- UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
+ UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
- UnownedStringSlice sqrtName = _getScalarFuncName(IntrinsicOp::Sqrt, elementType);
+ UnownedStringSlice sqrtName = _getScalarFuncName(HLSLIntrinsic::Op::Sqrt, elementType);
_emitSignature(funcName, specOp);
@@ -1037,16 +970,16 @@ void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName,
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitGetAtDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitGetAtDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 2);
IRType* srcType = funcType->getParamType(0);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
emitType(retType);
m_writer->emit("& ");
@@ -1073,11 +1006,11 @@ void CPPSourceEmitter::_emitGetAtDefinition(const UnownedStringSlice& funcName,
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 1);
IRType* paramType0 = funcType->getParamType(0);
@@ -1086,10 +1019,10 @@ void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcNa
IRBasicType* elementType = as<IRBasicType>(static_cast<IRVectorType*>(paramType0)->getElementType());
IRType* dotArgs[] = { paramType0, paramType0 };
- UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
- UnownedStringSlice rsqrtName = _getScalarFuncName(IntrinsicOp::RecipSqrt, elementType);
+ UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
+ UnownedStringSlice rsqrtName = _getScalarFuncName(HLSLIntrinsic::Op::RecipSqrt, elementType);
IRType* vecMulScalarArgs[] = { paramType0, elementType };
- UnownedStringSlice vecMulScalarName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
+ UnownedStringSlice vecMulScalarName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
TypeDimension dimA = _getTypeDimension(paramType0, false);
@@ -1113,15 +1046,15 @@ void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcNa
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 2);
IRType* srcType = funcType->getParamType(1);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
emitType(retType);
writer->emit(" ");
@@ -1179,15 +1112,15 @@ void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice&
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 2);
IRType* srcType = funcType->getParamType(1);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
emitType(retType);
writer->emit(" ");
@@ -1237,11 +1170,11 @@ void CPPSourceEmitter::_emitConstructFromScalarDefinition(const UnownedStringSli
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
{
SourceWriter* writer = getSourceWriter();
- IRFuncType* funcType = specOp.signatureType;
+ IRFuncType* funcType = specOp->signatureType;
SLANG_ASSERT(funcType->getParamCount() == 2);
IRType* paramType0 = funcType->getParamType(0);
@@ -1249,15 +1182,15 @@ void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName
IRBasicType* elementType = as<IRBasicType>(static_cast<IRVectorType*>(paramType0)->getElementType());
- // Make sure we have all these functions defined before emtting
+ // Make sure we have all these functions defined before emitting
IRType* dotArgs[] = { paramType0, paramType0 };
- UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
+ UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
IRType* subArgs[] = { paramType0, paramType0};
- UnownedStringSlice subFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Sub, subArgs, SLANG_COUNT_OF(subArgs), paramType0);
+ UnownedStringSlice subFuncName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Sub, subArgs, SLANG_COUNT_OF(subArgs), paramType0);
IRType* vecMulScalarArgs[] = { paramType0, elementType };
- UnownedStringSlice vecMulScalarFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
+ UnownedStringSlice vecMulScalarFuncName = _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
// Assumes C++
@@ -1273,58 +1206,60 @@ void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName
writer->emit("}\n\n");
}
-void CPPSourceEmitter::emitSpecializedOperationDefinition(const SpecializedIntrinsic& specOp)
+void CPPSourceEmitter::emitSpecializedOperationDefinition(const HLSLIntrinsic* specOp)
{
+ typedef HLSLIntrinsic::Op Op;
+
// Check if it's been emitted already, if not add it.
if (!m_intrinsicEmittedMap.AddIfNotExists(specOp, true))
{
return;
}
- switch (specOp.op)
+ switch (specOp->op)
{
- case IntrinsicOp::VecMatMul:
- case IntrinsicOp::Dot:
+ case Op::VecMatMul:
+ case Op::Dot:
{
return _emitVecMatMulDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::Any:
- case IntrinsicOp::All:
+ case Op::Any:
+ case Op::All:
{
return _emitAnyAllDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::Cross:
+ case Op::Cross:
{
return _emitCrossDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::Normalize:
+ case Op::Normalize:
{
return _emitNormalizeDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::Length:
+ case Op::Length:
{
return _emitLengthDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::Reflect:
+ case Op::Reflect:
{
return _emitReflectDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::ConstructConvert:
+ case Op::ConstructConvert:
{
return _emitConstructConvertDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::ConstructFromScalar:
+ case Op::ConstructFromScalar:
{
return _emitConstructFromScalarDefinition(_getFuncName(specOp), specOp);
}
- case IntrinsicOp::GetAt:
+ case Op::GetAt:
{
return _emitGetAtDefinition(_getFuncName(specOp), specOp);
}
default:
{
- const auto& info = getOperationInfo(specOp.op);
- const int paramCount = (info.numOperands < 0) ? int(specOp.signatureType->getParamCount()) : info.numOperands;
+ const auto& info = HLSLIntrinsic::getInfo(specOp->op);
+ const int paramCount = (info.numOperands < 0) ? int(specOp->signatureType->getParamCount()) : info.numOperands;
if (paramCount >= 1 && paramCount <= 3)
{
@@ -1337,44 +1272,24 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const SpecializedIntri
SLANG_ASSERT(!"Unhandled");
}
-CPPSourceEmitter::SpecializedIntrinsic CPPSourceEmitter::getSpecializedOperation(IntrinsicOp op, IRType*const* inArgTypes, int argTypesCount, IRType* retType)
+void CPPSourceEmitter::emitCall(const HLSLIntrinsic* specOp, IRInst* inst, const IRUse* operands, int numOperands, const EmitOpInfo& inOuterPrec)
{
- SpecializedIntrinsic specOp;
- specOp.op = op;
-
- List<IRType*> argTypes;
- argTypes.setCount(argTypesCount);
-
- for (int i = 0; i < argTypesCount; ++i)
- {
- argTypes[i] = m_typeSet.getType(inArgTypes[i]->getCanonicalType());
- }
-
- specOp.returnType = m_typeSet.getType(retType);
-
- // Use the typesets builder
- IRBuilder& builder = m_typeSet.getBuilder();
- specOp.signatureType = builder.getFuncType(argTypes, builder.getBasicType(BaseType::Void));
-
- return specOp;
-}
+ typedef HLSLIntrinsic::Op Op;
-void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst, const IRUse* operands, int numOperands, const EmitOpInfo& inOuterPrec)
-{
SLANG_UNUSED(inOuterPrec);
SourceWriter* writer = getSourceWriter();
// Getting the name means that this op is registered as used
- switch (specOp.op)
+ switch (specOp->op)
{
- case IntrinsicOp::Init:
+ case Op::Init:
{
// For C++ we don't need an init function
// For C we'll need the construct function for the return type
//UnownedStringSlice name = _getFuncName(specOp);
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
switch (retType->op)
{
@@ -1440,7 +1355,7 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst
}
break;
}
- case IntrinsicOp::Swizzle:
+ case Op::Swizzle:
{
// Currently only works for C++ (we use {} constuction) - which means we don't need to generate a function.
// For C we need to generate suitable construction function
@@ -1448,7 +1363,7 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst
const Index elementCount = Index(swizzleInst->getElementCount());
// TODO(JS): Not 100% sure this is correct on the parens handling front
- IRType* retType = specOp.returnType;
+ IRType* retType = specOp->returnType;
emitType(retType);
writer->emit("{");
@@ -1480,16 +1395,30 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst
}
default:
{
- const auto& info = getOperationInfo(specOp.op);
+ const auto& info = HLSLIntrinsic::getInfo(specOp->op);
// Make sure that the return type is available
bool isOperator = _isOperator(info.funcName);
UnownedStringSlice funcName = _getFuncName(specOp);
- useType(specOp.returnType);
+ switch (specOp->op)
+ {
+ case Op::ConstructFromScalar:
+ {
+ // We need to special case, because this may have come from a swizzle from a built in
+ // type, in that case the only parameter we want is the first one
+ numOperands = 1;
+ break;
+ }
+
+ default: break;
+ }
+
// add that we want a function
SLANG_ASSERT(info.numOperands < 0 || numOperands == info.numOperands);
+ useType(specOp->returnType);
+
if (isOperator)
{
// Just do the default output
@@ -1516,19 +1445,19 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst
}
}
-StringSlicePool::Handle CPPSourceEmitter::_calcScalarFuncName(IntrinsicOp op, IRBasicType* type)
+StringSlicePool::Handle CPPSourceEmitter::_calcScalarFuncName(HLSLIntrinsic::Op op, IRBasicType* type)
{
StringBuilder builder;
- builder << _getTypePrefix(type->op) << "_" << getOperationInfo(op).funcName;
+ builder << _getTypePrefix(type->op) << "_" << HLSLIntrinsic::getInfo(op).funcName;
return m_slicePool.add(builder);
}
-UnownedStringSlice CPPSourceEmitter::_getScalarFuncName(IntrinsicOp op, IRBasicType* type)
+UnownedStringSlice CPPSourceEmitter::_getScalarFuncName(HLSLIntrinsic::Op op, IRBasicType* type)
{
return m_slicePool.getSlice(_calcScalarFuncName(op, type));
}
-UnownedStringSlice CPPSourceEmitter::_getFuncName(const SpecializedIntrinsic& specOp)
+UnownedStringSlice CPPSourceEmitter::_getFuncName(const HLSLIntrinsic* specOp)
{
StringSlicePool::Handle handle = StringSlicePool::kNullHandle;
if (m_intrinsicNameMap.TryGetValue(specOp, handle))
@@ -1543,23 +1472,25 @@ UnownedStringSlice CPPSourceEmitter::_getFuncName(const SpecializedIntrinsic& sp
return m_slicePool.getSlice(handle);
}
-StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrinsic& specOp)
+StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const HLSLIntrinsic* specOp)
{
- if (specOp.isScalar())
+ typedef HLSLIntrinsic::Op Op;
+
+ if (specOp->isScalar())
{
- IRType* paramType = specOp.signatureType->getParamType(0);
+ IRType* paramType = specOp->signatureType->getParamType(0);
IRBasicType* basicType = as<IRBasicType>(paramType);
SLANG_ASSERT(basicType);
- return _calcScalarFuncName(specOp.op, basicType);
+ return _calcScalarFuncName(specOp->op, basicType);
}
else
{
- switch (specOp.op)
+ switch (specOp->op)
{
- case IntrinsicOp::ConstructConvert:
+ case Op::ConstructConvert:
{
// Work out the function name
- IRFuncType* signatureType = specOp.signatureType;
+ IRFuncType* signatureType = specOp->signatureType;
SLANG_ASSERT(signatureType->getParamCount() == 2);
IRType* dstType = signatureType->getParamType(0);
@@ -1574,10 +1505,10 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins
}
return m_slicePool.add(builder);
}
- case IntrinsicOp::ConstructFromScalar:
+ case Op::ConstructFromScalar:
{
// Work out the function name
- IRFuncType* signatureType = specOp.signatureType;
+ IRFuncType* signatureType = specOp->signatureType;
SLANG_ASSERT(signatureType->getParamCount() == 2);
IRType* dstType = signatureType->getParamType(0);
@@ -1591,18 +1522,18 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins
}
return m_slicePool.add(builder);
}
- case IntrinsicOp::GetAt:
+ case Op::GetAt:
{
return m_slicePool.add(UnownedStringSlice::fromLiteral("getAt"));
}
- case IntrinsicOp::SetAt:
+ case Op::SetAt:
{
return m_slicePool.add(UnownedStringSlice::fromLiteral("setAt"));
}
default: break;
}
- const auto& info = getOperationInfo(specOp.op);
+ const auto& info = HLSLIntrinsic::getInfo(specOp->op);
if (info.funcName.size())
{
if (!_isOperator(info.funcName))
@@ -1614,98 +1545,17 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins
}
}
-void CPPSourceEmitter::emitOperationCall(IntrinsicOp op, IRInst* inst, IRUse* operands, int operandCount, IRType* retType, const EmitOpInfo& inOuterPrec)
-{
- switch (op)
- {
- case IntrinsicOp::ConstructFromScalar:
- {
- SLANG_ASSERT(operandCount == 1);
- IRType* dstType = inst->getDataType();
- IRType* srcType = _getElementType(dstType);
- IRType* argTypes[2] = { dstType, srcType };
-
- SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes, 2, retType);
-
- emitCall(specOp, inst, operands, operandCount, inOuterPrec);
- return;
- }
- case IntrinsicOp::ConstructConvert:
- {
- SLANG_ASSERT(inst->getOperandCount() == 1);
- IRType* argTypes[2] = {inst->getDataType(), inst->getOperand(0)->getDataType() };
-
- SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes, 2, retType);
-
- IRFuncType* signatureType = specOp.signatureType;
- SLANG_UNUSED(signatureType);
-
- SLANG_ASSERT(signatureType->getParamType(0) != signatureType->getParamType(1));
-
- emitCall(specOp, inst, operands, operandCount, inOuterPrec);
- return;
- }
- default: break;
- }
-
- if (operandCount > 8)
- {
- List<IRType*> argTypes;
- argTypes.setCount(operandCount);
- for (int i = 0; i < operandCount; ++i)
- {
- // Hmm.. I'm assuming here that the operands exactly match the usage (ie no casting)
- argTypes[i] = operands[i].get()->getDataType();
- }
- SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes.getBuffer(), operandCount, retType);
- emitCall(specOp, inst, operands, operandCount, inOuterPrec);
- }
- else
- {
- IRType* argTypes[8];
- for (int i = 0; i < operandCount; ++i)
- {
- // Hmm.. I'm assuming here that the operands exactly match the usage (ie no casting)
- argTypes[i] = operands[i].get()->getDataType();
- }
- SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes, operandCount, retType);
- emitCall(specOp, inst, operands, operandCount, inOuterPrec);
- }
-}
-
/* !!!!!!!!!!!!!!!!!!!!!! CPPSourceEmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
CPPSourceEmitter::CPPSourceEmitter(const Desc& desc):
Super(desc),
m_slicePool(StringSlicePool::Style::Default),
- m_typeSet(desc.compileRequest->getSession())
+ m_typeSet(desc.compileRequest->getSession()),
+ m_opLookup(new HLSLIntrinsicOpLookup),
+ m_intrinsicSet(&m_typeSet, m_opLookup)
{
m_semanticUsedFlags = 0;
//m_semanticUsedFlags = SemanticUsedFlag::GroupID | SemanticUsedFlag::GroupThreadID | SemanticUsedFlag::DispatchThreadID;
-
- // Add all the operations with names (not ops like -, / etc) to the lookup map
- for (int i = 0; i < SLANG_COUNT_OF(s_operationInfos); ++i)
- {
- const auto& info = s_operationInfos[i];
- UnownedStringSlice slice = info.funcName;
-
- if (slice.size() > 0 && slice[0] >= 'a' && slice[0] <= 'z')
- {
- auto handle = m_slicePool.add(slice);
- Index index = Index(handle);
- // Make sure there is space
- if (index >= m_intrinsicOpMap.getCount())
- {
- Index oldSize = m_intrinsicOpMap.getCount();
- m_intrinsicOpMap.setCount(index + 1);
- for (Index j = oldSize; j < index; j++)
- {
- m_intrinsicOpMap[j] = IntrinsicOp::Invalid;
- }
- }
- m_intrinsicOpMap[index] = IntrinsicOp(i);
- }
- }
}
void CPPSourceEmitter::_emitInOutParamType(IRType* type, String const& name, IRType* valueType)
@@ -1936,6 +1786,8 @@ void CPPSourceEmitter::emitIntrinsicCallExpr(
IRTargetIntrinsicDecoration* targetIntrinsic,
EmitOpInfo const& inOuterPrec)
{
+ typedef HLSLIntrinsic::Op Op;
+
// TODO: Much of this logic duplicates code that is already
// in `CLikeSourceEmitter::emitIntrinsicCallExpr`. The only
// real difference is that when things bottom out on an ordinary
@@ -1994,11 +1846,17 @@ void CPPSourceEmitter::emitIntrinsicCallExpr(
}
else
{
- IntrinsicOp op = getOperationByName(name);
- if (op != IntrinsicOp::Invalid)
+ Op op = m_opLookup->getOpByName(name);
+ if (op != Op::Invalid)
{
IRUse* operands = inst->getOperands() + operandIndex;
- emitOperationCall(op, inst, operands, int(operandCount - operandIndex), inst->getDataType(), inOuterPrec);
+
+ // Work out the intrinsic used
+ HLSLIntrinsic intrinsic;
+ m_intrinsicSet.calcIntrinsic(op, inst->getDataType(), operands, int(operandCount - operandIndex), intrinsic);
+ HLSLIntrinsic* specOp = m_intrinsicSet.add(intrinsic);
+
+ emitCall(specOp, inst, operands, int(operandCount - operandIndex), inOuterPrec);
return;
}
}
@@ -2016,67 +1874,32 @@ void CPPSourceEmitter::emitIntrinsicCallExpr(
maybeCloseParens(needClose);
}
-bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec)
+bool CPPSourceEmitter::_tryEmitInstExprAsIntrinsic(IRInst* inst, const EmitOpInfo& inOuterPrec)
{
- SLANG_UNUSED(inOuterPrec);
-
- switch (inst->op)
+ HLSLIntrinsic* specOp = m_intrinsicSet.add(inst);
+ if (specOp)
{
- case kIROp_constructVectorFromScalar:
+ if (inst->op == kIROp_Call)
{
- SLANG_ASSERT(inst->getOperandCount() == 1);
- IRType* dstType = inst->getDataType();
-
- // Check it's a vector
- SLANG_ASSERT(dstType->op == kIROp_VectorType);
- // Source must be a scalar
- SLANG_ASSERT(as<IRBasicType>(inst->getOperand(0)->getDataType()));
-
- emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec);
- return true;
- }
- case kIROp_Construct:
- {
- IRType* dstType = inst->getDataType();
- IRType* srcType = inst->getOperand(0)->getDataType();
-
- if ((dstType->op == kIROp_VectorType || dstType->op == kIROp_MatrixType) &&
- inst->getOperandCount() == 1)
- {
- if (as<IRBasicType>(srcType))
- {
- emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec);
- }
- else
- {
- SLANG_ASSERT(_getElementType(dstType) != _getElementType(srcType));
- // If it's constructed from a type conversion
- emitOperationCall(IntrinsicOp::ConstructConvert, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec);
- }
- }
- else
- {
- emitOperationCall(IntrinsicOp::Init, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- }
- return true;
+ IRCall* call = static_cast<IRCall*>(inst);
+ emitCall(specOp, inst, call->getArgs(), int(call->getArgCount()), inOuterPrec);
}
- case kIROp_makeVector:
- case kIROp_MakeMatrix:
- {
- emitOperationCall(IntrinsicOp::Init, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- return true;
- }
- case kIROp_Mul_Matrix_Matrix:
- case kIROp_Mul_Matrix_Vector:
- case kIROp_Mul_Vector_Matrix:
+ else
{
- emitOperationCall(IntrinsicOp::VecMatMul, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- return true;
+ emitCall(specOp, inst, inst->getOperands(), int(inst->getOperandCount()), inOuterPrec);
}
- case kIROp_Dot:
+ return true;
+ }
+ return false;
+}
+
+bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec)
+{
+ switch (inst->op)
+ {
+ default:
{
- emitOperationCall(IntrinsicOp::Dot, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- return true;
+ return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec);
}
case kIROp_swizzle:
{
@@ -2096,11 +1919,7 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
{
// If the output is a scalar, then could only have been a .x, which we can just ignore the '.x' part
emitOperand(baseInst, inOuterPrec);
- }
- else
- {
- SLANG_ASSERT(dstType->op == kIROp_VectorType);
- emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), 1, dstType, inOuterPrec);
+ return true;
}
}
else
@@ -2110,15 +1929,11 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
{
// If just one thing is extracted then the . syntax will just work
defaultEmitInstExpr(inst, inOuterPrec);
- }
- else
- {
- // Will need to generate a swizzle method
- emitOperationCall(IntrinsicOp::Swizzle, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
+ return true;
}
}
-
- return true;
+ // try doing automatically
+ return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec);
}
case kIROp_Call:
{
@@ -2127,37 +1942,8 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
// Does this function declare any requirements.
handleCallExprDecorationsImpl(funcValue);
- // We want to detect any call to an intrinsic operation,
- // that we can emit it directly without mangling, etc.
- if(auto targetIntrinsic = findTargetIntrinsicDecoration(funcValue))
- {
- emitIntrinsicCallExpr(static_cast<IRCall*>(inst), targetIntrinsic, inOuterPrec);
- return true;
- }
-
- return false;
- }
- case kIROp_getElement:
- case kIROp_getElementPtr:
- {
- IRInst* target = inst->getOperand(0);
- if (target->getDataType()->op == kIROp_VectorType)
- {
- // Specially handle this
- emitOperationCall(IntrinsicOp::GetAt, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- return true;
- }
- return false;
- }
- default:
- {
- IntrinsicOp op = getOperation(inst->op);
- if (op != IntrinsicOp::Invalid)
- {
- emitOperationCall(op, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec);
- return true;
- }
- return false;
+ // try doing automatically
+ return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec);
}
}
}
diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h
index ffe4a1544..45a9ee35a 100644
--- a/source/slang/slang-emit-cpp.h
+++ b/source/slang/slang-emit-cpp.h
@@ -6,135 +6,18 @@
#include "slang-ir-clone.h"
#include "slang-ir-type-set.h"
+#include "slang-hlsl-intrinsic-set.h"
#include "../core/slang-string-slice-pool.h"
namespace Slang
{
-/* TODO(JS): Note that there are multiple methods to handle 'construction' operations. That is because 'construct' is used as a kind of
-generic 'construction' for built in types including vectors and matrices.
-
-For the moment the cpp emit code, determines what kind of construct is needed, and has special handling for ConstructConvert and
-ConstructFromScalar.
-
-That currently we do not see constructVectorFromScalar - for example when we do...
-
-int2 fromScalar = 1;
-
-This appears as a construction from an int.
-
-That the better thing to do would be that there were IR instructions for the specific types of construction. I suppose there is a question
-about whether there should be separate instructions for vector/matrix, or emit code should just use the destination type. In practice I think
-it's fine that there isn't an instruction separating vector/matrix. That being the case I guess we arguably don't need constructVectorFromScalar,
-just constructXXXFromScalar. Would be good if there was a suitable name to encompass vector/matrix.
-*/
-
-#define SLANG_CPP_INTRINSIC_OP(x) \
- x(Invalid, "", -1) \
- x(Init, "", -1) \
- \
- x(Mul, "*", 2) \
- x(Div, "/", 2) \
- x(Add, "+", 2) \
- x(Sub, "-", 2) \
- x(Lsh, "<<", 2) \
- x(Rsh, ">>", 2) \
- x(IRem, "%", 2) \
- x(FRem, "remainder", 2) \
- \
- x(Eql, "==", 2) \
- x(Neq, "!=", 2) \
- x(Greater, ">", 2) \
- x(Less, "<", 2) \
- x(Geq, ">=", 2) \
- x(Leq, "<=", 2) \
- \
- x(BitAnd, "&", 2) \
- x(BitXor, "^", 2) \
- x(BitOr, "|" , 2) \
- \
- x(And, "&&", 2) \
- x(Or, "||", 2) \
- \
- x(Neg, "-", 1) \
- x(Not, "!", 1) \
- x(BitNot, "~", 1) \
- \
- x(Any, "any", 1) \
- x(All, "all", 1) \
- \
- x(Swizzle, "", -1) \
- \
- x(Dot, "dot", 2) \
- x(VecMatMul, "mul", 2) \
- \
- x(Normalize, "normalize", 1) \
- x(Length, "length", 1) \
- \
- x(Sin, "sin", 1) \
- x(Cos, "cos", 1) \
- x(Tan, "tan", 1) \
- \
- x(ArcSin, "asin", 1) \
- x(ArcCos, "acos", 1) \
- x(ArcTan, "atan", 1) \
- \
- x(ArcTan2, "atan2", 2) \
- x(SinCos, "sincos", 3) \
- \
- x(Rcp, "rcp", 1) \
- x(Sign, "sign", 1) \
- x(Saturate, "saturate", 1) \
- x(Frac, "frac", 1) \
- \
- x(Ceil, "ceil", 1) \
- x(Floor, "floor", 1) \
- x(Trunc, "trunc", 1) \
- \
- x(Sqrt, "sqrt", 1) \
- x(RecipSqrt, "rsqrt", 1) \
- \
- x(Exp2, "exp2", 1) \
- x(Exp, "exp", 1) \
- \
- x(Abs, "abs", 1) \
- \
- x(Min, "min", 2) \
- x(Max, "max", 2) \
- x(Pow, "pow", 2) \
- x(FMod, "fmod", 2) \
- x(Cross, "cross", 2) \
- x(Reflect, "reflect", 2) \
- \
- x(SmoothStep, "smoothstep", 3) \
- x(Lerp, "lerp", 3) \
- x(Clamp, "clamp", 3) \
- x(Step, "step", 2) \
- \
- x(AsFloat, "asfloat", 1) \
- x(AsInt, "asint", -1) \
- x(AsUInt, "asuint", -1) \
- x(AsDouble, "asdouble", 2) \
- \
- x(ConstructConvert, "", 1) \
- x(ConstructFromScalar, "", 1) \
- \
- x(GetAt, "", 2) \
- x(SetAt, "", 3)
-
-
class CPPSourceEmitter: public CLikeSourceEmitter
{
public:
typedef CLikeSourceEmitter Super;
-#define SLANG_CPP_INTRINSIC_OP_ENUM(x, op, numOperands) x,
- enum class IntrinsicOp
- {
- SLANG_CPP_INTRINSIC_OP(SLANG_CPP_INTRINSIC_OP_ENUM)
- };
-
typedef uint32_t SemanticUsedFlags;
struct SemanticUsedFlag
{
@@ -146,42 +29,6 @@ public:
};
};
- struct OperationInfo
- {
- UnownedStringSlice name;
- UnownedStringSlice funcName;
- int8_t numOperands; ///< -1 if can't be handled automatically via amount of params
- };
-
- struct SpecializedIntrinsic
- {
- typedef SpecializedIntrinsic ThisType;
-
- UInt GetHashCode() const { return combineHash(int(op), Slang::GetHashCode(signatureType)); }
-
- bool operator==(const ThisType& rhs) const { return op == rhs.op && returnType == rhs.returnType && signatureType == rhs.signatureType; }
- bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
-
- bool isScalar() const
- {
- int paramCount = int(signatureType->getParamCount());
- for (int i = 0; i < paramCount; ++i)
- {
- IRType* paramType = signatureType->getParamType(i);
- // If any are vec or matrix, then we
- if (paramType->op == kIROp_MatrixType || paramType->op == kIROp_VectorType)
- {
- return false;
- }
- }
- return true;
- }
-
- IntrinsicOp op;
- IRType* returnType;
- IRFuncType* signatureType; // Same as funcType, but has return type of void
- };
-
struct TypeDimension
{
bool isScalar() const { return rowCount <= 1 && colCount <= 1; }
@@ -190,22 +37,13 @@ public:
int colCount;
};
- virtual SpecializedIntrinsic getSpecializedOperation(IntrinsicOp op, IRType*const* argTypes, int argTypesCount, IRType* retType);
virtual void useType(IRType* type);
- virtual void emitCall(const SpecializedIntrinsic& specOp, IRInst* inst, const IRUse* operands, int numOperands, const EmitOpInfo& inOuterPrec);
+ virtual void emitCall(const HLSLIntrinsic* specOp, IRInst* inst, const IRUse* operands, int numOperands, const EmitOpInfo& inOuterPrec);
virtual void emitTypeDefinition(IRType* type);
- virtual void emitSpecializedOperationDefinition(const SpecializedIntrinsic& specOp);
+ virtual void emitSpecializedOperationDefinition(const HLSLIntrinsic* specOp);
- void emitOperationCall(IntrinsicOp op, IRInst* inst, IRUse* operands, int operandCount, IRType* retType, const EmitOpInfo& inOuterPrec);
-
static UnownedStringSlice getBuiltinTypeName(IROp op);
-
- static const OperationInfo& getOperationInfo(IntrinsicOp op);
-
- static IntrinsicOp getOperation(IROp op);
-
- IntrinsicOp getOperationByName(const UnownedStringSlice& slice);
-
+
SourceWriter* getSourceWriter() const { return m_writer; }
CPPSourceEmitter(const Desc& desc);
@@ -233,34 +71,34 @@ protected:
IRTargetIntrinsicDecoration* targetIntrinsic,
EmitOpInfo const& inOuterPrec);
- void _emitVecMatMulDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
+ void _emitVecMatMulDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
- void _emitAryDefinition(const SpecializedIntrinsic& specOp);
+ void _emitAryDefinition(const HLSLIntrinsic* specOp);
// Really we don't want any of these defined like they are here, they should be defined in slang stdlib
- void _emitAnyAllDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitCrossDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitLengthDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitNormalizeDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitConstructConvertDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
- void _emitGetAtDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
+ void _emitAnyAllDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitCrossDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitLengthDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitNormalizeDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitReflectDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitConstructConvertDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
+ void _emitGetAtDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
- void _emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp);
+ void _emitSignature(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
void _emitInOutParamType(IRType* type, String const& name, IRType* valueType);
- UnownedStringSlice _getAndEmitSpecializedOperationDefinition(IntrinsicOp op, IRType*const* argTypes, Int argCount, IRType* retType);
+ UnownedStringSlice _getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op op, IRType*const* argTypes, Int argCount, IRType* retType);
static TypeDimension _getTypeDimension(IRType* type, bool vecSwap);
static void _emitAccess(const UnownedStringSlice& name, const TypeDimension& dimension, int row, int col, SourceWriter* writer);
- StringSlicePool::Handle _calcScalarFuncName(IntrinsicOp op, IRBasicType* type);
- UnownedStringSlice _getScalarFuncName(IntrinsicOp operation, IRBasicType* scalarType);
+ StringSlicePool::Handle _calcScalarFuncName(HLSLIntrinsic::Op, IRBasicType* type);
+ UnownedStringSlice _getScalarFuncName(HLSLIntrinsic::Op operation, IRBasicType* scalarType);
- UnownedStringSlice _getFuncName(const SpecializedIntrinsic& specOp);
- StringSlicePool::Handle _calcFuncName(const SpecializedIntrinsic& specOp);
+ UnownedStringSlice _getFuncName(const HLSLIntrinsic* specOp);
+ StringSlicePool::Handle _calcFuncName(const HLSLIntrinsic* specOp);
UnownedStringSlice _getTypeName(IRType* type);
StringSlicePool::Handle _calcTypeName(IRType* type);
@@ -276,16 +114,17 @@ protected:
void _emitInitAxisValues(const Int sizeAlongAxis[kThreadGroupAxisCount], const UnownedStringSlice& mulName, const UnownedStringSlice& addName);
- Dictionary<SpecializedIntrinsic, StringSlicePool::Handle> m_intrinsicNameMap;
+ bool _tryEmitInstExprAsIntrinsic(IRInst* inst, const EmitOpInfo& inOuterPrec);
+
Dictionary<IRType*, StringSlicePool::Handle> m_typeNameMap;
+ Dictionary<const HLSLIntrinsic*, StringSlicePool::Handle> m_intrinsicNameMap;
IRTypeSet m_typeSet;
+ RefPtr<HLSLIntrinsicOpLookup> m_opLookup;
+ HLSLIntrinsicSet m_intrinsicSet;
Dictionary<IRType*, bool> m_typeEmittedMap;
- Dictionary<SpecializedIntrinsic, bool> m_intrinsicEmittedMap;
-
- // Maps from a name (in the form of a handle/index from m_slicePool) to an operation
- List<IntrinsicOp> m_intrinsicOpMap;
+ Dictionary<const HLSLIntrinsic*, bool> m_intrinsicEmittedMap;
StringSlicePool m_slicePool;
diff --git a/source/slang/slang-hlsl-intrinsic-set.cpp b/source/slang/slang-hlsl-intrinsic-set.cpp
new file mode 100644
index 000000000..9bc7e7d54
--- /dev/null
+++ b/source/slang/slang-hlsl-intrinsic-set.cpp
@@ -0,0 +1,481 @@
+// slang-hlsl-intrinsic-set.cpp
+#include "slang-hlsl-intrinsic-set.h"
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+
+namespace Slang
+{
+
+/* static */const HLSLIntrinsic::Info HLSLIntrinsic::s_operationInfos[] =
+{
+#define SLANG_HLSL_INTRINSIC_OP_INFO(x, funcName, numOperands) { UnownedStringSlice::fromLiteral(#x), UnownedStringSlice::fromLiteral(funcName), int8_t(numOperands) },
+ SLANG_HLSL_INTRINSIC_OP(SLANG_HLSL_INTRINSIC_OP_INFO)
+};
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! HLSLIntrinsicSet !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+HLSLIntrinsicSet::HLSLIntrinsicSet(IRTypeSet* typeSet, HLSLIntrinsicOpLookup* lookup):
+ m_intrinsicFreeList(sizeof(HLSLIntrinsic), SLANG_ALIGN_OF(HLSLIntrinsic), 1024),
+ m_typeSet(typeSet),
+ m_opLookup(lookup)
+{
+}
+
+static IRBasicType* _getElementType(IRType* type)
+{
+ switch (type->op)
+ {
+ case kIROp_VectorType: type = static_cast<IRVectorType*>(type)->getElementType(); break;
+ case kIROp_MatrixType: type = static_cast<IRMatrixType*>(type)->getElementType(); break;
+ default: break;
+ }
+ return dynamicCast<IRBasicType>(type);
+}
+
+void HLSLIntrinsicSet::_calcIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRType*const* inArgs, Index argsCount, HLSLIntrinsic& out)
+{
+ IRBuilder& builder = m_typeSet->getBuilder();
+
+ // Check all types belong to the module
+
+ IRModule* module = builder.getModule();
+
+ SLANG_UNUSED(module);
+ SLANG_ASSERT(returnType->getModule() == module);
+
+ for (Index i = 0; i < argsCount; ++i)
+ {
+ SLANG_ASSERT(inArgs[i]->getModule() == module);
+ }
+
+ // Set up the out
+ out.op = op;
+ out.returnType = returnType;
+
+ switch (op)
+ {
+ case Op::ConstructFromScalar:
+ {
+ //SLANG_ASSERT(argsCount == 1);
+ SLANG_ASSERT(argsCount == 1);
+ IRType* srcType = _getElementType(returnType);
+ IRType* argTypes[2] = { returnType, srcType };
+
+ out.signatureType = builder.getFuncType(2, argTypes, builder.getVoidType());
+ break;
+ }
+ case Op::ConstructConvert:
+ {
+ // Make the return type a parameter, to make the signature take into account
+ SLANG_ASSERT(argsCount == 1);
+ IRType* argTypes[2] = { returnType, inArgs[0] };
+
+ out.signatureType = builder.getFuncType(2, argTypes, builder.getVoidType());
+ break;
+ }
+ default:
+ {
+ out.signatureType = builder.getFuncType(argsCount, inArgs, builder.getVoidType());
+ break;
+ }
+ }
+}
+
+void HLSLIntrinsicSet::calcIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRType*const* inArgTypes, Index argCount, HLSLIntrinsic& out)
+{
+ returnType = m_typeSet->getType(returnType);
+
+ if (argCount <= 8)
+ {
+ IRType* args[8];
+ for (Index i = 0; i < argCount; ++i)
+ {
+ args[i] = m_typeSet->getType(inArgTypes[i]);
+ }
+ _calcIntrinsic(op, returnType, args, argCount, out);
+ }
+ else
+ {
+ List<IRType*> args;
+ args.setCount(argCount);
+
+ for (Index i = 0; i < argCount; ++i)
+ {
+ args[i] = m_typeSet->getType(inArgTypes[i]);
+ }
+ _calcIntrinsic(op, returnType, args.getBuffer(), argCount, out);
+ }
+}
+
+void HLSLIntrinsicSet::calcIntrinsic(HLSLIntrinsic::Op op, IRInst* inst, Index operandCount, HLSLIntrinsic& out)
+{
+ IRType* returnType = m_typeSet->getType(inst->getDataType());
+ if (operandCount <= 8)
+ {
+ IRType* argTypes[8];
+ for (Index i = 0; i < operandCount; ++i)
+ {
+ auto operand = inst->getOperand(i);
+ argTypes[i] = m_typeSet->getType(operand->getDataType());
+ }
+ _calcIntrinsic(op, returnType, argTypes, operandCount, out);
+ }
+ else
+ {
+ List<IRType*> argTypes;
+ argTypes.setCount(operandCount);
+
+ for (Index i = 0; i < operandCount; ++i)
+ {
+ auto operand = inst->getOperand(i);
+ argTypes[i] = m_typeSet->getType(operand->getDataType());
+ }
+ _calcIntrinsic(op, returnType, argTypes.getBuffer(), operandCount, out);
+ }
+}
+
+void HLSLIntrinsicSet::calcIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRUse* inArgs, Index argCount, HLSLIntrinsic& out)
+{
+ returnType = m_typeSet->getType(returnType);
+
+ if (argCount <= 8)
+ {
+ IRType* argTypes[8];
+
+ for (Index i = 0; i < argCount; ++i)
+ {
+ auto operand = inArgs[i].get();
+ argTypes[i] = m_typeSet->getType(operand->getDataType());
+ }
+ _calcIntrinsic(op, returnType, argTypes, argCount, out);
+ }
+ else
+ {
+ List<IRType*> argTypes;
+ argTypes.setCount(argCount);
+
+ for (Index i = 0; i < argCount; ++i)
+ {
+ auto operand = inArgs[i].get();
+ argTypes[i] = m_typeSet->getType(operand->getDataType());
+ }
+ _calcIntrinsic(op, returnType, argTypes.getBuffer(), argCount, out);
+ }
+}
+
+HLSLIntrinsic* HLSLIntrinsicSet::add(IRInst* inst)
+{
+ HLSLIntrinsic intrinsic;
+ if (SLANG_SUCCEEDED(makeIntrinsic(inst, intrinsic)))
+ {
+ return add(intrinsic);
+ }
+ return nullptr;
+}
+
+SlangResult HLSLIntrinsicSet::makeIntrinsic(IRInst* inst, HLSLIntrinsic& out)
+{
+ // Mark as invalid...
+ out.op = Op::Invalid;
+
+ {
+ // See if we can just directly convert
+ Op op = HLSLIntrinsicOpLookup::getOpForIROp(inst->op);
+ if (op != Op::Invalid)
+ {
+ calcIntrinsic(op, inst, inst->getOperandCount(), out);
+ return SLANG_OK;
+ }
+ }
+
+ // All the special cases
+ switch (inst->op)
+ {
+ case kIROp_constructVectorFromScalar:
+ {
+ SLANG_ASSERT(inst->getOperandCount() == 1);
+ calcIntrinsic(Op::ConstructFromScalar, inst, 1, out);
+ return SLANG_OK;
+ }
+ case kIROp_Construct:
+ {
+ IRType* dstType = inst->getDataType();
+ IRType* srcType = inst->getOperand(0)->getDataType();
+
+ if ((dstType->op == kIROp_VectorType || dstType->op == kIROp_MatrixType) &&
+ inst->getOperandCount() == 1)
+ {
+ if (as<IRBasicType>(srcType))
+ {
+ calcIntrinsic(Op::ConstructFromScalar, inst, out);
+ }
+ else
+ {
+ SLANG_ASSERT(m_typeSet->getType(dstType) != m_typeSet->getType(srcType));
+ // If it's constructed from a type conversion
+ calcIntrinsic(Op::ConstructConvert, inst, out);
+ }
+ }
+ else
+ {
+ // We only emit as if it has one operand, but we can tell how many it actually has from the return type
+ calcIntrinsic(Op::Init, inst, 1, out);
+ }
+ return SLANG_OK;
+ }
+ case kIROp_makeVector:
+ case kIROp_MakeMatrix:
+ {
+ // We only emit as if it has one operand, but we can tell how many it actually has from the return type
+ calcIntrinsic(Op::Init, inst, 1, out);
+ return SLANG_OK;
+ }
+ case kIROp_swizzle:
+ {
+ // We don't need to add swizzle function, but we do output the need for some other functions
+
+ // For C++ we don't need to emit a swizzle function
+ // For C we need a construction function
+ auto swizzleInst = static_cast<IRSwizzle*>(inst);
+
+ IRInst* baseInst = swizzleInst->getBase();
+ IRType* baseType = baseInst->getDataType();
+
+ // If we are swizzling from a built in type,
+ if (as<IRBasicType>(baseType))
+ {
+ // We can swizzle a scalar type to be a vector, or just a scalar
+ IRType* dstType = swizzleInst->getDataType();
+ if (!as<IRBasicType>(dstType))
+ {
+ // If it's a scalar make sure we have construct from scalar, because we will want to use that
+ SLANG_ASSERT(dstType->op == kIROp_VectorType);
+ IRType* argTypes[] = { baseType };
+ calcIntrinsic(Op::ConstructFromScalar, inst->getDataType(), argTypes, 1, out);
+ return SLANG_OK;
+ }
+ }
+ else
+ {
+ const Index elementCount = Index(swizzleInst->getElementCount());
+ if (elementCount >= 1)
+ {
+ // Will need to generate a swizzle method
+ calcIntrinsic(Op::Swizzle, inst, out);
+ return SLANG_OK;
+ }
+ }
+ break;
+ }
+ case kIROp_getElement:
+ case kIROp_getElementPtr:
+ {
+ IRInst* target = inst->getOperand(0);
+ if (target->getDataType()->op == kIROp_VectorType)
+ {
+ // Specially handle this
+ calcIntrinsic(Op::GetAt, inst, out);
+ return SLANG_OK;
+ }
+ break;
+ }
+ case kIROp_Call:
+ {
+ IRCall* callInst = (IRCall*)inst;
+ auto funcValue = callInst->getCallee();
+
+ const Op op = m_opLookup->getOpFromTargetDecoration(funcValue);
+ if (op != Op::Invalid)
+ {
+ calcIntrinsic(op, inst->getDataType(), callInst->getArgs(), callInst->getArgCount(), out);
+ return SLANG_OK;
+ }
+ break;
+ }
+
+ default: break;
+ }
+
+ return SLANG_FAIL;
+}
+
+HLSLIntrinsic* HLSLIntrinsicSet::add(const HLSLIntrinsic& intrinsic)
+{
+ // Make sure it's valid(!)
+ SLANG_ASSERT(intrinsic.op != Op::Invalid);
+
+ HLSLIntrinsic* copy = (HLSLIntrinsic*)m_intrinsicFreeList.allocate();
+ *copy = intrinsic;
+ HLSLIntrinsicRef ref(copy);
+ HLSLIntrinsic** found = m_intrinsics.TryGetValueOrAdd(ref, copy);
+ if (found)
+ {
+ // If we have found an intrinsic, we can free the copy
+ m_intrinsicFreeList.deallocate(copy);
+ return *found;
+ }
+ return copy;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! HLSLIntrinsicOpLookup !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+HLSLIntrinsicOpLookup::HLSLIntrinsicOpLookup():
+ m_slicePool(StringSlicePool::Style::Default)
+{
+ // Add all the operations with names (not ops like -, / etc) to the lookup map
+ for (int i = 0; i < SLANG_COUNT_OF(HLSLIntrinsic::s_operationInfos); ++i)
+ {
+ const auto& info = HLSLIntrinsic::getInfo(Op(i));
+ UnownedStringSlice slice = info.funcName;
+
+ if (slice.size() > 0 && slice[0] >= 'a' && slice[0] <= 'z')
+ {
+ auto handle = m_slicePool.add(slice);
+ Index index = Index(handle);
+ // Make sure there is space
+ if (index >= m_sliceToOpMap.getCount())
+ {
+ Index oldSize = m_sliceToOpMap.getCount();
+ m_sliceToOpMap.setCount(index + 1);
+ for (Index j = oldSize; j < index; j++)
+ {
+ m_sliceToOpMap[j] = Op::Invalid;
+ }
+ }
+ m_sliceToOpMap[index] = Op(i);
+ }
+ }
+}
+
+HLSLIntrinsic::Op HLSLIntrinsicOpLookup::getOpByName(const UnownedStringSlice& slice)
+{
+ const Index index = m_slicePool.findIndex(slice);
+ return (index >= 0 && index < m_sliceToOpMap.getCount()) ? m_sliceToOpMap[index] : Op::Invalid;
+}
+
+static IRInst* _getSpecializedValue(IRSpecialize* specInst)
+{
+ auto base = specInst->getBase();
+ auto baseGeneric = as<IRGeneric>(base);
+ if (!baseGeneric)
+ return base;
+
+ auto lastBlock = baseGeneric->getLastBlock();
+ if (!lastBlock)
+ return base;
+
+ auto returnInst = as<IRReturnVal>(lastBlock->getTerminator());
+ if (!returnInst)
+ return base;
+
+ return returnInst->getVal();
+}
+
+HLSLIntrinsic::Op HLSLIntrinsicOpLookup::getOpFromTargetDecoration(IRInst* inInst)
+{
+ // An intrinsic generic function will be invoked through a `specialize` instruction,
+ // so the callee won't directly be the thing that is decorated. We will look up
+ // through specializations until we can see the actual thing being called.
+ //
+ IRInst* inst = inInst;
+ while (auto specInst = as<IRSpecialize>(inst))
+ {
+ inst = _getSpecializedValue(specInst);
+
+ // If `getSpecializedValue` can't find the result value
+ // of the generic being specialized, then it returns
+ // the original instruction. This would be a disaster
+ // for use because this loop would go on forever.
+ //
+ // This case should never happen if the stdlib is well-formed
+ // and the compiler is doing its job right.
+ //
+ SLANG_ASSERT(inst != specInst);
+ }
+
+ // We are just looking for the original name so we can match against it
+ for (auto dd : inst->getDecorations())
+ {
+ if (auto decor = as<IRTargetIntrinsicDecoration>(dd))
+ {
+ // TODO(JS): Should confirm that we'll always have this entry - which we need for lookups to work (we need the name
+ // not a targets transformation)
+ //
+ // It turns out that addCatchAllIntrinsicDecorationIfNeeded will add a target intrinsic with the
+ // original HLSL name, which has a target of ""
+ //
+ // It's not 100% clear this covers all the cases, but for now lets go with that
+ if (decor->getTargetName().size() == 0)
+ {
+ Op op = getOpByName(decor->getDefinition());
+ if (op != Op::Invalid)
+ {
+ return op;
+ }
+ }
+ }
+ }
+
+ return Op::Invalid;
+}
+
+HLSLIntrinsic::Op HLSLIntrinsicOpLookup::getOpForIROp(IRInst* inst)
+{
+ switch (inst->op)
+ {
+ case kIROp_Call:
+ {
+ return getOpFromTargetDecoration(inst);
+ }
+ default: break;
+ }
+ return getOpForIROp(inst->op);
+}
+
+/* static */HLSLIntrinsic::Op HLSLIntrinsicOpLookup::getOpForIROp(IROp op)
+{
+ switch (op)
+ {
+ case kIROp_Add: return Op::Add;
+ case kIROp_Mul: return Op::Mul;
+ case kIROp_Sub: return Op::Sub;
+ case kIROp_Div: return Op::Div;
+ case kIROp_Lsh: return Op::Lsh;
+ case kIROp_Rsh: return Op::Rsh;
+ case kIROp_IRem: return Op::IRem;
+ case kIROp_FRem: return Op::FRem;
+
+ case kIROp_Eql: return Op::Eql;
+ case kIROp_Neq: return Op::Neq;
+ case kIROp_Greater: return Op::Greater;
+ case kIROp_Less: return Op::Less;
+ case kIROp_Geq: return Op::Geq;
+ case kIROp_Leq: return Op::Leq;
+
+ case kIROp_BitAnd: return Op::BitAnd;
+ case kIROp_BitXor: return Op::BitXor;
+ case kIROp_BitOr: return Op::BitOr;
+
+ case kIROp_And: return Op::And;
+ case kIROp_Or: return Op::Or;
+
+ case kIROp_Neg: return Op::Neg;
+ case kIROp_Not: return Op::Not;
+ case kIROp_BitNot: return Op::BitNot;
+
+ case kIROp_constructVectorFromScalar: return Op::ConstructFromScalar;
+
+ case kIROp_Mul_Matrix_Matrix:
+ case kIROp_Mul_Matrix_Vector:
+ case kIROp_Mul_Vector_Matrix:
+ {
+ return Op::VecMatMul;
+ }
+ case kIROp_Dot: return Op::Dot;
+
+ default: return Op::Invalid;
+ }
+}
+
+}
diff --git a/source/slang/slang-hlsl-intrinsic-set.h b/source/slang/slang-hlsl-intrinsic-set.h
new file mode 100644
index 000000000..5e01c0599
--- /dev/null
+++ b/source/slang/slang-hlsl-intrinsic-set.h
@@ -0,0 +1,261 @@
+// slang-hlsl-intrinsic-set.h
+#pragma once
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+
+#include "slang-ir-type-set.h"
+
+#include "../core/slang-string-slice-pool.h"
+
+namespace Slang
+{
+
+/* TODO(JS): Note that there are multiple methods to handle 'construction' operations. That is because 'construct' is used as a kind of
+generic 'construction' for built in types including vectors and matrices.
+
+For the moment the cpp emit code, determines what kind of construct is needed, and has special handling for ConstructConvert and
+ConstructFromScalar.
+
+That currently we do not see constructVectorFromScalar - for example when we do...
+
+int2 fromScalar = 1;
+
+This appears as a construction from an int.
+
+That the better thing to do would be that there were IR instructions for the specific types of construction. I suppose there is a question
+about whether there should be separate instructions for vector/matrix, or emit code should just use the destination type. In practice I think
+it's fine that there isn't an instruction separating vector/matrix. That being the case I guess we arguably don't need constructVectorFromScalar,
+just constructXXXFromScalar. Would be good if there was a suitable name to encompass vector/matrix.
+*/
+#define SLANG_HLSL_INTRINSIC_OP(x) \
+ x(Invalid, "", -1) \
+ x(Init, "", -1) \
+ \
+ x(Mul, "*", 2) \
+ x(Div, "/", 2) \
+ x(Add, "+", 2) \
+ x(Sub, "-", 2) \
+ x(Lsh, "<<", 2) \
+ x(Rsh, ">>", 2) \
+ x(IRem, "%", 2) \
+ x(FRem, "remainder", 2) \
+ \
+ x(Eql, "==", 2) \
+ x(Neq, "!=", 2) \
+ x(Greater, ">", 2) \
+ x(Less, "<", 2) \
+ x(Geq, ">=", 2) \
+ x(Leq, "<=", 2) \
+ \
+ x(BitAnd, "&", 2) \
+ x(BitXor, "^", 2) \
+ x(BitOr, "|" , 2) \
+ \
+ x(And, "&&", 2) \
+ x(Or, "||", 2) \
+ \
+ x(Neg, "-", 1) \
+ x(Not, "!", 1) \
+ x(BitNot, "~", 1) \
+ \
+ x(Any, "any", 1) \
+ x(All, "all", 1) \
+ \
+ x(Swizzle, "", -1) \
+ \
+ x(Dot, "dot", 2) \
+ x(VecMatMul, "mul", 2) \
+ \
+ x(Normalize, "normalize", 1) \
+ x(Length, "length", 1) \
+ \
+ x(Sin, "sin", 1) \
+ x(Cos, "cos", 1) \
+ x(Tan, "tan", 1) \
+ \
+ x(ArcSin, "asin", 1) \
+ x(ArcCos, "acos", 1) \
+ x(ArcTan, "atan", 1) \
+ \
+ x(ArcTan2, "atan2", 2) \
+ x(SinCos, "sincos", 3) \
+ \
+ x(Rcp, "rcp", 1) \
+ x(Sign, "sign", 1) \
+ x(Saturate, "saturate", 1) \
+ x(Frac, "frac", 1) \
+ \
+ x(Ceil, "ceil", 1) \
+ x(Floor, "floor", 1) \
+ x(Trunc, "trunc", 1) \
+ \
+ x(Sqrt, "sqrt", 1) \
+ x(RecipSqrt, "rsqrt", 1) \
+ \
+ x(Exp2, "exp2", 1) \
+ x(Exp, "exp", 1) \
+ \
+ x(Abs, "abs", 1) \
+ \
+ x(Min, "min", 2) \
+ x(Max, "max", 2) \
+ x(Pow, "pow", 2) \
+ x(FMod, "fmod", 2) \
+ x(Cross, "cross", 2) \
+ x(Reflect, "reflect", 2) \
+ \
+ x(SmoothStep, "smoothstep", 3) \
+ x(Lerp, "lerp", 3) \
+ x(Clamp, "clamp", 3) \
+ x(Step, "step", 2) \
+ \
+ x(AsFloat, "asfloat", 1) \
+ x(AsInt, "asint", -1) \
+ x(AsUInt, "asuint", -1) \
+ x(AsDouble, "asdouble", 2) \
+ \
+ x(ConstructConvert, "", 1) \
+ x(ConstructFromScalar, "", 1) \
+ \
+ x(GetAt, "", 2) \
+ x(SetAt, "", 3)
+
+struct HLSLIntrinsic
+{
+ typedef HLSLIntrinsic ThisType;
+
+ enum class Op : uint8_t
+ {
+#define SLANG_HLSL_INTRINSIC_OP_ENUM(name, hlslName, numOperands) name,
+ SLANG_HLSL_INTRINSIC_OP(SLANG_HLSL_INTRINSIC_OP_ENUM)
+ };
+
+ struct Info
+ {
+ UnownedStringSlice name; ///< The enum name
+ UnownedStringSlice funcName; ///< The HLSL function name (if there is one)
+ int8_t numOperands; ///< -1 if can't be handled automatically via amount of params
+ };
+
+ bool operator==(const ThisType& rhs) const { return op == rhs.op && returnType == rhs.returnType && signatureType == rhs.signatureType; }
+ bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ bool isScalar() const
+ {
+ Index paramCount = Index(signatureType->getParamCount());
+ for (Index i = 0; i < paramCount; ++i)
+ {
+ IRType* paramType = signatureType->getParamType(i);
+ // If any are vec or matrix, then we
+ if (paramType->op == kIROp_MatrixType || paramType->op == kIROp_VectorType)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ int GetHashCode() const { return combineHash(int(op), combineHash(Slang::GetHashCode(returnType), Slang::GetHashCode(signatureType))); }
+
+ static const Info& getInfo(Op op) { return s_operationInfos[Index(op)]; }
+ static const Info s_operationInfos[];
+
+ Op op;
+ IRType* returnType;
+ IRFuncType* signatureType; // Same as funcType, but has return type of void
+};
+
+/* A helper type that allows comparing pointers to HLSLIntrinsic types as if they are the values */
+struct HLSLIntrinsicRef
+{
+ typedef HLSLIntrinsicRef ThisType;
+
+ int GetHashCode() const { return m_intrinsic->GetHashCode(); }
+ bool operator==(const ThisType& rhs) const { return m_intrinsic == rhs.m_intrinsic || (*m_intrinsic == *rhs.m_intrinsic); }
+ bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ HLSLIntrinsicRef():m_intrinsic(nullptr) {}
+ HLSLIntrinsicRef(const ThisType& rhs):m_intrinsic(rhs.m_intrinsic) {}
+ HLSLIntrinsicRef(const HLSLIntrinsic* intrinsic): m_intrinsic(intrinsic) {}
+ void operator=(const ThisType& rhs) { m_intrinsic = rhs.m_intrinsic; }
+
+ const HLSLIntrinsic* m_intrinsic;
+};
+
+class HLSLIntrinsicOpLookup : public RefObject
+{
+public:
+ typedef HLSLIntrinsic::Op Op;
+
+ Op getOpFromTargetDecoration(IRInst* inInst);
+ Op getOpByName(const UnownedStringSlice& slice);
+
+ Op getOpForIROp(IRInst* inst);
+
+ HLSLIntrinsicOpLookup();
+
+ /// Given an IROp returns the Op equivalent or Op::Invalid if not found
+ static Op getOpForIROp(IROp op);
+
+protected:
+
+ StringSlicePool m_slicePool;
+ List<Op> m_sliceToOpMap;
+};
+
+
+/* This is used so as to try and use slangs type system to uniquely identify types and specializations on intrinsic.
+That we want to have a pointer to a type be unique, and slang supports this through the m_sharedIRBuilder. BUT for this to
+work all work on the module must use the same sharedIRBuilder, and that appears to not be the case in terms
+of other passes.
+Even if it was the case when we may want to add types as part of emitting, we can't use the previously used
+shared builder, so again we end up with pointers to the same things not being the same thing.
+
+To work around this we clone types we want to use as keys into the 'unique module'.
+This is not necessary for all types though - as we assume nominal types *must* have unique pointers (that is the
+definition of nominal).
+
+This could be handled in other ways (for example not testing equality on pointer equality). Anyway for now this
+works, but probably needs to be handled in a better way. The better way may involve having guarantees about equality
+enabled in other code generation and making de-duping possible in emit code.
+
+Note that one pro for this approach is that it does not alter the source module. That as it stands it's not necessary
+for the source module to be immutable, because it is created for emitting and then discarded.
+ */
+class HLSLIntrinsicSet
+{
+public:
+ typedef HLSLIntrinsic::Op Op;
+
+ /* Note that calculating an intrinsic, the types will be added to the type set. That might mean subsequent code will
+ emit those types being required, which may not be the case */
+
+ void calcIntrinsic(Op op, IRType* returnType, IRType*const* args, Index argsCount, HLSLIntrinsic& out);
+ void calcIntrinsic(Op op, IRInst* inst, Index argsCount, HLSLIntrinsic& out);
+ void calcIntrinsic(Op op, IRType* returnType, IRUse* args, Index argCount, HLSLIntrinsic& out);
+ void calcIntrinsic(Op op, IRInst* inst, HLSLIntrinsic& out) { calcIntrinsic(op, inst, Index(inst->getOperandCount()), out); }
+
+ SlangResult makeIntrinsic(IRInst* inst, HLSLIntrinsic& out);
+
+ HLSLIntrinsic* add(const HLSLIntrinsic& intrinsic);
+
+ /// Returns the intrinsic constructed if there is one from the inst. If not possible to construct returns nullptr.
+ HLSLIntrinsic* add(IRInst* inst);
+
+ HLSLIntrinsicSet(IRTypeSet* typeSet, HLSLIntrinsicOpLookup* lookup);
+
+protected:
+ // All calcs must go through this choke point for some special case handling.
+ // NOTE that this function must only be called with unique types (ie from the m_typeSet)
+ void _calcIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRType*const* inArgs, Index argsCount, HLSLIntrinsic& out);
+
+ Dictionary<HLSLIntrinsicRef, HLSLIntrinsic*> m_intrinsics;
+
+ FreeList m_intrinsicFreeList; ///< the storage for the intrinsics when they are in the map
+
+ HLSLIntrinsicOpLookup* m_opLookup;
+ IRTypeSet* m_typeSet;
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 79f8e2b80..11fdad50d 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -1063,6 +1063,7 @@ struct IRCall : IRInst
IRInst* getCallee() { return getOperand(0); }
UInt getArgCount() { return getOperandCount() - 1; }
+ IRUse* getArgs() { return getOperands() + 1; }
IRInst* getArg(UInt index) { return getOperand(index + 1); }
};
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index df34de00c..017f99a1c 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -208,6 +208,7 @@
<ClInclude Include="slang-emit.h" />
<ClInclude Include="slang-expr-defs.h" />
<ClInclude Include="slang-file-system.h" />
+ <ClInclude Include="slang-hlsl-intrinsic-set.h" />
<ClInclude Include="slang-image-format-defs.h" />
<ClInclude Include="slang-ir-bind-existentials.h" />
<ClInclude Include="slang-ir-clone.h" />
@@ -291,6 +292,7 @@
<ClCompile Include="slang-emit-spirv.cpp" />
<ClCompile Include="slang-emit.cpp" />
<ClCompile Include="slang-file-system.cpp" />
+ <ClCompile Include="slang-hlsl-intrinsic-set.cpp" />
<ClCompile Include="slang-ir-bind-existentials.cpp" />
<ClCompile Include="slang-ir-clone.cpp" />
<ClCompile Include="slang-ir-constexpr.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index 6c3626a3b..350f12b6b 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -75,6 +75,9 @@
<ClInclude Include="slang-file-system.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-hlsl-intrinsic-set.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="slang-image-format-defs.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -320,6 +323,9 @@
<ClCompile Include="slang-file-system.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="slang-hlsl-intrinsic-set.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="slang-ir-bind-existentials.cpp">
<Filter>Source Files</Filter>
</ClCompile>