Skip to content

Commit 1932ec0

Browse files
committed
Components/profiling: improve _mcount documentation with RISC-V ABI and LLVM bug workaround
Enhance the `_mcount` function documentation in gprof.c with comprehensive explanations of the RISC-V mcount calling convention and LLVM compiler bug workaround. **Changes:** - Add detailed Doxygen-style function documentation explaining - Document LLVM bug llvm/llvm-project#121103 **Verification:** Tested with demo_profiling example using nuclei_llvm toolchain: # Install required tools $ pip3 install -U gprof2dot # Build and run (wait ~60s, then press Ctrl+C to terminate) $ make TOOLCHAIN=nuclei_llvm DOWNLOAD=ddr clean $ make TOOLCHAIN=nuclei_llvm DOWNLOAD=ddr -j all $ make TOOLCHAIN=nuclei_llvm DOWNLOAD=ddr -j run_qemu > demo_profiling.log Ctrl + C to stop it (after ~30s) # Parse and generate profiling graph $ python3 ../../../Components/profiling/parse.py demo_profiling.log $ riscv64-unknown-elf-gprof demo_profiling.elf gmon.out | gprof2dot | dot -Tpng -o output.png **Impact:** - Helps developers understand the RISC-V profiling ABI requirements - Documents the LLVM workaround for future maintenance - Provides references to relevant specifications and bug reports Signed-off-by: Huaqi Fang <578567190@qq.com>
1 parent af33d6c commit 1932ec0

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

Components/profiling/gprof.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,62 @@ static void monstartup(size_t lowpc, size_t highpc)
265265
moncontrol(1); /* start */
266266
}
267267

268+
/**
269+
* @brief _mcount function called by compiler instrumentation (-pg flag)
270+
*
271+
* This function is automatically inserted at the entry of each instrumented function
272+
* by the compiler when -pg flag is enabled. It records call graph information for
273+
* gprof profiling, tracking which functions call which other functions and how often.
274+
*
275+
* @param frompcindex On RISC-V GCC: The return address (frompc) passed in a0 register,
276+
* pointing to the call site in the caller function.
277+
* On RISC-V LLVM: This parameter is NOT correctly passed due to
278+
* LLVM bug, so we must use __builtin_return_address(1) instead.
279+
*
280+
* How it works (RISC-V calling convention):
281+
* - The compiler instruments each function to call _mcount at entry
282+
* - GCC correctly passes the return address (ra register value) as frompcindex
283+
* - selfpc (callee PC): Current function address, obtained via __builtin_return_address(0)
284+
* - frompc (caller PC): Return address pointing back to the call site
285+
* - GCC: Correctly passed as the frompcindex parameter
286+
* - LLVM: Bug causes incorrect parameter passing, use __builtin_return_address(1)
287+
* - The function records an "arc" from caller (frompc) to callee (selfpc) in the
288+
* profiling data structures (froms[] -> tos[] hash table)
289+
* - Each arc maintains a count of how many times this call pair occurred
290+
*
291+
* Compiler differences (RISC-V specific):
292+
* - GCC: Follows RISC-V mcount ABI - passes return address (frompc) in a0 register
293+
* Reference: https://github.com/bminor/glibc/blob/master/sysdeps/riscv/machine-gmon.h
294+
* - LLVM/Clang: LLVM bug https://github.com/llvm/llvm-project/issues/121103
295+
* The return address is NOT passed to _mcount on RISC-V/AArch64/LoongArch,
296+
* causing empty/broken gprof call graphs. Workaround: use __builtin_return_address(1)
297+
* - Both compilers: __builtin_return_address(0) reliably returns the current function's address
298+
*
299+
* @note This is a known LLVM miscompilation issue affecting profiling on RISC-V.
300+
* The fix requires modifying LLVM's EntryExitInstrumenter to pass ra to _mcount.
301+
*/
268302
void _mcount(uint32_t *frompcindex)
269303
{
270-
register uint32_t *selfpc asm("ra");
304+
/* Get current function address (callee PC)
305+
*
306+
* NOTE: asm("ra") not working as expected for LLVM compiler, but works on GCC compiler.
307+
* __builtin_return_address(0) works for both compilers to get selfpc(callee PC).
308+
*/
309+
register uint32_t *selfpc = __builtin_return_address(0);
310+
311+
/* Get caller function's return address (from PC)
312+
*
313+
* RISC-V mcount ABI specifies that frompc (return address) should be passed
314+
* as the first argument to _mcount, but LLVM fails to do this.
315+
*
316+
* LLVM/Clang: Use __builtin_return_address(1) due to LLVM bug
317+
* (https://github.com/llvm/llvm-project/issues/121103)
318+
* The LLVM EntryExitInstrumenter doesn't pass ra to _mcount on RISC-V
319+
* GCC: Use the frompcindex parameter directly (correctly passed per RISC-V ABI)
320+
*/
321+
#ifdef __clang__
322+
frompcindex = __builtin_return_address(1);
323+
#endif
271324
register struct tostruct *top;
272325
register struct tostruct *prevtop;
273326
register long toindex;

doc/source/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ This is release version of ``0.9.0`` of Nuclei SDK, which is still under develop
200200
- Add LLVM/Clang ``-coverage`` option support in ``gcov.c`` with comprehensive Doxygen-style documentation
201201
- Fix printf format specifiers in ``gcov.c`` and ``gprof.c`` for better portability
202202
- Enhance profiling component documentation with GCC/LLVM toolchain support matrix, usage guide and result analysis examples
203+
- Improve profiling component ``_mcount`` function documentation in ``Components/profiling/gprof.c`` with detailed RISC-V calling convention explanation and LLVM bug workaround (https://github.com/llvm/llvm-project/issues/121103)
203204

204205
V0.8.1
205206
------

0 commit comments

Comments
 (0)