The release of Apple Silicon-based Macs at the end of last year generated a flurry of news coverage and some surprises at the machine’s performance. This post details some background information on the experience of porting Firefox to run natively on these CPUs.
We’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to know about the new architecture, before moving on to the concept of Universal Binaries.
We’ll then explain how DRM/EME works on the new platform, talk about our experience with macOS Big Sur, and discuss various updater problems we had to deal with. We’ll conclude with the release and an overview of various other improvements that are in the pipeline.
These kinds of articles are very valuable, since Apple isn’t always forthcoming with documentation of specifications, and the new M1-based Macs are no exception. Big, massive projects like Firefox sharing their experiences can be quite useful to other developers.
Definitely an interesting read for me!
Firefox was already built to be portable, including on ARM. Mozilla hit a couple new macos quirks, but they have already done most of the multiarch legwork before.
Anyways they mentioned emulation performance…
Obviously most users are going to want to run the native version of firefox anyways, but I’m curious how many users are stuck with x86 emulation? For example they mentioned that for the time being firefox is dependent on binary x86 blobs to run netflix/google DRM, which has to be emulated. Aside: It’s such a shame HTML5 browsers got dragged down the path of DRM.
I’ve ranted about this on osnews before, but the growth of JITters means portability goes out the window: the browser only supports the instruction sets and calling conventions that it has explicitly been programmed to support. In this case, the important point is that ARM isn’t new, and this isn’t even the first ARM64 architecture.
But make no mistake: Firefox supports Intel and ARM. That’s it. Arguably this is four architectures, since both have 32 and 64 bit versions which are substantially different, but it is not portable anymore.
For the point about using x86 blobs for netflix/widevine, I thought they were saying these are out-of-proc so you can run a native browser and have an x86 plugin?
malxau,
That’s how I understood it too. The main process is native, but the DRM process requires x86 emulation.
This makes me question just how secure the DRM can possibly be if mozilla’s got it running in an isolated container. I imagine it wouldn’t be difficult for others to use the same mechanisms to circumvent the DRM without the DRM being any wiser.
The same argument can be said for MacOS (in fact, MacOS only supports x86-64 and ARM64), Windows, and a whole host of other “portable” projects.
Part of the point of having portable code isn’t to be able to support everything all at once, but to make it much easier to move to different architectures as the needs arise. The portability of Windows NT demonstrates this quite well, given that the 4 architectures it first supported were Alpha, MIPS, PPC and x86, but due to it’s portable code base, it’s been made to run on x86-64, Itanium, and most recently, ARM64
Well, I’d agree that today it’s appropriate to compare Firefox to a kernel, where certain portions require handwritten assembly for each system. It wasn’t always this way though; there was a time when it was essentially all C++, and the issues in porting were about adapting to the UI of the target platform.
It’s just worth remembering all of the platforms and architectures that Mozilla 1.0 supported: http://releases.mozilla.org/pub/mozilla/releases/mozilla1.0/
That’s absolutely untrue that it was portable because it was C++ and only needed UI work. In fact, many of those systems (like VMS, OS/2, HP-UX, and Solaris) barely had compatible C++ compilers and there had to be tons and tons of platform-specific hacks. There would have anyway though, the NSPR had to support almost all of those OSes and CPUs with separate code paths too (and it was, and still is, in C and not C++).
Firefox has a rather well optimized JS interpreter as well, which is very portable. You can run SpiderMonkey on any architecture, regardless of it has a code generator for it.
Can a single binary use two different architectures and do calling conventions between them exist?
If so, JIT could be targeting the native ISA separately from the rest of the application. Or, only some binary blobs could be left emulated. I don’t think current toolchains can deal with it today but with some work that could be sorted out. With Apple M1 setting a precedence in ISA emulation I expect it to spread to other designs as well.
ndrw,
Universal binaries do exactly that, however I think your question was about running both architectures simultaneously and switch on the fly, which is an interesting question. I think it would have to come down to what the emulator (and binary) are designed to do, but I don’t actually know the details of how this is implemented.
Having multiple calling conventions in one binary is quite common. The windows API uses it’s own calling convention which is often different from the language’s native convention. Pascal had it’s own conventions, the BIOS, etc. Obviously we declare the calling convention and the compiler usually takes care of it. As long as the developer has control over the code I don’t see why it would be a huge hurdle. Although not ideal, one could implement completely arbitrary conventions if necessary with assembly if the compiler doesn’t support it.
I’m not sure if apple’s rosetta emulator is capable of supporting both x86_64 and ARM_64 in the same process. Given that a process is an OS construct, I don’t think there’s a hardware reason it couldn’t work, you could “simply” have ARM threads go to physical cores and x86 threads go to emulated ones. It really depends on apple’s rosetta implementation.
I’m not sure what you mean, what precedence? All x86 emulators, including those that translate binary code, have to support JIT code generation techniques like java/javascript/etc, otherwise software would break under emulation. I’ve even tested running QEMU recursively under itself, haha.
No. You would need an RPC mechanism. Which Firefox does have from the Fission work, but I don’t think it was ever intended to work between dissimilar architectures and assumes both sides are the same architecture.
It’s not impossible to have something like a JIT that creates executables for a different target (you could even do that as multilib) but the marshaling for multiprocess where every single JIT call must be IPC would be absolutely not worth it, you’d be better off with the interpreter in almost all cases.
To be honest Apples new box is just another box to me. The internals are technically interesting I suppose but no more interesting than anything and else and mostly just a rearrangement of solved problems and known knowns. It does not excite me.
Porting schmorting. All code should be abstracted and portability a given. It’s not hard. That it takes a project like Firefox to put the discussion on the map is ridiculous.
You can also download free music on Firefox to install music for Apple at https://kostenloseklingeltone.de/