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