summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/emit.cpp2929
1 files changed, 103 insertions, 2826 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 98ce61b74..bed2488a1 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -128,6 +128,14 @@ struct SharedEmitContext
// The "effective" profile that is being used to emit code,
// combining information from the target and entry point.
Profile effectiveProfile;
+
+
+ // Are we at the start of a line, so that we should indent
+ // before writing any other text?
+ bool isAtStartOfLine = true;
+
+ // How far are we indented?
+ Int indentLevel = 0;
};
struct EmitContext
@@ -138,51 +146,6 @@ struct EmitContext
//
-#ifdef DEADCODE
-void requireGLSLVersion(
- EntryPointRequest* entryPoint,
- ProfileVersion version)
-{
- auto profile = entryPoint->profile;
- if (profile.getFamily() == ProfileFamily::GLSL)
- {
- // Check if this profile is newer
- if ((UInt)version > (UInt)profile.GetVersion())
- {
- profile.setVersion(version);
- entryPoint->profile = profile;
- }
- }
- else
- {
- // Non-GLSL target? Set it to a GLSL one.
- profile.setVersion(version);
- entryPoint->profile = profile;
- }
-}
-#endif
-
-//
-
-static String getStringOrIdentifierTokenValue(
- Token const& token)
-{
- switch(token.type)
- {
- default:
- SLANG_UNEXPECTED("needed an identifier or string literal");
- break;
-
- case TokenType::Identifier:
- return token.Content;
-
- case TokenType::StringLiteral:
- return getStringLiteralTokenValue(token);
- break;
- }
-}
-
-
enum EPrecedence
{
#define LEFT(NAME) \
@@ -220,57 +183,6 @@ enum EPrecedence
RIGHT(Prefix),
LEFT(Postfix),
NONASSOC(Atomic),
-
-#if 0
-
- kEPrecedence_None,
- kEPrecedence_Comma,
-
- kEPrecedence_Assign,
- kEPrecedence_AddAssign = kEPrecedence_Assign,
- kEPrecedence_SubAssign = kEPrecedence_Assign,
- kEPrecedence_MulAssign = kEPrecedence_Assign,
- kEPrecedence_DivAssign = kEPrecedence_Assign,
- kEPrecedence_ModAssign = kEPrecedence_Assign,
- kEPrecedence_LshAssign = kEPrecedence_Assign,
- kEPrecedence_RshAssign = kEPrecedence_Assign,
- kEPrecedence_OrAssign = kEPrecedence_Assign,
- kEPrecedence_AndAssign = kEPrecedence_Assign,
- kEPrecedence_XorAssign = kEPrecedence_Assign,
-
- kEPrecedence_General = kEPrecedence_Assign,
-
- kEPrecedence_Conditional, // "ternary"
- kEPrecedence_Or,
- kEPrecedence_And,
- kEPrecedence_BitOr,
- kEPrecedence_BitXor,
- kEPrecedence_BitAnd,
-
- kEPrecedence_Eql,
- kEPrecedence_Neq = kEPrecedence_Eql,
-
- kEPrecedence_Less,
- kEPrecedence_Greater = kEPrecedence_Less,
- kEPrecedence_Leq = kEPrecedence_Less,
- kEPrecedence_Geq = kEPrecedence_Less,
-
- kEPrecedence_Lsh,
- kEPrecedence_Rsh = kEPrecedence_Lsh,
-
- kEPrecedence_Add,
- kEPrecedence_Sub = kEPrecedence_Add,
-
- kEPrecedence_Mul,
- kEPrecedence_Div = kEPrecedence_Mul,
- kEPrecedence_Mod = kEPrecedence_Mul,
-
- kEPrecedence_Prefix,
- kEPrecedence_Postfix,
- kEPrecedence_Atomic = kEPrecedence_Postfix
-
-#endif
-
};
// Info on an op for emit purposes
@@ -398,20 +310,8 @@ struct TypeEmitArg
EDeclarator* declarator;
};
-struct ExprEmitArg
-{
- EOpInfo outerPrec;
-};
-
-struct DeclEmitArg
-{
- VarLayout* layout;
-};
-
struct EmitVisitor
: TypeVisitorWithArg<EmitVisitor, TypeEmitArg>
- , ExprVisitorWithArg<EmitVisitor, ExprEmitArg>
- , DeclVisitorWithArg<EmitVisitor, DeclEmitArg>
{
EmitContext* context;
EmitVisitor(EmitContext* context)
@@ -439,23 +339,61 @@ struct EmitVisitor
void emitTextSpan(char const* textBegin, char const* textEnd)
{
+ // Don't change anything given an empty string
+ if(textBegin == textEnd)
+ return;
+
// If the source location has changed in a way that required update,
// do it now!
flushSourceLocationChange();
+ // Note: we don't want to emit indentation on a line that is empty.
+ // The logic in `Emit(textBegin, textEnd)` below will have broken
+ // the text into lines, so we can simply check if a line consists
+ // of just a newline.
+ if(context->shared->isAtStartOfLine && *textBegin != '\n')
+ {
+ // We are about to emit text (other than a newline)
+ // at the start of a line, so we will emit the proper
+ // amount of indentation to keep things looking nice.
+ context->shared->isAtStartOfLine = false;
+ for(Int ii = 0; ii < context->shared->indentLevel; ++ii)
+ {
+ char const* indentString = " ";
+ size_t indentStringSize = strlen(indentString);
+ emitRawTextSpan(indentString, indentString + indentStringSize);
+
+ // We will also update our tracking location, just in
+ // case other logic needs it.
+ //
+ // TODO: We may need to have a switch that controls whether
+ // we are in "pretty-printing" mode or "follow the locations
+ // in the original code" mode.
+ context->shared->loc.column += indentStringSize;
+ }
+ }
+
// Emit the raw text
emitRawTextSpan(textBegin, textEnd);
// Update our logical position
- // TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things...
auto len = int(textEnd - textBegin);
context->shared->loc.column += len;
}
+ void indent()
+ {
+ context->shared->indentLevel++;
+ }
+
+ void dedent()
+ {
+ context->shared->indentLevel--;
+ }
+
void Emit(char const* textBegin, char const* textEnd)
{
char const* spanBegin = textBegin;
-
char const* spanEnd = spanBegin;
for(;;)
{
@@ -475,6 +413,7 @@ struct EmitVisitor
emitTextSpan(spanBegin, spanEnd);
context->shared->loc.line++;
context->shared->loc.column = 1;
+ context->shared->isAtStartOfLine = true;
// Start a new span for emit purposes
spanBegin = spanEnd;
@@ -619,10 +558,10 @@ struct EmitVisitor
switch (mode)
{
case LineDirectiveMode::None:
- SLANG_UNEXPECTED("should not trying to emit '#line' directive");
+ case LineDirectiveMode::Default:
+ SLANG_UNEXPECTED("should not be trying to emit '#line' directive");
return;
- case LineDirectiveMode::Default:
default:
// To try to make the default behavior reasonable, we will
// always use C-style line directives (to give the user
@@ -722,9 +661,18 @@ struct EmitVisitor
// Don't do any of this work if the user has requested that we
// not emit line directives.
auto mode = context->shared->entryPoint->compileRequest->lineDirectiveMode;
- if (mode == LineDirectiveMode::None)
+ switch(mode)
+ {
+ case LineDirectiveMode::None:
+ case LineDirectiveMode::Default:
+ // Default behavior is to not emit line directives, since they
+ // don't help readability much for IR-based output.
return;
+ default:
+ break;
+ }
+
// Ignore invalid source locations
if(sourceLocation.line <= 0)
return;
@@ -758,23 +706,6 @@ struct EmitVisitor
emitLineDirectiveAndUpdateSourceLocation(sourceLocation);
}
}
-
- // Now indent up to the appropriate column, so that error messages
- // that reference columns will be correct.
- //
- // TODO: This logic does not take into account whether indentation
- // came in as spaces or tabs, so there is necessarily going to be
- // coupling between how the downstream compiler counts columns,
- // and how we do.
- if(sourceLocation.column > context->shared->loc.column)
- {
- Slang::Int delta = sourceLocation.column - context->shared->loc.column;
- for( int ii = 0; ii < delta; ++ii )
- {
- emitRawText(" ");
- }
- context->shared->loc.column = sourceLocation.column;
- }
}
void advanceToSourceLocation(
@@ -812,38 +743,6 @@ struct EmitVisitor
emitLineDirectiveIfNeeded(context->shared->nextSourceLocation);
}
- void emitTokenWithLocation(Token const& token)
- {
- auto mode = context->shared->entryPoint->compileRequest->lineDirectiveMode;
- if (mode == LineDirectiveMode::None)
- return;
-
- if ((mode == LineDirectiveMode::None)
- || !token.loc.isValid())
- {
- // If we don't have the original position info, or we are in the
- // mode where the user didn't want line directives, we need to play
- // it safe and emit whitespace to line things up nicely
-
- if(token.flags & TokenFlag::AtStartOfLine)
- Emit("\n");
- // TODO(tfoley): macro expansion can currently lead to whitespace getting dropped,
- // so we will just insert it aggressively, to play it safe.
- else // if(token.flags & TokenFlag::AfterWhitespace)
- Emit(" ");
- }
- else
- {
- // If location information is available, and we are emitting
- // such information, then just advance our tracking location
- // to the right place.
- advanceToSourceLocation(token.loc);
- }
-
- // Emit the raw textual content of the token
- emit(token.Content);
- }
-
DiagnosticSink* getSink()
{
return &context->shared->entryPoint->compileRequest->mSink;
@@ -1369,118 +1268,10 @@ struct EmitVisitor
emitTypeImpl(type, nullptr);
}
- void emitTypeBasedOnExpr(Expr* expr, EDeclarator* declarator)
- {
- if (auto subscriptExpr = dynamic_cast<IndexExpr*>(expr))
- {
- // Looks like an array
- emitTypeBasedOnExpr(subscriptExpr->BaseExpression, declarator);
- Emit("[");
- if (auto indexExpr = subscriptExpr->IndexExpression)
- {
- EmitExpr(indexExpr);
- }
- Emit("]");
- }
- else
- {
- // Default case
- EmitExpr(expr);
- EmitDeclarator(declarator);
- }
- }
-
- void EmitType(TypeExp const& typeExp, Name* name, SourceLoc const& nameLoc)
- {
- if (!typeExp.type || typeExp.type->As<ErrorType>())
- {
- if (!typeExp.exp)
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unresolved type expression should have expression part");
- }
-
- EDeclarator nameDeclarator;
- nameDeclarator.flavor = EDeclarator::Flavor::name;
- nameDeclarator.name = name;
- nameDeclarator.loc = nameLoc;
-
- emitTypeBasedOnExpr(typeExp.exp, &nameDeclarator);
- }
- else
- {
- EmitType(typeExp.type,
- typeExp.exp ? typeExp.exp->loc : SourceLoc(),
- name, nameLoc);
- }
- }
-
- void EmitType(TypeExp const& typeExp, NameLoc const& nameAndLoc)
- {
- EmitType(typeExp, nameAndLoc.name, nameAndLoc.loc);
- }
-
- void EmitType(TypeExp const& typeExp, Name* name)
- {
- EmitType(typeExp, name, SourceLoc());
- }
-
- void emitTypeExp(TypeExp const& typeExp)
- {
- // TODO: we need to handle cases where the type part of things is bad...
- emitTypeImpl(typeExp.type, nullptr);
- }
-
//
// Expressions
//
- // Determine if an expression should not be emitted when it is the base of
- // a member reference expression.
- bool IsBaseExpressionImplicit(RefPtr<Expr> expr)
- {
- // HACK(tfoley): For now, anything with a constant-buffer type should be
- // left implicit.
-
- // Look through any dereferencing that took place
- RefPtr<Expr> e = expr;
- while (auto derefExpr = e.As<DerefExpr>())
- {
- e = derefExpr->base;
- }
-
- if (auto declRefExpr = e.As<DeclRefExpr>())
- {
- auto decl = declRefExpr->declRef.getDecl();
- if (decl && decl->HasModifier<TransparentModifier>())
- return true;
- }
-
- // Is the expression referencing a uniform parameter group,
- // but *not* a `ParameterBlock<T>`?
- if (auto parameterBlockType = e->type->As<ParameterBlockType>())
- {
- return false;
- }
- if (auto uniformParameterGroupType = e->type->As<UniformParameterGroupType>())
- {
- return true;
- }
-
- return false;
- }
-
-#if 0
- void EmitPostfixExpr(RefPtr<Expr> expr)
- {
- EmitExprWithPrecedence(expr, kEOp_Postfix);
- }
-#endif
-
- void EmitExpr(RefPtr<Expr> expr)
- {
- EmitExprWithPrecedence(expr, kEOp_General);
- }
-
bool MaybeEmitParens(EOpInfo& outerPrec, EOpInfo prec)
{
bool needParens = (prec.leftPrecedence <= outerPrec.leftPrecedence)
@@ -1495,118 +1286,6 @@ struct EmitVisitor
return needParens;
}
- // When we are going to emit an expression in an l-value context,
- // we may need to ignore certain constructs that the type-checker
- // might have introduced, but which interfere with our ability
- // to use it effectively in the target language
- RefPtr<Expr> prepareLValueExpr(
- RefPtr<Expr> expr)
- {
- for(;;)
- {
- if(auto typeCastExpr = expr.As<TypeCastExpr>())
- {
- expr = typeCastExpr->Arguments[0];
- }
- // TODO: any other cases?
- else
- {
- return expr;
- }
- }
-
- }
-
- void emitInfixExprImpl(
- EOpInfo outerPrec,
- EOpInfo prec,
- char const* op,
- RefPtr<InvokeExpr> binExpr,
- bool isAssign)
- {
- bool needsClose = MaybeEmitParens(outerPrec, prec);
-
- auto left = binExpr->Arguments[0];
- if(isAssign)
- {
- left = prepareLValueExpr(left);
- }
-
- EmitExprWithPrecedence(left, leftSide(outerPrec, prec));
- Emit(" ");
- Emit(op);
- Emit(" ");
- EmitExprWithPrecedence(binExpr->Arguments[1], rightSide(prec, outerPrec));
- if (needsClose)
- {
- Emit(")");
- }
- }
-
- void EmitBinExpr(EOpInfo outerPrec, EOpInfo prec, char const* op, RefPtr<InvokeExpr> binExpr)
- {
- emitInfixExprImpl(outerPrec, prec, op, binExpr, false);
- }
-
- void EmitBinAssignExpr(EOpInfo outerPrec, EOpInfo prec, char const* op, RefPtr<InvokeExpr> binExpr)
- {
- emitInfixExprImpl(outerPrec, prec, op, binExpr, true);
- }
-
- void emitUnaryExprImpl(
- EOpInfo outerPrec,
- EOpInfo prec,
- char const* preOp,
- char const* postOp,
- RefPtr<InvokeExpr> expr,
- bool isAssign)
- {
- bool needsClose = MaybeEmitParens(outerPrec, prec);
- Emit(preOp);
-
- auto arg = expr->Arguments[0];
- if(isAssign)
- {
- arg = prepareLValueExpr(arg);
- }
-
- if (preOp)
- {
- EmitExprWithPrecedence(arg, rightSide(prec, outerPrec));
- }
- else
- {
- SLANG_ASSERT(postOp);
- EmitExprWithPrecedence(arg, leftSide(outerPrec, prec));
- }
-
- Emit(postOp);
- if (needsClose)
- {
- Emit(")");
- }
- }
-
- void EmitUnaryExpr(
- EOpInfo outerPrec,
- EOpInfo prec,
- char const* preOp,
- char const* postOp,
- RefPtr<InvokeExpr> expr)
- {
- emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, false);
- }
-
- void EmitUnaryAssignExpr(
- EOpInfo outerPrec,
- EOpInfo prec,
- char const* preOp,
- char const* postOp,
- RefPtr<InvokeExpr> expr)
- {
- emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, true);
- }
-
bool isTargetIntrinsicModifierApplicable(
String const& targetName)
{
@@ -1621,25 +1300,6 @@ struct EmitVisitor
}
}
- // Determine if a target intrinsic modifer is applicable to the target
- // we are currently emitting code for.
- bool isTargetIntrinsicModifierApplicable(
- RefPtr<TargetIntrinsicModifier> modifier)
- {
- auto const& targetToken = modifier->targetToken;
-
- // If no target name was specified, then the modifier implicitly
- // applies to all targets.
- if(targetToken.type == TokenType::Unknown)
- return true;
-
- // Otherwise, we need to check if the target name matches what
- // we expect.
- auto const& targetName = targetToken.Content;
-
- return isTargetIntrinsicModifierApplicable(targetName);
- }
-
bool isTargetIntrinsicModifierApplicable(
IRTargetIntrinsicDecoration* decoration)
{
@@ -1653,171 +1313,6 @@ struct EmitVisitor
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.
- RefPtr<TargetIntrinsicModifier> findTargetIntrinsicModifier(
- RefPtr<ModifiableSyntaxNode> syntax)
- {
- RefPtr<TargetIntrinsicModifier> bestModifier;
- for(auto m : syntax->GetModifiersOfType<TargetIntrinsicModifier>())
- {
- if(!isTargetIntrinsicModifierApplicable(m))
- continue;
-
- // For now "better"-ness is defined as: a modifier
- // with a specified target is better than one without
- // (it is more specific)
- if(!bestModifier || bestModifier->targetToken.type == TokenType::Unknown)
- {
- bestModifier = m;
- }
- }
-
- return bestModifier;
- }
-
- void emitSimpleCallArgs(
- RefPtr<InvokeExpr> callExpr)
- {
- Emit("(");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit(")");
- }
-
- void emitTypeOrExpr(
- Type* type,
- Expr* expr)
- {
- if (type && !type->As<ErrorType>())
- {
- EmitType(type);
- }
- else
- {
- emitTypeBasedOnExpr(expr, nullptr);
- }
- }
-
- void emitSimpleConstructorCallExpr(
- RefPtr<InvokeExpr> callExpr,
- EOpInfo outerPrec)
- {
- if(context->shared->target == CodeGenTarget::HLSL)
- {
- // HLSL needs to special-case a constructor call with a single argument.
- if(callExpr->Arguments.Count() == 1)
- {
- auto prec = kEOp_Prefix;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- Emit("(");
- emitTypeOrExpr(callExpr->type.type, callExpr->FunctionExpr);
- Emit(") ");
-
- EmitExprWithPrecedence(callExpr->Arguments[0], rightSide(outerPrec, prec));
-
- if(needClose) Emit(")");
- return;
- }
- }
-
-
- // Default handling is to emit what amounts to an ordinary call,
- // but using the type of the expression directly as the "function" to call.
- auto prec = kEOp_Postfix;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- emitTypeOrExpr(callExpr->type.type, callExpr->FunctionExpr);
-
- emitSimpleCallArgs(callExpr);
-
- if (needClose)
- {
- Emit(")");
- }
- }
-
- void emitSimpleSubscriptCallExpr(
- RefPtr<InvokeExpr> callExpr,
- EOpInfo /*outerPrec*/)
- {
- auto funcExpr = callExpr->FunctionExpr;
-
- // We expect any subscript operation to be invoked as a member,
- // so the function expression had better be in the correct form.
- auto memberExpr = funcExpr.As<MemberExpr>();
- if(!memberExpr)
- {
- SLANG_UNEXPECTED("subscript needs base expression");
- }
-
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(")[");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit("]");
- }
-
- // Emit a call expression that doesn't involve any special cases,
- // just an expression of the form `f(a0, a1, ...)`
- void emitSimpleCallExpr(
- RefPtr<InvokeExpr> callExpr,
- EOpInfo outerPrec)
- {
- // We will first check if this represents a constructor call,
- // since those may need to be handled differently.
-
- auto funcExpr = callExpr->FunctionExpr;
- if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
- {
- auto declRef = funcDeclRefExpr->declRef;
- if (auto ctorDeclRef = declRef.As<ConstructorDecl>())
- {
- emitSimpleConstructorCallExpr(callExpr, outerPrec);
- return;
- }
-
- if(auto acessorDeclRef = declRef.As<AccessorDecl>())
- {
- declRef = acessorDeclRef.GetParent();
- }
-
- if(auto subscriptDeclRef = declRef.As<SubscriptDecl>())
- {
- emitSimpleSubscriptCallExpr(callExpr, outerPrec);
- return;
- }
-
- }
-
- // Once we've ruled out constructor calls, we can move on
- // to just emitting an ordinary calll expression.
-
- auto prec = kEOp_Postfix;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec));
-
- emitSimpleCallArgs(callExpr);
-
- if (needClose)
- {
- Emit(")");
- }
- }
-
void emitStringLiteral(
String const& value)
{
@@ -1861,152 +1356,6 @@ struct EmitVisitor
return result;
}
- void EmitExprWithPrecedence(RefPtr<Expr> expr, EOpInfo outerPrec)
- {
- ExprEmitArg arg;
- arg.outerPrec = outerPrec;
-
- ExprVisitorWithArg::dispatch(expr, arg);
- }
-
- void EmitExprWithPrecedence(RefPtr<Expr> expr, EPrecedence leftPrec, EPrecedence rightPrec)
- {
- EOpInfo outerPrec;
- outerPrec.leftPrecedence = leftPrec;
- outerPrec.rightPrecedence = rightPrec;
- }
-
- void visitGenericAppExpr(GenericAppExpr* expr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- EmitExprWithPrecedence(expr->FunctionExpr, leftSide(outerPrec, prec));
- Emit("<");
- bool first = true;
- for(auto aa : expr->Arguments)
- {
- if(!first) Emit(", ");
- EmitExpr(aa);
- first = false;
- }
- Emit(" >");
-
- if(needClose)
- {
- Emit(")");
- }
- }
-
- void visitSharedTypeExpr(SharedTypeExpr* expr, ExprEmitArg const&)
- {
- emitTypeExp(expr->base);
- }
-
- void visitSelectExpr(SelectExpr* selectExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Conditional;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, kEOp_Conditional);
-
- // TODO(tfoley): Need to ver the precedence here...
-
- EmitExprWithPrecedence(selectExpr->Arguments[0], leftSide(outerPrec, prec));
- Emit(" ? ");
- EmitExprWithPrecedence(selectExpr->Arguments[1], prec);
- Emit(" : ");
- EmitExprWithPrecedence(selectExpr->Arguments[2], rightSide(prec, outerPrec));
-
- if(needClose) Emit(")");
- }
-
- void visitParenExpr(ParenExpr* expr, ExprEmitArg const&)
- {
- Emit("(");
- EmitExprWithPrecedence(expr->base, kEOp_None);
- Emit(")");
- }
-
- void visitAssignExpr(AssignExpr* assignExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Assign;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
- EmitExprWithPrecedence(assignExpr->left, leftSide(outerPrec, prec));
- Emit(" = ");
- EmitExprWithPrecedence(assignExpr->right, rightSide(prec, outerPrec));
- if(needClose) Emit(")");
- }
-
- void emitUncheckedCallExpr(
- RefPtr<InvokeExpr> callExpr,
- Name* funcName,
- ExprEmitArg const& arg)
- {
- auto outerPrec = arg.outerPrec;
- auto funcExpr = callExpr->FunctionExpr;
-
- auto funcNameText = getText(funcName);
-
- // This can occur when we are dealing with unchecked input syntax,
- // because we are in "rewriter" mode. In this case we should go
- // ahead and emit things in the form that they were written.
- if( auto infixExpr = callExpr.As<InfixExpr>() )
- {
- auto prec = kEOp_Comma;
- for (auto opInfo : kInfixOpInfos)
- {
- if (funcNameText == opInfo->op)
- {
- prec = *opInfo;
- break;
- }
- }
-
- EmitBinExpr(
- outerPrec,
- prec,
- funcNameText.Buffer(),
- callExpr);
- }
- else if( auto prefixExpr = callExpr.As<PrefixExpr>() )
- {
- EmitUnaryExpr(
- outerPrec,
- kEOp_Prefix,
- funcNameText.Buffer(),
- "",
- callExpr);
- }
- else if(auto postfixExpr = callExpr.As<PostfixExpr>())
- {
- EmitUnaryExpr(
- outerPrec,
- kEOp_Postfix,
- "",
- funcNameText.Buffer(),
- callExpr);
- }
- else
- {
- bool needClose = MaybeEmitParens(outerPrec, kEOp_Postfix);
-
- EmitExpr(funcExpr);
-
- Emit("(");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit(")");
-
- if (needClose) Emit(")");
- }
- }
-
void requireGLSLExtension(String const& name)
{
Slang::requireGLSLExtension(&context->shared->extensionUsageTracker, name);
@@ -2044,594 +1393,11 @@ struct EmitVisitor
}
}
- void visitInvokeExpr(
- RefPtr<InvokeExpr> callExpr,
- ExprEmitArg const& arg)
- {
- auto outerPrec = arg.outerPrec;
-
- auto funcExpr = callExpr->FunctionExpr;
- if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
- {
- auto funcDeclRef = funcDeclRefExpr->declRef;
- auto funcDecl = funcDeclRef.getDecl();
- if (!funcDecl)
- {
- emitUncheckedCallExpr(callExpr, funcDeclRefExpr->name, arg);
- return;
- }
- // Note: We check for a "target intrinsic" modifier that flags the
- // operation as having a custom elaboration for a specific target
- // *before* we check for an "intrinsic op." The basic problem is
- // that a single operation could have both finds of modifiers on it.
- // The "target" intrinsic modifier tags something expansion during
- // our current source-to-source translation approach, while an
- // intrinsic op is needed for helping things lower to our IR.
- //
- // We need to check for this case first to make sure that when a
- // function gets an intrinsic op added it doesn't break existing
- // cross-compilation logic.
- //
- // The long term fix will be to not use the AST-based cross-compilation
- // logic (which has all kinds of problems) and instead use the IR
- // exclusively, at which point the notion of a "target intrinsic" modifier
- // goes away (although we may have something similar to express how
- // a particular op should lower/expand for a given target).
- else if(auto targetIntrinsicModifier = findTargetIntrinsicModifier(funcDecl))
- {
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- // Does this intrinsic requie a particular GLSL extension that wouldn't be available by default?
- if (auto requiredGLSLExtensionModifier = funcDecl->FindModifier<RequiredGLSLExtensionModifier>())
- {
- // If so, we had better request the extension.
- requireGLSLExtension(requiredGLSLExtensionModifier->extensionNameToken.Content);
- }
-
-#ifdef DEADCODE
- // Does this intrinsic requie a particular GLSL extension that wouldn't be available by default?
- if (auto requiredGLSLVersionModifier = funcDecl->FindModifier<RequiredGLSLVersionModifier>())
- {
- // If so, we had better request the extension.
- requireGLSLVersion((int) getIntegerLiteralValue(requiredGLSLVersionModifier->versionNumberToken));
- }
-#endif
- }
-
-
- if(targetIntrinsicModifier->definitionToken.type != TokenType::Unknown)
- {
- auto name = getStringOrIdentifierTokenValue(targetIntrinsicModifier->definitionToken);
-
- if(name.IndexOf('$') == -1)
- {
- // Simple case: it is just an ordinary name, so we call it like a builtin.
- //
- // TODO: this case could probably handle things like operators, for generality?
-
- emit(name);
- Emit("(");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit(")");
- return;
- }
- else
- {
- // General case: we are going to emit some more complex text.
-
- UInt argCount = callExpr->Arguments.Count();
-
- 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("(");
- EmitExpr(callExpr->Arguments[argIndex]);
- Emit(")");
- }
- break;
-
- case 'o':
- // For a call using object-oriented syntax, this
- // expands to the "base" object used for the call
- if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpr>())
- {
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(")");
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
- 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 > 0);
- if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpr>())
- {
- auto base = memberExpr->BaseExpression;
- if (auto baseTextureType = base->type->As<TextureType>())
- {
- emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler");
-
- if (auto samplerType = callExpr->Arguments[0]->type.type->As<SamplerStateType>())
- {
- if (samplerType->flavor == SamplerStateFlavor::SamplerComparisonState)
- {
- Emit("Shadow");
- }
- }
-
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(",");
- EmitExpr(callExpr->Arguments[0]);
- Emit(")");
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
-
- }
- 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.
-
- // Try to find a suitable sampler-type shader parameter in the global scope
- // (fingers crossed)
- RefPtr<VarDeclBase> samplerVar;
- for (auto dd : context->shared->program->Members)
- {
- if (auto varDecl = dd.As<VarDeclBase>())
- {
- if (auto samplerType = varDecl->type.type->As<SamplerStateType>())
- {
- samplerVar = varDecl;
- break;
- }
- }
- }
-
- if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpr>())
- {
- auto base = memberExpr->BaseExpression;
- if (auto baseTextureType = base->type->As<TextureType>())
- {
- emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler");
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(",");
- if (samplerVar)
- {
- EmitDeclRef(makeDeclRef(samplerVar.Ptr()));
- }
- else
- {
- Emit("SLANG_hack_samplerForTexelFetch");
- context->shared->needHackSamplerForTexelFetch = true;
- }
- Emit(")");
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
-
- }
- 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 > 0);
- if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpr>())
- {
- auto base = memberExpr->BaseExpression;
- if (auto baseTextureType = base->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");
- }
-
- }
- else
- {
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- }
- break;
-
- default:
- SLANG_UNEXPECTED("bad format in intrinsic definition");
- break;
- }
- }
-
- Emit(")");
- }
-
- return;
- }
-
- // If we fall through here, we will treat the call like any other.
- }
- else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
- {
- switch (intrinsicOpModifier->op)
- {
-#define CASE(NAME, OP) case kIROp_##NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
- CASE(Mul, *);
- CASE(Div, / );
- CASE(Mod, %);
- CASE(Add, +);
- CASE(Sub, -);
- CASE(Lsh, << );
- CASE(Rsh, >> );
- CASE(Eql, == );
- CASE(Neq, != );
- CASE(Greater, > );
- CASE(Less, < );
- CASE(Geq, >= );
- CASE(Leq, <= );
- CASE(BitAnd, &);
- CASE(BitXor, ^);
- CASE(BitOr, | );
- CASE(And, &&);
- CASE(Or, || );
-#undef CASE
-
-#define CASE(NAME, OP) case kIRPseudoOp_##NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
- CASE(Assign, =);
- CASE(AddAssign, +=);
- CASE(SubAssign, -=);
- CASE(MulAssign, *=);
- CASE(DivAssign, /=);
- CASE(ModAssign, %=);
- CASE(LshAssign, <<=);
- CASE(RshAssign, >>=);
- CASE(OrAssign, |=);
- CASE(AndAssign, &=);
- CASE(XorAssign, ^=);
-#undef CASE
-
- case kIRPseudoOp_Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return;
-
-#define CASE(NAME, OP) case NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
- CASE(kIRPseudoOp_Pos, +);
- CASE(kIROp_Neg, -);
- CASE(kIROp_Not, !);
- CASE(kIROp_BitNot, ~);
-#undef CASE
-
-#define CASE(NAME, OP) case kIRPseudoOp_##NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
- CASE(PreInc, ++);
- CASE(PreDec, --);
-#undef CASE
-
-#define CASE(NAME, OP) case kIRPseudoOp_##NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return
- CASE(PostInc, ++);
- CASE(PostDec, --);
-#undef CASE
-
- case kIROp_Dot:
- // HLSL allows `mul()` to be used as a synonym for `dot()`,
- // so we need to translate to `dot` for GLSL
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- Emit("dot(");
- EmitExpr(callExpr->Arguments[0]);
- Emit(", ");
- EmitExpr(callExpr->Arguments[1]);
- Emit(")");
- return;
- }
- break;
-
- case kIROp_Mul_Matrix_Matrix:
- case kIROp_Mul_Vector_Matrix:
- case kIROp_Mul_Matrix_Vector:
- // HLSL exposes these with the `mul()` function, while GLSL uses ordinary
- // `operator*`.
- //
- // The other critical detail here is that the way we handle matrix
- // conventions requires that the operands to the product be swapped.
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- Emit("((");
- EmitExpr(callExpr->Arguments[1]);
- Emit(") * (");
- EmitExpr(callExpr->Arguments[0]);
- Emit("))");
- return;
- }
- break;
-
- default:
- break;
- }
-
- // If none of the above matched, then we don't have a specific
- // case implemented to handle the opcode on the callee.
- //
- // We do one more special-case check here, in case the operation
- // is a "subscript" (array indexing) operation, because we'd
- // generally like to reproduce those as array indexing operations
- // in the output if that is how they were written in the input.
- if(auto subscriptDeclRef = funcDeclRef.As<SubscriptDecl>())
- {
- // We expect any subscript operation to be invoked as a member,
- // so the function expression had better be in the correct form.
- if(auto memberExpr = funcExpr.As<MemberExpr>())
- {
-
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(")[");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit("]");
- return;
- }
- }
-
- }
- }
- else if (auto overloadedExpr = funcExpr.As<OverloadedExpr>())
- {
- emitUncheckedCallExpr(callExpr, overloadedExpr->lookupResult2.getName(), arg);
- return;
- }
-
- // Fall through to default handling...
- emitSimpleCallExpr(callExpr, outerPrec);
- }
-
- void visitAggTypeCtorExpr(AggTypeCtorExpr* expr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- emitTypeExp(expr->base);
- Emit("(");
- bool first = true;
- for (auto aa : expr->Arguments)
- {
- if (!first) Emit(", ");
- EmitExpr(aa);
- first = false;
- }
- Emit(")");
-
- if(needClose) Emit(")");
- }
-
- void visitStaticMemberExpr(StaticMemberExpr* memberExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- // TODO(tfoley): figure out a good way to reference
- // declarations that might be generic and/or might
- // not be generated as lexically nested declarations...
-
- // TODO(tfoley): also, probably need to special case
- // this for places where we are using a built-in...
-
- auto base = memberExpr->BaseExpression;
- if (IsBaseExpressionImplicit(base))
- {
- // don't emit the base expression
- }
- else
- {
- EmitExprWithPrecedence(memberExpr->BaseExpression, leftSide(outerPrec, prec));
- Emit(".");
- }
-
- if (!memberExpr->declRef)
- {
- // This case arises when checking didn't find anything, but we were
- // in "rewrite" mode so we blazed ahead anyway.
- emitName(memberExpr->name);
- }
- else
- {
- EmitDeclRef(memberExpr->declRef);
-// emit(memberExpr->declRef.GetName());
- }
-
- if(needClose) Emit(")");
- }
-
- void visitMemberExpr(MemberExpr* memberExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- // TODO(tfoley): figure out a good way to reference
- // declarations that might be generic and/or might
- // not be generated as lexically nested declarations...
-
- // TODO(tfoley): also, probably need to special case
- // this for places where we are using a built-in...
-
- auto base = memberExpr->BaseExpression;
- if (IsBaseExpressionImplicit(base))
- {
- // don't emit the base expression
- }
- else
- {
- EmitExprWithPrecedence(memberExpr->BaseExpression, leftSide(outerPrec, prec));
- Emit(".");
- }
-
- if (!memberExpr->declRef)
- {
- // This case arises when checking didn't find anything, but we were
- // in "rewrite" mode so we blazed ahead anyway.
- emitName(memberExpr->name);
- }
- else
- {
- EmitDeclRef(memberExpr->declRef);
-// emit(memberExpr->declRef.GetName());
- }
-
- if(needClose) Emit(")");
- }
-
- void visitThisExpr(ThisExpr* /*expr*/, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Atomic;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- Emit("this");
-
- if(needClose) Emit(")");
- }
-
- void visitSwizzleExpr(SwizzleExpr* swizExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- EmitExprWithPrecedence(swizExpr->base, leftSide(outerPrec, prec));
- Emit(".");
- static const char* kComponentNames[] = { "x", "y", "z", "w" };
- int elementCount = swizExpr->elementCount;
- for (int ee = 0; ee < elementCount; ++ee)
- {
- Emit(kComponentNames[swizExpr->elementIndices[ee]]);
- }
-
- if(needClose) Emit(")");
- }
-
- void visitIndexExpr(IndexExpr* subscriptExpr, ExprEmitArg const& arg)
- {
- auto prec = kEOp_Postfix;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, prec);
-
- EmitExprWithPrecedence(subscriptExpr->BaseExpression, leftSide(outerPrec, prec));
- Emit("[");
- if (auto indexExpr = subscriptExpr->IndexExpression)
- {
- EmitExpr(indexExpr);
- }
- Emit("]");
-
- if(needClose) Emit(")");
- }
-
- void visitOverloadedExpr(OverloadedExpr* expr, ExprEmitArg const&)
- {
- emitName(expr->lookupResult2.getName());
- }
-
- void visitOverloadedExpr2(OverloadedExpr2* expr, ExprEmitArg const& arg)
- {
- ExprVisitorWithArg<Slang::EmitVisitor, Slang::ExprEmitArg>::dispatch(expr->candidiateExprs[0].Ptr(), arg);
- }
-
void setSampleRateFlag()
{
context->shared->entryPointLayout->flags |= EntryPointLayout::Flag::usesAnySampleRateInput;
}
- void doSampleRateInputCheck(VarDeclBase* decl)
- {
- if (decl->HasModifier<HLSLSampleModifier>())
- {
- setSampleRateFlag();
- }
- }
-
void doSampleRateInputCheck(Name* name)
{
auto text = getText(name);
@@ -2641,398 +1407,10 @@ struct EmitVisitor
}
}
- void visitVarExpr(VarExpr* varExpr, ExprEmitArg const& arg)
- {
- doSampleRateInputCheck(varExpr->name);
-
- auto prec = kEOp_Atomic;
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, kEOp_Atomic);
-
- // TODO: This won't be valid if we had to generate a qualified
- // reference for some reason.
- advanceToSourceLocation(varExpr->loc);
-
- // Because of the "rewriter" use case, it is possible that we will
- // be trying to emit an expression that hasn't been wired up to
- // any associated declaration. In that case, we will just emit
- // the variable name.
- //
- // TODO: A better long-term solution here is to have a distinct
- // case for an "unchecked" `NameExpr` that doesn't include
- // a declaration reference.
-
- if(varExpr->declRef)
- {
- EmitDeclRef(varExpr->declRef);
- }
- else
- {
- emit(varExpr->name);
- }
-
- if(needClose) Emit(")");
- }
-
- void visitDerefExpr(DerefExpr* derefExpr, ExprEmitArg const& arg)
- {
- // TODO(tfoley): dereference shouldn't always be implicit
- ExprVisitorWithArg::dispatch(derefExpr->base, arg);
- }
-
- void visitLiteralExpr(LiteralExpr*, ExprEmitArg const&)
- {
- // Disabling because we no longer use the AST-based emit path.
- //
- // Note: I'm keeping this code around for a while just in case
- // we want to borrow any of the logic here for how to apply
- // suffixes to numeric literals we emit.
- //
-#if 0
- auto outerPrec = arg.outerPrec;
- bool needClose = MaybeEmitParens(outerPrec, kEOp_Atomic);
-
- char const* suffix = "";
- auto type = litExpr->type.type;
- switch (litExpr->ConstType)
- {
- case ConstantExpr::ConstantType::Int:
- if(!type)
- {
- // Special case for "rewrite" mode
- emitTokenWithLocation(litExpr->token);
- break;
- }
- if(type->Equals(getSession()->getIntType()))
- {}
- else if(type->Equals(getSession()->getUIntType()))
- {
- suffix = "u";
- }
- else
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), litExpr, "unhandled type for integer literal");
- }
- Emit(litExpr->integerValue);
- Emit(suffix);
- break;
-
-
- case ConstantExpr::ConstantType::Float:
- if(!type)
- {
- // Special case for "rewrite" mode
- emitTokenWithLocation(litExpr->token);
- break;
- }
- if(type->Equals(getSession()->getFloatType()))
- {}
- else if(type->Equals(getSession()->getDoubleType()))
- {
- suffix = "l";
- }
- else
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), litExpr, "unhandled type for floating-point literal");
- }
- Emit(litExpr->floatingPointValue);
- Emit(suffix);
- break;
-
- case ConstantExpr::ConstantType::Bool:
- Emit(litExpr->integerValue ? "true" : "false");
- break;
- case ConstantExpr::ConstantType::String:
- emitStringLiteral(litExpr->stringValue);
- break;
- default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), litExpr, "unhandled kind of literal expression");
- break;
- }
- if(needClose) Emit(")");
-#endif
- }
-
- void visitTypeCastExpr(TypeCastExpr* castExpr, ExprEmitArg const& arg)
- {
- // We emit a type cast expression as a constructor call
- emitSimpleConstructorCallExpr(castExpr, arg.outerPrec);
-
-#if 0
- bool needClose = false;
- switch(context->shared->target)
- {
- case CodeGenTarget::GLSL:
- // GLSL requires constructor syntax for all conversions
- EmitType(castExpr->type);
- Emit("(");
- EmitExpr(castExpr->Expression);
- Emit(")");
- break;
-
- default:
- // HLSL (and C/C++) prefer cast syntax
- // (In fact, HLSL doesn't allow constructor syntax for some conversions it allows as a cast)
- {
- auto prec = kEOp_Prefix;
- auto outerPrec = arg.outerPrec;
- needClose = MaybeEmitParens(outerPrec, prec);
-
- Emit("(");
- EmitType(castExpr->type);
- Emit(")(");
- EmitExpr(castExpr->Expression);
- Emit(")");
- }
- break;
- }
- if(needClose) Emit(")");
-#endif
- }
-
- void visitInitializerListExpr(InitializerListExpr* expr, ExprEmitArg const&)
- {
- Emit("{ ");
- for(auto& arg : expr->args)
- {
- EmitExpr(arg);
- Emit(", ");
- }
- Emit("}");
- }
-
- //
- // Statements
- //
-
- // Emit a statement as a `{}`-enclosed block statement, but avoid adding redundant
- // curly braces if the statement is itself a block statement.
- void EmitBlockStmt(RefPtr<Stmt> stmt)
- {
- // TODO(tfoley): support indenting
- Emit("{\n");
- if( auto blockStmt = stmt.As<BlockStmt>() )
- {
- EmitStmt(blockStmt->body);
- }
- else
- {
- EmitStmt(stmt);
- }
- Emit("}\n");
- }
-
- void EmitLoopAttributes(RefPtr<Stmt> decl)
- {
- // NOTE: Emit-from-AST is gone, so this code is doing nothing.
- }
-
- void EmitUnparsedStmt(RefPtr<UnparsedStmt> stmt)
- {
- // TODO: actually emit the tokens that made up the statement...
- Emit("{\n");
- for( auto& token : stmt->tokens )
- {
- if (token.type == TokenType::Identifier)
- {
- doSampleRateInputCheck(token.getName());
- }
-
- emitTokenWithLocation(token);
- }
- Emit("}\n");
- }
-
- void EmitStmt(RefPtr<Stmt> stmt)
- {
- // TODO(tfoley): this shouldn't occur, but sometimes
- // lowering will get confused by an empty function body...
- if (!stmt)
- return;
-
- // Try to ensure that debugging can find the right location
- advanceToSourceLocation(stmt->loc);
-
- if (auto blockStmt = stmt.As<BlockStmt>())
- {
- EmitBlockStmt(blockStmt);
- return;
- }
- else if (auto seqStmt = stmt.As<SeqStmt>())
- {
- for (auto ss : seqStmt->stmts)
- {
- EmitStmt(ss);
- }
- return;
- }
- else if( auto unparsedStmt = stmt.As<UnparsedStmt>() )
- {
- EmitUnparsedStmt(unparsedStmt);
- return;
- }
- else if (auto exprStmt = stmt.As<ExpressionStmt>())
- {
- EmitExpr(exprStmt->Expression);
- Emit(";\n");
- return;
- }
- else if (auto returnStmt = stmt.As<ReturnStmt>())
- {
- Emit("return");
- if (auto expr = returnStmt->Expression)
- {
- Emit(" ");
- EmitExpr(expr);
- }
- Emit(";\n");
- return;
- }
- else if (auto declStmt = stmt.As<DeclStmt>())
- {
- EmitDecl(declStmt->decl);
- return;
- }
- else if (auto ifStmt = stmt.As<IfStmt>())
- {
- Emit("if(");
- EmitExpr(ifStmt->Predicate);
- Emit(")\n");
- EmitBlockStmt(ifStmt->PositiveStatement);
- if(auto elseStmt = ifStmt->NegativeStatement)
- {
- Emit("\nelse\n");
- EmitBlockStmt(elseStmt);
- }
- return;
- }
- else if (auto forStmt = stmt.As<ForStmt>())
- {
- // We are going to always take a `for` loop like:
- //
- // for(A; B; C) { D }
- //
- // and emit it as:
- //
- // { A; for(; B; C) { D } }
- //
- // This ensures that we are robust against any kind
- // of statement appearing in `A`, including things
- // that might occur due to lowering steps.
- //
-
- // The one wrinkle is that HLSL implements the
- // bad approach to scoping a `for` loop variable,
- // so we need to avoid those outer `{...}` when
- // we are emitting code that was written in HLSL.
- //
- bool brokenScoping = false;
- if (forStmt.As<UnscopedForStmt>())
- {
- brokenScoping = true;
- }
-
- auto initStmt = forStmt->InitialStatement;
- if(initStmt)
- {
- if(!brokenScoping)
- Emit("{\n");
- EmitStmt(initStmt);
- }
-
- EmitLoopAttributes(forStmt);
-
- Emit("for(;");
- if (auto testExp = forStmt->PredicateExpression)
- {
- EmitExpr(testExp);
- }
- Emit(";");
- if (auto incrExpr = forStmt->SideEffectExpression)
- {
- EmitExpr(incrExpr);
- }
- Emit(")\n");
- EmitBlockStmt(forStmt->Statement);
-
- if (initStmt)
- {
- if(!brokenScoping)
- Emit("}\n");
- }
-
- return;
- }
- else if (auto whileStmt = stmt.As<WhileStmt>())
- {
- EmitLoopAttributes(whileStmt);
-
- Emit("while(");
- EmitExpr(whileStmt->Predicate);
- Emit(")\n");
- EmitBlockStmt(whileStmt->Statement);
- return;
- }
- else if (auto doWhileStmt = stmt.As<DoWhileStmt>())
- {
- EmitLoopAttributes(doWhileStmt);
-
- Emit("do\n");
- EmitBlockStmt(doWhileStmt->Statement);
- Emit(" while(");
- EmitExpr(doWhileStmt->Predicate);
- Emit(");\n");
- return;
- }
- else if (auto discardStmt = stmt.As<DiscardStmt>())
- {
- Emit("discard;\n");
- return;
- }
- else if (auto emptyStmt = stmt.As<EmptyStmt>())
- {
- return;
- }
- else if (auto switchStmt = stmt.As<SwitchStmt>())
- {
- Emit("switch(");
- EmitExpr(switchStmt->condition);
- Emit(")\n");
- EmitBlockStmt(switchStmt->body);
- return;
- }
- else if (auto caseStmt = stmt.As<CaseStmt>())
- {
- Emit("case ");
- EmitExpr(caseStmt->expr);
- Emit(":\n");
- return;
- }
- else if (auto defaultStmt = stmt.As<DefaultStmt>())
- {
- Emit("default:\n");
- return;
- }
- else if (auto breakStmt = stmt.As<BreakStmt>())
- {
- Emit("break;\n");
- return;
- }
- else if (auto continueStmt = stmt.As<ContinueStmt>())
- {
- Emit("continue;\n");
- return;
- }
-
- SLANG_UNEXPECTED("unhandled statement kind");
- }
-
//
// Declaration References
//
- // Declaration References
-
void EmitVal(RefPtr<Val> val)
{
if (auto type = val.As<Type>())
@@ -3103,263 +1481,6 @@ struct EmitVisitor
}
-
- //
- // Declarations
- //
-
- void emitDeclImpl(
- Decl* decl,
- VarLayout* layout)
- {
- // Don't emit code for declarations that came from the stdlib.
- //
- // TODO(tfoley): We probably need to relax this eventually,
- // since different targets might have different sets of builtins.
- if (decl->HasModifier<FromStdLibModifier>())
- return;
-
- // Try to ensure that debugging can find the right location
- advanceToSourceLocation(decl->loc);
-
- DeclEmitArg arg;
- arg.layout = layout;
-
- DeclVisitorWithArg::dispatch(decl, arg);
- }
-
-#define IGNORED(NAME) \
- void visit##NAME(NAME*, DeclEmitArg const&) {}
-
- // Only used by stdlib
- IGNORED(SyntaxDecl)
- IGNORED(AttributeDecl)
-
- // Don't emit generic decls directly; we will only
- // ever emit particular instantiations of them.
- IGNORED(GenericDecl)
- IGNORED(GenericTypeConstraintDecl)
- IGNORED(GenericValueParamDecl)
- IGNORED(GenericTypeParamDecl)
-
- // Not epected to appear (probably dead code)
- IGNORED(ClassDecl)
-
- // Not semantically meaningful for emit, or expected
- // to be lowered out of existence before we get here
- IGNORED(InheritanceDecl)
- IGNORED(ExtensionDecl)
- IGNORED(ScopeDecl)
-
- // Catch-all cases where we handle the types that matter,
- // while others will be lowered out of exitence
- IGNORED(CallableDecl)
- IGNORED(AggTypeDeclBase)
-
- // Should not appear nested inside other decls
- IGNORED(ModuleDecl)
-
-#undef IGNORED
-
- void visitDeclGroup(DeclGroup* declGroup, DeclEmitArg const&)
- {
- for (auto decl : declGroup->decls)
- {
- EmitDecl(decl);
- }
- }
-
- void visitTypeDefDecl(TypeDefDecl* decl, DeclEmitArg const&)
- {
- // Note(tfoley): any `typedef`s should already have been filtered
- // out if we are generating GLSL.
- SLANG_RELEASE_ASSERT(context->shared->target != CodeGenTarget::GLSL);
-
- Emit("typedef ");
- EmitType(decl->type, decl->getNameAndLoc());
- Emit(";\n");
- }
-
- void visitAssocTypeDecl(AssocTypeDecl * /*assocType*/, DeclEmitArg const&)
- {
- SLANG_UNREACHABLE("visitAssocTypeDecl in EmitVisitor");
- }
-
-
- void visitImportDecl(ImportDecl* decl, DeclEmitArg const&)
- {
- // When in "rewriter" mode, we need to emit the code of the imported
- // module in-place at the `import` site.
-
- auto moduleDecl = decl->importedModuleDecl.Ptr();
-
- // We might import the same module along two different paths,
- // so we need to be careful to only emit each module once
- // per output.
- if(!context->shared->modulesAlreadyEmitted.Contains(moduleDecl))
- {
- // Add the module to our set before emitting it, just
- // in case a circular reference would lead us to
- // infinite recursion (but that shouldn't be allowed
- // in the first place).
- context->shared->modulesAlreadyEmitted.Add(moduleDecl);
-
- // TODO: do we need to modify the code generation environment at
- // all when doing this recursive emit?
-
- EmitDeclsInContainerUsingLayout(moduleDecl, context->shared->globalStructLayout);
- }
- }
-
- void visitEmptyDecl(EmptyDecl* decl, DeclEmitArg const&)
- {
- // GLSL uses empty declarations to carry semantically relevant modifiers,
- // so we can't just skip empty declarations in general
-
- EmitModifiers(decl);
- Emit(";\n");
- }
-
- bool shouldSkipModifierForDecl(
- Modifier* modifier,
- Decl* decl)
- {
- switch(context->shared->target)
- {
- default:
- break;
-
- case CodeGenTarget::GLSL:
- {
- // Don't emit interpolation mode modifiers on `struct` fields
- // (only allowed on global or block `in`/`out`)
- if (auto interpolationMod = dynamic_cast<InterpolationModeModifier*>(modifier))
- {
- if (auto fieldDecl = dynamic_cast<StructField*>(decl))
- {
- return true;
- }
- }
-
- }
- break;
- }
-
-
- return false;
- }
-
- // Emit any modifiers that should go in front of a declaration
- void EmitModifiers(RefPtr<Decl> decl)
- {
- // Emit any GLSL `layout` modifiers first
- bool anyLayout = false;
- for( auto mod : decl->GetModifiersOfType<GLSLUnparsedLayoutModifier>())
- {
- if(!anyLayout)
- {
- Emit("layout(");
- anyLayout = true;
- }
- else
- {
- Emit(", ");
- }
-
- emit(mod->getNameAndLoc());
- if(mod->valToken.type != TokenType::Unknown)
- {
- Emit(" = ");
- emit(mod->valToken.Content);
- }
- }
- if(anyLayout)
- {
- Emit(")\n");
- }
-
- for (auto mod = decl->modifiers.first; mod; mod = mod->next)
- {
- if (shouldSkipModifierForDecl(mod, decl))
- continue;
-
- advanceToSourceLocation(mod->loc);
-
- if (0) {}
-
- #define CASE(TYPE, KEYWORD) \
- else if(auto mod_##TYPE = mod.As<TYPE>()) Emit(#KEYWORD " ")
-
- #define CASE2(TYPE, HLSL_NAME, GLSL_NAME) \
- else if(auto mod_##TYPE = mod.As<TYPE>()) Emit((context->shared->target == CodeGenTarget::GLSL) ? (#GLSL_NAME " ") : (#HLSL_NAME " "))
-
- #define CASE2_RAW(TYPE, HLSL_NAME, GLSL_NAME) \
- else if(auto mod_##TYPE = mod.As<TYPE>()) Emit((context->shared->target == CodeGenTarget::GLSL) ? (GLSL_NAME) : (HLSL_NAME))
-
- CASE(RowMajorLayoutModifier, row_major);
- CASE(ColumnMajorLayoutModifier, column_major);
-
- CASE2(HLSLNoInterpolationModifier, nointerpolation, flat);
- CASE(HLSLPreciseModifier, precise);
- CASE(HLSLEffectSharedModifier, shared);
- CASE(HLSLGroupSharedModifier, groupshared);
- CASE(HLSLUniformModifier, uniform);
- CASE(HLSLVolatileModifier, volatile);
-
- CASE(InOutModifier, inout);
- CASE(InModifier, in);
- CASE(OutModifier, out);
-
- CASE(HLSLPointModifier, point);
- CASE(HLSLLineModifier, line);
- CASE(HLSLTriangleModifier, triangle);
- CASE(HLSLLineAdjModifier, lineadj);
- CASE(HLSLTriangleAdjModifier, triangleadj);
-
- CASE2_RAW(HLSLLinearModifier, "linear ", "");
- CASE(HLSLSampleModifier, sample);
- CASE(HLSLCentroidModifier, centroid);
-
- CASE(ConstModifier, const);
-
- #undef CASE
- #undef CASE2
-
- else if (auto staticModifier = mod.As<HLSLStaticModifier>())
- {
- // GLSL does not support the `static` keyword.
- // HLSL uses it both to mark global variables as being "thread-local"
- // (rather than shader inputs), and also seems to support function-`static`
- // variables.
- // The latter case needs to be dealt with in lowering anyway, so that
- // we only need to deal with globals here, and GLSL variables
- // don't need a `static` modifier anyway.
-
- switch(context->shared->target)
- {
- default:
- Emit("static ");
- break;
-
- case CodeGenTarget::GLSL:
- break;
- }
- }
-
- else if(auto simpleModifier = mod.As<SimpleModifier>())
- {
- emit(simpleModifier->getNameAndLoc());
- Emit(" ");
- }
-
- else
- {
- // skip any extra modifiers
- }
- }
- }
-
-
typedef unsigned int ESemanticMask;
enum
{
@@ -3374,39 +1495,16 @@ struct EmitVisitor
{
if (auto simple = semantic.As<HLSLSimpleSemantic>())
{
- Emit(": ");
+ Emit(" : ");
emit(simple->name.Content);
}
else if(auto registerSemantic = semantic.As<HLSLRegisterSemantic>())
{
// Don't print out semantic from the user, since we are going to print the same thing our own way...
- #if 0
- Emit(": register(");
- Emit(registerSemantic->registerName.Content);
- if(registerSemantic->componentMask.type != TokenType::Unknown)
- {
- Emit(".");
- Emit(registerSemantic->componentMask.Content);
- }
- Emit(")");
- #endif
}
else if(auto packOffsetSemantic = semantic.As<HLSLPackOffsetSemantic>())
{
// Don't print out semantic from the user, since we are going to print the same thing our own way...
- #if 0
- if(mask & kESemanticMask_NoPackOffset)
- return;
-
- Emit(": packoffset(");
- Emit(packOffsetSemantic->registerName.Content);
- if(packOffsetSemantic->componentMask.type != TokenType::Unknown)
- {
- Emit(".");
- Emit(packOffsetSemantic->componentMask.Content);
- }
- Emit(")");
- #endif
}
else
{
@@ -3437,99 +1535,6 @@ struct EmitVisitor
}
}
- void EmitDeclsInContainer(RefPtr<ContainerDecl> container)
- {
- for (auto member : container->Members)
- {
- EmitDecl(member);
- }
- }
-
- void EmitDeclsInContainerUsingLayout(
- RefPtr<ContainerDecl> container,
- RefPtr<StructTypeLayout> containerLayout)
- {
- for (auto member : container->Members)
- {
- RefPtr<VarLayout> memberLayout;
- if( containerLayout->mapVarToLayout.TryGetValue(member.Ptr(), memberLayout) )
- {
- EmitDeclUsingLayout(member, memberLayout);
- }
- else
- {
- // No layout for this decl
- EmitDecl(member);
- }
- }
- }
-
- void visitStructDecl(RefPtr<StructDecl> decl, DeclEmitArg const&)
- {
- // Don't emit a declaration that was only generated implicitly, for
- // the purposes of semantic checking.
- if(decl->HasModifier<ImplicitParameterGroupElementTypeModifier>())
- return;
-
- Emit("struct ");
- emitName(decl->getNameAndLoc());
- Emit("\n{\n");
-
- // TODO(tfoley): Need to hoist members functions, etc. out to global scope
- EmitDeclsInContainer(decl);
-
- Emit("};\n");
- }
-
- // Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
-
- void emitVarDeclHead(DeclRef<VarDeclBase> declRef)
- {
- EmitModifiers(declRef.getDecl());
-
- auto type = GetType(declRef);
- if (!type || type->As<ErrorType>())
- {
- EmitType(declRef.getDecl()->type, declRef.getDecl()->getName());
- }
- else
- {
- EmitType(GetType(declRef), declRef.getDecl()->getName());
- }
-
- EmitSemantics(declRef.getDecl());
- }
-
- void emitVarDeclInit(DeclRef<VarDeclBase> declRef)
- {
- // TODO(tfoley): technically have to apply substitution here too...
- if (auto initExpr = declRef.getDecl()->initExpr)
- {
- if (declRef.As<ParamDecl>()
- && context->shared->target == CodeGenTarget::GLSL)
- {
- // Don't emit default parameter values when lowering to GLSL
- }
- else
- {
- Emit(" = ");
- EmitExpr(initExpr);
- }
- }
- }
-
- void EmitVarDeclCommon(DeclRef<VarDeclBase> declRef)
- {
- emitVarDeclHead(declRef);
- emitVarDeclInit(declRef);
- }
-
- // Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
- void EmitVarDeclCommon(RefPtr<VarDeclBase> decl)
- {
- EmitVarDeclCommon(DeclRef<Decl>(decl.Ptr(), nullptr).As<VarDeclBase>());
- }
-
// A chain of variables to use for emitting semantic/layout info
struct EmitVarChain
{
@@ -3610,7 +1615,7 @@ struct EmitVisitor
// units, and then a "component" within that register, based on 4-byte
// offsets from there. We cannot support more fine-grained offsets than that.
- Emit(": ");
+ Emit(" : ");
Emit(uniformSemanticSpelling);
Emit("(c");
@@ -3649,7 +1654,7 @@ struct EmitVisitor
break;
default:
{
- Emit(": register(");
+ Emit(" : register(");
switch( kind )
{
case LayoutResourceKind::ConstantBuffer:
@@ -3714,25 +1719,6 @@ struct EmitVisitor
emitHLSLRegisterSemantics(&chain, uniformSemanticSpelling);
}
- static RefPtr<VarLayout> maybeFetchLayout(
- RefPtr<Decl> decl,
- RefPtr<VarLayout> layout)
- {
- // If we have already found layout info, don't go searching
- if (layout) return layout;
-
- // Otherwise, we need to look and see if computed layout
- // information has been attached to the declaration.
- auto modifier = decl->FindModifier<ComputedLayoutModifier>();
- if (!modifier) return nullptr;
-
- auto computedLayout = modifier->layout;
- SLANG_RELEASE_ASSERT(computedLayout);
-
- auto varLayout = computedLayout.As<VarLayout>();
- return varLayout;
- }
-
void emitHLSLParameterGroupFieldLayoutSemantics(
EmitVarChain* chain)
{
@@ -3755,160 +1741,6 @@ struct EmitVisitor
emitHLSLParameterGroupFieldLayoutSemantics(&chain);
}
- void emitHLSLParameterBlockDecl(
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
- RefPtr<VarLayout> varLayout)
- {
- EmitVarChain blockChain(varLayout);
-
- Emit("cbuffer ");
-
- emitName(varDecl);
-
- // We expect to always have layout information
- varLayout = maybeFetchLayout(varDecl, varLayout);
- SLANG_RELEASE_ASSERT(varLayout);
-
- // We expect the layout to be for a parameter group type...
- RefPtr<ParameterGroupTypeLayout> bufferLayout = varLayout->typeLayout.As<ParameterGroupTypeLayout>();
- SLANG_RELEASE_ASSERT(bufferLayout);
-
- RefPtr<VarLayout> containerVarLayout = bufferLayout->containerVarLayout;
- EmitVarChain containerChain(containerVarLayout, &blockChain);
-
- RefPtr<VarLayout> elementVarLayout = bufferLayout->elementVarLayout;
- EmitVarChain elementChain(elementVarLayout, &blockChain);
-
- EmitSemantics(varDecl, kESemanticMask_None);
-
- emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain);
-
- Emit("\n{\n");
-
- // The user wrote this declaration as, e.g.:
- //
- // ParameterBlock<Foo> gFoo;
- //
- // and we are desugaring it into something like:
- //
- // cbuffer anon0 { Foo gFoo; }
- //
-
- RefPtr<Type> elementType = parameterBlockType->elementType;
-
- EmitType(elementType, varDecl->getName());
-
- // The layout for the field ends up coming from the layout
- // for the parameter block as a whole.
- emitHLSLParameterGroupFieldLayoutSemantics(&elementChain);
-
- Emit(";\n");
- Emit("}\n");
- }
-
- void emitHLSLParameterGroupDecl(
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterGroupType> parameterGroupType,
- RefPtr<VarLayout> varLayout)
- {
- if( auto parameterBlockType = parameterGroupType->As<ParameterBlockType>())
- {
- emitHLSLParameterBlockDecl(varDecl, parameterBlockType, varLayout);
- return;
- }
- if( auto textureBufferType = parameterGroupType->As<TextureBufferType>() )
- {
- Emit("tbuffer ");
- }
- else
- {
- Emit("cbuffer ");
- }
-
- EmitVarChain blockChain(varLayout);
-
- // The data type that describes where stuff in the constant buffer should go
- RefPtr<Type> dataType = parameterGroupType->elementType;
-
- // We expect to always have layout information
- varLayout = maybeFetchLayout(varDecl, varLayout);
- SLANG_RELEASE_ASSERT(varLayout);
-
- // We expect the layout to be for a structured type...
- RefPtr<ParameterGroupTypeLayout> bufferLayout = varLayout->typeLayout.As<ParameterGroupTypeLayout>();
- SLANG_RELEASE_ASSERT(bufferLayout);
-
- auto containerVarLayout = bufferLayout->containerVarLayout;
- EmitVarChain containerChain(containerVarLayout, &blockChain);
-
- auto elementVarLayout = bufferLayout->elementVarLayout;
- EmitVarChain elementChain(elementVarLayout, &blockChain);
-
- RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementVarLayout->typeLayout.As<StructTypeLayout>();
- SLANG_RELEASE_ASSERT(structTypeLayout);
-
-
- Emit(" ");
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>() )
- {
- emitName(reflectionNameModifier->nameAndLoc);
- }
- else
- {
- emitName(varDecl->nameAndLoc);
- }
-
- EmitSemantics(varDecl, kESemanticMask_None);
-
- emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain);
-
- Emit("\n{\n");
-
- // We expect the data type to be a user-defined `struct` type,
- // but it might also be a "filtered" type that represents the
- // case where only some fields of the original type are valid
- // to appear inside of a `struct`.
- if (auto declRefType = dataType->As<DeclRefType>())
- {
- if (auto structRef = declRefType->declRef.As<StructDecl>())
- {
- int fieldCounter = 0;
-
- for (auto field : getMembersOfType<StructField>(structRef))
- {
- int fieldIndex = fieldCounter++;
-
- // Skip fields that have `void` type, since these represent
- // declarations that got legalized out of existence.
- if(GetType(field)->Equals(getSession()->getVoidType()))
- continue;
-
- emitVarDeclHead(field);
-
- RefPtr<VarLayout> fieldLayout = structTypeLayout->fields[fieldIndex];
- SLANG_RELEASE_ASSERT(fieldLayout->varDecl.GetName() == field.GetName());
-
- // Emit explicit layout annotations for every field
- emitHLSLParameterGroupFieldLayoutSemantics(fieldLayout, &elementChain);
-
- emitVarDeclInit(field);
-
- Emit(";\n");
- }
- }
- else
- {
- SLANG_UNEXPECTED("unexpected element type for parameter group");
- }
- }
- else
- {
- SLANG_UNEXPECTED("unexpected element type for parameter group");
- }
- Emit("}\n");
- }
-
void emitGLSLLayoutQualifier(
LayoutResourceKind kind,
EmitVarChain* chain)
@@ -4019,355 +1851,10 @@ struct EmitVisitor
}
}
- void emitGLSLParameterBlockDecl(
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
- RefPtr<VarLayout> varLayout)
- {
- EmitVarChain blockChain(varLayout);
-
- RefPtr<ParameterGroupTypeLayout> bufferLayout = varLayout->typeLayout.As<ParameterGroupTypeLayout>();
- SLANG_RELEASE_ASSERT(bufferLayout);
-
- auto containerVarLayout = bufferLayout->containerVarLayout;
- EmitVarChain containerChain(containerVarLayout, &blockChain);
-
- auto elementVarLayout = bufferLayout->elementVarLayout;
- EmitVarChain elementChain(elementVarLayout, &blockChain);
-
- EmitModifiers(varDecl);
- emitGLSLLayoutQualifiers(containerVarLayout, &blockChain);
- Emit("uniform ");
-
- emitName(varDecl);
-
- Emit("\n{\n");
-
-
- RefPtr<Type> elementType = parameterBlockType->elementType;
-
- EmitType(elementType, varDecl->getName());
- Emit(";\n");
-
- Emit("};\n");
- }
-
- void emitGLSLParameterGroupDecl(
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterGroupType> parameterGroupType,
- RefPtr<VarLayout> varLayout)
- {
- if( auto parameterBlockType = parameterGroupType->As<ParameterBlockType>())
- {
- emitGLSLParameterBlockDecl(varDecl, parameterBlockType, varLayout);
- return;
- }
-
- // The data type that describes where stuff in the constant buffer should go
- RefPtr<Type> dataType = parameterGroupType->elementType;
-
- // We expect the layout, if present, to be for a structured type...
- RefPtr<StructTypeLayout> structTypeLayout;
-
- EmitVarChain blockChain;
- if (varLayout)
- {
- blockChain = EmitVarChain(varLayout);
-
- auto typeLayout = varLayout->typeLayout;
- if (auto bufferLayout = typeLayout.As<ParameterGroupTypeLayout>())
- {
- typeLayout = bufferLayout->elementVarLayout->getTypeLayout();
-
- emitGLSLLayoutQualifiers(bufferLayout->containerVarLayout, &blockChain);
- }
- else
- {
- // Fallback: we somehow have a messed up layout
- emitGLSLLayoutQualifiers(varLayout, nullptr);
- }
-
- // We expect the element type to be structured.
- structTypeLayout = typeLayout.As<StructTypeLayout>();
- SLANG_RELEASE_ASSERT(structTypeLayout);
- }
-
-
- EmitModifiers(varDecl);
-
- // Emit an apprpriate declaration keyword based on the kind of block
- if (parameterGroupType->As<GLSLShaderStorageBufferType>())
- {
- Emit("buffer");
- }
- // Note: tested `buffer` case before `uniform`, since `GLSLShaderStorageBufferType`
- // is also a subclass of `UniformParameterGroupType`.
- else if (parameterGroupType->As<UniformParameterGroupType>())
- {
- Emit("uniform");
- }
- else if (parameterGroupType->As<GLSLInputParameterGroupType>())
- {
- Emit("in");
- }
- else if (parameterGroupType->As<GLSLOutputParameterGroupType>())
- {
- Emit("out");
- }
- else
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), varDecl, "unhandled GLSL shader parameter kind");
- Emit("uniform");
- }
-
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>() )
- {
- Emit(" ");
- emitName(reflectionNameModifier->nameAndLoc);
- }
-
- Emit("\n{\n");
-
- // We expect the data type to be a user-defined `struct` type,
- // but it might also be a "filtered" type that represents the
- // case where only some fields of the original type are valid
- // to appear inside of a `struct`.
- if (auto declRefType = dataType->As<DeclRefType>())
- {
-
- if (auto structRef = declRefType->declRef.As<StructDecl>())
- {
- int fieldCounter = 0;
- for (auto field : getMembersOfType<StructField>(structRef))
- {
- int fieldIndex = fieldCounter++;
-
- // Skip fields that have `void` type, since these represent
- // declarations that got legalized out of existence.
- if(GetType(field)->Equals(getSession()->getVoidType()))
- continue;
-
- if (structTypeLayout)
- {
- RefPtr<VarLayout> fieldLayout = structTypeLayout->fields[fieldIndex];
- // assert(fieldLayout);
-
- // TODO(tfoley): We may want to emit *some* of these,
- // some of the time...
- // emitGLSLLayoutQualifiers(fieldLayout);
- }
-
-
- EmitVarDeclCommon(field);
-
- Emit(";\n");
- }
- }
- else
- {
- SLANG_UNEXPECTED("unexpected element type for parameter group");
- }
- }
- else
- {
- SLANG_UNEXPECTED("unexpected element type for parameter group");
- }
-
-
-
- Emit("}");
-
- if( varDecl->getNameLoc().isValid() )
- {
- Emit(" ");
- emitName(varDecl->getName());
- }
-
- Emit(";\n");
- }
-
- void emitParameterGroupDecl(
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterGroupType> parameterGroupType,
- RefPtr<VarLayout> layout)
- {
- switch(context->shared->target)
- {
- case CodeGenTarget::HLSL:
- emitHLSLParameterGroupDecl(varDecl, parameterGroupType, layout);
- break;
-
- case CodeGenTarget::GLSL:
- emitGLSLParameterGroupDecl(varDecl, parameterGroupType, layout);
- break;
-
- default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), varDecl, "unhandled code generation target");
- break;
- }
- }
-
- void visitVarDeclBase(RefPtr<VarDeclBase> decl, DeclEmitArg const& arg)
- {
- // Global variable? Check if it is a sample-rate input.
- if (dynamic_cast<ModuleDecl*>(decl->ParentDecl))
- {
- if (decl->HasModifier<InModifier>())
- {
- doSampleRateInputCheck(decl);
- }
- }
-
- // Skip fields that have `void` type, since these may be introduced
- // as part of type leglaization.
- if(decl->getType()->Equals(getSession()->getVoidType()))
- return;
-
- RefPtr<VarLayout> layout = arg.layout;
- layout = maybeFetchLayout(decl, layout);
-
- // As a special case, a variable using a parameter block type
- // will be translated into a declaration using the more primitive
- // language syntax.
- //
- // TODO(tfoley): Be sure to unwrap arrays here, in the GLSL case.
- //
- // TODO(tfoley): Detect cases where we need to fall back to
- // ordinary variable declaration syntax in HLSL.
- //
- // TODO(tfoley): there might be a better way to detect this, e.g.,
- // with an attribute that gets attached to the variable declaration.
- if (auto parameterGroupType = decl->type->As<ParameterGroupType>())
- {
- emitParameterGroupDecl(decl, parameterGroupType, layout);
- return;
- }
-
-
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- if (decl->HasModifier<InModifier>())
- {
- emitGLSLLayoutQualifiers(layout, nullptr, LayoutResourceKind::VertexInput);
- }
- else if (decl->HasModifier<OutModifier>())
- {
- emitGLSLLayoutQualifiers(layout, nullptr, LayoutResourceKind::FragmentOutput);
- }
- else
- {
- emitGLSLLayoutQualifiers(layout, nullptr);
- }
-
- // If we have a uniform that wasn't tagged `uniform` in GLSL, then fix that here
- if (layout
- && !decl->HasModifier<HLSLUniformModifier>())
- {
- if (layout->FindResourceInfo(LayoutResourceKind::Uniform)
- || layout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot))
- {
- Emit("uniform ");
- }
- }
- }
-
- emitVarDeclHead(makeDeclRef(decl.Ptr()));
- emitHLSLRegisterSemantics(layout);
- emitVarDeclInit(makeDeclRef(decl.Ptr()));
-
- Emit(";\n");
- }
-
- void EmitParamDecl(RefPtr<ParamDecl> decl)
- {
- EmitVarDeclCommon(decl);
- }
-
- void visitFuncDecl(RefPtr<FuncDecl> decl, DeclEmitArg const&)
- {
- EmitModifiers(decl);
-
- // TODO: if a function returns an array type, or something similar that
- // isn't allowed by declarator syntax and/or language rules, we could
- // hypothetically wrap things in a `typedef` and work around it.
-
- EmitType(decl->ReturnType, decl->getNameAndLoc());
-
- Emit("(");
- bool first = true;
- for (auto paramDecl : decl->getMembersOfType<ParamDecl>())
- {
- if (!first) Emit(", ");
- EmitParamDecl(paramDecl);
- first = false;
- }
- Emit(")");
-
- EmitSemantics(decl);
-
- if (auto bodyStmt = decl->Body)
- {
- EmitBlockStmt(bodyStmt);
- }
- else
- {
- Emit(";\n");
- }
- }
void emitGLSLVersionDirective(
ModuleDecl* /*program*/)
{
-#ifdef DEADCODE
- // Did the user provide an explicit `#version` directive in their code?
- if( auto versionDirective = program->FindModifier<GLSLVersionDirective>() )
- {
- // TODO(tfoley): Emit an appropriate `#line` directive...
-
- Emit("#version ");
- emit(versionDirective->versionNumberToken.Content);
- if(versionDirective->glslProfileToken.type != TokenType::Unknown)
- {
- Emit(" ");
- emit(versionDirective->glslProfileToken.Content);
- }
- Emit("\n");
- return;
- }
-
- // No explicit version was given. This could be because we are cross-compiling,
- // but it also might just be that they user wrote GLSL without thinking about
- // versions.
-
- // First, look and see if the target profile gives us a version to use:
- auto profile = context->shared->entryPoint->profile;
- if (profile.getFamily() == ProfileFamily::GLSL)
- {
- switch (profile.GetVersion())
- {
-#define CASE(TAG, VALUE) \
- case ProfileVersion::TAG: Emit("#version " #VALUE "\n"); return
-
- CASE(GLSL_110, 110);
- CASE(GLSL_120, 120);
- CASE(GLSL_130, 130);
- CASE(GLSL_140, 140);
- CASE(GLSL_150, 150);
- CASE(GLSL_330, 330);
- CASE(GLSL_400, 400);
- CASE(GLSL_410, 410);
- CASE(GLSL_420, 420);
- CASE(GLSL_430, 430);
- CASE(GLSL_440, 440);
- CASE(GLSL_450, 450);
-#undef CASE
-
- default:
- break;
- }
- }
-#endif
-
auto effectiveProfile = context->shared->effectiveProfile;
if(effectiveProfile.getFamily() == ProfileFamily::GLSL)
{
@@ -4453,33 +1940,6 @@ struct EmitVisitor
// TODO: handle other cases...
}
- void EmitDecl(RefPtr<Decl> decl)
- {
-emitDeclImpl(decl, nullptr);
- }
-
- void EmitDeclUsingLayout(RefPtr<Decl> decl, RefPtr<VarLayout> layout)
- {
- emitDeclImpl(decl, layout);
- }
-
- void EmitDecl(RefPtr<DeclBase> declBase)
- {
- if( auto decl = declBase.As<Decl>() )
- {
- EmitDecl(decl);
- }
- else if( auto declGroup = declBase.As<DeclGroup>() )
- {
- for( auto d : declGroup->decls )
- EmitDecl(d);
- }
- else
- {
- SLANG_UNEXPECTED("unhandled declaration kind");
- }
- }
-
// Utility code for generating unique IDs as needed
// during the emit process (e.g., for declarations
// that didn't origianlly have names, but now need to).
@@ -4696,227 +2156,11 @@ emitDeclImpl(decl, nullptr);
}
-#if 0
- void emitIRSimpleType(
- EmitContext* context,
- IRType* type)
- {
- switch(type->op)
- {
- #define CASE(ID, NAME) \
- case kIROp_##ID: emit(#NAME); break
-
- CASE(Float32Type, float);
- CASE(Int32Type, int);
- CASE(UInt32Type, uint);
- CASE(VoidType, void);
- CASE(BoolType, bool);
-
- #undef CASE
-
-
- case kIROp_VectorType:
- emitIRVectorType(context, (IRVectorType*) type);
- break;
-
- case kIROp_MatrixType:
- emitIRMatrixType(context, (IRMatrixType*) type);
- break;
-
- case kIROp_StructType:
- emit(getName(type));
- break;
-
- case kIROp_TextureType:
- {
- auto textureType = (IRTextureType*) type;
-
- switch (context->shared->target)
- {
- case CodeGenTarget::HLSL:
- // TODO: actually look at the flavor and emit the right name
- emit("Texture2D");
- emit("<");
- emitIRType(context, textureType->getElementType(), nullptr);
- emit(">");
- break;
-
- case CodeGenTarget::GLSL:
- // TODO: actually look at the flavor and emit the right name
- // TODO: look at element type to emit the right prefix
- emit("texture2D");
- break;
-
- default:
- SLANG_UNEXPECTED("codegen target");
- break;
- }
- }
- break;
-
- case kIROp_ConstantBufferType:
- {
- auto tt = (IRConstantBufferType*) type;
- emit("ConstantBuffer<");
- emitIRType(context, tt->getElementType(), nullptr);
- emit(">");
- }
- break;
-
- case kIROp_TextureBufferType:
- {
- auto tt = (IRTextureBufferType*) type;
- emit("ConstantBuffer<");
- emitIRType(context, tt->getElementType(), nullptr);
- emit(">");
- }
- break;
-
- case kIROp_readWriteStructuredBufferType:
- {
- auto tt = (IRBufferType*) type;
- emit("RWStructuredBuffer<");
- emitIRType(context, tt->getElementType(), nullptr);
- emit(">");
- }
- break;
-
- case kIROp_structuredBufferType:
- {
- auto tt = (IRBufferType*) type;
- emit("StructuredBuffer<");
- emitIRType(context, tt->getElementType(), nullptr);
- emit(">");
- }
- break;
-
- case kIROp_SamplerType:
- {
- // TODO: actually look at the flavor and emit the right name
- emit("SamplerState");
- }
- break;
-
- case kIROp_TypeType:
- {
- // Note: this should actually be an error case, since
- // type-level operands shouldn't be exposed in generated
- // code, but I'm allowing this now to make the output
- // a bit more clear.
- emit("Type");
- }
- break;
-
-
- default:
- SLANG_UNIMPLEMENTED_X("type case for emit");
- break;
- }
-
- }
-#endif
-
CodeGenTarget getTarget(EmitContext* ctx)
{
return ctx->shared->target;
}
-#if 0
- void emitGLSLTypePrefix(
- EmitContext* context,
- IRType* type)
- {
- switch(type->op)
- {
- case kIROp_UInt32Type:
- emit("u");
- break;
-
- case kIROp_Int32Type:
- emit("i");
- break;
-
- case kIROp_BoolType:
- emit("b");
- break;
-
- case kIROp_Float32Type:
- // no prefix
- break;
-
- case kIROp_Float64Type:
- emit("d");
- break;
-
- // TODO: we should handle vector and matrix types here
- // and recurse into them to find the elemnt type,
- // just as a convenience.
-
- default:
- SLANG_UNEXPECTED("case for GLSL type prefix");
- break;
- }
- }
-#endif
-
-#if 0
- void emitIRType(
- EmitContext* context,
- IRType* type,
- IRDeclaratorInfo* declarator)
- {
- switch (type->op)
- {
- case kIROp_PtrType:
- {
- auto ptrType = (IRPtrType*)type;
-
- IRDeclaratorInfo ptrDeclarator;
- ptrDeclarator.flavor = IRDeclaratorInfo::Flavor::Ptr;
- ptrDeclarator.next = declarator;
- emitIRType(context, ptrType->getValueType(), &ptrDeclarator);
- }
- break;
-
- case kIROp_arrayType:
- {
- auto arrayType = (IRArrayType*)type;
-
- IRDeclaratorInfo arrayDeclarator;
- arrayDeclarator.flavor = IRDeclaratorInfo::Flavor::Array;
- arrayDeclarator.elementCount = arrayType->getElementCount();
- arrayDeclarator.next = declarator;
- emitIRType(context, arrayType->getElementType(), &arrayDeclarator);
- }
- break;
-
- default:
- emitIRSimpleType(context, type);
- emitDeclarator(context, declarator);
- break;
- }
- }
-
- void emitIRType(
- EmitContext* context,
- IRType* type,
- String const& name)
- {
- IRDeclaratorInfo declarator;
- declarator.flavor = IRDeclaratorInfo::Flavor::Simple;
- declarator.name = &name;
-
- emitIRType(context, type, &declarator);
- }
-
- void emitIRType(
- EmitContext* context,
- IRType* type)
- {
- emitIRType(context, type, (IRDeclaratorInfo*) nullptr);
- }
-#endif
-
// Hack to allow IR emit for global constant to override behavior
enum class IREmitMode
{
@@ -6357,21 +3601,25 @@ emitDeclImpl(decl, nullptr);
emit("if(");
emitIROperand(ctx, t->getCondition(), IREmitMode::Default);
emit(")\n{\n");
+ indent();
emitIRStmtsForBlocks(
ctx,
trueBlock,
afterBlock,
labels);
+ dedent();
emit("}\n");
// Don't emit the false block if it would be empty
if(falseBlock != afterBlock)
{
emit("else\n{\n");
+ indent();
emitIRStmtsForBlocks(
ctx,
falseBlock,
afterBlock,
labels);
+ dedent();
emit("}\n");
}
@@ -6433,7 +3681,7 @@ emitDeclImpl(decl, nullptr);
}
emit("for(;;)\n{\n");
-
+ indent();
emitIRStmtsForBlocks(
ctx,
targetBlock,
@@ -6442,7 +3690,7 @@ emitDeclImpl(decl, nullptr);
&subBreakLabel,
// After the first block, we can safely use the `continue` label too
&subContinueLabel);
-
+ dedent();
emit("}\n");
// Continue with the block after the loop
@@ -6668,9 +3916,13 @@ emitDeclImpl(decl, nullptr);
}
// Now emit the statements for this case.
+ indent();
emit("{\n");
+ indent();
emitIRStmtsForBlocks(ctx, caseLabel, caseEndLabel, &subLabels);
+ dedent();
emit("}\n");
+ dedent();
}
// If we've gone through all the cases and haven't
@@ -6679,10 +3931,14 @@ emitDeclImpl(decl, nullptr);
if(!defaultLabelHandled)
{
emit("default:\n");
+ indent();
emit("{\n");
+ indent();
emitIRStmtsForBlocks(ctx, defaultLabel, breakLabel, &subLabels);
emit("break;\n");
+ dedent();
emit("}\n");
+ dedent();
}
emit("}\n");
@@ -6968,6 +4224,10 @@ emitDeclImpl(decl, nullptr);
{
auto resultType = func->getResultType();
+ // Put a newline before the function so that
+ // the output will be more readable.
+ emit("\n");
+
// Deal with decorations that need
// to be emitted as attributes
auto entryPointLayout = asEntryPoint(func);
@@ -7019,6 +4279,7 @@ emitDeclImpl(decl, nullptr);
if(isDefinition(func))
{
emit("\n{\n");
+ indent();
// HACK: forward-declare all the local variables needed for the
// prameters of non-entry blocks.
@@ -7028,6 +4289,7 @@ emitDeclImpl(decl, nullptr);
emitIRStmtsForBlocks(ctx, func->getFirstBlock(), nullptr, nullptr);
+ dedent();
emit("}\n");
}
else
@@ -7179,9 +4441,6 @@ emitDeclImpl(decl, nullptr);
{
if(func->getGenericDecl())
{
- Emit("/* ");
- emitIRFuncDecl(ctx, func);
- Emit(" */\n");
return;
}
@@ -7390,6 +4649,7 @@ emitDeclImpl(decl, nullptr);
emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain);
emit("\n{\n");
+ indent();
auto elementType = type->getElementType();
@@ -7399,6 +4659,7 @@ emitDeclImpl(decl, nullptr);
emitHLSLParameterGroupFieldLayoutSemantics(&elementChain);
emit(";\n");
+ dedent();
emit("}\n");
}
@@ -7436,6 +4697,7 @@ emitDeclImpl(decl, nullptr);
emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain);
emit("\n{\n");
+ indent();
auto elementType = type->getElementType();
@@ -7481,6 +4743,7 @@ emitDeclImpl(decl, nullptr);
emit("/* unexpected */");
}
+ dedent();
emit("}\n");
}
@@ -7514,12 +4777,14 @@ emitDeclImpl(decl, nullptr);
Emit(ctx->shared->uniqueIDCounter++);
emit("\n{\n");
+ indent();
auto elementType = type->getElementType();
emitIRType(ctx, elementType, getIRName(varDecl));
emit(";\n");
+ dedent();
emit("};\n");
}
@@ -7566,6 +4831,7 @@ emitDeclImpl(decl, nullptr);
emit(getIRName(varDecl));
emit("\n{\n");
+ indent();
auto elementType = type->getElementType();
@@ -7627,6 +4893,7 @@ emitDeclImpl(decl, nullptr);
// the semantics of things, and will reduce the risk of
// collisions in the global namespace...
+ dedent();
emit("};\n");
}
@@ -7723,12 +4990,14 @@ emitDeclImpl(decl, nullptr);
Emit(ctx->shared->uniqueIDCounter++);
emit(" {\n");
+ indent();
auto elementType = structuredBufferType->getElementType();
emitIRType(ctx, elementType, getIRName(varDecl) + "[]");
emit(";\n");
+ dedent();
emit("}");
// TODO: we need to consider the case where the type of the variable is
@@ -7759,12 +5028,18 @@ emitDeclImpl(decl, nullptr);
initFuncName = getIRName(varDecl);
initFuncName.append("_init");
+
+ emit("\n");
emitIRType(ctx, varType, initFuncName);
Emit("()\n{\n");
+ indent();
emitIRStmtsForBlocks(ctx, varDecl->getFirstBlock(), nullptr, nullptr);
+ dedent();
Emit("}\n");
}
+ // Emit a blank line so that the formatting is nicer.
+ emit("\n");
if (auto paramBlockType = varType->As<UniformParameterGroupType>())
{
@@ -7945,9 +5220,10 @@ emitDeclImpl(decl, nullptr);
if(declRef.getDecl()->HasModifier<BuiltinModifier>())
return;
- Emit("struct ");
+ Emit("\nstruct ");
EmitDeclRef(declRef);
Emit("\n{\n");
+ indent();
for( auto ff : GetFields(declRef) )
{
if(ff.getDecl()->HasModifier<HLSLStaticModifier>())
@@ -7965,6 +5241,7 @@ emitDeclImpl(decl, nullptr);
emit(";\n");
}
+ dedent();
Emit("};\n");
}