PageRenderTime 78ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/compilers/1/alloc/parse_iloc.py

https://github.com/v/school
Python | 500 lines | 465 code | 29 blank | 6 comment | 32 complexity | 1ec2b030f445cdd77c7f7542974e677d MD5 | raw file
  1. import sys, os
  2. import re
  3. if (len(sys.argv) < 4):
  4. print "Not enough arguments"
  5. print "Usage: parse_iloc.py <iloc_file> <allocation_scheme> <number of total registers>"
  6. sys.exit();
  7. # Returns a hash that returns information about which virtual register is allocated to which physical register.
  8. def naive_register_allocate(code, available):
  9. register_freqs = {}
  10. for line in code:
  11. for register in line['read']:
  12. if(not register.startswith('r')):
  13. continue
  14. if(register in register_freqs):
  15. register_freqs[register] += 1
  16. else:
  17. register_freqs[register] = 1
  18. if(line['write'] and line['write'].startswith('r')):
  19. register = line['write']
  20. if(register in register_freqs):
  21. register_freqs[register] += 1
  22. else:
  23. register_freqs[register] = 1
  24. items = sorted(register_freqs.items(), key=lambda x: -1*x[1])
  25. # print items
  26. rv = {}
  27. for i in range(0, available):
  28. if i >= len(items):
  29. break
  30. rv[items[i][0]] = 'r'+str(i+1)
  31. # print rv
  32. return rv
  33. def is_used_in_future(reg, code, line):
  34. for i in range(line, len(code)):
  35. if reg in code[i]['read'] or reg == code[i]['write']:
  36. return True
  37. return False
  38. def my_max(iterable, key=lambda x: x):
  39. if not len(iterable):
  40. return None
  41. rv = iterable[0]
  42. for i in range(1, len(iterable)):
  43. if key(rv) < key(iterable[i]):
  44. rv = iterable[i]
  45. return rv
  46. def get_free_reg(assigned_registers, reg_list, reg):
  47. for physical_reg, assigned_reg in assigned_registers.items():
  48. if assigned_reg not in reg_list:
  49. assigned_registers[physical_reg] = reg
  50. return physical_reg
  51. print "ALLOCATION ERROR. PLEASE FIX"
  52. sys.exit()
  53. # Analyzes the live range conflicts in the code, and returns back register assignments.
  54. def analyze_conflicts(conflicts, available):
  55. register_start = {}
  56. register_end = {}
  57. prev_regs = set()
  58. for i, regs in conflicts.items():
  59. for reg in regs:
  60. if not register_start.has_key(reg):
  61. register_start[reg] = i
  62. register_end[reg] = i
  63. range_length = {}
  64. for reg, start_index in register_start.items():
  65. range_length[reg] = register_end[reg] - start_index
  66. lives = map(lambda x: len(x), conflicts.values())
  67. if lives:
  68. max_live = max(lives)
  69. else:
  70. max_live = available
  71. while max_live > available:
  72. #We spill the register with the longest live range
  73. to_spill = my_max(range_length.items(), lambda x: x[1])
  74. del range_length[to_spill[0]]
  75. for i, reg_list in conflicts.items():
  76. #This register is no longer in conflict
  77. conflicts[i].discard(to_spill[0])
  78. lives = map(lambda x: len(x), conflicts.values())
  79. max_live = max(lives)
  80. reg_assns = {}
  81. reg_num = 0
  82. assigned_registers = {}
  83. for i in range(0, available):
  84. if i > len(conflicts):
  85. break
  86. assigned_registers['r'+str(i+1)] = ''
  87. for i, reg_list in conflicts.items():
  88. for reg in reg_list:
  89. if not reg_assns.has_key(reg):
  90. reg_assns[reg] = get_free_reg(assigned_registers, reg_list, reg)
  91. return reg_assns
  92. def live_range_top_down_allocate(code, available):
  93. live_ranges = []
  94. prev_reg_list = set()
  95. for i in range(0, len(code)):
  96. line = code[i]
  97. # add read registers
  98. read_regs = filter(lambda x: x[0] == 'r', line['read'])
  99. prev_reg_list = prev_reg_list.union(read_regs)
  100. live_ranges.append(set())
  101. for reg in prev_reg_list:
  102. if is_used_in_future(reg, code, i):
  103. live_ranges[i].add(reg)
  104. else:
  105. live_ranges[i-1].add(reg)
  106. if line['write'] and line['write'][0] == 'r' :
  107. if is_used_in_future(line['write'], code, i):
  108. live_ranges[i].add(line['write'])
  109. prev_reg_list.add(line['write'])
  110. to_remove = []
  111. for elem in prev_reg_list:
  112. if not is_used_in_future(elem, code, i):
  113. to_remove.append(elem)
  114. prev_reg_list -= set(to_remove)
  115. conflicts = {}
  116. for i in range(0, len(live_ranges)):
  117. conflicts[i] = live_ranges[i]
  118. # print print_lineobj(code[i]), " Live Ranges: ", live_ranges[i]
  119. return analyze_conflicts(conflicts, available)
  120. # Replaces registers on one line object according to reg_assns.
  121. def replace_register(reg, reg_assns):
  122. if(not reg.startswith('r')):
  123. return reg
  124. if(reg_assns.has_key(reg)):
  125. return reg_assns[reg]
  126. else:
  127. return None
  128. def print_lineobj(lineobj):
  129. return lineobj['instruction']+ "\t"+ implode(lineobj['read'])+ " => "+ lineobj['write']
  130. #Implementation of php's implode
  131. def implode(arr, glue=", "):
  132. rv = ""
  133. for item in arr:
  134. rv += item + glue
  135. return rv[:-1*len(glue)]
  136. NUM_TOTAL_REGISTERS = int(sys.argv[3])
  137. NUM_FEASIBLE_REGISTERS = 2
  138. FEASIBLE_1 = 'r' + str(NUM_TOTAL_REGISTERS-NUM_FEASIBLE_REGISTERS+1)
  139. FEASIBLE_2 = 'r' + str(NUM_TOTAL_REGISTERS-NUM_FEASIBLE_REGISTERS+2)
  140. #Returns back the key FEASIBLE_1/FEASIBLE_2 that represents a free register, and a boolean true/false that specifies whether a spill should occur or not. Returns None if no free registers are found.
  141. def get_free_register(feasible_table, alloc_register=''):
  142. if not feasible_table[FEASIBLE_1]['value']:
  143. return FEASIBLE_1, True
  144. if not feasible_table[FEASIBLE_2]['value']:
  145. return FEASIBLE_2, True
  146. if feasible_table[FEASIBLE_1]['value'] == alloc_register :
  147. return FEASIBLE_1, False
  148. if feasible_table[FEASIBLE_2]['value'] == alloc_register :
  149. return FEASIBLE_2, False
  150. if feasible_table[FEASIBLE_1]['dirty']:
  151. return FEASIBLE_1, True
  152. if feasible_table[FEASIBLE_2]['dirty']:
  153. return FEASIBLE_2, True
  154. return None, True
  155. symbol_table = {}
  156. def get_address(reg):
  157. if(symbol_table.has_key(reg)):
  158. return str(symbol_table[reg])
  159. all_addresses = map(lambda x: x[1], symbol_table.items())
  160. if not all_addresses:
  161. lowest_addr = 1024
  162. else:
  163. lowest_addr = min(all_addresses)
  164. if lowest_addr < 0:
  165. print "RAN OUT OF SPILL SPACE!"
  166. sys.exit();
  167. symbol_table[reg] = (lowest_addr-4)
  168. return str(lowest_addr - 4);
  169. def spilled_before(reg):
  170. if(symbol_table.has_key(reg)):
  171. return str(symbol_table[reg])
  172. return None
  173. def shape(code, reg_assns):
  174. rv = ""
  175. # Structure:
  176. # feasible_table = [{FEASIBLE_1 => {'value' => 'r13', 'dirty' => 0}}]
  177. feasible_table = {FEASIBLE_1: {'value': '', 'dirty': 0}, FEASIBLE_2 : {'value' : '', 'dirty' : 0}}
  178. for lineobj in code:
  179. line = ""
  180. readregs = []
  181. for reg in lineobj['read']:
  182. newreg = replace_register(reg, reg_assns)
  183. # print 'line obj', lineobj, ' newreg ', newreg
  184. if(not newreg):
  185. #add load code.
  186. free_reg, spill = get_free_register(feasible_table, reg)
  187. if not free_reg:
  188. print "Error allocating ", reg, " on line ", lineobj, " feasible table = ", feasible_table, "\n"
  189. sys.exit()
  190. feasible_table[free_reg]['value'] = reg
  191. feasible_table[free_reg]['dirty'] = 0
  192. if spill:
  193. line += "// Loading "+reg+" from memory into "+free_reg+".\n"
  194. line += 'loadI '+get_address(reg)+' => '+free_reg+"\t//@"+reg+"\n"
  195. line += 'load '+free_reg+' => '+free_reg+"\n"
  196. line += "\n"
  197. else:
  198. line += '// Cache Hit: Loading '+reg+' from register '+free_reg+"\n"
  199. readregs.append(free_reg)
  200. else:
  201. readregs.append(newreg)
  202. line += lineobj['instruction']
  203. line += "\t"
  204. if(lineobj['instruction'] == 'store'):
  205. line += implode(readregs, ' => ' ) + "\n"
  206. else:
  207. line += implode(readregs)
  208. if readregs:
  209. line += " => "
  210. writereg = replace_register(lineobj['write'], reg_assns)
  211. if not writereg:
  212. #add store code
  213. free_reg, spill = get_free_register(feasible_table, lineobj['write'])
  214. if not free_reg:
  215. free_reg = FEASIBLE_2
  216. feasible_table[free_reg]['value'] = lineobj['write']
  217. feasible_table[free_reg]['dirty'] = 0
  218. line += free_reg + "\n"
  219. if free_reg == FEASIBLE_1:
  220. other_reg = FEASIBLE_2
  221. else:
  222. other_reg = FEASIBLE_1
  223. # add store code
  224. line += "//Storing "+ lineobj['write']+ " into memory. \n"
  225. line += " loadI "+get_address(lineobj['write'])+" => "+other_reg+"\t //@"+lineobj['write']+"\n"
  226. feasible_table[other_reg]['value'] = '@'+lineobj['write']
  227. feasible_table[other_reg]['dirty'] = 0
  228. line += " store "+free_reg+" => "+other_reg+"\n"
  229. writereg = lineobj['write']
  230. else:
  231. line += writereg + "\n"
  232. feasible_table[FEASIBLE_1]['dirty'] = 1
  233. feasible_table[FEASIBLE_2]['dirty'] = 1
  234. rv += line
  235. return rv
  236. def flip_dict(d):
  237. return dict([(v, k) for (k, v) in d.iteritems()])
  238. def calc_distance(code, index, reg):
  239. for i in range(index, len(code)):
  240. line = code[i]
  241. if(reg in line['read'] or reg == line['write']):
  242. return i - index
  243. return 100000000
  244. #Figures out the next register that you should use for allocation purposes. Also returns as a second return value, the register that was spilled to make this allocation.
  245. def bu_get_next_reg(code, index, symbol_table, reg, available):
  246. if symbol_table.has_key(reg):
  247. return symbol_table[reg], None
  248. if(len(symbol_table) < available):
  249. symbol_table[reg] = 'r' + str(len(symbol_table)+1)
  250. return symbol_table[reg], None
  251. distance = {}
  252. # how far away are all these virtual registers from use?
  253. for virtual_r, physical_r in symbol_table.iteritems():
  254. distance[virtual_r] = calc_distance(code, index, virtual_r)
  255. to_spill = my_max(distance.items(), key=lambda x: x[1])
  256. newreg = symbol_table[to_spill[0]]
  257. del symbol_table[to_spill[0]]
  258. symbol_table[reg] = newreg
  259. if(distance == 100000000):
  260. return newreg, None
  261. return newreg, to_spill[0]
  262. def bottom_up_allocate(code, available):
  263. register_assignments = []
  264. symbol_table = {}
  265. feasible_table = {FEASIBLE_1: {'value': '', 'dirty': 0}, FEASIBLE_2 : {'value' : '', 'dirty' : 0}}
  266. rv = ""
  267. for i in range(0, len(code)):
  268. gen_line = ""
  269. line = code[i]
  270. line_assignments = {}
  271. readregs = []
  272. # assign registers for read.
  273. for reg in line['read']:
  274. if reg.startswith('r'):
  275. # assigns a register to it. Spills if necessary.
  276. line_assignments[reg], spilled = bu_get_next_reg(code, i, symbol_table, reg, available)
  277. readregs.append(line_assignments[reg])
  278. if spilled:
  279. # generate spill code for spilled.
  280. gen_line += "// bbb Storing "+ spilled+ " into memory. \n"
  281. gen_line += " loadI "+get_address(spilled)+" => "+FEASIBLE_1+" //@ "+spilled+" \n"
  282. gen_line += " store "+line_assignments[reg]+" => "+FEASIBLE_1+" \n"
  283. else:
  284. readregs.append(reg)
  285. #need to load previously spilled read registers here.
  286. if i-1 > 0:
  287. old_regs = set(map(lambda x: x[0], register_assignments[i-1].items()))
  288. new_regs = set(filter(lambda x: x[0].startswith('r'), symbol_table.items()))
  289. to_load = filter(lambda x: spilled_before(x), new_regs - old_regs)
  290. for reg in to_load:
  291. gen_line += "//Loading "+reg+" into "+line_assignments[reg]+" from memory location "+get_address(reg)+" \n"
  292. gen_line += "loadI "+get_address(reg)+"\t => "+FEASIBLE_1+" //@"+reg+" \n"
  293. gen_line += "load "+FEASIBLE_1+"\t => "+line_assignments[reg]+" \n"
  294. gen_line += line['instruction'] + "\t"
  295. if line['instruction'] == 'store':
  296. gen_line += implode(readregs, ' => ') + "\n"
  297. else:
  298. gen_line += implode(readregs)
  299. if readregs:
  300. gen_line += ' => '
  301. if(line['write'] and line['write'].startswith('r')):
  302. line_assignments[line['write']], spilled = bu_get_next_reg(code, i, symbol_table, line['write'], available)
  303. # generate spill code for spilled. Add it before the current line of code.
  304. if spilled:
  305. #print "Spilled: ", spilled, " line['write'] ", line['write'], " line_assignments = ", line_assignments
  306. rv += "// aaa Storing "+ spilled+ " into memory. \n"
  307. rv += " loadI "+get_address(spilled)+" => "+FEASIBLE_1+" //@ "+spilled+" \n"
  308. rv += " store "+line_assignments[line['write']]+" => "+FEASIBLE_1+" \n"
  309. gen_line += line_assignments[line['write']] + "\n"
  310. else:
  311. gen_line += line['write'] + "\n"
  312. register_assignments.append(dict(symbol_table))
  313. rv += gen_line
  314. return rv
  315. code = []
  316. filename = sys.argv[1]
  317. #Build code data structure.
  318. for line in open(filename):
  319. line = line.strip()
  320. if(line.startswith('//')):
  321. continue
  322. if not line:
  323. continue
  324. lineobj = {}
  325. sp = line.split()
  326. lineobj['instruction'] = sp[0]
  327. lineobj['write'] = sp[-1]
  328. lineobj['read'] = []
  329. if(sp[-2] == '=>'):
  330. for i in range(1, len(sp)-2):
  331. register = sp[i].strip(',')
  332. # if(register not in lineobj['read']):
  333. lineobj['read'].append(register)
  334. if(lineobj['instruction'] == 'store'):
  335. lineobj['read'].append(lineobj['write'])
  336. lineobj['write'] = ''
  337. code.append(lineobj)
  338. #print code
  339. if sys.argv[2] == '0':
  340. register_freqs = naive_register_allocate(code, NUM_TOTAL_REGISTERS-NUM_FEASIBLE_REGISTERS)
  341. print "//Naive Register Assignments: ", register_freqs
  342. elif sys.argv[2] == '1':
  343. register_freqs = live_range_top_down_allocate(code, NUM_TOTAL_REGISTERS-NUM_FEASIBLE_REGISTERS)
  344. print "//Smart Register Assignments: ", register_freqs
  345. elif sys.argv[2] == '2':
  346. print bottom_up_allocate(code, NUM_TOTAL_REGISTERS-NUM_FEASIBLE_REGISTERS)
  347. sys.exit(0)
  348. else:
  349. print "Please enter valid allocation scheme: 0 or 1 or 2"
  350. print shape(code, register_freqs)