]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/llvm_util.rs
Auto merge of #55650 - nikic:funnel-shift, r=nagisa
[rust.git] / src / librustc_codegen_llvm / llvm_util.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use syntax_pos::symbol::Symbol;
12 use back::write::create_target_machine;
13 use llvm;
14 use rustc::session::Session;
15 use rustc::session::config::PrintRequest;
16 use libc::c_int;
17 use std::ffi::CString;
18 use syntax::feature_gate::UnstableFeatures;
19
20 use std::str;
21 use std::slice;
22 use std::sync::atomic::{AtomicBool, Ordering};
23 use std::sync::Once;
24
25 static POISONED: AtomicBool = AtomicBool::new(false);
26 static INIT: Once = Once::new();
27
28 pub(crate) fn init(sess: &Session) {
29     unsafe {
30         // Before we touch LLVM, make sure that multithreading is enabled.
31         INIT.call_once(|| {
32             if llvm::LLVMStartMultithreaded() != 1 {
33                 // use an extra bool to make sure that all future usage of LLVM
34                 // cannot proceed despite the Once not running more than once.
35                 POISONED.store(true, Ordering::SeqCst);
36             }
37
38             configure_llvm(sess);
39         });
40
41         if POISONED.load(Ordering::SeqCst) {
42             bug!("couldn't enable multi-threaded LLVM");
43         }
44     }
45 }
46
47 fn require_inited() {
48     INIT.call_once(|| bug!("llvm is not initialized"));
49     if POISONED.load(Ordering::SeqCst) {
50         bug!("couldn't enable multi-threaded LLVM");
51     }
52 }
53
54 unsafe fn configure_llvm(sess: &Session) {
55     let n_args = sess.opts.cg.llvm_args.len();
56     let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
57     let mut llvm_args = Vec::with_capacity(n_args + 1);
58
59     llvm::LLVMRustInstallFatalErrorHandler();
60
61     {
62         let mut add = |arg: &str| {
63             let s = CString::new(arg).unwrap();
64             llvm_args.push(s.as_ptr());
65             llvm_c_strs.push(s);
66         };
67         add("rustc"); // fake program name
68         if sess.time_llvm_passes() { add("-time-passes"); }
69         if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
70         if sess.opts.debugging_opts.disable_instrumentation_preinliner {
71             add("-disable-preinline");
72         }
73
74         for arg in &sess.opts.cg.llvm_args {
75             add(&(*arg));
76         }
77     }
78
79     llvm::LLVMInitializePasses();
80
81     ::rustc_llvm::initialize_available_targets();
82
83     llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
84                                  llvm_args.as_ptr());
85 }
86
87 // WARNING: the features after applying `to_llvm_feature` must be known
88 // to LLVM or the feature detection code will walk past the end of the feature
89 // array, leading to crashes.
90
91 const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
92     ("aclass", Some("arm_target_feature")),
93     ("mclass", Some("arm_target_feature")),
94     ("rclass", Some("arm_target_feature")),
95     ("dsp", Some("arm_target_feature")),
96     ("neon", Some("arm_target_feature")),
97     ("v5te", Some("arm_target_feature")),
98     ("v6k", Some("arm_target_feature")),
99     ("v6t2", Some("arm_target_feature")),
100     ("v7", Some("arm_target_feature")),
101     ("vfp2", Some("arm_target_feature")),
102     ("vfp3", Some("arm_target_feature")),
103     ("vfp4", Some("arm_target_feature")),
104 ];
105
106 const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
107     ("fp", Some("aarch64_target_feature")),
108     ("neon", Some("aarch64_target_feature")),
109     ("sve", Some("aarch64_target_feature")),
110     ("crc", Some("aarch64_target_feature")),
111     ("crypto", Some("aarch64_target_feature")),
112     ("ras", Some("aarch64_target_feature")),
113     ("lse", Some("aarch64_target_feature")),
114     ("rdm", Some("aarch64_target_feature")),
115     ("fp16", Some("aarch64_target_feature")),
116     ("rcpc", Some("aarch64_target_feature")),
117     ("dotprod", Some("aarch64_target_feature")),
118     ("v8.1a", Some("aarch64_target_feature")),
119     ("v8.2a", Some("aarch64_target_feature")),
120     ("v8.3a", Some("aarch64_target_feature")),
121 ];
122
123 const X86_WHITELIST: &[(&str, Option<&str>)] = &[
124     ("aes", None),
125     ("avx", None),
126     ("avx2", None),
127     ("avx512bw", Some("avx512_target_feature")),
128     ("avx512cd", Some("avx512_target_feature")),
129     ("avx512dq", Some("avx512_target_feature")),
130     ("avx512er", Some("avx512_target_feature")),
131     ("avx512f", Some("avx512_target_feature")),
132     ("avx512ifma", Some("avx512_target_feature")),
133     ("avx512pf", Some("avx512_target_feature")),
134     ("avx512vbmi", Some("avx512_target_feature")),
135     ("avx512vl", Some("avx512_target_feature")),
136     ("avx512vpopcntdq", Some("avx512_target_feature")),
137     ("bmi1", None),
138     ("bmi2", None),
139     ("fma", None),
140     ("fxsr", None),
141     ("lzcnt", None),
142     ("mmx", Some("mmx_target_feature")),
143     ("pclmulqdq", None),
144     ("popcnt", None),
145     ("rdrand", None),
146     ("rdseed", None),
147     ("sha", None),
148     ("sse", None),
149     ("sse2", None),
150     ("sse3", None),
151     ("sse4.1", None),
152     ("sse4.2", None),
153     ("sse4a", Some("sse4a_target_feature")),
154     ("ssse3", None),
155     ("tbm", Some("tbm_target_feature")),
156     ("xsave", None),
157     ("xsavec", None),
158     ("xsaveopt", None),
159     ("xsaves", None),
160 ];
161
162 const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
163     ("hvx", Some("hexagon_target_feature")),
164     ("hvx-double", Some("hexagon_target_feature")),
165 ];
166
167 const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
168     ("altivec", Some("powerpc_target_feature")),
169     ("power8-altivec", Some("powerpc_target_feature")),
170     ("power9-altivec", Some("powerpc_target_feature")),
171     ("power8-vector", Some("powerpc_target_feature")),
172     ("power9-vector", Some("powerpc_target_feature")),
173     ("vsx", Some("powerpc_target_feature")),
174 ];
175
176 const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
177     ("fp64", Some("mips_target_feature")),
178     ("msa", Some("mips_target_feature")),
179 ];
180
181 const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
182     ("simd128", Some("wasm_target_feature")),
183     ("atomics", Some("wasm_target_feature")),
184 ];
185
186 /// When rustdoc is running, provide a list of all known features so that all their respective
187 /// primtives may be documented.
188 ///
189 /// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
190 /// iterator!
191 pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
192     ARM_WHITELIST.iter().cloned()
193         .chain(AARCH64_WHITELIST.iter().cloned())
194         .chain(X86_WHITELIST.iter().cloned())
195         .chain(HEXAGON_WHITELIST.iter().cloned())
196         .chain(POWERPC_WHITELIST.iter().cloned())
197         .chain(MIPS_WHITELIST.iter().cloned())
198         .chain(WASM_WHITELIST.iter().cloned())
199 }
200
201 pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
202     let arch = if sess.target.target.arch == "x86_64" {
203         "x86"
204     } else {
205         &*sess.target.target.arch
206     };
207     match (arch, s) {
208         ("x86", "pclmulqdq") => "pclmul",
209         ("x86", "rdrand") => "rdrnd",
210         ("x86", "bmi1") => "bmi",
211         ("aarch64", "fp") => "fp-armv8",
212         ("aarch64", "fp16") => "fullfp16",
213         (_, s) => s,
214     }
215 }
216
217 pub fn target_features(sess: &Session) -> Vec<Symbol> {
218     let target_machine = create_target_machine(sess, true);
219     target_feature_whitelist(sess)
220         .iter()
221         .filter_map(|&(feature, gate)| {
222             if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
223                 Some(feature)
224             } else {
225                 None
226             }
227         })
228         .filter(|feature| {
229             let llvm_feature = to_llvm_feature(sess, feature);
230             let cstr = CString::new(llvm_feature).unwrap();
231             unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
232         })
233         .map(|feature| Symbol::intern(feature)).collect()
234 }
235
236 pub fn target_feature_whitelist(sess: &Session)
237     -> &'static [(&'static str, Option<&'static str>)]
238 {
239     match &*sess.target.target.arch {
240         "arm" => ARM_WHITELIST,
241         "aarch64" => AARCH64_WHITELIST,
242         "x86" | "x86_64" => X86_WHITELIST,
243         "hexagon" => HEXAGON_WHITELIST,
244         "mips" | "mips64" => MIPS_WHITELIST,
245         "powerpc" | "powerpc64" => POWERPC_WHITELIST,
246         // wasm32 on emscripten does not support these target features
247         "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
248         _ => &[],
249     }
250 }
251
252 pub fn print_version() {
253     // Can be called without initializing LLVM
254     unsafe {
255         println!("LLVM version: {}.{}",
256                  llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
257     }
258 }
259
260 pub fn get_major_version() -> u32 {
261     unsafe { llvm::LLVMRustVersionMajor() }
262 }
263
264 pub fn print_passes() {
265     // Can be called without initializing LLVM
266     unsafe { llvm::LLVMRustPrintPasses(); }
267 }
268
269 pub(crate) fn print(req: PrintRequest, sess: &Session) {
270     require_inited();
271     let tm = create_target_machine(sess, true);
272     unsafe {
273         match req {
274             PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
275             PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
276             _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
277         }
278     }
279 }
280
281 pub fn target_cpu(sess: &Session) -> &str {
282     let name = match sess.opts.cg.target_cpu {
283         Some(ref s) => &**s,
284         None => &*sess.target.target.options.cpu
285     };
286     if name != "native" {
287         return name
288     }
289
290     unsafe {
291         let mut len = 0;
292         let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
293         str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
294     }
295 }