summaryrefslogtreecommitdiffstats
path: root/source/slang/emit.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-09-14 15:37:05 -0700
committerGitHub <noreply@github.com>2017-09-14 15:37:05 -0700
commit10b62eecd94be53eca4ac2555af860f864966d76 (patch)
tree9a140acfda0e3f0755f2c120870c72d5a8f4b232 /source/slang/emit.cpp
parent8cdfce564546c03c2c1ce179561591276aeb23a8 (diff)
IR: handle control flow constructs (#186)
* IR: handle control flow constructs This change includes a bunch of fixes and additions to the IR path: - `slang-ir-assembly` is now a valid output target (so we can use it for testing) - This uses what used to be the IR "dumping" logic, revamped to support much prettier output. - A future change will need to add back support for less prettified output to use when actually debugging - IR generation for `for` loops and `if` statements is supported - HLSL output from the above control flow constructs is implemented - Revamped the handling of l-values, and in particular work on compound ops like `+=` - Add basic IR support for `groupshared` variables - Add basic IR support for storing compute thread-group size - Output semantics on entry point parameters - This uses the AST structures to find semantics, so its still needs work - Pass through loop unroll flags - This is required to match `fxc` output, at least until we implement unrolling ourselves. * Fixup: 64-bit build issues. * fixup for merge
Diffstat (limited to 'source/slang/emit.cpp')
-rw-r--r--source/slang/emit.cpp438
1 files changed, 423 insertions, 15 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 209436da4..25a0b5323 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -1,6 +1,7 @@
// emit.cpp
#include "emit.h"
+#include "ir-insts.h"
#include "lower.h"
#include "lower-to-ir.h"
#include "name.h"
@@ -3966,11 +3967,16 @@ emitDeclImpl(decl, nullptr);
{
Simple,
Ptr,
+ Array,
};
Flavor flavor;
IRDeclaratorInfo* next;
- String const* name;
+ union
+ {
+ String const* name;
+ IRInst* elementCount;
+ };
};
void emitDeclarator(
@@ -3991,6 +3997,13 @@ emitDeclImpl(decl, nullptr);
emit("*");
emitDeclarator(context, declarator->next);
break;
+
+ case IRDeclaratorInfo::Flavor::Array:
+ emitDeclarator(context, declarator->next);
+ emit("[");
+ emitIROperand(context, declarator->elementCount);
+ emit("]");
+ break;
}
}
@@ -4008,6 +4021,13 @@ emitDeclImpl(decl, nullptr);
emit(((IRConstant*) inst)->u.floatVal);
break;
+ case kIROp_boolConst:
+ {
+ bool val = ((IRConstant*)inst)->u.intVal != 0;
+ emit(val ? "true" : "false");
+ }
+ break;
+
default:
SLANG_UNIMPLEMENTED_X("val case for emit");
break;
@@ -4028,6 +4048,8 @@ emitDeclImpl(decl, nullptr);
CASE(Float32Type, float);
CASE(Int32Type, int);
CASE(UInt32Type, uint);
+ CASE(VoidType, void);
+ CASE(BoolType, bool);
#undef CASE
@@ -4084,6 +4106,15 @@ emitDeclImpl(decl, nullptr);
}
break;
+ case kIROp_structuredBufferType:
+ {
+ auto tt = (IRBufferType*) type;
+ emit("StructuredBuffer<");
+ emitIRType(context, tt->getElementType(), nullptr);
+ emit(">");
+ }
+ break;
+
case kIROp_SamplerType:
{
// TODO: actually look at the flavor and emit the right name
@@ -4127,11 +4158,11 @@ emitDeclImpl(decl, nullptr);
IRType* type,
IRDeclaratorInfo* declarator)
{
- switch( type->op )
+ switch (type->op)
{
case kIROp_PtrType:
{
- auto ptrType = (IRPtrType*) type;
+ auto ptrType = (IRPtrType*)type;
IRDeclaratorInfo ptrDeclarator;
ptrDeclarator.flavor = IRDeclaratorInfo::Flavor::Ptr;
@@ -4140,6 +4171,18 @@ emitDeclImpl(decl, nullptr);
}
break;
+ case kIROp_arrayType:
+ {
+ auto arrayType = (IRArrayType*)type;
+
+ IRDeclaratorInfo arrayDeclarator;
+ arrayDeclarator.flavor = IRDeclaratorInfo::Flavor::Array;
+ arrayDeclarator.elementCount = arrayType->getElementCount();
+ arrayDeclarator.next = declarator;
+ emitIRType(context, arrayType->getElementType(), &arrayDeclarator);
+ }
+ break;
+
default:
emitIRSimpleType(context, type);
emitDeclarator(context, declarator);
@@ -4178,6 +4221,7 @@ emitDeclImpl(decl, nullptr);
case kIROp_IntLit:
case kIROp_FloatLit:
+ case kIROp_boolConst:
case kIROp_FieldAddress:
case kIROp_getElementPtr:
return true;
@@ -4280,17 +4324,9 @@ emitDeclImpl(decl, nullptr);
switch(inst->op)
{
case kIROp_IntLit:
- {
- auto irConst = (IRConstant*) inst;
- emit(irConst->u.intVal);
- }
- break;
-
case kIROp_FloatLit:
- {
- auto irConst = (IRConstant*) inst;
- emit(irConst->u.floatVal);
- }
+ case kIROp_boolConst:
+ emitIRSimpleValue(context, inst);
break;
case kIROp_Construct:
@@ -4351,8 +4387,39 @@ emitDeclImpl(decl, nullptr);
CASE(kIROp_Div, /);
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, ||);
+
#undef CASE
+ case kIROp_Not:
+ {
+ if (inst->getType()->op == kIROp_BoolType)
+ {
+ emit("!");
+ }
+ else
+ {
+ emit("~");
+ }
+ emitIROperand(context, inst->getArg(1));
+ }
+ break;
+
case kIROp_Sample:
emitIROperand(context, inst->getArg(1));
emit(".Sample(");
@@ -4408,6 +4475,18 @@ emitDeclImpl(decl, nullptr);
emit("]");
break;
+ case kIROp_BufferStore:
+ emitIROperand(context, inst->getArg(1));
+ emit("[");
+ emitIROperand(context, inst->getArg(2));
+ emit("] = ");
+ emitIROperand(context, inst->getArg(3));
+ break;
+
+ case kIROp_GroupMemoryBarrierWithGroupSync:
+ emit("GroupMemoryBarrierWithGroupSync()");
+ break;
+
case kIROp_getElement:
case kIROp_getElementPtr:
emitIROperand(context, inst->getArg(1));
@@ -4426,8 +4505,29 @@ emitDeclImpl(decl, nullptr);
emit(")");
break;
+ case kIROp_swizzle:
+ {
+ auto ii = (IRSwizzle*)inst;
+ emitIROperand(context, ii->getBase());
+ emit(".");
+ UInt elementCount = ii->getElementCount();
+ for (UInt ee = 0; ee < elementCount; ++ee)
+ {
+ IRInst* irElementIndex = ii->getElementIndex(ee);
+ assert(irElementIndex->op == kIROp_IntLit);
+ IRConstant* irConst = (IRConstant*)irElementIndex;
+
+ UInt elementIndex = (UInt)irConst->u.intVal;
+ assert(elementIndex < 4);
+
+ char const* kComponents[] = { "x", "y", "z", "w" };
+ emit(kComponents[elementIndex]);
+ }
+ }
+ break;
+
default:
- emit("/* uhandled */");
+ emit("/* unhandled */");
break;
}
}
@@ -4478,6 +4578,51 @@ emitDeclImpl(decl, nullptr);
emitIROperand(context, ((IRReturnVal*) inst)->getVal());
emit(";\n");
break;
+
+ case kIROp_swizzleSet:
+ {
+ auto ii = (IRSwizzleSet*)inst;
+ emitIRInstResultDecl(context, inst);
+ emitIROperand(context, inst->getArg(1));
+ emit(";\n");
+ emitIROperand(context, inst);
+ emit(".");
+ UInt elementCount = ii->getElementCount();
+ for (UInt ee = 0; ee < elementCount; ++ee)
+ {
+ IRInst* irElementIndex = ii->getElementIndex(ee);
+ assert(irElementIndex->op == kIROp_IntLit);
+ IRConstant* irConst = (IRConstant*)irElementIndex;
+
+ UInt elementIndex = (UInt)irConst->u.intVal;
+ assert(elementIndex < 4);
+
+ char const* kComponents[] = { "x", "y", "z", "w" };
+ emit(kComponents[elementIndex]);
+ }
+ emit(" = ");
+ emitIROperand(context, inst->getArg(2));
+ emit(";\n");
+ }
+ break;
+
+#if 0
+ case kIROp_unconditionalBranch:
+ emit("// unconditionalBranch ");
+ emitIROperand(context, inst->getArg(1));
+ emit("\n");
+ break;
+
+ case kIROp_conditionalBranch:
+ emit("// conditionalBranch ");
+ emitIROperand(context, inst->getArg(1));
+ emit(" ");
+ emitIROperand(context, inst->getArg(2));
+ emit(" ");
+ emitIROperand(context, inst->getArg(3));
+ emit("\n");
+ break;
+#endif
}
}
@@ -4515,6 +4660,238 @@ emitDeclImpl(decl, nullptr);
}
}
+ // We want to emit a range of code in the IR, represented
+ // by the blocks that are logically in the interval [begin, end)
+ // which we consider as a single-entry multiple-exit region.
+ //
+ // Note: because there are multiple exists, control flow
+ // may exit this region with operations that do *not* branch
+ // to `end`, but such non-local control flow will hopefully
+ // be captured.
+ //
+ void emitIRStmtsForBlocks(
+ EmitContext* context,
+ IRBlock* begin,
+ IRBlock* end)
+ {
+ IRBlock* block = begin;
+ while(block != end)
+ {
+ // Start by emitting the non-terminator instructions in the block.
+ auto terminator = block->lastChild;
+ assert(isTerminatorInst(terminator));
+ for (auto inst = block->firstChild; inst != terminator; inst = inst->nextInst)
+ {
+ emitIRInst(context, inst);
+ }
+
+ // Now look at the terminator instruction, which will tell us what we need to emit next.
+
+ switch (terminator->op)
+ {
+ default:
+ SLANG_UNEXPECTED("terminator inst");
+ return;
+
+ case kIROp_ReturnVal:
+ case kIROp_ReturnVoid:
+ emitIRInst(context, terminator);
+ return;
+
+ case kIROp_if:
+ {
+ // One-sided `if` statement
+ auto t = (IRIf*)terminator;
+
+ auto trueBlock = t->getTrueBlock();
+ auto afterBlock = t->getAfterBlock();
+
+ emit("if(");
+ emitIROperand(context, t->getCondition());
+ emit(")\n{\n");
+ emitIRStmtsForBlocks(
+ context,
+ trueBlock,
+ afterBlock);
+ emit("}\n");
+
+ // Continue with the block after the `if`
+ block = afterBlock;
+ }
+ break;
+
+ case kIROp_ifElse:
+ {
+ // Two-sided `if` statement
+ auto t = (IRIfElse*)terminator;
+
+ auto trueBlock = t->getTrueBlock();
+ auto falseBlock = t->getFalseBlock();
+ auto afterBlock = t->getAfterBlock();
+
+ emit("if(");
+ emitIROperand(context, t->getCondition());
+ emit(")\n{\n");
+ emitIRStmtsForBlocks(
+ context,
+ trueBlock,
+ afterBlock);
+ emit("}\nelse\n{\n");
+ emitIRStmtsForBlocks(
+ context,
+ falseBlock,
+ afterBlock);
+ emit("}\n");
+
+ // Continue with the block after the `if`
+ block = afterBlock;
+ }
+ break;
+
+ case kIROp_loop:
+ {
+ // Header for a `while` or `for` loop
+ auto t = (IRLoop*)terminator;
+
+ auto targetBlock = t->getTargetBlock();
+ auto breakBlock = t->getBreakBlock();
+ auto continueBlock = t->getContinueBlock();
+
+ if (auto loopControlDecoration = t->findDecoration<IRLoopControlDecoration>())
+ {
+ switch (loopControlDecoration->mode)
+ {
+ case kIRLoopControl_Unroll:
+ emit("[unroll]\n");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // The challenging case for a loop is when
+ // there is a `continue` block that we
+ // need to deal with.
+ //
+ if (continueBlock == targetBlock)
+ {
+ // There is no continue block, so
+ // we only need to emit an endless
+ // loop and then manually `break`
+ // out of it in the right place(s)
+ emit("for(;;)\n{\n");
+
+ emitIRStmtsForBlocks(
+ context,
+ targetBlock,
+ nullptr);
+
+ emit("}\n");
+ }
+ else
+ {
+ // Okay, we've got a `continue` block,
+ // which means we really want to emit
+ // something akin to:
+ //
+ // for(;; <continueBlock>) { <bodyBlock> }
+ //
+ // In principle this isn't so bad, since the
+ // first case is just interVal [`continueBlock`, `targetBlock`)
+ // and the latter is the interval [`targetBlock`, `continueBlock`).
+ //
+ // The challenge of course is that a `for` statement
+ // only supports *expressions* in the continue part,
+ // and we might have expanded things into multiple
+ // instructions (especially if we inlined or desugared anything).
+ //
+ // There are a variety of ways we can support lowering this,
+ // but for now we are going to do something expedient
+ // that mimics what `fxc` seems to do:
+ //
+ // - Output loop body as `for(;;) { <bodyBlock> <continueBlock> }`
+ // - At any `continue` site, output `{ <continueBlock>; continue; }`
+ //
+ // This isn't ideal because it leads to code duplication, but
+ // it matches what `fxc` does so hopefully it will be the
+ // best option for our tests.
+ //
+
+ emit("for(;;)\n{\n");
+
+ // TODO: Okay, we *said* we'd do this special
+ // handling of the `continue` sites, but
+ // we aren't actually setting anything up here...
+ //
+
+ emitIRStmtsForBlocks(
+ context,
+ targetBlock,
+ nullptr);
+
+ emit("}\n");
+
+ }
+
+ // Continue with the block after the loop
+ block = breakBlock;
+ }
+ break;
+
+ case kIROp_break:
+ emit("break;\n");
+ return;
+
+ case kIROp_continue:
+ emit("continue;\n");
+ return;
+
+ case kIROp_loopTest:
+ {
+ // Loop condition being tested
+ auto t = (IRLoopTest*)terminator;
+
+ auto afterBlock = t->getTrueBlock();
+
+ emit("if(");
+ emitIROperand(context, t->getCondition());
+ emit(")\n{} else break;\n");
+
+ // Continue with the block after the test
+ block = afterBlock;
+ }
+ break;
+
+ case kIROp_unconditionalBranch:
+ {
+ // Unconditional branch as part of normal
+ // control flow. This is either a forward
+ // edge to the "next" block in an ordinary
+ // block, or a backward edge to the top
+ // of a loop.
+ auto t = (IRUnconditionalBranch*)terminator;
+ block = t->getTargetBlock();
+ }
+ break;
+
+ case kIROp_conditionalBranch:
+ SLANG_UNEXPECTED("terminator inst");
+ return;
+ }
+
+ // If we reach this point, then we've emitted
+ // one block, and we have a new block where
+ // control flow continues.
+ //
+ // We need to handle a special case here,
+ // when control flow jumps back to the
+ // starting block of the range we were
+ // asked to work with:
+ if (block == begin) return;
+ }
+ }
+
void emitIRFunc(
EmitContext* context,
IRFunc* func)
@@ -4522,6 +4899,19 @@ emitDeclImpl(decl, nullptr);
auto funcType = func->getType();
auto resultType = func->getResultType();
+ // Deal with decorations that need
+ // to be emitted as attributes
+ if (auto threadGroupSizeDecoration = func->findDecoration<IRComputeThreadGroupSizeDecoration>())
+ {
+ emit("[numthreads(");
+ for (int ii = 0; ii < 3; ++ii)
+ {
+ if (ii != 0) emit(", ");
+ Emit(threadGroupSizeDecoration->sizeAlongAxis[ii]);
+ }
+ emit(")]\n");
+ }
+
auto name = getName(func);
emitIRType(context, resultType, name);
@@ -4535,6 +4925,8 @@ emitDeclImpl(decl, nullptr);
auto paramName = getName(pp);
emitIRType(context, pp->getType(), paramName);
+
+ emitIRSemantics(context, pp);
}
emit(")");
@@ -4548,6 +4940,10 @@ emitDeclImpl(decl, nullptr);
emit("\n{\n");
// Need to emit the operations in the blocks of the function
+
+ emitIRStmtsForBlocks(context, func->getFirstBlock(), nullptr);
+
+#if 0
for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
{
// TODO: need to handle control flow and so forth...
@@ -4556,6 +4952,7 @@ emitDeclImpl(decl, nullptr);
emitIRInst(context, ii);
}
}
+#endif
emit("}\n");
}
@@ -4687,7 +5084,8 @@ emitDeclImpl(decl, nullptr);
IRVar* varDecl)
{
auto allocatedType = varDecl->getType();
- auto varType = ((IRPtrType*) allocatedType)->getValueType();
+ auto varType = allocatedType->getValueType();
+ auto addressSpace = allocatedType->getAddressSpace();
switch( varType->op )
{
@@ -4706,6 +5104,16 @@ emitDeclImpl(decl, nullptr);
emitIRVarModifiers(context, layout);
+ switch (addressSpace)
+ {
+ default:
+ break;
+
+ case kIRAddressSpace_GroupShared:
+ emit("groupshared ");
+ break;
+ }
+
emitIRType(context, varType, getName(varDecl));
emitIRSemantics(context, varDecl);