diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-05-04 12:01:30 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-04 12:01:30 -0700 |
| commit | 07a59b623e44348caf06d65b2578b51d86ba0af5 (patch) | |
| tree | dcb07f728cc1050bd36f7c323c3a1f2cc9ba6681 /source/slang/ir.cpp | |
| parent | ee47232fc17f31ef2bd95ca480372216a79def56 (diff) | |
Allow more complex compound expressions when emitting from IR (#552)
The emit logic already had an idea of when an instruction should be "folded" it its use site(s), and this change just expands on that logic to try to be more aggressive. The basic idea is that instead of outputting this:
```hlsl
float4 _S3 = a_0 + b_0;
float4 _S4 = c_0 * _S3;
d_0 = _S4;
```
we can hopefully output something like this:
```hlsl
d_0 = c_0 * (a_0 + b_0);
```
The way this works is that after dealing with the various special cases that decide an instruction `I` must/cannot be folded in, we look and see if it has the following properites:
* `I` has no side effects
* `I` has a single user, `U`
* `I` and `U` are in the same block (and `I` comes before `U` in that block)
* for every instruction `X` between `I` and `U` (exclusive), `X` has no side effects
If all of these conditions are true, then `I` can be folded in as a sub-expression when we emit `U`.
This change doesn't affect most of our test output, but there is still a single test with SPIR-V output that we compare against a GLSL baseline, and so that baseline had to be modified to match the GLSL we now generate.
Similar to #547, this change is not meant to provide a complete solution, but rather to take a concrete but low-risk step toward improving our output. Opportunities to improve the results further include:
* We can/should ensure that when outputting sub-expressions we keep extra parentheses to a minimum. The old logic for emitting from an AST had support for "unparsing" expressions with minimal parentheses, and we should try to do the same. This can be error-prone, because omitting parentheses can lead to silent failures, so it must be done carefully.
* We could try to be more aggressive about detecting what operations might have side effects. The most interesting case is function calls, where we should try to check if the callee is a function known to be side-effect-free. We could start by annotating most builtin functions with an attribute/decoration that indicates freedom from side effects. Deriving this attribute for user functions could be interesting, but we'd have to be careful since "nontermination" is technically a side effect.
* We could try to be more aggressive about determining what side effects in instructions `X` are "safe" for the instruction `I` to move across. For example, if `I` is a load from variable `a` and `X` is a store to variable `b`, then that would seem to be safe. This starts to get into issues of instruction scheduling, though, and that is probably beyond what we want Slang to be doing.
Diffstat (limited to 'source/slang/ir.cpp')
| -rw-r--r-- | source/slang/ir.cpp | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index f8ca54639..a7cc342ae 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -3071,6 +3071,78 @@ namespace Slang deallocate(); } + bool IRInst::mightHaveSideEffects() + { + // TODO: We should drive this based on flags specified + // in `ir-inst-defs.h` isntead of hard-coding things here, + // but this is good enough for now if we are conservative: + + if(as<IRType>(this)) + return false; + + if(as<IRGlobalValue>(this)) + return false; + + if(as<IRConstant>(this)) + return false; + + switch(op) + { + // By default, assume that we might have side effects, + // to safely cover all the instructions we haven't had time to think about. + default: + return true; + + case kIROp_Call: + // This is the most interesting. + return true; + + case kIROp_Nop: + case kIROp_Specialize: + case kIROp_lookup_interface_method: + case kIROp_Construct: + case kIROp_makeVector: + case kIROp_makeMatrix: + case kIROp_makeArray: + case kIROp_makeStruct: + case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or `volatile` loads + case kIROp_BufferLoad: + case kIROp_FieldExtract: + case kIROp_FieldAddress: + case kIROp_getElement: + case kIROp_getElementPtr: + case kIROp_constructVectorFromScalar: + case kIROp_swizzle: + case kIROp_swizzleSet: // Doesn't actually "set" anything - just returns the resulting vector + case kIROp_Add: + case kIROp_Sub: + case kIROp_Mul: + //case kIROp_Div: // TODO: We could split out integer vs. floating-point div/mod and assume the floating-point cases have no side effects + //case kIROp_Mod: + case kIROp_Lsh: + case kIROp_Rsh: + case kIROp_Eql: + case kIROp_Neq: + case kIROp_Greater: + case kIROp_Less: + case kIROp_Geq: + case kIROp_Leq: + case kIROp_BitAnd: + case kIROp_BitXor: + case kIROp_BitOr: + case kIROp_And: + case kIROp_Or: + case kIROp_Neg: + case kIROp_Not: + case kIROp_BitNot: + case kIROp_Select: + case kIROp_Dot: + case kIROp_Mul_Vector_Matrix: + case kIROp_Mul_Matrix_Vector: + case kIROp_Mul_Matrix_Matrix: + return false; + } + } // // Legalization of entry points for GLSL: |
