forked from ShivamSarodia/ShivyC
-
Notifications
You must be signed in to change notification settings - Fork 0
/
control.py
201 lines (140 loc) · 6.03 KB
/
control.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
"""IL commands for labels, jumps, and function calls."""
import shivyc.asm_cmds as asm_cmds
import shivyc.spots as spots
from shivyc.il_cmds.base import ILCommand
from shivyc.spots import LiteralSpot
class Label(ILCommand):
"""Label - Analogous to an ASM label."""
def __init__(self, label): # noqa D102
"""The label argument is an string label name unique to this label."""
self.label = label
def inputs(self): # noqa D102
return []
def outputs(self): # noqa D102
return []
def label_name(self): # noqa D102
return self.label
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
asm_code.add(asm_cmds.Label(self.label))
class Jump(ILCommand):
"""Jumps unconditionally to a label."""
def __init__(self, label): # noqa D102
self.label = label
def inputs(self): # noqa D102
return []
def outputs(self): # noqa D102
return []
def targets(self): # noqa D102
return [self.label]
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
asm_code.add(asm_cmds.Jmp(self.label))
class _GeneralJump(ILCommand):
"""General class for jumping to a label based on condition."""
# ASM command to output for this jump IL command.
# (asm_cmds.Je for JumpZero and asm_cmds.Jne for JumpNotZero)
asm_cmd = None
def __init__(self, cond, label): # noqa D102
self.cond = cond
self.label = label
def inputs(self): # noqa D102
return [self.cond]
def outputs(self): # noqa D102
return []
def targets(self): # noqa D102
return [self.label]
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
size = self.cond.ctype.size
if isinstance(spotmap[self.cond], LiteralSpot):
r = get_reg()
asm_code.add(asm_cmds.Mov(r, spotmap[self.cond], size))
cond_spot = r
else:
cond_spot = spotmap[self.cond]
zero_spot = LiteralSpot("0")
asm_code.add(asm_cmds.Cmp(cond_spot, zero_spot, size))
asm_code.add(self.command(self.label))
class JumpZero(_GeneralJump):
"""Jumps to a label if given condition is zero."""
command = asm_cmds.Je
class JumpNotZero(_GeneralJump):
"""Jumps to a label if given condition is zero."""
command = asm_cmds.Jne
class Return(ILCommand):
"""RETURN - returns the given value from function.
If arg is None, then returns from the function without putting any value
in the return register. Today, only supports values that fit in one
register.
"""
def __init__(self, arg=None): # noqa D102
# arg must already be cast to return type
self.arg = arg
def inputs(self): # noqa D102
return [self.arg]
def outputs(self): # noqa D102
return []
def clobber(self): # noqa D102
return [spots.RAX]
def abs_spot_pref(self): # noqa D102
return {self.arg: [spots.RAX]}
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
if self.arg and spotmap[self.arg] != spots.RAX:
size = self.arg.ctype.size
asm_code.add(asm_cmds.Mov(spots.RAX, spotmap[self.arg], size))
asm_code.add(asm_cmds.Mov(spots.RSP, spots.RBP, 8))
asm_code.add(asm_cmds.Pop(spots.RBP, None, 8))
asm_code.add(asm_cmds.Ret())
class Call(ILCommand):
"""Call a given function.
func - Pointer to function
args - Arguments of the function, in left-to-right order. Must match the
parameter types the function expects.
ret - If function has non-void return type, IL value to save the return
value. Its type must match the function return value.
"""
arg_regs = [spots.RDI, spots.RSI, spots.RDX, spots.RCX, spots.R8, spots.R9]
def __init__(self, func, args, ret): # noqa D102
self.func = func
self.args = args
self.ret = ret
self.void_return = self.func.ctype.arg.ret.is_void()
if len(self.args) > len(self.arg_regs):
raise NotImplementedError("too many arguments")
def inputs(self): # noqa D102
return [self.func] + self.args
def outputs(self): # noqa D102
return [] if self.void_return else [self.ret]
def clobber(self): # noqa D102
# All caller-saved registers are clobbered by function call
return [spots.RAX, spots.RCX, spots.RDX, spots.RSI, spots.RDI,
spots.R8, spots.R9, spots.R10, spots.R11]
def abs_spot_pref(self): # noqa D102
prefs = {} if self.void_return else {self.ret: [spots.RAX]}
for arg, reg in zip(self.args, self.arg_regs):
prefs[arg] = [reg]
return prefs
def abs_spot_conf(self): # noqa D102
# We don't want the function pointer to be in the same register as
# an argument will be placed into.
return {self.func: self.arg_regs[0:len(self.args)]}
def indir_write(self): # noqa D102
return self.args
def indir_read(self): # noqa D102
return self.args
def make_asm(self, spotmap, home_spots, get_reg, asm_code): # noqa D102
func_spot = spotmap[self.func]
func_size = self.func.ctype.size
ret_size = self.func.ctype.arg.ret.size
# Check if function pointer spot will be clobbered by moving the
# arguments into the correct registers.
if spotmap[self.func] in self.arg_regs[0:len(self.args)]:
# Get a register which isn't one of the unallowed registers.
r = get_reg([], self.arg_regs[0:len(self.args)])
asm_code.add(asm_cmds.Mov(r, spotmap[self.func], func_size))
func_spot = r
for arg, reg in zip(self.args, self.arg_regs):
if spotmap[arg] == reg:
continue
asm_code.add(asm_cmds.Mov(reg, spotmap[arg], arg.ctype.size))
asm_code.add(asm_cmds.Call(func_spot, None, self.func.ctype.size))
if not self.void_return and spotmap[self.ret] != spots.RAX:
asm_code.add(asm_cmds.Mov(spotmap[self.ret], spots.RAX, ret_size))