]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
1 | //! Set and unset common attributes on LLVM values. |
2 | ||
b7449926 | 3 | use std::ffi::CString; |
476ff2be | 4 | |
6a06907d | 5 | use cstr::cstr; |
dfeec247 | 6 | use rustc_codegen_ssa::traits::*; |
dfeec247 | 7 | use rustc_data_structures::small_c_str::SmallCStr; |
29967ef6 | 8 | use rustc_hir::def_id::DefId; |
ba9703b0 XL |
9 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; |
10 | use rustc_middle::ty::layout::HasTyCtxt; | |
ba9703b0 | 11 | use rustc_middle::ty::{self, TyCtxt}; |
cdc7bbd5 | 12 | use rustc_session::config::OptLevel; |
ba9703b0 | 13 | use rustc_session::Session; |
cdc7bbd5 | 14 | use rustc_target::spec::abi::Abi; |
3c0e092e | 15 | use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; |
041b39d2 | 16 | |
9fa01778 | 17 | use crate::attributes; |
9fa01778 | 18 | use crate::llvm::AttributePlace::Function; |
dfeec247 | 19 | use crate::llvm::{self, Attribute}; |
9fa01778 | 20 | use crate::llvm_util; |
29967ef6 | 21 | pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; |
b7449926 | 22 | |
9fa01778 XL |
23 | use crate::context::CodegenCx; |
24 | use crate::value::Value; | |
54a0048b SL |
25 | |
26 | /// Mark LLVM function to use provided inline heuristic. | |
27 | #[inline] | |
a2a8927a | 28 | fn inline<'ll>(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) { |
54a0048b SL |
29 | use self::InlineAttr::*; |
30 | match inline { | |
dfeec247 | 31 | Hint => Attribute::InlineHint.apply_llfn(Function, val), |
5bcae85e | 32 | Always => Attribute::AlwaysInline.apply_llfn(Function, val), |
dfeec247 | 33 | Never => { |
29967ef6 | 34 | if cx.tcx().sess.target.arch != "amdgpu" { |
b7449926 XL |
35 | Attribute::NoInline.apply_llfn(Function, val); |
36 | } | |
dfeec247 | 37 | } |
fc512014 | 38 | None => {} |
54a0048b SL |
39 | }; |
40 | } | |
41 | ||
74b04a01 XL |
42 | /// Apply LLVM sanitize attributes. |
43 | #[inline] | |
a2a8927a | 44 | pub fn sanitize<'ll>(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) { |
f035d41b XL |
45 | let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize; |
46 | if enabled.contains(SanitizerSet::ADDRESS) { | |
47 | llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); | |
48 | } | |
49 | if enabled.contains(SanitizerSet::MEMORY) { | |
50 | llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); | |
51 | } | |
52 | if enabled.contains(SanitizerSet::THREAD) { | |
53 | llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); | |
74b04a01 | 54 | } |
6a06907d XL |
55 | if enabled.contains(SanitizerSet::HWADDRESS) { |
56 | llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn); | |
57 | } | |
5099ac24 FG |
58 | if enabled.contains(SanitizerSet::MEMTAG) { |
59 | // Check to make sure the mte target feature is actually enabled. | |
60 | let sess = cx.tcx.sess; | |
61 | let features = llvm_util::llvm_global_features(sess).join(","); | |
62 | let mte_feature_enabled = features.rfind("+mte"); | |
63 | let mte_feature_disabled = features.rfind("-mte"); | |
64 | ||
65 | if mte_feature_enabled.is_none() || (mte_feature_disabled > mte_feature_enabled) { | |
66 | sess.err("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`"); | |
67 | } | |
68 | ||
69 | llvm::Attribute::SanitizeMemTag.apply_llfn(Function, llfn); | |
70 | } | |
74b04a01 XL |
71 | } |
72 | ||
54a0048b SL |
73 | /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. |
74 | #[inline] | |
5099ac24 FG |
75 | pub fn emit_uwtable(val: &Value) { |
76 | // NOTE: We should determine if we even need async unwind tables, as they | |
77 | // take have more overhead and if we can use sync unwind tables we | |
78 | // probably should. | |
79 | llvm::EmitUWTableAttr(val, true); | |
54a0048b SL |
80 | } |
81 | ||
0731742a | 82 | /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue. |
54a0048b | 83 | #[inline] |
a2a8927a | 84 | fn naked(val: &Value, is_naked: bool) { |
5bcae85e | 85 | Attribute::Naked.toggle_llfn(Function, val, is_naked); |
54a0048b SL |
86 | } |
87 | ||
a2a8927a | 88 | pub fn set_frame_pointer_type<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
136023e0 XL |
89 | let mut fp = cx.sess().target.frame_pointer; |
90 | // "mcount" function relies on stack pointer. | |
91 | // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>. | |
92 | if cx.sess().instrument_mcount() || matches!(cx.sess().opts.cg.force_frame_pointers, Some(true)) | |
93 | { | |
94 | fp = FramePointer::Always; | |
54a0048b | 95 | } |
136023e0 XL |
96 | let attr_value = match fp { |
97 | FramePointer::Always => cstr!("all"), | |
98 | FramePointer::NonLeaf => cstr!("non-leaf"), | |
99 | FramePointer::MayOmit => return, | |
100 | }; | |
101 | llvm::AddFunctionAttrStringValue( | |
102 | llfn, | |
103 | llvm::AttributePlace::Function, | |
104 | cstr!("frame-pointer"), | |
105 | attr_value, | |
106 | ); | |
3157f602 XL |
107 | } |
108 | ||
0731742a XL |
109 | /// Tell LLVM what instrument function to insert. |
110 | #[inline] | |
a2a8927a | 111 | fn set_instrument_function<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
0731742a XL |
112 | if cx.sess().instrument_mcount() { |
113 | // Similar to `clang -pg` behavior. Handled by the | |
114 | // `post-inline-ee-instrument` LLVM pass. | |
532ac7d7 XL |
115 | |
116 | // The function name varies on platforms. | |
117 | // See test/CodeGen/mcount.c in clang. | |
29967ef6 | 118 | let mcount_name = CString::new(cx.sess().target.mcount.as_str().as_bytes()).unwrap(); |
532ac7d7 | 119 | |
0731742a | 120 | llvm::AddFunctionAttrStringValue( |
dfeec247 XL |
121 | llfn, |
122 | llvm::AttributePlace::Function, | |
6a06907d | 123 | cstr!("instrument-function-entry-inlined"), |
dfeec247 XL |
124 | &mcount_name, |
125 | ); | |
0731742a XL |
126 | } |
127 | } | |
128 | ||
a2a8927a | 129 | fn set_probestack<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
041b39d2 | 130 | // Currently stack probes seem somewhat incompatible with the address |
e74abb32 XL |
131 | // sanitizer and thread sanitizer. With asan we're already protected from |
132 | // stack overflow anyway so we don't really need stack probes regardless. | |
f035d41b XL |
133 | if cx |
134 | .sess() | |
135 | .opts | |
136 | .debugging_opts | |
137 | .sanitizer | |
138 | .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) | |
139 | { | |
140 | return; | |
041b39d2 XL |
141 | } |
142 | ||
dc9dc135 XL |
143 | // probestack doesn't play nice either with `-C profile-generate`. |
144 | if cx.sess().opts.cg.profile_generate.enabled() { | |
0531ce1d XL |
145 | return; |
146 | } | |
147 | ||
8faf50e0 XL |
148 | // probestack doesn't play nice either with gcov profiling. |
149 | if cx.sess().opts.debugging_opts.profile { | |
150 | return; | |
151 | } | |
152 | ||
5869c6ff XL |
153 | let attr_value = match cx.sess().target.stack_probes { |
154 | StackProbeType::None => None, | |
155 | // Request LLVM to generate the probes inline. If the given LLVM version does not support | |
156 | // this, no probe is generated at all (even if the attribute is specified). | |
6a06907d | 157 | StackProbeType::Inline => Some(cstr!("inline-asm")), |
5869c6ff XL |
158 | // Flag our internal `__rust_probestack` function as the stack probe symbol. |
159 | // This is defined in the `compiler-builtins` crate for each architecture. | |
6a06907d | 160 | StackProbeType::Call => Some(cstr!("__rust_probestack")), |
5869c6ff XL |
161 | // Pick from the two above based on the LLVM version. |
162 | StackProbeType::InlineOrCall { min_llvm_version_for_inline } => { | |
163 | if llvm_util::get_version() < min_llvm_version_for_inline { | |
6a06907d | 164 | Some(cstr!("__rust_probestack")) |
5869c6ff | 165 | } else { |
6a06907d | 166 | Some(cstr!("inline-asm")) |
5869c6ff XL |
167 | } |
168 | } | |
169 | }; | |
170 | if let Some(attr_value) = attr_value { | |
171 | llvm::AddFunctionAttrStringValue( | |
172 | llfn, | |
173 | llvm::AttributePlace::Function, | |
6a06907d | 174 | cstr!("probe-stack"), |
5869c6ff XL |
175 | attr_value, |
176 | ); | |
177 | } | |
041b39d2 XL |
178 | } |
179 | ||
a2a8927a | 180 | fn set_stackprotector<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
3c0e092e XL |
181 | let sspattr = match cx.sess().stack_protector() { |
182 | StackProtector::None => return, | |
183 | StackProtector::All => Attribute::StackProtectReq, | |
184 | StackProtector::Strong => Attribute::StackProtectStrong, | |
185 | StackProtector::Basic => Attribute::StackProtect, | |
186 | }; | |
187 | ||
188 | sspattr.apply_llfn(Function, llfn) | |
189 | } | |
190 | ||
a2a8927a | 191 | pub fn apply_target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
0731742a | 192 | let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess)); |
b7449926 | 193 | llvm::AddFunctionAttrStringValue( |
dfeec247 XL |
194 | llfn, |
195 | llvm::AttributePlace::Function, | |
6a06907d | 196 | cstr!("target-cpu"), |
dfeec247 XL |
197 | target_cpu.as_c_str(), |
198 | ); | |
b7449926 XL |
199 | } |
200 | ||
a2a8927a | 201 | pub fn apply_tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { |
29967ef6 XL |
202 | if let Some(tune) = llvm_util::tune_cpu(cx.tcx.sess) { |
203 | let tune_cpu = SmallCStr::new(tune); | |
204 | llvm::AddFunctionAttrStringValue( | |
205 | llfn, | |
206 | llvm::AttributePlace::Function, | |
6a06907d | 207 | cstr!("tune-cpu"), |
29967ef6 XL |
208 | tune_cpu.as_c_str(), |
209 | ); | |
210 | } | |
211 | } | |
212 | ||
0bf4aa26 XL |
213 | /// Sets the `NonLazyBind` LLVM attribute on a given function, |
214 | /// assuming the codegen options allow skipping the PLT. | |
a2a8927a | 215 | pub fn non_lazy_bind<'ll>(sess: &Session, llfn: &'ll Value) { |
0bf4aa26 XL |
216 | // Don't generate calls through PLT if it's not necessary |
217 | if !sess.needs_plt() { | |
218 | Attribute::NonLazyBind.apply_llfn(Function, llfn); | |
219 | } | |
220 | } | |
221 | ||
a2a8927a | 222 | pub(crate) fn default_optimisation_attrs<'ll>(sess: &Session, llfn: &'ll Value) { |
9fa01778 XL |
223 | match sess.opts.optimize { |
224 | OptLevel::Size => { | |
225 | llvm::Attribute::MinSize.unapply_llfn(Function, llfn); | |
226 | llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); | |
227 | llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); | |
dfeec247 | 228 | } |
9fa01778 XL |
229 | OptLevel::SizeMin => { |
230 | llvm::Attribute::MinSize.apply_llfn(Function, llfn); | |
231 | llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); | |
232 | llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); | |
233 | } | |
234 | OptLevel::No => { | |
235 | llvm::Attribute::MinSize.unapply_llfn(Function, llfn); | |
236 | llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn); | |
237 | llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); | |
238 | } | |
239 | _ => {} | |
240 | } | |
241 | } | |
242 | ||
0731742a | 243 | /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) |
3157f602 | 244 | /// attributes. |
a2a8927a XL |
245 | pub fn from_fn_attrs<'ll, 'tcx>( |
246 | cx: &CodegenCx<'ll, 'tcx>, | |
247 | llfn: &'ll Value, | |
248 | instance: ty::Instance<'tcx>, | |
249 | ) { | |
60c5eb7d | 250 | let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); |
0531ce1d | 251 | |
9fa01778 XL |
252 | match codegen_fn_attrs.optimize { |
253 | OptimizeAttr::None => { | |
254 | default_optimisation_attrs(cx.tcx.sess, llfn); | |
255 | } | |
256 | OptimizeAttr::Speed => { | |
257 | llvm::Attribute::MinSize.unapply_llfn(Function, llfn); | |
258 | llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn); | |
259 | llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); | |
260 | } | |
261 | OptimizeAttr::Size => { | |
262 | llvm::Attribute::MinSize.apply_llfn(Function, llfn); | |
263 | llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); | |
264 | llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); | |
265 | } | |
266 | } | |
267 | ||
fc512014 XL |
268 | let inline_attr = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { |
269 | InlineAttr::Never | |
270 | } else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) { | |
271 | InlineAttr::Hint | |
272 | } else { | |
273 | codegen_fn_attrs.inline | |
274 | }; | |
275 | inline(cx, llfn, inline_attr); | |
b7449926 XL |
276 | |
277 | // The `uwtable` attribute according to LLVM is: | |
278 | // | |
279 | // This attribute indicates that the ABI being targeted requires that an | |
280 | // unwind table entry be produced for this function even if we can show | |
281 | // that no exceptions passes by it. This is normally the case for the | |
282 | // ELF x86-64 abi, but it can be disabled for some compilation units. | |
283 | // | |
284 | // Typically when we're compiling with `-C panic=abort` (which implies this | |
285 | // `no_landing_pads` check) we don't need `uwtable` because we can't | |
286 | // generate any exceptions! On Windows, however, exceptions include other | |
287 | // events such as illegal instructions, segfaults, etc. This means that on | |
288 | // Windows we end up still needing the `uwtable` attribute even if the `-C | |
289 | // panic=abort` flag is passed. | |
290 | // | |
f035d41b | 291 | // You can also find more info on why Windows always requires uwtables here: |
b7449926 | 292 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 |
f9f354fc | 293 | if cx.sess().must_emit_unwind_tables() { |
5099ac24 | 294 | attributes::emit_uwtable(llfn); |
b7449926 | 295 | } |
3157f602 | 296 | |
c295e0f8 XL |
297 | if cx.sess().opts.debugging_opts.profile_sample_use.is_some() { |
298 | llvm::AddFunctionAttrString(llfn, Function, cstr!("use-sample-profile")); | |
299 | } | |
300 | ||
cdc7bbd5 | 301 | // FIXME: none of these three functions interact with source level attributes. |
136023e0 | 302 | set_frame_pointer_type(cx, llfn); |
0731742a | 303 | set_instrument_function(cx, llfn); |
2c00a5a8 | 304 | set_probestack(cx, llfn); |
3c0e092e | 305 | set_stackprotector(cx, llfn); |
2c00a5a8 | 306 | |
94b46f34 | 307 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { |
0531ce1d XL |
308 | Attribute::Cold.apply_llfn(Function, llfn); |
309 | } | |
9fa01778 XL |
310 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) { |
311 | Attribute::ReturnsTwice.apply_llfn(Function, llfn); | |
312 | } | |
f9f354fc XL |
313 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) { |
314 | Attribute::ReadOnly.apply_llfn(Function, llfn); | |
315 | } | |
316 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) { | |
317 | Attribute::ReadNone.apply_llfn(Function, llfn); | |
318 | } | |
94b46f34 | 319 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { |
0531ce1d XL |
320 | naked(llfn, true); |
321 | } | |
94b46f34 | 322 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { |
dfeec247 | 323 | Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); |
0531ce1d | 324 | } |
1b1a35ee | 325 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { |
6a06907d | 326 | llvm::AddFunctionAttrString(llfn, Function, cstr!("cmse_nonsecure_entry")); |
1b1a35ee | 327 | } |
136023e0 XL |
328 | if let Some(align) = codegen_fn_attrs.alignment { |
329 | llvm::set_alignment(llfn, align as usize); | |
330 | } | |
f035d41b | 331 | sanitize(cx, codegen_fn_attrs.no_sanitize, llfn); |
94b46f34 | 332 | |
b7449926 XL |
333 | // Always annotate functions with the target-cpu they are compiled for. |
334 | // Without this, ThinLTO won't inline Rust functions into Clang generated | |
335 | // functions (because Clang annotates functions this way too). | |
0731742a | 336 | apply_target_cpu_attr(cx, llfn); |
29967ef6 XL |
337 | // tune-cpu is only conveyed through the attribute for our purpose. |
338 | // The target doesn't care; the subtarget reads our attribute. | |
339 | apply_tune_cpu_attr(cx, llfn); | |
b7449926 | 340 | |
5099ac24 FG |
341 | let function_features = |
342 | codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::<Vec<&str>>(); | |
343 | ||
344 | if let Some(f) = llvm_util::check_tied_features( | |
345 | cx.tcx.sess, | |
346 | &function_features.iter().map(|f| (*f, true)).collect(), | |
347 | ) { | |
348 | let span = cx | |
349 | .tcx | |
350 | .get_attrs(instance.def_id()) | |
351 | .iter() | |
352 | .find(|a| a.has_name(rustc_span::sym::target_feature)) | |
353 | .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); | |
354 | let msg = format!( | |
355 | "the target features {} must all be either enabled or disabled together", | |
356 | f.join(", ") | |
357 | ); | |
358 | let mut err = cx.tcx.sess.struct_span_err(span, &msg); | |
359 | err.help("add the missing features in a `target_feature` attribute"); | |
360 | err.emit(); | |
361 | return; | |
362 | } | |
363 | ||
364 | let mut function_features = function_features | |
6a06907d | 365 | .iter() |
5099ac24 FG |
366 | .flat_map(|feat| { |
367 | llvm_util::to_llvm_feature(cx.tcx.sess, feat) | |
c295e0f8 XL |
368 | .into_iter() |
369 | .map(|f| format!("+{}", f)) | |
370 | .collect::<Vec<String>>() | |
6a06907d | 371 | }) |
29967ef6 XL |
372 | .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { |
373 | InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), | |
374 | InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(), | |
375 | })) | |
6a06907d | 376 | .collect::<Vec<String>>(); |
0531ce1d | 377 | |
cdc7bbd5 XL |
378 | if cx.tcx.sess.target.is_like_wasm { |
379 | // If this function is an import from the environment but the wasm | |
380 | // import has a specific module/name, apply them here. | |
60c5eb7d XL |
381 | if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { |
382 | llvm::AddFunctionAttrStringValue( | |
383 | llfn, | |
384 | llvm::AttributePlace::Function, | |
6a06907d | 385 | cstr!("wasm-import-module"), |
60c5eb7d XL |
386 | &module, |
387 | ); | |
dfeec247 XL |
388 | |
389 | let name = | |
390 | codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); | |
a2a8927a | 391 | let name = CString::new(name.as_str()).unwrap(); |
dfeec247 XL |
392 | llvm::AddFunctionAttrStringValue( |
393 | llfn, | |
394 | llvm::AttributePlace::Function, | |
6a06907d | 395 | cstr!("wasm-import-name"), |
dfeec247 XL |
396 | &name, |
397 | ); | |
0531ce1d | 398 | } |
cdc7bbd5 XL |
399 | |
400 | // The `"wasm"` abi on wasm targets automatically enables the | |
401 | // `+multivalue` feature because the purpose of the wasm abi is to match | |
402 | // the WebAssembly specification, which has this feature. This won't be | |
403 | // needed when LLVM enables this `multivalue` feature by default. | |
404 | if !cx.tcx.is_closure(instance.def_id()) { | |
405 | let abi = cx.tcx.fn_sig(instance.def_id()).abi(); | |
406 | if abi == Abi::Wasm { | |
407 | function_features.push("+multivalue".to_string()); | |
408 | } | |
409 | } | |
410 | } | |
411 | ||
412 | if !function_features.is_empty() { | |
413 | let mut global_features = llvm_util::llvm_global_features(cx.tcx.sess); | |
414 | global_features.extend(function_features.into_iter()); | |
415 | let features = global_features.join(","); | |
416 | let val = CString::new(features).unwrap(); | |
417 | llvm::AddFunctionAttrStringValue( | |
418 | llfn, | |
419 | llvm::AttributePlace::Function, | |
420 | cstr!("target-features"), | |
421 | &val, | |
422 | ); | |
0531ce1d | 423 | } |
476ff2be SL |
424 | } |
425 | ||
dc9dc135 | 426 | fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> { |
dfeec247 | 427 | tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap()) |
0531ce1d | 428 | } |