SIMD Conformance to AdditiveArithmetic

(Bart Chrzaszcz) #1

Hey @scanon ,

I have a question regarding the SIMD protocol. Since SIMD is a vector, which is a regular mathematical object where additive operations are defined, why doesn't SIMD conform to the AdditiveArithmetic protocol?

This question arrises from the following issue I'm having. If I make the structs SIMD2/SIMD4 etc. conform to AdditiveArithmetic, there is a problem that both the AdditiveArithmetic and SIMD protocols define default implementations of the various additive operators, so the compiler can't pick between the 2.

1 Like
(Steve Canon) #2

SIMD cannot conform unconditionally to AdditiveArithmetic, because SIMD supports vectors of any type that conforms to SIMDScalar. E.g. you can have a SIMD2<Int32>, which doesn't define +, only &+, or a SIMD vector with Bool elements, which doesn't support addition at all.

SIMD types can conform conditionally:

extension SIMD4: AdditiveArithmetic where Scalar: BinaryFloatingPoint { }
(Bart Chrzaszcz) #3

This is what I added to the SIMDVectorTypes.swift.gyb file:

extension SIMD${n} : AdditiveArithmetic where Scalar: BinaryFloatingPoint {
  public static var zero: SIMD${n} {
    return SIMD${n}()

And I'm getting the following error:

(Steve Canon) #4

I'm not sure what toolchain you're using where you're having that problem, and I'm not able to reproduce it on any toolchain I have handy (but, you shouldn't need to add zero; that's already conditionally defined on SIMD types with FloatingPoint scalars).

(Bart Chrzaszcz) #5

Ahh yes that is my mistake. So I'm using the Swift for TensorFlow toolchain, which has modified AdditiveArithmetic by providing default implementations of += and -= as can be seen here.

I was wondering what you think about this, and whether a proposal or something should be done in order to provide the default implementation in master.

(Bart Chrzaszcz) #6

Specifically, I was thinking about whether to remove the default implementation of += and -= from CIMD, and adding them to AdditiveArithmetic instead.

(Steve Canon) #7

Naively, the (generic, but concrete) conditional implementation on the SIMD types should probably win over the protocol implementation in this case, though I haven't thought through all the details of that. (cc @Douglas_Gregor)

(Bart Chrzaszcz) #8

...conditional implementation on the SIMD types should probably win over...

Just to confirm, you mean that each of the SIMD2/SIMD4 structs should conditionally define the operators?

Also I'm guessing this may break ABI stability, but could the SIMD${n} structs (or the SIMD protocol instead) conform to the AdditiveArithmetic protocol?

(Steve Canon) #9

I'm saying they already do conditionally define the operators, and those existing definitions should "win" over the AdditiveArithmetic defaults, so there wouldn't be a conflict.

1 Like
(Michael Ilseman) #10

CC @xedin

(Bart Chrzaszcz) #11

I created a PR about this merging into the tensorflow branch. Feel free to ignore the Differentiable part which is a different implementation detail regarding the JIRA ticket. Wanted to see what everyone's opinion is on this CIMD change!

(Pavel Yaskevich) #12

Based on the current rules protocol requirements always wins over declarations found in extensions, but at the same time, protocol requirement is going to be worse than something found in the concrete type.

(Steve Canon) #13

Ah. So if there is an overload defined in a protocol extension (that is not a requirement), there's no rule to tie-break against an overload defined in a constrained extension on a generic type, because they're both "extensions"?

(Pavel Yaskevich) #14

Yes, if both overloads are found in extension we try to see whether Self of one could be used in other but that's pretty much the only thing ranking does (at the moment), so if both declarations came from extension of the same type they would be considered ambiguous.

Same problem exists when there is a protocol requirement and two extensions of the same protocol where one of them is default implementation e.g.

protocol P {
  func foo()

// default implementation
extension P {
   func foo() {

extension P where Self : BinaryInteger {
  func foo() { ... }

Type-checker would find all three declarations but always prefers the protocol requirement, although it's better to prefer constrained extension if Self is indeed a BinaryInteger.

I had PR to address this awhile back but couldn't go the distance with it -

(Jordan Rose) #15

That's a bit different: the protocol requirement is assumed to be more specific because it might be implemented by a concrete type.