1
1
use std:: {
2
+ collections:: hash_map:: Entry ,
2
3
convert:: { TryFrom , TryInto } ,
3
4
iter,
4
5
} ;
@@ -34,7 +35,7 @@ pub enum EmulateByNameResult<'mir, 'tcx> {
34
35
/// Jumping has already been taken care of.
35
36
AlreadyJumped ,
36
37
/// A MIR body has been found for the function
37
- MirBody ( & ' mir mir:: Body < ' tcx > ) ,
38
+ MirBody ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) ,
38
39
/// The item is not supported.
39
40
NotSupported ,
40
41
}
@@ -135,81 +136,91 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
135
136
fn lookup_exported_symbol (
136
137
& mut self ,
137
138
link_name : Symbol ,
138
- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
139
+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
139
140
let this = self . eval_context_mut ( ) ;
140
141
let tcx = this. tcx . tcx ;
141
142
142
143
// If the result was cached, just return it.
143
- if let Some ( instance) = this. machine . exported_symbols_cache . get ( & link_name) {
144
- return instance. map ( |instance| this. load_mir ( instance. def , None ) ) . transpose ( ) ;
145
- }
146
-
147
- // Find it if it was not cached.
148
- let mut instance_and_crate: Option < ( ty:: Instance < ' _ > , CrateNum ) > = None ;
149
- // `dependency_formats` includes all the transitive informations needed to link a crate,
150
- // which is what we need here since we need to dig out `exported_symbols` from all transitive
151
- // dependencies.
152
- let dependency_formats = tcx. dependency_formats ( ( ) ) ;
153
- let dependency_format = dependency_formats
154
- . iter ( )
155
- . find ( |( crate_type, _) | * crate_type == CrateType :: Executable )
156
- . expect ( "interpreting a non-executable crate" ) ;
157
- for cnum in
158
- iter:: once ( LOCAL_CRATE ) . chain ( dependency_format. 1 . iter ( ) . enumerate ( ) . filter_map (
159
- |( num, & linkage) | ( linkage != Linkage :: NotLinked ) . then_some ( CrateNum :: new ( num + 1 ) ) ,
160
- ) )
161
- {
162
- // We can ignore `_export_level` here: we are a Rust crate, and everything is exported
163
- // from a Rust crate.
164
- for & ( symbol, _export_level) in tcx. exported_symbols ( cnum) {
165
- if let ExportedSymbol :: NonGeneric ( def_id) = symbol {
166
- let attrs = tcx. codegen_fn_attrs ( def_id) ;
167
- let symbol_name = if let Some ( export_name) = attrs. export_name {
168
- export_name
169
- } else if attrs. flags . contains ( CodegenFnAttrFlags :: NO_MANGLE ) {
170
- tcx. item_name ( def_id)
171
- } else {
172
- // Skip over items without an explicitly defined symbol name.
173
- continue ;
174
- } ;
175
- if symbol_name == link_name {
176
- if let Some ( ( original_instance, original_cnum) ) = instance_and_crate {
177
- // Make sure we are consistent wrt what is 'first' and 'second'.
178
- let original_span = tcx. def_span ( original_instance. def_id ( ) ) . data ( ) ;
179
- let span = tcx. def_span ( def_id) . data ( ) ;
180
- if original_span < span {
181
- throw_machine_stop ! ( TerminationInfo :: MultipleSymbolDefinitions {
182
- link_name,
183
- first: original_span,
184
- first_crate: tcx. crate_name( original_cnum) ,
185
- second: span,
186
- second_crate: tcx. crate_name( cnum) ,
187
- } ) ;
144
+ // (Cannot use `or_insert` since the code below might have to throw an error.)
145
+ let entry = this. machine . exported_symbols_cache . entry ( link_name) ;
146
+ let instance = * match entry {
147
+ Entry :: Occupied ( e) => e. into_mut ( ) ,
148
+ Entry :: Vacant ( e) => {
149
+ // Find it if it was not cached.
150
+ let mut instance_and_crate: Option < ( ty:: Instance < ' _ > , CrateNum ) > = None ;
151
+ // `dependency_formats` includes all the transitive informations needed to link a crate,
152
+ // which is what we need here since we need to dig out `exported_symbols` from all transitive
153
+ // dependencies.
154
+ let dependency_formats = tcx. dependency_formats ( ( ) ) ;
155
+ let dependency_format = dependency_formats
156
+ . iter ( )
157
+ . find ( |( crate_type, _) | * crate_type == CrateType :: Executable )
158
+ . expect ( "interpreting a non-executable crate" ) ;
159
+ for cnum in iter:: once ( LOCAL_CRATE ) . chain (
160
+ dependency_format. 1 . iter ( ) . enumerate ( ) . filter_map ( |( num, & linkage) | {
161
+ ( linkage != Linkage :: NotLinked ) . then_some ( CrateNum :: new ( num + 1 ) )
162
+ } ) ,
163
+ ) {
164
+ // We can ignore `_export_level` here: we are a Rust crate, and everything is exported
165
+ // from a Rust crate.
166
+ for & ( symbol, _export_level) in tcx. exported_symbols ( cnum) {
167
+ if let ExportedSymbol :: NonGeneric ( def_id) = symbol {
168
+ let attrs = tcx. codegen_fn_attrs ( def_id) ;
169
+ let symbol_name = if let Some ( export_name) = attrs. export_name {
170
+ export_name
171
+ } else if attrs. flags . contains ( CodegenFnAttrFlags :: NO_MANGLE ) {
172
+ tcx. item_name ( def_id)
188
173
} else {
189
- throw_machine_stop ! ( TerminationInfo :: MultipleSymbolDefinitions {
190
- link_name,
191
- first: span,
192
- first_crate: tcx. crate_name( cnum) ,
193
- second: original_span,
194
- second_crate: tcx. crate_name( original_cnum) ,
195
- } ) ;
174
+ // Skip over items without an explicitly defined symbol name.
175
+ continue ;
176
+ } ;
177
+ if symbol_name == link_name {
178
+ if let Some ( ( original_instance, original_cnum) ) = instance_and_crate
179
+ {
180
+ // Make sure we are consistent wrt what is 'first' and 'second'.
181
+ let original_span =
182
+ tcx. def_span ( original_instance. def_id ( ) ) . data ( ) ;
183
+ let span = tcx. def_span ( def_id) . data ( ) ;
184
+ if original_span < span {
185
+ throw_machine_stop ! (
186
+ TerminationInfo :: MultipleSymbolDefinitions {
187
+ link_name,
188
+ first: original_span,
189
+ first_crate: tcx. crate_name( original_cnum) ,
190
+ second: span,
191
+ second_crate: tcx. crate_name( cnum) ,
192
+ }
193
+ ) ;
194
+ } else {
195
+ throw_machine_stop ! (
196
+ TerminationInfo :: MultipleSymbolDefinitions {
197
+ link_name,
198
+ first: span,
199
+ first_crate: tcx. crate_name( cnum) ,
200
+ second: original_span,
201
+ second_crate: tcx. crate_name( original_cnum) ,
202
+ }
203
+ ) ;
204
+ }
205
+ }
206
+ if !matches ! ( tcx. def_kind( def_id) , DefKind :: Fn | DefKind :: AssocFn ) {
207
+ throw_ub_format ! (
208
+ "attempt to call an exported symbol that is not defined as a function"
209
+ ) ;
210
+ }
211
+ instance_and_crate = Some ( ( ty:: Instance :: mono ( tcx, def_id) , cnum) ) ;
196
212
}
197
213
}
198
- if !matches ! ( tcx. def_kind( def_id) , DefKind :: Fn | DefKind :: AssocFn ) {
199
- throw_ub_format ! (
200
- "attempt to call an exported symbol that is not defined as a function"
201
- ) ;
202
- }
203
- instance_and_crate = Some ( ( ty:: Instance :: mono ( tcx, def_id) , cnum) ) ;
204
214
}
205
215
}
216
+
217
+ e. insert ( instance_and_crate. map ( |ic| ic. 0 ) )
206
218
}
219
+ } ;
220
+ match instance {
221
+ None => Ok ( None ) , // no symbol with this name
222
+ Some ( instance) => Ok ( Some ( ( this. load_mir ( instance. def , None ) ?, instance) ) ) ,
207
223
}
208
-
209
- let instance = instance_and_crate. map ( |ic| ic. 0 ) ;
210
- // Cache it and load its MIR, if found.
211
- this. machine . exported_symbols_cache . try_insert ( link_name, instance) . unwrap ( ) ;
212
- instance. map ( |instance| this. load_mir ( instance. def , None ) ) . transpose ( )
213
224
}
214
225
215
226
/// Emulates calling a foreign item, failing if the item is not supported.
@@ -225,7 +236,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
225
236
args : & [ OpTy < ' tcx , Tag > ] ,
226
237
ret : Option < ( & PlaceTy < ' tcx , Tag > , mir:: BasicBlock ) > ,
227
238
unwind : StackPopUnwind ,
228
- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
239
+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
229
240
let this = self . eval_context_mut ( ) ;
230
241
let attrs = this. tcx . get_attrs ( def_id) ;
231
242
let link_name = this
@@ -253,7 +264,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
253
264
this. check_abi_and_shim_symbol_clash ( abi, Abi :: Rust , link_name) ?;
254
265
let panic_impl_id = tcx. lang_items ( ) . panic_impl ( ) . unwrap ( ) ;
255
266
let panic_impl_instance = ty:: Instance :: mono ( tcx, panic_impl_id) ;
256
- return Ok ( Some ( & * this. load_mir ( panic_impl_instance. def , None ) ?) ) ;
267
+ return Ok ( Some ( (
268
+ & * this. load_mir ( panic_impl_instance. def , None ) ?,
269
+ panic_impl_instance,
270
+ ) ) ) ;
257
271
}
258
272
#[ rustfmt:: skip]
259
273
| "exit"
@@ -297,7 +311,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
297
311
this. go_to_block ( ret) ;
298
312
}
299
313
EmulateByNameResult :: AlreadyJumped => ( ) ,
300
- EmulateByNameResult :: MirBody ( mir) => return Ok ( Some ( mir) ) ,
314
+ EmulateByNameResult :: MirBody ( mir, instance ) => return Ok ( Some ( ( mir, instance ) ) ) ,
301
315
EmulateByNameResult :: NotSupported => {
302
316
if let Some ( body) = this. lookup_exported_symbol ( link_name) ? {
303
317
return Ok ( Some ( body) ) ;
@@ -328,11 +342,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
328
342
329
343
match allocator_kind {
330
344
AllocatorKind :: Global => {
331
- let body = this
345
+ let ( body, instance ) = this
332
346
. lookup_exported_symbol ( symbol) ?
333
347
. expect ( "symbol should be present if there is a global allocator" ) ;
334
348
335
- Ok ( EmulateByNameResult :: MirBody ( body) )
349
+ Ok ( EmulateByNameResult :: MirBody ( body, instance ) )
336
350
}
337
351
AllocatorKind :: Default => {
338
352
default ( this) ?;
0 commit comments