summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--source/core/slang-gcc-compiler-util.cpp8
-rw-r--r--source/slang/slang-emit-c-like.cpp92
-rw-r--r--source/slang/slang-emit-c-like.h14
-rw-r--r--source/slang/slang-emit-cpp.cpp1587
-rw-r--r--source/slang/slang-emit-cpp.h238
-rw-r--r--source/slang/slang-emit-source-writer.cpp11
-rw-r--r--source/slang/slang-emit-source-writer.h5
-rw-r--r--source/slang/slang-emit.cpp5
-rw-r--r--source/slang/slang-ir-insts.h18
-rw-r--r--source/slang/slang-ir.cpp27
-rw-r--r--source/slang/slang-ir.h4
-rw-r--r--source/slang/slang-mangled-lexer.h10
-rw-r--r--source/slang/slang-syntax.h38
-rw-r--r--tests/cross-compile/c-cross-compile.slang107
-rw-r--r--tests/cross-compile/slang-cpp-prelude.h122
-rw-r--r--tools/slang-test/slang-test-main.cpp84
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(