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/slang-hlsl-intrinsic-set.cpp | |
| 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/slang-hlsl-intrinsic-set.cpp')
| -rw-r--r-- | source/slang/slang-hlsl-intrinsic-set.cpp | 481 |
1 files changed, 481 insertions, 0 deletions
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; + } +} + +} |
