2022-03-26 12:29:37 -05:00
|
|
|
//! Module to handle integer operations.
|
|
|
|
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
|
|
|
|
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
|
|
|
|
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
|
|
use gccjit::{ComparisonOp, FunctionType, RValue, ToRValue, Type, UnaryOp, BinaryOp};
|
|
|
|
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
|
|
|
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp};
|
2023-09-06 18:01:04 -05:00
|
|
|
use rustc_middle::ty::{ParamEnv, Ty};
|
|
|
|
use rustc_target::abi::{Endian, call::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}};
|
|
|
|
use rustc_target::spec;
|
2022-03-26 12:29:37 -05:00
|
|
|
|
|
|
|
use crate::builder::ToGccComp;
|
|
|
|
use crate::{builder::Builder, common::{SignType, TypeReflection}, context::CodegenCx};
|
|
|
|
|
|
|
|
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|
|
|
pub fn gcc_urem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
// 128-bit unsigned %: __umodti3
|
|
|
|
self.multiplicative_operation(BinaryOp::Modulo, "mod", false, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_srem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
// 128-bit signed %: __modti3
|
|
|
|
self.multiplicative_operation(BinaryOp::Modulo, "mod", true, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let typ = a.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(typ) {
|
|
|
|
let operation =
|
|
|
|
if typ.is_bool() {
|
|
|
|
UnaryOp::LogicalNegate
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
UnaryOp::BitwiseNegate
|
|
|
|
};
|
|
|
|
self.cx.context.new_unary_op(None, operation, typ, a)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let element_type = typ.dyncast_array().expect("element type");
|
2023-09-06 18:01:04 -05:00
|
|
|
self.from_low_high_rvalues(typ,
|
2022-03-26 12:29:37 -05:00
|
|
|
self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.low(a)),
|
|
|
|
self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.high(a)),
|
2023-09-06 18:01:04 -05:00
|
|
|
)
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_neg(&self, a: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
if self.is_native_int_type(a_type) {
|
|
|
|
self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
|
|
|
|
}
|
|
|
|
else {
|
2023-10-07 14:15:02 -05:00
|
|
|
self.gcc_add(self.gcc_not(a), self.gcc_int(a_type, 1))
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_and(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.cx.bitwise_operation(BinaryOp::BitwiseAnd, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
let a_native = self.is_native_int_type(a_type);
|
|
|
|
let b_native = self.is_native_int_type(b_type);
|
|
|
|
if a_native && b_native {
|
|
|
|
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by a signed number.
|
|
|
|
// TODO(antoyo): cast to unsigned to do a logical shift if that does not work.
|
|
|
|
if a_type.is_signed(self) != b_type.is_signed(self) {
|
|
|
|
let b = self.context.new_cast(None, b, a_type);
|
|
|
|
a >> b
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
a >> b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if a_native && !b_native {
|
|
|
|
self.gcc_lshr(a, self.gcc_int_cast(b, a_type))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// NOTE: we cannot use the lshr builtin because it's calling hi() (to get the most
|
|
|
|
// significant half of the number) which uses lshr.
|
|
|
|
|
|
|
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
|
|
|
|
|
|
|
let func = self.current_func();
|
|
|
|
let then_block = func.new_block("then");
|
|
|
|
let else_block = func.new_block("else");
|
|
|
|
let after_block = func.new_block("after");
|
|
|
|
let b0_block = func.new_block("b0");
|
|
|
|
let actual_else_block = func.new_block("actual_else");
|
|
|
|
|
|
|
|
let result = func.new_local(None, a_type, "shiftResult");
|
|
|
|
|
|
|
|
let sixty_four = self.gcc_int(native_int_type, 64);
|
|
|
|
let sixty_three = self.gcc_int(native_int_type, 63);
|
|
|
|
let zero = self.gcc_zero(native_int_type);
|
|
|
|
let b = self.gcc_int_cast(b, native_int_type);
|
|
|
|
let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero);
|
|
|
|
self.llbb().end_with_conditional(None, condition, then_block, else_block);
|
|
|
|
|
|
|
|
let shift_value = self.gcc_sub(b, sixty_four);
|
|
|
|
let high = self.high(a);
|
|
|
|
let sign =
|
|
|
|
if a_type.is_signed(self) {
|
|
|
|
high >> sixty_three
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
zero
|
|
|
|
};
|
2023-09-06 18:01:04 -05:00
|
|
|
let array_value = self.from_low_high_rvalues(a_type, high >> shift_value, sign);
|
2022-03-26 12:29:37 -05:00
|
|
|
then_block.add_assignment(None, result, array_value);
|
|
|
|
then_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
|
|
|
|
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
|
|
|
|
|
|
|
|
b0_block.add_assignment(None, result, a);
|
|
|
|
b0_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
let shift_value = self.gcc_sub(sixty_four, b);
|
|
|
|
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
|
|
|
|
let unsigned_type = native_int_type.to_unsigned(&self.cx);
|
|
|
|
let casted_low = self.context.new_cast(None, self.low(a), unsigned_type);
|
|
|
|
let shifted_low = casted_low >> self.context.new_cast(None, b, unsigned_type);
|
|
|
|
let shifted_low = self.context.new_cast(None, shifted_low, native_int_type);
|
2023-09-06 18:01:04 -05:00
|
|
|
let array_value = self.from_low_high_rvalues(a_type,
|
2022-03-26 12:29:37 -05:00
|
|
|
(high << shift_value) | shifted_low,
|
|
|
|
high >> b,
|
2023-09-06 18:01:04 -05:00
|
|
|
);
|
2022-03-26 12:29:37 -05:00
|
|
|
actual_else_block.add_assignment(None, result, array_value);
|
|
|
|
actual_else_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
|
|
|
// state need to be updated.
|
|
|
|
self.switch_to_block(after_block);
|
|
|
|
|
|
|
|
result.to_rvalue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
2022-06-06 21:04:37 -05:00
|
|
|
if a_type != b_type {
|
|
|
|
if a_type.is_vector() {
|
|
|
|
// Vector types need to be bitcast.
|
|
|
|
// TODO(antoyo): perhaps use __builtin_convertvector for vector casting.
|
|
|
|
b = self.context.new_bitcast(None, b, a.get_type());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
b = self.context.new_cast(None, b, a.get_type());
|
|
|
|
}
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let signed = a_type.is_compatible_with(self.i128_type);
|
|
|
|
let func_name =
|
|
|
|
match (operation, signed) {
|
|
|
|
(BinaryOp::Plus, true) => "__rust_i128_add",
|
|
|
|
(BinaryOp::Plus, false) => "__rust_u128_add",
|
|
|
|
(BinaryOp::Minus, true) => "__rust_i128_sub",
|
|
|
|
(BinaryOp::Minus, false) => "__rust_u128_sub",
|
|
|
|
_ => unreachable!("unexpected additive operation {:?}", operation),
|
|
|
|
};
|
|
|
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
|
|
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false);
|
|
|
|
self.context.new_call(None, func, &[a, b])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_add(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.additive_operation(BinaryOp::Plus, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_mul(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.multiplicative_operation(BinaryOp::Mult, "mul", true, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_sub(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.additive_operation(BinaryOp::Minus, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn multiplicative_operation(&self, operation: BinaryOp, operation_name: &str, signed: bool, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
|
|
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let sign =
|
|
|
|
if signed {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
"u"
|
|
|
|
};
|
|
|
|
let func_name = format!("__{}{}ti3", sign, operation_name);
|
|
|
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
|
|
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false);
|
|
|
|
self.context.new_call(None, func, &[a, b])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_sdiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
// TODO(antoyo): check if the types are signed?
|
|
|
|
// 128-bit, signed: __divti3
|
|
|
|
// TODO(antoyo): convert the arguments to signed?
|
|
|
|
self.multiplicative_operation(BinaryOp::Divide, "div", true, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_udiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
// 128-bit, unsigned: __udivti3
|
|
|
|
self.multiplicative_operation(BinaryOp::Divide, "div", false, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
|
|
|
|
|
|
|
|
let new_kind =
|
|
|
|
match typ.kind() {
|
|
|
|
Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
|
|
|
|
Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
|
|
|
|
t @ (Uint(_) | Int(_)) => t.clone(),
|
|
|
|
_ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO(antoyo): remove duplication with intrinsic?
|
|
|
|
let name =
|
|
|
|
if self.is_native_int_type(lhs.get_type()) {
|
|
|
|
match oop {
|
|
|
|
OverflowOp::Add =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I8) => "__builtin_add_overflow",
|
|
|
|
Int(I16) => "__builtin_add_overflow",
|
|
|
|
Int(I32) => "__builtin_sadd_overflow",
|
|
|
|
Int(I64) => "__builtin_saddll_overflow",
|
|
|
|
Int(I128) => "__builtin_add_overflow",
|
|
|
|
|
|
|
|
Uint(U8) => "__builtin_add_overflow",
|
|
|
|
Uint(U16) => "__builtin_add_overflow",
|
|
|
|
Uint(U32) => "__builtin_uadd_overflow",
|
|
|
|
Uint(U64) => "__builtin_uaddll_overflow",
|
|
|
|
Uint(U128) => "__builtin_add_overflow",
|
|
|
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
OverflowOp::Sub =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I8) => "__builtin_sub_overflow",
|
|
|
|
Int(I16) => "__builtin_sub_overflow",
|
|
|
|
Int(I32) => "__builtin_ssub_overflow",
|
|
|
|
Int(I64) => "__builtin_ssubll_overflow",
|
|
|
|
Int(I128) => "__builtin_sub_overflow",
|
|
|
|
|
|
|
|
Uint(U8) => "__builtin_sub_overflow",
|
|
|
|
Uint(U16) => "__builtin_sub_overflow",
|
|
|
|
Uint(U32) => "__builtin_usub_overflow",
|
|
|
|
Uint(U64) => "__builtin_usubll_overflow",
|
|
|
|
Uint(U128) => "__builtin_sub_overflow",
|
|
|
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
OverflowOp::Mul =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I8) => "__builtin_mul_overflow",
|
|
|
|
Int(I16) => "__builtin_mul_overflow",
|
|
|
|
Int(I32) => "__builtin_smul_overflow",
|
|
|
|
Int(I64) => "__builtin_smulll_overflow",
|
|
|
|
Int(I128) => "__builtin_mul_overflow",
|
|
|
|
|
|
|
|
Uint(U8) => "__builtin_mul_overflow",
|
|
|
|
Uint(U16) => "__builtin_mul_overflow",
|
|
|
|
Uint(U32) => "__builtin_umul_overflow",
|
|
|
|
Uint(U64) => "__builtin_umulll_overflow",
|
|
|
|
Uint(U128) => "__builtin_mul_overflow",
|
|
|
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
match new_kind {
|
|
|
|
Int(I128) | Uint(U128) => {
|
|
|
|
let func_name =
|
|
|
|
match oop {
|
|
|
|
OverflowOp::Add =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I128) => "__rust_i128_addo",
|
|
|
|
Uint(U128) => "__rust_u128_addo",
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
OverflowOp::Sub =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I128) => "__rust_i128_subo",
|
|
|
|
Uint(U128) => "__rust_u128_subo",
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
OverflowOp::Mul =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead?
|
|
|
|
Uint(U128) => "__rust_u128_mulo",
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
};
|
2023-09-06 18:01:04 -05:00
|
|
|
return self.operation_with_overflow(func_name, lhs, rhs);
|
2022-03-26 12:29:37 -05:00
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
match oop {
|
|
|
|
OverflowOp::Mul =>
|
|
|
|
match new_kind {
|
|
|
|
Int(I32) => "__mulosi4",
|
|
|
|
Int(I64) => "__mulodi4",
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
_ => unimplemented!("overflow operation for {:?}", new_kind),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let intrinsic = self.context.get_builtin_function(&name);
|
|
|
|
let res = self.current_func()
|
|
|
|
// TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
|
|
|
|
.new_local(None, rhs.get_type(), "binopResult")
|
|
|
|
.get_address(None);
|
|
|
|
let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
|
|
|
|
(res.dereference(None).to_rvalue(), overflow)
|
|
|
|
}
|
|
|
|
|
2023-09-06 18:01:04 -05:00
|
|
|
pub fn operation_with_overflow(&self, func_name: &str, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) {
|
|
|
|
let a_type = lhs.get_type();
|
|
|
|
let b_type = rhs.get_type();
|
|
|
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
|
|
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
|
|
|
let result_field = self.context.new_field(None, a_type, "result");
|
|
|
|
let overflow_field = self.context.new_field(None, self.bool_type, "overflow");
|
|
|
|
|
|
|
|
let ret_ty = Ty::new_tup(self.tcx, &[self.tcx.types.i128, self.tcx.types.bool]);
|
|
|
|
let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ret_ty)).unwrap();
|
|
|
|
|
|
|
|
let arg_abi = ArgAbi {
|
|
|
|
layout,
|
|
|
|
mode: PassMode::Direct(ArgAttributes::new()),
|
|
|
|
};
|
|
|
|
let mut fn_abi = FnAbi {
|
|
|
|
args: vec![arg_abi.clone(), arg_abi.clone()].into_boxed_slice(),
|
|
|
|
ret: arg_abi,
|
|
|
|
c_variadic: false,
|
|
|
|
fixed_count: 2,
|
|
|
|
conv: Conv::C,
|
|
|
|
can_unwind: false,
|
|
|
|
};
|
|
|
|
fn_abi.adjust_for_foreign_abi(self.cx, spec::abi::Abi::C {
|
|
|
|
unwind: false,
|
|
|
|
}).unwrap();
|
|
|
|
|
|
|
|
let indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
|
|
|
|
|
|
|
|
let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]);
|
|
|
|
let result =
|
|
|
|
if indirect {
|
|
|
|
let return_value = self.current_func().new_local(None, return_type.as_type(), "return_value");
|
|
|
|
let return_param_type = return_type.as_type().make_pointer();
|
|
|
|
let return_param = self.context.new_parameter(None, return_param_type, "return_value");
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, self.type_void(), &[return_param, param_a, param_b], func_name, false);
|
|
|
|
self.llbb().add_eval(None, self.context.new_call(None, func, &[return_value.get_address(None), lhs, rhs]));
|
|
|
|
return_value.to_rvalue()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
|
|
|
|
self.context.new_call(None, func, &[lhs, rhs])
|
|
|
|
};
|
|
|
|
let overflow = result.access_field(None, overflow_field);
|
|
|
|
let int_result = result.access_field(None, result_field);
|
|
|
|
return (int_result, overflow);
|
|
|
|
}
|
|
|
|
|
2023-10-07 14:14:54 -05:00
|
|
|
pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
|
2022-03-26 12:29:37 -05:00
|
|
|
let a_type = lhs.get_type();
|
|
|
|
let b_type = rhs.get_type();
|
|
|
|
if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) {
|
2023-10-07 14:14:54 -05:00
|
|
|
// This algorithm is based on compiler-rt's __cmpti2:
|
|
|
|
// https://github.com/llvm-mirror/compiler-rt/blob/f0745e8476f069296a7c71accedd061dce4cdf79/lib/builtins/cmpti2.c#L21
|
|
|
|
let result = self.current_func().new_local(None, self.int_type, "icmp_result");
|
|
|
|
let block1 = self.current_func().new_block("block1");
|
|
|
|
let block2 = self.current_func().new_block("block2");
|
|
|
|
let block3 = self.current_func().new_block("block3");
|
|
|
|
let block4 = self.current_func().new_block("block4");
|
|
|
|
let block5 = self.current_func().new_block("block5");
|
|
|
|
let block6 = self.current_func().new_block("block6");
|
|
|
|
let block7 = self.current_func().new_block("block7");
|
|
|
|
let block8 = self.current_func().new_block("block8");
|
|
|
|
let after = self.current_func().new_block("after");
|
|
|
|
|
|
|
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
|
|
|
// NOTE: cast low to its unsigned type in order to perform a comparison correctly (e.g.
|
|
|
|
// the sign is only on high).
|
|
|
|
let unsigned_type = native_int_type.to_unsigned(&self.cx);
|
|
|
|
|
|
|
|
let lhs_low = self.context.new_cast(None, self.low(lhs), unsigned_type);
|
|
|
|
let rhs_low = self.context.new_cast(None, self.low(rhs), unsigned_type);
|
|
|
|
|
|
|
|
let condition = self.context.new_comparison(None, ComparisonOp::LessThan, self.high(lhs), self.high(rhs));
|
|
|
|
self.llbb().end_with_conditional(None, condition, block1, block2);
|
|
|
|
|
|
|
|
block1.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
|
|
|
|
block1.end_with_jump(None, after);
|
|
|
|
|
|
|
|
let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, self.high(lhs), self.high(rhs));
|
|
|
|
block2.end_with_conditional(None, condition, block3, block4);
|
|
|
|
|
|
|
|
block3.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
|
|
|
|
block3.end_with_jump(None, after);
|
|
|
|
|
|
|
|
let condition = self.context.new_comparison(None, ComparisonOp::LessThan, lhs_low, rhs_low);
|
|
|
|
block4.end_with_conditional(None, condition, block5, block6);
|
|
|
|
|
|
|
|
block5.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
|
|
|
|
block5.end_with_jump(None, after);
|
|
|
|
|
|
|
|
let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, lhs_low, rhs_low);
|
|
|
|
block6.end_with_conditional(None, condition, block7, block8);
|
|
|
|
|
|
|
|
block7.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
|
|
|
|
block7.end_with_jump(None, after);
|
|
|
|
|
|
|
|
block8.add_assignment(None, result, self.context.new_rvalue_one(self.int_type));
|
|
|
|
block8.end_with_jump(None, after);
|
|
|
|
|
|
|
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
|
|
|
// state need to be updated.
|
|
|
|
self.switch_to_block(after);
|
|
|
|
|
|
|
|
let cmp = result.to_rvalue();
|
2022-03-26 12:29:37 -05:00
|
|
|
let (op, limit) =
|
|
|
|
match op {
|
|
|
|
IntPredicate::IntEQ => {
|
|
|
|
return self.context.new_comparison(None, ComparisonOp::Equals, cmp, self.context.new_rvalue_one(self.int_type));
|
|
|
|
},
|
|
|
|
IntPredicate::IntNE => {
|
|
|
|
return self.context.new_comparison(None, ComparisonOp::NotEquals, cmp, self.context.new_rvalue_one(self.int_type));
|
|
|
|
},
|
2023-10-12 16:06:29 -05:00
|
|
|
// TODO(antoyo): cast to u128 for unsigned comparison. See below.
|
2022-03-26 12:29:37 -05:00
|
|
|
IntPredicate::IntUGT => (ComparisonOp::Equals, 2),
|
|
|
|
IntPredicate::IntUGE => (ComparisonOp::GreaterThanEquals, 1),
|
|
|
|
IntPredicate::IntULT => (ComparisonOp::Equals, 0),
|
|
|
|
IntPredicate::IntULE => (ComparisonOp::LessThanEquals, 1),
|
|
|
|
IntPredicate::IntSGT => (ComparisonOp::Equals, 2),
|
|
|
|
IntPredicate::IntSGE => (ComparisonOp::GreaterThanEquals, 1),
|
|
|
|
IntPredicate::IntSLT => (ComparisonOp::Equals, 0),
|
|
|
|
IntPredicate::IntSLE => (ComparisonOp::LessThanEquals, 1),
|
|
|
|
};
|
|
|
|
self.context.new_comparison(None, op, cmp, self.context.new_rvalue_from_int(self.int_type, limit))
|
|
|
|
}
|
2023-03-05 11:03:19 -06:00
|
|
|
else if a_type.get_pointee().is_some() && b_type.get_pointee().is_some() {
|
|
|
|
// NOTE: gcc cannot compare pointers to different objects, but rustc does that, so cast them to usize.
|
|
|
|
lhs = self.context.new_bitcast(None, lhs, self.usize_type);
|
|
|
|
rhs = self.context.new_bitcast(None, rhs, self.usize_type);
|
|
|
|
self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
|
|
|
|
}
|
2022-03-26 12:29:37 -05:00
|
|
|
else {
|
2023-03-05 11:03:19 -06:00
|
|
|
if a_type != b_type {
|
2022-03-26 12:29:37 -05:00
|
|
|
// NOTE: because libgccjit cannot compare function pointers.
|
2023-03-05 11:03:19 -06:00
|
|
|
if a_type.dyncast_function_ptr_type().is_some() && b_type.dyncast_function_ptr_type().is_some() {
|
2022-03-26 12:29:37 -05:00
|
|
|
lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
|
|
|
|
rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
|
|
|
|
}
|
|
|
|
// NOTE: hack because we try to cast a vector type to the same vector type.
|
2023-03-05 11:03:19 -06:00
|
|
|
else if format!("{:?}", a_type) != format!("{:?}", b_type) {
|
|
|
|
rhs = self.context.new_cast(None, rhs, a_type);
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
}
|
2023-10-12 16:06:29 -05:00
|
|
|
match op {
|
|
|
|
IntPredicate::IntUGT | IntPredicate::IntUGE | IntPredicate::IntULT | IntPredicate::IntULE => {
|
|
|
|
if !a_type.is_vector() {
|
|
|
|
let unsigned_type = a_type.to_unsigned(&self.cx);
|
|
|
|
lhs = self.context.new_cast(None, lhs, unsigned_type);
|
|
|
|
rhs = self.context.new_cast(None, rhs, unsigned_type);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// TODO(antoyo): we probably need to handle signed comparison for unsigned
|
|
|
|
// integers.
|
|
|
|
_ => (),
|
|
|
|
}
|
2022-03-26 12:29:37 -05:00
|
|
|
self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
|
|
|
a ^ b
|
|
|
|
}
|
|
|
|
else {
|
2023-09-06 18:01:04 -05:00
|
|
|
self.from_low_high_rvalues(a_type,
|
2022-03-26 12:29:37 -05:00
|
|
|
self.low(a) ^ self.low(b),
|
|
|
|
self.high(a) ^ self.high(b),
|
2023-09-06 18:01:04 -05:00
|
|
|
)
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
let a_native = self.is_native_int_type(a_type);
|
|
|
|
let b_native = self.is_native_int_type(b_type);
|
|
|
|
if a_native && b_native {
|
|
|
|
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
|
|
|
|
if a_type.is_unsigned(self) && b_type.is_signed(self) {
|
|
|
|
let a = self.context.new_cast(None, a, b_type);
|
|
|
|
let result = a << b;
|
|
|
|
self.context.new_cast(None, result, a_type)
|
|
|
|
}
|
|
|
|
else if a_type.is_signed(self) && b_type.is_unsigned(self) {
|
|
|
|
let b = self.context.new_cast(None, b, a_type);
|
|
|
|
a << b
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
a << b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if a_native && !b_native {
|
|
|
|
self.gcc_shl(a, self.gcc_int_cast(b, a_type))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// NOTE: we cannot use the ashl builtin because it's calling widen_hi() which uses ashl.
|
|
|
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
|
|
|
|
|
|
|
let func = self.current_func();
|
|
|
|
let then_block = func.new_block("then");
|
|
|
|
let else_block = func.new_block("else");
|
|
|
|
let after_block = func.new_block("after");
|
|
|
|
let b0_block = func.new_block("b0");
|
|
|
|
let actual_else_block = func.new_block("actual_else");
|
|
|
|
|
|
|
|
let result = func.new_local(None, a_type, "shiftResult");
|
|
|
|
|
|
|
|
let b = self.gcc_int_cast(b, native_int_type);
|
|
|
|
let sixty_four = self.gcc_int(native_int_type, 64);
|
|
|
|
let zero = self.gcc_zero(native_int_type);
|
|
|
|
let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero);
|
|
|
|
self.llbb().end_with_conditional(None, condition, then_block, else_block);
|
|
|
|
|
2023-09-06 18:01:04 -05:00
|
|
|
let array_value = self.from_low_high_rvalues(a_type,
|
2022-03-26 12:29:37 -05:00
|
|
|
zero,
|
|
|
|
self.low(a) << (b - sixty_four),
|
2023-09-06 18:01:04 -05:00
|
|
|
);
|
2022-03-26 12:29:37 -05:00
|
|
|
then_block.add_assignment(None, result, array_value);
|
|
|
|
then_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
|
|
|
|
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
|
|
|
|
|
|
|
|
b0_block.add_assignment(None, result, a);
|
|
|
|
b0_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
|
2023-09-06 18:01:04 -05:00
|
|
|
// TODO(antoyo): adjust this ^ comment.
|
2022-03-26 12:29:37 -05:00
|
|
|
let unsigned_type = native_int_type.to_unsigned(&self.cx);
|
|
|
|
let casted_low = self.context.new_cast(None, self.low(a), unsigned_type);
|
|
|
|
let shift_value = self.context.new_cast(None, sixty_four - b, unsigned_type);
|
|
|
|
let high_low = self.context.new_cast(None, casted_low >> shift_value, native_int_type);
|
2023-09-06 18:01:04 -05:00
|
|
|
|
|
|
|
let array_value = self.from_low_high_rvalues(a_type,
|
2022-03-26 12:29:37 -05:00
|
|
|
self.low(a) << b,
|
|
|
|
(self.high(a) << b) | high_low,
|
2023-09-06 18:01:04 -05:00
|
|
|
);
|
2022-03-26 12:29:37 -05:00
|
|
|
actual_else_block.add_assignment(None, result, array_value);
|
|
|
|
actual_else_block.end_with_jump(None, after_block);
|
|
|
|
|
|
|
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
|
|
|
// state need to be updated.
|
|
|
|
self.switch_to_block(after_block);
|
|
|
|
|
|
|
|
result.to_rvalue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_bswap(&mut self, mut arg: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
|
|
|
let arg_type = arg.get_type();
|
|
|
|
if !self.is_native_int_type(arg_type) {
|
|
|
|
let native_int_type = arg_type.dyncast_array().expect("get element type");
|
2023-09-06 18:01:04 -05:00
|
|
|
let lsb = self.low(arg);
|
2022-03-26 12:29:37 -05:00
|
|
|
let swapped_lsb = self.gcc_bswap(lsb, width / 2);
|
|
|
|
let swapped_lsb = self.context.new_cast(None, swapped_lsb, native_int_type);
|
2023-09-06 18:01:04 -05:00
|
|
|
let msb = self.high(arg);
|
2022-03-26 12:29:37 -05:00
|
|
|
let swapped_msb = self.gcc_bswap(msb, width / 2);
|
|
|
|
let swapped_msb = self.context.new_cast(None, swapped_msb, native_int_type);
|
|
|
|
|
|
|
|
// NOTE: we also need to swap the two elements here, in addition to swapping inside
|
|
|
|
// the elements themselves like done above.
|
2023-09-06 18:01:04 -05:00
|
|
|
return self.from_low_high_rvalues(arg_type, swapped_msb, swapped_lsb);
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(antoyo): check if it's faster to use string literals and a
|
|
|
|
// match instead of format!.
|
|
|
|
let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width));
|
|
|
|
// FIXME(antoyo): this cast should not be necessary. Remove
|
|
|
|
// when having proper sized integer types.
|
|
|
|
let param_type = bswap.get_param(0).to_rvalue().get_type();
|
|
|
|
if param_type != arg_type {
|
|
|
|
arg = self.bitcast(arg, param_type);
|
|
|
|
}
|
|
|
|
self.cx.context.new_call(None, bswap, &[arg])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|
|
|
pub fn gcc_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
|
|
|
|
if self.is_native_int_type_or_bool(typ) {
|
|
|
|
self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// NOTE: set the sign in high.
|
|
|
|
self.from_low_high(typ, int, -(int.is_negative() as i64))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
|
2023-08-13 14:34:21 -05:00
|
|
|
if typ.is_u128(self) {
|
|
|
|
// FIXME(antoyo): libgccjit cannot create 128-bit values yet.
|
|
|
|
let num = self.context.new_rvalue_from_long(self.u64_type, int as i64);
|
|
|
|
self.gcc_int_cast(num, typ)
|
|
|
|
}
|
|
|
|
else if self.is_native_int_type_or_bool(typ) {
|
2022-03-26 12:29:37 -05:00
|
|
|
self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.from_low_high(typ, int as i64, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
|
|
|
|
let low = num as u64;
|
|
|
|
let high = (num >> 64) as u64;
|
|
|
|
if num >> 64 != 0 {
|
|
|
|
// FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()?
|
|
|
|
if self.is_native_int_type(typ) {
|
|
|
|
let low = self.context.new_rvalue_from_long(self.u64_type, low as i64);
|
|
|
|
let high = self.context.new_rvalue_from_long(typ, high as i64);
|
|
|
|
|
|
|
|
let sixty_four = self.context.new_rvalue_from_long(typ, 64);
|
|
|
|
let shift = high << sixty_four;
|
|
|
|
shift | self.context.new_cast(None, low, typ)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.from_low_high(typ, low as i64, high as i64)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if typ.is_i128(self) {
|
2023-08-13 14:34:21 -05:00
|
|
|
// FIXME(antoyo): libgccjit cannot create 128-bit values yet.
|
2022-03-26 12:29:37 -05:00
|
|
|
let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
|
|
|
|
self.gcc_int_cast(num, typ)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.gcc_uint(typ, num as u64)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_zero(&self, typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
if self.is_native_int_type_or_bool(typ) {
|
|
|
|
self.context.new_rvalue_zero(typ)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.from_low_high(typ, 0, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_int_width(&self, typ: Type<'gcc>) -> u64 {
|
|
|
|
if self.is_native_int_type_or_bool(typ) {
|
|
|
|
typ.get_size() as u64 * 8
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// NOTE: the only unsupported types are u128 and i128.
|
|
|
|
128
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bitwise_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let a_type = a.get_type();
|
|
|
|
let b_type = b.get_type();
|
|
|
|
let a_native = self.is_native_int_type_or_bool(a_type);
|
|
|
|
let b_native = self.is_native_int_type_or_bool(b_type);
|
2022-06-06 21:04:37 -05:00
|
|
|
if a_type.is_vector() && b_type.is_vector() {
|
|
|
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
|
|
|
}
|
|
|
|
else if a_native && b_native {
|
2022-03-26 12:29:37 -05:00
|
|
|
if a_type != b_type {
|
|
|
|
b = self.context.new_cast(None, b, a_type);
|
|
|
|
}
|
|
|
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert!(!a_native && !b_native, "both types should either be native or non-native for or operation");
|
|
|
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
2023-09-06 18:01:04 -05:00
|
|
|
self.from_low_high_rvalues(a_type,
|
2022-03-26 12:29:37 -05:00
|
|
|
self.context.new_binary_op(None, operation, native_int_type, self.low(a), self.low(b)),
|
|
|
|
self.context.new_binary_op(None, operation, native_int_type, self.high(a), self.high(b)),
|
2023-09-06 18:01:04 -05:00
|
|
|
)
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_or(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.bitwise_operation(BinaryOp::BitwiseOr, a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/master/src/int/mod.rs#L379 instead?
|
|
|
|
pub fn gcc_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
let value_type = value.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) {
|
|
|
|
self.context.new_cast(None, value, dest_typ)
|
|
|
|
}
|
|
|
|
else if self.is_native_int_type_or_bool(dest_typ) {
|
|
|
|
self.context.new_cast(None, self.low(value), dest_typ)
|
|
|
|
}
|
|
|
|
else if self.is_native_int_type_or_bool(value_type) {
|
|
|
|
let dest_element_type = dest_typ.dyncast_array().expect("get element type");
|
|
|
|
|
|
|
|
// NOTE: set the sign of the value.
|
|
|
|
let zero = self.context.new_rvalue_zero(value_type);
|
|
|
|
let is_negative = self.context.new_comparison(None, ComparisonOp::LessThan, value, zero);
|
|
|
|
let is_negative = self.gcc_int_cast(is_negative, dest_element_type);
|
2023-09-06 18:01:04 -05:00
|
|
|
self.from_low_high_rvalues(dest_typ,
|
2022-03-26 12:29:37 -05:00
|
|
|
self.context.new_cast(None, value, dest_element_type),
|
|
|
|
self.context.new_unary_op(None, UnaryOp::Minus, dest_element_type, is_negative),
|
2023-09-06 18:01:04 -05:00
|
|
|
)
|
2022-03-26 12:29:37 -05:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Since u128 and i128 are the only types that can be unsupported, we know the type of
|
|
|
|
// value and the destination type have the same size, so a bitcast is fine.
|
2022-06-06 21:04:37 -05:00
|
|
|
|
|
|
|
// TODO(antoyo): perhaps use __builtin_convertvector for vector casting.
|
2022-03-26 12:29:37 -05:00
|
|
|
self.context.new_bitcast(None, value, dest_typ)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn int_to_float_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
let value_type = value.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(value_type) {
|
|
|
|
return self.context.new_cast(None, value, dest_typ);
|
|
|
|
}
|
|
|
|
|
|
|
|
let name_suffix =
|
|
|
|
match self.type_kind(dest_typ) {
|
|
|
|
TypeKind::Float => "tisf",
|
|
|
|
TypeKind::Double => "tidf",
|
|
|
|
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
|
|
|
|
};
|
|
|
|
let sign =
|
|
|
|
if signed {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
"un"
|
|
|
|
};
|
|
|
|
let func_name = format!("__float{}{}", sign, name_suffix);
|
|
|
|
let param = self.context.new_parameter(None, value_type, "n");
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false);
|
|
|
|
self.context.new_call(None, func, &[value])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_int_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.int_to_float_cast(true, value, dest_typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_uint_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.int_to_float_cast(false, value, dest_typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn float_to_int_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
let value_type = value.get_type();
|
|
|
|
if self.is_native_int_type_or_bool(dest_typ) {
|
|
|
|
return self.context.new_cast(None, value, dest_typ);
|
|
|
|
}
|
|
|
|
|
|
|
|
let name_suffix =
|
|
|
|
match self.type_kind(value_type) {
|
|
|
|
TypeKind::Float => "sfti",
|
|
|
|
TypeKind::Double => "dfti",
|
|
|
|
kind => panic!("cannot cast a {:?} to non-native integer", kind),
|
|
|
|
};
|
|
|
|
let sign =
|
|
|
|
if signed {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
"uns"
|
|
|
|
};
|
|
|
|
let func_name = format!("__fix{}{}", sign, name_suffix);
|
|
|
|
let param = self.context.new_parameter(None, value_type, "n");
|
|
|
|
let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false);
|
|
|
|
self.context.new_call(None, func, &[value])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_float_to_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.float_to_int_cast(true, value, dest_typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gcc_float_to_uint_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
|
|
|
self.float_to_int_cast(false, value, dest_typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn high(&self, value: RValue<'gcc>) -> RValue<'gcc> {
|
2023-09-06 18:01:04 -05:00
|
|
|
let index =
|
|
|
|
match self.sess().target.options.endian {
|
|
|
|
Endian::Little => 1,
|
|
|
|
Endian::Big => 0,
|
|
|
|
};
|
|
|
|
self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, index))
|
2022-03-26 12:29:37 -05:00
|
|
|
.to_rvalue()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn low(&self, value: RValue<'gcc>) -> RValue<'gcc> {
|
2023-09-06 18:01:04 -05:00
|
|
|
let index =
|
|
|
|
match self.sess().target.options.endian {
|
|
|
|
Endian::Little => 0,
|
|
|
|
Endian::Big => 1,
|
|
|
|
};
|
|
|
|
self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, index))
|
2022-03-26 12:29:37 -05:00
|
|
|
.to_rvalue()
|
|
|
|
}
|
|
|
|
|
2023-09-06 18:01:04 -05:00
|
|
|
fn from_low_high_rvalues(&self, typ: Type<'gcc>, low: RValue<'gcc>, high: RValue<'gcc>) -> RValue<'gcc> {
|
|
|
|
let (first, last) =
|
|
|
|
match self.sess().target.options.endian {
|
|
|
|
Endian::Little => (low, high),
|
|
|
|
Endian::Big => (high, low),
|
|
|
|
};
|
|
|
|
|
|
|
|
let values = [first, last];
|
|
|
|
self.context.new_array_constructor(None, typ, &values)
|
|
|
|
}
|
|
|
|
|
2022-03-26 12:29:37 -05:00
|
|
|
fn from_low_high(&self, typ: Type<'gcc>, low: i64, high: i64) -> RValue<'gcc> {
|
2023-09-06 18:01:04 -05:00
|
|
|
let (first, last) =
|
|
|
|
match self.sess().target.options.endian {
|
|
|
|
Endian::Little => (low, high),
|
|
|
|
Endian::Big => (high, low),
|
|
|
|
};
|
|
|
|
|
2022-03-26 12:29:37 -05:00
|
|
|
let native_int_type = typ.dyncast_array().expect("get element type");
|
|
|
|
let values = [
|
2023-09-06 18:01:04 -05:00
|
|
|
self.context.new_rvalue_from_long(native_int_type, first),
|
|
|
|
self.context.new_rvalue_from_long(native_int_type, last),
|
2022-03-26 12:29:37 -05:00
|
|
|
];
|
|
|
|
self.context.new_array_constructor(None, typ, &values)
|
|
|
|
}
|
|
|
|
}
|