wasmer_compiler/
misc.rs

1//! A common functionality used among various compilers.
2
3use itertools::Itertools;
4use wasmer_types::{FunctionType, LocalFunctionIndex, Type};
5
6/// Represents the kind of compiled function or module, used for debugging and identification
7/// purposes across multiple compiler backends (e.g., LLVM, Cranelift).
8#[derive(Debug, Clone)]
9pub enum CompiledKind {
10    /// A locally-defined function in the Wasm file.
11    Local(LocalFunctionIndex, String),
12    /// A function call trampoline for a given signature.
13    FunctionCallTrampoline(FunctionType),
14    /// A dynamic function trampoline for a given signature.
15    DynamicFunctionTrampoline(FunctionType),
16    /// An entire Wasm module.
17    Module,
18}
19
20/// Converts a slice of `Type` into a string signature, mapping each type to a specific character.
21/// Used to represent function signatures in a compact string form.
22pub fn types_to_signature(types: &[Type]) -> String {
23    let tokens = types
24        .iter()
25        .map(|ty| match ty {
26            Type::I32 => "i",
27            Type::I64 => "I",
28            Type::F32 => "f",
29            Type::F64 => "F",
30            Type::V128 => "v",
31            Type::ExternRef => "e",
32            Type::FuncRef => "r",
33            Type::ExceptionRef => "x",
34        })
35        .collect_vec();
36    // Apparently, LLVM has issues if the filename is too long, thus we compact it.
37    tokens
38        .chunk_by(|a, b| a == b)
39        .map(|chunk| {
40            if chunk.len() >= 8 {
41                format!("{}x{}", chunk.len(), chunk[0])
42            } else {
43                chunk.to_owned().join("")
44            }
45        })
46        .join("")
47}
48
49/// Sanitizes a string so it can be safely used as a filename.
50fn sanitize_filename(name: &str) -> String {
51    name.chars()
52        .map(|c| {
53            if c.is_alphanumeric() || c == '_' || c == '-' {
54                c
55            } else {
56                '_'
57            }
58        })
59        .collect()
60}
61
62/// Converts a kind into a filename, that we will use to dump
63/// the contents of the IR object file to.
64pub fn function_kind_to_filename(kind: &CompiledKind, suffix: &str) -> String {
65    match kind {
66        CompiledKind::Local(local_func_index, name) => {
67            let mut name = sanitize_filename(name);
68
69            // Limit to 255 characters to comply with common filesystem path component restrictions.
70            const PATH_LIMIT: usize = 255;
71
72            if name.len() + suffix.len() > PATH_LIMIT {
73                let id_string = local_func_index.as_u32().to_string();
74                name.truncate(PATH_LIMIT - id_string.len() - suffix.len() - 1);
75                name.push('_');
76                name.push_str(&id_string);
77                name.push_str(suffix);
78            } else {
79                name.push_str(suffix);
80            }
81
82            debug_assert!(name.len() <= PATH_LIMIT);
83            name
84        }
85        CompiledKind::FunctionCallTrampoline(func_type) => format!(
86            "trampoline_call_{}_{}",
87            types_to_signature(func_type.params()),
88            types_to_signature(func_type.results())
89        ),
90        CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
91            "trampoline_dynamic_{}_{}",
92            types_to_signature(func_type.params()),
93            types_to_signature(func_type.results())
94        ),
95        CompiledKind::Module => "module".into(),
96    }
97}