CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
142 lines
5.4 KiB
C++
142 lines
5.4 KiB
C++
// MIT License
|
|
//
|
|
// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#include "SQTTPass.h"
|
|
|
|
using namespace llvm;
|
|
|
|
SQTTInstrumentPass::BarrierKind SQTTInstrumentPass::classifyBarrier(CallInst* CI)
|
|
{
|
|
if (!CI) return BarrierKind::None;
|
|
Function* Callee = CI->getCalledFunction();
|
|
if (!Callee) return BarrierKind::None;
|
|
switch (Callee->getIntrinsicID())
|
|
{
|
|
case Intrinsic::amdgcn_s_barrier_signal:
|
|
case Intrinsic::amdgcn_s_barrier_signal_var:
|
|
case Intrinsic::amdgcn_s_barrier_signal_isfirst: return BarrierKind::Signal;
|
|
case Intrinsic::amdgcn_s_barrier_wait: return BarrierKind::Wait;
|
|
case Intrinsic::amdgcn_s_barrier: return BarrierKind::Full;
|
|
default: return BarrierKind::None;
|
|
}
|
|
}
|
|
|
|
bool SQTTInstrumentPass::instrumentBarriers(Function& F, GfxGen gen)
|
|
{
|
|
// Snapshot all insertion points before changing the CFG.
|
|
SmallVector<std::pair<Instruction*, uint32_t>, 8> insertions;
|
|
auto markerID = [this](BarrierKind kind)
|
|
{ return kind == BarrierKind::None ? 0 : FirstBarrierID + static_cast<uint32_t>(kind); };
|
|
for (auto& BB : F)
|
|
{
|
|
CallInst* signal = nullptr;
|
|
for (auto& I : BB)
|
|
{
|
|
auto* call = dyn_cast<CallInst>(&I);
|
|
BarrierKind kind = classifyBarrier(call);
|
|
if (signal && (signal->getNextNode() != &I || kind != BarrierKind::Wait))
|
|
{
|
|
insertions.push_back({signal->getNextNode(), markerID(BarrierKind::Signal)});
|
|
signal = nullptr;
|
|
}
|
|
if (kind == BarrierKind::Signal)
|
|
signal = call;
|
|
else if (kind == BarrierKind::Wait && signal)
|
|
{
|
|
insertions.push_back({call, markerID(BarrierKind::Full)});
|
|
signal = nullptr;
|
|
}
|
|
else if (kind == BarrierKind::Wait || kind == BarrierKind::Full)
|
|
insertions.push_back({call, markerID(kind)});
|
|
}
|
|
if (signal) insertions.push_back({signal->getNextNode(), markerID(BarrierKind::Signal)});
|
|
}
|
|
if (insertions.empty()) return false;
|
|
for (auto [before, markerID] : insertions)
|
|
{
|
|
IRBuilder<> B(before);
|
|
insertTraceMarker(B, encodeMarker(markerID, false, false), F, gen);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
SQTTInstrumentPass::MemOpKind SQTTInstrumentPass::classifyMemOp(Instruction* I)
|
|
{
|
|
if (Value* pointer = getMemoryPointer(I))
|
|
{
|
|
unsigned AS = cast<PointerType>(pointer->getType())->getAddressSpace();
|
|
// Atomics are read-modify-write, so use store markers.
|
|
if (AS == 3 || AS == 5) return MemOpKind::None;
|
|
return isa<LoadInst>(I) ? MemOpKind::Load : MemOpKind::Store;
|
|
}
|
|
auto* CI = dyn_cast<CallInst>(I);
|
|
Function* Callee = CI ? CI->getCalledFunction() : nullptr;
|
|
if (!Callee) return MemOpKind::None;
|
|
BufferOpKind kind = classifyBufferOp(Callee->getName());
|
|
return kind == BufferOpKind::Load ? MemOpKind::Load
|
|
: kind == BufferOpKind::None ? MemOpKind::None
|
|
: MemOpKind::Store;
|
|
}
|
|
|
|
bool SQTTInstrumentPass::instrumentMemoryOps(Function& F, GfxGen gen)
|
|
{
|
|
// Snapshot first: inserting a trace changes the instruction stream being chunked.
|
|
SmallVector<std::pair<Instruction*, uint32_t>, 16> insertions;
|
|
auto markerID = [this](MemOpKind kind)
|
|
{ return kind == MemOpKind::None ? 0 : FirstVmemID + static_cast<uint32_t>(kind); };
|
|
for (auto& BB : F)
|
|
{
|
|
MemOpKind runKind = MemOpKind::None;
|
|
Instruction* lastOp = nullptr;
|
|
unsigned runSize = 0, gap = 0;
|
|
auto flush = [&]
|
|
{
|
|
if (runSize) insertions.push_back({lastOp, markerID(runKind)});
|
|
runSize = 0;
|
|
};
|
|
for (auto& I : BB)
|
|
{
|
|
MemOpKind kind = classifyMemOp(&I);
|
|
if (kind == MemOpKind::None)
|
|
{
|
|
gap += lastOp != nullptr;
|
|
continue;
|
|
}
|
|
if (kind != runKind || gap > Config.MemoryMaxGap) flush();
|
|
|
|
runKind = kind;
|
|
lastOp = &I;
|
|
gap = 0;
|
|
if (++runSize == Config.MemoryChunkSize) flush();
|
|
}
|
|
flush();
|
|
}
|
|
if (insertions.empty()) return false;
|
|
|
|
for (auto [lastOp, markerID] : insertions)
|
|
{
|
|
IRBuilder<> B(lastOp->getNextNode());
|
|
insertTraceMarker(B, encodeMarker(markerID, false, false), F, gen);
|
|
}
|
|
return true;
|
|
}
|