diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2017-07-07 15:06:16 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-07-07 15:06:16 -0700 |
| commit | c07a6e6b5f5e0e4839b435ff6c15b821b6dead11 (patch) | |
| tree | 8107b45190a0d8a33d07dcd6500ed0dfa939d530 /source | |
| parent | 975e4b326cd2ef3ef0341d1fb7509315b9dee555 (diff) | |
| parent | f3fe9dddb8c528b4f9955d9105908600b4a8d0c8 (diff) | |
Merge pull request #59 from tfoleyNV/cross-compilation
More work on cross compilation
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/check.cpp | 108 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 4776 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 117 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 43 | ||||
| -rw-r--r-- | source/slang/syntax.h | 5 | ||||
| -rw-r--r-- | source/slang/visitor.h | 219 |
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" }; |
