summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2017-09-13Add an initial design document on interfacesTim Foley
This covers interfaces, generics, and associated types - hopefully with enough detail that we can start writing up example programs that we believe should compile.
2017-09-12Merge pull request #181 from tfoleyNV/ir-lowering-workTim Foley
Get IR working for `AdaptiveTessellationCS40/Render` test
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-11Merge pull request #180 from tfoleyNV/ir-lowering-workTim Foley
Get another test working with IR codedgen
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-11Merge pull request #179 from tfoleyNV/ir-lowering-workTim Foley
Support IR-based codegen for a few more examples.
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-11Merge pull request #178 from tfoleyNV/boilerplate-generatorTim Foley
Initial work on boilerplate code generator
2017-09-11Initial work on boilerplate code generatorTim Foley
The goal here is to get the Slang "standard library" code out of string literals and into something a bit more like an actual code file. This is handled by having a `slang-generate` tool that can translate a "template" file that mixes raw Slang code (or any language we want to generate...) with generation logic that is implemented in C++ (currently). This work isn't final by any stretch of the imagination, but it moves a lot of code and not merging it ASAP will complicate other changes. My expectation is that the generator tool will be beefed up on an as-needed basis, to get our stdlib code working. Similarly, the stdlib code does not really take advantage of the new approach as much as it could. That is something we can clean up along the way as we do modifications of the stdlib.
2017-09-07Merge pull request #177 from tfoleyNV/ir-workTim Foley
Replace old notion of "intrinsic" operations
2017-09-07Fixup: fix uninitialized memory bugTim Foley
This is a bug that already existed in the IR code, but wasn't getting triggered on existing test cases, and only seems to affect the 64-bit target right now. The "key" value being constructed to cache and re-use constants during IR generation wasn't actually being fully initialized, and so garbage values in it could cause the lookup of an existing value to fail where it should succeed.
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-06Merge pull request #176 from tfoleyNV/ir-workTim Foley
Continue work on IR-based codegen
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-05Merge pull request #175 from tfoleyNV/implicit-conversionTim Foley
Move implicit conversion operations to stdlib
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-09-05Move implicit conversion operations to stdlibTim Foley
- Previously, there were a variety of rules in `check.cpp` to pick the conversion cost for various cases involving scalar, vector, and matrix types. - The main problem of the previous approach is that any lowering pass would need to convert an arbitrary "type cast" node into the right low-level operation(s). - The new approach is that a type conversion (implicit or explicit) always resolves as a call to a constructor/initializer for the destination type. This means that the existing rules around marking operations as builtins should work for lowering. - The support this, the checking logic needs to perform lookup of intializers/constructors when asked to perform conversion between types. It does this by re-using the existing logic for lookup and overload resolution if/when a type was applied in an ordinary context. - Next, we define a modifier that can be attached to constructors/initializers to mark them as suitable for implicit conversion, and associate them with the correct cost to be used when doing overload comparisons. - We add the modifier to all the scalar-to-scalar cases in the stdlib, using the logic that previously existed in semantic checking. - Next we add cases for general vector-to-scalar conversions that also convert type, using the same cost computation as above. - This probably misses various cases, but at this point they can hopefully be added just in the stdlib. - One gotcha here is that in lowering, we need to make sure to lower any kind of call expression to another call expression of the same AST node class, so that we don't lose information on what casts were implicit/hidden in teh source-to-source case. Two notes for potential longer-term changes: 1. There is still some duplication between the type conversion declarations here and the "join" logic for types used for generic arguments. Ideally we'd eventually clean up the "join" logic to be based on convertability, but that isn't a high priority right now, as long as joins continue to pick the right type. 2. It is a bit gross to have to declare all the N^2 conversions for vector/matrix types to duplicate the cases for scalars. For the simple scalar-to-vector case, we might try to support multiple conversion "steps" where both a scalar-to-scalar and a scalar-to-vector step can be allowed (this could be tagged on the modifiers already introduced). That simple option doesn't scale to vector-to-vector element type conversions, though, where you'd really want to make it a generic with a constraint like: vector<T,N> init<U>(vector<U,N> value) where T : ConvertibleFrom<U>; Here the `ConvertibleFrom<U>` interface expresses the fact that a conforming type has an initializer that takes a `U`. What doesn't appear in this context is any notion of conversion costs. We'd need some kind of system for computing the conversion cost of the vector conversion from the cost of the `T` to `U` converion.
2017-08-31Merge pull request #174 from tfoleyNV/modifier-lowering-fixTim Foley
Fix some issues around cloned modifiers.
2017-08-31Fix some issues around cloned modifiers.Tim Foley
The root of the problem here is that: - We do a shallow copy of modifiers when "lowering" declarations/statements, by just copying over the head pointer of the linked list of modifiers - During lowering we sometimes add additional modifiers (only used during lowering), and these can thus accidentally get added to the end of the list of modifiers for the original declaration (rather than just the lowered decl) - If the same declaration is used by multiple entry points to be output, then a modifier added by the first entry point (which could reference entry-point-specific storage) will be earlier in the modifier list and might be picked up by a later entry point, so that we dereference already released memory The simple fix for right now is the use the support for a "shared" modifier node to ensure that each lowered declaration/statement gets a unique modifier list. A better long-term fix is: 1. Don't use modifiers to store general side-band information, and instead use proper lookup tables that own their contents. 2. Don't use a linked list to store modifiers (this was done to make splicing easy, but now we have a whole class of bugs related to bad splices), and be willing to clone them as needed.
2017-08-25Merge pull request #173 from tfoleyNV/resources-in-structs-fixesTim Foley
Fix some resources-in-structs bugs
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-25Try to print failure from AppVeyor testsTim Foley
2017-08-25Fixup: handle splice of multiple modifiersTim Foley
I changed the logic so that it might splice a new modifier into the existing linked list (not just at the end), but failed to account for the case where what we are splicing in isn't just a single modifier, but a whole *list* (which occurs when splicing in the shared modifiers themselves). This change handles that case with a bit of annoying linked-list cleverness.
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-17Merge pull request #170 from tfoleyNV/irTim Foley
Add some dummy logic to print IR to HLSL
2017-08-17[ir] Add support for "decorations" on instructionsTim Foley
The terminology here is similar to SPIR-V. For right now the only decoration exposed is a fairly brute-force one that just points back to a high-level declaration so that we can look up info on it that might affect how we print output.
2017-08-17[ir] Represent fields more direcltyTim Foley
Previously, a `StructType` was an ordinary instruction that took a variable number of types are operands, representing the types of fields. This ends up being inconvenient for a few reasons: - To add decorations to the fields, you'd end up having to decorate the struct type instead (SPIR-V has this problem) - You need to compute field indices during lowering, when you might prefer to defer that until later - The get/set field operations now need an index, which needs to be an explicit operand, which means a magic numeric literal floating around to represent the index The new approach fixes for the first two of these, and at least makes the last one a bit nicer. A `StructDecl` is now a parent instruction, and its sub-instructions represent the fields of the type - each field is an explicit instruction of type `StructField`. The operation to extract a field takes a direct reference the struct field, so everything is quite explicit.
2017-08-17Add some dummy logic to print IR to HLSLTim Foley
- Change IR instructions to just hold an integer opcode instead of a pointer to the "info" structure - Externalize definition of IR instructions to a header file, and use the "X macro" approach to allow generating different definitions - Add notion of function types to the IR, so that we can easily query the result type of a function - Add some convenience accesors to allow walking the IR in a strongly-typed manner (e.g., iterate over the parameters of a function) - TODO: these should really be changed to assert the type of things, as least in debug builds - Add very basic logic to `emit.cpp` so that it can walk the generated IR and start printing it back as HLSL - This isn't meant to be usable as-is, but it is a step toward where we need to go
2017-08-17Merge pull request #169 from tfoleyNV/irTim Foley
IR generation cleanup work
2017-08-17IR generation cleanup workTim Foley
- Make all instructions store their argument count for now, so we can iterate over them easily. - Longer term we might try to optimize for space because the common case is that the operand count is known, but keeping it simpler seems better for now - Split apart the creation of an instruction from adding it to a parent - Use the above capability to make sure that we add a function to its parent *after* all the parameter/result type emission has occured. - Perform simple value numbering for types during IR creation - This logic also tries to pick a good parent for any type instructions, so that types don't get created local to a function unless they really need to - Create all constants at global scope, and re-use when values are identical
2017-08-17Merge pull request #168 from tfoleyNV/resources-in-structs-controlTim Foley
Add a flag to control type splitting
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-16Merge pull request #167 from tfoleyNV/irTim Foley
More work on IR
2017-08-16Fixups for IR checkpointTim Foley
- The changes introduced a new path where we don't even go through the current "lowering" (really an AST-to-AST legalization pass), but this exposed a few issues I didn't anticipate: - First, we needed to make sure to pass in the computed layout information when emitting the original program (since the layout info is no longer automatically attached to AST nodes) - Second, we needed to take the sample-rate input checks that were being done in lowering before, and move them to the emit logic (which is really ugly, but I don't see a way around it for GLSL).
2017-08-16More work on IRTim Foley
With this change, basic generation of IR works for a trivial shader, and there is some basic support for dumping the generated IR in an assembly-like format. As with the other IR change, the use of the IR is statically disabled for now, so that existing users won't be affected.
2017-08-15Merge pull request #166 from tfoleyNV/add-builtins-to-coreTim Foley
Add user-defined builtins to the "core" module
2017-08-15Merge pull request #165 from tfoleyNV/irTim Foley
Starting to add intermediate representation (IR)
2017-08-15Add user-defined builtins to the "core" moduleTim Foley
The Slang API allows an expert user to feed in source code that the compiler then treats as if it came from the Slang "standard library." They can use this to introduce new builtin types, functions, etc. - so long as they are careful, and are willing to deal with the lack of any compatibility guarantees across versions. At some point I split the Slang standard library into distinct modules, so that GLSL and HLSL builtins wouldn't pollute each other's namespace. In that change, I had to decide what module any new user-defined builtins should get added to, and I apparently decided they should go into the module for the Slang language, which would only affect `.slang` files. This doesn't work at all if the user wants to declare new HLSL builtins. I've gone ahead and made user extensions add to the "core" module (which is used by all of HLSL, GLSL, and Slang), but a better long-term fix would be to let the user pick the module/language the new builtins should apply to.
2017-08-15Starting to add intermediate representation (IR)Tim Foley
Right now none of this is hooked up, but I want to get things checked in incrementally rather than have along long-lived branches. - Added placeholder declarations for IR representation of instructions, basic blocks, etc. - Start adding a `lower-to-ir` pass to translate from AST representation to IR Again: none of this is functional, so it shouldn't mess with existing users of the compiler.
2017-08-15Merge pull request #162 from tfoleyNV/gh-38Tim Foley
Improve diagnostics for overlapping/conflicting bindings
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-15Merge pull request #161 from tfoleyNV/gh-160Tim Foley
Handle possibility of bad types in varying input/output signature.
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-14Merge pull request #159 from tfoleyNV/name-typeTim Foley
Name type
2017-08-14Add an explicit `Name` typeTim Foley
Fixes #23 Up to this point, the compiler has used the ordinary `String` type to represent declaration names, which means a bunch of lookup structures throughout the compiler were string-to-whatever maps, which can reduce efficiency. It also means that things like the `Token` type end up carying a `String` by value and paying for things like reference-counting. This change adds a `Name` type that is used to represent names of variables, types, macros, etc. Names are cached and unique'd globally for a session, and the string-to-name mapping gets done during lexing. From that point on, most mapping is from pointers, which should make all the various table lookups faster. More importantly (possibly), this brings us one step closer to being able to pool-allocate the AST nodes.
2017-08-14Rename `Name` fields to `name`Tim Foley
This is in preparation for using `Name` as a type name.
2017-08-12Merge pull request #158 from tfoleyNV/syntax-lookupTim Foley
Data-driven parsing of modifiers
2017-08-12Data-driven parsing of modifiersTim Foley
Just like the previous change did for declaration keywords, this change uses the lexical environment to drive the lookup and dispatch of modifier parsing. This allows us to easily add modifiers to Slang, even when they might conflict with identifiers used in user code (because the modifier names are no longer special keywords, but ordinary identifiers). There was already some support for ideas like this with `__modifier` declarations (`ModifierDecl`) used to introduce some GLSL-specific keywords (so that they wouldn't pollute the namespace of HLSL files). The new approach changes these to be actual `syntax` declarations (`SyntaxDecl`) with the same representation as those used to introduce declaration keywords. Because many modifiers just introduce a single keyword that maps to a simple AST node (no further tokens/data), I modified the handling of syntax declarations so that they can take a user-data parameter, and this allows the common case ("just create an AST node of this type...") to be handled with minimal complications. This also adds in a general-purpose string-based lookup path for AST node classes, that should support programmatic creation in more cases. Statements are now the main case of keywords that need to be made table driven.
2017-08-11Merge pull request #157 from tfoleyNV/syntax-lookupTim Foley
Look up declaration keywords using ordinary scoping.