forked from ShivamSarodia/ShivyC
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmath.py
335 lines (245 loc) · 10.3 KB
/
math.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
"""IL commands for mathematical operations."""
import shivyc.asm_cmds as asm_cmds
import shivyc.spots as spots
from shivyc.il_cmds.base import ILCommand
class _AddMult(ILCommand):
"""Base class for ADD, MULT, and SUB."""
# Indicates whether this instruction is commutative. If not,
# a "neg" instruction is inserted when the order is flipped. Override
# this value in subclasses.
comm = False
# The ASM instruction to generate for this command. Override this value
# in subclasses.
Inst = None
def __init__(self, output, arg1, arg2): # noqa D102
self.output = output
self.arg1 = arg1
self.arg2 = arg2
def inputs(self): # noqa D102
return [self.arg1, self.arg2]
def outputs(self): # noqa D102
return [self.output]
def rel_spot_pref(self): # noqa D102
return {self.output: [self.arg1, self.arg2]}
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
"""Make the ASM for ADD, MULT, and SUB."""
ctype = self.arg1.ctype
size = ctype.size
arg1_spot = spotmap[self.arg1]
arg2_spot = spotmap[self.arg2]
# Get temp register for computation.
temp = get_reg([spotmap[self.output],
arg1_spot,
arg2_spot])
if temp == arg1_spot:
if not self._is_imm64(arg2_spot):
asm_code.add(self.Inst(temp, arg2_spot, size))
else:
temp2 = get_reg([], [temp])
asm_code.add(asm_cmds.Mov(temp2, arg2_spot, size))
asm_code.add(self.Inst(temp, temp2, size))
elif temp == arg2_spot:
if not self._is_imm64(arg1_spot):
asm_code.add(self.Inst(temp, arg1_spot, size))
else:
temp2 = get_reg([], [temp])
asm_code.add(asm_cmds.Mov(temp2, arg1_spot, size))
asm_code.add(self.Inst(temp, temp2, size))
if not self.comm:
asm_code.add(asm_cmds.Neg(temp, None, size))
else:
if (not self._is_imm64(arg1_spot) and
not self._is_imm64(arg2_spot)):
asm_code.add(asm_cmds.Mov(temp, arg1_spot, size))
asm_code.add(self.Inst(temp, arg2_spot, size))
elif (self._is_imm64(arg1_spot) and
not self._is_imm64(arg2_spot)):
asm_code.add(asm_cmds.Mov(temp, arg1_spot, size))
asm_code.add(self.Inst(temp, arg2_spot, size))
elif (not self._is_imm64(arg1_spot) and
self._is_imm64(arg2_spot)):
asm_code.add(asm_cmds.Mov(temp, arg2_spot, size))
asm_code.add(self.Inst(temp, arg1_spot, size))
if not self.comm:
asm_code.add(asm_cmds.Neg(temp, None, size))
else: # both are imm64
raise NotImplementedError(
"never reach because of constant folding")
if temp != spotmap[self.output]:
asm_code.add(asm_cmds.Mov(spotmap[self.output], temp, size))
class Add(_AddMult):
"""Adds arg1 and arg2, then saves to output.
IL values output, arg1, arg2 must all have the same type. No type
conversion or promotion is done here.
"""
comm = True
Inst = asm_cmds.Add
class Subtr(_AddMult):
"""Subtracts arg1 and arg2, then saves to output.
ILValues output, arg1, and arg2 must all have types of the same size.
"""
comm = False
Inst = asm_cmds.Sub
class Mult(_AddMult):
"""Multiplies arg1 and arg2, then saves to output.
IL values output, arg1, arg2 must all have the same type. No type
conversion or promotion is done here.
"""
comm = True
Inst = asm_cmds.Imul
class _BitShiftCmd(ILCommand):
"""Base class for bitwise shift commands."""
# The ASM instruction to generate for this command. Override this value
# in subclasses.
Inst = None
def __init__(self, output, arg1, arg2): # noqa D102
self.output = output
self.arg1 = arg1
self.arg2 = arg2
def inputs(self): # noqa D102
return [self.arg1, self.arg2]
def outputs(self): # noqa D102
return [self.output]
def clobber(self): # noqa D102
return [spots.RCX]
def abs_spot_pref(self): # noqa D102
return {self.arg2: [spots.RCX]}
def rel_spot_pref(self): # noqa D102
return {self.output: [self.arg1]}
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
arg1_spot = spotmap[self.arg1]
arg1_size = self.arg1.ctype.size
arg2_spot = spotmap[self.arg2]
arg2_size = self.arg2.ctype.size
# According Intel® 64 and IA-32 software developer's manual
# Vol. 2B 4-582 second (count) operand must be represented as
# imm8 or CL register.
if not self._is_imm8(arg2_spot) and arg2_spot != spots.RCX:
if arg1_spot == spots.RCX:
out_spot = spotmap[self.output]
temp_spot = get_reg([out_spot, arg1_spot],
[arg2_spot, spots.RCX])
asm_code.add(asm_cmds.Mov(temp_spot, arg1_spot, arg1_size))
arg1_spot = temp_spot
asm_code.add(asm_cmds.Mov(spots.RCX, arg2_spot, arg2_size))
arg2_spot = spots.RCX
if spotmap[self.output] == arg1_spot:
asm_code.add(self.Inst(arg1_spot, arg2_spot, arg1_size, 1))
else:
out_spot = spotmap[self.output]
temp_spot = get_reg([out_spot, arg1_spot], [arg2_spot])
if arg1_spot != temp_spot:
asm_code.add(asm_cmds.Mov(temp_spot, arg1_spot, arg1_size))
asm_code.add(self.Inst(temp_spot, arg2_spot, arg1_size, 1))
if temp_spot != out_spot:
asm_code.add(asm_cmds.Mov(out_spot, temp_spot, arg1_size))
class RBitShift(_BitShiftCmd):
"""Right bitwise shift operator for IL value.
Shifts each bit in IL value left operand to the right by position
indicated by right operand."""
Inst = asm_cmds.Sar
class LBitShift(_BitShiftCmd):
"""Left bitwise shift operator for IL value.
Shifts each bit in IL value left operand to the left by position
indicated by right operand."""
Inst = asm_cmds.Sal
class _DivMod(ILCommand):
"""Base class for ILCommand Div and Mod."""
# Register which contains the value we want after the x86 div or idiv
# command is executed. For the Div IL command, this is spots.RAX,
# and for the Mod IL command, this is spots.RDX.
return_reg = None
def __init__(self, output, arg1, arg2):
self.output = output
self.arg1 = arg1
self.arg2 = arg2
def inputs(self): # noqa D102
return [self.arg1, self.arg2]
def outputs(self): # noqa D102
return [self.output]
def clobber(self): # noqa D102
return [spots.RAX, spots.RDX]
def abs_spot_conf(self): # noqa D102
return {self.arg2: [spots.RDX, spots.RAX]}
def abs_spot_pref(self): # noqa D102
return {self.output: [self.return_reg],
self.arg1: [spots.RAX]}
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
ctype = self.arg1.ctype
size = ctype.size
output_spot = spotmap[self.output]
arg1_spot = spotmap[self.arg1]
arg2_spot = spotmap[self.arg2]
# Move first operand into RAX if we can do so without clobbering
# other argument
moved_to_rax = False
if spotmap[self.arg1] != spots.RAX and spotmap[self.arg2] != spots.RAX:
moved_to_rax = True
asm_code.add(asm_cmds.Mov(spots.RAX, arg1_spot, size))
# If the divisor is a literal or in a bad register, we must move it
# to a register.
if (self._is_imm(spotmap[self.arg2]) or
spotmap[self.arg2] in [spots.RAX, spots.RDX]):
r = get_reg([], [spots.RAX, spots.RDX])
asm_code.add(asm_cmds.Mov(r, arg2_spot, size))
arg2_final_spot = r
else:
arg2_final_spot = arg2_spot
# If we did not move to RAX above, do so here.
if not moved_to_rax and arg1_spot != self.return_reg:
asm_code.add(asm_cmds.Mov(spots.RAX, arg1_spot, size))
if ctype.signed:
if ctype.size == 4:
asm_code.add(asm_cmds.Cdq())
elif ctype.size == 8:
asm_code.add(asm_cmds.Cqo())
asm_code.add(asm_cmds.Idiv(arg2_final_spot, None, size))
else:
# zero out RDX register
asm_code.add(asm_cmds.Xor(spots.RDX, spots.RDX, size))
asm_code.add(asm_cmds.Div(arg2_final_spot, None, size))
if spotmap[self.output] != self.return_reg:
asm_code.add(asm_cmds.Mov(output_spot, self.return_reg, size))
class Div(_DivMod):
"""Divides given IL values.
IL values output, arg1, arg2 must all have the same type of size at least
int. No type conversion or promotion is done here.
"""
return_reg = spots.RAX
class Mod(_DivMod):
"""Divides given IL values.
IL values output, arg1, arg2 must all have the same type of size at least
int. No type conversion or promotion is done here.
"""
return_reg = spots.RDX
class _NegNot(ILCommand):
"""Base class for NEG and NOT."""
# The ASM instruction to generate for this command. Override this value
# in subclasses.
Inst = None
def __init__(self, output, arg): # noqa D102
self.output = output
self.arg = arg
def inputs(self): # noqa D102
return [self.arg]
def outputs(self): # noqa D102
return [self.output]
def rel_spot_pref(self): # noqa D102
return {self.output: [self.arg]}
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
size = self.arg.ctype.size
output_spot = spotmap[self.output]
arg_spot = spotmap[self.arg]
if output_spot != arg_spot:
asm_code.add(asm_cmds.Mov(output_spot, arg_spot, size))
asm_code.add(self.Inst(output_spot, None, size))
class Neg(_NegNot):
"""Negates given IL value (two's complement).
No type promotion is done here.
"""
Inst = asm_cmds.Neg
class Not(_NegNot):
"""Logically negates each bit of given IL value (one's complement).
No type promotion is done here.
"""
Inst = asm_cmds.Not