Hacker News

Best stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. Microsoft exec called AI scraping 'the largest theft of labor in human history'(techcrunch.com ↗)
    752comments
  2. I don't like passkeys(hawksley.dev ↗)
    703comments
  3. Hister: A private search engine for the pages you visit and the files you keep(github.com/asciimoo ↗)
    188comments
  4. Bend – a language that blocks AI mistakes via proof and runs on GPUs(bend-lang.com ↗)
    302comments
  5. Astra for Law(openai.com ↗)
    667comments
  6. Bonsai 2 27B: Near-Lossless Compression in a 9x Smaller Footprint(prismml.com ↗)
    188comments
  7. OpenJev(openjev.com ↗)
    238comments
  8. Cloudflare Quick Tunnels(cloudflare.com ↗)
    222comments
  9. A heap overflow and SSO misconfiguration to compromise OpenAI internal repos(hacktron.ai ↗)
    196comments
  10. Android 17 is the first since 3.x to add new APIs without releasing to the AOSP(grapheneos.social ↗)
    188comments
  11. How GLM built its own inference infrastructure(z.ai ↗)
    278comments
  12. CCC invites all model citizens to 40C3(ccc.de ↗)
    222comments
  13. Claude Code now reads AGENTS.md if there is no Claude.md(claude.com ↗)
    135comments
  14. How to Write with an LLM(sockpuppet.org ↗)
    246comments
  15. Everybody's Lost Their Minds(netmeister.org ↗)
    317comments
  16. US Military had close call after using AI for hallucinated intelligence report(cnn.com ↗)
    281comments
  17. One year of sponsored Servo development(servo.org ↗)
    144comments
  18. Iran school bombing: grounds to believe US was behind atrocity, UN finds(theguardian.com ↗)
    303comments
  19. Qwen 3.8 Omni Flash(qwen.ai ↗)
    124comments
  20. Jemalloc 5.4.0(github.com/jemalloc ↗)
    87comments
  21. AI safety is mostly a sex cult(skywriter.blue ↗)
    256comments
  22. Neovim have a ~$800k Bitcoin donation sitting untouched since 2023
    264comments
  23. Bend 2 and the Vibe-Coding Trap(liampwll.com ↗)
    228comments
  24. Warren Buffett Steps Down as Berkshire Chairman, Names Son to Replace Him(nytimes.com ↗)
    187comments
  25. Keys Not Included: recovering the signing keys for US driver's license barcodes(ryan.science ↗)
    152comments
  26. Canada welcomes EU proposal to become 'associate member'(bbc.com ↗)
    346comments
  27. Why I didn’t sign the Fields medallists’ letter(gowers.wordpress.com ↗)
    396comments
  28. The scourge of x86 emulation(fex-emu.com ↗)
    80comments
  29. ZCode, the GLM coding agent, silently uploads your Git history(tokenstead.ai ↗)
    13comments
  30. The American Religion of Self-Storage Facilities(newyorker.com ↗)
    440comments

The scourge of x86 emulation

272 pointsby 19h agofex-emu.com
80 comments
16h agoHN ↗

For reference , Fex is a translation framework for x86 to ARM much like Apple’s Rosetta2 and Microsoft’s Prism.

Valve sponsor development as it’s also the way the new Steam Frame supports x86 games. It’s also being used (as a fork) in Crossover Beta to replace the use of Rosetta2.

15h agoHN ↗

Why doesnt Stream require their binaries to be compiled to some bytecode and transpiled during the install ?

THen they wouldnt require any emulator for any new compiles.

15h agoHN ↗

Existing games will not be recompiled for the new bytecode target, and they want all of those games to work regardless.

15h agoHN ↗

I don't know, but assumed that Valve doesn't require studios to recompile their software or use any special tooling, it's basically just packaging of existing executables. This is also why they do Windows on Linux emulation.

12h agoHN ↗

This is also why they do Windows on Linux emulation.

They don't. They use WINE, which as the name says, Is Not an Emulator. :D

(I'll let my pedantic self out now)

11h agoHN ↗

despite the funny backronym, Wine actually IS a WINdows Emulator. Someone apparently told them that it's only called an emulator if it's emulating a CPU architecture, but that's not true.

10h agoHN ↗

I thought it was an implementation of the Win32 API as a compatibility layer.

What is it emulating exactly?

10h agoHN ↗

Does Windows NT have a Windows 9x API emulator?

10h agoHN ↗

If you squint a bit (well, a lot really), Win32S on 3.1 and Windows 95’s Win32 implementation are Windows NT emulators running on top of 16 bit Windows.

35m agoHN ↗

Windows NT doesn't emulated Windows, it is Windows. Wine is not Windows and emulates Windows.

8h agoHN ↗

its closer to a translator than an emulator

15h agoHN ↗

Why doesnt Stream require their binaries to be compiled to some bytecode and transpiled during the install ?

They still have to support the entire back-catalog. It's not reasonable to expect thousands of existing games to port to ARM

14h agoHN ↗

This is what Android does with Dalvik/Android Runtime. Apps you download from the store are compiled to bytecode that is then compiled to native code at installation time.

With the compilation making use of the native instruction set supported for your device, so it can make use of newer arm instructions on newer devices while still supporting older devices.

This is the major reason app installation is so slow in Android compared to iOS (which pre-compiles everything). But it also greatly improves app compatibility with older versions.

There is no way Valve would be able to convince all the myriad of game engine developers to support a proprietary (or even open source) bytecode format even in the future. Best they could do is make it an opt-in.

Android only pulled it off because Google provides the "engine" (Android SDK) for all apps made for the platform and it did from the start.

And even so, most Androids games actually bypass this system entirely and just hand-compile to native binaries anyway. Because, again, the game engines don't want to support this bytecode format and google doesn't offer a C++ toolchain themselves that the engine developers could integrate with.

14h agoHN ↗

Games use plenty of native code; so much in fact that when Intel tried to enter the Android tablet market with x86 they had to write an ARM emulator.

13h agoHN ↗

I mentioned this in the last paragraph of my previous message...

11h agoHN ↗

Your message seems to be arguing both points at the same time - at least that's how it reads to me; first, that it's absolutely feasible to do this because Android did, and then, that in reality game developers there casually defeated those efforts anyway.

11h agoHN ↗

Also didn't they actually do this and realize that Windows API on x86/64 was as stable as anything they could invent?

10h agoHN ↗

Apps you download from the store are compiled to bytecode that is then compiled to native code at installation time.

This is only true in Android 5 and 6.

Starting with Android 7, due to amount of time it took to compile in tiny ARM CPUs, limited optimisation levels, and Java's dynamic abilities, there was a full reboot of this process.

Which is an Assembly hand written interpreter, intended to allow for quick startup in case the JIT cache is empty, a JIT compiler, and an AOT compiler that only compiles the hot paths that the JIT already had a look into, and during idle times while charging so that it can have the CPU all for itself when doing optimisations.

Additionally several other measures were taken to optmise this process, via Play Store Services, devices can share PGO data gathered by the JIT so that each device doesn't have to start from scratch, thus the interpreter step can be skipped when metadata is available. To further improve this process, developers can do training runs, and ship the metadata themselves on the APKs.

Also during the compilation to native code, depending on code access patterns, the executable code gets remapped so that there is an optimal alignment for the instruction cache when reading from the executable code cache.

Google certainly offers clang as part of the NDK, and game development is one of the few officially supported scenarios to run native code on Android.

10h agoHN ↗

It's more than thousands, and not only it's not reasonable, it's not possible.

Plenty of games run on some critical optimizations that exploit native code quirks that may or may not hold up only because Windows papers over them by special-casing the game process at OS level. The source of such games may be stuck in IP limbo, with five different studios/companies claiming ownership (three of them dead or bankrupt), and even then, the only person who knows what and how needs to be reimplemented is on a remote tropical island, working for a competitor / happily retired / stuck collecting trash for a living, depending on how their career went.

Takes only a few such cases to turn out to be some well-known games with substantial legacy fanbase hoping to relive some childhood memories, to risk seriously damaging the "supporting everything" marketing angle.

Emulating underlying runtime is safer and more practical choice.

8h agoHN ↗

"It's not reasonable to expect thousands of existing games to port to ARM"

Realistically it is possible and not actually that difficult. The hardest part would be ensuring that specific code changes from older x86 code to modern ARM code actually translate over properly and perform as expected. The majority of work done there would have to be in the libraries that were used to build the game. For way older games, like the ones you'd run under DOS, it would be much easier, as most stuff was in ASM and C (you did have the occasional BASIC game or PASCAL game flying around which would likely need a little bit of extra work) so you'd just be doing direct translation of instructions.

5h agoHN ↗

Steam has decades of games. It's highly probably a large percentage of them aren't currently even in a buildable state on Intel anymore.

15h agoHN ↗

Several games in their library are actually just game updater/launcher binaries, which manage their associated game assets and binaries externally.

15h agoHN ↗

because there's no gaming-oriented toolchain for it

14h agoHN ↗

Because even with their effective monopoly they don't have the power to bully developers into doing whatever suits Valve. And it would be disastrous for the industry if they did.

13h agoHN ↗

I mean don't modern CPUs basically just run an x86_64 emulator on a RISC machine anyway? Like there's a layer between the microcode and the actual x86 instructions already.

13h agoHN ↗

I think it's more like a (kinda) VLIW machine but yes, pretty much

12h agoHN ↗

Yes, but the “architecture-independent” bytecode for these RISC machines that run underneath your various Intel and AMD CPU models is the x86_64 instruction set.

12h agoHN ↗

No, that's a myth. It's true that the instruction decoder in a CPU core translates from ISA opcodes to micro-ops for the backend, but those micro-ops are in many cases functionally very close to the ISA instruction and decidedly not RISC-like. For example, the ADD r/m64, imm32 instruction (read from memory, add constant, write to memory) is translated to a single micro-op on several Zen architectures [1], which is far from anything you could call "RISC".

[1] https://uops.info/html-instr/ADD_M64_I32.html

9h agoHN ↗

No, they don't. x86 is a superscalar processor, and it does what all superscalar processors do--translate the instructions into µops for execution and dispatch to the various execution units, with the µops not being 1-1 with the original instructions. However, it was supposed to be impossible for a CISC architecture to be superscalar, so this is what spawns the myth that it's somehow cheating by executing a RISC architecture under the hood.

3h agoHN ↗

Thanks, I didn't know that and now I do.

13h agoHN ↗

I hate that you're being downvoted for asking a perfectly reasonable question.

Guys, if you don't like the premise of the question, reply and say why, don't downvote.

I think it's a good question, and my guess for the answer is this - which is going to be the easier problem to solve?

"Hi guys if you want to publish on Steam you have to use this specific tooling and language that'll compile to native code on first run" and everyone just has to do it

or

"Hi guys we want to be able to publish everything so it runs on Arch Linux that we run on our Steamdeck, even if only Windows binaries are available, so given that Gaben is a billionaire what we'll do is just pay guys to make Wine not suck"

You choose.

13h agoHN ↗

That will open a can of worms. For one thing, Valve will be accused of trying to create a walled garden. Plus there's the whole problem of supporting the back-catalogue as others have said.

---

Maybe the best that they could to is to offer to distribute recompiled versions of the emulated binaries, similar to how they distribute compiled shaders. But I don't know if that would work, especially legally.

9h agoHN ↗

That dream of write-once-run-anywhere has been attempted for decades and is still a massive struggle. And Steam isn’t in a position to mandate that kind of massive change. They’re big, but they still have competition from other stores and from direct sales.

9h agoHN ↗

A few reasons:

Bytecode can't really abstract the differences in memory model between the two different processors without some kind of consequence. (IE, it would be slower.) I've personally done some high performance multithreaded programming in C# / .Net, but it only "works" because C# / .Net assumes the TSO memory model. (Described in TFA.)

In contrast, games need to squeak every cycle of performance out of their chips, and optimizations can be very CPU specific. When games target bytecode, they either won't be able to take full advantage of the hardware, or otherwise will need a lot of platform-specific fallbacks (that negate the point of bytecode anyway.)

(This is why I prefer console gaming or "simple" games that don't tax the hardware.)

9h agoHN ↗

Because nobody would publish through them anymore.

Because Valve has zero legitimacy to mandate that anyone changes their software for any reason.

Because this is such an apple-brained, anti developer, anti user move that noone else in the industry does it.

Because Valve mandating that for their own gain would bring in such a gigantic gatekeeper lawsuit.

Because you profiled your code, optimized, then you'd then rely on Valve's "transpiler" to not write back absolute dogshit code back to the users.

Because if it doesn't work well, people are going to blame the video game developer and never Valve.

The list is extremely long, and the only people it would benefit is Valve.

6h agoHN ↗

Disclaimer: I work for Valve, but I had nothing to do with Fex or anything related.

There are a lot of other replies here, but one additional consideration is which solution is generally beneficial outside the original problem domain. A Steam-specific bytecode is applicable to games on Steam, whereas x86 emulation on ARM is of general interest to many problem domains today. Working on the latter could be viewed altruistically (it benefits the larger community) or self-interestedly (other developers are more likely to do work for us), but either way, it's an argument for the generalist path.

16h agoHN ↗

As noted in the article, Apple solved this problem six years ago by simply adding an x86-compatible memory ordering mode to their chip when x86 emulation became important. Yet another way Apple's chips lead the industry.

16h agoHN ↗

Well well well “modeless” has decided to finally see the light of modes

12h agoHN ↗

Arm was never modeless. Thumb is a separate encoding with different instruction semantics and Jazelle ran Java bytecode. Both of them need a special branch instruction to enter. What they don't have is legacy modes like real, v8086 or native 16/32 protected that have no reason to exist when a CPU in long mode can run 16 and 32 bit code (in compatibility sub mode) just fine.

12h agoHN ↗

It was a joke based on the commenter’s username.

14h agoHN ↗

And as noted in the article, while that helps a lot with most of the issues, there are some corner-cases they still don't handle.

7h agoHN ↗

Yeah, I thought that was interesting. Knowing Apple they must’ve profiled a ton of code and decided the hit from not “fixing“ that wasn’t worth enough.

The M chips were already so much faster then the Intel chips Apple was using before (except on Mac Pro maybe) that it was probably still a net win.

14h agoHN ↗

The word "simply" is doing a lot of work there

11h agoHN ↗

A legitimate benefit of vertical integration. They control their own CPUs so they can just do that. Linux has to run on whatever it's given.

47m agoHN ↗

It's not the only such CPU, Fujitsu's also have TSO.

15h agoHN ↗

Great article ! This is the kind of content I always hope to find on HN's front page.

I really wonder how things are organized at Apple to allow for vertical integration to work so well. That feature alone must have involved so many people from so many different teams.

13h agoHN ↗

The actual product people understood it to be paramount to the success of the product and so I feel like from up high there was actual commitment.

7h agoHN ↗

They’ve been through this a few times before, and they absolutely know how important it is.

If they had tried to move to Apple Silicon and said “but none of your old software will work“ it would’ve been dead in the water. Look at how well the early Windows on ARM efforts went, although they were also hamstrung by hardware.

14h agoHN ↗

The intro of this article repeats the common assertion that

ARM is the most relaxed, allowing significant hardware optimizations; and x86 is the most strict, enforcing a very strong coherency model that doesn’t allow a lot of room for optimization

but I've seen some compelling arguments that a relaxed model doesn't necessarily have much of a benefit, https://fgiesen.wordpress.com/2026/08/25/memory-ordering-in-...

9h agoHN ↗

Is this just a question of what one considers to be “significant”? I’d consider 3% to be significant but the authors apparently don’t.

7h agoHN ↗

well it depends on your error bars. On many modern system you can have +/- 5% variation or more run to run just due to the non-determinism present in modern CPU architectures and operating systems (even things like room temperature, time of day, the number of environment variables, etc, can affect this). While you could maybe run a set of careful experiments to characterize and remove this, in my experience most researchers don't bother. So something as small as 3% would need a lot of convincing to me to make the argument that it is significant.

5h agoHN ↗

Multiple separate questions here.

First, is a measured improvement actually real or just an artifact of noise?

Second, if it is real, is 3% anywhere close to the true value?

Third, if 3% is real, is it an important difference?

I'm just commenting on the third one. If 3% is real, it's important.

I'm inclined to believe there's a real improvement. They made a lot of different measurements. If the measured improvement was a result of noise, you'd expect a lot of variation an a lot of measurements where TSA was actually faster, and then 3% was the average of that variation. There was a lot of variation (expected, because they were measuring different things) but nearly all of them had TSO being either neutral or slower. Looking at their benchmark graphs, I see two (out of dozens) where TSO was faster.

As far as being close to the true value, these results suggest there is no single true value, as it depends on the workload. No surprise there.

9h agoHN ↗

It’s a question of how much resources to allocate to the hardware team, and how much resources to be distributed diffusely to the software engineers but especially to the compiler team.

Even your linked paper contends that the actual observed slowdown is as much as 22% in the Geekbench example, but the thesis is that the slowdown is not inherent to TSO, but merely to the specific hardware implementation. Is it worthwhile for a company to optimize its TSO to chase the final gains, or is it better not to have this feature in the first place and just change the compiler?

Indeed my instinct is that it is better to do this in software, where the programmer clearly communicates which stores are ordered, and which may happen in arbitrary order.

9h agoHN ↗

[Disclaimer: I wrote Rosetta 2 and determined the spec for Apple's TSO mode, so I am obviously biased.]

Giesen's article comes off as well-meaning cope from an x86 fan. A relaxed memory model really does give you some performance. Another memory model flaw here in x86 is more architectural, which is that every instruction with the LOCK prefix is essentially a full barrier (of course, x86 could have provided different instructions while still being under TSO). In programs that make heavy usage of atomic reference counting, this actually helps quite a bit.

I would probably put that performance benefit in the single digit percentage range like my sibling comment, which may not seem like much to a SW engineer but is actually pretty serious in CPU microarchitecture. It also helps to be stacked with other architectural advantages over x86, e.g. fixed-length instructions, 32 GPRs (which Intel copied in APX), LDP/STP (which Intel also copied in APX), etc.

One of the old arguments from TSO enjoyers was that TSO helps avoid concurrency bugs that people would accidentally introduce, but this was before the C++ memory model propagated throughout the programming world. Nowadays, I think people generally conceptualize memory consistency in terms of acquire/release anyways, so why not use a CPU architecture that uses the same model?

8h agoHN ↗

Do you think there would be any worthwhile gains from relaxing address-dependent load ordering, like on Alpha/AXP? Or was that just a lot of extra pain for little reward?

7h agoHN ↗

Fun fact: ARM actually has relaxed address dependencies for non-temporal loads, although I don't know if too many implementations of ARM take advantage of this relaxation.

I think it is an interesting question. During the Alpha's lifetime as a non-hobbyist architecture, this decision was pretty much universally derided, but this was in the prehistoric eras of concurrent memory models, where people were just trying their best with a mix of C code, intrinsics, uses of `volatile` sprinkled around to hopefully disable optimizations, and inline assembly. When the C++11 memory model came around, they tried to integrate dependency ordering with `memory_order_consume`, and this famously failed, along with every attempt to fix it. I believe the plan is now for C++ (and later C?) to add special-cased RCU primitives.

The relaxation makes sense in the abstract. As evidenced from the `memory_order_consume` saga, compiler optimizations regularly violate dependency ordering anyways, so in the strictest sense you can't really rely on it. Of course, that doesn't stop people from "knowing" what their compilers will do in such a situation, but that strategy has become a worse one as the years have passed. I would feel better about the whole situation if there was a good greenfield design for a low-level PL that incorporates explicit dependency ordering.

I think it's less clear where the potential HW benefit is in a contemporary CPU. The obvious answer is value prediction, but any CPU performing value prediction has to deal with so many other microarchitectural conditions that can invalidate its speculation decisions that it's not clear this minor one is a huge burden. Of course, people who love TSO might make the same argument, i.e. that it's not a huge burden to snoop cache traffic and invalidate loads (although this is only the load half of TSO, not the store half). I know an Alpha architect who argued that Alpha was right for this decision. I even knew a Transmeta architect who argued for implementing sequential consistency in HW (as Transmeta and derived CPUs actually did).

In practice, microarchitectural structures have capacity/throughput limitations, and there are implementations and complexities that only come up in a real design, so everything needs to be evaluated in the context of a real project. I personally think the sweet spot falls to the weaker side of TSO, which also happens to be near the memory consistency model of the low-level languages we're using anyways.

7h agoHN ↗

and this famously failed, along with every attempt to fix it.

What was the cause of failure? I see that it is now deprecated which surprises me. It was my understanding that large portions of this were taken from the Java memory model and in the JVM, it seems to have been pretty successful.

Is it because the compilers themselves refused to respect it?

6h agoHN ↗

The C/C++ memory model starts with Java's data-race free memory model, but makes data races fully UB. Atomics are introduced into C/C++ that allows fine-grained definition of synchronization, with four different models of atomics: sequentially-consistent (equivalent to Java's volatile), release-acquire, release-consume, and relaxed (which are not synchronizing and are therefore closer to a defined data race than proper atomics). The Java 5 memory model doesn't have the concept of atomics, except that volatile variables act like C/C++ sequentially-consistent atomics.

Relaxed atomics took a couple of tries to specify, and still have a known out-of-thin-air hole that people are still struggling to solve. But the problem with relaxed atomics is that the practical operational semantics are pretty clear, but the formal model is quite tricky for various reasons.

Sequentially-consistent matches the model that most people naively think is going on in hardware (execute a single instruction at a time from a random thread). Release-acquire is a slight relaxation of that model that works well most of the time; the main difference between sequentially-consistent and release-acquire is that release-acquire requires you to specifically release and acquire on the same memory location to get the synchronization, whereas sequentially-consistent synchronizes across different memory locations. Since most multithreaded code tends to have a concept of something like a lock or mutex that is guarding access to a particular region of memory, release-acquire is usually sufficient.

Release-consume is supposed to be release-acquire, but only for data-dependent loads of the consume. This is the only part of the memory model that has really failed, and that's because compilers cannot really guarantee preservation of data dependence in the optimizer. (This also comes up with pointer provenance, FWIW).

6h agoHN ↗

As my sibling comment from jcranmer points out, it was only `memory_order_consume` that failed outright. The other aspects of the C++ memory model mostly worked out, at least after revision.

I believe the last attempt to come up with a comprehensive new proposal was P0190 (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p01...), which reduced the ordering-imposing dependencies in the model to essentially just be pointer dependencies (which does eliminate some legitimately useful cases, e.g. stealing bits from pointers). The paper mentions the unresolved problem of control dependencies from compiler knowledge of pointer equality, which is intertwined with the pointer provenance discussions from around the same time.

I would like to see the basic concept realized in another language some day.

3h agoHN ↗

Does any other architecture allow split cacheline atomic operations across sockets?

41m agoHN ↗

I don't think any other architecture ever supported it.

2h agoHN ↗

In my opinion, conceptualizing memory consistency in terms of acquire/release is wrong and it confuses many programmers.

One problem is that 2 kinds of ordered loads and stores are not enough, 4 kinds are needed.

Some algorithms need not only load-acquire and store-release, but also a store that is guaranteed to be executed before all subsequent stores and a load that is guaranteed to be executed after all previous loads. These properties are the opposite of those provided by load-acquire and store-release. Except for x86 where any loads and stores behave like this, the other popular ISAs do not have such loads and stores, so stronger than necessary instructions must be used, i.e. load barriers and store barriers. Moreover, the instruction that is named a load barrier in the Arm ISA is not a load barrier, but a stronger barrier, but it must be used instead of a load barrier as no better alternative exists.

Besides the fact that not only load-acquire and store-release, but also other 2 ordered loads and stores are needed, a much more serious problem is that load-acquire is not the instruction that is really needed.

I have never seen any useful algorithm where load-acquire is the correct instruction to use. In all algorithms, what you want is not an instruction, but a loop that compares memory repeatedly, waiting for some condition to be fulfilled. The 4 most frequent kinds of loops that are needed are wait-for-not-equal, wait-for-equal, wait-for-even and wait-for-zero.

These loops may execute a load-acquire, but that is not the desired behavior. What you really need are 2 kinds of barriers, one inside the loop and one immediately after the loop.

The internal barrier must prevent the CPU from speculatively executing many future loop instances beyond the conditional jump that terminates the loop body, as it normally does. On x86-64, the instruction PAUSE provides such a barrier. On Aarch64, I suspect that a load-acquire instruction does inhibit this kind of speculative execution, despite the fact that this behavior is not documented. Otherwise, a CPU executing this kind of loop would waste a lot of energy and resources.

The barrier after such a loop must prevent speculative memory accesses beyond it. The semantics of load-acquire are not really needed, because all such loads are done in a loop and the memory accesses that follow the loop cannot be executed before such a load, due to the control dependency created by the conditional jump that follows the load. Nonetheless, while normal execution is impossible, the CPU can execute speculatively any loads following the loop and only this speculative execution can break the acquire semantics. Therefore what you really need is a speculation barrier after the loop, not an acquire barrier, whose behavior is provided automatically by the loop, even without any instruction with acquire semantics.

On x86-64, if a loop is terminated by an unconditional jump, it is said that such a jump blocks the speculative accesses beyond it. This is why the examples provided by Intel in its optimization manual about how to write this kind of acquire loop show loops terminated with unconditional jumps, even if this makes the loops longer, as otherwise the unconditional jump could have been eliminated by moving the conditional jump at the end of the loop. On x86-64, an alternative to unconditional jumps is the LFENCE instruction, which is a barrier for speculative memory accesses.

The so-called load-acquire instruction of Arm might also implement the textbook behavior of load-acquire, of ordering the memory accesses, despite the fact that this behavior is always superfluous, but it must also have the undocumented behavior of being a speculation barrier for memory accesses, otherwise the Arm CPUs would have been very inefficient. However, dedicated speculation barriers of the 2 kinds needed inside the loop and outside the loop would have been more efficient than a load-acquire instruction.

4h agoHN ↗

  > but I've seen some compelling arguments that a relaxed model doesn't necessarily have much of a benefit,

"ELI5" oversimplified explanation why this is insane and can be no other way:

You write A (not in cache), you write B, you write C, ... you write Y, you read Z (in L1d).

In ARM, when A misses L1d, L2, and L3, you can issue writes to B..Y, and the read of Z (which hits in 2 cycles) and go on your merry way, your pipeline sure of the value of Z.

With TSO you CANNOT allow the writes to B..Y to be seen by anyone before A, because everyone expects to only see them after A, so you either buffer them or, eventually, sit on your proverbial ass and wait for A to drain. You might have the buffers to put off the consequences for a while, but that is explicitly a cost ARM lacks, AND(this one hurts the most) you can't simply let the load of Z become an architecturally committed observation before A is done. You can speculate it, but you have to preserve the TSO ordering constraints. Eventually you'll run out of write-buffer slots or other resources, or find that your guess of Z's value was ... wrong. This is an example of how TSO loses perf compared to relaxed. The list of how it gains perf compared to relaxed is shorter: { }

So, in some cases TSO is the same perf as relaxed, in no cases it is faster, in some cases it is slower. So it must be slower overall, since "overall" is a weighted mix of those. "By how much" is a question of detail and workload. However, unless your workload miraculously never suffers a cache miss while a later instruction hits, TSO must be slower. :)

14h agoHN ↗

Slightly related but this project (FEX) is amazing. I've been running Armada OS on multiple ARM handhelds and they're viable little linux machines now except with amazing battery life.

Most problems I run into are anti cheat related (EAC, etc.) but those can be circumvented for now. Feels kind of insane how far x86 emulation has come.

11h agoHN ↗

This honestly sounds like holy grail tech at this point; what kind of battery life are you looking at?

10h agoHN ↗

Wild! I don't actually do a lot of gaming, but the idea of having a small android device running desktop linux is what I've wanted for a long time!

9h agoHN ↗

I’ve got a port of FEX for MacOS to natively run Wine on M series.

Unfortunately, FEX is anti-AI, so I will have to fork.

9h agoHN ↗

The sooner RISC-V gets mass adoption and x86 becomes a historical/preservation/retro ISA the better.

7h agoHN ↗

"These two models are basically the two extremes of the spectrum; where ARM is the most relaxed, allowing significant hardware optimizations; and x86 is the most strict, enforcing a very strong coherency model that doesn’t allow a lot of room for optimization. [...] The best way to explain how the differences in memory models work is to start with how x86 handles this. With TSO being very strict in how it operates, the programmer can assume that when a memory store occurs, that this will be coherently visible to all other processors in the system. The weak memory model that ARM has is a bit less intuitive about how it operates. By default the regular memory loads and stores that ARM uses aren’t strictly coherent across processors in your system, allowing the CPU to operate more efficiently most of the time. When a store instruction executes, that piece of memory (the cacheline) isn’t immediately visible to other processors in the system. Saving on precious power and efficiency because it’s expensive in hardware to invalidate other core’s cachelines, or allow them to snoop another processor’s caches."

First of all great article! It's an absolute must-read for anyone who would design a CPU, GPU, NPU, xPU, Compiler, or Operating System.

It's an absolute must-read for any low-level Programmer.

We can almost think of these different ways of doing things (x86 vs. ARM) as a "battle of virtues" -- on the one hand, with x86, the low-level programmer gets guaranteed memory read consistency across all cores when any one core executes any single instruction which writes something to memory.

Virtuous! But, at the expense of constantly running a whole lot of extra circuits per instruction which use power and generate heat. It's necessary, damn necessary, for some instructions though!

But it isn't necessary for all instructions that write to memory, because whether it's necessary or not is determined by a lot of factors -- the program it's in, is the memory address used for shared communication or a shared data dependency between cores, etc., etc.

So, on the flip side, ARM uses what is called a "relaxed" model.

The low-level programmer gives up the x86 memory-consistent-across-all-cores-guarantee for every memory write, and now has the responsibility to issue additional instructions to get other cores to see that updated memory.

On the one hand, you've got more hardware complexity to make software simpler, on the other, you've got more software complexity to make hardware simpler.

Which is the "right" solution? Well I don't know. Both have their plusses and minuses from either side of the equation, hardware designer or low-level software designer. Still, it is a great issue to be aware of, and even though some posters had some good-faith and possibly very valid critiques of the article, I liked it! It's an important issue to be aware of, for hardware and software designers alike.

4h agoHN ↗

You can get around the memory ordering issue if you only emulate a single core. Performance suffers but for many legacy workloads performance is not critical.

1h agoHN ↗

A potential concern is that when jumping between x86 emulation and ARM code, that the ARM code will pay unnecessary overhead due to all its accesses being TSO now. While this is a reasonable concern, the amount of ARM native code executing under emulation approaches 0%.

This may be true when FEX is executing as a usermode whole-process emulator on Linux, but it is not true when FEX is built for Windows(/Wine)'s ARM64EC mode. With ARM64EC a thread could be running a very small amount of emulated code while everything else is native.

I believe Microsoft Office is built as ARM64EC in order to support x86_64 plugins, in this case the entire suite itself (along with all the system DLLs) are native ARM64EC code and the only emulation would be for plugins. Kingdom Come Deliverance 2 has an ARM64EC build where the main game EXE is small and x86_64, but the actual game engine is in an ARM64EC DLL.

I don't know of a good solution for this though, enabling/disabling TSO needs a kernel syscall so is too slow to be doing constantly when entering/leaving emulation. With cases like KCD2 where the game itself is ARM64EC, maybe it could be faster to not use hardware TSO.