Live data from Hacker News

Rust on the MOS 6502: Beyond Fibonacci

gergo.erdi.hu

31–40 of 40 posts

Re: Rust on the MOS 6502: Beyond Fibonacci

#31

I am not sure if it is a good idea to compile code targeted to modern processors to 8-bit CPUs like 6502. For example: Languages like C (or Rust) allocate variables on the stack because it is cheap with modern CPUs, but 8-bit CPUs don't have addressing modes to access them easily. (by the way, some modern CPUs like ARM also cannot add a register to a variable on the stack). The solution is not to use the stack for va…

Most of the inoptimality in the article isn't due to the issues you've raised, but rather due to us just starting to optimize LLVM-MOS.

First, I have utterly no idea why there are so many calls to memset; it looks like it's unrolling a loop or something... poorly. It also doesn't seem to be reusing registers when setting up the calls; that's also bad and should be fixed.

Second, if you take a look at the actual structure of the prologue and epilogue, you might notice that it's copying zero page to an absolute memory region called __clear_screen_sstk. This is because LLVM-MOS ran a whole-program analysis on the program and proved that at most one activation of that function could occur at any given time. Thus, it's "stack frame" was automatically allocated statically as a global array, not relative to a moving stack pointer.

The reason that the prologue and epilogue spends so much time copying in and out of the zero page is just that we haven't taught LLVM-MOS how to access the stack directly, but there's no technical obstacle to doing so. Once that's done, the whole body of the function would operate on __clear_screen_sstk directly, and the prologue and epilogue would disappear completely.

Of course, from the first point, you shouldn't need any stack locations to do the body of this routine; there's a big ball of yarn here, but pulling on any of a number of threads would unravel it.

Re: Rust on the MOS 6502: Beyond Fibonacci

#32

That is so cool. I saw some posts about LLVM-MOS a while ago, but at that point I thought it would be just another in a fairly long list of attempts to try and get LLVM to output 6502 instructions. I never expected it to come together this well! Especially considering that the author of the article mentions there were so many issues with LLVM-AVR, you'd expect them to exist in LLVM-MOS as well. Apparently not! I gues…

Up until just a few weeks ago, 100% of the codegen work we've put into LLVM-MOS has been to get it feature-complete and rock-solid. It's awesome to see that that work has paid off! We're just now starting to really optimize the compiler; there's definitely a long road ahead of us, but our preliminary investigations suggest that we'll be able to get the thing to emit really quite good 6502 assembly. Right now, it emit…

> We're just now starting to really optimize the compiler; there's definitely a long road ahead of us, but our preliminary investigations suggest that we'll be able to get the thing to emit really quite good 6502 assembly.

Is there any part of this optimization work that might be upstreamed to LLVM itself and benefit other architectures? Or is this stuff purely 6502-specific?

Re: Rust on the MOS 6502: Beyond Fibonacci

#33

Earlier quoted context omitted.

Up until just a few weeks ago, 100% of the codegen work we've put into LLVM-MOS has been to get it feature-complete and rock-solid. It's awesome to see that that work has paid off! We're just now starting to really optimize the compiler; there's definitely a long road ahead of us, but our preliminary investigations suggest that we'll be able to get the thing to emit really quite good 6502 assembly. Right now, it emit…

> We're just now starting to really optimize the compiler; there's definitely a long road ahead of us, but our preliminary investigations suggest that we'll be able to get the thing to emit really quite good 6502 assembly. Is there any part of this optimization work that might be upstreamed to LLVM itself and benefit other architectures? Or is this stuff purely 6502-specific?

Some of it might benefit AVR, which being 8-bit, shares some of the same problem space. But most of the changes we've made so far are of the kind where LLVM says "this doesn't happen", or "when it does, it's not important." And now the 6502 says, "uh, I actually do need that..."

So in absolute terms of maximizing the flexibility of LLVM, yes, the changes do seem to be broadly useful, but they're mostly in a direction that doesn't benefit most processors all that much.

For example, the 6502 really wants to replace stack usage with global usage; we do this absolutely whenever possible. Other targets actually run the opposite transformation; they replace global variables with stack ones! Placing things on the stack maximizes the chance it'll be in a fast CPU cache (or that it may be folded into a register; this does apply to us too.)

Re: Rust on the MOS 6502: Beyond Fibonacci

#34
Author of mentioned post on 6502.org forum here. In the meantime I worked a bit on implementing proper rust target-triple for 6502 (mos-unknown-none), code is here: https://github.com/mrk-its/rust/tree/mos_target

Then standard cargo tool may be used to directly build 6502 executable, some examples: https://github.com/mrk-its/a800-rust-test or https://github.com/mrk-its/llvm-mos-ferris-demo

Re: Rust on the MOS 6502: Beyond Fibonacci

#35

Earlier quoted context omitted.

> We're just now starting to really optimize the compiler; there's definitely a long road ahead of us, but our preliminary investigations suggest that we'll be able to get the thing to emit really quite good 6502 assembly. Is there any part of this optimization work that might be upstreamed to LLVM itself and benefit other architectures? Or is this stuff purely 6502-specific?

Some of it might benefit AVR, which being 8-bit, shares some of the same problem space. But most of the changes we've made so far are of the kind where LLVM says "this doesn't happen", or "when it does, it's not important." And now the 6502 says, "uh, I actually do need that..." So in absolute terms of maximizing the flexibility of LLVM, yes, the changes do seem to be broadly useful, but they're mostly in a direction…

> For example, the 6502 really wants to replace stack usage with global usage; we do this absolutely whenever possible.

If I understand what you're getting at, this transformation comes up all the time when compiling either coroutines or user-space "green" threads. More obviously, it could expand the usefulness of LLVM for targeting very low-end microcontrollers (even "modern" ones targeting varieties of ARM or other recent architectures) where stack space, and memory more generally is often at a premium.

Re: Rust on the MOS 6502: Beyond Fibonacci

#36
post #17

Er... the article doesn't make it clear, but I guess we're talking about cross-compilation here? So it's not "Rust" (or, as he writes later, LLVM) running on the 6502, just the code generated by the Rust compiler. Still cool though!

So WASM on 6502 next?

Re: Rust on the MOS 6502: Beyond Fibonacci

#37
post #24

Strange exercise because Rust and the 6502 original programming mood are totally different: a word of cleverness and the most obscure side effects in order to squeeze the last clock cycle. But everything is "hack value", I will respect.

I don't think you can get past that the 6502 was meant to be programmed in assembly. Some of the tricks needed to optimally use memory just don't lend themselves to higher level languages. I started with a lot of basic and then moved to assembler because it was the easiest path.

Re: Rust on the MOS 6502: Beyond Fibonacci

#38

Earlier quoted context omitted.

Don’t most people generally mean the target binary from the compiler and not the compiler itself when someone says “see * running on this architecture”? I can see for some dynamic languages there being a destination between the two, but for compiled binaries, generally Rust on X, it doesn’t seem important if rustc also runs on X (especially when discussing micro-controllers since one would rarely run a full compiler…

> Don’t most people And the rest are Forth users happily running interactive, extensible compilers with built in assemblers, block IO, screen editors in a multiuser, multitasking environment.

All 10 of them...sure.

Re: Rust on the MOS 6502: Beyond Fibonacci

#39
post #34

Author of mentioned post on 6502.org forum here. In the meantime I worked a bit on implementing proper rust target-triple for 6502 (mos-unknown-none), code is here: https://github.com/mrk-its/rust/tree/mos_target Then standard cargo tool may be used to directly build 6502 executable, some examples: https://github.com/mrk-its/a800-rust-test or https://github.com/mrk-its/llvm-mos-ferris-demo

That's cool! I wanted to avoid having to build Rust and/or LLVM from source myself, hence the somewhat awkward "tell Cargo we're on default target, let Clang sort it out at link time" setup.

Re: Rust on the MOS 6502: Beyond Fibonacci

#40

Earlier quoted context omitted.

They actually address some of that on their project page, see: https://llvm-mos.org/wiki/Findings

It's a good read, but I still maintain that the immovable zero page and stack make the '02 sub-optimal. The 816 lets you move both around, and the WDC C compiler at least does some nice things with this to allow a proper stack frame. I suspect that an LLVM backend for the 816 would have to be something quite a bit different from the 02.

The downside of the 65x816 compared to the 65x02 is the address/data line multiplexing. In order to add 8 more address lines without going above 40 pins,[0] they multiplexed them onto the data lines. So to decode the address, you need some support circuitry for latching and gating. The 65x816 datasheet (from WDC) gives a schematic for doing so, but it’s not as simple/clean as a 65x02.

I personally would choose the 65x816 over the other for a new design, but I can understand why it’s not as popular.

[0]: 40 was the de facto maximum. Although, the M68k had 64. That thing was a monster in size.

Post reply on HN