summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/emit.cpp99
-rw-r--r--source/slang/ir.cpp72
-rw-r--r--source/slang/ir.h11
3 files changed, 176 insertions, 6 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 4ed7dd5a7..c86634c02 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -2213,21 +2213,43 @@ struct EmitVisitor
IRInst* inst,
IREmitMode mode)
{
- // Certain opcodes should always be folded in
+ // Certain opcodes should never/always be folded in
switch( inst->op )
{
default:
break;
+ // Never fold these in, because they represent declarations
+ //
case kIROp_Var:
case kIROp_GlobalVar:
case kIROp_GlobalConstant:
case kIROp_Param:
return false;
+ // HACK: don't fold these in because we currently lower
+ // them to initializer lists, which aren't allowed in
+ // general expression contexts.
+ //
+ case kIROp_makeStruct:
+ case kIROp_makeArray:
+ return false;
+
+ // Always fold these in, because they are trivial
+ //
case kIROp_IntLit:
case kIROp_FloatLit:
case kIROp_boolConst:
+ return true;
+
+ // Always fold these in, because their results
+ // cannot be represented in the type system of
+ // our current targets.
+ //
+ // TODO: when we add C/C++ as an optional target,
+ // we could consider lowering insts that result
+ // in pointers directly.
+ //
case kIROp_FieldAddress:
case kIROp_getElementPtr:
case kIROp_Specialize:
@@ -2239,11 +2261,13 @@ struct EmitVisitor
if (mode == IREmitMode::GlobalConstant)
return true;
- // Certain *types* will usually want to be folded in,
- // because they aren't allowed as types for temporary
- // variables.
+ // Instructions with specific result *types* will usually
+ // want to be folded in, because they aren't allowed as types
+ // for temporary variables.
auto type = inst->getDataType();
+ // First we unwrap any layers of pointer-ness and array-ness
+ // from the types to get at the underlying data type.
while (auto ptrType = as<IRPtrTypeBase>(type))
{
type = ptrType->getValueType();
@@ -2253,6 +2277,15 @@ struct EmitVisitor
type = ptrType->getElementType();
}
+ // First we check for uniform parameter groups,
+ // because a `cbuffer` or GLSL `uniform` block
+ // does not have a first-class type that we can
+ // pass around.
+ //
+ // TODO: We need to ensure that type legalization
+ // cleans up cases where we use a parameter group
+ // or parameter block type as a function parameter...
+ //
if(as<IRUniformParameterGroupType>(type))
{
// TODO: we need to be careful here, because
@@ -2260,6 +2293,12 @@ struct EmitVisitor
// types.
return true;
}
+ //
+ // The stream-output and patch types need to be handled
+ // too, because they are not really first class (especially
+ // not in GLSL, but they also seem to confuse the HLSL
+ // compiler when they get used as temporaries).
+ //
else if (as<IRHLSLStreamOutputType>(type))
{
return true;
@@ -2289,8 +2328,56 @@ struct EmitVisitor
}
}
- // By default we will *not* fold things into their use sites.
- return false;
+ // Having dealt with all of the cases where we *must* fold things
+ // above, we can now deal with the more general cases where we
+ // *should not* fold things.
+
+ // Don't fold somethin with no users:
+ if(!inst->hasUses())
+ return false;
+
+ // Don't fold something that has multiple users:
+ if(inst->hasMoreThanOneUse())
+ return false;
+
+ // Don't fold something that might have side effects:
+ if(inst->mightHaveSideEffects())
+ return false;
+
+ // Okay, at this point we know our instruction must have a single use.
+ auto use = inst->firstUse;
+ SLANG_ASSERT(use);
+ SLANG_ASSERT(!use->nextUse);
+
+ auto user = use->getUser();
+
+ // We'd like to figure out if it is safe to fold our instruction into `user`
+
+ // First, let's make sure they are in the same block/parent:
+ if(inst->getParent() != user->getParent())
+ return false;
+
+ // Now let's look at all the instructions between this instruction
+ // and the user. If any of them might have side effects, then lets
+ // bail out now.
+ for(auto ii = inst->getNextInst(); ii != user; ii = ii->getNextInst())
+ {
+ if(!ii)
+ {
+ // We somehow reached the end of the block without finding
+ // the user, which doesn't make sense if uses dominate
+ // defs. Let's just play it safe and bail out.
+ return false;
+ }
+
+ if(ii->mightHaveSideEffects())
+ return false;
+ }
+
+ // Okay, if we reach this point then the user comes later in
+ // the same block, and there are no instructions with side
+ // effects in between, so it seems safe to fold things in.
+ return true;
}
bool isDerefBaseImplicit(
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:
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 5d9948b70..af08b9491 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -284,6 +284,17 @@ struct IRInst : public IRObject
// for those values.
void removeArguments();
+ /// Does this instruction have any uses?
+ bool hasUses() const { return firstUse != nullptr; }
+
+ /// Does this instructiomn have more than one use?
+ bool hasMoreThanOneUse() const { return firstUse != nullptr && firstUse->nextUse != nullptr; }
+
+ /// It is possible that this instruction has side effects?
+ ///
+ /// This is a conservative test, and will return `true` if an exact answer can't be determined.
+ bool mightHaveSideEffects();
+
// RTTI support
static bool isaImpl(IROp) { return true; }
};