Skip to content

[RFC] Typing of ite #122

@cdonovick

Description

@cdonovick

Currently the typing of ite is a mess and inconsistent with magma.
Now before you all go looking at the mess I want you all to consider the following code snippets and tell me which of the following should be allowable syntax (otherwise you will never understand why ite is such a mess).

Assume the following for all snippets:

BV = BitVector

class SBV(BV): pass
class TBV(BV): pass
class WrappedInt:
    def __init__(self, i):
        self.i = i
    
    def __int__(self):
        return self.i
        
bv1 = BV[8](4) # values here don't really matter
bv2 = BV[8](5)
sbv = SBV[8](4) 
tbv = TBV[8](4)
wi = WrappedInt(4)
b = Bit(0) # again the value here is arbitrary 

Also keep in mind the following property:

res = bv1 & 4
assert isinstance(res, BV[8])
assert res == BV[8](4) == 4

res = bv1 & sbv # Note: sbv & tbv is an error
assert isinstance(res, BV[8])
assert res == BV[8](4) == 4

res = bv1 & wi
assert isinstance(res, BV[8])
assert res == BV[8](4) == 4 

# The preceding may surprise you but would it make sense for it
# to fail and the following to succeed?

res = bv1 & (0 + wi)
res = bv1 & BV[8](wi)

Okay on to the code snippets

b.ite(bv1, 0)                 # 1
b.ite(bv1, wi)                # 2 (Note I consider this to be the same case as 1)
b.ite(bv1, sbv)               # 3

b.ite((bv1, sbv), (bv1, sbv)) # 4
b.ite((bv1, sbv), (sbv, sbv)) # 5

# 6
@ssa # this could be ast_tools or magma combinational
def foo(cond: Bit) -> (BV, BV):
    if cond:
        return bv1, bv2
    else:
        return bv2, bv1        
# 7
@ssa
def bar(cond: Bit) -> (BV, BV):
    if cond:
        r_val = bv1, bv2
    else:
        return bv2, bv1
    return r_val
   
# 8
@ssa
def foobar(cond: Bit) -> (BV, BV):
    if cond:
        r_val = bv1, bv2
    else:
        r_val = bv2, bv1
    return r_val

Currently, hwtypes with ast_tools rewrites* accepts all of these (if it doesn't there is a bug).

It would not be hard to convince me to drop 1 and 2, they are coercions. While it is consistent with other methods to coerce I am fine not coercing in ite and continuing to coerce in other methods (I would rather have no coercion at all)

I feel very strongly that 3 is correct sbv is a BV[8] so there is no reason it shouldn't work

4 and 5 are (partly) the cause of current mess but I feel strongly that if 4 is valid and 3 is valid then logically so must be 5.

Finally, the last 3 cases are the reason why hwtypes accepts tuples. Currently magma combinational will work with 6 and 7 but not 8. This is because magma is quite clever about unpacking return values before it muxes (by using the returns annotation to determine if a return value is a tuple), it however cannot support the arbitrary muxing of tuples which is why 8 does not work. Hence to support 8 we must support 4**.

I find allowing 6 and 7 but not 8 to be pretty unintuitive and hence undesirable.

Now these requirements leads to the really messy typing rules for ITE. See:
https://github.com/leonardt/hwtypes/blob/master/hwtypes/bit_vector.py#L82
https://github.com/leonardt/hwtypes/blob/master/hwtypes/smt_bit_vector.py#L137
https://github.com/leonardt/hwtypes/blob/master/hwtypes/bit_vector_util.py

*

@end_rewrite()
@if_to_phi(Bit.ite)
@bool_to_bit()
@ssa()
@begin_rewrite()

**
I tried to come up with an ast transformation that could robustly perform the necessary unpacking of tuples. However, all the solutions I came up with either required type annotations on ALL assignments (r_val: (BV, BV) = ...) or a static type inference engine (which would give us those annotations). Note as we a have dependent type system we cannot easily hack mypy (or any other static type checking tool that I am aware of) to generate annotations for us.

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions