diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-07-09 13:59:30 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-07-09 13:59:30 -0400 |
| commit | f52f5cd4a7b5b71617b949fc62a78abe8c4822b3 (patch) | |
| tree | 48f636f33e199b3508d779941c034f7be91debc4 /source/slang/slang-emit-cpp.h | |
| parent | 691ebae763e29327249735d67fbb231c75b17840 (diff) | |
WIP: slang to C++ code generation (#997)
* WIP: Emitting Cpp
* Added HLSLType instead of using IRInst - because they don't seem to be deduped.
* Removed need for lexer to take a String.
Added mechansim to lookup intrinsic functions on C++.
* A c/c++ cross compilation test.
* WIP Cpp output using cloning and slang types.
* More work to generate mul funcs.
* WIP: Outputting some simple C++.
* Expose findOrEmitHoistableInst to IRBuilder to aid cloning,
* Simplification for checking for BasicTypes.
Test infrastructure compiles output C++ code.
* Dot and mat/vec multiplication output.
* First pass at swizzling.
* First support for binary ops.
* Builtin binary and unary functions.
* Any and all.
* WIP adding support for other functions.
Added code to generate function signature.
* Add scalar functions to slang-cpp-prelude.h
* Support for most built in operations.
* Tested first ternary.
* Checking the emitting of corner cases functions - normalize, length, any, all, normalize, reflect.
* Check asfloat etc work.
* Fmod support.
* WIP Array handling in C++.
* First stage in being able to handl arbitrary type output for CLikeSourceEmitter
* Removed Handler/Emitter split - so can implement more easily complex type naming.
* Array passing by value first pass.
* Rename Array -> FixedArray
* Outputs structs in C++.
* Emit the thread config.
* Dimension -> TypeDimension
* SpecializedOperation -> SpecializedIntrinsic
Operation -> IntrinsicOp
Use shared impl of isNominalOp
Commented use of m_uniqueModule etc.
* Add code to test slang->cpp when compiled doesn't have errors. Does so by building shared library and exporting the entry point.
* Fix linux clang/gcc compile error about override not being specified.
* Make sure c-cross-compile is run on linux targets/smoke.
* Remove c-cross-compile.slang from smoke.
* Fix running tests/cross-compile/c-cross-compile.slang on Ubuntu 16.04
* Only add -std=c++11 for C++ source.
Diffstat (limited to 'source/slang/slang-emit-cpp.h')
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 238 |
1 files changed, 227 insertions, 11 deletions
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; }; } |
