llvm-project/compiler-rt/lib/profile/InstrProfilingPlatformOther.c
Joseph Huber ffd6a13b5f
[compiler-rt] Rework profile data handling for GPU targets (#187136)
Summary:
Currently, the GPU iterates through all of the present symbols and
copies them by prefix. This is inefficient as it requires a lot of small
high-latency data transfers rather than a few large ones. Additionally,
we force every single profiling symbol to have protected visibility.
This means potentially hundreds of unnecessary symbols in the symbol
table.

This PR changes the interface to move towards the start / stop section
handling. AMDGPU supports this natively as an ELF target, so we need
little changes. Instead of overriding visibility, we use a single table
to define the bounds that we can obtain with one contiguous load.

Using a table interface should also work for the in-progress HIP
implementation for this, as it wraps the start / stop sections into
standard void pointers which will be inside of an already mapped region
of memory, so they should be accessible from the HIP API.

NVPTX is more difficult as it is an ELF platform without this support. I
have hooked up the 'Other' handling to work around this, but even then
it's a bit of a stretch. I could remove this support here, but I wanted
to demonstrate that we can share the ABI. However, NVPTX will only work
if we force LTO and change the backend to emit variables in the same

TL;DR, we now do this:
```c
struct { start1, stop1, start2, stop2, start3, stop3, version; } device;
struct host = DtoH(lookup("device"));
counters = DtoH(host.stop - host.start)
version = DtoH(host.version);
```
2026-03-26 10:17:43 -05:00

157 lines
5.8 KiB
C

/*===- InstrProfilingPlatformOther.c - Profile data default platform ------===*\
|*
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|* See https://llvm.org/LICENSE.txt for license information.
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|*
\*===----------------------------------------------------------------------===*/
// This file defines a fallback implementation to compute the locations of
// profile data sections, for targets that don't have linker support. No
// commonly used targets use this codepath.
//
// This implementation expects the compiler instrumentation pass to define a
// constructor in each file which calls into this file.
#if (!defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \
!defined(__Fuchsia__) && !(defined(__sun__) && defined(__svr4__)) && \
!defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX) && \
!defined(__wasm__) && !defined(__HAIKU__) && \
!defined(COMPILER_RT_PROFILE_BAREMETAL)) || \
defined(__NVPTX__)
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#if defined(__NVPTX__)
extern __llvm_profile_gpu_sections INSTR_PROF_SECT_BOUNDS_TABLE;
#define DataFirst INSTR_PROF_SECT_BOUNDS_TABLE.DataStart
#define DataLast INSTR_PROF_SECT_BOUNDS_TABLE.DataStop
#define NamesFirst INSTR_PROF_SECT_BOUNDS_TABLE.NamesStart
#define NamesLast INSTR_PROF_SECT_BOUNDS_TABLE.NamesStop
#define CountersFirst INSTR_PROF_SECT_BOUNDS_TABLE.CountersStart
#define CountersLast INSTR_PROF_SECT_BOUNDS_TABLE.CountersStop
#else
static const __llvm_profile_data *DataFirst = NULL;
static const __llvm_profile_data *DataLast = NULL;
static const char *NamesFirst = NULL;
static const char *NamesLast = NULL;
static char *CountersFirst = NULL;
static char *CountersLast = NULL;
#endif
static const VTableProfData *VTableProfDataFirst = NULL;
static const VTableProfData *VTableProfDataLast = NULL;
static const char *VNamesFirst = NULL;
static const char *VNamesLast = NULL;
static char *BitmapFirst = NULL;
static char *BitmapLast = NULL;
static const void *getMinAddr(const void *A1, const void *A2) {
return A1 < A2 ? A1 : A2;
}
static const void *getMaxAddr(const void *A1, const void *A2) {
return A1 > A2 ? A1 : A2;
}
/*!
* \brief Register an instrumented function.
*
* Calls to this are emitted by clang with -fprofile-instr-generate. Such
* calls are only required (and only emitted) on targets where we haven't
* implemented linker magic to find the bounds of the sections.
*/
COMPILER_RT_VISIBILITY
void __llvm_profile_register_function(void *Data_) {
/* TODO: Only emit this function if we can't use linker magic. */
const __llvm_profile_data *Data = (__llvm_profile_data *)Data_;
#if defined(__NVPTX__)
// NVPTX stores absolute counter addresses to avoid circular dependencies in
// PTX global variable initializers. Convert to a relative offset so the
// host-side profile reader sees the standard format.
{
uintptr_t Rel = (uintptr_t)Data->CounterPtr - (uintptr_t)Data_;
__builtin_memcpy((char *)Data_ +
__builtin_offsetof(__llvm_profile_data, CounterPtr),
&Rel, sizeof(Rel));
}
#endif
if (!DataFirst) {
DataFirst = Data;
DataLast = Data + 1;
CountersFirst = (char *)((uintptr_t)Data_ + Data->CounterPtr);
CountersLast =
CountersFirst + Data->NumCounters * __llvm_profile_counter_entry_size();
return;
}
DataFirst = (const __llvm_profile_data *)getMinAddr(DataFirst, Data);
CountersFirst = (char *)getMinAddr(
CountersFirst, (char *)((uintptr_t)Data_ + Data->CounterPtr));
DataLast = (const __llvm_profile_data *)getMaxAddr(DataLast, Data + 1);
CountersLast = (char *)getMaxAddr(
CountersLast,
(char *)((uintptr_t)Data_ + Data->CounterPtr) +
Data->NumCounters * __llvm_profile_counter_entry_size());
}
COMPILER_RT_VISIBILITY
void __llvm_profile_register_names_function(void *NamesStart,
uint64_t NamesSize) {
if (!NamesFirst) {
NamesFirst = (const char *)NamesStart;
NamesLast = (const char *)NamesStart + NamesSize;
return;
}
NamesFirst = (const char *)getMinAddr(NamesFirst, NamesStart);
NamesLast =
(const char *)getMaxAddr(NamesLast, (const char *)NamesStart + NamesSize);
}
COMPILER_RT_VISIBILITY
const __llvm_profile_data *__llvm_profile_begin_data(void) { return DataFirst; }
COMPILER_RT_VISIBILITY
const __llvm_profile_data *__llvm_profile_end_data(void) { return DataLast; }
COMPILER_RT_VISIBILITY const VTableProfData *
__llvm_profile_begin_vtables(void) {
return VTableProfDataFirst;
}
COMPILER_RT_VISIBILITY const VTableProfData *__llvm_profile_end_vtables(void) {
return VTableProfDataLast;
}
COMPILER_RT_VISIBILITY
const char *__llvm_profile_begin_names(void) { return NamesFirst; }
COMPILER_RT_VISIBILITY
const char *__llvm_profile_end_names(void) { return NamesLast; }
COMPILER_RT_VISIBILITY
const char *__llvm_profile_begin_vtabnames(void) { return VNamesFirst; }
COMPILER_RT_VISIBILITY
const char *__llvm_profile_end_vtabnames(void) { return VNamesLast; }
COMPILER_RT_VISIBILITY
char *__llvm_profile_begin_counters(void) { return CountersFirst; }
COMPILER_RT_VISIBILITY
char *__llvm_profile_end_counters(void) { return CountersLast; }
COMPILER_RT_VISIBILITY
char *__llvm_profile_begin_bitmap(void) { return BitmapFirst; }
COMPILER_RT_VISIBILITY
char *__llvm_profile_end_bitmap(void) { return BitmapLast; }
COMPILER_RT_VISIBILITY
ValueProfNode *__llvm_profile_begin_vnodes(void) {
return 0;
}
COMPILER_RT_VISIBILITY
ValueProfNode *__llvm_profile_end_vnodes(void) { return 0; }
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = 0;
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = 0;
COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
return 0;
}
#endif