summaryrefslogtreecommitdiff
path: root/tests
AgeCommit message (Collapse)Author
2017-10-11Bug fixing (#207)Tim Foley
* Bug fix for vector initializer lists When a vector was initialized with an initializer list: float4 f = { 0, 1, 2, 3 }; we were following the logic for `struct` types (since `vector<T,N>` is technically a `struct` declaration in our stdlib...), but the type has no field, so we were (silently!) ignoring the actual operands. I've applied a simple fix where we cast the operands to the element type of the vector, but a more complete fix will be needed sooner or later where we check the operand counts properly, etc. * Create implicit cast AST nodes when calling initializers The logic for dealing with implicit conversions was recently beefed up so that it would look at `__init` declarations in the target type, but in those cases the front-end would always create an `InvokeExpr` even when we would rather get an `ImplicitCastExpr` or (in the "rewrite" case) a `HiddenImplicitCastExpr`. I've fixed this up for now by constructing a dummy expression to stand in for the "original" call expression when creating the final call (luckily our `TypeCastExpr` is already just a specialized `InvokeExpr`). A better long-term solution might be to have implicit-ness or hidden-ness be modifiers or flags, rather than needing to use specialized forms of call nodes. * Fix subscript operator for `RWTexture1D` The index type was being declared as `uint1` instead of `uint`, and that created problems for downstream HLSL compilation when we introduced expressions like `uav[uint1(index)]` - the compiler would complain that a vector is not a valid index type. * Fix up constant-folding of integer casts. The old logic was checking for `InvokeExpr` before `TypeCastExpr`, but in the new setup a type cast *is* an `InvokeExpr`, so that case was never triggering. All of the constant-folding code really needs to be revisited, though, so that it can use a more general-purpose evaluation scheme like the bytecode (so that we can handle a moral equivalent of `constexpr` in the long run). * Fix implicit conversion costs for vector types A recent change made it so that the logic for looking up implicit conversions now uses declarations of initializers in the standard library (rather than hand-coding all the cases in `check.cpp`). One mistake made there was that we dropped the logic for computing implicit conversions between vectors of the same size, but different element types. These conversions were still allowed by a catch-all (generic) declaration in the standard library, but that declaration didn't include any implicit conversion cost logic (since it was generic, there was no single cost to use). This change explicitly enumerates the required conversions with their costs. It is a bit unfortunate that this is an O(N^2) amount of code for N base types, but that seems unavoidable for now. * Handle "lowering" of overloaded expressions If we are in the `-no-checking` mode and the user calls an overloaded function from an `__import`ed file in a way such that Slang can't resolve the intended overload, we were failing to emit the definitions of the potential callees. This change simply adds a case for `OverloadedExpr` in `lower.cpp` that explicitly lowers all the declarations that might have been referenced. - There is a potentially for breakage here if we are outputting GLSL and one of the overloads is stage-specific. - A more refined approach might try to recognize which over the overloaded options are even potentially applicable, and then output only those, but doing this would be way more complicated. I've added a test case for this behavior, but it is a bit brittle because we need to confirm that we still produce the same error message as unmodified HLSL.
2017-10-09Fix emit logic for `cbuffer` member with initializer (#205)Tim Foley
Given an input declaration like: cbuffer C { int a = -1; } Slang was automatically generating a `packoffset` semantic to place the member manually, but was emitting it *after* the initializer of the original declaration: cbuffer C : register(b) { int a = -1 : packoffset(c0); } That syntax is invalid, of course, and we actually want: cbuffer C : register(b) { int a : packoffset(c0) = -1; } This wasn't spotted earlier because putting initializers on a `cbuffer` member is not commonly done, since it requires reading those values via the reflection API. Slang's reflection API currently provides no way to access default values like this, so they aren't of much use yet. Still, it is better to emit correct syntax even in cases like this one.
2017-10-09Preprocessor: fix `undef` and redefinition (#204)Tim Foley
* Preprocessor: fix `undef` and redefinition The logic for `undef` directives was failing to suppress macro expansion when reading the name to un-define, and so it wasn't actually working at all. We didn't notice this because we didn't have a test case, and users hadn't tried it. The logic for `define` had a similar bug, which meant that any attempt to define an already-defined macro would fail with a cryptic error, rather than raising the intended warning. Test cases have been added for both issues, along with the fixes. * fixup: add expected output for tests added
2017-10-09Parser: fix precedence of cast expressions (#203)Tim Foley
We were accidentally parsing this: (uint) a / b as this: (uint) ( a / b ) when it should be: ((uint) a) / b This is a bug that seems to have been inherited from a long time ago. It has taken a while to bite anybody because the only class of expressions it would hit are multiplicative ones, and in many cases the difference in the cast order won't be noticed for values in a limited range.
2017-10-06Perform some transformations on IR to legalize for GLSL (#200)Tim Foley
When outputting GLSL from a Slang or HLSL entry point, we need to translate any parameters or results of an entry-point function into global declarations of `in` or `out` parameters, as needed by GLSL. This change adds these transformations at the IR level, so that they don't need to complicate the emit logic. More detailed changes: - Make `render0` test use IR. It passes out of the box. - Fix test runner to not always dump diffs on failures I accidentally initialized an option to `true` instead of `false` when working on debugging the Travis CI failures. - Special-case output for component-wise multiplication to handle GLSL `matrixCompMul()` - Handle GLSL vs. HLSL output for calls to `mul()` - Output proper `layout(std140)` on GLSL constant buffer declarations - Require appropriate GLSL extension when emitting explicit `layout(offset = ...)` on constant buffer members - TODO: Need to avoid requiring this extension in cases where the offsets are what would be computed anyway. Realistically, should probably be emitting code with explicit padding, etc. to guarantee layouts. - Add an IR-based pass to translate entry point functions by eliminating their input/output parameters and replacing them with global variables. - Demangle names when calling target intrinsics The lowering to the IR will turn a call like `sin(foo)` into a call to a function declaration with a mangled name like `_S3sin...`. This works fine when the user is calling their own functions, since the name mangling will apply to both the definition and use sites, but for builtin functions it obviously isn't what we want. This change makes it so that we demangle the name of an instrinsic function just enough so that we can extract the original simple name, and make a call using that. These changes do nor provide 100% of what we need when translating to GLSL, so the `cross-compile-entry-point` test *still* hasn't been flipped over to use the IR (even though that is the test case I've been using to develop these changes).
2017-10-05Working on better handling of builtin functions in IR (#196)Tim Foley
The main change I was working on here was to start having more of the builtin functions (in this case, `cos`, `sin`, and `saturate`) just lower to the IR as calls to builtin functions (with declarations but no definition), rather than expect/require them to map to individual IR opcodes in every case. The main change there was the removal of some `intrinsic_op` modifiers in the stdlib. This then requires the `isTargetInstrinsic` logic in IR-based code emit to avoid emitting declarations for these intrinsics. The corresponding logic for emitting *calls* to these intrinsics is currently being skipped. Along the way, a variety of fixups were added: - In order to support lowering to GLSL, we need to handle cases where a variable/function name uses a GLSL reserved word. The right long-term fix there is to always use generated or mangled names, but for now I'm hacking it by adding a `_s` prefix to all names during IR-based emit. - This needs a flag to disable it, since some of our tests currently rely on checking binding information from generated HLSL/SPIR-V that will include these mangled/modified names. - Emit matrix layout modifiers appropriately for GLSL - Specialize IR parameter-block emission between GLSL and HLSL - Fix up argument count/index logic for a couple of opcodes that weren't fixed when removing the types from the explicit operand list - Fix up IR generation for calls to declarations with generic arguments. We were briefly adding the generic args to the ordinary argument list, which added complexity in several places. We now rely on the declaration-reference nodes in the IR to carry that extra info. - TODO: We actually need to make sure that this is the case, since we don't currently correctly generated specialized decl-refs when building IR for function calls The main test that would have been affected by this is `cross-compile-entry-point`, but I was not able to get that working fully with the IR. The main problem in this case was that when emitting GLSL we will need to perform certain required transformations on the IR to get legal code for GLSL. Notably: - We need to hoist entry-point parameters away from being function parameters, and make them be global variables. This is currently being hand-waved during the emit logic, but it seems way better to have it all get cleaned up in the IR first. - We need to scalarize entry-point parameters, because structure input/output is not supported as vertex input or fragment output (and it may be best to always scalarize anyway, to match HLSL semantics). (Note: "scalarize" here means to bust up structures, but not matrices/vectors)
2017-10-04IR: overhaul IR design/implementation (#195)Tim Foley
* IR: overhaul IR design/implementation Closes #192 Closes #188 This is a major overhaul of how the IR is implemented, with the primary goal of just using the AST-level type representation as the IR's type representation, rather than inventing an entire shadow set of types (as captured in issue #192). One consequence of this choice is that types in the IR are no longer explicit "instructions" and are not represented as ordinary operands (so a bunch of `+ 1` cases end up going away when enumerating ordinary operands). Along the way I also got rid of the embedded IDs in the IR (issue #188) because this wasn't too hard to deal with at the same time. Another related change was to split the `IRValue` and `IRInst` cases, so that there are values that are not also instructions. Non-instruction values are now used to represent literals, references to declarations, and would eventually be used for an `undef` value if we need one. IR functions, global variables, and basic blocks are all values (because they can appear as operands), but not instructions. The main benefit of this approach is that the top-level structure of a bytecode (BC) module is much simpler to understand and walk, and BC-level types are represented much more directly (such that we could conceivably use them for reflection soon). * fixup: 64-bit build fix * fixup: try to silence clang's pedantic dependent-type errors * fixup: bug in VM loading of constants
2017-09-29Get tests running/passing under Linux (#194)Tim Foley
* Get tests running/passing under Linux - Fix up `dlopen` abstraction - Fix up some test cases to request hlsl (rather than default to dxbc) so they can run on non-Windows targets - Fix up test runner ignore tests that can't run on current platform (and not count those as failure) - Fix file handle leeak in process spawner absttraction - Get additional test-related applications building - More tweaks to Travis script; in theory deployment is set up now (yeah, right) * fixup * fixup: Travis environment variable syntax * fixup: Buffer->begin * fixup: actually run full tests on one config * fixup: add build status badge for Travis
2017-09-22More work on IR-based lowering and cross-compilationTim Foley
None of these changes are made "live" at the moment. I'm just trying to get them checked in to avoid divering too far from `master` at any point during development. - Add basic emit logic to produce GLSL from the IR in a few cases (the existing IR emit logic was ad hoc and HLSL-specific) - When lowering a function declaration, walk up its chain of parent declarations to collect additional parameters as needed - When lowering a call, make sure to add generic arguments that come from the declaration reference being called - Attach a "mangled name" to symbols when lowering, so that we can eventually use that name to resolve things for linkage. - After the above work, I had to apply some fixups to make sure that generic arguments *don't* get added when the user is calling an `__intrinsic_op` function, since those should map 1-to-1 down to instructions with just their ordinary parameter list. A big open question right now is whether I should continue to represent the generic arguments as just part of the ordinary argument list for a function, or split them out into separate `applyGeneric` and `apply` steps. A strongly related question is whether a declaration with generic parameters should lower into a single declaration, or one declaration nested inside an outer generic declaration. A good future step at this point would be to eliminate a lot of the `__intrinsic_op` stuff in favor of having the builtin functions include their own definitions, which might be in terms of a new expression-level construct for writing inline IR operations. This can't be done until the existing AST-to-AST path is no longer needed for cross-compilation purposes. More immediate next steps here: - We need a way to round-trip calls to external declaration that get handled by this mangled-name logic. Basically, if we are asked to output HLSL and we see a call to `_S...GetDimensions...(float4, t, a, ...)` we need to be able to walk the mangled name and get back to `t.getDimensions(a, ...)` without a whole lot of manual definitions to make things round-trip. - In the other case, where a declaration isn't built-in for the chosen target, we need to be able to load a module of target-specific definitions (which will somehow map back to symbols with certain mangled names) and then look these up (by mangled name) and then load/link/inline them into the user's IR to satisfy requirements in their code.
2017-09-21Use WARP for D3D rendering tests.Tim Foley
This should in principle allow for the D3D-only tests to run on AppVeyor so that we can validate running things for CI purposes (and is probably a whole lot easier than trying to plug the VM up to the rendering tests). I've switched up one of the tests so that it should run even on AppVeyor, so fingers crossed that it will actually run.
2017-09-21Initial work on a "VM" for Slang code (#189)Tim Foley
At a high level, this commit adds two things: 1. A "bytecode" format for serializing Slang IR instructions and related structure (functions, "registers") 2. A virtual machine that can load and then execute code in that bytecode format. The reason for kicking off this work right now is that we *need* a way to run tests on Slang code generation that doesn't rely on having a GPU present (given that our CI runs on VM instances without GPUs), nor on textual comparison to the output of other compilers. With these features I've implemented a slapdash `slang-eval-test` test fixture that can run a (trivial) compute shader to very our compilation flow through to bytecode. Some key design constraints/challenges: - The bytecode format should be "position independent" so that a user can just load a blob of data and then inspect it without having to deserialize into another format, allocate memory, etc. Eventually the bytecode format might be a replacement for out current reflection API (we used to base reflection off a similar format, but the cost/benefit wasn't there at the time and we switched to just using the AST). - The VM should be able to execute bytecode functions without doing any per-operation translation, JIT, etc. (translation of more coarse-grained symbols is okay). For now the VM is just being used to run tests, but eventually I'd like it to be viable for: - Running Slang-based code in the context of the compiler itself. This starts with stuff like constant-folding in the front-end, but could expand to more general metaprogramming features. - Running Slang-based ocde within a runtime application (e.g., a game engine) that wants to be able to run things like "parameter shader" code, or even just evaluate compute-like code on CPU (e.g., when supporting particles on both CPU and GPU). - Finally, the bytecode format should ideally be able to round-trip back to the IR without unacceptable loss of information. This requirement and the previous one play off of each other, because things like a traditional SSA phi operation is ugly when you have to actually *execute* it. This doesn't matter right now when we don't have SSA yet, but it might be part of the decision-making here. The actual implementation is centralized in `bytecode.{h,cpp}` and `vm.{h.cpp}`. Big picture notes: - The space of opcodes is shared between IR and bytecode (BC), with the hope that this makes translation of operations between the two easy. - The actual bytecode instruction stream relies on a variable-length encoding for integer values, including opcodes and operand numbers, so that the common case is single-byte encoding. - In the long term I intend to have a rule that if you use a single-byte encoding for an opcode, then all operands are required to use single-byte encodings too. Operations that need multi-byte operands would then be forced to use a multi-byte encoding of the op, and would be sent down a slower path in the interpeter. - The "bytecode"'s outer structure is based on ordinary data structures linked with pointers, but they are "relative pointers" so the actual structure is position-independent. - There are two main kinds of operands: registers and "constants." An operand is a signed integer where non-negatie values indicate registers (with `index == operandVal`) and negative values indicate constants (with `index == ~operandVal`). - Registers are stored in the "stack frame" for a VM function call, and each has a fixed offset based on the size of the type and those that come before it. Conceptually, registers are allowed to overlap if they aren't live at the same time, and we manage this with a simple stack model: every register is supposed to identify the register that comes directly before it (this isn't implemented yet). - "Constants" are more realistically a representation of "captured" values, but they are currently also how constants come in. Basically we can use a compact range of indices in the bytecode for a function, and each of these indices indirectly refers to some value in the next outer scope. - The actual encoding of bytecode instructions right now is largely ad-hoc and very wasteful (we encode the type on everything, and we also encode everything as if it had varargs). - In some cases, an instruction needs to know the types of the values involved (e.g., because it needs to load an array element, which means copying a number of bytes based on the size). The way the VM works we have types attached to our registers, so we currently get sneaky and look at those types in some ops. Longer term is makes sense to encode the required type info directly in the BC. - There's a whole lot of hand-waving going on with how the actual top-level bytecode module gets loaded, because of the way we currently treat the top-level module as an instruction stream in the IR. This means that we try to represent the loaded module as a "stack frame" for a call to the module as a function, but that approach as serious problems, and isn't realistically what we want to do.
2017-09-14IR: handle control flow constructs (#186)Tim Foley
* IR: handle control flow constructs This change includes a bunch of fixes and additions to the IR path: - `slang-ir-assembly` is now a valid output target (so we can use it for testing) - This uses what used to be the IR "dumping" logic, revamped to support much prettier output. - A future change will need to add back support for less prettified output to use when actually debugging - IR generation for `for` loops and `if` statements is supported - HLSL output from the above control flow constructs is implemented - Revamped the handling of l-values, and in particular work on compound ops like `+=` - Add basic IR support for `groupshared` variables - Add basic IR support for storing compute thread-group size - Output semantics on entry point parameters - This uses the AST structures to find semantics, so its still needs work - Pass through loop unroll flags - This is required to match `fxc` output, at least until we implement unrolling ourselves. * Fixup: 64-bit build issues. * fixup for merge
2017-09-12Get IR working for `AdaptiveTessellationCS40/Render` testTim Foley
I had expected this to be the first case where control-flow instructions were needed, but it turns out that we aren't testing that entry point right now. The real work/fix here ended up being handling of the `row_major` layout qualifier on a matrix inside a `cbuffer`. The existing AST-based code was passing it through easily (although I don't believe it was handling the layout rules right). Getting it working in the IR involved beefing up the type-layout behavior so that it can handle explicit layout qualifiers (at least for matrix layout) which should also improve the layout computation for non-square matrices with nonstandard layout in the AST-based path. There are still some annoying things left to do: - The `row_major` and `column_major` layout qualifiers in HLSL/GLSL mean different things (well, they mean the reverse of one another) so I need to validate that the GLSL case is working remotely correctly. - The layout logic isn't handling other explicit-layout cases as supported by GLSL (but of course GLSL is a far lower priority than HLSL/Slang) - There is currently no way to pass in an explicit matrix layout flag to Slang to control the default behavior. - Any client who was using Slang for HLSL pass-through and then applying a non-default flag on their HLSL->* compilation will get nasty unexpected behavior when the IR goes live - and they are already dealing with nasty behavior around non-square matrices not matching layout between Slang and the downstream. - The logic that gleans layout modes from a variable declaration is currently only being applied for fields of structure types (which applies to `cbuffer` declarations as well), and not to global-scope uniform variables. - We need test cases for all of this.
2017-09-11Get another test working with IR codedgenTim Foley
- Add support for matrix types in IR/codegen - Add support for basic indexing operations in IR/codegen
2017-09-11Support IR-based codegen for a few more examples.Tim Foley
The main interesting change here is around support for lowering of calls to "subscript" operations (what a C++ programmer would think of as `operator[]`). An important infrastructure change here was to add an explicit AST-node representation for a "static member expression" which we use whenever a member is looked up in a type as opposed to a value. The implementation of this probably isn't robust yet, but it turns out to be important to be able to tell such cases apart.
2017-09-07Replace old notion of "intrinsic" operationsTim Foley
The code previously had an enumerated type for "intrinsic" operations, and allowed functions to be marked `__intrinsic_op(...)` to indicate the operation they map to. The nature of the IR meant that each of these intrinsic ops had to have a corresponding IR opcode, but the `enum` types weren't the same. This change cleans things up a bit by deciding that the `__intrinsic_op(...)` modifier names an actual IR opcode, and so the `IntrinsicOp` enum is gone. The biggest source of complexity here is that there are certain operations that need to be "intrinsic"-ish for the purposes of the current AST-based translation path, because we need them to round-trip from source to AST and back. Right now this is being handled by defining a bunch of "pseudo-ops" which can be used in the `__intrinsic_op` modifier, but which are *not* meant to be represented in the IR. Currently I don't actually handle this during IR generation. In the long run, once we are using IR for everything that needs cross-compilation, we should be able to eliminate the pseudo-ops in favor of just having these be ordinary (inline) functions defined in the stdlib (e.g., the `+=` operator can just have a direct definition). There was a second category of modifier that gets a little caught up in this, which is the `__intrinsic` modifier, which got used in two ways: 1. A function marked `__intrinsic(glsl, ...)` had what I call a "target intrinsic" modifier, which specified how to lower it for a specific target (e.g., GLSL). 2. A function just marked `__intrinsic` was supposed to be a marker for "this function shouldn't be emitted in the output, because the implementation is expected to be provided" The latter category of function should really be an `__intrinsic_op`, so I translated all those uses. I added a tiny bit of sugar so that `__intrinsic_op` without an explicit opcode will look up an opcode based on the name of the function being called, so that an operation like `sin` can automatically be plumbed through to an equivalent IR op. (The first category is a stopgap for the AST-based cross-compilation, and will hopefully be replaced by something better as we get the IR-based path working). Getting the switch from `__intrinsic` to `__intrinsic_op` working required shuffling around some code in `emit.cpp` that handles looking up those modifiers and emitting builtin operations appropriately during cross-compilation. Depending on where we go with things, a possible extension of this approach is to allow multiple operands to `__intrinsic_op` so that the first specifies the opcode, and then the rest are literal arguments to specify "sub-ops." This could help us handle stuff like texture-fetch operations without an explosion in the number of opcodes. I still need to think about whether this is a good idea or not.
2017-09-06Continue work on IR-based codegenTim Foley
This gets us far enough that we can convert a single test case to use the IR, under the new `-use-ir` flag. Getting this merged into mainline will at least ensure that we keep the IR path working in a minimal fashion, even when we have to add functionality the existing AST-based path There is definitely some clutter here from keeping both IR-based and AST-based translation around, but I don't want to have a long-lived branch for the IR that gets further and further away from the `master` branch that is actually getting used and tested. Summary of changes: - Add pointer types and basic `load` operation to be able to handle variable declarations - Add basic `call` instruction type - Add simple address math for field reference in l-value - Always add IR for referenced decls to global scope - Add notion of "intrinsic" type modifier, which maps a type declaration directly to an IR opcode (plus optional literal operands to handle things like texture/sampler flavor) - Improve printing of IR instructions, types, operands - Add constant-buffer type to IR - Allow any instruction to be detected as "should be folded into use sites" and use this to tag things of constant-buffer type - Also add logic for implicit base on member expressions, to handle references to `cbuffer` members - Add connection back to original decl to IR variables (including global shader parameters...) - Use reflection name instead of true name when emitting HLSL from IR (so that we can match HLSL output) - Make IR include decorations for type layout - Re-use existing emit logic for HLSL semantics to output `register` semantics for IR-based code - Make IR-based codegen be an option we can enable from the command line - It still isn't on by default (it can barely manage a trivial shader), but it seems better to enable it always instead of putting it under an `#ifdef` - Fix up how we check for intrinsic operations suring AST-based cross compilation so that adding new intrinsic ops for the IR won't break codegen.
2017-09-05Add a basic test case for HLSL implicit conversionsTim Foley
This at least tries to capture some of the basic rules about what implicit conversions are allowed.
2017-08-25Fixup for test failureTim Foley
AppVeyor has a different version of fxc installed by default, and it produces subtly different output for this test case. It seems like later versions are clever enough to completely eliminate an empty `cbuffer` declaration, but earlier versions aren't. I'm actually not entirely sure why Slang is successfully eliminating the cbuffer as well, but the output DXBC implies it was not generated.
2017-08-25Fix some resources-in-structs bugsTim Foley
Fixes #171 Fixes #172 These two bugs related to bad logic in handling of splitting resource-containing `cbuffer` declarations. - Issue #171 was the case where a `cbuffer` *only* had resource fields, in which case we crashed whenever referencing any field (some code was assuming there had to be non-resource fields) - Issue #172 was a case where two fields were declared with a single declaration (`Texture2D a, b;`), and the logic we had for tracking resource-type fields was accidentally tagging *both* fields with a single modifier so that field `b` would get confused for `a` in some contexts, and attempts to access `b` would crash. Both issues are now fixed, and regression tests have been added.
2017-08-17Add a flag to control type splittingTim Foley
The `-split-mixed-types` flag can be provided to command-line `slangc`, and the `SLANG_COMPILE_FLAG_SPLIT_MIXED_TYPE` flag can be passed to `spSetCompileFlags`. Either of these turns on a mode where Slang will split types that included both resource and non-resource fields. The declaration of such a type will just drop the resource fields, while a variable declare using such a type turns into multiple declararations: one for the non-resource fields, and then one for each resource field (recursively). This behavior was already implemented for GLSL support, and this change just adds a flag so that the user can turn it on unconditionally. Caveats: - This does not apply in "full rewriter" mode, which is what happens if the user doesn't use any `import`s. I could try to fix that, but it seems like in that mode people are asking to bypass as much of the compiler as possible. - When it *does* apply, it applies to user code as well as library/Slang code. So this will potentially rewrite the user's own HLSL in ways they wouldn't expect. I don't see a great way around it, though.
2017-08-15Add missing expected output file for test.Tim Foley
2017-08-15Improve diagnostics for overlapping/conflicting bindingsTim Foley
Closes #38 - Change overlapping bindings case from error to warning (it is *technically* allowed in HLSL/GLSL) - Make diagnostic messages for these cases include a note to point at the "other" declaration in each case, so that user can more easily isolate the problem - Unrelated fix: make sure `slangc` sets up its diagnostic callback *before* parsing command-line options so that error messages output during options parsing will be visible - Unrelated fix: make sure that formatting for diagnostic messages doesn't print diagnostic ID for notes (all have IDs < 0). - Note: eventually I'd like to not print diagnostic IDs at all (I think they are cluttering up our output), but doing that requires touching all the test cases...
2017-08-15Handle possibility of bad types in varying input/output signature.Tim Foley
Fixes #160 If the front-end runs into a type it doesn't understand in the parameter list of an entry point, it will create an `ErrorType` for that parameter, but then the parameter binding/layout rules will fail to create a `TypeLayout` for the prameter (and return `NULL`). There were some places where the code was expecting that operation to succeed unconditionally, and so would crash when there was a bad type. The specific case in the bug report was when the return type of a shader entry point was bad: // `vec4` is not an HLSL type vec4 main(...) { ... } Note that the specific case in the buf report only manifests in "rewriter" mode (when the Slang compiler isn't allowed to issue error messages from the front-end), but the same basic thing would happen if the varying parameter/output had used a type that is invalid for varying input/output: Texture2D main(...) { ... } I'm not 100% happy with just adding more `NULL` checks for this, because there is no easy way to tell if they are exhaustive. A better solution in the longer term might be to construct a kind of `ErrorTypeLayout` to represent cases where we wanted a type layout, but none could be constructed.
2017-08-10Make source location lightweightTim Foley
Fixes #24 So far the code has used a representation for source locations that is heavy-weight, but typical of research or hobby compilers: a `struct` type containing a line number and a (heap-allocated) string. This is actually very convenient for debugging, but it means that any data structure that might contain a source location needs careful memory management (because of those strings) and has a tendency to bloat. The new represnetation is that a source location is just a pointer-sized integer. In the simplest mental model, you can think of this as just counting every byte of source text that is passed in, and using those to name locations. Finding the path and line number that corresponds to a location involves a lookup step, but we can arrange to store all the files in an array sorted by their start locations, and do a binary search. Finding line numbers inside a file is similarly fast (one you pay a one-time cost to build an array of starting offsets for lines). More advanced compilers like clang actually go further and create a unique range of source locations to represent a file each time it gets included, so that they can track the include stack and reproduce it in diagnostic messages. I'm not doing anything that clever here.
2017-08-09Fix use of "pseudo-syntax" in current lowering passTim Foley
The so-called "lowering" pass (really a kind of AST-to-AST legalization pass right now) needs to handle some basic scalarization of structured types, and it does this by inventing what I call "pseuo-expressions" and "pseudo-declarations." For example, there is a pseudo-expression node type that represents a tuple of N other expressions, and certain operations act element-wise over such tuples. The problem was that the implementation introduced these out-of-band expression/declaration types into the existing AST hierarchy which led to a dilemma: - If these new AST nodes were declared like all the others (and integrated into the visitor dispatch approach, etc.) then every pass would need to deal with them even though they are meant to be a transient implementation detail of this one pass - But if the new nodes *aren't* declared like the others, then they can't meaningfully interact with visitor dispatch, and will just crash the compiler if they somehow "leak" through to latter passes. And because they are just ordinary AST nodes from a C++ type-system perspective, such leaking is entirely possible (if not probable) Hopefully that setup helps make the solution clear: instead of having the "lowering" pass map an expression to an expression, it needs to map an expression to a new data type (here called `LoweredExpr`) that can wrap *either* an ordinary expression (the common case) or one of the new out-of-band values. Any code that accepts a `LoweredExpr` needs to handle all the cases, or explicitly decide that it can't/won't deal with anything other than ordinary expressions. Most of the code changes are straightforward at that point, although the whole "lowering" approach is a bit fiddly right now, so gertting the tests passing took a bit of attention. I'm not sure our test coverage of all this code is great, so I wouldn't be surprised if some failures are lurking still.
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-22Make the "hack" sampler explicit for nowTim Foley
- We use this to work around the fact that, e.g., `Texture2D.Load` doesn't take a sampler, but the equivalent GLSL operation `texelFetch` requires one - Previously we tried to hide the sampler from the user, hoping that glslang would drop it and we could just ignore it, but that doesn't work - For now we'll go ahead and explicitly show the sampler in the reflection info so that an app can react appropriately - We also generate a unique binding for the sampler, instead of the old behavior that fixed it with `binding = 0` - We still fix it with `set = 0`, so it might still surprise users
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-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-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-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-17Add explicit operator overloads for scalar/matrix casesTim Foley
Fixes #103 - Previously I was relying on scalar-to-vector promotion to pick the right type in these cases, but I hadn't implemented scalar-to-matrix promotion (I should...) - Rather than relying on promotion behavior, this change goes ahead and adds explicit overloads. I think this is probably a better decision in the long term, since one might want to support these cases for operators, while warning (or erroring) on the more general cases of implicit conversion. - This covers matrix/scalar, scalar/matrix, vector/scalar, and scalar/vector cases
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-14Add reflection support for GLSL thread-group-size modifierTim Foley
Fixes #15 These are the modifiers like: layout(local_size_x = 16) in; Unlike the HLSL case, these don't get attache to the entry point function itself, so there is a bit more work involed in looking them up. Just to make sure I didn't mess up the HLSL case, I went ahead and added two tests for this capability: one for GLSL and one for HLSL.
2017-07-14Don't assign a `binding` to a `push_constant` bufferTim Foley
Fixes #12 - This was a latent issue, but the previous commit brought it to the front. - As indicated in #12, I don't allocate a descriptor-table slot to the block - Instead I allocate a `PushConstantBuffer` - Unlike what #12 asks for, I don't use a different resource type for the contents of the block - Pretty much all the logic is easiest if these continue to be just plain `Uniform` data
2017-07-13An array of resources in Vulkan only consumes one bindingTim Foley
Fixes #84 - When computing resource usage for an array type, don't multiply the resource usage of the element type by the element count foor descriptor-table-slot resources. - When reporting the "stride" of an array type through reflection, report the stride for descriptor table slots as zero, always.
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-12Add per-entry-point information to reflection JSON dumpsTim Foley
- This also adds reflection API for querying: - Entry point name - Entry point parameter list
2017-07-11Make parser recovering more robust to avoid infinite loopsTim Foley
Fixes #75 In order to avoid cascaded errors, I went ahead and made the parser refuse to skip past a `}` in recovery mode. The problem with this is that we fail to make forward progress if we are stuck on a `}` (this happens if you have an extra `}` at the global scope.
2017-07-11Merge pull request #73 from tfoleyNV/image-type-reflectionTim Foley
Improve reporting of GLSL `image*` types
2017-07-11Improve reporting of GLSL `image*` typesTim Foley
- Update stdlib so taht `image*` types have read-write access encoded in their type - TODO: this isn't 100% right, since there are GLSL qualifiers that might override this - Add a test case to verify that the reflection API reports `image*` parameters
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-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-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-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-08Revise the (only) rewriter-error test caseTim Foley
This was testing that we let the downstream compiler report a parse error, but that is no longer a goal (because we need to rewrite function bodies to deal with type validation). I've switched it to a type error in the function body, and confirmed that we do let fxc report the problem.