diff options
| -rw-r--r-- | source/slang/hlsl.meta.slang | 10 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-compiler.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 285 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 20 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 71 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 128 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 30 |
9 files changed, 299 insertions, 265 deletions
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index a24d6c414..986152e92 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -1223,8 +1223,14 @@ matrix<T,N,M> saturate(matrix<T,N,M> x) // Extract sign of value -__generic<T : __BuiltinSignedArithmeticType> int sign(T x); -__generic<T : __BuiltinSignedArithmeticType, let N : int> vector<int,N> sign(vector<T,N> x); +__generic<T : __BuiltinSignedArithmeticType> +__target_intrinsic(glsl, "int(sign($0))") +int sign(T x); + +__generic<T : __BuiltinSignedArithmeticType, let N : int> +__target_intrinsic(glsl, "ivec$N0(sign($0))") +vector<int,N> sign(vector<T,N> x); + __generic<T : __BuiltinSignedArithmeticType, let N : int, let M : int> matrix<int,N,M> sign(matrix<T,N,M> x); diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h index 86ec0c556..95e07658f 100644 --- a/source/slang/hlsl.meta.slang.h +++ b/source/slang/hlsl.meta.slang.h @@ -1299,8 +1299,14 @@ SLANG_RAW("}\n") SLANG_RAW("\n") SLANG_RAW("\n") SLANG_RAW("// Extract sign of value\n") -SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType> int sign(T x);\n") -SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType, let N : int> vector<int,N> sign(vector<T,N> x);\n") +SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType>\n") +SLANG_RAW("__target_intrinsic(glsl, \"int(sign($0))\")\n") +SLANG_RAW("int sign(T x);\n") +SLANG_RAW("\n") +SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType, let N : int>\n") +SLANG_RAW("__target_intrinsic(glsl, \"ivec$N0(sign($0))\")\n") +SLANG_RAW("vector<int,N> sign(vector<T,N> x);\n") +SLANG_RAW("\n") SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType, let N : int, let M : int> matrix<int,N,M> sign(matrix<T,N,M> x);\n") SLANG_RAW("\n") SLANG_RAW("\n") @@ -1571,7 +1577,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa) sb << "};\n"; } -SLANG_RAW("#line 1498 \"hlsl.meta.slang\"") +SLANG_RAW("#line 1504 \"hlsl.meta.slang\"") SLANG_RAW("\n") SLANG_RAW("\n") SLANG_RAW("\n") diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 1900342da..872b075d3 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1444,6 +1444,9 @@ namespace Slang List<RefPtr<ComponentType>> const& getUnspecializedEntryPoints() { return m_unspecializedEntryPoints; } + /// Does the code we are compiling represent part of the Slang standard library? + bool m_isStandardLibraryCode = false; + private: /// A component type that includes only the global scopes of the translation unit(s) that were compiled. RefPtr<ComponentType> m_globalComponentType; diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index a84e398ac..fbe3cb40a 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -358,6 +358,18 @@ bool CLikeSourceEmitter::isTargetIntrinsicModifierApplicable(IRTargetIntrinsicDe return isTargetIntrinsicModifierApplicable(targetName); } +bool CLikeSourceEmitter::isTargetIntrinsicModifierBetter(IRTargetIntrinsicDecoration* candidate, IRTargetIntrinsicDecoration* existing) +{ + // For now, the rule is that an empty string represents a catch-all + // definition, which is worse than any target-specific declaration. + // Therefore, if the new `candidate` has a non-empty target name + // specified, then it is automatically better (or at least as + // good) as `existing`. + // + SLANG_UNUSED(existing); + return candidate->getTargetName().size() != 0; +} + void CLikeSourceEmitter::emitStringLiteral(String const& value) { m_writer->emit("\""); @@ -1034,31 +1046,68 @@ void CLikeSourceEmitter::emitInstResultDecl(IRInst* inst) m_writer->emit(" = "); } -IRTargetIntrinsicDecoration* CLikeSourceEmitter::findTargetIntrinsicDecoration(IRInst* inst) +IRTargetIntrinsicDecoration* CLikeSourceEmitter::findTargetIntrinsicDecoration(IRInst* inInst) { + // An intrinsic generic function will be invoked through a `specialize` instruction, + // so the callee won't directly be the thing that is decorated. We will look up + // through specializations until we can see the actual thing being called. + // + IRInst* inst = inInst; + while (auto specInst = as<IRSpecialize>(inst)) + { + inst = getSpecializedValue(specInst); + + // If `getSpecializedValue` can't find the result value + // of the generic being specialized, then it returns + // the original instruction. This would be a disaster + // for use because this loop would go on forever. + // + // This case should never happen if the stdlib is well-formed + // and the compiler is doing its job right. + // + SLANG_ASSERT(inst != specInst); + } + + // We will search through all the `IRTargetIntrinsicDecoration`s on + // the instruction, looking for those that are applicable to the + // current code generation target. Among the application decorations + // we will try to find one that is "best" in the sense that it is + // more (or at least as) specialized for the target than the + // others. + // + IRTargetIntrinsicDecoration* best = nullptr; for(auto dd : inst->getDecorations()) { if (dd->op != kIROp_TargetIntrinsicDecoration) continue; auto targetIntrinsic = (IRTargetIntrinsicDecoration*)dd; - if (isTargetIntrinsicModifierApplicable(targetIntrinsic)) - return targetIntrinsic; + if (!isTargetIntrinsicModifierApplicable(targetIntrinsic)) + continue; + + if(!best || isTargetIntrinsicModifierBetter(targetIntrinsic, best)) + best = targetIntrinsic; } - return nullptr; + return best; } -/* static */bool CLikeSourceEmitter::isOrdinaryName(String const& name) +/* static */bool CLikeSourceEmitter::isOrdinaryName(UnownedStringSlice const& name) { char const* cursor = name.begin(); char const* end = name.end(); + // Consume an optional `.` at the start, which indicates + // the ordinary name is for a member function. + if(cursor != end && *cursor == '.') + cursor++; + while(cursor != end) { int c = *cursor++; if( (c >= 'a') && (c <= 'z') ) continue; if( (c >= 'A') && (c <= 'Z') ) continue; + if( (c >= '0') && (c <= '9') ) continue; if( c == '_' ) continue; return false; @@ -1066,9 +1115,8 @@ IRTargetIntrinsicDecoration* CLikeSourceEmitter::findTargetIntrinsicDecoration(I return true; } -void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( +void CLikeSourceEmitter::emitIntrinsicCallExpr( IRCall* inst, - IRFunc* /* func */, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec) { @@ -1081,7 +1129,7 @@ void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( args++; argCount--; - auto name = String(targetIntrinsic->getDefinition()); + auto name = targetIntrinsic->getDefinition(); if(isOrdinaryName(name)) { @@ -1089,6 +1137,20 @@ void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( auto prec = getInfo(EmitOp::Postfix); bool needClose = maybeEmitParens(outerPrec, prec); + // The definition string may be an ordinary name prefixed with `.` + // to indicate that the operation should be called as a member + // function on its first operand. + // + if(name[0] == '.') + { + emitOperand(args[0].get(), leftSide(outerPrec, prec)); + m_writer->emit("."); + + name = UnownedStringSlice(name.begin() + 1, name.end()); + args++; + argCount--; + } + m_writer->emit(name); m_writer->emit("("); for (Index aa = 0; aa < argCount; ++aa) @@ -1101,6 +1163,35 @@ void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( maybeCloseParens(needClose); return; } + else if(name == ".operator[]") + { + // The user is invoking a built-in subscript operator + // + // TODO: We might want to remove this bit of special-casing + // in favor of making all subscript operations in the standard + // library explicitly declare how they lower. On the flip + // side, that would require modifications to a very large + // number of declarations. + + auto prec = getInfo(EmitOp::Postfix); + bool needClose = maybeEmitParens(outerPrec, prec); + + Int argIndex = 0; + + emitOperand(args[argIndex++].get(), leftSide(outerPrec, prec)); + m_writer->emit("["); + emitOperand(args[argIndex++].get(), getInfo(EmitOp::General)); + m_writer->emit("]"); + + if(argIndex < argCount) + { + m_writer->emit(" = "); + emitOperand(args[argIndex++].get(), getInfo(EmitOp::General)); + } + + maybeCloseParens(needClose); + return; + } else { int openParenCount = 0; @@ -1516,151 +1607,6 @@ void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( } } -void CLikeSourceEmitter::emitIntrinsicCallExpr( - IRCall* inst, - IRFunc* func, - 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; - - - // - if (auto targetIntrinsicDecoration = findTargetIntrinsicDecoration(func)) - { - emitTargetIntrinsicCallExpr( - inst, - func, - targetIntrinsicDecoration, - outerPrec); - 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 - // 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++), leftSide(outerPrec, prec)); - m_writer->emit("["); - emitOperand(inst->getOperand(operandIndex++), getInfo(EmitOp::General)); - m_writer->emit("]"); - - if(operandIndex < operandCount) - { - m_writer->emit(" = "); - emitOperand(inst->getOperand(operandIndex++), 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), leftSide(outerPrec, prec)); - m_writer->emit("."); - operandIndex++; - } - // fixing issue #602 for GLSL sign function: https://github.com/shader-slang/slang/issues/602 - bool glslSignFix = getSourceStyle() == SourceStyle::GLSL && name == "sign"; - if (glslSignFix) - { - if (auto vectorType = as<IRVectorType>(inst->getDataType())) - { - m_writer->emit("ivec"); - m_writer->emit(as<IRConstant>(vectorType->getElementCount())->value.intVal); - m_writer->emit("("); - } - else if (auto scalarType = as<IRBasicType>(inst->getDataType())) - { - m_writer->emit("int("); - } - else - glslSignFix = false; - } - m_writer->emit(name); - m_writer->emit("("); - bool first = true; - for(; operandIndex < operandCount; ++operandIndex ) - { - if(!first) m_writer->emit(", "); - emitOperand(inst->getOperand(operandIndex), getInfo(EmitOp::General)); - first = false; - } - m_writer->emit(")"); - if (glslSignFix) - m_writer->emit(")"); - maybeCloseParens(needClose); -} - void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec) { auto funcValue = inst->getOperand(0); @@ -1670,9 +1616,9 @@ void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec) // We want to detect any call to an intrinsic operation, // that we can emit it directly without mangling, etc. - if(auto irFunc = asTargetIntrinsic(funcValue)) + if(auto targetIntrinsic = findTargetIntrinsicDecoration(funcValue)) { - emitIntrinsicCallExpr(inst, irFunc, outerPrec); + emitIntrinsicCallExpr(inst, targetIntrinsic, outerPrec); } else { @@ -2728,30 +2674,11 @@ IREntryPointLayout* CLikeSourceEmitter::asEntryPoint(IRFunc* func) bool CLikeSourceEmitter::isTargetIntrinsic(IRFunc* func) { - // For now we do this in an overly simplistic - // fashion: we say that *any* function declaration - // (rather then definition) must be an intrinsic: - return !isDefinition(func); -} - -IRFunc* CLikeSourceEmitter::asTargetIntrinsic(IRInst* value) -{ - if(!value) - return nullptr; - - while (auto specInst = as<IRSpecialize>(value)) - { - value = getSpecializedValue(specInst); - } - - if(value->op != kIROp_Func) - return nullptr; - - IRFunc* func = (IRFunc*) value; - if(!isTargetIntrinsic(func)) - return nullptr; - - return func; + // A function is a target intrinsic if and only if + // it has a suitable decoration marking it as a + // target intrinsic for the current compilation target. + // + return findTargetIntrinsicDecoration(func) != nullptr; } void CLikeSourceEmitter::emitFunc(IRFunc* func) diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 2b2e0d199..4b5c68b6c 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -140,6 +140,9 @@ public: bool isTargetIntrinsicModifierApplicable(IRTargetIntrinsicDecoration* decoration); + /// Is the `candidate` decoration more specialized for the current target than `existing`? + bool isTargetIntrinsicModifierBetter(IRTargetIntrinsicDecoration* candidate, IRTargetIntrinsicDecoration* existing); + void emitStringLiteral(const String& value); void emitVal(IRInst* val, const EmitOpInfo& outerPrec); @@ -184,19 +187,13 @@ public: // Check if the string being used to define a target intrinsic // is an "ordinary" name, such that we can simply emit a call // to the new name with the arguments of the old operation. - static bool isOrdinaryName(const String& name); - - void emitTargetIntrinsicCallExpr( + static bool isOrdinaryName(const UnownedStringSlice& name); + + void emitIntrinsicCallExpr( IRCall* inst, - IRFunc* /* func */, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec); - void emitIntrinsicCallExpr( - IRCall* inst, - IRFunc* func, - EmitOpInfo const& inOuterPrec); - void emitCallExpr(IRCall* inst, EmitOpInfo outerPrec); void emitInstExpr(IRInst* inst, EmitOpInfo const& inOuterPrec); @@ -253,11 +250,6 @@ public: // current code-generation target. bool isTargetIntrinsic(IRFunc* func); - // Check whether a given value names a target intrinsic, - // and return the IR function representing the intrinsic - // if it does. - IRFunc* asTargetIntrinsic(IRInst* value); - void emitFunc(IRFunc* func); void emitStruct(IRStructType* structType); diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 5ee0bc873..5592ba26a 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -2049,8 +2049,17 @@ void CPPSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc) } } -void CPPSourceEmitter::emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, EmitOpInfo const& inOuterPrec) +void CPPSourceEmitter::emitIntrinsicCallExpr( + IRCall* inst, + IRTargetIntrinsicDecoration* targetIntrinsic, + EmitOpInfo const& inOuterPrec) { + // TODO: Much of this logic duplicates code that is already + // in `CLikeSourceEmitter::emitIntrinsicCallExpr`. The only + // real difference is that when things bottom out on an ordinary + // function call there is logic to look up a C/C++-backend-specific + // opcode based on the function name, and emit using that. + auto outerPrec = inOuterPrec; bool needClose = false; @@ -2058,57 +2067,16 @@ void CPPSourceEmitter::emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, EmitOpI // 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(); + auto name = targetIntrinsic->getDefinition(); // 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[]") + if (name == ".operator[]") { // The user is invoking a built-in subscript operator @@ -2133,18 +2101,13 @@ void CPPSourceEmitter::emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, EmitOpI 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) + if (name[0] == '.') { // Looks like a member function call emitOperand(inst->getOperand(operandIndex), leftSide(outerPrec, prec)); m_writer->emit("."); + + name = UnownedStringSlice(name.begin()+1, name.end()); operandIndex++; } else @@ -2284,9 +2247,9 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut // We want to detect any call to an intrinsic operation, // that we can emit it directly without mangling, etc. - if (auto irFunc = asTargetIntrinsic(funcValue)) + if(auto targetIntrinsic = findTargetIntrinsicDecoration(funcValue)) { - emitIntrinsicCallExpr(static_cast<IRCall*>(inst), irFunc, inOuterPrec); + emitIntrinsicCallExpr(static_cast<IRCall*>(inst), targetIntrinsic, inOuterPrec); return true; } diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index 0af2d4555..fdc283574 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -226,7 +226,10 @@ protected: virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; - void emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, EmitOpInfo const& inOuterPrec); + void emitIntrinsicCallExpr( + IRCall* inst, + IRTargetIntrinsicDecoration* targetIntrinsic, + EmitOpInfo const& inOuterPrec); void _emitVecMatMulDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index eabe08de3..f040fb003 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5561,8 +5561,132 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> definition = definitionToken.Content; } - builder->addTargetIntrinsicDecoration(irInst, targetMod->targetToken.Content, definition.getUnownedSlice()); + UnownedStringSlice targetName; + auto& targetToken = targetMod->targetToken; + if( targetToken.type != TokenType::Unknown ) + { + targetName = targetToken.Content; + } + + builder->addTargetIntrinsicDecoration(irInst, targetName, definition.getUnownedSlice()); + } + } + + /// Is `decl` a member function (or effectively a member function) when considered as a stdlib declaration? + bool isStdLibMemberFuncDecl( + CallableDecl* decl) + { + // Constructors aren't really member functions, insofar + // as they aren't called with a `this` parameter. + // + // TODO: We may also want to exclude `static` functions + // here for the same reason, but this routine is only + // used for the stdlib, where we don't currently have + // any `static` member functions to worry about. + // + if(as<ConstructorDecl>(decl)) + return false; + + auto dd = decl->ParentDecl; + for(;;) + { + if(auto genericDecl = as<GenericDecl>(dd)) + { + dd = genericDecl->ParentDecl; + continue; + } + + if( auto subscriptDecl = as<SubscriptDecl>(dd) ) + { + dd = subscriptDecl->ParentDecl; + } + + break; } + + // Note: the use of `AggTypeDeclBase` here instead of just + // `AggTypeDecl` means that we consider a declaration that + // is under a `struct` *or* an `extension` to be a member + // function for our purposes. + // + if(as<AggTypeDeclBase>(dd)) + return true; + + return false; + } + + /// Add a "catch-all" decoration for a stdlib function if it would be needed + void addCatchAllIntrinsicDecorationIfNeeded( + IRInst* irInst, + FunctionDeclBase* decl) + { + // We don't need an intrinsic decoration on a function that has a body, + // since the body can be used as the "catch-all" case. + // + if(decl->Body) + return; + + // Only standard library declarations should get any kind of catch-all + // treatment by default. Declarations in user case are responsible + // for marking things as target intrinsics if they want to go down + // that (unsupported) route. + // + if(!isFromStdLib(decl)) + return; + + // No need to worry about functions that lower to intrinsic IR opcodes + // (or pseudo-ops). + // + if(decl->FindModifier<IntrinsicOpModifier>()) + return; + + // We also don't need an intrinsic decoration if the function already + // had a catch-all case on one of its target overloads. + // + for( auto f = decl->primaryDecl; f; f = f->nextDecl ) + { + for(auto targetMod : f->GetModifiersOfType<TargetIntrinsicModifier>()) + { + // If we find a catch-all case (marked as either *no* target + // token or an empty target name), then we should bail out. + // + if(targetMod->targetToken.type == TokenType::Unknown) + return; + else if(targetMod->targetToken.Content.size() == 0) + return; + } + } + + String definition; + + // If we have a member function, then we want the default intrinsic + // definition to reflect this fact so that we can emit it correctly + // (the assumption is that a catch-all definition of a member function + // is itself implemented as a member function). + // + if( isStdLibMemberFuncDecl(decl) ) + { + // We will mark member functions by appending a `.` to the + // start of their name. + // + definition.append("."); + } + + // We want to output the name of the declaration, + // but in some cases the actual `decl` that has + // to be emitted is not the one with the name. + // + // In particular, an accessor declaration (e.g., + // a `get`ter` in a subscript or property) doesn't + // have a name, but its parent should. + // + Decl* declForName = decl; + if(auto accessorDecl = as<AccessorDecl>(decl)) + declForName = decl->ParentDecl; + + definition.append(getText(declForName->getName())); + + getBuilder()->addTargetIntrinsicDecoration(irInst, UnownedStringSlice(), definition.getUnownedSlice()); } void addParamNameHint(IRInst* inst, ParameterInfo info) @@ -5922,6 +6046,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // for a particular target, then handle that here. addTargetIntrinsicDecorations(irFunc, decl); + addCatchAllIntrinsicDecorationIfNeeded(irFunc, decl); + // If this declaration requires certain GLSL extension (or a particular GLSL version) // for it to be usable, then declare that here. // diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 29b46c805..26a846d6d 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -968,6 +968,24 @@ void FrontEndCompileRequest::parseTranslationUnit( translationUnitSyntax->module = module; module->setModuleDecl(translationUnitSyntax); + // When compiling a module of code that belongs to the Slang + // standard library, we add a modifier to the module to act + // as a marker, so that downstream code can detect declarations + // that came from the standard library (by walking up their + // chain of ancestors and looking for the marker), and treat + // them differently from user declarations. + // + // We are adding the marker here, before we even parse the + // code in the module, in case the subsequent steps would + // like to treat the standard library differently. Alternatiely + // we could pass down the `m_isStandardLibraryCode` flag to + // these passes. + // + if( m_isStandardLibraryCode ) + { + translationUnitSyntax->modifiers.first = new FromStdLibModifier(); + } + for (auto sourceFile : translationUnit->getSourceFiles()) { auto tokens = preprocessSource( @@ -2570,7 +2588,7 @@ void Session::addBuiltinSource( RefPtr<FrontEndCompileRequest> compileRequest = new FrontEndCompileRequest( m_builtinLinkage, &sink); - + compileRequest->m_isStandardLibraryCode = true; // Set the source manager on the sink sink.sourceManager = sourceManager; @@ -2602,16 +2620,6 @@ void Session::addBuiltinSource( // Extract the AST for the code we just parsed auto syntax = compileRequest->translationUnits[translationUnitIndex]->getModuleDecl(); - // HACK(tfoley): mark all declarations in the "stdlib" so - // that we can detect them later (e.g., so we don't emit them) - for (auto m : syntax->Members) - { - auto fromStdLibModifier = new FromStdLibModifier(); - - fromStdLibModifier->next = m->modifiers.first; - m->modifiers.first = fromStdLibModifier; - } - // Add the resulting code to the appropriate scope if (!scope->containerDecl) { |
