No longer able to type check expressions with Xcode 10.2 (caused by new SIMD API?)

I know for certain that the three lines with i, j and k in test.swift did type check (quickly) prior to upgrading to Xcode 10.2 and macOS 10.14.4. But the compiler is now unable to type check them in reasonable time (i takes forever).

test.swift:

import simd

func test(_ p0: float2, _ p1: float2, _ p2: float2, _ p3: float2) {
    let i = 3*(-p0 + 3*p1) - (3*p2 + p3)
    let j = 6*(p0 - 2*p1 + p2)
    let k = 3*(p1 - p0)
    print(i, j, k)
}
test(float2(0.1, 1.2), float2(2.3, 3.4), float2(4.5, 5.6), float2(6.7, 7.8))

$ swiftc --version
Apple Swift version 5.0 (swiftlang-1001.0.69.5 clang-1001.0.46.3)
Target: x86_64-apple-darwin18.5.0
$ time swiftc test.swift 
test.swift:4:28: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
    let i = 3*(-p0 + 3*p1) - (3*p2 + p3)
            ~~~~~~~~~~~~~~~^~~~~~~~~~~~~

real	0m17.296s
user	0m16.647s
sys	0m0.613s

And a naive workaround like wrapping parts of the expressions in float2(…) will make this tiny program compile, but in 12 seconds!
testB.swift:

import simd

func test(_ p0: float2, _ p1: float2, _ p2: float2, _ p3: float2) {
    let i = 3*float2(-p0 + float2(3*p1)) - float2(3*p2 + p3)
    let j = float2(6*(p0 - 2*p1 + p2))
    let k = float2(3*(p1 - p0))
    print(i, j, k)
}
test(float2(0.1, 1.2), float2(2.3, 3.4), float2(4.5, 5.6), float2(6.7, 7.8))

$ time swiftc testB.swift 

real	0m12.477s
user	0m11.975s
sys	0m0.490s

Is this because float2 is now a typealias for the new generic SIMD2<Float> (cc @scanon )?

We have had to go through and modify a lot of our code because of this, and the build time has increased dramatically, especially in release builds.


There are of course better workaround/ways to break up the expressions. For example, in order to get it to compile it suffices to break up the expression for i into two parts iA and iB:
testC.swift:

import simd

func test(_ p0: float2, _ p1: float2, _ p2: float2, _ p3: float2) {
    let iA = 3*(-p0 + 3*p1)
    let iB = (3*p2 + p3)
    let i = iA - iB
    let j = 6*(p0 - 2*p1 + p2)
    let k = 3*(p1 - p0)
    print(i, j, k)
    print(i)
}
test(float2(0.1, 1.2), float2(2.3, 3.4), float2(4.5, 5.6), float2(6.7, 7.8))

But testC.swift still takes a long time to compile, 7 seconds, for 10 lines of code!
That's much longer than it took Xcode 10.1 (and macOS 10.14.3) to compile an entire medium sized project that included exactly the original expression for i from test.swift above, and lots and lots of other similar expressions.

This is a serious regression. We have to jump through hoops in order to try and reduce the ridiculously long compile times that now arises whenever we are writing code that uses SIMD types, this was not necessary before.

1 Like

This regression is still present in the most recent dev snapshot (2019-04-10).
Filed SR-10461.

I did some more testing and the following demonstration program seems to suggest that this regression is caused (at least in part) by the fact that float2 is now generic (a typealias for SIMD2<Float>).

It would be interesting to know if the behavior of the following program was the same in Xcode 10.1 and macOS 10.14.3. If it wasn't, then the compile time regression in the OP is more general, and not something that only has to do with the SIMD types.


//-----------------------------------------------------------------------------
// This program is meant to demonstrate the difference in the compiler's
// ability to type check the expression returned by the function foo,
// depending on wether Point is generic or not.
//
// So please comment out A and B to test both of them. A will compile
// quickly while B will be unable to type check within reasonable time,
// at least when I try it out with the def toolchain of Xcode 10.2 as
// well as with the most recent dev snapshot (2019-04-10).
//-----------------------------------------------------------------------------

typealias Point = Point2D_Float     // A
//typealias Point = Point2D<Float>  // B


//-----------------------------------------------------------------------------
// Note that:
// A ~ How it was prior to Xcode 10.2, when float2 was not generic.
// B ~ How it is in Xcode 10.2, when float2 is generic (SIMD2<Float>).
//-----------------------------------------------------------------------------


func foo(_ a: Point, _ b: Point, _ c: Point, _ d: Point) -> Point {
    return 2*(3*a - b) + (4*c - d) // Typechecks quickly if A but sloooow if B.
}


struct Point2D_Float {
    var x, y : Float
    init(_ x: Float, _ y: Float) { (self.x, self.y) = (x, y) }
    
    static func +(lhs: Point2D_Float, rhs: Point2D_Float) -> Point2D_Float {
        return Point2D_Float(lhs.x + rhs.x, lhs.y + rhs.y)
    }
    static func -(lhs: Point2D_Float, rhs: Point2D_Float) -> Point2D_Float {
        return Point2D_Float(lhs.x - rhs.x, lhs.y - rhs.y)
    }
    static func *(lhs: Float, rhs: Point2D_Float) -> Point2D_Float {
        return Point2D_Float(lhs * rhs.x, lhs * rhs.y)
    }
}

struct Point2D<Scalar: BinaryFloatingPoint> {
    var x, y : Scalar
    init(_ x: Scalar, _ y: Scalar) { (self.x, self.y) = (x, y) }

    static func +(lhs: Point2D, rhs: Point2D) -> Point2D {
        return Point2D(lhs.x + rhs.x, lhs.y + rhs.y)
    }
    static func -(lhs: Point2D, rhs: Point2D) -> Point2D {
        return Point2D(lhs.x - rhs.x, lhs.y - rhs.y)
    }
    static func *(lhs: Scalar, rhs: Point2D) -> Point2D {
        return Point2D(lhs * rhs.x, lhs * rhs.y)
    }
}


let result = foo(Point(1, 2), Point(3, 4), Point(5, 6), Point(7, 8))
print(result)

I have compiled your code with Xcode 10.1 (Swift 4.2) on a MacBook (Early 2016, 1.2 GHz Intel Core m5) with macOS 10.14.4. Version A compiles quickly, as expected:

# Version A
$ time swiftc main.swift 

real	0m0.207s
user	0m0.108s
sys	0m0.043s

Version B compiles as well, but much slower:

# Version B
$ time swiftc main.swift 

real	0m5.538s
user	0m4.979s
sys	0m0.447s

Interestingly, version B (but not version A) fails to compile in Xcode 10.1 as well if an import simd is added to the program:

# Version B with `import simd`
$ time swiftc main.swift 
main.swift:26:24: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
    return 2*(3*a - b) + (4*c - d) // Typechecks quickly if A but sloooow if B.
           ~~~~~~~~~~~~^~~~~~~~~~~

real	0m14.559s
user	0m13.381s
sys	0m1.093s
3 Likes

The code example in the OP is still too complex to type check, even for Xcode 10.2.1:

import simd

func test(_ p0: float2, _ p1: float2, _ p2: float2, _ p3: float2) {
    let i = 3*(-p0 + 3*p1) - (3*p2 + p3)
    let j = 6*(p0 - 2*p1 + p2)
    let k = 3*(p1 - p0)
    print(i, j, k)
}

swiftc --version
Apple Swift version 5.0.1 (swiftlang-1001.0.82.4 clang-1001.0.46.5)
Target: x86_64-apple-darwin18.5.0
$ time swiftc test.swift
test.swift:4:28: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
    let i = 3*(-p0 + 3*p1) - (3*p2 + p3)
            ~~~~~~~~~~~~~~~^~~~~~~~~~~~~

real	0m20.778s
user	0m18.693s
sys	0m1.627s

:´ /

(As previously noted, this is: SR-10461.)

Thanks for running that program on Xcode 10.1 (and macOS 10.14.4), interesting!

That makes one wonder what the results would be on a machine with macOS 10.14.3.
That is: Is version B not slow when compiled with Xcode 10.1 on macOS 10.14.3?

Perhaps the slow down in type checking has something to do with parts of Swift being in the OS as of macOS 10.14.4?

I'm trying to update our code to Swift 5 and unfortunately, I'm also seeing regressions where the code can't be compiled anymore :frowning_face: This is annoying and will end up costing a lot of time again (last time when we updated from Swift 4 to 4.2, we also ran into this problem).

We're not importing simd, but we do use a lot of operator overloads.

1 Like

I agree, this and the long standing auto-balancing brackets bug takes away a lot of the joy from my work days ...

It would be great if someone could look into SR-10461, keeping in mind that it's not specific to the new SIMD types, but rather that it has to do with operators and generics. I think this build time regression, which is still in Xcode 10.2.1, needs some attention and priority.

cc @tkremenek

1 Like

We could fix the compilation failures by breaking up some expressions, but compilation on CI now takes about twice as long and we had to increase the memory in the docker image because it was otherwise failing to compile (3GB wasn't enough).

So far, upgrading to Swift 5 seems like a step backwards.

1 Like