]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_gcc/src/int.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_codegen_gcc / src / int.rs
CommitLineData
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
5use std::convert::TryFrom;
6
7use gccjit::{ComparisonOp, FunctionType, RValue, ToRValue, Type, UnaryOp, BinaryOp};
8use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
9use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp};
10use rustc_middle::ty::Ty;
11
12use crate::builder::ToGccComp;
13use crate::{builder::Builder, common::{SignType, TypeReflection}, context::CodegenCx};
14
15impl<'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
537impl<'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}