summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/hlsl.meta.slang10
-rw-r--r--source/slang/hlsl.meta.slang.h12
-rw-r--r--source/slang/slang-compiler.h3
-rw-r--r--source/slang/slang-emit-c-like.cpp285
-rw-r--r--source/slang/slang-emit-c-like.h20
-rw-r--r--source/slang/slang-emit-cpp.cpp71
-rw-r--r--source/slang/slang-emit-cpp.h5
-rw-r--r--source/slang/slang-lower-to-ir.cpp128
-rw-r--r--source/slang/slang.cpp30
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)
{