diff options
| -rw-r--r-- | source/core/slang-gcc-compiler-util.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 92 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 1587 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 238 | ||||
| -rw-r--r-- | source/slang/slang-emit-source-writer.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-emit-source-writer.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 18 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 27 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-mangled-lexer.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-syntax.h | 38 | ||||
| -rw-r--r-- | tests/cross-compile/c-cross-compile.slang | 107 | ||||
| -rw-r--r-- | tests/cross-compile/slang-cpp-prelude.h | 122 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 84 |
16 files changed, 2191 insertions, 179 deletions
diff --git a/source/core/slang-gcc-compiler-util.cpp b/source/core/slang-gcc-compiler-util.cpp index 9f22526a1..d210fa829 100644 --- a/source/core/slang-gcc-compiler-util.cpp +++ b/source/core/slang-gcc-compiler-util.cpp @@ -338,6 +338,12 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse /* static */void GCCCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine) { cmdLine.addArg("-fvisibility=hidden"); + + if (options.sourceType == SourceType::CPP) + { + cmdLine.addArg("-std=c++11"); + } + // Use shared libraries //cmdLine.addArg("-shared"); @@ -454,6 +460,8 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse { // Make STD libs available cmdLine.addArg("-lstdc++"); + // Make maths lib available + cmdLine.addArg("-lm"); } } diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 02a89c220..4f718f884 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -34,7 +34,7 @@ struct CLikeSourceEmitter::EDeclarator { enum class Flavor { - name, + Name, Array, UnsizedArray, }; @@ -42,9 +42,8 @@ struct CLikeSourceEmitter::EDeclarator EDeclarator* next = nullptr; // Used for `Flavor::name` - Name* name; - SourceLoc loc; - + const StringSliceLoc* nameAndLoc; + // Used for `Flavor::Array` IRInst* elementCount; }; @@ -149,8 +148,8 @@ void CLikeSourceEmitter::emitDeclarator(EDeclarator* declarator) switch (declarator->flavor) { - case EDeclarator::Flavor::name: - m_writer->emitName(declarator->name, declarator->loc); + case EDeclarator::Flavor::Name: + m_writer->emitName(*declarator->nameAndLoc); break; case EDeclarator::Flavor::Array: @@ -250,40 +249,57 @@ void CLikeSourceEmitter::_emitType(IRType* type, EDeclarator* declarator) } -void CLikeSourceEmitter::emitType( - IRType* type, - SourceLoc const& typeLoc, - Name* name, - SourceLoc const& nameLoc) +void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc) { - m_writer->advanceToSourceLocation(typeLoc); + if (nameAndLoc) + { + // TODO(JS): No call to the previous version of this method was passing in a typeLoc, so disabled for + // now for simplicity + //m_writer->advanceToSourceLocation(typeLoc); - EDeclarator nameDeclarator; - nameDeclarator.flavor = EDeclarator::Flavor::name; - nameDeclarator.name = name; - nameDeclarator.loc = nameLoc; - _emitType(type, &nameDeclarator); + EDeclarator nameDeclarator; + nameDeclarator.flavor = EDeclarator::Flavor::Name; + nameDeclarator.nameAndLoc = nameAndLoc; + _emitType(type, &nameDeclarator); + } + else + { + _emitType(type, nullptr); + } } void CLikeSourceEmitter::emitType(IRType* type, Name* name) { - emitType(type, SourceLoc(), name, SourceLoc()); + SLANG_ASSERT(name); + StringSliceLoc nameAndLoc(name->text.getUnownedSlice()); + emitType(type, &nameAndLoc); } void CLikeSourceEmitter::emitType(IRType* type, const String& name) { - // HACK: the rest of the code wants a `Name`, - // so we'll create one for a bit... - Name tempName; - tempName.text = name; + StringSliceLoc nameAndLoc(name.getUnownedSlice()); + emitType(type, &nameAndLoc); +} - emitType(type, SourceLoc(), &tempName, SourceLoc()); +void CLikeSourceEmitter::emitType(IRType* type) +{ + emitType(type, (StringSliceLoc*)nullptr); } +void CLikeSourceEmitter::emitType(IRType* type, Name* name, SourceLoc const& nameLoc) +{ + SLANG_ASSERT(name); -void CLikeSourceEmitter::emitType(IRType* type) + StringSliceLoc nameAndLoc; + nameAndLoc.loc = nameLoc; + nameAndLoc.name = name->text.getUnownedSlice(); + + emitType(type, &nameAndLoc); +} + +void CLikeSourceEmitter::emitType(IRType* type, NameLoc const& nameAndLoc) { - _emitType(type, nullptr); + emitType(type, nameAndLoc.name, nameAndLoc.loc); } // @@ -324,20 +340,6 @@ bool CLikeSourceEmitter::isTargetIntrinsicModifierApplicable(const String& targe } } -void CLikeSourceEmitter::emitType(IRType* type, Name* name, SourceLoc const& nameLoc) -{ - emitType( - type, - SourceLoc(), - name, - nameLoc); -} - -void CLikeSourceEmitter::emitType(IRType* type, NameLoc const& nameAndLoc) -{ - emitType(type, nameAndLoc.name, nameAndLoc.loc); -} - bool CLikeSourceEmitter::isTargetIntrinsicModifierApplicable(IRTargetIntrinsicDecoration* decoration) { auto targetName = String(decoration->getTargetName()); @@ -1533,12 +1535,10 @@ void CLikeSourceEmitter::emitIntrinsicCallExpr( // auto linkageDecoration = valueForName->findDecoration<IRLinkageDecoration>(); SLANG_ASSERT(linkageDecoration); - auto mangledName = String(linkageDecoration->getMangledName()); - - + // We will use the `MangledLexer` to // help us split the original name into its pieces. - MangledLexer lexer(mangledName); + MangledLexer lexer(linkageDecoration->getMangledName()); // We'll read through the qualified name of the // symbol (e.g., `Texture2D<T>.Sample`) and then @@ -1655,7 +1655,7 @@ void CLikeSourceEmitter::emitCallExpr(IRCall* inst, IREmitMode mode, EmitOpInfo maybeCloseParens(needClose); } } - + void CLikeSourceEmitter::emitInstExpr(IRInst* inst, IREmitMode mode, const EmitOpInfo& inOuterPrec) { // Try target specific impl first @@ -1663,7 +1663,11 @@ void CLikeSourceEmitter::emitInstExpr(IRInst* inst, IREmitMode mode, const EmitO { return; } + defaultEmitInstExpr(inst, mode, inOuterPrec); +} +void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, IREmitMode mode, const EmitOpInfo& inOuterPrec) +{ EmitOpInfo outerPrec = inOuterPrec; bool needClose = false; switch(inst->op) diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 621b553cb..e33b704a9 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -133,15 +133,7 @@ public: void emitDeclarator(EDeclarator* declarator); - //void emitVectorTypeName(IRType* elementType, IRIntegerValue elementCount); - //void emitTextureType(IRTextureType* texType); - //void emitTextureSamplerType(IRTextureSamplerType* type); - //void emitImageType(IRGLSLImageType* type); - //void emitSamplerStateType(IRSamplerStateTypeBase* samplerStateType); - //void emitStructuredBufferType(IRHLSLStructuredBufferTypeBase* type); - //void emitUntypedBufferType(IRUntypedBufferResourceType* type); - - void emitType(IRType* type, const SourceLoc& typeLoc, Name* name, const SourceLoc& nameLoc); + void emitType(IRType* type, const StringSliceLoc* nameLoc) { emitTypeImpl(type, nameLoc); } void emitType(IRType* type, Name* name); void emitType(IRType* type, String const& name); void emitType(IRType* type); @@ -226,7 +218,8 @@ public: void emitCallExpr(IRCall* inst, IREmitMode mode, EmitOpInfo outerPrec); void emitInstExpr(IRInst* inst, IREmitMode mode, EmitOpInfo const& inOuterPrec); - + void defaultEmitInstExpr(IRInst* inst, IREmitMode mode, EmitOpInfo const& inOuterPrec); + BaseType extractBaseType(IRType* inType); void emitInst(IRInst* inst, IREmitMode mode); @@ -353,6 +346,7 @@ public: virtual void emitSimpleTypeImpl(IRType* type) = 0; virtual void emitVarDecorationsImpl(IRInst* varDecl) { SLANG_UNUSED(varDecl); } virtual void emitMatrixLayoutModifiersImpl(VarLayout* layout) { SLANG_UNUSED(layout); } + virtual void emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc); // Only needed for glsl output with $ prefix intrinsics - so perhaps removable in the future virtual void emitTextureOrTextureSamplerTypeImpl(IRTextureTypeBase* type, char const* baseName) { SLANG_UNUSED(type); SLANG_UNUSED(baseName); } 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) + { + + } } diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index d5d95507c..cd33dc431 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -3,37 +3,253 @@ #define SLANG_EMIT_CPP_H #include "slang-emit-c-like.h" +#include "slang-ir-clone.h" + +#include "../core/slang-string-slice-pool.h" namespace Slang { +#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(Mod, "%", 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) + + class CPPSourceEmitter: public CLikeSourceEmitter { public: typedef CLikeSourceEmitter Super; - enum class BuiltInCOp +#define SLANG_CPP_INTRINSIC_OP_ENUM(x, op, numOperands) x, + enum class IntrinsicOp { - Splat, //< Splat a single value to all values of a vector or matrix type - Init, //< Initialize with parameters (must match the type) + SLANG_CPP_INTRINSIC_OP(SLANG_CPP_INTRINSIC_OP_ENUM) }; - CPPSourceEmitter(const Desc& desc) : - Super(desc) - {} + struct OperationInfo + { + UnownedStringSlice name; + UnownedStringSlice funcName; + int8_t numOperands; ///< -1 if can't be handled automatically via amount of params + }; -protected: + struct SpecializedIntrinsic + { + typedef SpecializedIntrinsic ThisType; - void _emitCVecType(IROp op, Int size); - void _emitCMatType(IROp op, IRIntegerValue rowCount, IRIntegerValue colCount); - void _emitCFunc(BuiltInCOp cop, IRType* type); + 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; } + + int rowCount; + 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, CLikeSourceEmitter::IREmitMode mode, const EmitOpInfo& inOuterPrec); + virtual void emitTypeDefinition(IRType* type); + virtual void emitSpecializedOperationDefinition(const SpecializedIntrinsic& specOp); + + void emitOperationCall(IntrinsicOp op, IRInst* inst, IRUse* operands, int operandCount, IRType* retType, CLikeSourceEmitter::IREmitMode mode, 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); + +protected: + // Implement CLikeSourceEmitter interface virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) SLANG_OVERRIDE; virtual void emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLayout* entryPointLayout) SLANG_OVERRIDE; virtual void emitSimpleTypeImpl(IRType* type) SLANG_OVERRIDE; + virtual void emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc) SLANG_OVERRIDE; virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) SLANG_OVERRIDE; - virtual bool tryEmitInstExprImpl(IRInst* inst, IREmitMode mode, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; + virtual void emitPreprocessorDirectivesImpl() SLANG_OVERRIDE; + + void emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, IREmitMode mode, EmitOpInfo const& inOuterPrec); + + void _emitVecMatMulDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + + void _emitAryDefinition(const SpecializedIntrinsic& 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 _emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + + UnownedStringSlice _getAndEmitSpecializedOperationDefinition(IntrinsicOp 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); + + IRType* _getVecType(IRType* elementType, int elementCount); + + IRInst* _clone(IRInst* inst); + IRType* _cloneType(IRType* type) { return (IRType*)_clone((IRInst*)type); } + + StringSlicePool::Handle _calcScalarFuncName(IntrinsicOp op, IRBasicType* type); + UnownedStringSlice _getScalarFuncName(IntrinsicOp operation, IRBasicType* scalarType); + + UnownedStringSlice _getFuncName(const SpecializedIntrinsic& specOp); + StringSlicePool::Handle _calcFuncName(const SpecializedIntrinsic& specOp); + + UnownedStringSlice _getTypeName(IRType* type); + StringSlicePool::Handle _calcTypeName(IRType* type); + + Dictionary<SpecializedIntrinsic, StringSlicePool::Handle> m_intrinsicNameMap; + Dictionary<IRType*, StringSlicePool::Handle> m_typeNameMap; + + /* This is used so as to try and use slangs type system to uniquely identify types and specializations on intrinsice. + 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. + */ + RefPtr<IRModule> m_uniqueModule; + SharedIRBuilder m_sharedIRBuilder; + IRBuilder m_irBuilder; + + Dictionary<IRInst*, IRInst*> m_cloneMap; + + 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; + + StringSlicePool m_slicePool; }; } diff --git a/source/slang/slang-emit-source-writer.cpp b/source/slang/slang-emit-source-writer.cpp index 5f34afc81..5661a48bb 100644 --- a/source/slang/slang-emit-source-writer.cpp +++ b/source/slang/slang-emit-source-writer.cpp @@ -156,6 +156,12 @@ void SourceWriter::emit(const NameLoc& nameAndLoc) emit(getText(nameAndLoc.name)); } +void SourceWriter::emit(const StringSliceLoc& nameAndLoc) +{ + advanceToSourceLocation(nameAndLoc.loc); + emit(nameAndLoc.name); +} + void SourceWriter::emitName(Name* name, const SourceLoc& locIn) { advanceToSourceLocation(locIn); @@ -167,6 +173,11 @@ void SourceWriter::emitName(const NameLoc& nameAndLoc) emitName(nameAndLoc.name, nameAndLoc.loc); } +void SourceWriter::emitName(const StringSliceLoc& nameAndLoc) +{ + emit(nameAndLoc); +} + void SourceWriter::emitName(Name* name) { emitName(name, SourceLoc()); diff --git a/source/slang/slang-emit-source-writer.h b/source/slang/slang-emit-source-writer.h index e7bed815f..cc9fa1611 100644 --- a/source/slang/slang-emit-source-writer.h +++ b/source/slang/slang-emit-source-writer.h @@ -28,7 +28,8 @@ public: void emit(const String& text); void emit(const UnownedStringSlice& text); void emit(Name* name); - void emit(const NameLoc& nameAndLoc); + void emit(const NameLoc& nameAndLoc); + void emit(const StringSliceLoc& nameAndLoc); void emit(IntegerLiteralValue value); void emit(UInt value); void emit(int value); @@ -37,10 +38,12 @@ public: void emitChar(char c); /// Emit names (doing so can also advance to a new source location) + void emitName(const StringSliceLoc& nameAndLoc); void emitName(const NameLoc& nameAndLoc); void emitName(Name* name, const SourceLoc& loc); void emitName(Name* name); + /// Indent the text void indent(); /// Dedent (the opposite of indenting) the text diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index aa56bc0b2..cc532b9f7 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -237,6 +237,9 @@ String emitEntryPoint( sink->diagnose(SourceLoc(), Diagnostics::unableToGenerateCodeForTarget, getCodeGenTargetName(target)); return String(); } + + // Outside because we want to keep IR in scope whilst we are processing emits + LinkedIR linkedIR; { auto session = targetRequest->getSession(); @@ -248,7 +251,7 @@ String emitEntryPoint( // modules, and also select between the definitions of // any "profile-overloaded" symbols. // - auto linkedIR = linkIR( + linkedIR = linkIR( compileRequest, entryPoint, programLayout, diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 7229f04d1..4983bb97c 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -934,7 +934,23 @@ struct IRBuilder IRUndefined* emitUndefined(IRType* type); - + IRInst* findOrEmitHoistableInst( + IRType* type, + IROp op, + UInt operandListCount, + UInt const* listOperandCounts, + IRInst* const* const* listOperands); + IRInst* findOrEmitHoistableInst( + IRType* type, + IROp op, + UInt operandCount, + IRInst* const* operands); + IRInst* findOrEmitHoistableInst( + IRType* type, + IROp op, + IRInst* operand, + UInt operandCount, + IRInst* const* operands); IRModule* createModule(); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 4975ac824..c5dd5d530 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -1530,8 +1530,7 @@ namespace Slang } - IRInst* findOrEmitHoistableInst( - IRBuilder* builder, + IRInst* IRBuilder::findOrEmitHoistableInst( IRType* type, IROp op, UInt operandListCount, @@ -1544,7 +1543,7 @@ namespace Slang operandCount += listOperandCounts[ii]; } - auto& memoryArena = builder->getModule()->memoryArena; + auto& memoryArena = getModule()->memoryArena; void* cursor = memoryArena.getCursor(); // We are going to create a 'dummy' instruction on the memoryArena @@ -1581,7 +1580,7 @@ namespace Slang IRInstKey key = { inst }; // Ideally we would add if not found, else return if was found instead of testing & then adding. - IRInst** found = builder->sharedBuilder->globalValueNumberingMap.TryGetValueOrAdd(key, inst); + IRInst** found = sharedBuilder->globalValueNumberingMap.TryGetValueOrAdd(key, inst); SLANG_ASSERT(endCursor == memoryArena.getCursor()); // If it's found, just return, and throw away the instruction if (found) @@ -1600,7 +1599,7 @@ namespace Slang inst->typeUse.init(inst, type); } - maybeSetSourceLoc(builder, inst); + maybeSetSourceLoc(this, inst); IRUse*const operands = inst->getOperands(); for (UInt i = 0; i < operandCount; ++i) @@ -1613,20 +1612,18 @@ namespace Slang } } - addHoistableInst(builder, inst); + addHoistableInst(this, inst); return inst; } - IRInst* findOrEmitHoistableInst( - IRBuilder* builder, + IRInst* IRBuilder::findOrEmitHoistableInst( IRType* type, IROp op, UInt operandCount, IRInst* const* operands) { return findOrEmitHoistableInst( - builder, type, op, 1, @@ -1634,8 +1631,7 @@ namespace Slang &operands); } - IRInst* findOrEmitHoistableInst( - IRBuilder* builder, + IRInst* IRBuilder::findOrEmitHoistableInst( IRType* type, IROp op, IRInst* operand, @@ -1646,7 +1642,6 @@ namespace Slang IRInst* const* lists[] = { &operand, operands }; return findOrEmitHoistableInst( - builder, type, op, 2, @@ -1661,7 +1656,6 @@ namespace Slang IRInst* const* operands) { return (IRType*) findOrEmitHoistableInst( - this, nullptr, op, operandCount, @@ -1806,7 +1800,6 @@ namespace Slang IRType* resultType) { return (IRFuncType*) findOrEmitHoistableInst( - this, nullptr, kIROp_FuncType, resultType, @@ -1849,7 +1842,6 @@ namespace Slang IRType* const* caseTypes) { return (IRType*) findOrEmitHoistableInst( - this, getTypeKind(), kIROp_TaggedUnionType, caseCount, @@ -1882,7 +1874,6 @@ namespace Slang } return (IRType*) findOrEmitHoistableInst( - this, getTypeKind(), kIROp_BindExistentialsType, baseType, @@ -3853,7 +3844,7 @@ namespace Slang return true; } - static bool _isNominalOp(IROp op) + bool isNominalOp(IROp op) { // True if the op identity is 'nominal' switch (op) @@ -3892,7 +3883,7 @@ namespace Slang } // If the type is nominal - it can only be the same if the pointer is the same. - if (_isNominalOp(opA)) + if (isNominalOp(opA)) { // The pointer isn't the same (as that was already tested), so cannot be equal return false; diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 956d97ad3..f13568356 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1197,6 +1197,10 @@ IRInst* createEmptyInstWithSize( IRModule* module, IROp op, size_t totalSizeInBytes); + + /// True if the op type can be handled 'nominally' meaning that pointer identity is applicable. +bool isNominalOp(IROp op); + } #endif diff --git a/source/slang/slang-mangled-lexer.h b/source/slang/slang-mangled-lexer.h index 8ec86c982..6c8060cfd 100644 --- a/source/slang/slang-mangled-lexer.h +++ b/source/slang/slang-mangled-lexer.h @@ -42,7 +42,7 @@ public: UInt readParamCount(); /// Ctor - SLANG_FORCE_INLINE MangledLexer(String const& str); + SLANG_FORCE_INLINE MangledLexer(const UnownedStringSlice& slice); private: @@ -71,10 +71,10 @@ private: }; // -------------------------------------------------------------------------- - -SLANG_FORCE_INLINE MangledLexer::MangledLexer(String const& str) - : m_cursor(str.begin()) - , m_begin(str.begin()) - , m_end(str.end()) +SLANG_FORCE_INLINE MangledLexer::MangledLexer(const UnownedStringSlice& slice) + : m_cursor(slice.begin()) + , m_begin(slice.begin()) + , m_end(slice.end()) { _start(); } diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h index 049220ef9..d156750be 100644 --- a/source/slang/slang-syntax.h +++ b/source/slang/slang-syntax.h @@ -8,6 +8,8 @@ #include "slang-type-system-shared.h" #include "../../slang.h" +#include "slang-name.h" + #include <assert.h> namespace Slang @@ -120,14 +122,14 @@ namespace Slang : name(nullptr) {} - explicit NameLoc(Name* name) - : name(name) + explicit NameLoc(Name* inName) + : name(inName) {} - NameLoc(Name* name, SourceLoc loc) - : name(name) - , loc(loc) + NameLoc(Name* inName, SourceLoc inLoc) + : name(inName) + , loc(inLoc) {} NameLoc(Token const& token) @@ -136,6 +138,32 @@ namespace Slang {} }; + struct StringSliceLoc + { + UnownedStringSlice name; + SourceLoc loc; + + StringSliceLoc() + : name(nullptr) + {} + explicit StringSliceLoc(const UnownedStringSlice& inName) + : name(inName) + {} + StringSliceLoc(const UnownedStringSlice& inName, SourceLoc inLoc) + : name(inName) + , loc(inLoc) + {} + StringSliceLoc(Token const& token) + : loc(token.getLoc()) + { + Name* tokenName = token.getNameOrNull(); + if (tokenName) + { + name = tokenName->text.getUnownedSlice(); + } + } + }; + // Helper class for iterating over a list of heap-allocated modifiers struct ModifierList { diff --git a/tests/cross-compile/c-cross-compile.slang b/tests/cross-compile/c-cross-compile.slang new file mode 100644 index 000000000..29078f0b6 --- /dev/null +++ b/tests/cross-compile/c-cross-compile.slang @@ -0,0 +1,107 @@ +//TEST:CPP_COMPILER_COMPILE: -profile cs_5_0 -entry computeMain -target cpp + +enum Color +{ + Red, + Green = 2, + Blue, +} + +int test(int val) +{ + Color c = Color.Red; + + if(val > 1) + { + c = Color.Green; + } + + if(c == Color.Red) + { + if(val & 1) + { + c = Color.Blue; + } + } + + switch(c) + { + case Color.Red: + val = 1; + break; + + case Color.Green: + val = 2; + break; + + case Color.Blue: + val = 3; + break; + + default: + val = -1; + break; + } + + return (val << 4) + int(c); +} + +float sum(float a[3]) +{ + float total = a[0]; + for (int i = 1; i < 3; ++i) + { + total += a[i]; + } + return total; +} + +struct Thing +{ + int a; + float b; +}; + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out +RWStructuredBuffer<int> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + + Thing thing = { 10, -1.0 }; + + float array[3] = { thing.a, 2, 3}; + + float anotherArray[] = { 1, 2, 5 }; + + array[0] += anotherArray[1]; + + matrix<float, 2, 3> mat = { { sum(array), 1, 2 }, { 3, 4, 5} }; + vector<float, 2> vec = { float(tid + 1), float(tid + 2) }; + + + vector<float, 3> vec2 = max(sin(mul(vec, mat)), float3(1, 2, -1)); + vector<float, 3> vec3 = mul(vec, mat); + + float3 vec4 = lerp(vec2, vec3, float3(tid * (1.0f / 4), 1, 1)); + + float3 crossVec = normalize(cross(vec4, vec4)); + + vec2.x = fmod(crossVec.y, crossVec.x); + + vec2 = fmod(vec2, crossVec); + + vec2 += (-vec2.zyx) * 2 + crossVec * length(crossVec) + reflect(vec4, normalize(crossVec)); + + vector<bool, 3> z = vec2 > 0; + + int val = (int(tid) + (any(z) ? 1 : 0) + (all(z) ? 2 : 0)) % 100; + + val = asint(asfloat(asuint(asfloat(val)))); + + val = test(val); + + outputBuffer[tid] = val + int(dot(vec2, vec4)); +}
\ No newline at end of file diff --git a/tests/cross-compile/slang-cpp-prelude.h b/tests/cross-compile/slang-cpp-prelude.h new file mode 100644 index 000000000..f60fc8518 --- /dev/null +++ b/tests/cross-compile/slang-cpp-prelude.h @@ -0,0 +1,122 @@ + +#include <inttypes.h> +#include <math.h> +#include <inttypes.h> +#include <math.h> +#include <assert.h> +#include <stdlib.h> + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#if defined(_MSC_VER) +# define SLANG_SHARED_LIB_EXPORT __declspec(dllexport) +#else +# define SLANG_SHARED_LIB_EXPORT __attribute__((__visibility__("default"))) +//# define SLANG_SHARED_LIB_EXPORT __attribute__ ((dllexport)) __attribute__((__visibility__("default"))) +#endif + +#ifdef __cplusplus +# define SLANG_EXTERN_C extern "C" +#else +# define SLANG_EXTERN_C +#endif + +#define SLANG_EXPORT SLANG_EXTERN_C SLANG_SHARED_LIB_EXPORT + +template <typename T, size_t SIZE> +struct FixedArray +{ + const T& operator[](size_t index) const { assert(index < SIZE); return m_data[index]; } + T& operator[](size_t index) { assert(index < SIZE); return m_data[index]; } + + T m_data[SIZE]; +}; + +template <typename T> +struct RWStructuredBuffer +{ + T& operator[](size_t index) const { return data[index]; } + + T* data; + size_t count; +}; + +// ----------------------------- F32 ----------------------------------------- + +union Union32 +{ + uint32_t u; + int32_t i; + float f; +}; + +// Helpers +float F32_calcSafeRadians(float radians) +{ + float a = radians * (1.0f / M_PI); + a = (a < 0.0f) ? (::ceilf(a) - a) : (a - ::floorf(a)); + return (a * M_PI); +} + +// Unary +float F32_ceil(float f) { return ::ceilf(f); } +float F32_floor(float f) { return ::floorf(f); } +float F32_sin(float f) { return ::sinf(F32_calcSafeRadians(f)); } +float F32_cos(float f) { return ::cosf(F32_calcSafeRadians(f)); } +float F32_tan(float f) { return ::tanf(f); } +float F32_asin(float f) { return ::asinf(f); } +float F32_acos(float f) { return ::acosf(f); } +float F32_atan(float f) { return ::atanf(f); } +float F32_log2(float f) { return ::log2f(f); } +float F32_exp2(float f) { return ::exp2f(f); } +float F32_exp(float f) { return ::expf(f); } +float F32_abs(float f) { return ::fabsf(f); } +float F32_trunc(float f) { return ::truncf(f); } +float F32_sqrt(float f) { return ::sqrtf(f); } +float F32_rsqrt(float f) { return 1.0f / F32_sqrt(f); } +float F32_rcp(float f) { return 1.0f / f; } +float F32_sign(float f) { return ( f == 0.0f) ? f : (( f < 0.0f) ? -1.0f : 1.0f); } +float F32_saturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +float F32_frac(float f) { return f - F32_floor(f); } +float F32_radians(float f) { return f * 0.01745329222f; } + +// Binary +float F32_min(float a, float b) { return a < b ? a : b; } +float F32_max(float a, float b) { return a > b ? a : b; } +float F32_pow(float a, float b) { return ::powf(a, b); } +float F32_fmod(float a, float b) { return ::fmodf(a, b); } +float F32_step(float a, float b) { return float(a >= b); } +float F32_atan2(float a, float b) { return float(atan2(a, b)); } + +// Ternary +float F32_smoothstep(float min, float max, float x) { return x < min ? min : ((x > max) ? max : x / (max - min)); } +float F32_lerp(float x, float y, float s) { return x + s * (y - x); } +float F32_clamp(float x, float min, float max) { return ( x < min) ? min : ((x > max) ? max : x); } +void F32_sincos(float f, float& outSin, float& outCos) { outSin = F32_sin(f); outCos = F32_cos(f); } + +uint32_t F32_asuint(float f) { Union32 u; u.f = f; return u.u; } +int32_t F32_asint(float f) { Union32 u; u.f = f; return u.i; } + +// ----------------------------- I32 ----------------------------------------- + +int32_t I32_abs(int32_t f) { return (f < 0) ? -f : f; } + +int32_t I32_min(int32_t a, int32_t b) { return a < b ? a : b; } +int32_t I32_max(int32_t a, int32_t b) { return a > b ? a : b; } + +int32_t I32_clamp(int32_t x, int32_t min, int32_t max) { return ( x < min) ? min : ((x > max) ? max : x); } + +float I32_asfloat(int32_t x) { Union32 u; u.i = x; return u.f; } + +// ----------------------------- U32 ----------------------------------------- + +uint32_t U32_abs(uint32_t f) { return f; } + +uint32_t U32_min(uint32_t a, uint32_t b) { return a < b ? a : b; } +uint32_t U32_max(uint32_t a, uint32_t b) { return a > b ? a : b; } + +uint32_t U32_clamp(uint32_t x, uint32_t min, uint32_t max) { return ( x < min) ? min : ((x > max) ? max : x); } + +float U32_asfloat(uint32_t x) { Union32 u; u.u = x; return u.f; } diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 0543dd067..b6c29b423 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -1100,6 +1100,89 @@ static String _calcSummary(const CPPCompiler::Output& inOutput) return builder; } +static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) +{ + CPPCompilerSet* compilerSet = context->getCPPCompilerSet(); + CPPCompiler* compiler = compilerSet ? compilerSet->getDefaultCompiler() : nullptr; + + if (!compiler) + { + return TestResult::Ignored; + } + + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + + auto filePath999 = input.filePath; + auto outputStem = input.outputStem; + + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); + + cmdLine.addArg(filePath999); + + for (auto arg : input.testOptions->args) + { + cmdLine.addArg(arg); + } + + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); + + if (context->isCollectingRequirements()) + { + return TestResult::Pass; + } + + if (exeRes.resultCode != 0) + { + return TestResult::Fail; + } + + String actualOutput = exeRes.standardOutput; + + // Make the module name the same as the source file + auto filePath = input.filePath; + String directory = Path::getParentDirectory(input.outputStem); + String moduleName = Path::getFileNameWithoutExt(filePath); + String ext = Path::getFileExt(filePath); + String modulePath = Path::combine(directory, moduleName); + + // Find the target + UnownedStringSlice targetExt = UnownedStringSlice::fromLiteral("c"); + Index index = cmdLine.findArgIndex(UnownedStringSlice::fromLiteral("-target")); + if (index >= 0 && index + 1 < cmdLine.getArgCount()) + { + targetExt = cmdLine.m_args[index + 1].value.getUnownedSlice(); + } + + CPPCompiler::CompileOptions options; + options.sourceType = (targetExt == "c") ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP; + + // Create a filename to write this out to + String cppSource = modulePath + "." + targetExt; + Slang::File::writeAllText(cppSource, actualOutput); + + // Okay we can now try compiling + + // Compile this source + options.sourceFiles.add(cppSource); + options.modulePath = modulePath; + options.targetType = CPPCompiler::TargetType::SharedLibrary; + + CPPCompiler::Output output; + if (SLANG_FAILED(compiler->compile(options, output))) + { + return TestResult::Fail; + } + + if (output.getCountByType(CPPCompiler::OutputMessage::Type::Error) > 0) + { + return TestResult::Fail; + } + + return TestResult::Pass; +} + static TestResult runCPPCompilerExecute(TestContext* context, TestInput& input) { CPPCompilerSet* compilerSet = context->getCPPCompilerSet(); @@ -2134,6 +2217,7 @@ static const TestCommandInfo s_testCommandInfos[] = { "CROSS_COMPILE", &runCrossCompilerTest}, { "CPP_COMPILER_EXECUTE", &runCPPCompilerExecute}, { "CPP_COMPILER_SHARED_LIBRARY", &runCPPCompilerSharedLibrary}, + { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile} }; TestResult runTest( |
