git.s-ol.nu subv / b22ce8b
implement LabelRef s-ol 28 days ago
1 changed file(s) with 280 addition(s) and 20 deletion(s). Raw diff Collapse all Expand all
0 class Bitfield(object):
1 def __init__(self, val, size):
0 import re
1 from operator import __and__
2 from functools import reduce
3
4 class WordBase(object):
5 def slice_allowempty(self, range):
6 if not isinstance(range, slice):
7 range = slice(range, range)
8 hi, lo = range.start, range.stop
9
10 if hi < lo:
11 return empty
12
13 return self[range]
14
15 def __and__(self, other):
16 if isinstance(other, WordBase):
17 return BitsUnion(self, other)
18 raise NotImplementedError()
19
20 class BitsUnion(WordBase):
21 def __init__(self, *parts):
22 self.parts = parts
23
24 def __repr__(self):
25 return "BitsUnion({})".format(', '.join(repr(part) for part in self.parts))
26
27 def as_value(self, **kwargs):
28 """ reduce down to a single Bitfield value.
29
30 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value()
31 Bitfield(0x1234, 16)
32 >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).as_value(labels={'test': 0xf34f})
33 Bitfield(0x1234, 16)
34 """
35 return reduce(__and__, (part.as_value(**kwargs) for part in self.parts))
36
37 @property
38 def size(self):
39 """ get the total size.
40
41 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size
42 16
43 >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).size
44 16
45 """
46 return sum(part.size for part in self.parts)
47
48 def __getitem__(self, range):
49 """ slice a union given hi and lo bit indices.
50
51 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4]
52 Bitfield(0x23, 8)
53 >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4)))[11:4]
54 BitsUnion(Bitfield(0x2, 4), LabelRef('test', 0, ('imm', 32), 11:8))
55 """
56 if not isinstance(range, slice):
57 range = slice(range, range)
58 hi, lo = range.start, range.stop
59
60 size = self.size
61 if hi < lo:
62 raise ValueError("cant slice reverse range")
63 elif lo < 0 or hi >= size:
64 raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
65
66 i = size
67 result = Bitfield(0, 0)
68 for part in self.parts:
69 p_size = part.size
70 i -= p_size
71 if i > hi:
72 # still above hi
73 continue
74
75 r_hi = min(p_size - 1, hi - i)
76 r_lo = max(0, lo - i)
77
78 result = result & part[r_hi:r_lo]
79
80 if i <= lo:
81 # past lo
82 break
83
84 return result
85
86 def __and__(self, other):
87 if isinstance(other, BitsUnion):
88 return BitsUnion(*self.parts, *other.parts)
89 elif isinstance(other, WordBase):
90 return BitsUnion(*self.parts, other)
91 raise NotImplementedError()
92
93 class Bitfield(WordBase):
94 def __init__(self, val, size, extra=()):
95 if val < 0:
96 min = -(1 << (size - 1))
97 max = -1 - min
98
99 if val > max or val < min:
100 raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max))
101
102 if val < 0:
103 val = (1 << size) + val
104
2105 self.val = val
3106 self.size = size
107 self.extra = extra
4108
5109 def __repr__(self):
6 return "Bitfield(0x{:x}, {})".format(self.val, self.size)
110 """ format for debugging.
111 >>> Bitfield(0, 8)
112 Bitfield(0x0, 8)
113 >>> Bitfield(16, 8)
114 Bitfield(0x10, 8)
115 >>> Bitfield(-16, 8)
116 Bitfield(0xf0, 8)
117 >>> Bitfield(3, 5)
118 Bitfield(0x3, 5)
119 >>> Bitfield(3, 4, extra=('hello',))
120 Bitfield(0x3, 4, extra=('hello',))
121 """
122 extra = ''
123 if self.extra:
124 extra = ", extra={!r}".format(self.extra)
125 return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra)
7126
8127 def __str__(self):
9 return "0x{:x}'{}".format(self.val, self.size)
128 """ format as a word.
129 >>> str(Bitfield(0, 8))
130 '00/8'
131 >>> str(Bitfield(16, 8))
132 '10/8'
133 >>> str(Bitfield(3, 8))
134 '03/8'
135 >>> str(Bitfield(3, 5))
136 '03/5'
137 >>> str(Bitfield(3, 4))
138 '3/4'
139 >>> str(Bitfield(3, 4, extra=('hello',)))
140 '3/hello/4'
141 """
142 digits = 1 + ((self.size - 1) // 4)
143 base = "{:x}".format(self.val)
144 base = (digits - len(base)) * '0' + base
145 return '/'.join([base, *self.extra, str(self.size)])
146
147 def as_value(self, **kwargs):
148 return self
10149
11150 def __getitem__(self, range):
12151 """ slice a bitfield given hi and lo bit indices.
25164 >>> Bitfield(0xf, 4)[4:0]
26165 Traceback (most recent call last):
27166 ...
28 ValueError: slice [4:0] out of range of 0xf'4
167 ValueError: slice [4:0] out of range of f/4
29168 >>> Bitfield(0xf, 4)[2:3]
30169 Traceback (most recent call last):
31170 ...
33172 >>> Bitfield(0xf, 4)[3:-1]
34173 Traceback (most recent call last):
35174 ...
36 ValueError: slice [3:-1] out of range of 0xf'4
175 ValueError: slice [3:-1] out of range of f/4
37176 """
38177 if not isinstance(range, slice):
39178 range = slice(range, range)
48187 val = (self.val >> lo) & ((1 << size) - 1)
49188 return Bitfield(val, size)
50189
51 def slice_allowempty(self, range):
52 if not isinstance(range, slice):
53 range = slice(range, range)
54 hi, lo = range.start, range.stop
55
56 if hi < lo:
57 return empty
58
59 return self[range]
60
61190 def __and__(self, other):
62191 """ concatenate multiple bitfields.
63192
66195 >>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1)
67196 Bitfield(0xcd, 8)
68197 """
69 if not isinstance(other, Bitfield):
70 raise NotImplementedError()
71
72 return Bitfield(self.val << other.size | other.val, self.size + other.size)
198 if isinstance(other, Bitfield):
199 return Bitfield(self.val << other.size | other.val, self.size + other.size)
200 elif isinstance(other, WordBase):
201 return BitsUnion(self, other)
202 raise NotImplementedError()
203
204 global_slice = slice
205 class LabelRef(WordBase):
206 mode_re = re.compile(r'^(imm|off)(\d+)?$')
207
208 def __init__(self, label, offset, mode, slice=None):
209 if isinstance(mode, str):
210 match = LabelRef.mode_re.match(mode)
211 assert match, ValueError("invalid label reference tag")
212 mode = (match.group(1), int(match.group(2) or 32))
213
214 self.label = label
215 self.mode = mode
216 self.slice = slice or global_slice(self.mode[1]-1, 0)
217 self.offset = offset
218
219 def __repr__(self):
220 """ format for debugging.
221
222 >>> LabelRef("label", 0, ("imm", 32), slice(31,31))
223 LabelRef('label', 0, ('imm', 32), 31:31)
224 >>> LabelRef("label", 4, ("imm", 32), slice(11,0))
225 LabelRef('label', 4, ('imm', 32), 11:0)
226 >>> LabelRef("test", -4, "off32")
227 LabelRef('test', -4, ('off', 32), 31:0)
228 >>> LabelRef("test", 0, "off")
229 LabelRef('test', 0, ('off', 32), 31:0)
230 """
231 return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(self.label, self.offset, self.mode, self.slice)
232
233 def __str__(self):
234 """ format as a word.
235
236 >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31)))
237 'label/imm32/[31:31]'
238 >>> str(LabelRef("label", 0, ("imm", 32), slice(11,0)))
239 'label/imm32/[11:0]'
240 >>> str(LabelRef("test", 4, "off32"))
241 'test+4/off32/[31:0]'
242 >>> str(LabelRef("test", -12, "off"))
243 'test-12/off32/[31:0]'
244 """
245 offset = ''
246 if self.offset != 0:
247 offset = "{:+}".format(self.offset)
248
249 return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(self.label, offset, self.mode, self.slice)
250
251 @property
252 def size(self):
253 return self.slice.start - self.slice.stop + 1
254
255 def as_value(self, labels={}, **kwargs):
256 """ resolve the reference.
257
258 >>> labels = {'test': 0xabcd1234, 'main': 0x8000}
259 >>> LabelRef("test", 0, "imm").as_value(labels=labels)
260 Bitfield(0xabcd1234, 32)
261 >>> LabelRef("test", 0, "imm", slice(15, 0)).as_value(labels=labels)
262 Bitfield(0x1234, 16)
263 >>> LabelRef("test", 0, "imm", slice(31, 12)).as_value(labels=labels)
264 Bitfield(0xabcd1, 20)
265 >>> LabelRef("test", 4, "imm", slice(15, 0)).as_value(labels=labels)
266 Bitfield(0x1238, 16)
267 >>> LabelRef("test", 4, "imm", slice(15, 4)).as_value(labels=labels)
268 Bitfield(0x123, 12)
269 >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x6000)
270 Bitfield(0x2000, 32)
271 >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x800a)
272 Bitfield(0xfffffff6, 32)
273 >>> LabelRef("main", 0, "off", slice(7, 0)).as_value(labels=labels, pc=0x8012)
274 Bitfield(0xee, 8)
275 >>> LabelRef("main", 0, "off8").as_value(labels=labels, pc=0x8012)
276 Bitfield(0xee, 8)
277 >>> LabelRef("main", 0, "off4").as_value(labels=labels, pc=0x8012)
278 Traceback (most recent call last):
279 ...
280 ValueError: value -18 out of i4 range [-8;7]
281 >>> LabelRef("main", 4, "off", slice(7, 0)).as_value(labels=labels, pc=0x70fa)
282 Bitfield(0xa, 8)
283 """
284 if self.label not in labels:
285 raise ValueError("label '{}' unresolved".format(self.label))
286
287 value = labels[self.label] + self.offset
288
289 if self.mode[0] == 'off':
290 assert 'pc' in kwargs, TypeError("'pc' is required to resolve offset-references")
291 value = value - kwargs['pc']
292
293 field = Bitfield(value, self.mode[1])
294 return field[self.slice]
295
296 def __getitem__(self, range):
297 """ slice a label using hi and lo bit indices.
298
299 >>> label = LabelRef("label", 0, "imm32", slice(31,0))
300 >>> label.size
301 32
302 >>> label[31]
303 LabelRef('label', 0, ('imm', 32), 31:31)
304 >>> label[11:0]
305 LabelRef('label', 0, ('imm', 32), 11:0)
306 >>> label[11:0].size
307 12
308 >>> label[12:1]
309 LabelRef('label', 0, ('imm', 32), 12:1)
310 >>> label = LabelRef("abc", 0, "off32", slice(31,12))
311 >>> label.size
312 20
313 >>> label[9:0]
314 LabelRef('abc', 0, ('off', 32), 21:12)
315 >>> label[9:2]
316 LabelRef('abc', 0, ('off', 32), 21:14)
317 >>> label[20:8]
318 Traceback (most recent call last):
319 ...
320 ValueError: slice [20:8] out of range of abc/off32/[31:12]
321 """
322 if not isinstance(range, slice):
323 range = slice(range, range)
324 hi, lo = range.start, range.stop
325
326 if hi < lo:
327 raise ValueError("cant slice reverse range")
328 elif lo < 0 or hi >= self.size:
329 raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
330
331 new_slice = slice(self.slice.stop + hi, self.slice.stop + lo)
332 return LabelRef(self.label, self.offset, self.mode, new_slice)
73333
74334 empty = Bitfield(0, 0)
75335