]>
Commit | Line | Data |
---|---|---|
5e7ed085 FG |
1 | //! Module to handle integer operations. |
2 | //! This module exists because some integer types are not supported on some gcc platforms, e.g. | |
3 | //! 128-bit integers on 32-bit platforms and thus require to be handled manually. | |
4 | ||
5 | use std::convert::TryFrom; | |
6 | ||
7 | use gccjit::{ComparisonOp, FunctionType, RValue, ToRValue, Type, UnaryOp, BinaryOp}; | |
8 | use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; | |
9 | use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp}; | |
10 | use rustc_middle::ty::Ty; | |
11 | ||
12 | use crate::builder::ToGccComp; | |
13 | use crate::{builder::Builder, common::{SignType, TypeReflection}, context::CodegenCx}; | |
14 | ||
15 | impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { | |
16 | pub fn gcc_urem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
17 | // 128-bit unsigned %: __umodti3 | |
18 | self.multiplicative_operation(BinaryOp::Modulo, "mod", false, a, b) | |
19 | } | |
20 | ||
21 | pub fn gcc_srem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
22 | // 128-bit signed %: __modti3 | |
23 | self.multiplicative_operation(BinaryOp::Modulo, "mod", true, a, b) | |
24 | } | |
25 | ||
26 | pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> { | |
27 | let typ = a.get_type(); | |
28 | if self.is_native_int_type_or_bool(typ) { | |
29 | let operation = | |
30 | if typ.is_bool() { | |
31 | UnaryOp::LogicalNegate | |
32 | } | |
33 | else { | |
34 | UnaryOp::BitwiseNegate | |
35 | }; | |
36 | self.cx.context.new_unary_op(None, operation, typ, a) | |
37 | } | |
38 | else { | |
39 | // TODO(antoyo): use __negdi2 and __negti2 instead? | |
40 | let element_type = typ.dyncast_array().expect("element type"); | |
41 | let values = [ | |
42 | self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.low(a)), | |
43 | self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.high(a)), | |
44 | ]; | |
45 | self.cx.context.new_array_constructor(None, typ, &values) | |
46 | } | |
47 | } | |
48 | ||
49 | pub fn gcc_neg(&self, a: RValue<'gcc>) -> RValue<'gcc> { | |
50 | let a_type = a.get_type(); | |
51 | if self.is_native_int_type(a_type) { | |
52 | self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a) | |
53 | } | |
54 | else { | |
55 | let param_a = self.context.new_parameter(None, a_type, "a"); | |
56 | let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a], "__negti2", false); | |
57 | self.context.new_call(None, func, &[a]) | |
58 | } | |
59 | } | |
60 | ||
61 | pub fn gcc_and(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
62 | self.cx.bitwise_operation(BinaryOp::BitwiseAnd, a, b) | |
63 | } | |
64 | ||
65 | pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
66 | let a_type = a.get_type(); | |
67 | let b_type = b.get_type(); | |
68 | let a_native = self.is_native_int_type(a_type); | |
69 | let b_native = self.is_native_int_type(b_type); | |
70 | if a_native && b_native { | |
71 | // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by a signed number. | |
72 | // TODO(antoyo): cast to unsigned to do a logical shift if that does not work. | |
73 | if a_type.is_signed(self) != b_type.is_signed(self) { | |
74 | let b = self.context.new_cast(None, b, a_type); | |
75 | a >> b | |
76 | } | |
77 | else { | |
78 | a >> b | |
79 | } | |
80 | } | |
81 | else if a_native && !b_native { | |
82 | self.gcc_lshr(a, self.gcc_int_cast(b, a_type)) | |
83 | } | |
84 | else { | |
85 | // NOTE: we cannot use the lshr builtin because it's calling hi() (to get the most | |
86 | // significant half of the number) which uses lshr. | |
87 | ||
88 | let native_int_type = a_type.dyncast_array().expect("get element type"); | |
89 | ||
90 | let func = self.current_func(); | |
91 | let then_block = func.new_block("then"); | |
92 | let else_block = func.new_block("else"); | |
93 | let after_block = func.new_block("after"); | |
94 | let b0_block = func.new_block("b0"); | |
95 | let actual_else_block = func.new_block("actual_else"); | |
96 | ||
97 | let result = func.new_local(None, a_type, "shiftResult"); | |
98 | ||
99 | let sixty_four = self.gcc_int(native_int_type, 64); | |
100 | let sixty_three = self.gcc_int(native_int_type, 63); | |
101 | let zero = self.gcc_zero(native_int_type); | |
102 | let b = self.gcc_int_cast(b, native_int_type); | |
103 | let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero); | |
104 | self.llbb().end_with_conditional(None, condition, then_block, else_block); | |
105 | ||
106 | // TODO(antoyo): take endianness into account. | |
107 | let shift_value = self.gcc_sub(b, sixty_four); | |
108 | let high = self.high(a); | |
109 | let sign = | |
110 | if a_type.is_signed(self) { | |
111 | high >> sixty_three | |
112 | } | |
113 | else { | |
114 | zero | |
115 | }; | |
116 | let values = [ | |
117 | high >> shift_value, | |
118 | sign, | |
119 | ]; | |
120 | let array_value = self.context.new_array_constructor(None, a_type, &values); | |
121 | then_block.add_assignment(None, result, array_value); | |
122 | then_block.end_with_jump(None, after_block); | |
123 | ||
124 | let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero); | |
125 | else_block.end_with_conditional(None, condition, b0_block, actual_else_block); | |
126 | ||
127 | b0_block.add_assignment(None, result, a); | |
128 | b0_block.end_with_jump(None, after_block); | |
129 | ||
130 | let shift_value = self.gcc_sub(sixty_four, b); | |
131 | // NOTE: cast low to its unsigned type in order to perform a logical right shift. | |
132 | let unsigned_type = native_int_type.to_unsigned(&self.cx); | |
133 | let casted_low = self.context.new_cast(None, self.low(a), unsigned_type); | |
134 | let shifted_low = casted_low >> self.context.new_cast(None, b, unsigned_type); | |
135 | let shifted_low = self.context.new_cast(None, shifted_low, native_int_type); | |
136 | let values = [ | |
137 | (high << shift_value) | shifted_low, | |
138 | high >> b, | |
139 | ]; | |
140 | let array_value = self.context.new_array_constructor(None, a_type, &values); | |
141 | actual_else_block.add_assignment(None, result, array_value); | |
142 | actual_else_block.end_with_jump(None, after_block); | |
143 | ||
144 | // NOTE: since jumps were added in a place rustc does not expect, the current block in the | |
145 | // state need to be updated. | |
146 | self.switch_to_block(after_block); | |
147 | ||
148 | result.to_rvalue() | |
149 | } | |
150 | } | |
151 | ||
152 | fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { | |
153 | let a_type = a.get_type(); | |
154 | let b_type = b.get_type(); | |
155 | if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { | |
923072b8 FG |
156 | if a_type != b_type { |
157 | if a_type.is_vector() { | |
158 | // Vector types need to be bitcast. | |
159 | // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. | |
160 | b = self.context.new_bitcast(None, b, a.get_type()); | |
161 | } | |
162 | else { | |
163 | b = self.context.new_cast(None, b, a.get_type()); | |
164 | } | |
5e7ed085 FG |
165 | } |
166 | self.context.new_binary_op(None, operation, a_type, a, b) | |
167 | } | |
168 | else { | |
169 | let signed = a_type.is_compatible_with(self.i128_type); | |
170 | let func_name = | |
171 | match (operation, signed) { | |
172 | (BinaryOp::Plus, true) => "__rust_i128_add", | |
173 | (BinaryOp::Plus, false) => "__rust_u128_add", | |
174 | (BinaryOp::Minus, true) => "__rust_i128_sub", | |
175 | (BinaryOp::Minus, false) => "__rust_u128_sub", | |
176 | _ => unreachable!("unexpected additive operation {:?}", operation), | |
177 | }; | |
178 | let param_a = self.context.new_parameter(None, a_type, "a"); | |
179 | let param_b = self.context.new_parameter(None, b_type, "b"); | |
180 | let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false); | |
181 | self.context.new_call(None, func, &[a, b]) | |
182 | } | |
183 | } | |
184 | ||
185 | pub fn gcc_add(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
186 | self.additive_operation(BinaryOp::Plus, a, b) | |
187 | } | |
188 | ||
189 | pub fn gcc_mul(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
190 | self.multiplicative_operation(BinaryOp::Mult, "mul", true, a, b) | |
191 | } | |
192 | ||
193 | pub fn gcc_sub(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
194 | self.additive_operation(BinaryOp::Minus, a, b) | |
195 | } | |
196 | ||
197 | fn multiplicative_operation(&self, operation: BinaryOp, operation_name: &str, signed: bool, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
198 | let a_type = a.get_type(); | |
199 | let b_type = b.get_type(); | |
200 | if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { | |
201 | self.context.new_binary_op(None, operation, a_type, a, b) | |
202 | } | |
203 | else { | |
204 | let sign = | |
205 | if signed { | |
206 | "" | |
207 | } | |
208 | else { | |
209 | "u" | |
210 | }; | |
211 | let func_name = format!("__{}{}ti3", sign, operation_name); | |
212 | let param_a = self.context.new_parameter(None, a_type, "a"); | |
213 | let param_b = self.context.new_parameter(None, b_type, "b"); | |
214 | let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false); | |
215 | self.context.new_call(None, func, &[a, b]) | |
216 | } | |
217 | } | |
218 | ||
219 | pub fn gcc_sdiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
220 | // TODO(antoyo): check if the types are signed? | |
221 | // 128-bit, signed: __divti3 | |
222 | // TODO(antoyo): convert the arguments to signed? | |
223 | self.multiplicative_operation(BinaryOp::Divide, "div", true, a, b) | |
224 | } | |
225 | ||
226 | pub fn gcc_udiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
227 | // 128-bit, unsigned: __udivti3 | |
228 | self.multiplicative_operation(BinaryOp::Divide, "div", false, a, b) | |
229 | } | |
230 | ||
231 | pub fn gcc_checked_binop(&self, oop: OverflowOp, typ: Ty<'_>, lhs: <Self as BackendTypes>::Value, rhs: <Self as BackendTypes>::Value) -> (<Self as BackendTypes>::Value, <Self as BackendTypes>::Value) { | |
232 | use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*}; | |
233 | ||
234 | let new_kind = | |
235 | match typ.kind() { | |
236 | Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)), | |
237 | Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)), | |
238 | t @ (Uint(_) | Int(_)) => t.clone(), | |
239 | _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), | |
240 | }; | |
241 | ||
242 | // TODO(antoyo): remove duplication with intrinsic? | |
243 | let name = | |
244 | if self.is_native_int_type(lhs.get_type()) { | |
245 | match oop { | |
246 | OverflowOp::Add => | |
247 | match new_kind { | |
248 | Int(I8) => "__builtin_add_overflow", | |
249 | Int(I16) => "__builtin_add_overflow", | |
250 | Int(I32) => "__builtin_sadd_overflow", | |
251 | Int(I64) => "__builtin_saddll_overflow", | |
252 | Int(I128) => "__builtin_add_overflow", | |
253 | ||
254 | Uint(U8) => "__builtin_add_overflow", | |
255 | Uint(U16) => "__builtin_add_overflow", | |
256 | Uint(U32) => "__builtin_uadd_overflow", | |
257 | Uint(U64) => "__builtin_uaddll_overflow", | |
258 | Uint(U128) => "__builtin_add_overflow", | |
259 | ||
260 | _ => unreachable!(), | |
261 | }, | |
262 | OverflowOp::Sub => | |
263 | match new_kind { | |
264 | Int(I8) => "__builtin_sub_overflow", | |
265 | Int(I16) => "__builtin_sub_overflow", | |
266 | Int(I32) => "__builtin_ssub_overflow", | |
267 | Int(I64) => "__builtin_ssubll_overflow", | |
268 | Int(I128) => "__builtin_sub_overflow", | |
269 | ||
270 | Uint(U8) => "__builtin_sub_overflow", | |
271 | Uint(U16) => "__builtin_sub_overflow", | |
272 | Uint(U32) => "__builtin_usub_overflow", | |
273 | Uint(U64) => "__builtin_usubll_overflow", | |
274 | Uint(U128) => "__builtin_sub_overflow", | |
275 | ||
276 | _ => unreachable!(), | |
277 | }, | |
278 | OverflowOp::Mul => | |
279 | match new_kind { | |
280 | Int(I8) => "__builtin_mul_overflow", | |
281 | Int(I16) => "__builtin_mul_overflow", | |
282 | Int(I32) => "__builtin_smul_overflow", | |
283 | Int(I64) => "__builtin_smulll_overflow", | |
284 | Int(I128) => "__builtin_mul_overflow", | |
285 | ||
286 | Uint(U8) => "__builtin_mul_overflow", | |
287 | Uint(U16) => "__builtin_mul_overflow", | |
288 | Uint(U32) => "__builtin_umul_overflow", | |
289 | Uint(U64) => "__builtin_umulll_overflow", | |
290 | Uint(U128) => "__builtin_mul_overflow", | |
291 | ||
292 | _ => unreachable!(), | |
293 | }, | |
294 | } | |
295 | } | |
296 | else { | |
297 | match new_kind { | |
298 | Int(I128) | Uint(U128) => { | |
299 | let func_name = | |
300 | match oop { | |
301 | OverflowOp::Add => | |
302 | match new_kind { | |
303 | Int(I128) => "__rust_i128_addo", | |
304 | Uint(U128) => "__rust_u128_addo", | |
305 | _ => unreachable!(), | |
306 | }, | |
307 | OverflowOp::Sub => | |
308 | match new_kind { | |
309 | Int(I128) => "__rust_i128_subo", | |
310 | Uint(U128) => "__rust_u128_subo", | |
311 | _ => unreachable!(), | |
312 | }, | |
313 | OverflowOp::Mul => | |
314 | match new_kind { | |
315 | Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead? | |
316 | Uint(U128) => "__rust_u128_mulo", | |
317 | _ => unreachable!(), | |
318 | }, | |
319 | }; | |
320 | let a_type = lhs.get_type(); | |
321 | let b_type = rhs.get_type(); | |
322 | let param_a = self.context.new_parameter(None, a_type, "a"); | |
323 | let param_b = self.context.new_parameter(None, b_type, "b"); | |
324 | let result_field = self.context.new_field(None, a_type, "result"); | |
325 | let overflow_field = self.context.new_field(None, self.bool_type, "overflow"); | |
326 | let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]); | |
327 | let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false); | |
328 | let result = self.context.new_call(None, func, &[lhs, rhs]); | |
329 | let overflow = result.access_field(None, overflow_field); | |
330 | let int_result = result.access_field(None, result_field); | |
331 | return (int_result, overflow); | |
332 | }, | |
333 | _ => { | |
334 | match oop { | |
335 | OverflowOp::Mul => | |
336 | match new_kind { | |
337 | Int(I32) => "__mulosi4", | |
338 | Int(I64) => "__mulodi4", | |
339 | _ => unreachable!(), | |
340 | }, | |
341 | _ => unimplemented!("overflow operation for {:?}", new_kind), | |
342 | } | |
343 | } | |
344 | } | |
345 | }; | |
346 | ||
347 | let intrinsic = self.context.get_builtin_function(&name); | |
348 | let res = self.current_func() | |
349 | // TODO(antoyo): is it correct to use rhs type instead of the parameter typ? | |
350 | .new_local(None, rhs.get_type(), "binopResult") | |
351 | .get_address(None); | |
352 | let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None); | |
353 | (res.dereference(None).to_rvalue(), overflow) | |
354 | } | |
355 | ||
356 | pub fn gcc_icmp(&self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> { | |
357 | let a_type = lhs.get_type(); | |
358 | let b_type = rhs.get_type(); | |
359 | if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) { | |
360 | let signed = a_type.is_compatible_with(self.i128_type); | |
361 | let sign = | |
362 | if signed { | |
363 | "" | |
364 | } | |
365 | else { | |
366 | "u" | |
367 | }; | |
368 | let func_name = format!("__{}cmpti2", sign); | |
369 | let param_a = self.context.new_parameter(None, a_type, "a"); | |
370 | let param_b = self.context.new_parameter(None, b_type, "b"); | |
371 | let func = self.context.new_function(None, FunctionType::Extern, self.int_type, &[param_a, param_b], func_name, false); | |
372 | let cmp = self.context.new_call(None, func, &[lhs, rhs]); | |
373 | let (op, limit) = | |
374 | match op { | |
375 | IntPredicate::IntEQ => { | |
376 | return self.context.new_comparison(None, ComparisonOp::Equals, cmp, self.context.new_rvalue_one(self.int_type)); | |
377 | }, | |
378 | IntPredicate::IntNE => { | |
379 | return self.context.new_comparison(None, ComparisonOp::NotEquals, cmp, self.context.new_rvalue_one(self.int_type)); | |
380 | }, | |
381 | IntPredicate::IntUGT => (ComparisonOp::Equals, 2), | |
382 | IntPredicate::IntUGE => (ComparisonOp::GreaterThanEquals, 1), | |
383 | IntPredicate::IntULT => (ComparisonOp::Equals, 0), | |
384 | IntPredicate::IntULE => (ComparisonOp::LessThanEquals, 1), | |
385 | IntPredicate::IntSGT => (ComparisonOp::Equals, 2), | |
386 | IntPredicate::IntSGE => (ComparisonOp::GreaterThanEquals, 1), | |
387 | IntPredicate::IntSLT => (ComparisonOp::Equals, 0), | |
388 | IntPredicate::IntSLE => (ComparisonOp::LessThanEquals, 1), | |
389 | }; | |
390 | self.context.new_comparison(None, op, cmp, self.context.new_rvalue_from_int(self.int_type, limit)) | |
391 | } | |
353b0b11 FG |
392 | else if a_type.get_pointee().is_some() && b_type.get_pointee().is_some() { |
393 | // NOTE: gcc cannot compare pointers to different objects, but rustc does that, so cast them to usize. | |
394 | lhs = self.context.new_bitcast(None, lhs, self.usize_type); | |
395 | rhs = self.context.new_bitcast(None, rhs, self.usize_type); | |
396 | self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs) | |
397 | } | |
5e7ed085 | 398 | else { |
353b0b11 | 399 | if a_type != b_type { |
5e7ed085 | 400 | // NOTE: because libgccjit cannot compare function pointers. |
353b0b11 | 401 | if a_type.dyncast_function_ptr_type().is_some() && b_type.dyncast_function_ptr_type().is_some() { |
5e7ed085 FG |
402 | lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer()); |
403 | rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer()); | |
404 | } | |
405 | // NOTE: hack because we try to cast a vector type to the same vector type. | |
353b0b11 FG |
406 | else if format!("{:?}", a_type) != format!("{:?}", b_type) { |
407 | rhs = self.context.new_cast(None, rhs, a_type); | |
5e7ed085 FG |
408 | } |
409 | } | |
410 | self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs) | |
411 | } | |
412 | } | |
413 | ||
414 | pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
415 | let a_type = a.get_type(); | |
416 | let b_type = b.get_type(); | |
417 | if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { | |
418 | a ^ b | |
419 | } | |
420 | else { | |
421 | let values = [ | |
422 | self.low(a) ^ self.low(b), | |
423 | self.high(a) ^ self.high(b), | |
424 | ]; | |
425 | self.context.new_array_constructor(None, a_type, &values) | |
426 | } | |
427 | } | |
428 | ||
429 | pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
430 | let a_type = a.get_type(); | |
431 | let b_type = b.get_type(); | |
432 | let a_native = self.is_native_int_type(a_type); | |
433 | let b_native = self.is_native_int_type(b_type); | |
434 | if a_native && b_native { | |
435 | // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number. | |
436 | if a_type.is_unsigned(self) && b_type.is_signed(self) { | |
437 | let a = self.context.new_cast(None, a, b_type); | |
438 | let result = a << b; | |
439 | self.context.new_cast(None, result, a_type) | |
440 | } | |
441 | else if a_type.is_signed(self) && b_type.is_unsigned(self) { | |
442 | let b = self.context.new_cast(None, b, a_type); | |
443 | a << b | |
444 | } | |
445 | else { | |
446 | a << b | |
447 | } | |
448 | } | |
449 | else if a_native && !b_native { | |
450 | self.gcc_shl(a, self.gcc_int_cast(b, a_type)) | |
451 | } | |
452 | else { | |
453 | // NOTE: we cannot use the ashl builtin because it's calling widen_hi() which uses ashl. | |
454 | let native_int_type = a_type.dyncast_array().expect("get element type"); | |
455 | ||
456 | let func = self.current_func(); | |
457 | let then_block = func.new_block("then"); | |
458 | let else_block = func.new_block("else"); | |
459 | let after_block = func.new_block("after"); | |
460 | let b0_block = func.new_block("b0"); | |
461 | let actual_else_block = func.new_block("actual_else"); | |
462 | ||
463 | let result = func.new_local(None, a_type, "shiftResult"); | |
464 | ||
465 | let b = self.gcc_int_cast(b, native_int_type); | |
466 | let sixty_four = self.gcc_int(native_int_type, 64); | |
467 | let zero = self.gcc_zero(native_int_type); | |
468 | let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero); | |
469 | self.llbb().end_with_conditional(None, condition, then_block, else_block); | |
470 | ||
471 | // TODO(antoyo): take endianness into account. | |
472 | let values = [ | |
473 | zero, | |
474 | self.low(a) << (b - sixty_four), | |
475 | ]; | |
476 | let array_value = self.context.new_array_constructor(None, a_type, &values); | |
477 | then_block.add_assignment(None, result, array_value); | |
478 | then_block.end_with_jump(None, after_block); | |
479 | ||
480 | let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero); | |
481 | else_block.end_with_conditional(None, condition, b0_block, actual_else_block); | |
482 | ||
483 | b0_block.add_assignment(None, result, a); | |
484 | b0_block.end_with_jump(None, after_block); | |
485 | ||
486 | // NOTE: cast low to its unsigned type in order to perform a logical right shift. | |
487 | let unsigned_type = native_int_type.to_unsigned(&self.cx); | |
488 | let casted_low = self.context.new_cast(None, self.low(a), unsigned_type); | |
489 | let shift_value = self.context.new_cast(None, sixty_four - b, unsigned_type); | |
490 | let high_low = self.context.new_cast(None, casted_low >> shift_value, native_int_type); | |
491 | let values = [ | |
492 | self.low(a) << b, | |
493 | (self.high(a) << b) | high_low, | |
494 | ]; | |
495 | ||
496 | let array_value = self.context.new_array_constructor(None, a_type, &values); | |
497 | actual_else_block.add_assignment(None, result, array_value); | |
498 | actual_else_block.end_with_jump(None, after_block); | |
499 | ||
500 | // NOTE: since jumps were added in a place rustc does not expect, the current block in the | |
501 | // state need to be updated. | |
502 | self.switch_to_block(after_block); | |
503 | ||
504 | result.to_rvalue() | |
505 | } | |
506 | } | |
507 | ||
508 | pub fn gcc_bswap(&mut self, mut arg: RValue<'gcc>, width: u64) -> RValue<'gcc> { | |
509 | let arg_type = arg.get_type(); | |
510 | if !self.is_native_int_type(arg_type) { | |
511 | let native_int_type = arg_type.dyncast_array().expect("get element type"); | |
512 | let lsb = self.context.new_array_access(None, arg, self.context.new_rvalue_from_int(self.int_type, 0)).to_rvalue(); | |
513 | let swapped_lsb = self.gcc_bswap(lsb, width / 2); | |
514 | let swapped_lsb = self.context.new_cast(None, swapped_lsb, native_int_type); | |
515 | let msb = self.context.new_array_access(None, arg, self.context.new_rvalue_from_int(self.int_type, 1)).to_rvalue(); | |
516 | let swapped_msb = self.gcc_bswap(msb, width / 2); | |
517 | let swapped_msb = self.context.new_cast(None, swapped_msb, native_int_type); | |
518 | ||
519 | // NOTE: we also need to swap the two elements here, in addition to swapping inside | |
520 | // the elements themselves like done above. | |
521 | return self.context.new_array_constructor(None, arg_type, &[swapped_msb, swapped_lsb]); | |
522 | } | |
523 | ||
524 | // TODO(antoyo): check if it's faster to use string literals and a | |
525 | // match instead of format!. | |
526 | let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width)); | |
527 | // FIXME(antoyo): this cast should not be necessary. Remove | |
528 | // when having proper sized integer types. | |
529 | let param_type = bswap.get_param(0).to_rvalue().get_type(); | |
530 | if param_type != arg_type { | |
531 | arg = self.bitcast(arg, param_type); | |
532 | } | |
533 | self.cx.context.new_call(None, bswap, &[arg]) | |
534 | } | |
535 | } | |
536 | ||
537 | impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { | |
538 | pub fn gcc_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { | |
539 | if self.is_native_int_type_or_bool(typ) { | |
540 | self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from")) | |
541 | } | |
542 | else { | |
543 | // NOTE: set the sign in high. | |
544 | self.from_low_high(typ, int, -(int.is_negative() as i64)) | |
545 | } | |
546 | } | |
547 | ||
548 | pub fn gcc_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> { | |
549 | if self.is_native_int_type_or_bool(typ) { | |
550 | self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64) | |
551 | } | |
552 | else { | |
553 | self.from_low_high(typ, int as i64, 0) | |
554 | } | |
555 | } | |
556 | ||
557 | pub fn gcc_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> { | |
558 | let low = num as u64; | |
559 | let high = (num >> 64) as u64; | |
560 | if num >> 64 != 0 { | |
561 | // FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()? | |
562 | if self.is_native_int_type(typ) { | |
563 | let low = self.context.new_rvalue_from_long(self.u64_type, low as i64); | |
564 | let high = self.context.new_rvalue_from_long(typ, high as i64); | |
565 | ||
566 | let sixty_four = self.context.new_rvalue_from_long(typ, 64); | |
567 | let shift = high << sixty_four; | |
568 | shift | self.context.new_cast(None, low, typ) | |
569 | } | |
570 | else { | |
571 | self.from_low_high(typ, low as i64, high as i64) | |
572 | } | |
573 | } | |
574 | else if typ.is_i128(self) { | |
575 | let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64); | |
576 | self.gcc_int_cast(num, typ) | |
577 | } | |
578 | else { | |
579 | self.gcc_uint(typ, num as u64) | |
580 | } | |
581 | } | |
582 | ||
583 | pub fn gcc_zero(&self, typ: Type<'gcc>) -> RValue<'gcc> { | |
584 | if self.is_native_int_type_or_bool(typ) { | |
585 | self.context.new_rvalue_zero(typ) | |
586 | } | |
587 | else { | |
588 | self.from_low_high(typ, 0, 0) | |
589 | } | |
590 | } | |
591 | ||
592 | pub fn gcc_int_width(&self, typ: Type<'gcc>) -> u64 { | |
593 | if self.is_native_int_type_or_bool(typ) { | |
594 | typ.get_size() as u64 * 8 | |
595 | } | |
596 | else { | |
597 | // NOTE: the only unsupported types are u128 and i128. | |
598 | 128 | |
599 | } | |
600 | } | |
601 | ||
602 | fn bitwise_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { | |
603 | let a_type = a.get_type(); | |
604 | let b_type = b.get_type(); | |
605 | let a_native = self.is_native_int_type_or_bool(a_type); | |
606 | let b_native = self.is_native_int_type_or_bool(b_type); | |
923072b8 FG |
607 | if a_type.is_vector() && b_type.is_vector() { |
608 | self.context.new_binary_op(None, operation, a_type, a, b) | |
609 | } | |
610 | else if a_native && b_native { | |
5e7ed085 FG |
611 | if a_type != b_type { |
612 | b = self.context.new_cast(None, b, a_type); | |
613 | } | |
614 | self.context.new_binary_op(None, operation, a_type, a, b) | |
615 | } | |
616 | else { | |
617 | assert!(!a_native && !b_native, "both types should either be native or non-native for or operation"); | |
618 | let native_int_type = a_type.dyncast_array().expect("get element type"); | |
619 | let values = [ | |
620 | self.context.new_binary_op(None, operation, native_int_type, self.low(a), self.low(b)), | |
621 | self.context.new_binary_op(None, operation, native_int_type, self.high(a), self.high(b)), | |
622 | ]; | |
623 | self.context.new_array_constructor(None, a_type, &values) | |
624 | } | |
625 | } | |
626 | ||
627 | pub fn gcc_or(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { | |
628 | self.bitwise_operation(BinaryOp::BitwiseOr, a, b) | |
629 | } | |
630 | ||
631 | // TODO(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/master/src/int/mod.rs#L379 instead? | |
632 | pub fn gcc_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
633 | let value_type = value.get_type(); | |
634 | if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) { | |
635 | self.context.new_cast(None, value, dest_typ) | |
636 | } | |
637 | else if self.is_native_int_type_or_bool(dest_typ) { | |
638 | self.context.new_cast(None, self.low(value), dest_typ) | |
639 | } | |
640 | else if self.is_native_int_type_or_bool(value_type) { | |
641 | let dest_element_type = dest_typ.dyncast_array().expect("get element type"); | |
642 | ||
643 | // NOTE: set the sign of the value. | |
644 | let zero = self.context.new_rvalue_zero(value_type); | |
645 | let is_negative = self.context.new_comparison(None, ComparisonOp::LessThan, value, zero); | |
646 | let is_negative = self.gcc_int_cast(is_negative, dest_element_type); | |
647 | let values = [ | |
648 | self.context.new_cast(None, value, dest_element_type), | |
649 | self.context.new_unary_op(None, UnaryOp::Minus, dest_element_type, is_negative), | |
650 | ]; | |
651 | self.context.new_array_constructor(None, dest_typ, &values) | |
652 | } | |
653 | else { | |
654 | // Since u128 and i128 are the only types that can be unsupported, we know the type of | |
655 | // value and the destination type have the same size, so a bitcast is fine. | |
923072b8 FG |
656 | |
657 | // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. | |
5e7ed085 FG |
658 | self.context.new_bitcast(None, value, dest_typ) |
659 | } | |
660 | } | |
661 | ||
662 | fn int_to_float_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
663 | let value_type = value.get_type(); | |
664 | if self.is_native_int_type_or_bool(value_type) { | |
665 | return self.context.new_cast(None, value, dest_typ); | |
666 | } | |
667 | ||
668 | let name_suffix = | |
669 | match self.type_kind(dest_typ) { | |
670 | TypeKind::Float => "tisf", | |
671 | TypeKind::Double => "tidf", | |
672 | kind => panic!("cannot cast a non-native integer to type {:?}", kind), | |
673 | }; | |
674 | let sign = | |
675 | if signed { | |
676 | "" | |
677 | } | |
678 | else { | |
679 | "un" | |
680 | }; | |
681 | let func_name = format!("__float{}{}", sign, name_suffix); | |
682 | let param = self.context.new_parameter(None, value_type, "n"); | |
683 | let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false); | |
684 | self.context.new_call(None, func, &[value]) | |
685 | } | |
686 | ||
687 | pub fn gcc_int_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
688 | self.int_to_float_cast(true, value, dest_typ) | |
689 | } | |
690 | ||
691 | pub fn gcc_uint_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
692 | self.int_to_float_cast(false, value, dest_typ) | |
693 | } | |
694 | ||
695 | fn float_to_int_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
696 | let value_type = value.get_type(); | |
697 | if self.is_native_int_type_or_bool(dest_typ) { | |
698 | return self.context.new_cast(None, value, dest_typ); | |
699 | } | |
700 | ||
701 | let name_suffix = | |
702 | match self.type_kind(value_type) { | |
703 | TypeKind::Float => "sfti", | |
704 | TypeKind::Double => "dfti", | |
705 | kind => panic!("cannot cast a {:?} to non-native integer", kind), | |
706 | }; | |
707 | let sign = | |
708 | if signed { | |
709 | "" | |
710 | } | |
711 | else { | |
712 | "uns" | |
713 | }; | |
714 | let func_name = format!("__fix{}{}", sign, name_suffix); | |
715 | let param = self.context.new_parameter(None, value_type, "n"); | |
716 | let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false); | |
717 | self.context.new_call(None, func, &[value]) | |
718 | } | |
719 | ||
720 | pub fn gcc_float_to_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
721 | self.float_to_int_cast(true, value, dest_typ) | |
722 | } | |
723 | ||
724 | pub fn gcc_float_to_uint_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { | |
725 | self.float_to_int_cast(false, value, dest_typ) | |
726 | } | |
727 | ||
728 | fn high(&self, value: RValue<'gcc>) -> RValue<'gcc> { | |
729 | self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, 1)) | |
730 | .to_rvalue() | |
731 | } | |
732 | ||
733 | fn low(&self, value: RValue<'gcc>) -> RValue<'gcc> { | |
734 | self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, 0)) | |
735 | .to_rvalue() | |
736 | } | |
737 | ||
738 | fn from_low_high(&self, typ: Type<'gcc>, low: i64, high: i64) -> RValue<'gcc> { | |
739 | let native_int_type = typ.dyncast_array().expect("get element type"); | |
740 | let values = [ | |
741 | self.context.new_rvalue_from_long(native_int_type, low), | |
742 | self.context.new_rvalue_from_long(native_int_type, high), | |
743 | ]; | |
744 | self.context.new_array_constructor(None, typ, &values) | |
745 | } | |
746 | } |