summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-06-13 14:00:48 -0400
committerGitHub <noreply@github.com>2022-06-13 14:00:48 -0400
commit68d9d87f9385a8c7c29443dcfcbf70434dc889bd (patch)
tree2898c7443e21c311b4a2aa3ba36f9c07e5ae1bd0
parent522e14116b7c7d4eccf0a855ffbcc2076d44db88 (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.cpp363
-rw-r--r--tests/experimental/liveness/liveness-2.slang27
-rw-r--r--tests/experimental/liveness/liveness-2.slang.expected60
-rw-r--r--tests/experimental/liveness/liveness-3.slang49
-rw-r--r--tests/experimental/liveness/liveness-3.slang.expected144
-rw-r--r--tests/experimental/liveness/liveness-4.slang35
-rw-r--r--tests/experimental/liveness/liveness-4.slang.expected82
-rw-r--r--tests/experimental/liveness/liveness-5.slang42
-rw-r--r--tests/experimental/liveness/liveness-5.slang.expected107
-rw-r--r--tests/experimental/liveness/liveness-6.slang47
-rw-r--r--tests/experimental/liveness/liveness-6.slang.expected115
-rw-r--r--tests/experimental/liveness/liveness-7.slang32
-rw-r--r--tests/experimental/liveness/liveness-7.slang.expected66
-rw-r--r--tests/experimental/liveness/liveness.slang2
-rw-r--r--tests/experimental/liveness/liveness.slang.expected253
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;
}