summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-emit-cpp.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-07-09 13:59:30 -0400
committerGitHub <noreply@github.com>2019-07-09 13:59:30 -0400
commitf52f5cd4a7b5b71617b949fc62a78abe8c4822b3 (patch)
tree48f636f33e199b3508d779941c034f7be91debc4 /source/slang/slang-emit-cpp.cpp
parent691ebae763e29327249735d67fbb231c75b17840 (diff)
WIP: slang to C++ code generation (#997)
* WIP: Emitting Cpp * Added HLSLType instead of using IRInst - because they don't seem to be deduped. * Removed need for lexer to take a String. Added mechansim to lookup intrinsic functions on C++. * A c/c++ cross compilation test. * WIP Cpp output using cloning and slang types. * More work to generate mul funcs. * WIP: Outputting some simple C++. * Expose findOrEmitHoistableInst to IRBuilder to aid cloning, * Simplification for checking for BasicTypes. Test infrastructure compiles output C++ code. * Dot and mat/vec multiplication output. * First pass at swizzling. * First support for binary ops. * Builtin binary and unary functions. * Any and all. * WIP adding support for other functions. Added code to generate function signature. * Add scalar functions to slang-cpp-prelude.h * Support for most built in operations. * Tested first ternary. * Checking the emitting of corner cases functions - normalize, length, any, all, normalize, reflect. * Check asfloat etc work. * Fmod support. * WIP Array handling in C++. * First stage in being able to handl arbitrary type output for CLikeSourceEmitter * Removed Handler/Emitter split - so can implement more easily complex type naming. * Array passing by value first pass. * Rename Array -> FixedArray * Outputs structs in C++. * Emit the thread config. * Dimension -> TypeDimension * SpecializedOperation -> SpecializedIntrinsic Operation -> IntrinsicOp Use shared impl of isNominalOp Commented use of m_uniqueModule etc. * Add code to test slang->cpp when compiled doesn't have errors. Does so by building shared library and exporting the entry point. * Fix linux clang/gcc compile error about override not being specified. * Make sure c-cross-compile is run on linux targets/smoke. * Remove c-cross-compile.slang from smoke. * Fix running tests/cross-compile/c-cross-compile.slang on Ubuntu 16.04 * Only add -std=c++11 for C++ source.
Diffstat (limited to 'source/slang/slang-emit-cpp.cpp')
-rw-r--r--source/slang/slang-emit-cpp.cpp1587
1 files changed, 1504 insertions, 83 deletions
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index 08d5d2ee8..e89268106 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -6,10 +6,59 @@
#include "slang-emit-source-writer.h"
#include "slang-mangled-lexer.h"
+#include "slang-ir-clone.h"
+
#include <assert.h>
namespace Slang {
+static const char s_elemNames[] = "xyzw";
+
+static UnownedStringSlice _getTypePrefix(IROp op)
+{
+ switch (op)
+ {
+ case kIROp_BoolType: return UnownedStringSlice::fromLiteral("Bool");
+ case kIROp_IntType: return UnownedStringSlice::fromLiteral("I32");
+ case kIROp_UIntType: return UnownedStringSlice::fromLiteral("U32");
+ case kIROp_FloatType: return UnownedStringSlice::fromLiteral("F32");
+ case kIROp_Int64Type: return UnownedStringSlice::fromLiteral("I64");
+ case kIROp_DoubleType: return UnownedStringSlice::fromLiteral("F64");
+ default: return UnownedStringSlice::fromLiteral("?");
+ }
+}
+
+static IROp _getTypeStyle(IROp op)
+{
+ switch (op)
+ {
+ case kIROp_VoidType:
+ case kIROp_BoolType:
+ {
+ return op;
+ }
+ case kIROp_Int8Type:
+ case kIROp_Int16Type:
+ case kIROp_IntType:
+ case kIROp_UInt8Type:
+ case kIROp_UInt16Type:
+ case kIROp_UIntType:
+ case kIROp_Int64Type:
+ case kIROp_UInt64Type:
+ {
+ // All int like
+ return kIROp_IntType;
+ }
+ case kIROp_HalfType:
+ case kIROp_FloatType:
+ case kIROp_DoubleType:
+ {
+ // All float like
+ return kIROp_FloatType;
+ }
+ default: return kIROp_Invalid;
+ }
+}
static IROp _getCType(IROp op)
{
@@ -33,7 +82,7 @@ static IROp _getCType(IROp op)
case kIROp_Int64Type:
case kIROp_UInt64Type:
{
- // Promote all these to Int16, we can just vary the call to make these work
+ // Promote all these to Int64, we can just vary the call to make these work
return kIROp_Int64Type;
}
case kIROp_DoubleType:
@@ -60,6 +109,7 @@ static UnownedStringSlice _getCTypeVecPostFix(IROp op)
{
case kIROp_BoolType: return UnownedStringSlice::fromLiteral("B");
case kIROp_IntType: return UnownedStringSlice::fromLiteral("I");
+ case kIROp_UIntType: return UnownedStringSlice::fromLiteral("U");
case kIROp_FloatType: return UnownedStringSlice::fromLiteral("F");
case kIROp_Int64Type: return UnownedStringSlice::fromLiteral("I64");
case kIROp_DoubleType: return UnownedStringSlice::fromLiteral("F64");
@@ -67,154 +117,1525 @@ static UnownedStringSlice _getCTypeVecPostFix(IROp op)
}
}
-#if 0
-static UnownedStringSlice _getCTypeName(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)
{
- case kIROp_BoolType: return UnownedStringSlice::fromLiteral("Bool");
- case kIROp_IntType: return UnownedStringSlice::fromLiteral("I32");
- case kIROp_FloatType: return UnownedStringSlice::fromLiteral("F32");
- case kIROp_Int64Type: return UnownedStringSlice::fromLiteral("I64");
- case kIROp_DoubleType: return UnownedStringSlice::fromLiteral("F64");
- default: return UnownedStringSlice::fromLiteral("?");
+ case kIROp_VoidType: return UnownedStringSlice("void");
+ case kIROp_BoolType: return UnownedStringSlice("bool");
+
+ case kIROp_Int8Type: return UnownedStringSlice("int8_t");
+ case kIROp_Int16Type: return UnownedStringSlice("int16_t");
+ case kIROp_IntType: return UnownedStringSlice("int32_t");
+ case kIROp_Int64Type: return UnownedStringSlice("int64_t");
+
+ case kIROp_UInt8Type: return UnownedStringSlice("uint8_t");
+ case kIROp_UInt16Type: return UnownedStringSlice("uint16_t");
+ case kIROp_UIntType: return UnownedStringSlice("uint32_t");
+ case kIROp_UInt64Type: return UnownedStringSlice("uint64_t");
+
+ // Not clear just yet how we should handle half... we want all processing as float probly, but when reading/writing to memory converting
+
+ case kIROp_HalfType: return UnownedStringSlice("half");
+
+ case kIROp_FloatType: return UnownedStringSlice("float");
+ case kIROp_DoubleType: return UnownedStringSlice("double");
+ default: return UnownedStringSlice();
}
}
-#endif
-void CPPSourceEmitter::_emitCVecType(IROp op, Int size)
+
+/* static */const CPPSourceEmitter::OperationInfo& CPPSourceEmitter::getOperationInfo(IntrinsicOp op)
+{
+ return s_operationInfos[int(op)];
+}
+
+/* static */CPPSourceEmitter::IntrinsicOp CPPSourceEmitter::getOperation(IROp op)
{
- m_writer->emit("Vec");
- const UnownedStringSlice postFix = _getCTypeVecPostFix(_getCType(op));
- m_writer->emit(postFix);
- if (postFix.size() > 1)
+ switch (op)
{
- m_writer->emit("_");
+ 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_Mod: return IntrinsicOp::Mod;
+
+ 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;
}
- m_writer->emit(size);
}
-void CPPSourceEmitter::_emitCMatType(IROp op, IRIntegerValue rowCount, IRIntegerValue colCount)
+CPPSourceEmitter::IntrinsicOp CPPSourceEmitter::getOperationByName(const UnownedStringSlice& slice)
{
- m_writer->emit("Mat");
- const UnownedStringSlice postFix = _getCTypeVecPostFix(_getCType(op));
- m_writer->emit(postFix);
- if (postFix.size() > 1)
+ Index index = m_slicePool.findIndex(slice);
+ if (index >= 0 && index < m_intrinsicOpMap.getCount())
{
- m_writer->emit("_");
+ IntrinsicOp op = m_intrinsicOpMap[index];
+ if (op != IntrinsicOp::Invalid)
+ {
+ return op;
+ }
}
- m_writer->emit(rowCount);
- m_writer->emit(colCount);
+
+ return IntrinsicOp::Invalid;
}
-void CPPSourceEmitter::_emitCFunc(BuiltInCOp cop, IRType* type)
+void CPPSourceEmitter::emitTypeDefinition(IRType* inType)
{
- emitSimpleType(type);
- m_writer->emit("_");
+ IRType* type = _cloneType(inType);
+ if (m_typeEmittedMap.TryGetValue(type))
+ {
+ return;
+ }
- switch (cop)
+ if (type->getModule() != m_uniqueModule)
{
- case BuiltInCOp::Init: m_writer->emit("init");
- case BuiltInCOp::Splat: m_writer->emit("splat"); break;
+ // If defined in a different module, we assume they are emitted already. (Assumed to
+ // be a nominal type)
+ return;
+ }
+
+ SourceWriter* writer = getSourceWriter();
+
+ switch (type->op)
+ {
+ case kIROp_VectorType:
+ {
+ auto vecType = static_cast<IRVectorType*>(type);
+ int count = int(GetIntVal(vecType->getElementCount()));
+
+ SLANG_ASSERT(count > 0 && count < 4);
+
+ UnownedStringSlice typeName = _getTypeName(type);
+ UnownedStringSlice elemName = _getTypeName(vecType->getElementType());
+
+ writer->emit("struct ");
+ writer->emit(typeName);
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit(elemName);
+ writer->emit(" ");
+ for (int i = 0; i < count; ++i)
+ {
+ if (i > 0)
+ {
+ writer->emit(", ");
+ }
+ writer->emitChar(s_elemNames[i]);
+ }
+ writer->emit(";\n");
+
+ writer->dedent();
+ writer->emit("};\n\n");
+
+ m_typeEmittedMap.Add(type, true);
+ break;
+ }
+ case kIROp_MatrixType:
+ {
+ auto matType = static_cast<IRMatrixType*>(type);
+
+ const auto rowCount = int(GetIntVal(matType->getRowCount()));
+ const auto colCount = int(GetIntVal(matType->getColumnCount()));
+
+ IRType* vecType = _getVecType(matType->getElementType(), colCount);
+ emitTypeDefinition(vecType);
+
+ UnownedStringSlice typeName = _getTypeName(type);
+ UnownedStringSlice rowTypeName = _getTypeName(vecType);
+
+ writer->emit("struct ");
+ writer->emit(typeName);
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit(rowTypeName);
+ writer->emit(" rows[");
+ writer->emit(rowCount);
+ writer->emit("];\n");
+
+ writer->dedent();
+ writer->emit("};\n\n");
+
+ m_typeEmittedMap.Add(type, true);
+ break;
+ }
+ case kIROp_PtrType:
+ case kIROp_RefType:
+ {
+ // We don't need to output a definition for these types
+ break;
+ }
+ case kIROp_ArrayType:
+ case kIROp_UnsizedArrayType:
+ case kIROp_HLSLRWStructuredBufferType:
+ {
+ // We don't need to output a definition for these with C++ templates
+ // For C we may need to (or do casting at point of usage)
+ break;
+ }
+ default:
+ {
+ if (IRBasicType::isaImpl(type->op))
+ {
+ // Don't emit anything for built in types
+ return;
+ }
+ SLANG_ASSERT(!"Unhandled type");
+ break;
+ }
}
}
-void CPPSourceEmitter::emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type)
+UnownedStringSlice CPPSourceEmitter::_getTypeName(IRType* inType)
{
- SLANG_UNUSED(varDecl);
- SLANG_UNUSED(type);
- SLANG_ASSERT(!"Not implemented");
+ if (isNominalOp(inType->op))
+ {
+ StringSlicePool::Handle handle;
+ // NOTE! This is somewhat unusual -> we are going to add types which aren't cloned and belong to
+ // m_uniqueModule. We *assume* nominal types are de-duped
+ if (!m_typeNameMap.TryGetValue(inType, handle))
+ {
+ auto name = getName(inType);
+ handle = m_slicePool.add(name);
+ m_typeNameMap.Add(inType, handle);
+ }
+ return m_slicePool.getSlice(handle);
+ }
+
+ IRType* type = _cloneType(inType);
+
+ StringSlicePool::Handle handle = StringSlicePool::kNullHandle;
+ if (m_typeNameMap.TryGetValue(type, handle))
+ {
+ return m_slicePool.getSlice(handle);
+ }
+
+ handle = _calcTypeName(type);
+
+ m_typeNameMap.Add(type, handle);
+
+ SLANG_ASSERT(handle != StringSlicePool::kNullHandle);
+ return m_slicePool.getSlice(handle);
}
-void CPPSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLayout* entryPointLayout)
+StringSlicePool::Handle CPPSourceEmitter::_calcTypeName(IRType* type)
{
- SLANG_UNUSED(irFunc);
- SLANG_UNUSED(entryPointLayout);
- SLANG_ASSERT(!"Not implemented");
+ switch (type->op)
+ {
+ case kIROp_HalfType:
+ {
+ // Special case half
+ return m_slicePool.add(getBuiltinTypeName(kIROp_FloatType));
+ }
+ case kIROp_VectorType:
+ {
+ auto vecType = static_cast<IRVectorType*>(type);
+ auto vecCount = int(GetIntVal(vecType->getElementCount()));
+ const IROp elemType = vecType->getElementType()->op;
+
+ StringBuilder builder;
+ builder << "Vec";
+ UnownedStringSlice postFix = _getCTypeVecPostFix(elemType);
+
+ builder << postFix;
+ if (postFix.size() > 1)
+ {
+ builder << "_";
+ }
+ builder << vecCount;
+ return m_slicePool.add(builder);
+ }
+ case kIROp_MatrixType:
+ {
+ auto matType = static_cast<IRMatrixType*>(type);
+
+ auto elementType = matType->getElementType();
+ const auto rowCount = int(GetIntVal(matType->getRowCount()));
+ const auto colCount = int(GetIntVal(matType->getColumnCount()));
+
+ // Make sure there is the vector name too
+ _getTypeName(_getVecType(elementType, colCount));
+
+ StringBuilder builder;
+
+ builder << "Mat";
+ const UnownedStringSlice postFix = _getCTypeVecPostFix(_getCType(elementType->op));
+ builder << postFix;
+ if (postFix.size() > 1)
+ {
+ builder << "_";
+ }
+ builder << rowCount;
+ builder << colCount;
+
+ return m_slicePool.add(builder);
+ }
+ case kIROp_HLSLRWStructuredBufferType:
+ {
+ auto bufType = static_cast<IRHLSLRWStructuredBufferType*>(type);
+
+ StringBuilder builder;
+ builder << "RWStructuredBuffer<";
+ builder << _getTypeName(bufType->getElementType());
+ builder << ">";
+
+ return m_slicePool.add(builder);
+ }
+ case kIROp_ArrayType:
+ {
+ auto arrayType = static_cast<IRArrayType*>(type);
+ auto elementType = arrayType->getElementType();
+ int elementCount = int(GetIntVal(arrayType->getElementCount()));
+
+ StringBuilder builder;
+ builder << "FixedArray<";
+ builder << _getTypeName(elementType);
+ builder << ", " << elementCount << ">";
+
+ return m_slicePool.add(builder);
+ }
+ default:
+ {
+ if (IRBasicType::isaImpl(type->op))
+ {
+ return m_slicePool.add(getBuiltinTypeName(type->op));
+ }
+ break;
+ }
+ }
+
+ return StringSlicePool::kNullHandle;
}
-void CPPSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount)
+void CPPSourceEmitter::useType(IRType* type)
{
- _emitCVecType(elementType->op, Int(elementCount));
+ _getTypeName(type);
}
-void CPPSourceEmitter::emitSimpleTypeImpl(IRType* type)
+IRInst* CPPSourceEmitter::_clone(IRInst* inst)
{
- switch (type->op)
+ if (inst == nullptr)
{
- case kIROp_VoidType:
- case kIROp_BoolType:
- case kIROp_Int8Type:
- case kIROp_Int16Type:
- case kIROp_IntType:
- case kIROp_Int64Type:
- case kIROp_UInt8Type:
- case kIROp_UInt16Type:
- case kIROp_UIntType:
- case kIROp_UInt64Type:
- case kIROp_FloatType:
- case kIROp_DoubleType:
+ return nullptr;
+ }
+
+ // If it's in this module then we don't need to clone
+ if (inst->getModule() == m_uniqueModule)
+ {
+ return inst;
+ }
+
+ if (isNominalOp(inst->op))
+ {
+ // If it's nominal we don't bother copying, as we assumed it is already de-duped
+ return inst;
+ }
+
+ if (IRInst*const* newInstPtr = m_cloneMap.TryGetValue(inst))
+ {
+ return *newInstPtr;
+ }
+
+ // It would be nice if I could use ir-clone.cpp to do this -> but it doesn't clone
+ // operands. We wouldn't want to clone decorations, and it can't clone IRConstant(!) so
+ // it's no use
+
+ IRInst* clone = nullptr;
+
+ switch (inst->op)
+ {
+ case kIROp_IntLit:
{
- m_writer->emit(getDefaultBuiltinTypeName(type->op));
- return;
+ auto intLit = static_cast<IRConstant*>(inst);
+ IRType* cloneType = _cloneType(intLit->getDataType());
+ clone = m_irBuilder.getIntValue(cloneType, intLit->value.intVal);
+ break;
}
- case kIROp_HalfType:
+ default:
{
- m_writer->emit("float");
- return;
+ if (IRBasicType::isaImpl(inst->op))
+ {
+ clone = m_irBuilder.getType(inst->op);
+ }
+ else
+ {
+ IRType* irType = dynamicCast<IRType>(inst);
+ if (irType)
+ {
+ auto cloneType = _cloneType(inst->getFullType());
+ Index operandCount = Index(inst->getOperandCount());
+
+ List<IRInst*> cloneOperands;
+ cloneOperands.setCount(operandCount);
+
+ for (Index i = 0; i < operandCount; ++i)
+ {
+ cloneOperands[i] = _clone(inst->getOperand(i));
+ }
+
+ clone = m_irBuilder.findOrEmitHoistableInst(cloneType, inst->op, operandCount, cloneOperands.getBuffer());
+ }
+ else
+ {
+ // This cloning style only works on insts that are not unique
+ auto cloneType = _cloneType(inst->getFullType());
+
+ Index operandCount = Index(inst->getOperandCount());
+ clone = m_irBuilder.emitIntrinsicInst(cloneType, inst->op, operandCount, nullptr);
+ for (Index i = 0; i < operandCount; ++i)
+ {
+ auto cloneOperand = _clone(inst->getOperand(i));
+ clone->getOperands()[i].init(clone, cloneOperand);
+ }
+ }
+ }
+ break;
}
- case kIROp_StructType:
- m_writer->emit(getName(type));
- return;
+ }
+ m_cloneMap.Add(inst, clone);
+ return clone;
+}
+
+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);
+}
+
+/* static */CPPSourceEmitter::TypeDimension CPPSourceEmitter::_getTypeDimension(IRType* type, bool vecSwap)
+{
+ switch (type->op)
+ {
case kIROp_VectorType:
{
- auto vecType = (IRVectorType*)type;
- emitVectorTypeNameImpl(vecType->getElementType(), GetIntVal(vecType->getElementCount()));
- return;
+ auto vecType = static_cast<IRVectorType*>(type);
+ const int elemCount = int(GetIntVal(vecType->getElementCount()));
+ return (!vecSwap) ? TypeDimension{1, elemCount} : TypeDimension{ elemCount, 1};
}
case kIROp_MatrixType:
{
- auto matType = (IRMatrixType*)type;
+ auto matType = static_cast<IRMatrixType*>(type);
+ const int colCount = int(GetIntVal(matType->getColumnCount()));
+ const int rowCount = int(GetIntVal(matType->getRowCount()));
+ return TypeDimension{rowCount, colCount};
+ }
+ default: return TypeDimension{1, 1};
+ }
+}
- const auto rowCount = GetIntVal(matType->getRowCount());
- const auto colCount = GetIntVal(matType->getColumnCount());
+/* static */void CPPSourceEmitter::_emitAccess(const UnownedStringSlice& name, const TypeDimension& dimension, int row, int col, SourceWriter* writer)
+{
+ writer->emit(name);
+ const int comb = (dimension.colCount > 1 ? 2 : 0) | (dimension.rowCount > 1 ? 1 : 0);
+ switch (comb)
+ {
+ case 0:
+ {
+ break;
+ }
+ case 1:
+ case 2:
+ {
+ // Vector
+ int index = (row > col) ? row : col;
+ writer->emit(".");
+ writer->emitChar(s_elemNames[index]);
+ break;
+ }
+ case 3:
+ {
+ // Matrix
+ writer->emit(".rows[");
+ writer->emit(row);
+ writer->emit("].");
+ writer->emitChar(s_elemNames[col]);
+ break;
+ }
+ }
+}
- _emitCMatType(matType->getElementType()->op, rowCount, colCount);
+static bool _isOperator(const UnownedStringSlice& funcName)
+{
+ const char c = funcName[0];
+ return !((c >= 'a' && c <='z') || (c >= 'A' && c <= 'Z') || c == '_');
+}
- return;
+void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp)
+{
+ auto info = getOperationInfo(specOp.op);
+ auto funcName = info.funcName;
+ SLANG_ASSERT(funcName.size() > 0);
+
+ const bool isOperator = _isOperator(funcName);
+
+ SourceWriter* writer = getSourceWriter();
+
+ IRFuncType* funcType = specOp.signatureType;
+ const int numParams = int(funcType->getParamCount());
+ SLANG_ASSERT(numParams <= 3);
+
+ bool areAllScalar = true;
+ TypeDimension paramDims[3];
+ for (int i = 0; i < numParams; ++i)
+ {
+ paramDims[i]= _getTypeDimension(funcType->getParamType(i), false);
+ areAllScalar = areAllScalar && paramDims[i].isScalar();
+ }
+
+ // If all are scalar, then we don't need to emit a definition
+ if (areAllScalar)
+ {
+ return;
+ }
+
+ IRType* retType = specOp.returnType;
+ TypeDimension retDim = _getTypeDimension(retType, false);
+
+ UnownedStringSlice scalarFuncName(funcName);
+ if (isOperator)
+ {
+ StringBuilder builder;
+ builder << "operator";
+ builder << funcName;
+ _emitSignature(builder.getUnownedSlice(), specOp);
+ }
+ else
+ {
+ scalarFuncName = _getScalarFuncName(specOp.op, _getElementType(funcType->getParamType(0)));
+ _emitSignature(funcName, specOp);
+ }
+
+ writer->emit("\n{\n");
+ writer->indent();
+
+ emitType(retType);
+ writer->emit(" r;\n");
+
+ for (int i = 0; i < retDim.rowCount; ++i)
+ {
+ for (int j = 0; j < retDim.colCount; ++j)
+ {
+ _emitAccess(UnownedStringSlice::fromLiteral("r"), retDim, i, j, writer);
+ writer->emit(" = ");
+ if (isOperator)
+ {
+ switch (numParams)
+ {
+ case 1:
+ {
+ writer->emit(funcName);
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), paramDims[0], i, j, writer);
+ break;
+ }
+ case 2:
+ {
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), paramDims[0], i, j, writer);
+ writer->emit(" ");
+ writer->emit(funcName);
+ writer->emit(" ");
+ _emitAccess(UnownedStringSlice::fromLiteral("b"), paramDims[1], i, j, writer);
+ break;
+ }
+ default: SLANG_ASSERT(!"Unhandled");
+ }
+ }
+ else
+ {
+ writer->emit(scalarFuncName);
+ writer->emit("(");
+ for (int k = 0; k < numParams; k++)
+ {
+ if (k > 0)
+ {
+ writer->emit(", ");
+ }
+ char c = char('a' + k);
+ _emitAccess(UnownedStringSlice(&c, 1), paramDims[k], i, j, writer);
+ }
+ writer->emit(")");
+ }
+ writer->emit(";\n");
}
}
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled type for cpp target");
+ writer->emit("return r;\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
}
+void CPPSourceEmitter::_emitAnyAllDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ IRFuncType* funcType = specOp.signatureType;
+ SLANG_ASSERT(funcType->getParamCount() == 1);
+ IRType* paramType0 = funcType->getParamType(0);
+
+ SourceWriter* writer = getSourceWriter();
+
+ IRType* elementType = _getElementType(paramType0);
+ SLANG_ASSERT(elementType);
+ IRType* retType = specOp.returnType;
+ auto retTypeName = _getTypeName(retType);
+
+ IROp style = _getTypeStyle(elementType->op);
+
+ const TypeDimension dim = _getTypeDimension(paramType0, false);
+
+ _emitSignature(funcName, specOp);
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit("return ");
+
+ for (int i = 0; i < dim.rowCount; ++i)
+ {
+ for (int j = 0; j < dim.colCount; ++j)
+ {
+ if (i > 0 || j > 0)
+ {
+ if (specOp.op == IntrinsicOp::All)
+ {
+ writer->emit(" && ");
+ }
+ else
+ {
+ writer->emit(" || ");
+ }
+ }
+
+ switch (style)
+ {
+ case kIROp_BoolType:
+ {
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), dim, i, j, writer);
+ break;
+ }
+ case kIROp_IntType:
+ {
+ writer->emit("(");
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), dim, i, j, writer);
+ writer->emit(" != 0)");
+ break;
+ }
+ case kIROp_FloatType:
+ {
+ writer->emit("(");
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), dim, i, j, writer);
+ writer->emit(" != 0.0)");
+ break;
+ }
+ }
+ }
+ }
+
+ writer->emit(";\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
+}
+
+void CPPSourceEmitter::_emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ IRFuncType* funcType = specOp.signatureType;
+ const int paramsCount = int(funcType->getParamCount());
+ IRType* retType = specOp.returnType;
+
+ SourceWriter* writer = getSourceWriter();
+
+ emitType(retType);
+ writer->emit(" ");
+ writer->emit(funcName);
+ writer->emit("(");
+
+ for (int i = 0; i < paramsCount; ++i)
+ {
+ if (i > 0)
+ {
+ writer->emit(", ");
+ }
+
+ // We can't pass as const& for vector, scalar, array types, as they are pass by value
+ // For types passed by reference, we should do something different
+ IRType* paramType = funcType->getParamType(i);
+#if 0
+ writer->emit("const ");
+#endif
+ emitType(paramType);
+#if 0
+ if (dynamicCast<IRBasicType>(paramType))
+ {
+ writer->emit(" ");
+ }
+ else
+ {
+ writer->emit("& ");
+ }
+#else
+
+ writer->emit(" ");
+#endif
+
+ writer->emitChar(char('a' + i));
+ }
+ writer->emit(")");
+}
+
+void CPPSourceEmitter::_emitVecMatMulDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ IRFuncType* funcType = specOp.signatureType;
+ SLANG_ASSERT(funcType->getParamCount() == 2);
+ IRType* paramType0 = funcType->getParamType(0);
+ IRType* paramType1 = funcType->getParamType(1);
+ IRType* retType = specOp.returnType;
+
+ SourceWriter* writer = getSourceWriter();
+
+ _emitSignature(funcName, specOp);
+
+ writer->emit("\n{\n");
+ writer->indent();
+
+ emitType(retType);
+ writer->emit(" r;\n");
+
+ TypeDimension dimA = _getTypeDimension(paramType0, false);
+ TypeDimension dimB = _getTypeDimension(paramType1, true);
+ TypeDimension resultDim = _getTypeDimension(retType, paramType1->op == kIROp_VectorType);
+
+ for (int i = 0; i < resultDim.rowCount; ++i)
+ {
+ for (int j = 0; j < resultDim.colCount; ++j)
+ {
+ _emitAccess(UnownedStringSlice::fromLiteral("r"), resultDim, i, j, writer);
+ writer->emit(" = ");
+
+ for (int k = 0; k < dimA.colCount; k++)
+ {
+ if (k > 0)
+ {
+ writer->emit(" + ");
+ }
+ _emitAccess(UnownedStringSlice::fromLiteral("a"), dimA, i, k, writer);
+ writer->emit(" * ");
+ _emitAccess(UnownedStringSlice::fromLiteral("b"), dimB, k, j, writer);
+ }
+
+ writer->emit(";\n");
+ }
+ }
+
+ writer->emit("return r;\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
+}
+
+void CPPSourceEmitter::_emitCrossDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ _emitSignature(funcName, specOp);
+
+ SourceWriter* writer = getSourceWriter();
+
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit("return ");
+ 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)
+{
+ SpecializedIntrinsic specOp;
+ specOp.op = op;
+ specOp.returnType = retType;
+ specOp.signatureType = m_irBuilder.getFuncType(argCount, argTypes, m_irBuilder.getVoidType());
+
+ emitSpecializedOperationDefinition(specOp);
+ return _getFuncName(specOp);
+}
+
+void CPPSourceEmitter::_emitLengthDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ SourceWriter* writer = getSourceWriter();
+
+ IRFuncType* funcType = specOp.signatureType;
+ SLANG_ASSERT(funcType->getParamCount() == 1);
+ IRType* paramType0 = funcType->getParamType(0);
+
+ SLANG_ASSERT(paramType0->op == kIROp_VectorType);
+
+ 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 sqrtName = _getScalarFuncName(IntrinsicOp::Sqrt, elementType);
+
+ _emitSignature(funcName, specOp);
+
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit("return ");
+ writer->emit(sqrtName);
+ writer->emit("(");
+ writer->emit(dotFuncName);
+ writer->emit("(a, a));\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
+}
+
+void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ SourceWriter* writer = getSourceWriter();
+
+ IRFuncType* funcType = specOp.signatureType;
+ SLANG_ASSERT(funcType->getParamCount() == 1);
+ IRType* paramType0 = funcType->getParamType(0);
+
+ SLANG_ASSERT(paramType0->op == kIROp_VectorType);
+
+ 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);
+ IRType* vecMulScalarArgs[] = { paramType0, elementType };
+ UnownedStringSlice vecMulScalarName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
+
+ TypeDimension dimA = _getTypeDimension(paramType0, false);
+
+ // Assumes C++
+
+ _emitSignature(funcName, specOp);
+
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit("return ");
+
+ // Assumes C++ here
+ writer->emit("a * ");
+ writer->emit(rsqrtName);
+ writer->emit("(");
+ writer->emit(dotFuncName);
+ writer->emit("(a, a));\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
+}
+
+void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp)
+{
+ SourceWriter* writer = getSourceWriter();
+
+ IRFuncType* funcType = specOp.signatureType;
+ SLANG_ASSERT(funcType->getParamCount() == 2);
+ IRType* paramType0 = funcType->getParamType(0);
+
+ SLANG_ASSERT(paramType0->op == kIROp_VectorType);
+
+ IRBasicType* elementType = as<IRBasicType>(static_cast<IRVectorType*>(paramType0)->getElementType());
+
+ // Make sure we have all these functions defined before emtting
+ IRType* dotArgs[] = { paramType0, paramType0 };
+ UnownedStringSlice dotFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Dot, dotArgs, SLANG_COUNT_OF(dotArgs), elementType);
+
+ IRType* subArgs[] = { paramType0, paramType0};
+ UnownedStringSlice subFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Sub, subArgs, SLANG_COUNT_OF(subArgs), paramType0);
+
+ IRType* vecMulScalarArgs[] = { paramType0, elementType };
+ UnownedStringSlice vecMulScalarFuncName = _getAndEmitSpecializedOperationDefinition(IntrinsicOp::Mul, vecMulScalarArgs, SLANG_COUNT_OF(vecMulScalarArgs), paramType0);
+
+ // Assumes C++
+
+ _emitSignature(funcName, specOp);
+ writer->emit("\n{\n");
+ writer->indent();
+
+ writer->emit("return a - b * 2.0 * ");
+ writer->emit(dotFuncName);
+ writer->emit("(a, b);\n");
+
+ writer->dedent();
+ writer->emit("}\n\n");
+}
+
+void CPPSourceEmitter::emitSpecializedOperationDefinition(const SpecializedIntrinsic& specOp)
+{
+ // Check if it's been emitted already, if not add it.
+ if (!m_intrinsicEmittedMap.AddIfNotExists(specOp, true))
+ {
+ return;
+ }
+
+ switch (specOp.op)
+ {
+ case IntrinsicOp::VecMatMul:
+ case IntrinsicOp::Dot:
+ {
+ return _emitVecMatMulDefinition(_getFuncName(specOp), specOp);
+ }
+ case IntrinsicOp::Any:
+ case IntrinsicOp::All:
+ {
+ return _emitAnyAllDefinition(_getFuncName(specOp), specOp);
+ }
+ case IntrinsicOp::Cross:
+ {
+ return _emitCrossDefinition(_getFuncName(specOp), specOp);
+ }
+ case IntrinsicOp::Normalize:
+ {
+ return _emitNormalizeDefinition(_getFuncName(specOp), specOp);
+ }
+ case IntrinsicOp::Length:
+ {
+ return _emitLengthDefinition(_getFuncName(specOp), specOp);
+ }
+ case IntrinsicOp::Reflect:
+ {
+ return _emitReflectDefinition(_getFuncName(specOp), specOp);
+ }
+ default:
+ {
+ const auto& info = getOperationInfo(specOp.op);
+ if (info.numOperands >= 1 && info.numOperands <= 3)
+ {
+ return _emitAryDefinition(specOp);
+ }
+ break;
+ }
+ }
+
+ SLANG_ASSERT(!"Unhandled");
+}
+
+IRType* CPPSourceEmitter::_getVecType(IRType* elementType, int elementCount)
+{
+ elementType = _cloneType(elementType);
+ return m_irBuilder.getVectorType(elementType, m_irBuilder.getIntValue(m_irBuilder.getIntType(), elementCount));
+}
+
+CPPSourceEmitter::SpecializedIntrinsic CPPSourceEmitter::getSpecializedOperation(IntrinsicOp op, IRType*const* inArgTypes, int argTypesCount, IRType* retType)
+{
+ SpecializedIntrinsic specOp;
+ specOp.op = op;
+
+ List<IRType*> argTypes;
+ argTypes.setCount(argTypesCount);
+
+ for (int i = 0; i < argTypesCount; ++i)
+ {
+ argTypes[i] = _cloneType(inArgTypes[i]->getCanonicalType());
+ }
+
+ specOp.returnType = _cloneType(retType);
+ specOp.signatureType = m_irBuilder.getFuncType(argTypes, m_irBuilder.getBasicType(BaseType::Void));
+
+ return specOp;
+}
+
+void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst, const IRUse* operands, int numOperands, CLikeSourceEmitter::IREmitMode mode, const EmitOpInfo& inOuterPrec)
+{
+ SLANG_UNUSED(inOuterPrec);
+ SourceWriter* writer = getSourceWriter();
+
+ // Getting the name means that this op is registered as used
+
+ switch (specOp.op)
+ {
+ case IntrinsicOp::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;
+
+ switch (retType->op)
+ {
+ case kIROp_VectorType:
+ {
+ // Get the type name
+ emitType(retType);
+ writer->emitChar('{');
+
+ for (int i = 0; i < numOperands; ++i)
+ {
+ if (i > 0)
+ {
+ writer->emit(", ");
+ }
+ emitOperand(operands[i].get(), mode, getInfo(EmitOp::General));
+ }
+
+ writer->emitChar('}');
+ break;
+ }
+ case kIROp_MatrixType:
+ {
+ IRMatrixType* matType = static_cast<IRMatrixType*>(retType);
+
+ //int colsCount = int(GetIntVal(matType->getColumnCount()));
+ int rowsCount = int(GetIntVal(matType->getRowCount()));
+
+ SLANG_ASSERT(rowsCount == numOperands);
+
+ emitType(retType);
+ writer->emitChar('{');
+
+ for (int j = 0; j < rowsCount; ++j)
+ {
+ if (j > 0)
+ {
+ writer->emit(", ");
+ }
+ emitOperand(operands[j].get(), mode, getInfo(EmitOp::General));
+ }
+
+ writer->emitChar('}');
+ break;
+ }
+ default:
+ {
+ if (IRBasicType::isaImpl(retType->op))
+ {
+ SLANG_ASSERT(numOperands == 1);
+
+ writer->emit(getBuiltinTypeName(retType->op));
+ writer->emitChar('(');
+
+ emitOperand(operands[0].get(), mode, getInfo(EmitOp::General));
+
+ writer->emitChar(')');
+ break;
+ }
+
+ SLANG_ASSERT(!"Not handled");
+ }
+ }
+ break;
+ }
+ case IntrinsicOp::Swizzle:
+ {
+ // For C++ we don't need to emit a swizzle function
+ // For C we need a construction function
+ auto swizzleInst = static_cast<IRSwizzle*>(inst);
+ const Index elementCount = Index(swizzleInst->getElementCount());
+
+ if (elementCount == 1)
+ {
+ defaultEmitInstExpr(inst, mode, inOuterPrec);
+ }
+ else
+ {
+ // TODO(JS): Not sure this is correct on the parens handling front
+ IRType* retType = specOp.returnType;
+ emitType(retType);
+ writer->emit("{");
+
+ for (Index i = 0; i < elementCount; ++i)
+ {
+ if (i > 0)
+ {
+ writer->emit(", ");
+ }
+
+ auto outerPrec = getInfo(EmitOp::General);
+
+ auto prec = getInfo(EmitOp::Postfix);
+ emitOperand(swizzleInst->getBase(), mode, leftSide(outerPrec, prec));
+
+ writer->emit(".");
+
+ IRInst* irElementIndex = swizzleInst->getElementIndex(i);
+ SLANG_RELEASE_ASSERT(irElementIndex->op == kIROp_IntLit);
+ IRConstant* irConst = (IRConstant*)irElementIndex;
+ UInt elementIndex = (UInt)irConst->value.intVal;
+ SLANG_RELEASE_ASSERT(elementIndex < 4);
+
+ writer->emitChar(s_elemNames[elementIndex]);
+ }
+
+ writer->emit("}");
+ }
+ break;
+ }
+ default:
+ {
+ const auto& info = getOperationInfo(specOp.op);
+ // Make sure that the return type is available
+ bool isOperator = _isOperator(info.funcName);
+
+ UnownedStringSlice funcName = _getFuncName(specOp);
+
+ useType(specOp.returnType);
+ // add that we want a function
+ SLANG_ASSERT(numOperands == info.numOperands);
+
+ if (isOperator)
+ {
+ // Just do the default output
+ defaultEmitInstExpr(inst, mode, inOuterPrec);
+ }
+ else
+ {
+ writer->emit(funcName);
+ writer->emitChar('(');
+
+ for (int i = 0; i < numOperands; ++i)
+ {
+ if (i > 0)
+ {
+ writer->emit(", ");
+ }
+ emitOperand(operands[i].get(), mode, getInfo(EmitOp::General));
+ }
+
+ writer->emitChar(')');
+ }
+ break;
+ }
+ }
+}
+
+StringSlicePool::Handle CPPSourceEmitter::_calcScalarFuncName(IntrinsicOp op, IRBasicType* type)
+{
+ StringBuilder builder;
+ builder << _getTypePrefix(type->op) << "_" << getOperationInfo(op).funcName;
+ return m_slicePool.add(builder);
+}
+
+UnownedStringSlice CPPSourceEmitter::_getScalarFuncName(IntrinsicOp op, IRBasicType* type)
+{
+ return m_slicePool.getSlice(_calcScalarFuncName(op, type));
+}
+
+UnownedStringSlice CPPSourceEmitter::_getFuncName(const SpecializedIntrinsic& specOp)
+{
+ StringSlicePool::Handle handle = StringSlicePool::kNullHandle;
+ if (m_intrinsicNameMap.TryGetValue(specOp, handle))
+ {
+ return m_slicePool.getSlice(handle);
+ }
+
+ handle = _calcFuncName(specOp);
+ m_intrinsicNameMap.Add(specOp, handle);
+
+ SLANG_ASSERT(handle != StringSlicePool::kNullHandle);
+ return m_slicePool.getSlice(handle);
+}
+
+StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrinsic& specOp)
+{
+ if (specOp.isScalar())
+ {
+ IRType* paramType = specOp.signatureType->getParamType(0);
+ IRBasicType* basicType = as<IRBasicType>(paramType);
+ SLANG_ASSERT(basicType);
+ return _calcScalarFuncName(specOp.op, basicType);
+ }
+ else
+ {
+ const auto& info = getOperationInfo(specOp.op);
+ if (info.funcName.size())
+ {
+ if (!_isOperator(info.funcName))
+ {
+ return m_slicePool.add(info.funcName);
+ }
+ }
+ return m_slicePool.add(info.name);
+ }
+}
+
+void CPPSourceEmitter::emitOperationCall(IntrinsicOp op, IRInst* inst, IRUse* operands, int operandCount, IRType* retType, CLikeSourceEmitter::IREmitMode mode, const EmitOpInfo& inOuterPrec)
+{
+ 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, mode, 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, mode, inOuterPrec);
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!! CPPSourceEmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+CPPSourceEmitter::CPPSourceEmitter(const Desc& desc):
+ Super(desc)
+{
+ m_sharedIRBuilder.module = nullptr;
+ m_sharedIRBuilder.session = desc.compileRequest->getSession();
+
+ m_irBuilder.sharedBuilder = &m_sharedIRBuilder;
+
+ m_uniqueModule = m_irBuilder.createModule();
+ m_sharedIRBuilder.module = m_uniqueModule;
+
+ m_irBuilder.setInsertInto(m_irBuilder.getModule()->getModuleInst());
+
+ // 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::emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type)
+{
+ SLANG_UNUSED(varDecl);
+ SLANG_UNUSED(type);
+ SLANG_ASSERT(!"Not implemented");
+}
+
+void CPPSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLayout* entryPointLayout)
+{
+ SLANG_UNUSED(irFunc);
+
+ auto profile = m_effectiveProfile;
+ auto stage = entryPointLayout->profile.GetStage();
+
+ switch (stage)
+ {
+ case Stage::Compute:
+ {
+ static const UInt kAxisCount = 3;
+ UInt sizeAlongAxis[kAxisCount];
+
+ // TODO: this is kind of gross because we are using a public
+ // reflection API function, rather than some kind of internal
+ // utility it forwards to...
+ spReflectionEntryPoint_getComputeThreadGroupSize(
+ (SlangReflectionEntryPoint*)entryPointLayout,
+ kAxisCount,
+ &sizeAlongAxis[0]);
+
+ // TODO(JS): We might want to store this information such that it can be used to execute
+ m_writer->emit("// [numthreads(");
+ for (int ii = 0; ii < 3; ++ii)
+ {
+ if (ii != 0) m_writer->emit(", ");
+ m_writer->emit(sizeAlongAxis[ii]);
+ }
+ m_writer->emit(")]\n");
+ break;
+ }
+ default: break;
+ }
+
+ m_writer->emit("SLANG_EXPORT\n");
+}
+
+void CPPSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount)
+{
+ emitSimpleType(_getVecType(elementType, int(elementCount)));
+}
+
+void CPPSourceEmitter::emitSimpleTypeImpl(IRType* inType)
+{
+
+ UnownedStringSlice slice = _getTypeName(_cloneType(inType));
+ m_writer->emit(slice);
+}
+
+void CPPSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc)
+{
+ UnownedStringSlice slice = _getTypeName(type);
+ m_writer->emit(slice);
+
+ if (nameLoc)
+ {
+ m_writer->emit(" ");
+ m_writer->emitName(*nameLoc);
+ }
+}
+
+void CPPSourceEmitter::emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, IREmitMode mode, EmitOpInfo const& inOuterPrec)
+{
+ auto outerPrec = inOuterPrec;
+ bool needClose = false;
+
+ // For a call with N arguments, the instruction will
+ // have N+1 operands. We will start consuming operands
+ // starting at the index 1.
+ UInt operandCount = inst->getOperandCount();
+ UInt argCount = operandCount - 1;
+ UInt operandIndex = 1;
+
+ // Our current strategy for dealing with intrinsic
+ // calls is to "un-mangle" the mangled name, in
+ // order to figure out what the user was originally
+ // calling. This is a bit messy, and there might
+ // be better strategies (including just stuffing
+ // a pointer to the original decl onto the callee).
+
+ // If the intrinsic the user is calling is a generic,
+ // then the mangled name will have been set on the
+ // outer-most generic, and not on the leaf value
+ // (which is `func` above), so we need to walk
+ // upwards to find it.
+ //
+ IRInst* valueForName = func;
+ for (;;)
+ {
+ auto parentBlock = as<IRBlock>(valueForName->parent);
+ if (!parentBlock)
+ break;
+
+ auto parentGeneric = as<IRGeneric>(parentBlock->parent);
+ if (!parentGeneric)
+ break;
+
+ valueForName = parentGeneric;
+ }
+
+ // If we reach this point, we are assuming that the value
+ // has some kind of linkage, and thus a mangled name.
+ //
+ auto linkageDecoration = valueForName->findDecoration<IRLinkageDecoration>();
+ SLANG_ASSERT(linkageDecoration);
+
+ // We will use the `MangledLexer` to
+ // help us split the original name into its pieces.
+ MangledLexer lexer(linkageDecoration->getMangledName());
+
+ // We'll read through the qualified name of the
+ // symbol (e.g., `Texture2D<T>.Sample`) and then
+ // only keep the last segment of the name (e.g.,
+ // the `Sample` part).
+ auto name = lexer.readSimpleName();
+
+ // We will special-case some names here, that
+ // represent callable declarations that aren't
+ // ordinary functions, and thus may use different
+ // syntax.
+ if (name == "operator[]")
+ {
+ // The user is invoking a built-in subscript operator
+
+ auto prec = getInfo(EmitOp::Postfix);
+ needClose = maybeEmitParens(outerPrec, prec);
+
+ emitOperand(inst->getOperand(operandIndex++), mode, leftSide(outerPrec, prec));
+ m_writer->emit("[");
+ emitOperand(inst->getOperand(operandIndex++), mode, getInfo(EmitOp::General));
+ m_writer->emit("]");
+
+ if (operandIndex < operandCount)
+ {
+ m_writer->emit(" = ");
+ emitOperand(inst->getOperand(operandIndex++), mode, getInfo(EmitOp::General));
+ }
+
+ maybeCloseParens(needClose);
+ return;
+ }
+
+ auto prec = getInfo(EmitOp::Postfix);
+ needClose = maybeEmitParens(outerPrec, prec);
+
+ // The mangled function name currently records
+ // the number of explicit parameters, and thus
+ // doesn't include the implicit `this` parameter.
+ // We can compare the argument and parameter counts
+ // to figure out whether we have a member function call.
+ UInt paramCount = lexer.readParamCount();
+
+ if (argCount != paramCount)
+ {
+ // Looks like a member function call
+ emitOperand(inst->getOperand(operandIndex), mode, leftSide(outerPrec, prec));
+ m_writer->emit(".");
+ operandIndex++;
+ }
+ else
+ {
+ IntrinsicOp op = getOperationByName(name);
+ if (op != IntrinsicOp::Invalid)
+ {
+ IRUse* operands = inst->getOperands() + operandIndex;
+ emitOperationCall(op, inst, operands, int(operandCount - operandIndex), inst->getDataType(), mode, inOuterPrec);
+ return;
+ }
+ }
+
+ m_writer->emit(name);
+ m_writer->emit("(");
+ bool first = true;
+ for (; operandIndex < operandCount; ++operandIndex)
+ {
+ if (!first) m_writer->emit(", ");
+ emitOperand(inst->getOperand(operandIndex), mode, getInfo(EmitOp::General));
+ first = false;
+ }
+ m_writer->emit(")");
+ maybeCloseParens(needClose);
+}
bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, IREmitMode mode, const EmitOpInfo& inOuterPrec)
{
SLANG_UNUSED(inOuterPrec);
- //EmitOpInfo outerPrec = inOuterPrec;
- //bool needClose = false;
switch (inst->op)
{
case kIROp_Construct:
case kIROp_makeVector:
case kIROp_MakeMatrix:
- // Simple constructor call
- if (inst->getOperandCount() == 1)
+ {
+ emitOperationCall(IntrinsicOp::Init, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), mode, inOuterPrec);
+ return true;
+ }
+ case kIROp_Mul_Matrix_Matrix:
+ case kIROp_Mul_Matrix_Vector:
+ case kIROp_Mul_Vector_Matrix:
+ {
+ emitOperationCall(IntrinsicOp::VecMatMul, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), mode, inOuterPrec);
+ return true;
+ }
+ case kIROp_Dot:
+ {
+ emitOperationCall(IntrinsicOp::Dot, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), mode, inOuterPrec);
+ return true;
+ }
+ case kIROp_swizzle:
+ {
+ emitOperationCall(IntrinsicOp::Swizzle, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), mode, inOuterPrec);
+ return true;
+ }
+ case kIROp_Call:
+ {
+ auto funcValue = inst->getOperand(0);
+
+ // 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 irFunc = asTargetIntrinsic(funcValue))
{
- _emitCFunc(BuiltInCOp::Splat, inst->getDataType());
- emitArgs(inst, mode);
+ emitIntrinsicCallExpr(static_cast<IRCall*>(inst), irFunc, mode, inOuterPrec);
+ return true;
}
- else
+
+ return false;
+ }
+ default:
+ {
+ IntrinsicOp op = getOperation(inst->op);
+ if (op != IntrinsicOp::Invalid)
{
- _emitCFunc(BuiltInCOp::Init, inst->getDataType());
- emitArgs(inst, mode);
+ emitOperationCall(op, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), mode, inOuterPrec);
+ return true;
}
- return true;
- default:
return false;
+ }
+ }
+}
+
+void CPPSourceEmitter::emitPreprocessorDirectivesImpl()
+{
+ SourceWriter* writer = getSourceWriter();
+
+ writer->emit("\n");
+ writer->emit("#include \"slang-cpp-prelude.h\"\n\n");
+
+ // Emit the type definitions
+ for (const auto& keyValue : m_typeNameMap)
+ {
+ emitTypeDefinition(keyValue.Key);
+ }
+
+ // Emit all the intrinsics that were used
+
+ for (const auto& keyValue : m_intrinsicNameMap)
+ {
+ emitSpecializedOperationDefinition(keyValue.Key);
+ }
+
+ // Lets take a look at layout
+
+ ProgramLayout* programLayout = m_programLayout;
+
+ if (programLayout)
+ {
+
+
}
}