diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-02-04 18:45:50 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-02-04 15:45:50 -0800 |
| commit | fb053433ef64bbae50a8a10ea4381a5695019fac (patch) | |
| tree | 87298897bf88ec79b40343a868a283d1885357b9 /source | |
| parent | c40f10b704b8bd5a744cc9b3964344585436b1ac (diff) | |
Fix line offset problem (#1690)
* #include an absolute path didn't work - because paths were taken to always be relative.
* WIP diagnostics for line number output.
* Small param naming change
* Use x macro for pass through compile human name lookup/getting.
* WIP on parsing downstream compiler output.
* Split out parsing into ParseDiagnosticUtil.
Added test result of single line.
* Dump out the std output on fail to parse diagnostics.
* Change test type for syntax-error-intrinsic.slang be TEST not TEST_DIAGNOSTIC
* Use Index for StringUtil.
* WIP: First pass support for parsing Slang diagnostics.
* WIP Testing comparing with ParseDiagnosticUtil with previous ad-hoc mechanism.
* Use the new parsing mechanism for diagnostic comparisons.
* Fix layout on GLSL, doesn't have CR so runs into main.
* Split out switch on outputting intrinsic 'specials'.
Output code around intrinsic as emit - so that we get the appropriate indenting (and potentially other benefits).
* Improvements to diagnostics parsing.
Better error handling, and fallback handling.
Added ability to parse downstream compilers without a prefix.
Added ability to parse Slang with a prefix.
* DownstreamDiagnostic::Type -> Severity and related fixes.
* Small fixes around moving from DownstreamDiagnostic::Type -> Severity
* Fix handling of 'special intrinsic' expansion
* Split out the handling of intrinsic expansion into it's own type and files.
* Fixes to reading expected output - for SimpleLine test.
* Test using += to check #line output.
* A test around += and return.
* Small comment fixes.
Co-authored-by: Tim Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/core.meta.slang | 3 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 479 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-intrinsic-expand.cpp | 519 | ||||
| -rw-r--r-- | source/slang/slang-intrinsic-expand.h | 34 |
6 files changed, 567 insertions, 478 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index a60da422c..55f6c607b 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1921,11 +1921,12 @@ int getStringHash(String string); // Use will produce a syntax error in downstream compiler // Useful for testing diagnostics around compilation errors of downstream compiler +// It 'returns' an int so can be used in expressions without the front end complaining. __target_intrinsic(hlsl, " @ ") __target_intrinsic(glsl, " @ ") __target_intrinsic(cuda, " @ ") __target_intrinsic(cpp, " @ ") -void __SyntaxError(); +int __SyntaxError(); // Operators to apply to `enum` types diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index d21e16186..d916019c7 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -22,6 +22,8 @@ #include "slang-type-layout.h" #include "slang-visitor.h" +#include "slang-intrinsic-expand.h" + #include "slang-emit-source-writer.h" #include "slang-mangled-lexer.h" @@ -1417,481 +1419,8 @@ void CLikeSourceEmitter::emitIntrinsicCallExprImpl( } else { - int openParenCount = 0; - - const auto returnType = inst->getDataType(); - - // If it returns void -> then we don't need parenthesis - if (as<IRVoidType>(returnType) == nullptr) - { - m_writer->emit("("); - openParenCount++; - } - - // General case: we are going to emit some more complex text. - - char const* cursor = name.begin(); - char const* end = name.end(); - while(cursor != end) - { - char c = *cursor++; - if( c != '$' ) - { - // Not an escape sequence - m_writer->emitRawTextSpan(&c, &c+1); - continue; - } - - SLANG_RELEASE_ASSERT(cursor != end); - - char d = *cursor++; - - switch (d) - { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - // Simple case: emit one of the direct arguments to the call - Index argIndex = d - '0'; - SLANG_RELEASE_ASSERT((0 <= argIndex) && (argIndex < argCount)); - m_writer->emit("("); - emitOperand(args[argIndex].get(), getInfo(EmitOp::General)); - m_writer->emit(")"); - } - break; - - case 'T': - // Get the the 'element' type for the type of the param at the index - { - SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); - Index argIndex = (*cursor++) - '0'; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - IRType* type = args[argIndex].get()->getDataType(); - if (auto baseTextureType = as<IRTextureType>(type)) - { - type = baseTextureType->getElementType(); - } - emitType(type); - } - break; - - case 'S': - // Get the scalar type of a generic at specified index - { - SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); - Index argIndex = (*cursor++) - '0'; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - IRType* type = args[argIndex].get()->getDataType(); - if (auto baseTextureType = as<IRTextureType>(type)) - { - type = baseTextureType->getElementType(); - } - - IRBasicType* underlyingType = nullptr; - if (auto basicType = as<IRBasicType>(type)) - { - underlyingType = basicType; - } - else if (auto vectorType = as<IRVectorType>(type)) - { - underlyingType = as<IRBasicType>(vectorType->getElementType()); - } - else if (auto matrixType = as<IRMatrixType>(type)) - { - underlyingType = as<IRBasicType>(matrixType->getElementType()); - } - - SLANG_ASSERT(underlyingType); - - emitSimpleType(underlyingType); - } - break; - case 'p': - { - // If we are calling a D3D texturing operation in the form t.Foo(s, ...), - // then this form will pair up the t and s arguments as needed for a GLSL - // texturing operation. - SLANG_RELEASE_ASSERT(argCount >= 2); - - auto textureArg = args[0].get(); - auto samplerArg = args[1].get(); - - if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) - { - emitTextureOrTextureSamplerTypeImpl(baseTextureType, "sampler"); - - if (auto samplerType = as<IRSamplerStateTypeBase>(samplerArg->getDataType())) - { - if (as<IRSamplerComparisonStateType>(samplerType)) - { - m_writer->emit("Shadow"); - } - } - - m_writer->emit("("); - emitOperand(textureArg, getInfo(EmitOp::General)); - m_writer->emit(","); - emitOperand(samplerArg, getInfo(EmitOp::General)); - m_writer->emit(")"); - } - else - { - SLANG_UNEXPECTED("bad format in intrinsic definition"); - } - } - break; - - case 'c': - { - // When doing texture access in glsl the result may need to be cast. - // In particular if the underlying texture is 'half' based, glsl only accesses (read/write) - // as float. So we need to cast to a half type on output. - // When storing into a texture it is still the case the value written must be half - but - // we don't need to do any casting there as half is coerced to float without a problem. - SLANG_RELEASE_ASSERT(argCount >= 1); - - auto textureArg = args[0].get(); - if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) - { - auto elementType = baseTextureType->getElementType(); - IRBasicType* underlyingType = nullptr; - if (auto basicType = as<IRBasicType>(elementType)) - { - underlyingType = basicType; - } - else if (auto vectorType = as<IRVectorType>(elementType)) - { - underlyingType = as<IRBasicType>(vectorType->getElementType()); - } - - // We only need to output a cast if the underlying type is half. - if (underlyingType && underlyingType->op == kIROp_HalfType) - { - emitSimpleType(elementType); - m_writer->emit("("); - openParenCount++; - } - } - } - break; - - case 'z': - { - // If we are calling a D3D texturing operation in the form t.Foo(s, ...), - // where `t` is a `Texture*<T>`, then this is the step where we try to - // properly swizzle the output of the equivalent GLSL call into the right - // shape. - SLANG_RELEASE_ASSERT(argCount >= 1); - - auto textureArg = args[0].get(); - if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) - { - auto elementType = baseTextureType->getElementType(); - if (auto basicType = as<IRBasicType>(elementType)) - { - // A scalar result is expected - m_writer->emit(".x"); - } - else if (auto vectorType = as<IRVectorType>(elementType)) - { - // A vector result is expected - auto elementCount = getIntVal(vectorType->getElementCount()); - - if (elementCount < 4) - { - char const* swiz[] = { "", ".x", ".xy", ".xyz", "" }; - m_writer->emit(swiz[elementCount]); - } - } - else - { - // What other cases are possible? - } - } - else - { - SLANG_UNEXPECTED("bad format in intrinsic definition"); - } - } - break; - - case 'N': - { - // Extract the element count from a vector argument so that - // we can use it in the constructed expression. - - SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); - Index argIndex = (*cursor++) - '0'; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - auto vectorArg = args[argIndex].get(); - if (auto vectorType = as<IRVectorType>(vectorArg->getDataType())) - { - auto elementCount = getIntVal(vectorType->getElementCount()); - m_writer->emit(elementCount); - } - else - { - SLANG_UNEXPECTED("bad format in intrinsic definition"); - } - } - break; - - case 'V': - { - // Take an argument of some scalar/vector type and pad - // it out to a 4-vector with the same element type - // (this is the inverse of `$z`). - // - SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); - Index argIndex = (*cursor++) - '0'; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - auto arg = args[argIndex].get(); - IRIntegerValue elementCount = 1; - IRType* elementType = arg->getDataType(); - if (auto vectorType = as<IRVectorType>(elementType)) - { - elementCount = getIntVal(vectorType->getElementCount()); - elementType = vectorType->getElementType(); - } - - if(elementCount == 4) - { - // In the simple case, the operand is already a 4-vector, - // so we can just emit it as-is. - emitOperand(arg, getInfo(EmitOp::General)); - } - else - { - // Otherwise, we need to construct a 4-vector from the - // value we have, padding it out with zero elements as - // needed. - // - emitVectorTypeName(elementType, 4); - m_writer->emit("("); - emitOperand(arg, getInfo(EmitOp::General)); - for(IRIntegerValue ii = elementCount; ii < 4; ++ii) - { - m_writer->emit(", "); - if(getSourceLanguage() == SourceLanguage::GLSL) - { - emitSimpleType(elementType); - m_writer->emit("(0)"); - } - else - { - m_writer->emit("0"); - } - } - m_writer->emit(")"); - } - } - break; - - case 'a': - { - // We have an operation that needs to lower to either - // `atomic*` or `imageAtomic*` for GLSL, depending on - // whether its first operand is a subscript into an - // array. This `$a` is the first `a` in `atomic`, - // so we will replace it accordingly. - // - // TODO: This distinction should be made earlier, - // with the front-end picking the right overload - // based on the "address space" of the argument. - - Index argIndex = 0; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - auto arg = args[argIndex].get(); - if(arg->op == kIROp_ImageSubscript) - { - m_writer->emit("imageA"); - } - else - { - m_writer->emit("a"); - } - } - break; - - case 'A': - { - // We have an operand that represents the destination - // of an atomic operation in GLSL, and it should - // be lowered based on whether it is an ordinary l-value, - // or an image subscript. In the image subscript case - // this operand will turn into multiple arguments - // to the `imageAtomic*` function. - // - - Index argIndex = 0; - SLANG_RELEASE_ASSERT(argCount > argIndex); - - auto arg = args[argIndex].get(); - if(arg->op == kIROp_ImageSubscript) - { - if(getSourceLanguage() == SourceLanguage::GLSL) - { - // TODO: we don't handle the multisample - // case correctly here, where the last - // component of the image coordinate needs - // to be broken out into its own argument. - // - m_writer->emit("("); - emitOperand(arg->getOperand(0), getInfo(EmitOp::General)); - m_writer->emit("), "); - - // The coordinate argument will have been computed - // as a `vector<uint, N>` because that is how the - // HLSL image subscript operations are defined. - // In contrast, the GLSL `imageAtomic*` operations - // expect `vector<int, N>` coordinates, so we - // will hackily insert the conversion here as - // part of the intrinsic op. - // - auto coords = arg->getOperand(1); - auto coordsType = coords->getDataType(); - - auto coordsVecType = as<IRVectorType>(coordsType); - IRIntegerValue elementCount = 1; - if(coordsVecType) - { - coordsType = coordsVecType->getElementType(); - elementCount = getIntVal(coordsVecType->getElementCount()); - } - - SLANG_ASSERT(coordsType->op == kIROp_UIntType); - - if (elementCount > 1) - { - m_writer->emit("ivec"); - m_writer->emit(elementCount); - } - else - { - m_writer->emit("int"); - } - - m_writer->emit("("); - emitOperand(arg->getOperand(1), getInfo(EmitOp::General)); - m_writer->emit(")"); - } - else - { - m_writer->emit("("); - emitOperand(arg, getInfo(EmitOp::General)); - m_writer->emit(")"); - } - } - else - { - m_writer->emit("("); - emitOperand(arg, getInfo(EmitOp::General)); - m_writer->emit(")"); - } - } - break; - - // We will use the `$X` case as a prefix for - // special logic needed when cross-compiling ray-tracing - // shaders. - case 'X': - { - SLANG_RELEASE_ASSERT(*cursor); - switch(*cursor++) - { - case 'P': - { - // The `$XP` case handles looking up - // the associated `location` for a variable - // used as the argument ray payload at a - // trace call site. - - Index argIndex = 0; - SLANG_RELEASE_ASSERT(argCount > argIndex); - auto arg = args[argIndex].get(); - auto argLoad = as<IRLoad>(arg); - SLANG_RELEASE_ASSERT(argLoad); - auto argVar = argLoad->getOperand(0); - m_writer->emit(getRayPayloadLocation(argVar)); - } - break; - - case 'C': - { - // The `$XC` case handles looking up - // the associated `location` for a variable - // used as the argument callable payload at a - // call site. - - Index argIndex = 0; - SLANG_RELEASE_ASSERT(argCount > argIndex); - auto arg = args[argIndex].get(); - auto argLoad = as<IRLoad>(arg); - SLANG_RELEASE_ASSERT(argLoad); - auto argVar = argLoad->getOperand(0); - m_writer->emit(getCallablePayloadLocation(argVar)); - } - break; - - default: - SLANG_RELEASE_ASSERT(false); - break; - } - } - break; - - case 'P': - // Type-based prefix as used for CUDA and C++ targets - { - Index argIndex = 0; - SLANG_RELEASE_ASSERT(argCount > argIndex); - auto arg = args[argIndex].get(); - auto argType = arg->getDataType(); - - const char* str = ""; - switch(argType->op) - { - #define CASE(OP, STR) \ - case kIROp_##OP: str = #STR; break - - CASE(Int8Type, I8); - CASE(Int16Type, I16); - CASE(IntType, I32); - CASE(Int64Type, I64); - CASE(UInt8Type, U8); - CASE(UInt16Type, U16); - CASE(UIntType, U32); - CASE(UInt64Type, U64); - CASE(HalfType, F16); - CASE(FloatType, F32); - CASE(DoubleType, F64); - - #undef CASE - - default: - SLANG_UNEXPECTED("unexpected type in intrinsic definition"); - break; - } - m_writer->emit(str); - } - break; - - default: - SLANG_UNEXPECTED("bad format in intrinsic definition"); - break; - } - } - - // Close any remaining open parens - for (; openParenCount > 0; --openParenCount) - { - m_writer->emit(")"); - } + IntrinsicExpandContext context(this); + context.emit(inst, args, argCount, name); } } diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index b9bddca69..5846d99fd 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -100,6 +100,9 @@ public: /// Get the source manager SourceManager* getSourceManager() { return m_compileRequest->getSourceManager(); } + /// Get the source writer used + SourceWriter* getSourceWriter() const { return m_writer; } + /// Get the diagnostic sink DiagnosticSink* getSink() { return m_compileRequest->getSink();} LineDirectiveMode getLineDirectiveMode() { return m_compileRequest->getLineDirectiveMode(); } @@ -299,6 +302,8 @@ public: void emitVectorTypeName(IRType* elementType, IRIntegerValue elementCount) { emitVectorTypeNameImpl(elementType, elementCount); } + void emitTextureOrTextureSamplerType(IRTextureTypeBase* type, char const* baseName) { emitTextureOrTextureSamplerTypeImpl(type, baseName); } + virtual RefObject* getExtensionTracker() { return nullptr; } /// Gets a source language for a target for a target. Returns Unknown if not a known target @@ -313,6 +318,8 @@ public: protected: + + virtual bool doesTargetSupportPtrTypes() { return false; } virtual void emitLayoutSemanticsImpl(IRInst* inst, char const* uniformSemanticSpelling = "register") { SLANG_UNUSED(inst); SLANG_UNUSED(uniformSemanticSpelling); } virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) = 0; @@ -369,7 +376,6 @@ public: // Emit the argument list (including paranthesis) in a `CallInst` void _emitCallArgList(IRCall* call); - String _generateUniqueName(const UnownedStringSlice& slice); // Sort witnessTable entries according to the order defined in the witnessed interface type. diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index 2a28be70a..5fb7bd1d7 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -848,7 +848,7 @@ void GLSLSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPoin m_writer->emit(" = "); m_writer->emit(sizeAlongAxis[ii]); } - m_writer->emit(") in;"); + m_writer->emit(") in;\n"); } break; case Stage::Geometry: diff --git a/source/slang/slang-intrinsic-expand.cpp b/source/slang/slang-intrinsic-expand.cpp new file mode 100644 index 000000000..ca52dd5f0 --- /dev/null +++ b/source/slang/slang-intrinsic-expand.cpp @@ -0,0 +1,519 @@ +// slang-intrinsic-expand.cpp +#include "slang-intrinsic-expand.h" + +namespace Slang { + +void IntrinsicExpandContext::emit(IRCall* inst, IRUse* args, Int argCount, const UnownedStringSlice& intrinsicText) +{ + m_args = args; + m_argCount = argCount; + m_text = intrinsicText; + + const auto returnType = inst->getDataType(); + + // If it returns void -> then we don't need parenthesis + if (as<IRVoidType>(returnType) == nullptr) + { + m_writer->emit("("); + m_openParenCount++; + } + + { + char const* spanStart = intrinsicText.begin(); + char const* cursor = spanStart; + char const* end = intrinsicText.end(); + + while (cursor < end) + { + // Indicates the start of a 'special' sequence + if (*cursor == '$') + { + // Flush any chars not yet output + if (spanStart < cursor) + { + m_writer->emit(spanStart, cursor); + } + + // Requires special processing for output (ie we don't just copy chars verbatim) + cursor = _emitSpecial(cursor); + // The start is now after 'special' handling + spanStart = cursor; + } + else + { + cursor++; + } + } + + // Flush any non escaped + if (spanStart < end) + { + m_writer->emit(spanStart, end); + } + } + + // Close any remaining open parens + for (Index i = 0; i < m_openParenCount; ++i) + { + m_writer->emit(")"); + } +} + +const char* IntrinsicExpandContext::_emitSpecial(const char* cursor) +{ + const char*const end = m_text.end(); + + // Check we are at start of 'special' sequence + SLANG_ASSERT(*cursor == '$'); + cursor++; + + SLANG_RELEASE_ASSERT(cursor < end); + + const char d = *cursor++; + + switch (d) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + // Simple case: emit one of the direct arguments to the call + Index argIndex = d - '0'; + SLANG_RELEASE_ASSERT((0 <= argIndex) && (argIndex < m_argCount)); + m_writer->emit("("); + m_emitter->emitOperand(m_args[argIndex].get(), getInfo(EmitOp::General)); + m_writer->emit(")"); + } + break; + + case 'T': + // Get the the 'element' type for the type of the param at the index + { + SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); + Index argIndex = (*cursor++) - '0'; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + IRType* type = m_args[argIndex].get()->getDataType(); + if (auto baseTextureType = as<IRTextureType>(type)) + { + type = baseTextureType->getElementType(); + } + m_emitter->emitType(type); + } + break; + + case 'S': + // Get the scalar type of a generic at specified index + { + SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); + Index argIndex = (*cursor++) - '0'; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + IRType* type = m_args[argIndex].get()->getDataType(); + if (auto baseTextureType = as<IRTextureType>(type)) + { + type = baseTextureType->getElementType(); + } + + IRBasicType* underlyingType = nullptr; + if (auto basicType = as<IRBasicType>(type)) + { + underlyingType = basicType; + } + else if (auto vectorType = as<IRVectorType>(type)) + { + underlyingType = as<IRBasicType>(vectorType->getElementType()); + } + else if (auto matrixType = as<IRMatrixType>(type)) + { + underlyingType = as<IRBasicType>(matrixType->getElementType()); + } + + SLANG_ASSERT(underlyingType); + + m_emitter->emitSimpleType(underlyingType); + } + break; + case 'p': + { + // If we are calling a D3D texturing operation in the form t.Foo(s, ...), + // then this form will pair up the t and s arguments as needed for a GLSL + // texturing operation. + SLANG_RELEASE_ASSERT(m_argCount >= 2); + + auto textureArg = m_args[0].get(); + auto samplerArg = m_args[1].get(); + + if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) + { + m_emitter->emitTextureOrTextureSamplerType(baseTextureType, "sampler"); + + if (auto samplerType = as<IRSamplerStateTypeBase>(samplerArg->getDataType())) + { + if (as<IRSamplerComparisonStateType>(samplerType)) + { + m_writer->emit("Shadow"); + } + } + + m_writer->emit("("); + m_emitter->emitOperand(textureArg, getInfo(EmitOp::General)); + m_writer->emit(","); + m_emitter->emitOperand(samplerArg, getInfo(EmitOp::General)); + m_writer->emit(")"); + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + break; + + case 'c': + { + // When doing texture access in glsl the result may need to be cast. + // In particular if the underlying texture is 'half' based, glsl only accesses (read/write) + // as float. So we need to cast to a half type on output. + // When storing into a texture it is still the case the value written must be half - but + // we don't need to do any casting there as half is coerced to float without a problem. + SLANG_RELEASE_ASSERT(m_argCount >= 1); + + auto textureArg = m_args[0].get(); + if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) + { + auto elementType = baseTextureType->getElementType(); + IRBasicType* underlyingType = nullptr; + if (auto basicType = as<IRBasicType>(elementType)) + { + underlyingType = basicType; + } + else if (auto vectorType = as<IRVectorType>(elementType)) + { + underlyingType = as<IRBasicType>(vectorType->getElementType()); + } + + // We only need to output a cast if the underlying type is half. + if (underlyingType && underlyingType->op == kIROp_HalfType) + { + m_emitter->emitSimpleType(elementType); + m_writer->emit("("); + m_openParenCount++; + } + } + } + break; + + case 'z': + { + // If we are calling a D3D texturing operation in the form t.Foo(s, ...), + // where `t` is a `Texture*<T>`, then this is the step where we try to + // properly swizzle the output of the equivalent GLSL call into the right + // shape. + SLANG_RELEASE_ASSERT(m_argCount >= 1); + + auto textureArg = m_args[0].get(); + if (auto baseTextureType = as<IRTextureType>(textureArg->getDataType())) + { + auto elementType = baseTextureType->getElementType(); + if (auto basicType = as<IRBasicType>(elementType)) + { + // A scalar result is expected + m_writer->emit(".x"); + } + else if (auto vectorType = as<IRVectorType>(elementType)) + { + // A vector result is expected + auto elementCount = getIntVal(vectorType->getElementCount()); + + if (elementCount < 4) + { + char const* swiz[] = { "", ".x", ".xy", ".xyz", "" }; + m_writer->emit(swiz[elementCount]); + } + } + else + { + // What other cases are possible? + } + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + break; + + case 'N': + { + // Extract the element count from a vector argument so that + // we can use it in the constructed expression. + + SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); + Index argIndex = (*cursor++) - '0'; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + auto vectorArg = m_args[argIndex].get(); + if (auto vectorType = as<IRVectorType>(vectorArg->getDataType())) + { + auto elementCount = getIntVal(vectorType->getElementCount()); + m_writer->emit(elementCount); + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + break; + + case 'V': + { + // Take an argument of some scalar/vector type and pad + // it out to a 4-vector with the same element type + // (this is the inverse of `$z`). + // + SLANG_RELEASE_ASSERT(*cursor >= '0' && *cursor <= '9'); + Index argIndex = (*cursor++) - '0'; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + auto arg = m_args[argIndex].get(); + IRIntegerValue elementCount = 1; + IRType* elementType = arg->getDataType(); + if (auto vectorType = as<IRVectorType>(elementType)) + { + elementCount = getIntVal(vectorType->getElementCount()); + elementType = vectorType->getElementType(); + } + + if (elementCount == 4) + { + // In the simple case, the operand is already a 4-vector, + // so we can just emit it as-is. + m_emitter->emitOperand(arg, getInfo(EmitOp::General)); + } + else + { + // Otherwise, we need to construct a 4-vector from the + // value we have, padding it out with zero elements as + // needed. + // + m_emitter->emitVectorTypeName(elementType, 4); + m_writer->emit("("); + m_emitter->emitOperand(arg, getInfo(EmitOp::General)); + for (IRIntegerValue ii = elementCount; ii < 4; ++ii) + { + m_writer->emit(", "); + if (m_emitter->getSourceLanguage() == SourceLanguage::GLSL) + { + m_emitter->emitSimpleType(elementType); + m_writer->emit("(0)"); + } + else + { + m_writer->emit("0"); + } + } + m_writer->emit(")"); + } + } + break; + + case 'a': + { + // We have an operation that needs to lower to either + // `atomic*` or `imageAtomic*` for GLSL, depending on + // whether its first operand is a subscript into an + // array. This `$a` is the first `a` in `atomic`, + // so we will replace it accordingly. + // + // TODO: This distinction should be made earlier, + // with the front-end picking the right overload + // based on the "address space" of the argument. + + Index argIndex = 0; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + auto arg = m_args[argIndex].get(); + if (arg->op == kIROp_ImageSubscript) + { + m_writer->emit("imageA"); + } + else + { + m_writer->emit("a"); + } + } + break; + + case 'A': + { + // We have an operand that represents the destination + // of an atomic operation in GLSL, and it should + // be lowered based on whether it is an ordinary l-value, + // or an image subscript. In the image subscript case + // this operand will turn into multiple arguments + // to the `imageAtomic*` function. + // + + Index argIndex = 0; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + + auto arg = m_args[argIndex].get(); + if (arg->op == kIROp_ImageSubscript) + { + if (m_emitter->getSourceLanguage() == SourceLanguage::GLSL) + { + // TODO: we don't handle the multisample + // case correctly here, where the last + // component of the image coordinate needs + // to be broken out into its own argument. + // + m_writer->emit("("); + m_emitter->emitOperand(arg->getOperand(0), getInfo(EmitOp::General)); + m_writer->emit("), "); + + // The coordinate argument will have been computed + // as a `vector<uint, N>` because that is how the + // HLSL image subscript operations are defined. + // In contrast, the GLSL `imageAtomic*` operations + // expect `vector<int, N>` coordinates, so we + // will hackily insert the conversion here as + // part of the intrinsic op. + // + auto coords = arg->getOperand(1); + auto coordsType = coords->getDataType(); + + auto coordsVecType = as<IRVectorType>(coordsType); + IRIntegerValue elementCount = 1; + if (coordsVecType) + { + coordsType = coordsVecType->getElementType(); + elementCount = getIntVal(coordsVecType->getElementCount()); + } + + SLANG_ASSERT(coordsType->op == kIROp_UIntType); + + if (elementCount > 1) + { + m_writer->emit("ivec"); + m_writer->emit(elementCount); + } + else + { + m_writer->emit("int"); + } + + m_writer->emit("("); + m_emitter->emitOperand(arg->getOperand(1), getInfo(EmitOp::General)); + m_writer->emit(")"); + } + else + { + m_writer->emit("("); + m_emitter->emitOperand(arg, getInfo(EmitOp::General)); + m_writer->emit(")"); + } + } + else + { + m_writer->emit("("); + m_emitter->emitOperand(arg, getInfo(EmitOp::General)); + m_writer->emit(")"); + } + } + break; + + // We will use the `$X` case as a prefix for + // special logic needed when cross-compiling ray-tracing + // shaders. + case 'X': + { + SLANG_RELEASE_ASSERT(*cursor); + switch (*cursor++) + { + case 'P': + { + // The `$XP` case handles looking up + // the associated `location` for a variable + // used as the argument ray payload at a + // trace call site. + + Index argIndex = 0; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + auto arg = m_args[argIndex].get(); + auto argLoad = as<IRLoad>(arg); + SLANG_RELEASE_ASSERT(argLoad); + auto argVar = argLoad->getOperand(0); + m_writer->emit(m_emitter->getRayPayloadLocation(argVar)); + } + break; + + case 'C': + { + // The `$XC` case handles looking up + // the associated `location` for a variable + // used as the argument callable payload at a + // call site. + + Index argIndex = 0; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + auto arg = m_args[argIndex].get(); + auto argLoad = as<IRLoad>(arg); + SLANG_RELEASE_ASSERT(argLoad); + auto argVar = argLoad->getOperand(0); + m_writer->emit(m_emitter->getCallablePayloadLocation(argVar)); + } + break; + + default: + SLANG_RELEASE_ASSERT(false); + break; + } + } + break; + + case 'P': + // Type-based prefix as used for CUDA and C++ targets + { + Index argIndex = 0; + SLANG_RELEASE_ASSERT(m_argCount > argIndex); + auto arg = m_args[argIndex].get(); + auto argType = arg->getDataType(); + + const char* str = ""; + switch (argType->op) + { +#define CASE(OP, STR) \ + case kIROp_##OP: str = #STR; break + + CASE(Int8Type, I8); + CASE(Int16Type, I16); + CASE(IntType, I32); + CASE(Int64Type, I64); + CASE(UInt8Type, U8); + CASE(UInt16Type, U16); + CASE(UIntType, U32); + CASE(UInt64Type, U64); + CASE(HalfType, F16); + CASE(FloatType, F32); + CASE(DoubleType, F64); + +#undef CASE + + default: + SLANG_UNEXPECTED("unexpected type in intrinsic definition"); + break; + } + m_writer->emit(str); + } + break; + + default: + SLANG_UNEXPECTED("bad format in intrinsic definition"); + break; + } + + // Return the cursor position. Will be next character after characters processed + // for the 'special' + return cursor; +} + +} // namespace Slang diff --git a/source/slang/slang-intrinsic-expand.h b/source/slang/slang-intrinsic-expand.h new file mode 100644 index 000000000..468e1f80a --- /dev/null +++ b/source/slang/slang-intrinsic-expand.h @@ -0,0 +1,34 @@ +// slang-intrinsic-expand.h +#ifndef SLANG_INTRINSIC_EXPAND_H +#define SLANG_INTRINSIC_EXPAND_H + +#include "slang-emit-c-like.h" + +namespace Slang +{ + +/* Handles all the special case handling of expansions of intrinsics. In particular handles the expansion +of the 'special cases' prefixed with '$' */ +struct IntrinsicExpandContext +{ + IntrinsicExpandContext(CLikeSourceEmitter* emitter) : + m_emitter(emitter), + m_writer(emitter->getSourceWriter()) + { + } + + void emit(IRCall* inst, IRUse* args, Int argCount, const UnownedStringSlice& intrinsicText); + +protected: + const char* _emitSpecial(const char* cursor); + + SourceWriter* m_writer; + UnownedStringSlice m_text; + IRUse* m_args = nullptr; + Int m_argCount = 0; + Index m_openParenCount = 0; + CLikeSourceEmitter* m_emitter; +}; + +} +#endif |
