diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-02-07 13:41:43 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-02-07 13:41:43 -0800 |
| commit | 1fbc73d96fbc0a199d823dfb38fc8f02bf7ada0a (patch) | |
| tree | b4d894f1179a66219631539a95e422ba62988b04 /source/slang/emit.cpp | |
| parent | 662f43fff6721c6cd013a8f1b2639c2e29fe6be3 (diff) | |
Support __target_intrinsic modifiers in IR codegen (#401)
The standard library already has a bunch of these decorations, since they were added to support Slang->Vulkan codegen on the AST-to-AST path. This change makes the IR code generator able to exploit the modifiers so that we pick up a bunch of Vulkan support "for free" in the short term.
The basic change is in `lower-to-ir.cpp` where we copy over any `TargetIntrinsicModifier`s to become `IRTargetIntrinsicDecoration`s with the same information. We then need a bit of logic in `ir.cpp` to make sure we clone them as needed.
The core work of using the modifiers is in `emit.cpp`, where I basically just copy-pasted the existing logic that applied in the AST path (all the AST-related code there is dead, and we should clean it up soon).
The big change that comes with this logic is that when dealing with a member function, the numbering of the argument used in the intrinsic definition string changes, so that `$0` refers to the base object (whereas before the base object was looked up via the base expression of a `MemberExpr` used for the function). This requires a bunch of the definitions in the library to be updated; hopefully I caught them all.
For kicks, I've re-enabled a cross-compilation test just to confirm that we are generating valid SPIR-V for code that performs texture-fetch operations. I don't expect us to keep that test enabled as-is in the long term, though, because it would be much better to instead use render-test to do the same thing. Alas, beefing up the Vulkan support in render-test is an outstanding work item, and I didn't want to pollute this change with more work along those lines.
Diffstat (limited to 'source/slang/emit.cpp')
| -rw-r--r-- | source/slang/emit.cpp | 247 |
1 files changed, 239 insertions, 8 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 1ef42bdf0..f9f24fcf6 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -1559,6 +1559,20 @@ struct EmitVisitor emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, true); } + bool isTargetIntrinsicModifierApplicable( + String const& targetName) + { + switch(context->shared->target) + { + default: + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target"); + return false; + + case CodeGenTarget::GLSL: return targetName == "glsl"; + case CodeGenTarget::HLSL: return targetName == "hlsl"; + } + } + // Determine if a target intrinsic modifer is applicable to the target // we are currently emitting code for. bool isTargetIntrinsicModifierApplicable( @@ -1575,17 +1589,23 @@ struct EmitVisitor // we expect. auto const& targetName = targetToken.Content; - switch(context->shared->target) - { - default: - SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled code generation target"); - return false; + return isTargetIntrinsicModifierApplicable(targetName); + } - case CodeGenTarget::GLSL: return targetName == "glsl"; - case CodeGenTarget::HLSL: return targetName == "hlsl"; - } + bool isTargetIntrinsicModifierApplicable( + IRTargetIntrinsicDecoration* decoration) + { + auto targetName = decoration->targetName; + + // If no target name was specified, then the modifier implicitly + // applies to all targets. + if(targetName.Length() == 0) + return true; + + return isTargetIntrinsicModifierApplicable(targetName); } + // Find an intrinsic modifier appropriate to the current compilation target. // // If there are multiple such modifiers, this should return the best one. @@ -5414,6 +5434,205 @@ emitDeclImpl(decl, nullptr); } }; + IRTargetIntrinsicDecoration* findTargetIntrinsicDecoration( + EmitContext* /* ctx */, + IRFunc* func) + { + for (auto dd = func->firstDecoration; dd; dd = dd->next) + { + if (dd->op != kIRDecorationOp_TargetIntrinsic) + continue; + + auto targetIntrinsic = (IRTargetIntrinsicDecoration*)dd; + if (isTargetIntrinsicModifierApplicable(targetIntrinsic)) + return targetIntrinsic; + } + + return nullptr; + } + + void emitTargetIntrinsicCallExpr( + EmitContext* ctx, + IRCall* inst, + IRFunc* /* func */, + IRTargetIntrinsicDecoration* targetIntrinsic) + { + IRUse* args = inst->getArgs(); + UInt argCount = inst->getArgCount(); + + // First operand was the function to be called + args++; + argCount--; + + auto name = targetIntrinsic->definition; + + + if(name.IndexOf('$') == -1) + { + // Simple case: it is just an ordinary name, so we call it like a builtin. + + emit(name); + Emit("("); + for (UInt aa = 0; aa < argCount; ++aa) + { + if (aa != 0) Emit(", "); + emitIROperand(ctx, args[aa].usedValue); + } + Emit(")"); + return; + } + else + { + // General case: we are going to emit some more complex text. + + Emit("("); + + char const* cursor = name.begin(); + char const* end = name.end(); + while(cursor != end) + { + char c = *cursor++; + if( c != '$' ) + { + // Not an escape sequence + 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 + UInt argIndex = d - '0'; + SLANG_RELEASE_ASSERT((0 <= argIndex) && (argIndex < argCount)); + Emit("("); + emitIROperand(ctx, args[argIndex].usedValue); + Emit(")"); + } + 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].usedValue; + auto samplerArg = args[1].usedValue; + + if (auto baseTextureType = textureArg->type->As<TextureType>()) + { + emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler"); + + if (auto samplerType = samplerArg->type->As<SamplerStateType>()) + { + if (samplerType->flavor == SamplerStateType::Flavor::SamplerComparisonState) + { + Emit("Shadow"); + } + } + + Emit("("); + emitIROperand(ctx, textureArg); + Emit(","); + emitIROperand(ctx, samplerArg); + Emit(")"); + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + break; + + case 'P': + { + // Okay, we need a collosal hack to deal with the fact that GLSL `texelFetch()` + // for Vulkan seems to be completely broken by design. It's signature wants + // a `sampler2D` for consistency with its peers, but the actual SPIR-V operation + // ignores the sampler paart of it, and just used the `texture2D` part. + // + // The HLSL equivalent (e.g., `Texture2D.Load()`) doesn't provide a sampler + // argument, so we seemingly need to conjure one out of thin air. :( + // + // We are going to hack this *hard* for now. + + auto textureArg = args[0].usedValue; + if (auto baseTextureType = textureArg->type->As<TextureType>()) + { + emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler"); + Emit("("); + emitIROperand(ctx, textureArg); + Emit(","); + Emit("SLANG_hack_samplerForTexelFetch"); + context->shared->needHackSamplerForTexelFetch = true; + Emit(")"); + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + 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].usedValue; + if (auto baseTextureType = textureArg->type->As<TextureType>()) + { + auto elementType = baseTextureType->elementType; + if (auto basicType = elementType->As<BasicExpressionType>()) + { + // A scalar result is expected + Emit(".x"); + } + else if (auto vectorType = elementType->As<VectorExpressionType>()) + { + // A vector result is expected + auto elementCount = GetIntVal(vectorType->elementCount); + + if (elementCount < 4) + { + char const* swiz[] = { "", ".x", ".xy", ".xyz", "" }; + Emit(swiz[elementCount]); + } + } + else + { + // What other cases are possible? + } + } + else + { + SLANG_UNEXPECTED("bad format in intrinsic definition"); + } + } + break; + + + default: + SLANG_UNEXPECTED("bad format in intrinsic definition"); + break; + } + } + + Emit(")"); + } + } + void emitIntrinsicCallExpr( EmitContext* ctx, IRCall* inst, @@ -5426,6 +5645,18 @@ emitDeclImpl(decl, nullptr); UInt argCount = operandCount - 1; UInt operandIndex = 1; + + // + if (auto targetIntrinsicDecoration = findTargetIntrinsicDecoration(ctx, func)) + { + emitTargetIntrinsicCallExpr( + ctx, + inst, + func, + targetIntrinsicDecoration); + return; + } + // 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 |
