That date is pretty misleading unfortunately. And note "it happened recently" and "it took a pretty long time" are different claims.
IIRC the aspects of the ABI that "broke" across (say) 2013 and 2015 were generally (if not entirely) for the support of newer features, like thread-safe static initialization. That stuff was only standardized in C++11, which is 4 years, not something I'd call a "long time" (especially considering how long it took everyone to finish implementing C++11).
But even worse than that, it's not like GCC had been compatible with the basics before this. It couldn't even bring itself to mangle "void foo() { }" in a platform-compatible way, and it's not like that took until 2015 for Microsoft to settle on.
I was working an a library at the time, that was using purely pre-C++11 features, and I remember we had to make a build of the library for each MSVC versions we supported at the time. Because they were not compatible, even for basic stuff.
Quote from the link I pasted in the grand-parent comment:
> The Microsoft C++ (MSVC) compiler toolsets in Visual Studio 2013 and earlier don't guarantee binary compatibility across major versions. You can't link object files, static libraries, dynamic libraries, and executables built by different versions of these toolsets. The ABIs, object formats, and runtime libraries are incompatible.
I don't know what you're calling "basic stuff" but it doesn't matter. I'm pretty sure "void foo()" has mangled to ?foo@@YAXXZ for at least two decades, and like I said, I'm pretty sure *nix tools never even attempted to be compatible with that. So there's no excuse here.
maybe calling a void function with no arguments did not change, but other things changed in various ways making it not stable.
What would be the point for the GCC team to even try to be compatible with a changing and non-documented ABI?
It wouldn't buy anything to just support how to mangle basic function, as they couldn't still be called. You either had to be fully compatible, or aren't.
And an ABI can be fairly complicated to implement. For example, in what register do we pass a class passed by value? It can change depending on whether some of its members are float, int or double, or whether it has copy constructor. You get anything wrong and the user suddenly get weird crashes that are going to be extremely hard to debug.
Also I think the debug and release build are still incompatible.