Merge commit 'dab6863ce8' into subtree-update_cg_gcc_2025-12-04
This commit is contained in:
commit
94f1bfe1fc
10 changed files with 324 additions and 278 deletions
|
|
@ -25,6 +25,43 @@ We encourage new contributors to join our communication channels and introduce t
|
|||
|
||||
## Understanding Core Concepts
|
||||
|
||||
### Sysroot & compilation flags
|
||||
|
||||
#### What *is* the sysroot?
|
||||
The **sysroot** is the directory that stores the compiled standard
|
||||
library (`core`, `alloc`, `std`, `test`, …) and compiler built-ins.
|
||||
Rustup ships these libraries **pre-compiled with LLVM**.
|
||||
|
||||
**rustc_codegen_gcc** replaces LLVM with the GCC backend.
|
||||
|
||||
The freshly compiled sysroot ends up in
|
||||
`build/build_sysroot/...`.
|
||||
|
||||
A rebuild of sysroot is needed when
|
||||
|
||||
* the backend changes in a way that affects code generation, or
|
||||
* the user switches toolchains / updates submodules.
|
||||
|
||||
Both backend and sysroot can be built using different [profiles](https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles).
|
||||
That is exactly what the `--sysroot`, `--release-sysroot` and `--release` flag supported by the build system script `y.sh` take care of.
|
||||
|
||||
|
||||
#### Typical flag combinations
|
||||
|
||||
| Command | Backend Profile | Sysroot Profile | Usage Scenario |
|
||||
|--------------------------------------------|-------------------------------|----------------------------------|------------------------------------------------------------|
|
||||
| `./y.sh build` | dev* | n/a | Build backend in dev mode with optimized dependencies without rebuilding sysroot |
|
||||
| `./y.sh build --release` | release (optimized) | n/a | Build backend in release mode with optimized dependencies without rebuilding sysroot |
|
||||
| `./y.sh build --release --sysroot` | release (optimized) | dev | Build backend in release mode with optimized dependencies and sysroot in dev mode (unoptimized) |
|
||||
| `./y.sh build --sysroot` | dev* | dev | Build backend in dev mode with optimized dependencies and sysroot in dev mode (unoptimized) |
|
||||
| `./y.sh build --release-sysroot --sysroot`| dev* | release (optimized) | Build backend in dev mode and sysroot in release mode, both with optimized dependencies |
|
||||
|
||||
\* In `dev` mode, dependencies are compiled with optimizations, while the code of the backend itself is not.
|
||||
|
||||
|
||||
Note: `--release-sysroot` must be used together with `--sysroot`.
|
||||
|
||||
|
||||
### Common Development Tasks
|
||||
|
||||
#### Running Specific Tests
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gccjit_sys"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "263da4f60b7bb5d6a5b21efda961741051ebdbf0e380a09118b03cce66a8c77e"
|
||||
checksum = "4f81d901767ddba371a619fa9bba657066a4d3c5607ee69bbb557c1c5ba9bf85"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -46,8 +46,16 @@ impl BuildArg {
|
|||
println!(
|
||||
r#"
|
||||
`build` command help:
|
||||
|
||||
--sysroot : Build with sysroot"#
|
||||
--sysroot : When used on its own, build backend in dev mode with optimized dependencies
|
||||
and sysroot in dev mode (unoptimized)
|
||||
When used together with --release, build backend in release mode with optimized dependencies
|
||||
When used together with --release-sysroot,
|
||||
build the sysroot in release mode with optimized dependencies instead of in dev mode
|
||||
--release-sysroot : When combined with --sysroot, additionally
|
||||
build the sysroot in release mode with optimized dependencies.
|
||||
It has no effect if `--sysroot` is not specified.
|
||||
It should not be used on its own.
|
||||
--sysroot-panic-abort : Build the sysroot without unwinding support"#
|
||||
);
|
||||
ConfigInfo::show_usage();
|
||||
println!(" --help : Show this help");
|
||||
|
|
|
|||
|
|
@ -459,14 +459,11 @@ impl ConfigInfo {
|
|||
|
||||
pub fn show_usage() {
|
||||
println!(
|
||||
"\
|
||||
--features [arg] : Add a new feature [arg]
|
||||
" --features [arg] : Add a new feature [arg]
|
||||
--target-triple [arg] : Set the target triple to [arg]
|
||||
--target [arg] : Set the target to [arg]
|
||||
--release : Build backend in release mode with optimized dependencies
|
||||
--out-dir : Location where the files will be generated
|
||||
--release : Build in release mode
|
||||
--release-sysroot : Build sysroot in release mode
|
||||
--sysroot-panic-abort : Build the sysroot without unwinding support
|
||||
--config-file : Location of the config file to be used
|
||||
--gcc-path : Location of the GCC root folder
|
||||
--cg_gcc-path : Location of the rustc_codegen_gcc root folder (used
|
||||
|
|
|
|||
|
|
@ -64,8 +64,6 @@ fn show_usage() {
|
|||
r#"
|
||||
`test` command help:
|
||||
|
||||
--release : Build codegen in release mode
|
||||
--sysroot-panic-abort : Build the sysroot without unwinding support.
|
||||
--features [arg] : Add a new feature [arg]
|
||||
--use-system-gcc : Use system installed libgccjit
|
||||
--build-only : Only build rustc_codegen_gcc then exits
|
||||
|
|
@ -92,7 +90,6 @@ struct TestArg {
|
|||
test_args: Vec<String>,
|
||||
nb_parts: Option<usize>,
|
||||
current_part: Option<usize>,
|
||||
sysroot_panic_abort: bool,
|
||||
config_info: ConfigInfo,
|
||||
sysroot_features: Vec<String>,
|
||||
keep_lto_tests: bool,
|
||||
|
|
@ -128,9 +125,6 @@ impl TestArg {
|
|||
test_arg.current_part =
|
||||
Some(get_number_after_arg(&mut args, "--current-part")?);
|
||||
}
|
||||
"--sysroot-panic-abort" => {
|
||||
test_arg.sysroot_panic_abort = true;
|
||||
}
|
||||
"--keep-lto-tests" => {
|
||||
test_arg.keep_lto_tests = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,51 +287,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
// 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!(),
|
||||
},
|
||||
OverflowOp::Add => "__builtin_add_overflow",
|
||||
OverflowOp::Sub => "__builtin_sub_overflow",
|
||||
OverflowOp::Mul => "__builtin_mul_overflow",
|
||||
}
|
||||
} else {
|
||||
let (func_name, width) = match oop {
|
||||
|
|
|
|||
|
|
@ -441,43 +441,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
| sym::saturating_sub => {
|
||||
match int_type_width_signed(args[0].layout.ty, self) {
|
||||
Some((width, signed)) => match name {
|
||||
sym::ctlz | sym::cttz => {
|
||||
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");
|
||||
sym::ctlz => self.count_leading_zeroes(width, args[0].immediate()),
|
||||
|
||||
let arg = args[0].immediate();
|
||||
let result = func.new_local(None, self.u32_type, "zeros");
|
||||
let zero = self.cx.gcc_zero(arg.get_type());
|
||||
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
|
||||
self.llbb().end_with_conditional(None, cond, then_block, else_block);
|
||||
|
||||
let zero_result = self.cx.gcc_uint(self.u32_type, width);
|
||||
then_block.add_assignment(None, result, zero_result);
|
||||
then_block.end_with_jump(None, after_block);
|
||||
|
||||
// NOTE: since jumps were added in a place
|
||||
// count_leading_zeroes() does not expect, the current block
|
||||
// in the state need to be updated.
|
||||
self.switch_to_block(else_block);
|
||||
|
||||
let zeros = match name {
|
||||
sym::ctlz => self.count_leading_zeroes(width, arg),
|
||||
sym::cttz => self.count_trailing_zeroes(width, arg),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.llbb().add_assignment(None, result, zeros);
|
||||
self.llbb().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()
|
||||
sym::ctlz_nonzero => {
|
||||
self.count_leading_zeroes_nonzero(width, args[0].immediate())
|
||||
}
|
||||
sym::cttz => self.count_trailing_zeroes(width, args[0].immediate()),
|
||||
sym::cttz_nonzero => {
|
||||
self.count_trailing_zeroes_nonzero(width, args[0].immediate())
|
||||
}
|
||||
sym::ctlz_nonzero => self.count_leading_zeroes(width, args[0].immediate()),
|
||||
sym::cttz_nonzero => self.count_trailing_zeroes(width, args[0].immediate()),
|
||||
sym::ctpop => self.pop_count(args[0].immediate()),
|
||||
sym::bswap => {
|
||||
if width == 8 {
|
||||
|
|
@ -912,179 +884,175 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
self.gcc_int_cast(result, result_type)
|
||||
}
|
||||
|
||||
fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
// TODO(antoyo): use width?
|
||||
let arg_type = arg.get_type();
|
||||
let result_type = self.u32_type;
|
||||
let arg = if arg_type.is_signed(self.cx) {
|
||||
let new_type = arg_type.to_unsigned(self.cx);
|
||||
self.gcc_int_cast(arg, new_type)
|
||||
fn count_zeroes(&mut self, width: u64, arg: RValue<'gcc>, count_leading: bool) -> RValue<'gcc> {
|
||||
// if arg is 0, early return 0, else call count_leading_zeroes_nonzero or count_trailing_zeroes_nonzero
|
||||
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 result = func.new_local(None, self.u32_type, "zeros");
|
||||
let zero = self.cx.gcc_zero(arg.get_type());
|
||||
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
|
||||
self.llbb().end_with_conditional(None, cond, then_block, else_block);
|
||||
|
||||
let zero_result = self.cx.gcc_uint(self.u32_type, width);
|
||||
then_block.add_assignment(None, result, zero_result);
|
||||
then_block.end_with_jump(None, after_block);
|
||||
|
||||
// NOTE: since jumps were added in a place count_xxxxing_zeroes_nonzero() does not expect,
|
||||
// the current block in the state need to be updated.
|
||||
self.switch_to_block(else_block);
|
||||
|
||||
let zeros = if count_leading {
|
||||
self.count_leading_zeroes_nonzero(width, arg)
|
||||
} else {
|
||||
arg
|
||||
self.count_trailing_zeroes_nonzero(width, arg)
|
||||
};
|
||||
let arg_type = arg.get_type();
|
||||
let count_leading_zeroes =
|
||||
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
|
||||
// instead of using is_uint().
|
||||
if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) {
|
||||
"__builtin_clz"
|
||||
}
|
||||
else if arg_type.is_ulong(self.cx) {
|
||||
"__builtin_clzl"
|
||||
}
|
||||
else if arg_type.is_ulonglong(self.cx) {
|
||||
"__builtin_clzll"
|
||||
}
|
||||
else if width == 128 {
|
||||
// Algorithm from: https://stackoverflow.com/a/28433850/389119
|
||||
let array_type = self.context.new_array_type(None, arg_type, 3);
|
||||
let result = self.current_func()
|
||||
.new_local(None, array_type, "count_loading_zeroes_results");
|
||||
self.llbb().add_assignment(None, result, zeros);
|
||||
self.llbb().end_with_jump(None, after_block);
|
||||
|
||||
let sixty_four = self.const_uint(arg_type, 64);
|
||||
let shift = self.lshr(arg, sixty_four);
|
||||
let high = self.gcc_int_cast(shift, self.u64_type);
|
||||
let low = self.gcc_int_cast(arg, self.u64_type);
|
||||
// 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);
|
||||
|
||||
let zero = self.context.new_rvalue_zero(self.usize_type);
|
||||
let one = self.context.new_rvalue_one(self.usize_type);
|
||||
let two = self.context.new_rvalue_from_long(self.usize_type, 2);
|
||||
|
||||
let clzll = self.context.get_builtin_function("__builtin_clzll");
|
||||
|
||||
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(self.location, first_elem, first_value);
|
||||
|
||||
let second_elem = self.context.new_array_access(self.location, result, one);
|
||||
let cast = self.gcc_int_cast(self.context.new_call(self.location, clzll, &[low]), arg_type);
|
||||
let second_value = self.add(cast, sixty_four);
|
||||
self.llbb()
|
||||
.add_assignment(self.location, second_elem, second_value);
|
||||
|
||||
let third_elem = self.context.new_array_access(self.location, result, two);
|
||||
let third_value = self.const_uint(arg_type, 128);
|
||||
self.llbb()
|
||||
.add_assignment(self.location, third_elem, third_value);
|
||||
|
||||
let not_high = self.context.new_unary_op(self.location, UnaryOp::LogicalNegate, self.u64_type, high);
|
||||
let not_low = self.context.new_unary_op(self.location, 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(self.location, index, self.i32_type);
|
||||
|
||||
let res = self.context.new_array_access(self.location, result, index);
|
||||
|
||||
return self.gcc_int_cast(res.to_rvalue(), result_type);
|
||||
}
|
||||
else {
|
||||
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clzll");
|
||||
let arg = self.context.new_cast(self.location, 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(self.location, count_leading_zeroes, &[arg]) - diff;
|
||||
return self.context.new_cast(self.location, res, result_type);
|
||||
};
|
||||
let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes);
|
||||
let res = self.context.new_call(self.location, count_leading_zeroes, &[arg]);
|
||||
self.context.new_cast(self.location, res, result_type)
|
||||
result.to_rvalue()
|
||||
}
|
||||
|
||||
fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
let arg_type = arg.get_type();
|
||||
let result_type = self.u32_type;
|
||||
let arg = if arg_type.is_signed(self.cx) {
|
||||
let new_type = arg_type.to_unsigned(self.cx);
|
||||
self.gcc_int_cast(arg, new_type)
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
let arg_type = arg.get_type();
|
||||
let (count_trailing_zeroes, expected_type) =
|
||||
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
|
||||
// instead of using is_uint().
|
||||
if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) {
|
||||
// NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
|
||||
("__builtin_ctz", self.cx.uint_type)
|
||||
}
|
||||
else if arg_type.is_ulong(self.cx) {
|
||||
("__builtin_ctzl", self.cx.ulong_type)
|
||||
}
|
||||
else if arg_type.is_ulonglong(self.cx) {
|
||||
("__builtin_ctzll", self.cx.ulonglong_type)
|
||||
}
|
||||
else if arg_type.is_u128(self.cx) {
|
||||
// Adapted from the algorithm to count leading zeroes from: https://stackoverflow.com/a/28433850/389119
|
||||
let array_type = self.context.new_array_type(None, arg_type, 3);
|
||||
let result = self.current_func()
|
||||
.new_local(None, array_type, "count_loading_zeroes_results");
|
||||
|
||||
let sixty_four = self.gcc_int(arg_type, 64);
|
||||
let shift = self.gcc_lshr(arg, sixty_four);
|
||||
let high = self.gcc_int_cast(shift, self.u64_type);
|
||||
let low = self.gcc_int_cast(arg, self.u64_type);
|
||||
|
||||
let zero = self.context.new_rvalue_zero(self.usize_type);
|
||||
let one = self.context.new_rvalue_one(self.usize_type);
|
||||
let two = self.context.new_rvalue_from_long(self.usize_type, 2);
|
||||
|
||||
let ctzll = self.context.get_builtin_function("__builtin_ctzll");
|
||||
|
||||
let first_elem = self.context.new_array_access(self.location, result, zero);
|
||||
let first_value = self.gcc_int_cast(self.context.new_call(self.location, ctzll, &[low]), arg_type);
|
||||
self.llbb()
|
||||
.add_assignment(self.location, first_elem, first_value);
|
||||
|
||||
let second_elem = self.context.new_array_access(self.location, result, one);
|
||||
let second_value = self.gcc_add(self.gcc_int_cast(self.context.new_call(self.location, ctzll, &[high]), arg_type), sixty_four);
|
||||
self.llbb()
|
||||
.add_assignment(self.location, second_elem, second_value);
|
||||
|
||||
let third_elem = self.context.new_array_access(self.location, result, two);
|
||||
let third_value = self.gcc_int(arg_type, 128);
|
||||
self.llbb()
|
||||
.add_assignment(self.location, third_elem, third_value);
|
||||
|
||||
let not_low = self.context.new_unary_op(self.location, UnaryOp::LogicalNegate, self.u64_type, low);
|
||||
let not_high = self.context.new_unary_op(self.location, 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(self.location, index, self.i32_type);
|
||||
|
||||
let res = self.context.new_array_access(self.location, 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(self.location, 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(self.location, UnaryOp::BitwiseNegate, arg_type, arg);
|
||||
let cond = self.context.new_comparison(self.location, ComparisonOp::Equals, masked, mask);
|
||||
let diff = diff * self.context.new_cast(self.location, cond, self.int_type);
|
||||
let res = self.context.new_call(self.location, count_trailing_zeroes, &[casted_arg]) - diff;
|
||||
return self.context.new_cast(self.location, res, result_type);
|
||||
fn count_zeroes_nonzero(
|
||||
&mut self,
|
||||
width: u64,
|
||||
arg: RValue<'gcc>,
|
||||
count_leading: bool,
|
||||
) -> RValue<'gcc> {
|
||||
// Pre-condition: arg is guaranteed to not be 0 by caller
|
||||
fn use_builtin_function<'a, 'gcc, 'tcx>(
|
||||
builder: &mut Builder<'a, 'gcc, 'tcx>,
|
||||
builtin: &str,
|
||||
arg: RValue<'gcc>,
|
||||
arg_type: gccjit::Type<'gcc>,
|
||||
expected_type: gccjit::Type<'gcc>,
|
||||
) -> RValue<'gcc> {
|
||||
let arg = if arg_type != expected_type {
|
||||
builder.context.new_cast(builder.location, arg, expected_type)
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
|
||||
let arg = if arg_type != expected_type {
|
||||
self.context.new_cast(self.location, arg, expected_type)
|
||||
let builtin = builder.context.get_builtin_function(builtin);
|
||||
let res = builder.context.new_call(builder.location, builtin, &[arg]);
|
||||
builder.context.new_cast(builder.location, res, builder.u32_type)
|
||||
}
|
||||
|
||||
// TODO(antoyo): use width?
|
||||
let result_type = self.u32_type;
|
||||
let mut arg_type = arg.get_type();
|
||||
let arg = if arg_type.is_signed(self.cx) {
|
||||
arg_type = arg_type.to_unsigned(self.cx);
|
||||
self.gcc_int_cast(arg, arg_type)
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
let res = self.context.new_call(self.location, count_trailing_zeroes, &[arg]);
|
||||
self.context.new_cast(self.location, res, result_type)
|
||||
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
|
||||
// instead of using is_uint().
|
||||
if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) {
|
||||
let builtin = if count_leading { "__builtin_clz" } else { "__builtin_ctz" };
|
||||
use_builtin_function(self, builtin, arg, arg_type, self.cx.uint_type)
|
||||
} else if arg_type.is_ulong(self.cx) {
|
||||
let builtin = if count_leading { "__builtin_clzl" } else { "__builtin_ctzl" };
|
||||
use_builtin_function(self, builtin, arg, arg_type, self.cx.uint_type)
|
||||
} else if arg_type.is_ulonglong(self.cx) {
|
||||
let builtin = if count_leading { "__builtin_clzll" } else { "__builtin_ctzll" };
|
||||
use_builtin_function(self, builtin, arg, arg_type, self.cx.uint_type)
|
||||
} else if width == 128 {
|
||||
// arg is guaranteed to not be 0, so either its 64 high or 64 low bits are not 0
|
||||
// __buildin_clzll is UB when called with 0, so call it on the 64 high bits if they are not 0,
|
||||
// else call it on the 64 low bits and add 64. In the else case, 64 low bits can't be 0
|
||||
// because arg is not 0.
|
||||
// __buildin_ctzll is UB when called with 0, so call it on the 64 low bits if they are not 0,
|
||||
// else call it on the 64 high bits and add 64. In the else case, 64 high bits can't be 0
|
||||
// because arg is not 0.
|
||||
|
||||
let result = self.current_func().new_local(None, result_type, "count_zeroes_results");
|
||||
|
||||
let cz_then_block = self.current_func().new_block("cz_then");
|
||||
let cz_else_block = self.current_func().new_block("cz_else");
|
||||
let cz_after_block = self.current_func().new_block("cz_after");
|
||||
|
||||
let low = self.gcc_int_cast(arg, self.u64_type);
|
||||
let sixty_four = self.const_uint(arg_type, 64);
|
||||
let shift = self.lshr(arg, sixty_four);
|
||||
let high = self.gcc_int_cast(shift, self.u64_type);
|
||||
|
||||
let (first, second, builtin) = if count_leading {
|
||||
(low, high, self.context.get_builtin_function("__builtin_clzll"))
|
||||
} else {
|
||||
(high, low, self.context.get_builtin_function("__builtin_ctzll"))
|
||||
};
|
||||
|
||||
let zero_64 = self.const_uint(self.u64_type, 0);
|
||||
let cond = self.gcc_icmp(IntPredicate::IntNE, second, zero_64);
|
||||
self.llbb().end_with_conditional(self.location, cond, cz_then_block, cz_else_block);
|
||||
self.switch_to_block(cz_then_block);
|
||||
|
||||
let result_128 =
|
||||
self.gcc_int_cast(self.context.new_call(None, builtin, &[second]), result_type);
|
||||
|
||||
cz_then_block.add_assignment(self.location, result, result_128);
|
||||
cz_then_block.end_with_jump(self.location, cz_after_block);
|
||||
|
||||
self.switch_to_block(cz_else_block);
|
||||
let count_more_zeroes =
|
||||
self.gcc_int_cast(self.context.new_call(None, builtin, &[first]), result_type);
|
||||
let sixty_four_result_type = self.const_uint(result_type, 64);
|
||||
let count_result_type = self.add(count_more_zeroes, sixty_four_result_type);
|
||||
cz_else_block.add_assignment(self.location, result, count_result_type);
|
||||
cz_else_block.end_with_jump(self.location, cz_after_block);
|
||||
self.switch_to_block(cz_after_block);
|
||||
result.to_rvalue()
|
||||
} else {
|
||||
let byte_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, byte_diff * 8);
|
||||
let ull_arg = self.context.new_cast(self.location, arg, self.ulonglong_type);
|
||||
|
||||
let res = if count_leading {
|
||||
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clzll");
|
||||
self.context.new_call(self.location, count_leading_zeroes, &[ull_arg]) - diff
|
||||
} else {
|
||||
let count_trailing_zeroes = self.context.get_builtin_function("__builtin_ctzll");
|
||||
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(
|
||||
self.location,
|
||||
UnaryOp::BitwiseNegate,
|
||||
arg_type,
|
||||
arg,
|
||||
);
|
||||
let cond =
|
||||
self.context.new_comparison(self.location, ComparisonOp::Equals, masked, mask);
|
||||
let diff = diff * self.context.new_cast(self.location, cond, self.int_type);
|
||||
|
||||
self.context.new_call(self.location, count_trailing_zeroes, &[ull_arg]) - diff
|
||||
};
|
||||
self.context.new_cast(self.location, res, result_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
self.count_zeroes(width, arg, true)
|
||||
}
|
||||
|
||||
fn count_leading_zeroes_nonzero(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
// Pre-condition: arg is guaranteed to not be 0 by caller, else count_leading_zeros should be used
|
||||
self.count_zeroes_nonzero(width, arg, true)
|
||||
}
|
||||
|
||||
fn count_trailing_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
self.count_zeroes(width, arg, false)
|
||||
}
|
||||
|
||||
fn count_trailing_zeroes_nonzero(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||
// Pre-condition: arg is guaranteed to not be 0 by caller, else count_trailing_zeros should be used
|
||||
self.count_zeroes_nonzero(width, arg, false)
|
||||
}
|
||||
|
||||
fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
|
||||
|
|
@ -1196,14 +1164,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
let res = func.new_local(self.location, result_type, "saturating_sum");
|
||||
let supports_native_type = self.is_native_int_type(result_type);
|
||||
let overflow = if supports_native_type {
|
||||
let func_name = match width {
|
||||
8 => "__builtin_add_overflow",
|
||||
16 => "__builtin_add_overflow",
|
||||
32 => "__builtin_sadd_overflow",
|
||||
64 => "__builtin_saddll_overflow",
|
||||
128 => "__builtin_add_overflow",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let func_name = "__builtin_add_overflow";
|
||||
let overflow_func = self.context.get_builtin_function(func_name);
|
||||
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(self.location)], None)
|
||||
} else {
|
||||
|
|
@ -1267,14 +1228,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
let res = func.new_local(self.location, result_type, "saturating_diff");
|
||||
let supports_native_type = self.is_native_int_type(result_type);
|
||||
let overflow = if supports_native_type {
|
||||
let func_name = match width {
|
||||
8 => "__builtin_sub_overflow",
|
||||
16 => "__builtin_sub_overflow",
|
||||
32 => "__builtin_ssub_overflow",
|
||||
64 => "__builtin_ssubll_overflow",
|
||||
128 => "__builtin_sub_overflow",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let func_name = "__builtin_sub_overflow";
|
||||
let overflow_func = self.context.get_builtin_function(func_name);
|
||||
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(self.location)], None)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ mod type_of;
|
|||
use std::any::Any;
|
||||
use std::ffi::CString;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
|
@ -180,14 +181,18 @@ pub struct GccCodegenBackend {
|
|||
|
||||
static LTO_SUPPORTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn libgccjit_path(sysroot_path: &Path) -> PathBuf {
|
||||
let sysroot_lib_dir = sysroot_path.join("lib");
|
||||
sysroot_lib_dir.join("libgccjit.so")
|
||||
}
|
||||
|
||||
fn load_libgccjit_if_needed(sysroot_path: &Path) {
|
||||
if gccjit::is_loaded() {
|
||||
// Do not load a libgccjit second time.
|
||||
return;
|
||||
}
|
||||
|
||||
let sysroot_lib_dir = sysroot_path.join("lib");
|
||||
let libgccjit_target_lib_file = sysroot_lib_dir.join("libgccjit.so");
|
||||
let libgccjit_target_lib_file = libgccjit_path(sysroot_path);
|
||||
let path = libgccjit_target_lib_file.to_str().expect("libgccjit path");
|
||||
|
||||
let string = CString::new(path).expect("string to libgccjit path");
|
||||
|
|
@ -207,7 +212,16 @@ impl CodegenBackend for GccCodegenBackend {
|
|||
}
|
||||
|
||||
fn init(&self, sess: &Session) {
|
||||
load_libgccjit_if_needed(sess.opts.sysroot.path());
|
||||
// We use all_paths() instead of only path() in case the path specified by --sysroot is
|
||||
// invalid.
|
||||
// This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null.
|
||||
for path in sess.opts.sysroot.all_paths() {
|
||||
let libgccjit_target_lib_file = libgccjit_path(path);
|
||||
if let Ok(true) = fs::exists(libgccjit_target_lib_file) {
|
||||
load_libgccjit_if_needed(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -91,7 +91,19 @@ pub fn main_inner(profile: Profile) {
|
|||
}
|
||||
}
|
||||
match profile {
|
||||
Profile::Debug => {}
|
||||
Profile::Debug => {
|
||||
if test_target.is_ok() {
|
||||
// m68k doesn't have lubsan for now
|
||||
compiler.args(["-C", "llvm-args=sanitize-undefined"]);
|
||||
} else {
|
||||
compiler.args([
|
||||
"-C",
|
||||
"llvm-args=sanitize-undefined",
|
||||
"-C",
|
||||
"link-args=-lubsan",
|
||||
]);
|
||||
}
|
||||
}
|
||||
Profile::Release => {
|
||||
compiler.args(["-C", "opt-level=3", "-C", "lto=no"]);
|
||||
}
|
||||
|
|
|
|||
72
compiler/rustc_codegen_gcc/tests/run/int_intrinsics.rs
Normal file
72
compiler/rustc_codegen_gcc/tests/run/int_intrinsics.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Compiler:
|
||||
//
|
||||
// Run-time:
|
||||
#![feature(core_intrinsics, intrinsics)]
|
||||
#![no_main]
|
||||
|
||||
use std::intrinsics::black_box;
|
||||
|
||||
#[rustc_intrinsic]
|
||||
pub const fn ctlz<T: Copy>(_x: T) -> u32;
|
||||
|
||||
#[rustc_intrinsic]
|
||||
pub const fn cttz<T: Copy>(_x: T) -> u32;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
||||
macro_rules! check {
|
||||
($func_name:ident, $input:expr, $expected:expr, $res_ident:ident) => {{
|
||||
$res_ident += 1;
|
||||
if $func_name(black_box($input)) != $expected {
|
||||
return $res_ident;
|
||||
}
|
||||
}};
|
||||
}
|
||||
let mut res = 0;
|
||||
check!(ctlz, 0_u128, 128_u32, res);
|
||||
check!(ctlz, 1_u128, 127_u32, res);
|
||||
check!(ctlz, 0x4000_0000_0000_0000_0000_0000_0000_0000_u128, 1_u32, res);
|
||||
check!(ctlz, 0x8000_0000_0000_0000_0000_0000_0000_0000_u128, 0_u32, res);
|
||||
check!(cttz, 0_u128, 128_u32, res);
|
||||
check!(cttz, 1_u128, 0_u32, res);
|
||||
check!(cttz, 2_u128, 1_u32, res);
|
||||
check!(cttz, 0x8000_0000_0000_0000_0000_0000_0000_0000_u128, 127_u32, res);
|
||||
|
||||
check!(ctlz, 0_u64, 64_u32, res);
|
||||
check!(ctlz, 1_u64, 63_u32, res);
|
||||
check!(ctlz, 0x4000_0000_0000_0000_u64, 1_u32, res);
|
||||
check!(ctlz, 0x8000_0000_0000_0000_u64, 0_u32, res);
|
||||
check!(cttz, 0_u64, 64_u32, res);
|
||||
check!(cttz, 1_u64, 0_u32, res);
|
||||
check!(cttz, 2_u64, 1_u32, res);
|
||||
check!(cttz, 0x8000_0000_0000_0000_u64, 63_u32, res);
|
||||
|
||||
check!(ctlz, 0_u32, 32_u32, res);
|
||||
check!(ctlz, 1_u32, 31_u32, res);
|
||||
check!(ctlz, 0x4000_0000_u32, 1_u32, res);
|
||||
check!(ctlz, 0x8000_0000_u32, 0_u32, res);
|
||||
check!(cttz, 0_u32, 32_u32, res);
|
||||
check!(cttz, 1_u32, 0_u32, res);
|
||||
check!(cttz, 2_u32, 1_u32, res);
|
||||
check!(cttz, 0x8000_0000_u32, 31_u32, res);
|
||||
|
||||
check!(ctlz, 0_u16, 16_u32, res);
|
||||
check!(ctlz, 1_u16, 15_u32, res);
|
||||
check!(ctlz, 0x4000_u16, 1_u32, res);
|
||||
check!(ctlz, 0x8000_u16, 0_u32, res);
|
||||
check!(cttz, 0_u16, 16_u32, res);
|
||||
check!(cttz, 1_u16, 0_u32, res);
|
||||
check!(cttz, 2_u16, 1_u32, res);
|
||||
check!(cttz, 0x8000_u16, 15_u32, res);
|
||||
|
||||
check!(ctlz, 0_u8, 8_u32, res);
|
||||
check!(ctlz, 1_u8, 7_u32, res);
|
||||
check!(ctlz, 0x40_u8, 1_u32, res);
|
||||
check!(ctlz, 0x80_u8, 0_u32, res);
|
||||
check!(cttz, 0_u8, 8_u32, res);
|
||||
check!(cttz, 1_u8, 0_u32, res);
|
||||
check!(cttz, 2_u8, 1_u32, res);
|
||||
check!(cttz, 0x80_u8, 7_u32, res);
|
||||
|
||||
0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue