summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/compiler.h1
-rw-r--r--source/slang/diagnostic-defs.h4
-rw-r--r--source/slang/diagnostics.cpp6
-rw-r--r--source/slang/diagnostics.h4
-rw-r--r--source/slang/emit.cpp5
-rw-r--r--source/slang/ir-constexpr.cpp6
-rw-r--r--source/slang/ir-inst-defs.h60
-rw-r--r--source/slang/ir-insts.h23
-rw-r--r--source/slang/ir-legalize-types.cpp3
-rw-r--r--source/slang/ir-ssa.cpp15
-rw-r--r--source/slang/ir-validate.cpp182
-rw-r--r--source/slang/ir-validate.h35
-rw-r--r--source/slang/ir.cpp509
-rw-r--r--source/slang/ir.h15
-rw-r--r--source/slang/lower-to-ir.cpp44
-rw-r--r--source/slang/options.cpp4
-rw-r--r--source/slang/slang.natvis6
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters2
19 files changed, 761 insertions, 165 deletions
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index a91e03c55..6d0475a05 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -275,6 +275,7 @@ namespace Slang
bool shouldDumpIntermediates = false;
bool shouldDumpIR = false;
+ bool shouldValidateIR = false;
bool shouldSkipCodegen = false;
// How should `#line` directives be emitted (if at all)?
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index 06e553072..14efdb9a9 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -286,7 +286,9 @@ DIAGNOSTIC(40005, Error, topLevelModuleUsedWithoutSpecifyingBinding, "top level
DIAGNOSTIC(49999, Error, unknownSystemValueSemantic, "unknown system-value semantic '$0'")
-DIAGNOSTIC(40006, Error, needCompileTimeConstant, "expected a compile-time constant");
+DIAGNOSTIC(40006, Error, needCompileTimeConstant, "expected a compile-time constant")
+
+DIAGNOSTIC(40007, Internal, irValidationFailed, "IR validation failed: $0")
//
// 5xxxx - Target code generation.
diff --git a/source/slang/diagnostics.cpp b/source/slang/diagnostics.cpp
index 64713072d..03acf50dc 100644
--- a/source/slang/diagnostics.cpp
+++ b/source/slang/diagnostics.cpp
@@ -93,6 +93,12 @@ SourceLoc const& getDiagnosticPos(TypeExp const& typeExp)
return typeExp.exp->loc;
}
+SourceLoc const& getDiagnosticPos(IRInst* inst)
+{
+ return inst->sourceLoc;
+}
+
+
// Take the format string for a diagnostic message, along with its arguments, and turn it into a
static void formatDiagnosticMessage(StringBuilder& sb, char const* format, int argCount, DiagnosticArg const* const* args)
{
diff --git a/source/slang/diagnostics.h b/source/slang/diagnostics.h
index f92df030b..9bc3b3d8c 100644
--- a/source/slang/diagnostics.h
+++ b/source/slang/diagnostics.h
@@ -94,11 +94,13 @@ namespace Slang
inline SourceLoc const& getDiagnosticPos(SourceLoc const& pos) { return pos; }
class SyntaxNode;
- class ShaderClosure;
SourceLoc const& getDiagnosticPos(SyntaxNode const* syntax);
SourceLoc const& getDiagnosticPos(Token const& token);
SourceLoc const& getDiagnosticPos(TypeExp const& typeExp);
+ struct IRInst;
+ SourceLoc const& getDiagnosticPos(IRInst* inst);
+
template<typename T>
SourceLoc getDiagnosticPos(RefPtr<T> const& ptr)
{
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 189c1c4bd..fa090121f 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -3,6 +3,7 @@
#include "ir-insts.h"
#include "ir-ssa.h"
+#include "ir-validate.h"
#include "legalize-types.h"
#include "lower-to-ir.h"
#include "mangle.h"
@@ -8200,6 +8201,7 @@ String emitEntryPoint(
typeLegalizationContext.session = entryPoint->compileRequest->mSession;
IRModule* irModule = getIRModule(irSpecializationState);
+ auto compileRequest = translationUnit->compileRequest;
typeLegalizationContext.irModule = irModule;
@@ -8208,6 +8210,8 @@ String emitEntryPoint(
entryPoint,
&sharedContext.extensionUsageTracker);
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
// If the user specified the flag that they want us to dump
// IR, then do it here, for the target-specific, but
// un-specialized IR.
@@ -8254,6 +8258,7 @@ String emitEntryPoint(
// so that we can work with the individual fields).
constructSSA(irModule);
+ validateIRModuleIfEnabled(compileRequest, irModule);
// After all of the required optimization and legalization
// passes have been performed, we can emit target code from
diff --git a/source/slang/ir-constexpr.cpp b/source/slang/ir-constexpr.cpp
index 8463851ac..ca64f5f04 100644
--- a/source/slang/ir-constexpr.cpp
+++ b/source/slang/ir-constexpr.cpp
@@ -234,8 +234,7 @@ bool propagateConstExprBackward(
IRBuilder builder;
builder.sharedBuilder = &sharedBuilder;
- builder.curFunc = code;
- builder.curBlock = nullptr;
+ builder.setInsertInto(code);
bool anyChanges = false;
for(;;)
@@ -432,9 +431,6 @@ void propagateConstExpr(
context.sharedBuilder.module = module;
context.sharedBuilder.session = session;
context.builder.sharedBuilder = &context.sharedBuilder;
- context.builder.curFunc = nullptr;
- context.builder.curBlock = nullptr;
-
// We need to propagate information both forward and backward.
diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h
index cac3c086e..fbb3912d8 100644
--- a/source/slang/ir-inst-defs.h
+++ b/source/slang/ir-inst-defs.h
@@ -115,22 +115,25 @@ INST(makeStruct, makeStruct, 0, 0)
INST(Call, call, 1, 0)
-INST(Module, module, 0, PARENT)
+/*IRParentInst*/
-INST(Block, block, 0, PARENT)
+ INST(Module, module, 0, PARENT)
-/*IRGlobalValue*/
+ INST(Block, block, 0, PARENT)
- /*IRGlobalValueWithCode*/
- INST(Func, func, 0, PARENT)
- INST(global_var, global_var, 0, 0)
- INST(global_constant, global_constant, 0, 0)
- INST_RANGE(GlobalValueWithCode, Func, global_constant)
+ /*IRGlobalValue*/
- INST(witness_table, witness_table, 0, 0)
+ /*IRGlobalValueWithCode*/
+ INST(Func, func, 0, PARENT)
+ INST(global_var, global_var, 0, 0)
+ INST(global_constant, global_constant, 0, 0)
+ INST_RANGE(GlobalValueWithCode, Func, global_constant)
+
+ INST(witness_table, witness_table, 0, 0)
INST_RANGE(GlobalValue, Func, witness_table)
+INST_RANGE(ParentInst, Module, witness_table)
INST(witness_table_entry, witness_table_entry, 2, 0)
@@ -241,37 +244,10 @@ INST(BitOr, or , 2, 0)
INST(And, logicalAnd, 2, 0)
INST(Or, logicalOr, 2, 0)
-#if 0
-INST(Assign, assign, 2, 0)
-INST(AddAssign, addAssign, 2, 0)
-INST(SubAssign, subAssign, 2, 0)
-INST(SubAssign, subAssign, 2, 0)
-
-INTRINSIC(SubAssign)
-INTRINSIC(MulAssign)
-INTRINSIC(DivAssign)
-INTRINSIC(ModAssign)
-INTRINSIC(LshAssign)
-INTRINSIC(RshAssign)
-INTRINSIC(OrAssign)
-INTRINSIC(AndAssign)
-INTRINSIC(XorAssign)
-INTRINSIC(Pos)
-#endif
-
INST(Neg, neg, 1, 0)
INST(Not, not, 1, 0)
INST(BitNot, bitnot, 1, 0)
-#if 0
-INTRINSIC(PreInc)
-INTRINSIC(PreDec)
-INTRINSIC(PostInc)
-INTRINSIC(PostDec)
-
-INTRINSIC(Sequence)
-#endif
-
INST(Select, select, 3, 0)
INST(Dot, dot, 2, 0)
@@ -280,18 +256,6 @@ INST(Mul_Vector_Matrix, mulVectorMatrix, 2, 0)
INST(Mul_Matrix_Vector, mulMatrixVector, 2, 0)
INST(Mul_Matrix_Matrix, mulMatrixMatrix, 2, 0)
-#if 0
-INTRINSIC(Mul_Scalar_Scalar)
-INTRINSIC(Mul_Vector_Scalar)
-INTRINSIC(Mul_Scalar_Vector)
-INTRINSIC(Mul_Matrix_Scalar)
-INTRINSIC(Mul_Scalar_Matrix)
-INTRINSIC(InnerProduct_Vector_Vector)
-INTRINSIC(InnerProduct_Vector_Matrix)
-INTRINSIC(InnerProduct_Matrix_Vector)
-INTRINSIC(InnerProduct_Matrix_Matrix)
-#endif
-
// Texture sampling operation of the form `t.Sample(s,u)`
INST(Sample, sample, 3, 0)
diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h
index a3fd97351..a7109948f 100644
--- a/source/slang/ir-insts.h
+++ b/source/slang/ir-insts.h
@@ -430,16 +430,23 @@ struct IRBuilder
IRModule* getModule() { return sharedBuilder->module; }
- // The current function and block being inserted into
- // (or `null` if we aren't inserting).
- IRGlobalValueWithCode* curFunc = nullptr;
- IRBlock* curBlock = nullptr;
+ // The current parent being inserted into (this might
+ // be the global scope, a function, a block inside
+ // a function, etc.)
+ IRParentInst* insertIntoParent = nullptr;
//
- // An instruction in the current block that we should insert before
- IRInst* insertBeforeInst = nullptr;
+ // An instruction in the current parent that we should insert before
+ IRInst* insertBeforeInst = nullptr;
- IRGlobalValueWithCode* getFunc() { return curFunc; }
- IRBlock* getBlock() { return curBlock; }
+ // Get the current basic block we are inserting into (if any)
+ IRBlock* getBlock();
+
+ // Get the current function (or other value with code)
+ // that we are inserting into (if any).
+ IRGlobalValueWithCode* getFunc();
+
+ void setInsertInto(IRParentInst* insertInto);
+ void setInsertBefore(IRInst* insertBefore);
IRBuilderSourceLocRAII* sourceLocInfo = nullptr;
diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp
index 4570ecf81..7e380e237 100644
--- a/source/slang/ir-legalize-types.cpp
+++ b/source/slang/ir-legalize-types.cpp
@@ -762,8 +762,7 @@ static LegalVal legalizeInst(
// instructions generated will be placed after
// the location of the original instruction.
auto builder = context->builder;
- builder->curBlock = as<IRBlock>(inst->getParent());
- builder->insertBeforeInst = inst->getNextInst();
+ builder->setInsertBefore(inst->getNextInst());
LegalVal legalVal = legalizeInst(
context,
diff --git a/source/slang/ir-ssa.cpp b/source/slang/ir-ssa.cpp
index ef1f8c4d8..60ecddfbd 100644
--- a/source/slang/ir-ssa.cpp
+++ b/source/slang/ir-ssa.cpp
@@ -563,7 +563,7 @@ void processBlock(
// Any new instructions we create to represent
// the new value will get inserted before whatever
// instruction we are working with.
- blockInfo->builder.insertBeforeInst = ii;
+ blockInfo->builder.setInsertBefore(ii);
switch (ii->op)
{
@@ -617,7 +617,7 @@ void processBlock(
}
}
- blockInfo->builder.insertBeforeInst = block->getLastChild();
+ blockInfo->builder.setInsertBefore(block->getLastChild());
// Once we are done with all of the instructions
// in a block, we can mark it as "filled," which
@@ -709,8 +709,7 @@ static void breakCriticalEdges(
IRBuilder builder;
builder.sharedBuilder = &context->sharedBuilder;
- builder.curFunc = globalVal;
- builder.curBlock = pred;
+ builder.setInsertInto(pred);
// Create a new block that will sit "along" the edge
IRBlock* edgeBlock = builder.createBlock();
@@ -723,7 +722,7 @@ static void breakCriticalEdges(
// The edge block should branch (unconditionally)
// to the successor block.
- builder.curBlock = edgeBlock;
+ builder.setInsertInto(edgeBlock);
builder.emitBranch(succ);
// Insert the new block into the block list
@@ -763,9 +762,7 @@ void constructSSA(ConstructSSAContext* context)
blockInfo->block = bb;
blockInfo->builder.sharedBuilder = &context->sharedBuilder;
- blockInfo->builder.curBlock = bb;
- blockInfo->builder.curFunc = globalVal;
- blockInfo->builder.insertBeforeInst = bb->getLastInst();
+ blockInfo->builder.setInsertBefore(bb->getLastInst());
context->blockInfos.Add(bb, blockInfo);
}
@@ -830,7 +827,7 @@ void constructSSA(ConstructSSAContext* context)
IRTerminatorInst* oldTerminator = bb->getTerminator();
assert(oldTerminator);
- blockInfo->builder.insertBeforeInst = nullptr;
+ blockInfo->builder.setInsertInto(bb);
auto oldArgCount = oldTerminator->getOperandCount();
auto newArgCount = oldArgCount + addedArgCount;
diff --git a/source/slang/ir-validate.cpp b/source/slang/ir-validate.cpp
new file mode 100644
index 000000000..95b8f2dff
--- /dev/null
+++ b/source/slang/ir-validate.cpp
@@ -0,0 +1,182 @@
+// ir-validate.cpp
+#include "ir-validate.h"
+
+#include "ir.h"
+#include "ir-insts.h"
+
+namespace Slang
+{
+ struct IRValidateContext
+ {
+ // The IR module we are validating.
+ IRModule* module;
+
+ // A diagnostic sink to send errors to if anything is invalid.
+ DiagnosticSink* sink;
+
+ DiagnosticSink* getSink() { return sink; }
+
+ // A set of instructions we've seen, to help confirm that
+ // values are defined before they are used in a given block.
+ HashSet<IRInst*> seenInsts;
+ };
+
+ void validateIRInst(
+ IRValidateContext* context,
+ IRInst* inst);
+
+ void validate(IRValidateContext* context, bool condition, IRInst* inst, char const* message)
+ {
+ if (!condition)
+ {
+ context->getSink()->diagnose(inst, Diagnostics::irValidationFailed, message);
+ }
+ }
+
+ void validateIRInstChildren(
+ IRValidateContext* context,
+ IRParentInst* parent)
+ {
+ IRInst* prevChild = nullptr;
+ for (auto child = parent->getFirstChild(); child; child = child->getNextInst())
+ {
+ // We need to check the integrity of the parent/next/prev links of
+ // all of our instructions
+ validate(context, child->parent == parent, child, "parent link");
+ validate(context, child->prev == prevChild, child, "next/prev link");
+
+ // Recursively validate the instruction itself.
+ validateIRInst(context, child);
+
+ prevChild = child;
+ }
+ }
+
+ void validateIRInstOperand(
+ IRValidateContext* context,
+ IRInst* inst,
+ IRUse* operandUse)
+ {
+ // The `IRUse` for the operand had better have `inst` as its user.
+ validate(context, operandUse->getUser() == inst, inst, "operand user");
+
+ // The value we are using needs to fit into one of a few cases.
+ //
+ // * If the parent of `inst` and of `operand` is the same block, then
+ // we require that `operand` is defined before `inst`
+ //
+ // * If the parents of `inst` and `operand` are both blocks in the
+ // same functin, then the block defining `operand` must dominate
+ // the block defining `inst`.
+ //
+ // * Otherwise, we simply require that the parent of `operand` be
+ // an ancestor (transitive parent) of `inst`.
+
+ auto instParent = inst->getParent();
+
+ auto operandValue = operandUse->get();
+ auto operandParent = operandValue->getParent();
+
+ if (auto instParentBlock = as<IRBlock>(instParent))
+ {
+ if (auto operandParentBlock = as<IRBlock>(operandParent))
+ {
+ if (instParentBlock == operandParentBlock)
+ {
+ // If `operandValue` precedes `inst`, then we should
+ // have already seen it, because we scan parent instructions
+ // in order.
+ validate(context, context->seenInsts.Contains(operandValue), inst, "def must come before use in same block");
+ return;
+ }
+
+ auto instFunc = instParentBlock->getParent();
+ auto operandFunc = operandParentBlock->getParent();
+ if (instFunc == operandFunc)
+ {
+ // The two instructions are defined in different blocks of
+ // the same function (or another value with code). We need
+ // to validate that `operandParentBlock` dominates `instParentBlock`.
+ //
+ // TODO: implement this validation once we compute dominator trees.
+ //
+ // validate(context, operandParentBlock->dominates(instParentBlock), inst, "def must dominate use");
+ return;
+ }
+ }
+ }
+
+ // If the special cases above did not trigger, then either the two values
+ // are nested in the same parent, but that parent isn't a block, or they
+ // are nested in distinct parents, and those parents aren't both children
+ // of a function.
+ //
+ // In either case, we need to enforce that the parent of `operand` needs
+ // to be an ancestor of `inst`.
+ //
+ for (auto pp = instParent; pp; pp = pp->getParent())
+ {
+ if (pp == operandParent)
+ return;
+ }
+ //
+ // We failed to find `operandParent` while walking the ancestors of `inst`,
+ // so something had gone wrong.
+ validate(context, false, inst, "def must be ancestor of use");
+ }
+
+ void validateIRInstOperands(
+ IRValidateContext* context,
+ IRInst* inst)
+ {
+ UInt operandCount = inst->getOperandCount();
+ for (UInt ii = 0; ii < operandCount; ++ii)
+ {
+ validateIRInstOperand(context, inst, inst->getOperands() + ii);
+ }
+ }
+
+ void validateIRInst(
+ IRValidateContext* context,
+ IRInst* inst)
+ {
+ // Validate that any operands of the instruction are used appropriately
+ validateIRInstOperands(context, inst);
+ context->seenInsts.Add(inst);
+
+ // If `inst` is itself a parent instruction, then we need to recursively
+ // validate its children.
+ if (auto parent = as<IRParentInst>(inst))
+ {
+ validateIRInstChildren(context, parent);
+ }
+ }
+
+ void validateIRModule(IRModule* module, DiagnosticSink* sink)
+ {
+ IRValidateContext contextStorage;
+ IRValidateContext* context = &contextStorage;
+ context->module = module;
+ context->sink = sink;
+
+ auto moduleInst = module->moduleInst;
+
+ validate(context, moduleInst != nullptr, moduleInst, "module instruction");
+ validate(context, moduleInst->parent == nullptr, moduleInst, "module instruction parent");
+ validate(context, moduleInst->prev == nullptr, moduleInst, "module instruction prev");
+ validate(context, moduleInst->next == nullptr, moduleInst, "module instruction next");
+
+ validateIRInst(context, module->moduleInst);
+ }
+
+ void validateIRModuleIfEnabled(
+ CompileRequest* compileRequest,
+ IRModule* module)
+ {
+ if (!compileRequest->shouldValidateIR)
+ return;
+
+ auto sink = &compileRequest->mSink;
+ validateIRModule(module, sink);
+ }
+}
diff --git a/source/slang/ir-validate.h b/source/slang/ir-validate.h
new file mode 100644
index 000000000..0ebc69019
--- /dev/null
+++ b/source/slang/ir-validate.h
@@ -0,0 +1,35 @@
+// ir-validate.h
+#pragma once
+
+namespace Slang
+{
+ class CompileRequest;
+ class DiagnosticSink;
+ struct IRModule;
+
+
+ // Validate that an IR module obeys the invariants we need to enforce.
+ // For example:
+ //
+ // * Confirm that linked lists for children and for use-def chains are consistent
+ // (e.g., x.next.prev == x)
+ //
+ // * Confirm that parent/child relationships are correct (e.g., if is `x` is in
+ // `y.children`, then `x.parent == y`
+ //
+ // * Confirm that every operand of an instruction is valid to reference (i.e., it
+ // must either be defined earlier in the same block, in a different block that
+ // dominates the current one, or in a parent instruction of the block.
+ //
+ // * Confirm that every block ends with a terminator, and there are no terminators
+ // elsewhere in a block.
+ //
+ // * Confirm that all the parameters of a block come before any "ordinary" instructions.
+ void validateIRModule(IRModule* module, DiagnosticSink* sink);
+
+ // A wrapper that calls `validateIRModule` only when IR validation is enabled
+ // for the given compile request.
+ void validateIRModuleIfEnabled(
+ CompileRequest* compileRequest,
+ IRModule* module);
+}
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index b4d19c0d5..aeea83760 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -193,6 +193,48 @@ namespace Slang
}
}
+ IRInst* IRBlock::getFirstOrdinaryInst()
+ {
+ // Find the last parameter (if any) of the block
+ auto lastParam = getLastParam();
+ if (lastParam)
+ {
+ // If there is a last parameter, then the
+ // instructions after it are the ordinary
+ // instructions.
+ return lastParam->getNextInst();
+ }
+ else
+ {
+ // If there isn't a last parameter, then
+ // there must not have been *any* parameters,
+ // and so the first instruction in the block
+ // is also the first ordinary one.
+ return getFirstInst();
+ }
+ }
+
+ IRInst* IRBlock::getLastOrdinaryInst()
+ {
+ // Under normal circumstances, the last instruction
+ // in the block is also the last ordinary instruction.
+ // However, there is the special case of a block with
+ // only parameters (which might happen as a temporary
+ // state while we are building IR).
+ auto inst = getLastInst();
+
+ // If the last instruction is a parameter, then
+ // there are no ordinary instructions, so the last
+ // one is a null pointer.
+ if (as<IRParam>(inst))
+ return nullptr;
+
+ // Otherwise the last instruction is the last "ordinary"
+ // instruction as well.
+ return inst;
+ }
+
+
// The predecessors of a block should all show up as users
// of its value, so rather than explicitly store the CFG,
// we will recover it on demand from the use-def information.
@@ -423,6 +465,38 @@ namespace Slang
//
+ IRBlock* IRBuilder::getBlock()
+ {
+ return as<IRBlock>(insertIntoParent);
+ }
+
+ // Get the current function (or other value with code)
+ // that we are inserting into (if any).
+ IRGlobalValueWithCode* IRBuilder::getFunc()
+ {
+ auto pp = insertIntoParent;
+ if (auto block = as<IRBlock>(pp))
+ {
+ pp = pp->getParent();
+ }
+ return as<IRGlobalValueWithCode>(pp);
+ }
+
+
+ void IRBuilder::setInsertInto(IRParentInst* insertInto)
+ {
+ insertIntoParent = insertInto;
+ insertBeforeInst = nullptr;
+ }
+
+ void IRBuilder::setInsertBefore(IRInst* insertBefore)
+ {
+ SLANG_ASSERT(insertBefore);
+ insertIntoParent = insertBefore->parent;
+ insertBeforeInst = insertBefore;
+ }
+
+
// Add an instruction into the current scope
void IRBuilder::addInst(
IRInst* inst)
@@ -430,11 +504,10 @@ namespace Slang
if(insertBeforeInst)
{
inst->insertBefore(insertBeforeInst);
- return;
}
- else if (curBlock)
+ else if (insertIntoParent)
{
- inst->insertAtEnd(curBlock);
+ inst->insertAtEnd(insertIntoParent);
}
else
{
@@ -442,6 +515,192 @@ namespace Slang
}
}
+ // Given two parent instructions, pick the better one to use as as
+ // insertion location for a "hoistable" instruction.
+ //
+ IRParentInst* mergeCandidateParentsForHoistableInst(IRParentInst* left, IRParentInst* right)
+ {
+ // If either `left` or `right` is a block, then we need to be
+ // a bit careful, because blocks can see other values just using
+ // the dominance relationship, without a direct parent-child relationship.
+ //
+ // First, check if each of `left` and `right` is a block.
+ //
+ auto leftBlock = as<IRBlock>(left);
+ auto rightBlock = as<IRBlock>(right);
+ //
+ // As a special case, if both of these are blocks in the same parent,
+ // then we need to pick between them based on dominance.
+ //
+ if (leftBlock && rightBlock && (leftBlock->getParent() == rightBlock->getParent()))
+ {
+ // We assume that the order of basic blocks in a function is compatible
+ // with the dominance relationship (that is, if A dominates B, then
+ // A comes before B in the list of blocks), so it suffices to pick
+ // the *later* of the two blocks.
+ //
+ // There are ways we could try to speed up this search, but no matter
+ // what it will be O(n) in the number of blocks, unless we build
+ // an explicit dominator tree, which is infeasible during IR building.
+ // Thus we just do a simple linear walk here.
+ //
+ // We will start at `leftBlock` and walk forward, until either...
+ //
+ for (auto ll = leftBlock; ll; ll = ll->getNextBlock())
+ {
+ // ... we see `rightBlock` (in which case `rightBlock` came later), or ...
+ //
+ if (ll == rightBlock) return rightBlock;
+ }
+ //
+ // ... we run out of blocks (in which case `leftBlock` came later).
+ //
+ return leftBlock;
+ }
+
+ //
+ // If the special case above doesn't apply, then `left` or `right` might
+ // still be a block, but they aren't blocks nested in the same function.
+ // We will find the first non-block ancestor of `left` and/or `right`.
+ // This will either be the inst itself (it is isn't a block), or
+ // its immediate parent (if it *is* a block).
+ //
+ auto leftNonBlock = leftBlock ? leftBlock->getParent() : left;
+ auto rightNonBlock = rightBlock ? rightBlock->getParent() : right;
+
+ // If either side is null, then take the non-null one.
+ //
+ if (!leftNonBlock) return right;
+ if (!rightNonBlock) return left;
+
+ // If the non-block on the left or right is a descendent of
+ // the other, then that is what we should use.
+ //
+ IRParentInst* parentNonBlock = nullptr;
+ for (auto ll = leftNonBlock; ll; ll = ll->getParent())
+ {
+ if (ll == rightNonBlock)
+ {
+ parentNonBlock = leftNonBlock;
+ break;
+ }
+ }
+ for (auto rr = rightNonBlock; rr; rr = rr->getParent())
+ {
+ if (rr == leftNonBlock)
+ {
+ SLANG_ASSERT(!parentNonBlock);
+ parentNonBlock = rightNonBlock;
+ break;
+ }
+ }
+
+ // As a matter of validity in the IR, we expect one
+ // of the two to be an ancestor (in the non-block case),
+ // because otherwise we'd be violating the basic dominance
+ // assumptions.
+ //
+ SLANG_ASSERT(parentNonBlock);
+
+ // As a fallback, try to use the left parent as a default
+ // in case things go badly.
+ //
+ if (!parentNonBlock)
+ {
+ parentNonBlock = leftNonBlock;
+ }
+
+ IRParentInst* parent = parentNonBlock;
+
+ // At this point we've found a non-block parent where we
+ // could stick things, but we have to fix things up in
+ // case we should be inserting into a block beneath
+ // that non-block parent.
+ if (leftBlock && (parentNonBlock == leftNonBlock))
+ {
+ // We have a left block, and have picked its parent.
+
+ // It cannot be the case that there is a right block
+ // with the same parent, or else our special case
+ // would have triggered at the start.
+ SLANG_ASSERT(!rightBlock || (parentNonBlock != rightNonBlock));
+
+ parent = leftBlock;
+ }
+ else if (rightBlock && (parentNonBlock == rightNonBlock))
+ {
+ // We have a right block, and have picked its parent.
+
+ // We already tested above, so we know there isn't a
+ // matching situation on the left side.
+
+ parent = rightBlock;
+ }
+
+ // Okay, we've picked the parent we want to insert into,
+ // *but* one last special case arises, because an `IRGlobalValueWithCode`
+ // is not actually a suitable place to insert instructions.
+ // Furthermore, there is no actual need to insert instructions at
+ // that scope, because any parameters, etc. are actually attached
+ // to the block(s) within the function.
+ if (auto parentFunc = as<IRGlobalValueWithCode>(parent))
+ {
+ // Insert in the parent of the function (or other value with code).
+ // We know that the parent must be able to hold ordinary instructions,
+ // because it was able to hold this `IRGlobalValueWithCode`
+ parent = parentFunc->getParent();
+ }
+
+ return parent;
+ }
+
+ // Given an instruction that represents a constant, a type, etc.
+ // Try to "hoist" it as far toward the global scope as possible
+ // to insert it at a location where it will be maximally visible.
+ //
+ void addHoistableInst(
+ IRBuilder* builder,
+ IRInst* inst)
+ {
+ // Start with the assumption that we would insert this instruction
+ // into the global scope (the instruction that represents the module)
+ IRParentInst* parent = builder->getModule()->getModuleInst();
+
+ // The above decision might be invalid, because there might be
+ // one or more operands of the instruction that are defined in
+ // more deeply nested parents than the global scope.
+ //
+ // Therefore, we will scan the operands of the instruction, and
+ // look at the parents that define them.
+ //
+ UInt operandCount = inst->getOperandCount();
+ for (UInt ii = 0; ii < operandCount; ++ii)
+ {
+ auto operand = inst->getOperand(ii);
+ auto operandParent = operand->getParent();
+
+ parent = mergeCandidateParentsForHoistableInst(parent, operandParent);
+ }
+
+ // We better have ended up with a place to insert.
+ SLANG_ASSERT(parent);
+
+ // If we have chosen to insert into the same parent that the
+ // IRBuilder is configured to use, then respect its `insertBeforeInst`
+ // setting.
+ if (parent == builder->insertIntoParent)
+ {
+ builder->addInst(inst);
+ return;
+ }
+
+ // Otherwise, we just want to insert at the end of the chosen parent.
+ //
+ // TODO: be careful about inserting after the terminator of a block...
+
+ inst->insertAtEnd(parent);
+ }
+
static void maybeSetSourceLoc(
IRBuilder* builder,
IRInst* value)
@@ -747,11 +1006,6 @@ namespace Slang
UInt valueSize,
void const* value)
{
- // First, we need to pick a good insertion point
- // for the instruction, which we do by looking
- // at its operands.
- //
-
IRConstant keyInst;
memset(&keyInst, 0, sizeof(keyInst));
keyInst.op = op;
@@ -781,6 +1035,8 @@ namespace Slang
key.inst = irValue;
builder->sharedBuilder->constantMap.Add(key, irValue);
+ addHoistableInst(builder, irValue);
+
return irValue;
}
@@ -839,6 +1095,9 @@ namespace Slang
kIROp_decl_ref,
nullptr);
irValue->declRef = DeclRef<Decl>(declRef.decl, declRef.substitutions);
+
+ addHoistableInst(this, irValue);
+
return irValue;
}
@@ -1130,13 +1389,19 @@ namespace Slang
IRBlock* IRBuilder::emitBlock()
{
+ // Create a block
auto bb = createBlock();
- auto f = this->curFunc;
+ // If we are emitting into a function
+ // (or another value with code), then
+ // append the block to the function and
+ // set this block as the new parent for
+ // subsequent instructions we insert.
+ auto f = getFunc();
if (f)
{
f->addBlock(bb);
- this->curBlock = bb;
+ setInsertInto(bb);
}
return bb;
}
@@ -1155,8 +1420,7 @@ namespace Slang
IRType* type)
{
auto param = createParam(type);
-
- if (auto bb = curBlock)
+ if (auto bb = getBlock())
{
bb->addParam(param);
}
@@ -2236,16 +2500,45 @@ namespace Slang
dump(context, opInfo->name);
UInt argCount = inst->getOperandCount();
+ UInt ii = 0;
+
+ // Special case: make printing of `call` a bit
+ // nicer to look at
+ if (inst->op == kIROp_Call && argCount > 0)
+ {
+ dump(context, " ");
+ auto argVal = inst->getOperand(ii++);
+ dumpOperand(context, argVal);
+ }
+
+ bool first = true;
dump(context, "(");
- for (UInt ii = 0; ii < argCount; ++ii)
+ for (; ii < argCount; ++ii)
{
- if (ii != 0)
+ if (!first)
dump(context, ", ");
auto argVal = inst->getOperand(ii);
dumpOperand(context, argVal);
+
+ first = false;
}
+
+ // Special cases: literals and other instructions with no real operands
+ switch (inst->op)
+ {
+ case kIROp_IntLit:
+ case kIROp_FloatLit:
+ case kIROp_boolConst:
+ case kIROp_decl_ref:
+ dumpOperand(context, inst);
+ break;
+
+ default:
+ break;
+ }
+
dump(context, ")");
dump(context, "\n");
@@ -3613,7 +3906,7 @@ namespace Slang
shared.session = session;
IRBuilder builder;
builder.sharedBuilder = &shared;
- builder.curFunc = func;
+ builder.setInsertInto(func);
// We will start by looking at the return type of the
// function, because that will enable us to do an
@@ -3671,7 +3964,7 @@ namespace Slang
IRInst* returnValue = returnInst->getVal();
// Make sure we add these instructions to the right block
- builder.curBlock = bb;
+ builder.setInsertInto(bb);
// Write to our global variable(s) from the value being returned.
assign(&builder, resultGlobal, ScalarizedVal::value(returnValue));
@@ -3694,6 +3987,10 @@ namespace Slang
// and turn them into global variables.
if( auto firstBlock = func->getFirstBlock() )
{
+ // Any initialization code we insert for parameters needs
+ // to be at the start of the "ordinary" instructions in the block:
+ builder.setInsertBefore(firstBlock->getFirstOrdinaryInst());
+
UInt paramCounter = 0;
for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
{
@@ -3721,11 +4018,6 @@ namespace Slang
// cases.
auto paramType = pp->getDataType();
- // Any initialization code we insert nees to be at the start
- // of the block:
- builder.curBlock = firstBlock;
- builder.insertBeforeInst = firstBlock->getFirstInst();
-
// First we will special-case stage input/outputs that
// don't fit into the standard varying model.
// For right now we are only doing special-case handling
@@ -3787,8 +4079,7 @@ namespace Slang
// Okay, we have a declaration, and we want to modify it!
- builder.curBlock = bb;
- builder.insertBeforeInst = ii;
+ builder.setInsertBefore(ii);
assign(&builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2)));
}
@@ -3857,8 +4148,7 @@ namespace Slang
break;
}
- builder.curBlock = bb;
- builder.insertBeforeInst = terminatorInst;
+ builder.setInsertBefore(terminatorInst);
assign(&builder, globalOutputVal, localVal);
}
@@ -3898,6 +4188,7 @@ namespace Slang
for( auto pp = firstBlock->getFirstParam(); pp; )
{
auto next = pp->getNextParam();
+ pp->removeFromParent();
pp->deallocate();
pp = next;
}
@@ -4503,7 +4794,7 @@ namespace Slang
// Next we are going to clone the actual code.
IRBuilder builderStorage = *context->builder;
IRBuilder* builder = &builderStorage;
- builder->curFunc = clonedValue;
+ builder->setInsertInto(clonedValue);
// We will walk through the blocks of the function, and clone each of them.
@@ -4544,7 +4835,7 @@ namespace Slang
{
assert(cb);
- builder->curBlock = cb;
+ builder->setInsertInto(cb);
for (auto oi = ob->getFirstInst(); oi; oi = oi->getNextInst())
{
cloneInst(context, builder, oi);
@@ -5006,8 +5297,7 @@ namespace Slang
RefPtr<GlobalGenericParamSubstitution> createGlobalGenericParamSubstitution(
EntryPointRequest * entryPointRequest,
ProgramLayout * programLayout,
- IRSpecContext* context,
- IRModule* originalIRModule);
+ IRSpecContext* context);
struct IRSpecializationState
{
@@ -5070,7 +5360,7 @@ namespace Slang
context->builder = &sharedContext->builderStorage;
// Create the GlobalGenericParamSubstitution for substituting global generic types
// into user-provided type arguments
- auto globalParamSubst = createGlobalGenericParamSubstitution(entryPointRequest, programLayout, context, originalIRModule);
+ auto globalParamSubst = createGlobalGenericParamSubstitution(entryPointRequest, programLayout, context);
context->subst.globalGenParamSubstitutions = globalParamSubst;
@@ -5193,6 +5483,8 @@ namespace Slang
struct IRGenericSpecContext : IRSpecContextBase
{
+ IRSpecContextBase* parent = nullptr;
+
IRSharedSpecContext* getShared() { return shared; }
// Override the "maybe clone" logic so that we always clone
@@ -5216,7 +5508,10 @@ namespace Slang
if (context->getSymbols().TryGetValue(mangledName, symbol))
{
- return symbol->irGlobalValue;
+ // Note: the symbols always come from the source module,
+ // not the destination module, so we may need to clone
+ // them if we are doing an initialize specialization pass.
+ return cloneValue(context, symbol->irGlobalValue);
}
else
{
@@ -5231,7 +5526,8 @@ namespace Slang
subtypeWitness->sup));
if (context->getSymbols().TryGetValue(genericName, symbol))
{
- auto specInst = context->builder->emitSpecializeInst(subtypeWitness->sup, symbol->irGlobalValue, subDeclRef->declRef);
+ auto clonedSymbol = cloneValue(context, symbol->irGlobalValue);
+ auto specInst = context->builder->emitSpecializeInst(subtypeWitness->sup, clonedSymbol, subDeclRef->declRef);
return specInst;
}
else
@@ -5363,7 +5659,14 @@ namespace Slang
break;
default:
- return originalVal;
+ if (parent)
+ {
+ return parent->maybeCloneValue(originalVal);
+ }
+ else
+ {
+ return originalVal;
+ }
}
}
@@ -5463,11 +5766,17 @@ namespace Slang
}
IRFunc* getSpecializedFunc(
- IRSharedSpecContext* sharedContext,
- IRFunc* genericFunc,
- DeclRef<Decl> specDeclRef);
+ IRSharedSpecContext* sharedContext,
+ IRSpecContextBase* parentContext,
+ IRFunc* genericFunc,
+ DeclRef<Decl> specDeclRef);
- IRWitnessTable* specializeWitnessTable(IRSharedSpecContext * sharedContext, IRWitnessTable* originalTable, DeclRef<Decl> specDeclRef, IRWitnessTable* dstTable)
+ IRWitnessTable* specializeWitnessTable(
+ IRSharedSpecContext* sharedContext,
+ IRSpecContextBase* parentContext,
+ IRWitnessTable* originalTable,
+ DeclRef<Decl> specDeclRef,
+ IRWitnessTable* dstTable)
{
// First, we want to see if an existing specialization
// has already been made. To do that we will need to
@@ -5503,6 +5812,7 @@ namespace Slang
IRGenericSpecContext context;
context.shared = sharedContext;
+ context.parent = parentContext;
context.builder = &sharedContext->builderStorage;
context.subst = specDeclRef.substitutions;
context.subst.genericSubstitutions = newSubst;
@@ -5521,7 +5831,7 @@ namespace Slang
if (entry->satisfyingVal.get()->op == kIROp_Func)
{
IRFunc* func = (IRFunc*)entry->satisfyingVal.get();
- auto specFunc = getSpecializedFunc(sharedContext, func, specDeclRef);
+ auto specFunc = getSpecializedFunc(sharedContext, parentContext, func, specDeclRef);
entry->satisfyingVal.set(specFunc);
insertGlobalValueSymbol(sharedContext, specFunc);
}
@@ -5536,9 +5846,10 @@ namespace Slang
}
IRFunc* getSpecializedFunc(
- IRSharedSpecContext* sharedContext,
- IRFunc* genericFunc,
- DeclRef<Decl> specDeclRef)
+ IRSharedSpecContext* sharedContext,
+ IRSpecContextBase* parentContext,
+ IRFunc* genericFunc,
+ DeclRef<Decl> specDeclRef)
{
// First, we want to see if an existing specialization
// has already been made. To do that we will need to
@@ -5586,6 +5897,7 @@ namespace Slang
IRGenericSpecContext context;
context.shared = sharedContext;
+ context.parent = parentContext;
context.builder = &sharedContext->builderStorage;
context.subst = specDeclRef.substitutions;
context.subst.genericSubstitutions = newSubst;
@@ -5784,7 +6096,7 @@ namespace Slang
//
// We will first find or construct a specialized version
// of the callee funciton/
- auto specFunc = getSpecializedFunc(sharedContext, genericFunc, specDeclRef);
+ auto specFunc = getSpecializedFunc(sharedContext, nullptr, genericFunc, specDeclRef);
//
// Then we will replace the use sites for the `specialize`
// instruction with uses of the specialized function.
@@ -5797,7 +6109,7 @@ namespace Slang
{
// specialize a witness table
auto originalTable = (IRWitnessTable*)genericVal;
- auto specWitnessTable = specializeWitnessTable(sharedContext, originalTable, specDeclRef, nullptr);
+ auto specWitnessTable = specializeWitnessTable(sharedContext, nullptr, originalTable, specDeclRef, nullptr);
witnessTables.AddIfNotExists(specWitnessTable->mangledName, specWitnessTable);
specInst->replaceUsesWith(specWitnessTable);
specInst->removeAndDeallocate();
@@ -5823,7 +6135,7 @@ namespace Slang
IRWitnessTable* genTable = nullptr;
if (witnessTables.TryGetValue(genName, genTable))
{
- witnessTable = specializeWitnessTable(sharedContext, genTable, srcDeclRef, nullptr);
+ witnessTable = specializeWitnessTable(sharedContext, nullptr, genTable, srcDeclRef, nullptr);
witnessTables.AddIfNotExists(witnessTable->mangledName, witnessTable);
}
}
@@ -5890,11 +6202,23 @@ namespace Slang
RefPtr<GlobalGenericParamSubstitution> createGlobalGenericParamSubstitution(
EntryPointRequest * entryPointRequest,
ProgramLayout * programLayout,
- IRSpecContext* context,
- IRModule* originalIRModule)
+ IRSpecContext* context)
{
RefPtr<GlobalGenericParamSubstitution> globalParamSubst;
GlobalGenericParamSubstitution * curTailSubst = nullptr;
+
+ // Because we can't currently put `specialize` instructions inside
+ // witness tables, or at the global scope, we will track a set of
+ // witness tables that we need to clone, and then specialize
+ // from the original module(s) to get what we need.
+
+ struct WitnessTableCloneWorkItem
+ {
+ IRWitnessTable* dstTable;
+ IRWitnessTable* originalTable;
+ };
+ List<WitnessTableCloneWorkItem> witnessTablesToClone;
+
struct WitnessTableSpecializationWorkItem
{
IRWitnessTable* dstTable;
@@ -5902,6 +6226,10 @@ namespace Slang
DeclRef<Decl> specDeclRef;
};
List<WitnessTableSpecializationWorkItem> witnessTablesToSpecailize;
+
+ Dictionary<Name*, IRWitnessTable*> witnessTablesByName;
+ auto namePool = entryPointRequest->compileRequest->getNamePool();
+
for (auto param : programLayout->globalGenericParams)
{
auto paramSubst = new GlobalGenericParamSubstitution();
@@ -5920,41 +6248,64 @@ namespace Slang
{
if (subtypeWitness->sub->EqualsVal(paramSubst->actualType))
{
- auto witnessTableName = getMangledNameForConformanceWitness(subtypeWitness->sub, subtypeWitness->sup);
- auto findWitnessTableByName = [&](String name) -> IRGlobalValue*
+ auto witnessTableName = namePool->getName(getMangledNameForConformanceWitness(subtypeWitness->sub, subtypeWitness->sup));
+ auto findWitnessTableByName = [&](Name* name) -> IRWitnessTable*
{
- for (auto ii : originalIRModule->getGlobalInsts())
- {
- auto gv = as<IRGlobalValue>(ii);
- if (!gv)
- continue;
+ RefPtr<IRSpecSymbol> symbol;
+ if (!context->getSymbols().TryGetValue(name, symbol))
+ return nullptr;
- if (getText(gv->mangledName) == name)
- return gv;
- }
- return nullptr;
+ return (IRWitnessTable*) symbol->irGlobalValue;
};
- auto table = findWitnessTableByName(witnessTableName);
+
+ auto findCloneOfWitnessTableByName = [&](Name* name) -> IRWitnessTable*
+ {
+ IRWitnessTable* clonedTable = nullptr;
+ if (witnessTablesByName.TryGetValue(name, clonedTable))
+ return clonedTable;
+
+ IRWitnessTable* originalTable = findWitnessTableByName(name);
+ if (!originalTable)
+ return nullptr;
+
+ clonedTable = context->builder->createWitnessTable();
+
+ WitnessTableCloneWorkItem cloneWorkItem;
+ cloneWorkItem.originalTable = originalTable;
+ cloneWorkItem.dstTable = clonedTable;
+ witnessTablesToClone.Add(cloneWorkItem);
+
+ return clonedTable;
+ };
+
+ // First look for a non-generic witness table that matches
+ auto table = findCloneOfWitnessTableByName(witnessTableName);
if (!table)
{
+ // If we didn't find a non-generic table, then maybe we are looking at
+ // a specialization of a generic witness table.
if (auto subDeclRefType = subtypeWitness->sub.As<DeclRefType>())
{
auto defaultSubst = createDefaultSubstitutions(entryPointRequest->compileRequest->mSession, subDeclRefType->declRef.getDecl());
- auto genericWitnessTableName = getMangledNameForConformanceWitness(DeclRef<Decl>(subDeclRefType->declRef.getDecl(), defaultSubst), subtypeWitness->sup);
- table = findWitnessTableByName(genericWitnessTableName);
- SLANG_ASSERT(table);
- WitnessTableSpecializationWorkItem workItem;
- workItem.srcTable = (IRWitnessTable*)cloneGlobalValue(context, (IRWitnessTable*)(table));
- workItem.dstTable = context->builder->createWitnessTable();
- workItem.dstTable->mangledName = context->getModule()->session->getNameObj(getMangledNameForConformanceWitness(subDeclRefType->declRef, subtypeWitness->sup));
- workItem.specDeclRef = subDeclRefType->declRef;
- witnessTablesToSpecailize.Add(workItem);
- table = workItem.dstTable;
+ auto genericWitnessTableName = namePool->getName(
+ getMangledNameForConformanceWitness(DeclRef<Decl>(subDeclRefType->declRef.getDecl(), defaultSubst), subtypeWitness->sup));
+
+ IRWitnessTable* genericTable = findCloneOfWitnessTableByName(genericWitnessTableName);
+ SLANG_ASSERT(genericTable);
+
+ WitnessTableSpecializationWorkItem specializeWorkItem;
+ specializeWorkItem.srcTable = genericTable;
+ specializeWorkItem.dstTable = context->builder->createWitnessTable();
+ specializeWorkItem.dstTable->mangledName = context->getModule()->session->getNameObj(getMangledNameForConformanceWitness(subDeclRefType->declRef, subtypeWitness->sup));
+ specializeWorkItem.specDeclRef = subDeclRefType->declRef;
+
+ witnessTablesToSpecailize.Add(specializeWorkItem);
+ table = specializeWorkItem.dstTable;
}
}
- else
- table = cloneGlobalValue(context, (IRWitnessTable*)(table));
+ // We expect to find the table no matter what.
SLANG_ASSERT(table);
+
IRProxyVal * tableVal = new IRProxyVal();
tableVal->inst.init(nullptr, table);
paramSubst->witnessTables.Add(KeyValuePair<RefPtr<Type>, RefPtr<Val>>(subtypeWitness->sup, tableVal));
@@ -5962,12 +6313,26 @@ namespace Slang
}
}
}
+
+ for (auto workItem : witnessTablesToClone)
+ {
+ cloneWitnessTableWithoutRegistering(
+ context,
+ workItem.originalTable,
+ workItem.dstTable);
+ }
+
for (auto workItem : witnessTablesToSpecailize)
{
int diff = 0;
- specializeWitnessTable(context->shared, workItem.srcTable,
- workItem.specDeclRef.SubstituteImpl(SubstitutionSet(nullptr, nullptr, globalParamSubst), &diff), workItem.dstTable);
+ specializeWitnessTable(
+ context->shared,
+ context,
+ workItem.srcTable,
+ workItem.specDeclRef.SubstituteImpl(SubstitutionSet(nullptr, nullptr, globalParamSubst), &diff),
+ workItem.dstTable);
}
+
return globalParamSubst;
}
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 95a7a97d1..35a6915f6 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -431,6 +431,11 @@ struct IRParentInst : IRInst
IRInst* getFirstChild() { return children.first; }
IRInst* getLastChild() { return children.last; }
IRInstListBase getChildren() { return children; }
+
+ static bool isaImpl(IROp op)
+ {
+ return (op >= kIROp_FirstParentInst) && (op <= kIROp_LastParentInst);
+ }
};
// A basic block is a parent instruction that adds the constraint
@@ -475,6 +480,16 @@ struct IRBlock : IRParentInst
void addParam(IRParam* param);
+ // The "ordinary" instructions come after the parameters
+ IRInst* getFirstOrdinaryInst();
+ IRInst* getLastOrdinaryInst();
+ IRInstList<IRInst> getOrdinaryInsts()
+ {
+ return IRInstList<IRInst>(
+ getFirstOrdinaryInst(),
+ getLastOrdinaryInst());
+ }
+
// The parent of a basic block is assumed to be a
// value with code (e.g., a function, global variable
// with initializer, etc.).
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 9508bb86d..cfd1c89a4 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -7,6 +7,7 @@
#include "ir-constexpr.h"
#include "ir-insts.h"
#include "ir-ssa.h"
+#include "ir-validate.h"
#include "mangle.h"
#include "type-layout.h"
#include "visitor.h"
@@ -1931,8 +1932,8 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
{
auto builder = getBuilder();
- auto prevBlock = builder->curBlock;
- auto parentFunc = prevBlock ? prevBlock->getParent() : builder->curFunc;
+ auto prevBlock = builder->getBlock();
+ auto parentFunc = prevBlock ? prevBlock->getParent() : builder->getFunc();
// If the previous block doesn't already have
// a terminator instruction, then be sure to
@@ -1944,8 +1945,7 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
parentFunc->addBlock(block);
- builder->curFunc = parentFunc;
- builder->curBlock = block;
+ builder->setInsertInto(block);
}
// Start a new block at the current location.
@@ -2495,7 +2495,7 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
// Remember the initial block so that we can add to it
// after we've collected all the `case`s
- auto initialBlock = builder->curBlock;
+ auto initialBlock = builder->getBlock();
// Next, create a block to use as the target for any `break` statements
auto breakLabel = createBlock();
@@ -2504,8 +2504,7 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
// that we can find it for nested statements.
context->shared->breakLabels.Add(stmt, breakLabel);
- builder->curFunc = initialBlock->getParent();
- builder->curBlock = nullptr;
+ builder->setInsertInto(initialBlock->getParent());
// Iterate over the body of the statement, looking
// for `case` or `default` statements:
@@ -2525,10 +2524,11 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
// Double check that we aren't in the initial
// block, so we don't get tripped up on an
// empty `switch`.
- if(builder->curBlock != initialBlock)
+ auto curBlock = builder->getBlock();
+ if(curBlock != initialBlock)
{
// Is the block already terminated?
- if(!builder->curBlock->getTerminator())
+ if(!curBlock->getTerminator())
{
// Not terminated, so add one.
builder->emitBreak(breakLabel);
@@ -2542,7 +2542,7 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
// Now that we've collected the cases, we are
// prepared to emit the `switch` instruction
// itself.
- builder->curBlock = initialBlock;
+ builder->setInsertInto(initialBlock);
builder->emitSwitch(
conditionVal,
breakLabel,
@@ -2819,9 +2819,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
context->irBuilder->createWitnessTableEntry(witnessTable,
context->irBuilder->getDeclRefVal(subInheritanceDeclRef), cpyTable);
- // HACK: we are re-using the entries in a pre-existing table here,
- // which is not how things are supposed to work.
- cpyTable->children = witnessTable->children;
+ // We need to copy all the entries from the original table to this new table.
+ for (auto entry : witnessTable->getEntries())
+ {
+ context->irBuilder->createWitnessTableEntry(cpyTable,
+ entry->requirementKey.get(),
+ entry->satisfyingVal.get());
+ }
witnessTablesDictionary.Add(cpyTable->mangledName, cpyTable);
walkInheritanceHierarchyAndCreateWitnessTableCopies(witnessTable, subType, subInheritanceDeclRef.getDecl());
@@ -3024,7 +3028,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
IRBuilder subBuilderStorage = *getBuilder();
IRBuilder* subBuilder = &subBuilderStorage;
- subBuilder->curFunc = irGlobal;
+ subBuilder->setInsertInto(irGlobal);
IRGenContext subContextStorage = *context;
IRGenContext* subContext = &subContextStorage;
@@ -3034,7 +3038,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// TODO: set up a parent IR decl to put the instructions into
IRBlock* entryBlock = subBuilder->emitBlock();
- subBuilder->curBlock = entryBlock;
+ subBuilder->setInsertInto(entryBlock);
LoweredValInfo initVal = lowerLValueExpr(subContext, initExpr);
subContext->irBuilder->emitReturn(getSimpleVal(subContext, initVal));
@@ -3570,7 +3574,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// need to create an IR function here
IRFunc* irFunc = subBuilder->createFunc();
- subBuilder->curFunc = irFunc;
+ subBuilder->setInsertInto(irFunc);
trySetMangledName(irFunc, decl);
@@ -3673,7 +3677,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// This is a function definition, so we need to actually
// construct IR for the body...
IRBlock* entryBlock = subBuilder->emitBlock();
- subBuilder->curBlock = entryBlock;
+ subBuilder->setInsertInto(entryBlock);
UInt paramTypeIndex = 0;
for( auto paramInfo : parameterLists.params )
@@ -3790,7 +3794,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// We need to carefully add a terminator instruction to the end
// of the body, in case the user didn't do so.
- if (!subContext->irBuilder->curBlock->getTerminator())
+ if (!subContext->irBuilder->getBlock()->getTerminator())
{
if (irResultType->Equals(context->getSession()->getVoidType()))
{
@@ -4227,6 +4231,8 @@ IRModule* generateIRForTranslationUnit(
ensureDecl(context, decl);
}
+ validateIRModuleIfEnabled(compileRequest, module);
+
// We will perform certain "mandatory" optimization passes now.
// These passes serve two purposes:
//
@@ -4267,6 +4273,8 @@ IRModule* generateIRForTranslationUnit(
// "fragile" in that we'd now need to recompile when
// a module we depend on changes.
+ validateIRModuleIfEnabled(compileRequest, module);
+
// If we are being sked to dump IR during compilation,
// then we can dump the initial IR for the module here.
if(compileRequest->shouldDumpIR)
diff --git a/source/slang/options.cpp b/source/slang/options.cpp
index fc4eea3fc..5929adabb 100644
--- a/source/slang/options.cpp
+++ b/source/slang/options.cpp
@@ -276,6 +276,10 @@ struct OptionsParser
{
requestImpl->shouldDumpIR = true;
}
+ else if(argStr == "-validate-ir" )
+ {
+ requestImpl->shouldValidateIR = true;
+ }
else if(argStr == "-skip-codegen" )
{
requestImpl->shouldSkipCodegen = true;
diff --git a/source/slang/slang.natvis b/source/slang/slang.natvis
index 1dd3b5ef5..489005620 100644
--- a/source/slang/slang.natvis
+++ b/source/slang/slang.natvis
@@ -92,6 +92,7 @@
</ArrayItems>
</Expand>
</Synthetic>
+ <Item Name="[parent]">parent</Item>
<Synthetic Name="[uses]">
<Expand>
<LinkedListItems>
@@ -117,6 +118,7 @@
</LinkedListItems>
</Expand>
</Synthetic>
+ <Item Name="[parent]">parent</Item>
<Synthetic Name="[uses]">
<Expand>
<LinkedListItems>
@@ -142,6 +144,7 @@
</LinkedListItems>
</Expand>
</Synthetic>
+ <Item Name="[parent]">parent</Item>
<Synthetic Name="[uses]">
<Expand>
<LinkedListItems>
@@ -159,7 +162,8 @@
<Expand>
<Item Name="[op]">op</Item>
<Item Name="[type]">type</Item>
- <ExpandedItem>declRef</ExpandedItem>
+ <Item Name="[declRef]">declRef</Item>
+ <Item Name="[parent]">parent</Item>
</Expand>
</Type>
<Type Name="Slang::IRUse">
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index c36045c24..a0ca926be 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -183,6 +183,7 @@
<ClInclude Include="ir-ssa.h" />
<ClInclude Include="ir-type-defs.h" />
<ClInclude Include="ir-types.h" />
+ <ClInclude Include="ir-validate.h" />
<ClInclude Include="ir.h" />
<ClInclude Include="legalize-types.h" />
<ClInclude Include="lexer.h" />
@@ -225,6 +226,7 @@
<ClCompile Include="ir-constexpr.cpp" />
<ClCompile Include="ir-legalize-types.cpp" />
<ClCompile Include="ir-ssa.cpp" />
+ <ClCompile Include="ir-validate.cpp" />
<ClCompile Include="ir.cpp" />
<ClCompile Include="legalize-types.cpp" />
<ClCompile Include="lexer.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index ee4576d0a..55140a4da 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -50,6 +50,7 @@
<ClInclude Include="ir-types.h" />
<ClInclude Include="ir-type-defs.h" />
<ClInclude Include="type-system-shared.h" />
+ <ClInclude Include="ir-validate.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="check.cpp" />
@@ -83,6 +84,7 @@
<ClCompile Include="memory_pool.cpp" />
<ClCompile Include="ir-constexpr.cpp" />
<ClCompile Include="type-system-shared.cpp" />
+ <ClCompile Include="ir-validate.cpp" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="core.meta.slang" />