feat(int.rs&build.rs): Add location info to arithmetic operators

TODO:
1. Clean the unnecessary locations in builder.rs & int.rs
2. Add demangling support
3. Add debug scope support
4. Add vtable support
5. Clean up builder.rs locations
This commit is contained in:
tempdragon 2024-02-24 19:56:29 +08:00
parent c638defad7
commit 2ffe9d1eef
3 changed files with 199 additions and 181 deletions

View File

@ -26,6 +26,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::mir::Rvalue;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_span::Span;
@ -398,6 +399,16 @@ impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> {
type DIVariable = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DIVariable;
}
pub fn set_rval_location<'a, 'gcc, 'tcx>(bx: &mut Builder<'a,'gcc,'tcx>, r:RValue<'gcc>) -> RValue<'gcc> {
if bx.loc.is_some(){
unsafe {
r.set_location(bx.loc.unwrap());
}
}
r
}
impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Builder<'a, 'gcc, 'tcx> {
Builder::with_cx(cx, block)
@ -612,7 +623,7 @@ fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
// FIXME(antoyo): this seems to produce the wrong result.
return self.context.new_call(self.loc, fmodf, &[a, b]);
}
else if let Some(vector_type) = a_type_unqualified.dyncast_vector() {
if let Some(vector_type) = a_type_unqualified.dyncast_vector() {
assert_eq!(a_type_unqualified, b.get_type().unqualified());
let num_units = vector_type.get_num_units();
@ -630,7 +641,7 @@ fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
assert_eq!(a_type_unqualified, self.cx.double_type);
let fmod = self.context.get_builtin_function("fmod");
return self.context.new_call(self.loc, fmod, &[a, b]);
self.context.new_call(self.loc, fmod, &[a, b])
}
fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
@ -652,73 +663,80 @@ fn and(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
}
fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.cx.gcc_or(a, b)
let ret = self.cx.gcc_or(a, b, self.loc);
if self.loc.is_some() {
unsafe { ret.set_location(self.loc.unwrap()); }
}
ret
}
fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_xor(a, b)
set_rval_location(self,self.gcc_xor(a, b))
}
fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_neg(a)
set_rval_location(self,self.gcc_neg(a))
}
fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
self.cx.context.new_unary_op(self.loc, UnaryOp::Minus, a.get_type(), a)
set_rval_location(self,self.cx.context.new_unary_op(self.loc, UnaryOp::Minus, a.get_type(), a))
}
fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_not(a)
set_rval_location(self,self.gcc_not(a))
}
fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_add(a, b)
set_rval_location(self,self.gcc_add(a, b))
}
fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_add(a, b)
set_rval_location(self,self.gcc_add(a, b))
}
fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_sub(a, b)
set_rval_location(self,self.gcc_sub(a, b))
}
fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
// TODO(antoyo): should generate poison value?
self.gcc_sub(a, b)
set_rval_location(self,self.gcc_sub(a, b))
}
fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_mul(a, b)
set_rval_location(self,self.gcc_mul(a, b))
}
fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.gcc_mul(a, b)
set_rval_location(self,self.gcc_mul(a, b))
}
fn fadd_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
lhs + rhs
set_rval_location(self,lhs + rhs)
}
fn fsub_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
lhs - rhs
set_rval_location(self,lhs - rhs)
}
fn fmul_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
lhs * rhs
set_rval_location(self,lhs * rhs)
}
fn fdiv_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
lhs / rhs
set_rval_location(self,lhs / rhs)
}
fn frem_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
// NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
self.frem(lhs, rhs)
let i = self.frem(lhs, rhs);
set_rval_location(self,i);
i
}
fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
@ -1005,28 +1023,28 @@ fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
}
fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
self.gcc_float_to_uint_cast(value, dest_ty)
set_rval_location(self,self.gcc_float_to_uint_cast(value, dest_ty))
}
fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
self.gcc_float_to_int_cast(value, dest_ty)
set_rval_location(self,self.gcc_float_to_int_cast(value, dest_ty))
}
fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
self.gcc_uint_to_float_cast(value, dest_ty)
set_rval_location(self,self.gcc_uint_to_float_cast(value, dest_ty))
}
fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
self.gcc_int_to_float_cast(value, dest_ty)
set_rval_location(self,self.gcc_int_to_float_cast(value, dest_ty))
}
fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
// TODO(antoyo): make sure it truncates.
self.context.new_cast(self.loc, value, dest_ty)
set_rval_location(self,self.context.new_cast(self.loc, value, dest_ty))
}
fn fpext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
self.context.new_cast(self.loc, value, dest_ty)
set_rval_location(self,self.context.new_cast(self.loc, value, dest_ty))
}
fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {

View File

@ -4,7 +4,7 @@
use std::convert::TryFrom;
use gccjit::{ComparisonOp, FunctionType, RValue, ToRValue, Type, UnaryOp, BinaryOp};
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp};
use rustc_middle::ty::{ParamEnv, Ty};
@ -35,13 +35,13 @@ pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> {
else {
UnaryOp::BitwiseNegate
};
self.cx.context.new_unary_op(None, operation, typ, a)
self.cx.context.new_unary_op(self.loc, operation, typ, a)
}
else {
let element_type = typ.dyncast_array().expect("element type");
self.from_low_high_rvalues(typ,
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)),
self.cx.context.new_unary_op(self.loc, UnaryOp::BitwiseNegate, element_type, self.low(a)),
self.cx.context.new_unary_op(self.loc, UnaryOp::BitwiseNegate, element_type, self.high(a)),
)
}
}
@ -49,7 +49,7 @@ pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> {
pub fn gcc_neg(&self, a: RValue<'gcc>) -> RValue<'gcc> {
let a_type = a.get_type();
if self.is_native_int_type(a_type) || a_type.is_vector() {
self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
self.cx.context.new_unary_op(self.loc, UnaryOp::Minus, a.get_type(), a)
}
else {
self.gcc_add(self.gcc_not(a), self.gcc_int(a_type, 1))
@ -57,7 +57,7 @@ pub fn gcc_neg(&self, a: RValue<'gcc>) -> RValue<'gcc> {
}
pub fn gcc_and(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.cx.bitwise_operation(BinaryOp::BitwiseAnd, a, b)
self.cx.bitwise_operation(BinaryOp::BitwiseAnd, a, b, self.loc)
}
pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
@ -69,7 +69,7 @@ pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
// 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);
let b = self.context.new_cast(self.loc, b, a_type);
a >> b
}
else {
@ -95,14 +95,14 @@ pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
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 result = func.new_local(self.loc, 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);
self.llbb().end_with_conditional(self.loc, condition, then_block, else_block);
let shift_value = self.gcc_sub(b, sixty_four);
let high = self.high(a);
@ -114,27 +114,27 @@ pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
zero
};
let array_value = self.from_low_high_rvalues(a_type, high >> shift_value, sign);
then_block.add_assignment(None, result, array_value);
then_block.end_with_jump(None, after_block);
then_block.add_assignment(self.loc, result, array_value);
then_block.end_with_jump(self.loc, after_block);
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
else_block.end_with_conditional(self.loc, condition, b0_block, actual_else_block);
b0_block.add_assignment(None, result, a);
b0_block.end_with_jump(None, after_block);
b0_block.add_assignment(self.loc, result, a);
b0_block.end_with_jump(self.loc, 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);
let casted_low = self.context.new_cast(self.loc, self.low(a), unsigned_type);
let shifted_low = casted_low >> self.context.new_cast(self.loc, b, unsigned_type);
let shifted_low = self.context.new_cast(self.loc, shifted_low, native_int_type);
let array_value = self.from_low_high_rvalues(a_type,
(high << shift_value) | shifted_low,
high >> b,
);
actual_else_block.add_assignment(None, result, array_value);
actual_else_block.end_with_jump(None, after_block);
actual_else_block.add_assignment(self.loc, result, array_value);
actual_else_block.end_with_jump(self.loc, after_block);
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
// state need to be updated.
@ -152,13 +152,13 @@ fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue
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());
b = self.context.new_bitcast(self.loc, b, a.get_type());
}
else {
b = self.context.new_cast(None, b, a.get_type());
b = self.context.new_cast(self.loc, b, a.get_type());
}
}
self.context.new_binary_op(None, operation, a_type, a, b)
self.context.new_binary_op(self.loc, operation, a_type, a, b)
}
else {
debug_assert!(a_type.dyncast_array().is_some());
@ -172,10 +172,10 @@ fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue
(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])
let param_a = self.context.new_parameter(self.loc, a_type, "a");
let param_b = self.context.new_parameter(self.loc, b_type, "b");
let func = self.context.new_function(self.loc, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false);
self.context.new_call(self.loc, func, &[a, b])
}
}
@ -335,10 +335,10 @@ pub fn gcc_checked_binop(&self, oop: OverflowOp, typ: Ty<'_>, lhs: <Self as Back
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);
.new_local(self.loc, rhs.get_type(), "binopResult")
.get_address(self.loc);
let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
(res.dereference(None).to_rvalue(), overflow)
(res.dereference(self.loc).to_rvalue(), overflow)
}
pub fn operation_with_overflow(&self, func_name: &str, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) {
@ -346,10 +346,10 @@ pub fn operation_with_overflow(&self, func_name: &str, lhs: RValue<'gcc>, rhs: R
let b_type = rhs.get_type();
debug_assert!(a_type.dyncast_array().is_some());
debug_assert!(b_type.dyncast_array().is_some());
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 param_a = self.context.new_parameter(self.loc, a_type, "a");
let param_b = self.context.new_parameter(self.loc, b_type, "b");
let result_field = self.context.new_field(self.loc, a_type, "result");
let overflow_field = self.context.new_field(self.loc, 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();
@ -372,23 +372,23 @@ pub fn operation_with_overflow(&self, func_name: &str, lhs: RValue<'gcc>, rhs: R
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 return_type = self.context.new_struct_type(self.loc, "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_value = self.current_func().new_local(self.loc, 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]));
let return_param = self.context.new_parameter(self.loc, return_param_type, "return_value");
let func = self.context.new_function(self.loc, FunctionType::Extern, self.type_void(), &[return_param, param_a, param_b], func_name, false);
self.llbb().add_eval(self.loc, self.context.new_call(self.loc, func, &[return_value.get_address(self.loc), 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 func = self.context.new_function(self.loc, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
self.context.new_call(self.loc, func, &[lhs, rhs])
};
let overflow = result.access_field(None, overflow_field);
let int_result = result.access_field(None, result_field);
return (int_result, overflow);
let overflow = result.access_field(self.loc, overflow_field);
let int_result = result.access_field(self.loc, result_field);
(int_result, overflow)
}
pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
@ -397,7 +397,7 @@ pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RVa
if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) {
// 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 result = self.current_func().new_local(self.loc, 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");
@ -413,35 +413,35 @@ pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RVa
// 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 lhs_low = self.context.new_cast(self.loc, self.low(lhs), unsigned_type);
let rhs_low = self.context.new_cast(self.loc, 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);
let condition = self.context.new_comparison(self.loc, ComparisonOp::LessThan, self.high(lhs), self.high(rhs));
self.llbb().end_with_conditional(self.loc, condition, block1, block2);
block1.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
block1.end_with_jump(None, after);
block1.add_assignment(self.loc, result, self.context.new_rvalue_zero(self.int_type));
block1.end_with_jump(self.loc, after);
let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, self.high(lhs), self.high(rhs));
block2.end_with_conditional(None, condition, block3, block4);
let condition = self.context.new_comparison(self.loc, ComparisonOp::GreaterThan, self.high(lhs), self.high(rhs));
block2.end_with_conditional(self.loc, condition, block3, block4);
block3.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
block3.end_with_jump(None, after);
block3.add_assignment(self.loc, result, self.context.new_rvalue_from_int(self.int_type, 2));
block3.end_with_jump(self.loc, after);
let condition = self.context.new_comparison(None, ComparisonOp::LessThan, lhs_low, rhs_low);
block4.end_with_conditional(None, condition, block5, block6);
let condition = self.context.new_comparison(self.loc, ComparisonOp::LessThan, lhs_low, rhs_low);
block4.end_with_conditional(self.loc, condition, block5, block6);
block5.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
block5.end_with_jump(None, after);
block5.add_assignment(self.loc, result, self.context.new_rvalue_zero(self.int_type));
block5.end_with_jump(self.loc, after);
let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, lhs_low, rhs_low);
block6.end_with_conditional(None, condition, block7, block8);
let condition = self.context.new_comparison(self.loc, ComparisonOp::GreaterThan, lhs_low, rhs_low);
block6.end_with_conditional(self.loc, condition, block7, block8);
block7.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
block7.end_with_jump(None, after);
block7.add_assignment(self.loc, result, self.context.new_rvalue_from_int(self.int_type, 2));
block7.end_with_jump(self.loc, after);
block8.add_assignment(None, result, self.context.new_rvalue_one(self.int_type));
block8.end_with_jump(None, after);
block8.add_assignment(self.loc, result, self.context.new_rvalue_one(self.int_type));
block8.end_with_jump(self.loc, after);
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
// state need to be updated.
@ -451,10 +451,10 @@ pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RVa
let (op, limit) =
match op {
IntPredicate::IntEQ => {
return self.context.new_comparison(None, ComparisonOp::Equals, cmp, self.context.new_rvalue_one(self.int_type));
return self.context.new_comparison(self.loc, 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));
return self.context.new_comparison(self.loc, ComparisonOp::NotEquals, cmp, self.context.new_rvalue_one(self.int_type));
},
// TODO(antoyo): cast to u128 for unsigned comparison. See below.
IntPredicate::IntUGT => (ComparisonOp::Equals, 2),
@ -466,39 +466,39 @@ pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RVa
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))
self.context.new_comparison(self.loc, op, cmp, self.context.new_rvalue_from_int(self.int_type, limit))
}
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)
lhs = self.context.new_bitcast(self.loc, lhs, self.usize_type);
rhs = self.context.new_bitcast(self.loc, rhs, self.usize_type);
self.context.new_comparison(self.loc, op.to_gcc_comparison(), lhs, rhs)
}
else {
if a_type != b_type {
// NOTE: because libgccjit cannot compare function pointers.
if a_type.dyncast_function_ptr_type().is_some() && b_type.dyncast_function_ptr_type().is_some() {
lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
lhs = self.context.new_cast(self.loc, lhs, self.usize_type.make_pointer());
rhs = self.context.new_cast(self.loc, rhs, self.usize_type.make_pointer());
}
// NOTE: hack because we try to cast a vector type to the same vector type.
else if format!("{:?}", a_type) != format!("{:?}", b_type) {
rhs = self.context.new_cast(None, rhs, a_type);
rhs = self.context.new_cast(self.loc, rhs, a_type);
}
}
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);
lhs = self.context.new_cast(self.loc, lhs, unsigned_type);
rhs = self.context.new_cast(self.loc, rhs, unsigned_type);
}
},
// TODO(antoyo): we probably need to handle signed comparison for unsigned
// integers.
_ => (),
}
self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
self.context.new_comparison(self.loc, op.to_gcc_comparison(), lhs, rhs)
}
}
@ -528,12 +528,12 @@ pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
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 a = self.context.new_cast(self.loc, a, b_type);
let result = a << b;
self.context.new_cast(None, result, a_type)
self.context.new_cast(self.loc, 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);
let b = self.context.new_cast(self.loc, b, a_type);
a << b
}
else {
@ -557,40 +557,40 @@ pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
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 result = func.new_local(self.loc, 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);
self.llbb().end_with_conditional(self.loc, condition, then_block, else_block);
let array_value = self.from_low_high_rvalues(a_type,
zero,
self.low(a) << (b - sixty_four),
);
then_block.add_assignment(None, result, array_value);
then_block.end_with_jump(None, after_block);
then_block.add_assignment(self.loc, result, array_value);
then_block.end_with_jump(self.loc, after_block);
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
else_block.end_with_conditional(self.loc, condition, b0_block, actual_else_block);
b0_block.add_assignment(None, result, a);
b0_block.end_with_jump(None, after_block);
b0_block.add_assignment(self.loc, result, a);
b0_block.end_with_jump(self.loc, after_block);
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
// TODO(antoyo): adjust this ^ comment.
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);
let casted_low = self.context.new_cast(self.loc, self.low(a), unsigned_type);
let shift_value = self.context.new_cast(self.loc, sixty_four - b, unsigned_type);
let high_low = self.context.new_cast(self.loc, casted_low >> shift_value, native_int_type);
let array_value = self.from_low_high_rvalues(a_type,
self.low(a) << b,
(self.high(a) << b) | high_low,
);
actual_else_block.add_assignment(None, result, array_value);
actual_else_block.end_with_jump(None, after_block);
actual_else_block.add_assignment(self.loc, result, array_value);
actual_else_block.end_with_jump(self.loc, after_block);
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
// state need to be updated.
@ -606,10 +606,10 @@ pub fn gcc_bswap(&mut self, mut arg: RValue<'gcc>, width: u64) -> RValue<'gcc> {
let native_int_type = arg_type.dyncast_array().expect("get element type");
let lsb = self.low(arg);
let swapped_lsb = self.gcc_bswap(lsb, width / 2);
let swapped_lsb = self.context.new_cast(None, swapped_lsb, native_int_type);
let swapped_lsb = self.context.new_cast(self.loc, swapped_lsb, native_int_type);
let msb = self.high(arg);
let swapped_msb = self.gcc_bswap(msb, width / 2);
let swapped_msb = self.context.new_cast(None, swapped_msb, native_int_type);
let swapped_msb = self.context.new_cast(self.loc, 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.
@ -625,7 +625,7 @@ pub fn gcc_bswap(&mut self, mut arg: RValue<'gcc>, width: u64) -> RValue<'gcc> {
if param_type != arg_type {
arg = self.bitcast(arg, param_type);
}
self.cx.context.new_call(None, bswap, &[arg])
self.cx.context.new_call(self.loc, bswap, &[arg])
}
}
@ -700,33 +700,33 @@ pub fn gcc_int_width(&self, typ: Type<'gcc>) -> u64 {
}
}
fn bitwise_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
fn bitwise_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>, loc: Option<Location<'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);
if a_type.is_vector() && b_type.is_vector() {
let b = self.bitcast_if_needed(b, a_type);
self.context.new_binary_op(None, operation, a_type, a, b)
self.context.new_binary_op(loc, operation, a_type, a, b)
}
else if a_native && b_native {
if a_type != b_type {
b = self.context.new_cast(None, b, a_type);
b = self.context.new_cast(loc, b, a_type);
}
self.context.new_binary_op(None, operation, a_type, a, b)
self.context.new_binary_op(loc, 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");
self.from_low_high_rvalues(a_type,
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)),
self.context.new_binary_op(loc, operation, native_int_type, self.low(a), self.low(b)),
self.context.new_binary_op(loc, operation, native_int_type, self.high(a), self.high(b)),
)
}
}
pub fn gcc_or(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
self.bitwise_operation(BinaryOp::BitwiseOr, a, b)
pub fn gcc_or(&self, a: RValue<'gcc>, b: RValue<'gcc>, loc: Option<Location<'gcc>>) -> RValue<'gcc> {
self.bitwise_operation(BinaryOp::BitwiseOr, a, b, loc)
}
// TODO(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/master/src/int/mod.rs#L379 instead?

View File

@ -640,7 +640,7 @@ fn bit_reverse(&mut self, width: u64, value: RValue<'gcc>) -> RValue<'gcc> {
let new_low = self.gcc_int_cast(reversed_high, typ);
let new_high = self.shl(self.gcc_int_cast(reversed_low, typ), sixty_four);
self.gcc_or(new_low, new_high)
self.gcc_or(new_low, new_high, self.loc)
},
_ => {
panic!("cannot bit reverse with width = {}", width);
@ -685,44 +685,44 @@ fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc
let first_elem = self.context.new_array_access(None, result, zero);
let first_value = self.gcc_int_cast(self.context.new_call(None, clzll, &[high]), arg_type);
self.llbb()
.add_assignment(None, first_elem, first_value);
.add_assignment(self.loc, first_elem, first_value);
let second_elem = self.context.new_array_access(None, result, one);
let cast = self.gcc_int_cast(self.context.new_call(None, clzll, &[low]), arg_type);
let second_elem = self.context.new_array_access(self.loc, result, one);
let cast = self.gcc_int_cast(self.context.new_call(self.loc, clzll, &[low]), arg_type);
let second_value = self.add(cast, sixty_four);
self.llbb()
.add_assignment(None, second_elem, second_value);
.add_assignment(self.loc, second_elem, second_value);
let third_elem = self.context.new_array_access(None, result, two);
let third_elem = self.context.new_array_access(self.loc, result, two);
let third_value = self.const_uint(arg_type, 128);
self.llbb()
.add_assignment(None, third_elem, third_value);
.add_assignment(self.loc, third_elem, third_value);
let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
let not_high = self.context.new_unary_op(self.loc, UnaryOp::LogicalNegate, self.u64_type, high);
let not_low = self.context.new_unary_op(self.loc, UnaryOp::LogicalNegate, self.u64_type, low);
let not_low_and_not_high = not_low & not_high;
let index = not_high + not_low_and_not_high;
// NOTE: the following cast is necessary to avoid a GIMPLE verification failure in
// gcc.
// TODO(antoyo): do the correct verification in libgccjit to avoid an error at the
// compilation stage.
let index = self.context.new_cast(None, index, self.i32_type);
let index = self.context.new_cast(self.loc, index, self.i32_type);
let res = self.context.new_array_access(None, result, index);
let res = self.context.new_array_access(self.loc, result, index);
return self.gcc_int_cast(res.to_rvalue(), arg_type);
}
else {
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clzll");
let arg = self.context.new_cast(None, arg, self.ulonglong_type);
let arg = self.context.new_cast(self.loc, arg, self.ulonglong_type);
let diff = self.ulonglong_type.get_size() as i64 - arg_type.get_size() as i64;
let diff = self.context.new_rvalue_from_long(self.int_type, diff * 8);
let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff;
return self.context.new_cast(None, res, arg_type);
let res = self.context.new_call(self.loc, count_leading_zeroes, &[arg]) - diff;
return self.context.new_cast(self.loc, res, arg_type);
};
let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes);
let res = self.context.new_call(None, count_leading_zeroes, &[arg]);
self.context.new_cast(None, res, arg_type)
let res = self.context.new_call(self.loc, count_leading_zeroes, &[arg]);
self.context.new_cast(self.loc, res, arg_type)
}
fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
@ -766,58 +766,58 @@ fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'g
let ctzll = self.context.get_builtin_function("__builtin_ctzll");
let first_elem = self.context.new_array_access(None, result, zero);
let first_value = self.gcc_int_cast(self.context.new_call(None, ctzll, &[low]), arg_type);
let first_elem = self.context.new_array_access(self.loc, result, zero);
let first_value = self.gcc_int_cast(self.context.new_call(self.loc, ctzll, &[low]), arg_type);
self.llbb()
.add_assignment(None, first_elem, first_value);
.add_assignment(self.loc, first_elem, first_value);
let second_elem = self.context.new_array_access(None, result, one);
let second_value = self.gcc_add(self.gcc_int_cast(self.context.new_call(None, ctzll, &[high]), arg_type), sixty_four);
let second_elem = self.context.new_array_access(self.loc, result, one);
let second_value = self.gcc_add(self.gcc_int_cast(self.context.new_call(self.loc, ctzll, &[high]), arg_type), sixty_four);
self.llbb()
.add_assignment(None, second_elem, second_value);
.add_assignment(self.loc, second_elem, second_value);
let third_elem = self.context.new_array_access(None, result, two);
let third_elem = self.context.new_array_access(self.loc, result, two);
let third_value = self.gcc_int(arg_type, 128);
self.llbb()
.add_assignment(None, third_elem, third_value);
.add_assignment(self.loc, third_elem, third_value);
let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
let not_low = self.context.new_unary_op(self.loc, UnaryOp::LogicalNegate, self.u64_type, low);
let not_high = self.context.new_unary_op(self.loc, UnaryOp::LogicalNegate, self.u64_type, high);
let not_low_and_not_high = not_low & not_high;
let index = not_low + not_low_and_not_high;
// NOTE: the following cast is necessary to avoid a GIMPLE verification failure in
// gcc.
// TODO(antoyo): do the correct verification in libgccjit to avoid an error at the
// compilation stage.
let index = self.context.new_cast(None, index, self.i32_type);
let index = self.context.new_cast(self.loc, index, self.i32_type);
let res = self.context.new_array_access(None, result, index);
let res = self.context.new_array_access(self.loc, result, index);
return self.gcc_int_cast(res.to_rvalue(), result_type);
}
else {
let count_trailing_zeroes = self.context.get_builtin_function("__builtin_ctzll");
let arg_size = arg_type.get_size();
let casted_arg = self.context.new_cast(None, arg, self.ulonglong_type);
let casted_arg = self.context.new_cast(self.loc, arg, self.ulonglong_type);
let byte_diff = self.ulonglong_type.get_size() as i64 - arg_size as i64;
let diff = self.context.new_rvalue_from_long(self.int_type, byte_diff * 8);
let mask = self.context.new_rvalue_from_long(arg_type, -1); // To get the value with all bits set.
let masked = mask & self.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, arg);
let cond = self.context.new_comparison(None, ComparisonOp::Equals, masked, mask);
let diff = diff * self.context.new_cast(None, cond, self.int_type);
let res = self.context.new_call(None, count_trailing_zeroes, &[casted_arg]) - diff;
return self.context.new_cast(None, res, result_type);
let masked = mask & self.context.new_unary_op(self.loc, UnaryOp::BitwiseNegate, arg_type, arg);
let cond = self.context.new_comparison(self.loc, ComparisonOp::Equals, masked, mask);
let diff = diff * self.context.new_cast(self.loc, cond, self.int_type);
let res = self.context.new_call(self.loc, count_trailing_zeroes, &[casted_arg]) - diff;
return self.context.new_cast(self.loc, res, result_type);
};
let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
let arg =
if arg_type != expected_type {
self.context.new_cast(None, arg, expected_type)
self.context.new_cast(self.loc, arg, expected_type)
}
else {
arg
};
let res = self.context.new_call(None, count_trailing_zeroes, &[arg]);
self.context.new_cast(None, res, result_type)
let res = self.context.new_call(self.loc, count_trailing_zeroes, &[arg]);
self.context.new_cast(self.loc, res, result_type)
}
fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
@ -859,8 +859,8 @@ fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
let counter = self.current_func().new_local(None, counter_type, "popcount_counter");
let val = self.current_func().new_local(None, value_type, "popcount_value");
let zero = self.gcc_zero(counter_type);
self.llbb().add_assignment(None, counter, zero);
self.llbb().add_assignment(None, val, value);
self.llbb().add_assignment(self.loc, counter, zero);
self.llbb().add_assignment(self.loc, val, value);
self.br(loop_head);
// check if value isn't zero
@ -874,12 +874,12 @@ fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
let one = self.gcc_int(value_type, 1);
let sub = self.gcc_sub(val.to_rvalue(), one);
let op = self.gcc_and(val.to_rvalue(), sub);
loop_body.add_assignment(None, val, op);
loop_body.add_assignment(self.loc, val, op);
// counter += 1
let one = self.gcc_int(counter_type, 1);
let op = self.gcc_add(counter.to_rvalue(), one);
loop_body.add_assignment(None, counter, op);
loop_body.add_assignment(self.loc, counter, op);
self.br(loop_head);
// end of loop
@ -922,7 +922,7 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
if signed {
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
let func = self.current_func.borrow().expect("func");
let res = func.new_local(None, result_type, "saturating_sum");
let res = func.new_local(self.loc, result_type, "saturating_sum");
let supports_native_type = self.is_native_int_type(result_type);
let overflow =
if supports_native_type {
@ -936,7 +936,7 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
_ => unreachable!(),
};
let overflow_func = self.context.get_builtin_function(func_name);
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None)
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(self.loc)], None)
}
else {
let func_name =
@ -945,7 +945,7 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
_ => unreachable!(),
};
let (int_result, overflow) = self.operation_with_overflow(func_name, lhs, rhs);
self.llbb().add_assignment(None, res, int_result);
self.llbb().add_assignment(self.loc, res, int_result);
overflow
};
@ -958,10 +958,10 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
let uint_max = self.gcc_not(self.gcc_int(unsigned_type, 0));
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
then_block.add_assignment(None, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
then_block.end_with_jump(None, after_block);
then_block.add_assignment(self.loc, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
then_block.end_with_jump(self.loc, after_block);
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
self.llbb().end_with_conditional(self.loc, overflow, then_block, after_block);
// NOTE: since jumps were added in a place rustc does not
// expect, the current block in the state need to be updated.
@ -974,7 +974,7 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
let res = self.gcc_add(lhs, rhs);
let cond = self.gcc_icmp(IntPredicate::IntULT, res, lhs);
let value = self.gcc_neg(self.gcc_int_cast(cond, result_type));
self.gcc_or(res, value)
self.gcc_or(res, value, self.loc)
}
}
@ -984,7 +984,7 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
if signed {
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
let func = self.current_func.borrow().expect("func");
let res = func.new_local(None, result_type, "saturating_diff");
let res = func.new_local(self.loc, result_type, "saturating_diff");
let supports_native_type = self.is_native_int_type(result_type);
let overflow =
if supports_native_type {
@ -998,7 +998,7 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
_ => unreachable!(),
};
let overflow_func = self.context.get_builtin_function(func_name);
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None)
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(self.loc)], None)
}
else {
let func_name =
@ -1007,7 +1007,7 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
_ => unreachable!(),
};
let (int_result, overflow) = self.operation_with_overflow(func_name, lhs, rhs);
self.llbb().add_assignment(None, res, int_result);
self.llbb().add_assignment(self.loc, res, int_result);
overflow
};
@ -1020,10 +1020,10 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
let uint_max = self.gcc_not(self.gcc_int(unsigned_type, 0));
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
then_block.add_assignment(None, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
then_block.end_with_jump(None, after_block);
then_block.add_assignment(self.loc, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
then_block.end_with_jump(self.loc, after_block);
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
self.llbb().end_with_conditional(self.loc, overflow, then_block, after_block);
// NOTE: since jumps were added in a place rustc does not
// expect, the current block in the state need to be updated.