3 ms·
> Even that isn’t guaranteed. If you don’t say “atomic”, the compiler might decide to split up a store to optimize for speed or code size! This isn’t hypothetic
by tjoff 3y ago
> Even that isn’t guaranteed. If you don’t say “atomic”, the compiler might decide to split up a store to optimize for speed or code size! This isn’t hypothetical; Greg Parker ran into this with libobjc.
Maybe I'm tired and lack imagination, but please enlighten me. How can splitting up a store speed something up or reduce the code size? Assuming it is aligned and on a 32bit native CPU.
- compiler-guy 3y agoThe linked thread doesn't say exactly how, but I can construct some way in my head: Final 32-bit store effectively stores two 16-bit values or'ed together as high-16 and low-16. You can get this without any shifting and simple addition and the proper values. If these two sixteen bit values are calculated along two very different paths which have different lengths, the compiler might opt to spill one of them early, only to reload the spilled value just before the final addition. From there it isn't too hard to see some liveness analysis determine that the reload, addition, and immediate store affect onlye bits of the final store, and you can remove parts of it. Pure speculation on my part that this is what happened, but all of these intermediate steps are performed all the time by modern optimizing compilers.
- _nalply 3y agoOr if I understood it correctly, there are platforms like CHERI which have metadata about memory, for example to detect invalid access. This metadata might race with the data itself.
- jrtc27 3y agoThe hardware must, and does, ensure that the metadata (both addressable - bounds, permissions, etc - and non-addressable - the tag) is kept atomic with the address portion of the capability, as otherwise you would be able to forge capabilities via such races. That is, you will never see a torn capability write, and the tag is updated atomically with every write, capability or not. This is easy to do since capabilities are always within a single cache line.
- stouset 3y agoThe value being written was composed from multiple inputs. Instead of instructions to combine those inputs into a register and store them, it performed multiple partial stores of the values it was composed from. Think two u16s being ORed into a u32. Maybe it chose this optimization due to register pressure, and not wanting to spill values that were still going to be used?
- junon 3y ago16 bit CPUs for one. They often lack the instructions.
- kimixa 3y agoYou don't even need unusual architectures - e.g. it's pretty easy to find a constant that might be fast to encode a value as 2 immediate and store the value in memory as two halfword stores on ARM - see https://alisdair.mcdiarmid.org/arm-immediate-value-encoding/ https://alisdair.mcdiarmid.org/arm-immediate-value-encoding/ for the immediate encoding. It might be quicker than loading those immediates into a register, shifting and or-ing them as necessary, and then doing a single store. Especially if your focus is on code size (for small platforms or cache utilization reasons). An I wouldn't be surprised if there's some weird x86 encoding that in some cases helps code size similarly, with different encoded instruction lengths allowing for different immediate sizes.