diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-06-28 13:34:38 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-07-06 09:17:04 -0700 |
| commit | f145e09a6dcbcf326f782b3e6a76dbf291c792cf (patch) | |
| tree | 88a04619ceaaa37b87199dd82334cc9d102c156d /source/slang/emit.cpp | |
| parent | c0d2c17bc73bc2a8863e086af3ea395ad09465ee (diff) | |
Start to support cross-compilation via "lowering" pass
- The big change here is the introduction of a "lowering" pass that takes an input AST from the semantic checker, and produces an output AST suitable for emitting. The intention is that he lowering pass is responsible for:
- Stripping out unused code (when we have enough information to do so), by only outputting declarations that are transitively references from an entry point
- When cross-compiling to GLSL, generating a suitable `void main()` entry point to wrap the user-written entry-point function
- (Eventually) legalizing types in the program, by scalarizing aggregate types that mix uniform and resource types
- (Eventually) instantiating generic declarations so that the resulting code only deals with fully specialized declarations
- (Eventually) de-sugaring OOP constructs into basic "structs and functions" form
- (Eventually) instantiating code that depends on interface types at the concrete types chosen
- It is clear that there is still a lot of work to be done there, to this change is really about getting infrastructure in place without breaking the existing test cases.
- One cleanup here is that we get rid of the idea of whole-translation-unit output, since that was specific to HLSL output, and there is really no strong reason for keeping it. Users should now just ask for the output for each entry point that they wanted to generate.
- The biggest source of complexity for the lowering process is that it needs to produce the same AST structure as the input, to deal with the complexity of the rewriter case. That is, we need the output to be able to reproduce the input exactly in the case where we are rewriting and nothing needs to change, so the output format needs at least the degrees of freedom of the input.
- As a result, we end up having to distinguish "rewriter" and "full" modes in both lowering and code-emit steps, so that we can react appropriately.
- Generating a GLSL `main()` also adds a lot of complexity. Right now I'm using the simplest approach, where we always output the Slang/HLSL entry point as an ordinary function (as written) and then emit a simple GLSL `main()` to call it. I generate globals for all the shader inputs/outputs (these need to be scalarized and have explicit `location`s attached), and then collect these into the `struct` types of the original parameters as needed.
- This approach will start to have some major down-sides once we have to deal with "arrayed" input/output
- A long-term question here is how to replace entry-point parameter types with scalarized and/or "transposed" versions, while still letting the original code work as written (including copying those inputs to temporary arrays)
- Split `BlockStatementSyntaxNode` into:
- `BlockStmt` which just provides a scope around a `body` statement
- `SeqStmt` which just allows multiple statements to be treated as one
- Change how we emit `for` loops, to deal with the case where the initialization part might expand into multiple statements
- Basically `for(A;B;C) {D}` becomes `{A; for(;B;C) {D}}`, so we can handle arbitrary statements for `A`
- As an additional wrinkle, when we are rewriting HLSL, we just generate `A; for(;B;C) {D}` to deal with the broken scoping there
- This change is needed because the lowering pass was sometimes expanding the original initialization statement `A` into a block `{A}`. Certainly if it declared multiple variables we'd need to handle it, and this seemed the easiest way
- A more significant challenge for lowering would come if/when we ever wanted to support true short-circuiting behavior for `&&` and `||`
- For right now I'm not changing the behavior of the "rewriter" mode, so we still have `UnparsedStmt` instances being generated, but it is clear that eventually we need to parse *all* input, even if we can't type-check 100% of it. This is required so that we can rewrite user code that might refer to a shader input with interface type.
Diffstat (limited to 'source/slang/emit.cpp')
| -rw-r--r-- | source/slang/emit.cpp | 435 |
1 files changed, 248 insertions, 187 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index dc7c68aa4..71b39aabb 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -1,6 +1,7 @@ // emit.cpp #include "emit.h" +#include "lower.h" #include "syntax.h" #include "type-layout.h" @@ -13,8 +14,16 @@ namespace Slang { -struct EmitContext +// Shared state for an entire emit session +struct SharedEmitContext { + // The target language we want to generate code for + CodeGenTarget target; + + // A set of words reserved by the target + Dictionary<String, String> reservedWords; + + // The string of code we've built so far StringBuilder sb; // Current source position for tracking purposes... @@ -22,12 +31,6 @@ struct EmitContext CodePosition nextSourceLocation; bool needToUpdateSourceLocation; - // The target language we want to generate code for - CodeGenTarget target; - - // A set of words reserved by the target - Dictionary<String, String> reservedWords; - // For GLSL output, we can't emit traidtional `#line` directives // with a file path in them, so we maintain a map that associates // each path with a unique integer, and then we output those @@ -45,6 +48,18 @@ struct EmitContext // TODO: This will probably change if we represent imports // explicitly in the layout data. StructTypeLayout* globalStructLayout; + + ProgramLayout* programLayout; +}; + +struct EmitContext +{ + // The shared context that is in effect + SharedEmitContext* shared; + + // Are we in "rewrite" mode, where we are trying to reproduce the input + // code as closely as posible? + bool isRewrite; }; // @@ -79,7 +94,7 @@ static void emitRawTextSpan(EmitContext* context, char const* textBegin, char co // TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things... auto len = int(textEnd - textBegin); - context->sb.Append(textBegin, len); + context->shared->sb.Append(textBegin, len); } static void emitRawText(EmitContext* context, char const* text) @@ -99,7 +114,7 @@ static void emitTextSpan(EmitContext* context, char const* textBegin, char const // Update our logical position // TODO(tfoley): Need to make "corelib" not use `int` for pointer-sized things... auto len = int(textEnd - textBegin); - context->loc.Col += len; + context->shared->loc.Col += len; } static void Emit(EmitContext* context, char const* textBegin, char const* textEnd) @@ -123,8 +138,8 @@ static void Emit(EmitContext* context, char const* textBegin, char const* textEn // At the end of a line, we need to update our tracking // information on code positions emitTextSpan(context, spanBegin, spanEnd); - context->loc.Line++; - context->loc.Col = 1; + context->shared->loc.Line++; + context->shared->loc.Col = 1; // Start a new span for emit purposes spanBegin = spanEnd; @@ -144,7 +159,7 @@ static void emit(EmitContext* context, String const& text) static bool isReservedWord(EmitContext* context, String const& name) { - return context->reservedWords.TryGetValue(name) != nullptr; + return context->shared->reservedWords.TryGetValue(name) != nullptr; } static void emitName( @@ -449,7 +464,7 @@ static bool isTargetIntrinsicModifierApplicable( // we expect. auto const& targetName = targetToken.Content; - switch(context->target) + switch(context->shared->target) { default: assert(!"unexpected"); @@ -658,7 +673,7 @@ static void emitCallExpr( case IntrinsicOp::InnerProduct_Vector_Vector: // HLSL allows `mul()` to be used as a synonym for `dot()`, // so we need to translate to `dot` for GLSL - if (context->target == CodeGenTarget::GLSL) + if (context->shared->target == CodeGenTarget::GLSL) { Emit(context, "dot("); EmitExpr(context, callExpr->Arguments[0]); @@ -677,7 +692,7 @@ static void emitCallExpr( // // The other critical detail here is that the way we handle matrix // conventions requires that the operands to the product be swapped. - if (context->target == CodeGenTarget::GLSL) + if (context->shared->target == CodeGenTarget::GLSL) { Emit(context, "(("); EmitExpr(context, callExpr->Arguments[1]); @@ -825,6 +840,13 @@ static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntax Emit(context, " : "); EmitExprWithPrecedence(context, selectExpr->Arguments[2], kPrecedence_Conditional); } + else if (auto assignExpr = expr.As<AssignExpr>()) + { + needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Assign); + EmitExprWithPrecedence(context, assignExpr->left, kPrecedence_Assign); + Emit(context, " = "); + EmitExprWithPrecedence(context, assignExpr->right, kPrecedence_Assign); + } else if (auto callExpr = expr.As<InvokeExpressionSyntaxNode>()) { emitCallExpr(context, callExpr, outerPrec); @@ -970,7 +992,7 @@ static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntax } else if (auto castExpr = expr.As<TypeCastExpressionSyntaxNode>()) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::GLSL: // GLSL requires constructor syntax for all conversions @@ -1227,7 +1249,7 @@ static void emitTextureType( EmitContext* context, RefPtr<TextureType> texType) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::HLSL: emitHLSLTextureType(context, texType); @@ -1247,7 +1269,7 @@ static void emitTextureSamplerType( EmitContext* context, RefPtr<TextureSamplerType> type) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::GLSL: emitGLSLTextureSamplerType(context, type); @@ -1263,7 +1285,7 @@ static void emitImageType( EmitContext* context, RefPtr<GLSLImageType> type) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::HLSL: emitHLSLTextureType(context, type); @@ -1300,7 +1322,7 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara } else if (auto vecType = type->As<VectorExpressionType>()) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::GLSL: case CodeGenTarget::GLSL_Vulkan: @@ -1331,7 +1353,7 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara } else if (auto matType = type->As<MatrixExpressionType>()) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::GLSL: case CodeGenTarget::GLSL_Vulkan: @@ -1386,7 +1408,7 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type, EDeclara } else if (auto samplerStateType = type->As<SamplerStateType>()) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::HLSL: default: @@ -1474,7 +1496,8 @@ static void EmitType(EmitContext* context, RefPtr<ExpressionType> type) static void EmitType(EmitContext* context, TypeExp const& typeExp, Token const& nameToken) { - EmitType(context, typeExp.type, typeExp.exp->Position, nameToken.Content, nameToken.Position); + EmitType(context, typeExp.type, + typeExp.exp ? typeExp.exp->Position : CodePosition(), nameToken.Content, nameToken.Position); } static void EmitType(EmitContext* context, TypeExp const& typeExp, String const& name) @@ -1497,12 +1520,9 @@ static void EmitBlockStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt { // TODO(tfoley): support indenting Emit(context, "{\n"); - if( auto blockStmt = stmt.As<BlockStatementSyntaxNode>() ) + if( auto blockStmt = stmt.As<BlockStmt>() ) { - for (auto s : blockStmt->Statements) - { - EmitStmt(context, s); - } + EmitStmt(context, blockStmt->body); } else { @@ -1544,7 +1564,7 @@ static void emitLineDirective( emitRawText(context, " "); - if(context->target == CodeGenTarget::GLSL) + if(context->shared->target == CodeGenTarget::GLSL) { auto path = sourceLocation.FileName; @@ -1556,10 +1576,10 @@ static void emitLineDirective( // extension and then emit a traditional line directive. int id = 0; - if(!context->mapGLSLSourcePathToID.TryGetValue(path, id)) + if(!context->shared->mapGLSLSourcePathToID.TryGetValue(path, id)) { - id = context->glslSourceIDCount++; - context->mapGLSLSourcePathToID.Add(path, id); + id = context->shared->glslSourceIDCount++; + context->shared->mapGLSLSourcePathToID.Add(path, id); } sprintf(buffer, "%d", id); @@ -1611,9 +1631,9 @@ static void emitLineDirectiveAndUpdateSourceLocation( { emitLineDirective(context, sourceLocation); - context->loc.FileName = sourceLocation.FileName; - context->loc.Line = sourceLocation.Line; - context->loc.Col = 1; + context->shared->loc.FileName = sourceLocation.FileName; + context->shared->loc.Line = sourceLocation.Line; + context->shared->loc.Col = 1; } static void emitLineDirectiveIfNeeded( @@ -1628,24 +1648,24 @@ static void emitLineDirectiveIfNeeded( // a differnet file or line, *or* if the source location is // somehow later on the line than what we want to emit, // then we need to emit a new `#line` directive. - if(sourceLocation.FileName != context->loc.FileName - || sourceLocation.Line != context->loc.Line - || sourceLocation.Col < context->loc.Col) + if(sourceLocation.FileName != context->shared->loc.FileName + || sourceLocation.Line != context->shared->loc.Line + || sourceLocation.Col < context->shared->loc.Col) { // Special case: if we are in the same file, and within a small number // of lines of the target location, then go ahead and output newlines // to get us caught up. enum { kSmallLineCount = 3 }; - auto lineDiff = sourceLocation.Line - context->loc.Line; - if(sourceLocation.FileName == context->loc.FileName - && sourceLocation.Line > context->loc.Line + auto lineDiff = sourceLocation.Line - context->shared->loc.Line; + if(sourceLocation.FileName == context->shared->loc.FileName + && sourceLocation.Line > context->shared->loc.Line && lineDiff <= kSmallLineCount) { for(int ii = 0; ii < lineDiff; ++ii ) { Emit(context, "\n"); } - assert(sourceLocation.Line == context->loc.Line); + assert(sourceLocation.Line == context->shared->loc.Line); } else { @@ -1661,14 +1681,14 @@ static void emitLineDirectiveIfNeeded( // came in as spaces or tabs, so there is necessarily going to be // coupling between how the downstream compiler counts columns, // and how we do. - if(sourceLocation.Col > context->loc.Col) + if(sourceLocation.Col > context->shared->loc.Col) { - int delta = sourceLocation.Col - context->loc.Col; + int delta = sourceLocation.Col - context->shared->loc.Col; for( int ii = 0; ii < delta; ++ii ) { emitRawText(context, " "); } - context->loc.Col = sourceLocation.Col; + context->shared->loc.Col = sourceLocation.Col; } } @@ -1680,22 +1700,22 @@ static void advanceToSourceLocation( if(sourceLocation.Line <= 0) return; - context->needToUpdateSourceLocation = true; - context->nextSourceLocation = sourceLocation; + context->shared->needToUpdateSourceLocation = true; + context->shared->nextSourceLocation = sourceLocation; } static void flushSourceLocationChange( EmitContext* context) { - if(!context->needToUpdateSourceLocation) + if(!context->shared->needToUpdateSourceLocation) return; // Note: the order matters here, because trying to update // the source location may involve outputting text that // advances the location, and outputting text is what // triggers this flush operation. - context->needToUpdateSourceLocation = false; - emitLineDirectiveIfNeeded(context, context->nextSourceLocation); + context->shared->needToUpdateSourceLocation = false; + emitLineDirectiveIfNeeded(context, context->shared->nextSourceLocation); } static void emitTokenWithLocation(EmitContext* context, Token const& token) @@ -1737,11 +1757,19 @@ static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt) // Try to ensure that debugging can find the right location advanceToSourceLocation(context, stmt->Position); - if (auto blockStmt = stmt.As<BlockStatementSyntaxNode>()) + if (auto blockStmt = stmt.As<BlockStmt>()) { EmitBlockStmt(context, blockStmt); return; } + else if (auto seqStmt = stmt.As<SeqStmt>()) + { + for (auto ss : seqStmt->stmts) + { + EmitStmt(context, ss); + } + return; + } else if( auto unparsedStmt = stmt.As<UnparsedStmt>() ) { EmitUnparsedStmt(context, unparsedStmt); @@ -1784,17 +1812,43 @@ static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt) } else if (auto forStmt = stmt.As<ForStatementSyntaxNode>()) { - EmitLoopAttributes(context, 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. + // - Emit(context, "for("); - if (auto initStmt = forStmt->InitialStatement) + // The one wrinkle is that HLSL implements the + // bad approach to scoping a `for` loop variable, + // so we need to avoid those outer `{...}` when + // we are generating HLSL via "rewrite" (that is, + // without our semantic checks). + // + bool brokenScoping = false; + if (context->shared->target == CodeGenTarget::HLSL + && context->isRewrite) { - EmitStmt(context, initStmt); + brokenScoping = true; } - else + + auto initStmt = forStmt->InitialStatement; + if(initStmt) { - Emit(context, ";"); + if(!brokenScoping) + Emit(context, "{\n"); + EmitStmt(context, initStmt); } + + EmitLoopAttributes(context, forStmt); + + Emit(context, "for(;"); if (auto testExp = forStmt->PredicateExpression) { EmitExpr(context, testExp); @@ -1806,6 +1860,13 @@ static void EmitStmt(EmitContext* context, RefPtr<StatementSyntaxNode> stmt) } Emit(context, ")\n"); EmitBlockStmt(context, forStmt->Statement); + + if (initStmt) + { + if(!brokenScoping) + Emit(context, "}\n"); + } + return; } else if (auto whileStmt = stmt.As<WhileStatementSyntaxNode>()) @@ -2085,7 +2146,7 @@ static void EmitSemantic(EmitContext* context, RefPtr<HLSLSemantic> semantic, ES static void EmitSemantics(EmitContext* context, RefPtr<Decl> decl, ESemanticMask mask = kESemanticMask_Default ) { // Don't emit semantics if we aren't translating down to HLSL - switch (context->target) + switch (context->shared->target) { case CodeGenTarget::HLSL: break; @@ -2263,7 +2324,7 @@ static void emitHLSLRegisterSemantics( { if (!layout) return; - switch( context->target ) + switch( context->shared->target ) { default: return; @@ -2278,6 +2339,25 @@ static void emitHLSLRegisterSemantics( } } +static RefPtr<VarLayout> maybeFetchLayout( + RefPtr<Decl> decl, + RefPtr<VarLayout> layout) +{ + // If we have already found layout info, don't go searching + if (layout) return layout; + + // Otherwise, we need to look and see if computed layout + // information has been attached to the declaration. + auto modifier = decl->FindModifier<ComputedLayoutModifier>(); + if (!modifier) return nullptr; + + auto computedLayout = modifier->layout; + assert(computedLayout); + + auto varLayout = computedLayout.As<VarLayout>(); + return varLayout; +} + static void emitHLSLParameterBlockDecl( EmitContext* context, RefPtr<VarDeclBase> varDecl, @@ -2292,6 +2372,7 @@ static void emitHLSLParameterBlockDecl( assert(declRefType); // We expect to always have layout information + layout = maybeFetchLayout(varDecl, layout); assert(layout); // We expect the layout to be for a structured type... @@ -2325,13 +2406,16 @@ static void emitHLSLParameterBlockDecl( Emit(context, "\n{\n"); if (auto structRef = declRefType->declRef.As<StructSyntaxNode>()) { + int fieldCounter = 0; + for (auto field : getMembersOfType<StructField>(structRef)) { + int fieldIndex = fieldCounter++; + EmitVarDeclCommon(context, field); - RefPtr<VarLayout> fieldLayout; - structTypeLayout->mapVarToLayout.TryGetValue(field.getDecl(), fieldLayout); - assert(fieldLayout); + RefPtr<VarLayout> fieldLayout = structTypeLayout->fields[fieldIndex]; + assert(fieldLayout->varDecl.GetName() == field.GetName()); // Emit explicit layout annotations for every field for( auto rr : fieldLayout->resourceInfos ) @@ -2415,7 +2499,7 @@ emitGLSLLayoutQualifiers( { if(!layout) return; - switch( context->target ) + switch( context->shared->target ) { default: return; @@ -2493,7 +2577,7 @@ static void emitGLSLParameterBlockDecl( { RefPtr<VarLayout> fieldLayout; structTypeLayout->mapVarToLayout.TryGetValue(field.getDecl(), fieldLayout); - assert(fieldLayout); +// assert(fieldLayout); // TODO(tfoley): We may want to emit *some* of these, // some of the time... @@ -2521,7 +2605,7 @@ static void emitParameterBlockDecl( RefPtr<ParameterBlockType> parameterBlockType, RefPtr<VarLayout> layout) { - switch(context->target) + switch(context->shared->target) { case CodeGenTarget::HLSL: emitHLSLParameterBlockDecl(context, varDecl, parameterBlockType, layout); @@ -2539,6 +2623,8 @@ static void emitParameterBlockDecl( static void EmitVarDecl(EmitContext* context, RefPtr<VarDeclBase> decl, RefPtr<VarLayout> layout) { + layout = maybeFetchLayout(decl, layout); + // As a special case, a variable using a parameter block type // will be translated into a declaration using the more primitive // language syntax. @@ -2606,7 +2692,7 @@ static void emitGLSLPreprocessorDirectives( EmitContext* context, RefPtr<ProgramSyntaxNode> program) { - switch(context->target) + switch(context->shared->target) { // Don't emit this stuff unless we are targetting GLSL default: @@ -2658,78 +2744,6 @@ static void emitGLSLPreprocessorDirectives( // TODO: handle other cases... } -static void EmitProgram( - EmitContext* context, - RefPtr<ProgramSyntaxNode> program, - RefPtr<ProgramLayout> programLayout) -{ - // There may be global-scope modifiers that we should emit now - emitGLSLPreprocessorDirectives(context, program); - - switch(context->target) - { - case CodeGenTarget::GLSL: - { - // TODO(tfoley): Need a plan for how to enable/disable these as needed... -// Emit(context, "#extension GL_GOOGLE_cpp_style_line_directive : require\n"); - } - break; - - default: - break; - } - - - // Layout information for the global scope is either an ordinary - // `struct` in the common case, or a constant buffer in the case - // where there were global-scope uniforms. - auto globalScopeLayout = programLayout->globalScopeLayout; - if( auto globalStructLayout = globalScopeLayout.As<StructTypeLayout>() ) - { - context->globalStructLayout = globalStructLayout.Ptr(); - - // The `struct` case is easy enough to handle: we just - // emit all the declarations directly, using their layout - // information as a guideline. - EmitDeclsInContainerUsingLayout(context, program, globalStructLayout); - } - else if(auto globalConstantBufferLayout = globalScopeLayout.As<ParameterBlockTypeLayout>()) - { - // TODO: the `cbuffer` case really needs to be emitted very - // carefully, but that is beyond the scope of what a simple rewriter - // can easily do (without semantic analysis, etc.). - // - // The crux of the problem is that we need to collect all the - // global-scope uniforms (but not declarations that don't involve - // uniform storage...) and put them in a single `cbuffer` declaration, - // so that we can give it an explicit location. The fields in that - // declaration might use various type declarations, so we'd really - // need to emit all the type declarations first, and that involves - // some large scale reorderings. - // - // For now we will punt and just emit the declarations normally, - // and hope that the global-scope block (`$Globals`) gets auto-assigned - // the same location that we manually asigned it. - - auto elementTypeLayout = globalConstantBufferLayout->elementTypeLayout; - auto elementTypeStructLayout = elementTypeLayout.As<StructTypeLayout>(); - - // We expect all constant buffers to contain `struct` types for now - assert(elementTypeStructLayout); - - context->globalStructLayout = elementTypeStructLayout.Ptr(); - - EmitDeclsInContainerUsingLayout( - context, - program, - elementTypeStructLayout); - } - else - { - assert(!"unexpected"); - } -} - static void EmitDeclImpl(EmitContext* context, RefPtr<Decl> decl, RefPtr<VarLayout> layout) { // Don't emit code for declarations that came from the stdlib. @@ -2783,18 +2797,18 @@ static void EmitDeclImpl(EmitContext* context, RefPtr<Decl> decl, RefPtr<VarLayo // 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->modulesAlreadyEmitted.Contains(moduleDecl)) + 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->modulesAlreadyEmitted.Add(moduleDecl); + context->shared->modulesAlreadyEmitted.Add(moduleDecl); // TODO: do we need to modify the code generation environment at // all when doing this recursive emit? - EmitDeclsInContainerUsingLayout(context, moduleDecl, context->globalStructLayout); + EmitDeclsInContainerUsingLayout(context, moduleDecl, context->shared->globalStructLayout); } return; @@ -2839,7 +2853,7 @@ static void registerReservedWord( EmitContext* context, String const& name) { - context->reservedWords.Add(name, name); + context->shared->reservedWords.Add(name, name); } static void registerReservedWords( @@ -2847,7 +2861,7 @@ static void registerReservedWords( { #define WORD(NAME) registerReservedWord(context, #NAME) - switch (context->target) + switch (context->shared->target) { case CodeGenTarget::GLSL: WORD(attribute); @@ -2990,68 +3004,115 @@ static void registerReservedWords( } } -String emitProgram( - ProgramSyntaxNode* program, +bool isRewriteRequest( + SourceLanguage sourceLanguage, + CodeGenTarget target); + +String emitEntryPoint( + EntryPointRequest* entryPoint, ProgramLayout* programLayout, CodeGenTarget target) { - // TODO(tfoley): only emit symbols on-demand, as needed by a particular entry point + auto translationUnit = entryPoint->getTranslationUnit(); + + SharedEmitContext sharedContext; + sharedContext.target = target; + + sharedContext.programLayout = programLayout; + + // Layout information for the global scope is either an ordinary + // `struct` in the common case, or a constant buffer in the case + // where there were global-scope uniforms. + auto globalScopeLayout = programLayout->globalScopeLayout; + StructTypeLayout* globalStructLayout = nullptr; + if( auto globalStructLayout = globalScopeLayout.As<StructTypeLayout>() ) + { + globalStructLayout = globalStructLayout.Ptr(); + } + else if(auto globalConstantBufferLayout = globalScopeLayout.As<ParameterBlockTypeLayout>()) + { + // TODO: the `cbuffer` case really needs to be emitted very + // carefully, but that is beyond the scope of what a simple rewriter + // can easily do (without semantic analysis, etc.). + // + // The crux of the problem is that we need to collect all the + // global-scope uniforms (but not declarations that don't involve + // uniform storage...) and put them in a single `cbuffer` declaration, + // so that we can give it an explicit location. The fields in that + // declaration might use various type declarations, so we'd really + // need to emit all the type declarations first, and that involves + // some large scale reorderings. + // + // For now we will punt and just emit the declarations normally, + // and hope that the global-scope block (`$Globals`) gets auto-assigned + // the same location that we manually asigned it. + + auto elementTypeLayout = globalConstantBufferLayout->elementTypeLayout; + auto elementTypeStructLayout = elementTypeLayout.As<StructTypeLayout>(); + + // We expect all constant buffers to contain `struct` types for now + assert(elementTypeStructLayout); + + globalStructLayout = elementTypeStructLayout.Ptr(); + } + else + { + assert(!"unexpected"); + } + sharedContext.globalStructLayout = globalStructLayout; EmitContext context; - context.target = target; + context.shared = &sharedContext; + context.isRewrite = isRewriteRequest( + translationUnit->sourceLanguage, + target); + // TODO: this should only need to take the shared context registerReservedWords(&context); - EmitProgram(&context, program, programLayout); + auto translationUnitSyntax = translationUnit->SyntaxNode.Ptr(); - String code = context.sb.ProduceString(); - return code; - -#if 0 - // HACK(tfoley): Invoke the D3D HLSL compiler on the result, to validate it + // There may be global-scope modifiers that we should emit now + emitGLSLPreprocessorDirectives(&context, translationUnitSyntax); -#ifdef _WIN32 + switch(target) { - HMODULE d3dCompiler = LoadLibraryA("d3dcompiler_47"); - assert(d3dCompiler); - - pD3DCompile D3DCompile_ = (pD3DCompile)GetProcAddress(d3dCompiler, "D3DCompile"); - assert(D3DCompile_); - - ID3DBlob* codeBlob; - ID3DBlob* diagnosticsBlob; - HRESULT hr = D3DCompile_( - code.begin(), - code.Length(), - "slang", - nullptr, - nullptr, - "main", - "ps_5_0", - 0, - 0, - &codeBlob, - &diagnosticsBlob); - if (codeBlob) codeBlob->Release(); - if (diagnosticsBlob) - { - String diagnostics = (char const*) diagnosticsBlob->GetBufferPointer(); - fprintf(stderr, "%s", diagnostics.begin()); - OutputDebugStringA(diagnostics.begin()); - diagnosticsBlob->Release(); - } - if (FAILED(hr)) + case CodeGenTarget::GLSL: { - int f = 9; + // TODO(tfoley): Need a plan for how to enable/disable these as needed... +// Emit(context, "#extension GL_GOOGLE_cpp_style_line_directive : require\n"); } + break; + + default: + break; } - #include <d3dcompiler.h> -#endif + auto lowered = lowerEntryPoint(entryPoint, programLayout, target); + + EmitDeclsInContainer(&context, lowered.program.Ptr()); + +#if 0 + if( isRewrite ) + { + // In rewrite mode, we will just emit the text of the translation unit as given, + // and not pay attention to the specific entry point that was requested. + // + // It is a user error to request GLSL output and have an entry point name + // other than `main`. + EmitDeclsInContainerUsingLayout(&context, translationUnitSyntax, globalStructLayout); + } + else + { + // We are being asked to emit a single entry point in "full" mode. + emitEntryPoint(&context, entryPoint); + } #endif -} + String code = sharedContext.sb.ProduceString(); + return code; +} } // namespace Slang |
