diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/compiler.h | 1 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 4 | ||||
| -rw-r--r-- | source/slang/diagnostics.cpp | 6 | ||||
| -rw-r--r-- | source/slang/diagnostics.h | 4 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 5 | ||||
| -rw-r--r-- | source/slang/ir-constexpr.cpp | 6 | ||||
| -rw-r--r-- | source/slang/ir-inst-defs.h | 60 | ||||
| -rw-r--r-- | source/slang/ir-insts.h | 23 | ||||
| -rw-r--r-- | source/slang/ir-legalize-types.cpp | 3 | ||||
| -rw-r--r-- | source/slang/ir-ssa.cpp | 15 | ||||
| -rw-r--r-- | source/slang/ir-validate.cpp | 182 | ||||
| -rw-r--r-- | source/slang/ir-validate.h | 35 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 509 | ||||
| -rw-r--r-- | source/slang/ir.h | 15 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 44 | ||||
| -rw-r--r-- | source/slang/options.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang.natvis | 6 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 2 |
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" /> |
