[llvm] [XRay] Don't count debug instructions towards the instruction threshold (PR #219100)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 20:53:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
Author: Alexey Milovidov (alexey-milovidov)
<details>
<summary>Changes</summary>
`XRayInstrumentation` decides whether a loop-free function is too small to instrument by counting MachineInstrs:
```cpp
// Count the number of MachineInstr`s in MachineFunction
uint64_t MICount = 0;
for (const auto &MBB : MF)
MICount += MBB.size();
bool TooFewInstrs = MICount < XRayThreshold;
```
`MachineBasicBlock::size()` counts the debug pseudo-instructions too, so building with debug info instruments functions that the very same source built without it leaves alone. Two consequences:
* the binary that ships is not the binary that was tested, and its code layout differs;
* a size or layout comparison between a `-g` build and a `-g0` build of one commit shows a difference that no source change caused.
Measured on ClickHouse (aarch64, `-O3 -flto=thin -fxray-instrument -fxray-modes=none -fxray-instrumentation-bundle=function`, default threshold of 200), comparing a `-g` build against a `-g0` build, both after `strip --strip-debug`:
| section | Δ bytes |
|---|---:|
| `xray_instr_map` | +925,024 |
| `xray_fn_idx` | +176,448 |
| `.text` (entry/exit sled NOPs plus function alignment) | +989,952 |
| `.symtab` | +1,059,096 |
| `.strtab` | +39,895 |
| `.eh_frame` / `.gcc_except_table` / `.eh_frame_hdr` | +16,436 |
That is 3.06 MiB on a 712 MiB binary (0.43%), and it all reconciles to one number: `xray_fn_idx` grows by exactly 11,028 entries of 16 bytes, i.e. **11,028 functions instrumented only because the module carries debug info**. The `.symtab` growth is 2 × 11,028 `.Lxray_sleds_start*`/`.Lxray_fn_idx*` labels plus 22,063 AArch64 `$d` mapping symbols for the new per-function sections, and `.text` is 28,907 sleds of 32 bytes.
This patch skips debug instructions in the count. `-g0` behaviour is unchanged by construction, so no existing test moves.
The added test pins the boundary: a function with two real instructions and three debug records against a threshold of four. It is instrumented before this patch and not after, and a companion function above the threshold is still instrumented either way.
I kept the predicate at `MI.isDebugInstr()` rather than `MI.isMetaInstruction()` deliberately: the latter would also stop counting `IMPLICIT_DEF`, `KILL`, `CFI_INSTRUCTION` and friends, which would change which functions get instrumented in builds without debug info as well. Happy to switch if reviewers prefer that.
---
Full diff: https://github.com/llvm/llvm-project/pull/219100.diff
2 Files Affected:
- (modified) llvm/lib/CodeGen/XRayInstrumentation.cpp (+6-2)
- (added) llvm/test/CodeGen/X86/xray-instruction-threshold-debug-info.ll (+45)
``````````diff
diff --git a/llvm/lib/CodeGen/XRayInstrumentation.cpp b/llvm/lib/CodeGen/XRayInstrumentation.cpp
index f23357ef0bd17..0a69e7d05fe5e 100644
--- a/llvm/lib/CodeGen/XRayInstrumentation.cpp
+++ b/llvm/lib/CodeGen/XRayInstrumentation.cpp
@@ -217,10 +217,14 @@ bool XRayInstrumentation::run(MachineFunction &MF) {
if (XRayThreshold == std::numeric_limits<uint64_t>::max())
return false;
- // Count the number of MachineInstr`s in MachineFunction
+ // Count the number of MachineInstr`s in MachineFunction, skipping debug
+ // instructions: they are not code, so whether a function is instrumented
+ // must not depend on whether the module was built with debug info.
uint64_t MICount = 0;
for (const auto &MBB : MF)
- MICount += MBB.size();
+ MICount += llvm::count_if(MBB.instrs(), [](const MachineInstr &MI) {
+ return !MI.isDebugInstr();
+ });
bool TooFewInstrs = MICount < XRayThreshold;
diff --git a/llvm/test/CodeGen/X86/xray-instruction-threshold-debug-info.ll b/llvm/test/CodeGen/X86/xray-instruction-threshold-debug-info.ll
new file mode 100644
index 0000000000000..5d5d8647ba63b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/xray-instruction-threshold-debug-info.ll
@@ -0,0 +1,45 @@
+; Debug instructions are not code, so they must not count towards the XRay
+; instruction threshold: whether a function gets instrumented has to be the
+; same whether or not the module is built with debug info.
+;
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+; Two real instructions and three debug records, with a threshold of four: the
+; function stays uninstrumented.
+define i32 @below_threshold(i32 %a) nounwind uwtable "xray-instruction-threshold"="4" !dbg !4 {
+entry:
+ #dbg_value(i32 %a, !7, !DIExpression(), !10)
+ #dbg_value(i32 %a, !8, !DIExpression(), !10)
+ #dbg_value(i32 %a, !9, !DIExpression(), !10)
+ ret i32 %a, !dbg !10
+}
+
+; CHECK-LABEL: below_threshold:
+; CHECK-NOT: xray_sled
+
+; The same function over the threshold is still instrumented.
+define i32 @above_threshold(i32 %a) nounwind uwtable "xray-instruction-threshold"="2" !dbg !11 {
+entry:
+ ret i32 %a, !dbg !12
+}
+
+; CHECK-LABEL: above_threshold:
+; CHECK: xray_sled_0:
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "t.c", directory: "/")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !{i32 7, !"Dwarf Version", i32 5}
+!4 = distinct !DISubprogram(name: "below_threshold", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!5 = !DISubroutineType(types: !6)
+!6 = !{!13, !13}
+!7 = !DILocalVariable(name: "x", scope: !4, file: !1, line: 1, type: !13)
+!8 = !DILocalVariable(name: "y", scope: !4, file: !1, line: 1, type: !13)
+!9 = !DILocalVariable(name: "z", scope: !4, file: !1, line: 1, type: !13)
+!10 = !DILocation(line: 1, column: 1, scope: !4)
+!11 = distinct !DISubprogram(name: "above_threshold", scope: !1, file: !1, line: 2, type: !5, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0)
+!12 = !DILocation(line: 2, column: 1, scope: !11)
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
``````````
</details>
https://github.com/llvm/llvm-project/pull/219100
More information about the llvm-commits
mailing list