Skip to content

Commit 112cc78

Browse files
antoyoFractalFir
authored andcommitted
Merge pull request #684 from FractalFir/master
Fix to 128 bit int unaligned loads
2 parents 9aec231 + 1afdb55 commit 112cc78

File tree

5 files changed

+236
-3
lines changed

5 files changed

+236
-3
lines changed

build_system/src/fuzz.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use std::ffi::OsStr;
2+
use std::path::Path;
3+
4+
use crate::utils::run_command_with_output;
5+
6+
fn show_usage() {
7+
println!(
8+
r#"
9+
`fuzz` command help:
10+
--help : Show this help"#
11+
);
12+
}
13+
14+
pub fn run() -> Result<(), String> {
15+
// We skip binary name and the `fuzz` command.
16+
let mut args = std::env::args().skip(2);
17+
let mut start = 0;
18+
let mut count = 100;
19+
let mut threads =
20+
std::thread::available_parallelism().map(|threads| threads.get()).unwrap_or(1);
21+
while let Some(arg) = args.next() {
22+
match arg.as_str() {
23+
"--help" => {
24+
show_usage();
25+
return Ok(());
26+
}
27+
"--start" => {
28+
start =
29+
str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?)
30+
.map_err(|err| (format!("Fuzz start not a number {err:?}!")))?;
31+
}
32+
"--count" => {
33+
count =
34+
str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?)
35+
.map_err(|err| (format!("Fuzz count not a number {err:?}!")))?;
36+
}
37+
"-j" | "--jobs" => {
38+
threads = str::parse(
39+
&args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?,
40+
)
41+
.map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?;
42+
}
43+
_ => return Err(format!("Unknown option {}", arg)),
44+
}
45+
}
46+
// Ensure that we have a cloned version of rustlantis on hand.
47+
if !std::fs::exists("rustlantis").unwrap_or_default() {
48+
let cmd: &[&dyn AsRef<OsStr>] =
49+
&[&"git", &"clone", &"https://github.com/cbeuw/rustlantis.git"];
50+
run_command_with_output(cmd, Some(&Path::new(".")))?;
51+
}
52+
// Ensure that we are on the newest rustlantis commit.
53+
let cmd: &[&dyn AsRef<OsStr>] = &[&"git", &"pull", &"origin"];
54+
run_command_with_output(cmd, Some(&Path::new("rustlantis")))?;
55+
// Build the release version of rustlantis
56+
let cmd: &[&dyn AsRef<OsStr>] = &[&"cargo", &"build", &"--release"];
57+
run_command_with_output(cmd, Some(&Path::new("rustlantis")))?;
58+
fuzz_range(start, start + count, threads);
59+
Ok(())
60+
}
61+
fn fuzz_range(start: u64, end: u64, threads: usize) {
62+
use std::sync::Arc;
63+
use std::sync::atomic::{AtomicU64, Ordering};
64+
use std::time::{Duration, Instant};
65+
66+
let total = end - start;
67+
let start = Arc::new(AtomicU64::new(start));
68+
let start_time = Instant::now();
69+
for _ in 0..threads {
70+
let start = start.clone();
71+
std::thread::spawn(move || {
72+
while start.load(Ordering::Relaxed) < end {
73+
let next = start.fetch_add(1, Ordering::Relaxed);
74+
75+
match test(next) {
76+
Err(err) => {
77+
println!("test({}) failed because {err:?}", next);
78+
let mut out_path: std::path::PathBuf =
79+
"target/fuzz/compiletime_error".into();
80+
std::fs::create_dir_all(&out_path).unwrap();
81+
out_path.push(&format!("fuzz{next}.rs"));
82+
std::fs::copy(err, out_path).unwrap();
83+
}
84+
Ok(Err(err)) => {
85+
println!("The LLVM and GCC results don't match for {err:?}");
86+
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_error".into();
87+
std::fs::create_dir_all(&out_path).unwrap();
88+
out_path.push(&format!("fuzz{next}.rs"));
89+
std::fs::copy(err, out_path).unwrap();
90+
}
91+
Ok(Ok(())) => (),
92+
}
93+
}
94+
});
95+
}
96+
while start.load(Ordering::Relaxed) < end {
97+
let hundred_millis = Duration::from_millis(500);
98+
std::thread::sleep(hundred_millis);
99+
let remaining = end - start.load(Ordering::Relaxed);
100+
let fuzzed = total - remaining;
101+
let iter_per_sec = fuzzed as f64 / start_time.elapsed().as_secs_f64();
102+
println!(
103+
"fuzzed {fuzzed} cases({}%), at rate {iter_per_sec} iter/s, remaining ~{}s",
104+
(100 * fuzzed) as f64 / total as f64,
105+
(remaining as f64) / iter_per_sec
106+
)
107+
}
108+
}
109+
/// Builds & runs a file with LLVM.
110+
fn debug_llvm(path: &std::path::Path) -> Result<Vec<u8>, String> {
111+
let exe_path = path.with_extension("llvm_elf");
112+
let output = std::process::Command::new("rustc")
113+
.arg(path)
114+
.arg("-o")
115+
.arg(&exe_path)
116+
.output()
117+
.map_err(|err| format!("{err:?}"))?;
118+
if !output.status.success() {
119+
return Err(format!("LLVM compilation failed:{output:?}"));
120+
}
121+
let output =
122+
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
123+
if !output.status.success() {
124+
return Err(format!(
125+
"The program at {path:?}, compiled with LLVM, exited unsuccessfully:{output:?}"
126+
));
127+
}
128+
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
129+
Ok(output.stdout)
130+
}
131+
132+
/// Builds & runs a file with GCC.
133+
fn release_gcc(path: &std::path::Path) -> Result<Vec<u8>, String> {
134+
let exe_path = path.with_extension("gcc_elf");
135+
let output = std::process::Command::new("./y.sh")
136+
.arg("rustc")
137+
.arg(path)
138+
.arg("-O")
139+
.arg("-o")
140+
.arg(&exe_path)
141+
.output()
142+
.map_err(|err| format!("{err:?}"))?;
143+
if !output.status.success() {
144+
return Err(format!("GCC compilation failed:{output:?}"));
145+
}
146+
let output =
147+
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
148+
if !output.status.success() {
149+
return Err(format!(
150+
"The program at {path:?}, compiled with GCC, exited unsuccessfully:{output:?}"
151+
));
152+
}
153+
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
154+
Ok(output.stdout)
155+
}
156+
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
157+
fn test(seed: u64) -> Result<Result<(), std::path::PathBuf>, String> {
158+
let source_file = generate(seed)?;
159+
let llvm_res = debug_llvm(&source_file)?;
160+
let gcc_res = release_gcc(&source_file)?;
161+
if llvm_res != gcc_res {
162+
Ok(Err(source_file))
163+
} else {
164+
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
165+
Ok(Ok(()))
166+
}
167+
}
168+
/// Generates a new rustlantis file for us to run tests on.
169+
fn generate(seed: u64) -> Result<std::path::PathBuf, String> {
170+
use std::io::Write;
171+
let mut out_path = std::env::temp_dir();
172+
out_path.push(&format!("fuzz{seed}.rs"));
173+
// We need to get the command output here.
174+
let out = std::process::Command::new("cargo")
175+
.args(["run", "--release", "--bin", "generate"])
176+
.arg(&format!("{seed}"))
177+
.current_dir("rustlantis")
178+
.output()
179+
.map_err(|err| format!("{err:?}"))?;
180+
std::fs::File::create(&out_path)
181+
.map_err(|err| format!("{err:?}"))?
182+
.write_all(&out.stdout)
183+
.map_err(|err| format!("{err:?}"))?;
184+
Ok(out_path)
185+
}

build_system/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ Commands:
4242
test : Runs tests for the project.
4343
info : Displays information about the build environment and project configuration.
4444
clone-gcc : Clones the GCC compiler from a specified source.
45-
fmt : Runs rustfmt"
45+
fmt : Runs rustfmt
46+
fuzz : Fuzzes `cg_gcc` using rustlantis"
4647
);
4748
}
4849

@@ -56,6 +57,7 @@ pub enum Command {
5657
Test,
5758
Info,
5859
Fmt,
60+
Fuzz,
5961
}
6062

6163
fn main() {
@@ -75,6 +77,7 @@ fn main() {
7577
Some("info") => Command::Info,
7678
Some("clone-gcc") => Command::CloneGcc,
7779
Some("fmt") => Command::Fmt,
80+
Some("fuzz") => Command::Fuzz,
7881
Some("--help") => {
7982
usage();
8083
process::exit(0);
@@ -97,6 +100,7 @@ fn main() {
97100
Command::Info => info::run(),
98101
Command::CloneGcc => clone_gcc::run(),
99102
Command::Fmt => fmt::run(),
103+
Command::Fuzz => fuzz::run(),
100104
} {
101105
eprintln!("Command failed to run: {e}");
102106
process::exit(1);

src/builder.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
924924
// dereference after a drop, for instance.
925925
// FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
926926
// Ideally, we shouldn't need to do this check.
927-
let aligned_type = if pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type {
927+
// FractalFir: the `align == self.int128_align` check ensures we *do* call `get_aligned` if
928+
// the alignment of a `u128`/`i128` is not the one mandated by the ABI. This ensures we handle
929+
// under-aligned loads correctly.
930+
let aligned_type = if (pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type)
931+
&& align == self.int128_align
932+
{
928933
pointee_ty
929934
} else {
930935
pointee_ty.get_aligned(align.bytes())

src/context.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::collections::HashMap;
44
use gccjit::{
55
Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
66
};
7-
use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
7+
use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
88
use rustc_codegen_ssa::base::wants_msvc_seh;
99
use rustc_codegen_ssa::errors as ssa_errors;
1010
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods};
@@ -135,6 +135,9 @@ pub struct CodegenCx<'gcc, 'tcx> {
135135

136136
#[cfg(feature = "master")]
137137
pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>,
138+
/// The alignment of a u128/i128 type.
139+
// We cache this, since it is needed for alignment checks during loads.
140+
pub int128_align: Align,
138141
}
139142

140143
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@@ -226,6 +229,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
226229
}
227230

228231
let mut cx = Self {
232+
int128_align: tcx
233+
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.i128))
234+
.expect("Can't get the layout of `i128`")
235+
.align
236+
.abi,
229237
const_cache: Default::default(),
230238
codegen_unit,
231239
context,

tests/run/packed_u128.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Compiler:
2+
//
3+
// Run-time:
4+
// status: 0
5+
6+
#![feature(no_core)]
7+
#![no_std]
8+
#![no_core]
9+
#![no_main]
10+
11+
extern crate mini_core;
12+
use intrinsics::black_box;
13+
use mini_core::*;
14+
#[repr(packed(1))]
15+
pub struct ScalarInt {
16+
data: u128,
17+
size: u8,
18+
}
19+
#[inline(never)]
20+
#[no_mangle]
21+
fn read_data(a: &ScalarInt) {
22+
black_box(a.data);
23+
}
24+
25+
#[no_mangle]
26+
extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 {
27+
let data =
28+
[black_box(ScalarInt { data: 0, size: 1 }), black_box(ScalarInt { data: 0, size: 1 })];
29+
read_data(&data[1]);
30+
0
31+
}

0 commit comments

Comments
 (0)