├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── LICENSE-Apache ├── README.md ├── build.rs ├── cargo-tests ├── build_core │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── src │ │ ├── .gitignore │ │ ├── lib.rs │ │ └── main.rs │ └── test.cpp └── vecs │ ├── Cargo.toml │ └── src │ └── lib.rs ├── codegen ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── LICENSE-Apache ├── build.rs ├── rust-toolchain.toml ├── src │ ├── cgu.rs │ ├── function.rs │ ├── lib.rs │ ├── rust.rs │ ├── souce_builder.rs │ ├── souce_builder │ │ ├── predefined.rs │ │ └── typedef.rs │ ├── statics.rs │ ├── test.rs │ ├── test │ │ └── utilis.rs │ └── utilis.rs └── tests │ ├── add.rs │ ├── btree.rs │ ├── fuzz0.rs │ ├── generics.rs │ ├── gimili.rs │ ├── pass_ind.rs │ ├── raw_waker.rs │ └── statics.rs ├── packer ├── Cargo.toml └── src │ └── main.rs └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.hpp 3 | *.a 4 | *.elf 5 | *.rs_bridge 6 | *.hpp 7 | *.hpp.gch 8 | *.txt 9 | *.out 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "iostream": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "cargo-cppbridge" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "packer" 11 | version = "0.1.0" 12 | 13 | [[package]] 14 | name = "rustc-demangle" 15 | version = "0.1.24" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 18 | 19 | [[package]] 20 | name = "rustc_codegen_c" 21 | version = "0.1.0" 22 | dependencies = [ 23 | "rustc-demangle", 24 | ] 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-cppbridge" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | [workspace] 8 | members = ["codegen", "packer"] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 FractalFir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # seabridge(*siː brɪʤ*) - next-gen tool for easy Rust / C++ interop 2 | 3 | **WARNING: This tool is in early research phase. It paritaly works, but it has some *very* rough edges and is nothing more than a tech demo ATM.** 4 | 5 | **Since it is so early in developement, this README may not be up to date and is likelly riddled with spelling/other mistakes. Sorry.** 6 | **While I try to be clear about what is implemented and planned,** 7 | **it is important to note that many features are still quite buggy.** 8 | 9 | Seabridge is an experimental tool, which uses rich type information from the rust compiler to generate high-quality C++ bindings to Rust code. 10 | 11 | Thanks to this unqiue, deep access to the internal state of the compiler, 12 | sebaridge can generate C++ bindings to all Rust types(DONE, bit buggy), functions(WIP, demangled + generic support + shims, still bit buggy and limited), and statics(WIP, no demangling yet). 13 | 14 | This(in theory) will allow you to *just* use a Rust crate inside a C++ codebase, without much setup. The tool is also able to translate high level Rust concepts, like generics, into C++ equivalents(templates). 15 | 16 | 17 | For most scenarios, you will not need to write any glue code(generics may require a bit of setup in some cases). 18 | 19 | 20 | ## Solving ABI instability 21 | 22 | ### Solving the quiestion of unstable layout: 23 | 24 | In most case, the unstable layout of Rust types makes it impossible to generate stable and usable bindings to types not marked with `#[repr(C)]`. 25 | However, since seabridge uses the same layout calculations used by the corresponding version of the Rust compiler, the bindings it genrates match with the Rust layout of types. 26 | 27 | As long as the bindings are generated for the right target, and the C++ code is recompiled when the Rust code / bindings change, seabridge can **guarantee** the memory layout of types on both side matches. 28 | 29 | ```cpp 30 | // Translated Box defintion, generated by seabridge 31 | namespace alloc::boxed { 32 | // Generics translated into templates with specialization, 33 | // Aligement preserved using atributes. 34 | template < > struct __attribute__((aligned(8))) 35 | Box < int32_t, ::alloc::alloc::Global > { 36 | ::core::ptr::unique::Unique < int32_t > f0; 37 | }; 38 | } 39 | ``` 40 | 41 | However, this approach also comes with some limitations regarding generics. Rust generics must be instantiated(used) on the Rust side, since they will not get exported otherwise. 42 | 43 | In practice, this is almost never a problem, since you will almost never use a Rust generic only on the C++ side. All generics used within function signatures / statics, and types used 44 | within those types will get exported correctly. 45 | 46 | ### Keeping up with the everchanging ABI: 47 | 48 | TODO: write about translating Rust ABI to C(implemented, but limited/flawed(*unhandled egde cases*) approach), and about generating C-to-Rust shims(WIP, should be rock-solid in theory). 49 | 50 | # High level translation 51 | 52 | The ultimate goal of Seabridge is allowing you to forget that the code you are using was originaly written in Rust. 53 | 54 | Seabrdige translates Rust generics into C++ templates(with some limitations), allowing you to use generic Rust types form C++: 55 | ```rust 56 | #[no_mangle] 57 | fn my_fn(args:(i32,f64,&[u8]))->Box{ 58 | eprintln!("Recived args:{args:?}"); 59 | Box::new(args.0) 60 | } 61 | ``` 62 | ```cpp 63 | #include 64 | int main() { 65 | uint8_t* slice_content = (uint8_t*)"Hi Bob"; 66 | // Create Rust slice 67 | RustSlice slice; 68 | slice.ptr = slice_content; 69 | slice.len = 6; 70 | // Create Rust tuple 71 | RustTuple args = {8,3.14159,slice}; 72 | // Just call a Rust function 73 | alloc::boxed::Box rust_box = my_crate::my_fn(args); 74 | 75 | } 76 | ``` 77 | Seabridge will also(in the future!) translate `Drop` impls into C++ destructors. 78 | 79 | # LICENSE 80 | 81 | Parts of Seabrdige's code come from `rustc_codegen_clr`, and, as such, it is licensed under the same license as it and the Rust compiler: MIT or Apache. -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | fn main() { 3 | let tests = Path::new("cargo-tests"); 4 | let test_dir = std::fs::read_dir(tests).expect("Could not open the cargo test directory"); 5 | let mut dirs: Vec = Vec::new(); 6 | for entry in test_dir { 7 | let entry = entry.unwrap(); 8 | let path = entry.path(); 9 | if path.is_dir() { 10 | dirs.push(path.file_stem().unwrap().to_str().unwrap().to_string()); 11 | } 12 | } 13 | //panic!("dirs:{dirs:?}") 14 | } 15 | -------------------------------------------------------------------------------- /cargo-tests/build_core/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-gnu" 3 | [unstable] 4 | build-std = ["core","alloc","panic_abort","std"] 5 | 6 | -------------------------------------------------------------------------------- /cargo-tests/build_core/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.txt 3 | -------------------------------------------------------------------------------- /cargo-tests/build_core/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "build_core" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /cargo-tests/build_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "build_core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | [lib] 6 | crate-type = ["staticlib"] 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | 10 | [workspace] 11 | [profile.release.build-override] 12 | codegen-units = 1 -------------------------------------------------------------------------------- /cargo-tests/build_core/src/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /cargo-tests/build_core/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FractalFir/seabridge/d27f618cdcab3a81840cdbebd91e626138dfdf06/cargo-tests/build_core/src/lib.rs -------------------------------------------------------------------------------- /cargo-tests/build_core/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /cargo-tests/build_core/test.cpp: -------------------------------------------------------------------------------- 1 | #include "core.hpp" 2 | #include "alloc.hpp" 3 | #include 4 | #include 5 | int main(){ 6 | alloc::ffi::c_str::CString cstring = alloc::ffi::c_str::cstring::from_raw((int8_t*)"Hello :)"); 7 | // Assemble a Rust `&str` in C++ 8 | char32_t* utf8_location = (char32_t*)__FILE__; 9 | uintptr_t location_length = strlen((char*)utf8_location); 10 | RustStr file = {utf8_location,location_length}; 11 | // Call a function from core to create a panic location - infromation about where a panic is comming from. 12 | core::panic::location::Location loc = core::panic::location::location::internal_constructor(file,__LINE__,0); 13 | // Create the panic message, to print something nice to stdout! 14 | char32_t* utf8_data = (char32_t*)"This panic message is comming straight from C++ land :). I am panicking to print cause `std` bindings don't work yet."; 15 | uintptr_t message_length = strlen((char*)utf8_data); 16 | RustStr message = {utf8_data,message_length}; 17 | // Once again, just call a function from core like it is a "normal" C++ function. 18 | // This will abort, but this is the only way to print something from `core`(`std` bindings don't work yet) 19 | core::panicking::panic(message,&loc); 20 | } 21 | 22 | // -------------------------------------------------------------------------------- /cargo-tests/vecs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vecs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /cargo-tests/vecs/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: u64, right: u64) -> u64 { 2 | left + right 3 | } 4 | -------------------------------------------------------------------------------- /codegen/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *-ice-*.txt 3 | /tests/*.c 4 | -------------------------------------------------------------------------------- /codegen/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "rustc-demangle" 7 | version = "0.1.24" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 10 | 11 | [[package]] 12 | name = "rustc_codegen_c" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "rustc-demangle", 16 | ] 17 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustc_codegen_c" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rustc-demangle = "0.1.24" 8 | [lib] 9 | crate-type = ["dylib"] 10 | [profile.release] 11 | debug = true 12 | -------------------------------------------------------------------------------- /codegen/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 FractalFir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codegen/LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /codegen/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::OsStr, 3 | fs::DirEntry, 4 | io::{Read, Write}, 5 | path::Path, 6 | }; 7 | 8 | // Builds the dynamic test list 9 | fn visit_dirs(dir: &Path, cb: &mut impl FnMut(&DirEntry)) -> std::io::Result<()> { 10 | if dir.is_dir() { 11 | for entry in std::fs::read_dir(dir)? { 12 | let entry = entry?; 13 | let path = entry.path(); 14 | if path.is_dir() { 15 | visit_dirs(&path, cb)?; 16 | } else { 17 | cb(&entry); 18 | } 19 | } 20 | } 21 | Ok(()) 22 | } 23 | fn test_from_path(path: &Path) -> String { 24 | assert!(path.is_relative()); 25 | let test_name = path 26 | .file_stem() 27 | .expect("Test has no name") 28 | .to_str() 29 | .unwrap(); 30 | // Open the file to detect some config options. 31 | let mut file = std::fs::File::open(path).expect("Could not open test file"); 32 | let mut file_content = String::new(); 33 | file.read_to_string(&mut file_content) 34 | .expect("Could not read test file"); 35 | let stable = if file_content.contains("//test:unstable") { 36 | "unstable" 37 | } else { 38 | "stable" 39 | }; 40 | // Check if this file is an executable or library 41 | let test_body = if file_content.contains("fn main()") { 42 | format!("let _lock = super::COMPILE_LOCK.lock().unwrap(); \nlet executable = crate::test::utilis::compile_file(std::path::Path::new({path:?}), false, IS_RELEASE); crate::test::utilis::run_test(&executable);") 43 | } else { 44 | format!( 45 | "crate::test::utilis::compile_file(std::path::Path::new({path:?}), true, IS_RELEASE);" 46 | ) 47 | }; 48 | // Check if test contains a `main` function, and onlt run it if so. 49 | format!("mod {test_name}{{ 50 | #[cfg(test)] 51 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 52 | mod debug{{\n 53 | #[cfg(test)]\n const IS_RELEASE:bool = false;\n 54 | #[test]\n fn {stable}(){{{test_body}}}}}\n mod release{{\n #[cfg(test)]\n const IS_RELEASE:bool = true;\n #[test]\n fn {stable}(){{{test_body}}}}}}}\n") 55 | } 56 | fn main() { 57 | // Tell Cargo that if the given file changes, to rerun this build script. 58 | println!("cargo::rerun-if-changed=tests"); 59 | let mut test_string: String = "mod utilis;\n".into(); 60 | visit_dirs(Path::new("tests"), &mut |file| { 61 | let path = file.path(); 62 | if path.extension() != Some(OsStr::new("rs")) { 63 | eprintln!("Not a test file {file:?} {:?}", path.extension()); 64 | return; 65 | } 66 | test_string.push_str(&test_from_path(&path)); 67 | }) 68 | .expect("Could not read the test directory"); 69 | let mut out = 70 | std::fs::File::create("src/test.rs").expect("Could not create the test source file!"); 71 | out.write_all(test_string.as_bytes()).unwrap(); 72 | } 73 | -------------------------------------------------------------------------------- /codegen/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["rustfmt", "clippy", "rust-src", "rustc-dev", "llvm-tools-preview"] 4 | -------------------------------------------------------------------------------- /codegen/src/cgu.rs: -------------------------------------------------------------------------------- 1 | /// Keeps track of all members of a given CGU. Used to quickly check if something is defined in this cgu or not. 2 | /// This allows us to define local functions in a slightly more efficent manner, reducing the number of unneded 3 | struct CGUInfo{} -------------------------------------------------------------------------------- /codegen/src/function.rs: -------------------------------------------------------------------------------- 1 | use rustc_middle::mir::mono::MonoItemData; 2 | use rustc_middle::ty::FloatTy; 3 | use rustc_middle::ty::GenericArg; 4 | use rustc_middle::ty::Instance; 5 | use rustc_middle::ty::IntTy; 6 | use rustc_middle::ty::List; 7 | use rustc_middle::ty::PseudoCanonicalInput; 8 | use rustc_middle::ty::Ty; 9 | use rustc_middle::ty::TyCtxt; 10 | use rustc_middle::ty::TyKind; 11 | use rustc_middle::ty::TypingEnv; 12 | use rustc_middle::ty::UintTy; 13 | 14 | use rustc_target::callconv::{ArgAttribute, PassMode}; 15 | 16 | use rustc_hir::Mutability; 17 | 18 | use rustc_span::def_id::DefId; 19 | 20 | use crate::instance_try_resolve; 21 | use crate::monomorphize; 22 | use crate::souce_builder::is_zst; 23 | use crate::souce_builder::CSourceBuilder; 24 | use crate::souce_builder::SymbolCase; 25 | 26 | use std::fmt::Write; 27 | /// Checks if a given function is public or not. 28 | fn is_public<'tcx>(finstance: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> bool { 29 | if !finstance.def_id().is_local() { 30 | eprintln!("{finstance:?} is not local."); 31 | return true; 32 | } 33 | tcx.visibility(finstance.def_id()).is_public() 34 | } 35 | /// Compiles a function into a C function defintion. 36 | pub(crate) fn compile_function<'tcx>( 37 | finstance: Instance<'tcx>, 38 | _data: MonoItemData, 39 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 40 | tcx: TyCtxt<'tcx>, 41 | ) { 42 | if source_builder.is_defined(finstance) { 43 | return; 44 | } 45 | if tcx.cross_crate_inlinable(finstance.def_id()) { 46 | eprintln!("WARNING {finstance:?} is cross-crate inlineable, so no bindings can be generated for it safely"); 47 | return; 48 | } 49 | 50 | // Skip non-public functions 51 | if !is_public(finstance, tcx) { 52 | return; 53 | } 54 | source_builder.add_fn_def(finstance, tcx); 55 | } 56 | /// Retrives the arg names from debug info when possible, otherwise retunring a set of unqiue names otherwise. 57 | pub fn arg_names<'tcx>(_instance: Instance<'tcx>, _tcx: TyCtxt<'tcx>, args: usize) -> Vec { 58 | (0..args).map(|arg| format!("a{arg}")).collect() 59 | } 60 | /// Calls the C-to-rust shim. 61 | pub fn call_shim<'tcx>( 62 | instance: Instance<'tcx>, 63 | tcx: TyCtxt<'tcx>, 64 | shim_name: &str, 65 | _source_builder: &mut CSourceBuilder, 66 | ) -> String { 67 | let uncodumented = rustc_middle::ty::List::empty(); 68 | 69 | let abi = tcx 70 | .fn_abi_of_instance(PseudoCanonicalInput { 71 | typing_env: TypingEnv::fully_monomorphized(), 72 | value: (instance, uncodumented), 73 | }) 74 | .expect("Could not compute fn abi"); 75 | 76 | let args = arg_names(instance, tcx, abi.args.len()); 77 | let args: String = (&abi.args) 78 | .into_iter() 79 | .zip(args.iter()) 80 | .filter_map(|(arg, name)| { 81 | // Refence: https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/abi/call/enum.PassMode.html 82 | match &arg.mode { 83 | // Ignored, so not in the sig. 84 | PassMode::Ignore => None, 85 | 86 | _ => Some(name.as_ref()), 87 | } 88 | }) 89 | .intersperse(",") 90 | .collect(); 91 | match &abi.ret.mode { 92 | PassMode::Ignore => { 93 | format!("\t{shim_name}({args});\n") 94 | } 95 | _ => { 96 | format!("\treturn {shim_name}({args});\n") 97 | } 98 | } 99 | } 100 | /// Creates the declaration of this funcion(its signature and name). 101 | #[allow(clippy::format_collect, clippy::too_many_lines)] 102 | pub fn fn_decl<'tcx>( 103 | instance: Instance<'tcx>, 104 | tcx: TyCtxt<'tcx>, 105 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 106 | fn_name: &str, 107 | ) -> String { 108 | // The purpose of this arg is not documented... 109 | let uncodumented = rustc_middle::ty::List::empty(); 110 | 111 | let abi = tcx 112 | .fn_abi_of_instance(PseudoCanonicalInput { 113 | typing_env: TypingEnv::fully_monomorphized(), 114 | value: (instance, uncodumented), 115 | }) 116 | .expect("Could not compute fn abi"); 117 | // Handle the ABI of all the argument types 118 | let args = arg_names(instance, tcx, abi.args.len()); 119 | let args: String = (&abi.args) 120 | .into_iter() 121 | .zip(args.iter()) 122 | .filter_map(|(arg, name)| { 123 | // Refence: https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/abi/call/enum.PassMode.html 124 | match &arg.mode { 125 | // Ignored, so not in the sig. 126 | PassMode::Ignore => None, 127 | // PassMode::Direct:Passed directly by value. MUST be a scalar(initger, char, bool, float) or vector of scalars. 128 | // Some of the ArgAttibutes is ignored for now, since it *should* be already handled by the C compiler. 129 | PassMode::Direct(attrs) => { 130 | let restrict = if attrs.regular.contains(ArgAttribute::NoAlias) 131 | && source_builder.supports_restrict() 132 | && arg.layout.ty.is_primitive() 133 | { 134 | " restrict" 135 | } else { 136 | "" 137 | }; 138 | Some(format!( 139 | "{}{restrict} {name}", 140 | c_type_string(arg.layout.ty, tcx, source_builder, instance) 141 | )) 142 | } 143 | 144 | _ => Some(format!( 145 | "{} {}", 146 | c_type_string(arg.layout.ty, tcx, source_builder, instance), 147 | name 148 | )), 149 | } 150 | }) 151 | .intersperse(",".to_string()) 152 | .collect(); 153 | let ret: String = match &abi.ret.mode { 154 | PassMode::Ignore => "void".into(), 155 | 156 | // PassMode::Direct:Passed directly by value. MUST be a scalar(initger, char, bool, float) or vector of scalars. 157 | // Some of the ArgAttibutes is ignored for now, since it *should* be already handled by the C compiler. 158 | PassMode::Direct(attrs) => { 159 | let restrict = if attrs.regular.contains(ArgAttribute::NoAlias) 160 | && source_builder.supports_restrict() 161 | { 162 | "restrict " 163 | } else { 164 | "" 165 | }; 166 | format!( 167 | "{restrict}{}", 168 | c_type_string(abi.ret.layout.ty, tcx, source_builder, instance) 169 | ) 170 | } 171 | _ => c_type_string(abi.ret.layout.ty, tcx, source_builder, instance).to_string(), 172 | }; 173 | 174 | format!("{ret} {fn_name}({args})") 175 | } 176 | 177 | /// Turns a given type `ty` into a C type string, adding typedefs if need be. 178 | #[allow(clippy::format_collect, clippy::too_many_lines)] 179 | pub fn c_type_string<'tcx>( 180 | ty: Ty<'tcx>, 181 | tcx: TyCtxt<'tcx>, 182 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 183 | instance: Instance<'tcx>, 184 | ) -> String { 185 | source_builder.add_ty_templates(ty, tcx); 186 | if !source_builder.delayed_typedefs().contains(&ty) { 187 | source_builder.add_typedefs(ty, tcx, instance); 188 | } 189 | eprintln!( 190 | "c_type_string source_builder:{:?}", 191 | source_builder.delayed_typedefs() 192 | ); 193 | let ty = monomorphize(instance, ty, tcx); 194 | match ty.kind() { 195 | TyKind::Array(elem, length) => format!( 196 | "struct RustArr<{elem},{length}>", 197 | elem = c_type_string(*elem, tcx, source_builder, instance) 198 | ), 199 | TyKind::RawPtr(inner, mutability) | TyKind::Ref(_, inner, mutability) => { 200 | let mutability_str = match mutability { 201 | Mutability::Not => "const", 202 | Mutability::Mut => "", 203 | }; 204 | if crate::is_fat_ptr(ty, tcx, instance) { 205 | match inner.kind() { 206 | TyKind::Str => { 207 | let mutability = match mutability { 208 | Mutability::Not => false, 209 | Mutability::Mut => true, 210 | }; 211 | format!("RustStr<{mutability}>") 212 | } 213 | TyKind::Slice(elem) => { 214 | let tpe = c_type_string(*elem, tcx, source_builder, instance); 215 | format!("RustSlice<{tpe}>") 216 | } 217 | TyKind::Dynamic(_, _, _) => "RustDyn".to_string(), 218 | _ => format!( 219 | "RustFatPtr<{inner}>", 220 | inner = c_type_string(*inner, tcx, source_builder, instance) 221 | ), 222 | } 223 | } else if is_zst(*inner, tcx) { 224 | format!("void {mutability_str}*") 225 | } else { 226 | match inner.kind() { 227 | TyKind::Adt(def, gargs) => { 228 | if !source_builder.is_ty_defined(*inner) { 229 | let adt_instance = instance_try_resolve(def.did(), tcx, gargs); 230 | 231 | let poly_gargs = 232 | List::>::identity_for_item( 233 | tcx, 234 | adt_instance.def_id(), 235 | ); 236 | crate::souce_builder::add_ty_template( 237 | source_builder, 238 | tcx, 239 | Instance::new(adt_instance.def_id(), poly_gargs) 240 | .ty(tcx, TypingEnv::fully_monomorphized()), 241 | ); 242 | } 243 | format!( 244 | "{} {mutability_str}*", 245 | adt_ident(tcx, gargs, def.did(), source_builder, instance) 246 | ) 247 | } 248 | _ => format!( 249 | "{} {mutability_str}*", 250 | c_type_string(*inner, tcx, source_builder, instance) 251 | ), 252 | } 253 | } 254 | } /* 255 | TyKind::Ref(_, inner, mutability) => { 256 | let mutability = match mutability { 257 | Mutability::Not => "const", 258 | Mutability::Mut => "", 259 | }; 260 | if crate::is_fat_ptr(ty, tcx, instance) { 261 | match inner.kind() { 262 | TyKind::Str => { 263 | format!("RustStr") 264 | } 265 | TyKind::Slice(elem) => { 266 | let tpe = c_type_string(*elem, tcx, source_builder, instance); 267 | format!("RustSlice<{tpe}>") 268 | } 269 | TyKind::Dynamic(_, _, _) => format!("RustDyn"), 270 | _ => format!( 271 | "RustFatPtr<{inner}>", 272 | inner = c_type_string(*inner, tcx, source_builder, instance) 273 | ), 274 | } 275 | } else if is_zst(*inner, tcx) { 276 | format!("void {mutability}*") 277 | } else { 278 | format!( 279 | "{} {mutability}&", 280 | c_type_string(*inner, tcx, source_builder, instance) 281 | ) 282 | } 283 | }*/ 284 | TyKind::Char => "uint32_t".into(), 285 | TyKind::Bool => "bool".into(), 286 | TyKind::Int(int) => match int { 287 | IntTy::I8 => "int8_t", 288 | IntTy::I16 => "int16_t", 289 | IntTy::I32 => "int32_t", 290 | IntTy::I64 => "int64_t", 291 | IntTy::I128 => { 292 | if source_builder.supports_i128() { 293 | "__int128_t" 294 | } else { 295 | todo!("Can't yet emulate i128.") 296 | } 297 | } 298 | IntTy::Isize => "intptr_t", 299 | } 300 | .into(), 301 | TyKind::Uint(uint) => match uint { 302 | UintTy::U8 => "uint8_t", 303 | UintTy::U16 => "uint16_t", 304 | UintTy::U32 => "uint32_t", 305 | UintTy::U64 => "uint64_t", 306 | UintTy::U128 => { 307 | if source_builder.supports_i128() { 308 | "__uint128_t" 309 | } else { 310 | todo!("Can't yet emulate i128.") 311 | } 312 | } 313 | UintTy::Usize => "uintptr_t", 314 | } 315 | .into(), 316 | TyKind::Float(float) => match float { 317 | FloatTy::F16 => { 318 | if source_builder.supports_f16() { 319 | "_Float16".into() 320 | } else { 321 | todo!("Can't emulate f16 yet.") 322 | } 323 | } 324 | FloatTy::F32 => "float".into(), 325 | FloatTy::F64 => "double".into(), 326 | FloatTy::F128 => { 327 | if source_builder.supports_f128() { 328 | "__float128".into() 329 | } else { 330 | todo!("Can't emulate f128 yet.") 331 | } 332 | } 333 | }, 334 | TyKind::Adt(def, gargs) => adt_ident(tcx, gargs, def.did(), source_builder, instance), 335 | TyKind::FnPtr(_, _) => "RustFn*".into(), 336 | TyKind::Closure(did, gargs) => { 337 | let adt_instance = instance_try_resolve(*did, tcx, gargs); 338 | // Get the mangled path: it is absolute, and not poluted by types being rexported 339 | format!("struct {}", crate::instance_ident(adt_instance, tcx)) 340 | } 341 | TyKind::Tuple(elems) => { 342 | let generic_string: String = elems 343 | .iter() 344 | .map(|ty| { 345 | crate::souce_builder::generic_ty_string(ty, tcx, source_builder, instance) 346 | }) 347 | .intersperse(",".into()) 348 | .collect(); 349 | let generic_string = format!("<{generic_string}>"); 350 | 351 | format!("RustTuple{generic_string}") 352 | } 353 | TyKind::Slice(_) => mangle(ty, tcx), 354 | TyKind::Never => "void".into(), 355 | TyKind::Str | TyKind::Dynamic(_, _, _) => { 356 | let name = mangle(ty, tcx); 357 | format!("struct {name}") 358 | } 359 | TyKind::FnDef(_, _) => { 360 | use std::hash::Hash; 361 | use std::hash::Hasher; 362 | #[allow(deprecated)] 363 | use std::hash::SipHasher; 364 | #[allow(deprecated)] 365 | let mut hasher = SipHasher::new_with_keys(0xDEAD_C0FFE, 0xBEEF_BABE); 366 | ty.hash(&mut hasher); 367 | 368 | format!("RustFnDef<0x{:x}>", { hasher.finish() }) 369 | } 370 | _ => todo!("Can't turn {ty:?} into a c type", ty = ty.kind()), 371 | } 372 | } 373 | /// Gets the string used to refer to a C++ typedef of an ADT. 374 | fn adt_ident<'tcx>( 375 | tcx: TyCtxt<'tcx>, 376 | gargs: &'tcx List>, 377 | def: DefId, 378 | source_builder: &mut CSourceBuilder<'tcx>, 379 | finstance: Instance<'tcx>, 380 | ) -> String { 381 | let adt_instance = instance_try_resolve(def, tcx, gargs); 382 | // Get the mangled path: it is absolute, and not poluted by types being rexported 383 | let ident = crate::instance_ident(adt_instance, tcx); 384 | let generic_string = 385 | crate::souce_builder::generic_string(gargs, tcx, source_builder, finstance); 386 | if let Some(path) = crate::souce_builder::symbol_to_path(&ident, SymbolCase::Pascal) { 387 | format!( 388 | "{}{generic_string}", 389 | path.iter() 390 | .map(|s| format!("::{}", s.as_str())) 391 | .collect::() 392 | ) 393 | } else { 394 | ident.to_string() 395 | } 396 | } 397 | /// Returns a mangled name of this type. 398 | pub fn mangle<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> String { 399 | match ty.kind() { 400 | TyKind::Int(int) => match int { 401 | IntTy::I8 => "i8", 402 | IntTy::I16 => "i16", 403 | IntTy::I32 => "i32", 404 | IntTy::I64 => "i64", 405 | IntTy::I128 => "i128", 406 | IntTy::Isize => "isize", 407 | } 408 | .into(), 409 | TyKind::Uint(int) => match int { 410 | UintTy::U8 => "u8", 411 | UintTy::U16 => "u16", 412 | UintTy::U32 => "u32", 413 | UintTy::U64 => "u64", 414 | UintTy::U128 => "u128", 415 | UintTy::Usize => "usize", 416 | } 417 | .into(), 418 | TyKind::Float(float) => match float { 419 | FloatTy::F16 => "f16", 420 | FloatTy::F32 => "f32", 421 | FloatTy::F64 => "f64", 422 | FloatTy::F128 => "f128", 423 | } 424 | .into(), 425 | TyKind::Slice(inner) => format!("sl{}", mangle(*inner, tcx)), 426 | TyKind::Str => "ss".into(), 427 | TyKind::Array(elem, len) => format!("a{len}{elem}", elem = mangle(*elem, tcx)), 428 | TyKind::Ref(_, ty, muta) => match muta { 429 | Mutability::Mut => format!("rm{}", mangle(*ty, tcx)), 430 | Mutability::Not => format!("rc{}", mangle(*ty, tcx)), 431 | }, 432 | TyKind::RawPtr(ty, muta) => match muta { 433 | Mutability::Mut => format!("pm{}", mangle(*ty, tcx)), 434 | Mutability::Not => format!("pc{}", mangle(*ty, tcx)), 435 | }, 436 | TyKind::Dynamic(_, _, _) => { 437 | use std::hash::Hash; 438 | use std::hash::Hasher; 439 | let mut s = std::hash::DefaultHasher::new(); 440 | ty.hash(&mut s); 441 | format!("d{:x}", s.finish()) 442 | } 443 | TyKind::Adt(def, gargs) => { 444 | let adt_instance = instance_try_resolve(def.did(), tcx, gargs); 445 | // Get the mangled path: it is absolute, and not poluted by types being rexported 446 | format!( 447 | "a{}", 448 | crate::instance_ident(adt_instance, tcx,).replace('.', "_") 449 | ) 450 | } 451 | TyKind::Tuple(elems) => format!( 452 | "t{}{}", 453 | elems.len(), 454 | elems.iter().fold(String::new(), |mut output: String, e| { 455 | let m = mangle(e, tcx); 456 | let _ = write!(output, "{m_len}{m}", m_len = m.len()); 457 | output 458 | }) 459 | ), 460 | TyKind::Bool => "b".into(), 461 | TyKind::Char => "c".into(), 462 | TyKind::FnPtr(binder, _) => { 463 | let fn_ptr = 464 | tcx.normalize_erasing_late_bound_regions(TypingEnv::fully_monomorphized(), *binder); 465 | format!( 466 | "fp{}{}{}", 467 | fn_ptr.inputs().len(), 468 | fn_ptr 469 | .inputs() 470 | .iter() 471 | .fold(String::new(), |mut output: String, e| { 472 | let m = mangle(*e, tcx); 473 | let _ = write!(output, "{m_len}{m}", m_len = m.len()); 474 | output 475 | }), 476 | { 477 | let m = mangle(fn_ptr.output(), tcx); 478 | format!("{m_len}{m}", m_len = m.len()) 479 | } 480 | ) 481 | } 482 | TyKind::Closure(did, gargs) => { 483 | let adt_instance = instance_try_resolve(*did, tcx, gargs); 484 | // Get the mangled path: it is absolute, and not poluted by types being rexported 485 | crate::instance_ident(adt_instance, tcx).to_string() 486 | } 487 | _ => todo!("Can't mangle {ty:?}"), 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature( 2 | rustc_private, 3 | iter_intersperse, 4 | int_roundings, 5 | let_chains, 6 | hash_set_entry 7 | )] 8 | #![warn(clippy::pedantic)] 9 | #![warn(missing_docs)] 10 | #![warn(clippy::missing_docs_in_private_items)] 11 | 12 | //! `rustc_codegen_c` is a Rust compiler backend, capable of turing Rust MIR into C source files. The project aims to turn Rust into UB-free C, 13 | //! taking many lessons from the previous implementation, `rustc_codegen_clr`. It is written mostly from scrath, with C in mind. The emmited C should also be human-readable, 14 | //! with high-quality type translation, with the code preserving high-level constructs, such as loops or if's. 15 | //! 16 | //! The emmited `C` should also be compatible with ealier versions of `C`, using as few extensions as possible. 17 | //! 18 | //! The project aims to be highly configurable, allowing it to work efficently with a wide range of compilers. 19 | //! 20 | //! Since the goal is to write an easy-to-understand and maintaiable backend, All functions and types within the project need to be documented, and written in a straigtforward way. 21 | //! The number of dependencies shoul also be minimal. 22 | //! 23 | //! The ABI of functions compiled by the projec also should closely follow, if not fully match with the Rust compiler ABI. 24 | 25 | extern crate rustc_abi; 26 | 27 | extern crate rustc_codegen_ssa; 28 | extern crate rustc_const_eval; 29 | extern crate rustc_data_structures; 30 | extern crate rustc_driver; 31 | extern crate rustc_errors; 32 | extern crate rustc_hir; 33 | extern crate rustc_index; 34 | extern crate rustc_metadata; 35 | extern crate rustc_middle; 36 | extern crate rustc_session; 37 | extern crate rustc_span; 38 | extern crate rustc_symbol_mangling; 39 | extern crate rustc_target; 40 | extern crate rustc_ty_utils; 41 | extern crate stable_mir; 42 | /// Handles turning a Rust function into a C one, and is resposible for converting the ABI. 43 | mod function; 44 | /// Used for generating Rust code. 45 | mod rust; 46 | /// Builds the C source, and also contains the config. 47 | mod souce_builder; 48 | /// Handles turning a Rust static into a C one. 49 | mod statics; 50 | /// This module contains a list of generated test cases, created from the .rs files in `tests`. 51 | // Auto-generated, contains no docs. 52 | #[allow(missing_docs, clippy::missing_docs_in_private_items)] 53 | mod test; 54 | /// Misc utilities 55 | mod utilis; 56 | 57 | use rustc_codegen_ssa::back::archive::ArArchiveBuilder; 58 | use rustc_codegen_ssa::back::archive::ArchiveBuilder; 59 | use rustc_codegen_ssa::back::archive::ArchiveBuilderBuilder; 60 | use rustc_codegen_ssa::traits::CodegenBackend; 61 | use rustc_codegen_ssa::CodegenResults; 62 | use rustc_codegen_ssa::CompiledModule; 63 | use rustc_codegen_ssa::CrateInfo; 64 | use rustc_codegen_ssa::ModuleKind; 65 | 66 | use rustc_session::config::OutputFilenames; 67 | use rustc_session::Session; 68 | 69 | use rustc_data_structures::fx::FxIndexMap; 70 | 71 | use rustc_middle::dep_graph::WorkProduct; 72 | use rustc_middle::dep_graph::WorkProductId; 73 | use rustc_middle::mir::mono::MonoItem; 74 | use rustc_middle::ty::EarlyBinder; 75 | use rustc_middle::ty::Instance; 76 | use rustc_middle::ty::Ty; 77 | use rustc_middle::ty::TyCtxt; 78 | use rustc_middle::ty::TypeFoldable; 79 | use rustc_middle::ty::TypingEnv; 80 | 81 | use rustc_span::def_id::DefId; 82 | 83 | use rustc_metadata::EncodedMetadata; 84 | 85 | use souce_builder::CSourceBuilder; 86 | use souce_builder::StringBuilder; 87 | 88 | use std::path::Path; 89 | 90 | use rustc_middle::ty::List; 91 | 92 | use rustc_middle::ty::GenericArg; 93 | 94 | use rustc_middle::ty::PseudoCanonicalInput; 95 | 96 | use std::any::Any; 97 | /// Retrives the name of a static. 98 | fn static_ident(stotic: DefId, tcx: TyCtxt<'_>) -> String { 99 | instance_ident(Instance::mono(tcx, stotic), tcx) 100 | } 101 | /// Retrives the name of an instance. This name *must* be unqiue, altough it may be trimmed. 102 | fn instance_ident<'tcx>(instance: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> String { 103 | rustc_symbol_mangling::symbol_name_for_instance_in_crate(tcx, instance, instance.def_id().krate) 104 | .to_string() 105 | .replace(['.', ' '], "_") 106 | } 107 | #[no_mangle] 108 | /// Entrypoint of the codegen. This function starts the backend up, and returns a reference to it to rustc. 109 | pub extern "Rust" fn __rustc_codegen_backend() -> Box { 110 | Box::new(CBackend) 111 | } 112 | /// Packs all the files created by the codegen into an `rlib`. Highly inspired by cranelifts glue code. 113 | struct RlibArchiveBuilder; 114 | impl ArchiveBuilderBuilder for RlibArchiveBuilder { 115 | fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box { 116 | Box::new(ArArchiveBuilder::new( 117 | sess, 118 | &rustc_codegen_ssa::back::archive::DEFAULT_OBJECT_READER, 119 | )) 120 | } 121 | fn create_dll_import_lib( 122 | &self, 123 | _sess: &Session, 124 | _lib_name: &str, 125 | _dll_imports: std::vec::Vec, 126 | _tmpdir: &Path, 127 | ) { 128 | unimplemented!("creating dll imports is not supported"); 129 | } 130 | } 131 | /// The C backend. 132 | struct CBackend; 133 | impl CodegenBackend for CBackend { 134 | /// Used for codegen-specifc diagnostics: currently, no additional diagnositcs are implemented, so this can stay empty. 135 | fn locale_resource(&self) -> &'static str { 136 | "" 137 | } 138 | /// Compiles a given crate, turning each codegen unit into a separate `C` source file 139 | fn codegen_crate<'a>( 140 | &self, 141 | tcx: TyCtxt<'_>, 142 | metadata: EncodedMetadata, 143 | _need_metadata_module: bool, 144 | ) -> Box { 145 | // What is this `defid_set`? The doc's don't seem to explain it too well... 146 | let monos = tcx.collect_and_partition_mono_items(()); 147 | let crate_info = CrateInfo::new(tcx, "??".to_string()); 148 | // Generate a separate source file for each cgu. 149 | let source_files = { 150 | let mut source_bilder = CSourceBuilder::new( 151 | tcx.sess 152 | .target 153 | .pointer_width 154 | .try_into() 155 | .expect("Targets with pointer size bigger than 256 not supported!"), 156 | ); 157 | let name = crate_info.local_crate_name.to_string(); 158 | for (item, data) in monos 159 | .codegen_units 160 | .iter() 161 | .flat_map(rustc_middle::mir::mono::CodegenUnit::items) 162 | { 163 | rustc_middle::ty::print::with_no_trimmed_paths! {match item { 164 | MonoItem::Fn(finstance) => { 165 | eprintln!("Defining function:{finstance:?}"); 166 | function::compile_function(*finstance, *data, &mut source_bilder, tcx,); 167 | } 168 | MonoItem::Static(static_def) => { 169 | statics::define_static(*static_def, *data, &mut source_bilder, tcx); 170 | } 171 | MonoItem::GlobalAsm(asm) => { 172 | eprintln!("Global asm not supported ATM. asm:{asm:?}"); 173 | } 174 | }} 175 | } 176 | (name, source_bilder.into_source_files()) 177 | }; 178 | 179 | Box::new((source_files, metadata, crate_info)) 180 | } 181 | /// Saves the in-memory C source file 182 | fn join_codegen( 183 | &self, 184 | ongoing_codegen: Box, 185 | _sess: &Session, 186 | outputs: &OutputFilenames, 187 | ) -> (CodegenResults, FxIndexMap) { 188 | let ((name, (header, rs_bridge)), metadata, crate_info) = *ongoing_codegen 189 | .downcast::<( 190 | (String, (StringBuilder, StringBuilder)), 191 | EncodedMetadata, 192 | CrateInfo, 193 | )>() 194 | .expect("in join_codegen: ongoing_codegen is not an Assembly"); 195 | let modules = vec![{ 196 | use std::io::Write; 197 | let header_path = outputs 198 | .temp_path_ext("hpp", Some(&name)) 199 | .with_file_name(&name) 200 | .with_extension("hpp"); 201 | std::fs::File::create(&header_path) 202 | .unwrap() 203 | .write_all(header.bytes()) 204 | .unwrap(); 205 | let rust_bridge_source = outputs 206 | .temp_path_ext("rs_bridge", Some(&name)) 207 | .with_file_name(&name) 208 | .with_extension("rs_bridge"); 209 | std::fs::File::create(&rust_bridge_source) 210 | .unwrap() 211 | .write_all(rs_bridge.bytes()) 212 | .unwrap(); 213 | let out = std::process::Command::new("g++") 214 | .arg(&header_path) 215 | .arg("-fsyntax-only") 216 | .arg("-std=c++20") 217 | .output() 218 | .expect("Could not run a syntax check using g++"); 219 | assert!(out.status.success(), "{out:?}"); 220 | if std::env::var("SB_NO_COMPILE_SHIM").is_err() { 221 | let rust_bridge_lib = outputs 222 | .temp_path_ext("elf", Some(&name)) 223 | .with_file_name(&name) 224 | .with_extension("elf"); 225 | let out = std::process::Command::new("rustc") 226 | .arg(&rust_bridge_source) 227 | .arg("-O") 228 | .arg("--crate-type=staticlib") 229 | .arg("-o") 230 | .arg(&rust_bridge_lib) 231 | .output() 232 | .expect("Could not compile the Rust bridge code."); 233 | eprintln!( 234 | "rust_bridge_source:{rust_bridge_source:?} rust_bridge_lib:{rust_bridge_lib:?}" 235 | ); 236 | assert!(out.status.success(), "stdout:{} stderr:{}",String::from_utf8(out.stdout).unwrap(),String::from_utf8(out.stderr).unwrap()); 237 | CompiledModule { 238 | name, 239 | kind: ModuleKind::Regular, 240 | object: Some(rust_bridge_lib), 241 | bytecode: Some(header_path), 242 | dwarf_object: None, 243 | llvm_ir: None, 244 | assembly: None, 245 | } 246 | } else { 247 | CompiledModule { 248 | name, 249 | kind: ModuleKind::Regular, 250 | object: None, 251 | bytecode: Some(header_path), 252 | dwarf_object: None, 253 | llvm_ir: None, 254 | assembly: Some(rust_bridge_source), 255 | } 256 | } 257 | }]; 258 | let codegen_results = CodegenResults { 259 | modules, 260 | allocator_module: None, 261 | metadata_module: None, 262 | metadata, 263 | crate_info, 264 | }; 265 | assert!(std::env::var("FORCE_FAIL").is_err()); 266 | (codegen_results, FxIndexMap::default()) 267 | } 268 | /// Collects all the files emmited by the codegen for a specific crate, and turns them into a .rlib file containing all the C source files and metadata. 269 | fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { 270 | use rustc_codegen_ssa::back::link::link_binary; 271 | link_binary(sess, &RlibArchiveBuilder, codegen_results, outputs); 272 | //todo!(); 273 | } 274 | } 275 | /// Turns a possibly generic type `T` into a concreate one, removing lifetimes. 276 | pub fn monomorphize<'tcx, T: TypeFoldable> + Clone>( 277 | instance: Instance<'tcx>, 278 | ty: T, 279 | ctx: TyCtxt<'tcx>, 280 | ) -> T { 281 | instance.instantiate_mir_and_normalize_erasing_regions( 282 | ctx, 283 | TypingEnv::fully_monomorphized(), 284 | EarlyBinder::bind(ty), 285 | ) 286 | } 287 | /// Checks the layout of this type to see if it is fat or tree. 288 | /// # Panics 289 | /// Panics if `ptr_type` is not a pointer. 290 | #[must_use] 291 | pub fn is_fat_ptr<'tcx>( 292 | ptr_type: Ty<'tcx>, 293 | tcx: TyCtxt<'tcx>, 294 | method: rustc_middle::ty::Instance<'tcx>, 295 | ) -> bool { 296 | use rustc_abi::BackendRepr; 297 | let ptr_type = monomorphize(method, ptr_type, tcx); 298 | let layout = tcx 299 | .layout_of(rustc_middle::ty::PseudoCanonicalInput { 300 | typing_env: TypingEnv::fully_monomorphized(), 301 | value: ptr_type, 302 | }) 303 | .expect("Can't get layout of a type.") 304 | .layout; 305 | let abi = layout.0 .0.backend_repr; 306 | match abi { 307 | BackendRepr::Scalar(_) => false, 308 | BackendRepr::ScalarPair(_, _) => true, 309 | _ => panic!("Unexpected abi of pointer to {ptr_type:?}. The ABI was:{abi:?}"), 310 | } 311 | } 312 | pub fn instance_try_resolve<'tcx>( 313 | adt: DefId, 314 | tcx: TyCtxt<'tcx>, 315 | gargs: &'tcx List>, 316 | ) -> Instance<'tcx> { 317 | tcx.resolve_instance_raw(PseudoCanonicalInput { 318 | typing_env: rustc_middle::ty::TypingEnv::fully_monomorphized(), 319 | value: (adt, gargs), 320 | }) 321 | .unwrap() 322 | .unwrap() 323 | } 324 | -------------------------------------------------------------------------------- /codegen/src/rust.rs: -------------------------------------------------------------------------------- 1 | use crate::souce_builder::{self, CSourceBuilder}; 2 | use crate::{instance_try_resolve, monomorphize}; 3 | use rustc_middle::ty::Instance; 4 | use rustc_middle::ty::PseudoCanonicalInput; 5 | use rustc_middle::ty::Ty; 6 | use rustc_middle::ty::TyCtxt; 7 | use rustc_middle::ty::TyKind; 8 | use rustc_middle::ty::TypingEnv; 9 | 10 | use rustc_target::callconv::PassMode; 11 | 12 | use rustc_hir::Mutability; 13 | 14 | pub fn rust_shim<'tcx>( 15 | souce_builder: &mut CSourceBuilder<'tcx>, 16 | original_name: &str, 17 | shim_name: &str, 18 | instance: Instance<'tcx>, 19 | tcx: TyCtxt<'tcx>, 20 | ) { 21 | // 22 | let uncodumented = rustc_middle::ty::List::empty(); 23 | let abi = tcx 24 | .fn_abi_of_instance(PseudoCanonicalInput { 25 | typing_env: TypingEnv::fully_monomorphized(), 26 | value: (instance, uncodumented), 27 | }) 28 | .expect("Could not compute fn abi"); 29 | let args = crate::function::arg_names(instance, tcx, abi.args.len()); 30 | let shim_ret: String = rust_type_string(abi.ret.layout.ty, tcx, souce_builder, instance, false); 31 | let shim_args: String = (&abi.args) 32 | .into_iter() 33 | .zip(args.iter()) 34 | .filter_map(|(arg, name)| { 35 | // Refence: https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/abi/call/enum.PassMode.html 36 | match &arg.mode { 37 | // Ignored, so not in the sig. 38 | PassMode::Ignore => None, 39 | // PassMode::Direct:Passed directly by value. MUST be a scalar(initger, char, bool, float) or vector of scalars. 40 | // Some of the ArgAttibutes is ignored for now, since it *should* be already handled by the C compiler. 41 | _ => Some(format!( 42 | "{}:{}", 43 | name, 44 | rust_type_string(arg.layout.ty, tcx, souce_builder, instance, false), 45 | )), 46 | } 47 | }) 48 | .intersperse(",".to_string()) 49 | .collect(); 50 | let escaped_shim_name = shim_name.replace('$', "ds"); 51 | let escaped_real_name = original_name.replace('$', "ds"); 52 | souce_builder.add_rust(&format!( 53 | "extern \"Rust\"{{#[link_name = \"{original_name}\"]pub fn {escaped_real_name}({shim_args})->{shim_ret};}}\n", 54 | )); 55 | let real_args: String = (&abi.args) 56 | .into_iter() 57 | .zip(args.iter()) 58 | .map(|(arg, name)| { 59 | format!( 60 | "{}:{}", 61 | name, 62 | rust_type_string(arg.layout.ty, tcx, souce_builder, instance, true), 63 | ) 64 | }) 65 | .intersperse(",".to_string()) 66 | .collect(); 67 | 68 | let real_ret: String = rust_type_string(abi.ret.layout.ty, tcx, souce_builder, instance, true); 69 | let translated_args: String = (&abi.args) 70 | .into_iter() 71 | .zip(args.iter()) 72 | .map(|(arg, name)| { 73 | // Refence: https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/abi/call/enum.PassMode.html 74 | match &arg.mode { 75 | // Ignored, so not in the sig. 76 | PassMode::Ignore => "()".into(), 77 | // PassMode::Direct:Passed directly by value. MUST be a scalar(initger, char, bool, float) or vector of scalars. 78 | // Some of the ArgAttibutes is ignored for now, since it *should* be already handled by the C compiler. 79 | _ => format!("{name}.into()"), 80 | } 81 | }) 82 | .intersperse((",").to_string()) 83 | .collect(); 84 | let translator_body = format!("unsafe{{{escaped_real_name}({translated_args}).into()}}"); 85 | 86 | souce_builder.add_rust(&format!( 87 | "#[export_name = \"{shim_name}\"]\n#[linkage = \"linkonce_odr\"]\npub extern \"C\" fn {escaped_shim_name}({real_args})->{real_ret}{{\n{translator_body}\n}}\n", 88 | )); 89 | } 90 | 91 | pub fn rust_type_string<'tcx>( 92 | ty: Ty<'tcx>, 93 | tcx: TyCtxt<'tcx>, 94 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 95 | instance: Instance<'tcx>, 96 | c_safe: bool, 97 | ) -> String { 98 | let ty = monomorphize(instance, ty, tcx); 99 | match ty.kind() { 100 | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) | TyKind::Bool => format!("{ty:?}"), 101 | TyKind::Char => "u32".into(), 102 | TyKind::Ref(_, inner, mutability) | TyKind::RawPtr(inner, mutability) => { 103 | let mutability = match mutability { 104 | Mutability::Not => "const", 105 | Mutability::Mut => "mut", 106 | }; 107 | if crate::is_fat_ptr(ty, tcx, instance) { 108 | match inner.kind() { 109 | TyKind::Str => { 110 | if c_safe { 111 | "RustStr".into() 112 | } else { 113 | "*const str".into() 114 | } 115 | } 116 | TyKind::Slice(elem) => { 117 | if c_safe { 118 | "RustSlice".into() 119 | } else { 120 | format!( 121 | "*const [{elem}]", 122 | elem = 123 | rust_type_string(*elem, tcx, source_builder, instance, c_safe) 124 | ) 125 | } 126 | } 127 | TyKind::Dynamic(_, _, _) => { 128 | if c_safe { 129 | "RustDyn".into() 130 | } else { 131 | "*const [u8]".into() 132 | } 133 | } 134 | _ => "RustFatPtr".to_string(), 135 | } 136 | } else if souce_builder::is_zst(*inner, tcx) { 137 | format!("*{mutability} ()") 138 | } else { 139 | format!( 140 | "*{mutability} {}", 141 | rust_type_string(*inner, tcx, source_builder, instance, c_safe) 142 | ) 143 | } 144 | } 145 | TyKind::Adt(def, gargs) => { 146 | let adt_instance = instance_try_resolve(def.did(), tcx, gargs); 147 | // Get the mangled path: it is absolute, and not poluted by types being rexported 148 | crate::instance_ident(adt_instance, tcx).replace('$', "ds") 149 | } 150 | TyKind::Closure(did, gargs) => { 151 | let adt_instance = instance_try_resolve(*did, tcx, gargs); 152 | // Get the mangled path: it is absolute, and not poluted by types being rexported 153 | crate::instance_ident(adt_instance, tcx).replace('$', "ds") 154 | } 155 | TyKind::Tuple(elements) => { 156 | if elements.is_empty() { 157 | "()".into() 158 | } else { 159 | crate::function::mangle(ty, tcx).to_string() 160 | } 161 | /*} else { 162 | format!( 163 | "({})", 164 | elements 165 | .iter() 166 | .map(|ty| rust_type_string(ty, tcx, source_builder, instance, c_safe)) 167 | .intersperse(",".to_string()) 168 | .collect::() 169 | ) 170 | } */ 171 | } 172 | TyKind::Slice(_) => "RawSlice".into(), 173 | TyKind::Array(elem, length) => format!( 174 | "[{elem};{length}]", 175 | elem = rust_type_string(*elem, tcx, source_builder, instance, c_safe) 176 | ), 177 | TyKind::Never => { 178 | if c_safe { 179 | eprintln!("Never in C."); 180 | "!".into() 181 | } else { 182 | "!".into() 183 | } 184 | } 185 | TyKind::FnDef(_, _) => { 186 | if c_safe { 187 | eprintln!("FnDef in C."); 188 | "()".into() 189 | } else { 190 | "()".into() 191 | } 192 | } 193 | TyKind::FnPtr(_, _) => "*const ()".to_owned(), 194 | TyKind::Dynamic(_, _, _) => "Dynamic".into(), 195 | _ => todo!("Can't convert {ty:?} to a Rust type."), 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /codegen/src/souce_builder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use crate::instance_try_resolve; 4 | use crate::rust::rust_type_string; 5 | use crate::rustc_middle::ty::TypeVisitableExt; 6 | use rustc_middle::ty::AdtKind; 7 | use rustc_middle::ty::GenericArg; 8 | use rustc_middle::ty::Instance; 9 | use rustc_middle::ty::IntTy; 10 | use rustc_middle::ty::List; 11 | use rustc_middle::ty::Mutability; 12 | use rustc_middle::ty::ParamTy; 13 | use rustc_middle::ty::PseudoCanonicalInput; 14 | use rustc_middle::ty::Ty; 15 | use rustc_middle::ty::TyCtxt; 16 | use rustc_middle::ty::TyKind; 17 | use rustc_middle::ty::TypingEnv; 18 | use rustc_middle::ty::UintTy; 19 | 20 | use rustc_abi::FieldsShape; 21 | use rustc_abi::Variants; 22 | 23 | use rustc_abi::Size; 24 | 25 | use crate::function::c_type_string; 26 | use crate::function::mangle; 27 | use crate::rust::rust_shim; 28 | mod predefined; 29 | mod typedef; 30 | use predefined::{C_TYPEDEFS, MANDATORY_HEADERS}; 31 | 32 | /// An append-only UTF-8 string. 33 | // In the future, its append-only nature can be used for optimzations. 34 | // For example, it could be implemented using memory pages, with flags encouraging the kernel to swap it out of ram. 35 | // Those memmapped pages can then be diretly coppied to a file. 36 | #[derive(Default, Debug)] 37 | pub struct StringBuilder { 38 | /// Inner append-only buffer 39 | buffer: String, 40 | } 41 | impl StringBuilder { 42 | /// Appends a string to this builder. 43 | pub fn push(&mut self, s: impl AsRef) { 44 | let s = s.as_ref(); 45 | assert_eq!( 46 | s.chars().filter(|c| *c == '{').count(), 47 | s.chars().filter(|c| *c == '}').count() 48 | ); 49 | self.buffer.push_str(s); 50 | } 51 | /// A reference to the underlying buffer, to be written to disk. 52 | pub fn bytes(&self) -> &[u8] { 53 | self.buffer.as_bytes() 54 | } 55 | } 56 | /// A special structure allowing for types to be defined. 57 | #[derive(Debug)] 58 | pub struct CSourceBuilder<'tcx> { 59 | /// The generated C++ header file 60 | source_file: StringBuilder, 61 | /// Rust bridge file 62 | rust_file: StringBuilder, 63 | /// Defines what ``size_of(uintptr_t)`` is for a given target. 64 | size_of_usize: u8, 65 | /// Lists all the headers included by this file. 66 | includes: HashSet, 67 | /// Contains all already `defined` functions. 68 | defined: HashSet>, 69 | /// Contains all already `decalred` functions. 70 | decalred: HashSet>, 71 | /// Contains all types, for which a full defintion has been provided. 72 | defined_tys: HashSet>, 73 | /// Contains all types, for which a declaration has been provided. 74 | declared_tys: HashSet>, 75 | /// Types which don't yet need to be fully defined, but should be defined at some later point. 76 | delayed_typedefs: std::collections::vec_deque::VecDeque>, 77 | 78 | rust_uids: HashSet, 79 | } 80 | impl<'tcx> CSourceBuilder<'tcx> { 81 | pub fn delayed_typedefs(&self) -> &std::collections::vec_deque::VecDeque> { 82 | &self.delayed_typedefs 83 | } 84 | /// Checks if the type defintion of a given type is already present. 85 | pub fn is_ty_defined(&mut self, ty: Ty<'tcx>) -> bool { 86 | if ty.is_primitive() { 87 | return true; 88 | } 89 | self.defined_tys.contains(&ty) 90 | } 91 | /// Checks if the type declaration of a given type is already present. 92 | pub fn is_ty_declared(&mut self, ty: Ty<'tcx>) -> bool { 93 | if ty.is_primitive() { 94 | return true; 95 | } 96 | self.declared_tys.contains(&ty) 97 | } 98 | /// Checks if a given instance is declared(it may not be defined, so its implementation may be somewhere else) 99 | pub fn set_declared(&mut self, instance: Instance<'tcx>) { 100 | self.decalred.insert(instance); 101 | } 102 | 103 | /// Checks if this instance is `defined` 104 | pub fn is_defined(&self, instance: Instance<'tcx>) -> bool { 105 | self.defined.contains(&instance) 106 | } 107 | /// Checks if this instance is `declared` 108 | pub fn is_declared(&self, instance: Instance<'tcx>) -> bool { 109 | self.decalred.contains(&instance) 110 | } 111 | /// Includes the specified source file if it is not already include. 112 | pub fn include(&mut self, file_name: &str) { 113 | assert!(!file_name.contains('#')); 114 | assert!(!file_name.contains('<')); 115 | assert!(!file_name.contains('>')); 116 | self.source_file.push(format!("#include <{file_name}>\n")); 117 | assert!(self.includes.insert(file_name.to_owned())); 118 | } 119 | /// When compiling the C source file, asserts that a given condtion `cond` is true. The `message` must be a valid C identifier name. 120 | pub fn static_assert(&mut self, cond: &str, message: &str) { 121 | assert!( 122 | message 123 | .chars() 124 | .all(|c| c.is_alphanumeric() || c == '_' || c == '$'), 125 | "Invalid static assert message:{message:?}" 126 | ); 127 | self.source_file 128 | .push(format!("typedef char assert_{message}[({cond})?1:-1];\n")); 129 | } 130 | /// A convience wrapper around [`Self::static_assert`], checking the size of a given type at compile time. 131 | pub fn assert_sizeof(&mut self, type_string: &str, size: u64) { 132 | if size == 0 { 133 | return; 134 | } 135 | self.static_assert( 136 | &format!("sizeof({type_string}) == {size}"), 137 | &format!( 138 | "sizeof_{type_string}_is_{size}", 139 | type_string = type_string.replace('*', "ptr").replace(' ', "_") 140 | ), 141 | ); 142 | } 143 | /// A convience wrapper around [`Self::static_assert`], checking the aligement of a given type at compile time. 144 | pub fn assert_alignof(&mut self, type_string: &str, alignment: u64) { 145 | self.static_assert( 146 | &format!("alignof({type_string}) == {alignment}"), 147 | &format!( 148 | "alignof_{type_string}_is_{alignment}", 149 | type_string = type_string.replace('*', "ptr").replace(' ', "_") 150 | ), 151 | ); 152 | } 153 | pub fn add_rust(&mut self, s: &str) { 154 | self.rust_file.push(s); 155 | } 156 | /// Adds a rust defitioninon with given `uid`, guaranteeing that no duplicates exist. 157 | /// This is a workaround for some nasty `C` issues. 158 | pub fn add_rust_if_missing(&mut self, s: &str, uid: &str) { 159 | assert!(!s.contains('$')); 160 | match self.rust_uids.entry(uid.to_string()) { 161 | std::collections::hash_set::Entry::Occupied(_) => (), 162 | std::collections::hash_set::Entry::Vacant(vacant_entry) => { 163 | self.rust_file.push(s); 164 | vacant_entry.insert(); 165 | } 166 | }; 167 | } 168 | /// Creates a new source file with the specified settings. 169 | /// `size_of_usize`: result of computing ``size_of(uintptr_t)`` for a given target. Is in bytes. 170 | pub fn new(size_of_usize: u8) -> Self { 171 | let mut res = Self { 172 | size_of_usize, 173 | source_file: StringBuilder::default(), 174 | rust_file: StringBuilder::default(), 175 | includes: HashSet::default(), 176 | defined: HashSet::default(), 177 | decalred: HashSet::default(), 178 | defined_tys: HashSet::default(), 179 | declared_tys: HashSet::default(), 180 | delayed_typedefs: std::collections::vec_deque::VecDeque::with_capacity(0x100), 181 | rust_uids: HashSet::default(), 182 | }; 183 | MANDATORY_HEADERS.iter().for_each(|h| res.include(h)); 184 | C_TYPEDEFS.iter().for_each(|h| res.source_file.push(h)); 185 | res.assert_sizeof("void*", (res.size_of_usize / 8).into()); 186 | 187 | res.rust_file.push("#![feature(slice_ptr_get,linkage)]\n"); 188 | res.rust_file 189 | .push("#![allow(non_camel_case_types,unreachable_code)]"); 190 | res.rust_file.push("#[derive(Clone,Copy)]#[repr(C)] 191 | pub struct RustStr{ptr:*const char, len:usize} 192 | impl Into<*const str> for RustStr{ 193 | fn into(self) -> *const str { 194 | unsafe{std::slice::from_raw_parts(self.ptr as *const u8,self.len) as *const [u8] as *const str} 195 | } 196 | }"); 197 | res.rust_file.push( 198 | "#[derive(Clone,Copy)]#[repr(C)]pub struct RustFatPtr{ptr:*const char, meta:usize}\n", 199 | ); 200 | res.rust_file 201 | .push("#[derive(Clone,Copy)]#[repr(C)]pub struct RawSlice;\n"); 202 | res.rust_file 203 | .push("#[derive(Clone,Copy)]#[repr(C)]pub struct Dynamic;\n"); 204 | res.rust_file.push( 205 | "#[derive(Clone,Copy)]#[repr(C)] 206 | pub struct RustSlice{ptr:*const char, len:usize} 207 | impl Into<*const [T]> for RustSlice{ 208 | fn into(self) -> *const [T]{ 209 | unsafe{std::slice::from_raw_parts(self.ptr as *const T,self.len) as *const _} 210 | } 211 | }", 212 | ); 213 | res.rust_file.push( 214 | "#[derive(Clone,Copy)]#[repr(C)] 215 | pub struct RustDyn{ptr:*const u8, len:usize} 216 | impl Into<*const [T]> for RustDyn{ 217 | fn into(self) -> *const [T]{ 218 | unsafe{std::slice::from_raw_parts(self.ptr as *const T,self.len) as *const _} 219 | } 220 | } 221 | impl Into for *const [T]{ 222 | fn into(self) -> RustDyn{ 223 | RustDyn{ptr:self.as_ptr() as *const u8, len:self.len()} 224 | } 225 | }", 226 | ); 227 | res.rust_file.push( 228 | "#[repr(C)]#[derive(Clone,Copy)] 229 | struct RustTag{ 230 | offset:[u8;TAG_OFFSET], 231 | tag:Tag, 232 | }", 233 | ); 234 | res 235 | } 236 | /// Checks if the target `C` compiler supports the `restrict` keyword. 237 | // TODO: check if the C version is > C99 or the compiler is known to support `restrict`. 238 | #[allow(clippy::unused_self)] 239 | pub(crate) fn supports_restrict(&self) -> bool { 240 | true 241 | } 242 | /// Generates templates for a given function. 243 | pub fn add_ty_templates(&mut self, t: Ty<'tcx>, tcx: TyCtxt<'tcx>) { 244 | use crate::rustc_middle::ty::TypeVisitable; 245 | t.visit_with(&mut TemplateGenerator { tcx, sb: self }); 246 | } 247 | /// Adds all the typedefs this type needs. 248 | pub fn add_typedefs(&mut self, t: Ty<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { 249 | self.delayed_typedefs.push_front(t); 250 | self.delayed_typedefs.push_front(t); 251 | while let Some(t) = self.delayed_typedefs.pop_back() { 252 | add_ty(self, tcx, t, instance); 253 | } 254 | } 255 | /// Adds a new function *declaration* to this source files. 256 | pub fn add_fn_decl(&mut self, finstance: Instance<'tcx>, tcx: TyCtxt<'tcx>) { 257 | if !self.is_declared(finstance) { 258 | let fn_name = crate::instance_ident(finstance, tcx) 259 | .to_string() 260 | .replace('.', "_"); 261 | 262 | let shim_symbol = c_shim_name(&fn_name); 263 | let shim_decl = crate::function::fn_decl(finstance, tcx, self, &shim_symbol); 264 | let body = format!( 265 | "{call_shim}\n", 266 | call_shim = crate::function::call_shim(finstance, tcx, &shim_symbol, self) 267 | ); 268 | // Check if this function is not representable in C++ due to C++ limitations 269 | if unsuported_garg_in_sig(finstance, tcx) { 270 | eprintln!("WARNING:{finstance:?} contains gargs which are not representable in C++. Skipping."); 271 | return; 272 | } 273 | let generic_instance = instance_try_resolve(finstance.def_id(), tcx, finstance.args); 274 | let poly_gargs = List::>::identity_for_item( 275 | tcx, 276 | generic_instance.def_id(), 277 | ); 278 | if !poly_gargs.is_empty() 279 | && tcx.codegen_fn_attrs(finstance.def_id()).flags.contains( 280 | rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags::TRACK_CALLER, 281 | ) 282 | { 283 | eprintln!("WARNING: {finstance:?} is generic, and has `track_caller`, so bindings to it can't be generated for now."); 284 | return; 285 | } 286 | if self 287 | .add_fn_template(Instance::new(generic_instance.def_id(), poly_gargs), tcx) 288 | .is_none() 289 | { 290 | return; 291 | } 292 | if let Some(path) = symbol_to_path(&fn_name, SymbolCase::Snake) { 293 | // 294 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 295 | let namespace: String = beg 296 | .iter() 297 | .map(std::string::String::as_str) 298 | .intersperse("::") 299 | .collect(); 300 | let gargs = finstance.args; 301 | gargs 302 | .iter() 303 | .filter_map(rustc_middle::ty::GenericArg::as_type) 304 | .for_each(|ty| add_ty(self, tcx, ty, finstance)); 305 | // 306 | let generic_string = generic_string(gargs, tcx, self, finstance); 307 | // Template preifx 308 | 309 | let template = if generic_string.is_empty() { 310 | String::new() 311 | } else { 312 | "template<>".to_string() 313 | }; 314 | 315 | let decl = crate::function::fn_decl( 316 | finstance, 317 | tcx, 318 | self, 319 | &format!("{end}{generic_string}"), 320 | ); 321 | self.source_file 322 | .push(format!("#ifndef _RUST_{fn_name}\nextern \"C\" {shim_decl};\nnamespace {namespace}{{ \n /*fndecl*/ {template} {decl}{{{body}}} }}\n#define _RUST_{fn_name} 1\n#endif\n",)); 323 | } else { 324 | let decl = crate::function::fn_decl(finstance, tcx, self, &fn_name); 325 | self.source_file 326 | .push(format!("extern \"C\" {shim_decl};\n")); 327 | self.source_file.push(&decl); 328 | self.source_file.push(format!("{{{body}}};\n")); 329 | } 330 | rust_shim(self, &fn_name, &shim_symbol, finstance, tcx); 331 | self.set_declared(finstance); 332 | } 333 | } 334 | /// Adds a function definition(the implementation of a function). 335 | pub fn add_fn_def(&mut self, finstance: Instance<'tcx>, tcx: TyCtxt<'tcx>) { 336 | self.add_fn_decl(finstance, tcx); 337 | } 338 | #[allow(clippy::unused_self)] 339 | /// Checks if the target compiler supports 128 bit ints, or if it requires emulation. 340 | pub fn supports_i128(&self) -> bool { 341 | true 342 | } 343 | #[allow(clippy::unused_self)] 344 | /// Checks if the target compiler supports 16 bit floats, or if it requires emulation. 345 | pub fn supports_f16(&self) -> bool { 346 | true 347 | } 348 | #[allow(clippy::unused_self)] 349 | /// Checks if the target compiler supports 128 bit floats, or if it requires emulation. 350 | pub fn supports_f128(&self) -> bool { 351 | true 352 | } 353 | #[allow(clippy::unused_self)] 354 | /// Checks if the target compiler supports the alignment attribute, or if it requires emulation. 355 | pub fn is_align_attr_supported(&self) -> bool { 356 | true 357 | } 358 | /// Truns `self` into the underlying C source file buffer. 359 | pub fn into_source_files(mut self) -> (StringBuilder, StringBuilder) { 360 | self.source_file 361 | .push("#ifdef __unix__\n#define unix 1\n#endif\n"); 362 | (self.source_file, self.rust_file) 363 | } 364 | /// Returns a mutable reference to the backing buffer. 365 | pub fn source_file_mut(&mut self) -> &mut StringBuilder { 366 | &mut self.source_file 367 | } 368 | #[allow(clippy::unused_self)] 369 | /// Checks if the target compiler supports the section attribute 370 | pub fn supports_section(&self) -> bool { 371 | true 372 | } 373 | /// Delays a type definition 374 | pub fn delay_typedef(&mut self, ty: Ty<'tcx>) { 375 | self.delayed_typedefs.push_front(ty); 376 | } 377 | /// Creates function template for a C function. 378 | fn add_fn_template(&mut self, genetric_fn: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> Option<()> { 379 | if self.is_declared(genetric_fn) { 380 | return None; 381 | } 382 | let fn_name = crate::instance_ident(genetric_fn, tcx) 383 | .to_string() 384 | .replace('.', "_"); 385 | let Some(path) = symbol_to_path(&fn_name, SymbolCase::Snake) else { 386 | eprintln!( 387 | "Skipping template declaration for {fn_name} cause it can't be turned into a path." 388 | ); 389 | return None; 390 | }; 391 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 392 | let namespace: String = beg 393 | .iter() 394 | .map(std::string::String::as_str) 395 | .intersperse("::") 396 | .collect(); 397 | let mut t_idx = 0; 398 | let mut c_idx = 0; 399 | let garg_body: String = genetric_fn 400 | .args 401 | .iter() 402 | .filter_map(|garg| { 403 | if garg.as_type().is_some() { 404 | let ts = format!("typename T{t_idx}"); 405 | t_idx += 1; 406 | Some(ts) 407 | } else { 408 | garg.as_const()?; 409 | 410 | let cs = format!("typename TC{c_idx}, TC{c_idx} C{c_idx}"); 411 | c_idx += 1; 412 | Some(cs) 413 | } 414 | }) 415 | .intersperse(",".to_string()) 416 | .collect(); 417 | let template = if garg_body.is_empty() { 418 | String::new() 419 | } else { 420 | format!("template<{garg_body}> ") 421 | }; 422 | // TODO: fully 423 | let polyfn_sig = tcx.fn_sig(genetric_fn.def_id()); 424 | let Some(ret) = (polyfn_sig.skip_binder()).output().no_bound_vars() else { 425 | eprintln!("Skipping template declaration for {fn_name} cause it has a generic return."); 426 | return None; 427 | }; 428 | let Some(inputs) = (polyfn_sig.skip_binder()).inputs().no_bound_vars() else { 429 | eprintln!("Skipping template declaration for {fn_name} cause it has a generic arg."); 430 | return None; 431 | }; 432 | if ret.has_bound_vars() { 433 | eprintln!("Skipping template declaration for {fn_name} cause it has a generic(has_escaping_bound_vars) return."); 434 | return None; 435 | } 436 | let inputs = inputs.iter().enumerate().map(|(arg_idx,arg)|{if is_generic(*arg){ 437 | if let TyKind::Param(param) = arg.kind(){ 438 | Some(format!("{} a{arg_idx}", paramidx_to_name(*param,genetric_fn.args))) 439 | }else{ 440 | eprintln!("Skipping template declaration for {fn_name} cause it has a generic(is_generic) arg."); 441 | None 442 | } 443 | }else { 444 | eprintln!("arg:{arg:?}"); 445 | assert!(!matches!(arg.kind(),TyKind::Param(_))); 446 | Some(format!("{} a{arg_idx}",c_type_string(*arg, tcx, self, genetric_fn))) 447 | }}).collect::>>()?; 448 | let inputs = inputs 449 | .into_iter() 450 | .intersperse(",".into()) 451 | .collect::(); 452 | let ret = if is_generic(ret) { 453 | if let TyKind::Param(param) = ret.kind() { 454 | paramidx_to_name(*param, genetric_fn.args) 455 | } else { 456 | eprintln!("Skipping template declaration for {fn_name} cause it has a generic(is_generic) return."); 457 | return None; 458 | } 459 | } else if is_zst(ret, tcx) { 460 | "void".into() 461 | } else { 462 | c_type_string(ret, tcx, self, genetric_fn) 463 | }; 464 | 465 | let decl = format!("{ret} {end}({inputs})"); 466 | self.source_file.push(format!( 467 | "namespace {namespace}{{ /*fn template*/ 468 | {template}{decl}; }}", 469 | )); 470 | self.source_file.push(";\n"); 471 | self.set_declared(genetric_fn); 472 | Some(()) 473 | } 474 | } 475 | /// Adds the type `t` to `sb` 476 | #[allow(clippy::too_many_lines)] 477 | fn add_ty<'tcx>( 478 | sb: &mut CSourceBuilder<'tcx>, 479 | tcx: TyCtxt<'tcx>, 480 | t: Ty<'tcx>, 481 | instance: Instance<'tcx>, 482 | ) { 483 | // If this type is fully defined, then its children must be fully defined, so this is OK. 484 | let t = crate::monomorphize(instance, t, tcx); 485 | sb.add_ty_templates(t, tcx); 486 | if sb.is_ty_defined(t) { 487 | return; 488 | } 489 | eprintln!("{t} is not defined."); 490 | match t.kind() { 491 | TyKind::FnDef(_, _) => (), 492 | TyKind::Ref(_, inner, _) | TyKind::RawPtr(inner, _) => { 493 | if !crate::is_fat_ptr(t, tcx, instance) { 494 | sb.delay_typedef(*inner); 495 | sb.defined_tys.insert(t); 496 | } 497 | } 498 | TyKind::Array(elem, _) => { 499 | add_ty(sb, tcx, *elem, instance); 500 | } 501 | TyKind::Tuple(elems) => { 502 | sb.defined_tys.insert(t); 503 | let layout = tcx 504 | .layout_of(PseudoCanonicalInput { 505 | typing_env: TypingEnv::fully_monomorphized(), 506 | value: t, 507 | }) 508 | .expect("Could not compute the layout of a type."); 509 | let Variants::Single { index: _ } = layout.variants else { 510 | panic!("Tuple must have single variant layout."); 511 | }; 512 | let name = mangle(t, tcx); 513 | match &layout.fields { 514 | FieldsShape::Primitive => { 515 | panic!("type {name} has primitive layout, but is not primitive.") 516 | } 517 | FieldsShape::Arbitrary { 518 | offsets, 519 | memory_index: _, 520 | } => { 521 | let mut offsets: Vec<(_, _)> = offsets.clone().into_iter_enumerated().collect(); 522 | // Sort fields by their offset, to guarantee they are in C order. 523 | offsets.sort_by(|(_, offset_a), (_, offset_b)| offset_a.cmp(offset_b)); 524 | let mut last_offset = Size::from_bytes(0); 525 | let mut pad_count = 0; 526 | let mut fields = String::new(); 527 | let mut rust_fields = String::new(); 528 | for (field_idx, offset) in &offsets { 529 | if *offset != last_offset { 530 | assert!(offset.bytes() > last_offset.bytes()); 531 | let pad_size = offset.bytes() - last_offset.bytes(); 532 | fields.push_str(&format!("uint8_t pad_{pad_count}[{pad_size}];\n",)); 533 | rust_fields.push_str(&format!("pad_{pad_count}:[u8;{pad_size}],")); 534 | pad_count += 1; 535 | } 536 | let field_ty = elems[field_idx.as_usize()]; 537 | add_ty(sb, tcx, field_ty, instance); 538 | if is_zst(crate::monomorphize(instance, field_ty, tcx), tcx) { 539 | continue; 540 | } 541 | let field_name = format!("f{}", field_idx.as_usize()); 542 | fields.push_str(&format!( 543 | "{} {field_name};\n", 544 | c_type_string(field_ty, tcx, sb, instance) 545 | )); 546 | rust_fields.push_str(&format!( 547 | "{field_name}:{},", 548 | rust_type_string(field_ty, tcx, sb, instance, true) 549 | )); 550 | last_offset = *offset + size(field_ty, tcx); 551 | } 552 | let generic_string: String = elems 553 | .iter() 554 | .map(|ty| generic_ty_string(ty, tcx, sb, instance)) 555 | .intersperse(",".into()) 556 | .collect(); 557 | let generic_string = format!("<{generic_string}>"); 558 | let align = if sb.is_align_attr_supported() { 559 | format!("__attribute__ ((aligned ({})))\n", layout.align.abi.bytes()) 560 | } else { 561 | String::new() 562 | }; 563 | let mangled_name = mangle(t, tcx); 564 | sb.add_rust_if_missing( 565 | &format!("#[derive(Clone,Copy)]#[repr(C)]\nstruct {mangled_name}{{{rust_fields}}}\n"), 566 | &mangled_name, 567 | ); 568 | sb.source_file.push(format!( 569 | "#ifndef _RUST_{mangled_name}\ntemplate <> struct {align}RustTuple{generic_string}{{\n{fields}}};\n#define _RUST_{mangled_name} 1\n#endif\n" 570 | )); 571 | } 572 | //FieldsShape::Union => 573 | _ => todo!("Unhandled field layout: {:?}", layout.fields), 574 | } 575 | } 576 | TyKind::FnPtr(binder, _) => { 577 | let fn_ptr = 578 | tcx.normalize_erasing_late_bound_regions(TypingEnv::fully_monomorphized(), *binder); 579 | 580 | fn_ptr.inputs().iter().for_each(|t| sb.delay_typedef(*t)); 581 | sb.delay_typedef(fn_ptr.output()); 582 | } 583 | TyKind::Closure(did, gargs) => { 584 | let adt_instance = instance_try_resolve(*did, tcx, gargs); 585 | // Get the mangled path: it is absolute, and not poluted by types being rexported 586 | let name = crate::instance_ident(adt_instance, tcx).replace('.', "_"); 587 | let layout = tcx 588 | .layout_of(PseudoCanonicalInput { 589 | typing_env: TypingEnv::fully_monomorphized(), 590 | value: t, 591 | }) 592 | .expect("Could not compute the layout of a type."); 593 | 594 | match &layout.variants { 595 | Variants::Empty=>todo!(), 596 | Variants::Single { index: _ } => match &layout.fields{ 597 | FieldsShape::Primitive => panic!("type {name} has primitive layout, but is not primitive."), 598 | FieldsShape::Arbitrary{offsets, memory_index:_}=>{ 599 | let clsoure_fields:Vec<_> = gargs. as_closure().upvar_tys().to_vec(); 600 | let mut offsets:Vec<(_,_)> = offsets.clone().into_iter_enumerated().collect(); 601 | // Sort fields by their offset, to guarantee they are in C order. 602 | offsets.sort_by(|(_,offset_a),(_,offset_b)|offset_a.cmp(offset_b)); 603 | let mut last_offset = Size::from_bytes(0); 604 | let mut pad_count = 0; 605 | let mut fields = String::new(); 606 | for (field_idx,offset) in &offsets{ 607 | if *offset != last_offset{ 608 | assert!(offset.bytes() > last_offset.bytes()); 609 | fields.push_str(&format!("uint8_t pad_{pad_count}[{}];\n",offset.bytes() - last_offset.bytes())); 610 | pad_count += 1; 611 | } 612 | let field_ty = &clsoure_fields[field_idx.as_usize()]; 613 | add_ty(sb,tcx,*field_ty,instance); 614 | if is_zst( crate::monomorphize(instance, *field_ty, tcx),tcx){ 615 | continue; 616 | } 617 | let field_name = format!("f{}",field_idx.as_usize()); 618 | fields.push_str(&format!("{} {field_name};\n",c_type_string(*field_ty, tcx, sb,instance))); 619 | last_offset = *offset + size(*field_ty,tcx); 620 | } 621 | let align = if sb.is_align_attr_supported(){ 622 | format!("__attribute__ ((aligned ({})))\n",layout.align.abi.bytes()) 623 | } else{ 624 | String::new() 625 | }; 626 | sb.source_file.push(format!("#ifndef _RUST_TY_DEF_{name}\nstruct {align}{name}{{\n{fields}}};\n#define _RUST_TY_DEF_{name} 1\n#endif\n")); 627 | sb.assert_sizeof(&format!("struct {name}"), layout.size.bytes()); 628 | sb.assert_alignof(&format!("struct {name}"), layout.align.abi.bytes()); 629 | } 630 | //FieldsShape::Union => 631 | _=>todo!("Unhandled field layout: {:?}",layout.fields), 632 | } 633 | Variants::Multiple { 634 | tag, 635 | tag_encoding, 636 | tag_field, 637 | variants, 638 | } => todo!( 639 | "Can't encode enums quite yet! {tag:?} {tag_encoding:?} {tag_field:?} {variants:?}" 640 | ), 641 | } 642 | } 643 | TyKind::Dynamic(_, _, _) => { 644 | let name = mangle(t, tcx); 645 | sb.source_file.push(format!("struct {name}{{}};\n")); 646 | } 647 | TyKind::Adt(def, gargs) => { 648 | let adt_instance = instance_try_resolve(def.did(), tcx, gargs); 649 | let poly_gargs = List::>::identity_for_item( 650 | tcx, 651 | adt_instance.def_id(), 652 | ); 653 | add_ty_template( 654 | sb, 655 | tcx, 656 | Instance::new(adt_instance.def_id(), poly_gargs) 657 | .ty(tcx, TypingEnv::fully_monomorphized()), 658 | ); 659 | // Get the mangled path: it is absolute, and not poluted by types being rexported 660 | let name = crate::instance_ident(adt_instance, tcx); 661 | eprintln!("{t:?}"); 662 | let layout = tcx 663 | .layout_of(PseudoCanonicalInput { 664 | typing_env: TypingEnv::fully_monomorphized(), 665 | value: t, 666 | }) 667 | .expect("Could not compute the layout of a type."); 668 | let generics = generic_string(gargs, tcx, sb, instance); 669 | let template_preifx = if generics.is_empty() { 670 | "" 671 | } else { 672 | "template<> " 673 | }; 674 | 675 | match &layout.variants { 676 | Variants::Empty => { 677 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 678 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 679 | let namespace: String = beg 680 | .iter() 681 | .map(std::string::String::as_str) 682 | .intersperse("::") 683 | .collect(); 684 | 685 | sb.source_file.push(format!( 686 | "\n#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace} {{{template_preifx}struct {end}{generics}{{}};}}\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 687 | )); 688 | } else { 689 | sb.source_file 690 | .push(format!("\n#ifndef _RUST_TY_DEF_{name} struct {name}{{}};#define _RUST_TY_DEF_{name} 1\n#endif\n")); 691 | sb.assert_sizeof(&format!("struct {name}"), layout.size.bytes()); 692 | sb.assert_alignof(&format!("struct {name}"), layout.align.abi.bytes()); 693 | } 694 | }, 695 | Variants::Single { index: _ } => match &layout.fields { 696 | FieldsShape::Primitive if is_zst(t, tcx) => { 697 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 698 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 699 | let namespace: String = beg 700 | .iter() 701 | .map(std::string::String::as_str) 702 | .intersperse("::") 703 | .collect(); 704 | 705 | sb.source_file.push(format!( 706 | "\n#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace} {{{template_preifx}struct {end}{generics}{{}};}}\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 707 | )); 708 | } else { 709 | sb.source_file 710 | .push(format!("\n#ifndef _RUST_TY_DEF_{name} struct {name}{{}};#define _RUST_TY_DEF_{name} 1\n#endif\n")); 711 | sb.assert_sizeof(&format!("struct {name}"), layout.size.bytes()); 712 | sb.assert_alignof(&format!("struct {name}"), layout.align.abi.bytes()); 713 | } 714 | } 715 | FieldsShape::Primitive => { 716 | panic!("type {name} has primitive layout, but is not primitive.") 717 | } 718 | FieldsShape::Arbitrary { 719 | offsets, 720 | memory_index: _, 721 | } => { 722 | let variant_0 = def.variants().iter().next().unwrap(); 723 | let mut offsets: Vec<(_, _)> = 724 | offsets.clone().into_iter_enumerated().collect(); 725 | // Sort fields by their offset, to guarantee they are in C order. 726 | offsets.sort_by(|(_, offset_a), (_, offset_b)| offset_a.cmp(offset_b)); 727 | let mut last_offset = Size::from_bytes(0); 728 | let mut pad_count = 0; 729 | let mut fields = String::new(); 730 | let mut rust_fields = String::new(); 731 | for (field_idx, offset) in &offsets { 732 | if *offset != last_offset { 733 | assert!(offset.bytes() > last_offset.bytes()); 734 | fields.push_str(&format!( 735 | "uint8_t pad_{pad_count}[{}];\n", 736 | offset.bytes() - last_offset.bytes() 737 | )); 738 | pad_count += 1; 739 | } 740 | let field_def = &variant_0.fields[*field_idx]; 741 | let field_ty = field_def.ty(tcx, gargs); 742 | 743 | add_ty(sb, tcx, field_ty, instance); 744 | if is_zst( 745 | crate::monomorphize(instance, field_def.ty(tcx, gargs), tcx), 746 | tcx, 747 | ) { 748 | continue; 749 | } 750 | let field_name = if field_def 751 | .name 752 | .to_string() 753 | .chars() 754 | .next() 755 | .is_some_and(char::is_alphabetic) 756 | { 757 | escape_forbidden_symbol(&field_def.name.to_string()) 758 | } else { 759 | format!("f{}", field_def.name) 760 | }; 761 | fields.push_str(&format!( 762 | "{} {field_name};\n", 763 | c_type_string(field_def.ty(tcx, gargs), tcx, sb, instance) 764 | )); 765 | rust_fields.push_str(&format!( 766 | "{field_name}:{},\n", 767 | crate::rust::rust_type_string( 768 | field_def.ty(tcx, gargs), 769 | tcx, 770 | sb, 771 | instance, 772 | true 773 | ) 774 | )); 775 | last_offset = *offset + size(field_def.ty(tcx, gargs), tcx); 776 | } 777 | let align = if sb.is_align_attr_supported() { 778 | format!("__attribute__ ((aligned ({})))\n", layout.align.abi.bytes()) 779 | } else { 780 | String::new() 781 | }; 782 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 783 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 784 | let namespace: String = beg 785 | .iter() 786 | .map(std::string::String::as_str) 787 | .intersperse("::") 788 | .collect(); 789 | if def.adt_kind() == AdtKind::Enum { 790 | sb.source_file.push(format!( 791 | "#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace} {{{template_preifx}union {align}{end}{generics}{{\n{fields}}};}}\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 792 | )); 793 | } else { 794 | sb.source_file.push(format!( 795 | "#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace} {{{template_preifx}struct {align}{end}{generics}{{\n{fields}}};}}\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 796 | )); 797 | } 798 | let name = name.replace('$', "ds"); 799 | sb.add_rust_if_missing( 800 | &format!("#[derive(Clone,Copy)]#[repr(C)]\nstruct {name}{{\n{rust_fields}}}\n"), 801 | &name, 802 | ); 803 | } else { 804 | sb.source_file.push(format!( 805 | "#ifndef _RUST_TY_DEF_{name}\nstruct {align}{name}{{\n{fields}}};\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 806 | )); 807 | sb.assert_sizeof(&format!("struct {name}"), layout.size.bytes()); 808 | sb.assert_alignof(&format!("struct {name}"), layout.align.abi.bytes()); 809 | 810 | let name = name.replace('$', "ds"); 811 | sb.add_rust_if_missing( 812 | &format!("#[derive(Clone,Copy)]#[repr(C)]\nstruct {name}{{\n{rust_fields}}}\n"), 813 | &name 814 | ); 815 | } 816 | } 817 | FieldsShape::Union(_) => { 818 | let mut fields = String::new(); 819 | let mut rust_fields = String::new(); 820 | for field_def in def.all_fields() { 821 | add_ty(sb, tcx, field_def.ty(tcx, gargs), instance); 822 | if is_zst( 823 | crate::monomorphize(instance, field_def.ty(tcx, gargs), tcx), 824 | tcx, 825 | ) { 826 | continue; 827 | } 828 | let field_name = if field_def 829 | .name 830 | .to_string() 831 | .chars() 832 | .next() 833 | .is_some_and(|c| !c.is_whitespace()) 834 | { 835 | format!("f{}", field_def.name) 836 | } else { 837 | escape_forbidden_symbol(&field_def.name.to_string()) 838 | }; 839 | fields.push_str(&format!( 840 | "{} {field_name};\n", 841 | c_type_string(field_def.ty(tcx, gargs), tcx, sb, instance) 842 | )); 843 | rust_fields.push_str(&format!( 844 | "{field_name}:{},", 845 | rust_type_string(field_def.ty(tcx, gargs), tcx, sb, instance, true) 846 | )); 847 | } 848 | let align = if sb.is_align_attr_supported() { 849 | format!("__attribute__ ((aligned ({})))\n", layout.align.abi.bytes()) 850 | } else { 851 | String::new() 852 | }; 853 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 854 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 855 | let namespace: String = beg 856 | .iter() 857 | .map(std::string::String::as_str) 858 | .intersperse("::") 859 | .collect(); 860 | sb.source_file.push(format!( 861 | "#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace} {{{template_preifx}union {align}{end}{generics}{{\n{fields}}};}}\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 862 | )); 863 | let name = name.replace('$', "ds"); 864 | sb.add_rust_if_missing( 865 | &format!("#[derive(Clone,Copy)]#[repr(C)]\n union {name}{{{rust_fields}}}\n"), 866 | &name, 867 | ); 868 | } else { 869 | sb.source_file.push(format!( 870 | "#ifndef _RUST_TY_DEF_{name}\n{template_preifx}union {align}{name}{generics}{{\n{fields}}};\n#define _RUST_TY_DEF_{name} 1\n#endif\n" 871 | )); 872 | sb.assert_sizeof(&format!("union {name}"), layout.size.bytes()); 873 | sb.assert_alignof(&format!("union {name}"), layout.align.abi.bytes()); 874 | let name = name.replace('$', "ds"); 875 | sb.add_rust_if_missing( 876 | &format!("#[derive(Clone,Copy)]#[repr(C)]\n union {name}{{{rust_fields}}}\n"), 877 | &name, 878 | ); 879 | } 880 | } 881 | FieldsShape::Array { .. } => { 882 | todo!("Unhandled field layout: {:?}", layout.fields) 883 | } 884 | }, 885 | Variants::Multiple { 886 | tag, 887 | tag_encoding: _, 888 | tag_field: _, 889 | variants, 890 | } => { 891 | assert_eq!(variants.len(), def.variants().len()); 892 | let varaints: Vec<(String,(String,String))> = variants 893 | .into_iter() 894 | .zip(def.variants()) 895 | .filter_map(|(layout_variant, adt_variant)| { 896 | match &layout_variant.fields { 897 | FieldsShape::Primitive => panic!( 898 | "type {name} has primitive layout, but is not primitive." 899 | ), 900 | FieldsShape::Arbitrary { 901 | offsets, 902 | memory_index: _, 903 | } => { 904 | let mut offsets: Vec<(_, _)> = 905 | offsets.clone().into_iter_enumerated().collect(); 906 | // Sort fields by their offset, to guarantee they are in C order. 907 | offsets.sort_by(|(_, offset_a), (_, offset_b)| { 908 | offset_a.cmp(offset_b) 909 | }); 910 | let mut last_offset = Size::from_bytes(0); 911 | let mut pad_count = 0; 912 | let mut fields = String::new(); 913 | let mut rust_fields = String::new(); 914 | for (field_idx, offset) in &offsets { 915 | if *offset != last_offset { 916 | assert!(offset.bytes() > last_offset.bytes()); 917 | let pad_size = offset.bytes() - last_offset.bytes(); 918 | fields.push_str(&format!( 919 | "uint8_t pad_{pad_count}[{pad_size}];\n", 920 | )); 921 | rust_fields.push_str(&format!("pad{pad_count}:[u8;{pad_size}],")); 922 | pad_count += 1; 923 | } 924 | let field_def = &adt_variant.fields[*field_idx]; 925 | add_ty(sb, tcx, field_def.ty(tcx, gargs), instance); 926 | if is_zst(field_def.ty(tcx, gargs), tcx) { 927 | continue; 928 | } 929 | let field_name = if field_def 930 | .name 931 | .to_string() 932 | .chars() 933 | .next() 934 | .is_some_and(|c| !c.is_whitespace()) 935 | { 936 | format!("f{}", field_def.name) 937 | } else { 938 | escape_forbidden_symbol(&field_def.name.to_string()) 939 | }; 940 | fields.push_str(&format!( 941 | "{} {field_name};\n", 942 | c_type_string( 943 | field_def.ty(tcx, gargs), 944 | tcx, 945 | sb, 946 | instance 947 | ) 948 | )); 949 | rust_fields.push_str(&format!( 950 | "{field_name}:{},\n", 951 | rust_type_string( 952 | field_def.ty(tcx, gargs), 953 | tcx, 954 | sb, 955 | instance, 956 | true, 957 | ) 958 | )); 959 | last_offset = *offset + size(field_def.ty(tcx, gargs), tcx); 960 | } 961 | let variant_name = adt_variant.name.to_string(); 962 | if offsets.is_empty() { 963 | None 964 | } else { 965 | let full_variant_name = format!("{name}{variant_name}").replace('$',"ds"); 966 | Some((format!("struct{{\n{fields}}} {variant_name};\n"),(full_variant_name.clone(),format!("#[repr(C)]\n#[derive(Clone,Copy)]\nstruct {full_variant_name}{{{rust_fields}}}\n")))) 967 | } 968 | } 969 | _ => todo!("Unhandled variant layout: {:?}", layout.fields), 970 | } 971 | }) 972 | .collect(); 973 | let tag_type = primitive_to_type(tag.primitive()); 974 | assert_eq!( 975 | layout.fields.count(), 976 | 1, 977 | "enum contains only one variant-shared field: tag." 978 | ); 979 | let tag_offset = layout.fields.offset(0).bytes(); 980 | let align = if sb.is_align_attr_supported() { 981 | format!("__attribute__ ((aligned ({})))\n", layout.align.abi.bytes()) 982 | } else { 983 | String::new() 984 | }; 985 | let pad = if tag_offset == 0 { 986 | String::new() 987 | } else { 988 | format!("uint8_t tag_pad[{tag_offset}];") 989 | }; 990 | 991 | let varaint_string = varaints 992 | .iter() 993 | .map(|(vs, _)| vs.as_str()) 994 | .collect::(); 995 | for (_, (variant_name, variant_def)) in &varaints { 996 | sb.add_rust_if_missing(variant_def, variant_name); 997 | } 998 | let rust_enum_fields = varaints 999 | .iter() 1000 | .map(|(_, (variant_name, _))| format!("\t{variant_name}:{variant_name},\n")) 1001 | .collect::(); 1002 | sb.add_rust_if_missing(&format!("#[derive(Clone,Copy)]#[repr(C)]\nunion {name}{{tag:RustTag<{tag_offset},{primitive}>,{rust_enum_fields}}}\n",primitive = primitive_to_rust_type(tag.primitive()),name = name.replace('$',"ds")), &name); 1003 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 1004 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 1005 | let namespace: String = beg 1006 | .iter() 1007 | .map(std::string::String::as_str) 1008 | .intersperse("::") 1009 | .collect(); 1010 | sb.source_file 1011 | .push(format!("#ifndef _RUST_TY_DEF_{name}\nnamespace {namespace}{{{template_preifx}union {align}{end}{generics}{{\n{varaint_string}\nstruct{{{pad}{tag_type} tag;}} tag;\n}};\n}}\n#define _RUST_TY_DEF_{name} \n#endif\n")); 1012 | } else { 1013 | sb.source_file 1014 | .push(format!("#ifndef _RUST_TY_DEF_{name}\n{template_preifx}union {align}{name}{generics}{{\n{varaint_string}\nstruct{{{pad}{tag_type} tag;}} tag;\n}};\n#define _RUST_TY_DEF_{name} 1\n#endif\n")); 1015 | sb.assert_sizeof(&format!("union {name}"), layout.size.bytes()); 1016 | sb.assert_alignof(&format!("union {name}"), layout.align.abi.bytes()); 1017 | } 1018 | } 1019 | } 1020 | } 1021 | TyKind::Never => (), 1022 | TyKind::Slice(elem) => { 1023 | let name = mangle(t, tcx); 1024 | sb.source_file.push(format!("#ifndef _RUST_TY_DEF_{name}\nstruct {name} {{}};\n#define _RUST_TY_DEF_{name}\n#endif\n")); 1025 | add_ty(sb, tcx, *elem, instance); 1026 | } 1027 | TyKind::Str => { 1028 | sb.source_file.push("struct ss {};\n"); 1029 | } 1030 | _ => todo!("Can't add the relevant typedefs for type {:?}", t.kind()), 1031 | } 1032 | sb.defined_tys.insert(t); 1033 | } 1034 | /// Finds the name of a C-to-Rust shim 1035 | pub fn c_shim_name(fn_name: &str) -> String { 1036 | const SHIM_NAME: &str = "c_to_rust_shim"; 1037 | if !fn_name.ends_with('E') { 1038 | return format!("{fn_name}{SHIM_NAME}"); 1039 | } 1040 | if fn_name.len() < 20 { 1041 | return format!("{fn_name}{SHIM_NAME}"); 1042 | } 1043 | let hash_len = &fn_name[(fn_name.len() - 20)..(fn_name.len() - 18)]; 1044 | if hash_len != "17" { 1045 | return format!("{fn_name}{SHIM_NAME}"); 1046 | } 1047 | let (prefix, generic_hash) = fn_name.split_at(fn_name.len() - 17 - 2 - 1); 1048 | format!( 1049 | "{prefix}{shim_len}{SHIM_NAME}{generic_hash}", 1050 | shim_len = SHIM_NAME.len() 1051 | ) 1052 | } 1053 | /// Adds a generic template for a type. 1054 | pub fn add_ty_template<'tcx>( 1055 | sb: &mut CSourceBuilder<'tcx>, 1056 | tcx: TyCtxt<'tcx>, 1057 | generic_t: Ty<'tcx>, 1058 | ) { 1059 | if sb.is_ty_declared(generic_t) { 1060 | return; 1061 | } 1062 | 1063 | if let TyKind::Adt(def, gargs) = generic_t.kind() { 1064 | assert!(sb.declared_tys.insert(generic_t)); 1065 | 1066 | let adt_instance = instance_try_resolve(def.did(), tcx, gargs); 1067 | // Get the mangled path: it is absolute, and not poluted by types being rexported 1068 | let name = crate::instance_ident(adt_instance, tcx); 1069 | let ty_type = match def.adt_kind() { 1070 | rustc_middle::ty::AdtKind::Union => "union", 1071 | rustc_middle::ty::AdtKind::Enum if !def.variants().is_empty() => "union", 1072 | rustc_middle::ty::AdtKind::Struct | rustc_middle::ty::AdtKind::Enum => "struct", 1073 | }; 1074 | let mut t_idx = 0; 1075 | let mut c_idx = 0; 1076 | let garg_body: String = gargs 1077 | .iter() 1078 | .filter_map(|garg| { 1079 | if garg.as_type().is_some() { 1080 | let ts = format!("typename T{t_idx}"); 1081 | t_idx += 1; 1082 | Some(ts) 1083 | } else { 1084 | garg.as_const()?; 1085 | 1086 | let cs = format!("typename TC{c_idx}, TC{c_idx} C{c_idx}"); 1087 | c_idx += 1; 1088 | Some(cs) 1089 | } 1090 | }) 1091 | .intersperse(",".to_string()) 1092 | .collect(); 1093 | let template = if garg_body.is_empty() { 1094 | String::new() 1095 | } else { 1096 | format!("template<{garg_body}> ") 1097 | }; 1098 | 1099 | if let Some(path) = symbol_to_path(&name, SymbolCase::Pascal) { 1100 | let (beg, end) = (&path[..(path.len() - 1)], &path[path.len() - 1]); 1101 | let namespace: String = beg 1102 | .iter() 1103 | .map(std::string::String::as_str) 1104 | .intersperse("::") 1105 | .collect(); 1106 | sb.source_file.push(format!( 1107 | "\nnamespace {namespace} {{{template}{ty_type} {end};}}\n" 1108 | )); 1109 | } else { 1110 | sb.source_file 1111 | .push(format!("\n{template}{ty_type} {name};\n")); 1112 | } 1113 | } 1114 | } 1115 | /// Calculates the size of a type 1116 | pub fn size<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Size { 1117 | tcx.layout_of(PseudoCanonicalInput { 1118 | typing_env: TypingEnv::fully_monomorphized(), 1119 | value: ty, 1120 | }) 1121 | .expect("Could not compute the layout of a type.") 1122 | .size 1123 | } 1124 | /// Checks if a type is zero sized, and should be skipped. 1125 | pub fn is_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> bool { 1126 | tcx.layout_of(PseudoCanonicalInput { 1127 | typing_env: TypingEnv::fully_monomorphized(), 1128 | value: ty, 1129 | }) 1130 | .expect("Could not compute the layout of a type.") 1131 | .is_zst() 1132 | } 1133 | /// Converts a Primtive to a C type string. 1134 | fn primitive_to_type(primitive: rustc_abi::Primitive) -> &'static str { 1135 | use rustc_abi::Integer; 1136 | use rustc_abi::Primitive; 1137 | match primitive { 1138 | Primitive::Int(int, sign) => match (int, sign) { 1139 | (Integer::I8, true) => "int8_t", 1140 | (Integer::I16, true) => "int16_t", 1141 | (Integer::I32, true) => "int32_t", 1142 | (Integer::I64, true) => "int64_t", 1143 | (Integer::I128, true) => "__int128_t", 1144 | (Integer::I8, false) => "uint8_t", 1145 | (Integer::I16, false) => "uint16_t", 1146 | (Integer::I32, false) => "uint32_t", 1147 | (Integer::I64, false) => "uint64_t", 1148 | (Integer::I128, false) => "__uint128_t", 1149 | }, 1150 | Primitive::Float(rustc_abi::Float::F16) => todo!(), 1151 | Primitive::Float(rustc_abi::Float::F32) => "float", 1152 | Primitive::Float(rustc_abi::Float::F64) => "double", 1153 | Primitive::Float(rustc_abi::Float::F128) => todo!("No support for 128 bit floats yet!"), 1154 | Primitive::Pointer(_) => "void*", 1155 | } 1156 | } 1157 | /// Converts a Primtive to a Rust type string. 1158 | fn primitive_to_rust_type(primitive: rustc_abi::Primitive) -> &'static str { 1159 | use rustc_abi::Integer; 1160 | use rustc_abi::Primitive; 1161 | match primitive { 1162 | Primitive::Int(int, sign) => match (int, sign) { 1163 | (Integer::I8, true) => "i8", 1164 | (Integer::I16, true) => "i16", 1165 | (Integer::I32, true) => "i32", 1166 | (Integer::I64, true) => "i64", 1167 | (Integer::I128, true) => "i128", 1168 | (Integer::I8, false) => "u8", 1169 | (Integer::I16, false) => "u16", 1170 | (Integer::I32, false) => "u32", 1171 | (Integer::I64, false) => "u64", 1172 | (Integer::I128, false) => "u128", 1173 | }, 1174 | Primitive::Float(rustc_abi::Float::F16) => todo!(), 1175 | Primitive::Float(rustc_abi::Float::F32) => "f32", 1176 | Primitive::Float(rustc_abi::Float::F64) => "f64", 1177 | Primitive::Float(rustc_abi::Float::F128) => todo!("No support for 128 bit floats yet!"), 1178 | Primitive::Pointer(_) => "*const ()", 1179 | } 1180 | } 1181 | /// Makes the first letter of a string uppercase 1182 | fn make_first_letter_uppercase(s: &str) -> String { 1183 | let mut c = s.chars(); 1184 | match c.next() { 1185 | None => String::new(), 1186 | Some(f) => f.to_uppercase().collect::() + c.as_str(), 1187 | } 1188 | } 1189 | /// Case required by a symbol 1190 | pub enum SymbolCase { 1191 | /// `PascalCase` 1192 | Pascal, 1193 | /// `snake_case` 1194 | Snake, 1195 | /// `SCREAMING_CASE` 1196 | #[allow(dead_code)] 1197 | Screeming, 1198 | } 1199 | impl SymbolCase { 1200 | /// Changes a given string to the selected case 1201 | fn make_case(&self, s: &str) -> String { 1202 | match self { 1203 | Self::Pascal => make_first_letter_uppercase(s), 1204 | Self::Snake => s.to_lowercase(), 1205 | Self::Screeming => s.to_uppercase(), 1206 | } 1207 | } 1208 | } 1209 | /// Escapes stirngs witch are reserved in C++. 1210 | pub fn escape_forbidden_symbol(symbol: &str) -> String { 1211 | match symbol { 1212 | "float" => "_float".to_string(), 1213 | "int" => "_int".to_string(), 1214 | "char" => "_char".to_string(), 1215 | "private" => "_private".to_string(), 1216 | "new" => "_new".to_string(), 1217 | "typeid" => "_typeid".to_string(), 1218 | "and" => "_and".to_string(), 1219 | "unsigned" => "_unsigned".to_string(), 1220 | "signed" => "_signed".to_string(), 1221 | _ => symbol.to_string(), 1222 | } 1223 | } 1224 | /// Changes a mangled symbol to a path, if possible. 1225 | pub fn symbol_to_path(symbol: &str, case: SymbolCase) -> Option> { 1226 | let demangled = format!("{:#}", rustc_demangle::demangle(symbol)); 1227 | if demangled.contains(['.', '>', '{', '}']) { 1228 | None 1229 | } else { 1230 | let last = demangled.split("::").count() - 1; 1231 | Some( 1232 | demangled 1233 | .split("::") 1234 | .enumerate() 1235 | .map(|(idx, e)| { 1236 | let res = if idx == last { 1237 | case.make_case(e) 1238 | } else { 1239 | e.to_string().to_lowercase() 1240 | }; 1241 | escape_forbidden_symbol(res.as_str()) 1242 | }) 1243 | .collect(), 1244 | ) 1245 | } 1246 | } 1247 | /// Gets the string representing a generic arg. 1248 | pub fn generic_ty_string<'tcx>( 1249 | ty: Ty<'tcx>, 1250 | tcx: TyCtxt<'tcx>, 1251 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 1252 | instance: Instance<'tcx>, 1253 | ) -> String { 1254 | match ty.kind() { 1255 | TyKind::Uint(UintTy::Usize) => "usize".into(), 1256 | TyKind::Int(IntTy::Isize) => "isize".into(), 1257 | TyKind::Char => "RustChar".into(), 1258 | TyKind::RawPtr(inner, mutability) | TyKind::Ref(_, inner, mutability) => { 1259 | let mutability_str = match mutability { 1260 | Mutability::Not => "const", 1261 | Mutability::Mut => "", 1262 | }; 1263 | if crate::is_fat_ptr(ty, tcx, instance) { 1264 | match inner.kind() { 1265 | TyKind::Str => { 1266 | let mutability = match mutability { 1267 | Mutability::Not => false, 1268 | Mutability::Mut => true, 1269 | }; 1270 | format!("RustStr<{mutability}>") 1271 | } 1272 | TyKind::Slice(elem) => { 1273 | let tpe = generic_ty_string(*elem, tcx, source_builder, instance); 1274 | format!("RustSlice<{tpe}>") 1275 | } 1276 | TyKind::Dynamic(_, _, _) => "RustDyn".to_string(), 1277 | _ => format!( 1278 | "RustFatPtr<{inner}>", 1279 | inner = generic_ty_string(*inner, tcx, source_builder, instance) 1280 | ), 1281 | } 1282 | } else if is_zst(*inner, tcx) { 1283 | format!("void {mutability_str}*") 1284 | } else { 1285 | format!( 1286 | "{} {mutability_str}*", 1287 | generic_ty_string(*inner, tcx, source_builder, instance) 1288 | ) 1289 | } 1290 | } 1291 | _ => { 1292 | source_builder.delay_typedef(ty); 1293 | c_type_string(ty, tcx, source_builder, instance) 1294 | } 1295 | } 1296 | } 1297 | /// Geets a string representing the generic args of an adt/fn def 1298 | pub fn generic_string<'tcx>( 1299 | list: &'tcx List>, 1300 | tcx: TyCtxt<'tcx>, 1301 | source_builder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 1302 | finstance: Instance<'tcx>, 1303 | ) -> String { 1304 | let garg_body: String = list 1305 | .iter() 1306 | .filter_map(|garg| { 1307 | if let Some(ty) = garg.as_type() { 1308 | Some(generic_ty_string(ty, tcx, source_builder, finstance)) 1309 | } else { 1310 | let cst = garg.as_const()?; 1311 | let cst = cst.to_value(); 1312 | let ty = cst.ty; 1313 | let val_tree = cst.valtree; 1314 | let c_type = generic_ty_string(ty, tcx, source_builder, finstance); 1315 | let val = match ty.kind() { 1316 | TyKind::Bool => { 1317 | format!("{}", val_tree.try_to_scalar().unwrap().to_bool().unwrap()) 1318 | } 1319 | TyKind::Uint(UintTy::Usize) => format!( 1320 | "usize{{{val}}}", 1321 | val = val_tree 1322 | .try_to_scalar() 1323 | .unwrap() 1324 | .to_target_usize(&tcx) 1325 | .unwrap() 1326 | ), 1327 | //TyKind::Uint(_)=>format!("{:x}",val_tree.try_to_scalar().unwrap().to_u128().unwrap()).into(), 1328 | //TyKind::Int(_)=>format!("{:x}",val_tree.try_to_scalar().unwrap().to_i128().unwrap()).into(), 1329 | _ => { 1330 | use std::hash::Hash; 1331 | use std::hash::Hasher; 1332 | #[allow(deprecated)] 1333 | use std::hash::SipHasher; 1334 | #[allow(deprecated)] 1335 | let mut hasher = SipHasher::new_with_keys(0xDEAD_C0FFE, 0xBEEF_BABE); 1336 | val_tree.hash(&mut hasher); 1337 | format!("{:#}", { hasher.finish() }) 1338 | } 1339 | }; 1340 | Some(format!("{c_type}, {val}")) 1341 | } 1342 | }) 1343 | .intersperse(",".to_string()) 1344 | .collect(); 1345 | if garg_body.is_empty() { 1346 | garg_body 1347 | } else { 1348 | format!("<{garg_body}>") 1349 | } 1350 | } 1351 | struct TemplateGenerator<'tcx, 'sb> { 1352 | tcx: TyCtxt<'tcx>, 1353 | sb: &'sb mut CSourceBuilder<'tcx>, 1354 | } 1355 | impl<'tcx> rustc_middle::ty::TypeVisitor> for TemplateGenerator<'tcx, '_> { 1356 | fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { 1357 | use crate::rustc_middle::ty::TypeVisitable; 1358 | 1359 | if let TyKind::Adt(def, gargs) = t.kind() { 1360 | self.sb.source_file.push(format!("// {gargs:?}\n")); 1361 | let adt_instance = instance_try_resolve(def.did(), self.tcx, gargs); 1362 | let poly_gargs = List::>::identity_for_item( 1363 | self.tcx, 1364 | adt_instance.def_id(), 1365 | ); 1366 | let generic_t = Instance::new(adt_instance.def_id(), poly_gargs) 1367 | .ty(self.tcx, TypingEnv::fully_monomorphized()); 1368 | if self.sb.is_ty_declared(generic_t) { 1369 | return; 1370 | } 1371 | for generic in gargs.as_slice() { 1372 | if let Some(ty) = generic.as_type() { 1373 | ty.visit_with(self); 1374 | } 1375 | } 1376 | add_ty_template(self.sb, self.tcx, generic_t); 1377 | } 1378 | } 1379 | } 1380 | /// Checks if a given type is generic or not 1381 | #[allow(clippy::match_same_arms)] 1382 | fn is_generic(ty: Ty) -> bool { 1383 | match ty.kind() { 1384 | TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => is_generic(*inner), 1385 | TyKind::Tuple(elements) => elements.iter().any(|ty| is_generic(ty)), 1386 | TyKind::Param(_) => true, 1387 | TyKind::Adt(_, generics) => generics 1388 | .iter() 1389 | .filter_map(rustc_middle::ty::GenericArg::as_type) 1390 | .any(|ty| is_generic(ty)), 1391 | TyKind::Slice(inner) => is_generic(*inner), 1392 | TyKind::Int(_) 1393 | | TyKind::Uint(_) 1394 | | TyKind::Float(_) 1395 | | TyKind::Char 1396 | | TyKind::Bool 1397 | | TyKind::Never 1398 | | TyKind::Str => false, 1399 | TyKind::Placeholder(_) => true, 1400 | TyKind::Infer(_) => true, 1401 | TyKind::Array(elem, length) => is_generic(*elem) | length.try_to_value().is_none(), 1402 | TyKind::Foreign(_) => false, 1403 | TyKind::Pat(_, _) | TyKind::Bound(_, _) => true, 1404 | TyKind::Dynamic(_, _, _) => false, 1405 | TyKind::Closure(_, generics) 1406 | | TyKind::CoroutineClosure(_, generics) 1407 | | TyKind::Coroutine(_, generics) 1408 | | TyKind::CoroutineWitness(_, generics) => generics 1409 | .iter() 1410 | .filter_map(rustc_middle::ty::GenericArg::as_type) 1411 | .any(|ty| is_generic(ty)), 1412 | TyKind::FnDef(_, generics) => generics 1413 | .iter() 1414 | .filter_map(rustc_middle::ty::GenericArg::as_type) 1415 | .any(|ty| is_generic(ty)), 1416 | TyKind::Alias(_, _) => true, 1417 | TyKind::FnPtr(binder, _) => { 1418 | if let Some(inner) = binder.no_bound_vars() { 1419 | inner.inputs_and_output.iter().any(|ty| is_generic(ty)) 1420 | } else { 1421 | false 1422 | } 1423 | } 1424 | TyKind::Error(_) => true, 1425 | TyKind::UnsafeBinder(_) => todo!("Can't determine if UnsafeBinder is generic"), 1426 | // _ => todo!("Can't determine if {ty:?} is generic"), 1427 | } 1428 | } 1429 | /// DEBUG: displays the kind of a type. 1430 | #[allow(dead_code)] 1431 | fn display_ty_kind(ty: Ty) { 1432 | eprintln!( 1433 | "{}", 1434 | match ty.kind() { 1435 | TyKind::RawPtr(_, _) | TyKind::Ref(_, _, _) => "RawPtr", 1436 | TyKind::Tuple(_) => "Tuple", 1437 | TyKind::Param(_) => "Param", 1438 | TyKind::Adt(_, _) => "Adt", 1439 | TyKind::Slice(_) => "Slice", 1440 | TyKind::Int(_) 1441 | | TyKind::Uint(_) 1442 | | TyKind::Float(_) 1443 | | TyKind::Char 1444 | | TyKind::Bool 1445 | | TyKind::Never 1446 | | TyKind::Str => "primitive", 1447 | TyKind::Placeholder(_) => "Placeholder", 1448 | TyKind::Infer(_) => "Infer", 1449 | TyKind::Array(_, _) => "Array", 1450 | TyKind::Foreign(_) => "Foreign", 1451 | TyKind::Pat(_, _) | TyKind::Bound(_, _) => "Bound", 1452 | TyKind::Dynamic(_, _, _) => "Dynamic", 1453 | TyKind::Closure(_, _) 1454 | | TyKind::CoroutineClosure(_, _) 1455 | | TyKind::Coroutine(_, _) 1456 | | TyKind::CoroutineWitness(_, _) => "Closure", 1457 | TyKind::FnDef(_, _) => "FnDef", 1458 | TyKind::Alias(_, _) => "Alias", 1459 | TyKind::FnPtr(_, _) => "FnPtr", 1460 | TyKind::Error(_) => "Error", 1461 | TyKind::UnsafeBinder(_) => "UnsafeBinder", 1462 | //_=>todo!("Can't determine if {ty:?} is generic") 1463 | } 1464 | ); 1465 | } 1466 | /// Changes the parameter index into a short type name 1467 | fn paramidx_to_name(param: ParamTy, gargs: &List) -> String { 1468 | let mut c_idx = 0; 1469 | let mut t_idx = 0; 1470 | for (curr_idx, garg) in gargs.iter().enumerate() { 1471 | if garg.as_type().is_some() { 1472 | if std::convert::TryInto::::try_into(curr_idx).unwrap() == param.index { 1473 | return format!("T{t_idx}"); 1474 | } 1475 | t_idx += 1; 1476 | } else { 1477 | let Some(_) = garg.as_const() else { 1478 | continue; 1479 | }; 1480 | if std::convert::TryInto::::try_into(curr_idx).unwrap() == param.index { 1481 | return format!("TC{c_idx}"); 1482 | } 1483 | c_idx += 1; 1484 | } 1485 | } 1486 | panic!() 1487 | } 1488 | /// Checks if the function contains a specific combo of of types / generics that make creating a template for it impossible/nearly impossible. 1489 | #[allow(clippy::match_same_arms)] 1490 | fn unsuported_garg_in_sig<'tcx>(instance: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> bool { 1491 | // TODO: this just checks if it is *possible* for one of gargs to cause an issue, but this may be too conservative. 1492 | // TODO: make this recursive. 1493 | instance 1494 | .args 1495 | .iter() 1496 | .filter_map(rustc_middle::ty::GenericArg::as_type) 1497 | .any(|ty| match ty.kind() { 1498 | // Needed because `char32_t` and `uint32_t` are not distinct types in C++. 1499 | TyKind::Char => true, 1500 | // Needed because `uintptr_t` and `uint{pointer_size}_t` are not distinct types in C++. 1501 | TyKind::Uint(UintTy::Usize) | TyKind::Int(IntTy::Isize) => true, 1502 | // Zero sized, and not present in some sigs. 1503 | TyKind::FnDef(_, _) => true, 1504 | TyKind::Closure(_, _) => is_zst(ty, tcx), 1505 | _ => false, 1506 | }) 1507 | } 1508 | -------------------------------------------------------------------------------- /codegen/src/souce_builder/predefined.rs: -------------------------------------------------------------------------------- 1 | /// Headers stricitly neccessary for the transalation. 2 | pub const MANDATORY_HEADERS: &[&str] = &["stdint.h", "stdexcept", "span"]; 3 | /// Builtin, layout-compatible C++ repr of a Rust array 4 | pub const RUST_ARR: &str = "#ifndef RUST_ARR_DEFINED 5 | template class RustArr{ 6 | T arr[SIZE]; 7 | T& operator[](uintptr_t index){ 8 | if(index > SIZE) throw std::out_of_range(\"Index out of range in Rust array\"); 9 | return arr[index]; 10 | } 11 | }; 12 | #define RUST_ARR_DEFINED 1 13 | #endif\n"; 14 | /// Builtin, layout-compatible C++ repr of a Rust *[T]. 15 | pub const RUST_SLICE: &str = "#ifndef RUST_SLICE 16 | template class RustSlice{ 17 | T* ptr; 18 | uintptr_t len; 19 | T& operator[](uintptr_t idx){ 20 | if (idx > len)throw std::out_of_range(\"Index out of range in Rust slice\"); 21 | return (ptr+idx); 22 | } 23 | }; 24 | #define RUST_SLICE 1 25 | #endif\n"; 26 | /// Builtin, layout-compatible C++ repr Rust *dyn Trait 27 | pub const RUST_DYN: &str = "#ifndef RUST_DYN 28 | class RustDyn{ 29 | void* ptr; 30 | void* vtable; 31 | }; 32 | #define RUST_DYN 1 33 | #endif\n"; 34 | /// Builtin, layout-compatible C++ repr of Rust *str 35 | pub const RUST_STR: &str = "#ifndef RUST_STR 36 | template struct RustStr{ 37 | char32_t* utrf8_data; 38 | uintptr_t len; 39 | }; 40 | #define RUST_STR 1 41 | #endif 42 | #ifndef RUST_FAT_PTR 43 | template struct RustFatPtr{ 44 | T* data; 45 | void* metadata; 46 | }; 47 | #define RUST_FAT_PTR 1 48 | #endif\n"; 49 | /// Rust-fn typdef 50 | pub const RUST_FN_DEF: &str = " 51 | #ifndef RUST_FN_DEF 52 | template struct RustFnDef{}; 53 | #define RUST_FN_DEF 1 54 | #endif\n"; 55 | /// Rust isize type-wrapper 56 | pub const ISIZE: &str = 57 | "#ifndef _RUST_ISIZE\nstruct isize{intptr_t i;};\n#define _RUST_ISIZE 1\n#endif\n"; 58 | /// Rust usize type-wrapper 59 | pub const USIZE: &str = 60 | "#ifndef _RUST_USIZE\nstruct usize{uintptr_t i;};\n#define _RUST_USIZE 1\n#endif\n"; 61 | /// Rust char wrapper 62 | pub const CHAR: &str = 63 | "#ifndef _RUST_CHAR\nstruct RustChar{uint32_t i;};\n#define _RUST_CHAR 1\n#endif\n"; 64 | /// Mandatory C tpe definitions 65 | pub const C_TYPEDEFS: &[&str] = &[ 66 | RUST_ARR, 67 | RUST_SLICE, 68 | RUST_DYN, 69 | RUST_STR, 70 | RUST_FN_DEF, 71 | ISIZE, 72 | USIZE, 73 | CHAR, 74 | "struct RustFn;\n", 75 | "#define RUST_IMMUTABLE false\n", 76 | "#define RUST_MUTABLE true\n", 77 | "template struct RustTuple;\n", 78 | "#undef unix\n", 79 | "#define restrict __restrict\n", 80 | ]; 81 | -------------------------------------------------------------------------------- /codegen/src/souce_builder/typedef.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /codegen/src/statics.rs: -------------------------------------------------------------------------------- 1 | use rustc_middle::mir::mono::Linkage; 2 | use rustc_middle::mir::mono::MonoItemData; 3 | use rustc_middle::ty::Instance; 4 | use rustc_middle::ty::TyCtxt; 5 | 6 | use rustc_span::def_id::DefId; 7 | 8 | use crate::function::c_type_string; 9 | use crate::souce_builder::is_zst; 10 | /// Turn a Rust static into a C static. 11 | pub(crate) fn static_decl<'tcx>( 12 | static_def: DefId, 13 | data: MonoItemData, 14 | source_bilder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 15 | tcx: TyCtxt<'tcx>, 16 | ) -> Option { 17 | let inst = Instance::mono(tcx, static_def); 18 | // Get the linkage of this static - is it common(defined here), extern(defined somehwere else) or static (private in C) 19 | // This is done first, because in header mode, we don't want to export types used by a private sumbol, which will not be included anyway. 20 | let attrs = tcx.codegen_fn_attrs(static_def); 21 | let linkage = match attrs.linkage { 22 | Some(Linkage::Internal) => { 23 | return None; 24 | } 25 | Some(Linkage::Common | Linkage::External | Linkage::AvailableExternally) | None => "extern", 26 | Some( 27 | Linkage::LinkOnceAny 28 | | Linkage::LinkOnceODR 29 | | Linkage::WeakAny 30 | | Linkage::ExternalWeak 31 | | Linkage::WeakODR, 32 | ) => panic!("{:?} unsuported", data.linkage), 33 | }; 34 | // Get the type of this static, without bound vars(generics), and then turn into a c type. 35 | let static_ty = tcx.type_of(static_def); 36 | let static_ty = static_ty.no_bound_vars().expect("Generic static."); 37 | let tpe = c_type_string(static_ty, tcx, source_bilder, inst); 38 | // If the static is a ZST, skip it. 39 | if is_zst(static_ty, tcx) { 40 | return None; 41 | } 42 | // Get the unqiue static name 43 | let static_name = crate::static_ident(static_def, tcx); 44 | // If section supported, and specified, provide it 45 | let section = if source_bilder.supports_section() 46 | && let Some(section) = attrs.link_section 47 | { 48 | format!("__attribute__((section(\"{section:?}\")))") 49 | } else { 50 | String::new() 51 | }; 52 | // Check if this is thread local. If so, mark it as such. 53 | let thread_local = if attrs 54 | .flags 55 | .contains(rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags::THREAD_LOCAL) 56 | { 57 | "_ThreadLocal" 58 | } else { 59 | "" 60 | }; 61 | // Assemble a static string 62 | Some(format!( 63 | "{thread_local}{section}{linkage} {tpe} {static_name}" 64 | )) 65 | } 66 | /// Adds a static varaible definiton to this source file. 67 | pub(crate) fn define_static<'tcx>( 68 | static_def: DefId, 69 | data: MonoItemData, 70 | source_bilder: &mut crate::souce_builder::CSourceBuilder<'tcx>, 71 | tcx: TyCtxt<'tcx>, 72 | ) { 73 | // The linkage, type, and name of the static. 74 | let Some(decl) = static_decl(static_def, data, source_bilder, tcx) else { 75 | // If the decl is empty, then this is a private symbol in header mode or a zst, so it should not be exported. 76 | return; 77 | }; 78 | source_bilder.source_file_mut().push(format!("{decl};\n")); 79 | } 80 | -------------------------------------------------------------------------------- /codegen/src/test.rs: -------------------------------------------------------------------------------- 1 | mod utilis; 2 | mod fuzz0{ 3 | #[cfg(test)] 4 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 5 | mod debug{ 6 | 7 | #[cfg(test)] 8 | const IS_RELEASE:bool = false; 9 | 10 | #[test] 11 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/fuzz0.rs"), true, IS_RELEASE);}} 12 | mod release{ 13 | #[cfg(test)] 14 | const IS_RELEASE:bool = true; 15 | #[test] 16 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/fuzz0.rs"), true, IS_RELEASE);}}} 17 | mod btree{ 18 | #[cfg(test)] 19 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 20 | mod debug{ 21 | 22 | #[cfg(test)] 23 | const IS_RELEASE:bool = false; 24 | 25 | #[test] 26 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/btree.rs"), true, IS_RELEASE);}} 27 | mod release{ 28 | #[cfg(test)] 29 | const IS_RELEASE:bool = true; 30 | #[test] 31 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/btree.rs"), true, IS_RELEASE);}}} 32 | mod raw_waker{ 33 | #[cfg(test)] 34 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 35 | mod debug{ 36 | 37 | #[cfg(test)] 38 | const IS_RELEASE:bool = false; 39 | 40 | #[test] 41 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/raw_waker.rs"), true, IS_RELEASE);}} 42 | mod release{ 43 | #[cfg(test)] 44 | const IS_RELEASE:bool = true; 45 | #[test] 46 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/raw_waker.rs"), true, IS_RELEASE);}}} 47 | mod generics{ 48 | #[cfg(test)] 49 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 50 | mod debug{ 51 | 52 | #[cfg(test)] 53 | const IS_RELEASE:bool = false; 54 | 55 | #[test] 56 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/generics.rs"), true, IS_RELEASE);}} 57 | mod release{ 58 | #[cfg(test)] 59 | const IS_RELEASE:bool = true; 60 | #[test] 61 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/generics.rs"), true, IS_RELEASE);}}} 62 | mod pass_ind{ 63 | #[cfg(test)] 64 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 65 | mod debug{ 66 | 67 | #[cfg(test)] 68 | const IS_RELEASE:bool = false; 69 | 70 | #[test] 71 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/pass_ind.rs"), true, IS_RELEASE);}} 72 | mod release{ 73 | #[cfg(test)] 74 | const IS_RELEASE:bool = true; 75 | #[test] 76 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/pass_ind.rs"), true, IS_RELEASE);}}} 77 | mod gimili{ 78 | #[cfg(test)] 79 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 80 | mod debug{ 81 | 82 | #[cfg(test)] 83 | const IS_RELEASE:bool = false; 84 | 85 | #[test] 86 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/gimili.rs"), true, IS_RELEASE);}} 87 | mod release{ 88 | #[cfg(test)] 89 | const IS_RELEASE:bool = true; 90 | #[test] 91 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/gimili.rs"), true, IS_RELEASE);}}} 92 | mod add{ 93 | #[cfg(test)] 94 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 95 | mod debug{ 96 | 97 | #[cfg(test)] 98 | const IS_RELEASE:bool = false; 99 | 100 | #[test] 101 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/add.rs"), true, IS_RELEASE);}} 102 | mod release{ 103 | #[cfg(test)] 104 | const IS_RELEASE:bool = true; 105 | #[test] 106 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/add.rs"), true, IS_RELEASE);}}} 107 | mod statics{ 108 | #[cfg(test)] 109 | static COMPILE_LOCK:std::sync::Mutex<()> = std::sync::Mutex::new(()); 110 | mod debug{ 111 | 112 | #[cfg(test)] 113 | const IS_RELEASE:bool = false; 114 | 115 | #[test] 116 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/statics.rs"), true, IS_RELEASE);}} 117 | mod release{ 118 | #[cfg(test)] 119 | const IS_RELEASE:bool = true; 120 | #[test] 121 | fn stable(){crate::test::utilis::compile_file(std::path::Path::new("tests/statics.rs"), true, IS_RELEASE);}}} 122 | -------------------------------------------------------------------------------- /codegen/src/test/utilis.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use std::{ 3 | collections::HashMap, hash::{BuildHasherDefault, DefaultHasher, Hash}, path::{Path, PathBuf}, sync::LazyLock 4 | }; 5 | /// Builds the codegen backend, returning a relative path to it. May only be called directly from the test harness. 6 | fn build_backend() -> &'static Path { 7 | fn build_internal() -> &'static Path { 8 | let mut cmd = std::process::Command::new("cargo"); 9 | cmd.arg("build"); 10 | // Build a matching version of the codegen. If the tests are run in release, use the release version, otherwise use debug. 11 | if !cfg!(debug_assertions) { 12 | cmd.arg("release"); 13 | } 14 | // Run cargo to build the backend. If it fails, print that error message. 15 | let out = cmd 16 | .output() 17 | .expect("Could not run cargo to build the backend!"); 18 | assert!(out.status.success(), "Failed to build the backend"); 19 | // Computes the target-specifc, relative the path to the codegen backend. \Supported OSs: Linux, MacOS, Windows. 20 | #[cfg(all(target_family = "unix", not(target_os = "macos")))] 21 | let res = Path::new(if cfg!(debug_assertions) { 22 | "../target/debug/librustc_codegen_c.so" 23 | } else { 24 | "../target/release/librustc_codegen_c.so" 25 | }); 26 | #[cfg(target_os = "macos")] 27 | let res = Path::new(if cfg!(debug_assertions) { 28 | "../target/debug/librustc_codegen_c.dylib" 29 | } else { 30 | "../target/release/librustc_codegen_c.dylib" 31 | }); 32 | #[cfg(target_os = "windows")] 33 | let res = Path::new(if cfg!(debug_assertions) { 34 | "../target/debug/librustc_codegen_c.dll" 35 | } else { 36 | "../target/release/librustc_codegen_c.dll" 37 | }); 38 | #[cfg(not(any(target_os = "windows", target_family = "unix")))] 39 | compile_error!("Unsported host OS."); 40 | res 41 | } 42 | static BUILD_STATUS: LazyLock<&'static Path> = LazyLock::new(build_internal); 43 | *BUILD_STATUS 44 | } 45 | /// Returns an absolute path to the default or user-specified(via `CC` enviroment variable) c compiler. 46 | /// The OS C compiler can also serve as a linker. By replacing the linker with a C compiler, we can "link" c files by compiling them. 47 | pub fn linker() -> &'static Path { 48 | static C_COMPILER: LazyLock = 49 | LazyLock::new(|| std::env::var("CC").unwrap_or("cc".into())); 50 | C_COMPILER.as_ref() 51 | } 52 | /// Compiles any given `.rs` file, with the specified optimzation level, and additonal flags 53 | pub fn compile_file(path: &Path, is_lib: bool, is_release: bool) -> PathBuf { 54 | // Preparares the right `rustc` flags, with a specified backend and linker(C compiler). 55 | let backend = build_backend(); 56 | let linker = linker(); 57 | let mut cmd = std::process::Command::new("rustc"); 58 | cmd.arg("-Z"); 59 | cmd.arg(format!( 60 | "codegen_backend={backend}", 61 | backend = backend.display() 62 | )); 63 | cmd.arg("-C"); 64 | cmd.arg(format!("linker={}", linker.display())); 65 | cmd.arg("-Zinline-mir=no"); 66 | cmd.arg("-Cinline-threshold=999999999"); 67 | cmd.arg(path); 68 | cmd.arg("--edition=2021"); 69 | //cmd.arg("-Z"); 70 | //cmd.arg("unstable-options"); 71 | // Calculates the outputfile path. 72 | let out_path = if is_release { 73 | path.to_owned() 74 | } else { 75 | path.with_file_name(format!( 76 | "{}_debug", 77 | path.file_stem().unwrap().to_str().unwrap().to_owned() 78 | )) 79 | }; 80 | let out_path = if is_lib { 81 | out_path.with_extension(NATIVE_LIB_EXTENSION) 82 | } else { 83 | out_path.with_extension(NATIVE_EXEC_EXTENSION) 84 | }; 85 | // If this is a library, compile the output to an object file. 86 | if is_lib { 87 | cmd.arg("--crate-type=staticlib"); 88 | } 89 | cmd.arg("-o"); 90 | cmd.arg(&out_path); 91 | // Sets the right flags for compiler optimization 92 | if is_release { 93 | cmd.arg("-O"); 94 | } 95 | // Run `rustc`, and panic if it fails. 96 | let out = cmd.output().expect("Could not start the Rust compiler"); 97 | if !out.status.success() { 98 | let stderr = String::from_utf8_lossy(&out.stderr).to_string(); 99 | let stdout = String::from_utf8_lossy(&out.stdout).to_string(); 100 | panic!("Rust compiler error. Command:\n{cmd:?}\nstderr:{stderr}\nstdout:{stdout}"); 101 | } 102 | out_path 103 | } 104 | const NATIVE_LIB_EXTENSION: &str = "a"; 105 | const NATIVE_EXEC_EXTENSION: &str = "elf"; 106 | -------------------------------------------------------------------------------- /codegen/src/utilis.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use rustc_middle::ty::InstanceKind; 3 | use rustc_middle::ty::TyCtxt; 4 | 5 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; 6 | use rustc_middle::middle::exported_symbols::SymbolExportInfo; 7 | use rustc_middle::middle::exported_symbols::SymbolExportLevel; 8 | use rustc_middle::mir::mono::MonoItem; 9 | use rustc_middle::mir::mono::Visibility; 10 | 11 | use rustc_hir::LangItem; 12 | 13 | use rustc_span::def_id::DefId; 14 | /// Finds the default visibility of a definition 15 | fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { 16 | let export_level = if is_generic { 17 | // Generic functions never have export-level C. 18 | SymbolExportLevel::Rust 19 | } else { 20 | match tcx.reachable_non_generics(id.krate).get(&id) { 21 | Some(SymbolExportInfo { 22 | level: SymbolExportLevel::C, 23 | .. 24 | }) => SymbolExportLevel::C, 25 | _ => SymbolExportLevel::Rust, 26 | } 27 | }; 28 | match export_level { 29 | // C-export level items remain at `Default` to allow C code to 30 | // access and interpose them. 31 | SymbolExportLevel::C => Visibility::Default, 32 | 33 | // For all other symbols, `default_visibility` determines which visibility to use. 34 | SymbolExportLevel::Rust => tcx.sess.default_visibility().into(), 35 | } 36 | } 37 | /// Finds the visibility of static 38 | fn static_visibility(tcx: TyCtxt<'_>, can_be_internalized: &mut bool, def_id: DefId) -> Visibility { 39 | if tcx.is_reachable_non_generic(def_id) { 40 | *can_be_internalized = false; 41 | default_visibility(tcx, def_id, false) 42 | } else { 43 | Visibility::Hidden 44 | } 45 | } 46 | /// Gets the visibility of an item. 47 | pub fn mono_item_visibility<'tcx>( 48 | tcx: TyCtxt<'tcx>, 49 | mono_item: &MonoItem<'tcx>, 50 | can_be_internalized: &mut bool, 51 | export_generics: bool, 52 | ) -> Visibility { 53 | let instance = match mono_item { 54 | // This is pretty complicated; see below. 55 | MonoItem::Fn(instance) => instance, 56 | 57 | // Misc handling for generics and such, but otherwise: 58 | MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id), 59 | MonoItem::GlobalAsm(item_id) => { 60 | return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id()); 61 | } 62 | }; 63 | 64 | let def_id = match instance.def { 65 | InstanceKind::Item(def_id) 66 | | InstanceKind::DropGlue(def_id, Some(_)) 67 | | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, 68 | 69 | // We match the visibility of statics here 70 | InstanceKind::ThreadLocalShim(def_id) => { 71 | return static_visibility(tcx, can_be_internalized, def_id); 72 | } 73 | 74 | // These are all compiler glue and such, never exported, always hidden. 75 | InstanceKind::VTableShim(..) 76 | | InstanceKind::ReifyShim(..) 77 | | InstanceKind::FnPtrShim(..) 78 | | InstanceKind::Virtual(..) 79 | | InstanceKind::Intrinsic(..) 80 | | InstanceKind::ClosureOnceShim { .. } 81 | | InstanceKind::ConstructCoroutineInClosureShim { .. } 82 | | InstanceKind::DropGlue(..) 83 | | InstanceKind::AsyncDropGlueCtorShim(..) 84 | | InstanceKind::CloneShim(..) 85 | | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, 86 | }; 87 | 88 | // The `start_fn` lang item is actually a monomorphized instance of a 89 | // function in the standard library, used for the `main` function. We don't 90 | // want to export it so we tag it with `Hidden` visibility but this symbol 91 | // is only referenced from the actual `main` symbol which we unfortunately 92 | // don't know anything about during partitioning/collection. As a result we 93 | // forcibly keep this symbol out of the `internalization_candidates` set. 94 | // 95 | // FIXME: eventually we don't want to always force this symbol to have 96 | // hidden visibility, it should indeed be a candidate for 97 | // internalization, but we have to understand that it's referenced 98 | // from the `main` symbol we'll generate later. 99 | // 100 | // This may be fixable with a new `InstanceKind` perhaps? Unsure! 101 | if tcx.is_lang_item(def_id, LangItem::Start) { 102 | *can_be_internalized = false; 103 | return Visibility::Hidden; 104 | } 105 | 106 | let is_generic = instance.args.non_erasable_generics().next().is_some(); 107 | 108 | // Upstream `DefId` instances get different handling than local ones. 109 | let Some(def_id) = def_id.as_local() else { 110 | return if export_generics && is_generic { 111 | // If it is an upstream monomorphization and we export generics, we must make 112 | // it available to downstream crates. 113 | *can_be_internalized = false; 114 | default_visibility(tcx, def_id, true) 115 | } else { 116 | Visibility::Hidden 117 | }; 118 | }; 119 | 120 | if is_generic { 121 | if export_generics { 122 | if tcx.is_unreachable_local_definition(def_id) { 123 | // This instance cannot be used from another crate. 124 | Visibility::Hidden 125 | } else { 126 | // This instance might be useful in a downstream crate. 127 | *can_be_internalized = false; 128 | default_visibility(tcx, def_id.to_def_id(), true) 129 | } 130 | } else { 131 | // We are not exporting generics or the definition is not reachable 132 | // for downstream crates, we can internalize its instantiations. 133 | Visibility::Hidden 134 | } 135 | } else { 136 | // If this isn't a generic function then we mark this a `Default` if 137 | // this is a reachable item, meaning that it's a symbol other crates may 138 | // use when they link to us. 139 | if tcx.is_reachable_non_generic(def_id.to_def_id()) { 140 | *can_be_internalized = false; 141 | debug_assert!(!is_generic); 142 | return default_visibility(tcx, def_id.to_def_id(), false); 143 | } 144 | 145 | // If this isn't reachable then we're gonna tag this with `Hidden` 146 | // visibility. In some situations though we'll want to prevent this 147 | // symbol from being internalized. 148 | // 149 | // There's two categories of items here: 150 | // 151 | // * First is weak lang items. These are basically mechanisms for 152 | // libcore to forward-reference symbols defined later in crates like 153 | // the standard library or `#[panic_handler]` definitions. The 154 | // definition of these weak lang items needs to be referencable by 155 | // libcore, so we're no longer a candidate for internalization. 156 | // Removal of these functions can't be done by LLVM but rather must be 157 | // done by the linker as it's a non-local decision. 158 | // 159 | // * Second is "std internal symbols". Currently this is primarily used 160 | // for allocator symbols. Allocators are a little weird in their 161 | // implementation, but the idea is that the compiler, at the last 162 | // minute, defines an allocator with an injected object file. The 163 | // `alloc` crate references these symbols (`__rust_alloc`) and the 164 | // definition doesn't get hooked up until a linked crate artifact is 165 | // generated. 166 | // 167 | // The symbols synthesized by the compiler (`__rust_alloc`) are thin 168 | // veneers around the actual implementation, some other symbol which 169 | // implements the same ABI. These symbols (things like `__rg_alloc`, 170 | // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std 171 | // internal symbols". 172 | // 173 | // The std-internal symbols here **should not show up in a dll as an 174 | // exported interface**, so they return `false` from 175 | // `is_reachable_non_generic` above and we'll give them `Hidden` 176 | // visibility below. Like the weak lang items, though, we can't let 177 | // LLVM internalize them as this decision is left up to the linker to 178 | // omit them, so prevent them from being internalized. 179 | let attrs = tcx.codegen_fn_attrs(def_id); 180 | if attrs 181 | .flags 182 | .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) 183 | { 184 | *can_be_internalized = false; 185 | } 186 | 187 | Visibility::Hidden 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /codegen/tests/add.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::boxed_local)] 2 | #[inline(never)] 3 | pub fn add(lhs: i128, rhs: i128) -> i128 { 4 | lhs + rhs 5 | } 6 | #[inline(never)] 7 | pub fn copy(val: &mut [i32; 8]) -> [i32; 8] { 8 | *val 9 | } 10 | #[derive(Clone, Copy)] 11 | pub struct Vec3(pub f32, pub f32, pub f32); 12 | #[no_mangle] 13 | pub fn recive_vec3(val: Vec3) -> Vec3 { 14 | val 15 | } 16 | #[inline(never)] 17 | #[no_mangle] 18 | pub fn recive_box(_val: Box) {} 19 | #[inline(never)] 20 | pub fn recive_vec3_ref(val: &Vec3) { 21 | recive_vec3(*val); 22 | } 23 | /* 24 | #[inline(never)] 25 | #[no_mangle] 26 | pub fn pass_btree( 27 | val: std::collections::BTreeMap>, 28 | ) -> std::collections::BTreeMap> { 29 | val 30 | }*/ 31 | 32 | #[no_mangle] 33 | pub fn non_null_ptr( 34 | ptr: std::mem::ManuallyDrop>, 35 | ) -> std::mem::ManuallyDrop> { 36 | ptr 37 | } 38 | /* 39 | macro_rules! require_type { 40 | ($uid:ident,$ty:ty) => { 41 | mod $uid { 42 | #[used] 43 | static DROP: std::sync::LazyLock<()> = std::sync::LazyLock::new(|| { 44 | let _ = |tmp: $ty| drop(tmp); 45 | }); 46 | } 47 | }; 48 | } 49 | macro_rules! require_fn { 50 | ($uid:ident,$fn:expr) => { 51 | mod $uid { 52 | use super::*; 53 | #[used] 54 | static KEEP_FN: std::sync::LazyLock<()> = std::sync::LazyLock::new(|| { 55 | std::hint::black_box($fn); 56 | }); 57 | } 58 | }; 59 | } 60 | 61 | require_type!(BOXI32, Box); 62 | require_fn!(RBOXI32, recive_box); 63 | */ 64 | -------------------------------------------------------------------------------- /codegen/tests/btree.rs: -------------------------------------------------------------------------------- 1 | #![feature(allocator_api)] 2 | #![allow(dead_code, non_camel_case_types)] 3 | use std::ptr::NonNull; 4 | #[repr(C)] 5 | pub struct aiocb { 6 | __next_prio: *mut aiocb, 7 | } 8 | pub struct NodeRef { 9 | node: NonNull, 10 | } 11 | struct LeafNode { 12 | parent: Option>, 13 | } 14 | 15 | type Map = NodeRef; 16 | #[no_mangle] 17 | #[inline(never)] 18 | pub fn rcv_btree(val: Map) -> Map { 19 | val 20 | } 21 | #[no_mangle] 22 | #[inline(never)] 23 | pub fn rcv_aiocb(aiocb: &aiocb) -> &aiocb { 24 | aiocb 25 | } 26 | -------------------------------------------------------------------------------- /codegen/tests/generics.rs: -------------------------------------------------------------------------------- 1 | pub fn pass(val: T) -> T { 2 | val 3 | } 4 | #[no_mangle] 5 | pub fn recive_ptr(idx: u8) -> *mut () { 6 | match idx { 7 | 0 => { 8 | let tmp: fn(u8) -> u8 = pass; 9 | tmp as *mut () 10 | } 11 | 1 => { 12 | let tmp: fn(u16) -> u16 = pass; 13 | tmp as *mut () 14 | } 15 | 2 => { 16 | let tmp: fn(u32) -> u32 = pass; 17 | tmp as *mut () 18 | } 19 | _ => std::ptr::null_mut::<()>(), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /codegen/tests/gimili.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::collections::btree_map; 3 | use std::sync::Arc; 4 | #[no_mangle] 5 | pub fn abrev(ab: &mut AbbreviationsCache) { 6 | ab.abbreviations.insert(0, Err(())); 7 | } 8 | #[derive(Debug, Default)] 9 | pub struct AbbreviationsCache { 10 | abbreviations: btree_map::BTreeMap, ()>>, 11 | } 12 | #[derive(Debug, Default, Clone)] 13 | pub struct Abbreviations { 14 | vec: Vec, 15 | map: btree_map::BTreeMap, 16 | } 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | pub struct Abbreviation { 19 | code: u64, 20 | tag: DwTag, 21 | has_children: DwChildren, 22 | attributes: Attributes, 23 | } 24 | #[derive(Debug, Clone, PartialEq, Eq)] 25 | pub struct DwTag(pub u16); 26 | #[derive(Debug, Clone, PartialEq, Eq)] 27 | pub struct DwChildren(pub u8); 28 | #[derive(Debug, Clone, PartialEq, Eq)] 29 | pub(crate) enum Attributes { 30 | Inline { 31 | buf: [AttributeSpecification; MAX_ATTRIBUTES_INLINE], 32 | len: usize, 33 | }, 34 | Heap(Vec), 35 | } 36 | #[derive(Debug, Clone, PartialEq, Eq)] 37 | pub struct AttributeSpecification { 38 | //name: constants::DwAt, 39 | //form: constants::DwForm, 40 | implicit_const_value: i64, 41 | } 42 | const MAX_ATTRIBUTES_INLINE: usize = 5; 43 | -------------------------------------------------------------------------------- /codegen/tests/pass_ind.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub fn add(lhs: i128, rhs: i128) -> i128 { 3 | lhs + rhs 4 | } 5 | #[no_mangle] 6 | pub fn sum(a: f32, b: f32, c: f32, d: f32) -> f32 { 7 | a + b + c + d 8 | } 9 | #[no_mangle] 10 | pub fn deref_ptr(val: &mut u64) -> u64 { 11 | *val 12 | } 13 | 14 | pub fn str_get(_v_al: &str) -> char { 15 | 'u' 16 | } 17 | #[no_mangle] 18 | pub fn slice_get(val: &[u8], idx: usize) -> u8 { 19 | val[idx] 20 | } 21 | pub trait Odd { 22 | fn be_funny(&self); 23 | } 24 | #[no_mangle] 25 | pub fn pass_dyn(ptr: *const dyn Odd) -> *const dyn Odd { 26 | ptr 27 | } 28 | -------------------------------------------------------------------------------- /codegen/tests/raw_waker.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | fn pass_waker(_waker: &std::task::RawWaker) {} 3 | -------------------------------------------------------------------------------- /codegen/tests/statics.rs: -------------------------------------------------------------------------------- 1 | #![feature( 2 | lang_items, 3 | adt_const_params, 4 | associated_type_defaults, 5 | core_intrinsics, 6 | unsized_const_params 7 | )] 8 | #![allow( 9 | internal_features, 10 | incomplete_features, 11 | unused_variables, 12 | dead_code, 13 | unused_imports 14 | )] 15 | 16 | static mut INT32: i32 = 0; 17 | macro_rules! test_numeric_static { 18 | ($tpe:ty) => {{ 19 | static TMP: $tpe = 64 as $tpe; 20 | std::hint::black_box(&TMP); 21 | }}; 22 | } 23 | static NANY: f32 = f32::NAN; 24 | static NANY2: f64 = f64::NAN; 25 | #[no_mangle] 26 | pub fn test_main() { 27 | let zero = unsafe { INT32 }; 28 | assert_eq!(zero, 0); 29 | unsafe { INT32 += 1 }; 30 | let one = unsafe { INT32 }; 31 | assert_eq!(one, 1); 32 | unsafe { INT32 += 1 }; 33 | let two = unsafe { INT32 }; 34 | assert_eq!(two, 2); 35 | unsafe { INT32 *= two }; 36 | let four = unsafe { INT32 }; 37 | assert_eq!(four, 4); 38 | test_numeric_static!(i8); 39 | test_numeric_static!(i16); 40 | test_numeric_static!(i32); 41 | test_numeric_static!(i64); 42 | test_numeric_static!(i128); 43 | test_numeric_static!(u8); 44 | test_numeric_static!(u16); 45 | test_numeric_static!(u32); 46 | test_numeric_static!(u64); 47 | test_numeric_static!(u128); 48 | test_numeric_static!(f32); 49 | test_numeric_static!(f64); 50 | std::hint::black_box(&NANY); 51 | std::hint::black_box(&NANY2); 52 | } 53 | -------------------------------------------------------------------------------- /packer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "packer" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /packer/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{os::unix::process::CommandExt, path::PathBuf, process::Command}; 2 | 3 | struct CargoTask{ 4 | out_dir:PathBuf, 5 | crate_dir:PathBuf, 6 | } 7 | type BuildError = (); 8 | impl CargoTask{ 9 | 10 | pub fn new(out_dir: impl Into, crate_dir: impl Into) -> Self { 11 | Self { out_dir:out_dir.into(), crate_dir:crate_dir.into() } 12 | } 13 | 14 | fn crate_target(&self)->PathBuf{ 15 | let mut target = self.crate_dir.clone(); 16 | target.push("target"); 17 | target 18 | } 19 | fn header_target(&self) -> PathBuf{ 20 | let mut target = self.crate_target(); 21 | target.push("header_target"); 22 | target 23 | } 24 | pub fn build_headers(&self)->Result<(),BuildError>{ 25 | let header_target = self.header_target(); 26 | let mut cmd = Command::new("cargo"); 27 | cmd.env("CARGO_TARGET_DIR", header_target); 28 | cmd.arg("build"); 29 | let backend = std::path::absolute(std::env::current_exe().unwrap().with_file_name("librustc_codegen_c").with_extension("so")).unwrap().to_str().unwrap().to_string(); 30 | let packer = std::env::current_exe().unwrap().with_file_name("packer"); 31 | let flags = format!("-Z codegen-backend={backend} -Zcross-crate-inline-threshold=0 -Zinline-mir=no"); 32 | cmd.env("RUSTFLAGS",flags ); 33 | let out = cmd.output().unwrap(); 34 | panic!("cmd:{cmd:?} out:{out:?}"); 35 | } 36 | } 37 | fn main() { 38 | let out_dir = std::env::args().nth(1).unwrap(); 39 | let crate_dir = std::env::args().nth(2).unwrap_or_else(||std::env::current_dir().unwrap().to_string_lossy().to_string()); 40 | let task = CargoTask::new(out_dir, crate_dir); 41 | task.build_headers().unwrap(); 42 | } 43 | --------------------------------------------------------------------------------