transform: add -print-allocs-cover and restore -print-allocs reason output

PR #5220 changed -print-allocs output to the go coverage tool format, which
replaced the original human-readable explanation of why each object had to be
heap allocated. That explanation is useful on its own, so this restores it as
the default behavior of -print-allocs and moves the coverage format behind a
-print-allocs-cover flag.

Signed-off-by: Piotr Bocheński <piotr@bochen.ski>
This commit is contained in:
Piotr Bocheński
2026-06-11 21:33:05 +02:00
committed by Ron Evans
parent c33113ab5f
commit 4465360f3d
9 changed files with 130 additions and 91 deletions
+20 -25
View File
@@ -46,10 +46,6 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
complex128Type := ctx.StructType([]llvm.Type{ctx.DoubleType(), ctx.DoubleType()}, false)
maxAlign := int64(targetData.ABITypeAlignment(complex128Type))
if printAllocs != nil {
fmt.Fprintln(os.Stderr, "mode: set")
}
// Find all allocator calls.
var heapallocs []llvm.Value
for _, allocator := range allocators {
@@ -61,7 +57,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
if heapalloc.Operand(0).IsAConstantInt().IsNil() {
// Do not allocate variable length arrays on the stack.
if logAllocs {
logAlloc(logger, heapalloc, "size is not constant")
logger(getPosition(heapalloc), "size is not constant")
}
continue
}
@@ -70,7 +66,8 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
if size > maxStackAlloc {
// The maximum size for a stack allocation.
if logAllocs {
logAlloc(logger, heapalloc, fmt.Sprintf("object size %d exceeds maximum stack allocation size %d", size, maxStackAlloc))
logger(getPosition(heapalloc),
fmt.Sprintf("object size %d exceeds maximum stack allocation size %d", size, maxStackAlloc))
}
continue
}
@@ -107,7 +104,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
if atPos.Line != 0 {
msg = fmt.Sprintf("escapes at line %d", atPos.Line)
}
logAlloc(logger, heapalloc, msg)
logger(getPosition(heapalloc), msg)
}
continue
}
@@ -146,6 +143,22 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, maxStackAlloc u
}
}
// FormatAllocReason renders the heap allocation in a human-readable format.
func FormatAllocReason(pos token.Position, reason string) string {
return fmt.Sprintf("%s: object allocated on the heap: %s", pos.String(), reason)
}
// FormatAllocCover renders the heap allocation in the go coverage tool format.
func FormatAllocCover(pos token.Position) string {
if pos.Filename == "" || pos.Line <= 0 {
return "" // no position info; a blank line would corrupt the profile
}
// Highlight the whole line: column 1 to one past the last byte (the end
// column is exclusive, so add 1 to the line length).
endCol := max(lineLengthAt(pos.Filename, pos.Line), 1) + 1
return fmt.Sprintf("%s:%d.1,%d.%d 1 0", pos.Filename, pos.Line, pos.Line, endCol)
}
// valueEscapesAt returns the instruction where the given value may escape and a
// nil llvm.Value if it definitely doesn't. The value must be an instruction.
func valueEscapesAt(value llvm.Value) llvm.Value {
@@ -189,24 +202,6 @@ func valueEscapesAt(value llvm.Value) llvm.Value {
return llvm.Value{}
}
// logAlloc prints a message to stderr explaining why the given object had to be
// allocated on the heap.
func logAlloc(logger func(token.Position, string), allocCall llvm.Value, reason string) {
pos := getPosition(allocCall)
if pos.Filename == "" || pos.Line <= 0 {
logger(pos, "")
return
}
endCol := lineLengthAt(pos.Filename, pos.Line)
if endCol < 1 {
endCol = 1
}
// Only emit the coverprofile line, without position prefix.
logger(token.Position{}, fmt.Sprintf("%s:%d.1,%d.%d 1 0", pos.Filename, pos.Line, pos.Line, endCol))
}
func lineLengthAt(filename string, lineNumber int) int {
f, err := os.Open(filename)
if err != nil {