]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
1 | use gccjit::{RValue, Type}; |
2 | use rustc_codegen_ssa::base::compare_simd_types; | |
3 | use rustc_codegen_ssa::common::{TypeKind, span_invalid_monomorphization_error}; | |
4 | use rustc_codegen_ssa::mir::operand::OperandRef; | |
5 | use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; | |
6 | use rustc_hir as hir; | |
7 | use rustc_middle::span_bug; | |
8 | use rustc_middle::ty::layout::HasTyCtxt; | |
9 | use rustc_middle::ty::{self, Ty}; | |
10 | use rustc_span::{Span, Symbol, sym}; | |
11 | ||
12 | use crate::builder::Builder; | |
13 | ||
14 | pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result<RValue<'gcc>, ()> { | |
15 | // macros for error handling: | |
16 | macro_rules! emit_error { | |
17 | ($msg: tt) => { | |
18 | emit_error!($msg, ) | |
19 | }; | |
20 | ($msg: tt, $($fmt: tt)*) => { | |
21 | span_invalid_monomorphization_error( | |
22 | bx.sess(), span, | |
23 | &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), | |
24 | name, $($fmt)*)); | |
25 | } | |
26 | } | |
27 | ||
28 | macro_rules! return_error { | |
29 | ($($fmt: tt)*) => { | |
30 | { | |
31 | emit_error!($($fmt)*); | |
32 | return Err(()); | |
33 | } | |
34 | } | |
35 | } | |
36 | ||
37 | macro_rules! require { | |
38 | ($cond: expr, $($fmt: tt)*) => { | |
39 | if !$cond { | |
40 | return_error!($($fmt)*); | |
41 | } | |
42 | }; | |
43 | } | |
44 | ||
45 | macro_rules! require_simd { | |
46 | ($ty: expr, $position: expr) => { | |
47 | require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) | |
48 | }; | |
49 | } | |
50 | ||
51 | let tcx = bx.tcx(); | |
52 | let sig = | |
53 | tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx)); | |
54 | let arg_tys = sig.inputs(); | |
55 | let name_str = &*name.as_str(); | |
56 | ||
57 | // every intrinsic below takes a SIMD vector as its first argument | |
58 | require_simd!(arg_tys[0], "input"); | |
59 | let in_ty = arg_tys[0]; | |
60 | ||
61 | let comparison = match name { | |
62 | sym::simd_eq => Some(hir::BinOpKind::Eq), | |
63 | sym::simd_ne => Some(hir::BinOpKind::Ne), | |
64 | sym::simd_lt => Some(hir::BinOpKind::Lt), | |
65 | sym::simd_le => Some(hir::BinOpKind::Le), | |
66 | sym::simd_gt => Some(hir::BinOpKind::Gt), | |
67 | sym::simd_ge => Some(hir::BinOpKind::Ge), | |
68 | _ => None, | |
69 | }; | |
70 | ||
71 | let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); | |
72 | if let Some(cmp_op) = comparison { | |
73 | require_simd!(ret_ty, "return"); | |
74 | ||
75 | let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); | |
76 | require!( | |
77 | in_len == out_len, | |
78 | "expected return type with length {} (same as input type `{}`), \ | |
79 | found `{}` with length {}", | |
80 | in_len, | |
81 | in_ty, | |
82 | ret_ty, | |
83 | out_len | |
84 | ); | |
85 | require!( | |
86 | bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, | |
87 | "expected return type with integer elements, found `{}` with non-integer `{}`", | |
88 | ret_ty, | |
89 | out_ty | |
90 | ); | |
91 | ||
92 | return Ok(compare_simd_types( | |
93 | bx, | |
94 | args[0].immediate(), | |
95 | args[1].immediate(), | |
96 | in_elem, | |
97 | llret_ty, | |
98 | cmp_op, | |
99 | )); | |
100 | } | |
101 | ||
102 | if let Some(stripped) = name_str.strip_prefix("simd_shuffle") { | |
103 | let n: u64 = stripped.parse().unwrap_or_else(|_| { | |
104 | span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") | |
105 | }); | |
106 | ||
107 | require_simd!(ret_ty, "return"); | |
108 | ||
109 | let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); | |
110 | require!( | |
111 | out_len == n, | |
112 | "expected return type of length {}, found `{}` with length {}", | |
113 | n, | |
114 | ret_ty, | |
115 | out_len | |
116 | ); | |
117 | require!( | |
118 | in_elem == out_ty, | |
119 | "expected return element type `{}` (element of input `{}`), \ | |
120 | found `{}` with element type `{}`", | |
121 | in_elem, | |
122 | in_ty, | |
123 | ret_ty, | |
124 | out_ty | |
125 | ); | |
126 | ||
127 | let vector = args[2].immediate(); | |
128 | ||
129 | return Ok(bx.shuffle_vector( | |
130 | args[0].immediate(), | |
131 | args[1].immediate(), | |
132 | vector, | |
133 | )); | |
134 | } | |
135 | ||
136 | macro_rules! arith_binary { | |
137 | ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { | |
138 | $(if name == sym::$name { | |
139 | match in_elem.kind() { | |
140 | $($(ty::$p(_))|* => { | |
141 | return Ok(bx.$call(args[0].immediate(), args[1].immediate())) | |
142 | })* | |
143 | _ => {}, | |
144 | } | |
145 | require!(false, | |
146 | "unsupported operation on `{}` with element `{}`", | |
147 | in_ty, | |
148 | in_elem) | |
149 | })* | |
150 | } | |
151 | } | |
152 | ||
153 | arith_binary! { | |
154 | simd_add: Uint, Int => add, Float => fadd; | |
155 | simd_sub: Uint, Int => sub, Float => fsub; | |
156 | simd_mul: Uint, Int => mul, Float => fmul; | |
157 | simd_div: Uint => udiv, Int => sdiv, Float => fdiv; | |
158 | simd_rem: Uint => urem, Int => srem, Float => frem; | |
159 | simd_shl: Uint, Int => shl; | |
160 | simd_shr: Uint => lshr, Int => ashr; | |
161 | simd_and: Uint, Int => and; | |
162 | simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors. | |
163 | simd_xor: Uint, Int => xor; | |
164 | } | |
165 | ||
166 | unimplemented!("simd {}", name); | |
167 | } |