255
|
1 import logging
|
211
|
2 import ir
|
249
|
3 from target import Label, Comment, Alignment, LabelRef, Imm32, DebugInfo
|
218
|
4 import cortexm3 as arm
|
211
|
5 from ppci import CompilerError
|
269
|
6 import flowgraph
|
|
7 import registerallocator
|
|
8 from instructionselector import InstructionSelector
|
272
|
9 import irmach
|
275
|
10 from irmach import makeIns
|
|
11 import canon
|
|
12 import asm
|
274
|
13
|
|
14 class ArmFrame(irmach.Frame):
|
|
15 """
|
|
16 Arm specific frame for functions.
|
|
17 """
|
275
|
18 def __init__(self, name):
|
|
19 # We use r7 as frame pointer.
|
|
20 super().__init__(name)
|
|
21 self.regs = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6']
|
|
22 self.rv = ir.Temp('special_RV')
|
|
23 self.p1 = ir.Temp('special_P1')
|
|
24 self.p2 = ir.Temp('special_P2')
|
|
25 self.p3 = ir.Temp('special_P3')
|
|
26 self.p4 = ir.Temp('special_P4')
|
|
27 self.fp = ir.Temp('special_FP')
|
|
28 # Pre-colored registers:
|
|
29 self.tempMap = {}
|
|
30 self.tempMap[self.rv] = 'r0'
|
|
31 self.tempMap[self.p1] = 'r1'
|
|
32 self.tempMap[self.p2] = 'r2'
|
|
33 self.tempMap[self.p3] = 'r3'
|
|
34 self.tempMap[self.p4] = 'r4'
|
|
35 self.tempMap[self.fp] = 'r7'
|
|
36 self.locVars = {}
|
|
37 self.parMap = {}
|
276
|
38 # Literal pool:
|
|
39 self.constants = []
|
275
|
40
|
|
41 def argLoc(self, pos):
|
|
42 """
|
|
43 Gets the function parameter location in IR-code format.
|
|
44 """
|
|
45 if pos == 0:
|
|
46 return self.p1
|
|
47 elif pos == 1:
|
|
48 return self.p2
|
|
49 elif pos == 2:
|
|
50 return self.p3
|
|
51 elif pos == 3:
|
|
52 return self.p4
|
|
53 else:
|
|
54 raise NotImplementedError('No more than 4 parameters implemented')
|
|
55
|
|
56 def allocVar(self, lvar):
|
|
57 if lvar not in self.locVars:
|
|
58 self.locVars[lvar] = self.stacksize
|
|
59 self.stacksize = self.stacksize + 4
|
|
60 return self.locVars[lvar]
|
|
61
|
276
|
62 def addConstant(self, value):
|
|
63 lab_name = '{}_literal_{}'.format(self.name, len(self.constants))
|
|
64 self.constants.append((lab_name, value))
|
|
65 return lab_name
|
|
66
|
275
|
67 def EntryExitGlue3(self):
|
|
68 """
|
|
69 Add code for the prologue and the epilogue. Add a label, the
|
|
70 return instruction and the stack pointer adjustment for the frame.
|
|
71 """
|
|
72 self.instructions.insert(0, makeIns('{}:'.format(self.name)))
|
|
73 self.instructions.insert(1, makeIns('push {lr, r7}'))
|
|
74 self.instructions.insert(2, makeIns('mov r7, sp'))
|
|
75 self.instructions.insert(3, makeIns('add sp, sp, {}'.format(self.stacksize)))
|
|
76 self.instructions.append(makeIns('sub sp, sp, {}'.format(self.stacksize)))
|
|
77 self.instructions.append(makeIns('pop {pc,r7}'))
|
276
|
78 # Add constant literals:
|
|
79 for ln, v in self.constants:
|
|
80 self.instructions.append(makeIns('{}:'.format(ln)))
|
|
81 self.instructions.append(makeIns('dcd {}'.format(v)))
|
274
|
82
|
|
83
|
268
|
84 class ArmInstructionSelector(InstructionSelector):
|
276
|
85
|
269
|
86 """ Instruction selector for the arm architecture """
|
268
|
87 def munchExpr(self, e):
|
|
88 if isinstance(e, ir.Alloc):
|
|
89 return 0
|
275
|
90 elif isinstance(e, ir.Binop) and e.operation == '+' and isinstance(e.b, ir.Const) and e.b.value < 8:
|
|
91 a = self.munchExpr(e.a)
|
|
92 d = self.newTmp()
|
|
93 self.emit('add %d0, %s0, {}'.format(e.b.value), dst=[d], src=[a])
|
|
94 return d
|
268
|
95 elif isinstance(e, ir.Binop) and e.operation == '+':
|
275
|
96 a = self.munchExpr(e.a)
|
|
97 b = self.munchExpr(e.b)
|
268
|
98 d = self.newTmp()
|
|
99 self.emit('add %d0, %s0, %s1', dst=[d], src=[a, b])
|
|
100 return d
|
275
|
101 elif isinstance(e, ir.Binop) and e.operation == '-' and isinstance(e.b, ir.Const) and e.b.value < 8:
|
|
102 a = self.munchExpr(e.a)
|
|
103 d = self.newTmp()
|
|
104 self.emit('sub %d0, %s0, {}'.format(e.b.value), dst=[d], src=[a])
|
|
105 return d
|
269
|
106 elif isinstance(e, ir.Binop) and e.operation == '-':
|
275
|
107 a = self.munchExpr(e.a)
|
|
108 b = self.munchExpr(e.b)
|
269
|
109 d = self.newTmp()
|
|
110 self.emit('sub %d0, %s0, %s1', dst=[d], src=[a, b])
|
|
111 return d
|
268
|
112 elif isinstance(e, ir.Binop) and e.operation == '|':
|
275
|
113 a = self.munchExpr(e.a)
|
|
114 b = self.munchExpr(e.b)
|
268
|
115 d = self.newTmp()
|
276
|
116 self.emit('mov %d0, %s0', src=[a], dst=[d])
|
|
117 self.emit('orr %d0, %s0', dst=[d], src=[b, d])
|
268
|
118 return d
|
|
119 elif isinstance(e, ir.Binop) and e.operation == '<<':
|
275
|
120 a = self.munchExpr(e.a)
|
|
121 b = self.munchExpr(e.b)
|
268
|
122 d = self.newTmp()
|
276
|
123 self.emit('mov %d0, %s0', src=[a], dst=[d])
|
|
124 self.emit('lsl %d0, %s0', dst=[d], src=[b, d]) # TODO: is d a source variable?
|
268
|
125 return d
|
|
126 elif isinstance(e, ir.Binop) and e.operation == '*':
|
275
|
127 a = self.munchExpr(e.a)
|
|
128 b = self.munchExpr(e.b)
|
268
|
129 d = self.newTmp()
|
276
|
130 self.emit('mov %d0, %s0', src=[a], dst=[d])
|
|
131 self.emit('mul %d0, %s0', dst=[d], src=[b, d])
|
268
|
132 return d
|
275
|
133 elif isinstance(e, ir.Const) and e.value < 256:
|
268
|
134 d = self.newTmp()
|
275
|
135 self.emit('mov %d0, {}'.format(e.value), dst=[d])
|
|
136 return d
|
276
|
137 elif isinstance(e, ir.Const) and e.value < (2**31):
|
|
138 d = self.newTmp()
|
|
139 ln = self.frame.addConstant(e.value)
|
|
140 self.emit('ldr %d0, {}'.format(ln), dst=[d])
|
|
141 return d
|
275
|
142 elif isinstance(e, ir.Mem) and isinstance(e.e, ir.Binop) and \
|
|
143 e.e.operation == '+' and isinstance(e.e.b, ir.Const):
|
|
144 base = self.munchExpr(e.e.a)
|
|
145 d = self.newTmp()
|
|
146 self.emit('ldr %d0, [%s0 + {}]'.format(e.e.b.value), src=[base], dst=[d])
|
268
|
147 return d
|
|
148 elif isinstance(e, ir.Mem):
|
|
149 # Load from memory
|
275
|
150 base = self.munchExpr(e.e)
|
268
|
151 d = self.newTmp()
|
275
|
152 self.emit('ldr %d0, [%s0]', src=[base], dst=[d])
|
268
|
153 return d
|
|
154 elif isinstance(e, ir.Temp):
|
275
|
155 return e
|
272
|
156 elif isinstance(e, ir.Call):
|
275
|
157 # Move arguments into proper locations:
|
|
158 reguses = []
|
|
159 for i, a in enumerate(e.arguments):
|
|
160 loc = self.frame.argLoc(i)
|
|
161 m = ir.Move(loc, a)
|
|
162 self.munchStm(m)
|
|
163 if isinstance(loc, ir.Temp):
|
|
164 reguses.append(loc)
|
|
165 self.emit('bl {}'.format(e.f.name), src=reguses, dst=[self.frame.rv])
|
|
166 d = self.newTmp()
|
|
167 self.move(d, self.frame.rv)
|
|
168 return d
|
268
|
169 else:
|
272
|
170 raise NotImplementedError('Expr --> {}'.format(e))
|
268
|
171
|
|
172 def munchStm(self, s):
|
275
|
173 if isinstance(s, ir.Terminator):
|
|
174 pass
|
|
175 elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem) and isinstance(s.dst.e, ir.Binop) and s.dst.e.operation == '+' and isinstance(s.dst.e.a, ir.Temp) and isinstance(s.dst.e.b, ir.Const):
|
|
176 val = self.munchExpr(s.src)
|
|
177 self.emit('str %s1, [%s0 + {}]'.format(s.dst.e.b.value), src=[s.dst.e.a, val])
|
|
178 elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Mem):
|
268
|
179 memloc = self.munchExpr(s.dst.e)
|
|
180 val = self.munchExpr(s.src)
|
275
|
181 self.emit('str %s1, [%s0]', src=[memloc, val])
|
268
|
182 elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Temp):
|
|
183 val = self.munchExpr(s.src)
|
275
|
184 dreg = s.dst
|
269
|
185 self.emit('mov %d0, %s0', dst=[dreg], src=[val])
|
275
|
186 elif isinstance(s, ir.Exp):
|
|
187 # Generate expression code and discard the result.
|
|
188 x = self.munchExpr(s.e)
|
|
189 self.emit('mov r0, r0', src=[x])
|
268
|
190 elif isinstance(s, ir.Jump):
|
269
|
191 tgt = self.targets[s.target]
|
275
|
192 self.emit('b {}'.format(s.target.name), jumps=[tgt])
|
268
|
193 elif isinstance(s, ir.CJump):
|
269
|
194 a = self.munchExpr(s.a)
|
|
195 b = self.munchExpr(s.b)
|
|
196 self.emit('cmp %s0, %s1', src=[a, b])
|
|
197 ntgt = self.targets[s.lab_no]
|
|
198 ytgt = self.targets[s.lab_yes]
|
276
|
199 jmp_ins = makeIns('b {}'.format(s.lab_no.name), jumps=[ntgt])
|
269
|
200 # Explicitely add fallthrough:
|
276
|
201 self.emit('beq {}'.format(s.lab_yes.name), jumps=[ytgt, jmp_ins])
|
269
|
202 self.emit2(jmp_ins)
|
268
|
203 else:
|
274
|
204 raise NotImplementedError('Stmt --> {}'.format(s))
|
268
|
205
|
|
206
|
274
|
207 # TODO: this class could be target independent:
|
211
|
208 class ArmCodeGenerator:
|
268
|
209 def __init__(self, outs):
|
269
|
210 # TODO: schedule traces in better order.
|
|
211 # This is optional!
|
268
|
212 self.ins_sel = ArmInstructionSelector()
|
|
213 self.outs = outs
|
|
214 self.outs.getSection('code').address = 0x08000000
|
|
215 self.outs.getSection('data').address = 0x20000000
|
|
216
|
270
|
217 def useUnused(self, inslist):
|
|
218 # Use unused temporaries at the end of the list
|
|
219 defTemps = []
|
272
|
220 useTemps = []
|
|
221 for i in inslist:
|
|
222 for d in iter(i.dst):
|
|
223 defTemps.append(d)
|
|
224 for s in iter(i.src):
|
|
225 useTemps.append(s)
|
|
226 defTemps = set(defTemps)
|
|
227 useTemps = set(useTemps)
|
|
228 unUsed = defTemps - useTemps
|
275
|
229 assert not unUsed
|
272
|
230 for uu in unUsed:
|
|
231 inslist.append(irmach.AbstractInstruction('use %s0', src=[uu]))
|
|
232 #print(useTemps)
|
270
|
233
|
274
|
234 def allocFrame(self, f):
|
|
235 """
|
|
236 Do register allocation for a single stack frame.
|
|
237 """
|
|
238 ilist = f.instructions
|
|
239 self.useUnused(ilist)
|
|
240 cfg = flowgraph.FlowGraph(ilist)
|
|
241 f.cfg = cfg
|
269
|
242 ig = registerallocator.InterferenceGraph(cfg)
|
274
|
243 f.ig = ig
|
269
|
244
|
|
245 ra = registerallocator.RegisterAllocator()
|
275
|
246 regMap = ra.registerAllocate(ig, f.regs, f.tempMap)
|
272
|
247 # Use allocated registers:
|
274
|
248 for i in ilist:
|
270
|
249 i.src = tuple(regMap[t] for t in i.src)
|
|
250 i.dst = tuple(regMap[t] for t in i.dst)
|
275
|
251
|
|
252 def generateFunc(self, irfunc):
|
|
253 # Create a frame for this function:
|
|
254 frame = ArmFrame(irfunc.name)
|
|
255 # Canonicalize the intermediate language:
|
|
256 canon.make(irfunc, frame)
|
|
257 # print('after canonicalize:')
|
|
258 # irfunc.dump()
|
|
259 self.ins_sel.munchFunction(irfunc, frame)
|
|
260 # print('Selected instructions:')
|
|
261 #for i in frame.instructions:
|
|
262 # print(i)
|
|
263
|
|
264 # Do register allocation:
|
|
265 self.allocFrame(frame)
|
|
266 # TODO: Peep-hole here?
|
|
267
|
|
268 # Add label and return and stack adjustment:
|
|
269 frame.EntryExitGlue3()
|
|
270 return frame
|
274
|
271
|
|
272 def generate(self, ircode):
|
|
273 # Munch program into a bunch of frames. One frame per function.
|
|
274 # Each frame has a flat list of abstract instructions.
|
275
|
275 # Generate code for all functions:
|
|
276 self.frames = [self.generateFunc(func) for func in ircode.Functions]
|
274
|
277
|
275
|
278 # Materialize assembly
|
|
279 # Reparse the register allocated instructions into a stream of
|
|
280 # real instructions.
|
|
281 # TODO: this is ugly via string representations. This could be
|
|
282 # another interface?
|
|
283 assembler = asm.Assembler(target=arm.armtarget, stream=self.outs)
|
|
284 self.outs.selectSection('code')
|
|
285 for frame in self.frames:
|
|
286 for i in frame.instructions:
|
|
287 assembler.assemble_line(str(i))
|
268
|
288
|
276
|
289 # TODO: fixup references, do this in another way?
|
|
290 self.outs.backpatch()
|
|
291 self.outs.backpatch()
|
275
|
292 return self.frames
|
268
|
293
|