From c41bf9603972362ee2936247d81b21d495081470 Mon Sep 17 00:00:00 2001
From: DianQK <dianqk@dianqk.net>
Date: Sat, 18 Nov 2023 20:01:01 +0800
Subject: [PATCH] Add thinlto support to codegen, assembly and coverage tests

---
 src/tools/compiletest/src/runtest.rs | 70 ++++++++++++++++++++++------
 tests/assembly/thin-lto.rs           |  8 ++++
 tests/codegen/thin-lto.rs            |  7 +++
 tests/coverage/thin-lto.cov-map      |  8 ++++
 tests/coverage/thin-lto.coverage     |  5 ++
 tests/coverage/thin-lto.rs           |  4 ++
 6 files changed, 87 insertions(+), 15 deletions(-)
 create mode 100644 tests/assembly/thin-lto.rs
 create mode 100644 tests/codegen/thin-lto.rs
 create mode 100644 tests/coverage/thin-lto.cov-map
 create mode 100644 tests/coverage/thin-lto.coverage
 create mode 100644 tests/coverage/thin-lto.rs

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 10a11e8e291..af224360319 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -474,14 +474,12 @@ impl<'test> TestCx<'test> {
             self.fatal("missing --coverage-dump");
         };
 
-        let proc_res = self.compile_test_and_save_ir();
+        let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir();
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
         drop(proc_res);
 
-        let llvm_ir_path = self.output_base_name().with_extension("ll");
-
         let mut dump_command = Command::new(coverage_dump_path);
         dump_command.arg(llvm_ir_path);
         let proc_res = self.run_command_to_procres(&mut dump_command);
@@ -2785,10 +2783,54 @@ impl<'test> TestCx<'test> {
         proc_res.fatal(None, || on_failure(*self));
     }
 
+    fn get_output_file(&self, extension: &str) -> TargetLocation {
+        let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
+        if thin_lto {
+            TargetLocation::ThisDirectory(self.output_base_dir())
+        } else {
+            // This works with both `--emit asm` (as default output name for the assembly)
+            // and `ptx-linker` because the latter can write output at requested location.
+            let output_path = self.output_base_name().with_extension(extension);
+            let output_file = TargetLocation::ThisFile(output_path.clone());
+            output_file
+        }
+    }
+
+    fn get_filecheck_file(&self, extension: &str) -> PathBuf {
+        let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
+        if thin_lto {
+            let name = self.testpaths.file.file_stem().unwrap().to_str().unwrap();
+            let canonical_name = name.replace('-', "_");
+            let mut output_file = None;
+            for entry in self.output_base_dir().read_dir().unwrap() {
+                if let Ok(entry) = entry {
+                    let entry_path = entry.path();
+                    let entry_file = entry_path.file_name().unwrap().to_str().unwrap();
+                    if entry_file.starts_with(&format!("{}.{}", name, canonical_name))
+                        && entry_file.ends_with(extension)
+                    {
+                        assert!(
+                            output_file.is_none(),
+                            "thinlto doesn't support multiple cgu tests"
+                        );
+                        output_file = Some(entry_file.to_string());
+                    }
+                }
+            }
+            if let Some(output_file) = output_file {
+                self.output_base_dir().join(output_file)
+            } else {
+                self.output_base_name().with_extension(extension)
+            }
+        } else {
+            self.output_base_name().with_extension(extension)
+        }
+    }
+
     // codegen tests (using FileCheck)
 
-    fn compile_test_and_save_ir(&self) -> ProcRes {
-        let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
+    fn compile_test_and_save_ir(&self) -> (ProcRes, PathBuf) {
+        let output_file = self.get_output_file("ll");
         let input_file = &self.testpaths.file;
         let rustc = self.make_compile_args(
             input_file,
@@ -2799,15 +2841,13 @@ impl<'test> TestCx<'test> {
             Vec::new(),
         );
 
-        self.compose_and_run_compiler(rustc, None)
+        let proc_res = self.compose_and_run_compiler(rustc, None);
+        let output_path = self.get_filecheck_file("ll");
+        (proc_res, output_path)
     }
 
     fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
-        // This works with both `--emit asm` (as default output name for the assembly)
-        // and `ptx-linker` because the latter can write output at requested location.
-        let output_path = self.output_base_name().with_extension("s");
-
-        let output_file = TargetLocation::ThisFile(output_path.clone());
+        let output_file = self.get_output_file("s");
         let input_file = &self.testpaths.file;
 
         let mut emit = Emit::None;
@@ -2837,7 +2877,9 @@ impl<'test> TestCx<'test> {
             Vec::new(),
         );
 
-        (self.compose_and_run_compiler(rustc, None), output_path)
+        let proc_res = self.compose_and_run_compiler(rustc, None);
+        let output_path = self.get_filecheck_file("s");
+        (proc_res, output_path)
     }
 
     fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
@@ -2870,7 +2912,7 @@ impl<'test> TestCx<'test> {
             self.fatal("missing --llvm-filecheck");
         }
 
-        let proc_res = self.compile_test_and_save_ir();
+        let (proc_res, output_path) = self.compile_test_and_save_ir();
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
@@ -2878,8 +2920,6 @@ impl<'test> TestCx<'test> {
         if let Some(PassMode::Build) = self.pass_mode() {
             return;
         }
-
-        let output_path = self.output_base_name().with_extension("ll");
         let proc_res = self.verify_with_filecheck(&output_path);
         if !proc_res.status.success() {
             self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
diff --git a/tests/assembly/thin-lto.rs b/tests/assembly/thin-lto.rs
new file mode 100644
index 00000000000..deb8fd21d14
--- /dev/null
+++ b/tests/assembly/thin-lto.rs
@@ -0,0 +1,8 @@
+// compile-flags: -O -C lto=thin -C prefer-dynamic=no
+// only-x86_64-unknown-linux-gnu
+// assembly-output: emit-asm
+
+// CHECK: main
+
+pub fn main() {
+}
diff --git a/tests/codegen/thin-lto.rs b/tests/codegen/thin-lto.rs
new file mode 100644
index 00000000000..7991cad7a0c
--- /dev/null
+++ b/tests/codegen/thin-lto.rs
@@ -0,0 +1,7 @@
+// compile-flags: -O -C lto=thin -C prefer-dynamic=no
+// only-x86_64-unknown-linux-gnu
+
+// CHECK: main
+
+pub fn main() {
+}
diff --git a/tests/coverage/thin-lto.cov-map b/tests/coverage/thin-lto.cov-map
new file mode 100644
index 00000000000..7e84e398f84
--- /dev/null
+++ b/tests/coverage/thin-lto.cov-map
@@ -0,0 +1,8 @@
+Function name: thin_lto::main
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 03, 01, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 3, 1) to (start + 1, 2)
+
diff --git a/tests/coverage/thin-lto.coverage b/tests/coverage/thin-lto.coverage
new file mode 100644
index 00000000000..21abb5dce04
--- /dev/null
+++ b/tests/coverage/thin-lto.coverage
@@ -0,0 +1,5 @@
+   LL|       |// compile-flags: -O -C lto=thin -C prefer-dynamic=no
+   LL|       |
+   LL|      1|pub fn main() {
+   LL|      1|}
+
diff --git a/tests/coverage/thin-lto.rs b/tests/coverage/thin-lto.rs
new file mode 100644
index 00000000000..050aac26319
--- /dev/null
+++ b/tests/coverage/thin-lto.rs
@@ -0,0 +1,4 @@
+// compile-flags: -O -C lto=thin -C prefer-dynamic=no
+
+pub fn main() {
+}