| Commit message (Collapse) | Author | Age |
| ... | |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The catch with these operations is that they return a vector based on the scalar of the element type of the texture.
That is, given `Texture2D<float> t` the operation `t.GatherRed(...)` should return a `float4`.
The ideal way to solve this would use associated types, but we aren't there yet, so I am using extension declarations.
An extension can "capture" the identity of the element type, like so:
__generic<T, let N : int> __extension Texture2D<vector<T,N> > { ... }
That extension will match `Texture2D<float3>` and correctly capture `T == float`, so that we can use it in other operations.
Getting this working required a bunch of changes:
- Actually emit the relevant extension declarations in the stdlib
- Fix the parser to be able to parse `Texture2D<vector<T,N> >` (that is, a nested generic app).
- I actually went ahead and significantly overhauled the expression parser while I was there, because I just couldn't deal with the existing code any longer.
- Added support for general-case lookup to look through `__extension` declarations. I had logic in place to special-case this for looking up "constructors" but hadn't done anything for general member lookup yet.
- This required some annoying holes to be punched through the layers, because lookup might need to invoke semantic analysis to ensure that an extension has been checked.
- There is some first-pass code trying to support looking up a `typedef` nested inside the `vector` type. This is a nice idea in principle, but the problem is that the `Texture2D<T>` definition would be looking up `T.Element` and not `float4.Element`, and that means we'd need machinery for doing lookup *through* interface conformances for a type parameter like `T`
The big gotcha here is that none of this logic applies to `Texture2D<float>` (the original case I mentioned) because I am matching vector types and not scalars.
Matching scalars *should* be as easy as:
__generic<T : __BuitlinScalarType> __extension Texture2D<T> { ... }
But I'd need to confirm that interface constraints like that actually work, or else that extension would *also* apply to `Texture2D<float4>` and break everything.
|
| |
|
|
| |
This helps ensure that we pull things into scope at the right time.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The main user-visible change here is that instead of `spAddTranslationUnitEntryPoint` we have `spAddEntryPoint`, to reflect that the list of entry points is "global" to a compile request.
As a result, `spGetEntryPointSource` now only needs the entry point index, and not the translation unit index.
There are a bunch more behind-the-scenes changes, though, reflecting a streamlining of the concepts related to compilation into a smaller number of classes.
Now there is:
- `Session` (unchanged) to manage the lifetimes of shared stuff like the stdlib
- `CompileRequest` (merges in `CompileOptions`) to handle all the lifetime related to a single invocation of the compiler
- `TranslationUnitRequest` (merges `TranslationUnitOptions`, `CompileUnit`) to represent a single translation unit ("module") that the user is trying to compile. This is a single file for HLSL/GLSL, but can be multiple files for Slang.
- `EntryPointRequest` (merges `EntryPointOption` and a bit of `EntryPointResult`) to track a single entry point that the user is asking to compile (that entry point always comes from a single translation unit)
A lot of functions used to take some combination of these and end up with really long signatures.
I've given most of the objects "parent" pointers so that they can get back to all the context they need, so most functions don't need as many parameters.
It may eventually be important to tease these apart again, in particular:
- The code-generation side of things (the `*Result` types) might need to be pulled out in case we want to codegen multiple times from the same AST
- Similarly, the layout stuff may also need to be pulled out, in case we want to lay things out multiple times with different rules.
|
| |
|
|
| |
That is, even if hte user specified the `-no-checking` option (or the equivalent via API), we still want/need to apply full semantic checks to Slang code, so that cross-compilation will be possible.
|
| |
|
|
|
|
| |
When in rewriter mode, the emit logic will never see function applications inside function bodies, but it *will* see function application expressions at global scope, and some of these expressions might be unchecked.
The challenge here is that even simple math operations now show up as function calls, so we need a bit of special-case logic to detect unchecked calls and then emit them using the syntax they were written with (e.g., use infix syntax if they were written as an infix expression).
|
| |
|
|
|
|
|
|
|
|
| |
For context: a `DeclRef` is supposed to capture both a pointer to a particualr declaration, and also any information needed to specialize that declaration for a context (e.g., generic parameter substitutions).
The existing approach had a hiearchy of specialized decl-ref types that mirrored the AST hierarchy, but that led to a lot of boilerplate where you had to recapitulate the exact same hierarchy.
The new appraoch basically treats `DeclRef<T>` as a sort of "smart pointer" in that it wraps a pointer to a `T` (the declaration), plus a side field for the specialization info, and then allows it to be cast as needed to other types (where the pointer cast would be allowed), while carrying along the side info.
To enable this, all the things that used to be member functions of declaration-reference types are now free functions that take a `DeclRef<T>` for some specific `T` as a parameter.
|
| |
|
|
| |
It is always easier to add back code when you need it, than it is to maintain code you aren't using.
|
| |
|
|
| |
This gets rid of one unecessary namespace.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Add a test case for `interface` declarations and the exected implicit type conversion rules around them
- Rename exising "trait" declaration kind to "interface"
- There was already basic syntax for `__trait` declarations, and a bunch of related machinery.
- Not all of it worked as needed, but it was clearly a start at solving the problem
- Change `InterfaceConformanceDecl` to a more general `InheritanceDecl` that covers inheritance from any type expression (leave it to other code to validate the cases that should be allowed)
- Instead of keeping a raw `bases` array on interface/trait declarations, turn all inheritance clauses into `IheritanceDecl` members
- Add support for inheritance clause on `struct` types
- Remove the `__conforms` syntax only used in the stdlib, in favor of conentional `: Base` style syntax already in place for aggregate types
- Make sure that the parser pushes a new scope around he member declarations of an aggregate type, so that lookup in member functions will correctly find members of the enclosing type
- In `TryCoerceImpl`, allow a type that conforms to an interface to be implicitly conveted to the corresponding interface type.
This leaves out a lot of major functionality:
- There is no validation that a type provides all the members it is supposed to as part of fulfilling a claimed interface conformance
- The lookup process needs to deal with inherited members at some point.
- We can avoid this for now if we don't allow inheritance for concrete types
- When it comes time to handle it, it *might* be possible to implement by considering an `InheritanceDecl` to be, conceptually, a member of the inherited type, with a `__transparent` modifier
- The lookup rules member functions do *not* deal with a lot of stuff:
- There is no `this` expression right now
- The semantic checker does not rewrite `foo` to `this.foo`, so downstream stages aren't going to get things in a clean format
- There is no handling of mutability currently
- The right answer there is probably to make member functions on `struct` types non-mutating by default, and add a qualifier to opt in to mutability. I believe this is actually what the OOP syntax in HLSL did way back when.
- There is no handling of `static` members, and thus no checking to make sure that non-static members aren't referenced in static functions
- None of this affects down-stream code generation right now, so it probably won't actually produce anything valid.
- This is where we start needing a suitable IR to use for lowering, to manage the complexity.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is a large change that contains many pieces:
- Update the `cross-compile0` test to actually make use of cross compilation.
Now the `cross-compile0.hlsl` file contains both HLSL and GLSL source code, and then imports code from `cross-compile0.slang`, which provides a "library" (one function) that can be shared between both the HLSL and GLSL version of things.
- Fixed a bug in the support for backslash-escaped newlines.
- Added a new `__import` declaration type (replaces the `using` directive that was still around in a vestigial form)
An `__import` causes the compiler to look for a Slang source file (currently using the ordinary `#include` lookup logic), and then parse/check the found file as an additional module ("translation unit"), before making its declarations visible in the current scope.
- Refactored the main compilation flow to be simpler. There were the `ShaderCompiler` and `ShaderCompilerImpl` classes that weren't relaly doing anything, but added complexity to the whole workflow.
- The `render-test` application has been heavily modified to better support testing cross-compilation workflows. At the most basic level we are starting to distinguish pass-through vs. rewriter workflows, and are passing various `#define`s down to the compiler(s) to let the source code be customized as needed for each case.
Several annoying corner cases are caused here by having to support the GLSL compilation model, which really wants each entry point in its own specific translation unit, whereas we really want to keep things nicely contained in single files.
- Added support for `__intrinsic` operations to have target-specific behavior.
This allows a function to be given a different name for some specific target (so a call gets emitted as a call to that other operation).
More generally, the library writer can put together an arbitrary format string that will be used in place of expressions that call the given function, e.g.:
__intrinsic(hlsl, "$1 - $0") __intrinsic int foo(int a, int b);
Given this declaration, a call like `foo(x,y)` will code generate as `x - y` for HLSL, and as `foo(x,y)` for all other targets.
Annoying things still to be dealt with:
- The way that I'm filtering the user-provided options when passing things down to the compilation of dynamically loaded modules is a bit ad hoc. It would be good to have a systematic notion of which options will be inherited and which won't. There is also more code duplication than I'd like, so we risk having the compiler behave differently when compiling a file at the top level, vs. because of `__import`.
- Adding target-specific behavior to intrinsics is all well and good, but the current approach means we can only add this to the original declaration, which limits the ability to easily extend the set of targets.
A better approach long-term would be to add a more robust notion of target-based overload resolution (which would happen after semantic checking). Then one mechanism would be used to find the right target-specific overload to use for an operation, and then each (target-specific) definition could use a simpler attribute to intercept code-generation behavior.
Note that we might eventually need a similar notion to deal with stage- or profile-specific functions and the overloading behavior around them, so using this for intrinsics doesn't seem like a bad idea.
|
| |
|