rustc_codegen_nvvm 0.3.0

A codegen backend for Rustc which targets the libnvvm CUDA library
use libc::c_int;
use rustc_metadata::dynamic_lib::DynamicLibrary;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_target::spec::MergeFunctions;
use std::ffi::CString;

use std::mem;
use std::path::Path;
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

use crate::llvm;

static POISONED: AtomicBool = AtomicBool::new(false);
static INIT: Once = Once::new();

pub(crate) fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

unsafe fn configure_llvm(sess: &Session) {
    // TODO(RDambrosio016): We override the meaning of llvm-args to pass our own nvvm args,
    // but we should probably retain a way to pass args to LLVM.
    let n_args = sess.opts.cg.llvm_args.len() + sess.target.llvm_args.len();
    let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
    let mut llvm_args = Vec::with_capacity(n_args + 1);

    // fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
    //     full_arg
    //         .trim()
    //         .split(|c: char| c == '=' || c.is_whitespace())
    //         .next()
    //         .unwrap_or("")
    // }

    // let cg_opts = sess.opts.cg.llvm_args.iter();
    // let tg_opts = sess.target.llvm_args.iter();
    // let sess_args = cg_opts.chain(tg_opts);

    // dont print anything in here or it will interfere with cargo trying to print stuff which will
    // cause the compilation to fail in mysterious ways.

    // let user_specified_args: FxHashSet<_> = sess_args
    //     .clone()
    //     .map(|s| llvm_arg_to_arg_name(s))
    //     .filter(|s| !s.is_empty())
    //     .collect();

    {
        // This adds the given argument to LLVM. Unless `force` is true
        // user specified arguments are *not* overridden.
        let mut add = |arg: &str, _force: bool| {
            // if force || !user_specified_args.contains(llvm_arg_to_arg_name(arg)) {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
            // }
        };
        // Set the llvm "program name" to make usage and invalid argument messages more clear.
        // add("rustc -Cllvm-args=\"...\" with", true);
        if sess.time_llvm_passes() {
            add("-time-passes", false);
        }
        if sess.print_llvm_passes() {
            add("-debug-pass=Structure", false);
        }
        if !sess.opts.debugging_opts.no_generate_arange_section {
            add("-generate-arange-section", false);
        }

        match sess
            .opts
            .debugging_opts
            .merge_functions
            .unwrap_or(sess.target.merge_functions)
        {
            MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
            MergeFunctions::Aliases => {
                add("-mergefunc-use-aliases", false);
            }
        }

        // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes
        // during inlining. Unfortunately these may block other optimizations.
        add(
            "-preserve-alignment-assumptions-during-inlining=false",
            false,
        );

        // Use non-zero `import-instr-limit` multiplier for cold callsites.
        add("-import-cold-multiplier=0.1", false);

        // for arg in sess_args {
        //     add(&(*arg), true);
        // }
    }

    llvm::LLVMInitializePasses();

    for plugin in &sess.opts.debugging_opts.llvm_plugins {
        let path = Path::new(plugin);
        let res = DynamicLibrary::open(path);
        match res {
            Ok(_) => {}
            Err(e) => bug!("couldn't load plugin: {}", e),
        }
        mem::forget(res);
    }

    llvm::LLVMInitializeNVPTXTarget();
    llvm::LLVMInitializeNVPTXTargetInfo();
    llvm::LLVMInitializeNVPTXTargetMC();
    llvm::LLVMInitializeNVPTXAsmPrinter();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr());
}