]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/llvm_util.rs
Auto merge of #54720 - davidtwco:issue-51191, r=nikomatsakis
[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 mut llvm_c_strs = Vec::new();
56     let mut llvm_args = Vec::new();
57
58     {
59         let mut add = |arg: &str| {
60             let s = CString::new(arg).unwrap();
61             llvm_args.push(s.as_ptr());
62             llvm_c_strs.push(s);
63         };
64         add("rustc"); // fake program name
65         if sess.time_llvm_passes() { add("-time-passes"); }
66         if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
67         if sess.opts.debugging_opts.disable_instrumentation_preinliner {
68             add("-disable-preinline");
69         }
70
71         for arg in &sess.opts.cg.llvm_args {
72             add(&(*arg));
73         }
74     }
75
76     llvm::LLVMInitializePasses();
77
78     ::rustc_llvm::initialize_available_targets();
79
80     llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
81                                  llvm_args.as_ptr());
82 }
83
84 // WARNING: the features after applying `to_llvm_feature` must be known
85 // to LLVM or the feature detection code will walk past the end of the feature
86 // array, leading to crashes.
87
88 const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
89     ("aclass", Some("arm_target_feature")),
90     ("mclass", Some("arm_target_feature")),
91     ("rclass", Some("arm_target_feature")),
92     ("dsp", Some("arm_target_feature")),
93     ("neon", Some("arm_target_feature")),
94     ("v5te", Some("arm_target_feature")),
95     ("v6k", Some("arm_target_feature")),
96     ("v6t2", Some("arm_target_feature")),
97     ("v7", Some("arm_target_feature")),
98     ("vfp2", Some("arm_target_feature")),
99     ("vfp3", Some("arm_target_feature")),
100     ("vfp4", Some("arm_target_feature")),
101 ];
102
103 const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
104     ("fp", Some("aarch64_target_feature")),
105     ("neon", Some("aarch64_target_feature")),
106     ("sve", Some("aarch64_target_feature")),
107     ("crc", Some("aarch64_target_feature")),
108     ("crypto", Some("aarch64_target_feature")),
109     ("ras", Some("aarch64_target_feature")),
110     ("lse", Some("aarch64_target_feature")),
111     ("rdm", Some("aarch64_target_feature")),
112     ("fp16", Some("aarch64_target_feature")),
113     ("rcpc", Some("aarch64_target_feature")),
114     ("dotprod", Some("aarch64_target_feature")),
115     ("v8.1a", Some("aarch64_target_feature")),
116     ("v8.2a", Some("aarch64_target_feature")),
117     ("v8.3a", Some("aarch64_target_feature")),
118 ];
119
120 const X86_WHITELIST: &[(&str, Option<&str>)] = &[
121     ("aes", None),
122     ("avx", None),
123     ("avx2", None),
124     ("avx512bw", Some("avx512_target_feature")),
125     ("avx512cd", Some("avx512_target_feature")),
126     ("avx512dq", Some("avx512_target_feature")),
127     ("avx512er", Some("avx512_target_feature")),
128     ("avx512f", Some("avx512_target_feature")),
129     ("avx512ifma", Some("avx512_target_feature")),
130     ("avx512pf", Some("avx512_target_feature")),
131     ("avx512vbmi", Some("avx512_target_feature")),
132     ("avx512vl", Some("avx512_target_feature")),
133     ("avx512vpopcntdq", Some("avx512_target_feature")),
134     ("bmi1", None),
135     ("bmi2", None),
136     ("fma", None),
137     ("fxsr", None),
138     ("lzcnt", None),
139     ("mmx", Some("mmx_target_feature")),
140     ("pclmulqdq", None),
141     ("popcnt", None),
142     ("rdrand", None),
143     ("rdseed", None),
144     ("sha", None),
145     ("sse", None),
146     ("sse2", None),
147     ("sse3", None),
148     ("sse4.1", None),
149     ("sse4.2", None),
150     ("sse4a", Some("sse4a_target_feature")),
151     ("ssse3", None),
152     ("tbm", Some("tbm_target_feature")),
153     ("xsave", None),
154     ("xsavec", None),
155     ("xsaveopt", None),
156     ("xsaves", None),
157 ];
158
159 const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
160     ("hvx", Some("hexagon_target_feature")),
161     ("hvx-double", Some("hexagon_target_feature")),
162 ];
163
164 const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
165     ("altivec", Some("powerpc_target_feature")),
166     ("power8-altivec", Some("powerpc_target_feature")),
167     ("power9-altivec", Some("powerpc_target_feature")),
168     ("power8-vector", Some("powerpc_target_feature")),
169     ("power9-vector", Some("powerpc_target_feature")),
170     ("vsx", Some("powerpc_target_feature")),
171 ];
172
173 const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
174     ("fp64", Some("mips_target_feature")),
175     ("msa", Some("mips_target_feature")),
176 ];
177
178 const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
179     ("simd128", Some("wasm_target_feature")),
180     ("atomics", Some("wasm_target_feature")),
181 ];
182
183 /// When rustdoc is running, provide a list of all known features so that all their respective
184 /// primtives may be documented.
185 ///
186 /// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
187 /// iterator!
188 pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
189     ARM_WHITELIST.iter().cloned()
190         .chain(AARCH64_WHITELIST.iter().cloned())
191         .chain(X86_WHITELIST.iter().cloned())
192         .chain(HEXAGON_WHITELIST.iter().cloned())
193         .chain(POWERPC_WHITELIST.iter().cloned())
194         .chain(MIPS_WHITELIST.iter().cloned())
195         .chain(WASM_WHITELIST.iter().cloned())
196 }
197
198 pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
199     let arch = if sess.target.target.arch == "x86_64" {
200         "x86"
201     } else {
202         &*sess.target.target.arch
203     };
204     match (arch, s) {
205         ("x86", "pclmulqdq") => "pclmul",
206         ("x86", "rdrand") => "rdrnd",
207         ("x86", "bmi1") => "bmi",
208         ("aarch64", "fp") => "fp-armv8",
209         ("aarch64", "fp16") => "fullfp16",
210         (_, s) => s,
211     }
212 }
213
214 pub fn target_features(sess: &Session) -> Vec<Symbol> {
215     let target_machine = create_target_machine(sess, true);
216     target_feature_whitelist(sess)
217         .iter()
218         .filter_map(|&(feature, gate)| {
219             if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
220                 Some(feature)
221             } else {
222                 None
223             }
224         })
225         .filter(|feature| {
226             let llvm_feature = to_llvm_feature(sess, feature);
227             let cstr = CString::new(llvm_feature).unwrap();
228             unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
229         })
230         .map(|feature| Symbol::intern(feature)).collect()
231 }
232
233 pub fn target_feature_whitelist(sess: &Session)
234     -> &'static [(&'static str, Option<&'static str>)]
235 {
236     match &*sess.target.target.arch {
237         "arm" => ARM_WHITELIST,
238         "aarch64" => AARCH64_WHITELIST,
239         "x86" | "x86_64" => X86_WHITELIST,
240         "hexagon" => HEXAGON_WHITELIST,
241         "mips" | "mips64" => MIPS_WHITELIST,
242         "powerpc" | "powerpc64" => POWERPC_WHITELIST,
243         "wasm32" => WASM_WHITELIST,
244         _ => &[],
245     }
246 }
247
248 pub fn print_version() {
249     // Can be called without initializing LLVM
250     unsafe {
251         println!("LLVM version: {}.{}",
252                  llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
253     }
254 }
255
256 pub fn print_passes() {
257     // Can be called without initializing LLVM
258     unsafe { llvm::LLVMRustPrintPasses(); }
259 }
260
261 pub(crate) fn print(req: PrintRequest, sess: &Session) {
262     require_inited();
263     let tm = create_target_machine(sess, true);
264     unsafe {
265         match req {
266             PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
267             PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
268             _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
269         }
270     }
271 }
272
273 pub fn target_cpu(sess: &Session) -> &str {
274     let name = match sess.opts.cg.target_cpu {
275         Some(ref s) => &**s,
276         None => &*sess.target.target.options.cpu
277     };
278     if name != "native" {
279         return name
280     }
281
282     unsafe {
283         let mut len = 0;
284         let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
285         str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
286     }
287 }