diff --git a/README.md b/README.md index 649a0f6..6d0ed04 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,27 @@ C based backend for rustc +[![CI](https://github.com/rust-lang/rustc_codegen_c/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-lang/rustc_codegen_c/actions/workflows/ci.yml) + +This a C codegen backend for rustc, which lowers Rust MIR to C code and compiles +it with a C compiler. + +This code is still highly experimental and not ready for production use. + +## Try it + +In the root directory of the project, run the following command: + +```bash +./y rustc examples/basic_math.rs +./build/basic_math +``` + +The usage of `./y` can be viewed from `./y help`. + +Note: only Linux is supported at the moment. `clang` is required to compile C code, +and LLVM FileCheck is required to test the codegen. + ## License This project is licensed under a dual license: MIT or Apache 2.0. diff --git a/bootstrap/Cargo.lock b/bootstrap/Cargo.lock index 698bdbd..f32432f 100644 --- a/bootstrap/Cargo.lock +++ b/bootstrap/Cargo.lock @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" @@ -305,6 +305,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "strsim" version = "0.11.1" @@ -435,5 +441,7 @@ dependencies = [ "env_logger", "glob", "log", + "regex", + "similar", "which", ] diff --git a/bootstrap/Cargo.toml b/bootstrap/Cargo.toml index 3c61ecd..78e4729 100644 --- a/bootstrap/Cargo.toml +++ b/bootstrap/Cargo.toml @@ -10,4 +10,6 @@ color-print = "0.3.6" env_logger = "0.11.5" glob = "0.3.1" log = "0.4.22" +regex = "1.11.1" +similar = "2.6.0" which = "6.0.1" diff --git a/bootstrap/src/fmt.rs b/bootstrap/src/fmt.rs index e4a1d3b..33553ff 100644 --- a/bootstrap/src/fmt.rs +++ b/bootstrap/src/fmt.rs @@ -23,7 +23,7 @@ impl Run for FmtCommand { .args(["--manifest-path", "crates/Cargo.toml"]) .arg("--all"), ); - for file in glob("example/**/*.rs").unwrap() { + for file in glob("examples/**/*.rs").unwrap() { self.perform(Command::new("rustfmt").args(["--edition", "2021"]).arg(file.unwrap())); } for file in glob("tests/**/*.rs").unwrap() { diff --git a/bootstrap/src/test.rs b/bootstrap/src/test.rs index 476651f..90d9d68 100644 --- a/bootstrap/src/test.rs +++ b/bootstrap/src/test.rs @@ -5,6 +5,7 @@ use anstream::{eprint as print, eprintln as println}; use clap::Args; use color_print::{cprint, cprintln}; use glob::glob; +use similar::{ChangeTag, TextDiff}; use which::which; use crate::manifest::Manifest; @@ -12,7 +13,11 @@ use crate::Run; /// Run tests #[derive(Args, Debug)] -pub struct TestCommand {} +pub struct TestCommand { + /// Update the blessed output + #[clap(long)] + pub bless: bool, +} impl Run for TestCommand { fn run(&self, manifest: &Manifest) { @@ -37,12 +42,21 @@ impl Run for TestCommand { TestType::FileCheck => { cprint!("File checking {}...", testcase.name); testcase.build(manifest); - filechecker.run(&testcase.source, &testcase.output); + filechecker.run(&testcase); + } + TestType::Bless => { + cprint!("Blessing {}...", testcase.name); + testcase.build(manifest); + bless(self.bless, &testcase); } TestType::Compile => { cprint!("Compiling {}...", testcase.name); testcase.build(manifest); } + TestType::CompileLib => { + cprint!("Compiling lib {}...", testcase.name); + testcase.build_lib(manifest); + } } cprintln!("OK"); } @@ -51,18 +65,15 @@ impl Run for TestCommand { impl TestCommand { pub fn collect_testcases(&self, manifest: &Manifest) -> Vec { - let mut result = vec![]; + let mut tests = vec![]; // Examples - for case in glob("example/*.rs").unwrap() { + for case in glob("examples/*.rs").unwrap() { let case = case.unwrap(); let filename = case.file_stem().unwrap(); - if filename == "mini_core" { - continue; - } - let name = format!("example/{}", filename.to_string_lossy()); - let output = manifest.out_dir.join("example").join(filename); - result.push(TestCase { name, source: case, output, test: TestType::Compile }) + let name = format!("examples/{}", filename.to_string_lossy()); + let output_file = manifest.out_dir.join("examples").join(filename); + tests.push(TestCase { name, source: case, output_file, test: TestType::Compile }) } // Codegen tests @@ -70,39 +81,103 @@ impl TestCommand { let case = case.unwrap(); let filename = case.file_stem().unwrap(); let name = format!("codegen/{}", filename.to_string_lossy()); - let output = manifest.out_dir.join("tests/codegen").join(filename); - result.push(TestCase { name, source: case, output, test: TestType::FileCheck }) + let output_file = manifest.out_dir.join("tests/codegen").join(filename); + tests.push(TestCase { name, source: case, output_file, test: TestType::FileCheck }) + } + + // Bless tests - the output should be the same as the last run + for case in glob("tests/bless/*.rs").unwrap() { + let case = case.unwrap(); + let filename = case.file_stem().unwrap(); + let name = format!("bless/{}", filename.to_string_lossy()); + let output_file = manifest.out_dir.join("tests/bless").join(filename); + tests.push(TestCase { name, source: case, output_file, test: TestType::Bless }) + } + + // Collect test-auxiliary + let aux_use = regex::Regex::new(r"^//@\s*aux-build:(?P.*)").unwrap(); + let mut auxiliary = vec![]; + for case in tests.iter() { + let source = std::fs::read_to_string(&case.source).unwrap(); + for cap in aux_use.captures_iter(&source) { + let fname = cap.name("fname").unwrap().as_str(); + let source = Path::new("tests/auxiliary").join(fname); + let filename = source.file_stem().unwrap(); + let name = format!("auxiliary/{}", filename.to_string_lossy()); + let output_file = manifest.out_dir.join(filename); // aux files are output to the base directory + auxiliary.push(TestCase { name, source, output_file, test: TestType::CompileLib }) + } } - result + // Compile auxiliary before the tests + let mut cases = auxiliary; + cases.extend(tests); + cases } } pub enum TestType { + /// Test an executable can be compiled Compile, + /// Test a library can be compiled + CompileLib, + /// Run LLVM FileCheck on the generated code FileCheck, + /// Bless test - the output should be the same as the last run + Bless, } pub struct TestCase { pub name: String, pub source: PathBuf, - pub output: PathBuf, + pub output_file: PathBuf, pub test: TestType, } impl TestCase { pub fn build(&self, manifest: &Manifest) { - std::fs::create_dir_all(self.output.parent().unwrap()).unwrap(); + let output_dir = self.output_file.parent().unwrap(); + std::fs::create_dir_all(output_dir).unwrap(); let mut command = manifest.rustc(); command .args(["--crate-type", "bin"]) .arg("-O") .arg(&self.source) .arg("-o") - .arg(&self.output); + .arg(&self.output_file); log::debug!("running {:?}", command); command.status().unwrap(); } + + pub fn build_lib(&self, manifest: &Manifest) { + let output_dir = self.output_file.parent().unwrap(); + std::fs::create_dir_all(output_dir).unwrap(); + let mut command = manifest.rustc(); + command + .args(["--crate-type", "lib"]) + .arg("-O") + .arg(&self.source) + .arg("--out-dir") // we use `--out-dir` to integrate with the default name convention + .arg(output_dir); // so here we ignore the filename and just use the directory + log::debug!("running {:?}", command); + command.status().unwrap(); + } + + /// Get the generated C file f + pub fn generated(&self) -> PathBuf { + let case = self.source.file_stem().unwrap().to_string_lossy(); + let generated = std::fs::read_dir(self.output_file.parent().unwrap()) + .unwrap() + .filter_map(|entry| entry.ok()) + .find(|entry| { + let filename = entry.file_name(); + let filename = filename.to_string_lossy(); + filename.ends_with(".c") && filename.starts_with(case.as_ref()) + }); + + assert!(generated.is_some(), "could not find {case}'s generated file"); + generated.unwrap().path() + } } struct FileChecker { @@ -126,25 +201,41 @@ impl FileChecker { Self { filecheck } } - fn run(&self, source: &Path, output: &Path) { - let case = source.file_stem().unwrap().to_string_lossy(); - let generated = std::fs::read_dir(output.parent().unwrap()) - .unwrap() - .filter_map(|entry| entry.ok()) - .find(|entry| { - let filename = entry.file_name(); - let filename = filename.to_string_lossy(); - filename.ends_with(".c") && filename.starts_with(case.as_ref()) - }); - - assert!(generated.is_some(), "could not find {case}'s generated file"); - let generated = generated.unwrap(); - - let generated = File::open(generated.path()).unwrap(); + fn run(&self, case: &TestCase) { + let generated = File::open(case.generated()).unwrap(); let mut command = std::process::Command::new(&self.filecheck); - command.arg(source).stdin(generated); + command.arg(&case.source).stdin(generated); log::debug!("running {:?}", command); let output = command.output().unwrap(); - assert!(output.status.success(), "failed to run FileCheck on {case}"); + assert!( + output.status.success(), + "failed to run FileCheck on {}", + case.source.file_stem().unwrap().to_string_lossy() + ); + } +} + +fn bless(update: bool, case: &TestCase) { + let output = case.generated(); + let blessed = case.source.with_extension("c"); + if update { + std::fs::copy(output, blessed).unwrap(); + } else { + let output = std::fs::read_to_string(output).unwrap(); + let blessed = std::fs::read_to_string(blessed).unwrap(); + + let diff = TextDiff::from_lines(&blessed, &output); + if diff.ratio() < 1.0 { + cprintln!("output does not match blessed output"); + for change in diff.iter_all_changes() { + let lineno = change.old_index().unwrap_or(change.new_index().unwrap_or(0)); + match change.tag() { + ChangeTag::Equal => print!(" {:4}| {}", lineno, change), + ChangeTag::Insert => cprint!("+{:4}| {}", lineno, change), + ChangeTag::Delete => cprint!("-{:4}| {}", lineno, change), + } + } + std::process::exit(1); + } } } diff --git a/crates/Cargo.lock b/crates/Cargo.lock index 87c0f98..063b212 100644 --- a/crates/Cargo.lock +++ b/crates/Cargo.lock @@ -5,6 +5,9 @@ version = 3 [[package]] name = "rustc_codegen_c" version = "0.1.0" +dependencies = [ + "rustc_codegen_c_ast", +] [[package]] name = "rustc_codegen_c_ast" diff --git a/crates/rustc_codegen_c/Cargo.toml b/crates/rustc_codegen_c/Cargo.toml index 98d3d36..5a52156 100644 --- a/crates/rustc_codegen_c/Cargo.toml +++ b/crates/rustc_codegen_c/Cargo.toml @@ -1,13 +1,14 @@ [package] name = "rustc_codegen_c" -version = "0.1.0" edition = "2021" +version.workspace = true [lib] crate-type = ["dylib"] [dependencies] +rustc_codegen_c_ast = { path = "../rustc_codegen_c_ast" } # This package uses rustc crates. [package.metadata.rust-analyzer] -rustc_private=true +rustc_private = true diff --git a/crates/rustc_codegen_c/src/base.rs b/crates/rustc_codegen_c/src/base.rs index 354ba2f..bdac635 100644 --- a/crates/rustc_codegen_c/src/base.rs +++ b/crates/rustc_codegen_c/src/base.rs @@ -1,9 +1,61 @@ -use rustc_codegen_ssa::ModuleCodegen; +use std::time::Instant; + +use rustc_codegen_c_ast::{ModuleArena, ModuleCtx}; +use rustc_codegen_ssa::mono_item::MonoItemExt; +use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; +use rustc_middle::dep_graph; use rustc_middle::ty::TyCtxt; +use crate::builder::Builder; +use crate::context::CodegenCx; + +/// Needed helper functions +const HELPER: &str = include_str!("./helper.h"); + +// note: parallel +// it seems this function will be invoked parallelly (if parallel codegen is enabled) + pub fn compile_codegen_unit( - _tcx: TyCtxt<'_>, - _cgu_name: rustc_span::Symbol, -) -> (ModuleCodegen<()>, u64) { - todo!() + tcx: TyCtxt<'_>, + cgu_name: rustc_span::Symbol, +) -> (ModuleCodegen, u64) { + let start_time = Instant::now(); + + let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu_name, + module_codegen, + Some(dep_graph::hash_result), + ); + + let time_to_codegen = start_time.elapsed(); + let cost = time_to_codegen + .as_secs() + .saturating_mul(1_000_000_000) + .saturating_add(time_to_codegen.subsec_nanos() as u64); + + (module, cost) +} + +fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegen { + let cgu = tcx.codegen_unit(cgu_name); + + let mcx = ModuleArena::new(HELPER); + let mcx = ModuleCtx(&mcx); + let cx = CodegenCx::new(tcx, mcx); + + let mono_items = cgu.items_in_deterministic_order(tcx); + for &(mono_item, data) in &mono_items { + mono_item.predefine::>(&cx, data.linkage, data.visibility); + } + + // ... and now that we have everything pre-defined, fill out those definitions. + for &(mono_item, _) in &mono_items { + mono_item.define::>(&cx); + } + + let module = mcx.to_string(); + ModuleCodegen { name: cgu_name.to_string(), module_llvm: module, kind: ModuleKind::Regular } } diff --git a/crates/rustc_codegen_c/src/builder.rs b/crates/rustc_codegen_c/src/builder.rs new file mode 100644 index 0000000..a067393 --- /dev/null +++ b/crates/rustc_codegen_c/src/builder.rs @@ -0,0 +1,727 @@ +#![allow(unused_variables)] // TODO + +use std::ops::Deref; + +use rustc_abi::{HasDataLayout, TargetDataLayout}; +use rustc_codegen_c_ast::func::CFunc; +use rustc_codegen_ssa::traits::{BackendTypes, BuilderMethods, HasCodegen}; +use rustc_middle::ty::layout::{ + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, + TyAndLayout, +}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; +use rustc_target::spec::{HasTargetSpec, Target}; + +use crate::context::CodegenCx; + +mod abi; +mod asm; +mod coverage_info; +mod debug_info; +mod intrinsic_call; +mod r#static; + +/// Codegen builder. +/// +/// It is created for each function and provides the local context for code generation. +pub struct Builder<'a, 'tcx, 'mx> { + /// The associated codegen context. + pub cx: &'a CodegenCx<'tcx, 'mx>, + bb: CFunc<'mx>, +} + +impl<'a, 'tcx, 'mx> Deref for Builder<'a, 'tcx, 'mx> { + type Target = CodegenCx<'tcx, 'mx>; + + fn deref<'b>(&'b self) -> &'a Self::Target { + self.cx + } +} + +impl<'tcx, 'mx> HasCodegen<'tcx> for Builder<'_, 'tcx, 'mx> { + type CodegenCx = CodegenCx<'tcx, 'mx>; +} + +impl<'tcx, 'mx> HasDataLayout for Builder<'_, 'tcx, 'mx> { + fn data_layout(&self) -> &TargetDataLayout { + todo!() + } +} + +impl<'tcx, 'mx> HasTyCtxt<'tcx> for Builder<'_, 'tcx, 'mx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.cx.tcx() + } +} + +impl<'tcx, 'mx> HasParamEnv<'tcx> for Builder<'_, 'tcx, 'mx> { + fn param_env(&self) -> ParamEnv<'tcx> { + self.cx.param_env() + } +} + +impl<'tcx, 'mx> BackendTypes for Builder<'_, 'tcx, 'mx> { + type Value = as BackendTypes>::Value; + type Function = as BackendTypes>::Function; + type BasicBlock = as BackendTypes>::BasicBlock; + type Type = as BackendTypes>::Type; + type Funclet = as BackendTypes>::Funclet; + + type DIScope = as BackendTypes>::DIScope; + type DILocation = as BackendTypes>::DILocation; + type DIVariable = as BackendTypes>::DIVariable; +} + +impl<'tcx, 'mx> HasTargetSpec for Builder<'_, 'tcx, 'mx> { + fn target_spec(&self) -> &Target { + todo!() + } +} + +impl<'tcx, 'mx> LayoutOfHelpers<'tcx> for Builder<'_, 'tcx, 'mx> { + type LayoutOfResult = TyAndLayout<'tcx>; + + fn handle_layout_err(&self, err: LayoutError<'tcx>, span: rustc_span::Span, ty: Ty<'tcx>) -> ! { + todo!() + } +} + +impl<'tcx, 'mx> FnAbiOfHelpers<'tcx> for Builder<'_, 'tcx, 'mx> { + type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; + + fn handle_fn_abi_err( + &self, + err: FnAbiError<'tcx>, + span: rustc_span::Span, + fn_abi_request: FnAbiRequest<'tcx>, + ) -> ! { + todo!() + } +} + +impl<'a, 'tcx, 'mx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx, 'mx> { + fn build(cx: &'a Self::CodegenCx, llbb: Self::BasicBlock) -> Self { + Self { cx, bb: llbb } + } + + fn cx(&self) -> &Self::CodegenCx { + self.cx + } + + fn llbb(&self) -> Self::BasicBlock { + todo!() + } + + fn set_span(&mut self, _span: rustc_span::Span) {} + + fn append_block(cx: &'a Self::CodegenCx, llfn: Self::Function, name: &str) -> Self::BasicBlock { + // assume there is only one basic block + // more complicated cases will be handled in the future + llfn + } + + fn append_sibling_block(&mut self, name: &str) -> Self::BasicBlock { + todo!() + } + + fn switch_to_block(&mut self, llbb: Self::BasicBlock) { + todo!() + } + + fn ret_void(&mut self) { + self.bb.0.push_stmt(self.cx.mcx.ret(None)); + } + + fn ret(&mut self, v: Self::Value) { + self.bb.0.push_stmt(self.cx.mcx.ret(Some(self.cx.mcx.value(v)))) + } + + fn br(&mut self, dest: Self::BasicBlock) { + todo!() + } + + fn cond_br( + &mut self, + cond: Self::Value, + then_llbb: Self::BasicBlock, + else_llbb: Self::BasicBlock, + ) { + todo!() + } + + fn switch( + &mut self, + v: Self::Value, + else_llbb: Self::BasicBlock, + cases: impl ExactSizeIterator, + ) { + todo!() + } + + fn invoke( + &mut self, + llty: Self::Type, + fn_attrs: Option<&rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs>, + fn_abi: Option<&rustc_target::abi::call::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>>, + llfn: Self::Value, + args: &[Self::Value], + then: Self::BasicBlock, + catch: Self::BasicBlock, + funclet: Option<&Self::Funclet>, + instance: Option>, + ) -> Self::Value { + todo!() + } + + fn unreachable(&mut self) { + todo!() + } + + fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fadd_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn sub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fsub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fsub_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fsub_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn mul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fmul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fmul_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fmul_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn exactudiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn exactsdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fdiv_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn fdiv_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn frem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn frem_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn frem_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn shl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn lshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_sadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_uadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_ssub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_usub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_smul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn and(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn or(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn xor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + todo!() + } + + fn neg(&mut self, v: Self::Value) -> Self::Value { + todo!() + } + + fn fneg(&mut self, v: Self::Value) -> Self::Value { + todo!() + } + + fn not(&mut self, v: Self::Value) -> Self::Value { + todo!() + } + + fn checked_binop( + &mut self, + oop: rustc_codegen_ssa::traits::OverflowOp, + ty: rustc_middle::ty::Ty<'_>, + lhs: Self::Value, + rhs: Self::Value, + ) -> (Self::Value, Self::Value) { + todo!() + } + + fn from_immediate(&mut self, val: Self::Value) -> Self::Value { + todo!() + } + + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: rustc_abi::Scalar) -> Self::Value { + todo!() + } + + fn alloca(&mut self, size: rustc_abi::Size, align: rustc_abi::Align) -> Self::Value { + todo!() + } + + fn dynamic_alloca(&mut self, size: Self::Value, align: rustc_abi::Align) -> Self::Value { + todo!() + } + + fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: rustc_abi::Align) -> Self::Value { + todo!() + } + + fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value { + todo!() + } + + fn atomic_load( + &mut self, + ty: Self::Type, + ptr: Self::Value, + order: rustc_codegen_ssa::common::AtomicOrdering, + size: rustc_abi::Size, + ) -> Self::Value { + todo!() + } + + fn load_operand( + &mut self, + place: rustc_codegen_ssa::mir::place::PlaceRef<'tcx, Self::Value>, + ) -> rustc_codegen_ssa::mir::operand::OperandRef<'tcx, Self::Value> { + todo!() + } + + fn write_operand_repeatedly( + &mut self, + elem: rustc_codegen_ssa::mir::operand::OperandRef<'tcx, Self::Value>, + count: u64, + dest: rustc_codegen_ssa::mir::place::PlaceRef<'tcx, Self::Value>, + ) { + todo!() + } + + fn range_metadata(&mut self, load: Self::Value, range: rustc_abi::WrappingRange) { + todo!() + } + + fn nonnull_metadata(&mut self, load: Self::Value) { + todo!() + } + + fn store( + &mut self, + val: Self::Value, + ptr: Self::Value, + align: rustc_abi::Align, + ) -> Self::Value { + todo!() + } + + fn store_with_flags( + &mut self, + val: Self::Value, + ptr: Self::Value, + align: rustc_abi::Align, + flags: rustc_codegen_ssa::MemFlags, + ) -> Self::Value { + todo!() + } + + fn atomic_store( + &mut self, + val: Self::Value, + ptr: Self::Value, + order: rustc_codegen_ssa::common::AtomicOrdering, + size: rustc_abi::Size, + ) { + todo!() + } + + fn gep(&mut self, ty: Self::Type, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value { + todo!() + } + + fn inbounds_gep( + &mut self, + ty: Self::Type, + ptr: Self::Value, + indices: &[Self::Value], + ) -> Self::Value { + todo!() + } + + fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn sitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fptrunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn fpext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn ptrtoint(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn inttoptr(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn bitcast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + /// Performs cast between integers, x as ty in Rust. + /// + /// If the bit width is different, a truncation or extension is required. + /// The type of extension—sign-extension or zero-extension—depends on the + /// signedness of the source type. + /// + /// According to the C17 standard, section "6.3.1.3 Signed and unsigned + /// integers", casting to an unsigned integer behaves the same as in Rust. + /// However, casting to a signed integer is implementation-defined. + /// + /// Therefore, a two-step cast is necessary. First, cast to an unsigned + /// integer via explicit conversion. Then, use a helper function to cast the + /// result to a signed integer. + fn intcast(&mut self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value { + let mcx = self.cx.mcx; + let ret = self.bb.0.next_local_var(); + + let mut cast = mcx.cast(dest_ty, mcx.value(val)); + if dest_ty.is_signed() { + cast = mcx.call( + mcx.raw("__rust_utos"), + vec![ + mcx.raw(dest_ty.to_unsigned().to_str()), + mcx.raw(dest_ty.to_str()), + cast, + mcx.raw(dest_ty.max_value()), + ], + ); + } + self.bb.0.push_stmt(mcx.decl_stmt(mcx.var(ret, dest_ty, Some(cast)))); + ret + } + + fn pointercast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn icmp( + &mut self, + op: rustc_codegen_ssa::common::IntPredicate, + lhs: Self::Value, + rhs: Self::Value, + ) -> Self::Value { + todo!() + } + + fn fcmp( + &mut self, + op: rustc_codegen_ssa::common::RealPredicate, + lhs: Self::Value, + rhs: Self::Value, + ) -> Self::Value { + todo!() + } + + fn memcpy( + &mut self, + dst: Self::Value, + dst_align: rustc_abi::Align, + src: Self::Value, + src_align: rustc_abi::Align, + size: Self::Value, + flags: rustc_codegen_ssa::MemFlags, + ) { + todo!() + } + + fn memmove( + &mut self, + dst: Self::Value, + dst_align: rustc_abi::Align, + src: Self::Value, + src_align: rustc_abi::Align, + size: Self::Value, + flags: rustc_codegen_ssa::MemFlags, + ) { + todo!() + } + + fn memset( + &mut self, + ptr: Self::Value, + fill_byte: Self::Value, + size: Self::Value, + align: rustc_abi::Align, + flags: rustc_codegen_ssa::MemFlags, + ) { + todo!() + } + + fn select( + &mut self, + cond: Self::Value, + then_val: Self::Value, + else_val: Self::Value, + ) -> Self::Value { + todo!() + } + + fn va_arg(&mut self, list: Self::Value, ty: Self::Type) -> Self::Value { + todo!() + } + + fn extract_element(&mut self, vec: Self::Value, idx: Self::Value) -> Self::Value { + todo!() + } + + fn vector_splat(&mut self, num_elts: usize, elt: Self::Value) -> Self::Value { + todo!() + } + + fn extract_value(&mut self, agg_val: Self::Value, idx: u64) -> Self::Value { + todo!() + } + + fn insert_value(&mut self, agg_val: Self::Value, elt: Self::Value, idx: u64) -> Self::Value { + todo!() + } + + fn set_personality_fn(&mut self, personality: Self::Value) { + todo!() + } + + fn cleanup_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value) { + todo!() + } + + fn filter_landing_pad(&mut self, pers_fn: Self::Value) -> (Self::Value, Self::Value) { + todo!() + } + + fn resume(&mut self, exn0: Self::Value, exn1: Self::Value) { + todo!() + } + + fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet { + todo!() + } + + fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option) { + todo!() + } + + fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet { + todo!() + } + + fn catch_switch( + &mut self, + parent: Option, + unwind: Option, + handlers: &[Self::BasicBlock], + ) -> Self::Value { + todo!() + } + + fn atomic_cmpxchg( + &mut self, + dst: Self::Value, + cmp: Self::Value, + src: Self::Value, + order: rustc_codegen_ssa::common::AtomicOrdering, + failure_order: rustc_codegen_ssa::common::AtomicOrdering, + weak: bool, + ) -> (Self::Value, Self::Value) { + todo!() + } + + fn atomic_rmw( + &mut self, + op: rustc_codegen_ssa::common::AtomicRmwBinOp, + dst: Self::Value, + src: Self::Value, + order: rustc_codegen_ssa::common::AtomicOrdering, + ) -> Self::Value { + todo!() + } + + fn atomic_fence( + &mut self, + order: rustc_codegen_ssa::common::AtomicOrdering, + scope: rustc_codegen_ssa::common::SynchronizationScope, + ) { + todo!() + } + + fn set_invariant_load(&mut self, load: Self::Value) { + todo!() + } + + fn lifetime_start(&mut self, ptr: Self::Value, size: rustc_abi::Size) { + todo!() + } + + fn lifetime_end(&mut self, ptr: Self::Value, size: rustc_abi::Size) { + todo!() + } + + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ) { + todo!() + } + + fn call( + &mut self, + llty: Self::Type, + fn_attrs: Option<&rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs>, + fn_abi: Option<&rustc_target::abi::call::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>>, + llfn: Self::Value, + args: &[Self::Value], + funclet: Option<&Self::Funclet>, + instance: Option>, + ) -> Self::Value { + todo!() + } + + fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + todo!() + } + + fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/abi.rs b/crates/rustc_codegen_c/src/builder/abi.rs new file mode 100644 index 0000000..563aa87 --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/abi.rs @@ -0,0 +1,38 @@ +use rustc_codegen_c_ast::expr::CValue; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::{AbiBuilderMethods, ArgAbiMethods}; +use rustc_middle::ty::Ty; +use rustc_target::abi::call::ArgAbi; + +use crate::builder::Builder; + +impl<'tcx, 'mx> AbiBuilderMethods<'tcx> for Builder<'_, 'tcx, 'mx> { + fn get_param(&mut self, index: usize) -> Self::Value { + // Params are first n variables in the function + CValue::Local(index) + } +} + +impl<'tcx, 'mx> ArgAbiMethods<'tcx> for Builder<'_, 'tcx, 'mx> { + fn store_fn_arg( + &mut self, + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + idx: &mut usize, + dst: PlaceRef<'tcx, Self::Value>, + ) { + todo!() + } + + fn store_arg( + &mut self, + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + val: Self::Value, + dst: PlaceRef<'tcx, Self::Value>, + ) { + todo!() + } + + fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/asm.rs b/crates/rustc_codegen_c/src/builder/asm.rs new file mode 100644 index 0000000..a967044 --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/asm.rs @@ -0,0 +1,20 @@ +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_codegen_ssa::traits::{AsmBuilderMethods, InlineAsmOperandRef}; +use rustc_middle::ty::Instance; + +use crate::builder::Builder; + +impl<'tcx, 'mx> AsmBuilderMethods<'tcx> for Builder<'_, 'tcx, 'mx> { + fn codegen_inline_asm( + &mut self, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperandRef<'tcx, Self>], + options: InlineAsmOptions, + line_spans: &[rustc_span::Span], + instance: Instance<'_>, + dest: Option, + catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>, + ) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/coverage_info.rs b/crates/rustc_codegen_c/src/builder/coverage_info.rs new file mode 100644 index 0000000..adabf33 --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/coverage_info.rs @@ -0,0 +1,13 @@ +use rustc_codegen_ssa::traits::CoverageInfoBuilderMethods; + +use crate::builder::Builder; + +impl<'tcx, 'mx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, 'tcx, 'mx> { + fn add_coverage( + &mut self, + instance: rustc_middle::ty::Instance<'tcx>, + kind: &rustc_middle::mir::coverage::CoverageKind, + ) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/debug_info.rs b/crates/rustc_codegen_c/src/builder/debug_info.rs new file mode 100644 index 0000000..374a380 --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/debug_info.rs @@ -0,0 +1,32 @@ +use rustc_codegen_ssa::traits::DebugInfoBuilderMethods; + +use crate::builder::Builder; + +impl DebugInfoBuilderMethods for Builder<'_, '_, '_> { + fn dbg_var_addr( + &mut self, + dbg_var: Self::DIVariable, + dbg_loc: Self::DILocation, + variable_alloca: Self::Value, + direct_offset: rustc_abi::Size, + // NB: each offset implies a deref (i.e. they're steps in a pointer chain). + indirect_offsets: &[rustc_abi::Size], + // Byte range in the `dbg_var` covered by this fragment, + // if this is a fragment of a composite `DIVariable`. + fragment: Option>, + ) { + todo!() + } + + fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation) { + todo!() + } + + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { + todo!() + } + + fn set_var_name(&mut self, value: Self::Value, name: &str) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/intrinsic_call.rs b/crates/rustc_codegen_c/src/builder/intrinsic_call.rs new file mode 100644 index 0000000..de31b4a --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/intrinsic_call.rs @@ -0,0 +1,52 @@ +use rustc_codegen_ssa::mir::operand::OperandRef; +use rustc_codegen_ssa::traits::IntrinsicCallMethods; +use rustc_middle::ty::{Instance, Ty}; +use rustc_target::abi::call::FnAbi; + +use crate::builder::Builder; + +impl<'tcx, 'mx> IntrinsicCallMethods<'tcx> for Builder<'_, 'tcx, 'mx> { + fn codegen_intrinsic_call( + &mut self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OperandRef<'tcx, Self::Value>], + llresult: Self::Value, + span: rustc_span::Span, + ) -> Result<(), Instance<'tcx>> { + todo!() + } + + fn abort(&mut self) { + todo!() + } + + fn assume(&mut self, val: Self::Value) { + todo!() + } + + fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value { + todo!() + } + + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { + todo!() + } + + fn type_checked_load( + &mut self, + llvtable: Self::Value, + vtable_byte_offset: u64, + typeid: Self::Value, + ) -> Self::Value { + todo!() + } + + fn va_start(&mut self, val: Self::Value) -> Self::Value { + todo!() + } + + fn va_end(&mut self, val: Self::Value) -> Self::Value { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/builder/static.rs b/crates/rustc_codegen_c/src/builder/static.rs new file mode 100644 index 0000000..122048a --- /dev/null +++ b/crates/rustc_codegen_c/src/builder/static.rs @@ -0,0 +1,10 @@ +use rustc_codegen_ssa::traits::StaticBuilderMethods; +use rustc_hir::def_id::DefId; + +use crate::builder::Builder; + +impl<'tcx, 'mx> StaticBuilderMethods for Builder<'_, 'tcx, 'mx> { + fn get_static(&mut self, def_id: DefId) -> Self::Value { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context.rs b/crates/rustc_codegen_c/src/context.rs new file mode 100644 index 0000000..afdfbfc --- /dev/null +++ b/crates/rustc_codegen_c/src/context.rs @@ -0,0 +1,103 @@ +#![allow(unused_variables)] // TODO + +use std::cell::RefCell; + +use rustc_abi::{HasDataLayout, TargetDataLayout}; +use rustc_codegen_c_ast::expr::CValue; +use rustc_codegen_c_ast::func::CFunc; +use rustc_codegen_c_ast::ty::CTy; +use rustc_codegen_c_ast::ModuleCtx; +use rustc_codegen_ssa::traits::BackendTypes; +use rustc_hash::FxHashMap; +use rustc_middle::ty::layout::{ + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, + TyAndLayout, +}; +use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; +use rustc_target::spec::{HasTargetSpec, Target}; + +mod asm; +mod base_type; +mod r#const; +mod debug_info; +mod layout_type; +mod misc; +mod pre_define; +mod r#static; +mod type_membership; + +/// Codegen context. +/// +/// It provides the global context for code generation. +pub struct CodegenCx<'tcx, 'mx> { + /// The type context. See [`TyCtxt`]. + pub tcx: TyCtxt<'tcx>, + /// The output and context for the outputed module. + pub mcx: ModuleCtx<'mx>, + /// Mapping from Rust function instances to their corresponding C functions. + pub function_instances: RefCell, CFunc<'mx>>>, +} + +impl<'tcx, 'mx> CodegenCx<'tcx, 'mx> { + pub fn new(tcx: TyCtxt<'tcx>, mcx: ModuleCtx<'mx>) -> Self { + mcx.module().push_include("stdint.h"); + Self { tcx, mcx, function_instances: RefCell::new(FxHashMap::default()) } + } +} + +impl<'tcx, 'mx> BackendTypes for CodegenCx<'tcx, 'mx> { + type Value = CValue<'mx>; + type Function = CFunc<'mx>; + type BasicBlock = CFunc<'mx>; + type Type = CTy<'mx>; + type Funclet = (); + type DIScope = (); + type DILocation = (); + type DIVariable = (); +} + +impl<'tcx, 'mx> HasTargetSpec for CodegenCx<'tcx, 'mx> { + fn target_spec(&self) -> &Target { + todo!() + } +} + +impl<'tcx, 'mx> HasParamEnv<'tcx> for CodegenCx<'tcx, 'mx> { + fn param_env(&self) -> ParamEnv<'tcx> { + ParamEnv::reveal_all() + } +} + +impl<'tcx, 'mx> HasTyCtxt<'tcx> for CodegenCx<'tcx, 'mx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'tcx, 'mx> HasDataLayout for CodegenCx<'tcx, 'mx> { + fn data_layout(&self) -> &TargetDataLayout { + todo!() + } +} + +impl<'tcx, 'mx> LayoutOfHelpers<'tcx> for CodegenCx<'tcx, 'mx> { + type LayoutOfResult = TyAndLayout<'tcx>; + + fn handle_layout_err(&self, err: LayoutError<'tcx>, span: rustc_span::Span, ty: Ty<'tcx>) -> ! { + todo!() + } +} + +impl<'tcx, 'mx> FnAbiOfHelpers<'tcx> for CodegenCx<'tcx, 'mx> { + type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; + + fn handle_fn_abi_err( + &self, + err: FnAbiError<'tcx>, + span: rustc_span::Span, + fn_abi_request: FnAbiRequest<'tcx>, + ) -> ! { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/asm.rs b/crates/rustc_codegen_c/src/context/asm.rs new file mode 100644 index 0000000..768fb9e --- /dev/null +++ b/crates/rustc_codegen_c/src/context/asm.rs @@ -0,0 +1,16 @@ +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_codegen_ssa::traits::{AsmMethods, GlobalAsmOperandRef}; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> AsmMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn codegen_global_asm( + &self, + template: &[InlineAsmTemplatePiece], + operands: &[GlobalAsmOperandRef<'tcx>], + options: InlineAsmOptions, + line_spans: &[rustc_span::Span], + ) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/base_type.rs b/crates/rustc_codegen_c/src/context/base_type.rs new file mode 100644 index 0000000..8254373 --- /dev/null +++ b/crates/rustc_codegen_c/src/context/base_type.rs @@ -0,0 +1,85 @@ +use rustc_codegen_ssa::traits::BaseTypeMethods; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> BaseTypeMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn type_i8(&self) -> Self::Type { + todo!() + } + + fn type_i16(&self) -> Self::Type { + todo!() + } + + fn type_i32(&self) -> Self::Type { + todo!() + } + + fn type_i64(&self) -> Self::Type { + todo!() + } + + fn type_i128(&self) -> Self::Type { + todo!() + } + + fn type_isize(&self) -> Self::Type { + todo!() + } + + fn type_f16(&self) -> Self::Type { + todo!() + } + + fn type_f32(&self) -> Self::Type { + todo!() + } + + fn type_f64(&self) -> Self::Type { + todo!() + } + + fn type_f128(&self) -> Self::Type { + todo!() + } + + fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type { + todo!() + } + + fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type { + todo!() + } + + fn type_kind(&self, ty: Self::Type) -> rustc_codegen_ssa::common::TypeKind { + todo!() + } + + fn type_ptr(&self) -> Self::Type { + todo!() + } + + fn type_ptr_ext(&self, address_space: rustc_abi::AddressSpace) -> Self::Type { + todo!() + } + + fn element_type(&self, ty: Self::Type) -> Self::Type { + todo!() + } + + fn vector_length(&self, ty: Self::Type) -> usize { + todo!() + } + + fn float_width(&self, ty: Self::Type) -> usize { + todo!() + } + + fn int_width(&self, ty: Self::Type) -> u64 { + todo!() + } + + fn val_ty(&self, v: Self::Value) -> Self::Type { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/const.rs b/crates/rustc_codegen_c/src/context/const.rs new file mode 100644 index 0000000..99c868f --- /dev/null +++ b/crates/rustc_codegen_c/src/context/const.rs @@ -0,0 +1,111 @@ +use rustc_codegen_c_ast::expr::CValue; +use rustc_codegen_ssa::traits::ConstMethods; +use rustc_const_eval::interpret::{ConstAllocation, Scalar}; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> ConstMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn const_null(&self, t: Self::Type) -> Self::Value { + todo!() + } + + fn const_undef(&self, t: Self::Type) -> Self::Value { + todo!() + } + + fn const_poison(&self, t: Self::Type) -> Self::Value { + todo!() + } + + fn const_int(&self, t: Self::Type, i: i64) -> Self::Value { + todo!() + } + + fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value { + todo!() + } + + fn const_uint_big(&self, t: Self::Type, u: u128) -> Self::Value { + todo!() + } + + fn const_bool(&self, val: bool) -> Self::Value { + todo!() + } + + fn const_i16(&self, i: i16) -> Self::Value { + todo!() + } + + fn const_i32(&self, i: i32) -> Self::Value { + todo!() + } + + fn const_i8(&self, i: i8) -> Self::Value { + todo!() + } + + fn const_u32(&self, i: u32) -> Self::Value { + todo!() + } + + fn const_u64(&self, i: u64) -> Self::Value { + todo!() + } + + fn const_u128(&self, i: u128) -> Self::Value { + todo!() + } + + fn const_usize(&self, i: u64) -> Self::Value { + todo!() + } + + fn const_u8(&self, i: u8) -> Self::Value { + todo!() + } + + fn const_real(&self, t: Self::Type, val: f64) -> Self::Value { + todo!() + } + + fn const_str(&self, s: &str) -> (Self::Value, Self::Value) { + todo!() + } + + fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value { + todo!() + } + + fn const_to_opt_uint(&self, v: Self::Value) -> Option { + todo!() + } + + fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option { + todo!() + } + + fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value { + todo!() + } + + fn scalar_to_backend( + &self, + cv: Scalar, + layout: rustc_target::abi::Scalar, + llty: Self::Type, + ) -> Self::Value { + match cv { + Scalar::Int(scalar) => CValue::Scalar(scalar.to_int(scalar.size())), + Scalar::Ptr(_, _) => todo!(), + } + } + + fn const_ptr_byte_offset( + &self, + val: Self::Value, + offset: rustc_target::abi::Size, + ) -> Self::Value { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/debug_info.rs b/crates/rustc_codegen_c/src/context/debug_info.rs new file mode 100644 index 0000000..2ee05fc --- /dev/null +++ b/crates/rustc_codegen_c/src/context/debug_info.rs @@ -0,0 +1,69 @@ +use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind}; +use rustc_codegen_ssa::traits::DebugInfoMethods; +use rustc_middle::mir::Body; +use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; +use rustc_target::abi::call::FnAbi; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> DebugInfoMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn create_vtable_debuginfo( + &self, + ty: Ty<'tcx>, + trait_ref: Option>, + vtable: Self::Value, + ) { + todo!() + } + + fn create_function_debug_context( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + llfn: Self::Function, + mir: &Body<'tcx>, + ) -> Option> { + None + } + + fn dbg_scope_fn( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + maybe_definition_llfn: Option, + ) -> Self::DIScope { + todo!() + } + + fn dbg_loc( + &self, + scope: Self::DIScope, + inlined_at: Option, + span: rustc_span::Span, + ) -> Self::DILocation { + todo!() + } + + fn extend_scope_to_file( + &self, + scope_metadata: Self::DIScope, + file: &rustc_span::SourceFile, + ) -> Self::DIScope { + todo!() + } + + fn debuginfo_finalize(&self) { + todo!() + } + + fn create_dbg_var( + &self, + variable_name: rustc_span::Symbol, + variable_type: Ty<'tcx>, + scope_metadata: Self::DIScope, + variable_kind: VariableKind, + span: rustc_span::Span, + ) -> Self::DIVariable { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/layout_type.rs b/crates/rustc_codegen_c/src/context/layout_type.rs new file mode 100644 index 0000000..fb10c22 --- /dev/null +++ b/crates/rustc_codegen_c/src/context/layout_type.rs @@ -0,0 +1,58 @@ +use rustc_abi::Abi; +use rustc_codegen_ssa::traits::LayoutTypeMethods; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Ty; +use rustc_target::abi::call::FnAbi; +use rustc_type_ir::TyKind; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type { + todo!() + } + + fn cast_backend_type(&self, ty: &rustc_target::abi::call::CastTarget) -> Self::Type { + todo!() + } + + fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type { + todo!() + } + + fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type { + todo!() + } + + fn reg_backend_type(&self, ty: &rustc_target::abi::call::Reg) -> Self::Type { + todo!() + } + + fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type { + match layout.ty.kind() { + TyKind::Int(int) => self.mcx.get_int_type(*int), + TyKind::Uint(uint) => self.mcx.get_uint_type(*uint), + _ => todo!(), + } + } + + fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool { + match layout.abi { + Abi::Scalar(_) | Abi::Vector { .. } => true, + Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false, + } + } + + fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool { + todo!() + } + + fn scalar_pair_element_backend_type( + &self, + layout: TyAndLayout<'tcx>, + index: usize, + immediate: bool, + ) -> Self::Type { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/misc.rs b/crates/rustc_codegen_c/src/context/misc.rs new file mode 100644 index 0000000..9487ed3 --- /dev/null +++ b/crates/rustc_codegen_c/src/context/misc.rs @@ -0,0 +1,48 @@ +use std::cell::RefCell; + +use rustc_codegen_ssa::traits::MiscMethods; +use rustc_hash::FxHashMap; +use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> MiscMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn vtables( + &self, + ) -> &RefCell, Option>), Self::Value>> { + todo!() + } + + fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function { + *self.function_instances.borrow().get(&instance).unwrap() + } + + fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value { + todo!() + } + + fn eh_personality(&self) -> Self::Value { + todo!() + } + + fn sess(&self) -> &rustc_session::Session { + self.tcx.sess + } + + fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { + todo!() + } + + fn set_frame_pointer_type(&self, llfn: Self::Function) { + todo!() + } + + fn apply_target_cpu_attr(&self, llfn: Self::Function) { + todo!() + } + + fn declare_c_main(&self, fn_type: Self::Type) -> Option { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/pre_define.rs b/crates/rustc_codegen_c/src/context/pre_define.rs new file mode 100644 index 0000000..b10c73b --- /dev/null +++ b/crates/rustc_codegen_c/src/context/pre_define.rs @@ -0,0 +1,39 @@ +use rustc_codegen_c_ast::func::CFuncKind; +use rustc_codegen_ssa::traits::{LayoutTypeMethods, PreDefineMethods}; +use rustc_data_structures::intern::Interned; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::{self, Instance}; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> PreDefineMethods<'tcx> for CodegenCx<'tcx, 'mx> { + fn predefine_static( + &self, + def_id: DefId, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str, + ) { + todo!() + } + + fn predefine_fn( + &self, + instance: Instance<'tcx>, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str, + ) { + let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); + + let args = fn_abi.args.iter().map(|arg| self.immediate_backend_type(arg.layout)); + let ret = self.immediate_backend_type(fn_abi.ret.layout); + + let func = CFuncKind::new(self.mcx.alloc_str(symbol_name), ret, args); + let func = Interned::new_unchecked(self.mcx.func(func)); + self.mcx.module().push_func(func); + self.function_instances.borrow_mut().insert(instance, func); + } +} diff --git a/crates/rustc_codegen_c/src/context/static.rs b/crates/rustc_codegen_c/src/context/static.rs new file mode 100644 index 0000000..4110068 --- /dev/null +++ b/crates/rustc_codegen_c/src/context/static.rs @@ -0,0 +1,23 @@ +use rustc_abi::Align; +use rustc_codegen_ssa::traits::StaticMethods; +use rustc_hir::def_id::DefId; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> StaticMethods for CodegenCx<'tcx, 'mx> { + fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value { + todo!() + } + + fn codegen_static(&self, def_id: DefId) { + todo!() + } + + fn add_used_global(&self, global: Self::Value) { + todo!() + } + + fn add_compiler_used_global(&self, global: Self::Value) { + todo!() + } +} diff --git a/crates/rustc_codegen_c/src/context/type_membership.rs b/crates/rustc_codegen_c/src/context/type_membership.rs new file mode 100644 index 0000000..2d6bc53 --- /dev/null +++ b/crates/rustc_codegen_c/src/context/type_membership.rs @@ -0,0 +1,5 @@ +use rustc_codegen_ssa::traits::TypeMembershipMethods; + +use crate::context::CodegenCx; + +impl<'tcx, 'mx> TypeMembershipMethods<'tcx> for CodegenCx<'tcx, 'mx> {} diff --git a/crates/rustc_codegen_c/src/helper.h b/crates/rustc_codegen_c/src/helper.h new file mode 100644 index 0000000..907f233 --- /dev/null +++ b/crates/rustc_codegen_c/src/helper.h @@ -0,0 +1,12 @@ +/* Some helper macros for the generated code */ + +/** Casts an unsigned integer to a signed integer of the same size. + * This is used to avoid UB when do integer casting in Rust. + * + * The parameter `u` is the unsigned type, `s` is the signed type, + * `v` is the value to cast, and `m` is the maximum value of the signed type.\ + * + * example: `__rust_utos(uint32_t, int32_t, x, INT32_MAX)` + */ +#define __rust_utos(u, s, v, m) \ + ((v) <= (m) ? ((s)v) : ((s)((u)(v) - (u)(m) - 1))) diff --git a/crates/rustc_codegen_c/src/lib.rs b/crates/rustc_codegen_c/src/lib.rs index 949b2d7..488d805 100644 --- a/crates/rustc_codegen_c/src/lib.rs +++ b/crates/rustc_codegen_c/src/lib.rs @@ -1,15 +1,22 @@ #![feature(rustc_private)] +extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_codegen_ssa; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_fluent_macro; +extern crate rustc_hash; +extern crate rustc_hir; extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; +extern crate rustc_target; +extern crate rustc_type_ir; +extern crate tracing; use std::sync::Arc; @@ -37,7 +44,8 @@ use rustc_span::ErrorGuaranteed; mod archive; mod base; -mod util; +mod builder; +mod context; mod write; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -51,7 +59,7 @@ impl CodegenBackend for CCodegen { } fn provide(&self, providers: &mut Providers) { - providers.global_backend_features = |tcx, ()| util::global_backend_features(tcx) + providers.global_backend_features = |_tcx, ()| vec![] } fn codegen_crate( @@ -88,6 +96,10 @@ impl CodegenBackend for CCodegen { ) -> Result<(), ErrorGuaranteed> { link_binary(sess, &crate::archive::ArArchiveBuilderBuilder, &codegen_results, outputs) } + + fn supports_parallel(&self) -> bool { + false + } } impl ExtraBackendMethods for CCodegen { @@ -140,7 +152,7 @@ impl ThinBufferMethods for ThinBuffer { } impl WriteBackendMethods for CCodegen { - type Module = (); + type Module = String; type TargetMachine = (); type TargetMachineError = (); type ModuleBuffer = ModuleBuffer; diff --git a/crates/rustc_codegen_c/src/util.rs b/crates/rustc_codegen_c/src/util.rs deleted file mode 100644 index e7859b0..0000000 --- a/crates/rustc_codegen_c/src/util.rs +++ /dev/null @@ -1,5 +0,0 @@ -use rustc_middle::ty::TyCtxt; - -pub fn global_backend_features(_tcx: TyCtxt<'_>) -> Vec { - vec![] -} diff --git a/crates/rustc_codegen_c/src/write.rs b/crates/rustc_codegen_c/src/write.rs index c4e1195..9358b3f 100644 --- a/crates/rustc_codegen_c/src/write.rs +++ b/crates/rustc_codegen_c/src/write.rs @@ -1,20 +1,63 @@ +use std::fs; +use std::io::Write; +use std::process::Stdio; + +use rustc_codegen_ssa::back::command::Command; use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_errors::{DiagCtxtHandle, FatalError}; +use rustc_session::config::OutputType; +use tracing::error; pub(crate) unsafe fn codegen( - _cgcx: &CodegenContext, + cgcx: &CodegenContext, _dcx: DiagCtxtHandle<'_>, - _module: ModuleCodegen<()>, + module: ModuleCodegen, _config: &ModuleConfig, ) -> Result { - todo!() + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + let c_out = obj_out.with_extension("c"); + + // output c source code + let c_out_file = fs::File::create(&c_out).map_err(|_| FatalError)?; + writeln!(&c_out_file, "// file: {}.c", module.name).map_err(|_| FatalError)?; + write!(&c_out_file, "{}", module.module_llvm).map_err(|_| FatalError)?; + + // invoke cc to compile + // FIXME: configure cc + // FIXME: handle long command line (windows) + // FIXME: flush_linked_file (windows) + let mut cmd = Command::new("clang"); + cmd.arg(&c_out).arg("-o").arg(&obj_out).arg("-c"); + let mut cmd = cmd.command(); + let output = match cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .and_then(|child| child.wait_with_output()) + { + Ok(output) => output, + Err(e) => { + error!("failed to spawn C compiler: {}", e); + return Err(FatalError); + } + }; + + if !output.status.success() { + error!("compiler stderr:\n{}", String::from_utf8_lossy(&output.stderr)); + error!("compiler stdout:\n{}", String::from_utf8_lossy(&output.stdout)); + return Err(FatalError); + } + + Ok(module.into_compiled_module(true, false, false, false, false, &cgcx.output_filenames)) } pub(crate) fn link( _cgcx: &CodegenContext, _dcx: DiagCtxtHandle<'_>, - mut _modules: Vec>, -) -> Result, FatalError> { + mut _modules: Vec>, +) -> Result, FatalError> { unimplemented!(); } diff --git a/examples/basic_math.rs b/examples/basic_math.rs new file mode 100644 index 0000000..c870300 --- /dev/null +++ b/examples/basic_math.rs @@ -0,0 +1,17 @@ +//@ aux-build:mini_core.rs + +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate mini_core; + +#[no_mangle] +pub fn main() -> i32 { + 0 +} + +#[no_mangle] +pub fn foo(x: u8, _y: u8) -> i64 { + x as i64 +} diff --git a/tests/auxiliary/mini_core.rs b/tests/auxiliary/mini_core.rs new file mode 100644 index 0000000..b93dd93 --- /dev/null +++ b/tests/auxiliary/mini_core.rs @@ -0,0 +1,39 @@ +#![feature(no_core, lang_items, rustc_attrs, intrinsics, decl_macro)] +#![no_core] +#![allow(internal_features)] + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +pub unsafe trait Copy {} + +unsafe impl Copy for bool {} +unsafe impl Copy for u8 {} +unsafe impl Copy for u16 {} +unsafe impl Copy for u32 {} +unsafe impl Copy for u64 {} +unsafe impl Copy for usize {} +unsafe impl Copy for i8 {} +unsafe impl Copy for i16 {} +unsafe impl Copy for i32 {} +unsafe impl Copy for isize {} +unsafe impl Copy for f32 {} +unsafe impl Copy for f64 {} +unsafe impl Copy for char {} +unsafe impl<'a, T: ?Sized> Copy for &'a T {} +unsafe impl Copy for *const T {} +unsafe impl Copy for *mut T {} + +pub mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + pub fn printf(format: *const i8, ...) -> i32; + pub fn malloc(size: usize) -> *mut u8; + pub fn free(ptr: *mut u8); + pub fn memcpy(dst: *mut u8, src: *const u8, size: usize); + pub fn memmove(dst: *mut u8, src: *const u8, size: usize); + pub fn strncpy(dst: *mut u8, src: *const u8, size: usize); + } +} diff --git a/tests/bless/basic_math.c b/tests/bless/basic_math.c new file mode 100644 index 0000000..eb12df6 --- /dev/null +++ b/tests/bless/basic_math.c @@ -0,0 +1,26 @@ +// file: basic_math.3cfc46df15d2d47-cgu.0.c +#include + +/* Some helper macros for the generated code */ + +/** Casts an unsigned integer to a signed integer of the same size. + * This is used to avoid UB when do integer casting in Rust. + * + * The parameter `u` is the unsigned type, `s` is the signed type, + * `v` is the value to cast, and `m` is the maximum value of the signed type.\ + * + * example: `__rust_utos(uint32_t, int32_t, x, INT32_MAX)` + */ +#define __rust_utos(u, s, v, m) \ + ((v) <= (m) ? ((s)v) : ((s)((u)(v) - (u)(m) - 1))) + +int32_t main(); +int64_t foo(uint8_t _0, uint16_t _1, uint32_t _2); + +int32_t main() { return 0; } + +int64_t foo(uint8_t _0, uint16_t _1, uint32_t _2) +{ + int64_t _3 = __rust_utos(uint64_t, int64_t, (int64_t) _0, INT64_MAX); + return _3; +} diff --git a/tests/bless/basic_math.rs b/tests/bless/basic_math.rs new file mode 100644 index 0000000..689b04a --- /dev/null +++ b/tests/bless/basic_math.rs @@ -0,0 +1,17 @@ +//@ aux-build:mini_core.rs + +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate mini_core; + +#[no_mangle] +pub fn main() -> i32 { + 0 +} + +#[no_mangle] +pub fn foo(x: u8, _y: u16, _z: u32) -> i64 { + x as i64 +} diff --git a/tests/codegen/filename.rs b/tests/codegen/filename.rs new file mode 100644 index 0000000..0a5d4b9 --- /dev/null +++ b/tests/codegen/filename.rs @@ -0,0 +1,22 @@ +//! Test that the generated code has the filename and function name in it + +//@ aux-build:mini_core.rs + +// CHECK: filename + +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate mini_core; + +// CHECK: function_name +#[no_mangle] +pub fn function_name() -> i32 { + 0 +} + +#[no_mangle] +pub fn main() -> i32 { + 0 +} diff --git a/tests/codegen/params_count.rs b/tests/codegen/params_count.rs new file mode 100644 index 0000000..6468ed3 --- /dev/null +++ b/tests/codegen/params_count.rs @@ -0,0 +1,26 @@ +//! Test that the generated code has the right number of parameters + +//@ aux-build:mini_core.rs + +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate mini_core; + +// CHECK-LABEL: foo +// CHECK-LABEL: main + +// expect three int params +// CHECK-LABEL: foo +// CHECK: (int32_t {{[[:alnum:]_]*}}, int32_t {{[[:alnum:]_]*}}, int32_t {{[[:alnum:]_]*}}) +// CHECK: return 0; +#[no_mangle] +pub fn foo(_x: i32, _y: i32, _z: i32) -> i32 { + 0 +} + +#[no_mangle] +pub fn main() -> i32 { + 0 +} diff --git a/tests/codegen/ret_value.rs b/tests/codegen/ret_value.rs new file mode 100644 index 0000000..19ce069 --- /dev/null +++ b/tests/codegen/ret_value.rs @@ -0,0 +1,16 @@ +//! Test that we can return a value from a function + +//@ aux-build:mini_core.rs + +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate mini_core; + +// CHECK-LABEL: main +// CHECK: return 42; +#[no_mangle] +pub fn main() -> i32 { + 42 +}