Yes, but the compiler does have to preserve any observable behavior produced by the call to the standard library function. Being able to omit this inserted yield() by the as if rule would mean that it isn't observable, which would also mean that the compiler could already add or not add it anywhere as needed without changing the behavior of the program. Which would seemingly make the inserted yield() pointless as it would have no effect.
Arguably the only change to program behavior is to performance characteristics on hosted environments. It does not change any observable behavior otherwise.
In environments where there are strong forward progress guarantees a busy infinite loop does the same as far as the abstract machine is concerned, as the OS will eventually put the thread to sleep anyway and other threads can make progress. How soon the thread yields is not "observable behavior" (as defined by the standard document).
What I'm most annoyed at with the variable initialization change is that:
- It's potentially a performance change in every single function, especially ones that have sizable fixed-size buffers
- If you have regressions you have to spray [[indeterminate]] everywhere, because there is no coarser way of suppressing it.
- While the language says unrecognized attributes are ignored, compilers frequently warn on unrecognized attributes. Clang, for instance, currently warns on [[indeterminate]].
- There is no defined macro name for backwards compatibility.
Which means that libraries are going have to all declare their own macros for [[indeterminate]] and pepper their code with it.
Pretty sure the internal precision setting works the same way on the 8087, it's documented in the original 8087 datasheet. Windows sets it to 53-bit so there are no excess precision surprises with doubles, and you could run Windows 95 with an 80386 + 80287.
> though it seems to be off by default since MSVC2022
This seems to be a typo in the docs, VS2022 is 17.x and still generates volatile metadata. VS2026 is 18.x.
Last time I tested it, the penalty in Prism for running x64 code without volatile metadata was ~25% on Snapdragon X.
ARM64EC code is essentially x64 code pre-translated to ARM64. It's built against the x64 emulation ABI conventions but runs directly as native ARM64 code. Translation thunking conventions allow for cross-calling between ARM64EC and emulated x64 code.
A lot of the OS libs are shipped in Windows 11 ARM as ARM64X, so they're hybrid ARM64EC+ARM64. x86 libs like MSVCRT.DLL do still seem to be compiled with volatile metadata. DUMPBIN /LOADCONFIG reveals if volatile metadata has been included.
Strange, because .NET was specifically designed to be a JITted environment and taking advantage of SSE2 when available would ordinarily be an advantage of a JIT. But sure enough, .NET 4.0 x86 still uses x87 instructions for math. It's not even good x87, this is surprisingly bad:
There is a significant difference between a stack-based ISA and a stack-based bytecode. In bytecode, it's fine or even a requirement to empty the stack between loop iterations. The JIT will then enregister variables across the loop as appropriate.
With x87, however, that causes extra overhead from loads and stores that's best avoided. Unused stack space can be used to cache frequently used variables, but as operations must use ST(0) as one parameter, FXCH instructions must be used to swap around variables. Matching the x87 stack state on entry and exit of the loop is tricky and compilers historically have had trouble doing it. Different FPUs also differed on the efficiency of FXCH so there were often situations where a particular arrangement would double the speed of a routine on one CPU model and halve it on another.
Not to mention the size difference as well. The JVM stack is 2^16 in size while x87 has 8.
The java compiler can practically pretend like the stack is infinite in size while a compiler dealing with x87 has to contend with spillage in all but the most trivial of algorithms.
The answer's all over the place with each successive CPU generation. Originally Intel CPUs had adds faster than multiplies, then both went through the FMA unit so they were the same, then they added a fast FP adder, etc. And current timings on uops.info now show FP fma 4c and mul 3c over two multiply units, and add 2c over two separate addition units.
Sadly, I've never seen a C++ compiler use this (old) technique for lambda reference captures. The main compilers all seem to just use individual references for each capture instead of a single reference to the stack frame, which makes the lambdas with a lot of reference captures more expensive.
Things get complicated when a lambda that capture by reference is capturing things that are not on a single stack frame (or a stack frame at all). Then you have references to references. You could rely on the optimizer, but the capture has ABI implications.
Does ABI really matter when the lambda is always compiled in the same translation unit as the function from which variables are captured? Seems to me that compilers should be free to optimize the simple cases to a single stack frame reference while falling back to whatever for others.
Yes, it does. The lambda still needs to follow the C++ object model in case someone might use it like a regular C++ object. It's possible to change the ABI with escape analysis that proves you know all of the uses of the lambda to change the ABI, but a) that escape analysis is surprisingly easily defeated [1] and b) ABI-changing optimizations tend to be much more common in research papers than production compilers because getting them right on real code is a lot more difficult than it looks.
[1] The lambda function probably has the same linkage as the function the lambda is contained in, which likely isn't "the only copy of this function is in this TU" but rather "this function may appear in several TUs, but all of these copies are equivalent and you can pick whichever one you like as the actual body." Very different opportunities there!
Direct2D has some complex algorithms because it was designed for high-quality antialiased rasterization on DX9-class hardware, particularly tesselation, and suitable not only for vector graphics but also text glyphs. This bleeds over into the API, which is quite complex and somewhat annoying at times. It revives the GDI-style interface of needing to create and select a solid brush object instead of just passing a color, and drawing text with colored spans is embarrassingly involved (it involves writing a custom rendering bridge between Direct2D and DirectWrite).
The other problem is that Wine has only implemented a minimal amount of the Direct2D API to get specific programs working that aren't heavy graphics programs. Two major omissions, the last time I looked, were that Wine's implementation does not support antialiasing at all, and its ArcTo() draws a line.
reply