Skip to content

Commit d3f8e37

Browse files
committed
[BOLT] Add unit tests for negate_ra_state cfi handling
- also add match_dwarf.py, a tool used by the unit tests.
1 parent 21279d3 commit d3f8e37

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
2+
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
3+
# RUN: llvm-bolt %t.exe -o %t.exe.bolt | FileCheck %s
4+
# check that the output is listing foo as incorrect
5+
# CHECK: BOLT-INFO: inconsistent RAStates in function foo
6+
7+
# check that foo got Ignored, so it's not in the new .text section
8+
# llvm-objdump %t.exe -d -j .text > %t.exe.dump
9+
# RUN: not grep "<foo>:" %t.exe.dump
10+
11+
12+
.text
13+
.globl foo
14+
.p2align 2
15+
.type foo,@function
16+
foo:
17+
.cfi_startproc
18+
hint #25
19+
.cfi_negate_ra_state
20+
sub sp, sp, #16
21+
stp x29, x30, [sp, #16] // 16-byte Folded Spill
22+
.cfi_def_cfa_offset 16
23+
str w0, [sp, #12]
24+
ldr w8, [sp, #12]
25+
.cfi_negate_ra_state
26+
add w0, w8, #1
27+
ldp x29, x30, [sp, #16] // 16-byte Folded Reload
28+
add sp, sp, #16
29+
hint #29
30+
.cfi_negate_ra_state
31+
ret
32+
.Lfunc_end1:
33+
.size foo, .Lfunc_end1-foo
34+
.cfi_endproc
35+
36+
.global _start
37+
.type _start, %function
38+
_start:
39+
b foo
40+
41+
.reloc 0, R_AARCH64_NONE
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
2+
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
3+
4+
# RUN: llvm-objdump %t.exe -d > %t.exe.dump
5+
# RUN: llvm-objdump --dwarf=frames %t.exe -D > %t.exe.dump-dwarf
6+
# RUN: match-dwarf %t.exe.dump %t.exe.dump-dwarf foo > orig.txt
7+
8+
# RUN: llvm-bolt %t.exe -o %t.exe.bolt
9+
10+
# RUN: llvm-objdump %t.exe.bolt -d > %t.exe.bolt.dump
11+
# RUN: llvm-objdump --dwarf=frames %t.exe.bolt > %t.exe.bolt.dump-dwarf
12+
# RUN: match-dwarf %t.exe.bolt.dump %t.exe.bolt.dump-dwarf foo > bolted.txt
13+
14+
# RUN: diff orig.txt bolted.txt
15+
16+
.text
17+
.globl foo
18+
.p2align 2
19+
.type foo,@function
20+
foo:
21+
.cfi_startproc
22+
hint #25
23+
.cfi_negate_ra_state
24+
sub sp, sp, #16
25+
stp x29, x30, [sp, #16] // 16-byte Folded Spill
26+
.cfi_def_cfa_offset 16
27+
str w0, [sp, #12]
28+
ldr w8, [sp, #12]
29+
add w0, w8, #1
30+
ldp x29, x30, [sp, #16] // 16-byte Folded Reload
31+
add sp, sp, #16
32+
hint #29
33+
.cfi_negate_ra_state
34+
ret
35+
.Lfunc_end1:
36+
.size foo, .Lfunc_end1-foo
37+
.cfi_endproc
38+
39+
.global _start
40+
.type _start, %function
41+
_start:
42+
b foo

bolt/test/lit.cfg.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
config.substitutions.append(("%cxxflags", ""))
9090

9191
link_fdata_cmd = os.path.join(config.test_source_root, "link_fdata.py")
92+
match_dwarf_cmd = os.path.join(config.test_source_root, "match_dwarf.py")
9293

9394
tool_dirs = [config.llvm_tools_dir, config.test_source_root]
9495

@@ -131,6 +132,12 @@
131132
ToolSubst("llvm-readobj", unresolved="fatal"),
132133
ToolSubst("llvm-dwp", unresolved="fatal"),
133134
ToolSubst("split-file", unresolved="fatal"),
135+
ToolSubst(
136+
"match-dwarf",
137+
command=sys.executable,
138+
unresolved="fatal",
139+
extra_args=[match_dwarf_cmd],
140+
),
134141
]
135142
llvm_config.add_tool_substitutions(tools, tool_dirs)
136143

bolt/test/match_dwarf.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env python3
2+
3+
# This tool helps matching dwarf dumps
4+
# (= the output from running llvm-objdump --dwarf=frames),
5+
# by address to function names (which are parsed from a normal objdump).
6+
# The script is used for checking if .cfi_negate_ra_state CFIs
7+
# are generated by BOLT the same way they are generated by LLVM.
8+
9+
import argparse
10+
import subprocess
11+
import sys
12+
import re
13+
14+
15+
class NameDwarfPair(object):
16+
def __init__(self, name, body):
17+
self.name = name
18+
self.body = body
19+
self.finalized = False
20+
21+
def append(self, body_line):
22+
# only store elements into the body until the first whitespace line is encountered.
23+
if body_line.isspace():
24+
self.finalized = True
25+
if not self.finalized:
26+
self.body += body_line
27+
28+
def print(self):
29+
print(self.name)
30+
print(self.body)
31+
32+
def parse_negates(self):
33+
negate_offsets = []
34+
loc = 0
35+
# TODO: make sure this is not printed in hex
36+
re_advloc = f"DW_CFA_advance_loc: (\d+)"
37+
38+
for line in self.body.splitlines():
39+
# if line matches advance_loc int
40+
match = re.search(re_advloc, line)
41+
if match:
42+
loc += int(match.group(1))
43+
if "DW_CFA_AARCH64_negate_ra_state" in line:
44+
negate_offsets.append(loc)
45+
46+
self.negate_offsets = negate_offsets
47+
48+
def __eq__(self, other):
49+
return self.name == other.name and self.negate_offsets == other.negate_offsets
50+
51+
52+
def parse_objdump(objdump):
53+
"""
54+
Parse and return address-to-name dictionary from objdump file
55+
"""
56+
addr_name_dict = dict()
57+
re_function = re.compile(r"^([0-9a-fA-F]+)\s<(.*)>:$")
58+
with open(objdump, "r") as f:
59+
for line in f.readlines():
60+
match = re_function.match(line)
61+
if not match:
62+
continue
63+
m_addr = match.groups()[0]
64+
m_name = match.groups()[1]
65+
addr_name_dict[int(m_addr, 16)] = m_name
66+
67+
return addr_name_dict
68+
69+
70+
def parse_dwarf(dwarfdump, addr_name_dict):
71+
"""
72+
Parse dwarf dump, and match names to blocks using the dict from the objdump.
73+
Return a list of NameDwarfPairs.
74+
"""
75+
re_address_line = re.compile(r".*pc=([0-9a-fA-F]{8})\.\.\.([0-9a-fA-F]{8})")
76+
with open(dwarfdump, "r") as dw:
77+
functions = []
78+
for line in dw.readlines():
79+
match = re_address_line.match(line)
80+
if not match:
81+
if len(functions) > 0:
82+
functions[-1].append(line)
83+
continue
84+
pc_start_address = match.groups()[0]
85+
name = addr_name_dict.get(int(pc_start_address, 16))
86+
functions.append(NameDwarfPair(name, ""))
87+
88+
return functions
89+
90+
91+
def main():
92+
parser = argparse.ArgumentParser()
93+
parser.add_argument("objdump", help="Objdump file")
94+
parser.add_argument(
95+
"dwarfdump", help="dwarf dump file created with 'llvm-objdump --dwarf=frames'"
96+
)
97+
parser.add_argument("function", help="Function to search CFIs in.")
98+
99+
args = parser.parse_args()
100+
101+
addr_name_dict = parse_objdump(args.objdump)
102+
functions = parse_dwarf(args.dwarfdump, addr_name_dict)
103+
104+
for f in functions:
105+
if f.name == args.function:
106+
f.parse_negates()
107+
print(f.negate_offsets)
108+
break
109+
else:
110+
print(f"{args.function} not found")
111+
exit(-1)
112+
113+
114+
main()

0 commit comments

Comments
 (0)