Skip to content
Closed
25 changes: 25 additions & 0 deletions compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,31 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
ret.write_cvalue(fx, ret_lane);
}

sym::simd_splat => {
intrinsic_args!(fx, args => (value); intrinsic);

if !ret.layout().ty.is_simd() {
report_simd_type_validation_error(fx, intrinsic, span, ret.layout().ty);
return;
}
let (lane_count, lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);

if value.layout().ty != lane_ty {
fx.tcx.dcx().span_fatal(
span,
format!(
"[simd_splat] expected element type {lane_ty:?}, got {got:?}",
got = value.layout().ty
),
);
}

for i in 0..lane_count {
let ret_lane = ret.place_lane(fx, i.into());
ret_lane.write_cvalue(fx, value);
}
}

sym::simd_neg
| sym::simd_bswap
| sym::simd_bitreverse
Expand Down
36 changes: 36 additions & 0 deletions compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,42 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
return Ok(bx.vector_select(vector_mask, arg1, args[2].immediate()));
}

#[cfg(feature = "master")]
if name == sym::simd_splat {
let (out_len, out_ty) = require_simd2!(ret_ty, SimdReturn);

require!(
args[0].layout.ty == out_ty,
InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: out_ty,
vector_type: ret_ty,
}
);

let vec_ty = llret_ty.unqualified().dyncast_vector().expect("vector return type");
let elem_ty = vec_ty.get_element_type();

// Cast pointer type to usize (GCC does not support pointer SIMD vectors).
let value = args[0];
let scalar = if value.layout.ty.is_numeric() {
value.immediate()
} else if value.layout.ty.is_raw_ptr() {
bx.ptrtoint(value.immediate(), elem_ty)
} else {
return_error!(InvalidMonomorphization::UnsupportedOperation {
span,
name,
in_ty: ret_ty,
in_elem: value.layout.ty
});
};

let elements = vec![scalar; out_len as usize];
return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &elements));
}

// every intrinsic below takes a SIMD vector as its first argument
require_simd!(
args[0].layout.ty,
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,31 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
}

if name == sym::simd_splat {
let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn);

require!(
args[0].layout.ty == out_ty,
InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: out_ty,
vector_type: ret_ty,
}
);

// `insertelement <N x elem> poison, elem %x, i32 0`
let poison_vec = bx.const_poison(llret_ty);
let idx0 = bx.const_i32(0);
let v0 = bx.insert_element(poison_vec, args[0].immediate(), idx0);

// `shufflevector <N x elem> v0, <N x elem> poison, <N x i32> zeroinitializer`
// The masks is all zeros, so this splats lane 0 (which has our element in it).
let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty));

return Ok(splat);
}

// every intrinsic below takes a SIMD vector as its first argument
let (in_len, in_elem) = require_simd!(args[0].layout.ty, SimdInput);
let in_ty = args[0].layout.ty;
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1074,8 +1074,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if constant_ty.is_simd() {
// However, some SIMD types do not actually use the vector ABI
// (in particular, packed SIMD types do not). Ensure we exclude those.
//
// We also have to exclude vectors of pointers because `immediate_const_vector`
// does not work for those.
let layout = bx.layout_of(constant_ty);
if let BackendRepr::SimdVector { .. } = layout.backend_repr {
let (_, element_ty) = constant_ty.simd_size_and_type(bx.tcx());
if let BackendRepr::SimdVector { .. } = layout.backend_repr
&& element_ty.is_numeric()
{
let (llval, ty) = self.immediate_const_vector(bx, constant);
return OperandRef {
val: OperandValue::Immediate(llval),
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
self.copy_op(&self.project_index(&input, index)?, &dest)?;
}
sym::simd_splat => {
let elem = &args[0];
let (dest, dest_len) = self.project_to_simd(&dest)?;

for i in 0..dest_len {
let place = self.project_index(&dest, i)?;
self.copy_op(elem, &place)?;
}
}
sym::simd_neg
| sym::simd_fabs
| sym::simd_ceil
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ pub(crate) fn check_intrinsic_type(
sym::simd_extract | sym::simd_extract_dyn => {
(2, 0, vec![param(0), tcx.types.u32], param(1))
}
sym::simd_splat => (2, 0, vec![param(1)], param(0)),
sym::simd_cast
| sym::simd_as
| sym::simd_cast_ptr
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,7 @@ symbols! {
simd_shr,
simd_shuffle,
simd_shuffle_const_generic,
simd_splat,
simd_sub,
simd_trunc,
simd_with_exposed_provenance,
Expand Down
7 changes: 7 additions & 0 deletions library/core/src/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ pub const unsafe fn simd_insert_dyn<T, U>(x: T, idx: u32, val: U) -> T;
#[rustc_intrinsic]
pub const unsafe fn simd_extract_dyn<T, U>(x: T, idx: u32) -> U;

/// Creates a vector where every lane has the provided value.
///
/// `T` must be a vector with element type `U`.
#[rustc_nounwind]
#[rustc_intrinsic]
pub const unsafe fn simd_splat<T, U>(value: U) -> T;

/// Adds two simd vectors elementwise.
///
/// `T` must be a vector of integers or floats.
Expand Down
4 changes: 3 additions & 1 deletion src/doc/rustc-dev-guide/src/tests/compiletest.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,9 @@ to link to the extern crate to make the crate be available as an extern prelude.
That allows you to specify the additional syntax of the `--extern` flag, such as
renaming a dependency. For example, `//@ aux-crate:foo=bar.rs` will compile
`auxiliary/bar.rs` and make it available under then name `foo` within the test.
This is similar to how Cargo does dependency renaming.
This is similar to how Cargo does dependency renaming. It is also possible to
specify [`--extern` modifiers](https://github.com/rust-lang/rust/issues/98405).
For example, `//@ aux-crate:noprelude:foo=bar.rs`.

`aux-bin` is similar to `aux-build` but will build a binary instead of a
library. The binary will be available in `auxiliary/bin` relative to the working
Expand Down
16 changes: 8 additions & 8 deletions src/doc/rustc-dev-guide/src/tests/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ Directives can generally be found by browsing the

See [Building auxiliary crates](compiletest.html#building-auxiliary-crates)

| Directive | Explanation | Supported test suites | Possible values |
|-----------------------|-------------------------------------------------------------------------------------------------------|----------------------------------------|-----------------------------------------------|
| `aux-bin` | Build a aux binary, made available in `auxiliary/bin` relative to test directory | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file |
| `aux-build` | Build a separate crate from the named source file | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file |
| `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make`/`run-make-cargo` | `<extern_prelude_name>=<path/to/aux/file.rs>` |
| `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file |
| `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make`/`run-make-cargo` | Path to auxiliary proc-macro `.rs` file |
| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make`/`run-make-cargo` | N/A |
| Directive | Explanation | Supported test suites | Possible values |
|-----------------------|-------------------------------------------------------------------------------------------------------|----------------------------------------|--------------------------------------------------------------------|
| `aux-bin` | Build a aux binary, made available in `auxiliary/bin` relative to test directory | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file |
| `aux-build` | Build a separate crate from the named source file | All except `run-make`/`run-make-cargo` | Path to auxiliary `.rs` file |
| `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make`/`run-make-cargo` | `[<extern_modifiers>:]<extern_prelude_name>=<path/to/aux/file.rs>` |
| `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file |
| `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make`/`run-make-cargo` | Path to auxiliary proc-macro `.rs` file |
| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make`/`run-make-cargo` | N/A |

[^pm]: please see the [Auxiliary proc-macro section](compiletest.html#auxiliary-proc-macro) in the compiletest chapter for specifics.

Expand Down
33 changes: 26 additions & 7 deletions src/tools/compiletest/src/directives/auxiliary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ use std::iter;
use super::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO};
use crate::common::Config;
use crate::directives::DirectiveLine;
use crate::util::static_regex;

#[cfg(test)]
mod tests;

/// The value of an `aux-crate` directive.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AuxCrate {
/// Contains `--extern` modifiers, if any. See the tracking issue for more
/// info: <https://github.com/rust-lang/rust/issues/98405>
/// With `aux-crate: noprelude:foo=bar.rs` this will be `noprelude`.
pub extern_modifiers: Option<String>,
/// With `aux-crate: foo=bar.rs` this will be `foo`.
/// With `aux-crate: noprelude:foo=bar.rs` this will be `noprelude:foo`.
/// With `aux-crate: noprelude:foo=bar.rs` this will be `foo`.
pub name: String,
/// With `aux-crate: foo=bar.rs` this will be `bar.rs`.
pub path: String,
Expand Down Expand Up @@ -74,9 +82,20 @@ pub(super) fn parse_and_update_aux(
}

fn parse_aux_crate(r: String) -> AuxCrate {
let mut parts = r.trim().splitn(2, '=');
AuxCrate {
name: parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
path: parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
}
let r = r.trim();

// Matches:
// name=path
// modifiers:name=path
let caps = static_regex!(r"^(?:(?<modifiers>[^=]*?):)?(?<name>[^=]*)=(?<path>.*)$")
.captures(r)
.unwrap_or_else(|| {
panic!("couldn't parse aux-crate value `{r}` (should be e.g. `log=log.rs`)")
});

let modifiers = caps.name("modifiers").map(|m| m.as_str().to_string());
let name = caps["name"].to_string();
let path = caps["path"].to_string();

AuxCrate { extern_modifiers: modifiers, name, path }
}
27 changes: 27 additions & 0 deletions src/tools/compiletest/src/directives/auxiliary/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::*;

#[test]
fn test_aux_crate_value_no_modifiers() {
assert_eq!(
AuxCrate { extern_modifiers: None, name: "foo".to_string(), path: "foo.rs".to_string() },
parse_aux_crate("foo=foo.rs".to_string())
);
}

#[test]
fn test_aux_crate_value_with_modifiers() {
assert_eq!(
AuxCrate {
extern_modifiers: Some("noprelude".to_string()),
name: "foo".to_string(),
path: "foo.rs".to_string()
},
parse_aux_crate("noprelude:foo=foo.rs".to_string())
);
}

#[test]
#[should_panic(expected = "couldn't parse aux-crate value `foo.rs` (should be e.g. `log=log.rs`)")]
fn test_aux_crate_value_invalid() {
parse_aux_crate("foo.rs".to_string());
}
33 changes: 23 additions & 10 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1277,23 +1277,36 @@ impl<'test> TestCx<'test> {
.replace('-', "_")
};

let add_extern =
|rustc: &mut Command, aux_name: &str, aux_path: &str, aux_type: AuxType| {
let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type);
if let Some(lib_name) = lib_name {
rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir, lib_name));
}
};
let add_extern = |rustc: &mut Command,
extern_modifiers: Option<&str>,
aux_name: &str,
aux_path: &str,
aux_type: AuxType| {
let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type);
if let Some(lib_name) = lib_name {
let modifiers_and_name = match extern_modifiers {
Some(modifiers) => format!("{modifiers}:{aux_name}"),
None => aux_name.to_string(),
};
rustc.arg("--extern").arg(format!("{modifiers_and_name}={aux_dir}/{lib_name}"));
}
};

for AuxCrate { name, path } in &self.props.aux.crates {
for AuxCrate { extern_modifiers, name, path } in &self.props.aux.crates {
let aux_type = self.build_auxiliary(&path, &aux_dir, None);
add_extern(rustc, name, path, aux_type);
add_extern(rustc, extern_modifiers.as_deref(), name, path, aux_type);
}

for proc_macro in &self.props.aux.proc_macros {
self.build_auxiliary(proc_macro, &aux_dir, Some(AuxType::ProcMacro));
let crate_name = path_to_crate_name(proc_macro);
add_extern(rustc, &crate_name, proc_macro, AuxType::ProcMacro);
add_extern(
rustc,
None, // `extern_modifiers`
&crate_name,
proc_macro,
AuxType::ProcMacro,
);
}

// Build any `//@ aux-codegen-backend`, and pass the resulting library
Expand Down
7 changes: 4 additions & 3 deletions tests/assembly-llvm/cstring-merging.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// MIPS assembler uses the label prefix `$anon.` for local anonymous variables
// other architectures (including ARM and x86-64) use the prefix `.Lanon.`
// Hexagon uses `.string` instead of `.asciz` for null-terminated strings
//@ only-linux
//@ assembly-output: emit-asm
//@ compile-flags: --crate-type=lib -Copt-level=3 -Cllvm-args=-enable-global-merge=0
Expand All @@ -9,21 +10,21 @@ use std::ffi::CStr;

// CHECK: .section .rodata.str1.{{[12]}},"aMS"
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "foo"
// CHECK-NEXT: .{{asciz|string}} "foo"
#[unsafe(no_mangle)]
static CSTR: &[u8; 4] = b"foo\0";

// CHECK-NOT: .section
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "bar"
// CHECK-NEXT: .{{asciz|string}} "bar"
#[unsafe(no_mangle)]
pub fn cstr() -> &'static CStr {
c"bar"
}

// CHECK-NOT: .section
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "baz"
// CHECK-NEXT: .{{asciz|string}} "baz"
#[unsafe(no_mangle)]
pub fn manual_cstr() -> &'static str {
"baz\0"
Expand Down
33 changes: 33 additions & 0 deletions tests/codegen-llvm/simd/splat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//@ compile-flags: -Copt-level=3
#![crate_type = "lib"]
#![no_std]
#![feature(repr_simd, core_intrinsics)]
use core::intrinsics::simd::simd_splat;

#[path = "../../auxiliary/minisimd.rs"]
mod minisimd;
use minisimd::*;

// Test that `simd_splat` produces the canonical LLVM splat sequence.

#[no_mangle]
unsafe fn int(x: u16) -> u16x2 {
// CHECK-LABEL: int
// CHECK: start:
// CHECK-NEXT: %0 = insertelement <2 x i16> poison, i16 %x, i64 0
// CHECK-NEXT: %1 = shufflevector <2 x i16> %0, <2 x i16> poison, <2 x i32> zeroinitializer
// CHECK-NEXT: store
// CHECK-NEXT: ret
simd_splat(x)
}

#[no_mangle]
unsafe fn float(x: f32) -> f32x4 {
// CHECK-LABEL: float
// CHECK: start:
// CHECK-NEXT: %0 = insertelement <4 x float> poison, float %x, i64 0
// CHECK-NEXT: %1 = shufflevector <4 x float> %0, <4 x float> poison, <4 x i32> zeroinitializer
// CHECK-NEXT: store
// CHECK-NEXT: ret
simd_splat(x)
}
Loading
Loading