summaryrefslogtreecommitdiff
path: root/source/slang/lower.cpp
AgeCommit message (Collapse)Author
2017-07-23Fixup for the glslang bug workaroundTim Foley
There was a bug where the intialization expression for a variable was being lowered after the declaration was added to the output code, so that any sub-expressions that get hoisted out actually get computed *after* the original variable. This obviously led to downstream compilation failure. I've updated the test case to stress this scenario.
2017-07-23Work around glslang issue 988Tim Foley
The basic bug there is that if you have a member of `struct` type in a `uniform` block and then pass a reference to that member directly to a call: ``` struct Foo { vec4 bar; }; uniform U { Foo foo; }; void main() { doSomething(foo); } ``` then glslang generates invalid SPIR-V which seems to cause an issue for some drivers. This change works around the problem by detecting cases where an argument to a function call is a reference to `uniform` block member (of `struct` type) and then rewrites the code to move that value to a temporary before the call.
2017-07-21Don't add `flat` qualifier to integer fragment outputTim Foley
Fixes #133 We already had logic to skip adding `flat` to a vertex input, and this just extends it to not adding `flat` to a fragment output. Note that explicit qualifiers in the input HLSL/Slang will still be carried through to the output, so it is still possible for a Slang user to shoot themself in the foot with interpolation qualifiers.
2017-07-20Require extension when using `gl_Layer` in VSTim Foley
The requirements for using `gl_Layer` differ by stage, and so we need to pick an appropriate GL version based on the target stage, and then also require a specific extension for anything other than geometry or fragment.
2017-07-20Translate NV single-pass stereo extension from Slang to GLSLTim Foley
- The easy part here is treating `NV_` prefixed semantics as another case of "system-value" semantics - Mapping the new semantics (`NV_X_RIGHT` and `NV_VIEWPORT_MASK`) to their GLSL equivalents is harder - Instead of a single "right-eye vertex" output, GLSL defines an array of per-view positions - Instead of a vector of masks, GLSL defines an array of per-view masks - Another point here is that a lot of semantics that appear as `uint` in HLSL are `int` in GLSL, which can lead to conversion issues. - The approach here is to have the lowering pass introduce a notion of assignment with "fixups," which will try to cast things as needed - When assigning to a simple value with the "wrong" type, introduce a cast - When assigning to an array from a vector, break out multiple assignments of individual vector/array elements - In order to facilitate the above, I needed to add actual types to the magic expressions I introduce to represent GLSL builtin variables. These were taken by scanning the online documentation for GL, so they might not be perfect. - Major issues with the approach in this change: - No attempt is being made here to check that the original declaration used a type appropriate to the semantic. The assumption is that this logic only ever triggers for Slang entry points, or GLSL entry points using a Slang `struct` type for input/output (and for right now Slang code is only ever written by "understanding" developers) - In the case of a Slang entry point, we always copy varying parameters in/out around the call to `main_`, so this approach should handle calls to functions with `out` or `in out` parameters okay, but it is *not* robust to cases where we don't want to copy in all the entry point parameters first thing (e.g., a GS), so that will have to change - In the GLSL case (or if we revise the approach to Slang entry points), there is going to be a problem if these converted varying parameters are ever passed as arguments to `out` or `in out` parameters. In these cases we need to do more sleight-of-hand to reify a temporary variable and do the necessary copy-in/copy-out. Being able to do that logic relies on having correct information about callees, which requires having robust semantic analysis of the function body. There is only so much we can do... - A better long-term approach would not rely on an ad-hoc "fixup" conversion during assignment, but would instead implement the GLSL builtin variables as, effectively, global "property" declarations that have both `get` and `set` accessors, and then tunnel a reference to such a property down through lowering, where it can lower to uses of the "getter" or "setter" as appropriate in context (and the result type of the getter/setter can be what we'd want/expect).
2017-07-19Try to improve handling of failures during compilationTim Foley
The change is mostly about trying to make sure the compiler "fails safe" when it encounters an internal assumption that isn't met. Most internal errors will now throw exceptions (yes, exceptions are evil, but this will work for now), and these get caught in `spCompile` so that they don't propagate to the user (they just see a message that compilation aborted due to an internal error). Subsequent changes are going to need to work on diagnosing as many of these situations as possible, so that users can at least know what construct in their code was unexpected or unhandled by the compiler.
2017-07-19Fix up translation of `GetDimensions()`Tim Foley
Fixes #122 - In cases with an explicit mip level being specified, there was a mistake in how the argument for setting the mip level in the GLSL code was constructed that led to a parse error in GLSL - Also, that argument is a `uint` in HLSL and an `int` in GLSL, so an explicit cast was needed - The GLSL functions here seem to require a newer GLSL (at least higher than `420`), so I had to add in a capability for builtins to specify a required GLSL version. For now I made these ones require `450`. - Added a test case to confirm that our lowering works (for some definition of "works")
2017-07-18Add a compile-time loop construct to SlangTim Foley
The basic syntax is: $for(i in Range(0,99)) { /* stuff goes here */ } Note that the exact form is very restrictive. All that you are allowed to change is `i`, `0`, `99` or `/* stuff goes here */`. As a tiny bit of syntax sugar, the following should work: $for(i in Range(99)) { /* stuff goes here */ } Note that the range given is half-open (C++ iterator `[begin,end)` style). Both the beginning and end of the range must be compile-time constant expressions that Slang knows how to constant-fold. The implementation will basically generate code for `/* stuff goes here */` N times, once for each value in the half-open range. Each time, the variable `i` will be replaced with a different compile-time-constant expression. While I was working on a test case for this, I also found that our build of glslang had an issue with resource limits, so I fixed that. Clients will need to build a new glslang to use the fix.
2017-07-18Support scalarization of varying input/output for GLSLTim Foley
GLSL technically supports varying (`in`, `out`) parameters of `struct` type, but there are some annoying constraints (not allowed for VS input), and it doesn't work with how an HLSL user would usually put "system-value" inputs/outputs into a `struct` together with ordinary inputs/outputs. To work around this, this change adds support for using an imported Slang `struct` type for an `in` or `out` parameter, in which case it will (1) be scalarized and (2) will have system-value semantics mapped appropriately, just as for an entry-point parameter when cross-compiling an HLSL-style `main()`. Changes: - Add a notion of a `VaryingTupleExpr` and `VaryingTupleVarDecl`, similar to those for the resources-in-structs case - Trigger use of these when we have a global-scope varying in/out using an imported `struct` type - Also use these in the cross-compilation case for ordinary varying input/output (since this approach seems like it should be more general, and can hopefully handle stuff like GS input/output some day) - When generating parameter binding information, special case global-scope input/output, and treat it the same as entry-point-parameter input/output - Revamp how used resource ranges are computed so that we can eventually make this specific to an entry point - Actually implement first signs of life for `maybeMoveTemp` so that assignments to the tuple-ified outputs will work better - Add first test case that actually seems to work - Add diagnostics for conflicting explicit bindings on a parameter - Add diagnostic for different parameters with overlapping bindings - Make global-scope varying input/output use a tracking data structure specific to the translation unit for computing locations (so that they are independent of other TUs)
2017-07-17Handle arrays when scalarizing "resources in structs"Tim Foley
The basic idea is that an array of `struct`s will get scalarized into per-field arrays (for any fields that need to be scalarized). So given: struct Foo { float x; Texture2D t; }; cbuffer C { Foo foo[4]; } We'll get output like: struct Foo { float x; }; cbuffer C { Foo foo[4]; } Texture2D C_foo_t[4]; (Of course the output would also be translated over to GLSL, but I'm only concerned about this one transformation here).
2017-07-17Pick correct GLSL version when `gl_Layer` usedTim Foley
`gl_Layer` as a fragment input requires at least version 4.30 of GLSL, so we try to track that information when we see the name used. Note that this does *not* override a user-specified `#version` line. This required re-ordering when lowering happens relative to emitting the `#version` directive, since this code works by actually modifying the chosen profile for the entry point. Yes, that is kind of gross and we should do something cleaner in the long term.
2017-07-17Handle `flat` interpolation cases in cross compilationTim Foley
Fixes #104 - Map HLSL `nointerpolation` to GLSL `flat` - When lowering a `struct` type varying input/output, look for interpolation modifiers along the "chain" from the leaf field up to the original shader input variable (and take the first one found) - Not sure if this is strictly needed, but it seems like a reasonable policy - Add `flat` to varying input of integer type, with no other interpolation modifier - Note: I do *not* do anything to ignore a manually imposed interpolation modifier that might be incorrect
2017-07-14Adjust type layout when parameter block constains member using the same resourceTim Foley
If we have something like to following in HLSL: cbuffer C { Texture2D t; ... } and we are compiling to GLSL, then both `C` and `C.t` consume the same kind of resource (a descriptor-table slot). The way reflection was working right now, querying the index of `C` would return its binding (let's say it is `4` just to be concrete) and then a query on `C::t` would give its offset, which was being computed as `0` because it is the first field in the logical `struct` type. That obviously leads to bad math and requires some subtle `+1`s in cases to get things right (e.g., when scalaring during lowering, I had to carefully add one in some cases). It is unreasonable to expect users to deal with this. This commit changes it so that the offset of field `C::t` is `1` so that hopefully more things Just Work. The special-case logic in lowering is now gone. One important catch here is that this pretty much only works in the case where the element type of a parameter block is a `struct` type (which is really all that makes sense right now). If we ever want to generalize this in the future, then it will probably be necessary to change the `TypeLayout` case for parameter blocks to store a `VarLayout` for the element, rather than just a `TypeLayout`.
2017-07-12Add tuple lowering logic for assignmentTim Foley
- When assigning tuples `(a0, ...) = (b0, ...)` generate a tuple of assignments `(a0 = b0, ...)` - Given an expression statement on a tuple `(a0, ...);` generate a sequence of statements `a0; ...`
2017-07-12Add basic reflection query for checking if entry point is "sample-rate"Tim Foley
- This really just checks two basic things: 1. Was there any global variable declared with `in` and `sample`? 2. Did any code encountered during lowering referenece `gl_SampleIndex`? - This doesn't cover what HLSL could need, nor what we would need for cross-compilation. Consider it GLSL-specific for now. - In order to generate the information with even a reasonable chance of being accurate (not giving a ton of false positives) I tried to integrate the checks into the lowering process (so they only see code that is referenced, one hopes). - For this to work with my testing setup, I needed to make sure that lowering is always performed, prior to emitting reflection info - This change broke several reflection tests, because they had been using code that wouldn't actually pass the downstream compiler. I checked in fixes for those.
2017-07-11Bug fixes for resources-in-structs.Tim Foley
2017-07-11Bug fix for lowering of tuple types without layout.Tim Foley
- Don't try to extract the body layout for a field without a layout
2017-07-11Initial work on handling resources in structs during cross-compilationTim Foley
- The basic idea is that during the "lowering" pass, some types (notably: aggregate types that contain resource variables) will get turned into "tuple" types, which are pseduo-types that aren't meant to survive lowering. - An attempt to declare a variable with a tuple type expands into a tuple of declarations - An attempt to reference such a tuple-ified variable leads to a tuple of expressions - An attempt to extract a member from such a tuple expression will pick the appropriate sub-element - Dereference a tuple by dereferencing the primary expression - Expand a tuple in the argument list to a call into N arguments (by recursively flattening the tuple) - Don't create tuple types when not generating GLSL - Make sure to preserve the specialized type of a call expression through lowering, since emission of unchecked calls relies on that info. - TODO: maybe the infix/prefix/postifx/select information should come in as a side-band? Should we have modifiers on expressions? - Make sure to offset the layout for a nested field based on teh base offset of its parent variable, when generating declarations for nested fields
2017-07-11Add GLSL translations for many `SV_*` semanticsTim Foley
I haven't tried to be 100% exhaustie, but this should cover the main cases we are likely to encounter in library code.
2017-07-11Don't emitting an imported declaration unless it is used.Tim Foley
This helps avoid the problem where we emit a function that does a `discard` and thus get a GLSL compilation failure in a vertex shader (that doesn't even call the function).
2017-07-10Try to be more robust against un-checked types during lowering, etc.Tim Foley
- Try to handle `ErrorType` gracefully when computing type layouts - When outputting a `TypeExp`, if the type part is errorneous (or missing), try to use the expression part - Make sure to lower the expressions side of a `TypeExp` during lowering
2017-07-10Start handling system-value semantics during loweringTim Foley
I hadn't been lowering `SV_Position` outputs to `gl_Position`, and had somehow been relying on hidden driver behavior that I guess made things Just Work. This change adds some infrastructure to handle `SV_` semantics during lowering of an entry point (currently only covering `SV_Position` and `SV_Target`, FWIW). As a byproduct, this also means that a `VarLayout` stores semantic info, which could conceivably be exposed through reflection data now.
2017-07-10More cross-compilation fixesTim Foley
- Add GLSL mappings for more `Texture*` methods - The annoying one here is `Texture*.Load()` because it doesn't take a sampler, but the GLSL equivalent needs one (while the SPIR-V does *not*). I've hacked this pretty seriously for now. - Try to ensure that we add `uniform` to global declarations that need it in GLSL - When outputting an `in` or `out` variable that might have been created from an `inout` shader parameter, filter the layout qualifiers that we output to only cover the appropriate resource kind.
2017-07-09Ensure that lowered globals for `inout` shader parameters have unique namesTim Foley
If the user had a shader entry point with an `inout` parameter, we would end up lowering it to two GLSL global variables with the same name. This change adds a `SLANG_in_` or `SLANG_out_` prefix to the two declarations. Note: I haven't dealt with the issue that we end up printing two different `layout` qualifiers on such a variable...
2017-07-09Fix up scoping for cross-compiled `main()` bodyTim Foley
The earier changes to add sequence statements and change how the `isBuildingStmt` logic in lowering works doesn't work for this logic, which assumes it can just set `isBuildingStmt` and be sure that decls will go into the right place.
2017-07-09Pick layout rules based on target languge, not source.Tim Foley
The tricky bit here was that the `reflection-json` output format isn't really a code generation target like the others, and we need to be able to have multiple "targets" active to make sense of it. This needs cleaning-up.
2017-07-08Move renaming logic to lowering pass.Tim Foley
Code in Slang that is cross-compiled *might* introduce declarations that collide with language keywords that are reserved in the target. This was previously being dealth with during final code emission, but the challenge there is that we want to allow user code that is being "rewritten" to use whatever identifiers it wants (they know better than us what is an error), and only apply renaming to our own code. The approach here is to apply renaming during lowering - we validate each declaration to make sure its name is valid. Any expressions/types that refer to those declarations will automatically get emitted with the new name (while unchecked expressions will continue to be emitted with their existing name). This isn't quite perfect, since we could in theory still rename a declaration in user code. A more robust version down the line would try to determine if a declaration was nested inside code for the "rewriter." Also note that this does *not* deal with any issues of name conflicts that might arise between modules. That would require a more complete and robust renaming pass, which seems tricky for me to pull off.
2017-07-08Add back `UnparsedStmt`Tim Foley
If the user doesn't use any `import` declarations, there is no reason to parse their code at all, so having the option of falling back to `UnparsedStmt` can potentially save us some headaches down the road. The new rule now is that if you have the "no checking" flag on, *and* the parser hasn't yet seen any `import` declarations, then it still used `UnparsedStmt` to avoid touching function bodies. Otherwise, I go ahead and parse function bodies, and assume I can rewrite any code I can semantically understand.
2017-07-08Differentiate HLSL `for` loops in ASTTim Foley
HLSL has the bad scoping behavior for `for` loops, and we need to respect that. But, we need to have correct scoping for GLSL, and we'd like it for Slang. We also need to ensure that `for` loops written in a "correct" language get the correct behavior when emitted as HLSL. There was already code to handle this in the emit pass, but it was unfortunately using an `isRewrite` flag to try to tell if the HLSL behavior was wanted. This doesn't work when the code being emitted might come from a mix of languages. This change adds a distinct `UnscopedForStmt` syntax node type, and uses that when parsing HLSL input (bot not for other languages). We make sure to preserve this node type through lowering, and then specialize our emit logic on this case. With this, there are no more remaining uses of `isRewrite` in the emit logic, which is good because it didn't mean what I needed it to mean any more (since we now emit only a single module, that was merged during lowering).
2017-07-08Fully parse function bodies, even in "rewriter" modeTim Foley
This is in anticipation of needing to have more complete knowledge to be able to handle user code that `import`s library functionality. The big picture of this change is just to remove the `UnparsedStmt` class that was used to hold the bodies of user functions as opaque token streams, and thus to let the full parser and compiler loose on that code. That is the easy part, of course, and the hard part is all the fixes that this requires in the rest of the compielr to make this even remotely work. Subsequent commit address a lot of other issues, so this particular commit mostly represents work-in-progress. One detail is that this change puts a conditional around nearly every diagnostic message in `check.cpp` to suppress thing when in rewriter mode. I have yet to check how that works out if there are errors in anything we actually need to understand for the purposes of generating reflection data.
2017-07-07Eliminate `typedef`s when lowering to GLSLTim Foley
GLSL doesn't support `typedef` declarations. The lowering code already lowered any named types (references to `typedef`s) to their underlying definition when targetting GLSL. This changes makes sure that we also don't generate any lowered output for `typedef` declarations in the source program.
2017-07-07Fix up visitor approach.Tim Foley
The existing code used a catch-all `visit()` method, and then relied on overloading to find the right version (allowing fallback to a `visit()` method taking a base-class parameter). This approach works, but has some big down-sides: - When browsing the code, you have a bunch of identically-named methods, and it can be hard to find the one you want. - It is impossible to use inheritance to implement fallback for `visit()` methods, because *any* method in the derived class with that name hides *all* methods with the same name in a base class This change makes the `visit()` methods use the name of the corresponding syntax class, and then has visitors inherit the fallback methods they need from the base visitor template class.
2017-07-06More exhaustive loweringTim Foley
- Handle all statement cases explicitly (rather than falling back to the "structural recursion" mess) - Handle back-references from child statements to their parents
2017-07-06Add missing declaration types to lowering pass.Tim Foley
Most of these are cases we don't expect to encounter, but the big missing one was `TypeDefDecl`.
2017-07-06Fix many warnings-as-errors issues.Tim Foley
The code should now compile cleanly with warnings as errors for VS2015 with `W3`. Most of the changes had to do with propagating a real pointer-sized integer type through code that had been using `int`.
2017-07-06Start to support cross-compilation via "lowering" passTim Foley
- 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.