Skip to content

Fix/enable features #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ error: failed to build archive: failed to open object file: No such file or dire
That can be caused by the fact that you try to compile with `lto = "fat"`, but you didn't compile the sysroot with LTO.
(Not sure if that's the reason since I cannot reproduce anymore. Maybe it happened when forgetting setting `FAT_LTO`.)

### ld: cannot find crtbegin.o

When compiling an executable with libgccijt, if setting the `*LIBRARY_PATH` variables to the install directory, you will get the following errors:

```
ld: cannot find crtbegin.o: No such file or directory
ld: cannot find -lgcc: No such file or directory
ld: cannot find -lgcc: No such file or directory
libgccjit.so: error: error invoking gcc driver
```

To fix this, set the variables to `gcc-build/build/gcc`.

### How to debug GCC LTO

Run do the command with `-v -save-temps` and then extract the `lto1` line from the output and run that under the debugger.
Expand Down
16 changes: 16 additions & 0 deletions messages.ftl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
codegen_gcc_unknown_ctarget_feature_prefix =
unknown feature specified for `-Ctarget-feature`: `{$feature}`
.note = features must begin with a `+` to enable or `-` to disable it

codegen_gcc_invalid_minimum_alignment =
invalid minimum global alignment: {$err}

Expand All @@ -23,3 +27,15 @@ codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and st
codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`

codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})

codegen_gcc_unknown_ctarget_feature =
unknown feature specified for `-Ctarget-feature`: `{$feature}`
.note = it is still passed through to the codegen backend
.possible_feature = you might have meant: `{$rust_feature}`
.consider_filing_feature_request = consider filing a feature request

codegen_gcc_missing_features =
add the missing features in a `target_feature` attribute

codegen_gcc_target_feature_disable_or_enable =
the target features {$features} must all be either enabled or disabled together
1 change: 1 addition & 0 deletions src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_middle::ty::Ty;
#[cfg(feature = "master")]
use rustc_session::config;
use rustc_target::abi::call::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind};

Expand Down
89 changes: 26 additions & 63 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,72 +4,13 @@ use gccjit::Function;
use rustc_attr::InstructionSetAttr;
#[cfg(feature="master")]
use rustc_attr::InlineAttr;
use rustc_codegen_ssa::target_features::tied_target_features;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty;
#[cfg(feature="master")]
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_session::Session;
use rustc_span::symbol::sym;
use smallvec::{smallvec, SmallVec};

use crate::{context::CodegenCx, errors::TiedTargetFeatures};

// Given a map from target_features to whether they are enabled or disabled,
// ensure only valid combinations are allowed.
pub fn check_tied_features(sess: &Session, features: &FxHashMap<&str, bool>) -> Option<&'static [&'static str]> {
for tied in tied_target_features(sess) {
// Tied features must be set to the same value, or not set at all
let mut tied_iter = tied.iter();
let enabled = features.get(tied_iter.next().unwrap());
if tied_iter.any(|feature| enabled != features.get(feature)) {
return Some(tied);
}
}
None
}

// TODO(antoyo): maybe move to a new module gcc_util.
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
match (arch, s) {
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
("x86", "pclmulqdq") => smallvec!["pclmul"],
("x86", "rdrand") => smallvec!["rdrnd"],
("x86", "bmi1") => smallvec!["bmi"],
("x86", "cmpxchg16b") => smallvec!["cx16"],
("x86", "avx512vaes") => smallvec!["vaes"],
("x86", "avx512gfni") => smallvec!["gfni"],
("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
// NOTE: seems like GCC requires 'avx512bw' for 'avx512vbmi2'.
("x86", "avx512vbmi2") => smallvec!["avx512vbmi2", "avx512bw"],
// NOTE: seems like GCC requires 'avx512bw' for 'avx512bitalg'.
("x86", "avx512bitalg") => smallvec!["avx512bitalg", "avx512bw"],
("aarch64", "rcpc2") => smallvec!["rcpc-immo"],
("aarch64", "dpb") => smallvec!["ccpp"],
("aarch64", "dpb2") => smallvec!["ccdp"],
("aarch64", "frintts") => smallvec!["fptoint"],
("aarch64", "fcma") => smallvec!["complxnum"],
("aarch64", "pmuv3") => smallvec!["perfmon"],
("aarch64", "paca") => smallvec!["pauth"],
("aarch64", "pacg") => smallvec!["pauth"],
// Rust ties fp and neon together. In LLVM neon implicitly enables fp,
// but we manually enable neon when a feature only implicitly enables fp
("aarch64", "f32mm") => smallvec!["f32mm", "neon"],
("aarch64", "f64mm") => smallvec!["f64mm", "neon"],
("aarch64", "fhm") => smallvec!["fp16fml", "neon"],
("aarch64", "fp16") => smallvec!["fullfp16", "neon"],
("aarch64", "jsconv") => smallvec!["jsconv", "neon"],
("aarch64", "sve") => smallvec!["sve", "neon"],
("aarch64", "sve2") => smallvec!["sve2", "neon"],
("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"],
("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(_, s) => smallvec![s],
}
}
use crate::gcc_util::{check_tied_features, to_gcc_features};

/// Get GCC attribute for the provided inline heuristic.
#[cfg(feature="master")]
Expand Down Expand Up @@ -153,11 +94,33 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
}))
.collect::<Vec<_>>();

// TODO(antoyo): check if we really need global backend features. (Maybe they could be applied
// globally?)
// TODO(antoyo): cg_llvm adds global features to each function so that LTO keep them.
// Check if GCC requires the same.
let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
function_features.extend(&mut global_features);
let target_features = function_features.join(",");
let target_features = function_features
.iter()
.filter_map(|feature| {
// FIXME(antoyo): for some reasons, disabling SSE results in the following error when
// compiling Rust for Linux:
// SSE register return with SSE disabled
// TODO(antoyo): support soft-float and retpoline-external-thunk.
if feature.contains("soft-float") || feature.contains("retpoline-external-thunk") || *feature == "-sse" {
return None;
}

if feature.starts_with('-') {
Some(format!("no{}", feature))
}
else if feature.starts_with('+') {
Some(feature[1..].to_string())
}
else {
Some(feature.to_string())
}
})
.collect::<Vec<_>>()
.join(",");
if !target_features.is_empty() {
#[cfg(feature="master")]
func.add_attribute(FnAttribute::Target(&target_features));
Expand Down
41 changes: 13 additions & 28 deletions src/base.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use std::collections::HashSet;
use std::env;
use std::sync::Arc;
use std::time::Instant;

use gccjit::{
Context,
FunctionType,
GlobalKind,
};
#[cfg(feature="master")]
use gccjit::TargetInfo;
use rustc_middle::dep_graph;
use rustc_middle::ty::TyCtxt;
#[cfg(feature="master")]
Expand All @@ -22,8 +19,7 @@ use rustc_codegen_ssa::traits::DebugInfoMethods;
use rustc_session::config::DebugInfo;
use rustc_span::Symbol;

#[cfg(not(feature="master"))]
use crate::TargetInfo;
use crate::{LockedTargetInfo, gcc_util};
use crate::GccContext;
use crate::builder::Builder;
use crate::context::CodegenCx;
Expand Down Expand Up @@ -70,7 +66,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
}
}

pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: Arc<TargetInfo>) -> (ModuleCodegen<GccContext>, u64) {
pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: LockedTargetInfo) -> (ModuleCodegen<GccContext>, u64) {
let prof_timer = tcx.prof.generic_activity("codegen_module");
let start_time = Instant::now();

Expand All @@ -89,7 +85,7 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: Arc<
// the time we needed for codegenning it.
let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;

fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, target_info): (Symbol, Arc<TargetInfo>)) -> ModuleCodegen<GccContext> {
fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, target_info): (Symbol, LockedTargetInfo)) -> ModuleCodegen<GccContext> {
let cgu = tcx.codegen_unit(cgu_name);
// Instantiate monomorphizations without filling out definitions yet...
let context = Context::default();
Expand All @@ -102,32 +98,16 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: Arc<
.map(|string| &string[1..])
.collect();

let add_cpu_feature_flag = |feature: &str| {
// FIXME(antoyo): some tests cause a segfault in GCC when not enabling all these
// features.
if (true || target_info.cpu_supports(feature)) && !disabled_features.contains(feature) {
context.add_command_line_option(&format!("-m{}", feature));
}
};

// TODO(antoyo): only set on x86 platforms.
context.add_command_line_option("-masm=intel");

let features = ["sse2", "avx", "avx2", "sha", "fma", "gfni", "f16c", "aes", "bmi2", "rtm",
"vaes", "vpclmulqdq", "xsavec",
];

for feature in &features {
add_cpu_feature_flag(feature);
if !disabled_features.contains("avx") {
// NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for
// SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead.
// FIXME(antoyo): use the proper builtins for llvm.x86.sse2.cmp.pd and similar.
context.add_command_line_option("-mavx");
}

// TODO(antoyo): only add the following cli arguments if the feature is supported.
context.add_command_line_option("-mpclmul");
context.add_command_line_option("-mfma4");
context.add_command_line_option("-m64");
context.add_command_line_option("-mbmi");
//context.add_command_line_option("-mavxvnni"); // The CI doesn't support this option.

for arg in &tcx.sess.opts.cg.llvm_args {
context.add_command_line_option(arg);
}
Expand All @@ -145,6 +125,11 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: Arc<
context.add_command_line_option("-fno-pie");
}

let target_cpu = gcc_util::target_cpu(tcx.sess);
if target_cpu != "generic" {
context.add_command_line_option(&format!("-march={}", target_cpu));
}

if tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) {
context.add_command_line_option("-ffunction-sections");
context.add_command_line_option("-fdata-sections");
Expand Down
56 changes: 54 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_macros::Diagnostic;
use rustc_errors::{
DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, IntoDiagnosticArg,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::Span;
use std::borrow::Cow;

use crate::fluent_generated as fluent;

#[derive(Diagnostic)]
#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
#[note]
pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
pub feature: &'a str,
}

#[derive(Diagnostic)]
#[diag(codegen_gcc_unknown_ctarget_feature)]
#[note]
pub(crate) struct UnknownCTargetFeature<'a> {
pub feature: &'a str,
#[subdiagnostic]
pub rust_feature: PossibleFeature<'a>,
}

#[derive(Subdiagnostic)]
pub(crate) enum PossibleFeature<'a> {
#[help(codegen_gcc_possible_feature)]
Some { rust_feature: &'a str },
#[help(codegen_gcc_consider_filing_feature_request)]
None,
}

struct ExitCode(Option<i32>);

impl IntoDiagnosticArg for ExitCode {
Expand Down Expand Up @@ -71,3 +99,27 @@ pub(crate) struct LtoDylib;
pub(crate) struct LtoBitcodeFromRlib {
pub gcc_err: String,
}

pub(crate) struct TargetFeatureDisableOrEnable<'a> {
pub features: &'a [&'a str],
pub span: Option<Span>,
pub missing_features: Option<MissingFeatures>,
}

#[derive(Subdiagnostic)]
#[help(codegen_gcc_missing_features)]
pub(crate) struct MissingFeatures;

impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut diag = sess.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
if let Some(span) = self.span {
diag.set_span(span);
};
if let Some(missing_features) = self.missing_features {
diag.subdiagnostic(missing_features);
}
diag.set_arg("features", self.features.join(", "));
diag
}
}
Loading