diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-12-20 14:01:33 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-12-20 14:01:33 -0500 |
| commit | 79b52bb8ac2a6059f5bbdc17be22725400b74aad (patch) | |
| tree | 41569fe825ecae951ff0881c11f356fb1b71dfb8 /source/slang | |
| parent | 9f0e9d6ba431d8deb000b4fe6ff03c879d662f45 (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/slang')
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 498 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 213 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-intrinsic-set.cpp | 481 | ||||
| -rw-r--r-- | source/slang/slang-hlsl-intrinsic-set.h | 261 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 1 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 6 |
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> |
