Skip to content

[Mini-RFC] Tracking issue for single source cross-compilation #51623

Closed
@DiamondLovesYou

Description

@DiamondLovesYou

As suggested, this issue is about the Rust single source cross compilation story. I've had some success with this while I was working on mir-hsa and the AMDGPU Stuff. In my case, the HSA API is used, which accepts GPU native ELF files directly, and therefore enables me to not have to write a new codegen backend. I have done this with the following changes to Rust:

  • Plugin provided rustc-intrinsics, which run post monomorphization to have access to only concrete types and usage. They are limited to inserting MIR statements just before the original TerminatorKind::Call. This is useful because all Rust functions have a unique type. Thus a call the intrinsic like this one (note the upvar stuff is nonfunctional, however. I need to put more thought into that part) will “return” the DefId (rather, equivalent info) of the function passed in. After expansion, trans rewrites the terminator to be a direct goto. This is the plugin that expands the kernel_info_for intrinsic mentioned/linked earlier.

This expansion originally occurred during trans, like when traditional LLVM intrinsics are handled. However, I think it could be made to happen before collection and partitioning. Either way, this implementation as is allows crate authors to use plugin intrinsics and not force downstream crates to also load that plugin (downstream crates can't be able to call the intrinsics generically).

Here is the trait for the plugin registry:

pub trait MirPluginIntrinsicTrans {
    fn trans_simple_intrinsic<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                        name: &str,
                                        source_info: SourceInfo,
                                        sig: &FnSig<'tcx>,
                                        parent_mir: &mir::Mir<'tcx>,
                                        parent_param_substs: &'tcx Substs<'tcx>,
                                        args: &Vec<Operand<'tcx>>,
                                        return_place: Place<'tcx>,
                                        extra_stmts: &mut Vec<StatementKind<'tcx>>)
        where 'tcx: 'a;
}

The extra statements needed to store the return values are put into extra_stmts, which are translated just after the function returns. The other parameters are so one has the needed things to monomorphize types, and for debugging info.

My impl had Rust just trusting that the "return" value provided by the plugin matched the type specified by the intrinsic declaration. This is probably not what we want long term.

  • A codegen/”debugging” flag to always save/keep Rust’s cstore metadata in outputs. Combined with -Z always-encode-mir, we now have every function’s MIR available at runtime. My original impl made this hard coded ‘cause I’m lazy, so I’ll need to fix this before issuing a PR.

  • An extra codegen/”debugging” flag to force librustc_mir::monomorphize to translate everything, without relying on upstream definitions and a linker. I made this inaccessible to everyone except specialized rustc drivers, ie can’t use rustc -Z retrans-all-deps .., to prevent misuse.

  • Make librustc_metadata::schema public, so one can use CrateRoot and friends. Here is how the metadata was loaded (finding every dylib, including dylibs not actually mapped into the current process, is done elsewhere).

  • Make Rust accept a mono item collector root override. This is used by a special rustc driver at compiled program runtime to rerun trans for specific functions.

Issues (as implemented, so mostly issues related to my runtime impl):

  • This method is limited to Rust code only.
  • It also doesn’t allow performing codegen at compile-time (ie generating for the cross when you’re compiling for the host; the cross must be “compiled” at runtime).
  • Globals referenced will not be shared. Or, globals are defined in the output. Host/GPU ABI differences.
  • Closure upvars are ignored (IIRC, they didn’t work at all when I tried with my prototype, but I decided to ignore this while I got other things working so..). The runtime (eg my mir-hsa::runtime crate) will need to know about the upvars of a closure so that they can get mapped on to the GPU.
  • New Send like trait: NumaSend. This is needed so that types can do appropriate serialization of inner memory regions when they are sent to other memories. Doesn’t seem like there is a whole lotta thought into this area, yet.

Has anyone else worked on any single source prototype? I see this topic on discuss.rust-lang.org, but nothing recent.

Related: #51575 and #38789

RFC of sorts, so please criticize. I'm going to submit patches for the proposed changes above, and I would like to at least get something functionally equivalent accepted.
@eddyb

Edit log:

  1. Fix missing link

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generationA-crossArea: Cross compilationC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions