diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-05-25 10:53:20 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-05-25 12:48:49 +0000 |
| commit | b22ce8bf080dd9b35ed8c760155f6156390c011c (patch) | |
| tree | c50d522f799499270b51a6481d5cdb5d33f11eb3 | |
| parent | add JUMPING.md (diff) | |
| download | subv-b22ce8bf080dd9b35ed8c760155f6156390c011c.tar.gz subv-b22ce8bf080dd9b35ed8c760155f6156390c011c.zip | |
implement LabelRef
| -rw-r--r-- | bits.py | 298 |
1 files changed, 279 insertions, 19 deletions
@@ -1,13 +1,152 @@ -class Bitfield(object): - def __init__(self, val, size): +import re +from operator import __and__ +from functools import reduce + +class WordBase(object): + def slice_allowempty(self, range): + if not isinstance(range, slice): + range = slice(range, range) + hi, lo = range.start, range.stop + + if hi < lo: + return empty + + return self[range] + + def __and__(self, other): + if isinstance(other, WordBase): + return BitsUnion(self, other) + raise NotImplementedError() + +class BitsUnion(WordBase): + def __init__(self, *parts): + self.parts = parts + + def __repr__(self): + return "BitsUnion({})".format(', '.join(repr(part) for part in self.parts)) + + def as_value(self, **kwargs): + """ reduce down to a single Bitfield value. + + >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value() + Bitfield(0x1234, 16) + >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).as_value(labels={'test': 0xf34f}) + Bitfield(0x1234, 16) + """ + return reduce(__and__, (part.as_value(**kwargs) for part in self.parts)) + + @property + def size(self): + """ get the total size. + + >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size + 16 + >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).size + 16 + """ + return sum(part.size for part in self.parts) + + def __getitem__(self, range): + """ slice a union given hi and lo bit indices. + + >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4] + Bitfield(0x23, 8) + >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4)))[11:4] + BitsUnion(Bitfield(0x2, 4), LabelRef('test', 0, ('imm', 32), 11:8)) + """ + if not isinstance(range, slice): + range = slice(range, range) + hi, lo = range.start, range.stop + + size = self.size + if hi < lo: + raise ValueError("cant slice reverse range") + elif lo < 0 or hi >= size: + raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)) + + i = size + result = Bitfield(0, 0) + for part in self.parts: + p_size = part.size + i -= p_size + if i > hi: + # still above hi + continue + + r_hi = min(p_size - 1, hi - i) + r_lo = max(0, lo - i) + + result = result & part[r_hi:r_lo] + + if i <= lo: + # past lo + break + + return result + + def __and__(self, other): + if isinstance(other, BitsUnion): + return BitsUnion(*self.parts, *other.parts) + elif isinstance(other, WordBase): + return BitsUnion(*self.parts, other) + raise NotImplementedError() + +class Bitfield(WordBase): + def __init__(self, val, size, extra=()): + if val < 0: + min = -(1 << (size - 1)) + max = -1 - min + + if val > max or val < min: + raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max)) + + if val < 0: + val = (1 << size) + val + self.val = val self.size = size + self.extra = extra def __repr__(self): - return "Bitfield(0x{:x}, {})".format(self.val, self.size) + """ format for debugging. + >>> Bitfield(0, 8) + Bitfield(0x0, 8) + >>> Bitfield(16, 8) + Bitfield(0x10, 8) + >>> Bitfield(-16, 8) + Bitfield(0xf0, 8) + >>> Bitfield(3, 5) + Bitfield(0x3, 5) + >>> Bitfield(3, 4, extra=('hello',)) + Bitfield(0x3, 4, extra=('hello',)) + """ + extra = '' + if self.extra: + extra = ", extra={!r}".format(self.extra) + return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra) def __str__(self): - return "0x{:x}'{}".format(self.val, self.size) + """ format as a word. + >>> str(Bitfield(0, 8)) + '00/8' + >>> str(Bitfield(16, 8)) + '10/8' + >>> str(Bitfield(3, 8)) + '03/8' + >>> str(Bitfield(3, 5)) + '03/5' + >>> str(Bitfield(3, 4)) + '3/4' + >>> str(Bitfield(3, 4, extra=('hello',))) + '3/hello/4' + """ + digits = 1 + ((self.size - 1) // 4) + base = "{:x}".format(self.val) + base = (digits - len(base)) * '0' + base + return '/'.join([base, *self.extra, str(self.size)]) + + def as_value(self, **kwargs): + return self def __getitem__(self, range): """ slice a bitfield given hi and lo bit indices. @@ -26,7 +165,7 @@ class Bitfield(object): >>> Bitfield(0xf, 4)[4:0] Traceback (most recent call last): ... - ValueError: slice [4:0] out of range of 0xf'4 + ValueError: slice [4:0] out of range of f/4 >>> Bitfield(0xf, 4)[2:3] Traceback (most recent call last): ... @@ -34,7 +173,7 @@ class Bitfield(object): >>> Bitfield(0xf, 4)[3:-1] Traceback (most recent call last): ... - ValueError: slice [3:-1] out of range of 0xf'4 + ValueError: slice [3:-1] out of range of f/4 """ if not isinstance(range, slice): range = slice(range, range) @@ -49,16 +188,6 @@ class Bitfield(object): val = (self.val >> lo) & ((1 << size) - 1) return Bitfield(val, size) - def slice_allowempty(self, range): - if not isinstance(range, slice): - range = slice(range, range) - hi, lo = range.start, range.stop - - if hi < lo: - return empty - - return self[range] - def __and__(self, other): """ concatenate multiple bitfields. @@ -67,10 +196,141 @@ class Bitfield(object): >>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1) Bitfield(0xcd, 8) """ - if not isinstance(other, Bitfield): - raise NotImplementedError() + if isinstance(other, Bitfield): + return Bitfield(self.val << other.size | other.val, self.size + other.size) + elif isinstance(other, WordBase): + return BitsUnion(self, other) + raise NotImplementedError() + +global_slice = slice +class LabelRef(WordBase): + mode_re = re.compile(r'^(imm|off)(\d+)?$') + + def __init__(self, label, offset, mode, slice=None): + if isinstance(mode, str): + match = LabelRef.mode_re.match(mode) + assert match, ValueError("invalid label reference tag") + mode = (match.group(1), int(match.group(2) or 32)) + + self.label = label + self.mode = mode + self.slice = slice or global_slice(self.mode[1]-1, 0) + self.offset = offset + + def __repr__(self): + """ format for debugging. + + >>> LabelRef("label", 0, ("imm", 32), slice(31,31)) + LabelRef('label', 0, ('imm', 32), 31:31) + >>> LabelRef("label", 4, ("imm", 32), slice(11,0)) + LabelRef('label', 4, ('imm', 32), 11:0) + >>> LabelRef("test", -4, "off32") + LabelRef('test', -4, ('off', 32), 31:0) + >>> LabelRef("test", 0, "off") + LabelRef('test', 0, ('off', 32), 31:0) + """ + return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(self.label, self.offset, self.mode, self.slice) + + def __str__(self): + """ format as a word. + + >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31))) + 'label/imm32/[31:31]' + >>> str(LabelRef("label", 0, ("imm", 32), slice(11,0))) + 'label/imm32/[11:0]' + >>> str(LabelRef("test", 4, "off32")) + 'test+4/off32/[31:0]' + >>> str(LabelRef("test", -12, "off")) + 'test-12/off32/[31:0]' + """ + offset = '' + if self.offset != 0: + offset = "{:+}".format(self.offset) + + return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(self.label, offset, self.mode, self.slice) + + @property + def size(self): + return self.slice.start - self.slice.stop + 1 + + def as_value(self, labels={}, **kwargs): + """ resolve the reference. + + >>> labels = {'test': 0xabcd1234, 'main': 0x8000} + >>> LabelRef("test", 0, "imm").as_value(labels=labels) + Bitfield(0xabcd1234, 32) + >>> LabelRef("test", 0, "imm", slice(15, 0)).as_value(labels=labels) + Bitfield(0x1234, 16) + >>> LabelRef("test", 0, "imm", slice(31, 12)).as_value(labels=labels) + Bitfield(0xabcd1, 20) + >>> LabelRef("test", 4, "imm", slice(15, 0)).as_value(labels=labels) + Bitfield(0x1238, 16) + >>> LabelRef("test", 4, "imm", slice(15, 4)).as_value(labels=labels) + Bitfield(0x123, 12) + >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x6000) + Bitfield(0x2000, 32) + >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x800a) + Bitfield(0xfffffff6, 32) + >>> LabelRef("main", 0, "off", slice(7, 0)).as_value(labels=labels, pc=0x8012) + Bitfield(0xee, 8) + >>> LabelRef("main", 0, "off8").as_value(labels=labels, pc=0x8012) + Bitfield(0xee, 8) + >>> LabelRef("main", 0, "off4").as_value(labels=labels, pc=0x8012) + Traceback (most recent call last): + ... + ValueError: value -18 out of i4 range [-8;7] + >>> LabelRef("main", 4, "off", slice(7, 0)).as_value(labels=labels, pc=0x70fa) + Bitfield(0xa, 8) + """ + if self.label not in labels: + raise ValueError("label '{}' unresolved".format(self.label)) + + value = labels[self.label] + self.offset + + if self.mode[0] == 'off': + assert 'pc' in kwargs, TypeError("'pc' is required to resolve offset-references") + value = value - kwargs['pc'] + + field = Bitfield(value, self.mode[1]) + return field[self.slice] + + def __getitem__(self, range): + """ slice a label using hi and lo bit indices. + + >>> label = LabelRef("label", 0, "imm32", slice(31,0)) + >>> label.size + 32 + >>> label[31] + LabelRef('label', 0, ('imm', 32), 31:31) + >>> label[11:0] + LabelRef('label', 0, ('imm', 32), 11:0) + >>> label[11:0].size + 12 + >>> label[12:1] + LabelRef('label', 0, ('imm', 32), 12:1) + >>> label = LabelRef("abc", 0, "off32", slice(31,12)) + >>> label.size + 20 + >>> label[9:0] + LabelRef('abc', 0, ('off', 32), 21:12) + >>> label[9:2] + LabelRef('abc', 0, ('off', 32), 21:14) + >>> label[20:8] + Traceback (most recent call last): + ... + ValueError: slice [20:8] out of range of abc/off32/[31:12] + """ + if not isinstance(range, slice): + range = slice(range, range) + hi, lo = range.start, range.stop + + if hi < lo: + raise ValueError("cant slice reverse range") + elif lo < 0 or hi >= self.size: + raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)) - return Bitfield(self.val << other.size | other.val, self.size + other.size) + new_slice = slice(self.slice.stop + hi, self.slice.stop + lo) + return LabelRef(self.label, self.offset, self.mode, new_slice) empty = Bitfield(0, 0) |
