cranelift_simplejit/
backend.rs

1//! Defines `SimpleJITModule`.
2
3use crate::memory::Memory;
4use cranelift_codegen::binemit::{
5    Addend, CodeInfo, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
6};
7use cranelift_codegen::isa::TargetIsa;
8use cranelift_codegen::settings::Configurable;
9use cranelift_codegen::{self, ir, settings};
10use cranelift_entity::SecondaryMap;
11use cranelift_module::{
12    DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module,
13    ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult,
14};
15use cranelift_native;
16#[cfg(not(windows))]
17use libc;
18use log::info;
19use std::collections::HashMap;
20use std::convert::TryInto;
21use std::ffi::CString;
22use std::io::Write;
23use std::ptr;
24use target_lexicon::PointerWidth;
25#[cfg(windows)]
26use winapi;
27
28const EXECUTABLE_DATA_ALIGNMENT: u64 = 0x10;
29const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
30const READONLY_DATA_ALIGNMENT: u64 = 0x1;
31
32/// A builder for `SimpleJITModule`.
33pub struct SimpleJITBuilder {
34    isa: Box<dyn TargetIsa>,
35    symbols: HashMap<String, *const u8>,
36    libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
37}
38
39impl SimpleJITBuilder {
40    /// Create a new `SimpleJITBuilder`.
41    ///
42    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
43    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
44    /// floating point instructions, and for stack probes. If you don't know what to use for this
45    /// argument, use `cranelift_module::default_libcall_names()`.
46    pub fn new(libcall_names: Box<dyn Fn(ir::LibCall) -> String>) -> Self {
47        let mut flag_builder = settings::builder();
48        // On at least AArch64, "colocated" calls use shorter-range relocations,
49        // which might not reach all definitions; we can't handle that here, so
50        // we require long-range relocation types.
51        flag_builder.set("use_colocated_libcalls", "false").unwrap();
52        let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
53            panic!("host machine is not supported: {}", msg);
54        });
55        let isa = isa_builder.finish(settings::Flags::new(flag_builder));
56        Self::with_isa(isa, libcall_names)
57    }
58
59    /// Create a new `SimpleJITBuilder` with an arbitrary target. This is mainly
60    /// useful for testing.
61    ///
62    /// SimpleJIT requires a `TargetIsa` configured for non-PIC.
63    ///
64    /// To create a `SimpleJITBuilder` for native use, use the `new` constructor
65    /// instead.
66    ///
67    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
68    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
69    /// floating point instructions, and for stack probes. If you don't know what to use for this
70    /// argument, use `cranelift_module::default_libcall_names()`.
71    pub fn with_isa(
72        isa: Box<dyn TargetIsa>,
73        libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
74    ) -> Self {
75        debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code");
76        let symbols = HashMap::new();
77        Self {
78            isa,
79            symbols,
80            libcall_names,
81        }
82    }
83
84    /// Define a symbol in the internal symbol table.
85    ///
86    /// The JIT will use the symbol table to resolve names that are declared,
87    /// but not defined, in the module being compiled.  A common example is
88    /// external functions.  With this method, functions and data can be exposed
89    /// to the code being compiled which are defined by the host.
90    ///
91    /// If a symbol is defined more than once, the most recent definition will
92    /// be retained.
93    ///
94    /// If the JIT fails to find a symbol in its internal table, it will fall
95    /// back to a platform-specific search (this typically involves searching
96    /// the current process for public symbols, followed by searching the
97    /// platform's C runtime).
98    pub fn symbol<K>(&mut self, name: K, ptr: *const u8) -> &Self
99    where
100        K: Into<String>,
101    {
102        self.symbols.insert(name.into(), ptr);
103        self
104    }
105
106    /// Define multiple symbols in the internal symbol table.
107    ///
108    /// Using this is equivalent to calling `symbol` on each element.
109    pub fn symbols<It, K>(&mut self, symbols: It) -> &Self
110    where
111        It: IntoIterator<Item = (K, *const u8)>,
112        K: Into<String>,
113    {
114        for (name, ptr) in symbols {
115            self.symbols.insert(name.into(), ptr);
116        }
117        self
118    }
119}
120
121/// A `SimpleJITModule` implements `Module` and emits code and data into memory where it can be
122/// directly called and accessed.
123///
124/// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITModule` instances.
125pub struct SimpleJITModule {
126    isa: Box<dyn TargetIsa>,
127    symbols: HashMap<String, *const u8>,
128    libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
129    memory: MemoryHandle,
130    declarations: ModuleDeclarations,
131    functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
132    data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
133    functions_to_finalize: Vec<FuncId>,
134    data_objects_to_finalize: Vec<DataId>,
135}
136
137/// A record of a relocation to perform.
138#[derive(Clone)]
139struct RelocRecord {
140    offset: CodeOffset,
141    reloc: Reloc,
142    name: ir::ExternalName,
143    addend: Addend,
144}
145
146struct StackMapRecord {
147    #[allow(dead_code)]
148    offset: CodeOffset,
149    #[allow(dead_code)]
150    stack_map: StackMap,
151}
152
153#[derive(Clone)]
154struct CompiledBlob {
155    ptr: *mut u8,
156    size: usize,
157    relocs: Vec<RelocRecord>,
158}
159
160/// A handle to allow freeing memory allocated by the `Module`.
161struct MemoryHandle {
162    code: Memory,
163    readonly: Memory,
164    writable: Memory,
165}
166
167/// A `SimpleJITProduct` allows looking up the addresses of all functions and data objects
168/// defined in the original module.
169pub struct SimpleJITProduct {
170    memory: MemoryHandle,
171    declarations: ModuleDeclarations,
172    functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
173    data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
174}
175
176impl SimpleJITProduct {
177    /// Free memory allocated for code and data segments of compiled functions.
178    ///
179    /// # Safety
180    ///
181    /// Because this function invalidates any pointers retrived from the
182    /// corresponding module, it should only be used when none of the functions
183    /// from that module are currently executing and none of the `fn` pointers
184    /// are called afterwards.
185    pub unsafe fn free_memory(&mut self) {
186        self.memory.code.free_memory();
187        self.memory.readonly.free_memory();
188        self.memory.writable.free_memory();
189    }
190
191    /// Get the `FuncOrDataId` associated with the given name.
192    pub fn func_or_data_for_func(&self, name: &str) -> Option<FuncOrDataId> {
193        self.declarations.get_name(name)
194    }
195
196    /// Return the address of a function.
197    pub fn lookup_func(&self, func_id: FuncId) -> *const u8 {
198        self.functions[func_id]
199            .as_ref()
200            .unwrap_or_else(|| panic!("{} is not defined", func_id))
201            .ptr
202    }
203
204    /// Return the address and size of a data object.
205    pub fn lookup_data(&self, data_id: DataId) -> (*const u8, usize) {
206        let data = self.data_objects[data_id]
207            .as_ref()
208            .unwrap_or_else(|| panic!("{} is not defined", data_id));
209        (data.ptr, data.size)
210    }
211}
212
213impl SimpleJITModule {
214    fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
215        self.symbols
216            .get(name)
217            .copied()
218            .or_else(|| lookup_with_dlsym(name))
219    }
220
221    fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
222        match *name {
223            ir::ExternalName::User { .. } => {
224                let (name, linkage) = if self.declarations.is_function(name) {
225                    let func_id = self.declarations.get_function_id(name);
226                    match &self.functions[func_id] {
227                        Some(compiled) => return compiled.ptr,
228                        None => {
229                            let decl = self.declarations.get_function_decl(func_id);
230                            (&decl.name, decl.linkage)
231                        }
232                    }
233                } else {
234                    let data_id = self.declarations.get_data_id(name);
235                    match &self.data_objects[data_id] {
236                        Some(compiled) => return compiled.ptr,
237                        None => {
238                            let decl = self.declarations.get_data_decl(data_id);
239                            (&decl.name, decl.linkage)
240                        }
241                    }
242                };
243                if let Some(ptr) = self.lookup_symbol(&name) {
244                    ptr
245                } else if linkage == Linkage::Preemptible {
246                    0 as *const u8
247                } else {
248                    panic!("can't resolve symbol {}", name);
249                }
250            }
251            ir::ExternalName::LibCall(ref libcall) => {
252                let sym = (self.libcall_names)(*libcall);
253                self.lookup_symbol(&sym)
254                    .unwrap_or_else(|| panic!("can't resolve libcall {}", sym))
255            }
256            _ => panic!("invalid ExternalName {}", name),
257        }
258    }
259
260    /// Returns the address of a finalized function.
261    pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
262        let info = &self.functions[func_id];
263        debug_assert!(
264            !self.functions_to_finalize.iter().any(|x| *x == func_id),
265            "function not yet finalized"
266        );
267        info.as_ref()
268            .expect("function must be compiled before it can be finalized")
269            .ptr
270    }
271
272    /// Returns the address and size of a finalized data object.
273    pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
274        let info = &self.data_objects[data_id];
275        debug_assert!(
276            !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
277            "data object not yet finalized"
278        );
279        let compiled = info
280            .as_ref()
281            .expect("data object must be compiled before it can be finalized");
282
283        (compiled.ptr, compiled.size)
284    }
285
286    fn record_function_for_perf(&self, ptr: *mut u8, size: usize, name: &str) {
287        // The Linux perf tool supports JIT code via a /tmp/perf-$PID.map file,
288        // which contains memory regions and their associated names.  If we
289        // are profiling with perf and saving binaries to PERF_BUILDID_DIR
290        // for post-profile analysis, write information about each function
291        // we define.
292        if cfg!(target_os = "linux") && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
293            let mut map_file = ::std::fs::OpenOptions::new()
294                .create(true)
295                .append(true)
296                .open(format!("/tmp/perf-{}.map", ::std::process::id()))
297                .unwrap();
298
299            let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
300        }
301    }
302
303    fn finalize_function(&mut self, id: FuncId) {
304        use std::ptr::write_unaligned;
305
306        let func = self.functions[id]
307            .as_ref()
308            .expect("function must be compiled before it can be finalized");
309
310        for &RelocRecord {
311            reloc,
312            offset,
313            ref name,
314            addend,
315        } in &func.relocs
316        {
317            debug_assert!((offset as usize) < func.size);
318            let at = unsafe { func.ptr.offset(offset as isize) };
319            let base = self.get_definition(name);
320            // TODO: Handle overflow.
321            let what = unsafe { base.offset(addend as isize) };
322            match reloc {
323                Reloc::Abs4 => {
324                    // TODO: Handle overflow.
325                    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
326                    unsafe {
327                        write_unaligned(at as *mut u32, what as u32)
328                    };
329                }
330                Reloc::Abs8 => {
331                    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
332                    unsafe {
333                        write_unaligned(at as *mut u64, what as u64)
334                    };
335                }
336                Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
337                    // TODO: Handle overflow.
338                    let pcrel = ((what as isize) - (at as isize)) as i32;
339                    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
340                    unsafe {
341                        write_unaligned(at as *mut i32, pcrel)
342                    };
343                }
344                Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
345                _ => unimplemented!(),
346            }
347        }
348    }
349
350    fn finalize_data(&mut self, id: DataId) {
351        use std::ptr::write_unaligned;
352
353        let data = self.data_objects[id]
354            .as_ref()
355            .expect("data object must be compiled before it can be finalized");
356
357        for &RelocRecord {
358            reloc,
359            offset,
360            ref name,
361            addend,
362        } in &data.relocs
363        {
364            debug_assert!((offset as usize) < data.size);
365            let at = unsafe { data.ptr.offset(offset as isize) };
366            let base = self.get_definition(name);
367            // TODO: Handle overflow.
368            let what = unsafe { base.offset(addend as isize) };
369            match reloc {
370                Reloc::Abs4 => {
371                    // TODO: Handle overflow.
372                    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
373                    unsafe {
374                        write_unaligned(at as *mut u32, what as u32)
375                    };
376                }
377                Reloc::Abs8 => {
378                    #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
379                    unsafe {
380                        write_unaligned(at as *mut u64, what as u64)
381                    };
382                }
383                Reloc::X86PCRel4
384                | Reloc::X86CallPCRel4
385                | Reloc::X86GOTPCRel4
386                | Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
387                _ => unimplemented!(),
388            }
389        }
390    }
391
392    /// Finalize all functions and data objects that are defined but not yet finalized.
393    /// All symbols referenced in their bodies that are declared as needing a definition
394    /// must be defined by this point.
395    ///
396    /// Use `get_finalized_function` and `get_finalized_data` to obtain the final
397    /// artifacts.
398    pub fn finalize_definitions(&mut self) {
399        for func in std::mem::take(&mut self.functions_to_finalize) {
400            let decl = self.declarations.get_function_decl(func);
401            debug_assert!(decl.linkage.is_definable());
402            self.finalize_function(func);
403        }
404        for data in std::mem::take(&mut self.data_objects_to_finalize) {
405            let decl = self.declarations.get_data_decl(data);
406            debug_assert!(decl.linkage.is_definable());
407            self.finalize_data(data);
408        }
409
410        // Now that we're done patching, prepare the memory for execution!
411        self.memory.readonly.set_readonly();
412        self.memory.code.set_readable_and_executable();
413    }
414
415    /// Create a new `SimpleJITModule`.
416    pub fn new(builder: SimpleJITBuilder) -> Self {
417        let memory = MemoryHandle {
418            code: Memory::new(),
419            readonly: Memory::new(),
420            writable: Memory::new(),
421        };
422
423        Self {
424            isa: builder.isa,
425            symbols: builder.symbols,
426            libcall_names: builder.libcall_names,
427            memory,
428            declarations: ModuleDeclarations::default(),
429            functions: SecondaryMap::new(),
430            data_objects: SecondaryMap::new(),
431            functions_to_finalize: Vec::new(),
432            data_objects_to_finalize: Vec::new(),
433        }
434    }
435}
436
437impl<'simple_jit_backend> Module for SimpleJITModule {
438    fn isa(&self) -> &dyn TargetIsa {
439        &*self.isa
440    }
441
442    fn declarations(&self) -> &ModuleDeclarations {
443        &self.declarations
444    }
445
446    fn declare_function(
447        &mut self,
448        name: &str,
449        linkage: Linkage,
450        signature: &ir::Signature,
451    ) -> ModuleResult<FuncId> {
452        let (id, _decl) = self
453            .declarations
454            .declare_function(name, linkage, signature)?;
455        Ok(id)
456    }
457
458    fn declare_data(
459        &mut self,
460        name: &str,
461        linkage: Linkage,
462        writable: bool,
463        tls: bool,
464    ) -> ModuleResult<DataId> {
465        assert!(!tls, "SimpleJIT doesn't yet support TLS");
466        let (id, _decl) = self
467            .declarations
468            .declare_data(name, linkage, writable, tls)?;
469        Ok(id)
470    }
471
472    fn define_function<TS>(
473        &mut self,
474        id: FuncId,
475        ctx: &mut cranelift_codegen::Context,
476        trap_sink: &mut TS,
477    ) -> ModuleResult<ModuleCompiledFunction>
478    where
479        TS: TrapSink,
480    {
481        info!("defining function {}: {}", id, ctx.func.display(self.isa()));
482        let CodeInfo {
483            total_size: code_size,
484            ..
485        } = ctx.compile(self.isa())?;
486
487        let decl = self.declarations.get_function_decl(id);
488        if !decl.linkage.is_definable() {
489            return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
490        }
491
492        if !self.functions[id].is_none() {
493            return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
494        }
495
496        self.functions_to_finalize.push(id);
497        let size = code_size as usize;
498        let ptr = self
499            .memory
500            .code
501            .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
502            .expect("TODO: handle OOM etc.");
503
504        self.record_function_for_perf(ptr, size, &decl.name);
505
506        let mut reloc_sink = SimpleJITRelocSink::default();
507        let mut stack_map_sink = SimpleJITStackMapSink::default();
508        unsafe {
509            ctx.emit_to_memory(
510                &*self.isa,
511                ptr,
512                &mut reloc_sink,
513                trap_sink,
514                &mut stack_map_sink,
515            )
516        };
517
518        self.functions[id] = Some(CompiledBlob {
519            ptr,
520            size,
521            relocs: reloc_sink.relocs,
522        });
523
524        Ok(ModuleCompiledFunction { size: code_size })
525    }
526
527    fn define_function_bytes(
528        &mut self,
529        id: FuncId,
530        bytes: &[u8],
531    ) -> ModuleResult<ModuleCompiledFunction> {
532        let decl = self.declarations.get_function_decl(id);
533        if !decl.linkage.is_definable() {
534            return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
535        }
536
537        let total_size: u32 = match bytes.len().try_into() {
538            Ok(total_size) => total_size,
539            _ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
540        };
541
542        if !self.functions[id].is_none() {
543            return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
544        }
545
546        self.functions_to_finalize.push(id);
547        let size = bytes.len();
548        let ptr = self
549            .memory
550            .code
551            .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
552            .expect("TODO: handle OOM etc.");
553
554        self.record_function_for_perf(ptr, size, &decl.name);
555
556        unsafe {
557            ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
558        }
559
560        self.functions[id] = Some(CompiledBlob {
561            ptr,
562            size,
563            relocs: vec![],
564        });
565
566        Ok(ModuleCompiledFunction { size: total_size })
567    }
568
569    fn define_data(&mut self, id: DataId, data: &DataContext) -> ModuleResult<()> {
570        let decl = self.declarations.get_data_decl(id);
571        if !decl.linkage.is_definable() {
572            return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
573        }
574
575        if !self.data_objects[id].is_none() {
576            return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
577        }
578
579        assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
580
581        self.data_objects_to_finalize.push(id);
582
583        let &DataDescription {
584            ref init,
585            ref function_decls,
586            ref data_decls,
587            ref function_relocs,
588            ref data_relocs,
589            custom_segment_section: _,
590            align,
591        } = data.description();
592
593        let size = init.size();
594        let ptr = if decl.writable {
595            self.memory
596                .writable
597                .allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
598                .expect("TODO: handle OOM etc.")
599        } else {
600            self.memory
601                .readonly
602                .allocate(size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
603                .expect("TODO: handle OOM etc.")
604        };
605
606        match *init {
607            Init::Uninitialized => {
608                panic!("data is not initialized yet");
609            }
610            Init::Zeros { .. } => {
611                unsafe { ptr::write_bytes(ptr, 0, size) };
612            }
613            Init::Bytes { ref contents } => {
614                let src = contents.as_ptr();
615                unsafe { ptr::copy_nonoverlapping(src, ptr, size) };
616            }
617        }
618
619        let reloc = match self.isa.triple().pointer_width().unwrap() {
620            PointerWidth::U16 => panic!(),
621            PointerWidth::U32 => Reloc::Abs4,
622            PointerWidth::U64 => Reloc::Abs8,
623        };
624        let mut relocs = Vec::new();
625        for &(offset, id) in function_relocs {
626            relocs.push(RelocRecord {
627                reloc,
628                offset,
629                name: function_decls[id].clone(),
630                addend: 0,
631            });
632        }
633        for &(offset, id, addend) in data_relocs {
634            relocs.push(RelocRecord {
635                reloc,
636                offset,
637                name: data_decls[id].clone(),
638                addend,
639            });
640        }
641
642        self.data_objects[id] = Some(CompiledBlob { ptr, size, relocs });
643
644        Ok(())
645    }
646}
647
648impl SimpleJITModule {
649    /// SimpleJIT emits code and data into memory as it processes them. This
650    /// method performs no additional processing, but returns a handle which
651    /// allows freeing the allocated memory. Otherwise said memory is leaked
652    /// to enable safe handling of the resulting pointers.
653    ///
654    /// This method does not need to be called when access to the memory
655    /// handle is not required.
656    pub fn finish(mut self) -> SimpleJITProduct {
657        self.finalize_definitions();
658
659        SimpleJITProduct {
660            memory: self.memory,
661            declarations: self.declarations,
662            functions: self.functions,
663            data_objects: self.data_objects,
664        }
665    }
666}
667
668#[cfg(not(windows))]
669fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
670    let c_str = CString::new(name).unwrap();
671    let c_str_ptr = c_str.as_ptr();
672    let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
673    if sym.is_null() {
674        None
675    } else {
676        Some(sym as *const u8)
677    }
678}
679
680#[cfg(windows)]
681fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
682    const MSVCRT_DLL: &[u8] = b"msvcrt.dll\0";
683
684    let c_str = CString::new(name).unwrap();
685    let c_str_ptr = c_str.as_ptr();
686
687    unsafe {
688        let handles = [
689            // try to find the searched symbol in the currently running executable
690            ptr::null_mut(),
691            // try to find the searched symbol in local c runtime
692            winapi::um::libloaderapi::GetModuleHandleA(MSVCRT_DLL.as_ptr() as *const i8),
693        ];
694
695        for handle in &handles {
696            let addr = winapi::um::libloaderapi::GetProcAddress(*handle, c_str_ptr);
697            if addr.is_null() {
698                continue;
699            }
700            return Some(addr as *const u8);
701        }
702
703        None
704    }
705}
706
707#[derive(Default)]
708struct SimpleJITRelocSink {
709    relocs: Vec<RelocRecord>,
710}
711
712impl RelocSink for SimpleJITRelocSink {
713    fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
714        unimplemented!();
715    }
716
717    fn reloc_external(
718        &mut self,
719        offset: CodeOffset,
720        _srcloc: ir::SourceLoc,
721        reloc: Reloc,
722        name: &ir::ExternalName,
723        addend: Addend,
724    ) {
725        self.relocs.push(RelocRecord {
726            offset,
727            reloc,
728            name: name.clone(),
729            addend,
730        });
731    }
732
733    fn reloc_jt(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::JumpTable) {
734        match reloc {
735            Reloc::X86PCRelRodata4 => {
736                // Not necessary to record this unless we are going to split apart code and its
737                // jumptbl/rodata.
738            }
739            _ => {
740                panic!("Unhandled reloc");
741            }
742        }
743    }
744
745    fn reloc_constant(&mut self, _offset: CodeOffset, reloc: Reloc, _constant: ir::ConstantOffset) {
746        match reloc {
747            Reloc::X86PCRelRodata4 => {
748                // Not necessary to record this unless we are going to split apart code and its
749                // jumptbl/rodata.
750            }
751            _ => {
752                panic!("Unhandled reloc");
753            }
754        }
755    }
756}
757
758#[derive(Default)]
759struct SimpleJITStackMapSink {
760    stack_maps: Vec<StackMapRecord>,
761}
762
763impl StackMapSink for SimpleJITStackMapSink {
764    fn add_stack_map(&mut self, offset: CodeOffset, stack_map: StackMap) {
765        self.stack_maps.push(StackMapRecord { offset, stack_map });
766    }
767}