summaryrefslogtreecommitdiff
path: root/source/slang/slang-emit-c-like.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-emit-c-like.cpp')
-rw-r--r--source/slang/slang-emit-c-like.cpp285
1 files changed, 106 insertions, 179 deletions
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)