summaryrefslogtreecommitdiff
path: root/source/slang/emit.cpp
AgeCommit message (Collapse)Author
2017-07-10Handle function name properly for unchecked callTim Foley
The emit logic was checking for a missing decl, and then asking that same (missing) declaration for its name.
2017-07-10Fix emission of `static` for HLSLTim Foley
I forgot to include a trailing space.
2017-07-10Add support for `imageBuffer`Tim Foley
This was mostly just a missing `typedef` in the Slang standard library code. This should also cover `textureBuffer` and `samplerBuffer`.
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-10Cleanups for test cases:Tim Foley
- Allow a code-generation target of `NONE` in order to suppress ordinary output in test cases where we don't care about the actual output (just pass/fail result) - Add explicit `location` layout qualifiers to intermediate vertex-to-fragment variables in GLSL test cases for rendering, to work around apparent Intel driver bugs.
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-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-08Revise how hidden implicit casts are recognized.Tim Foley
The old approach used an `isRewriter` flag in the emit logic, but I kind of need that flag to go away. Instead, I now how the semantic checking pass detect whether an implicitly-generated type cast is in rewriter code, and if so it uses the new `HiddenImplicitCastExpr` AST node. The emit logic then looks for that specific node and eliminates it.
2017-07-08Try to emit expressions with minimal parenthesesTim Foley
When emitting code it is easy to be overly defensive and emit tons of extra parentheses. In some cases these are just annoying, and make the output more cluttered than it needs to be. In other cases, though, being over-aggressive here can actually break things when a downstream compiler has more stringent requirements (e.g., doesn't allow a general expression for the function in a call expression, but only a particular set of atomic/postfix expressions). This wasn't as bad when we weren't parsing function bodies in user code, but now that we *are* it becomes important to not emit bad parentheses and screw up their code. At the same time, though, we don't want to fail to output them and silently break code. A nice property today is that we preserve parentheses in the input code, so hopefully we don't ever break operator precedence. The code already had an approach to avoid *some* parens, by tracking an "outer precedence" and only emitting parens for an expression if they were needed relative to this outer precedence. That approach doesn't handle associativity, and so it doesn't work for things like chains of postfix operators. The new approach basically tracks *two* outer precedence levels: one on the left, and one on the right. When recursing into a sub-expression of an op (e.g., the `A` in `A + B`) on of the precdence levels for the recursive call will come from the outer environment, and the other from the operation itself (e.g., `A` has `(X, +)` as its left/right precdence, where `X` is whatever was to the left of `A + B`, while `B` gets `(+,Y)`). One more piece of the puzzle is that an operator like `+` actually exposes *two* precedence levels: one for the left-hand side and one for the right-hand, so that if both `A` and `B` are themselves uses of `+`, `A` won't get parens, but `B` will. Finally, when we have an un-checked application of an operator (which our AST stores as something like a function-call node), we do a little lookup step to find the corresponding operator and its precedence (while for things that actually got resolved we *know* the precedence.
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-07Map HLSL `lerp` to GLSL `mix`Tim Foley
- Also fix a bug in `emit.cpp` where I wasn't detecting `__intrinsic` remaps that don't include `$` (this is a byproduct of changing the string index type to be unsigned; other bugs like this may be lurking)
2017-07-07Don't emit `[unroll]` or `[loop]` to GLSLTim Foley
- I really ought to make these semantically understood attributes, which might make this logic easier - Long-term if we start emitting SPIR-V directly, we should translte these, of course
2017-07-07Add GLSL equivalents for some stdlib operations.Tim Foley
- Map HLSL `atan2(y,x)` to GLSL `atan(y,x)` - Add support for `$p` escape in `__intrinsic` modifier, which allows generating GLSL texture-sampler pairs more easily - Also added a `$o` escape to represent the base object in an OOP-style call (`obj.F(...)`) - This isn't used in the texture functions right now, because getting the right GLSL texture type in this context is a bit thorny (the prefix depends on the generic parameter being used) - Used the `$p` capability to add mappings for `SampleBias` and `SampleLevel`
2017-07-07Overhaul emit logic to use visitor abstractionTim Foley
- This is in preparation for splitting out HLSL vs. GLSL emit as different cases. - Along the way, I added more cases to the visitor implementation, to handle visitors with arguments - This is getting a bit busy, though, and we might be reaching the breaking point where a more general bit of meta-magic is needed to clean things up (either going further down the ugly template route, or plugging in a more real code generation strategy)
2017-07-07Don't emit the `static` keyword when generating GLSLTim Foley
GLSL doesn't support `static` at all, while HLSL uses it for multiple things: - To mark global variables that are "thread local" rather than shader parameters - In the C/C++ style to mark `static` allocation for variables inside a function or type The latter case needs to be handled during lowering (but isn't handled right now). The former case can be solved just by dropping the `static` keyword.
2017-07-07Fix signed/unsigned comparison warning.Tim Foley
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-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.
2017-06-28Store integer literals at high precision in ASTTim Foley
The lexer was creating an `unsigned long long` value, and then the AST was storing it in an `int`. This change makes both use a `long long`. This is obviously still a stopgap until I can get arbitrary precisions in here.
2017-06-28Actually respect suffixes on numeric literals.Tim Foley
- Add logic to extract the value and suffix from a numeric literal - This duplicates some of the lexing logic, but this is hard to avoid without redundant runtime work - Note that I'm not using and stdlib string-to-number code. This should be more robust once it is working, but it is obviously error prone in the near term. The main up-sides to this are: - We can handle binary integer literals - We can handle hexadecimal floating-point literals without stdlib support - We can hypothetically support digit separators, if we ever wanted - The parser looks at the suffix characters sliced off by the lexer, and tries to pick a type to use for a literal - It uses `NULL` if there is no suffix, to avoid some nasty order dependencies where the stdlib might need to parse a number before it has seen the definition of `int` - Right now I only handle a few cases, so there may be bugs lurking here - The emit logic needs to handle the fact that a literal node in the AST might have a non-default type attached. - Right now I just quickly check for the most likely types, and emit the literal with a matching suffix. This doesn't seem robust if any source language supports a suffix for a type where a target has no corresponding suffix. In the long term some amount of casting is probably required.
2017-06-28Rename literal tokens.Tim Foley
These had a typo (`Literial`), so they needed a fix eventually. I also went ahead and made things a bit more verbose (`IntegerLiteral`, `FloatingPointLiteral`) because these names don't get used often enough for the brevity to pay off.
2017-06-27Emit global-scope parameters from imported files.Tim Foley
This fixes up a bug in the earlier change that provided reflection for imported parameters; I'd failed to confirm that the code generation logic can handle imported parameters correctly. The main fix was to have an `import` declaration automatically use the global-scope layout already determined, sine imported parameters will in general appear there.
2017-06-26Fix parsing of string literals.Tim Foley
String literals can be used as part of attributes, but we lacked an actual AST representation for them. This change adds basic parsing for string literals, as well as emit logic for them. I also included a fix for parsing of chained right-associative operators. To test these fixes, I've re-enabled one of the HLSL tests I disabled a while back. It would be good to go through and see how many of those we can re-enable now.
2017-06-21Emit: Add support for `while` and `do {} while` statementsTim Foley
These were being passed over by the emit logic because I didn't have tests that used them.
2017-06-20Only emit each `import`ed module once.Tim Foley
If the user imports a module along more than one path, we need to make sure we don't emit the code twice. I handle this by keeping a set of already-emitted modules. Down the line, a more robust code generation strategy for non-"rewriter" use cases would be handling this at the per-declaration level, and this logic wouldn't really be needed.
2017-06-19Emit `#line` directives more aggressivelyTim Foley
We now output a line directive for (nearly) every declaration, statement, and modifier, so that hopefully there will be fewer cases where a downstream error doesn't point to the correct line. This exposes a lot of issues where we can/should clean up the simplicity of the code we emit (e.g., not output redundant parens; tracking source locations for types better). These kinds of issues will need to be addressed in follow-on changes. A few big ones: - Because GLSL doesn't allow for file names in `#line` directives, we really need to expose some data that can clean up error messages (or can be used by an application to do the same) so that they know which file is which. - We really need a command line option (and an equivalent API flag) to turn off emission of `#line` directies, so that the user can get moderately clean code as output.
2017-06-19Don't emit redundant `#line` directivesTim Foley
If the line number for the next token is within a small range, then go ahead and output newlines to get caught up, rather than emit a `#line` directive. This saves a small amount of clutter, and in the particular case where the number of lines is 1, it stops our current behavior of putting a directive on each line.
2017-06-16Bug fix: handle unchecked operator application in emit logicTim Foley
When in rewriter mode, the emit logic will never see function applications inside function bodies, but it *will* see function application expressions at global scope, and some of these expressions might be unchecked. The challenge here is that even simple math operations now show up as function calls, so we need a bit of special-case logic to detect unchecked calls and then emit them using the syntax they were written with (e.g., use infix syntax if they were written as an infix expression).
2017-06-15Replace `DeclRef` approachTim Foley
For context: a `DeclRef` is supposed to capture both a pointer to a particualr declaration, and also any information needed to specialize that declaration for a context (e.g., generic parameter substitutions). The existing approach had a hiearchy of specialized decl-ref types that mirrored the AST hierarchy, but that led to a lot of boilerplate where you had to recapitulate the exact same hierarchy. The new appraoch basically treats `DeclRef<T>` as a sort of "smart pointer" in that it wraps a pointer to a `T` (the declaration), plus a side field for the specialization info, and then allows it to be cast as needed to other types (where the pointer cast would be allowed), while carrying along the side info. To enable this, all the things that used to be member functions of declaration-reference types are now free functions that take a `DeclRef<T>` for some specific `T` as a parameter.
2017-06-15Rename `Slang::Compiler` -> `Slang`Tim Foley
This gets rid of one unecessary namespace.
2017-06-13First pass at support for cross-compilationTim Foley
This is a large change that contains many pieces: - Update the `cross-compile0` test to actually make use of cross compilation. Now the `cross-compile0.hlsl` file contains both HLSL and GLSL source code, and then imports code from `cross-compile0.slang`, which provides a "library" (one function) that can be shared between both the HLSL and GLSL version of things. - Fixed a bug in the support for backslash-escaped newlines. - Added a new `__import` declaration type (replaces the `using` directive that was still around in a vestigial form) An `__import` causes the compiler to look for a Slang source file (currently using the ordinary `#include` lookup logic), and then parse/check the found file as an additional module ("translation unit"), before making its declarations visible in the current scope. - Refactored the main compilation flow to be simpler. There were the `ShaderCompiler` and `ShaderCompilerImpl` classes that weren't relaly doing anything, but added complexity to the whole workflow. - The `render-test` application has been heavily modified to better support testing cross-compilation workflows. At the most basic level we are starting to distinguish pass-through vs. rewriter workflows, and are passing various `#define`s down to the compiler(s) to let the source code be customized as needed for each case. Several annoying corner cases are caused here by having to support the GLSL compilation model, which really wants each entry point in its own specific translation unit, whereas we really want to keep things nicely contained in single files. - Added support for `__intrinsic` operations to have target-specific behavior. This allows a function to be given a different name for some specific target (so a call gets emitted as a call to that other operation). More generally, the library writer can put together an arbitrary format string that will be used in place of expressions that call the given function, e.g.: __intrinsic(hlsl, "$1 - $0") __intrinsic int foo(int a, int b); Given this declaration, a call like `foo(x,y)` will code generate as `x - y` for HLSL, and as `foo(x,y)` for all other targets. Annoying things still to be dealt with: - The way that I'm filtering the user-provided options when passing things down to the compilation of dynamically loaded modules is a bit ad hoc. It would be good to have a systematic notion of which options will be inherited and which won't. There is also more code duplication than I'd like, so we risk having the compiler behave differently when compiling a file at the top level, vs. because of `__import`. - Adding target-specific behavior to intrinsics is all well and good, but the current approach means we can only add this to the original declaration, which limits the ability to easily extend the set of targets. A better approach long-term would be to add a more robust notion of target-based overload resolution (which would happen after semantic checking). Then one mechanism would be used to find the right target-specific overload to use for an operation, and then each (target-specific) definition could use a simpler attribute to intercept code-generation behavior. Note that we might eventually need a similar notion to deal with stage- or profile-specific functions and the overloading behavior around them, so using this for intrinsics doesn't seem like a bad idea.
2017-06-09Initial import of code.Tim Foley