From 8c50f9f2d51ffd77903b742993dae6a663b38286 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 29 Mar 2018 11:57:57 -0700 Subject: 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. --- source/slang/emit.cpp | 3187 ++++--------------------------------------------- 1 file changed, 232 insertions(+), 2955 deletions(-) (limited to 'source') 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 - , ExprVisitorWithArg - , DeclVisitorWithArg { 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(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()) - { - 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) - { - // HACK(tfoley): For now, anything with a constant-buffer type should be - // left implicit. - - // Look through any dereferencing that took place - RefPtr e = expr; - while (auto derefExpr = e.As()) - { - e = derefExpr->base; - } - - if (auto declRefExpr = e.As()) - { - auto decl = declRefExpr->declRef.getDecl(); - if (decl && decl->HasModifier()) - return true; - } - - // Is the expression referencing a uniform parameter group, - // but *not* a `ParameterBlock`? - if (auto parameterBlockType = e->type->As()) - { - return false; - } - if (auto uniformParameterGroupType = e->type->As()) - { - return true; - } - - return false; - } - -#if 0 - void EmitPostfixExpr(RefPtr expr) - { - EmitExprWithPrecedence(expr, kEOp_Postfix); - } -#endif - - void EmitExpr(RefPtr 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 prepareLValueExpr( - RefPtr expr) - { - for(;;) - { - if(auto typeCastExpr = expr.As()) - { - expr = typeCastExpr->Arguments[0]; - } - // TODO: any other cases? - else - { - return expr; - } - } - - } - - void emitInfixExprImpl( - EOpInfo outerPrec, - EOpInfo prec, - char const* op, - RefPtr 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 binExpr) - { - emitInfixExprImpl(outerPrec, prec, op, binExpr, false); - } - - void EmitBinAssignExpr(EOpInfo outerPrec, EOpInfo prec, char const* op, RefPtr binExpr) - { - emitInfixExprImpl(outerPrec, prec, op, binExpr, true); - } - - void emitUnaryExprImpl( - EOpInfo outerPrec, - EOpInfo prec, - char const* preOp, - char const* postOp, - RefPtr 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 expr) - { - emitUnaryExprImpl(outerPrec, prec, preOp, postOp, expr, false); - } - - void EmitUnaryAssignExpr( - EOpInfo outerPrec, - EOpInfo prec, - char const* preOp, - char const* postOp, - RefPtr 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 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,1386 +1313,104 @@ 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 findTargetIntrinsicModifier( - RefPtr syntax) + void emitStringLiteral( + String const& value) { - RefPtr bestModifier; - for(auto m : syntax->GetModifiersOfType()) + emit("\""); + for (auto c : value) { - if(!isTargetIntrinsicModifierApplicable(m)) - continue; + // TODO: This needs a more complete implementation, + // especially if we want to support Unicode. - // 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) + char buffer[] = { c, 0 }; + switch (c) { - bestModifier = m; + default: + emit(buffer); + break; + + case '\"': emit("\\\""); + case '\'': emit("\\\'"); + case '\\': emit("\\\\"); + case '\n': emit("\\n"); + case '\r': emit("\\r"); + case '\t': emit("\\t"); } } - - return bestModifier; + emit("\""); } - void emitSimpleCallArgs( - RefPtr callExpr) + EOpInfo leftSide(EOpInfo const& outerPrec, EOpInfo const& prec) { - Emit("("); - UInt argCount = callExpr->Arguments.Count(); - for (UInt aa = 0; aa < argCount; ++aa) - { - if (aa != 0) Emit(", "); - EmitExpr(callExpr->Arguments[aa]); - } - Emit(")"); + EOpInfo result; + result.leftPrecedence = outerPrec.leftPrecedence; + result.rightPrecedence = prec.leftPrecedence; + return result; } - void emitTypeOrExpr( - Type* type, - Expr* expr) + EOpInfo rightSide(EOpInfo const& prec, EOpInfo const& outerPrec) { - if (type && !type->As()) - { - EmitType(type); - } - else - { - emitTypeBasedOnExpr(expr, nullptr); - } - } - - void emitSimpleConstructorCallExpr( - RefPtr 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 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(); - 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 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()) - { - auto declRef = funcDeclRefExpr->declRef; - if (auto ctorDeclRef = declRef.As()) - { - emitSimpleConstructorCallExpr(callExpr, outerPrec); - return; - } - - if(auto acessorDeclRef = declRef.As()) - { - declRef = acessorDeclRef.GetParent(); - } - - if(auto subscriptDeclRef = declRef.As()) - { - 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) - { - emit("\""); - for (auto c : value) - { - // TODO: This needs a more complete implementation, - // especially if we want to support Unicode. - - 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("\""); - } - - EOpInfo leftSide(EOpInfo const& outerPrec, EOpInfo const& prec) - { - EOpInfo result; - result.leftPrecedence = outerPrec.leftPrecedence; - result.rightPrecedence = prec.leftPrecedence; - return result; - } - - EOpInfo rightSide(EOpInfo const& prec, EOpInfo const& outerPrec) - { - EOpInfo result; - result.leftPrecedence = prec.rightPrecedence; - result.rightPrecedence = outerPrec.rightPrecedence; - return result; - } - - void EmitExprWithPrecedence(RefPtr expr, EOpInfo outerPrec) - { - ExprEmitArg arg; - arg.outerPrec = outerPrec; - - ExprVisitorWithArg::dispatch(expr, arg); - } - - void EmitExprWithPrecedence(RefPtr 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 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() ) - { - 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() ) - { - EmitUnaryExpr( - outerPrec, - kEOp_Prefix, - funcNameText.Buffer(), - "", - callExpr); - } - else if(auto postfixExpr = callExpr.As()) - { - 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(")"); - } + EOpInfo result; + result.leftPrecedence = prec.rightPrecedence; + result.rightPrecedence = outerPrec.rightPrecedence; + return result; } void requireGLSLExtension(String const& name) { Slang::requireGLSLExtension(&context->shared->extensionUsageTracker, name); - } - - void requireGLSLVersion(ProfileVersion version) - { - if (context->shared->target != CodeGenTarget::GLSL) - return; - - Slang::requireGLSLVersionImpl(&context->shared->extensionUsageTracker, version); - } - - void requireGLSLVersion(int version) - { - switch (version) - { - #define CASE(NUMBER) \ - case NUMBER: requireGLSLVersion(ProfileVersion::GLSL_##NUMBER); break - - CASE(110); - CASE(120); - CASE(130); - CASE(140); - CASE(150); - CASE(330); - CASE(400); - CASE(410); - CASE(420); - CASE(430); - CASE(440); - CASE(450); - - #undef CASE - } - } - - void visitInvokeExpr( - RefPtr callExpr, - ExprEmitArg const& arg) - { - auto outerPrec = arg.outerPrec; - - auto funcExpr = callExpr->FunctionExpr; - if (auto funcDeclRefExpr = funcExpr.As()) - { - 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()) - { - // 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()) - { - // 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()) - { - 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()) - { - auto base = memberExpr->BaseExpression; - if (auto baseTextureType = base->type->As()) - { - emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler"); - - if (auto samplerType = callExpr->Arguments[0]->type.type->As()) - { - 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 samplerVar; - for (auto dd : context->shared->program->Members) - { - if (auto varDecl = dd.As()) - { - if (auto samplerType = varDecl->type.type->As()) - { - samplerVar = varDecl; - break; - } - } - } - - if (auto memberExpr = callExpr->FunctionExpr.As()) - { - auto base = memberExpr->BaseExpression; - if (auto baseTextureType = base->type->As()) - { - 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*`, 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()) - { - auto base = memberExpr->BaseExpression; - if (auto baseTextureType = base->type->As()) - { - auto elementType = baseTextureType->elementType; - if (auto basicType = elementType->As()) - { - // A scalar result is expected - Emit(".x"); - } - else if (auto vectorType = elementType->As()) - { - // 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()) - { - 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()) - { - // 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()) - { - - 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()) - { - 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::dispatch(expr->candidiateExprs[0].Ptr(), arg); - } - - void setSampleRateFlag() - { - context->shared->entryPointLayout->flags |= EntryPointLayout::Flag::usesAnySampleRateInput; - } - - void doSampleRateInputCheck(VarDeclBase* decl) - { - if (decl->HasModifier()) - { - setSampleRateFlag(); - } - } - - void doSampleRateInputCheck(Name* name) - { - auto text = getText(name); - if (text == "gl_SampleID") - { - setSampleRateFlag(); - } - } - - 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) - { - // TODO(tfoley): support indenting - Emit("{\n"); - if( auto blockStmt = stmt.As() ) - { - EmitStmt(blockStmt->body); - } - else - { - EmitStmt(stmt); - } - Emit("}\n"); - } - - void EmitLoopAttributes(RefPtr decl) - { - // NOTE: Emit-from-AST is gone, so this code is doing nothing. - } - - void EmitUnparsedStmt(RefPtr 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) - { - // 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()) - { - EmitBlockStmt(blockStmt); - return; - } - else if (auto seqStmt = stmt.As()) - { - for (auto ss : seqStmt->stmts) - { - EmitStmt(ss); - } - return; - } - else if( auto unparsedStmt = stmt.As() ) - { - EmitUnparsedStmt(unparsedStmt); - return; - } - else if (auto exprStmt = stmt.As()) - { - EmitExpr(exprStmt->Expression); - Emit(";\n"); - return; - } - else if (auto returnStmt = stmt.As()) - { - Emit("return"); - if (auto expr = returnStmt->Expression) - { - Emit(" "); - EmitExpr(expr); - } - Emit(";\n"); - return; - } - else if (auto declStmt = stmt.As()) - { - EmitDecl(declStmt->decl); - return; - } - else if (auto ifStmt = stmt.As()) - { - 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()) - { - // 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()) - { - 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"); - } + } + void requireGLSLVersion(ProfileVersion version) + { + if (context->shared->target != CodeGenTarget::GLSL) return; - } - else if (auto whileStmt = stmt.As()) - { - EmitLoopAttributes(whileStmt); - Emit("while("); - EmitExpr(whileStmt->Predicate); - Emit(")\n"); - EmitBlockStmt(whileStmt->Statement); - return; - } - else if (auto doWhileStmt = stmt.As()) - { - EmitLoopAttributes(doWhileStmt); + Slang::requireGLSLVersionImpl(&context->shared->extensionUsageTracker, version); + } - Emit("do\n"); - EmitBlockStmt(doWhileStmt->Statement); - Emit(" while("); - EmitExpr(doWhileStmt->Predicate); - Emit(");\n"); - return; - } - else if (auto discardStmt = stmt.As()) - { - Emit("discard;\n"); - return; - } - else if (auto emptyStmt = stmt.As()) - { - return; - } - else if (auto switchStmt = stmt.As()) - { - Emit("switch("); - EmitExpr(switchStmt->condition); - Emit(")\n"); - EmitBlockStmt(switchStmt->body); - return; - } - else if (auto caseStmt = stmt.As()) - { - Emit("case "); - EmitExpr(caseStmt->expr); - Emit(":\n"); - return; - } - else if (auto defaultStmt = stmt.As()) - { - Emit("default:\n"); - return; - } - else if (auto breakStmt = stmt.As()) + void requireGLSLVersion(int version) + { + switch (version) { - Emit("break;\n"); - return; + #define CASE(NUMBER) \ + case NUMBER: requireGLSLVersion(ProfileVersion::GLSL_##NUMBER); break + + CASE(110); + CASE(120); + CASE(130); + CASE(140); + CASE(150); + CASE(330); + CASE(400); + CASE(410); + CASE(420); + CASE(430); + CASE(440); + CASE(450); + + #undef CASE } - else if (auto continueStmt = stmt.As()) + } + + void setSampleRateFlag() + { + context->shared->entryPointLayout->flags |= EntryPointLayout::Flag::usesAnySampleRateInput; + } + + void doSampleRateInputCheck(Name* name) + { + auto text = getText(name); + if (text == "gl_SampleID") { - Emit("continue;\n"); - return; + setSampleRateFlag(); } - - SLANG_UNEXPECTED("unhandled statement kind"); } // // Declaration References // - // Declaration References - void EmitVal(RefPtr val) { if (auto type = val.As()) @@ -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()) - 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(modifier)) - { - if (auto fieldDecl = dynamic_cast(decl)) - { - return true; - } - } - - } - break; - } - - - return false; - } - - // Emit any modifiers that should go in front of a declaration - void EmitModifiers(RefPtr decl) - { - // Emit any GLSL `layout` modifiers first - bool anyLayout = false; - for( auto mod : decl->GetModifiersOfType()) - { - 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()) Emit(#KEYWORD " ") - - #define CASE2(TYPE, HLSL_NAME, GLSL_NAME) \ - else if(auto mod_##TYPE = mod.As()) 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()) 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()) - { - // 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()) - { - 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()) { - Emit(": "); + Emit(" : "); emit(simple->name.Content); } else if(auto registerSemantic = semantic.As()) { // 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()) { // 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 { @@ -3417,117 +1515,24 @@ struct EmitVisitor void EmitSemantics(RefPtr decl, ESemanticMask mask = kESemanticMask_Default ) { - // Don't emit semantics if we aren't translating down to HLSL - switch (context->shared->target) - { - case CodeGenTarget::HLSL: - break; - - default: - return; - } - - for (auto mod = decl->modifiers.first; mod; mod = mod->next) - { - auto semantic = mod.As(); - if (!semantic) - continue; - - EmitSemantic(semantic, mask); - } - } - - void EmitDeclsInContainer(RefPtr container) - { - for (auto member : container->Members) - { - EmitDecl(member); - } - } - - void EmitDeclsInContainerUsingLayout( - RefPtr container, - RefPtr containerLayout) - { - for (auto member : container->Members) - { - RefPtr memberLayout; - if( containerLayout->mapVarToLayout.TryGetValue(member.Ptr(), memberLayout) ) - { - EmitDeclUsingLayout(member, memberLayout); - } - else - { - // No layout for this decl - EmitDecl(member); - } - } - } - - void visitStructDecl(RefPtr decl, DeclEmitArg const&) - { - // Don't emit a declaration that was only generated implicitly, for - // the purposes of semantic checking. - if(decl->HasModifier()) - 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 declRef) - { - EmitModifiers(declRef.getDecl()); - - auto type = GetType(declRef); - if (!type || type->As()) - { - EmitType(declRef.getDecl()->type, declRef.getDecl()->getName()); - } - else - { - EmitType(GetType(declRef), declRef.getDecl()->getName()); - } - - EmitSemantics(declRef.getDecl()); - } - - void emitVarDeclInit(DeclRef declRef) - { - // TODO(tfoley): technically have to apply substitution here too... - if (auto initExpr = declRef.getDecl()->initExpr) - { - if (declRef.As() - && context->shared->target == CodeGenTarget::GLSL) - { - // Don't emit default parameter values when lowering to GLSL - } - else - { - Emit(" = "); - EmitExpr(initExpr); - } + // Don't emit semantics if we aren't translating down to HLSL + switch (context->shared->target) + { + case CodeGenTarget::HLSL: + break; + + default: + return; } - } - void EmitVarDeclCommon(DeclRef declRef) - { - emitVarDeclHead(declRef); - emitVarDeclInit(declRef); - } + for (auto mod = decl->modifiers.first; mod; mod = mod->next) + { + auto semantic = mod.As(); + if (!semantic) + continue; - // Shared emit logic for variable declarations (used for parameters, locals, globals, fields) - void EmitVarDeclCommon(RefPtr decl) - { - EmitVarDeclCommon(DeclRef(decl.Ptr(), nullptr).As()); + EmitSemantic(semantic, mask); + } } // A chain of variables to use for emitting semantic/layout info @@ -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 maybeFetchLayout( - RefPtr decl, - RefPtr 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(); - if (!modifier) return nullptr; - - auto computedLayout = modifier->layout; - SLANG_RELEASE_ASSERT(computedLayout); - - auto varLayout = computedLayout.As(); - return varLayout; - } - void emitHLSLParameterGroupFieldLayoutSemantics( EmitVarChain* chain) { @@ -3755,160 +1741,6 @@ struct EmitVisitor emitHLSLParameterGroupFieldLayoutSemantics(&chain); } - void emitHLSLParameterBlockDecl( - RefPtr varDecl, - RefPtr parameterBlockType, - RefPtr 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 bufferLayout = varLayout->typeLayout.As(); - SLANG_RELEASE_ASSERT(bufferLayout); - - RefPtr containerVarLayout = bufferLayout->containerVarLayout; - EmitVarChain containerChain(containerVarLayout, &blockChain); - - RefPtr 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 gFoo; - // - // and we are desugaring it into something like: - // - // cbuffer anon0 { Foo gFoo; } - // - - RefPtr 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 varDecl, - RefPtr parameterGroupType, - RefPtr varLayout) - { - if( auto parameterBlockType = parameterGroupType->As()) - { - emitHLSLParameterBlockDecl(varDecl, parameterBlockType, varLayout); - return; - } - if( auto textureBufferType = parameterGroupType->As() ) - { - Emit("tbuffer "); - } - else - { - Emit("cbuffer "); - } - - EmitVarChain blockChain(varLayout); - - // The data type that describes where stuff in the constant buffer should go - RefPtr 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 bufferLayout = varLayout->typeLayout.As(); - SLANG_RELEASE_ASSERT(bufferLayout); - - auto containerVarLayout = bufferLayout->containerVarLayout; - EmitVarChain containerChain(containerVarLayout, &blockChain); - - auto elementVarLayout = bufferLayout->elementVarLayout; - EmitVarChain elementChain(elementVarLayout, &blockChain); - - RefPtr structTypeLayout = bufferLayout->elementVarLayout->typeLayout.As(); - SLANG_RELEASE_ASSERT(structTypeLayout); - - - Emit(" "); - if( auto reflectionNameModifier = varDecl->FindModifier() ) - { - 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()) - { - if (auto structRef = declRefType->declRef.As()) - { - int fieldCounter = 0; - - for (auto field : getMembersOfType(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 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) @@ -3948,426 +1780,81 @@ struct EmitVisitor Emit("layout(offset = "); Emit(index); - Emit(")\n"); - } - } - break; - - case LayoutResourceKind::VertexInput: - case LayoutResourceKind::FragmentOutput: - Emit("layout(location = "); - Emit(index); - Emit(")\n"); - break; - - case LayoutResourceKind::SpecializationConstant: - Emit("layout(constant_id = "); - Emit(index); - Emit(")\n"); - break; - - case LayoutResourceKind::ConstantBuffer: - case LayoutResourceKind::ShaderResource: - case LayoutResourceKind::UnorderedAccess: - case LayoutResourceKind::SamplerState: - case LayoutResourceKind::DescriptorTableSlot: - Emit("layout(binding = "); - Emit(index); - if(space) - { - Emit(", set = "); - Emit(space); - } - Emit(")\n"); - break; - - case LayoutResourceKind::PushConstantBuffer: - Emit("layout(push_constant)\n"); - break; - - } - } - - void emitGLSLLayoutQualifiers( - RefPtr layout, - EmitVarChain* inChain, - LayoutResourceKind filter = LayoutResourceKind::None) - { - if(!layout) return; - - switch( context->shared->target ) - { - default: - return; - - case CodeGenTarget::GLSL: - break; - } - - EmitVarChain chain(layout, inChain); - - for( auto info : layout->resourceInfos ) - { - // Skip info that doesn't match our filter - if (filter != LayoutResourceKind::None - && filter != info.kind) - { - continue; - } - - emitGLSLLayoutQualifier(info.kind, &chain); - } - } - - void emitGLSLParameterBlockDecl( - RefPtr varDecl, - RefPtr parameterBlockType, - RefPtr varLayout) - { - EmitVarChain blockChain(varLayout); - - RefPtr bufferLayout = varLayout->typeLayout.As(); - 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 elementType = parameterBlockType->elementType; - - EmitType(elementType, varDecl->getName()); - Emit(";\n"); - - Emit("};\n"); - } - - void emitGLSLParameterGroupDecl( - RefPtr varDecl, - RefPtr parameterGroupType, - RefPtr varLayout) - { - if( auto parameterBlockType = parameterGroupType->As()) - { - emitGLSLParameterBlockDecl(varDecl, parameterBlockType, varLayout); - return; - } - - // The data type that describes where stuff in the constant buffer should go - RefPtr dataType = parameterGroupType->elementType; - - // We expect the layout, if present, to be for a structured type... - RefPtr structTypeLayout; - - EmitVarChain blockChain; - if (varLayout) - { - blockChain = EmitVarChain(varLayout); - - auto typeLayout = varLayout->typeLayout; - if (auto bufferLayout = typeLayout.As()) - { - 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(); - SLANG_RELEASE_ASSERT(structTypeLayout); - } - - - EmitModifiers(varDecl); - - // Emit an apprpriate declaration keyword based on the kind of block - if (parameterGroupType->As()) - { - Emit("buffer"); - } - // Note: tested `buffer` case before `uniform`, since `GLSLShaderStorageBufferType` - // is also a subclass of `UniformParameterGroupType`. - else if (parameterGroupType->As()) - { - Emit("uniform"); - } - else if (parameterGroupType->As()) - { - Emit("in"); - } - else if (parameterGroupType->As()) - { - Emit("out"); - } - else - { - SLANG_DIAGNOSE_UNEXPECTED(getSink(), varDecl, "unhandled GLSL shader parameter kind"); - Emit("uniform"); - } - - if( auto reflectionNameModifier = varDecl->FindModifier() ) - { - 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()) - { - - if (auto structRef = declRefType->declRef.As()) - { - int fieldCounter = 0; - for (auto field : getMembersOfType(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 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"); + } + } + break; - Emit(";\n"); - } + case LayoutResourceKind::VertexInput: + case LayoutResourceKind::FragmentOutput: + Emit("layout(location = "); + Emit(index); + Emit(")\n"); + break; - void emitParameterGroupDecl( - RefPtr varDecl, - RefPtr parameterGroupType, - RefPtr layout) - { - switch(context->shared->target) - { - case CodeGenTarget::HLSL: - emitHLSLParameterGroupDecl(varDecl, parameterGroupType, layout); + case LayoutResourceKind::SpecializationConstant: + Emit("layout(constant_id = "); + Emit(index); + Emit(")\n"); break; - case CodeGenTarget::GLSL: - emitGLSLParameterGroupDecl(varDecl, parameterGroupType, layout); + case LayoutResourceKind::ConstantBuffer: + case LayoutResourceKind::ShaderResource: + case LayoutResourceKind::UnorderedAccess: + case LayoutResourceKind::SamplerState: + case LayoutResourceKind::DescriptorTableSlot: + Emit("layout(binding = "); + Emit(index); + if(space) + { + Emit(", set = "); + Emit(space); + } + Emit(")\n"); break; - default: - SLANG_DIAGNOSE_UNEXPECTED(getSink(), varDecl, "unhandled code generation target"); + case LayoutResourceKind::PushConstantBuffer: + Emit("layout(push_constant)\n"); break; + } } - void visitVarDeclBase(RefPtr decl, DeclEmitArg const& arg) + void emitGLSLLayoutQualifiers( + RefPtr layout, + EmitVarChain* inChain, + LayoutResourceKind filter = LayoutResourceKind::None) { - // Global variable? Check if it is a sample-rate input. - if (dynamic_cast(decl->ParentDecl)) - { - if (decl->HasModifier()) - { - 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 layout = arg.layout; - layout = maybeFetchLayout(decl, layout); + if(!layout) return; - // 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()) + switch( context->shared->target ) { - emitParameterGroupDecl(decl, parameterGroupType, layout); + default: return; + + case CodeGenTarget::GLSL: + break; } + EmitVarChain chain(layout, inChain); - if (context->shared->target == CodeGenTarget::GLSL) + for( auto info : layout->resourceInfos ) { - if (decl->HasModifier()) - { - emitGLSLLayoutQualifiers(layout, nullptr, LayoutResourceKind::VertexInput); - } - else if (decl->HasModifier()) - { - emitGLSLLayoutQualifiers(layout, nullptr, LayoutResourceKind::FragmentOutput); - } - else + // Skip info that doesn't match our filter + if (filter != LayoutResourceKind::None + && filter != info.kind) { - emitGLSLLayoutQualifiers(layout, nullptr); + continue; } - // If we have a uniform that wasn't tagged `uniform` in GLSL, then fix that here - if (layout - && !decl->HasModifier()) - { - if (layout->FindResourceInfo(LayoutResourceKind::Uniform) - || layout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) - { - Emit("uniform "); - } - } + emitGLSLLayoutQualifier(info.kind, &chain); } - - emitVarDeclHead(makeDeclRef(decl.Ptr())); - emitHLSLRegisterSemantics(layout); - emitVarDeclInit(makeDeclRef(decl.Ptr())); - - Emit(";\n"); - } - - void EmitParamDecl(RefPtr decl) - { - EmitVarDeclCommon(decl); } - void visitFuncDecl(RefPtr 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()) - { - 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() ) - { - // 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) - { -emitDeclImpl(decl, nullptr); - } - - void EmitDeclUsingLayout(RefPtr decl, RefPtr layout) - { - emitDeclImpl(decl, layout); - } - - void EmitDecl(RefPtr declBase) - { - if( auto decl = declBase.As() ) - { - EmitDecl(decl); - } - else if( auto declGroup = declBase.As() ) - { - 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()) { @@ -7945,9 +5220,10 @@ emitDeclImpl(decl, nullptr); if(declRef.getDecl()->HasModifier()) return; - Emit("struct "); + Emit("\nstruct "); EmitDeclRef(declRef); Emit("\n{\n"); + indent(); for( auto ff : GetFields(declRef) ) { if(ff.getDecl()->HasModifier()) @@ -7965,6 +5241,7 @@ emitDeclImpl(decl, nullptr); emit(";\n"); } + dedent(); Emit("};\n"); } -- cgit v1.2.3