Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a prototype name mangler
Browse files Browse the repository at this point in the history
This commit adds the `hieratika-mangler` crate, responsible for
implementing the hieratika name mangling scheme in a centralized
fashion.

As it exists, the name mangling scheme implemented is _not final_, and
is likely to change and evolve as the requirements are clarified. To
that end, the mangling design docs have not been updated, as no coherent
design process has yet taken place for the mangling scheme.
iamrecursion committed Jan 23, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 525a2bb commit 9cea036
Showing 10 changed files with 408 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ members = [
"crates/cairoc",
"crates/flo",
"crates/test-utils",
"crates/mangler",
]

# Cairo is excluded because it is imported as git submodule and it has its own Cargo workspace.
@@ -55,6 +56,7 @@ hieratika-compiler = { path = "crates/compiler" }
hieratika-driver = { path = "crates/driver" }
hieratika-errors = { path = "crates/error" }
hieratika-flo = { path = "crates/flo" }
hieratika-mangler = { path = "crates/mangler" }
hieratika-test-utils = { path = "crates/test-utils" }
miette = { version = "7.4.0", features = ["fancy"] }
starknet-types-core = "0.1.7"
9 changes: 8 additions & 1 deletion crates/error/src/lib.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
pub mod backtrace;
pub mod compile;
pub mod mangle;

use thiserror::Error;

@@ -28,9 +29,15 @@ pub type Result<T> = miette::Result<T, WithBacktrace<Error>>;
/// _truly_ public interface of this library should return this error type.
#[derive(Debug, Error)]
pub enum Error {
/// Errors in the compilation of cairo code to FLO.
#[error(transparent)]
CairoCompile(#[from] compile::cairo::Error),

/// Errors in the compilation of LLVM code to FLO.
#[error(transparent)]
LLVMCompile(#[from] compile::llvm::Error),

/// Errors in the mangling or de-mangling of names.
#[error(transparent)]
CairoCompile(#[from] compile::cairo::Error),
Mangle(#[from] mangle::Error),
}
24 changes: 24 additions & 0 deletions crates/error/src/mangle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Errors in the mangling process.
use thiserror::Error;

use crate::backtrace::WithBacktrace;

/// The result type for use in the mangler.
pub type Result<T> = miette::Result<T, WithBacktrace<Error>>;

/// This error type is for use during the process of compilation from LLVM IR to
/// the Cairo IR.
#[derive(Debug, Error)]
pub enum Error {
#[error("The input {_0} is not a valid mangle input")]
InvalidInput(String),
}

impl Error {
/// Constructs [`Error::InvalidInput`] while allowing conversion from a
/// [`str`].
pub fn invalid_input(mangle_input: &str) -> Self {
Self::InvalidInput(mangle_input.to_string())
}
}
20 changes: 20 additions & 0 deletions crates/mangler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "hieratika-mangler"
version.workspace = true
homepage.workspace = true
repository.workspace = true
license-file.workspace = true

authors.workspace = true
description = "A crate implementing the Hieratika internal mangling scheme."
keywords.workspace = true
categories.workspace = true

edition.workspace = true
rust-version.workspace = true

[dependencies]
hieratika-errors.workspace = true
hieratika-flo.workspace = true

[dev-dependencies]
4 changes: 4 additions & 0 deletions crates/mangler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Hieratika Mangler

This crate is responsible for implementing the name mangling algorithm used by Hieratika. For more
information, please see the [docs](../../docs/Name%20Mangling.md) on name mangling.
17 changes: 17 additions & 0 deletions crates/mangler/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! This module contains constants that specify portions of the mangling
//! behavior.
/// A marker used to separate sections in the mangled string.
pub const SECTION_SEPARATOR: &str = "$";

/// A marker used to signify the beginning of a snapshot mangle segment.
pub const BEGIN_SNAPSHOT: &str = "m";

/// A marker used to signify the beginning of an array.
pub const BEGIN_ARRAY: &str = "A";

/// A marker used to signify the beginning of a struct.
pub const BEGIN_STRUCT: &str = "S";

/// A marker used to signify the end of a struct.
pub const END_STRUCT: &str = "s";
101 changes: 101 additions & 0 deletions crates/mangler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! This crate implements Hieratika's name mangling scheme, providing a
//! centralized place for the mangling functionality to live that can be
//! accessed by all the other crates.
//!
//! See [`mangle`] for the primary interface to the crate and for usage
//! examples.
//!
//! # Mangling Scheme
//!
//! The mangling scheme is designed to ensure a compact and unique
//! representation for functions across different modules that may share name
//! and/or type information. Please note that the current design of this
//! mangling scheme is **not final** and is subject to change.
#![warn(clippy::all, clippy::cargo, clippy::pedantic)]
#![expect(clippy::multiple_crate_versions)] // Enforced by our dependencies

pub mod constants;
pub mod mapping;
mod util;

use hieratika_flo::types::Type;

use crate::{
constants::SECTION_SEPARATOR,
mapping::{mangle_params, mangle_returns},
};

/// The result type for the library.
pub type Result<T> = hieratika_errors::mangle::Result<T>;

/// The error type for the library.
pub type Error = hieratika_errors::mangle::Error;

/// Generates the mangled form of the provided `name`.
///
/// A mangled name consists of the return types string, followed by the function
/// name, followed by the params string, followed by the function's module, all
/// separated using the [`SECTION_SEPARATOR`].
///
/// ```
/// use hieratika_flo::types::Type;
/// use hieratika_mangler::{NameInfo, mangle};
///
/// let func_name = NameInfo::new(
/// "my_func",
/// "my_module",
/// vec![Type::Double, Type::Bool],
/// vec![Type::Float, Type::Bool],
/// );
///
/// assert_eq!(mangle(func_name).unwrap(), "fc$my_func$dc$my_module");
/// ```
///
/// # Errors
///
/// - [`Error::InvalidInput`] if any of the types in the input `name` cannot be
/// mangled.
pub fn mangle(name: NameInfo) -> Result<String> {
let returns_string = mangle_returns(&name.return_types)?;
let params_string = mangle_params(&name.parameter_types)?;
let func_name = name.name;
let func_module = name.module;
Ok([returns_string, func_name, params_string, func_module].join(SECTION_SEPARATOR))
}

/// The inputs to the name mangling process.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NameInfo {
/// The name of the symbol to be mangled.
pub name: String,

/// The name of the module in which the symbol was found.
pub module: String,

/// The types of the parameters to the function to be mangled.
pub parameter_types: Vec<Type>,

/// The types of the returns from the function to be mangled.
pub return_types: Vec<Type>,
}

impl NameInfo {
/// Creates a new [`NameInfo`] instance from the provided information.
#[must_use]
pub fn new(
name: &str,
module: &str,
parameter_types: Vec<Type>,
return_types: Vec<Type>,
) -> Self {
let name = name.into();
let module = module.into();
NameInfo {
name,
module,
parameter_types,
return_types,
}
}
}
214 changes: 214 additions & 0 deletions crates/mangler/src/mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//! This file contains the functionality for mapping types to mangled
//! identifiers.
use hieratika_flo::types::{ArrayType, StructType, Type};

use crate::{
Result,
constants::{BEGIN_ARRAY, BEGIN_SNAPSHOT, BEGIN_STRUCT, END_STRUCT},
util,
};

/// Generates the mangled string representing the provided `return_types`.
///
/// The return types mangle consists of the concatenation of the mangled forms
/// of all the return types.
///
/// ```
/// use hieratika_flo::types::Type;
/// use hieratika_mangler::mapping::mangle_returns;
///
/// let return_types = vec![Type::Bool, Type::Unsigned8, Type::Void];
///
/// assert_eq!(mangle_returns(&return_types).unwrap(), "cBv");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if any of the provided `return_types`
/// cannot be mangled.
pub fn mangle_returns(return_types: &[Type]) -> Result<String> {
Ok(return_types
.iter()
.map(mangle_type)
.collect::<Result<Vec<_>>>()?
.join(""))
}

/// Generates the mangled string representing the provided `param_types`.
///
/// The return types mangle consists of the concatenation of the mangled forms
/// of all the parameter types
///
/// ```
/// use hieratika_flo::types::Type;
/// use hieratika_mangler::mapping::mangle_params;
///
/// let return_types = vec![Type::Bool, Type::Unsigned8, Type::Void];
///
/// assert_eq!(mangle_params(&return_types).unwrap(), "cBv");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if any of the provided `param_types` cannot
/// be mangled.
pub fn mangle_params(param_types: &[Type]) -> Result<String> {
Ok(param_types
.iter()
.map(mangle_type)
.collect::<Result<Vec<_>>>()?
.join(""))
}

/// Maps from the provided `typ` to the corresponding mangle expression.
///
/// ```
/// use hieratika_flo::types::Type;
/// use hieratika_mangler::mapping::mangle_type;
///
/// assert_eq!(mangle_type(&Type::Bool).unwrap(), "c");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if the provided `typ` is
/// [`Type::Unspecified`] and hence cannot be mangled.
pub fn mangle_type(typ: &Type) -> Result<String> {
let str = match typ {
Type::Void => "v",
Type::Bool => "c",
Type::Enum => "e",
Type::Unsigned8 => "B",
Type::Unsigned16 => "H",
Type::Unsigned32 => "I",
Type::Unsigned64 => "Q",
Type::Unsigned128 => "O",
Type::Signed8 => "b",
Type::Signed16 => "h",
Type::Signed24 => "x",
Type::Signed32 => "i",
Type::Signed64 => "q",
Type::Signed128 => "o",
Type::WeaklyTypedFelt => "w",
Type::Float => "f",
Type::Double => "d",
Type::Pointer => "p",
Type::Snapshot(snap) => &mangle_snapshot(snap.as_ref())?,
Type::Array(array_ty) => &mangle_array(array_ty)?,
Type::Struct(struct_ty) => &mangle_struct(struct_ty)?,
Type::Unspecified => Err(util::invalid_input_err(typ))?,
};

Ok(str.into())
}

/// Mangles the provided `typ` as a snapshot.
///
/// A snapshot mangle takes the form of the [`BEGIN_SNAPSHOT`] constant followed
/// by the mangle expression for the type being snapshotted.
///
/// ```
/// use hieratika_flo::types::Type;
/// use hieratika_mangler::mapping::mangle_snapshot;
///
/// assert_eq!(mangle_snapshot(&Type::Enum).unwrap(), "me");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if the snapshot child `typ` cannot be
/// mangled.
pub fn mangle_snapshot(typ: &Type) -> Result<String> {
let child_type = mangle_type(typ)?;
Ok(format!("{BEGIN_SNAPSHOT}{child_type}"))
}

/// Maps from the provided `arr` type to the corresponding mangling expression.
///
/// An array type mangle takes the form of the [`BEGIN_ARRAY`] constant,
/// followed by the mangle expression for the array's element type, followed by
/// a number denoting the array's length.
///
/// ```
/// use hieratika_flo::types::{ArrayType, PoisonType, Type};
/// use hieratika_mangler::mapping::mangle_array;
///
/// let array_type = ArrayType {
/// member_type: Box::new(Type::Signed128),
/// length: 10,
/// diagnostics: Vec::new(),
/// location: None,
/// poison: PoisonType::None,
/// };
///
/// assert_eq!(mangle_array(&array_type).unwrap(), "Ao10");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if the array's element type cannot be
/// mangled.
pub fn mangle_array(arr: &ArrayType) -> Result<String> {
let child_typ = mangle_type(arr.member_type.as_ref())?;
let len = arr.length;
Ok(format!("{BEGIN_ARRAY}{child_typ}{len}"))
}

/// Maps from the provided `struct_ty` to the corresponding mangling expression.
///
/// A struct type mangle takes the form of the [`BEGIN_STRUCT`] constant,
/// followed by the concatenation of the mangle expression for each of the
/// struct elements, followed by the [`END_STRUCT`] constant.
///
/// If a struct contains another struct, the start and end markers must be
/// balanced, with an end marker referring to the closest preceding begin
/// marker.
///
/// ```
/// use hieratika_flo::types::{ArrayType, PoisonType, StructType, Type};
/// use hieratika_mangler::mapping::mangle_struct;
///
/// let inner_array = ArrayType {
/// member_type: Box::new(Type::Signed128),
/// length: 10,
/// diagnostics: Vec::new(),
/// location: None,
/// poison: PoisonType::None,
/// };
///
/// let inner_struct = StructType {
/// members: vec![Type::Bool, Type::Signed8],
/// diagnostics: Vec::new(),
/// location: None,
/// poison: PoisonType::None,
/// };
///
/// let outer_struct = StructType {
/// members: vec![
/// Type::Bool,
/// Type::Unsigned32,
/// Type::Array(inner_array),
/// Type::Struct(inner_struct),
/// ],
/// diagnostics: Vec::new(),
/// location: None,
/// poison: PoisonType::None,
/// };
///
/// assert_eq!(mangle_struct(&outer_struct).unwrap(), "ScIAo10Scbss");
/// ```
///
/// # Errors
///
/// - [`crate::Error::InvalidInput`] if any of the struct's element types cannot
/// be mangled.
pub fn mangle_struct(struct_ty: &StructType) -> Result<String> {
let elems_string = struct_ty
.members
.iter()
.map(mangle_type)
.collect::<Result<Vec<_>>>()?
.join("");
Ok(format!("{BEGIN_STRUCT}{elems_string}{END_STRUCT}"))
}
10 changes: 10 additions & 0 deletions crates/mangler/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Internal utilities for the mangling crate.
use hieratika_flo::types::Type;

use crate::Error;

/// Constructs an invalid input error.
pub fn invalid_input_err(typ: &Type) -> Error {
Error::InvalidInput(format!("{typ:?}"))
}

0 comments on commit 9cea036

Please sign in to comment.