summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-07-07 15:06:16 -0700
committerGitHub <noreply@github.com>2017-07-07 15:06:16 -0700
commitc07a6e6b5f5e0e4839b435ff6c15b821b6dead11 (patch)
tree8107b45190a0d8a33d07dcd6500ed0dfa939d530 /source
parent975e4b326cd2ef3ef0341d1fb7509315b9dee555 (diff)
parentf3fe9dddb8c528b4f9955d9105908600b4a8d0c8 (diff)
Merge pull request #59 from tfoleyNV/cross-compilation
More work on cross compilation
Diffstat (limited to 'source')
-rw-r--r--source/slang/check.cpp108
-rw-r--r--source/slang/emit.cpp4776
-rw-r--r--source/slang/lower.cpp117
-rw-r--r--source/slang/slang-stdlib.cpp43
-rw-r--r--source/slang/syntax.h5
-rw-r--r--source/slang/visitor.h219
6 files changed, 2825 insertions, 2443 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 2d61c60a4..7c67473be 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -1072,7 +1072,7 @@ namespace Slang
EnsureDecl(decl, DeclCheckState::Checked);
}
- void visit(GenericDecl* genericDecl)
+ void visitGenericDecl(GenericDecl* genericDecl)
{
// check the parameters
for (auto m : genericDecl->Members)
@@ -1097,12 +1097,12 @@ namespace Slang
checkDecl(genericDecl->inner);
}
- void visit(InterfaceDecl* /*decl*/)
+ void visitInterfaceDecl(InterfaceDecl* /*decl*/)
{
// TODO: do some actual checking of members here
}
- void visit(InheritanceDecl* inheritanceDecl)
+ void visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
// check the type being inherited from
auto base = inheritanceDecl->base;
@@ -1144,27 +1144,27 @@ namespace Slang
return constIntVal;
}
- void visit(ModifierDecl*)
+ void visitModifierDecl(ModifierDecl*)
{
// These are only used in the stdlib, so no checking is needed
}
- void visit(GenericTypeParamDecl*)
+ void visitGenericTypeParamDecl(GenericTypeParamDecl*)
{
// These are only used in the stdlib, so no checking is needed for now
}
- void visit(GenericValueParamDecl*)
+ void visitGenericValueParamDecl(GenericValueParamDecl*)
{
// These are only used in the stdlib, so no checking is needed for now
}
- void visit(GenericTypeConstraintDecl*)
+ void visitGenericTypeConstraintDecl(GenericTypeConstraintDecl*)
{
// These are only used in the stdlib, so no checking is needed for now
}
- void visit(Modifier*)
+ void visitModifier(Modifier*)
{
// Do nothing with modifiers for now
}
@@ -1270,7 +1270,7 @@ namespace Slang
decl->modifiers.first = resultModifiers;
}
- void visit(ProgramSyntaxNode* programNode)
+ void visitProgramSyntaxNode(ProgramSyntaxNode* programNode)
{
// Try to register all the builtin decls
for (auto decl : programNode->Members)
@@ -1347,7 +1347,7 @@ namespace Slang
}
}
- void visit(ClassSyntaxNode * classNode)
+ void visitClassSyntaxNode(ClassSyntaxNode * classNode)
{
if (classNode->IsChecked(DeclCheckState::Checked))
return;
@@ -1360,7 +1360,7 @@ namespace Slang
}
}
- void visit(StructField* field)
+ void visitStructField(StructField* field)
{
// TODO: bottleneck through general-case variable checking
@@ -1368,7 +1368,7 @@ namespace Slang
field->SetCheckState(DeclCheckState::Checked);
}
- void visit(StructSyntaxNode * structNode)
+ void visitStructSyntaxNode(StructSyntaxNode * structNode)
{
if (structNode->IsChecked(DeclCheckState::Checked))
return;
@@ -1380,7 +1380,7 @@ namespace Slang
}
}
- void visit(DeclGroup* declGroup)
+ void visitDeclGroup(DeclGroup* declGroup)
{
for (auto decl : declGroup->decls)
{
@@ -1388,7 +1388,7 @@ namespace Slang
}
}
- void visit(TypeDefDecl* decl)
+ void visitTypeDefDecl(TypeDefDecl* decl)
{
if (decl->IsChecked(DeclCheckState::Checked)) return;
@@ -1403,7 +1403,7 @@ namespace Slang
StmtVisitor::dispatch(stmt);
}
- void visit(FunctionSyntaxNode *functionNode)
+ void visitFunctionSyntaxNode(FunctionSyntaxNode *functionNode)
{
if (functionNode->IsChecked(DeclCheckState::Checked))
return;
@@ -1528,12 +1528,12 @@ namespace Slang
}
}
- void visit(ScopeDecl*)
+ void visitScopeDecl(ScopeDecl*)
{
// Nothing to do
}
- void visit(ParameterSyntaxNode* para)
+ void visitParameterSyntaxNode(ParameterSyntaxNode* para)
{
// TODO: This needs to bottleneck through the common variable checks
@@ -1567,7 +1567,7 @@ namespace Slang
ValidateFunctionRedeclaration(functionNode);
}
- void visit(VarDeclrStatementSyntaxNode* stmt)
+ void visitVarDeclrStatementSyntaxNode(VarDeclrStatementSyntaxNode* stmt)
{
// We directly dispatch here instead of using `EnsureDecl()` for two
// reasons:
@@ -1581,12 +1581,12 @@ namespace Slang
DeclVisitor::dispatch(stmt->decl);
}
- void visit(BlockStmt* stmt)
+ void visitBlockStmt(BlockStmt* stmt)
{
checkStmt(stmt->body);
}
- void visit(SeqStmt* stmt)
+ void visitSeqStmt(SeqStmt* stmt)
{
for(auto ss : stmt->stmts)
{
@@ -1608,7 +1608,7 @@ namespace Slang
return nullptr;
}
- void visit(BreakStatementSyntaxNode *stmt)
+ void visitBreakStatementSyntaxNode(BreakStatementSyntaxNode *stmt)
{
auto outer = FindOuterStmt<BreakableStmt>();
if (!outer)
@@ -1617,7 +1617,7 @@ namespace Slang
}
stmt->parentStmt = outer;
}
- void visit(ContinueStatementSyntaxNode *stmt)
+ void visitContinueStatementSyntaxNode(ContinueStatementSyntaxNode *stmt)
{
auto outer = FindOuterStmt<LoopStmt>();
if (!outer)
@@ -1645,7 +1645,7 @@ namespace Slang
return e;
}
- void visit(DoWhileStatementSyntaxNode *stmt)
+ void visitDoWhileStatementSyntaxNode(DoWhileStatementSyntaxNode *stmt)
{
PushOuterStmt(stmt);
stmt->Predicate = checkPredicateExpr(stmt->Predicate);
@@ -1653,7 +1653,7 @@ namespace Slang
PopOuterStmt(stmt);
}
- void visit(ForStatementSyntaxNode *stmt)
+ void visitForStatementSyntaxNode(ForStatementSyntaxNode *stmt)
{
PushOuterStmt(stmt);
checkStmt(stmt->InitialStatement);
@@ -1669,7 +1669,7 @@ namespace Slang
PopOuterStmt(stmt);
}
- void visit(SwitchStmt* stmt)
+ void visitSwitchStmt(SwitchStmt* stmt)
{
PushOuterStmt(stmt);
// TODO(tfoley): need to coerce condition to an integral type...
@@ -1682,7 +1682,7 @@ namespace Slang
PopOuterStmt(stmt);
}
- void visit(CaseStmt* stmt)
+ void visitCaseStmt(CaseStmt* stmt)
{
// TODO(tfoley): Need to coerce to type being switch on,
// and ensure that value is a compile-time constant
@@ -1702,7 +1702,7 @@ namespace Slang
stmt->expr = expr;
stmt->parentStmt = switchStmt;
}
- void visit(DefaultStmt* stmt)
+ void visitDefaultStmt(DefaultStmt* stmt)
{
auto switchStmt = FindOuterStmt<SwitchStmt>();
if (!switchStmt)
@@ -1711,30 +1711,30 @@ namespace Slang
}
stmt->parentStmt = switchStmt;
}
- void visit(IfStatementSyntaxNode *stmt)
+ void visitIfStatementSyntaxNode(IfStatementSyntaxNode *stmt)
{
stmt->Predicate = checkPredicateExpr(stmt->Predicate);
checkStmt(stmt->PositiveStatement);
checkStmt(stmt->NegativeStatement);
}
- void visit(UnparsedStmt*)
+ void visitUnparsedStmt(UnparsedStmt*)
{
// Nothing to do
}
- void visit(EmptyStatementSyntaxNode*)
+ void visitEmptyStatementSyntaxNode(EmptyStatementSyntaxNode*)
{
// Nothing to do
}
- void visit(DiscardStatementSyntaxNode*)
+ void visitDiscardStatementSyntaxNode(DiscardStatementSyntaxNode*)
{
// Nothing to do
}
- void visit(ReturnStatementSyntaxNode *stmt)
+ void visitReturnStatementSyntaxNode(ReturnStatementSyntaxNode *stmt)
{
if (!stmt->Expression)
{
@@ -1837,7 +1837,7 @@ namespace Slang
}
}
- void visit(Variable* varDecl)
+ void visitVariable(Variable* varDecl)
{
TypeExp typeExp = CheckUsableType(varDecl->Type);
#if 0
@@ -1887,19 +1887,19 @@ namespace Slang
varDecl->SetCheckState(DeclCheckState::Checked);
}
- void visit(WhileStatementSyntaxNode *stmt)
+ void visitWhileStatementSyntaxNode(WhileStatementSyntaxNode *stmt)
{
PushOuterStmt(stmt);
stmt->Predicate = checkPredicateExpr(stmt->Predicate);
checkStmt(stmt->Statement);
PopOuterStmt(stmt);
}
- void visit(ExpressionStatementSyntaxNode *stmt)
+ void visitExpressionStatementSyntaxNode(ExpressionStatementSyntaxNode *stmt)
{
stmt->Expression = CheckExpr(stmt->Expression);
}
- RefPtr<ExpressionSyntaxNode> visit(ConstantExpressionSyntaxNode *expr)
+ RefPtr<ExpressionSyntaxNode> visitConstantExpressionSyntaxNode(ConstantExpressionSyntaxNode *expr)
{
// The expression might already have a type, determined by its suffix
if(expr->Type.type)
@@ -2216,7 +2216,7 @@ namespace Slang
return DeclRefType::Create(declRef)->As<VectorExpressionType>();
}
- RefPtr<ExpressionSyntaxNode> visit(IndexExpressionSyntaxNode* subscriptExpr)
+ RefPtr<ExpressionSyntaxNode> visitIndexExpressionSyntaxNode(IndexExpressionSyntaxNode* subscriptExpr)
{
auto baseExpr = subscriptExpr->BaseExpression;
baseExpr = CheckExpr(baseExpr);
@@ -2421,7 +2421,7 @@ namespace Slang
//
- RefPtr<ExpressionSyntaxNode> visit(AssignExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitAssignExpr(AssignExpr* expr)
{
expr->left = CheckExpr(expr->left);
@@ -2440,7 +2440,7 @@ namespace Slang
//
- void visit(ExtensionDecl* decl)
+ void visitExtensionDecl(ExtensionDecl* decl)
{
if (decl->IsChecked(DeclCheckState::Checked)) return;
@@ -2483,7 +2483,7 @@ namespace Slang
decl->SetCheckState(DeclCheckState::Checked);
}
- void visit(ConstructorDecl* decl)
+ void visitConstructorDecl(ConstructorDecl* decl)
{
if (decl->IsChecked(DeclCheckState::Checked)) return;
decl->SetCheckState(DeclCheckState::CheckingHeader);
@@ -2499,7 +2499,7 @@ namespace Slang
}
- void visit(SubscriptDecl* decl)
+ void visitSubscriptDecl(SubscriptDecl* decl)
{
if (decl->IsChecked(DeclCheckState::Checked)) return;
decl->SetCheckState(DeclCheckState::CheckingHeader);
@@ -2516,7 +2516,7 @@ namespace Slang
decl->SetCheckState(DeclCheckState::Checked);
}
- void visit(AccessorDecl* decl)
+ void visitAccessorDecl(AccessorDecl* decl)
{
// TODO: check the body!
@@ -4369,7 +4369,7 @@ namespace Slang
}
}
- RefPtr<ExpressionSyntaxNode> visit(GenericAppExpr * genericAppExpr)
+ RefPtr<ExpressionSyntaxNode> visitGenericAppExpr(GenericAppExpr * genericAppExpr)
{
// We are applying a generic to arguments, but there might be multiple generic
// declarations with the same name, so this becomes a specialized case of
@@ -4520,7 +4520,7 @@ namespace Slang
#endif
}
- RefPtr<ExpressionSyntaxNode> visit(SharedTypeExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitSharedTypeExpr(SharedTypeExpr* expr)
{
if (!expr->Type.Ptr())
{
@@ -4578,7 +4578,7 @@ namespace Slang
return rs;
}
- RefPtr<ExpressionSyntaxNode> visit(InvokeExpressionSyntaxNode *expr)
+ RefPtr<ExpressionSyntaxNode> visitInvokeExpressionSyntaxNode(InvokeExpressionSyntaxNode *expr)
{
// check the base expression first
expr->FunctionExpr = CheckExpr(expr->FunctionExpr);
@@ -4593,7 +4593,7 @@ namespace Slang
}
- RefPtr<ExpressionSyntaxNode> visit(VarExpressionSyntaxNode *expr)
+ RefPtr<ExpressionSyntaxNode> visitVarExpressionSyntaxNode(VarExpressionSyntaxNode *expr)
{
// If we've already resolved this expression, don't try again.
if (expr->declRef)
@@ -4615,7 +4615,7 @@ namespace Slang
return expr;
}
- RefPtr<ExpressionSyntaxNode> visit(TypeCastExpressionSyntaxNode * expr)
+ RefPtr<ExpressionSyntaxNode> visitTypeCastExpressionSyntaxNode(TypeCastExpressionSyntaxNode * expr)
{
expr->Expression = CheckTerm(expr->Expression);
auto targetType = CheckProperType(expr->TargetType);
@@ -4694,19 +4694,19 @@ namespace Slang
// deal with this cases here, even if they are no-ops.
//
- RefPtr<ExpressionSyntaxNode> visit(DerefExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitDerefExpr(DerefExpr* expr)
{
assert(!"unexpected");
return expr;
}
- RefPtr<ExpressionSyntaxNode> visit(SwizzleExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitSwizzleExpr(SwizzleExpr* expr)
{
assert(!"unexpected");
return expr;
}
- RefPtr<ExpressionSyntaxNode> visit(OverloadedExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitOverloadedExpr(OverloadedExpr* expr)
{
assert(!"unexpected");
return expr;
@@ -4850,7 +4850,7 @@ namespace Slang
}
- RefPtr<ExpressionSyntaxNode> visit(MemberExpressionSyntaxNode * expr)
+ RefPtr<ExpressionSyntaxNode> visitMemberExpressionSyntaxNode(MemberExpressionSyntaxNode * expr)
{
expr->BaseExpression = CheckExpr(expr->BaseExpression);
@@ -4949,7 +4949,7 @@ namespace Slang
//
- RefPtr<ExpressionSyntaxNode> visit(InitializerListExpr* expr)
+ RefPtr<ExpressionSyntaxNode> visitInitializerListExpr(InitializerListExpr* expr)
{
// When faced with an initializer list, we first just check the sub-expressions blindly.
// Actually making them conform to a desired type will wait for when we know the desired
@@ -4995,12 +4995,12 @@ namespace Slang
}
}
- void visit(EmptyDecl* /*decl*/)
+ void visitEmptyDecl(EmptyDecl* /*decl*/)
{
// nothing to do
}
- void visit(ImportDecl* decl)
+ void visitImportDecl(ImportDecl* decl)
{
if(decl->IsChecked(DeclCheckState::Checked))
return;
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 08d2218dc..df74d19e4 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -4,6 +4,7 @@
#include "lower.h"
#include "syntax.h"
#include "type-layout.h"
+#include "visitor.h"
#include <assert.h>
@@ -64,1283 +65,726 @@ struct EmitContext
//
-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, Token const& nameToken);
-static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, String const& name);
-static void EmitType(EmitContext* context, RefPtr<ExpressionType> type);
-
-static void EmitType(EmitContext* context, TypeExp const& typeExp, Token const& nameToken);
-static void EmitType(EmitContext* context, TypeExp const& typeExp, String const& name);
-static void EmitType(EmitContext* context, TypeExp const& typeExp);
-
-static void EmitExpr(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr);
-static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt);
-static void EmitDeclRef(EmitContext* context, DeclRef<Decl> declRef);
-
-static void advanceToSourceLocation(
- EmitContext* context,
- CodePosition const& sourceLocation);
-
-static void flushSourceLocationChange(
- EmitContext* context);
-
-// 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->shared->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)
-{
- // If the source location has changed in a way that required update,
- // do it now!
- flushSourceLocationChange(context);
-
- // 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->shared->loc.Col += len;
-}
-
-static void Emit(EmitContext* context, char const* textBegin, char const* textEnd)
+static String getStringOrIdentifierTokenValue(
+ Token const& token)
{
- char const* spanBegin = textBegin;
-
- char const* spanEnd = spanBegin;
- for(;;)
+ switch(token.Type)
{
- if(spanEnd == textEnd)
- {
- // We have a whole range of text waiting to be flushed
- emitTextSpan(context, spanBegin, spanEnd);
- return;
- }
-
- auto c = *spanEnd++;
+ default:
+ assert(!"unexpected");
+ return "";
- if( c == '\n' )
- {
- // At the end of a line, we need to update our tracking
- // information on code positions
- emitTextSpan(context, spanBegin, spanEnd);
- context->shared->loc.Line++;
- context->shared->loc.Col = 1;
+ case TokenType::Identifier:
+ return token.Content;
- // Start a new span for emit purposes
- spanBegin = spanEnd;
- }
+ case TokenType::StringLiteral:
+ return getStringLiteralTokenValue(token);
+ break;
}
}
-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->shared->reservedWords.TryGetValue(name) != nullptr;
-}
+//
-static void emitName(
- EmitContext* context,
- String const& inName,
- CodePosition const& loc)
+// represents a declarator for use in emitting types
+struct EDeclarator
{
- 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))
+ enum class Flavor
{
- name = name + "_";
- }
-
- advanceToSourceLocation(context, loc);
- emit(context, name);
-}
+ Name,
+ Array,
+ UnsizedArray,
+ };
+ Flavor flavor;
+ EDeclarator* next = nullptr;
-static void emitName(EmitContext* context, Token const& nameToken)
-{
- emitName(context, nameToken.Content, nameToken.Position);
-}
+ // Used for `Flavor::Name`
+ String name;
+ CodePosition loc;
-static void emitName(EmitContext* context, String const& name)
-{
- emitName(context, name, CodePosition());
-}
+ // Used for `Flavor::Array`
+ IntVal* elementCount;
+};
-static void Emit(EmitContext* context, IntegerLiteralValue value)
+struct TypeEmitArg
{
- char buffer[32];
- sprintf(buffer, "%lld", value);
- Emit(context, buffer);
-}
-
+ EDeclarator* declarator;
+};
-static void Emit(EmitContext* context, UInt value)
+struct ExprEmitArg
{
- char buffer[32];
- sprintf(buffer, "%llu", (unsigned long long)(value));
- Emit(context, buffer);
-}
+ int outerPrec;
+};
-static void Emit(EmitContext* context, int value)
+struct DeclEmitArg
{
- char buffer[16];
- sprintf(buffer, "%d", value);
- Emit(context, buffer);
-}
+ VarLayout* layout;
+};
-static void Emit(EmitContext* context, double value)
+struct EmitVisitor
+ : TypeVisitorWithArg<EmitVisitor, TypeEmitArg>
+ , ExprVisitorWithArg<EmitVisitor, ExprEmitArg>
+ , DeclVisitorWithArg<EmitVisitor, DeclEmitArg>
{
- // TODO(tfoley): need to print things in a way that can round-trip
- char buffer[128];
- sprintf(buffer, "%.20ff", value);
- Emit(context, buffer);
-}
-
-static void emitTokenWithLocation(EmitContext* context, Token const& token);
+ EmitContext* context;
+ EmitVisitor(EmitContext* context)
+ : context(context)
+ {}
-// Expressions
+ // Low-level emit logic
-// 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>())
+ void emitRawTextSpan(char const* textBegin, char const* textEnd)
{
- e = derefExpr->base;
+ // TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things...
+ auto len = int(textEnd - textBegin);
+
+ context->shared->sb.Append(textBegin, len);
}
- // Is the expression referencing a constant buffer?
- if (auto cbufferType = e->Type->As<ConstantBufferType>())
+
+ void emitRawText(char const* text)
{
- return true;
+ emitRawTextSpan(text, text + strlen(text));
}
- 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);
-}
+ void emitTextSpan(char const* textBegin, char const* textEnd)
+ {
+ // If the source location has changed in a way that required update,
+ // do it now!
+ flushSourceLocationChange();
-static void EmitExpr(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr)
-{
- EmitExprWithPrecedence(context, expr, kPrecedence_General);
-}
+ // Emit the raw text
+ emitRawTextSpan(textBegin, textEnd);
-static bool MaybeEmitParens(EmitContext* context, int outerPrec, int prec)
-{
- if (prec <= outerPrec)
- {
- Emit(context, "(");
- return true;
+ // 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.Col += len;
}
- 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(;;)
+ void Emit(char const* textBegin, char const* textEnd)
{
- if(auto typeCastExpr = expr.As<TypeCastExpressionSyntaxNode>())
- {
- expr = typeCastExpr->Expression;
- }
- // TODO: any other cases?
- else
- {
- return expr;
- }
- }
+ char const* spanBegin = textBegin;
-}
+ char const* spanEnd = spanBegin;
+ for(;;)
+ {
+ if(spanEnd == textEnd)
+ {
+ // We have a whole range of text waiting to be flushed
+ emitTextSpan(spanBegin, spanEnd);
+ return;
+ }
-static void emitInfixExprImpl(
- EmitContext* context,
- int outerPrec,
- int prec,
- char const* op,
- RefPtr<InvokeExpressionSyntaxNode> binExpr,
- bool isAssign)
-{
- bool needsClose = MaybeEmitParens(context, outerPrec, prec);
+ auto c = *spanEnd++;
- auto left = binExpr->Arguments[0];
- if(isAssign)
- {
- left = prepareLValueExpr(context, left);
+ if( c == '\n' )
+ {
+ // At the end of a line, we need to update our tracking
+ // information on code positions
+ emitTextSpan(spanBegin, spanEnd);
+ context->shared->loc.Line++;
+ context->shared->loc.Col = 1;
+
+ // Start a new span for emit purposes
+ spanBegin = spanEnd;
+ }
+ }
}
- EmitExprWithPrecedence(context, left, prec);
- Emit(context, " ");
- Emit(context, op);
- Emit(context, " ");
- EmitExprWithPrecedence(context, binExpr->Arguments[1], prec);
- if (needsClose)
+ void Emit(char const* text)
{
- Emit(context, ")");
+ Emit(text, text + strlen(text));
}
-}
-
-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)
+ void emit(String const& text)
{
- arg = prepareLValueExpr(context, arg);
+ Emit(text.begin(), text.end());
}
- EmitExprWithPrecedence(context, arg, prec);
- Emit(context, postOp);
- if (needsClose)
+ bool isReservedWord(String const& name)
{
- Emit(context, ")");
+ return context->shared->reservedWords.TryGetValue(name) != nullptr;
}
-}
-
-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);
-}
+ void emitName(
+ String const& inName,
+ CodePosition const& loc)
+ {
+ String name = inName;
-// Determine if a target intrinsic modifer is applicable to the target
-// we are currently emitting code for.
-static bool isTargetIntrinsicModifierApplicable(
- EmitContext* context,
- RefPtr<TargetIntrinsicModifier> modifier)
-{
- auto const& targetToken = modifier->targetToken;
+ // 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 no target name was specified, then the modifier implicitly
- // applies to all targets.
- if(targetToken.Type == TokenType::Unknown)
- return true;
+ if (isReservedWord(name))
+ {
+ name = name + "_";
+ }
- // Otherwise, we need to check if the target name matches what
- // we expect.
- auto const& targetName = targetToken.Content;
+ advanceToSourceLocation(loc);
+ emit(name);
+ }
- switch(context->shared->target)
+ void emitName(Token const& nameToken)
{
- default:
- assert(!"unexpected");
- return false;
-
- case CodeGenTarget::GLSL: return targetName == "glsl";
- case CodeGenTarget::HLSL: return targetName == "hlsl";
+ emitName(nameToken.Content, nameToken.Position);
}
-}
-// Find an intrinsic modifier appropriate to the current compilation target.
-//
-// If there are multiple such modifiers, this should return the best one.
-static RefPtr<TargetIntrinsicModifier> findTargetIntrinsicModifier(
- EmitContext* context,
- RefPtr<ModifiableSyntaxNode> syntax)
-{
- RefPtr<TargetIntrinsicModifier> bestModifier;
- for(auto m : syntax->GetModifiersOfType<TargetIntrinsicModifier>())
+ void emitName(String const& name)
{
- if(!isTargetIntrinsicModifierApplicable(context, 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;
- }
+ emitName(name, CodePosition());
}
- return bestModifier;
-}
-
-static String getStringOrIdentifierTokenValue(
- Token const& token)
-{
- switch(token.Type)
+ void Emit(IntegerLiteralValue value)
{
- default:
- assert(!"unexpected");
- return "";
-
- case TokenType::Identifier:
- return token.Content;
-
- case TokenType::StringLiteral:
- return getStringLiteralTokenValue(token);
- break;
+ char buffer[32];
+ sprintf(buffer, "%lld", value);
+ Emit(buffer);
}
-}
-// Emit a call expression that doesn't involve any special cases,
-// just an expression of the form `f(a0, a1, ...)`
-static void emitSimpleCallExpr(
- EmitContext* context,
- RefPtr<InvokeExpressionSyntaxNode> callExpr,
- int outerPrec)
-{
- bool needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Postfix);
- auto funcExpr = callExpr->FunctionExpr;
- if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
+ void Emit(UInt value)
{
- auto declRef = funcDeclRefExpr->declRef;
- if (auto ctorDeclRef = declRef.As<ConstructorDecl>())
- {
- // 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);
+ char buffer[32];
+ sprintf(buffer, "%llu", (unsigned long long)(value));
+ Emit(buffer);
}
- Emit(context, "(");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
+ void Emit(int value)
{
- if (aa != 0) Emit(context, ", ");
- EmitExpr(context, callExpr->Arguments[aa]);
+ char buffer[16];
+ sprintf(buffer, "%d", value);
+ Emit(buffer);
}
- Emit(context, ")");
- if (needClose)
+ void Emit(double value)
{
- Emit(context, ")");
+ // TODO(tfoley): need to print things in a way that can round-trip
+ char buffer[128];
+ sprintf(buffer, "%.20ff", value);
+ Emit(buffer);
}
-}
-static void emitCallExpr(
- EmitContext* context,
- RefPtr<InvokeExpressionSyntaxNode> callExpr,
- int outerPrec)
-{
- auto funcExpr = callExpr->FunctionExpr;
- if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
+
+ // Emit a `#line` directive to the output.
+ // Doesn't udpate state of source-location tracking.
+ void emitLineDirective(
+ CodePosition const& sourceLocation)
{
- auto funcDeclRef = funcDeclRefExpr->declRef;
- auto funcDecl = funcDeclRef.getDecl();
- if(!funcDecl)
+ emitRawText("\n#line ");
+
+ char buffer[16];
+ sprintf(buffer, "%d", sourceLocation.Line);
+ emitRawText(buffer);
+
+ emitRawText(" ");
+
+ if(context->shared->target == CodeGenTarget::GLSL)
{
- // 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>() )
- {
- EmitBinExpr(
- context,
- outerPrec,
- kPrecedence_Comma,
- funcDeclRefExpr->name.Buffer(),
- callExpr);
- }
- else if( auto prefixExpr = callExpr.As<PrefixExpr>() )
- {
- EmitUnaryExpr(
- context,
- outerPrec,
- kPrecedence_Prefix,
- funcDeclRefExpr->name.Buffer(),
- "",
- callExpr);
- }
- else if(auto postfixExpr = callExpr.As<PostfixExpr>())
- {
- EmitUnaryExpr(
- context,
- outerPrec,
- kPrecedence_Postfix,
- "",
- funcDeclRefExpr->name.Buffer(),
- callExpr);
- }
- else
+ auto path = sourceLocation.FileName;
+
+ // GLSL doesn't support the traditional form of a `#line` directive without
+ // an extension. Rather than depend on that extension we will output
+ // a directive in the traditional GLSL fashion.
+ //
+ // TODO: Add some kind of configuration where we require the appropriate
+ // extension and then emit a traditional line directive.
+
+ int id = 0;
+ if(!context->shared->mapGLSLSourcePathToID.TryGetValue(path, id))
{
- emitSimpleCallExpr(context, callExpr, outerPrec);
+ id = context->shared->glslSourceIDCount++;
+ context->shared->mapGLSLSourcePathToID.Add(path, id);
}
- return;
+
+ sprintf(buffer, "%d", id);
+ emitRawText(buffer);
}
- else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
+ else
{
- switch (intrinsicOpModifier->op)
+ // The simple case is to emit the path for the current source
+ // location. We need to be a little bit careful with this,
+ // because the path might include backslash characters if we
+ // are on Windows, and we want to canonicalize those over
+ // to forward slashes.
+ //
+ // TODO: Canonicalization like this should be done centrally
+ // in a module that tracks source files.
+
+ emitRawText("\"");
+ for(auto c : sourceLocation.FileName)
{
-#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->shared->target == CodeGenTarget::GLSL)
+ char charBuffer[] = { c, 0 };
+ switch(c)
{
- Emit(context, "dot(");
- EmitExpr(context, callExpr->Arguments[0]);
- Emit(context, ", ");
- EmitExpr(context, callExpr->Arguments[1]);
- Emit(context, ")");
- return;
- }
- break;
+ default:
+ emitRawText(charBuffer);
+ 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 incoming file path might use `/` and/or `\\` as
+ // a directory separator. We want to canonicalize this.
//
- // 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(context, "((");
- EmitExpr(context, callExpr->Arguments[1]);
- Emit(context, ") * (");
- EmitExpr(context, callExpr->Arguments[0]);
- Emit(context, "))");
- return;
+ // TODO: should probably canonicalize paths to not use backslash somewhere else
+ // in the compilation pipeline...
+ case '\\':
+ emitRawText("/");
+ break;
}
- break;
-
- default:
- break;
}
+ emitRawText("\"");
}
- else if(auto targetIntrinsicModifier = findTargetIntrinsicModifier(context, funcDecl))
- {
- if(targetIntrinsicModifier->definitionToken.Type != TokenType::Unknown)
- {
- auto name = getStringOrIdentifierTokenValue(targetIntrinsicModifier->definitionToken);
-
- if(name.IndexOf('$') < 0)
- {
- // 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(context, name);
- Emit(context, "(");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(context, ", ");
- EmitExpr(context, callExpr->Arguments[aa]);
- }
- Emit(context, ")");
- return;
- }
- else
- {
- // General case: we are going to emit some more complex text.
-
- UInt argCount = callExpr->Arguments.Count();
-
- Emit(context, "(");
-
- char const* cursor = name.begin();
- char const* end = name.end();
- while(cursor != end)
- {
- char c = *cursor++;
- if( c != '$' )
- {
- // Not an escape sequence
- emitRawTextSpan(context, &c, &c+1);
- continue;
- }
-
- assert(cursor != end);
-
- char d = *cursor++;
- assert(('0' <= d) && (d <= '9'));
- UInt argIndex = d - '0';
- assert((0 <= argIndex) && (argIndex < argCount));
- Emit(context, "(");
- EmitExpr(context, callExpr->Arguments[argIndex]);
- Emit(context, ")");
- }
-
- Emit(context, ")");
- }
+ emitRawText("\n");
+ }
- return;
- }
+ // Emit a `#line` directive to the output, and also
+ // ensure that source location tracking information
+ // is correct based on the directive we just output.
+ void emitLineDirectiveAndUpdateSourceLocation(
+ CodePosition const& sourceLocation)
+ {
+ emitLineDirective(sourceLocation);
+
+ context->shared->loc.FileName = sourceLocation.FileName;
+ context->shared->loc.Line = sourceLocation.Line;
+ context->shared->loc.Col = 1;
+ }
- // TODO: emit as approperiate for this target
+ void emitLineDirectiveIfNeeded(
+ CodePosition const& sourceLocation)
+ {
+ // Ignore invalid source locations
+ if(sourceLocation.Line <= 0)
+ return;
- // We might be calling an intrinsic subscript operation,
- // and should desugar it accordingly
- if(auto subscriptDeclRef = funcDeclRef.As<SubscriptDecl>())
+ // 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->shared->loc.FileName
+ || sourceLocation.Line != context->shared->loc.Line
+ || sourceLocation.Col < context->shared->loc.Col)
+ {
+ // Special case: if we are in the same file, and within a small number
+ // of lines of the target location, then go ahead and output newlines
+ // to get us caught up.
+ enum { kSmallLineCount = 3 };
+ auto lineDiff = sourceLocation.Line - context->shared->loc.Line;
+ if(sourceLocation.FileName == context->shared->loc.FileName
+ && sourceLocation.Line > context->shared->loc.Line
+ && lineDiff <= kSmallLineCount)
{
- // 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>())
+ for(int ii = 0; ii < lineDiff; ++ii )
{
-
- Emit(context, "(");
- EmitExpr(context, memberExpr->BaseExpression);
- Emit(context, ")[");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(context, ", ");
- EmitExpr(context, callExpr->Arguments[aa]);
- }
- Emit(context, "]");
- return;
+ Emit("\n");
}
+ assert(sourceLocation.Line == context->shared->loc.Line);
+ }
+ else
+ {
+ // Go ahead and output a `#line` directive to get us caught up
+ emitLineDirectiveAndUpdateSourceLocation(sourceLocation);
}
}
- }
-
- // Fall through to default handling...
- emitSimpleCallExpr(context, callExpr, outerPrec);
-}
-
-static void emitStringLiteral(
- EmitContext* context,
- String const& value)
-{
- emit(context, "\"");
- for (auto c : value)
- {
- // TODO: This needs a more complete implementation,
- // especially if we want to support Unicode.
- char buffer[] = { c, 0 };
- switch (c)
+ // 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->shared->loc.Col)
{
- default:
- emit(context, buffer);
- break;
-
- case '\"': emit(context, "\\\"");
- case '\'': emit(context, "\\\'");
- case '\\': emit(context, "\\\\");
- case '\n': emit(context, "\\n");
- case '\r': emit(context, "\\r");
- case '\t': emit(context, "\\t");
+ int delta = sourceLocation.Col - context->shared->loc.Col;
+ for( int ii = 0; ii < delta; ++ii )
+ {
+ emitRawText(" ");
+ }
+ context->shared->loc.Col = sourceLocation.Col;
}
}
- emit(context, "\"");
-}
-static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntaxNode> expr, int outerPrec)
-{
- bool needClose = false;
- if (auto selectExpr = expr.As<SelectExpressionSyntaxNode>())
+ void advanceToSourceLocation(
+ CodePosition const& sourceLocation)
{
- needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Conditional);
+ // Skip invalid locations
+ if(sourceLocation.Line <= 0)
+ return;
- 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 assignExpr = expr.As<AssignExpr>())
- {
- needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Assign);
- EmitExprWithPrecedence(context, assignExpr->left, kPrecedence_Assign);
- Emit(context, " = ");
- EmitExprWithPrecedence(context, assignExpr->right, kPrecedence_Assign);
+ context->shared->needToUpdateSourceLocation = true;
+ context->shared->nextSourceLocation = sourceLocation;
}
- 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...
+ void flushSourceLocationChange()
+ {
+ if(!context->shared->needToUpdateSourceLocation)
+ return;
- // TODO(tfoley): also, probably need to special case
- // this for places where we are using a built-in...
+ // Note: the order matters here, because trying to update
+ // the source location may involve outputting text that
+ // advances the location, and outputting text is what
+ // triggers this flush operation.
+ context->shared->needToUpdateSourceLocation = false;
+ emitLineDirectiveIfNeeded(context->shared->nextSourceLocation);
+ }
- auto base = memberExpr->BaseExpression;
- if (IsBaseExpressionImplicit(context, base))
+ void emitTokenWithLocation(Token const& token)
+ {
+ if( token.Position.FileName.Length() != 0 )
{
- // don't emit the base expression
+ advanceToSourceLocation(token.Position);
}
else
{
- EmitExprWithPrecedence(context, memberExpr->BaseExpression, kPrecedence_Postfix);
- Emit(context, ".");
+ // 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("\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(" ");
}
- emitName(context, memberExpr->declRef.GetName());
+ // Emit the raw textual content of the token
+ emit(token.Content);
}
- 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);
-
- // TODO: This won't be valid if we had to generate a qualified
- // reference for some reason.
- advanceToSourceLocation(context, varExpr->Position);
- // 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.
+ //
+ // Types
+ //
- if(varExpr->declRef)
+ void Emit(RefPtr<IntVal> val)
+ {
+ if(auto constantIntVal = val.As<ConstantIntVal>())
+ {
+ Emit(constantIntVal->value);
+ }
+ else if(auto varRefVal = val.As<GenericParamIntVal>())
{
- EmitDeclRef(context, varExpr->declRef);
+ EmitDeclRef(varRefVal->declRef);
}
else
{
- emitName(context, varExpr->name);
+ assert(!"unimplemented");
}
}
- 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>())
+
+ void EmitDeclarator(EDeclarator* declarator)
{
- needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Atomic);
+ if (!declarator) return;
- char const* suffix = "";
- auto type = litExpr->Type.type;
- switch (litExpr->ConstType)
+ Emit(" ");
+
+ switch (declarator->flavor)
{
- case ConstantExpressionSyntaxNode::ConstantType::Int:
- if(!type)
- {
- // Special case for "rewrite" mode
- emitTokenWithLocation(context, litExpr->token);
- break;
- }
- if(type->Equals(ExpressionType::GetInt()))
- {}
- else if(type->Equals(ExpressionType::GetUInt()))
- {
- suffix = "u";
- }
- else
- {
- assert(!"unimplemented");
- }
- Emit(context, litExpr->integerValue);
- Emit(context, suffix);
+ case EDeclarator::Flavor::Name:
+ emitName(declarator->name, declarator->loc);
break;
-
- case ConstantExpressionSyntaxNode::ConstantType::Float:
- if(!type)
+ case EDeclarator::Flavor::Array:
+ EmitDeclarator(declarator->next);
+ Emit("[");
+ if(auto elementCount = declarator->elementCount)
{
- // Special case for "rewrite" mode
- emitTokenWithLocation(context, litExpr->token);
- break;
+ Emit(elementCount);
}
- if(type->Equals(ExpressionType::GetFloat()))
- {}
- else if(type->Equals(ExpressionType::getDoubleType()))
- {
- suffix = "l";
- }
- else
- {
- assert(!"unimplemented");
- }
- Emit(context, litExpr->floatingPointValue);
- Emit(context, suffix);
+ Emit("]");
break;
- case ConstantExpressionSyntaxNode::ConstantType::Bool:
- Emit(context, litExpr->integerValue ? "true" : "false");
- break;
- case ConstantExpressionSyntaxNode::ConstantType::String:
- emitStringLiteral(context, litExpr->stringValue);
+ case EDeclarator::Flavor::UnsizedArray:
+ EmitDeclarator(declarator->next);
+ Emit("[]");
break;
+
default:
assert(!"unreachable");
break;
}
}
- else if (auto castExpr = expr.As<TypeCastExpressionSyntaxNode>())
+
+ void emitGLSLTypePrefix(
+ RefPtr<ExpressionType> type)
{
- switch(context->shared->target)
+ if(auto basicElementType = type->As<BasicExpressionType>())
{
- 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);
+ switch (basicElementType->BaseType)
+ {
+ case BaseType::Float:
+ // no prefix
+ break;
- Emit(context, "(");
- EmitType(context, castExpr->Type);
- Emit(context, ")(");
- EmitExpr(context, castExpr->Expression);
- Emit(context, ")");
- break;
+ case BaseType::Int: Emit("i"); break;
+ case BaseType::UInt: Emit("u"); break;
+ case BaseType::Bool: Emit("b"); break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
}
-
- }
- else if(auto initExpr = expr.As<InitializerListExpr>())
- {
- Emit(context, "{ ");
- for(auto& arg : initExpr->args)
+ else if(auto vectorType = type->As<VectorExpressionType>())
{
- EmitExpr(context, arg);
- Emit(context, ", ");
+ emitGLSLTypePrefix(vectorType->elementType);
+ }
+ else if(auto matrixType = type->As<MatrixExpressionType>())
+ {
+ emitGLSLTypePrefix(matrixType->getElementType());
+ }
+ else
+ {
+ assert(!"unreachable");
}
- 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
+ void emitHLSLTextureType(
+ RefPtr<TextureTypeBase> texType)
{
- Name,
- Array,
- UnsizedArray,
- };
- Flavor flavor;
- EDeclarator* next = nullptr;
+ switch(texType->getAccess())
+ {
+ case SLANG_RESOURCE_ACCESS_READ:
+ break;
- // Used for `Flavor::Name`
- String name;
- CodePosition loc;
+ case SLANG_RESOURCE_ACCESS_READ_WRITE:
+ Emit("RW");
+ break;
- // Used for `Flavor::Array`
- IntVal* elementCount;
-};
+ case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
+ Emit("RasterizerOrdered");
+ break;
-static void EmitDeclarator(EmitContext* context, EDeclarator* declarator)
-{
- if (!declarator) return;
+ case SLANG_RESOURCE_ACCESS_APPEND:
+ Emit("Append");
+ break;
- Emit(context, " ");
+ case SLANG_RESOURCE_ACCESS_CONSUME:
+ Emit("Consume");
+ break;
- switch (declarator->flavor)
- {
- case EDeclarator::Flavor::Name:
- emitName(context, declarator->name, declarator->loc);
- break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
- case EDeclarator::Flavor::Array:
- EmitDeclarator(context, declarator->next);
- Emit(context, "[");
- if(auto elementCount = declarator->elementCount)
+ switch (texType->GetBaseShape())
{
- Emit(context, elementCount);
+ case TextureType::Shape1D: Emit("Texture1D"); break;
+ case TextureType::Shape2D: Emit("Texture2D"); break;
+ case TextureType::Shape3D: Emit("Texture3D"); break;
+ case TextureType::ShapeCube: Emit("TextureCube"); break;
+ default:
+ assert(!"unreachable");
+ break;
}
- Emit(context, "]");
- break;
-
- case EDeclarator::Flavor::UnsizedArray:
- EmitDeclarator(context, declarator->next);
- Emit(context, "[]");
- break;
- default:
- assert(!"unreachable");
- break;
+ if (texType->isMultisample())
+ {
+ Emit("MS");
+ }
+ if (texType->isArray())
+ {
+ Emit("Array");
+ }
+ Emit("<");
+ EmitType(texType->elementType);
+ Emit(" >");
}
-}
-static void emitGLSLTypePrefix(
- EmitContext* context,
- RefPtr<ExpressionType> type)
-{
- if(auto basicElementType = type->As<BasicExpressionType>())
+ void emitGLSLTextureOrTextureSamplerType(
+ RefPtr<TextureTypeBase> type,
+ char const* baseName)
{
- switch (basicElementType->BaseType)
- {
- case BaseType::Float:
- // no prefix
- break;
+ emitGLSLTypePrefix(type->elementType);
- case BaseType::Int: Emit(context, "i"); break;
- case BaseType::UInt: Emit(context, "u"); break;
- case BaseType::Bool: Emit(context, "b"); break;
+ Emit(baseName);
+ switch (type->GetBaseShape())
+ {
+ case TextureType::Shape1D: Emit("1D"); break;
+ case TextureType::Shape2D: Emit("2D"); break;
+ case TextureType::Shape3D: Emit("3D"); break;
+ case TextureType::ShapeCube: Emit("Cube"); break;
default:
assert(!"unreachable");
break;
}
+
+ if (type->isMultisample())
+ {
+ Emit("MS");
+ }
+ if (type->isArray())
+ {
+ Emit("Array");
+ }
}
- 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())
+ void emitGLSLTextureType(
+ RefPtr<TextureType> texType)
{
- 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;
+ emitGLSLTextureOrTextureSamplerType(texType, "texture");
}
- switch (texType->GetBaseShape())
+ void emitGLSLTextureSamplerType(
+ RefPtr<TextureSamplerType> type)
{
- 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;
+ emitGLSLTextureOrTextureSamplerType(type, "sampler");
}
- if (texType->isMultisample())
+ void emitGLSLImageType(
+ RefPtr<GLSLImageType> type)
{
- Emit(context, "MS");
+ emitGLSLTextureOrTextureSamplerType(type, "image");
}
- if (texType->isArray())
+
+ void emitTextureType(
+ RefPtr<TextureType> texType)
{
- Emit(context, "Array");
- }
- Emit(context, "<");
- EmitType(context, texType->elementType);
- Emit(context, " >");
-}
+ switch(context->shared->target)
+ {
+ case CodeGenTarget::HLSL:
+ emitHLSLTextureType(texType);
+ break;
-static void emitGLSLTextureOrTextureSamplerType(
- EmitContext* context,
- RefPtr<TextureTypeBase> type,
- char const* baseName)
-{
- emitGLSLTypePrefix(context, type->elementType);
+ case CodeGenTarget::GLSL:
+ emitGLSLTextureType(texType);
+ break;
- 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;
+ default:
+ assert(!"unreachable");
+ break;
+ }
}
- if (type->isMultisample())
+ void emitTextureSamplerType(
+ RefPtr<TextureSamplerType> type)
{
- 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");
-}
+ switch(context->shared->target)
+ {
+ case CodeGenTarget::GLSL:
+ emitGLSLTextureSamplerType(type);
+ break;
-static void emitGLSLImageType(
- EmitContext* context,
- RefPtr<GLSLImageType> type)
-{
- emitGLSLTextureOrTextureSamplerType(context, type, "image");
-}
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ }
-static void emitTextureType(
- EmitContext* context,
- RefPtr<TextureType> texType)
-{
- switch(context->shared->target)
+ void emitImageType(
+ RefPtr<GLSLImageType> type)
{
- case CodeGenTarget::HLSL:
- emitHLSLTextureType(context, texType);
- break;
+ switch(context->shared->target)
+ {
+ case CodeGenTarget::HLSL:
+ emitHLSLTextureType(type);
+ break;
- case CodeGenTarget::GLSL:
- emitGLSLTextureType(context, texType);
- break;
+ case CodeGenTarget::GLSL:
+ emitGLSLImageType(type);
+ break;
- default:
- assert(!"unreachable");
- break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
}
-}
-static void emitTextureSamplerType(
- EmitContext* context,
- RefPtr<TextureSamplerType> type)
-{
- switch(context->shared->target)
+ void emitTypeImpl(RefPtr<ExpressionType> type, EDeclarator* declarator)
{
- case CodeGenTarget::GLSL:
- emitGLSLTextureSamplerType(context, type);
- break;
+ TypeEmitArg arg;
+ arg.declarator = declarator;
- default:
- assert(!"unreachable");
- break;
+ TypeVisitorWithArg::dispatch(type, arg);
}
-}
-static void emitImageType(
- EmitContext* context,
- RefPtr<GLSLImageType> type)
-{
- switch(context->shared->target)
- {
- case CodeGenTarget::HLSL:
- emitHLSLTextureType(context, type);
- break;
+#define UNEXPECTED(NAME) \
+ void visit##NAME(NAME*, TypeEmitArg const& arg) \
+ { Emit(#NAME); EmitDeclarator(arg.declarator); }
- case CodeGenTarget::GLSL:
- emitGLSLImageType(context, type);
- break;
+ UNEXPECTED(ErrorType);
+ UNEXPECTED(OverloadGroupType);
+ UNEXPECTED(FuncType);
+ UNEXPECTED(TypeType);
+ UNEXPECTED(GenericDeclRefType);
+ UNEXPECTED(InitializerListType);
- default:
- assert(!"unreachable");
- break;
+#undef UNEXPECTED
+
+ void visitNamedExpressionType(NamedExpressionType* type, TypeEmitArg const& arg)
+ {
+ // Named types are valid for GLSL
+ if (context->shared->target == CodeGenTarget::GLSL)
+ {
+ emitTypeImpl(GetType(type->declRef), arg.declarator);
+ return;
+ }
+
+ EmitDeclRef(type->declRef);
+ EmitDeclarator(arg.declarator);
}
-}
-static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclarator* declarator)
-{
- if (auto basicType = type->As<BasicExpressionType>())
+ void visitBasicExpressionType(BasicExpressionType* basicType, TypeEmitArg const& arg)
{
+ auto declarator = arg.declarator;
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;
+ case BaseType::Void: Emit("void"); break;
+ case BaseType::Int: Emit("int"); break;
+ case BaseType::Float: Emit("float"); break;
+ case BaseType::UInt: Emit("uint"); break;
+ case BaseType::Bool: Emit("bool"); break;
default:
assert(!"unreachable");
break;
}
- EmitDeclarator(context, declarator);
- return;
+ EmitDeclarator(declarator);
}
- else if (auto vecType = type->As<VectorExpressionType>())
+
+ void visitVectorExpressionType(VectorExpressionType* vecType, TypeEmitArg const& arg)
{
+ auto declarator = arg.declarator;
switch(context->shared->target)
{
case CodeGenTarget::GLSL:
case CodeGenTarget::GLSL_Vulkan:
case CodeGenTarget::GLSL_Vulkan_OneDesc:
{
- emitGLSLTypePrefix(context, vecType->elementType);
- Emit(context, "vec");
- Emit(context, vecType->elementCount);
+ emitGLSLTypePrefix(vecType->elementType);
+ Emit("vec");
+ Emit(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, ">");
+ Emit("vector<");
+ EmitType(vecType->elementType);
+ Emit(",");
+ Emit(vecType->elementCount);
+ Emit(">");
break;
default:
@@ -1348,36 +792,37 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara
break;
}
- EmitDeclarator(context, declarator);
- return;
+ EmitDeclarator(declarator);
}
- else if (auto matType = type->As<MatrixExpressionType>())
+
+ void visitMatrixExpressionType(MatrixExpressionType* matType, TypeEmitArg const& arg)
{
+ auto declarator = arg.declarator;
switch(context->shared->target)
{
case CodeGenTarget::GLSL:
case CodeGenTarget::GLSL_Vulkan:
case CodeGenTarget::GLSL_Vulkan_OneDesc:
{
- emitGLSLTypePrefix(context, matType->getElementType());
- Emit(context, "mat");
- Emit(context, matType->getRowCount());
+ emitGLSLTypePrefix(matType->getElementType());
+ Emit("mat");
+ Emit(matType->getRowCount());
// TODO(tfoley): only emit the next bit
// for non-square matrix
- Emit(context, "x");
- Emit(context, matType->getColumnCount());
+ Emit("x");
+ Emit(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, "> ");
+ Emit("matrix<");
+ EmitType(matType->getElementType());
+ Emit(",");
+ Emit(matType->getRowCount());
+ Emit(",");
+ Emit(matType->getColumnCount());
+ Emit("> ");
break;
default:
@@ -1385,37 +830,41 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara
break;
}
- EmitDeclarator(context, declarator);
- return;
+ EmitDeclarator(declarator);
}
- else if (auto texType = type->As<TextureType>())
+
+ void visitTextureType(TextureType* texType, TypeEmitArg const& arg)
{
- emitTextureType(context, texType);
- EmitDeclarator(context, declarator);
- return;
+ auto declarator = arg.declarator;
+ emitTextureType(texType);
+ EmitDeclarator(declarator);
}
- else if (auto textureSamplerType = type->As<TextureSamplerType>())
+
+ void visitTextureSamplerType(TextureSamplerType* textureSamplerType, TypeEmitArg const& arg)
{
- emitTextureSamplerType(context, textureSamplerType);
- EmitDeclarator(context, declarator);
- return;
+ auto declarator = arg.declarator;
+ emitTextureSamplerType(textureSamplerType);
+ EmitDeclarator(declarator);
}
- else if (auto imageType = type->As<GLSLImageType>())
+
+ void visitGLSLImageType(GLSLImageType* imageType, TypeEmitArg const& arg)
{
- emitImageType(context, imageType);
- EmitDeclarator(context, declarator);
- return;
+ auto declarator = arg.declarator;
+ emitImageType(imageType);
+ EmitDeclarator(declarator);
}
- else if (auto samplerStateType = type->As<SamplerStateType>())
+
+ void visitSamplerStateType(SamplerStateType* samplerStateType, TypeEmitArg const& arg)
{
+ auto declarator = arg.declarator;
switch(context->shared->target)
{
case CodeGenTarget::HLSL:
default:
switch (samplerStateType->flavor)
{
- case SamplerStateType::Flavor::SamplerState: Emit(context, "SamplerState"); break;
- case SamplerStateType::Flavor::SamplerComparisonState: Emit(context, "SamplerComparisonState"); break;
+ case SamplerStateType::Flavor::SamplerState: Emit("SamplerState"); break;
+ case SamplerStateType::Flavor::SamplerComparisonState: Emit("SamplerComparisonState"); break;
default:
assert(!"unreachable");
break;
@@ -1423,23 +872,24 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara
break;
case CodeGenTarget::GLSL:
- Emit(context, "sampler");
+ Emit("sampler");
break;
}
-
- EmitDeclarator(context, declarator);
- return;
+ EmitDeclarator(declarator);
}
- else if (auto declRefType = type->As<DeclRefType>())
- {
- EmitDeclRef(context, declRefType->declRef);
- EmitDeclarator(context, declarator);
- return;
+ void visitDeclRefType(DeclRefType* declRefType, TypeEmitArg const& arg)
+ {
+ auto declarator = arg.declarator;
+ EmitDeclRef(declRefType->declRef);
+ EmitDeclarator(declarator);
}
- else if( auto arrayType = type->As<ArrayExpressionType>() )
+
+ void visitArrayExpressionType(ArrayExpressionType* arrayType, TypeEmitArg const& arg)
{
+ auto declarator = arg.declarator;
+
EDeclarator arrayDeclarator;
arrayDeclarator.next = declarator;
@@ -1454,1545 +904,2257 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara
}
- EmitType(context, arrayType->BaseType, &arrayDeclarator);
- return;
+ emitTypeImpl(arrayType->BaseType, &arrayDeclarator);
}
- throw "unimplemented";
-}
-
-static void EmitType(
- EmitContext* context,
- RefPtr<ExpressionType> type,
- CodePosition const& typeLoc,
- String const& name,
- CodePosition const& nameLoc)
-{
- advanceToSourceLocation(context, typeLoc);
-
- EDeclarator nameDeclarator;
- nameDeclarator.flavor = EDeclarator::Flavor::Name;
- nameDeclarator.name = name;
- nameDeclarator.loc = nameLoc;
- EmitType(context, type, &nameDeclarator);
-}
-
+ void EmitType(
+ RefPtr<ExpressionType> type,
+ CodePosition const& typeLoc,
+ String const& name,
+ CodePosition const& nameLoc)
+ {
+ advanceToSourceLocation(typeLoc);
-static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, Token const& nameToken)
-{
- EmitType(context, type, CodePosition(), nameToken.Content, nameToken.Position);
-}
+ EDeclarator nameDeclarator;
+ nameDeclarator.flavor = EDeclarator::Flavor::Name;
+ nameDeclarator.name = name;
+ nameDeclarator.loc = nameLoc;
+ emitTypeImpl(type, &nameDeclarator);
+ }
-static void EmitType(EmitContext* context, RefPtr<ExpressionType> type)
-{
- EmitType(context, type, nullptr);
-}
-static void EmitType(EmitContext* context, TypeExp const& typeExp, Token const& nameToken)
-{
- EmitType(context, typeExp.type,
- typeExp.exp ? typeExp.exp->Position : CodePosition(),
- nameToken.Content, nameToken.Position);
-}
+ void EmitType(RefPtr<ExpressionType> type, Token const& nameToken)
+ {
+ EmitType(type, CodePosition(), nameToken.Content, nameToken.Position);
+ }
-static void EmitType(EmitContext* context, TypeExp const& typeExp, String const& name)
-{
- EmitType(context, typeExp.type,
- typeExp.exp ? typeExp.exp->Position : CodePosition(),
- name, CodePosition());
-}
+ void EmitType(RefPtr<ExpressionType> type)
+ {
+ emitTypeImpl(type, nullptr);
+ }
-// Statements
+ void EmitType(TypeExp const& typeExp, Token const& nameToken)
+ {
+ EmitType(typeExp.type,
+ typeExp.exp ? typeExp.exp->Position : CodePosition(),
+ nameToken.Content, nameToken.Position);
+ }
-// 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<BlockStmt>() )
+ void EmitType(TypeExp const& typeExp, String const& name)
{
- EmitStmt(context, blockStmt->body);
+ EmitType(typeExp.type,
+ typeExp.exp ? typeExp.exp->Position : CodePosition(),
+ name, CodePosition());
}
- else
+
+ void emitTypeExp(TypeExp const& typeExp)
{
- EmitStmt(context, stmt);
+ // TODO: we need to handle cases where the type part of things is bad...
+ emitTypeImpl(typeExp.type, nullptr);
}
- 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`).
+ //
+ // Expressions
+ //
- for(auto attr : decl->GetModifiersOfType<HLSLUncheckedAttribute>())
+ // Determine if an expression should not be emitted when it is the base of
+ // a member reference expression.
+ bool IsBaseExpressionImplicit(RefPtr<ExpressionSyntaxNode> expr)
{
- if(attr->nameToken.Content == "loop")
+ // 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>())
{
- Emit(context, "[loop]");
+ e = derefExpr->base;
}
- else if(attr->nameToken.Content == "unroll")
+ // Is the expression referencing a constant buffer?
+ if (auto cbufferType = e->Type->As<ConstantBufferType>())
{
- Emit(context, "[unroll]");
+ return true;
}
+
+ return false;
}
-}
-// Emit a `#line` directive to the output.
-// Doesn't udpate state of source-location tracking.
-static void emitLineDirective(
- EmitContext* context,
- CodePosition const& sourceLocation)
-{
- emitRawText(context, "\n#line ");
+ 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,
- char buffer[16];
- sprintf(buffer, "%d", sourceLocation.Line);
- emitRawText(context, buffer);
+ kPrecedence_Conditional, // "ternary"
+ kPrecedence_Or,
+ kPrecedence_And,
+ kPrecedence_BitOr,
+ kPrecedence_BitXor,
+ kPrecedence_BitAnd,
- emitRawText(context, " ");
+ 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
+ };
- if(context->shared->target == CodeGenTarget::GLSL)
+ void EmitPostfixExpr(RefPtr<ExpressionSyntaxNode> expr)
{
- auto path = sourceLocation.FileName;
+ EmitExprWithPrecedence(expr, kPrecedence_Postfix);
+ }
- // GLSL doesn't support the traditional form of a `#line` directive without
- // an extension. Rather than depend on that extension we will output
- // a directive in the traditional GLSL fashion.
- //
- // TODO: Add some kind of configuration where we require the appropriate
- // extension and then emit a traditional line directive.
+ void EmitExpr(RefPtr<ExpressionSyntaxNode> expr)
+ {
+ EmitExprWithPrecedence(expr, kPrecedence_General);
+ }
- int id = 0;
- if(!context->shared->mapGLSLSourcePathToID.TryGetValue(path, id))
+ bool MaybeEmitParens(int outerPrec, int prec)
+ {
+ if (prec <= outerPrec)
{
- id = context->shared->glslSourceIDCount++;
- context->shared->mapGLSLSourcePathToID.Add(path, id);
+ Emit("(");
+ return true;
}
-
- sprintf(buffer, "%d", id);
- emitRawText(context, buffer);
+ return false;
}
- else
- {
- // The simple case is to emit the path for the current source
- // location. We need to be a little bit careful with this,
- // because the path might include backslash characters if we
- // are on Windows, and we want to canonicalize those over
- // to forward slashes.
- //
- // TODO: Canonicalization like this should be done centrally
- // in a module that tracks source files.
- emitRawText(context, "\"");
- for(auto c : sourceLocation.FileName)
+ // 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<ExpressionSyntaxNode> prepareLValueExpr(
+ RefPtr<ExpressionSyntaxNode> expr)
+ {
+ for(;;)
{
- char charBuffer[] = { c, 0 };
- switch(c)
+ if(auto typeCastExpr = expr.As<TypeCastExpressionSyntaxNode>())
{
- default:
- emitRawText(context, charBuffer);
- break;
-
- // The incoming file path might use `/` and/or `\\` as
- // a directory separator. We want to canonicalize this.
- //
- // TODO: should probably canonicalize paths to not use backslash somewhere else
- // in the compilation pipeline...
- case '\\':
- emitRawText(context, "/");
- break;
+ expr = typeCastExpr->Expression;
+ }
+ // TODO: any other cases?
+ else
+ {
+ return expr;
}
}
- emitRawText(context, "\"");
- }
- emitRawText(context, "\n");
-}
+ }
-// Emit a `#line` directive to the output, and also
-// ensure that source location tracking information
-// is correct based on the directive we just output.
-static void emitLineDirectiveAndUpdateSourceLocation(
- EmitContext* context,
- CodePosition const& sourceLocation)
-{
- emitLineDirective(context, sourceLocation);
-
- context->shared->loc.FileName = sourceLocation.FileName;
- context->shared->loc.Line = sourceLocation.Line;
- context->shared->loc.Col = 1;
-}
+ void emitInfixExprImpl(
+ int outerPrec,
+ int prec,
+ char const* op,
+ RefPtr<InvokeExpressionSyntaxNode> binExpr,
+ bool isAssign)
+ {
+ bool needsClose = MaybeEmitParens(outerPrec, prec);
-static void emitLineDirectiveIfNeeded(
- EmitContext* context,
- CodePosition const& sourceLocation)
-{
- // Ignore invalid source locations
- if(sourceLocation.Line <= 0)
- return;
-
- // 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->shared->loc.FileName
- || sourceLocation.Line != context->shared->loc.Line
- || sourceLocation.Col < context->shared->loc.Col)
- {
- // Special case: if we are in the same file, and within a small number
- // of lines of the target location, then go ahead and output newlines
- // to get us caught up.
- enum { kSmallLineCount = 3 };
- auto lineDiff = sourceLocation.Line - context->shared->loc.Line;
- if(sourceLocation.FileName == context->shared->loc.FileName
- && sourceLocation.Line > context->shared->loc.Line
- && lineDiff <= kSmallLineCount)
- {
- for(int ii = 0; ii < lineDiff; ++ii )
- {
- Emit(context, "\n");
- }
- assert(sourceLocation.Line == context->shared->loc.Line);
- }
- else
+ auto left = binExpr->Arguments[0];
+ if(isAssign)
{
- // Go ahead and output a `#line` directive to get us caught up
- emitLineDirectiveAndUpdateSourceLocation(context, sourceLocation);
+ left = prepareLValueExpr(left);
}
- }
- // 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->shared->loc.Col)
- {
- int delta = sourceLocation.Col - context->shared->loc.Col;
- for( int ii = 0; ii < delta; ++ii )
+ EmitExprWithPrecedence(left, prec);
+ Emit(" ");
+ Emit(op);
+ Emit(" ");
+ EmitExprWithPrecedence(binExpr->Arguments[1], prec);
+ if (needsClose)
{
- emitRawText(context, " ");
+ Emit(")");
}
- context->shared->loc.Col = sourceLocation.Col;
}
-}
-
-static void advanceToSourceLocation(
- EmitContext* context,
- CodePosition const& sourceLocation)
-{
- // Skip invalid locations
- if(sourceLocation.Line <= 0)
- return;
-
- context->shared->needToUpdateSourceLocation = true;
- context->shared->nextSourceLocation = sourceLocation;
-}
-static void flushSourceLocationChange(
- EmitContext* context)
-{
- if(!context->shared->needToUpdateSourceLocation)
- return;
-
- // Note: the order matters here, because trying to update
- // the source location may involve outputting text that
- // advances the location, and outputting text is what
- // triggers this flush operation.
- context->shared->needToUpdateSourceLocation = false;
- emitLineDirectiveIfNeeded(context, context->shared->nextSourceLocation);
-}
-
-static void emitTokenWithLocation(EmitContext* context, Token const& token)
-{
- if( token.Position.FileName.Length() != 0 )
+ void EmitBinExpr(int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr)
{
- advanceToSourceLocation(context, token.Position);
+ emitInfixExprImpl(outerPrec, prec, op, binExpr, false);
}
- 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, " ");
+ void EmitBinAssignExpr(int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr)
+ {
+ emitInfixExprImpl(outerPrec, prec, op, binExpr, true);
}
- // 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 )
+ void emitUnaryExprImpl(
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr,
+ bool isAssign)
{
- emitTokenWithLocation(context, token);
- }
- Emit(context, "}\n");
-}
+ bool needsClose = MaybeEmitParens(outerPrec, prec);
+ Emit(preOp);
-static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt)
-{
- // Try to ensure that debugging can find the right location
- advanceToSourceLocation(context, stmt->Position);
+ auto arg = expr->Arguments[0];
+ if(isAssign)
+ {
+ arg = prepareLValueExpr(arg);
+ }
- if (auto blockStmt = stmt.As<BlockStmt>())
- {
- EmitBlockStmt(context, blockStmt);
- return;
- }
- else if (auto seqStmt = stmt.As<SeqStmt>())
- {
- for (auto ss : seqStmt->stmts)
+ EmitExprWithPrecedence(arg, prec);
+ Emit(postOp);
+ if (needsClose)
{
- EmitStmt(context, ss);
+ Emit(")");
}
- return;
}
- else if( auto unparsedStmt = stmt.As<UnparsedStmt>() )
+
+ void EmitUnaryExpr(
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr)
{
- EmitUnparsedStmt(context, unparsedStmt);
- return;
+ emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, false);
}
- else if (auto exprStmt = stmt.As<ExpressionStatementSyntaxNode>())
+
+ void EmitUnaryAssignExpr(
+ int outerPrec,
+ int prec,
+ char const* preOp,
+ char const* postOp,
+ RefPtr<InvokeExpressionSyntaxNode> expr)
{
- EmitExpr(context, exprStmt->Expression);
- Emit(context, ";\n");
- return;
+ emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, true);
}
- else if (auto returnStmt = stmt.As<ReturnStatementSyntaxNode>())
+
+ // Determine if a target intrinsic modifer is applicable to the target
+ // we are currently emitting code for.
+ bool isTargetIntrinsicModifierApplicable(
+ RefPtr<TargetIntrinsicModifier> modifier)
{
- Emit(context, "return");
- if (auto expr = returnStmt->Expression)
+ 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;
+
+ switch(context->shared->target)
{
- Emit(context, " ");
- EmitExpr(context, expr);
+ default:
+ assert(!"unexpected");
+ return false;
+
+ case CodeGenTarget::GLSL: return targetName == "glsl";
+ case CodeGenTarget::HLSL: return targetName == "hlsl";
}
- Emit(context, ";\n");
- return;
}
- else if (auto declStmt = stmt.As<VarDeclrStatementSyntaxNode>())
- {
- EmitDecl(context, declStmt->decl);
- return;
- }
- else if (auto ifStmt = stmt.As<IfStatementSyntaxNode>())
+
+ // 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)
{
- Emit(context, "if(");
- EmitExpr(context, ifStmt->Predicate);
- Emit(context, ")\n");
- EmitBlockStmt(context, ifStmt->PositiveStatement);
- if(auto elseStmt = ifStmt->NegativeStatement)
+ RefPtr<TargetIntrinsicModifier> bestModifier;
+ for(auto m : syntax->GetModifiersOfType<TargetIntrinsicModifier>())
{
- Emit(context, "\nelse\n");
- EmitBlockStmt(context, elseStmt);
+ 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;
+
+ return bestModifier;
}
- else if (auto forStmt = stmt.As<ForStatementSyntaxNode>())
+
+ // Emit a call expression that doesn't involve any special cases,
+ // just an expression of the form `f(a0, a1, ...)`
+ void emitSimpleCallExpr(
+ RefPtr<InvokeExpressionSyntaxNode> callExpr,
+ int outerPrec)
{
- // 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.
- //
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix);
- // 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 generating HLSL via "rewrite" (that is,
- // without our semantic checks).
- //
- bool brokenScoping = false;
- if (context->shared->target == CodeGenTarget::HLSL
- && context->isRewrite)
+ auto funcExpr = callExpr->FunctionExpr;
+ if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
{
- brokenScoping = true;
+ auto declRef = funcDeclRefExpr->declRef;
+ if (auto ctorDeclRef = declRef.As<ConstructorDecl>())
+ {
+ // We really want to emit a reference to the type begin constructed
+ EmitType(callExpr->Type);
+ }
+ else
+ {
+ // default case: just emit the decl ref
+ EmitExpr(funcExpr);
+ }
}
-
- auto initStmt = forStmt->InitialStatement;
- if(initStmt)
+ else
{
- if(!brokenScoping)
- Emit(context, "{\n");
- EmitStmt(context, initStmt);
+ // default case: just emit the expression
+ EmitPostfixExpr(funcExpr);
}
- EmitLoopAttributes(context, forStmt);
-
- Emit(context, "for(;");
- if (auto testExp = forStmt->PredicateExpression)
+ Emit("(");
+ UInt argCount = callExpr->Arguments.Count();
+ for (UInt aa = 0; aa < argCount; ++aa)
{
- EmitExpr(context, testExp);
+ if (aa != 0) Emit(", ");
+ EmitExpr(callExpr->Arguments[aa]);
}
- Emit(context, ";");
- if (auto incrExpr = forStmt->SideEffectExpression)
+ Emit(")");
+
+ if (needClose)
{
- EmitExpr(context, incrExpr);
+ Emit(")");
}
- Emit(context, ")\n");
- EmitBlockStmt(context, forStmt->Statement);
+ }
- if (initStmt)
+ void emitStringLiteral(
+ String const& value)
+ {
+ emit("\"");
+ for (auto c : value)
{
- if(!brokenScoping)
- Emit(context, "}\n");
- }
+ // TODO: This needs a more complete implementation,
+ // especially if we want to support Unicode.
- return;
+ char buffer[] = { c, 0 };
+ switch (c)
+ {
+ default:
+ emit(buffer);
+ break;
+
+ case '\"': emit("\\\"");
+ case '\'': emit("\\\'");
+ case '\\': emit("\\\\");
+ case '\n': emit("\\n");
+ case '\r': emit("\\r");
+ case '\t': emit("\\t");
+ }
+ }
+ emit("\"");
}
- else if (auto whileStmt = stmt.As<WhileStatementSyntaxNode>())
+
+ void EmitExprWithPrecedence(RefPtr<ExpressionSyntaxNode> expr, int outerPrec)
{
- EmitLoopAttributes(context, whileStmt);
+ ExprEmitArg arg;
+ arg.outerPrec = outerPrec;
- Emit(context, "while(");
- EmitExpr(context, whileStmt->Predicate);
- Emit(context, ")\n");
- EmitBlockStmt(context, whileStmt->Statement);
- return;
+ ExprVisitorWithArg::dispatch(expr, arg);
}
- else if (auto doWhileStmt = stmt.As<DoWhileStatementSyntaxNode>())
- {
- EmitLoopAttributes(context, doWhileStmt);
- Emit(context, "do(");
- EmitBlockStmt(context, doWhileStmt->Statement);
- Emit(context, " while(");
- EmitExpr(context, doWhileStmt->Predicate);
- Emit(context, ")\n");
- return;
+#define UNEXPECTED(NAME) \
+ void visit##NAME(NAME*, ExprEmitArg const&) \
+ { Emit(#NAME); }
+
+ UNEXPECTED(GenericAppExpr);
+
+#undef UNEXPECTED
+
+ void visitSharedTypeExpr(SharedTypeExpr* expr, ExprEmitArg const& arg)
+ {
+ emitTypeExp(expr->base);
}
- else if (auto discardStmt = stmt.As<DiscardStatementSyntaxNode>())
+
+ void visitSelectExpressionSyntaxNode(SelectExpressionSyntaxNode* selectExpr, ExprEmitArg const& arg)
{
- Emit(context, "discard;\n");
- return;
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Conditional);
+
+ EmitExprWithPrecedence(selectExpr->Arguments[0], kPrecedence_Conditional);
+ Emit(" ? ");
+ EmitExprWithPrecedence(selectExpr->Arguments[1], kPrecedence_Conditional);
+ Emit(" : ");
+ EmitExprWithPrecedence(selectExpr->Arguments[2], kPrecedence_Conditional);
+
+ if(needClose) Emit(")");
}
- else if (auto emptyStmt = stmt.As<EmptyStatementSyntaxNode>())
+
+ void visitAssignExpr(AssignExpr* assignExpr, ExprEmitArg const& arg)
{
- return;
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Assign);
+ EmitExprWithPrecedence(assignExpr->left, kPrecedence_Assign);
+ Emit(" = ");
+ EmitExprWithPrecedence(assignExpr->right, kPrecedence_Assign);
+ if(needClose) Emit(")");
}
- else if (auto switchStmt = stmt.As<SwitchStmt>())
+
+ void visitInvokeExpressionSyntaxNode(
+ RefPtr<InvokeExpressionSyntaxNode> callExpr,
+ ExprEmitArg const& arg)
{
- Emit(context, "switch(");
- EmitExpr(context, switchStmt->condition);
- Emit(context, ")\n");
- EmitBlockStmt(context, switchStmt->body);
- return;
+ auto outerPrec = arg.outerPrec;
+
+ auto funcExpr = callExpr->FunctionExpr;
+ if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>())
+ {
+ auto funcDeclRef = funcDeclRefExpr->declRef;
+ auto funcDecl = funcDeclRef.getDecl();
+ if(!funcDecl)
+ {
+ // 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>() )
+ {
+ EmitBinExpr(
+ outerPrec,
+ kPrecedence_Comma,
+ funcDeclRefExpr->name.Buffer(),
+ callExpr);
+ }
+ else if( auto prefixExpr = callExpr.As<PrefixExpr>() )
+ {
+ EmitUnaryExpr(
+ outerPrec,
+ kPrecedence_Prefix,
+ funcDeclRefExpr->name.Buffer(),
+ "",
+ callExpr);
+ }
+ else if(auto postfixExpr = callExpr.As<PostfixExpr>())
+ {
+ EmitUnaryExpr(
+ outerPrec,
+ kPrecedence_Postfix,
+ "",
+ funcDeclRefExpr->name.Buffer(),
+ callExpr);
+ }
+ else
+ {
+ emitSimpleCallExpr(callExpr, outerPrec);
+ }
+ return;
+ }
+ else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
+ {
+ switch (intrinsicOpModifier->op)
+ {
+ #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(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(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(outerPrec, kPrecedence_Comma, ",", callExpr); return;
+
+ #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return
+ CASE(Neg, -);
+ CASE(Not, !);
+ CASE(BitNot, ~);
+ #undef CASE
+
+ #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return
+ CASE(PreInc, ++);
+ CASE(PreDec, --);
+ #undef CASE
+
+ #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(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->shared->target == CodeGenTarget::GLSL)
+ {
+ Emit("dot(");
+ EmitExpr(callExpr->Arguments[0]);
+ Emit(", ");
+ EmitExpr(callExpr->Arguments[1]);
+ Emit(")");
+ 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->shared->target == CodeGenTarget::GLSL)
+ {
+ Emit("((");
+ EmitExpr(callExpr->Arguments[1]);
+ Emit(") * (");
+ EmitExpr(callExpr->Arguments[0]);
+ Emit("))");
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if(auto targetIntrinsicModifier = findTargetIntrinsicModifier(funcDecl))
+ {
+ 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;
+ }
+
+ 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';
+ 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<MemberExpressionSyntaxNode>())
+ {
+ Emit("(");
+ EmitExpr(memberExpr->BaseExpression);
+ Emit(")");
+ }
+ else
+ {
+ assert(!"unexpected");
+ }
+ 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.
+ assert(argCount > 0);
+ if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpressionSyntaxNode>())
+ {
+ auto base = memberExpr->BaseExpression;
+ if (auto baseTextureType = base->Type->As<TextureType>())
+ {
+ emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler");
+ Emit("(");
+ EmitExpr(memberExpr->BaseExpression);
+ Emit(",");
+ EmitExpr(callExpr->Arguments[0]);
+ Emit(")");
+ }
+ else
+ {
+ assert(!"unexpected");
+ }
+
+ }
+ else
+ {
+ assert(!"unexpected");
+ }
+ break;
+
+ default:
+ assert(!"unexpected");
+ break;
+ }
+ }
+
+ Emit(")");
+ }
+
+ return;
+ }
+
+ // TODO: emit as approperiate for this target
+
+ // We might be calling an intrinsic subscript operation,
+ // and should desugar it accordingly
+ 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<MemberExpressionSyntaxNode>())
+ {
+
+ 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;
+ }
+ }
+ }
+ }
+
+ // Fall through to default handling...
+ emitSimpleCallExpr(callExpr, outerPrec);
}
- else if (auto caseStmt = stmt.As<CaseStmt>())
+
+
+
+ void visitMemberExpressionSyntaxNode(MemberExpressionSyntaxNode* memberExpr, ExprEmitArg const& arg)
{
- Emit(context, "case ");
- EmitExpr(context, caseStmt->expr);
- Emit(context, ":\n");
- return;
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(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(base))
+ {
+ // don't emit the base expression
+ }
+ else
+ {
+ EmitExprWithPrecedence(memberExpr->BaseExpression, kPrecedence_Postfix);
+ Emit(".");
+ }
+
+ emitName(memberExpr->declRef.GetName());
+
+ if(needClose) Emit(")");
}
- else if (auto defaultStmt = stmt.As<DefaultStmt>())
+
+ void visitSwizzleExpr(SwizzleExpr* swizExpr, ExprEmitArg const& arg)
{
- Emit(context, "default:{}\n");
- return;
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix);
+
+ EmitExprWithPrecedence(swizExpr->base, kPrecedence_Postfix);
+ 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(")");
}
- else if (auto breakStmt = stmt.As<BreakStatementSyntaxNode>())
+
+ void visitIndexExpressionSyntaxNode(IndexExpressionSyntaxNode* indexExpr, ExprEmitArg const& arg)
{
- Emit(context, "break;\n");
- return;
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix);
+
+ EmitExprWithPrecedence(indexExpr->BaseExpression, kPrecedence_Postfix);
+ Emit("[");
+ EmitExpr(indexExpr->IndexExpression);
+ Emit("]");
+
+ if(needClose) Emit(")");
}
- else if (auto continueStmt = stmt.As<ContinueStatementSyntaxNode>())
+
+ void visitOverloadedExpr(OverloadedExpr* expr, ExprEmitArg const& arg)
{
- Emit(context, "continue;\n");
- return;
+ emitName(expr->lookupResult2.getName());
}
- throw "unimplemented";
+ void visitVarExpressionSyntaxNode(VarExpressionSyntaxNode* varExpr, ExprEmitArg const& arg)
+ {
+ bool needClose = MaybeEmitParens(arg.outerPrec, kPrecedence_Atomic);
-}
+ // TODO: This won't be valid if we had to generate a qualified
+ // reference for some reason.
+ advanceToSourceLocation(varExpr->Position);
-// Declaration References
+ // 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.
-static void EmitVal(EmitContext* context, RefPtr<Val> val)
-{
- if (auto type = val.As<ExpressionType>())
- {
- EmitType(context, type);
+ if(varExpr->declRef)
+ {
+ EmitDeclRef(varExpr->declRef);
+ }
+ else
+ {
+ emitName(varExpr->name);
+ }
+
+ if(needClose) Emit(")");
}
- else if (auto intVal = val.As<IntVal>())
+
+ void visitDerefExpr(DerefExpr* derefExpr, ExprEmitArg const& arg)
{
- Emit(context, intVal);
+ auto outerPrec = arg.outerPrec;
+
+ // TODO(tfoley): dereference shouldn't always be implicit
+ EmitExprWithPrecedence(derefExpr->base, outerPrec);
}
- else
+
+ void visitConstantExpressionSyntaxNode(ConstantExpressionSyntaxNode* litExpr, ExprEmitArg const& arg)
{
- // Note(tfoley): ignore unhandled cases for semantics for now...
-// assert(!"unimplemented");
- }
-}
+ auto outerPrec = arg.outerPrec;
+ bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Atomic);
-static void EmitDeclRef(EmitContext* context, DeclRef<Decl> declRef)
-{
- // TODO: need to qualify a declaration name based on parent scopes/declarations
+ char const* suffix = "";
+ auto type = litExpr->Type.type;
+ switch (litExpr->ConstType)
+ {
+ case ConstantExpressionSyntaxNode::ConstantType::Int:
+ if(!type)
+ {
+ // Special case for "rewrite" mode
+ emitTokenWithLocation(litExpr->token);
+ break;
+ }
+ if(type->Equals(ExpressionType::GetInt()))
+ {}
+ else if(type->Equals(ExpressionType::GetUInt()))
+ {
+ suffix = "u";
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
+ Emit(litExpr->integerValue);
+ Emit(suffix);
+ break;
+
+
+ case ConstantExpressionSyntaxNode::ConstantType::Float:
+ if(!type)
+ {
+ // Special case for "rewrite" mode
+ emitTokenWithLocation(litExpr->token);
+ break;
+ }
+ if(type->Equals(ExpressionType::GetFloat()))
+ {}
+ else if(type->Equals(ExpressionType::getDoubleType()))
+ {
+ suffix = "l";
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
+ Emit(litExpr->floatingPointValue);
+ Emit(suffix);
+ break;
- // Emit the name for the declaration itself
- emitName(context, declRef.GetName());
+ case ConstantExpressionSyntaxNode::ConstantType::Bool:
+ Emit(litExpr->integerValue ? "true" : "false");
+ break;
+ case ConstantExpressionSyntaxNode::ConstantType::String:
+ emitStringLiteral(litExpr->stringValue);
+ break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ if(needClose) Emit(")");
+ }
- // 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<GenericDecl>())
+ void visitTypeCastExpressionSyntaxNode(TypeCastExpressionSyntaxNode* castExpr, ExprEmitArg const& arg)
{
- // Only do this for declarations of appropriate flavors
- if(auto funcDeclRef = declRef.As<FunctionDeclBase>())
+ bool needClose = false;
+ switch(context->shared->target)
{
- // Don't emit generic arguments for functions, because HLSL doesn't allow them
- return;
+ 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)
+ needClose = MaybeEmitParens(arg.outerPrec, kPrecedence_Prefix);
+
+ Emit("(");
+ EmitType(castExpr->Type);
+ Emit(")(");
+ EmitExpr(castExpr->Expression);
+ Emit(")");
+ break;
}
+ if(needClose) Emit(")");
+ }
- Substitutions* subst = declRef.substitutions.Ptr();
- Emit(context, "<");
- UInt argCount = subst->args.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
+ void visitInitializerListExpr(InitializerListExpr* expr, ExprEmitArg const& arg)
+ {
+ Emit("{ ");
+ for(auto& arg : expr->args)
{
- if (aa != 0) Emit(context, ",");
- EmitVal(context, subst->args[aa]);
+ EmitExpr(arg);
+ Emit(", ");
}
- Emit(context, " >");
+ Emit("}");
}
-}
-
-// Declarations
+ //
+ // Statements
+ //
-// 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>())
+ // 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<StatementSyntaxNode> stmt)
{
- if(!anyLayout)
+ // TODO(tfoley): support indenting
+ Emit("{\n");
+ if( auto blockStmt = stmt.As<BlockStmt>() )
{
- Emit(context, "layout(");
- anyLayout = true;
+ EmitStmt(blockStmt->body);
}
else
{
- Emit(context, ", ");
+ EmitStmt(stmt);
}
+ Emit("}\n");
+ }
- emit(context, mod->nameToken.Content);
- if(mod->valToken.Type != TokenType::Unknown)
+ void EmitLoopAttributes(RefPtr<StatementSyntaxNode> decl)
+ {
+ // Don't emit these attributes for GLSL, because it doesn't understand them
+ if (context->shared->target == CodeGenTarget::GLSL)
+ return;
+
+ // 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>())
{
- Emit(context, " = ");
- emit(context, mod->valToken.Content);
+ if(attr->nameToken.Content == "loop")
+ {
+ Emit("[loop]");
+ }
+ else if(attr->nameToken.Content == "unroll")
+ {
+ Emit("[unroll]");
+ }
}
}
- if(anyLayout)
+
+
+ void EmitUnparsedStmt(RefPtr<UnparsedStmt> stmt)
{
- Emit(context, ")\n");
+ // TODO: actually emit the tokens that made up the statement...
+ Emit("{\n");
+ for( auto& token : stmt->tokens )
+ {
+ emitTokenWithLocation(token);
+ }
+ Emit("}\n");
}
- for (auto mod = decl->modifiers.first; mod; mod = mod->next)
+ void EmitStmt(RefPtr<StatementSyntaxNode> stmt)
{
- advanceToSourceLocation(context, mod->Position);
+ // Try to ensure that debugging can find the right location
+ advanceToSourceLocation(stmt->Position);
- if (0) {}
+ 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<ExpressionStatementSyntaxNode>())
+ {
+ EmitExpr(exprStmt->Expression);
+ Emit(";\n");
+ return;
+ }
+ else if (auto returnStmt = stmt.As<ReturnStatementSyntaxNode>())
+ {
+ Emit("return");
+ if (auto expr = returnStmt->Expression)
+ {
+ Emit(" ");
+ EmitExpr(expr);
+ }
+ Emit(";\n");
+ return;
+ }
+ else if (auto declStmt = stmt.As<VarDeclrStatementSyntaxNode>())
+ {
+ EmitDecl(declStmt->decl);
+ return;
+ }
+ else if (auto ifStmt = stmt.As<IfStatementSyntaxNode>())
+ {
+ 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<ForStatementSyntaxNode>())
+ {
+ // 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.
+ //
- #define CASE(TYPE, KEYWORD) \
- else if(auto mod_##TYPE = mod.As<TYPE>()) Emit(context, #KEYWORD " ")
+ // 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 generating HLSL via "rewrite" (that is,
+ // without our semantic checks).
+ //
+ bool brokenScoping = false;
+ if (context->shared->target == CodeGenTarget::HLSL
+ && context->isRewrite)
+ {
+ brokenScoping = true;
+ }
- 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);
+ auto initStmt = forStmt->InitialStatement;
+ if(initStmt)
+ {
+ if(!brokenScoping)
+ Emit("{\n");
+ EmitStmt(initStmt);
+ }
- CASE(InOutModifier, inout);
- CASE(InModifier, in);
- CASE(OutModifier, out);
+ EmitLoopAttributes(forStmt);
- CASE(HLSLPointModifier, point);
- CASE(HLSLLineModifier, line);
- CASE(HLSLTriangleModifier, triangle);
- CASE(HLSLLineAdjModifier, lineadj);
- CASE(HLSLTriangleAdjModifier, triangleadj);
+ Emit("for(;");
+ if (auto testExp = forStmt->PredicateExpression)
+ {
+ EmitExpr(testExp);
+ }
+ Emit(";");
+ if (auto incrExpr = forStmt->SideEffectExpression)
+ {
+ EmitExpr(incrExpr);
+ }
+ Emit(")\n");
+ EmitBlockStmt(forStmt->Statement);
- CASE(HLSLLinearModifier, linear);
- CASE(HLSLSampleModifier, sample);
- CASE(HLSLCentroidModifier, centroid);
+ if (initStmt)
+ {
+ if(!brokenScoping)
+ Emit("}\n");
+ }
- CASE(ConstModifier, const);
+ return;
+ }
+ else if (auto whileStmt = stmt.As<WhileStatementSyntaxNode>())
+ {
+ EmitLoopAttributes(whileStmt);
- #undef CASE
+ Emit("while(");
+ EmitExpr(whileStmt->Predicate);
+ Emit(")\n");
+ EmitBlockStmt(whileStmt->Statement);
+ return;
+ }
+ else if (auto doWhileStmt = stmt.As<DoWhileStatementSyntaxNode>())
+ {
+ EmitLoopAttributes(doWhileStmt);
- // 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("do(");
+ EmitBlockStmt(doWhileStmt->Statement);
+ Emit(" while(");
+ EmitExpr(doWhileStmt->Predicate);
+ Emit(")\n");
+ return;
+ }
+ else if (auto discardStmt = stmt.As<DiscardStatementSyntaxNode>())
{
- Emit(context, "[");
- emit(context, uncheckedAttr->nameToken.Content);
- auto& args = uncheckedAttr->args;
- auto argCount = args.Count();
- if (argCount != 0)
- {
- Emit(context, "(");
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(context, ", ");
- EmitExpr(context, args[aa]);
- }
- Emit(context, ")");
- }
- Emit(context, "]");
+ Emit("discard;\n");
+ return;
}
-
- else if(auto simpleModifier = mod.As<SimpleModifier>())
+ else if (auto emptyStmt = stmt.As<EmptyStatementSyntaxNode>())
{
- emit(context, simpleModifier->nameToken.Content);
- Emit(context, " ");
+ return;
}
-
- else
+ else if (auto switchStmt = stmt.As<SwitchStmt>())
{
- // skip any extra modifiers
+ 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<BreakStatementSyntaxNode>())
+ {
+ Emit("break;\n");
+ return;
+ }
+ else if (auto continueStmt = stmt.As<ContinueStatementSyntaxNode>())
+ {
+ Emit("continue;\n");
+ return;
}
- }
-}
+ throw "unimplemented";
-typedef unsigned int ESemanticMask;
-enum
-{
- kESemanticMask_None = 0,
+ }
- kESemanticMask_NoPackOffset = 1 << 0,
+ //
+ // Declaration References
+ //
- kESemanticMask_Default = kESemanticMask_NoPackOffset,
-};
+ // Declaration References
-static void EmitSemantic(EmitContext* context, RefPtr<HLSLSemantic> semantic, ESemanticMask /*mask*/)
-{
- if (auto simple = semantic.As<HLSLSimpleSemantic>())
+ void EmitVal(RefPtr<Val> val)
{
- 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)
+ if (auto type = val.As<ExpressionType>())
{
- Emit(context, ".");
- Emit(context, registerSemantic->componentMask.Content);
+ EmitType(type);
+ }
+ else if (auto intVal = val.As<IntVal>())
+ {
+ Emit(intVal);
+ }
+ else
+ {
+ // Note(tfoley): ignore unhandled cases for semantics for now...
+ // assert(!"unimplemented");
}
- Emit(context, ")");
-#endif
}
- else if(auto packOffsetSemantic = semantic.As<HLSLPackOffsetSemantic>())
+
+ void EmitDeclRef(DeclRef<Decl> declRef)
{
- // 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;
+ // TODO: need to qualify a declaration name based on parent scopes/declarations
- Emit(context, ": packoffset(");
- Emit(context, packOffsetSemantic->registerName.Content);
- if(packOffsetSemantic->componentMask.Type != TokenType::Unknown)
+ // Emit the name for the declaration itself
+ emitName(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<GenericDecl>())
{
- Emit(context, ".");
- Emit(context, packOffsetSemantic->componentMask.Content);
+ // Only do this for declarations of appropriate flavors
+ if(auto funcDeclRef = declRef.As<FunctionDeclBase>())
+ {
+ // Don't emit generic arguments for functions, because HLSL doesn't allow them
+ return;
+ }
+
+ Substitutions* subst = declRef.substitutions.Ptr();
+ Emit("<");
+ UInt argCount = subst->args.Count();
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(",");
+ EmitVal(subst->args[aa]);
+ }
+ Emit(" >");
}
- Emit(context, ")");
-#endif
+
}
- else
+
+
+ //
+ // Declarations
+ //
+
+ void emitDeclImpl(
+ Decl* decl,
+ VarLayout* layout)
{
- assert(!"unimplemented");
+ // 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->Position);
+
+ DeclEmitArg arg;
+ arg.layout = layout;
+
+ DeclVisitorWithArg::dispatch(decl, arg);
}
-}
+#define IGNORED(NAME) \
+ void visit##NAME(NAME*, DeclEmitArg const&) {}
-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->shared->target)
+ // Only used by stdlib
+ IGNORED(ModifierDecl)
+
+ // 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(ClassSyntaxNode)
+
+ // 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(ProgramSyntaxNode)
+
+#undef IGNORED
+
+ void visitDeclGroup(DeclGroup* declGroup, DeclEmitArg const&)
{
- case CodeGenTarget::HLSL:
- break;
+ for (auto decl : declGroup->decls)
+ {
+ EmitDecl(decl);
+ }
+ }
- default:
- return;
+ void visitTypeDefDecl(TypeDefDecl* decl, DeclEmitArg const&)
+ {
+ // Note(tfoley): any `typedef`s should already have been filtered
+ // out if we are generating GLSL.
+ assert(context->shared->target != CodeGenTarget::GLSL);
+
+ Emit("typedef ");
+ EmitType(decl->Type, decl->Name.Content);
+ Emit(";\n");
}
- for (auto mod = decl->modifiers.first; mod; mod = mod->next)
+ void visitImportDecl(ImportDecl* decl, DeclEmitArg const&)
{
- auto semantic = mod.As<HLSLSemantic>();
- if (!semantic)
- continue;
+ // 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);
- EmitSemantic(context, semantic, mask);
+ // TODO: do we need to modify the code generation environment at
+ // all when doing this recursive emit?
+
+ EmitDeclsInContainerUsingLayout(moduleDecl, context->shared->globalStructLayout);
+ }
}
-}
-static void EmitDeclsInContainer(EmitContext* context, RefPtr<ContainerDecl> container)
-{
- for (auto member : container->Members)
+ void visitEmptyDecl(EmptyDecl* decl, DeclEmitArg const&)
{
- EmitDecl(context, member);
+ // GLSL uses empty declarations to carry semantically relevant modifiers,
+ // so we can't just skip empty declarations in general
+
+ EmitModifiers(decl);
+ Emit(";\n");
}
-}
-static void EmitDeclsInContainerUsingLayout(
- EmitContext* context,
- RefPtr<ContainerDecl> container,
- RefPtr<StructTypeLayout> containerLayout)
-{
- for (auto member : container->Members)
+ // Emit any modifiers that should go in front of a declaration
+ void EmitModifiers(RefPtr<Decl> decl)
{
- RefPtr<VarLayout> memberLayout;
- if( containerLayout->mapVarToLayout.TryGetValue(member.Ptr(), memberLayout) )
+ // Emit any GLSL `layout` modifiers first
+ bool anyLayout = false;
+ for( auto mod : decl->GetModifiersOfType<GLSLUnparsedLayoutModifier>())
{
- EmitDeclUsingLayout(context, member, memberLayout);
+ if(!anyLayout)
+ {
+ Emit("layout(");
+ anyLayout = true;
+ }
+ else
+ {
+ Emit(", ");
+ }
+
+ emit(mod->nameToken.Content);
+ if(mod->valToken.Type != TokenType::Unknown)
+ {
+ Emit(" = ");
+ emit(mod->valToken.Content);
+ }
}
- else
+ if(anyLayout)
{
- // No layout for this decl
- EmitDecl(context, member);
+ Emit(")\n");
}
- }
-}
-static void EmitTypeDefDecl(EmitContext* context, RefPtr<TypeDefDecl> decl)
-{
- // TODO(tfoley): check if current compilation target even supports typedefs
+ for (auto mod = decl->modifiers.first; mod; mod = mod->next)
+ {
+ advanceToSourceLocation(mod->Position);
- Emit(context, "typedef ");
- EmitType(context, decl->Type, decl->Name.Content);
- Emit(context, ";\n");
-}
+ if (0) {}
-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;
+ #define CASE(TYPE, KEYWORD) \
+ else if(auto mod_##TYPE = mod.As<TYPE>()) Emit(#KEYWORD " ")
- Emit(context, "struct ");
- emitName(context, decl->Name);
- Emit(context, "\n{\n");
+ CASE(RowMajorLayoutModifier, row_major);
+ CASE(ColumnMajorLayoutModifier, column_major);
+ CASE(HLSLNoInterpolationModifier, nointerpolation);
+ CASE(HLSLPreciseModifier, precise);
+ CASE(HLSLEffectSharedModifier, shared);
+ CASE(HLSLGroupSharedModifier, groupshared);
+ CASE(HLSLUniformModifier, uniform);
+ CASE(HLSLVolatileModifier, volatile);
- // TODO(tfoley): Need to hoist members functions, etc. out to global scope
- EmitDeclsInContainer(context, decl);
+ CASE(InOutModifier, inout);
+ CASE(InModifier, in);
+ CASE(OutModifier, out);
- Emit(context, "};\n");
-}
+ CASE(HLSLPointModifier, point);
+ CASE(HLSLLineModifier, line);
+ CASE(HLSLTriangleModifier, triangle);
+ CASE(HLSLLineAdjModifier, lineadj);
+ CASE(HLSLTriangleAdjModifier, triangleadj);
-// Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
-static void EmitVarDeclCommon(EmitContext* context, DeclRef<VarDeclBase> declRef)
-{
- EmitModifiers(context, declRef.getDecl());
+ CASE(HLSLLinearModifier, linear);
+ CASE(HLSLSampleModifier, sample);
+ CASE(HLSLCentroidModifier, centroid);
- EmitType(context, GetType(declRef), declRef.getDecl()->getNameToken());
+ CASE(ConstModifier, const);
- EmitSemantics(context, declRef.getDecl());
+ #undef CASE
- // TODO(tfoley): technically have to apply substitution here too...
- if (auto initExpr = declRef.getDecl()->Expr)
- {
- Emit(context, " = ");
- EmitExpr(context, initExpr);
- }
-}
+ 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;
-// Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
-static void EmitVarDeclCommon(EmitContext* context, RefPtr<VarDeclBase> decl)
-{
- EmitVarDeclCommon(context, DeclRef<Decl>(decl.Ptr(), nullptr).As<VarDeclBase>());
-}
+ case CodeGenTarget::GLSL:
+ break;
+ }
+ }
-// 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;
+ // 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("[");
+ emit(uncheckedAttr->nameToken.Content);
+ auto& args = uncheckedAttr->args;
+ auto argCount = args.Count();
+ if (argCount != 0)
+ {
+ Emit("(");
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(", ");
+ EmitExpr(args[aa]);
+ }
+ Emit(")");
+ }
+ Emit("]");
+ }
- // 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.
+ else if(auto simpleModifier = mod.As<SimpleModifier>())
+ {
+ emit(simpleModifier->nameToken.Content);
+ Emit(" ");
+ }
- Emit(context, ": packoffset(c");
+ else
+ {
+ // skip any extra modifiers
+ }
+ }
+ }
- // 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;
+ typedef unsigned int ESemanticMask;
+ enum
+ {
+ kESemanticMask_None = 0,
- size_t startRegister = offset / registerSize;
- Emit(context, int(startRegister));
+ kESemanticMask_NoPackOffset = 1 << 0,
- size_t byteOffsetInRegister = offset % registerSize;
+ kESemanticMask_Default = kESemanticMask_NoPackOffset,
+ };
- // 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)
+ void EmitSemantic(RefPtr<HLSLSemantic> semantic, ESemanticMask /*mask*/)
+ {
+ if (auto simple = semantic.As<HLSLSimpleSemantic>())
{
- // The value had better occupy a whole number of components.
- assert(byteOffsetInRegister % componentSize == 0);
-
- size_t startComponent = byteOffsetInRegister / componentSize;
+ 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;
- static const char* kComponentNames[] = {"x", "y", "z", "w"};
- Emit(context, ".");
- Emit(context, kComponentNames[startComponent]);
+ Emit(": packoffset(");
+ Emit(packOffsetSemantic->registerName.Content);
+ if(packOffsetSemantic->componentMask.Type != TokenType::Unknown)
+ {
+ Emit(".");
+ Emit(packOffsetSemantic->componentMask.Content);
+ }
+ Emit(")");
+ #endif
+ }
+ else
+ {
+ assert(!"unimplemented");
}
- Emit(context, ")");
}
- else
+
+
+ void EmitSemantics(RefPtr<Decl> decl, ESemanticMask mask = kESemanticMask_Default )
{
- Emit(context, ": register(");
- switch( info.kind )
+ // Don't emit semantics if we aren't translating down to HLSL
+ switch (context->shared->target)
{
- 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");
+ case CodeGenTarget::HLSL:
break;
+
default:
- assert(!"unexpected");
- break;
+ return;
}
- Emit(context, info.index);
- if(info.space)
+
+ for (auto mod = decl->modifiers.first; mod; mod = mod->next)
{
- Emit(context, ", space");
- Emit(context, info.space);
+ auto semantic = mod.As<HLSLSemantic>();
+ if (!semantic)
+ continue;
+
+ EmitSemantic(semantic, mask);
}
- 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->shared->target )
+ void EmitDeclsInContainer(RefPtr<ContainerDecl> container)
{
- default:
- return;
-
- case CodeGenTarget::HLSL:
- break;
+ for (auto member : container->Members)
+ {
+ EmitDecl(member);
+ }
}
- for( auto rr : layout->resourceInfos )
+ void EmitDeclsInContainerUsingLayout(
+ RefPtr<ContainerDecl> container,
+ RefPtr<StructTypeLayout> containerLayout)
{
- emitHLSLRegisterSemantic(context, rr);
+ 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);
+ }
+ }
}
-}
-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;
- assert(computedLayout);
+ void visitStructSyntaxNode(RefPtr<StructSyntaxNode> decl, DeclEmitArg const&)
+ {
+ // Don't emit a declaration that was only generated implicitly, for
+ // the purposes of semantic checking.
+ if(decl->HasModifier<ImplicitParameterBlockElementTypeModifier>())
+ return;
- auto varLayout = computedLayout.As<VarLayout>();
- return varLayout;
-}
+ Emit("struct ");
+ emitName(decl->Name);
+ Emit("\n{\n");
-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;
+ // TODO(tfoley): Need to hoist members functions, etc. out to global scope
+ EmitDeclsInContainer(decl);
- // We expect/require the data type to be a user-defined `struct` type
- auto declRefType = dataType->As<DeclRefType>();
- assert(declRefType);
+ Emit("};\n");
+ }
- // We expect to always have layout information
- layout = maybeFetchLayout(varDecl, layout);
- assert(layout);
+ // Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
+ void EmitVarDeclCommon(DeclRef<VarDeclBase> declRef)
+ {
+ EmitModifiers(declRef.getDecl());
- // We expect the layout to be for a structured type...
- RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
- assert(bufferLayout);
+ EmitType(GetType(declRef), declRef.getDecl()->getNameToken());
- RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
- assert(structTypeLayout);
+ EmitSemantics(declRef.getDecl());
- if( auto constantBufferType = parameterBlockType->As<ConstantBufferType>() )
- {
- Emit(context, "cbuffer ");
- }
- else if( auto textureBufferType = parameterBlockType->As<TextureBufferType>() )
- {
- Emit(context, "tbuffer ");
+ // TODO(tfoley): technically have to apply substitution here too...
+ if (auto initExpr = declRef.getDecl()->Expr)
+ {
+ Emit(" = ");
+ EmitExpr(initExpr);
+ }
}
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ // Shared emit logic for variable declarations (used for parameters, locals, globals, fields)
+ void EmitVarDeclCommon(RefPtr<VarDeclBase> decl)
{
- Emit(context, " ");
- emitName(context, reflectionNameModifier->nameToken);
+ EmitVarDeclCommon(DeclRef<Decl>(decl.Ptr(), nullptr).As<VarDeclBase>());
}
- 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<StructSyntaxNode>())
+ // Emit a single `regsiter` semantic, as appropriate for a given resource-type-specific layout info
+ void emitHLSLRegisterSemantic(
+ VarLayout::ResourceInfo const& info)
{
- int fieldCounter = 0;
-
- for (auto field : getMembersOfType<StructField>(structRef))
+ if( info.kind == LayoutResourceKind::Uniform )
{
- int fieldIndex = fieldCounter++;
+ size_t offset = info.index;
- EmitVarDeclCommon(context, field);
+ // 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.
- RefPtr<VarLayout> fieldLayout = structTypeLayout->fields[fieldIndex];
- assert(fieldLayout->varDecl.GetName() == field.GetName());
+ Emit(": packoffset(c");
- // Emit explicit layout annotations for every field
- for( auto rr : fieldLayout->resourceInfos )
- {
- auto kind = rr.kind;
+ // Size of a logical `c` register in bytes
+ auto registerSize = 16;
- auto offsetResource = rr;
+ // Size of each component of a logical `c` register, in bytes
+ auto componentSize = 4;
- 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...
+ size_t startRegister = offset / registerSize;
+ Emit(int(startRegister));
- // 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);
+ size_t byteOffsetInRegister = offset % registerSize;
- offsetResource.index += cbufferResource->index;
- offsetResource.space += cbufferResource->space;
- }
+ // 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);
- emitHLSLRegisterSemantic(context, offsetResource);
- }
+ size_t startComponent = byteOffsetInRegister / componentSize;
- Emit(context, ";\n");
+ static const char* kComponentNames[] = {"x", "y", "z", "w"};
+ Emit(".");
+ Emit(kComponentNames[startComponent]);
+ }
+ Emit(")");
+ }
+ else
+ {
+ Emit(": register(");
+ switch( info.kind )
+ {
+ case LayoutResourceKind::ConstantBuffer:
+ Emit("b");
+ break;
+ case LayoutResourceKind::ShaderResource:
+ Emit("t");
+ break;
+ case LayoutResourceKind::UnorderedAccess:
+ Emit("u");
+ break;
+ case LayoutResourceKind::SamplerState:
+ Emit("s");
+ break;
+ default:
+ assert(!"unexpected");
+ break;
+ }
+ Emit(info.index);
+ if(info.space)
+ {
+ Emit(", space");
+ Emit(info.space);
+ }
+ Emit(")");
}
}
- Emit(context, "}\n");
-}
-static void
-emitGLSLLayoutQualifier(
- EmitContext* context,
- VarLayout::ResourceInfo const& info)
-{
- switch(info.kind)
+ // Emit all the `register` semantics that are appropriate for a particular variable layout
+ void emitHLSLRegisterSemantics(
+ RefPtr<VarLayout> layout)
{
- case LayoutResourceKind::Uniform:
- Emit(context, "layout(offset = ");
- Emit(context, info.index);
- Emit(context, ")\n");
- break;
+ if (!layout) return;
- case LayoutResourceKind::VertexInput:
- case LayoutResourceKind::FragmentOutput:
- Emit(context, "layout(location = ");
- Emit(context, info.index);
- Emit(context, ")\n");
- break;
+ switch( context->shared->target )
+ {
+ default:
+ return;
- case LayoutResourceKind::SpecializationConstant:
- Emit(context, "layout(constant_id = ");
- Emit(context, info.index);
- Emit(context, ")\n");
- break;
+ case CodeGenTarget::HLSL:
+ 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)
+ for( auto rr : layout->resourceInfos )
{
- Emit(context, ", set = ");
- Emit(context, info.space);
+ emitHLSLRegisterSemantic(rr);
}
- Emit(context, ")\n");
- break;
}
-}
-
-static void
-emitGLSLLayoutQualifiers(
- EmitContext* context,
- RefPtr<VarLayout> layout)
-{
- if(!layout) return;
- switch( context->shared->target )
+ static RefPtr<VarLayout> maybeFetchLayout(
+ RefPtr<Decl> decl,
+ RefPtr<VarLayout> layout)
{
- default:
- return;
+ // If we have already found layout info, don't go searching
+ if (layout) return layout;
- case CodeGenTarget::GLSL:
- break;
+ // 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;
+ assert(computedLayout);
+
+ auto varLayout = computedLayout.As<VarLayout>();
+ return varLayout;
}
- for( auto info : layout->resourceInfos )
+ void emitHLSLParameterBlockDecl(
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
{
- emitGLSLLayoutQualifier(context, info);
- }
-}
+ // The data type that describes where stuff in the constant buffer should go
+ RefPtr<ExpressionType> dataType = parameterBlockType->elementType;
-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
+ layout = maybeFetchLayout(varDecl, layout);
+ assert(layout);
+
+ // We expect the layout to be for a structured type...
+ RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
+ assert(bufferLayout);
- // We expect/require the data type to be a user-defined `struct` type
- auto declRefType = dataType->As<DeclRefType>();
- assert(declRefType);
+ RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
+ assert(structTypeLayout);
- // We expect to always have layout information
- assert(layout);
+ if( auto constantBufferType = parameterBlockType->As<ConstantBufferType>() )
+ {
+ Emit("cbuffer ");
+ }
+ else if( auto textureBufferType = parameterBlockType->As<TextureBufferType>() )
+ {
+ Emit("tbuffer ");
+ }
- // We expect the layout to be for a structured type...
- RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
- assert(bufferLayout);
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ {
+ Emit(" ");
+ emitName(reflectionNameModifier->nameToken);
+ }
- RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
- assert(structTypeLayout);
+ EmitSemantics(varDecl, kESemanticMask_None);
- emitGLSLLayoutQualifiers(context, layout);
+ auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
+ assert(info);
+ emitHLSLRegisterSemantic(*info);
- EmitModifiers(context, varDecl);
+ Emit("\n{\n");
+ if (auto structRef = declRefType->declRef.As<StructSyntaxNode>())
+ {
+ int fieldCounter = 0;
- // 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");
- }
+ for (auto field : getMembersOfType<StructField>(structRef))
+ {
+ int fieldIndex = fieldCounter++;
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
- {
- Emit(context, " ");
- emitName(context, reflectionNameModifier->nameToken);
+ EmitVarDeclCommon(field);
+
+ RefPtr<VarLayout> fieldLayout = structTypeLayout->fields[fieldIndex];
+ assert(fieldLayout->varDecl.GetName() == field.GetName());
+
+ // 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(offsetResource);
+ }
+
+ Emit(";\n");
+ }
+ }
+ Emit("}\n");
}
- Emit(context, "\n{\n");
- if (auto structRef = declRefType->declRef.As<StructSyntaxNode>())
+ void emitGLSLLayoutQualifier(
+ VarLayout::ResourceInfo const& info)
{
- for (auto field : getMembersOfType<StructField>(structRef))
+ switch(info.kind)
{
- RefPtr<VarLayout> fieldLayout;
- structTypeLayout->mapVarToLayout.TryGetValue(field.getDecl(), fieldLayout);
-// assert(fieldLayout);
+ case LayoutResourceKind::Uniform:
+ Emit("layout(offset = ");
+ Emit(info.index);
+ Emit(")\n");
+ break;
- // TODO(tfoley): We may want to emit *some* of these,
- // some of the time...
-// emitGLSLLayoutQualifiers(context, fieldLayout);
+ case LayoutResourceKind::VertexInput:
+ case LayoutResourceKind::FragmentOutput:
+ Emit("layout(location = ");
+ Emit(info.index);
+ Emit(")\n");
+ break;
- EmitVarDeclCommon(context, field);
+ case LayoutResourceKind::SpecializationConstant:
+ Emit("layout(constant_id = ");
+ Emit(info.index);
+ Emit(")\n");
+ break;
- Emit(context, ";\n");
+ case LayoutResourceKind::ConstantBuffer:
+ case LayoutResourceKind::ShaderResource:
+ case LayoutResourceKind::UnorderedAccess:
+ case LayoutResourceKind::SamplerState:
+ case LayoutResourceKind::DescriptorTableSlot:
+ Emit("layout(binding = ");
+ Emit(info.index);
+ if(info.space)
+ {
+ Emit(", set = ");
+ Emit(info.space);
+ }
+ Emit(")\n");
+ break;
}
}
- Emit(context, "}");
- if( varDecl->Name.Type != TokenType::Unknown )
+ void emitGLSLLayoutQualifiers(
+ RefPtr<VarLayout> layout)
{
- Emit(context, " ");
- emitName(context, varDecl->Name);
- }
+ if(!layout) return;
- Emit(context, ";\n");
-}
-
-static void emitParameterBlockDecl(
- EmitContext* context,
- RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
- RefPtr<VarLayout> layout)
-{
- switch(context->shared->target)
- {
- case CodeGenTarget::HLSL:
- emitHLSLParameterBlockDecl(context, varDecl, parameterBlockType, layout);
- break;
+ switch( context->shared->target )
+ {
+ default:
+ return;
- case CodeGenTarget::GLSL:
- emitGLSLParameterBlockDecl(context, varDecl, parameterBlockType, layout);
- break;
+ case CodeGenTarget::GLSL:
+ break;
+ }
- default:
- assert(!"unexpected");
- break;
+ for( auto info : layout->resourceInfos )
+ {
+ emitGLSLLayoutQualifier(info);
+ }
}
-}
-static void EmitVarDecl(EmitContext* context, RefPtr<VarDeclBase> decl, RefPtr<VarLayout> 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 parameterBlockType = decl->Type->As<ParameterBlockType>())
+ void emitGLSLParameterBlockDecl(
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
{
- emitParameterBlockDecl(context, decl, parameterBlockType, layout);
- return;
- }
+ // The data type that describes where stuff in the constant buffer should go
+ RefPtr<ExpressionType> dataType = parameterBlockType->elementType;
- emitGLSLLayoutQualifiers(context, layout);
+ // We expect/require the data type to be a user-defined `struct` type
+ auto declRefType = dataType->As<DeclRefType>();
+ assert(declRefType);
- EmitVarDeclCommon(context, decl);
+ // We expect to always have layout information
+ assert(layout);
- emitHLSLRegisterSemantics(context, layout);
+ // We expect the layout to be for a structured type...
+ RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
+ assert(bufferLayout);
- Emit(context, ";\n");
-}
+ RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
+ assert(structTypeLayout);
-static void EmitParamDecl(EmitContext* context, RefPtr<ParameterSyntaxNode> decl)
-{
- EmitVarDeclCommon(context, decl);
-}
+ emitGLSLLayoutQualifiers(layout);
-static void EmitFuncDecl(EmitContext* context, RefPtr<FunctionSyntaxNode> decl)
-{
- EmitModifiers(context, decl);
+ EmitModifiers(varDecl);
- // 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.
+ // Emit an apprpriate declaration keyword based on the kind of block
+ if (parameterBlockType->As<ConstantBufferType>())
+ {
+ Emit("uniform");
+ }
+ else if (parameterBlockType->As<GLSLInputParameterBlockType>())
+ {
+ Emit("in");
+ }
+ else if (parameterBlockType->As<GLSLOutputParameterBlockType>())
+ {
+ Emit("out");
+ }
+ else if (parameterBlockType->As<GLSLShaderStorageBufferType>())
+ {
+ Emit("buffer");
+ }
+ else
+ {
+ assert(!"unexpected");
+ Emit("uniform");
+ }
- EmitType(context, decl->ReturnType, decl->Name);
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ {
+ Emit(" ");
+ emitName(reflectionNameModifier->nameToken);
+ }
- Emit(context, "(");
- bool first = true;
- for (auto paramDecl : decl->getMembersOfType<ParameterSyntaxNode>())
- {
- if (!first) Emit(context, ", ");
- EmitParamDecl(context, paramDecl);
- first = false;
- }
- Emit(context, ")");
+ Emit("\n{\n");
+ if (auto structRef = declRefType->declRef.As<StructSyntaxNode>())
+ {
+ for (auto field : getMembersOfType<StructField>(structRef))
+ {
+ RefPtr<VarLayout> fieldLayout;
+ structTypeLayout->mapVarToLayout.TryGetValue(field.getDecl(), fieldLayout);
+ // assert(fieldLayout);
- EmitSemantics(context, decl);
+ // TODO(tfoley): We may want to emit *some* of these,
+ // some of the time...
+ // emitGLSLLayoutQualifiers(fieldLayout);
- if (auto bodyStmt = decl->Body)
- {
- EmitBlockStmt(context, bodyStmt);
- }
- else
- {
- Emit(context, ";\n");
- }
-}
+ EmitVarDeclCommon(field);
-static void emitGLSLPreprocessorDirectives(
- EmitContext* context,
- RefPtr<ProgramSyntaxNode> program)
-{
- switch(context->shared->target)
- {
- // Don't emit this stuff unless we are targetting GLSL
- default:
- return;
+ Emit(";\n");
+ }
+ }
+ Emit("}");
- case CodeGenTarget::GLSL:
- break;
+ if( varDecl->Name.Type != TokenType::Unknown )
+ {
+ Emit(" ");
+ emitName(varDecl->Name);
+ }
+
+ Emit(";\n");
}
- if( auto versionDirective = program->FindModifier<GLSLVersionDirective>() )
+ void emitParameterBlockDecl(
+ RefPtr<VarDeclBase> varDecl,
+ RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<VarLayout> layout)
{
- // TODO(tfoley): Emit an appropriate `#line` directive...
-
- Emit(context, "#version ");
- emit(context, versionDirective->versionNumberToken.Content);
- if(versionDirective->glslProfileToken.Type != TokenType::Unknown)
+ switch(context->shared->target)
{
- Emit(context, " ");
- emit(context, versionDirective->glslProfileToken.Content);
+ case CodeGenTarget::HLSL:
+ emitHLSLParameterBlockDecl(varDecl, parameterBlockType, layout);
+ break;
+
+ case CodeGenTarget::GLSL:
+ emitGLSLParameterBlockDecl(varDecl, parameterBlockType, layout);
+ break;
+
+ default:
+ assert(!"unexpected");
+ break;
}
- Emit(context, "\n");
}
- else
+
+ void visitVarDeclBase(RefPtr<VarDeclBase> decl, DeclEmitArg const& arg)
{
- // No explicit version was given (probably because we are cross-compiling).
+ 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.
//
- // We need to pick an appropriate version, ideally based on the features
- // that the shader ends up using.
+ // TODO(tfoley): Detect cases where we need to fall back to
+ // ordinary variable declaration syntax in HLSL.
//
- // For now we just fall back to a reasonably recent version.
+ // 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(decl, parameterBlockType, layout);
+ return;
+ }
- Emit(context, "#version 420\n");
- }
+ emitGLSLLayoutQualifiers(layout);
- // TODO: when cross-compiling we may need to output additional `#extension` directives
- // based on the features that we have used.
+ EmitVarDeclCommon(decl);
- for( auto extensionDirective : program->GetModifiersOfType<GLSLExtensionDirective>() )
- {
- // TODO(tfoley): Emit an appropriate `#line` directive...
+ emitHLSLRegisterSemantics(layout);
- Emit(context, "#extension ");
- emit(context, extensionDirective->extensionNameToken.Content);
- Emit(context, " : ");
- emit(context, extensionDirective->dispositionToken.Content);
- Emit(context, "\n");
+ Emit(";\n");
}
- // TODO: handle other cases...
-}
+ void EmitParamDecl(RefPtr<ParameterSyntaxNode> decl)
+ {
+ EmitVarDeclCommon(decl);
+ }
-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;
+ void visitFunctionSyntaxNode(RefPtr<FunctionSyntaxNode> 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.
- // Try to ensure that debugging can find the right location
- advanceToSourceLocation(context, decl->Position);
+ EmitType(decl->ReturnType, decl->Name);
- 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;
+ Emit("(");
+ bool first = true;
+ for (auto paramDecl : decl->getMembersOfType<ParameterSyntaxNode>())
+ {
+ if (!first) Emit(", ");
+ EmitParamDecl(paramDecl);
+ first = false;
+ }
+ Emit(")");
+
+ EmitSemantics(decl);
+
+ if (auto bodyStmt = decl->Body)
+ {
+ EmitBlockStmt(bodyStmt);
+ }
+ else
+ {
+ Emit(";\n");
+ }
}
- else if (auto classDecl = decl.As<ClassSyntaxNode>())
- {
- return;
- }
- else if( auto importDecl = decl.As<ImportDecl>())
+
+ void emitGLSLPreprocessorDirectives(
+ RefPtr<ProgramSyntaxNode> program)
{
- // When in "rewriter" mode, we need to emit the code of the imported
- // module in-place at the `import` site.
+ switch(context->shared->target)
+ {
+ // Don't emit this stuff unless we are targetting GLSL
+ default:
+ return;
- auto moduleDecl = importDecl->importedModuleDecl.Ptr();
+ case CodeGenTarget::GLSL:
+ break;
+ }
- // 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))
+ if( auto versionDirective = program->FindModifier<GLSLVersionDirective>() )
{
- // 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(tfoley): Emit an appropriate `#line` directive...
- // TODO: do we need to modify the code generation environment at
- // all when doing this recursive emit?
+ Emit("#version ");
+ emit(versionDirective->versionNumberToken.Content);
+ if(versionDirective->glslProfileToken.Type != TokenType::Unknown)
+ {
+ Emit(" ");
+ emit(versionDirective->glslProfileToken.Content);
+ }
+ Emit("\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.
- EmitDeclsInContainerUsingLayout(context, moduleDecl, context->shared->globalStructLayout);
+ Emit("#version 420\n");
}
- return;
- }
- else if( auto emptyDecl = decl.As<EmptyDecl>() )
- {
- EmitModifiers(context, emptyDecl);
- Emit(context, ";\n");
- return;
- }
- throw "unimplemented";
-}
+ // TODO: when cross-compiling we may need to output additional `#extension` directives
+ // based on the features that we have used.
-static void EmitDecl(EmitContext* context, RefPtr<Decl> decl)
-{
- EmitDeclImpl(context, decl, nullptr);
-}
+ for( auto extensionDirective : program->GetModifiersOfType<GLSLExtensionDirective>() )
+ {
+ // TODO(tfoley): Emit an appropriate `#line` directive...
-static void EmitDeclUsingLayout(EmitContext* context, RefPtr<Decl> decl, RefPtr<VarLayout> layout)
-{
- EmitDeclImpl(context, decl, layout);
-}
+ Emit("#extension ");
+ emit(extensionDirective->extensionNameToken.Content);
+ Emit(" : ");
+ emit(extensionDirective->dispositionToken.Content);
+ Emit("\n");
+ }
-static void EmitDecl(EmitContext* context, RefPtr<DeclBase> declBase)
-{
- if( auto decl = declBase.As<Decl>() )
- {
- EmitDecl(context, decl);
+ // TODO: handle other cases...
}
- else if(auto declGroup = declBase.As<DeclGroup>())
+
+ void EmitDecl(RefPtr<Decl> decl)
{
- for(auto d : declGroup->decls)
- EmitDecl(context, d);
+ emitDeclImpl(decl, nullptr);
}
- else
+
+ void EmitDeclUsingLayout(RefPtr<Decl> decl, RefPtr<VarLayout> layout)
{
- throw "unimplemented";
+ emitDeclImpl(decl, layout);
}
-}
-static void registerReservedWord(
- EmitContext* context,
- String const& name)
-{
- context->shared->reservedWords.Add(name, name);
-}
+ 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
+ {
+ throw "unimplemented";
+ }
+ }
-static void registerReservedWords(
- EmitContext* context)
-{
-#define WORD(NAME) registerReservedWord(context, #NAME)
+ void registerReservedWord(
+ String const& name)
+ {
+ context->shared->reservedWords.Add(name, name);
+ }
- switch (context->shared->target)
+ void registerReservedWords()
{
- 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;
+ #define WORD(NAME) registerReservedWord(#NAME)
- default:
- break;
+ switch (context->shared->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;
+ }
}
-}
+};
bool isRewriteRequest(
SourceLanguage sourceLanguage,
@@ -3057,14 +3219,16 @@ String emitEntryPoint(
translationUnit->sourceLanguage,
target);
+ EmitVisitor visitor(&context);
+
// TODO: this should only need to take the shared context
- registerReservedWords(&context);
+ visitor.registerReservedWords();
auto translationUnitSyntax = translationUnit->SyntaxNode.Ptr();
// There may be global-scope modifiers that we should emit now
- emitGLSLPreprocessorDirectives(&context, translationUnitSyntax);
+ visitor.emitGLSLPreprocessorDirectives(translationUnitSyntax);
switch(target)
{
@@ -3081,7 +3245,7 @@ String emitEntryPoint(
auto lowered = lowerEntryPoint(entryPoint, programLayout, target);
- EmitDeclsInContainer(&context, lowered.program.Ptr());
+ visitor.EmitDeclsInContainer(lowered.program.Ptr());
#if 0
if( isRewrite )
diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp
index f9bf97107..b90573495 100644
--- a/source/slang/lower.cpp
+++ b/source/slang/lower.cpp
@@ -76,6 +76,7 @@ struct StructuralTransformVisitorBase
}
};
+#if 0
template<typename V>
struct StructuralTransformStmtVisitor
: StructuralTransformVisitorBase<V>
@@ -107,6 +108,7 @@ struct StructuralTransformStmtVisitor
#include "object-meta-end.h"
};
+#endif
template<typename V>
RefPtr<StatementSyntaxNode> structuralTransform(
@@ -130,7 +132,7 @@ struct StructuralTransformExprVisitor
#define SYNTAX_CLASS(NAME, BASE, ...) \
- RefPtr<ExpressionSyntaxNode> visit(NAME* obj) { \
+ RefPtr<ExpressionSyntaxNode> visit##NAME(NAME* obj) { \
RefPtr<NAME> result = new NAME(*obj); \
transformFields(result, obj); \
return result; \
@@ -216,8 +218,7 @@ struct LoweringVisitor
: ExprVisitor<LoweringVisitor, RefPtr<ExpressionSyntaxNode>>
, StmtVisitor<LoweringVisitor, void>
, DeclVisitor<LoweringVisitor, RefPtr<Decl>>
- , TypeVisitor<LoweringVisitor, RefPtr<ExpressionType>>
- , ValVisitor<LoweringVisitor, RefPtr<Val>>
+ , ValVisitor<LoweringVisitor, RefPtr<Val>, RefPtr<ExpressionType>>
{
//
SharedLoweringContext* shared;
@@ -247,12 +248,12 @@ struct LoweringVisitor
return ValVisitor::dispatch(val);
}
- RefPtr<Val> visit(GenericParamIntVal* val)
+ RefPtr<Val> visitGenericParamIntVal(GenericParamIntVal* val)
{
return new GenericParamIntVal(translateDeclRef(DeclRef<Decl>(val->declRef)).As<VarDeclBase>());
}
- RefPtr<Val> visit(ConstantIntVal* val)
+ RefPtr<Val> visitConstantIntVal(ConstantIntVal* val)
{
return val;
}
@@ -276,40 +277,40 @@ struct LoweringVisitor
return result;
}
- RefPtr<ExpressionType> visit(ErrorType* type)
+ RefPtr<ExpressionType> visitErrorType(ErrorType* type)
{
return type;
}
- RefPtr<ExpressionType> visit(OverloadGroupType* type)
+ RefPtr<ExpressionType> visitOverloadGroupType(OverloadGroupType* type)
{
return type;
}
- RefPtr<ExpressionType> visit(InitializerListType* type)
+ RefPtr<ExpressionType> visitInitializerListType(InitializerListType* type)
{
return type;
}
- RefPtr<ExpressionType> visit(GenericDeclRefType* type)
+ RefPtr<ExpressionType> visitGenericDeclRefType(GenericDeclRefType* type)
{
return new GenericDeclRefType(translateDeclRef(DeclRef<Decl>(type->declRef)).As<GenericDecl>());
}
- RefPtr<ExpressionType> visit(FuncType* type)
+ RefPtr<ExpressionType> visitFuncType(FuncType* type)
{
RefPtr<FuncType> loweredType = new FuncType();
loweredType->declRef = translateDeclRef(DeclRef<Decl>(type->declRef)).As<CallableDecl>();
return loweredType;
}
- RefPtr<ExpressionType> visit(DeclRefType* type)
+ RefPtr<ExpressionType> visitDeclRefType(DeclRefType* type)
{
auto loweredDeclRef = translateDeclRef(type->declRef);
return DeclRefType::Create(loweredDeclRef);
}
- RefPtr<ExpressionType> visit(NamedExpressionType* type)
+ RefPtr<ExpressionType> visitNamedExpressionType(NamedExpressionType* type)
{
if (shared->target == CodeGenTarget::GLSL)
{
@@ -320,12 +321,12 @@ struct LoweringVisitor
return new NamedExpressionType(translateDeclRef(DeclRef<Decl>(type->declRef)).As<TypeDefDecl>());
}
- RefPtr<ExpressionType> visit(TypeType* type)
+ RefPtr<ExpressionType> visitTypeType(TypeType* type)
{
return new TypeType(lowerType(type->type));
}
- RefPtr<ExpressionType> visit(ArrayExpressionType* type)
+ RefPtr<ExpressionType> visitArrayExpressionType(ArrayExpressionType* type)
{
RefPtr<ArrayExpressionType> loweredType = new ArrayExpressionType();
loweredType->BaseType = lowerType(type->BaseType);
@@ -350,7 +351,7 @@ struct LoweringVisitor
}
// catch-all
- RefPtr<ExpressionSyntaxNode> visit(
+ RefPtr<ExpressionSyntaxNode> visitExpressionSyntaxNode(
ExpressionSyntaxNode* expr)
{
return structuralTransform(expr, this);
@@ -404,7 +405,7 @@ struct LoweringVisitor
return result;
}
- RefPtr<ExpressionSyntaxNode> visit(
+ RefPtr<ExpressionSyntaxNode> visitVarExpressionSyntaxNode(
VarExpressionSyntaxNode* expr)
{
// If the expression didn't get resolved, we can leave it as-is
@@ -428,7 +429,7 @@ struct LoweringVisitor
return loweredExpr;
}
- RefPtr<ExpressionSyntaxNode> visit(
+ RefPtr<ExpressionSyntaxNode> visitMemberExpressionSyntaxNode(
MemberExpressionSyntaxNode* expr)
{
auto loweredBase = lowerExpr(expr->BaseExpression);
@@ -521,7 +522,7 @@ struct LoweringVisitor
StmtVisitor::dispatch(stmt);
}
- RefPtr<ScopeDecl> visit(ScopeDecl* decl)
+ RefPtr<ScopeDecl> visitScopeDecl(ScopeDecl* decl)
{
RefPtr<ScopeDecl> loweredDecl = new ScopeDecl();
lowerDeclCommon(loweredDecl, decl);
@@ -594,7 +595,7 @@ struct LoweringVisitor
addStmt(stmt);
}
- void visit(BlockStmt* stmt)
+ void visitBlockStmt(BlockStmt* stmt)
{
RefPtr<BlockStmt> loweredStmt = new BlockStmt();
lowerScopeStmtFields(loweredStmt, stmt);
@@ -606,7 +607,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(SeqStmt* stmt)
+ void visitSeqStmt(SeqStmt* stmt)
{
for( auto ss : stmt->stmts )
{
@@ -614,12 +615,12 @@ struct LoweringVisitor
}
}
- void visit(ExpressionStatementSyntaxNode* stmt)
+ void visitExpressionStatementSyntaxNode(ExpressionStatementSyntaxNode* stmt)
{
addExprStmt(lowerExpr(stmt->Expression));
}
- void visit(VarDeclrStatementSyntaxNode* stmt)
+ void visitVarDeclrStatementSyntaxNode(VarDeclrStatementSyntaxNode* stmt)
{
DeclVisitor::dispatch(stmt->decl);
}
@@ -651,42 +652,42 @@ struct LoweringVisitor
loweredStmt->parentStmt = translateStmtRef(originalStmt->parentStmt);
}
- void visit(ContinueStatementSyntaxNode* stmt)
+ void visitContinueStatementSyntaxNode(ContinueStatementSyntaxNode* stmt)
{
RefPtr<ContinueStatementSyntaxNode> loweredStmt = new ContinueStatementSyntaxNode();
lowerChildStmtFields(loweredStmt, stmt);
addStmt(loweredStmt);
}
- void visit(BreakStatementSyntaxNode* stmt)
+ void visitBreakStatementSyntaxNode(BreakStatementSyntaxNode* stmt)
{
RefPtr<BreakStatementSyntaxNode> loweredStmt = new BreakStatementSyntaxNode();
lowerChildStmtFields(loweredStmt, stmt);
addStmt(loweredStmt);
}
- void visit(DefaultStmt* stmt)
+ void visitDefaultStmt(DefaultStmt* stmt)
{
RefPtr<DefaultStmt> loweredStmt = new DefaultStmt();
lowerChildStmtFields(loweredStmt, stmt);
addStmt(loweredStmt);
}
- void visit(DiscardStatementSyntaxNode* stmt)
+ void visitDiscardStatementSyntaxNode(DiscardStatementSyntaxNode* stmt)
{
RefPtr<DiscardStatementSyntaxNode> loweredStmt = new DiscardStatementSyntaxNode();
lowerStmtFields(loweredStmt, stmt);
addStmt(loweredStmt);
}
- void visit(EmptyStatementSyntaxNode* stmt)
+ void visitEmptyStatementSyntaxNode(EmptyStatementSyntaxNode* stmt)
{
RefPtr<EmptyStatementSyntaxNode> loweredStmt = new EmptyStatementSyntaxNode();
lowerStmtFields(loweredStmt, stmt);
addStmt(loweredStmt);
}
- void visit(UnparsedStmt* stmt)
+ void visitUnparsedStmt(UnparsedStmt* stmt)
{
RefPtr<UnparsedStmt> loweredStmt = new UnparsedStmt();
lowerStmtFields(loweredStmt, stmt);
@@ -696,7 +697,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(CaseStmt* stmt)
+ void visitCaseStmt(CaseStmt* stmt)
{
RefPtr<CaseStmt> loweredStmt = new CaseStmt();
lowerChildStmtFields(loweredStmt, stmt);
@@ -706,7 +707,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(IfStatementSyntaxNode* stmt)
+ void visitIfStatementSyntaxNode(IfStatementSyntaxNode* stmt)
{
RefPtr<IfStatementSyntaxNode> loweredStmt = new IfStatementSyntaxNode();
lowerStmtFields(loweredStmt, stmt);
@@ -718,7 +719,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(SwitchStmt* stmt)
+ void visitSwitchStmt(SwitchStmt* stmt)
{
RefPtr<SwitchStmt> loweredStmt = new SwitchStmt();
lowerScopeStmtFields(loweredStmt, stmt);
@@ -732,7 +733,7 @@ struct LoweringVisitor
}
- void visit(ForStatementSyntaxNode* stmt)
+ void visitForStatementSyntaxNode(ForStatementSyntaxNode* stmt)
{
RefPtr<ForStatementSyntaxNode> loweredStmt = new ForStatementSyntaxNode();
lowerScopeStmtFields(loweredStmt, stmt);
@@ -747,7 +748,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(WhileStatementSyntaxNode* stmt)
+ void visitWhileStatementSyntaxNode(WhileStatementSyntaxNode* stmt)
{
RefPtr<WhileStatementSyntaxNode> loweredStmt = new WhileStatementSyntaxNode();
lowerScopeStmtFields(loweredStmt, stmt);
@@ -760,7 +761,7 @@ struct LoweringVisitor
addStmt(loweredStmt);
}
- void visit(DoWhileStatementSyntaxNode* stmt)
+ void visitDoWhileStatementSyntaxNode(DoWhileStatementSyntaxNode* stmt)
{
RefPtr<DoWhileStatementSyntaxNode> loweredStmt = new DoWhileStatementSyntaxNode();
lowerScopeStmtFields(loweredStmt, stmt);
@@ -805,7 +806,7 @@ struct LoweringVisitor
assign(expr, createVarRef(expr->Position, varDecl));
}
- void visit(ReturnStatementSyntaxNode* stmt)
+ void visitReturnStatementSyntaxNode(ReturnStatementSyntaxNode* stmt)
{
auto loweredStmt = new ReturnStatementSyntaxNode();
lowerStmtCommon(loweredStmt, stmt);
@@ -1004,60 +1005,66 @@ struct LoweringVisitor
// Catch-all
- RefPtr<Decl> visit(ModifierDecl*)
+ RefPtr<Decl> visitModifierDecl(ModifierDecl*)
{
// should not occur in user code
SLANG_UNEXPECTED("modifiers shouldn't occur in user code");
}
- RefPtr<Decl> visit(GenericValueParamDecl*)
+ RefPtr<Decl> visitGenericValueParamDecl(GenericValueParamDecl*)
{
SLANG_UNEXPECTED("generics should be lowered to specialized decls");
}
- RefPtr<Decl> visit(GenericTypeParamDecl*)
+ RefPtr<Decl> visitGenericTypeParamDecl(GenericTypeParamDecl*)
{
SLANG_UNEXPECTED("generics should be lowered to specialized decls");
}
- RefPtr<Decl> visit(GenericTypeConstraintDecl*)
+ RefPtr<Decl> visitGenericTypeConstraintDecl(GenericTypeConstraintDecl*)
{
SLANG_UNEXPECTED("generics should be lowered to specialized decls");
}
- RefPtr<Decl> visit(GenericDecl*)
+ RefPtr<Decl> visitGenericDecl(GenericDecl*)
{
SLANG_UNEXPECTED("generics should be lowered to specialized decls");
}
- RefPtr<Decl> visit(ProgramSyntaxNode*)
+ RefPtr<Decl> visitProgramSyntaxNode(ProgramSyntaxNode*)
{
SLANG_UNEXPECTED("module decls should be lowered explicitly");
}
- RefPtr<Decl> visit(SubscriptDecl*)
+ RefPtr<Decl> visitSubscriptDecl(SubscriptDecl*)
{
// We don't expect to find direct references to a subscript
// declaration, but rather to the underlying accessors
return nullptr;
}
- RefPtr<Decl> visit(InheritanceDecl*)
+ RefPtr<Decl> visitInheritanceDecl(InheritanceDecl*)
{
// We should deal with these explicitly, as part of lowering
// the type that contains them.
return nullptr;
}
- RefPtr<Decl> visit(ExtensionDecl*)
+ RefPtr<Decl> visitExtensionDecl(ExtensionDecl*)
{
// Extensions won't exist in the lowered code: their members
// will turn into ordinary functions that get called explicitly
return nullptr;
}
- RefPtr<Decl> visit(TypeDefDecl* decl)
+ RefPtr<Decl> visitTypeDefDecl(TypeDefDecl* decl)
{
+ if (shared->target == CodeGenTarget::GLSL)
+ {
+ // GLSL does not support `typedef`, so we will lower it out of existence here
+ return nullptr;
+ }
+
RefPtr<TypeDefDecl> loweredDecl = new TypeDefDecl();
lowerDeclCommon(loweredDecl, decl);
@@ -1067,7 +1074,7 @@ struct LoweringVisitor
return loweredDecl;
}
- RefPtr<ImportDecl> visit(ImportDecl* decl)
+ RefPtr<ImportDecl> visitImportDecl(ImportDecl* decl)
{
// No need to translate things here if we are
// in "full" mode, because we will selectively
@@ -1086,7 +1093,7 @@ struct LoweringVisitor
return nullptr;
}
- RefPtr<EmptyDecl> visit(EmptyDecl* decl)
+ RefPtr<EmptyDecl> visitEmptyDecl(EmptyDecl* decl)
{
// Empty declarations are really only useful in GLSL,
// where they are used to hold metadata that doesn't
@@ -1103,7 +1110,7 @@ struct LoweringVisitor
return loweredDecl;
}
- RefPtr<Decl> visit(AggTypeDecl* decl)
+ RefPtr<Decl> visitAggTypeDecl(AggTypeDecl* decl)
{
// We want to lower any aggregate type declaration
// to just a `struct` type that contains its fields.
@@ -1145,7 +1152,7 @@ struct LoweringVisitor
return loweredDecl;
}
- RefPtr<VarDeclBase> visit(
+ RefPtr<VarDeclBase> visitVariable(
Variable* decl)
{
auto loweredDecl = lowerVarDeclCommon(new Variable(), decl);
@@ -1173,13 +1180,13 @@ struct LoweringVisitor
return loweredDecl;
}
- RefPtr<VarDeclBase> visit(
+ RefPtr<VarDeclBase> visitStructField(
StructField* decl)
{
return lowerVarDeclCommon(new StructField(), decl);
}
- RefPtr<VarDeclBase> visit(
+ RefPtr<VarDeclBase> visitParameterSyntaxNode(
ParameterSyntaxNode* decl)
{
return lowerVarDeclCommon(new ParameterSyntaxNode(), decl);
@@ -1191,7 +1198,7 @@ struct LoweringVisitor
}
- RefPtr<Decl> visit(
+ RefPtr<Decl> visitDeclGroup(
DeclGroup* group)
{
for (auto decl : group->decls)
@@ -1201,7 +1208,7 @@ struct LoweringVisitor
return nullptr;
}
- RefPtr<FunctionSyntaxNode> visit(
+ RefPtr<FunctionSyntaxNode> visitFunctionDeclBase(
FunctionDeclBase* decl)
{
// TODO: need to generate a name
@@ -1474,7 +1481,7 @@ struct LoweringVisitor
RefPtr<EntryPointLayout> entryPointLayout)
{
// First, loer the entry-point function as an ordinary function:
- auto loweredEntryPointFunc = visit(entryPointDecl);
+ auto loweredEntryPointFunc = visitFunctionDeclBase(entryPointDecl);
// Now we will generate a `void main() { ... }` function to call the lowered code.
RefPtr<FunctionSyntaxNode> mainDecl = new FunctionSyntaxNode();
@@ -1672,7 +1679,7 @@ struct LoweringVisitor
{
// Default case: lower an entry point just like any other function
default:
- return visit(entryPointDecl);
+ return visitFunctionDeclBase(entryPointDecl);
// For Slang->GLSL translation, we need to lower things from HLSL-style
// declarations over to GLSL conventions
diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp
index ff727cbb6..e1bba8885 100644
--- a/source/slang/slang-stdlib.cpp
+++ b/source/slang/slang-stdlib.cpp
@@ -341,9 +341,20 @@ __generic<T : __BuiltinFloatingPointType> __intrinsic T atan(T x);
__generic<T : __BuiltinFloatingPointType, let N : int> __intrinsic vector<T,N> atan(vector<T,N> x);
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int> __intrinsic matrix<T,N,M> atan(matrix<T,N,M> x);
-__generic<T : __BuiltinFloatingPointType> __intrinsic T atan2(T y, T x);
-__generic<T : __BuiltinFloatingPointType, let N : int> __intrinsic vector<T,N> atan2(vector<T,N> y, vector<T,N> x);
-__generic<T : __BuiltinFloatingPointType, let N : int, let M : int> __intrinsic matrix<T,N,M> atan2(matrix<T,N,M> y, matrix<T,N,M> x);
+__generic<T : __BuiltinFloatingPointType>
+__intrinsic(glsl,"atan($0,$1)")
+__intrinsic
+T atan2(T y, T x);
+
+__generic<T : __BuiltinFloatingPointType, let N : int>
+__intrinsic(glsl,"atan($0,$1)")
+__intrinsic
+vector<T,N> atan2(vector<T,N> y, vector<T,N> x);
+
+__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
+__intrinsic(glsl,"atan($0,$1)")
+__intrinsic
+matrix<T,N,M> atan2(matrix<T,N,M> y, matrix<T,N,M> x);
// Ceiling (HLSL SM 1.0)
__generic<T : __BuiltinFloatingPointType> __intrinsic T ceil(T x);
@@ -581,9 +592,20 @@ __generic<T : __BuiltinFloatingPointType, let N : int, let M : int> __intrinsic
__generic<T : __BuiltinFloatingPointType, let N : int> __intrinsic T length(vector<T,N> x);
// Linear interpolation
-__generic<T : __BuiltinFloatingPointType> __intrinsic T lerp(T x, T y, T s);
-__generic<T : __BuiltinFloatingPointType, let N : int> __intrinsic vector<T,N> lerp(vector<T,N> x, vector<T,N> y, vector<T,N> s);
-__generic<T : __BuiltinFloatingPointType, let N : int, let M : int> __intrinsic matrix<T,N,M> lerp(matrix<T,N,M> x, matrix<T,N,M> y, matrix<T,N,M> s);
+__generic<T : __BuiltinFloatingPointType>
+__intrinsic(glsl, mix)
+__intrinsic
+T lerp(T x, T y, T s);
+
+__generic<T : __BuiltinFloatingPointType, let N : int>
+__intrinsic(glsl, mix)
+__intrinsic
+vector<T,N> lerp(vector<T,N> x, vector<T,N> y, vector<T,N> s);
+
+__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
+__intrinsic(glsl, mix)
+__intrinsic
+matrix<T,N,M> lerp(matrix<T,N,M> x, matrix<T,N,M> y, matrix<T,N,M> s);
// Legacy lighting function (obsolete)
__intrinsic float4 lit(float n_dot_l, float n_dot_h, float m);
@@ -1324,7 +1346,6 @@ namespace Slang
flavor |= (access << 8);
-
// emit a generic signature
// TODO: allow for multisample count to come in as well...
sb << "__generic<T = float4> ";
@@ -1475,11 +1496,15 @@ namespace Slang
// `SampleBias()`
+ sb << "__intrinsic(glsl, \"texture($p, $1, $2)\")\n";
+ sb << "__intrinsic\n";
sb << "T SampleBias(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias);\n";
if( baseShape != TextureType::ShapeCube )
{
+ sb << "__intrinsic(glsl, \"textureOffset($p, $1, $2, $3)\")\n";
+ sb << "__intrinsic\n";
sb << "T SampleBias(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias, ";
sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
@@ -1533,12 +1558,16 @@ namespace Slang
// `SampleLevel`
+ sb << "__intrinsic(glsl, \"textureLod($p, $1, $2)\")\n";
+ sb << "__intrinsic\n";
sb << "T SampleLevel(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float level);\n";
if( baseShape != TextureType::ShapeCube )
{
+ sb << "__intrinsic(glsl, \"textureLodOffset($p, $1, $2, $3)\")\n";
+ sb << "__intrinsic\n";
sb << "T SampleLevel(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float level, ";
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index 4329f6cbd..1704c1224 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -671,6 +671,11 @@ namespace Slang
bool isValid() const { return item.declRef.getDecl() != nullptr; }
bool isOverloaded() const { return items.Count() > 1; }
+
+ String const& getName() const
+ {
+ return items.Count() > 1 ? items[0].declRef.GetName() : item.declRef.GetName();
+ }
};
struct SemanticsVisitor;
diff --git a/source/slang/visitor.h b/source/slang/visitor.h
index 391769eca..7000927c7 100644
--- a/source/slang/visitor.h
+++ b/source/slang/visitor.h
@@ -24,8 +24,8 @@ struct ITypeVisitor
#include "object-meta-end.h"
};
-template<typename Derived, typename Result = void>
-struct TypeVisitor : ITypeVisitor
+template<typename Derived, typename Result = void, typename Base = ITypeVisitor>
+struct TypeVisitor : Base
{
Result dispatch(ExpressionType* type)
{
@@ -37,16 +37,24 @@ struct TypeVisitor : ITypeVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "type-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "type-defs.h"
+#include "object-meta-end.h"
};
-template<typename Derived>
-struct TypeVisitor<Derived,void> : ITypeVisitor
+template<typename Derived, typename Base>
+struct TypeVisitor<Derived,void,Base> : Base
{
void dispatch(ExpressionType* type)
{
@@ -56,12 +64,47 @@ struct TypeVisitor<Derived,void> : ITypeVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "type-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "type-defs.h"
+#include "object-meta-end.h"
+};
+
+template<typename Derived, typename Arg, typename Base = ITypeVisitor>
+struct TypeVisitorWithArg : Base
+{
+ void dispatch(ExpressionType* type, Arg const& arg)
+ {
+ type->accept(this, (void*)&arg);
+ }
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
+#define SYNTAX_CLASS(NAME, BASE) \
+ virtual void dispatch_##NAME(NAME* obj, void* arg) override \
+ { ((Derived*) this)->visit##NAME(obj, *(Arg*)arg); }
+
+#include "object-meta-begin.h"
+#include "type-defs.h"
+#include "object-meta-end.h"
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj, Arg const& arg) \
+ { ((Derived*) this)->visit##BASE(obj, arg); }
+
+#include "object-meta-begin.h"
+#include "type-defs.h"
+#include "object-meta-end.h"
};
//
@@ -92,12 +135,20 @@ struct ExprVisitor : IExprVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "expr-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "expr-defs.h"
+#include "object-meta-end.h"
};
template<typename Derived>
@@ -111,12 +162,47 @@ struct ExprVisitor<Derived,void> : IExprVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
+
+#include "object-meta-begin.h"
+#include "expr-defs.h"
+#include "object-meta-end.h"
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "expr-defs.h"
+#include "object-meta-end.h"
+};
+
+template<typename Derived, typename Arg>
+struct ExprVisitorWithArg : IExprVisitor
+{
+ void dispatch(ExpressionSyntaxNode* obj, Arg const& arg)
+ {
+ obj->accept(this, (void*)&arg);
+ }
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
+#define SYNTAX_CLASS(NAME, BASE) \
+ virtual void dispatch_##NAME(NAME* obj, void* arg) override \
+ { ((Derived*) this)->visit##NAME(obj, *(Arg*)arg); }
#include "object-meta-begin.h"
#include "expr-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj, Arg const& arg) \
+ { ((Derived*) this)->visit##BASE(obj, arg); }
+
+#include "object-meta-begin.h"
+#include "expr-defs.h"
+#include "object-meta-end.h"
};
//
@@ -147,12 +233,20 @@ struct StmtVisitor : IStmtVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "stmt-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "stmt-defs.h"
+#include "object-meta-end.h"
};
template<typename Derived>
@@ -166,12 +260,20 @@ struct StmtVisitor<Derived,void> : IStmtVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "stmt-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "stmt-defs.h"
+#include "object-meta-end.h"
};
//
@@ -202,12 +304,20 @@ struct DeclVisitor : IDeclVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "decl-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "decl-defs.h"
+#include "object-meta-end.h"
};
template<typename Derived>
@@ -221,14 +331,50 @@ struct DeclVisitor<Derived,void> : IDeclVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
+
+#include "object-meta-begin.h"
+#include "decl-defs.h"
+#include "object-meta-end.h"
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "decl-defs.h"
+#include "object-meta-end.h"
+};
+
+template<typename Derived, typename Arg>
+struct DeclVisitorWithArg : IDeclVisitor
+{
+ void dispatch(DeclBase* obj, Arg const& arg)
+ {
+ obj->accept(this, (void*)&arg);
+ }
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
+#define SYNTAX_CLASS(NAME, BASE) \
+ virtual void dispatch_##NAME(NAME* obj, void* arg) override \
+ { ((Derived*) this)->visit##NAME(obj, *(Arg*)arg); }
#include "object-meta-begin.h"
#include "decl-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj, Arg const& arg) \
+ { ((Derived*) this)->visit##BASE(obj, arg); }
+
+#include "object-meta-begin.h"
+#include "decl-defs.h"
+#include "object-meta-end.h"
};
+
//
// Modifier Visitors
//
@@ -257,12 +403,20 @@ struct ModifierVisitor : IModifierVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "modifier-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "modifier-defs.h"
+#include "object-meta-end.h"
};
template<typename Derived>
@@ -276,12 +430,20 @@ struct ModifierVisitor<Derived, void> : IModifierVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "modifier-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "modifier-defs.h"
+#include "object-meta-end.h"
};
//
@@ -299,8 +461,8 @@ struct IValVisitor : ITypeVisitor
#include "object-meta-end.h"
};
-template<typename Derived, typename Result = void>
-struct ValVisitor : IValVisitor
+template<typename Derived, typename Result = void, typename TypeResult = void>
+struct ValVisitor : TypeVisitor<Derived, TypeResult, IValVisitor>
{
Result dispatch(Val* val)
{
@@ -312,17 +474,24 @@ struct ValVisitor : IValVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void* extra) override \
- { *(Result*)extra = ((Derived*) this)->visit(obj); }
+ { *(Result*)extra = ((Derived*) this)->visit##NAME(obj); }
#include "object-meta-begin.h"
#include "val-defs.h"
-#include "type-defs.h"
#include "object-meta-end.h"
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ Result visit##NAME(NAME* obj) \
+ { return ((Derived*) this)->visit##BASE(obj); }
+
+#include "object-meta-begin.h"
+#include "val-defs.h"
+#include "object-meta-end.h"
};
template<typename Derived>
-struct ValVisitor<Derived, void> : IValVisitor
+struct ValVisitor<Derived, void, void> : TypeVisitor<Derived, void, IValVisitor>
{
void dispatch(Val* val)
{
@@ -332,11 +501,19 @@ struct ValVisitor<Derived, void> : IValVisitor
#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) /* empty */
#define SYNTAX_CLASS(NAME, BASE) \
virtual void dispatch_##NAME(NAME* obj, void*) override \
- { ((Derived*) this)->visit(obj); }
+ { ((Derived*) this)->visit##NAME(obj); }
+
+#include "object-meta-begin.h"
+#include "val-defs.h"
+#include "object-meta-end.h"
+
+#define ABSTRACT_SYNTAX_CLASS(NAME,BASE) SYNTAX_CLASS(NAME, BASE)
+#define SYNTAX_CLASS(NAME, BASE) \
+ void visit##NAME(NAME* obj) \
+ { ((Derived*) this)->visit##BASE(obj); }
#include "object-meta-begin.h"
#include "val-defs.h"
-#include "type-defs.h"
#include "object-meta-end.h"
};