rust-lang / rustc_codegen_gcc

libgccjit AOT codegen for rustc
Apache License 2.0
913 stars 60 forks source link

Targeting an arch in gcc but not in LLVM. #49

Open cr1901 opened 3 years ago

cr1901 commented 3 years ago

I'm on vacation till Wednesday, but I figured I should ask this question now while it's fresh on my mind:

I'm interested in getting Rust support as soon as possible for a few architectures with the new gcc codegen; SuperH would be the highest priority for me. What current obstacles exist for me to try out cross compiling to bare-metal sh2/j2, and reporting back (and maybe even fixing, if I can get a grasp on RTL!) any issues I can find. What obstacles exist for attempting to test sh2 codegen using rustc? I can think of a few, but would love to know more:

  1. I understand that distributing libgccjit.so is more complicated than the LLVM backend because each libgccjit.so can only target one arch. Has there been movement on how all the different libgccjit.sos should coexist?
    1. I don't remember where, but I heard libgccjit.so is difficult to use in a cross-compile setting; how true is that?
  2. I'm not sure if libcore is tested for any arch where libstd doesn't exist in Rust proper, let alone for gcc codegen backends. Is there a way to run the libcore tests in qemu or the gdb simulators? Testing libcore would be a good litmus test for bare-metal sh2/j21.
  3. Upstream rustc would have to have a target registered for sh2/j2, and would somehow need to know that only the gcc codegen backend is supported for now (LLVM backend might come later, but it's not a high priority). Does this "choose a backend" logic already exist in upstream Rust? Is there additional ABI work that needs to be done to add sh2 upstream?

I assume 2. and 3. need to be handled first before I can start actual codegen for "archs which only have a gcc port". I'd be interested in getting the ball rolling there (at the very least testing, or contributing code depending on how much time I can allocate to this).

Footnotes

  1. Right now the common Linux ABI seems to be MMU-less fdpic, and I have no idea whether rustc meaningfully supports anything like it.
antoyo commented 3 years ago

I'm on vacation too.

  1. will be under discussion to see what we can do with rustup
  2. I guess we could cross-compile the libcore tests and run them in qemu.
  3. Not sure I understand. For the first part, you mean in rustup? For the "choose a backend", there's already a command line argument to select the gcc/cranelift codegen, is this what you mean? For the ABI, there might be some work needed in Rust indeed. There will also be work needed for the new inline assembly syntax.
glaubitz commented 3 years ago

I'm one of Debian's porters for the less common architectures such as m68k and sh4 (SuperH).

I can bootstrap rustc for any architecture in Debian once rustc_codegen_gcc has been upstreamed into rustc and the Rust compiler can be switched to use libgccjit rather than libllvm.

I guess we will have to add the various boilerplates for the additional architecture to rustc then once rustc_codegen_gcc has been upstreamed.

cr1901 commented 3 years ago

I'm back from vacation.

For the first part, you mean in rustup?

Not necessarily, rustc proper also supports the --target flag.

For the "choose a backend", there's already a command line argument to select the gcc/cranelift codegen, is this what you mean?

What I mean is, something like rustc --target=sh2 by itself should "just work" by defaulting to a libgccjit codegen at a suitable path, and error if the appropriate libgccjit is not installed. Additionally, something like rustc --target=sh2 -Zcodegen-backend=/path/to/libllvm should also error out because there is no libllvm that supports sh2. Though maybe logic like that already exists in libllvm?

@antoyo I guess my overarching question is: What can I do to start adding cross-compilation support to rustc_codegen_gcc, taking the above into account, without getting in your way?

antoyo commented 3 years ago

Sorry for the delay.

As far as I know --target is merely the target triple used and it has no logic to choose a backend. If you would like to change this behaviour (or add a new flag doing what you would like) I suggest you start a discussion somewhere (maybe on Zulip) and ultimately open a MCP to propose the change to the compiler team.

To start helping on the rustc side, you could start a discussion to see whether you can start adding the new architectures you would want in this directory and the new inline-asm register support for those new architectures so that this code can be completed with those new architectures. Maybe you'll have to wait until the gcc codegen is merged, but this discussion can start now ;) .

bjorn3 commented 3 years ago

The target spec used to have a field to specify which backend to use. This was introduced to allow emscripten to use a rustc_codegen_llvm linked against emscripten's llvm instead of the newer default llvm used by rustc. I removed this field in https://github.com/rust-lang/rust/pull/74394 as the emscripten support no longer needed a different llvm and thus the field wasn't used anymore.

cr1901 commented 3 years ago

@bjorn3 Are you aware of any difficulties adding backend selection back, in similar to how it was (before it was removed)?

Maybe you'll have to wait until the gcc codegen is merged, but this discussion can start now ;) .

@antoyo Things have changed on my end in the past 4 weeks, so right now I don't have the time I thought I would to commit to starting a backend port (besides testing x86). But I can at least open issues, get feedback, and study your code in the interim :). I just need a few weeks to get things sorted.

antoyo commented 3 years ago

Some notes from my attempts at running Rust code in a Debian m68k qemu virtual machine:

I was able to run a simple program using core and a "hello world" using the std on m68k.

cr1901 commented 3 years ago

@antoyo Excellent :D! In the coming days, I'll see if I can duplicate this setup in the coming days in rcg_docker, which has bitrotted a bit since your rebase.

MabezDev commented 2 years ago

The object crate will need to support the new architectures in order to compile dylib. The libc crate will need to support the new architectures (it seems to be my last blocker in order to run the tests from libcore on m68k).

Are these required for no_std builds? I'd like to take a stab at compiling for Xtensa using the GCC backend. Is it possible to statically compile core etc with just specifying TARGET_TRIPLE?

bjorn3 commented 2 years ago

Are these required for no_std builds?

The object crate needs to support it the new target. While in the past it was only necessary for dylibs, rustc now also uses object for writing object files containing the crate metadata in rlibs.

Is it possible to statically compile core etc with just specifying TARGET_TRIPLE?

Once you add support to the object crate (and update rustc with the new version) and compile libgccjit to target xtensa (every compiled version of gcc can only target a single triple unlike llvm) it may work.

antoyo commented 2 years ago

@MabezDev Also, if you want to use inline assembly, that will require an update in rustc to support this architecture.

glaubitz commented 2 years ago

@antoyo Do you have a quick guide on how to bootstrap rustc using the backend for a given architecture?

I would love to give it a try with m68k which currently uses the work-in-progress LLVM backend.

MabezDev commented 2 years ago

@MabezDev Also, if you want to use inline assembly, that will require an update in rustc to support this architecture.

Thanks for the hint! Fortunately I've already added Xtensa assembly support in our fork that uses a custom llvm backend: https://github.com/esp-rs/rust/commit/9d4ab85fba15755c4cfa14437f422421cd073d34

antoyo commented 2 years ago

@antoyo Do you have a quick guide on how to bootstrap rustc using the backend for a given architecture?

I would love to give it a try with m68k which currently uses the work-in-progress LLVM backend.

That might be simpler to wait until upstream rustc is updated with the latest changes in this repository, but if you want to do it manually locally, here's how to do it.

Add the missing changes from rustc_codegen_gcc in your local rustc repo (might require to use the commit for nightly-2022-03-26).

Update the config.toml in the rustc repo to add the following config:

changelog-seen = 2

[rust]
codegen-backends = ["gcc"]
deny-warnings = false
new-symbol-mangling = true

then, run the following command:

LIBRARY_PATH=/path/to/libgccjit_dir LD_LIBRARY_PATH=/path/to/libgccjit_dir ./x.py install -j4 --stage 1 --host m68k-unknown-linux-gnu --target m68k-unknown-linux-gnu

I haven't tried cross-compiling rustc yet, so that might not work.

bjorn3 commented 2 years ago

I haven't tried cross-compiling rustc yet, so that might not work.

It probably won't work. Rustc needs to be compiled for the build machine in addition to the chosen host to build the standard library. This would require some way to choose the right libgccjit for depending on the triple to compile for.

glaubitz commented 2 years ago

Rustc needs to be compiled for the build machine in addition to the chosen host to build the standard library.

Wouldn't it be possible to build a Rust cross-compiler with the standard Rust compiler on the host system?

This would require some way to choose the right libgccjit for depending on the triple to compile for.

On Debian unstable, we should have libgccjit versions for all targets.

bjorn3 commented 2 years ago

Wouldn't it be possible to build a Rust cross-compiler with the standard Rust compiler on the host system?

The standard library needs to be compiled by the exact same rustc version as the one that actually uses it. This means that we first have to build stage1 rustc for the host using the bootstrap compiler. This stage1 rustc can then build libstd. And finally another stage1 rustc can be built for the target which can then load the aformentioned libstd version.

antoyo commented 2 years ago

Wouldn't it be possible to build a Rust cross-compiler with the standard Rust compiler on the host system?

The standard library needs to be compiled by the exact same rustc version as the one that actually uses it. This means that we first have to build stage1 rustc for the host using the bootstrap compiler. This stage1 rustc can then build libstd. And finally another stage1 rustc can be built for the target which can then load the aformentioned libstd version.

Do you have any suggestion on how we could do that, perhaps by specifying multiple libgccjit.so paths in the config file?

bjorn3 commented 2 years ago

That would be an option.

glaubitz commented 2 years ago

One option might be to build the suitable libgccjit version during the build process of rustc itself similar to what's already done with LLVM.

Would that be feasible?

antoyo commented 2 years ago

@glaubitz Continuing the discussion from #123 here:

m68k support in libc is complete now, but the rustc source tree has not updated to the latest version of libc due to a Illumos-related change breaking the CI (it's being worked on).

One thing you might try is editing the file build_sysroot/sysroot_src/library/std/Cargo.toml (that you get after running prepare.sh) in the repo of rustc_codegen_gcc to use the latest version of libc:

[patch.crates-io]
libc = { git = "https://github.com/rust-lang/libc", default-features = false, features = ['rustc-dep-of-std'] }

That might work.

glaubitz commented 2 years ago

@glaubitz Continuing the discussion from #123 here:

That might work.

I think I can just update the libc crate in my local branch using cargo update -p libc --precise, so that's probably not the biggest issue.

The only thing that I haven't understood yet: From where do I get a libgccjit that can cross-compile for a given architecture? Do I build libgccjit from source for that purpose?

antoyo commented 2 years ago

The only thing that I haven't understood yet: From where do I get a libgccjit that can cross-compile for a given architecture? Do I build libgccjit from source for that purpose?

Yes, you always have to compile it because I have patches that have not been included in any released libgccjit. The readme contains the instructions of how to do so..

By the way, the instructions in the readme are outdated:

glaubitz commented 2 years ago

OK, thanks a lot! I will give it a try!

antoyo commented 2 years ago

You also won't need this step if rustc now have the m68k target triple:

Since rustc doesn't support this architecture yet, set it back to TARGET_TRIPLE="mips-unknown-linux-gnu" (or another target having the same attributes). Alternatively, create a [target specification file](https://book.avr-rust.com/005.1-the-target-specification-json-file.html) (note that the arch specified in this file must be supported by the rust compiler).

Also, I realized the instructions are not that clear, so you can take a look at this commit to understand what is meant in the instructions.

antoyo commented 2 years ago

I think I can just update the libc crate in my local branch using cargo update -p libc --precise, so that's probably not the biggest issue.

Just in case I wasn't clear, you want to use the latest libc in the program you compile, not within rustc, so you'll have to update the file build_sysroot/sysroot_src/library/std/Cargo.toml like I mentioned earlier.

The reason I couldn't run the tests of libcore on m68k was seemingly because the test runner would run the test executables with a syscall and one was called with an invalid value (since it wasn't using the constants for m68k).

antoyo commented 2 years ago

@glaubitz One more thing, in case you don't know already: use my fork of gcc as some of my patches were not merged yet upstream.

dkm commented 1 year ago

FYI, I've merged the support for libgccjit in crosstool-ng (https://github.com/crosstool-ng/crosstool-ng/pull/1873). This may help in getting the lib for some other target (initial goal was to have a cross rustc_cg_gcc in compiler-explorer https://github.com/compiler-explorer/compiler-explorer/issues/3760)

benjhar commented 1 year ago

Is there any update on this? I'm interested in the SuperH architecture exactly as well, does the opener or anyone else have any information/progress?

glaubitz commented 1 year ago

Is there any update on this? I'm interested in the SuperH architecture exactly as well, does the opener or anyone else have any information/progress?

If you're interested in SuperH, you might want to join #linux-sh on Libera IRC as well as #debian-ports on OFTC IRC.

antoyo commented 1 year ago

Is there any update on this? I'm interested in the SuperH architecture exactly as well, does the opener or anyone else have any information/progress?

I put in the wiki some information about someone who was able to compile some Rust code for SuperH.

glaubitz commented 1 year ago

Is there any update on this? I'm interested in the SuperH architecture exactly as well, does the opener or anyone else have any information/progress?

I put in the wiki some information about someone who was able to compile some Rust code for SuperH.

Wow, very cool. I guess we can start adding non-LLVM architectures to Rust's libc and std libraries.

benjhar commented 1 year ago

Is there any update on this? I'm interested in the SuperH architecture exactly as well, does the opener or anyone else have any information/progress?

I put in the wiki some information about someone who was able to compile some Rust code for SuperH.

That's really interesting! It turns out that the author of that Dockerfile has embarked on exactly the same project I have. I'll have a look at the project. Thanks.

antoyo commented 7 months ago

@glaubitz wrote:

@antoyo Do you have a guide on how to get started adding a new architecture to rustc using rustc_codegen_gcc?

I have heard from people in the Dreamcast community that they had success with sh-elf using rustc_codegen_gcc, so I would like to add sh4-unknown-linux-gnu using this backend.

Let's continue the discussion here instead of in #123 since that issue is closed (or you could open a new issue if you prefer).

I have no idea how to do this, but I can work with you to find out this information and to make the changes to rustc if necessary. You could start by asking on Zulip in the appropriate stream and cc me or I can do that if you prefer.

glaubitz commented 7 months ago

@antoyo Here is a write-up that details how to use rustc_codegen_gcc to target bare-metal SuperH (sh-elf): https://dreamcast.wiki/Rust_on_Dreamcast

This should help us get started to add support for {alpha,hppa,sh4}-unknown-linux-gnu.

If I understood you correctly, you've already got a working compiler for m68k-unknown-linux-gnu? If yes, have all the necessary patches already been upstreamed? And, if yes, can you tell me how to build a native rustc for m68k-unknown-linux-gnu with the help of rustc_codegen_gcc?

antoyo commented 7 months ago

@glaubitz: I have a working cross-compiler targetting m68k-unknown-linux-gnu from x86-64. You mean the patches to GCC? No, I still have a bunch to merge for gcc 14 (and the one necessary for unwinding will not be merged for gcc 14).

I could try building a native rustc for m68k-unknown-linux-gnu, but it is very likely it won't be able to compile many programs (I haven't tried on x86 for a long time, but back then, that was an issue). Would that still be useful for you?

Did you try to cross-compile other softwares (like librsvg or cargo) for m68k?

glaubitz commented 3 months ago

Some notes from my attempts at running Rust code in a Debian m68k qemu virtual machine:

* WIP PR adding notes for cross-compilation: [Add notes for cross-compilation to gcc-only targets #68](https://github.com/rust-lang/rustc_codegen_gcc/pull/68)

* The `object` crate will need to support the new [architectures](https://github.com/gimli-rs/object/blob/d26bc89508852bfe446e06ba5a22f0066ab9e1f1/src/common.rs#L5) in order to compile dylib.

* The `libc` crate will need to support the new [architectures](https://github.com/rust-lang/libc/tree/master/src/unix/linux_like/linux/arch) (it seems to be my last blocker in order to run the tests from libcore on m68k).

I was able to run a simple program using core and a "hello world" using the std on m68k.

m68k support has been added to libc in https://github.com/rust-lang/libc/commit/7a027d34c2d748b23a81fb75c9b84242fe70fcda. Support in object is missing, but I can take care of that, too.

What I still don't understand: How do we make rustc aware of an architecture which is not supported by rustc_codegenllvm? This would be the first step before we can add support for these new architectures to libc as the libc crate can only add targets that rustc recognizes.

GuillaumeGomez commented 3 months ago

Maybe by adding a vec into the target info: supported_by: Vec<Backend> where Backend would be an enum composed of Llvm, Gcc and Cranelift?

glaubitz commented 3 months ago

Maybe by adding a vec into the target info: supported_by: Vec<Backend> where Backend would be an enum composed of Llvm, Gcc and Cranelift?

Sounds plausible to me. Do you have a rough idea how this would look like?

GuillaumeGomez commented 3 months ago

A new field in base that is used for all targets (example here) I suppose?

dkm commented 3 months ago

Have you checked https://rustc-dev-guide.rust-lang.org/building/new-target.html ?

On June 29, 2024 1:36:48 PM UTC, Guillaume Gomez @.***> wrote:

A new field in base that is used for all targets (example here) I suppose?

-- Reply to this email directly or view it on GitHub: https://github.com/rust-lang/rustc_codegen_gcc/issues/49#issuecomment-2198197026 You are receiving this because you commented.

Message ID: @.***>

glaubitz commented 3 months ago

Have you checked https://rustc-dev-guide.rust-lang.org/building/new-target.html ?

I have added rustc_codegen_llvm targets before. But I have no clue how to wire up rustc_codegen_gcc targets.

And, more surprisingly, the instructions to build rustc for the Dreamcast using the sh-elf don't even seem to patch the Rust tree in any way to add support for this target.

I would expect that we would need to add spec files to rustc_target at least.

darcagn commented 3 months ago

Have you checked https://rustc-dev-guide.rust-lang.org/building/new-target.html ?

I have added rustc_codegen_llvm targets before. But I have no clue how to wire up rustc_codegen_gcc targets.

And, more surprisingly, the instructions to build rustc for the Dreamcast using the sh-elf don't even seem to patch the Rust tree in any way to add support for this target.

I would expect that we would need to add spec files to rustc_target at least.

I'm using a little bit of a trick for that in my scripts that wrap the rustc_codegen_gcc tools. rustc thinks it's compiling for MIPS, and so it outputs object files that contain the SH code from GCC but with MIPS architecture specified in the header. The script then rewrites to SH before sending these as input to the linker.

As for the libc crate, I've been able to add support for our homebrew KallistiOS on Dreamcast because I am gating the configuration behind a kallistios OS target in the target JSON file and in forked libc/rust crates and not worrying about architecture-specific configurations. I haven't published these forks yet but I will do so in the coming weeks as time permits.

It has been working fine doing this so far, but obviously this isn't the right way to handle things and we'll need to add SH, etc. to rustc properly in the long run.

glaubitz commented 3 months ago

I'm using a little bit of a trick for that in my scripts that wrap the rustc_codegen_gcc tools. rustc thinks it's compiling for MIPS, and so it outputs object files that contain the SH code from GCC but with MIPS architecture specified in the header. The script then rewrites to SH before sending these as input to the linker.

OK, that clarifies my confusion. Thanks a lot!

As for the libc crate, I've been able to add support for our homebrew KallistiOS on Dreamcast because I am gating the configuration behind a kallistios OS target in the target JSON file and in forked libc/rust crates and not worrying about architecture-specific configurations. I haven't published these forks yet but I will do so in the coming weeks as time permits.

Adding libc is relatively easy if you're just adding a new Linux target (which is what I'm interested in).

It has been working fine doing this so far, but obviously this isn't the right way to handle things and we'll need to add SH, etc. to rustc properly in the long run.

Would be great to document this as well as possible to ease the process of adding new libgccjit targets for others.

gyrovorbis commented 3 months ago

I'm not super familiar with what exactly @darcagn had to go through to get all of this set up with rustc_codegen_gcc targeting SH4, but I have been on the KallistiOS side helping to support the back-end needs for the Rust stdlib and even tokio async library on the Sega Dreamcast, working on providing a pthread layer of abstraction above our own custom kernel among a few other things in C...

And holy crap, guys, I can't believe how well this stuff works and just how quickly @darcagn was able to work his way up to having async and advanced Rust concurrency running just fine on the Sega Dreamcast with our TLS model (currently only supports local-exec), our atomics (we provide our own just toggling interrupts), and many other things...

I really think this is going to be able to bring a lot of epic Rust stuff to the SH arch, and I would love to see us getting support for our target upstreamed....

Also its's so nice to see all of you SH rockstars here and how much interest there is in bringing this cool new stuff to the target. I'll have to join the IRC server!

EDIT: @antoyo, should the "Rust on Dreamcast" article by @darcagn get added to the wiki as an SH link as well? He even bought a .rs site for his Rust Dreamcast stuff: http://dreamcast.rs. Haha.

glaubitz commented 3 months ago

I'm not super familiar with what exactly @darcagn had to go through to get all of this set up with rustc_codegen_gcc targeting SH4, but I have been on the KallistiOS side helping to support the back-end needs for the Rust stdlib and even tokio async library on the Sega Dreamcast, working on providing a pthread layer of abstraction above our own custom kernel among a few other things in C...

And holy crap, guys, I can't believe how well this stuff works and just how quickly @darcagn was able to work his way up to having async and advanced Rust concurrency running just fine on the Sega Dreamcast with our TLS model (currently only supports local-exec), our atomics (we provide our own just toggling interrupts), and many other things...

This gives me hope that we can get rustc_codegen_gcc get to work for alpha, hppa, m68k and sh4 in Debian as well.

I really think this is going to be able to bring a lot of epic Rust stuff to the SH arch, and I would love to see us getting support for our target upstreamed....

Also its's so nice to see all of you SH rockstars here and how much interest there is in bringing this cool new stuff to the target. I'll have to join the IRC server!

Please keep in mind that there are still a lot of open issues with the SH backend in GCC, so please help me organize a crowdfunding campaign to pay an experienced GCC developer to revamp the SH backend in GCC. The SH backend really isn't in a good shape at the moment with lots of source packages triggering ICEs.

glaubitz commented 2 months ago

@antoyo Are there plans to add GCC upstream as a git submodule to the rustc git tree similar to LLVM?

I think it would be great to have rustc_codegen_gcc eventually work exactly like rustc_codegen_llvm, so that in the end the build system will automatically build libgccjit for targets that have GCC backends only.

GuillaumeGomez commented 2 months ago

https://github.com/rust-lang/rust/pull/125419

glaubitz commented 2 months ago

rust-lang/rust#125419

Woohoo! Awesome, thanks!

dkm commented 2 months ago

Looks like there's some confusion about the GPL license in the comments. Hope this won't push it back or trigger false alarm :). Nice to see it included in rustc :thumbup: