Skip to content

Commit 9b4aca6

Browse files
committed
[WIP] Generate vendor intrinsic codegen through precompilation using cg_llvm
1 parent ab10da2 commit 9b4aca6

File tree

5 files changed

+309
-0
lines changed

5 files changed

+309
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/dist
55
/target
66
/build_system/target
7+
/gen_intrinsics/target
78

89
# Downloaded by certain scripts
910
/rust

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"rust-analyzer.linkedProjects": [
1212
"./Cargo.toml",
1313
"./build_system/Cargo.toml",
14+
"./gen_intrinsics/Cargo.toml",
1415
{
1516
"crates": [
1617
{

gen_intrinsics/Cargo.lock

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen_intrinsics/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "gen_intrinsics"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[workspace]
7+
8+
[dependencies]
9+
prettyplease = "0.2.20"
10+
proc-macro2 = "1.0.84"
11+
quote = "1.0.36"
12+
syn = { version = "2.0.66", features = ["full", "visit", "extra-traits"] }
13+
14+
[features]
15+
unstable-features = [] # for rust-analyzer

gen_intrinsics/src/main.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
use std::io::Write;
2+
use std::process::Stdio;
3+
4+
use syn::parse::Parser;
5+
use syn::visit::Visit;
6+
use syn::Ident;
7+
8+
fn main() {
9+
println!("Running rustc -Zunpretty=expanded --edition=2021 core_arch/src/lib.rs ...");
10+
let expanded_file = std::process::Command::new("rustc")
11+
.arg("-Zunpretty=expanded")
12+
.arg("--edition=2021")
13+
//.arg("--target=x86_64-unknown-linux-gnu")
14+
.arg("../build/stdlib/library/stdarch/crates/core_arch/src/lib.rs")
15+
.output()
16+
.unwrap()
17+
.stdout;
18+
19+
println!("Parsing expanded source");
20+
let file = syn::parse_str::<syn::File>(std::str::from_utf8(&expanded_file).unwrap()).unwrap();
21+
22+
println!("Visting all LLVM intrinsics");
23+
let mut visitor = Visitor { llvm_intrinsics: vec![], structs: vec![], aliases: vec![] };
24+
visitor.visit_file(&file);
25+
26+
println!();
27+
28+
let mut ts = proc_macro2::TokenStream::new();
29+
ts.extend(quote::quote! {
30+
#![feature(abi_unadjusted, link_llvm_intrinsics, repr_simd, simd_ffi)]
31+
#![allow(dead_code, improper_ctypes, improper_ctypes_definitions, internal_features, non_camel_case_types)]
32+
});
33+
34+
let structs = visitor.structs;
35+
ts.extend(quote::quote! {
36+
#(#structs)*
37+
});
38+
39+
let aliases = visitor.aliases;
40+
ts.extend(quote::quote! {
41+
#(#aliases)*
42+
});
43+
44+
visitor.llvm_intrinsics.sort_by_key(|func| func.link_name.clone());
45+
visitor.llvm_intrinsics.dedup_by_key(|func| func.link_name.clone());
46+
for LlvmIntrinsicDef { abi, link_name, mut sig } in visitor.llvm_intrinsics {
47+
let mangled_name = Ident::new(&link_name.replace('.', "__"), sig.ident.span());
48+
sig.ident = mangled_name.clone();
49+
50+
ts.extend(quote::quote! {
51+
extern #abi {
52+
#[link_name = #link_name]
53+
#sig;
54+
}
55+
});
56+
57+
sig.ident = Ident::new(&format!("__rust_cranelift_{mangled_name}"), sig.ident.span());
58+
let args = sig
59+
.inputs
60+
.iter()
61+
.map(|arg| match arg {
62+
syn::FnArg::Typed(syn::PatType { pat, .. }) => match &**pat {
63+
syn::Pat::Ident(ident) => ident.ident.clone(),
64+
syn::Pat::Wild(_) => unreachable!("{sig:?}"),
65+
_ => unreachable!("{pat:?}"),
66+
},
67+
_ => unreachable!(),
68+
})
69+
.collect::<Vec<_>>();
70+
71+
ts.extend(quote::quote! {
72+
#[no_mangle]
73+
#[target_feature(enable = "neon,aes,sha2,sha3,sm4,crc,frintts,tme,i8mm,fcma,dotprod,rdm")] // FIXME infer from context
74+
unsafe extern "C" #sig {
75+
#mangled_name(#(#args,)*)
76+
}
77+
});
78+
}
79+
80+
let generated_code = prettyplease::unparse(&syn::parse2::<syn::File>(ts).unwrap());
81+
82+
println!("{}", generated_code);
83+
84+
std::fs::write("target/foo.rs", &generated_code).unwrap();
85+
86+
println!("Compiling blob");
87+
let mut child = std::process::Command::new("rustc")
88+
.arg("-Copt-level=3")
89+
.arg("--crate-type")
90+
.arg("cdylib")
91+
.arg("--emit=obj")
92+
//.arg("--target=x86_64-unknown-linux-gnu")
93+
.arg("--out-dir")
94+
.arg("target")
95+
.arg("-")
96+
.stdin(Stdio::piped())
97+
.spawn()
98+
.unwrap();
99+
100+
child.stdin.as_ref().unwrap().write_all(generated_code.as_bytes()).unwrap();
101+
child.stdin.as_ref().unwrap().flush().unwrap();
102+
let status = child.wait().unwrap();
103+
assert!(status.success(), "{status}");
104+
}
105+
106+
struct Visitor {
107+
llvm_intrinsics: Vec<LlvmIntrinsicDef>,
108+
structs: Vec<syn::ItemStruct>,
109+
aliases: Vec<syn::ItemType>,
110+
}
111+
112+
struct LlvmIntrinsicDef {
113+
abi: String,
114+
link_name: String,
115+
sig: syn::Signature,
116+
}
117+
118+
impl<'ast> Visit<'ast> for Visitor {
119+
fn visit_item_struct(&mut self, i: &'ast syn::ItemStruct) {
120+
let Some(repr_attr) = i.attrs.iter().find(|attr| attr.path().is_ident("repr")) else {
121+
return;
122+
};
123+
124+
if !repr_attr
125+
.parse_args::<syn::Ident>()
126+
.map_or(false, |repr| repr.to_owned() == "simd" || repr.to_owned() == "C")
127+
{
128+
return;
129+
}
130+
131+
let mut ty = i.clone();
132+
ty.attrs = ty.attrs.into_iter().filter(|attr| attr.path().is_ident("repr")).collect();
133+
134+
self.structs.push(ty);
135+
}
136+
137+
fn visit_item_type(&mut self, i: &'ast syn::ItemType) {
138+
let mut alias = i.clone();
139+
alias.attrs = alias.attrs.into_iter().filter(|attr| attr.path().is_ident("repr")).collect();
140+
141+
self.aliases.push(alias);
142+
}
143+
144+
fn visit_item_foreign_mod(&mut self, i: &'ast syn::ItemForeignMod) {
145+
let abi = i.abi.name.as_ref().unwrap().value();
146+
147+
'items: for item in &i.items {
148+
match &item {
149+
syn::ForeignItem::Fn(i) => {
150+
let link_name_attr =
151+
i.attrs.iter().find(|attr| attr.path().is_ident("link_name")).unwrap();
152+
153+
let link_name =
154+
match link_name_attr.meta.require_name_value().unwrap().value.clone() {
155+
syn::Expr::Lit(syn::ExprLit {
156+
lit: syn::Lit::Str(link_name), ..
157+
}) => link_name.value(),
158+
_ => unreachable!(),
159+
};
160+
161+
assert!(
162+
i.attrs
163+
.iter()
164+
.filter(|attr| !attr.path().is_ident("link_name"))
165+
.collect::<Vec<_>>()
166+
.is_empty()
167+
);
168+
169+
let mut sig = i.sig.clone();
170+
171+
if link_name == "llvm.x86.avx512.mask.cvtss2sd.round" {
172+
match sig.inputs.iter_mut().nth(1).unwrap() {
173+
syn::FnArg::Typed(syn::PatType { ref mut pat, .. }) => match &mut **pat
174+
{
175+
syn::Pat::Ident(name) => {
176+
name.ident = Ident::new("b", name.ident.span());
177+
}
178+
_ => unreachable!(),
179+
},
180+
_ => unreachable!(),
181+
}
182+
}
183+
184+
// FIXME remove this patching
185+
match sig.inputs.iter_mut().nth(0) {
186+
Some(syn::FnArg::Typed(syn::PatType { ref mut pat, .. })) => {
187+
match &mut **pat {
188+
syn::Pat::Wild(_) => {
189+
**pat =
190+
syn::Pat::parse_single.parse2(quote::quote! { a }).unwrap();
191+
}
192+
_ => {}
193+
}
194+
}
195+
_ => {}
196+
}
197+
198+
// FIXME remove this skipping
199+
match &*link_name {
200+
_ if link_name.starts_with("llvm.aarch64.neon.ld") => continue 'items,
201+
_ if link_name.starts_with("llvm.aarch64.neon.st") => continue 'items,
202+
_ if link_name.starts_with("llvm.aarch64.neon.rshrn") => continue 'items,
203+
_ if link_name.starts_with("llvm.aarch64.neon.sq") => continue 'items,
204+
_ if link_name.starts_with("llvm.aarch64.neon.uq") => continue 'items,
205+
_ if link_name.starts_with("llvm.aarch64.neon.vcvt") => continue 'items,
206+
_ if link_name.starts_with("llvm.aarch64.neon.vsli") => continue 'items,
207+
_ if link_name.starts_with("llvm.aarch64.neon.vsri") => continue 'items,
208+
209+
"llvm.prefetch"
210+
| "llvm.aarch64.dmb"
211+
| "llvm.aarch64.dsb"
212+
| "llvm.aarch64.hint"
213+
| "llvm.aarch64.isb"
214+
| "llvm.aarch64.crypto.xar" => continue 'items,
215+
216+
"llvm.aarch64.crypto.sm3tt1a"
217+
| "llvm.aarch64.crypto.sm3tt1b"
218+
| "llvm.aarch64.crypto.sm3tt2a"
219+
| "llvm.aarch64.crypto.sm3tt2b"
220+
| "llvm.aarch64.tcancel" => continue 'items,
221+
_ => {}
222+
}
223+
224+
self.llvm_intrinsics.push(LlvmIntrinsicDef {
225+
abi: abi.clone(),
226+
link_name,
227+
sig,
228+
});
229+
}
230+
_ => {}
231+
}
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)