Skip to content

Commit aea5087

Browse files
Rollup merge of #128537 - Jamesbarford:118980-const-vector, r=RalfJung,nikic
const vector passed through to codegen This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic #118209 r​? RalfJung
2 parents c6e3385 + 27ca35a commit aea5087

File tree

7 files changed

+170
-17
lines changed

7 files changed

+170
-17
lines changed

compiler/rustc_codegen_gcc/src/common.rs

+5
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
160160
self.context.new_struct_constructor(None, struct_type.as_type(), None, values)
161161
}
162162

163+
fn const_vector(&self, values: &[RValue<'gcc>]) -> RValue<'gcc> {
164+
let typ = self.type_vector(values[0].get_type(), values.len() as u64);
165+
self.context.new_rvalue_from_vector(None, typ, values)
166+
}
167+
163168
fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option<u64> {
164169
// TODO(antoyo)
165170
None

compiler/rustc_codegen_llvm/src/common.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,6 @@ impl<'ll> CodegenCx<'ll, '_> {
9797
unsafe { llvm::LLVMConstArray2(ty, elts.as_ptr(), len) }
9898
}
9999

100-
pub fn const_vector(&self, elts: &[&'ll Value]) -> &'ll Value {
101-
let len = c_uint::try_from(elts.len()).expect("LLVMConstVector elements len overflow");
102-
unsafe { llvm::LLVMConstVector(elts.as_ptr(), len) }
103-
}
104-
105100
pub fn const_bytes(&self, bytes: &[u8]) -> &'ll Value {
106101
bytes_in_context(self.llcx, bytes)
107102
}
@@ -221,6 +216,11 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
221216
struct_in_context(self.llcx, elts, packed)
222217
}
223218

219+
fn const_vector(&self, elts: &[&'ll Value]) -> &'ll Value {
220+
let len = c_uint::try_from(elts.len()).expect("LLVMConstVector elements len overflow");
221+
unsafe { llvm::LLVMConstVector(elts.as_ptr(), len) }
222+
}
223+
224224
fn const_to_opt_uint(&self, v: &'ll Value) -> Option<u64> {
225225
try_as_const_integral(v).and_then(|v| unsafe {
226226
let mut i = 0u64;

compiler/rustc_codegen_ssa/src/mir/block.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -923,8 +923,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
923923
// third argument must be constant. This is
924924
// checked by the type-checker.
925925
if i == 2 && intrinsic.name == sym::simd_shuffle {
926+
// FIXME: the simd_shuffle argument is actually an array,
927+
// not a vector, so we need this special hack to make sure
928+
// it is passed as an immediate. We should pass the
929+
// shuffle indices as a vector instead to avoid this hack.
926930
if let mir::Operand::Constant(constant) = &arg.node {
927-
let (llval, ty) = self.simd_shuffle_indices(bx, constant);
931+
let (llval, ty) = self.immediate_const_vector(bx, constant);
928932
return OperandRef {
929933
val: Immediate(llval),
930934
layout: bx.layout_of(ty),

compiler/rustc_codegen_ssa/src/mir/constant.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_middle::mir::interpret::ErrorHandled;
22
use rustc_middle::ty::layout::HasTyCtxt;
3-
use rustc_middle::ty::{self, Ty};
3+
use rustc_middle::ty::{self, Ty, ValTree};
44
use rustc_middle::{bug, mir, span_bug};
55
use rustc_target::abi::Abi;
66

@@ -28,7 +28,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2828
.expect("erroneous constant missed by mono item collection")
2929
}
3030

31-
/// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
31+
/// This is a convenience helper for `immediate_const_vector`. It has the precondition
3232
/// that the given `constant` is an `Const::Unevaluated` and must be convertible to
3333
/// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
3434
///
@@ -59,23 +59,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
5959
self.cx.tcx().const_eval_resolve_for_typeck(ty::ParamEnv::reveal_all(), uv, constant.span)
6060
}
6161

62-
/// process constant containing SIMD shuffle indices
63-
pub fn simd_shuffle_indices(
62+
/// process constant containing SIMD shuffle indices & constant vectors
63+
pub fn immediate_const_vector(
6464
&mut self,
6565
bx: &Bx,
6666
constant: &mir::ConstOperand<'tcx>,
6767
) -> (Bx::Value, Ty<'tcx>) {
6868
let ty = self.monomorphize(constant.ty());
69+
let ty_is_simd = ty.is_simd();
70+
// FIXME: ideally we'd assert that this is a SIMD type, but simd_shuffle
71+
// in its current form relies on a regular array being passed as an
72+
// immediate argument. This hack can be removed once that is fixed.
73+
let field_ty = if ty_is_simd {
74+
ty.simd_size_and_type(bx.tcx()).1
75+
} else {
76+
ty.builtin_index().unwrap()
77+
};
78+
6979
let val = self
7080
.eval_unevaluated_mir_constant_to_valtree(constant)
7181
.ok()
7282
.map(|x| x.ok())
7383
.flatten()
7484
.map(|val| {
75-
let field_ty = ty.builtin_index().unwrap();
76-
let values: Vec<_> = val
77-
.unwrap_branch()
78-
.iter()
85+
// Depending on whether this is a SIMD type with an array field
86+
// or a type with many fields (one for each elements), the valtree
87+
// is either a single branch with N children, or a root node
88+
// with exactly one child which then in turn has many children.
89+
// So we look at the first child to determine whether it is a
90+
// leaf or whether we have to go one more layer down.
91+
let branch_or_leaf = val.unwrap_branch();
92+
let first = branch_or_leaf.get(0).unwrap();
93+
let field_iter = match first {
94+
ValTree::Branch(_) => first.unwrap_branch().iter(),
95+
ValTree::Leaf(_) => branch_or_leaf.iter(),
96+
};
97+
let values: Vec<_> = field_iter
7998
.map(|field| {
8099
if let Some(prim) = field.try_to_scalar() {
81100
let layout = bx.layout_of(field_ty);
@@ -84,11 +103,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
84103
};
85104
bx.scalar_to_backend(prim, scalar, bx.immediate_backend_type(layout))
86105
} else {
87-
bug!("simd shuffle field {:?}", field)
106+
bug!("field is not a scalar {:?}", field)
88107
}
89108
})
90109
.collect();
91-
bx.const_struct(&values, false)
110+
if ty_is_simd { bx.const_vector(&values) } else { bx.const_struct(&values, false) }
92111
})
93112
.unwrap_or_else(|| {
94113
bx.tcx().dcx().emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });

compiler/rustc_codegen_ssa/src/mir/operand.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
635635
self.codegen_consume(bx, place.as_ref())
636636
}
637637

638-
mir::Operand::Constant(ref constant) => self.eval_mir_constant_to_operand(bx, constant),
638+
mir::Operand::Constant(ref constant) => {
639+
let constant_ty = self.monomorphize(constant.ty());
640+
// Most SIMD vector constants should be passed as immediates.
641+
// (In particular, some intrinsics really rely on this.)
642+
if constant_ty.is_simd() {
643+
// However, some SIMD types do not actually use the vector ABI
644+
// (in particular, packed SIMD types do not). Ensure we exclude those.
645+
let layout = bx.layout_of(constant_ty);
646+
if let Abi::Vector { .. } = layout.abi {
647+
let (llval, ty) = self.immediate_const_vector(bx, constant);
648+
return OperandRef {
649+
val: OperandValue::Immediate(llval),
650+
layout: bx.layout_of(ty),
651+
};
652+
}
653+
}
654+
self.eval_mir_constant_to_operand(bx, constant)
655+
}
639656
}
640657
}
641658
}

compiler/rustc_codegen_ssa/src/traits/consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
3030

3131
fn const_str(&self, s: &str) -> (Self::Value, Self::Value);
3232
fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value;
33+
fn const_vector(&self, elts: &[Self::Value]) -> Self::Value;
3334

3435
fn const_to_opt_uint(&self, v: Self::Value) -> Option<u64>;
3536
fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option<u128>;

tests/codegen/const-vector.rs

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
2+
3+
// This test checks that constants of SIMD type are passed as immediate vectors.
4+
// We ensure that both vector representations (struct with fields and struct wrapping array) work.
5+
#![crate_type = "lib"]
6+
#![feature(abi_unadjusted)]
7+
#![feature(const_trait_impl)]
8+
#![feature(repr_simd)]
9+
#![feature(rustc_attrs)]
10+
#![feature(simd_ffi)]
11+
#![allow(non_camel_case_types)]
12+
13+
// Setting up structs that can be used as const vectors
14+
#[repr(simd)]
15+
#[derive(Clone)]
16+
pub struct i8x2(i8, i8);
17+
18+
#[repr(simd)]
19+
#[derive(Clone)]
20+
pub struct i8x2_arr([i8; 2]);
21+
22+
#[repr(simd)]
23+
#[derive(Clone)]
24+
pub struct f32x2(f32, f32);
25+
26+
#[repr(simd)]
27+
#[derive(Clone)]
28+
pub struct f32x2_arr([f32; 2]);
29+
30+
#[repr(simd, packed)]
31+
#[derive(Copy, Clone)]
32+
pub struct Simd<T, const N: usize>([T; N]);
33+
34+
// The following functions are required for the tests to ensure
35+
// that they are called with a const vector
36+
37+
extern "unadjusted" {
38+
#[no_mangle]
39+
fn test_i8x2(a: i8x2);
40+
}
41+
42+
extern "unadjusted" {
43+
#[no_mangle]
44+
fn test_i8x2_two_args(a: i8x2, b: i8x2);
45+
}
46+
47+
extern "unadjusted" {
48+
#[no_mangle]
49+
fn test_i8x2_mixed_args(a: i8x2, c: i32, b: i8x2);
50+
}
51+
52+
extern "unadjusted" {
53+
#[no_mangle]
54+
fn test_i8x2_arr(a: i8x2_arr);
55+
}
56+
57+
extern "unadjusted" {
58+
#[no_mangle]
59+
fn test_f32x2(a: f32x2);
60+
}
61+
62+
extern "unadjusted" {
63+
#[no_mangle]
64+
fn test_f32x2_arr(a: f32x2_arr);
65+
}
66+
67+
extern "unadjusted" {
68+
#[no_mangle]
69+
fn test_simd(a: Simd<i32, 4>);
70+
}
71+
72+
extern "unadjusted" {
73+
#[no_mangle]
74+
fn test_simd_unaligned(a: Simd<i32, 3>);
75+
}
76+
77+
// Ensure the packed variant of the simd struct does not become a const vector
78+
// if the size is not a power of 2
79+
// CHECK: %"Simd<i32, 3>" = type { [3 x i32] }
80+
81+
pub fn do_call() {
82+
unsafe {
83+
// CHECK: call void @test_i8x2(<2 x i8> <i8 32, i8 64>
84+
test_i8x2(const { i8x2(32, 64) });
85+
86+
// CHECK: call void @test_i8x2_two_args(<2 x i8> <i8 32, i8 64>, <2 x i8> <i8 8, i8 16>
87+
test_i8x2_two_args(const { i8x2(32, 64) }, const { i8x2(8, 16) });
88+
89+
// CHECK: call void @test_i8x2_mixed_args(<2 x i8> <i8 32, i8 64>, i32 43, <2 x i8> <i8 8, i8 16>
90+
test_i8x2_mixed_args(const { i8x2(32, 64) }, 43, const { i8x2(8, 16) });
91+
92+
// CHECK: call void @test_i8x2_arr(<2 x i8> <i8 32, i8 64>
93+
test_i8x2_arr(const { i8x2_arr([32, 64]) });
94+
95+
// CHECK: call void @test_f32x2(<2 x float> <float 0x3FD47AE140000000, float 0x3FE47AE140000000>
96+
test_f32x2(const { f32x2(0.32, 0.64) });
97+
98+
// CHECK: void @test_f32x2_arr(<2 x float> <float 0x3FD47AE140000000, float 0x3FE47AE140000000>
99+
test_f32x2_arr(const { f32x2_arr([0.32, 0.64]) });
100+
101+
// CHECK: call void @test_simd(<4 x i32> <i32 2, i32 4, i32 6, i32 8>
102+
test_simd(const { Simd::<i32, 4>([2, 4, 6, 8]) });
103+
104+
// CHECK: call void @test_simd_unaligned(%"Simd<i32, 3>" %1
105+
test_simd_unaligned(const { Simd::<i32, 3>([2, 4, 6]) });
106+
}
107+
}

0 commit comments

Comments
 (0)