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