summaryrefslogtreecommitdiffstats
path: root/source/slang/emit.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-06-09 11:34:21 -0700
committerTim Foley <tfoley@nvidia.com>2017-06-09 13:44:59 -0700
commitfcf83dbf9effab3bd98bad2b83b2468b7eb05cfd (patch)
tree41047c94883b86ec085a81597391ce3ef557cd43 /source/slang/emit.cpp
parent52e8d4b9a27ab0060f874c3a63ab531847be35c0 (diff)
Initial import of code.
Diffstat (limited to 'source/slang/emit.cpp')
-rw-r--r--source/slang/emit.cpp2537
1 files changed, 2537 insertions, 0 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
new file mode 100644
index 000000000..6b488a68f
--- /dev/null
+++ b/source/slang/emit.cpp
@@ -0,0 +1,2537 @@
+// emit.cpp
+#include "emit.h"
+
+#include "syntax.h"
+#include "type-layout.h"
+
+#include <assert.h>
+
+#ifdef _WIN32
+#include <d3dcompiler.h>
+#pragma warning(disable:4996)
+#endif
+
+namespace Slang { namespace Compiler {
+
+struct EmitContext
+{
+ StringBuilder sb;
+
+ // Current source position for tracking purposes...
+ CodePosition loc;
+
+ // The target language we want to generate code for
+ CodeGenTarget target;
+
+ // A set of words reserved by the target
+ Dictionary<String, String> reservedWords;
+};
+
+//
+
+static void EmitDecl(EmitContext* context, RefPtr<Decl> decl);
+static void EmitDecl(EmitContext* context, RefPtr<DeclBase> declBase);
+static void EmitDeclUsingLayout(EmitContext* context, RefPtr<Decl> decl, RefPtr<VarLayout> layout);
+static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, String const& name);
+static void EmitType(EmitContext* context, RefPtr<ExpressionType> type);
+static void EmitExpr(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr);
+static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt);
+static void EmitDeclRef(EmitContext* context, DeclRef declRef);
+
+// Low-level emit logic
+
+static void emitRawTextSpan(EmitContext* context, char const* textBegin, char const* textEnd)
+{
+ // TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things...
+ auto len = int(textEnd - textBegin);
+
+ context->sb.Append(textBegin, len);
+}
+
+static void emitRawText(EmitContext* context, char const* text)
+{
+ emitRawTextSpan(context, text, text + strlen(text));
+}
+
+static void emitTextSpan(EmitContext* context, char const* textBegin, char const* textEnd)
+{
+ // Emit the raw text
+ emitRawTextSpan(context, 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->loc.Col += len;
+}
+
+static void Emit(EmitContext* context, char const* textBegin, char const* textEnd)
+{
+ char const* spanBegin = textBegin;
+
+ char const* spanEnd = spanBegin;
+ for(;;)
+ {
+ if(spanEnd == textEnd)
+ {
+ // We have a whole range of text waiting to be flushed
+ emitTextSpan(context, spanBegin, spanEnd);
+ return;
+ }
+
+ auto c = *spanEnd++;
+
+ if( c == '\n' )
+ {
+ // At the end of a line, we need to update our tracking
+ // information on code positions
+ emitTextSpan(context, spanBegin, spanEnd);
+ context->loc.Line++;
+ context->loc.Col = 1;
+
+ // Start a new span for emit purposes
+ spanBegin = spanEnd;
+ }
+ }
+}
+
+static void Emit(EmitContext* context, char const* text)
+{
+ Emit(context, text, text + strlen(text));
+}
+
+static void emit(EmitContext* context, String const& text)
+{
+ Emit(context, text.begin(), text.end());
+}
+
+static bool isReservedWord(EmitContext* context, String const& name)
+{
+ return context->reservedWords.TryGetValue(name) != nullptr;
+}
+
+static void emitName(EmitContext* context, String const& inName)
+{
+ String name = inName;
+
+ // By default, we would like to emit a name in the generated
+ // code exactly as it appeared in the soriginal program.
+ // When that isn't possible, we'd like to emit a name as
+ // close to the original as possible (to ensure that existing
+ // debugging tools still work reasonably well).
+ //
+ // One reason why a name might not be allowed as-is is that
+ // it could collide with a reserved word in the target language.
+ // Another reason is that it might not follow a naming convention
+ // imposed by the target (e.g., in GLSL names starting with
+ // `gl_` or containing `__` are reserved).
+ //
+ // Given a name that should not be allowed, we want to
+ // change it to a name that *is* allowed. e.g., by adding
+ // `_` to the end of a reserved word.
+ //
+ // The next problem this creates is that the modified name
+ // could not collide with an existing use of the same
+ // (valid) name.
+ //
+ // For now we are going to solve this problem in a simple
+ // and ad hoc fashion, but longer term we'll want to do
+ // something sytematic.
+
+ if (isReservedWord(context, name))
+ {
+ name = name + "_";
+ }
+
+ emit(context, name);
+}
+
+static void Emit(EmitContext* context, UInt value)
+{
+ char buffer[32];
+ sprintf(buffer, "%llu", (unsigned long long)(value));
+ Emit(context, buffer);
+}
+
+static void Emit(EmitContext* context, int value)
+{
+ char buffer[16];
+ sprintf(buffer, "%d", value);
+ Emit(context, buffer);
+}
+
+static void Emit(EmitContext* context, double value)
+{
+ // TODO(tfoley): need to print things in a way that can round-trip
+ char buffer[128];
+ sprintf(buffer, "%.20ff", value);
+ Emit(context, buffer);
+}
+
+// Expressions
+
+// Determine if an expression should not be emitted when it is the base of
+// a member reference expression.
+static bool IsBaseExpressionImplicit(EmitContext* /*context*/, RefPtr<ExpressionSyntaxNode> expr)
+{
+ // HACK(tfoley): For now, anything with a constant-buffer type should be
+ // left implicit.
+
+ // Look through any dereferencing that took place
+ RefPtr<ExpressionSyntaxNode> e = expr;
+ while (auto derefExpr = e.As<DerefExpr>())
+ {
+ e = derefExpr->base;
+ }
+ // Is the expression referencing a constant buffer?
+ if (auto cbufferType = e->Type->As<ConstantBufferType>())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+enum
+{
+ kPrecedence_None,
+ kPrecedence_Comma,
+
+ kPrecedence_Assign,
+ kPrecedence_AddAssign = kPrecedence_Assign,
+ kPrecedence_SubAssign = kPrecedence_Assign,
+ kPrecedence_MulAssign = kPrecedence_Assign,
+ kPrecedence_DivAssign = kPrecedence_Assign,
+ kPrecedence_ModAssign = kPrecedence_Assign,
+ kPrecedence_LshAssign = kPrecedence_Assign,
+ kPrecedence_RshAssign = kPrecedence_Assign,
+ kPrecedence_OrAssign = kPrecedence_Assign,
+ kPrecedence_AndAssign = kPrecedence_Assign,
+ kPrecedence_XorAssign = kPrecedence_Assign,
+
+ kPrecedence_General = kPrecedence_Assign,
+
+ kPrecedence_Conditional, // "ternary"
+ kPrecedence_Or,
+ kPrecedence_And,
+ kPrecedence_BitOr,
+ kPrecedence_BitXor,
+ kPrecedence_BitAnd,
+
+ kPrecedence_Eql,
+ kPrecedence_Neq = kPrecedence_Eql,
+
+ kPrecedence_Less,
+ kPrecedence_Greater = kPrecedence_Less,
+ kPrecedence_Leq = kPrecedence_Less,
+ kPrecedence_Geq = kPrecedence_Less,
+
+ kPrecedence_Lsh,
+ kPrecedence_Rsh = kPrecedence_Lsh,
+
+ kPrecedence_Add,
+ kPrecedence_Sub = kPrecedence_Add,
+
+ kPrecedence_Mul,
+ kPrecedence_Div = kPrecedence_Mul,
+ kPrecedence_Mod = kPrecedence_Mul,
+
+ kPrecedence_Prefix,
+ kPrecedence_Postfix,
+ kPrecedence_Atomic = kPrecedence_Postfix
+};
+
+static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr, int outerPrec);
+
+static void EmitPostfixExpr(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr)
+{
+ EmitExprWithPrecedence(context, expr, kPrecedence_Postfix);
+}
+
+static void EmitExpr(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr)
+{
+ EmitExprWithPrecedence(context, expr, kPrecedence_General);
+}
+
+static bool MaybeEmitParens(EmitContext* context, int outerPrec, int prec)
+{
+ if (prec <= outerPrec)
+ {
+ Emit(context, "(");
+ return true;
+ }
+ return false;
+}
+
+// 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
+static RefPtr<ExpressionSyntaxNode> prepareLValueExpr(
+ EmitContext* context,
+ RefPtr<ExpressionSyntaxNode> expr)
+{
+ for(;;)
+ {
+ if(auto typeCastExpr = expr.As<TypeCastExpressionSyntaxNode>())
+ {
+ expr = typeCastExpr->Expression;
+ }
+ // TODO: any other cases?
+ else
+ {
+ return expr;
+ }
+ }
+
+}
+
+static void emitInfixExprImpl(
+ EmitContext* context,
+ int outerPrec,
+ int prec,
+ char const* op,
+ RefPtr<InvokeExpressionSyntaxNode> binExpr,
+ bool isAssign)
+{
+ bool needsClose = MaybeEmitParens(context, outerPrec, prec);
+
+ auto left = binExpr->Arguments[0];
+ if(isAssign)
+ {
+ left = prepareLValueExpr(context, left);
+ }
+
+ EmitExprWithPrecedence(context, left, prec);
+ Emit(context, " ");
+ Emit(context, op);
+ Emit(context, " ");
+ EmitExprWithPrecedence(context, binExpr->Arguments[1], prec);
+ if (needsClose)
+ {
+ Emit(context, ")");
+ }
+}
+
+static void EmitBinExpr(EmitContext* context, int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr)
+{
+ emitInfixExprImpl(context, outerPrec, prec, op, binExpr, false);
+}
+
+static void EmitBinAssignExpr(EmitContext* context, int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr)
+{
+ emitInfixExprImpl(context, outerPrec, prec, op, binExpr, true);
+}
+
+static void emitUnaryExprImpl(
+ EmitContext* context,
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr,
+ bool isAssign)
+{
+ bool needsClose = MaybeEmitParens(context, outerPrec, prec);
+ Emit(context, preOp);
+
+ auto arg = expr->Arguments[0];
+ if(isAssign)
+ {
+ arg = prepareLValueExpr(context, arg);
+ }
+
+ EmitExprWithPrecedence(context, arg, prec);
+ Emit(context, postOp);
+ if (needsClose)
+ {
+ Emit(context, ")");
+ }
+}
+
+static void EmitUnaryExpr(
+ EmitContext* context,
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr)
+{
+ emitUnaryExprImpl(context, outerPrec, prec, preOp, postOp, expr, false);
+}
+
+static void EmitUnaryAssignExpr(
+ EmitContext* context,
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr)
+{
+ emitUnaryExprImpl(context, outerPrec, prec, preOp, postOp, expr, true);
+}
+
+static void emitCallExpr(
+ EmitContext* context,
+ RefPtr<InvokeExpressionSyntaxNode> callExpr,
+ int outerPrec)
+{
+ auto funcExpr = callExpr->FunctionExpr;
+ if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
+ {
+ auto funcDeclRef = funcDeclRefExpr->declRef;
+ auto funcDecl = funcDeclRef.GetDecl();
+ if (auto intrinsicModifier = funcDecl->FindModifier<IntrinsicModifier>())
+ {
+ switch (intrinsicModifier->op)
+ {
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(context, outerPrec, kPrecedence_##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 IntrinsicOp::NAME: EmitBinAssignExpr(context, outerPrec, kPrecedence_##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 IntrinsicOp::Sequence: EmitBinExpr(context, outerPrec, kPrecedence_Comma, ",", callExpr); return;
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(context, outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return
+ CASE(Neg, -);
+ CASE(Not, !);
+ CASE(BitNot, ~);
+#undef CASE
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(context, outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return
+ CASE(PreInc, ++);
+ CASE(PreDec, --);
+#undef CASE
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(context, outerPrec, kPrecedence_Postfix, "", #OP, callExpr); return
+ CASE(PostInc, ++);
+ CASE(PostDec, --);
+#undef CASE
+
+ case IntrinsicOp::InnerProduct_Vector_Vector:
+ // HLSL allows `mul()` to be used as a synonym for `dot()`,
+ // so we need to translate to `dot` for GLSL
+ if (context->target == CodeGenTarget::GLSL)
+ {
+ Emit(context, "dot(");
+ EmitExpr(context, callExpr->Arguments[0]);
+ Emit(context, ", ");
+ EmitExpr(context, callExpr->Arguments[1]);
+ Emit(context, ")");
+ return;
+ }
+ break;
+
+ case IntrinsicOp::InnerProduct_Matrix_Matrix:
+ case IntrinsicOp::InnerProduct_Matrix_Vector:
+ case IntrinsicOp::InnerProduct_Vector_Matrix:
+ // 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->target == CodeGenTarget::GLSL)
+ {
+ Emit(context, "((");
+ EmitExpr(context, callExpr->Arguments[1]);
+ Emit(context, ") * (");
+ EmitExpr(context, callExpr->Arguments[0]);
+ Emit(context, "))");
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+
+ // We might be calling an intrinsic subscript operation,
+ // and should desugar it accordingly
+ if(auto subscriptDeclRef = funcDeclRef.As<SubscriptDeclRef>())
+ {
+ // 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<MemberExpressionSyntaxNode>())
+ {
+
+ Emit(context, "(");
+ EmitExpr(context, memberExpr->BaseExpression);
+ Emit(context, ")[");
+ int argCount = callExpr->Arguments.Count();
+ for (int aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(context, ", ");
+ EmitExpr(context, callExpr->Arguments[aa]);
+ }
+ Emit(context, "]");
+ return;
+ }
+ }
+ }
+ }
+
+ // Fall through to default handling...
+
+ bool needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Postfix);
+
+ if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
+ {
+ auto declRef = funcDeclRefExpr->declRef;
+ if (auto ctorDeclRef = declRef.As<ConstructorDeclRef>())
+ {
+ // We really want to emit a reference to the type begin constructed
+ EmitType(context, callExpr->Type);
+ }
+ else
+ {
+ // default case: just emit the decl ref
+ EmitExpr(context, funcExpr);
+ }
+ }
+ else
+ {
+ // default case: just emit the expression
+ EmitPostfixExpr(context, funcExpr);
+ }
+
+ Emit(context, "(");
+ int argCount = callExpr->Arguments.Count();
+ for (int aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(context, ", ");
+ EmitExpr(context, callExpr->Arguments[aa]);
+ }
+ Emit(context, ")");
+
+ if (needClose)
+ {
+ Emit(context, ")");
+ }
+}
+
+static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr, int outerPrec)
+{
+ bool needClose = false;
+ if (auto selectExpr = expr.As<SelectExpressionSyntaxNode>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Conditional);
+
+ EmitExprWithPrecedence(context, selectExpr->Arguments[0], kPrecedence_Conditional);
+ Emit(context, " ? ");
+ EmitExprWithPrecedence(context, selectExpr->Arguments[1], kPrecedence_Conditional);
+ Emit(context, " : ");
+ EmitExprWithPrecedence(context, selectExpr->Arguments[2], kPrecedence_Conditional);
+ }
+ else if (auto callExpr = expr.As<InvokeExpressionSyntaxNode>())
+ {
+ emitCallExpr(context, callExpr, outerPrec);
+ }
+ else if (auto memberExpr = expr.As<MemberExpressionSyntaxNode>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Postfix);
+
+ // 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(context, base))
+ {
+ // don't emit the base expression
+ }
+ else
+ {
+ EmitExprWithPrecedence(context, memberExpr->BaseExpression, kPrecedence_Postfix);
+ Emit(context, ".");
+ }
+
+ emitName(context, memberExpr->declRef.GetName());
+ }
+ else if (auto swizExpr = expr.As<SwizzleExpr>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Postfix);
+
+ EmitExprWithPrecedence(context, swizExpr->base, kPrecedence_Postfix);
+ Emit(context, ".");
+ static const char* kComponentNames[] = { "x", "y", "z", "w" };
+ int elementCount = swizExpr->elementCount;
+ for (int ee = 0; ee < elementCount; ++ee)
+ {
+ Emit(context, kComponentNames[swizExpr->elementIndices[ee]]);
+ }
+ }
+ else if (auto indexExpr = expr.As<IndexExpressionSyntaxNode>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Postfix);
+
+ EmitExprWithPrecedence(context, indexExpr->BaseExpression, kPrecedence_Postfix);
+ Emit(context, "[");
+ EmitExpr(context, indexExpr->IndexExpression);
+ Emit(context, "]");
+ }
+ else if (auto varExpr = expr.As<VarExpressionSyntaxNode>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Atomic);
+
+ // 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(context, varExpr->declRef);
+ }
+ else
+ {
+ emitName(context, varExpr->Variable);
+ }
+ }
+ else if (auto derefExpr = expr.As<DerefExpr>())
+ {
+ // TODO(tfoley): dereference shouldn't always be implicit
+ EmitExprWithPrecedence(context, derefExpr->base, outerPrec);
+ }
+ else if (auto litExpr = expr.As<ConstantExpressionSyntaxNode>())
+ {
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Atomic);
+
+ switch (litExpr->ConstType)
+ {
+ case ConstantExpressionSyntaxNode::ConstantType::Int:
+ Emit(context, litExpr->IntValue);
+ break;
+ case ConstantExpressionSyntaxNode::ConstantType::Float:
+ Emit(context, litExpr->FloatValue);
+ break;
+ case ConstantExpressionSyntaxNode::ConstantType::Bool:
+ Emit(context, litExpr->IntValue ? "true" : "false");
+ break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ }
+ else if (auto castExpr = expr.As<TypeCastExpressionSyntaxNode>())
+ {
+ switch(context->target)
+ {
+ case CodeGenTarget::GLSL:
+ // GLSL requires constructor syntax for all conversions
+ EmitType(context, castExpr->Type);
+ Emit(context, "(");
+ EmitExpr(context, castExpr->Expression);
+ Emit(context, ")");
+ 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)
+ needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Prefix);
+
+ Emit(context, "(");
+ EmitType(context, castExpr->Type);
+ Emit(context, ")(");
+ EmitExpr(context, castExpr->Expression);
+ Emit(context, ")");
+ break;
+ }
+
+ }
+ else if(auto initExpr = expr.As<InitializerListExpr>())
+ {
+ Emit(context, "{ ");
+ for(auto& arg : initExpr->args)
+ {
+ EmitExpr(context, arg);
+ Emit(context, ", ");
+ }
+ Emit(context, "}");
+ }
+ else
+ {
+ throw "unimplemented";
+ }
+ if (needClose)
+ {
+ Emit(context, ")");
+ }
+}
+
+// Types
+
+void Emit(EmitContext* context, RefPtr<IntVal> val)
+{
+ if(auto constantIntVal = val.As<ConstantIntVal>())
+ {
+ Emit(context, constantIntVal->value);
+ }
+ else if(auto varRefVal = val.As<GenericParamIntVal>())
+ {
+ EmitDeclRef(context, varRefVal->declRef);
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
+}
+
+// represents a declarator for use in emitting types
+struct EDeclarator
+{
+ enum class Flavor
+ {
+ Name,
+ Array,
+ UnsizedArray,
+ };
+ Flavor flavor;
+ EDeclarator* next = nullptr;
+
+ // Used for `Flavor::Name`
+ String name;
+
+ // Used for `Flavor::Array`
+ IntVal* elementCount;
+};
+
+static void EmitDeclarator(EmitContext* context, EDeclarator* declarator)
+{
+ if (!declarator) return;
+
+ Emit(context, " ");
+
+ switch (declarator->flavor)
+ {
+ case EDeclarator::Flavor::Name:
+ emitName(context, declarator->name);
+ break;
+
+ case EDeclarator::Flavor::Array:
+ EmitDeclarator(context, declarator->next);
+ Emit(context, "[");
+ if(auto elementCount = declarator->elementCount)
+ {
+ Emit(context, elementCount);
+ }
+ Emit(context, "]");
+ break;
+
+ case EDeclarator::Flavor::UnsizedArray:
+ EmitDeclarator(context, declarator->next);
+ Emit(context, "[]");
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+}
+
+static void emitGLSLTypePrefix(
+ EmitContext* context,
+ RefPtr<ExpressionType> type)
+{
+ if(auto basicElementType = type->As<BasicExpressionType>())
+ {
+ switch (basicElementType->BaseType)
+ {
+ case BaseType::Float:
+ // no prefix
+ break;
+
+ case BaseType::Int: Emit(context, "i"); break;
+ case BaseType::UInt: Emit(context, "u"); break;
+ case BaseType::Bool: Emit(context, "b"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ }
+ else if(auto vectorType = type->As<VectorExpressionType>())
+ {
+ emitGLSLTypePrefix(context, vectorType->elementType);
+ }
+ else if(auto matrixType = type->As<MatrixExpressionType>())
+ {
+ emitGLSLTypePrefix(context, matrixType->getElementType());
+ }
+ else
+ {
+ assert(!"unreachable");
+ }
+}
+
+static void emitHLSLTextureType(
+ EmitContext* context,
+ RefPtr<TextureTypeBase> texType)
+{
+ switch(texType->getAccess())
+ {
+ case SLANG_RESOURCE_ACCESS_READ:
+ break;
+
+ case SLANG_RESOURCE_ACCESS_READ_WRITE:
+ Emit(context, "RW");
+ break;
+
+ case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
+ Emit(context, "RasterizerOrdered");
+ break;
+
+ case SLANG_RESOURCE_ACCESS_APPEND:
+ Emit(context, "Append");
+ break;
+
+ case SLANG_RESOURCE_ACCESS_CONSUME:
+ Emit(context, "Consume");
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ switch (texType->GetBaseShape())
+ {
+ case TextureType::Shape1D: Emit(context, "Texture1D"); break;
+ case TextureType::Shape2D: Emit(context, "Texture2D"); break;
+ case TextureType::Shape3D: Emit(context, "Texture3D"); break;
+ case TextureType::ShapeCube: Emit(context, "TextureCube"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ if (texType->isMultisample())
+ {
+ Emit(context, "MS");
+ }
+ if (texType->isArray())
+ {
+ Emit(context, "Array");
+ }
+ Emit(context, "<");
+ EmitType(context, texType->elementType);
+ Emit(context, ">");
+}
+
+static void emitGLSLTextureOrTextureSamplerType(
+ EmitContext* context,
+ RefPtr<TextureTypeBase> type,
+ char const* baseName)
+{
+ emitGLSLTypePrefix(context, type->elementType);
+
+ Emit(context, baseName);
+ switch (type->GetBaseShape())
+ {
+ case TextureType::Shape1D: Emit(context, "1D"); break;
+ case TextureType::Shape2D: Emit(context, "2D"); break;
+ case TextureType::Shape3D: Emit(context, "3D"); break;
+ case TextureType::ShapeCube: Emit(context, "Cube"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ if (type->isMultisample())
+ {
+ Emit(context, "MS");
+ }
+ if (type->isArray())
+ {
+ Emit(context, "Array");
+ }
+}
+
+static void emitGLSLTextureType(
+ EmitContext* context,
+ RefPtr<TextureType> texType)
+{
+ emitGLSLTextureOrTextureSamplerType(context, texType, "texture");
+}
+
+static void emitGLSLTextureSamplerType(
+ EmitContext* context,
+ RefPtr<TextureSamplerType> type)
+{
+ emitGLSLTextureOrTextureSamplerType(context, type, "sampler");
+}
+
+static void emitGLSLImageType(
+ EmitContext* context,
+ RefPtr<GLSLImageType> type)
+{
+ emitGLSLTextureOrTextureSamplerType(context, type, "image");
+}
+
+static void emitTextureType(
+ EmitContext* context,
+ RefPtr<TextureType> texType)
+{
+ switch(context->target)
+ {
+ case CodeGenTarget::HLSL:
+ emitHLSLTextureType(context, texType);
+ break;
+
+ case CodeGenTarget::GLSL:
+ emitGLSLTextureType(context, texType);
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+}
+
+static void emitTextureSamplerType(
+ EmitContext* context,
+ RefPtr<TextureSamplerType> type)
+{
+ switch(context->target)
+ {
+ case CodeGenTarget::GLSL:
+ emitGLSLTextureSamplerType(context, type);
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+}
+
+static void emitImageType(
+ EmitContext* context,
+ RefPtr<GLSLImageType> type)
+{
+ switch(context->target)
+ {
+ case CodeGenTarget::HLSL:
+ emitHLSLTextureType(context, type);
+ break;
+
+ case CodeGenTarget::GLSL:
+ emitGLSLImageType(context, type);
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+}
+
+static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclarator* declarator)
+{
+ if (auto basicType = type->As<BasicExpressionType>())
+ {
+ switch (basicType->BaseType)
+ {
+ case BaseType::Void: Emit(context, "void"); break;
+ case BaseType::Int: Emit(context, "int"); break;
+ case BaseType::Float: Emit(context, "float"); break;
+ case BaseType::UInt: Emit(context, "uint"); break;
+ case BaseType::Bool: Emit(context, "bool"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto vecType = type->As<VectorExpressionType>())
+ {
+ switch(context->target)
+ {
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::GLSL_Vulkan:
+ case CodeGenTarget::GLSL_Vulkan_OneDesc:
+ {
+ emitGLSLTypePrefix(context, vecType->elementType);
+ Emit(context, "vec");
+ Emit(context, vecType->elementCount);
+ }
+ break;
+
+ case CodeGenTarget::HLSL:
+ // TODO(tfoley): should really emit these with sugar
+ Emit(context, "vector<");
+ EmitType(context, vecType->elementType);
+ Emit(context, ",");
+ Emit(context, vecType->elementCount);
+ Emit(context, ">");
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ Emit(context, " ");
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto matType = type->As<MatrixExpressionType>())
+ {
+ switch(context->target)
+ {
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::GLSL_Vulkan:
+ case CodeGenTarget::GLSL_Vulkan_OneDesc:
+ {
+ emitGLSLTypePrefix(context, matType->getElementType());
+ Emit(context, "mat");
+ Emit(context, matType->getRowCount());
+ // TODO(tfoley): only emit the next bit
+ // for non-square matrix
+ Emit(context, "x");
+ Emit(context, matType->getColumnCount());
+ }
+ break;
+
+ case CodeGenTarget::HLSL:
+ // TODO(tfoley): should really emit these with sugar
+ Emit(context, "matrix<");
+ EmitType(context, matType->getElementType());
+ Emit(context, ",");
+ Emit(context, matType->getRowCount());
+ Emit(context, ",");
+ Emit(context, matType->getColumnCount());
+ Emit(context, "> ");
+ break;
+
+ default:
+ assert(!"unreachable");
+ break;
+ }
+
+ Emit(context, " ");
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto texType = type->As<TextureType>())
+ {
+ emitTextureType(context, texType);
+ Emit(context, " ");
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto textureSamplerType = type->As<TextureSamplerType>())
+ {
+ emitTextureSamplerType(context, textureSamplerType);
+ Emit(context, " ");
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto imageType = type->As<GLSLImageType>())
+ {
+ emitImageType(context, imageType);
+ Emit(context, " ");
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto samplerStateType = type->As<SamplerStateType>())
+ {
+ switch(context->target)
+ {
+ case CodeGenTarget::HLSL:
+ default:
+ switch (samplerStateType->flavor)
+ {
+ case SamplerStateType::Flavor::SamplerState: Emit(context, "SamplerState"); break;
+ case SamplerStateType::Flavor::SamplerComparisonState: Emit(context, "SamplerComparisonState"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ break;
+
+ case CodeGenTarget::GLSL:
+ Emit(context, "sampler");
+ break;
+ }
+
+
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if (auto declRefType = type->As<DeclRefType>())
+ {
+ EmitDeclRef(context, declRefType->declRef);
+
+ EmitDeclarator(context, declarator);
+ return;
+ }
+ else if( auto arrayType = type->As<ArrayExpressionType>() )
+ {
+ EDeclarator arrayDeclarator;
+ arrayDeclarator.next = declarator;
+
+ if(arrayType->ArrayLength)
+ {
+ arrayDeclarator.flavor = EDeclarator::Flavor::Array;
+ arrayDeclarator.elementCount = arrayType->ArrayLength.Ptr();
+ }
+ else
+ {
+ arrayDeclarator.flavor = EDeclarator::Flavor::UnsizedArray;
+ }
+
+
+ EmitType(context, arrayType->BaseType, &arrayDeclarator);
+ return;
+ }
+
+ throw "unimplemented";
+}
+
+static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, String const& name)
+{
+ EDeclarator nameDeclarator;
+ nameDeclarator.flavor = EDeclarator::Flavor::Name;
+ nameDeclarator.name = name;
+ EmitType(context, type, &nameDeclarator);
+}
+
+static void EmitType(EmitContext* context, RefPtr<ExpressionType> type)
+{
+ EmitType(context, type, nullptr);
+}
+
+// Statements
+
+// Emit a statement as a `{}`-enclosed block statement, but avoid adding redundant
+// curly braces if the statement is itself a block statement.
+static void EmitBlockStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt)
+{
+ // TODO(tfoley): support indenting
+ Emit(context, "{\n");
+ if( auto blockStmt = stmt.As<BlockStatementSyntaxNode>() )
+ {
+ for (auto s : blockStmt->Statements)
+ {
+ EmitStmt(context, s);
+ }
+ }
+ else
+ {
+ EmitStmt(context, stmt);
+ }
+ Emit(context, "}\n");
+}
+
+static void EmitLoopAttributes(EmitContext* context, RefPtr<StatementSyntaxNode> decl)
+{
+ // TODO(tfoley): There really ought to be a semantic checking step for attributes,
+ // that turns abstract syntax into a concrete hierarchy of attribute types (e.g.,
+ // a specific `LoopModifier` or `UnrollModifier`).
+
+ for(auto attr : decl->GetModifiersOfType<HLSLUncheckedAttribute>())
+ {
+ if(attr->nameToken.Content == "loop")
+ {
+ Emit(context, "[loop]");
+ }
+ else if(attr->nameToken.Content == "unroll")
+ {
+ Emit(context, "[unroll]");
+ }
+ }
+}
+
+static void advanceToSourceLocation(
+ EmitContext* context,
+ CodePosition const& sourceLocation)
+{
+ // If we are currently emitting code at a source location with
+ // a differnet file or line, *or* if the source location is
+ // somehow later on the line than what we want to emit,
+ // then we need to emit a new `#line` directive.
+ if(sourceLocation.FileName != context->loc.FileName
+ || sourceLocation.Line != context->loc.Line
+ || sourceLocation.Col < context->loc.Col)
+ {
+ emitRawText(context, "\n#line ");
+
+ char buffer[16];
+ sprintf(buffer, "%d", sourceLocation.Line);
+ emitRawText(context, buffer);
+
+ emitRawText(context, "\"");
+ for(auto c : sourceLocation.FileName)
+ {
+ char charBuffer[] = { c, 0 };
+ switch(c)
+ {
+ default:
+ emitRawText(context, charBuffer);
+ break;
+
+ // TODO: should probably canonicalize paths to not use backslash somewhere else
+ // in the compilation pipeline...
+ case '\\':
+ emitRawText(context, "/");
+ break;
+ }
+ }
+ emitRawText(context, "\"\n");
+
+ context->loc.FileName = sourceLocation.FileName;
+ context->loc.Line = sourceLocation.Line;
+ context->loc.Col = 1;
+ }
+
+ // 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.Col > context->loc.Col)
+ {
+ int delta = sourceLocation.Col - context->loc.Col;
+ for( int ii = 0; ii < delta; ++ii )
+ {
+ emitRawText(context, " ");
+ }
+ context->loc.Col = sourceLocation.Col;
+ }
+}
+
+static void emitTokenWithLocation(EmitContext* context, Token const& token)
+{
+ if( token.Position.FileName.Length() != 0 )
+ {
+ advanceToSourceLocation(context, token.Position);
+ }
+ else
+ {
+ // If we don't have the original position info, we need to play
+ // it safe and emit whitespace to line things up nicely
+
+ if(token.flags & TokenFlag::AtStartOfLine)
+ Emit(context, "\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(context, " ");
+ }
+
+ // Emit the raw textual content of the token
+ emit(context, token.Content);
+}
+
+static void EmitUnparsedStmt(EmitContext* context, RefPtr<UnparsedStmt> stmt)
+{
+ // TODO: actually emit the tokens that made up the statement...
+ Emit(context, "{\n");
+ for( auto& token : stmt->tokens )
+ {
+ emitTokenWithLocation(context, token);
+ }
+ Emit(context, "}\n");
+}
+
+static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt)
+{
+ if (auto blockStmt = stmt.As<BlockStatementSyntaxNode>())
+ {
+ EmitBlockStmt(context, blockStmt);
+ return;
+ }
+ else if( auto unparsedStmt = stmt.As<UnparsedStmt>() )
+ {
+ EmitUnparsedStmt(context, unparsedStmt);
+ return;
+ }
+ else if (auto exprStmt = stmt.As<ExpressionStatementSyntaxNode>())
+ {
+ EmitExpr(context, exprStmt->Expression);
+ Emit(context, ";\n");
+ return;
+ }
+ else if (auto returnStmt = stmt.As<ReturnStatementSyntaxNode>())
+ {
+ Emit(context, "return");
+ if (auto expr = returnStmt->Expression)
+ {
+ Emit(context, " ");
+ EmitExpr(context, expr);
+ }
+ Emit(context, ";\n");
+ return;
+ }
+ else if (auto declStmt = stmt.As<VarDeclrStatementSyntaxNode>())
+ {
+ EmitDecl(context, declStmt->decl);
+ return;
+ }
+ else if (auto ifStmt = stmt.As<IfStatementSyntaxNode>())
+ {
+ Emit(context, "if(");
+ EmitExpr(context, ifStmt->Predicate);
+ Emit(context, ")\n");
+ EmitBlockStmt(context, ifStmt->PositiveStatement);
+ if(auto elseStmt = ifStmt->NegativeStatement)
+ {
+ Emit(context, "\nelse\n");
+ EmitBlockStmt(context, elseStmt);
+ }
+ return;
+ }
+ else if (auto forStmt = stmt.As<ForStatementSyntaxNode>())
+ {
+ EmitLoopAttributes(context, forStmt);
+
+ Emit(context, "for(");
+ if (auto initStmt = forStmt->InitialStatement)
+ {
+ EmitStmt(context, initStmt);
+ }
+ else
+ {
+ Emit(context, ";");
+ }
+ if (auto testExp = forStmt->PredicateExpression)
+ {
+ EmitExpr(context, testExp);
+ }
+ Emit(context, ";");
+ if (auto incrExpr = forStmt->SideEffectExpression)
+ {
+ EmitExpr(context, incrExpr);
+ }
+ Emit(context, ")\n");
+ EmitBlockStmt(context, forStmt->Statement);
+ return;
+ }
+ else if (auto discardStmt = stmt.As<DiscardStatementSyntaxNode>())
+ {
+ Emit(context, "discard;\n");
+ return;
+ }
+ else if (auto emptyStmt = stmt.As<EmptyStatementSyntaxNode>())
+ {
+ return;
+ }
+ else if (auto switchStmt = stmt.As<SwitchStmt>())
+ {
+ Emit(context, "switch(");
+ EmitExpr(context, switchStmt->condition);
+ Emit(context, ")\n");
+ EmitBlockStmt(context, switchStmt->body);
+ return;
+ }
+ else if (auto caseStmt = stmt.As<CaseStmt>())
+ {
+ Emit(context, "case ");
+ EmitExpr(context, caseStmt->expr);
+ Emit(context, ":\n");
+ return;
+ }
+ else if (auto defaultStmt = stmt.As<DefaultStmt>())
+ {
+ Emit(context, "default:{}\n");
+ return;
+ }
+ else if (auto breakStmt = stmt.As<BreakStatementSyntaxNode>())
+ {
+ Emit(context, "break;\n");
+ return;
+ }
+ else if (auto continueStmt = stmt.As<ContinueStatementSyntaxNode>())
+ {
+ Emit(context, "continue;\n");
+ return;
+ }
+
+ throw "unimplemented";
+
+}
+
+// Declaration References
+
+static void EmitVal(EmitContext* context, RefPtr<Val> val)
+{
+ if (auto type = val.As<ExpressionType>())
+ {
+ EmitType(context, type);
+ }
+ else if (auto intVal = val.As<IntVal>())
+ {
+ Emit(context, intVal);
+ }
+ else
+ {
+ // Note(tfoley): ignore unhandled cases for semantics for now...
+// assert(!"unimplemented");
+ }
+}
+
+static void EmitDeclRef(EmitContext* context, DeclRef declRef)
+{
+ // TODO: need to qualify a declaration name based on parent scopes/declarations
+
+ // Emit the name for the declaration itself
+ emitName(context, declRef.GetName());
+
+ // If the declaration is nested directly in a generic, then
+ // we need to output the generic arguments here
+ auto parentDeclRef = declRef.GetParent();
+ if (auto genericDeclRef = parentDeclRef.As<GenericDeclRef>())
+ {
+ // Only do this for declarations of appropriate flavors
+ if(auto funcDeclRef = declRef.As<FuncDeclBaseRef>())
+ {
+ // Don't emit generic arguments for functions, because HLSL doesn't allow them
+ return;
+ }
+
+ Substitutions* subst = declRef.substitutions.Ptr();
+ Emit(context, "<");
+ int argCount = subst->args.Count();
+ for (int aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(context, ",");
+ EmitVal(context, subst->args[aa]);
+ }
+ Emit(context, ">");
+ }
+
+}
+
+// Declarations
+
+// Emit any modifiers that should go in front of a declaration
+static void EmitModifiers(EmitContext* context, RefPtr<Decl> decl)
+{
+ // Emit any GLSL `layout` modifiers first
+ bool anyLayout = false;
+ for( auto mod : decl->GetModifiersOfType<GLSLUnparsedLayoutModifier>())
+ {
+ if(!anyLayout)
+ {
+ Emit(context, "layout(");
+ anyLayout = true;
+ }
+ else
+ {
+ Emit(context, ", ");
+ }
+
+ emit(context, mod->nameToken.Content);
+ if(mod->valToken.Type != TokenType::Unknown)
+ {
+ Emit(context, " = ");
+ emit(context, mod->valToken.Content);
+ }
+ }
+ if(anyLayout)
+ {
+ Emit(context, ")\n");
+ }
+
+ for (auto mod = decl->modifiers.first; mod; mod = mod->next)
+ {
+ if (0) {}
+
+ #define CASE(TYPE, KEYWORD) \
+ else if(auto mod_##TYPE = mod.As<TYPE>()) Emit(context, #KEYWORD " ")
+
+ CASE(RowMajorLayoutModifier, row_major);
+ CASE(ColumnMajorLayoutModifier, column_major);
+ CASE(HLSLNoInterpolationModifier, nointerpolation);
+ CASE(HLSLPreciseModifier, precise);
+ CASE(HLSLEffectSharedModifier, shared);
+ CASE(HLSLGroupSharedModifier, groupshared);
+ CASE(HLSLStaticModifier, static);
+ 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);
+
+ CASE(HLSLLinearModifier, linear);
+ CASE(HLSLSampleModifier, sample);
+ CASE(HLSLCentroidModifier, centroid);
+
+ CASE(ConstModifier, const);
+
+ #undef CASE
+
+ // TODO: eventually we should be checked these modifiers, but for
+ // now we can emit them unchecked, I guess
+ else if (auto uncheckedAttr = mod.As<HLSLAttribute>())
+ {
+ Emit(context, "[");
+ emit(context, uncheckedAttr->nameToken.Content);
+ auto& args = uncheckedAttr->args;
+ auto argCount = args.Count();
+ if (argCount != 0)
+ {
+ Emit(context, "(");
+ for (int aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(context, ", ");
+ EmitExpr(context, args[aa]);
+ }
+ Emit(context, ")");
+ }
+ Emit(context, "]");
+ }
+
+ else if(auto simpleModifier = mod.As<SimpleModifier>())
+ {
+ emit(context, simpleModifier->nameToken.Content);
+ Emit(context, " ");
+ }
+
+ else
+ {
+ // skip any extra modifiers
+ }
+ }
+}
+
+
+typedef unsigned int ESemanticMask;
+enum
+{
+ kESemanticMask_None = 0,
+
+ kESemanticMask_NoPackOffset = 1 << 0,
+
+ kESemanticMask_Default = kESemanticMask_NoPackOffset,
+};
+
+static void EmitSemantic(EmitContext* context, RefPtr<HLSLSemantic> semantic, ESemanticMask /*mask*/)
+{
+ if (auto simple = semantic.As<HLSLSimpleSemantic>())
+ {
+ Emit(context, ": ");
+ emit(context, 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(context, ": register(");
+ Emit(context, registerSemantic->registerName.Content);
+ if(registerSemantic->componentMask.Type != TokenType::Unknown)
+ {
+ Emit(context, ".");
+ Emit(context, registerSemantic->componentMask.Content);
+ }
+ Emit(context, ")");
+#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(context, ": packoffset(");
+ Emit(context, packOffsetSemantic->registerName.Content);
+ if(packOffsetSemantic->componentMask.Type != TokenType::Unknown)
+ {
+ Emit(context, ".");
+ Emit(context, packOffsetSemantic->componentMask.Content);
+ }
+ Emit(context, ")");
+#endif
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
+}
+
+
+static void EmitSemantics(EmitContext* context, RefPtr<Decl> decl, ESemanticMask mask = kESemanticMask_Default )
+{
+ // Don't emit semantics if we aren't translating down to HLSL
+ switch (context->target)
+ {
+ case CodeGenTarget::HLSL:
+ break;
+
+ default:
+ return;
+ }
+
+ for (auto mod = decl->modifiers.first; mod; mod = mod->next)
+ {
+ auto semantic = mod.As<HLSLSemantic>();
+ if (!semantic)
+ continue;
+
+ EmitSemantic(context, semantic, mask);
+ }
+}
+
+static void EmitDeclsInContainer(EmitContext* context, RefPtr<ContainerDecl> container)
+{
+ for (auto member : container->Members)
+ {
+ EmitDecl(context, member);
+ }
+}
+
+static void EmitDeclsInContainerUsingLayout(
+ EmitContext* context,
+ RefPtr<ContainerDecl> container,
+ RefPtr<StructTypeLayout> containerLayout)
+{
+ for (auto member : container->Members)
+ {
+ RefPtr<VarLayout> memberLayout;
+ if( containerLayout->mapVarToLayout.TryGetValue(member.Ptr(), memberLayout) )
+ {
+ EmitDeclUsingLayout(context, member, memberLayout);
+ }
+ else
+ {
+ // No layout for this decl
+ EmitDecl(context, member);
+ }
+ }
+}
+
+static void EmitTypeDefDecl(EmitContext* context, RefPtr<TypeDefDecl> decl)
+{
+ // TODO(tfoley): check if current compilation target even supports typedefs
+
+ Emit(context, "typedef ");
+ EmitType(context, decl->Type, decl->Name.Content);
+ Emit(context, ";\n");
+}
+
+static void EmitStructDecl(EmitContext* context, RefPtr<StructSyntaxNode> decl)
+{
+ // Don't emit a declaration that was only generated implicitly, for
+ // the purposes of semantic checking.
+ if(decl->HasModifier<ImplicitParameterBlockElementTypeModifier>())
+ return;
+
+ Emit(context, "struct ");
+ emitName(context, decl->Name.Content);
+ Emit(context, "\n{\n");
+
+ // TODO(tfoley): Need to hoist members functions, etc. out to global scope
+ EmitDeclsInContainer(context, decl);
+
+ Emit(context, "};\n");
+}
+
+// Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
+static void EmitVarDeclCommon(EmitContext* context, VarDeclBaseRef declRef)
+{
+ EmitModifiers(context, declRef.GetDecl());
+
+ EmitType(context, declRef.GetType(), declRef.GetName());
+
+ EmitSemantics(context, declRef.GetDecl());
+
+ // TODO(tfoley): technically have to apply substitution here too...
+ if (auto initExpr = declRef.GetDecl()->Expr)
+ {
+ Emit(context, " = ");
+ EmitExpr(context, initExpr);
+ }
+}
+
+// Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
+static void EmitVarDeclCommon(EmitContext* context, RefPtr<VarDeclBase> decl)
+{
+ EmitVarDeclCommon(context, DeclRef(decl.Ptr(), nullptr).As<VarDeclBaseRef>());
+}
+
+// Emit a single `regsiter` semantic, as appropriate for a given resource-type-specific layout info
+static void emitHLSLRegisterSemantic(
+ EmitContext* context,
+ VarLayout::ResourceInfo const& info)
+{
+ if( info.kind == LayoutResourceKind::Uniform )
+ {
+ size_t offset = info.index;
+
+ // The HLSL `c` register space is logically grouped in 16-byte registers,
+ // while we try to traffic in byte offsets. That means we need to pick
+ // a register number, based on the starting offset in 16-byte register
+ // 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(context, ": packoffset(c");
+
+ // Size of a logical `c` register in bytes
+ auto registerSize = 16;
+
+ // Size of each component of a logical `c` register, in bytes
+ auto componentSize = 4;
+
+ size_t startRegister = offset / registerSize;
+ Emit(context, int(startRegister));
+
+ size_t byteOffsetInRegister = offset % registerSize;
+
+ // If this field doesn't start on an even register boundary,
+ // then we need to emit additional information to pick the
+ // right component to start from
+ if (byteOffsetInRegister != 0)
+ {
+ // The value had better occupy a whole number of components.
+ assert(byteOffsetInRegister % componentSize == 0);
+
+ size_t startComponent = byteOffsetInRegister / componentSize;
+
+ static const char* kComponentNames[] = {"x", "y", "z", "w"};
+ Emit(context, ".");
+ Emit(context, kComponentNames[startComponent]);
+ }
+ Emit(context, ")");
+ }
+ else
+ {
+ Emit(context, ": register(");
+ switch( info.kind )
+ {
+ case LayoutResourceKind::ConstantBuffer:
+ Emit(context, "b");
+ break;
+ case LayoutResourceKind::ShaderResource:
+ Emit(context, "t");
+ break;
+ case LayoutResourceKind::UnorderedAccess:
+ Emit(context, "u");
+ break;
+ case LayoutResourceKind::SamplerState:
+ Emit(context, "s");
+ break;
+ default:
+ assert(!"unexpected");
+ break;
+ }
+ Emit(context, info.index);
+ if(info.space)
+ {
+ Emit(context, ", space");
+ Emit(context, info.space);
+ }
+ Emit(context, ")");
+ }
+}
+
+// Emit all the `register` semantics that are appropriate for a particular variable layout
+static void emitHLSLRegisterSemantics(
+ EmitContext* context,
+ RefPtr<VarLayout> layout)
+{
+ if (!layout) return;
+
+ switch( context->target )
+ {
+ default:
+ return;
+
+ case CodeGenTarget::HLSL:
+ break;
+ }
+
+ for( auto rr : layout->resourceInfos )
+ {
+ emitHLSLRegisterSemantic(context, rr);
+ }
+}
+
+static void emitHLSLParameterBlockDecl(
+ EmitContext* context,
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
+{
+ // The data type that describes where stuff in the constant buffer should go
+ RefPtr<ExpressionType> dataType = parameterBlockType->elementType;
+
+ // We expect/require the data type to be a user-defined `struct` type
+ auto declRefType = dataType->As<DeclRefType>();
+ assert(declRefType);
+
+ // We expect to always have layout information
+ assert(layout);
+
+ // We expect the layout to be for a structured type...
+ RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
+ assert(bufferLayout);
+
+ RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
+ assert(structTypeLayout);
+
+ if( auto constantBufferType = parameterBlockType->As<ConstantBufferType>() )
+ {
+ Emit(context, "cbuffer ");
+ }
+ else if( auto textureBufferType = parameterBlockType->As<TextureBufferType>() )
+ {
+ Emit(context, "tbuffer ");
+ }
+
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ {
+ Emit(context, " ");
+ emitName(context, reflectionNameModifier->nameToken.Content);
+ }
+
+ EmitSemantics(context, varDecl, kESemanticMask_None);
+
+ auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
+ assert(info);
+ emitHLSLRegisterSemantic(context, *info);
+
+ Emit(context, "\n{\n");
+ if (auto structRef = declRefType->declRef.As<StructDeclRef>())
+ {
+ for (auto field : structRef.GetMembersOfType<FieldDeclRef>())
+ {
+ EmitVarDeclCommon(context, field);
+
+ RefPtr<VarLayout> fieldLayout;
+ structTypeLayout->mapVarToLayout.TryGetValue(field.GetDecl(), fieldLayout);
+ assert(fieldLayout);
+
+ // Emit explicit layout annotations for every field
+ for( auto rr : fieldLayout->resourceInfos )
+ {
+ auto kind = rr.kind;
+
+ auto offsetResource = rr;
+
+ if(kind != LayoutResourceKind::Uniform)
+ {
+ // Add the base index from the cbuffer into the index of the field
+ //
+ // TODO(tfoley): consider maybe not doing this, since it actually
+ // complicates logic around constant buffers...
+
+ // If the member of the cbuffer uses a resource, it had better
+ // appear as part of the cubffer layout as well.
+ auto cbufferResource = layout->FindResourceInfo(kind);
+ assert(cbufferResource);
+
+ offsetResource.index += cbufferResource->index;
+ offsetResource.space += cbufferResource->space;
+ }
+
+ emitHLSLRegisterSemantic(context, offsetResource);
+ }
+
+ Emit(context, ";\n");
+ }
+ }
+ Emit(context, "}\n");
+}
+
+static void
+emitGLSLLayoutQualifier(
+ EmitContext* context,
+ VarLayout::ResourceInfo const& info)
+{
+ switch(info.kind)
+ {
+ case LayoutResourceKind::Uniform:
+ Emit(context, "layout(offset = ");
+ Emit(context, info.index);
+ Emit(context, ")\n");
+ break;
+
+ case LayoutResourceKind::VertexInput:
+ case LayoutResourceKind::FragmentOutput:
+ Emit(context, "layout(location = ");
+ Emit(context, info.index);
+ Emit(context, ")\n");
+ break;
+
+ case LayoutResourceKind::SpecializationConstant:
+ Emit(context, "layout(constant_id = ");
+ Emit(context, info.index);
+ Emit(context, ")\n");
+ break;
+
+ case LayoutResourceKind::ConstantBuffer:
+ case LayoutResourceKind::ShaderResource:
+ case LayoutResourceKind::UnorderedAccess:
+ case LayoutResourceKind::SamplerState:
+ case LayoutResourceKind::DescriptorTableSlot:
+ Emit(context, "layout(binding = ");
+ Emit(context, info.index);
+ if(info.space)
+ {
+ Emit(context, ", set = ");
+ Emit(context, info.space);
+ }
+ Emit(context, ")\n");
+ break;
+ }
+}
+
+static void
+emitGLSLLayoutQualifiers(
+ EmitContext* context,
+ RefPtr<VarLayout> layout)
+{
+ if(!layout) return;
+
+ switch( context->target )
+ {
+ default:
+ return;
+
+ case CodeGenTarget::GLSL:
+ break;
+ }
+
+ for( auto info : layout->resourceInfos )
+ {
+ emitGLSLLayoutQualifier(context, info);
+ }
+}
+
+static void emitGLSLParameterBlockDecl(
+ EmitContext* context,
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
+{
+ // The data type that describes where stuff in the constant buffer should go
+ RefPtr<ExpressionType> dataType = parameterBlockType->elementType;
+
+ // We expect/require the data type to be a user-defined `struct` type
+ auto declRefType = dataType->As<DeclRefType>();
+ assert(declRefType);
+
+ // We expect to always have layout information
+ assert(layout);
+
+ // We expect the layout to be for a structured type...
+ RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
+ assert(bufferLayout);
+
+ RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
+ assert(structTypeLayout);
+
+ emitGLSLLayoutQualifiers(context, layout);
+
+ EmitModifiers(context, varDecl);
+
+ // Emit an apprpriate declaration keyword based on the kind of block
+ if (parameterBlockType->As<ConstantBufferType>())
+ {
+ Emit(context, "uniform");
+ }
+ else if (parameterBlockType->As<GLSLInputParameterBlockType>())
+ {
+ Emit(context, "in");
+ }
+ else if (parameterBlockType->As<GLSLOutputParameterBlockType>())
+ {
+ Emit(context, "out");
+ }
+ else if (parameterBlockType->As<GLSLShaderStorageBufferType>())
+ {
+ Emit(context, "buffer");
+ }
+ else
+ {
+ assert(!"unexpected");
+ Emit(context, "uniform");
+ }
+
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ {
+ Emit(context, " ");
+ emitName(context, reflectionNameModifier->nameToken.Content);
+ }
+
+ Emit(context, "\n{\n");
+ if (auto structRef = declRefType->declRef.As<StructDeclRef>())
+ {
+ for (auto field : structRef.GetMembersOfType<FieldDeclRef>())
+ {
+ RefPtr<VarLayout> fieldLayout;
+ structTypeLayout->mapVarToLayout.TryGetValue(field.GetDecl(), fieldLayout);
+ assert(fieldLayout);
+
+ // TODO(tfoley): We may want to emit *some* of these,
+ // some of the time...
+// emitGLSLLayoutQualifiers(context, fieldLayout);
+
+ EmitVarDeclCommon(context, field);
+
+ Emit(context, ";\n");
+ }
+ }
+ Emit(context, "}");
+
+ if( varDecl->Name.Type != TokenType::Unknown )
+ {
+ Emit(context, " ");
+ emitName(context, varDecl->Name.Content);
+ }
+
+ Emit(context, ";\n");
+}
+
+static void emitParameterBlockDecl(
+ EmitContext* context,
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
+{
+ switch(context->target)
+ {
+ case CodeGenTarget::HLSL:
+ emitHLSLParameterBlockDecl(context, varDecl, parameterBlockType, layout);
+ break;
+
+ case CodeGenTarget::GLSL:
+ emitGLSLParameterBlockDecl(context, varDecl, parameterBlockType, layout);
+ break;
+
+ default:
+ assert(!"unexpected");
+ break;
+ }
+}
+
+static void EmitVarDecl(EmitContext* context, RefPtr<VarDeclBase> decl, RefPtr<VarLayout> 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 parameterBlockType = decl->Type->As<ParameterBlockType>())
+ {
+ emitParameterBlockDecl(context, decl, parameterBlockType, layout);
+ return;
+ }
+
+ emitGLSLLayoutQualifiers(context, layout);
+
+ EmitVarDeclCommon(context, decl);
+
+ emitHLSLRegisterSemantics(context, layout);
+
+ Emit(context, ";\n");
+}
+
+static void EmitParamDecl(EmitContext* context, RefPtr<ParameterSyntaxNode> decl)
+{
+ EmitVarDeclCommon(context, decl);
+}
+
+static void EmitFuncDecl(EmitContext* context, RefPtr<FunctionSyntaxNode> decl)
+{
+ EmitModifiers(context, 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(context, decl->ReturnType, decl->Name.Content);
+
+ Emit(context, "(");
+ bool first = true;
+ for (auto paramDecl : decl->GetMembersOfType<ParameterSyntaxNode>())
+ {
+ if (!first) Emit(context, ", ");
+ EmitParamDecl(context, paramDecl);
+ first = false;
+ }
+ Emit(context, ")");
+
+ EmitSemantics(context, decl);
+
+ if (auto bodyStmt = decl->Body)
+ {
+ EmitBlockStmt(context, bodyStmt);
+ }
+ else
+ {
+ Emit(context, ";\n");
+ }
+}
+
+static void emitGLSLPreprocessorDirectives(
+ EmitContext* context,
+ RefPtr<ProgramSyntaxNode> program)
+{
+ switch(context->target)
+ {
+ // Don't emit this stuff unless we are targetting GLSL
+ default:
+ return;
+
+ case CodeGenTarget::GLSL:
+ break;
+ }
+
+ if( auto versionDirective = program->FindModifier<GLSLVersionDirective>() )
+ {
+ // TODO(tfoley): Emit an appropriate `#line` directive...
+
+ Emit(context, "#version ");
+ emit(context, versionDirective->versionNumberToken.Content);
+ if(versionDirective->glslProfileToken.Type != TokenType::Unknown)
+ {
+ Emit(context, " ");
+ emit(context, versionDirective->glslProfileToken.Content);
+ }
+ Emit(context, "\n");
+ }
+ else
+ {
+ // No explicit version was given (probably because we are cross-compiling).
+ //
+ // We need to pick an appropriate version, ideally based on the features
+ // that the shader ends up using.
+ //
+ // For now we just fall back to a reasonably recent version.
+
+ Emit(context, "#version 420\n");
+ }
+
+ // TODO: when cross-compiling we may need to output additional `#extension` directives
+ // based on the features that we have used.
+
+ for( auto extensionDirective : program->GetModifiersOfType<GLSLExtensionDirective>() )
+ {
+ // TODO(tfoley): Emit an appropriate `#line` directive...
+
+ Emit(context, "#extension ");
+ emit(context, extensionDirective->extensionNameToken.Content);
+ Emit(context, " : ");
+ emit(context, extensionDirective->dispositionToken.Content);
+ Emit(context, "\n");
+ }
+
+ // TODO: handle other cases...
+}
+
+static void EmitProgram(
+ EmitContext* context,
+ RefPtr<ProgramSyntaxNode> program,
+ RefPtr<ProgramLayout> programLayout)
+{
+ // There may be global-scope modifiers that we should emit now
+ emitGLSLPreprocessorDirectives(context, program);
+
+ switch(context->target)
+ {
+ case CodeGenTarget::GLSL:
+ {
+ // TODO(tfoley): Need a plan for how to enable/disable these as needed...
+// Emit(context, "#extension GL_GOOGLE_cpp_style_line_directive : require\n");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+
+ // Layout information for the global scope is either an ordinary
+ // `struct` in the common case, or a constant buffer in the case
+ // where there were global-scope uniforms.
+ auto globalScopeLayout = programLayout->globalScopeLayout;
+ if( auto globalStructLayout = globalScopeLayout.As<StructTypeLayout>() )
+ {
+ // The `struct` case is easy enough to handle: we just
+ // emit all the declarations directly, using their layout
+ // information as a guideline.
+ EmitDeclsInContainerUsingLayout(context, program, globalStructLayout);
+ }
+ else if(auto globalConstantBufferLayout = globalScopeLayout.As<ParameterBlockTypeLayout>())
+ {
+ // TODO: the `cbuffer` case really needs to be emitted very
+ // carefully, but that is beyond the scope of what a simple rewriter
+ // can easily do (without semantic analysis, etc.).
+ //
+ // The crux of the problem is that we need to collect all the
+ // global-scope uniforms (but not declarations that don't involve
+ // uniform storage...) and put them in a single `cbuffer` declaration,
+ // so that we can give it an explicit location. The fields in that
+ // declaration might use various type declarations, so we'd really
+ // need to emit all the type declarations first, and that involves
+ // some large scale reorderings.
+ //
+ // For now we will punt and just emit the declarations normally,
+ // and hope that the global-scope block (`$Globals`) gets auto-assigned
+ // the same location that we manually asigned it.
+
+ auto elementTypeLayout = globalConstantBufferLayout->elementTypeLayout;
+ auto elementTypeStructLayout = elementTypeLayout.As<StructTypeLayout>();
+
+ // We expect all constant buffers to contain `struct` types for now
+ assert(elementTypeStructLayout);
+
+ EmitDeclsInContainerUsingLayout(
+ context,
+ program,
+ elementTypeStructLayout);
+ }
+ else
+ {
+ assert(!"unexpected");
+ }
+}
+
+static void EmitDeclImpl(EmitContext* context, RefPtr<Decl> decl, RefPtr<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;
+
+ if (auto typeDefDecl = decl.As<TypeDefDecl>())
+ {
+ EmitTypeDefDecl(context, typeDefDecl);
+ return;
+ }
+ else if (auto structDecl = decl.As<StructSyntaxNode>())
+ {
+ EmitStructDecl(context, structDecl);
+ return;
+ }
+ else if (auto varDecl = decl.As<VarDeclBase>())
+ {
+ EmitVarDecl(context, varDecl, layout);
+ return;
+ }
+ else if (auto funcDecl = decl.As<FunctionSyntaxNode>())
+ {
+ EmitFuncDecl(context, funcDecl);
+ return;
+ }
+ else if (auto genericDecl = decl.As<GenericDecl>())
+ {
+ // Don't emit generic decls directly; we will only
+ // ever emit particular instantiations of them.
+ return;
+ }
+ else if (auto classDecl = decl.As<ClassSyntaxNode>())
+ {
+ return;
+ }
+ else if( auto emptyDecl = decl.As<EmptyDecl>() )
+ {
+ EmitModifiers(context, emptyDecl);
+ Emit(context, ";\n");
+ return;
+ }
+ throw "unimplemented";
+}
+
+static void EmitDecl(EmitContext* context, RefPtr<Decl> decl)
+{
+ EmitDeclImpl(context, decl, nullptr);
+}
+
+static void EmitDeclUsingLayout(EmitContext* context, RefPtr<Decl> decl, RefPtr<VarLayout> layout)
+{
+ EmitDeclImpl(context, decl, layout);
+}
+
+static void EmitDecl(EmitContext* context, RefPtr<DeclBase> declBase)
+{
+ if( auto decl = declBase.As<Decl>() )
+ {
+ EmitDecl(context, decl);
+ }
+ else if(auto declGroup = declBase.As<DeclGroup>())
+ {
+ for(auto d : declGroup->decls)
+ EmitDecl(context, d);
+ }
+ else
+ {
+ throw "unimplemented";
+ }
+}
+
+static void registerReservedWord(
+ EmitContext* context,
+ String const& name)
+{
+ context->reservedWords.Add(name, name);
+}
+
+static void registerReservedWords(
+ EmitContext* context)
+{
+#define WORD(NAME) registerReservedWord(context, #NAME)
+
+ switch (context->target)
+ {
+ case CodeGenTarget::GLSL:
+ WORD(attribute);
+ WORD(const);
+ WORD(uniform);
+ WORD(varying);
+ WORD(buffer);
+
+ WORD(shared);
+ WORD(coherent);
+ WORD(volatile);
+ WORD(restrict);
+ WORD(readonly);
+ WORD(writeonly);
+ WORD(atomic_unit);
+ WORD(layout);
+ WORD(centroid);
+ WORD(flat);
+ WORD(smooth);
+ WORD(noperspective);
+ WORD(patch);
+ WORD(sample);
+ WORD(break);
+ WORD(continue);
+ WORD(do);
+ WORD(for);
+ WORD(while);
+ WORD(switch);
+ WORD(case);
+ WORD(default);
+ WORD(if);
+ WORD(else);
+ WORD(subroutine);
+ WORD(in);
+ WORD(out);
+ WORD(inout);
+ WORD(float);
+ WORD(double);
+ WORD(int);
+ WORD(void);
+ WORD(bool);
+ WORD(true);
+ WORD(false);
+ WORD(invariant);
+ WORD(precise);
+ WORD(discard);
+ WORD(return);
+
+ WORD(lowp);
+ WORD(mediump);
+ WORD(highp);
+ WORD(precision);
+ WORD(struct);
+ WORD(uint);
+
+ WORD(common);
+ WORD(partition);
+ WORD(active);
+ WORD(asm);
+ WORD(class);
+ WORD(union);
+ WORD(enum);
+ WORD(typedef);
+ WORD(template);
+ WORD(this);
+ WORD(resource);
+
+ WORD(goto);
+ WORD(inline);
+ WORD(noinline);
+ WORD(public);
+ WORD(static);
+ WORD(extern);
+ WORD(external);
+ WORD(interface);
+ WORD(long);
+ WORD(short);
+ WORD(half);
+ WORD(fixed);
+ WORD(unsigned);
+ WORD(superp);
+ WORD(input);
+ WORD(output);
+ WORD(filter);
+ WORD(sizeof);
+ WORD(cast);
+ WORD(namespace);
+ WORD(using);
+
+#define CASE(NAME) \
+ WORD(NAME ## 2); WORD(NAME ## 3); WORD(NAME ## 4)
+
+ CASE(mat);
+ CASE(dmat);
+ CASE(mat2x);
+ CASE(mat3x);
+ CASE(mat4x);
+ CASE(dmat2x);
+ CASE(dmat3x);
+ CASE(dmat4x);
+ CASE(vec);
+ CASE(ivec);
+ CASE(bvec);
+ CASE(dvec);
+ CASE(uvec);
+ CASE(hvec);
+ CASE(fvec);
+
+#undef CASE
+
+#define CASE(NAME) \
+ WORD(NAME ## 1D); \
+ WORD(NAME ## 2D); \
+ WORD(NAME ## 3D); \
+ WORD(NAME ## Cube); \
+ WORD(NAME ## 1DArray); \
+ WORD(NAME ## 2DArray); \
+ WORD(NAME ## 3DArray); \
+ WORD(NAME ## CubeArray);\
+ WORD(NAME ## 2DMS); \
+ WORD(NAME ## 2DMSArray) \
+ /* end */
+
+#define CASE2(NAME) \
+ CASE(NAME); \
+ CASE(i ## NAME); \
+ CASE(u ## NAME) \
+ /* end */
+
+ CASE2(sampler);
+ CASE2(image);
+ CASE2(texture);
+
+#undef CASE2
+#undef CASE
+ break;
+
+ default:
+ break;
+ }
+}
+
+String emitProgram(
+ ProgramSyntaxNode* program,
+ ProgramLayout* programLayout,
+ CodeGenTarget target)
+{
+ // TODO(tfoley): only emit symbols on-demand, as needed by a particular entry point
+
+ EmitContext context;
+ context.target = target;
+
+ registerReservedWords(&context);
+
+ EmitProgram(&context, program, programLayout);
+
+ String code = context.sb.ProduceString();
+
+ return code;
+
+#if 0
+ // HACK(tfoley): Invoke the D3D HLSL compiler on the result, to validate it
+
+#ifdef _WIN32
+ {
+ HMODULE d3dCompiler = LoadLibraryA("d3dcompiler_47");
+ assert(d3dCompiler);
+
+ pD3DCompile D3DCompile_ = (pD3DCompile)GetProcAddress(d3dCompiler, "D3DCompile");
+ assert(D3DCompile_);
+
+ ID3DBlob* codeBlob;
+ ID3DBlob* diagnosticsBlob;
+ HRESULT hr = D3DCompile_(
+ code.begin(),
+ code.Length(),
+ "slang",
+ nullptr,
+ nullptr,
+ "main",
+ "ps_5_0",
+ 0,
+ 0,
+ &codeBlob,
+ &diagnosticsBlob);
+ if (codeBlob) codeBlob->Release();
+ if (diagnosticsBlob)
+ {
+ String diagnostics = (char const*) diagnosticsBlob->GetBufferPointer();
+ fprintf(stderr, "%s", diagnostics.begin());
+ OutputDebugStringA(diagnostics.begin());
+ diagnosticsBlob->Release();
+ }
+ if (FAILED(hr))
+ {
+ int f = 9;
+ }
+ }
+
+ #include <d3dcompiler.h>
+#endif
+#endif
+
+}
+
+
+}} // Slang::Compiler