Skip to content

[DRAFT] Update the minimum external LLVM to 20 #145071

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
9 changes: 2 additions & 7 deletions compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,16 +1097,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
ty: Ty<'tcx>,
lhs: Self::Value,
rhs: Self::Value,
) -> Option<Self::Value> {
// FIXME: See comment on the definition of `three_way_compare`.
if crate::llvm_util::get_version() < (20, 0, 0) {
return None;
}

) -> Self::Value {
let size = ty.primitive_size(self.tcx);
let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" };

Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]))
self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])
}

/* Miscellaneous instructions */
Expand Down
29 changes: 0 additions & 29 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,35 +173,6 @@ pub(crate) unsafe fn create_module<'ll>(
let mut target_data_layout = sess.target.data_layout.to_string();
let llvm_version = llvm_util::get_version();

if llvm_version < (20, 0, 0) {
if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") {
// LLVM 20 defines three additional address spaces for alternate
// pointer kinds used in Windows.
// See https://github.com/llvm/llvm-project/pull/111879
target_data_layout =
target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", "");
}
if sess.target.arch.starts_with("sparc") {
// LLVM 20 updates the sparc layout to correctly align 128 bit integers to 128 bit.
// See https://github.com/llvm/llvm-project/pull/106951
target_data_layout = target_data_layout.replace("-i128:128", "");
}
if sess.target.arch.starts_with("mips64") {
// LLVM 20 updates the mips64 layout to correctly align 128 bit integers to 128 bit.
// See https://github.com/llvm/llvm-project/pull/112084
target_data_layout = target_data_layout.replace("-i128:128", "");
}
if sess.target.arch.starts_with("powerpc64") {
// LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit.
// See https://github.com/llvm/llvm-project/pull/118004
target_data_layout = target_data_layout.replace("-i128:128", "");
}
if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") {
// LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit.
// See https://github.com/llvm/llvm-project/pull/119204
target_data_layout = target_data_layout.replace("-i128:128", "");
}
}
if llvm_version < (21, 0, 0) {
if sess.target.arch == "nvptx64" {
// LLVM 21 updated the default layout on nvptx: https://github.com/llvm/llvm-project/pull/124961
Expand Down
58 changes: 2 additions & 56 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
// Before LLVM 20 those two features were packaged together as b16b16
("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
// Rust ties fp and neon together.
("aarch64", "neon") => Some(LLVMFeature::with_dependencies(
Expand All @@ -262,56 +259,15 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
// Filter out features that are not supported by the current LLVM version
("aarch64", "fpmr") => None, // only existed in 18
("arm", "fp16") => Some(LLVMFeature::new("fullfp16")),
// NVPTX targets added in LLVM 20
("nvptx64", "sm_100") if get_version().0 < 20 => None,
("nvptx64", "sm_100a") if get_version().0 < 20 => None,
("nvptx64", "sm_101") if get_version().0 < 20 => None,
("nvptx64", "sm_101a") if get_version().0 < 20 => None,
("nvptx64", "sm_120") if get_version().0 < 20 => None,
("nvptx64", "sm_120a") if get_version().0 < 20 => None,
("nvptx64", "ptx86") if get_version().0 < 20 => None,
("nvptx64", "ptx87") if get_version().0 < 20 => None,
// Filter out features that are not supported by the current LLVM version
("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq")
if get_version().0 < 20 =>
{
None
}
// Filter out features that are not supported by the current LLVM version
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
(
"s390x",
"message-security-assist-extension12"
| "concurrent-functions"
| "miscellaneous-extensions-4"
| "vector-enhancements-3"
| "vector-packed-decimal-enhancement-3",
) if get_version().0 < 20 => None,
// Enable the evex512 target feature if an avx512 target feature is enabled.
("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies(
s,
smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")],
)),
// Support for `wide-arithmetic` will first land in LLVM 20 as part of
// llvm/llvm-project#111598
("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None,
("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")),
// In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used".
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28
// Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available".
// https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26
("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
// These new `amx` variants and `movrs` were introduced in LLVM20
("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose")
if get_version().0 < 20 =>
{
None
}
("x86", "movrs") if get_version().0 < 20 => None,
("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")),
("x86", "avx10.2") if get_version().0 < 20 => None,
("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")),
("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")),
("x86", "apxf") => Some(LLVMFeature::with_dependencies(
"egpr",
smallvec![
Expand Down Expand Up @@ -715,17 +671,7 @@ pub(crate) fn global_llvm_features(
};

// Features implied by an implicit or explicit `--target`.
features.extend(
sess.target
.features
.split(',')
.filter(|v| !v.is_empty())
// Drop +v8plus feature introduced in LLVM 20.
// (Hard-coded target features do not go through `to_llvm_feature` since they already
// are LLVM feature names, hence we need a special case here.)
.filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
.map(String::from),
);
features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));

if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind {
features.push("+exception-handling".into());
Expand Down
30 changes: 1 addition & 29 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,36 +895,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
mir::BinOp::Cmp => {
use std::cmp::Ordering;
assert!(!is_float);
if let Some(value) = bx.three_way_compare(lhs_ty, lhs, rhs) {
return value;
}
let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed);
if bx.cx().tcx().sess.opts.optimize == OptLevel::No {
// FIXME: This actually generates tighter assembly, and is a classic trick
// <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>
// However, as of 2023-11 it optimizes worse in things like derived
// `PartialOrd`, so only use it in debug for now. Once LLVM can handle it
// better (see <https://github.com/llvm/llvm-project/issues/73417>), it'll
// be worth trying it in optimized builds as well.
let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs);
let gtext = bx.zext(is_gt, bx.type_i8());
let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
let ltext = bx.zext(is_lt, bx.type_i8());
bx.unchecked_ssub(gtext, ltext)
} else {
// These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`,
// from <https://github.com/rust-lang/rust/pull/63767>.
let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs);
let ge = bx.select(
is_ne,
bx.cx().const_i8(Ordering::Greater as i8),
bx.cx().const_i8(Ordering::Equal as i8),
);
bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge)
}
bx.three_way_compare(lhs_ty, lhs, rhs)
}
mir::BinOp::AddWithOverflow
| mir::BinOp::SubWithOverflow
Expand Down
41 changes: 34 additions & 7 deletions compiler/rustc_codegen_ssa/src/traits/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::ops::Deref;

use rustc_abi::{Align, Scalar, Size, WrappingRange};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::mir;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::{AtomicOrdering, Instance, Ty};
use rustc_session::config::OptLevel;
Expand Down Expand Up @@ -405,15 +406,41 @@ pub trait BuilderMethods<'a, 'tcx>:
fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value;

/// Returns `-1` if `lhs < rhs`, `0` if `lhs == rhs`, and `1` if `lhs > rhs`.
// FIXME: Move the default implementation from `codegen_scalar_binop` into this method and
// remove the `Option` return once LLVM 20 is the minimum version.
fn three_way_compare(
&mut self,
_ty: Ty<'tcx>,
_lhs: Self::Value,
_rhs: Self::Value,
) -> Option<Self::Value> {
None
ty: Ty<'tcx>,
lhs: Self::Value,
rhs: Self::Value,
) -> Self::Value {
// FIXME: This implementation was designed around LLVM's ability to optimize, but `cg_llvm`
// overrides this to just use `@llvm.scmp`/`ucmp` since LLVM 20. This default impl should be
// reevaluated with respect to the remaining backends like cg_gcc, whether they might use
// specialized implementations as well, or continue to use a generic implementation here.
use std::cmp::Ordering;
let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed());
if self.cx().sess().opts.optimize == OptLevel::No {
// This actually generates tighter assembly, and is a classic trick:
// <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>.
// However, as of 2023-11 it optimized worse in LLVM in things like derived
// `PartialOrd`, so we were only using it in debug. Since LLVM now uses its own
// intrinsics, it may be be worth trying it in optimized builds for other backends.
let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs);
let gtext = self.zext(is_gt, self.type_i8());
let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs);
let ltext = self.zext(is_lt, self.type_i8());
self.unchecked_ssub(gtext, ltext)
} else {
// These operations were better optimized by LLVM, before `@llvm.scmp`/`ucmp` in 20.
// See <https://github.com/rust-lang/rust/pull/63767>.
let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs);
let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs);
let ge = self.select(
is_ne,
self.cx().const_i8(Ordering::Greater as i8),
self.cx().const_i8(Ordering::Equal as i8),
);
self.select(is_lt, self.cx().const_i8(Ordering::Less as i8), ge)
}
}

fn memcpy(
Expand Down
Loading
Loading