
This patch is part of a set of patches that add an `-fextend-lifetimes` flag to clang, which extends the lifetimes of local variables and parameters for improved debuggability. In addition to that flag, the patch series adds a pragma to selectively disable `-fextend-lifetimes`, and an `-fextend-this-ptr` flag which functions as `-fextend-lifetimes` for this pointers only. All changes and tests in these patches were written by Wolfgang Pieb (@wolfy1961), while Stephen Tozer (@SLTozer) has handled review and merging. The extend lifetimes flag is intended to eventually be set on by `-Og`, as discussed in the RFC here: https://discourse.llvm.org/t/rfc-redefine-og-o1-and-add-a-new-level-of-og/72850 This patch implements a new intrinsic instruction in LLVM, `llvm.fake.use` in IR and `FAKE_USE` in MIR, that takes a single operand and has no effect other than "using" its operand, to ensure that its operand remains live until after the fake use. This patch does not emit fake uses anywhere; the next patch in this sequence causes them to be emitted from the clang frontend, such that for each variable (or this) a fake.use operand is inserted at the end of that variable's scope, using that variable's value. This patch covers everything post-frontend, which is largely just the basic plumbing for a new intrinsic/instruction, along with a few steps to preserve the fake uses through optimizations (such as moving them ahead of a tail call or translating them through SROA). Co-authored-by: Stephen Tozer <stephen.tozer@sony.com>
108 lines
4.8 KiB
Python
108 lines
4.8 KiB
Python
#!/usr/bin/python3
|
|
|
|
# Parsing dwarfdump's output to determine whether the location list for the
|
|
# parameter "b" covers all of the function. The script searches for information
|
|
# in the input file to determine the [prologue, epilogue) range for the
|
|
# function, the location list range for "b", and checks that the latter covers
|
|
# the entirety of the former.
|
|
import re
|
|
import sys
|
|
|
|
DebugInfoPattern = r"\.debug_info contents:"
|
|
DebugLinePattern = r"\.debug_line contents:"
|
|
ProloguePattern = r"^\s*0x([0-9a-f]+)\s.+prologue_end"
|
|
EpiloguePattern = r"^\s*0x([0-9a-f]+)\s.+epilogue_begin"
|
|
FormalPattern = r"^0x[0-9a-f]+:\s+DW_TAG_formal_parameter"
|
|
LocationPattern = r"DW_AT_location\s+\[DW_FORM_([a-z_]+)\](?:.*0x([a-f0-9]+))"
|
|
DebugLocPattern = r'\[0x([a-f0-9]+),\s+0x([a-f0-9]+)\) ".text": (.+)$'
|
|
|
|
SeenDebugInfo = False
|
|
SeenDebugLine = False
|
|
LocationRanges = None
|
|
PrologueEnd = None
|
|
EpilogueBegin = None
|
|
|
|
# The dwarfdump output should contain the DW_AT_location for "b" first, then the
|
|
# line table which should contain prologue_end and epilogue_begin entries.
|
|
with open(sys.argv[1], "r") as dwarf_dump_file:
|
|
dwarf_iter = iter(dwarf_dump_file)
|
|
for line in dwarf_iter:
|
|
if not SeenDebugInfo and re.match(DebugInfoPattern, line):
|
|
SeenDebugInfo = True
|
|
if not SeenDebugLine and re.match(DebugLinePattern, line):
|
|
SeenDebugLine = True
|
|
# Get the range of DW_AT_location for "b".
|
|
if LocationRanges is None:
|
|
if match := re.match(FormalPattern, line):
|
|
# Go until we either find DW_AT_location or reach the end of this entry.
|
|
location_match = None
|
|
while location_match is None:
|
|
if (line := next(dwarf_iter, "")) == "\n":
|
|
raise RuntimeError(
|
|
".debug_info output is missing DW_AT_location for 'b'"
|
|
)
|
|
location_match = re.search(LocationPattern, line)
|
|
# Variable has whole-scope location, represented by an empty tuple.
|
|
if location_match.group(1) == "exprloc":
|
|
LocationRanges = ()
|
|
continue
|
|
if location_match.group(1) != "sec_offset":
|
|
raise RuntimeError(
|
|
f"Unhandled form for DW_AT_location: DW_FORM_{location_match.group(1)}"
|
|
)
|
|
# Variable has location range list.
|
|
if (
|
|
debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, ""))
|
|
) is None:
|
|
raise RuntimeError(f"Invalid location range list for 'b'")
|
|
LocationRanges = (
|
|
int(debug_loc_match.group(1), 16),
|
|
int(debug_loc_match.group(2), 16),
|
|
)
|
|
while (
|
|
debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, ""))
|
|
) is not None:
|
|
match_loc_start = int(debug_loc_match.group(1), 16)
|
|
match_loc_end = int(debug_loc_match.group(2), 16)
|
|
match_expr = debug_loc_match.group(3)
|
|
if match_loc_start != LocationRanges[1]:
|
|
raise RuntimeError(
|
|
f"Location list for 'b' is discontinuous from [0x{LocationRanges[1]:x}, 0x{match_loc_start:x})"
|
|
)
|
|
if "stack_value" in match_expr:
|
|
raise RuntimeError(
|
|
f"Location list for 'b' contains a stack_value expression: {match_expr}"
|
|
)
|
|
LocationRanges = (LocationRanges[0], match_loc_end)
|
|
# Get the prologue_end address.
|
|
elif PrologueEnd is None:
|
|
if match := re.match(ProloguePattern, line):
|
|
PrologueEnd = int(match.group(1), 16)
|
|
# Get the epilogue_begin address.
|
|
elif EpilogueBegin is None:
|
|
if match := re.match(EpiloguePattern, line):
|
|
EpilogueBegin = int(match.group(1), 16)
|
|
break
|
|
|
|
if not SeenDebugInfo:
|
|
raise RuntimeError(".debug_info section not found.")
|
|
if not SeenDebugLine:
|
|
raise RuntimeError(".debug_line section not found.")
|
|
|
|
if LocationRanges is None:
|
|
raise RuntimeError(".debug_info output is missing parameter 'b'")
|
|
if PrologueEnd is None:
|
|
raise RuntimeError(".debug_line output is missing prologue_end")
|
|
if EpilogueBegin is None:
|
|
raise RuntimeError(".debug_line output is missing epilogue_begin")
|
|
|
|
if len(LocationRanges) == 2 and (
|
|
LocationRanges[0] > PrologueEnd or LocationRanges[1] < EpilogueBegin
|
|
):
|
|
raise RuntimeError(
|
|
f"""Location list for 'b' does not cover the whole function:")
|
|
Prologue to Epilogue = [0x{PrologueEnd:x}, 0x{EpilogueBegin:x})
|
|
Location range = [0x{LocationRanges[0]:x}, 0x{LocationRanges[1]:x})
|
|
"""
|
|
)
|