fixll.py raw
1 #!/usr/bin/env python3
2 import re, sys, subprocess
3
4 path = sys.argv[1]
5
6 for iteration in range(100):
7 result = subprocess.run(
8 ['clang-21', '--target=x86_64-unknown-linux-musleabihf', '-c', '-o', '/dev/null', path],
9 capture_output=True, text=True
10 )
11 if result.returncode == 0:
12 print(f"CLEAN after {iteration} iterations")
13 sys.exit(0)
14
15 err = result.stderr.split('\n')[0]
16 if 'error:' not in err:
17 print(f"Non-error output: {err}")
18 sys.exit(1)
19
20 print(f"iter {iteration}: {err}")
21
22 lines = open(path).readlines()
23
24 # Parse error line number
25 m = re.search(r':(\d+):\d+: error:', err)
26 if not m:
27 print(" can't parse line number")
28 sys.exit(1)
29 err_line = int(m.group(1)) - 1
30
31 # Pattern 1: '%reg' defined with type 'ptr' but expected '{...}'
32 m = re.search(r"'(%\S+)' defined with type 'ptr' but expected '(\{[^']+\})'", err)
33 if m:
34 reg, expected = m.group(1), m.group(2)
35 for j in range(err_line - 1, max(err_line - 200, 0), -1):
36 if lines[j].strip().startswith(reg + ' = load ptr,'):
37 lines[j] = lines[j].replace(reg + ' = load ptr,', reg + ' = load ' + expected + ',')
38 open(path, 'w').writelines(lines)
39 print(f" fixed load at line {j+1}")
40 break
41 if lines[j].strip().startswith(reg + ' = '):
42 print(f" {reg} defined at line {j+1} as: {lines[j].rstrip()}")
43 # If it's a function param, fix the define line
44 for k in range(j, max(j-500, 0), -1):
45 if lines[k].startswith('define'):
46 if 'ptr ' + reg in lines[k]:
47 lines[k] = lines[k].replace('ptr ' + reg, expected + ' ' + reg, 1)
48 open(path, 'w').writelines(lines)
49 print(f" fixed param in define at line {k+1}")
50 break
51 break
52 continue
53
54 # Pattern: store iN %reg where %reg is ptr param
55 m = re.search(r"'(%\S+)' defined with type 'ptr' but expected '(i\d+)'", err)
56 if m:
57 reg, expected = m.group(1), m.group(2)
58 for j in range(err_line - 1, max(err_line - 500, 0), -1):
59 if lines[j].startswith('define') and 'ptr ' + reg in lines[j]:
60 lines[j] = lines[j].replace('ptr ' + reg, expected + ' ' + reg, 1)
61 open(path, 'w').writelines(lines)
62 print(f' fixed param type at line {j+1}')
63 break
64 continue
65
66 # Pattern: '%reg' defined with type 'iN' but expected 'ptr'
67 m = re.search(r"'(%\S+)' defined with type '(i\d+)' but expected 'ptr'", err)
68 if m:
69 reg, actual = m.group(1), m.group(2)
70 fix_reg = reg + '.itp' + str(iteration)
71 fix_line = ' ' + fix_reg + ' = inttoptr ' + actual + ' ' + reg + ' to ptr\n'
72 lines[err_line] = lines[err_line].replace('ptr ' + reg, 'ptr ' + fix_reg)
73 lines.insert(err_line, fix_line)
74 open(path, 'w').writelines(lines)
75 print(f" inserted inttoptr at line {err_line+1}")
76 continue
77
78 # Pattern: '%reg' defined with type '{...}' but expected 'iN'
79 m = re.search(r"'(%\S+)' defined with type '(\{[^']+\})' but expected '(i\d+)'", err)
80 if m:
81 reg, actual, expected = m.group(1), m.group(2), m.group(3)
82 for j in range(err_line - 1, max(err_line - 200, 0), -1):
83 if lines[j].strip().startswith(reg + ' = load ' + actual + ','):
84 lines[j] = lines[j].replace('load ' + actual + ',', 'load ' + expected + ',')
85 open(path, 'w').writelines(lines)
86 print(f" fixed struct->int load at line {j+1}")
87 break
88 if lines[j].strip().startswith(reg + ' = '):
89 break
90 continue
91
92 # Pattern 2: '%reg' defined with type '{...}' but expected 'ptr'
93 m = re.search(r"'(%\S+)' defined with type '(\{[^']+\})' but expected 'ptr'", err)
94 if m:
95 reg, actual = m.group(1), m.group(2)
96 fix_reg = reg + '.fix' + str(iteration)
97 fix_line = ' ' + fix_reg + ' = extractvalue ' + actual + ' ' + reg + ', 0\n'
98 lines[err_line] = lines[err_line].replace('ptr ' + reg, 'ptr ' + fix_reg)
99 lines.insert(err_line, fix_line)
100 open(path, 'w').writelines(lines)
101 print(f" inserted extractvalue at line {err_line+1}")
102 continue
103
104 # Pattern 3: trunc to void
105 if 'void type only allowed' in err:
106 m = re.search(r'trunc (i\d+) (%\S+) to void', lines[err_line])
107 if m:
108 lines[err_line] = lines[err_line].replace(
109 'trunc ' + m.group(1) + ' ' + m.group(2) + ' to void',
110 'add ' + m.group(1) + ' ' + m.group(2) + ', 0'
111 )
112 open(path, 'w').writelines(lines)
113 print(f" fixed trunc to void at line {err_line+1}")
114 continue
115
116 # Pattern 4: null must be a pointer type
117 if 'null must be a pointer' in err:
118 lines[err_line] = lines[err_line].replace(' null,', ' zeroinitializer,')
119 open(path, 'w').writelines(lines)
120 print(f" fixed null -> zeroinitializer at line {err_line+1}")
121 continue
122
123 # Pattern: integer constant must have integer type (ptr used where int expected)
124 if 'integer constant must have integer type' in err:
125 line = lines[err_line]
126 # Fix: icmp eq ptr %x, N -> icmp eq i32 %x, N (with ptrtoint)
127 m2 = re.search(r'icmp (\S+) ptr (%\S+), (\d+)', line)
128 if m2:
129 fix_reg = m2.group(2) + '.pti' + str(iteration)
130 fix_line = ' ' + fix_reg + ' = ptrtoint ptr ' + m2.group(2) + ' to i32\n'
131 lines[err_line] = line.replace(
132 'icmp ' + m2.group(1) + ' ptr ' + m2.group(2) + ', ' + m2.group(3),
133 'icmp ' + m2.group(1) + ' i32 ' + fix_reg + ', ' + m2.group(3)
134 )
135 lines.insert(err_line, fix_line)
136 open(path, 'w').writelines(lines)
137 print(f" fixed ptr/int comparison at line {err_line+1}")
138 continue
139
140 # Pattern: expected type 'ptr' (generic)
141 if "expected 'ptr'" in err:
142 m2 = re.search(r"'(%\S+)' defined with type '(\S+)' but expected 'ptr'", err)
143 if m2:
144 reg, actual = m2.group(1), m2.group(2)
145 if actual.startswith('i'):
146 fix_reg = reg + '.itp' + str(iteration)
147 fix_line = ' ' + fix_reg + ' = inttoptr ' + actual + ' ' + reg + ' to ptr\n'
148 lines[err_line] = lines[err_line].replace('ptr ' + reg, 'ptr ' + fix_reg)
149 lines.insert(err_line, fix_line)
150 open(path, 'w').writelines(lines)
151 print(f" inserted inttoptr for {actual} at line {err_line+1}")
152 continue
153
154 # Pattern: iN where struct expected (corrupted type)
155 m = re.search(r"'(%\S+)' defined with type '(i\d+)' but expected '(\{[^']+\})'", err)
156 if m:
157 reg, actual, expected = m.group(1), m.group(2), m.group(3)
158 for j in range(err_line - 1, max(err_line - 200, 0), -1):
159 if lines[j].strip().startswith(reg + ' = load ' + actual + ','):
160 lines[j] = lines[j].replace('load ' + actual + ',', 'load ' + expected + ',')
161 open(path, 'w').writelines(lines)
162 print(f' fixed load type at line {j+1}')
163 break
164 if lines[j].strip().startswith(reg + ' = '):
165 print(f' {reg} defined differently: {lines[j].rstrip()}')
166 break
167 continue
168
169 print(f" UNHANDLED: {err}")
170 sys.exit(1)
171
172 print("Hit max iterations")
173 sys.exit(1)
174