Rust on i586

The Rust compiler recently gained support for targeting i586-class processors.

Why is this interesting? The i586 CPU architecture also known as P5 came out in 1993, with the first Intel Pentium processor. Surely this ancient CPU isn't relevant now, right? (Don't call me Shirley)

True, while original Pentium processors are way too old and limited to be useful, this new support makes Rust compatible with a number of not-quite-as-old and still useful hardware.

Background

To explain why, I'll need to explain i686. The i686 architecture came out a little bit after i586, was introduced with the Pentium Pro processor, and has been used in the Pentium II and Pentium III processors. i686 is important because nearly all 32-bit x86 Linux software uses i686 as its target.

The 32-bit x86 Rust compiler also targets the "i686" CPU architecture. Sortof. Rust doesn't actually work on these processors, despite claims, because it actually targets the Pentium 4 processor, by using SSE2 instructions that were first introduced on that chip. If you try to run it on a real i686 (a Pentium Pro, Pentium II, or Pentium III), it will try to execute SSE2 instructions which that chip knows nothing about, and crash.

(Side note: Pentium 4 uses a different microarchitecture called NetBurst, sometimes also referred to as "i786", which is an unofficial name that indicates its position as the successor to i686. NetBurst introduced SSE2, HyperThreading, and numerous other things to the x86 architecture. It's also remembered for being rather poor-performing, power-hungry, and hot compared to contemporary chips due to the incredible increase in chip complexity and in particular, the incredibly long pipeline it used. NetBurst was discontinued after the Pentium 4, and its successor was much more similar to the i686, but it retained the SSE2 instructions and some other additions. Almost nobody writes code that specifically targets NetBurst because it is a very unique chip which is difficult to optimize for. However, its instruction set is the relevant part to this discussion. Anyway...)

Probably 99% of people running 32-bit Linux don't care that Rust incorrectly equates i686 with Pentium 4, because that chip first came out in 2006, which was ten years ago, and they're all running much newer processors, so the difference doesn't matter to them.

Well, I'm one of the 1%. :)

I've had a particular dual-processor Pentium III machine around for a long time, since back in 2008 or so when I got it for free when I was in college. It's a pretty nice machine, a Dell PowerEdge 2400/1000 server, with two of the fastest Pentium III chips they made (each at 1.0 GHz ohh yeah!), and it has a hardware RAID card hooked up to four 15,000RPM SCSI hard drives. So its I/O performance is really quite good, and its CPU performance is "acceptable". Also it's really cool looking, so I keep it around for fun.

The Pentium III is an i686, but Rust won't run on it because of the SSE2 instructions. This discrepancy between their claimed i686 support and the fact that it won't run on a number of actual i686 chips has generated a bit of discussion in the Rust community. Ultimately it was decided that redefining their i686 target would possibly cause breakage and might be a performance regression for many people (SSE2 enables some really nice optimizations). i586 and i686 are basically identical from an instruction set perspective, so the Rust maintainers decided to add i586 as a new target, and basically concede that their i686 isn't quite accurate, but is going to stay that way. A bit confusing, but a workable solution.

Building Rust for i586

Okay, with the explanation out of the way, let's get down to compiling the Rust compiler so it will run on a i586-class processor. To do this, you'll need a modern computer with a Pentium 4 or newer processor, either 32-bit or 64-bit. This is because most of the Rust compiler is written in Rust itself, so it needs a working Rust compiler to start the process. (This is called "bootstrapping".)

Start by downloading and installing a nightly build (binary) of Rust. This is necessary because currently, the i586 support is still new, and hasn't reached the Beta or Stable channels. Then download the corresponding Rust source code. It's not necessary that they match exactly down to the commit hash, but they should be close.

Change directory to the Rust source code root and run these commands:

> export CFLAGS="-march=pentium"; export CXXFLAGS="-march=pentium"

This is needed because a number of Rust compiler components are written in C and C++, and so the C/C++ compiler needs to be told that we're targeting this specific CPU microarchitecture. (The Rust Build System should do this automatically, but doesn't yet.)

> ./configure --build=i586-unknown-linux-gnu --disable-jemalloc --enable-local-rust --prefix=/usr

Argument explanation:

  • --build=i586-unknown-linux-gnu - This tells the Rust Build System that we're building for an i586-class processor, in a system made by an unknown vendor, on the Linux kernel, using the GNU ABI. This is called a "triple" (even though it has four components -- originally the last part, specifying the ABI, wasn't part of it).
  • --disable-jemalloc - This disables support for jemalloc, a high-performance memory allocator used by Rust. I've read that jemalloc has problems without SSE2 support, so I've disabled it as a precaution.
  • --enable-local-rust - This tells the build system to use the Rust compiler already installed on my machine, instead of downloading a snapshot to use.
  • --prefix=/usr - This specifies where things will be installed. You might alternatively want to set it to /usr/local to keep this installation from conflicting with anything from your Linux distribution.

After running configure, you're ready to start the build. Run:

> make -j5

Replace 5 with the number of CPU cores on your machine (times two if you have HyperThreading), plus one.

Now go for a walk, enjoy some beverage, and come back in a couple hours. On my quad-core Xeon E5-2609 v2 with 16GB of RAM, this build took a little over an hour and a half. Your time may vary.

> mkdir pkg
> make DESTDIR="pkg" install

This copies the installation into the proper place as if the pkg folder was the root of the filesystem.

> tar cjvf rust-nightly-i586.tar.bz2 -C pkg .

This zips up everything in the pkg directory in a way that if you were to extract it to the root of a filesystem, everything would be in the right place.

Now copy this tarball to your old pre-Pentium 4 system, and run:

> cd /
> tar xvf /whatever/path/to/rust-nightly-i586.tar.bz2

And now you should have a working Rust compiler on your old machine!