11use std:: {
2+ collections:: hash_map:: Entry ,
23 convert:: { TryFrom , TryInto } ,
34 iter,
45} ;
@@ -34,7 +35,7 @@ pub enum EmulateByNameResult<'mir, 'tcx> {
3435 /// Jumping has already been taken care of.
3536 AlreadyJumped ,
3637 /// A MIR body has been found for the function
37- MirBody ( & ' mir mir:: Body < ' tcx > ) ,
38+ MirBody ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) ,
3839 /// The item is not supported.
3940 NotSupported ,
4041}
@@ -135,81 +136,91 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
135136 fn lookup_exported_symbol (
136137 & mut self ,
137138 link_name : Symbol ,
138- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
139+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
139140 let this = self . eval_context_mut ( ) ;
140141 let tcx = this. tcx . tcx ;
141142
142143 // 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)
188173 } 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) ) ;
196212 }
197213 }
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) ) ;
204214 }
205215 }
216+
217+ e. insert ( instance_and_crate. map ( |ic| ic. 0 ) )
206218 }
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) ) ) ,
207223 }
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 ( )
213224 }
214225
215226 /// 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
225236 args : & [ OpTy < ' tcx , Tag > ] ,
226237 ret : Option < ( & PlaceTy < ' tcx , Tag > , mir:: BasicBlock ) > ,
227238 unwind : StackPopUnwind ,
228- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
239+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
229240 let this = self . eval_context_mut ( ) ;
230241 let attrs = this. tcx . get_attrs ( def_id) ;
231242 let link_name = this
@@ -253,7 +264,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
253264 this. check_abi_and_shim_symbol_clash ( abi, Abi :: Rust , link_name) ?;
254265 let panic_impl_id = tcx. lang_items ( ) . panic_impl ( ) . unwrap ( ) ;
255266 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+ ) ) ) ;
257271 }
258272 #[ rustfmt:: skip]
259273 | "exit"
@@ -297,7 +311,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
297311 this. go_to_block ( ret) ;
298312 }
299313 EmulateByNameResult :: AlreadyJumped => ( ) ,
300- EmulateByNameResult :: MirBody ( mir) => return Ok ( Some ( mir) ) ,
314+ EmulateByNameResult :: MirBody ( mir, instance ) => return Ok ( Some ( ( mir, instance ) ) ) ,
301315 EmulateByNameResult :: NotSupported => {
302316 if let Some ( body) = this. lookup_exported_symbol ( link_name) ? {
303317 return Ok ( Some ( body) ) ;
@@ -328,11 +342,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
328342
329343 match allocator_kind {
330344 AllocatorKind :: Global => {
331- let body = this
345+ let ( body, instance ) = this
332346 . lookup_exported_symbol ( symbol) ?
333347 . expect ( "symbol should be present if there is a global allocator" ) ;
334348
335- Ok ( EmulateByNameResult :: MirBody ( body) )
349+ Ok ( EmulateByNameResult :: MirBody ( body, instance ) )
336350 }
337351 AllocatorKind :: Default => {
338352 default ( this) ?;
0 commit comments