https://github.com/shader-slang/slang

sort by:
Revision Author Date Message Commit Date
caf6f7a All compiler fixes to get ir branch work with falcor feature demo. - support overloaded generic function. this involves adding a new expression type, `OverloadedExpr2` to hold the candidate expressions for the generic function decl being referenced. - make BitNot a normal IROp instead of an IRPseudoOp - make sure we clone the decorations of parameters when cloning ir functions - propagate geometry shader entry point attributes (`[maxvertexcount]` and `[instance]`) through HLSL emit - IR emit: handle geometry shader entry-point parameter decorations, such as 'triangle'. - IR emit: treat geometry shader stream output typed ir value as `should fold into use`. 18 January 2018, 01:54:56 UTC
68f529a Merge pull request #370 from csyonghe/master bug fixes to get falcor example shader code to compile. 16 January 2018, 22:52:06 UTC
a74a549 bug fixes to get falcor example shader code to compile. 1. prevent cyclic lookups when an interface inherits transitively from itself. 2. in `createGlobalGenericParamSubstitution`, create a default substitution for the base type declref before using it to lookup the witness table. 16 January 2018, 20:38:36 UTC
59691ae Allow extension on interface (#369) This completes item 5 in issue #361. The interesting change is that when checking for interface conformance, we include the requirements (include transitive interfaces) defined in extensions as well. (check.cpp line 1946) All the other changes are for one thing: reoder the semantic checkings to two explicit stages: check header and check body. In check header phase, we check everything except function bodies, register all extensions with their target decls, then check interface conformances for all concrete types. In body checking phase, we look inside the function bodies and check concrete statements/expressions. This change ensures that we take extension into consideration in all places where it should be. 16 January 2018, 18:52:10 UTC
68fd448 Merge pull request #367 from csyonghe/extension2 Support transitive interfaces 16 January 2018, 03:22:39 UTC
513f56b cleanup debug code 15 January 2018, 23:30:11 UTC
3d4eaf3 Support transitive interfaces This commit is a bunch of quick hacks to get transitive interfaces to work. The idea is for each concrete type we create one giant witness table that contains entries for all the transitively reachable interface requirements, and then create one copy of that witness table for each interface it implements. `DoLocalLookupImpl` now also looks up in inherited interface decles when looking up for a symbol in an interface decl. When visiting `InheritanceDecl` in `lower-to-ir`, create copies of the giant witness table for each transitively inherited interface, so that these witness tables can be found later when the IR is specialized. Re-enable the `copy all witness tables` hack in `specializeIRForEntryPoint` to ensure those transitive witness tables are copied over. 15 January 2018, 23:18:25 UTC
8abae05 Merge pull request #365 from csyonghe/extension Allow extension of a concrete type to implement additional interface 15 January 2018, 21:54:57 UTC
cff418b Merge branch 'master' into extension 15 January 2018, 21:54:32 UTC
e86ab5f Merge pull request #366 from csyonghe/keywordrename add support for `extension` and `type_param` keywords 15 January 2018, 21:21:34 UTC
8e67c48 add support for `extension` and `type_param` keywords 15 January 2018, 02:38:35 UTC
436f0e7 temporary workaround to fix test case failures. 14 January 2018, 23:20:46 UTC
ae8ef43 Fixup field lookup from a member function defined in an extension This fixes item 2 in #361 Modifies existing extension-multi-interface.slang test case to cover the additional scenario. 14 January 2018, 21:52:02 UTC
d33e6b7 allow extension of a concrete type to implement additional interface Also support the scenario that the extension declares conformance to interface I, and a method M in I is already supported by the base implementation. 14 January 2018, 21:22:11 UTC
d4dab2c Merge pull request #364 from csyonghe/assoctype Support nested generics 13 January 2018, 21:17:02 UTC
4d2086f Fix creation of `ThisTypeSubstitution`. `createDefaultSubstitutions` now responsible for creating a `ThisTypeSubstitution` when `decl` is an `InterfaceDecl`. This is to ensure a reference to an associated type decl from the same interface that defines the assoctype decl will get a `ThisTypeSubstitution` so that the right hand side of it can be replaced by future substitutions. 13 January 2018, 20:17:43 UTC
99f49e4 remove out-of-date changes 13 January 2018, 19:30:37 UTC
4b284da Support nested generics fixes #362 13 January 2018, 04:55:38 UTC
df6eeb9 Refactor substitution representation in DeclRefBase (#363) This commit changes the type of `DeclRefBase::substitutions` from `RefPtr<Substitutions>` to `SubstitutionSet`, which is a new type defined as following: ``` struct SubstitutionSet { RefPtr<GenericSubstitution> genericSubstitutions; RefPtr<ThisTypeSubstitution> thisTypeSubstitution; RefPtr<GlobalGenericParamSubstitution> globalGenParamSubstitutions; } ``` This change get rid of most helper functions to retreive the substitution of a certain type, as well as surgery operations to insert a `ThisTypeSubstitution` or `GlobalGenericTypeSubstittuion` at top or bottom of the substitution chain. It also simplies type comparison when certain type of substitution should not be considered as part of type definition. 12 January 2018, 22:15:56 UTC
8daafcc bruteforce implementation of witness table resolution for associated (#358) 09 January 2018, 18:50:44 UTC
3d435f7 Bug fixes for Slang integration (#356) * fix #353 * move validateEntryPoint to after all entrypoints has been checked * bug fix: DeclRefType::SubstituteImpl should change ioDiff * bug fix: generic resource usage should have count of 1 instead of 0. * update test case 04 January 2018, 21:41:09 UTC
e90dfcf Merge pull request #349 from csyonghe/master Add API for querying TypeLayout from a Type 04 January 2018, 02:09:35 UTC
550405d Fix type lookup of global type arguments Global type argument lookup should be done in both loaded modules and current trnaslation units. This is the same as the logic of spReflection_FindTypeByName, so it is extracted into `CompileRequest::lookupGlobalDecl(Name*)` method and reused in places. 04 January 2018, 01:15:03 UTC
61b206d Merge https://github.com/shader-slang/slang 04 January 2018, 01:12:46 UTC
5da16a6 Fix bug around arrays of structs of resources (#352) Should fix #351 The basic problem is that the type layout logic in Slang isn't taking into account the way that resource-type fields in aggregate types get split. When you just have a bare aggregate, this oversight doesn't cause a problem, but once you put those aggregates into an array, the problems become clear. Given: ```hlsl struct Test { Texture2D a; Texture2D b; }; Test test[8]; ``` The default type-layout algorithm gives `Test::a` an offset of zero, and `Test::b` an offset of one. However, after splitting, we have something like: ```hlsl Texture2D test_a[8]; Texture2D test_b[8]; ``` It is clear in this case that `test_b` can't start at an offset of one relative to `test_a` - it needs to start at `register(t8)`. This change handles things by adjusting the layout of an array type to account for this detail as soon as it is created. The alternative would have been to not change layout rules at all, but to instead try to adjust things at the point where types get split (and the layout for the un-split case gets applied to the split variable). The reason for doing it the way it is in this change is that the reflection API will hopefully provide accurate information. Related to reflection information, one thing that is missing here is proper computation of the "stride" for an array like this. We'll see if that needs to be addressed in a follow-up. 04 January 2018, 00:22:37 UTC
771da26 spReflection_FindTypeByName: add lookup in translationUnits. 03 January 2018, 20:53:16 UTC
36bc65a Merge https://github.com/shader-slang/slang 03 January 2018, 20:47:46 UTC
9f89c3b Add API for querying TypeLayout from a Type Added two API functions: 1. `spReflection_FindTypeByName`, which returns a DeclRefType to the struct type with the given name. The function finds from all loaded modules in a `CompileRequest` for a decl with the given name, construct a `Type` object and cache it in `CompileRequest::types` dictionary. The subsequent calls to `spReflection_FindTypeByName` with the same name will simply returned the cached Type objects. 2. `spReflection_GetTypeLayout`, which returns a `TypeLayout` for a given `Type`. This function creates and caches the `TypeLayout` in the `TargetRequest` object that is used to create the `ProgramLayout`. 03 January 2018, 20:46:23 UTC
f52b93e add call to `EnsureDecl` in `SpecializeGenericForOverload`. 03 January 2018, 20:38:48 UTC
51f0701 Fix struct decl order again (#348) * Add core.natvis file to Slang DLL build It seems that when `slang.dll` gets loaded into a user's project, the debugger is able to pick up the custom visualizers implemented in `slang.natvis` (which is directly added to the DLL project) but not `core.natvis` (which is added to a static library project that the DLL project references). Adding `core.natvis` to the DLL project directly seems to resolve this and greatly improve the debugging experience when in user code. * Bug fix: emit type of CB before CB when using IR The problem here was the logic for emitting types used by an IR declaration before the declaration. I refactored it to share logic between variables with initializers and functions, but in doing so failed to have an ordinary variable (which includes constant buffers) ensure that its own type was emitted before the variable. This is a one-line fix. 03 January 2018, 18:17:17 UTC
1e0aabf no-codegen compile flag and global generics reflection (#347) * no-codegen compile flag and global generics reflection 1. Add SLANG_COMPILE_FLAG_NO_CODEGEN (-no-codegen) compiler flag to skip code generation stage, so that a shader that uses global generic type parmameters can be parsed, checked and introspected without knowing the final specialization. 2. Add reflection API to query for global generic type parameters, global parameters of generic type, and the generic type parameter index related to a global generic parameter. 3. Add a reflection test case for global generic type parameters. * add expected result for global-type-params test case. * fix reflection json output. * fix branch condition errors * fix expected result for global-type-params.slang * fix expected test case output 03 January 2018, 06:04:50 UTC
66f9a7c Bug fix for humane source location computation. (#346) Fixes #345 A brief refresher: a `SourceLoc` in the Slang implementation is just an integer (more or less an absolute byte index into all of the source compiled so far). We convert that integer to a "humane" source location (a file name and line/column numbers) by finding the file and line that match the integer via binary search. The data structures used for that search are owned by a `SourceManager`. In order to avoid running out of source locations when used in a long-running application (that might reload shaders many times), the implementation creates one `SourceManager` per `CompileRequest`, along with a single shared `SourceManager` that is used for locations in the builtin libraries. The root of the bug here was that some code was using the `SourceManager` for a compile request when it should have been using the one for the builtins. This happened because one source manager was asked to translate a `SourceLoc` into a humane location, which first involves "expanding" that location (figuring out which file it belongs to, and which source manager owns that file), and failed to realize that the expanded location might use a different source manager (either the current one or one of its "parents"). I fixed this by reworking the API so that the mapping from an expanded location to a humane one is no longer a member of a source manager (since the correct source manager can be looked up in the associated expanded location). Hopefully this will prevent this class of error in the future. 02 January 2018, 18:44:05 UTC
17369d9 Always respect dependency order when lowering decls via AST (#344) Fixes #333 The code in `ast-legalize` is passed an array of declarations that have been ordered by dependencies using a topological sort. Unfortunately, it was only using that list in the case where the request was considered to be a "rewrite" request, and would otherwise rely on the order in which things get forced during the recursive walk (which doesn't really work for our needs). 02 January 2018, 17:29:21 UTC
bdc1fa5 Merge pull request #342 from csyonghe/fix-341 Fix type substitution for associatedtype 29 December 2017, 04:32:02 UTC
37cff92 fixup substitution of typedef associated type implementation via GetType() call. 29 December 2017, 03:51:58 UTC
ee1ea35 Merge branch 'struct-in-generic' 28 December 2017, 22:43:45 UTC
9efd959 Fix substitution for associatedtype. fixes #341 When a typedef definition is used to satisfy an associated type, we must also substitute the resulting typedef type using parent substitution, in the case that the typedef is a generic application. 28 December 2017, 22:39:14 UTC
bcfe836 Merge pull request #340 from csyonghe/struct-in-generic Fix NameExprType returning deleted canonical type when it's in a generic parent 28 December 2017, 12:18:27 UTC
d46aeb0 Fix NameExprType returning deleted canonical type when it's in a generic parent. fixes #339 `NamedExpressionType::CreateCanonicalType()` may return a deleted pointer. The original implementation is as follows: ``` Type* NamedExpressionType::CreateCanonicalType() { return GetType(declRef)->GetCanonicalType(); } ``` If `GetType()` returns a newly constructed Type (this happens when the `typedef` is defined inside a generic parent, which triggers a non-trivial substitution), the temporary type will be deleted when the function returns. The fix is to store the temporary type as a field of NamedExpressionType (`innerType`). A relevant fix (though not the true cause of issue #339) is to have `Type::GetCanonicalType()` also hold a `RefPtr` to the constructed canonical type, when the canonical type is not `this`. This prevents a returned canonical type being assigned to a RefPtr, which makes it possible for that RefPtr to be the sole owner of the canonical type and deleteing the canonical type when that RefPtr is destroyed. 28 December 2017, 11:53:19 UTC
7e340c2 Merge pull request #337 from csyonghe/master Using a visitor to replace lookup scopes of generic function's return type expr 28 December 2017, 07:13:37 UTC
57d9498 Merge branch 'master' into master 28 December 2017, 02:52:39 UTC
eaf3d84 Using a visitor to systematically replace lookup scopes of generic function's return type expression. fixes #336 Add a `ReplaceScopeVisitor` to replace the scopes of the return type expression tree to use the generic decl's scope instead of module's scope after parser has determined the decl is a generic function header decl. 28 December 2017, 02:51:56 UTC
e370fe2 Merge pull request #335 from csyonghe/master Support nested generic types (e.g. L<T<S>>) 28 December 2017, 01:17:36 UTC
d55b56b fix test cases 28 December 2017, 00:19:09 UTC
3379f7a fix project file. 28 December 2017, 00:02:22 UTC
87cba14 Support nested generic types (e.g. L<T<S>>) fixes #325 This commit includes following changes: 1. Including a default DeclaredSubtypeWitness argument when creating a default GenericSubstitution for a DeclRefType, so that the witness argument can be successfully replaced with an actual witness table after specialization. (check,cpp) 2. Not emitting full mangled name for struct field members. Since the declref of the member access instruction do not include necessary generic substitutions for its parent generic parameters, so the mangled names of the declaration site and use site mismatches. Instead we just emit the original name for struct fields. (emit.cpp) 3. Allow IRWitnessTable to represent a generic witness table for generic structs. Adds necessary fields to IRWitnessTable for generic specialization. For now, the user field of the IRUse is not used and is nullptr. (ir-inst.h) 4. Make IRProxyVal use an IRUse instead of an IRValue*, so that an IRValue referenced by IRProxyVal (as a substitution argument) can be managed by the def-use chain for easy replacement. This is used for specializing witness tables. (ir.cpp, ir.h) 5. Add a `String dumpIRFunc(IRFunc*)` function for debugging. 6. Add name mangling for generic / specialized witness tables (mangle.cpp) 7. improved natvis file for inspecting witness tables. 8. Add specialization of witness tables: 1) `findWitnessTable` will simply return the specialize IRInst for a generic witness table. 2) make `cloneSubstitutionArg` call `cloneValue` to clone the argument instead of calling `context->maybeCloneValue`, so we can make use of the cloned value lookup machanism to directly return the specialized witness table (which is done when we process the `specialize` instruction on the generic witness table before process the decl ref). 3) bug fix: the argument in ir.cpp:3338 should be `newArg` instead of `arg`. 4) add `specializeWitnessTable` function to specailize a generic witness table. It clones the witness table, and recursively calls `getSpecailizedFunc` for the witness table entries. 5) make `specailizeGenerics` function also handle the case when an operand of the `specialize` instruction is a witness table. We will call `specializeWitnessTable` here and replace the `specialize` instruction with the specialized witness table. The replacement mechanism based on IR def-use chain works here because we have already make IRProxyVal a part of the def-use chain. 9. Add two more test cases for nested generics with constraints. (generic-list.slang and generic-struct-with-constraint.slang) 27 December 2017, 23:50:32 UTC
6924239 Merge pull request #330 from tfoleyNV/implicit-generic-app Support generic type constraints when implicitly invoking generic 27 December 2017, 02:04:53 UTC
fd0b8ca fix ast-lowering of `TransitiveSubtypeWitness` values. 27 December 2017, 01:20:54 UTC
4b53fd5 Merge branch 'master' into implicit-generic-app 22 December 2017, 20:09:39 UTC
d1c3852 Support for transitive subtype witnesses (#331) * Change stdlib `saturate` to explicitly specialize `clamp` This exposes issue #329, and so gives us an easy way to see if transitive subtype witnesses have been implemented correctly. * Fixup: invoke correct `clamp` overloads When switching the `clamp` calls in the stdlib definition of `saturate` I made two big mistakes: 1. I was passing in `<T>` in all cases, instead of, e.g., `<vector<T,N>>` in the vector case 2. Of course, the overloads don't actually take `<vector<T,N>>` for the vector case, because `vector<T,N>` is not a `__BuiltinArithmeticType` (`T` is), so instead it should be `clamp<T,N>(...)`. The issue behind (2) is that we don't support "conditional conformances," which would be a way to say that when `T : __BuiltinArithmeticType` then `vector<T,N> : __BuiltinArithmeticType`. That would be a great long-term wish-list feature, but not something I can see us adding in a hurry. Anyway the fix here is the simple one: change the vector/matrix call sites to invoke the correct overload in each case. * Add a notion of transitive subtype witnesses There are two pieces here: 1. Add the `TransitiveSubtypeWitness` class. This is a witness that `A : C` that works by storing nested subtype witnesses that show that `A : B` and `B : C` for some intermediate type `B`. All the basic `Val` operations are easy enough to define on this. - The one gotcha case is whether we can ever simplify away a `TransitiveSubtypeWitness` as part of substitution. That is, if we end up substituting so that both `A` and `B` end up as the same type, then we really just need the `B : C` sub-part. Stuff like that is left as future work. 2. Make the logic in `check.cpp` that constructs subtype witnesses based on found inheritance and constraint declarations able to build up transitive chains. Most of the required infrastructure was already there (the search process maintains a trail of "breadcrumbs" that represent all the steps getting from `A : B` to `B : C` to `C : D` ...). This change does *not* deal with the required changes in the IR to take advantage of transitive witnesses. 22 December 2017, 20:09:26 UTC
7ebc987 Support generic type constraints when implicitly invoking generic Fixes #326 This basically just copy-pastes logic from the explicit case over to the implicit case. After we've solved for the explicit type/value arguments, we loop over the constraints and for each one we try to find a suitable subtype witness to use (after substituting in the arguments solved so far). This change includes a test case for the new functionality. 22 December 2017, 18:50:32 UTC
fab52a1 Merge pull request #324 from tfoleyNV/generic-struct-specialization Support generic `struct` types during IR-based emit 22 December 2017, 00:50:22 UTC
0049015 Support generic `struct` types during IR-based emit Fixes #318 Most of the required support was actually in place, so this is just a bunch of fixes: - Detect when we are in "full IR" mode, so that we can always emit `struct` declarations with their mangled named (which will produce different names for different specializations, since we emit decl-refs) - Carefully exclude builtin types from this for now. We'll need a more complete solution for mapping HLSL/Slang builtin types to their GLSL equivalents soon. - Skip emitting types referenced by generic IR functions, since they might not be usable. - Also fix things up so that we emit types used in the initializer for any global variables. - Fix bug in generic specialization where we specialize the same function more than once, with different type arguments. We were crashing on a `Dictionary::Add` call where the key already exists from a previous specialization attempt. - Fix name-mangling logic so that when outputting a possibly-specialized generic it looks for the outer-most `GenericSubstitution` rather than just the first one in the list. This is to handle the way that we insert other substitutions willy-nilly in places where they realistically don't belong. :( All of these changes together allow us to pass a slightly modified (more advanced) version of the test case posted to #318. 22 December 2017, 00:16:23 UTC
6f68127 IR: fixes for subscript accessors (#322) * IR: fixes for subscript accessors Fixes #320 This is a bunch of fixes for handling of `__subscript` operations on builtin types (notably `RWStructuredBuffer` and `StructuredBuffer` at this point). - Automatically add a `GetterDecl` to any subscript decalratio was declithout any accessors. This avoids hitting a null- dereference in the emit logic. - Add a notion of a `RefAccessor` (declared with `ref`) as a peer to getters and setters. The idea is that a `ref` accessor returns a pointer to the element data, so that it can be used for both getting and setting values. This is closer to the behavior of `RWStructuredBuffer` element access in HLSL. - Fixes for dealing with "access chains" where there might be a combination of a subscript (where the is a `get` and `set` but no `ref`) and member access, so that we have to read the base value into a temp, modify it, and then write it back. - This logic is still a bit of a mess, so we will eventually want to take a more consistent pass over this to deal with how we "materialize" values for setters. - Update `RWStructuredBuffer` to have a `ref` accessor, and then fix up the IR tests to handle the new opcode that I added for it. - Note: I didn't handle this as an intrinsic simply because the `tests/ir/*` tests aren't really set up to handle builtins with ugly mangled names. * Fixup: type error in VM for buffer element ref I was using the result type of the op as the element type for computing the element address, but the result type is a pointer to the real element type. This caused test failures on 64-bit platforms, where the stride of the buffer in the `ir/factorial` test needs to be 4. The fix is to assume the result type is a pointer, and extract the pointed-to type out of that. 21 December 2017, 01:35:10 UTC
35318fb More fixes for Falcor IR support (#317) * Fix: try to improve float literal formatting An earlier change switched to using the C++ stdlib to format floats, but I neglected to check that it would correctly print values that happen to be integral with a decimal place (e.g., print `1.0` instead of just `1`). That causes some obscure cases to fail (e.g., when you write `1.0.xxxx` in HLSL, and we print out `1.xxxx`). I've tried to resolve the issue by using the "fixed" output format, but that may also create problems for extremely large or small values (which really need to use scientific notation). However, this at least puts us back where we were before... * Add support for source locations to the IR - Add a `sourceLocation` field to all `IRValue`s - Add some logic to associate locations with operations while lowering (using a slightly "clever" RAII approach) - Make sure that when cloning instructions, we also clone the location - Make the emit logic use the existing support for source locations when emitting code from the IR A nice cleanup to this work would be to have the source locations for IR values not be hyper-specific about both line and column; it is probably enough to just emit on the correct line. 20 December 2017, 22:06:59 UTC
8e0e6b6 Support simple generics syntax (#319) * Support simple generics syntax. This commit enables simpler generics syntax, e.g. T test<T>(T arg) {} or struct Gen<T>{T x;}; * Support simple generics syntax. This commit enables simpler generics syntax, e.g. T test<T>(T arg) {} or struct Gen<T>{T x;}; * add expected test result for compute/generics-syntax.slang 20 December 2017, 16:01:36 UTC
563fc0c Fix up parameter block emit for mixed rewriter+IR (#316) * Fix up parameter block emit for mixed rewriter+IR The basic problem here arose when a parameter block was declared in code that will be lowered via IR, but is referenced from user code that will use the AST-based lowering pass. The type legalization would split up the parameter block, and the AST would refer to the new variables, but it would fail to recognize when those variables represent constant buffers, and thus should get implicit-dereference behavior. The fix here was just to propagate type information through when creating new AST (pseudo-)expressions to represent IR declarations that were split. A more complete fix down the road should try to make sure that both the expression emit logic and the declaration-emit logic agree on whether a particular declaration of a parameter block or constant buffer needs to support the "implicit dereference" case or not. I'm leaving that to future work. With this change, two test cases that had been disabled now pass. * Fixup: don't do implicit deref for `ParameterBlock` values Right now the rules for `ParameterBlock` and all other uniform praameter groups needs to be different, but the `isImplicitBaseExpr` logic wasn't taking that into account. 19 December 2017, 23:17:23 UTC
acf2625 Fix floating-point literal emit to be locale-independent. (#315) There was a bug that arose in the context of Falcor, because: - Slang uses `sprintf` to format floating-point values when outputting HLSL/GLSL source - Falcor (or a library it uses) does the equivalent of `setlocale(LC_ALL, "")` to set the global locale for the process based on the user's environment variables This led to a floating-point literal of `0.5` getting printed as `0,5f`, with a comma for the decimal point. This then gets consumed by `glslang` which (luckily for us) complains that `5f` is not a valid floating-point literal in their language (since it has neither decimal point nor exponent). The most expedient fix in this case was to switch from using C `sprintf` for formatting floating-point numbers over to using the C++ `<stdio>` implementation, which allows the locale to be set per-stream so that we don't have to rely on (or potentially disrupt) the global locale set by an application using Slang. Longer term if we have the time/resources to do the Right Thing, it would be best to implement our own printing logic for floating-point numbers, since we would eventually need/want to support arbitrary-precision numbers for literals. 19 December 2017, 19:49:36 UTC
393e25f Work on getting rewriter + IR playing nice together. (#314) * Work on getting rewriter + IR playing nice together. There are a few different changes here, with the goal of improving the interaction between the "rewriter" code generation approach and the new IR and type legalization code. The main changes are: - Add a new pass that occurs before the AST legalization pass, which walks the (used) AST declarations and tries to discover (1) which declarations need to be specialized/lowered via the IR, and (2) which declarations need to be included in the resulting AST module. - AST-based legalization now uses the generated list when in "rewriter" mode, so that we should be working around issues that users were seeing with types not getting emitted. - TODO: we still need an equivalent fixup in the case of non-"rewriter" emit, so this may still be a problem for `.slang` files. - IR type legalization now precedes AST legalization, so that we can record information on how any IR global values got legalized (e.g., if they got split). Then AST legalization includes logic to reconstruct suitable tuple expressions to reference a split global. - When emitting using IR + AST, we walk all of the declarations that we decided belonged to the IR, but which were subsequently referenced in the AST, to make sure they get output (this would include `struct` types that are declared in a file compiled via IR, but never used in IR-based code). The rewriter+IR use case still doesn't *quite* work, but the logic for walking the AST in a pre-pass ends up being needed/useful to fix some pure rewriter bugs, so I'm getting this checked in sooner rather than later. * Fixup: walk arguments to generic declaration reference The gotcha here is that the code for walking the AST would walk a line of code like: SomeType a; and know to traverse the declaration of `SomeType`, but if it saw a line of code like: ParameterBlock<SomeType> b; it would traverse the declaration of `ParameterBlock`, but fail to visit that of `SomeType`. 18 December 2017, 23:14:59 UTC
46b68ed Add sample-rate-input detection for HLSL. (#312) * Add sample-rate-input detection for HLSL. In the HLSL case, it is possible to do this detection entirely based on declared signatures (it doesn't have a dependency on code generation like the GLSL case does). I've added test cases for the two main ways that a shader can become sample rate: 1. Qualify a fragment input with `sample` 2. Accept an input with the `SV_SampleIndex` semantic In each case I nested the input inside a `struct` to try to match common HLSL idiom, and to make sure that we handle the nested case. This code is *not* robust against shaders that declare such an input and then never use it, but that is to be expected given the goals for Slang. * Fixup: add missing test output files 15 December 2017, 22:26:58 UTC
81c0cd8 More fixups for parameter block binding generation (#311) * More fixups for parameter block binding generation The bug in this case arises when there is both a parameter block and global-scope resources, all of which are relying on automatic binding assignment. If the parameter block is the first global-scope parameter that gets encountered, then it is possible for it to allocate regsiter space/set zero for itself, which confuses the logic for handling other global-scope parameters (which assumes that *they* get space/set zero). I've also made some fixup to the reflection test harness and reflection API code: - Have the hardness handle register-space allocations when printing, and be sure to only show their `index` and not their `space` (since that would be redundant) - Have the reflection API only auto-redirect queries on a parameter group type layout to its container type layout *if* the container type layout has a non-zero number of resource allocations. The problem that arises here is a `ParameterBlock<X>` where `X` doesn't contain any uniforms, so that no container is needed. In that case the container ends up with no resource allocation(s). * Fixups for test failures. - The thread-group size tests failed because they had shader parameters with no resources to back them (built-in `SV_` inputs), and the printing of those changed. I fixed up the baseline, but also had to fix a few bugs in the reflection test fixture's printing logic. - The GLSL parameter block test revealed a corner case of the existing logic: because we always need to generate a binding for the "hack" sampler (even if code doesn't end up needing it), and that sampler should always go in the "default" set (should be set zero), the user's `ParameterBlock` will always end up as `set=1` or later, even if there are no other global-scope parameters. - This will be fixed once we don't have to rely on glslang's annoying behavior in this one case, either because glslang gets fixed, or because we implement our own SPIR-V codegen. 15 December 2017, 19:57:53 UTC
4137f9d More fixups for Vulkan parameter block bindings (#309) I'm adding a small cross-compilation test to try to make sure that we are testing the binding generation for GLSL output. We probably still need a more complex test that uses multiple blocks, plus variables not in a block. The big changes here are: - Change the `containerTypeLayout` field to a `containerVarLayout` in the `ParameterGroupTypeLayout`, so that we can store the base offsets for the fields in a uniform fashion (even though these will all be zero). - Switch the emit logic to carefully use either the container or element var layout depending on what they are emitting bindings for. This involved adding something akin to the "reflection path" notion that Falcor has to use, but only for the emit step. 14 December 2017, 19:51:29 UTC
6d61421 Fix parameter block binding for Vulkan (#308) Fixes #307 This ends up being a major overhaul over how type layout computation is structured and exposed. The big problems all arise around cases where both the "container" for a parameter block or CB, and the "element" type both use the same kind of resource. E.g., if you define a CB with a texture in it, then in Vulkan both the CB and the texture use the same kind of resource, and so if you query the CB's resource usage it will just tell you it uses two descriptor-table slots, but nothing more than that. Similar confusion still arises in the HLSL case, when a CB with a texture in it reports its parameter category as "mixed" so that a user might query for a category they didn't mean to. There were also cases in the existing code where a parameter block might expose *both* a register-space usage and another concrete resource type, which isn't right. The most important changes here are: - A `ParameterGroupTypeLayout` now has a more refined internal structure, consisting of: - A `containerTypeLayout`, which represents the resource usage of the buffer/block itself (e.g., if a constant buffer had to be allocated) - An `elementVarLayout` which stores the offsets that need to be applied to get from the `VarLayout` for an instance of this parameter-group type to the offsets of its elements. The `TypeLayout` for this variable layout should be the "raw" type of the block/CB element. - The `offsetElementTypeLayout` (formerly just `elementTypeLayout`) which represents the element type, but in the case of a `struct` element type, will have fields offset similar to the `elementVarLayout`. This is what all the old code used to use, so we need to keep it for compatibility. - When doing reflection on a `ParameterGroupTypeLayout`, we now only report the resource usage of the `containerTypeLayout`. This is technically a potentially breaking change in the public API, but I don't think Falcor will mind, since they actually want something closer to this behavior. - Add a new public API for querying the element variable layout of a parameter block of constant buffer. This could be used by savvy applications to fold the handling of CB element offsetting into some notion of a "reflection path." This would be required for applications that want to handle CBs or parameter blocks where the element type is *not* a `struct` type. - Remove old logic for applying an offset when creating a type layout for constant buffer element, and instead perform offsetting more uniformly later, by constructing the `offsetElementTypeLayout` from the `rawElementTypeLayout`. This is useful both because we want to keep both (the "raw" type layout becomes the type layout of the `elementVarLayout`), and also because we can decide later whether we even want to allocate a CB register for a buffer, based on whether it actually contains any uniform data. - Fix cases where we might end up with a parameter block type reporting both that it uses a whole register space (and thus should not expose the resource usage of the container/element type) *and* a constant-buffer register/slot. The latter should be hidden inside the regsiter space. - Clean up the `spReflectionParameter_GetBinding{Index,Space}` functions to just route to `spReflectionVariableLayout_Get{Offset,Space}`, using the "default" category of the parameter - Try to make the `GetSpace` query take into account cases where a variable also has an explicit `RegisterSpace` allocation. - This probably still needs some cleanup, since ideally we'd just move things into the `space` field on the `ReosurceInfo` and have an invariant that a variable *either* has a `RegisterSpace` allocation, or it has other resource infos, but never both... - Add some ad-hoc logic so that if the user queries for a binding index/space using a parameter category that doesn't actually apply (e.g., they query for a D3D `t` register when using Vulkan), we can optionally remap it to the resource type they "probably" meant. This is a mess of Do What I Mean code, but it is also what our users want right now. - Fix various bits of emit logic so that if a parameter block has a register space/set allocated to it, we properly output that as part of the binding information for it. - This is another thing that might be cleaned up if we rationale the way that things get split during legalization. - Add a GLSL case for emitting a parameter block variable as a `cbuffer`. 13 December 2017, 23:48:29 UTC
0f55649 Cleanups to `ParameterBlock<T>` behavior. (#304) * Cleanups to `ParameterBlock<T>` behavior. These add some more realistic tests using the `ParameterBlock<T>` support, and show that it can work with the "rewriter" mode. Unfortunately, this code does *not* currently work with the rewriter + the IR at once. That will need to be fixed in a follow-on change, because I now see that the root problem is pretty ugly. * cleanup 08 December 2017, 22:23:12 UTC
301cdf5 Make AST and IR share type legalization code (#303) * Make AST and IR share type legalization code A previous change already made it so that the AST-to-AST lowering/legalization pass could work together with IR-based lowering of `import`ed code, but that change didn't take into account the case where a function written in the AST needed to call an IR function and pass in a type that required legalization. Both the IR-based and AST-based passes had their own approaches to type legalization, that mostly agreed on the desired output, but they ended up creating their own representations for legalized types which would mean that for a function call the caller and callee might end up legalizing the parameter list to use different types. This change tries to fix this issue (and adds a new test case that relies on the fix) by massively overhauling the AST-based legalization pass so that it uses the same type legalization code as the IR. The shared code has been moved out into `legalize-types.{h,cpp}`. Notes: - I eliminated the `FilteredTupleType` type, since it was starting to cause code duplication in a lot of places. Instead, type legalization just creates new `struct` types to represent the result of filtering. - One big consequence of this is that the `LegalType::pair` case needs to remember for each field in the original type which field (if any) in the new `struct` type it maps to - A big source of complexity (and probably bugs) in this code is trying to figure out how to parent these new `struct` definitions effectively. A good follow-on change would be something that outputs declarations on-demand during the AST emit logic (as we do for the IR), just to avoid some of this song and dance. - The old AST type legalization had a notion of both a "tuple" type and a "varying tuple" type. The "tuple" case was quite complex, and combined behavior currently handled by `LegalType::pair` (for splitting into ordinary and special sides) and `LegalType::tuple` (for holding multiple distinct elements to represent the fields of an aggregate). The "varying tuple" case was closer to `LegalType::tuple`, so I tried to just re-use the existing logic for that too. The one place this potentially gets messy is in `reifyTuple()`. - The messiest bit of handling the "varying tuple" concept (which is used for GLSL shader inputs/outputs since they have to be scalarized) is that when passing them as function arguments we need to reify the tuple back into a structured value. Because the `LegalExpr` hierarchy doesn't have type information, but constructing a value of the "original" type requires such information, things get a little messy. - I did *not* try to deal with any of the logic related to handling system inputs/outputs for cross-compilation purposes. Of course, the long-term goal is that any actual cross-compilation is handled via the IR, but this change can't afford to break the AST-based path just yet. As a result, there is still quite a bit of complexity in the handling of assignment, to deal with cases where "fixups" are required. * fixup: bad code in macro, not caught by Visual Studio compiler * fixup: more stuff missed by VS compiler * fixup: VS continutes to miss stuff in UNREACHABLE_RETURN 06 December 2017, 21:55:31 UTC
b487516 Add API to query stage of varying parameter (#302) Fixes #301 The problem here is that if you have input GLSL code like: ```glsl // example.vs in vec3 pos; ``` and: ```glsl // example.fs in vec3 worldPos; ``` Then both `pos` and `worldPos` are reflected as global variables (parameters of the *program*), which both get bound to "varying input" resources, but there is no way to tell through the API that `pos` is a vertex parameter while `worldPos` is a fragment one. The original request in issue #301 was to expose parameters like this not as a global variables, but rather as parameters of the entry point in their specific file. That is, treat it as if the user had written, e.g.: ```glsl // example.vs void vsMain(in vec3 pos) { ... } ``` Doing that would unify the GLSL and HLSL/Slang cases a bit, but would require the Slang reflection API to lie about the structure of code the user wrote. At a more basic level, that would have been hard to implement because the current reflection API just exposes the underlying AST, and the AST *needs* to leave `pos` at the global scope so that when we go and spit GLSL back out we retain the original structure. This PR implements a more simplistic solution, where the user is allowed to query the stage that a varying parameter "belongs" to. For right now I'm only enabling this to work for varying parameters (but it doesn't care if they are entry-point or global-scope varyings). Despite what I said on #301, this should work for both the top-level parameter's variable layout, *and* any variable layouts for fields within its type reflection. In terms of implementation, I took the simple but wasteful route: every `VarLayout` now has a `stage` field that is by default initialized to `SLANG_STAGE_NONE`. When collecting varying parameters, I take advantage of the fact that everything bottlenecks through `processEntryPointParameter()` which takes an `EntryPointParameterState` so that I can set the `VarLayout::stage` field for any varying parameter in one place. While I was making this change, I also did a bit of cleanup so that the "official" names for the varying parameter categories are `VARYING_INPUT` and `VARYING_OUTPUT`, with `VERTEX_INPUT` and `FRAGMENT_OUTPUT` being "deprecated" in principle. I didn't do the bulk rename inside the codebase yet. 29 November 2017, 19:45:15 UTC
7139380 Enable HLSL/GLSL "rewrite" + IR-based Slang codegen (#300) The big picture here is that the AST-to-AST pass in `ast-legalize` will now detect when a declaration being referenced comes from an `import`ed module, and (if IR codegen is enabled), it will trigger cloning of the IR for the chosen symbol into an IR module that will sit alongside the legalized AST. Then, during HLSL/GLSL code emit, we emit all the IR-based code first, and then the AST-based code. Whenever the AST code references a symbol that was lowered via IR (we keep track of these) we emit the mangled name of the IR symbol. Notes/details: - A lot of the logic for cloning IR symbols referenced by the AST matches the same logic that would clone them for completely IR-based codegen, so I tried to hoist out the common logic and share it (e.g., so that we apply the same guaranteed transformations in both cases). This required basically rewriting the logic in `emit.cpp` that decomposed the various cases. - There is a new compute test case added to test this functionality. `tests/compute/rewriter.hlsl` confirms that we can use the `-no-checking` mode for the HLSL code, but still make use of a library of Slang code that employs generics, etc. - Adding this test case required adding a new compute test mode that invokes `render-test` with the `-hlsl-rewrite` flag. - It turns out that the existing `tests/render/cross-compile0.hlsl` test should have been using this functionality already. It was opting into the use of the IR via `-use-ir`, and the `render-test` application already tries to set `-no-checking` for non-Slang input languages by default. Fixing the code path this test triggers means that it is now a second test of rewriter+IR codegen. - The `translateDeclRef` logic in `ast-legalize.cpp` seemed sloppy in places, and would potentially clone declarations, when declaration references were desired. I tried to clean a bit of this up, so some call sites are now changed. - This change tries to clean up some work around cloning of global values - All global value kinds (not just functions) now go through the logic of trying to pick a "best" definition, so that they can be used when we are linking multiple modules - The logic for registering cloned values has been unified a bit, so that clients always pass in an `IROriginalValuesForClone` that either wraps a single value (maybe just null), or an `IRSpecSymbol*` that gives a list of values to regsiter the new value as a clone for. - I made one piece of code that was cloning witness tables as part of generic specializations *not* register a clone. I think this is correct because we may specialize the same generic multiple ways, so registering any values we clone is not the right idea, but I might be missing something... - I also reorganized this logic so that it would be easier to clone a global value when we only know its mangled name (which is the case when it is the AST that triggers cloning) - I made sure that when loading a module via `import`, the translation unit for the new module copies the `-use-ir` flag from the overall compile request, if it is present (otherwise we wouldn't generate IR for loaded modules at all... oops). - Note that `getSpecializedGlobalValueForDeclRef()`, which is the main routine used by the AST legalization to trigger cloning of an IR value does *not* currently handle declaration references that require specialization. - This change does *not* deal with trying to unify the type legalization logic between the AST-to-AST rewriter and the IR-based codegen, so if you call an imported function with types that require legalization, Bad Things are expected to happen right now. 29 November 2017, 03:49:21 UTC
4951003 Generate IR per-module for loaded modules (#299) The basic idea here is that for each module that gets loaded via `import`, we should also generate the initial IR for the declarations in that module at the time it gets loaded. Furthermore, when we generate initial IR for a module, we will only generate IR *declarations* (not *definitions*) for any functions/variables in modules it imports. Later, when cloning IR to begin code generation for an entry point, we will effectively "link" all of the loadedm modules together, so that a given global value can get its definition from any of the IR modules present. - Change the `loadedModulesList` and related data structures to hold a new `LoadedModule` type, instead of just the AST (and then have a `LoadedModule` own both the AST and the IR module) - Share some logic between the `import` and `#import` cases, so that we always try to generate IR for modules we load. - Make sure that IR generation always gets skipped if the command-line flags tell us not to use the IR. - A few small fixups for cases that didn't arise in IR lowering so far, but come up when we try to actually generate IR for things like the stdlib. There are some notable gaps in this work right now: - The stdlib modules are exempted from this behavior; we always generate IR for stdlib functions in any user module that calls them. This is just a workaround for the fact that the stdlib modules don't show up in the list of imported modules right now. - We don't currently have logic that does the "linking" step for global variables like we do for functions. We really need to look up the symbols with the same mangled name, and favor any one of them that has a definition (if there is one) - Similarly, the handling of witness tables is incomplete. During initial IR generation, we should probably be generating empty witness tables for any conformances that were declared in other modules (but are being used locally in this module), and then the "linking" step should favor non-empty witness tables over empty ones. Still, all the test cases pass with the code like this, and this seems like an important step in the right direction. 28 November 2017, 14:42:13 UTC
3199385 Cleanups (#298) * Rename `lower.{h,cpp}` to `ast-legalize.{h,cpp}` This pass isn't really performing lowering akin to `lower-to-ir.{h,cpp}` so the file name is misleading. By renaming this pass we emphasize its role as an AST-related pass. Also update the comment at the top of `ast-legalize.h` to reflect the intended purpose of this pass in a world where we have the IR up and running. * Allow `import` as an alias for `__import` The use of double underscores to mark our new syntax has so far had two purposes: 1. It helps identify syntax that isn't meant to be exposed to users in its current form (e.g., `__generic` gets a double underscore because we want users to have a more pleasant surface syntax for generics that they write). This rationale doesn't apply to `__import`, which is a major language feature that users need to interact with. 2. It helps avoid the problem where the compiler treats something as a keyword that isn't supposed to be reserved in HLSL/GLSL and so causes existing user code to fail to parse (e.g., when the user tries to write a function called `import`). This no longer matters because we look up almost all of our keywords using the existing lexical scoping in the language (so the user can shadow almost any keyword with a local declaration). So, neither of the original two reasons applies to `__import`, and it makes sense to expose it as `import`. Doing so is a one-line change. 28 November 2017, 00:33:28 UTC
109ee8a Fix substitution mechanism to remove special cases for global params (#297) Add a new function: `substituteSubstitutions(Substitutions * substHead, Substitutions subst, int * ioDiff)` This function substitutes the type arguments referenced in a linked list of substitutions headed at `substHead` using the substitutions specified by `subst`. If the linked list `substHead` does not contain `GlobalGenericParamSubstitution` entries, they will be added to the bottom (outter most) of the linked list. Note that this function should be called when `substHead` is known to be the head of substitution linked list because the existance of `GlobalGenericPaaramSubstitution` is detected assuming the linked lists starts at `substHead`. If a substitution that is not the head of a substitution linked list is passed in, duplicate `GlobalGenericParamSubstitution`s could be appended to the linked list. This means that this function should *not* be called in places like `GenericSubstitution::SubstitutionImpl()` for its outer substitutions, because `outer` is obviously not the head of the linked list. Instead, use this function to substitution the substitution lists of `DeclRef` etc. instead of calling `declRef.substitutions->SubstituteImpl()` where the head to the linked list is known as a member of that class. With this function, IRSpecContext::maybeCloneType() is simplified down to `originalType->Substitute(subst)` Updates `DeclRefBase::SubstituteImpl` and `DeclRefType::SubstituteImpl` to call `substituteSubstitutions` instead of making direct `substitutions->SubstituteImpl` call. Providing actual implementation of `GlobalGenericParamSubstitution::SubstituteImpl` instead of just returning `this` to deal with potential situations where a true substitution is needed. 24 November 2017, 16:55:54 UTC
83d49ce Merge pull request #293 from csyonghe/generic-param-fix Fixup global generic parameters 22 November 2017, 22:32:15 UTC
581b30d Merge branch 'master' into generic-param-fix 22 November 2017, 21:58:16 UTC
56e49fe Fix emitting of loop attributes for HLSL pass-through (#296) Fixes #295. The code previously had a white list of attributes that it passed through, implemented in `emit.cpp` in an ad hoc fashion. The fix here is to just pass through whatever attributes the user wrote, and then let the downstream compiler diagnose if any of them are errorneous. 22 November 2017, 19:28:29 UTC
cd2d646 Merge branch 'master' into generic-param-fix 22 November 2017, 00:55:23 UTC
43434bf update input layout definition of test case `global-type-param-in-entrypoint` 22 November 2017, 00:26:56 UTC
64108c8 Add logic to propagate GlobalGenericParamSubstitution 22 November 2017, 00:26:17 UTC
37315c9 IR: support global variable with initializers (#294) The big change here is that the ability to contain basic blocks with instructions in them has been hoisted from `IRFunc` into a new base type `IRGlobalValueWithCode` shared with `IRGlobalVar`. The basic blocks of a global variable define initialization logic for it; they can be looked at like a function that returns the initial value. Places in the IR that used to assume functions contain all the code need to be updated, but so far I only handled the cloning step. The emit logic currently handles an initializer for a global variable by outputting its logic as a separate function, and then having the variable call that function to initialize itself. This should be cleaned up over time so that we generate an ordinary expression whenever possible. I also made the emit logic correctly label any global variable without a layout (that is, any that don't represent a shader parameter) as `static` so that the downstream HLSL compiler sees them as variables rather than parameters. 20 November 2017, 21:45:10 UTC
ab49ac3 fix warnings 20 November 2017, 10:49:57 UTC
d6130ba fixup global generic parameters 1. simplify RoundUpToAlignment() 2. add new a render-compute test case to cover the situation where the entry-point interface (parameter/return types of an entry-point function) is dependent on the global generic type. 3. initial fixes to get this test case to compile (but is not producing correct HLSL output yet) 20 November 2017, 10:28:22 UTC
3dff5a5 IR: add lowering for initializer list expressions (#290) * IR: add lowering for initializer list expressions This is relatively straightforward in the easy cases, because the front-end will have already type-checked the elements of the initializer list, and attached an appropriate type to the overall expression. Notes: - We are assuming in this code that if the user provides a "flattened" initializer list when dealing with nested aggregates, then the front-end is responsible for grouping things up apprporiately (this is not actually implemented in the front-end today). - I have only handled arrays and `struct` types here, so uses of initializer lists for anything else will fail. - I have not tried to handle the common HLSL idiom of using `{0}` as a way to default-initialize things, even when their first field is not compatible with the expression `0` - I have not implemented support for default-initializing fields/elements beyond those for which explicit initializers were provided. This can be addressed as a follow-on change. This change is one clear place where the front-end lowering logic could potentially be made much cleaner using a "destination-driven" code generation strategy. For example, given the following code ```hlsl struct A { int a0; a1; }; struct B { A b0; A b1; }; struct C { B c0; B c1; }; // ... C c = { { { 0, 1 }, {2, 3}, }, /* ... */ }; ``` Our current code generator will end up allocating local variables for 1 instance of `C`, two instances of `B`, and four instances of `C`, for over 3x the allocation that would be done by a good destination-driven code generator. Yes, later optimization passes should be able to clean up the waste, but avoiding the waste from the start should result in faster compiles and also easier debugging (since intermediate IR won't be as messy in general). * Fixup: try to appease clang compiler 18 November 2017, 03:38:26 UTC
ba594d0 IR: Add support for `out` and `inout` parameters (#289) These were already being handled a little bit, by lowering an `out T` or `inout T` function parameter in the AST to a function parameter with type `T*` in the IR, and then emiting explicit loads/stores. The HLSL emit logic, however, couldn't tell the difference between an `out` parameter, an `inout`, or a true pointer (if we ever needed to support them...). The intention (not fully implemented) was that we'd use a hierarchy of types rooted at `PtrTypeBase`: - `PtrTypeBase` - `Ptr`: "real" pointers in the C/C++ sense - `OutTypeBase`: pointers used to represent by-reference parameter passing - `OutType`: IR level type for an `out` parameter - `InOutType`: IR level type for an `inout` or `in out` parameter Actually implementing this involved: - Adding a bit more flexibility to the `Session::getPtrType` logic to allow for creating any of the concrete types above - Making the `lower-to-ir` logic create the right type for function parameters (instead of just using `PtrType`) - Making the HLSL emit logic check for the `OutType` and `InOutType` cases rather than just `PtrType` - Changing a bunch of small places in the code so that they use `PtrTypeBase` instead of `PtrType` when they should handle any of the above cases, and also make a few places check for `OutTypeBase` instead of `PtrType` or `PtrTypeBase`, when they are really trying to capture by-reference parameters - Add a test case that uses all of the different cases we care about (without these fixes, this test case generates errors from fxc because of variables being used before being initialized, becaues parameters get declared `out` that should be `inout`). A minor point here is that we are playing a bit fast and loose right now because the IR does not actually enforce any type checks. From the standpoint of the front end, `Ptr<T>`, `Out<T>`, and `InOut<T>` are all unrelated types (each is just a `struct` declared in `core.meta.slang`), but this doesn't really matter because none of these are types our current users are explicitly using. In the IR it makes perfect sense to allow `Out<T>` or `InOut<T>` as the operand of a `load` or `store` instruction (and ditto for `getFieldAddr`, etc.) - there instructions just apply to any `PtrTypeBase`. The place where this potentially gets tricky is whether an `Out<T>` can be used where a `Ptr<T>` is expected, or vice vers (e.g., can I just pass my local variable's pointer directly to an `Out<T>` function parameter? I'm going to ignore these issues for now, since the code currently works for our test case. 18 November 2017, 03:01:58 UTC
54bf54b Add support for global generic parameters (#285) * Add support for global generic parameters (In-progress work) This commit include: 1. Update Slang API to allow specification of generic type arguments in an `EntryPointRequest` 2. Add parsing of `__generic_param` construct, which becomes a GlobalGenericParamDecl, contains members of `GenericTypeConstraintDecl`. 3. Semantics checking will check whether the provided type arguments conform to the interfaces as defined by the generic parameter, and store SubtypeWitness values in the EntryPointRequest, which will be used by `specializeIRForEntryPoint` when generating final IR. 4. Add a new type of substitution - `GlobalGenericParamSubstitution` for subsittuting references to `__generic_param` decls or to its member `GenericTypeConsraintDecl` with the actual type argument or witness tables. 5. Update `IRSpecContext` to apply `GlobalGenericParamSubstitution` when specializing the IR for an EntryPointRequest. 6. Update `render-test` to take additional `type` inputs, which specifies the type arguments to substitute into the global `__generic_param` types. This commit does not include ProgramLayout specialization. * IR: pass through `[unroll]` attribute (#284) The initial lowering was adding an `IRLoopControlDecoration` to the instruction at the head of a loop, but this was getting dropped when the IR gets cloned for a particular entry point. The fix was simply to add a case for loop-control decorations to `cloneDecoration`. * fix warnings * IR: support `CompileTimeForStmt` (#286) This statement type is a bit of a hack, to support loops that *must* be unrolled. The AST-to-AST pass handles them by cloning the AST for the loop body N times, and it was easy enough to do the same thing for the IR: emit the instructions for the body N times. The only thing that requires a bit of care is that now we might see the same variable declarations multiple times, so we need to play it safe and overwrite existing entries in our map from declarations to their IR values. Of course a better answer long-term would be to do the actual unrolling in the IR. This is especially true because we might some day want to support compile-time/must-unroll loops in functions, where the loop counter comes in as a parameter (but must still be compile-time-constant at every call site). * Add support for global generic parameters (In-progress work) This commit include: 1. Update Slang API to allow specification of generic type arguments in an `EntryPointRequest` 2. Add parsing of `__generic_param` construct, which becomes a GlobalGenericParamDecl, contains members of `GenericTypeConstraintDecl`. 3. Semantics checking will check whether the provided type arguments conform to the interfaces as defined by the generic parameter, and store SubtypeWitness values in the EntryPointRequest, which will be used by `specializeIRForEntryPoint` when generating final IR. 4. Add a new type of substitution - `GlobalGenericParamSubstitution` for subsittuting references to `__generic_param` decls or to its member `GenericTypeConsraintDecl` with the actual type argument or witness tables. 5. Update `IRSpecContext` to apply `GlobalGenericParamSubstitution` when specializing the IR for an EntryPointRequest. 6. Update `render-test` to take additional `type` inputs, which specifies the type arguments to substitute into the global `__generic_param` types. progress on parameter binding * Add a more contrived test case for specializing parameter bindings * update render-test to align buffers to 256 bytes (to get rid of D3D complains on minimal buffer size). * adding one more test case for parameter binding specialization. * Cleanup according to @tfoleyNV 's suggestions. * fix a bug introduced in the cleanup 18 November 2017, 02:26:21 UTC
0298a04 IR: support `CompileTimeForStmt` (#286) This statement type is a bit of a hack, to support loops that *must* be unrolled. The AST-to-AST pass handles them by cloning the AST for the loop body N times, and it was easy enough to do the same thing for the IR: emit the instructions for the body N times. The only thing that requires a bit of care is that now we might see the same variable declarations multiple times, so we need to play it safe and overwrite existing entries in our map from declarations to their IR values. Of course a better answer long-term would be to do the actual unrolling in the IR. This is especially true because we might some day want to support compile-time/must-unroll loops in functions, where the loop counter comes in as a parameter (but must still be compile-time-constant at every call site). 17 November 2017, 15:09:58 UTC
0e3d9ba IR: pass through `[unroll]` attribute (#284) The initial lowering was adding an `IRLoopControlDecoration` to the instruction at the head of a loop, but this was getting dropped when the IR gets cloned for a particular entry point. The fix was simply to add a case for loop-control decorations to `cloneDecoration`. 17 November 2017, 03:31:57 UTC
871fbee Revise type legalization so it can handle constant buffers (#282) * Revise type legalization so it can handle constant buffers The existing legalization approach with "tuples" can handle scalarizing a `struct` type with resource-type fields in it, but it had several big gaps. The most notable is that given a type that mixes uniform and resource fields, we can't just blindly scalarize things: ``` struct P { float4 a; float4 b; Texture2D t; }; cbuffer C { P gParam[8]; }; ``` The existing code was completely ignoring the declaration of `gParam` inside `C`, but even if we fixed that issue, we'd get something like: ``` cbuffer C { float4 gParam_a[8]; float4 gParam_b[8]; }; Texture2D gParam_t[8]; ``` In this case we've completely changed the layout of the uniform buffer, by switching from AOS to SOA. Even if we could get the type layout logic and the IR to agree on this, it would be a surprise to users, and "principle of least surprise" should be a big deal on a project with as many moving parts as ours. The right thing to do is to have legalization create a "stripped" version of the original type `P` and use that: ``` struct P_stripped { float4 a; float4 b; }; cbuffer C { P_stripped gParam[8]; }; Texture2D gParam_t[8]; ``` Then at a call site, this: ``` foo(gParam); ``` becomes: ``` foo(gParam, gParam_t); ``` This is exactly how the current AST-to-AST legalization handles mixed uniform and resource types, but the way it does it involves some annoying kludges: - That pass has a notion of a "tuple" similar to our legalization, but every tuple has an optional "primary" entry for all the uniform data, plus tuple elements for the resources, and a given field may be represented on one side, the other, or both. It makes the code for handling tuples very messy. - That pass does the "stripping" of types by actually marking up the AST declarations (this is okay because it is constructing a new AST as it goes), so that when they get emitted certain fields don't actually show up. That is, we fix the problem with type `P` by actually *modifying* the user's declaration of `P`. That seems out of bounds for the IR. This change fixes the problem in our IR type legalization while trying to avoid the problems of the AST-to-AST pass by using two new ideas: 1. We add a new case for `LegalType` (and `LegalVal`) that is a "pair" type, where a pair consists of both an "ordinary" type (for uniform data) and a "special" type (for resource data). E.g., after legalization, the type for `C` (which can be over-simplified to `ConstantBuffer<P>` for our purposes), will be a `LegalType::pair` where the ordinary side is `ConstantBuffer<P_stripped>` and the special side is a tuple containing the `Texture2D` field. 2. We add a new (and annoyingly hacky) AST-level type called `FilteredTupleType` which is semantically a sort of tuple type (it holds a list of elements, and the elements have their own types), but which remembers an "original type" that it was created from, and for each element remembers the field of the original type that it corresponds to. This is used to construct a type like `P_stripped` as an actual AST-level structural type. The core logic for legalizing an aggregate type had to get more complicated just because of the new pair case, so there is now a `TupleTypeBuilder` that asists with taking an aggregate type, processing its fields, and then picking the right `LegalType` representation for the result. Other smaller changes: - Made the legalization logic actually legalize `PtrType<T>`. E.g., if `T` legalizes to a tuple, we need to construct a tuple of pointer types. The same exact thing needs to be applied to arrays, and any other generic type that should "distribute over" pairs/tuples. - Made the legalization logic actually legalize `ConstantBuffer<T>` and similar. The basic idea there is if `T` maps to a pair, we wrap `ConstantBuffer<...>` around the ordinary side, and `implicitDeref` around the special side. - Removed a bunch of `#ifdef`ed-out code from the end of `ir-legalize-types.cpp`. That was code from my first attempt at legalization that failed miserably (trying to do it via local changes and a work list instead of a global rewrite pass), but it had some code I wanted to reference when writing the version that actually got checked in (should have deleted the code earlier, though). - Added a bunch of cases for `LegalType::none` (and the `LegalVal` equivalent) that helped simplify the logic fo the `pair` case by allowing me to *always* dispatch to both the "ordinary" and "special" sides, even if they might not actually be present. - Renamed `TupleType` and `TupleVal` over to `TuplePseudoType` and `TuplePseudoval` to recognize the fact that we might actually need/want *real* tuples in the type system, to go along with these fake ones (that need to be optimized away). The biggest doubt I have about this change is the whole `FilteredTupleType` thing; it seems like an obviously contrived type to add to the front-end type system that really only solves IR-level problems. A cleaner approach might have been to just add a plain old `TupleType` to the front-end type system (and initially I started with that), and then have yet another `LegalType`/`LegalVal` case that handles mapping from the fields of the original type to the numbered tuple elements. I expect we'll actually want to make that change in the future (especially if we ever add true tuples to the front-end), but for right now I let myself be swayed by the desire to have these stripped/filtered types get names that explain their provenance ("where they came from") to make our output code more debuggable. The way I've done it is probably overkill, though, and we need a much more complete effort on the readability and debuggability of our output before anything like that is worth worrying about. * Fixup: typo * Fixup: fix output of "non-mangled" names for test cases - Make sure to attach high-level decls to variables created as part of type legalization - Also, try to share more of the code between the different cases of variables - Fix up `parameter-blocks` test case that was passing `-no-mangle` but expecting mangled names in the output - Fix up `multiple-parameter-blocks` to not rely on `-no-mangle` for now, because it would lead to two global variables with the same name (need to fix that underlying issue eventually). - Also fix name generation logic so that we only use "original" names (from high-level decls) specifically when the `-no-mangle` flag is on, and otherwise use IR-level names. * Fix: handle constant buffers better in render-test - Don't request both CB and SRV usage for buffers, since that is illegal - Also, don't try to create an SRV when user requested a CB (since the required usage flag won't be there) - Record the input buffer type on the `D3DBinding` for a buffer, and use that to tell us when to bind a CB instead of SRV/UAV - Fix expected output for `cbuffer-legalize` test now that we are actually feeding it correct cbuffer dta. 16 November 2017, 22:01:16 UTC
1a0a7f6 Various IR fixes for Falcor (#280) - Change function mangling so we use `p<parameterCount>p` instead of just `p<parameterCount>` to avoid the parameter count running into digits at the start of a mangled type name and tripping up the un-mangling logic. - We really need to step back at some point and define our mangling scheme a bit more carefully, especially if we are going to keep going down this road where un-mangling things is important for generating HLSL output. - Also allow the unmangling logic to unmangle a few more cases of generic parameters, so that it can skip over them to get to the parameter count of the underlying function. - Add a notion of an `unreachable` instruction to the IR, and emit it as the terminator (if needed) at the end of the last block for a function with a non-void return type. - This does *not* implement any logic to emit a diagnostic if the `unreachable` turns out to be potentially reachable - Fix a bug in IR specialization of generics where we can't create two different specializations of the same function, because both get registered in the same hash map With all these fixes, testing in Falcor modified to use the full Slang compiler and IR for all HLSL/Slang: - The UI and text rendering shaders yield HLSL that compiles without error; no idea if they actually *work* - The ModelViewer shaders yield HLSL, but there are some issues (looks like type legalization isn't applying to stuff inside constant buffers) 15 November 2017, 18:50:15 UTC
59a4c0c IR: add support for `switch` statements (#278) * IR: add support for `switch` statements Fixes #273 This is just something we hadn't gotten to yet on the IR. The actual design of the instruction is unsurprising (once you take into consideration the requirement for structured control flow). A `switch` instruction takes the form: switch <condition> <breakLabel> <defaultLabel> [<caseVal> <caseLabel>]* Where `condition` is the value to switch on, `breakLabel` is the "join point" after the original `switch` statement, `defaultLabel` is where to go if the value doesn't match any case, and each pair of `caseVal` and `caseLabel` is what to do on a particular value. It is required that `caseVal` be a literal, but this isn't currently being enforced in the IR (the front-end should be making a check and constant-folding the case labels). For structured control flow, we also make the assumption that the cases are in order: cases with the same label must be grouped together, and any case that falls through to another must come right before it. Given this representation, the emit logic can reconstruct a `switch` statement with relative ease, given the machinery we already have. It makes sure to group together case values with the same label (again, assuming they are contiguous), and will insert the `default:` label in with whatever group it belongs to. Actually emitting code for a `switch` statement seems superficially simple, until you realize that a complete implementation needs to handle stuff like "Duff's Device." The current implementation makes the assumption that all `case` and `default` statements are directly nested under a `switch`, and that there is no way for control flow to enter a case except by the `switch` itself, or fall-through. In order to facilitate the grouping of cases in the IR-to-HLSL emit logic, the AST-to-IR lowering logic tries to detect cases where there are multiple `case`s in a row, and emit only a single label for them. One big/annoying gotcha is that we don't properly handle the case where a `default:` case has a non-trivial fall-throguh to another case. That seems fine for now since HLSL doesn't support fall-through anyway, but it probably needs to get detected somewhere in the Slang compiler (e.g., maybe we should add a diagnostic pass over the IR that detects target-specific problems like that and emits errors). * IR: Add support for empty statements. - Add empty statement in `lower-to-ir.cpp` - Go ahead and eliminate the statement catch-all and explicitly enumerate the cases we don't support - Fix up parser for block statements so that it doesn't leave a null statement as the body of a `{}` - Add an empty statement to one of the cases for the `switch` test, to ensure we are testing empty statements 14 November 2017, 19:33:36 UTC
7c4ad87 Merge pull request #277 from csyonghe/ir-legalization IR Legalization of function parameters 14 November 2017, 02:31:40 UTC
38f0113 Merge branch 'ir-legalization' of https://github.com/csyonghe/slang 13 November 2017, 23:39:44 UTC
fd056b0 Legalization of function parameter types. This commit fixes issue #275 This commit includes following changes: 1. legalize function parameter IRParam instructions 2. legalize function parameter types in IRFuncType 3. legalize call sites (IRCall) with proper arguments 4. legalize local vars that has a mixed resource type. 13 November 2017, 23:39:17 UTC
81f295f remove unfinished test case 13 November 2017, 23:38:38 UTC
3dc6c1b fix merge bug 13 November 2017, 23:37:30 UTC
891c2c6 fix merge bug 13 November 2017, 23:35:22 UTC
bbf58ba Merge branch 'ir-legalization' of https://github.com/csyonghe/slang 13 November 2017, 23:26:10 UTC
fe22b6f Legalization of function parameter types. This commit addresses issue #275 This commit includes following changes: 1. legalize function parameter IRParam instructions 2. legalize function parameter types in IRFuncType 3. legalize call sites (IRCall) with proper arguments 4. legalize local vars that has a mixed resource type. 13 November 2017, 23:25:41 UTC
57f737d Legalization of function parameter types. This commit addresses issue #275 This commit includes following changes: 1. legalize function parameter IRParam instructions 2. legalize function parameter types in IRFuncType 3. legalize call sites (IRCall) with proper arguments 4. legalize local vars that has a mixed resource type. 13 November 2017, 23:22:34 UTC
c9d9424 Parameter block work (#276) * Don't auto-enable IR use for compute tests The `COMPARE_COMPUTE` and `COMPARE_RENDER_COMPUTE` test fixtures were set up to always enable the `-use-ir` flag on Slang, which precludes having any tests that confirm functionality on the old non-IR path (which is still required by our main customer). This change adds the `-xslang -use-ir` flags explicitly to any compute test cases that left them out, and makes the fixture no longer add it by default. * Continue building out parameter block support The initial front-end logic for parameter blocks was already added, but they are still missing a bunch of functionality. This change addresses some of the known issues: - Bug fix: don't try to emit HLSL `register` bindings for variables that consume whole register spaces/sets - Overhaul type layout logic so that it can make decisions based on a given code generation target (currently passed in as a `TargetRequest`), which allows us to decide whether or not a parameter block should get its own register set on a per-target basis. - Always use a register space/set for Vulkan - Never use a register space/set for HLSL SM 5.0 and lower - By default, don't use register spaces/sets for HLSL output - Add a command-line flag and some "target flags" to enable register-space usage for D3D targets - Hackily add initial support for parameter blocks in the AST-to-AST path - This just blindly lowers `ParameterBlock<T>` to `T`, which shouldn't quite work - A more complete overhaul will probably need to wait until the AST-to-AST legalization is changed to use the `LegalType`s from the IR legalization pass. - Add a compute-based test case to actually run code using parameter blocks - This file runs test cases both with and without the IR 13 November 2017, 22:17:09 UTC
c9368fe IR: Add support for break and continue statements (#272) * IR: Add support for break and continue statements The front-end is already doing the work of connecting this statements to their "parent" statement, so we just needed to build a map from the `Stmt*` to the corresponding `IRBlock*`s to use for break/continue when outputting any loop statement, and then look up in the map for the branch target when outputting a break/continue. When we get around to adding `switch` statements, the same pattern should work just fine. I also added support for `do/while` statements in IR codegen, and made sure to exercise those in one of the test cases I added. There is also an unrelated IR codegen fix for when there is a "bound subscript" on the RHS of an assignment. * IR: fix handling of do/while and continue Thanks to @csyonghe for pointing out my mistake in the earlier commit. I implemented `continue` for `do/while` loops incorrectly, branching to the head of the loop instead of the loop test. I'll try to blame this mistake on the fact that I never use `do/while` loops because I think they are awful. :) The fix for that issue wasn't too bad (see `lower-to-ir.cpp`) but it surfaces a much more serious issue: I wasn't actually implementing `continue` correctly *at all* when it comes to generating HLSL/GLSL from the IR (I can't easily make an excuse for that one). The basic issue at the heart of this is that given an input statement like: ``` for(int ii = 0; ii < N; ii = doSomething(ii)) { ... } ``` The continue clause (`ii = doSomething(ii)`) could expand into many instructions (across multiple blocks, if we inline), and there is in general no guarantee when we are done that we can package up that code as an expression and spit out a new `for` loop (the same basic argument applies to a `do { ... } while(someComplexExpression())`. So, if we assume that in general we have to generate a full *statement* for the `continue` clause, what can we emit? - We could try to "outline" the continue code into its own function, so that we can call it from an expression. That could work, but has high implementation complexity. - We could introduce additional `bool` variables for control flow, outputting something like: ``` bool useContinueBlock = false; for(;;) { if(useContinueBlock) { <CONTINUE CODE>; } useContinueBlock = true; <LOOP TEST> <LOOP BODY> } ``` This works but user might balk at the extra variable we introduce. - We could duplicate the code at each continue site. That is, we emit the loop as: ``` for(;;) { <LOOP TEST> <LOOP BODY> <CONTINUE CODE> } ``` but then whenever we'd like to emit `continue;` we instead emit `{ <CONTINUE CODE>; continue; }`. This doesn't introduce any extra variables, but it causes code duplication (limited, if we don't have too many `continue` sites, and the continue clause is small - which are the common cases). When I was initially working on the IR codegen I picked that last option just because it is what `fxc` seems to do, but I neglected to actually *implement* the special-case codegen for a `continue` instruction. This change addresses that (see `emit.cpp`). Finally, once things were fixed the `continue` test case produced the results Yong told me to expect, but it also produced a warning from the downstream HLSL compiler ("hey, your loop doesn't ever actually *loop*!"), so I reworked the test back to one that actually loops (but still tests `continue`). As a final aside in this essay of a commit message: the current IR representation of control flow uses special-case instructions for various cases of unconditional branch (and two variations on `if`), but these are not strictly necessary, and a future change will hopefully clean it up. The biggest catch in doing that is that it will require the IR->source codegen to carefully track which blocks represent which kinds of branch targets in context (e.g., you can't assume that a `continue` that nees the special handling above will appear as a distinct kind of instruction). 09 November 2017, 17:18:16 UTC
9a26ba8 Merge pull request #267 from csyonghe/work Don't update generated .h file if its not changed. 08 November 2017, 18:35:11 UTC
da58c70 Merge branch 'master' into work 08 November 2017, 17:55:08 UTC
back to top