diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2022-06-13 14:00:48 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-13 14:00:48 -0400 |
| commit | 68d9d87f9385a8c7c29443dcfcbf70434dc889bd (patch) | |
| tree | 2898c7443e21c311b4a2aa3ba36f9c07e5ae1bd0 | |
| parent | 522e14116b7c7d4eccf0a855ffbcc2076d44db88 (diff) | |
Liveness fixes and improvements (#2270)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Use TerminatedUnownedStringSlice for literals in output C++.
* Remove Escape/Unescape functions used in slang-token-reader.cpp
Add target type of 'host-cpp' etc to map to the target types.
* Fix some corner cases around string encoding.
* Added unit test for string escaping.
Fixed some assorted escaping bugs.
* Updated test output.
* Added decode test.
* Stop using hex output, to get around 'greedy' aspect. Use octal instead.
* Added HostHostCallable
Small changes to use ArtifactDesc/Info instead of large switches.
* Fix C++ emit to handle arbitrary function export.
* Add options handling for callable without an output being specified.
* Can compile with COM interface. Added example using com interface.
* Use the IR Ptr type instead of hack in C++ emit for interfaces.
* Fix issue with outputting the COM call when ptr is used.
* Fix crash issue on compilation failure.
* Add support for __global.
* Added `ActualGlobalRate`
Added special handling around globals and COM interfaces.
Tested out in cpu-com-example.
* Fix typo in NodeBase.
* Support for accessing globals by name working.
* Bounds checking for C++
Improved bounds checks for CUDA.
* Check that actual global initialization is working.
* Fix typo.
* Refactor the com replacement such that it doesn't need a cache or do anything special with GlobalVar.
* Fix typo in CUDA prelude.
* Remove context.
Only create replacement if needed.
* Split out COM host-callable into a unit-test.
* host-callable com testing on C++and llvm.
* Comment around the COM ptr replacement.
* WIP Zero bound test.
* Disable com test on vs 32 bit.
Fix C++ prelude
* Disable 32 bit targets testing com host-callable.
* For now disable zero index test.
* Enable bounds checking for CPU/CUDA.
* Small fixes.
Disable CUDA zero index bound fix.
* Add test result for bound check.
* Work around for index wrapping issue.
* Added Fixed array test.
* Only enable prelude asserts via SLANG_PRELUDE_ENABLE_ASSERT (unless defined by the user)
* Small fix around instCount.
* Improve liveness loop handing and tests.
* Improve liveness comment.
* More conservative loop handling.
* Make liveness deterministic to make testing work.
* Added 'span tidy'
Added some more tests.
* Simplify span simplification, because could collapse inappropriate spans.
* Updated liveness with simple loop tracking.
* Update test results.
* Small tidy up.
* Update comments in liveness tests.
* Improve liveness comments.
* Loop handling without needing LoopInfo tracking.
* Improve liveness comments.
* Small fix around removing uninteresting spans.
Improve naming.
| -rw-r--r-- | source/slang/slang-ir-liveness.cpp | 363 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-2.slang | 27 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-2.slang.expected | 60 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-3.slang | 49 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-3.slang.expected | 144 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-4.slang | 35 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-4.slang.expected | 82 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-5.slang | 42 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-5.slang.expected | 107 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-6.slang | 47 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-6.slang.expected | 115 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-7.slang | 32 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness-7.slang.expected | 66 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness.slang | 2 | ||||
| -rw-r--r-- | tests/experimental/liveness/liveness.slang.expected | 253 |
15 files changed, 1151 insertions, 273 deletions
diff --git a/source/slang/slang-ir-liveness.cpp b/source/slang/slang-ir-liveness.cpp index 7b0e9bea8..6f2af9fa0 100644 --- a/source/slang/slang-ir-liveness.cpp +++ b/source/slang/slang-ir-liveness.cpp @@ -112,7 +112,7 @@ public: struct LivenessContext { - enum class BlockIndex : Index; + enum class BlockIndex : Index { Invalid = -1 }; // NOTE! Care must be taken changing the order. // canPromote checks if a result can be 'promoted'. @@ -140,39 +140,57 @@ struct LivenessContext /// Block info (indexed via BlockIndex), that is valid across analysing liveness of a root struct BlockInfo { - void reset() + + /// Reset any information for a start + void resetForStart() { result = BlockResult::NotVisited; + loopParentBlockIndex = BlockIndex::Invalid; + } + + /// Reset any information needed for a new root + void resetForRoot() + { + resetForStart(); + runStart = 0; runCount = 0; lastInst = nullptr; instCount = 0; } - void resetResult() - { - result = BlockResult::NotVisited; - } - BlockResult result; ///< The result for this block - Index runStart; ///< The start index in m_instRuns index. This defines a instruction of interest in order in a block. - Count runCount; ///< The count of the amount insts in the run - IRInst* lastInst; ///< Last inst seen - Count instCount; ///< The total amount of start/access instruction seen in the block + // These are reset for *each* liveness start + BlockIndex loopParentBlockIndex; ///< Block index to block which is the parent for this block. Only valid for blocks that are loop starts. + BlockResult result; ///< The result for this block + + // These remain constant for all live starts to a root. + Index runStart; ///< The start index in m_instRuns index. This defines a instruction of interest in order in a block. + Count runCount; ///< The count of the amount insts in the run + IRInst* lastInst; ///< Last inst seen + Count instCount; ///< The total amount of start/access instruction seen in the block }; - /// Block info (indexed via BlockIndex), that is valid across a function - struct FunctionBlockInfo + /// Block info (indexed via BlockIndex), that is fixed across a function + struct FixedBlockInfo { void init(IRBlock* inBlock) { block = inBlock; successorsStart = 0; successorsCount = 0; + breakBlockIndex = BlockIndex::Invalid; + targetBlockIndex = BlockIndex::Invalid; } - IRBlock* block; - Index successorsStart; ///< Indexes into block successors - Count successorsCount; + bool isLoopStart() const { return breakBlockIndex != BlockIndex::Invalid; } + + IRBlock* block; ///< The block + + BlockIndex breakBlockIndex; ///< If this block terminates in a loop holds the break block + BlockIndex targetBlockIndex; ///< If this block terminates in a loop holds the target block + + Index successorsStart; ///< Indexes into block successors + Count successorsCount; ///< How many successors }; /// Process the module @@ -195,11 +213,11 @@ struct LivenessContext /// Process a successor to a block /// Can only be called after a call to _findAliasesAndAccesses for the root. - BlockResult _processSuccessor(BlockIndex blockIndex); + BlockResult _processSuccessor(BlockIndex blockIndex, BlockIndex loopBlockIndex); /// Process a block /// Can only be called after a call to _findAliasesAndAccesses for the root. - BlockResult _processBlock(BlockIndex blockIndex, const ConstArrayView<IRInst*>& run); + BlockResult _processBlock(BlockIndex blockIndex, const ConstArrayView<IRInst*>& run, BlockIndex loopBlockIndex); /// Process all the locations in the function /// NOTE: All locations must be to the same function, and ordered by root. @@ -261,13 +279,22 @@ struct LivenessContext /// Get block info BlockInfo* _getBlockInfo(BlockIndex blockIndex) { return &m_blockInfos[Index(blockIndex)]; } + /// Get block info fixed across a function being analyzed. + const FixedBlockInfo& _getFixedBlockInfo(BlockIndex blockIndex) const { return m_fixedBlockInfos[Index(blockIndex)]; } + /// Get the block from the index - IRBlock* _getBlock(BlockIndex blockIndex) const { return m_functionBlockInfos[Index(blockIndex)].block; } + IRBlock* _getBlock(BlockIndex blockIndex) const { return m_fixedBlockInfos[Index(blockIndex)].block; } /// True if the terminator can be considered an access /// This allows us to elide a scope end if the root is returned bool _isAccessTerminator(IRTerminatorInst* terminator); + /// Order the range starts in a deterministic manner + void _orderRangeStartsDeterministically(); + + /// Remove any end/start spands within a block, that aren't 'interesting. + void _tidyUninterestingSpans(); + /// Gets the instructions of interest for this info, in the order they appear within the block ConstArrayView<IRInst*> _getRun(const BlockInfo* info) { @@ -277,7 +304,7 @@ struct LivenessContext /// Gets all of the successors for the blockIdnex ConstArrayView<BlockIndex> _getSuccessors(BlockIndex blockIndex) { - const auto& info = m_functionBlockInfos[Index(blockIndex)]; + const auto& info = m_fixedBlockInfos[Index(blockIndex)]; return makeConstArrayView(m_blockSuccessors.getBuffer() + info.successorsStart, info.successorsCount); } @@ -297,12 +324,13 @@ struct LivenessContext Dictionary<IRBlock*, BlockIndex> m_blockIndexMap; ///< Map from a block to a block index List<BlockInfo> m_blockInfos; ///< Information about blocks, for the current root - List<FunctionBlockInfo> m_functionBlockInfos; ///< Information about blocks across the current function - List<BlockIndex> m_blockSuccessors; ///< Successors for a blocks, accessed via + List<FixedBlockInfo> m_fixedBlockInfos; ///< Information about blocks across the current function + List<BlockIndex> m_blockSuccessors; ///< Successors for a blocks, accessed via FixedBlockInfo List<IRInst*> m_instRuns; ///< Instructions of interest in order. Indexed into via BlockInfo [runStart, runStart + runCount) List<IRLiveRangeStart*> m_rangeStarts; ///< All the starts within a function, ordered by referenced + List<IRLiveRangeEnd*> m_rangeEnds; ///< All of the ends added IRModule* m_module; SharedIRBuilder m_sharedBuilder; @@ -346,7 +374,6 @@ static void _findFuncs(IRModule* module, List<IRFunc*>& ioFuncs) } } - void LivenessContext::_maybeAddEndAtBlockStart(BlockIndex blockIndex) { auto block = _getBlock(blockIndex); @@ -367,7 +394,7 @@ LivenessContext::BlockResult LivenessContext::_addBlockResult(BlockIndex blockIn return result; } -LivenessContext::BlockResult LivenessContext::_processSuccessor(BlockIndex blockIndex) +LivenessContext::BlockResult LivenessContext::_processSuccessor(BlockIndex blockIndex, BlockIndex loopBlockIndex) { auto blockInfo = _getBlockInfo(blockIndex); @@ -390,7 +417,6 @@ LivenessContext::BlockResult LivenessContext::_processSuccessor(BlockIndex block // Unless it is the start block (the block containing live start) *and* the root is // in the block. // The live start can only be after the var, because the var is only in scope then. - // // We need to check if we are in the live start block, as we then need to process // up until the live start. @@ -405,7 +431,52 @@ LivenessContext::BlockResult LivenessContext::_processSuccessor(BlockIndex block SLANG_ASSERT(startIndex >= 0); // We want to run all the way up to the start - return _processBlock(blockIndex, run.head(startIndex)); + return _processBlock(blockIndex, run.head(startIndex), loopBlockIndex); + } + + // If we are looping + if (loopBlockIndex != BlockIndex::Invalid) + { + // And what we are branching too is the start of the current loop + const auto& loopInfo = _getFixedBlockInfo(loopBlockIndex); + if (loopInfo.targetBlockIndex == blockIndex) + { + // This block has been visisted, that means it has been traversed to get here + // meaning the root *must* be live on the looping. + + // TODO(JS): + // The solution used here is somewhat conservative, it assumes if a branch back to the start of the loop can be reached that + // + // * There might be some path where the loop might exit + // * There might be some path where the root(variable or alias) may be loaded/or stored + // + // If these assumptions are wrong it will lead to + // + // * Potentially a liveness end that is never hit(outside of the loop) + // * Potentially liveness for a root that spans across the loop even if that is not actually necessary + // + // This could be improved on but would probably need something like 'loop analysis' that specially determined + // those scenarios, such that the assumptions aren't needed. It would need to be 'separate analysis', because + // the liveness traversal is a kind of incremental depth first traversal. But for loop analysis it would require + // at loop start the result on all paths through the loop. + + const auto breakBlockIndex = loopInfo.breakBlockIndex; + + const auto parentLoop = _getBlockInfo(loopBlockIndex)->loopParentBlockIndex; + + // Process what comes after the loop (in the scope of the parent loop if any) + result = _processSuccessor(breakBlockIndex, parentLoop); + if (result != BlockResult::Found) + { + // If an end is not found from the break, + // we just insert an end at the start of the break block + + _maybeAddEndAtBlockStart(breakBlockIndex); + result = _addBlockResult(breakBlockIndex, BlockResult::Found); + } + + return result; + } } // Otherwise just return result @@ -431,8 +502,21 @@ LivenessContext::BlockResult LivenessContext::_processSuccessor(BlockIndex block // Mark that it is visited _addBlockResult(blockIndex, BlockResult::Visited); + // Special case leaving the loop. + // If we are in a loop + if (loopBlockIndex != BlockIndex::Invalid) + { + auto& loopInfo = m_fixedBlockInfos[Index(loopBlockIndex)]; + // And the block we are going to is the break block then we are no longer in this loop + if (loopInfo.breakBlockIndex == blockIndex) + { + // So the loop we are currently in must be the parent + loopBlockIndex = _getBlockInfo(loopBlockIndex)->loopParentBlockIndex; + } + } + // Else process the block to try and find the last used instruction - return _processBlock(blockIndex, _getRun(blockInfo)); + return _processBlock(blockIndex, _getRun(blockInfo), loopBlockIndex); } Index LivenessContext::_indexOfRootStart(const ConstArrayView<IRInst*>& run) @@ -504,7 +588,7 @@ void LivenessContext::_maybeAddEndAfterInst(IRInst* inst) // Just add end of scope after the inst m_builder.setInsertLoc(IRInsertLoc::after(inst)); // Add the live end inst - m_builder.emitLiveRangeEnd(m_root); + m_rangeEnds.add(m_builder.emitLiveRangeEnd(m_root)); } } @@ -515,7 +599,7 @@ void LivenessContext::_maybeAddEndBeforeInst(IRInst* inst) // Just add end of scope after the inst m_builder.setInsertLoc(IRInsertLoc::before(inst)); // Add the live end inst - m_builder.emitLiveRangeEnd(m_root); + m_rangeEnds.add(m_builder.emitLiveRangeEnd(m_root)); } } @@ -540,7 +624,17 @@ LivenessContext::BlockResult LivenessContext::_completeBlock(BlockIndex blockInd return _addBlockResult(blockIndex, BlockResult::NotFound); } -LivenessContext::BlockResult LivenessContext::_processBlock(BlockIndex blockIndex, const ConstArrayView<IRInst*>& run) +static IRLoop* _getLoopTerminator(IRBlock* block) +{ + auto terminator = block->getTerminator(); + if (terminator->getOp() == kIROp_loop) + { + return static_cast<IRLoop*>(terminator); + } + return nullptr; +} + +LivenessContext::BlockResult LivenessContext::_processBlock(BlockIndex blockIndex, const ConstArrayView<IRInst*>& run, BlockIndex loopBlockIndex) { // Note that the run must be some part of the run for the block indicated by blockIndex. One of // @@ -565,6 +659,27 @@ LivenessContext::BlockResult LivenessContext::_processBlock(BlockIndex blockInde } } + // If we hit a loop add the information and make this the current loop info + { + const auto& fixedInfo = _getFixedBlockInfo(blockIndex); + if (fixedInfo.isLoopStart()) + { + SLANG_ASSERT(_getLoopTerminator(fixedInfo.block)); + + // Get the varaying block info + auto blockInfo = _getBlockInfo(blockIndex); + + // Its either not set or set to the same value + SLANG_ASSERT(blockInfo->loopParentBlockIndex == BlockIndex::Invalid || blockInfo->loopParentBlockIndex == loopBlockIndex); + + // Set the parent loop block index + blockInfo->loopParentBlockIndex = loopBlockIndex; + + // This block is the current loop block + loopBlockIndex = blockIndex; + } + } + // Zero initialize all the counts Index foundCounts[Index(BlockResult::CountOf)] = { 0 }; @@ -586,7 +701,7 @@ LivenessContext::BlockResult LivenessContext::_processBlock(BlockIndex blockInde // If we always access through successorResults (ie RAIIStackArray type), things will be fine though. // Process the successor - const auto successorResult = _processSuccessor(successorBlockIndex); + const auto successorResult = _processSuccessor(successorBlockIndex, loopBlockIndex); successorResults[i] = successorResult; @@ -637,7 +752,7 @@ void LivenessContext::_addInst(IRInst* inst) auto block = as<IRBlock>(inst->getParent()); // Get the index to get the info - auto blockIndex = m_blockIndexMap[block]; + const BlockIndex blockIndex = m_blockIndexMap[block]; auto blockInfo = _getBlockInfo(blockIndex); @@ -653,6 +768,12 @@ void LivenessContext::_addInst(IRInst* inst) void LivenessContext::_addAccessInst(IRInst* inst) { + // If we already have it don't need to add again + if (m_accessSet.Contains(inst)) + { + return; + } + // Add to the access set m_accessSet.Add(inst); @@ -753,7 +874,8 @@ void LivenessContext::_findAliasesAndAccesses(IRInst* root) } case kIROp_Store: { - // In terms of liveness, stores can be ignored + // In terms of liveness, stores can be ignored (although in the future we will probably + // want to support to make loops analysis work more accurately) break; } case kIROp_getElement: @@ -794,7 +916,7 @@ void LivenessContext::_findAndEmitRangeEnd(IRLiveRangeStart* liveRangeStart) // Reset the result for (auto& blockInfo : m_blockInfos) { - blockInfo.resetResult(); + blockInfo.resetForStart(); } // Store root information, so don't have to pass around methods @@ -839,7 +961,7 @@ void LivenessContext::_findAndEmitRangeEnd(IRLiveRangeStart* liveRangeStart) _addBlockResult(rootStartBlockIndex, BlockResult::Visited); // Recursively find results - auto foundResult = _processBlock(rootStartBlockIndex, run); + auto foundResult = _processBlock(rootStartBlockIndex, run, BlockIndex::Invalid); if (foundResult == BlockResult::NotFound) { @@ -866,11 +988,10 @@ bool LivenessContext::_isNormalRunInst(IRInst* inst) } // NOTE! - // The ops in th elist above are the only ops *currently* that indicate an access. + // The ops in the list above are the only ops *currently* that indicate an access. // Has to be consistent with `_findAliasesAndAccesses` if (op == kIROp_Call || - op == kIROp_Load || - op == kIROp_Call) + op == kIROp_Load) { // Just because it's the right type *doesn't* mean it's an access, it has to also // be in the access set @@ -1019,7 +1140,7 @@ void LivenessContext::_processRoot(IRLiveRangeStart* const* rangeStarts, Count r // Reset the order range for all blocks for (auto& info : m_blockInfos) { - info.reset(); + info.resetForRoot(); } m_instRuns.clear(); @@ -1073,8 +1194,9 @@ void LivenessContext::_processFunction(IRFunc* func) m_blockIndexMap.Clear(); m_blockInfos.clear(); - m_functionBlockInfos.clear(); + m_fixedBlockInfos.clear(); m_blockSuccessors.clear(); + m_rangeEnds.clear(); { // First we find all the blocks in the function, we add to the map @@ -1086,10 +1208,10 @@ void LivenessContext::_processFunction(IRFunc* func) IRBlock* blockInst = as<IRBlock>(block); m_blockIndexMap.Add(blockInst, BlockIndex(index++)); - FunctionBlockInfo functionBlockInfo; - functionBlockInfo.init(blockInst); + FixedBlockInfo fixedBlockInfo; + fixedBlockInfo.init(blockInst); - m_functionBlockInfos.add(functionBlockInfo); + m_fixedBlockInfos.add(fixedBlockInfo); } // Allocate space for the root block infos @@ -1097,9 +1219,17 @@ void LivenessContext::_processFunction(IRFunc* func) // Now we have the map, work out the successors as BlockIndex for each block // and add those to m_blockSuccessors. They are indexed via successorsIndex/Count in the FunctionBlockInfos - for (auto& info : m_functionBlockInfos) + for (auto& fixedInfo : m_fixedBlockInfos) { - auto block = info.block; + auto block = fixedInfo.block; + + // Set up the break block indices if we have a loop + if (auto loop = _getLoopTerminator(block)) + { + // Set the break/continue block indices + fixedInfo.breakBlockIndex = m_blockIndexMap[loop->getBreakBlock()]; + fixedInfo.targetBlockIndex = m_blockIndexMap[loop->getTargetBlock()]; + } // Add all the successors auto successors = block->getSuccessors(); @@ -1107,8 +1237,8 @@ void LivenessContext::_processFunction(IRFunc* func) const Index successorsStart = m_blockSuccessors.getCount(); const Count successorsCount = successors.getCount(); - info.successorsStart = successorsStart; - info.successorsCount = successorsCount; + fixedInfo.successorsStart = successorsStart; + fixedInfo.successorsCount = successorsCount; m_blockSuccessors.setCount(successorsStart + successorsCount); @@ -1141,6 +1271,140 @@ void LivenessContext::_processFunction(IRFunc* func) start = end; } } + + // Remove any end/start spands within a block, that aren't 'interesting. + _tidyUninterestingSpans(); +} + +static bool _isRootTypeScalar(IRType* type) +{ + // Liveness range start/end are through ptr type + if (auto ptrType = as<IRPtrType>(type)) + { + // Strip the ptr + type = ptrType->getValueType(); + } + return as<IRBasicType>(type) != nullptr; +} + +void LivenessContext::_tidyUninterestingSpans() +{ + // We are looking for spans from an end to a start for a scalar variable. + // Only scalar for now so even if the span is 'big' the cost is probably low. + // + // A more sophisticated implementation could perhaps look in the span if there is only a full store + // for a struct/large type. Would also need some concept of the 'amount of insts' to determine if worth it. + + const Count count = m_rangeEnds.getCount(); + + for (Index i = 0; i < count; ++i) + { + auto end = m_rangeEnds[i]; + auto root = end->getReferenced(); + + // If it's not scalar then we ignore + if (!_isRootTypeScalar(root->getDataType())) + { + continue; + } + + // Look for a start to the same root in the block + // A more sophisticated implementation might try to look across unconditional branches + // but since only *one* end is stored for potentially multiple starts, and that a block + // might have multiple predecessors, we ignore for now. + IRLiveRangeStart* start = nullptr; + for (auto cur = end->getNextInst(); cur; cur = cur->getNextInst()) + { + // If it's a start + if (auto foundStart = as<IRLiveRangeStart>(cur)) + { + // and a start to the same root + if (foundStart->getReferenced() == root) + { + start = foundStart; + break; + } + } + } + + // If we found a matching start, lets just remove the span + if (start) + { + m_rangeEnds[i] = nullptr; + const Index startIndex = m_rangeStarts.indexOf(start); + + SLANG_ASSERT(startIndex >= 0); + if (startIndex >= 0) + { + m_rangeStarts[startIndex] = nullptr; + } + + // Delete the matching end -> start span + start->removeAndDeallocate(); + end->removeAndDeallocate(); + } + } +} + +void LivenessContext::_orderRangeStartsDeterministically() +{ + const Index rangeStartsCount = m_rangeStarts.getCount(); + if (rangeStartsCount <= 1) + { + // One or less there is no reordering + return; + } + + // The fast way is to just order by the roots pointers. + // Unfortunately that is unstable, as it depends on the allocation location which varies. + + // Sort into referenced/root start + //m_rangeStarts.sort([&](IRLiveRangeStart* a, IRLiveRangeStart* b) -> bool { return a->getReferenced() < b->getReferenced(); }); + + // The order that the starts is *found* is deterministic, so we'll use that as part of the key to sort. + + struct Entry + { + IRLiveRangeStart* start; + Index foundIndex; ///< The found index + Index rootIndex; ///< Index for the root + }; + + Int orderCounter = 0; + + Dictionary<IRInst*, Index> rootOrderMap; + List<Entry> entries; + entries.setCount(rangeStartsCount); + for (Index i = 0; i < rangeStartsCount; ++i) + { + auto start = m_rangeStarts[i]; + auto root = start->getReferenced(); + + Index order = -1; + + if (auto orderPtr = rootOrderMap.TryGetValueOrAdd(root, orderCounter + 1)) + { + order = *orderPtr; + } + else + { + order = ++orderCounter; + } + + Entry& entry = entries[i]; + entry.start = start; + entry.foundIndex = i; + entry.rootIndex = order; + } + + // Sort by the root indices and if equal sort by the found order + entries.sort([&](const Entry& a, const Entry& b) -> bool { return (a.rootIndex < b.rootIndex) || (a.rootIndex == b.rootIndex && a.foundIndex < b.foundIndex); }); + + // Copy back + for (Index i = 0; i < rangeStartsCount; ++i) + { + m_rangeStarts[i] = entries[i].start; + } } void LivenessContext::process() @@ -1158,9 +1422,10 @@ void LivenessContext::process() if (m_rangeStarts.getCount() > 0) { - // Sort into referenced/root start - m_rangeStarts.sort([&](IRLiveRangeStart* a, IRLiveRangeStart* b) -> bool { return a->getReferenced() < b->getReferenced(); }); + // Order the range starts by root deterministically + _orderRangeStartsDeterministically(); + // Process the function _processFunction(func); } } diff --git a/tests/experimental/liveness/liveness-2.slang b/tests/experimental/liveness/liveness-2.slang new file mode 100644 index 000000000..9d4ab275d --- /dev/null +++ b/tests/experimental/liveness/liveness-2.slang @@ -0,0 +1,27 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling. + +int calcThing(int offset) +{ + int idx[3]; + + for (int i = 0; i < 3; ++i) + { + idx[i] = offset + i; + } + + // Now read back + return idx[0] + idx[1] + idx[2]; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-2.slang.expected b/tests/experimental/liveness/liveness-2.slang.expected new file mode 100644 index 000000000..c742fa1fc --- /dev/null +++ b/tests/experimental/liveness/liveness-2.slang.expected @@ -0,0 +1,60 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0[3], spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[3], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + int idx_0[3]; + livenessStart_0(idx_0, 0); + int i_0; + livenessStart_1(i_0, 0); + i_0 = 0; + for(;;) + { + if(i_0 < 3) + { + } + else + { + break; + } + idx_0[i_0] = offset_0 + i_0; + i_0 = i_0 + 1; + } + livenessEnd_0(i_0, 0); + int _S1 = idx_0[0] + idx_0[1]; + int _S2 = idx_0[2]; + livenessEnd_1(idx_0, 0); + return _S1 + _S2; +} + +layout(std430, binding = 0) buffer _S3 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S4 = uint(index_0); + int _S5 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S4)]) = _S5; + return; +} + +} diff --git a/tests/experimental/liveness/liveness-3.slang b/tests/experimental/liveness/liveness-3.slang new file mode 100644 index 000000000..1d289aa68 --- /dev/null +++ b/tests/experimental/liveness/liveness-3.slang @@ -0,0 +1,49 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling, with more complex structure + +int calcThing(int offset) +{ + int total = 0; + int another[2] = { 1, 2}; + + for (int k = 0; k < 20; ++k) + { + int idx[3] = {}; + + for (int i = 0; i < 17; ++i) + { + int modRange = i % 3; + + another[i & 1] += modRange; + + if (i % 3) + { + offset += 1; + } + + idx[modRange] += offset + i; + } + + if ((k + 7) % 5 == 4) + { + return total; + } + + // Now read back + total += idx[0] + idx[1] + idx[2]; + } + + return -total; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-3.slang.expected b/tests/experimental/liveness/liveness-3.slang.expected new file mode 100644 index 000000000..614fd1387 --- /dev/null +++ b/tests/experimental/liveness/liveness-3.slang.expected @@ -0,0 +1,144 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0[2], spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_2(spirv_by_reference int _0[3], spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[3], spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_2(spirv_by_reference int _0[2], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + int another_0[2]; + livenessStart_0(another_0, 0); + const int _S1[2] = { 1, 2 }; + another_0 = _S1; + int k_0; + int _S2; + int total_0; + livenessStart_1(k_0, 0); + k_0 = 0; + livenessStart_1(_S2, 0); + _S2 = offset_0; + livenessStart_1(total_0, 0); + total_0 = 0; + for(;;) + { + if(k_0 < 20) + { + } + else + { + break; + } + int idx_0[3]; + livenessStart_2(idx_0, 0); + const int _S3[3] = { 0, 0, 0 }; + idx_0 = _S3; + int _S4 = _S2; + int i_0; + int _S5; + livenessStart_1(i_0, 0); + i_0 = 0; + livenessStart_1(_S5, 0); + _S5 = _S4; + for(;;) + { + if(i_0 < 17) + { + } + else + { + break; + } + int modRange_0 = i_0 % 3; + another_0[i_0 & 1] = another_0[i_0 & 1] + modRange_0; + int _S6 = i_0 % 3; + int _S7; + if(bool(_S6)) + { + int _S8 = _S5; + livenessEnd_0(_S5, 0); + int _S9 = _S8 + 1; + livenessStart_1(_S7, 0); + _S7 = _S9; + } + else + { + int _S10 = _S5; + livenessEnd_0(_S5, 0); + livenessStart_1(_S7, 0); + _S7 = _S10; + } + idx_0[modRange_0] = idx_0[modRange_0] + (_S7 + i_0); + i_0 = i_0 + 1; + livenessStart_1(_S5, 0); + int _S11 = _S7; + livenessEnd_0(_S7, 0); + _S5 = _S11; + } + livenessEnd_0(i_0, 0); + livenessEnd_0(_S2, 0); + int _S12 = (k_0 + 7) % 5; + if(_S12 == 4) + { + livenessEnd_0(_S5, 0); + livenessEnd_1(idx_0, 0); + livenessEnd_0(k_0, 0); + livenessEnd_2(another_0, 0); + return total_0; + } + int _S13 = idx_0[0] + idx_0[1]; + int _S14 = idx_0[2]; + livenessEnd_1(idx_0, 0); + int _S15 = _S13 + _S14; + int _S16 = total_0; + livenessEnd_0(total_0, 0); + int total_1 = _S16 + _S15; + k_0 = k_0 + 1; + livenessStart_1(_S2, 0); + int _S17 = _S5; + livenessEnd_0(_S5, 0); + _S2 = _S17; + livenessStart_1(total_0, 0); + total_0 = total_1; + } + livenessEnd_0(_S2, 0); + livenessEnd_0(k_0, 0); + livenessEnd_2(another_0, 0); + int _S18 = total_0; + livenessEnd_0(total_0, 0); + return - _S18; +} + +layout(std430, binding = 0) buffer _S19 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S20 = uint(index_0); + int _S21 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S20)]) = _S21; + return; +} + +} diff --git a/tests/experimental/liveness/liveness-4.slang b/tests/experimental/liveness/liveness-4.slang new file mode 100644 index 000000000..d18d127f6 --- /dev/null +++ b/tests/experimental/liveness/liveness-4.slang @@ -0,0 +1,35 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling, with more complex structure +// Here to test anothers liveness. + +int calcThing(int offset) +{ + int another[2] = { 1, 2}; + + for (int k = 0; k < 20; ++k) + { + for (int i = 0; i < 17; ++i) + { + another[i & 1] += k + i; + } + + if ((k + 7) % 5 == 4) + { + return 1; + } + } + + return -2; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-4.slang.expected b/tests/experimental/liveness/liveness-4.slang.expected new file mode 100644 index 000000000..52c6ebb32 --- /dev/null +++ b/tests/experimental/liveness/liveness-4.slang.expected @@ -0,0 +1,82 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0[2], spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[2], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + int another_0[2]; + livenessStart_0(another_0, 0); + const int _S1[2] = { 1, 2 }; + another_0 = _S1; + int k_0; + livenessStart_1(k_0, 0); + k_0 = 0; + for(;;) + { + if(k_0 < 20) + { + } + else + { + break; + } + int i_0; + livenessStart_1(i_0, 0); + i_0 = 0; + for(;;) + { + if(i_0 < 17) + { + } + else + { + break; + } + another_0[i_0 & 1] = another_0[i_0 & 1] + (k_0 + i_0); + i_0 = i_0 + 1; + } + livenessEnd_0(i_0, 0); + int _S2 = (k_0 + 7) % 5; + if(_S2 == 4) + { + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + return 1; + } + k_0 = k_0 + 1; + } + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + return -2; +} + +layout(std430, binding = 0) buffer _S3 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S4 = uint(index_0); + int _S5 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S4)]) = _S5; + return; +} + +} diff --git a/tests/experimental/liveness/liveness-5.slang b/tests/experimental/liveness/liveness-5.slang new file mode 100644 index 000000000..52370d61b --- /dev/null +++ b/tests/experimental/liveness/liveness-5.slang @@ -0,0 +1,42 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling, with more complex structure + +int calcThing(int offset) +{ + int total = 0; + int another[2] = { 1, 2}; + + for (int k = 0; k < 20; ++k) + { + for (int i = 0; i < 17; ++i) + { + another[i & 1] += k + i; + } + + total += another[k & 1]; + + if ((k + 7) % 5 == 4) + { + return 1; + } + } + + if (total > 4) + { + total = -total; + } + + return total; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-5.slang.expected b/tests/experimental/liveness/liveness-5.slang.expected new file mode 100644 index 000000000..ea6e37036 --- /dev/null +++ b/tests/experimental/liveness/liveness-5.slang.expected @@ -0,0 +1,107 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0[2], spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[2], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + int another_0[2]; + livenessStart_0(another_0, 0); + const int _S1[2] = { 1, 2 }; + another_0 = _S1; + int k_0; + int total_0; + livenessStart_1(k_0, 0); + k_0 = 0; + livenessStart_1(total_0, 0); + total_0 = 0; + for(;;) + { + if(k_0 < 20) + { + } + else + { + break; + } + int i_0; + livenessStart_1(i_0, 0); + i_0 = 0; + for(;;) + { + if(i_0 < 17) + { + } + else + { + break; + } + another_0[i_0 & 1] = another_0[i_0 & 1] + (k_0 + i_0); + i_0 = i_0 + 1; + } + livenessEnd_0(i_0, 0); + int _S2 = another_0[k_0 & 1]; + int _S3 = total_0; + livenessEnd_0(total_0, 0); + int total_1 = _S3 + _S2; + int _S4 = (k_0 + 7) % 5; + if(_S4 == 4) + { + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + return 1; + } + k_0 = k_0 + 1; + livenessStart_1(total_0, 0); + total_0 = total_1; + } + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + int total_2; + if(total_0 > 4) + { + int _S5 = total_0; + livenessEnd_0(total_0, 0); + int _S6 = - _S5; + livenessStart_1(total_2, 0); + total_2 = _S6; + } + else + { + int _S7 = total_0; + livenessEnd_0(total_0, 0); + livenessStart_1(total_2, 0); + total_2 = _S7; + } + return total_2; +} + +layout(std430, binding = 0) buffer _S8 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S9 = uint(index_0); + int _S10 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S9)]) = _S10; + return; +} + +} diff --git a/tests/experimental/liveness/liveness-6.slang b/tests/experimental/liveness/liveness-6.slang new file mode 100644 index 000000000..890b06fe5 --- /dev/null +++ b/tests/experimental/liveness/liveness-6.slang @@ -0,0 +1,47 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling, with more complex structure +// More testing around liveness and loops. + +int calcThing(int offset) +{ + int total = 0; + int another[2] = { 1, 2}; + + for (int k = 0; k < 20; ++k) + { + int arr[2] = { 2, 3}; + + for (int i = 0; i < 17; ++i) + { + another[i & 1] += k + i; + arr[k & 1] += i; + } + + total += another[k & 1]; + total += arr[k & 1]; + + if ((k + 7) % 5 == 4) + { + return 1; + } + } + + if (total > 4) + { + total = -total; + } + + return total; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-6.slang.expected b/tests/experimental/liveness/liveness-6.slang.expected new file mode 100644 index 000000000..ac1894f95 --- /dev/null +++ b/tests/experimental/liveness/liveness-6.slang.expected @@ -0,0 +1,115 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0[2], spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[2], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + int another_0[2]; + livenessStart_0(another_0, 0); + const int _S1[2] = { 1, 2 }; + another_0 = _S1; + int k_0; + int total_0; + livenessStart_1(k_0, 0); + k_0 = 0; + livenessStart_1(total_0, 0); + total_0 = 0; + for(;;) + { + if(k_0 < 20) + { + } + else + { + break; + } + int arr_0[2]; + livenessStart_0(arr_0, 0); + const int _S2[2] = { 2, 3 }; + arr_0 = _S2; + int i_0; + livenessStart_1(i_0, 0); + i_0 = 0; + for(;;) + { + if(i_0 < 17) + { + } + else + { + break; + } + another_0[i_0 & 1] = another_0[i_0 & 1] + (k_0 + i_0); + arr_0[k_0 & 1] = arr_0[k_0 & 1] + i_0; + i_0 = i_0 + 1; + } + livenessEnd_0(i_0, 0); + int _S3 = another_0[k_0 & 1]; + int _S4 = total_0; + livenessEnd_0(total_0, 0); + int total_1 = _S4 + _S3; + int _S5 = arr_0[k_0 & 1]; + livenessEnd_1(arr_0, 0); + int total_2 = total_1 + _S5; + int _S6 = (k_0 + 7) % 5; + if(_S6 == 4) + { + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + return 1; + } + k_0 = k_0 + 1; + livenessStart_1(total_0, 0); + total_0 = total_2; + } + livenessEnd_0(k_0, 0); + livenessEnd_1(another_0, 0); + int total_3; + if(total_0 > 4) + { + int _S7 = total_0; + livenessEnd_0(total_0, 0); + int _S8 = - _S7; + livenessStart_1(total_3, 0); + total_3 = _S8; + } + else + { + int _S9 = total_0; + livenessEnd_0(total_0, 0); + livenessStart_1(total_3, 0); + total_3 = _S9; + } + return total_3; +} + +layout(std430, binding = 0) buffer _S10 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S11 = uint(index_0); + int _S12 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S11)]) = _S12; + return; +} + +} diff --git a/tests/experimental/liveness/liveness-7.slang b/tests/experimental/liveness/liveness-7.slang new file mode 100644 index 000000000..0324f90b9 --- /dev/null +++ b/tests/experimental/liveness/liveness-7.slang @@ -0,0 +1,32 @@ +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +// Test loop handling, with more complex structure, and when code is unreachable +// More testing around liveness and loops. + +int calcThing(int offset) +{ + int arr[2] = { 2, 3}; + + for (int k = 0; k < 20; ++k) + { + if ((k + offset) & 1) + { + return arr[0]; + } + else + { + return arr[1]; + } + } +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int index = dispatchThreadID.x; + + outputBuffer[index] = calcThing(index); +} diff --git a/tests/experimental/liveness/liveness-7.slang.expected b/tests/experimental/liveness/liveness-7.slang.expected new file mode 100644 index 000000000..5b0c5b58a --- /dev/null +++ b/tests/experimental/liveness/liveness-7.slang.expected @@ -0,0 +1,66 @@ +result code = 0 +standard error = { +} +standard output = { +#version 450 +#extension GL_EXT_spirv_intrinsics : require +layout(row_major) uniform; +layout(row_major) buffer; +spirv_instruction(id = 256) +void livenessStart_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_0(spirv_by_reference int _0, spirv_literal int _1); + +spirv_instruction(id = 256) +void livenessStart_1(spirv_by_reference int _0[2], spirv_literal int _1); + +spirv_instruction(id = 257) +void livenessEnd_1(spirv_by_reference int _0[2], spirv_literal int _1); + +int calcThing_0(int offset_0) +{ + const int _S1[2] = { 2, 3 }; + int k_0; + int _S2; + int arr_0[2]; + livenessStart_0(k_0, 0); + livenessEnd_0(k_0, 0); + k_0 = 0; + livenessStart_0(_S2, 0); + _S2 = offset_0; + livenessStart_1(arr_0, 0); + arr_0 = _S1; + for(;;) + { + int _S3 = _S2; + livenessEnd_0(_S2, 0); + if(bool(0 + _S3 & 1)) + { + int _S4[2] = arr_0; + livenessEnd_1(arr_0, 0); + return _S4[0]; + } + else + { + int _S5[2] = arr_0; + livenessEnd_1(arr_0, 0); + return _S5[1]; + } + } +} + +layout(std430, binding = 0) buffer _S6 { + int _data[]; +} outputBuffer_0; +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; +void main() +{ + int index_0 = int(gl_GlobalInvocationID.x); + uint _S7 = uint(index_0); + int _S8 = calcThing_0(index_0); + ((outputBuffer_0)._data[(_S7)]) = _S8; + return; +} + +} diff --git a/tests/experimental/liveness/liveness.slang b/tests/experimental/liveness/liveness.slang index 2e531244e..cf3d37a8f 100644 --- a/tests/experimental/liveness/liveness.slang +++ b/tests/experimental/liveness/liveness.slang @@ -1,4 +1,4 @@ -//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness +//TEST:SIMPLE:-target glsl -entry computeMain -profile cs_6_3 -track-liveness -line-directive-mode none //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer RWStructuredBuffer<int> outputBuffer; diff --git a/tests/experimental/liveness/liveness.slang.expected b/tests/experimental/liveness/liveness.slang.expected index 7301d88bf..c74206573 100644 --- a/tests/experimental/liveness/liveness.slang.expected +++ b/tests/experimental/liveness/liveness.slang.expected @@ -6,55 +6,29 @@ standard output = { #extension GL_EXT_spirv_intrinsics : require layout(row_major) uniform; layout(row_major) buffer; - -#line 85 0 spirv_instruction(id = 256) void livenessStart_0(spirv_by_reference int _0, spirv_literal int _1); - -#line 85 spirv_instruction(id = 256) void livenessStart_1(spirv_by_reference uint _0, spirv_literal int _1); - -#line 85 spirv_instruction(id = 257) void livenessEnd_0(spirv_by_reference uint _0, spirv_literal int _1); - -#line 85 spirv_instruction(id = 257) void livenessEnd_1(spirv_by_reference int _0, spirv_literal int _1); - -#line 24 1 int someSlowFunc_0(int a_0) { uint _S1 = uint(a_0); - -#line 26 int i_0; - -#line 26 uint v_0; - -#line 26 livenessStart_0(i_0, 0); - -#line 26 i_0 = 0; - -#line 26 livenessStart_1(v_0, 0); - -#line 26 v_0 = _S1; - -#line 26 for(;;) { - -#line 27 if(i_0 < a_0 * 20) { } @@ -62,53 +36,18 @@ int someSlowFunc_0(int a_0) { break; } - -#line 29 uint _S2 = v_0 >> 1; - -#line 29 uint _S3 = v_0; - -#line 29 livenessEnd_0(v_0, 0); - -#line 29 uint _S4 = (_S2 | _S3 << 31) * uint(i_0); - -#line 27 - int _S5 = i_0; - -#line 27 - livenessEnd_1(i_0, 0); - -#line 27 - int i_1 = _S5 + 1; - -#line 27 - livenessStart_0(i_0, 0); - -#line 27 - i_0 = i_1; - -#line 27 + i_0 = i_0 + 1; livenessStart_1(v_0, 0); - -#line 27 v_0 = _S4; - -#line 27 } - -#line 27 livenessEnd_1(i_0, 0); - - - return int(v_0); } - -#line 6 struct SomeStruct_0 { int a_1; @@ -116,239 +55,107 @@ struct SomeStruct_0 int c_0[100]; }; - -#line 17 SomeStruct_0 makeSomeStruct_0() { - const int _S6[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - -#line 19 - SomeStruct_0 s_0 = { 0, 0, _S6 }; + const int _S5[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + SomeStruct_0 s_0 = { 0, 0, _S5 }; return s_0; } - -#line 15 -layout(std430, binding = 1) buffer _S7 { +layout(std430, binding = 1) buffer _S6 { int _data[]; } anotherBuffer_0; - -#line 40 int doThing_0(SomeStruct_0 s_1) { return s_1.x_0 * 2 + 1; } - -#line 34 int somethingElse_0(inout SomeStruct_0 s_2) { s_2.x_0 = s_2.x_0 + 1; return s_2.x_0; } - -#line 4 -layout(std430, binding = 0) buffer _S8 { +layout(std430, binding = 0) buffer _S7 { int _data[]; } outputBuffer_0; - -#line 4 spirv_instruction(id = 256) void livenessStart_2(spirv_by_reference SomeStruct_0 _0, spirv_literal int _1); - -#line 62 spirv_instruction(id = 257) void livenessEnd_2(spirv_by_reference SomeStruct_0 _0, spirv_literal int _1); - -#line 46 layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; void main() { - -#line 48 int index_0 = int(gl_GlobalInvocationID.x); - -#line 48 - int i_2; - -#line 48 + int i_1; int res_0; - -#line 48 - livenessStart_0(i_2, 0); - -#line 48 - i_2 = 0; - -#line 48 + livenessStart_0(i_1, 0); + i_1 = 0; livenessStart_0(res_0, 0); - -#line 48 res_0 = index_0; - -#line 48 for(;;) { - - - if(i_2 < index_0) + if(i_1 < index_0) { } else { break; } - -#line 54 int v_1 = someSlowFunc_0(index_0); - SomeStruct_0 s_3; - -#line 56 livenessStart_2(s_3, 0); SomeStruct_0 t_0; - -#line 57 livenessStart_2(t_0, 0); - -#line 57 - SomeStruct_0 _S9 = makeSomeStruct_0(); - -#line 57 - t_0 = _S9; - const int _S10[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - -#line 58 - SomeStruct_0 u_0 = { 0, 0, _S10 }; - -#line 58 + SomeStruct_0 _S8 = makeSomeStruct_0(); + t_0 = _S8; + const int _S9[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + SomeStruct_0 u_0 = { 0, 0, _S9 }; SomeStruct_0 u_1; - if(bool(v_1 & 256)) { s_3.x_0 = ((anotherBuffer_0)._data[(uint(v_1 & 3))]); t_0.x_0 = ((anotherBuffer_0)._data[(uint(v_1 & 3))]); - -#line 63 livenessStart_2(u_1, 0); - -#line 63 u_1 = u_0; - -#line 63 } else { - - SomeStruct_0 x_1; - -#line 68 livenessStart_2(x_1, 0); - -#line 68 x_1 = u_0; x_1.x_0 = ((anotherBuffer_0)._data[(uint(v_1 & 3))]) + 1; - SomeStruct_0 _S11 = x_1; - -#line 70 + SomeStruct_0 _S10 = x_1; livenessEnd_2(x_1, 0); - -#line 70 livenessStart_2(u_1, 0); - -#line 70 - u_1 = _S11; - -#line 70 + u_1 = _S10; } - - - s_3.c_0[index_0 & 7] = s_3.c_0[index_0 & 7] + 1; - - int _S12 = s_3.x_0 + t_0.x_0; - -#line 76 - SomeStruct_0 _S13 = u_1; - -#line 76 + int _S11 = s_3.x_0 + t_0.x_0; + SomeStruct_0 _S12 = u_1; livenessEnd_2(u_1, 0); - -#line 76 - int _S14 = _S12 + _S13.x_0; - -#line 76 - int _S15 = doThing_0(t_0); - -#line 76 - int _S16 = _S14 + _S15; - -#line 76 - int _S17 = somethingElse_0(t_0); - -#line 76 + int _S13 = _S11 + _S12.x_0; + int _S14 = doThing_0(t_0); + int _S15 = _S13 + _S14; + int _S16 = somethingElse_0(t_0); livenessEnd_2(t_0, 0); - -#line 76 - int _S18 = _S16 + _S17; - -#line 76 - int _S19 = s_3.c_0[2]; - -#line 76 + int _S17 = _S15 + _S16; + int _S18 = s_3.c_0[2]; livenessEnd_2(s_3, 0); - -#line 76 - int _S20 = _S18 + _S19; - -#line 76 - int _S21 = res_0; - -#line 76 + int _S19 = _S17 + _S18; + int _S20 = res_0; livenessEnd_1(res_0, 0); - -#line 76 - int res_1 = _S21 + _S20; - -#line 52 - int _S22 = i_2; - -#line 52 - livenessEnd_1(i_2, 0); - -#line 52 - int i_3 = _S22 + 1; - -#line 52 - livenessStart_0(i_2, 0); - -#line 52 - i_2 = i_3; - -#line 52 + int res_1 = _S20 + _S19; + i_1 = i_1 + 1; livenessStart_0(res_0, 0); - -#line 52 res_0 = res_1; - -#line 52 } - -#line 52 - livenessEnd_1(i_2, 0); - -#line 52 - int _S23 = res_0; - -#line 52 + livenessEnd_1(i_1, 0); + int _S21 = res_0; livenessEnd_1(res_0, 0); - -#line 79 - ((outputBuffer_0)._data[(uint(index_0))]) = _S23; + ((outputBuffer_0)._data[(uint(index_0))]) = _S21; return; } |
