[Libre-soc-dev] my current plan regarding simdsignal

Jacob Lifshay programmerjake at gmail.com
Fri Oct 22 18:28:29 BST 2021

On Fri, Oct 22, 2021, 04:05 lkcl <luke.leighton at gmail.com> wrote:
> also: given the "carrying of context" (PartType), the idea of throwing
> out SimdSignal.ptype and making it a global is *definitely* a nonstarter.
> it was already a non-starter due to being global (which is frowned
> on in general, in python, so please don't start making design assumptions
> that rely on any globals of any kind, please) but it's just not going to

To be clear, SimdSignal would always access its SimdScope through a member
variable, specifically: self.shape.scope. The SimdScope global is just so
end-user code doesn't have to pass in a SimdScope instance everywhere,
since that is useless verbosity. SimdShape/SimdSignal constructors always,
as their first step, retrieve the current global SimdScope instance *only
if* a specific instance is not passed in. All SimdShape/SimdSignal
implementation code will pass scope=self.shape.scope.

with SimdScope(...) as s:
    s1 = SimdShape(5) # scope retrieved in constructor from SimdScope.get()
by default
    assert s1.scope is s
    s2 = SimdShape(5, scope=s) # we can explicitly pass in a scope
    assert s2.scope is s
    assert s1 == s2
    a = SimdSignal(5) # scope retrieved in constructor from SimdScope.get()
by default
    assert a.shape == s1
    assert a.shape.scope is s
    b = SimdSignal(5, scope=s) # we can explicitly pass in a scope
    assert b.shape.scope is s
    assert b.shape == s1
    c = SimdSignal(s1) # we can explicitly pass in a shape, scope read from
    assert c.shape.scope is s
    assert c.shape is s1

Also, do note that SimdScope can explicitly represent the state of
SimdSignal becoming unvectorized scalar code:
SimdScope(module=..., scalar=True), which produces a SimdScope instance
with member variables set to:
self.module = module
self.vec_el_counts = SimdMap({IntElWid.I64: 1})
self.full_el_count = 1
self.elwid = IntElWid.I64 # constant, not a Signal
self.elwid_type = IntElWid
# no self.scalar member variable, if we want one it should be a property:
class SimdScope:
    def scalar(self):
        retval = False
        for index, el_count in enumerate(self.vec_el_counts.values()):
            if index != 0 or el_count != 1:
                return False
            retval = True
        if retval:
            assert isinstance(self.elwid, self.elwid_type)
            assert isinstance(self.elwid, Value)
        return retval

This allows SimdSignal to easily detect if it should act like a scalar:

class SimdSignal(...):
    def __init__(self, shape, *, sig=None, scope=None, src_loc_at=0,
        self.shape = SimdShape.cast(shape, scope=scope) # calls
SimdScope.get if needed
        if sig is None:
            sig = Signal(self.shape.width, src_loc_at=1 + src_loc_at,
        self.sig = sig
        """if self.shape.scope.scalar, then `sig` is our scalar Value"""

    def cast(value, *, scope=None):
        if isinstance(value, SimdSignal):
            assert scope is None or value.shape.scope is scope, (
                "incompatible SimdScopes, everything in an ALU must "
                "use the exact same SimdScope instance")
            return value
        if scope is None:
            scope = SimdScope.get()
        value = Value.cast(value)
        if scope.scalar:
            # scalar, so we return a SimdSignal with sig set to value
            return SimdSignal(value.shape(), scope=scope, sig=value)
        # TODO: splat to convert scalar to SIMD

    # all operations detect scalar-ness and short-circuit
    def __add__(self, other):
        other = SimdSignal.cast(other, scope=self.shape.scope)
        if self.shape.scope.scalar:
            return SimdSignal.cast(self.sig + other.sig,
        # normal SIMD path



More information about the Libre-soc-dev mailing list