Add try_use_var method to cranelift-frontend. (#4588)

* Add `try_use_var` method to `cranelift-frontend`.
- Unlike `use_var`, this method does not panic if the variable has not been defined
before use

* Add `try_declare_var` and `try_def_var`.
- Also implement Error for error enums.

* Use `write!` macro.

* Add `write!` use I missed.
This commit is contained in:
Teymour Aldridge
2022-08-05 00:19:15 +08:00
committed by GitHub
parent 1fc11bbe51
commit ad223c5234

View File

@@ -1,8 +1,9 @@
//! A frontend for building Cranelift IR from other languages. //! A frontend for building Cranelift IR from other languages.
use crate::ssa::{SSABuilder, SideEffects}; use crate::ssa::{SSABuilder, SideEffects};
use crate::variable::Variable; use crate::variable::Variable;
use core::fmt::{self, Debug};
use cranelift_codegen::cursor::{Cursor, FuncCursor}; use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::{EntitySet, SecondaryMap}; use cranelift_codegen::entity::{EntityRef, EntitySet, SecondaryMap};
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
@@ -162,6 +163,91 @@ impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// An error encountered when calling [`FunctionBuilder::try_use_var`].
pub enum UseVariableError {
UsedBeforeDeclared(Variable),
}
impl fmt::Display for UseVariableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UseVariableError::UsedBeforeDeclared(variable) => {
write!(
f,
"variable {} was used before it was defined",
variable.index()
)?;
}
}
Ok(())
}
}
impl std::error::Error for UseVariableError {}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// An error encountered when calling [`FunctionBuilder::try_declare_var`].
pub enum DeclareVariableError {
DeclaredMultipleTimes(Variable),
}
impl std::error::Error for DeclareVariableError {}
impl fmt::Display for DeclareVariableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeclareVariableError::DeclaredMultipleTimes(variable) => {
write!(
f,
"variable {} was declared multiple times",
variable.index()
)?;
}
}
Ok(())
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// An error encountered when defining the initial value of a variable.
pub enum DefVariableError {
/// The variable was instantiated with a value of the wrong type.
///
/// note: to obtain the type of the value, you can call
/// [`cranelift_codegen::ir::dfg::DataFlowGraph::value_type`] (using the
/// [`FunctionBuilder.func.dfg`] field)
TypeMismatch(Variable, Value),
/// The value was defined (in a call to [`FunctionBuilder::def_var`]) before
/// it was declared (in a call to [`FunctionBuilder::declare_var`]).
DefinedBeforeDeclared(Variable),
}
impl fmt::Display for DefVariableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DefVariableError::TypeMismatch(variable, value) => {
write!(
f,
"the types of variable {} and value {} are not the same.
The `Value` supplied to `def_var` must be of the same type as
the variable was declared to be of in `declare_var`.",
variable.index(),
value.as_u32()
)?;
}
DefVariableError::DefinedBeforeDeclared(variable) => {
write!(
f,
"the value of variabe {} was declared before it was defined",
variable.index()
)?;
}
}
Ok(())
}
}
/// This module allows you to create a function in Cranelift IR in a straightforward way, hiding /// This module allows you to create a function in Cranelift IR in a straightforward way, hiding
/// all the complexity of its internal representation. /// all the complexity of its internal representation.
/// ///
@@ -290,27 +376,33 @@ impl<'a> FunctionBuilder<'a> {
self.handle_ssa_side_effects(side_effects); self.handle_ssa_side_effects(side_effects);
} }
/// In order to use a variable in a `use_var`, you need to declare its type with this method. /// Declares the type of a variable, so that it can be used later (by calling
pub fn declare_var(&mut self, var: Variable, ty: Type) { /// [`FunctionBuilder::use_var`]). This function will return an error if it
debug_assert_eq!( /// was not possible to use the variable.
self.func_ctx.types[var], pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
types::INVALID, if self.func_ctx.types[var] != types::INVALID {
"variable {:?} is declared twice", return Err(DeclareVariableError::DeclaredMultipleTimes(var));
var }
);
self.func_ctx.types[var] = ty; self.func_ctx.types[var] = ty;
Ok(())
} }
/// Returns the Cranelift IR value corresponding to the utilization at the current program /// In order to use a variable (by calling [`FunctionBuilder::use_var`]), you need
/// position of a previously defined user variable. /// to first declare its type with this method.
pub fn use_var(&mut self, var: Variable) -> Value { pub fn declare_var(&mut self, var: Variable, ty: Type) {
self.try_declare_var(var, ty)
.unwrap_or_else(|_| panic!("the variable {:?} has been declared multiple times", var))
}
/// Returns the Cranelift IR necessary to use a previously defined user
/// variable, returning an error if this is not possible.
pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
let (val, side_effects) = { let (val, side_effects) = {
let ty = *self.func_ctx.types.get(var).unwrap_or_else(|| { let ty = *self
panic!( .func_ctx
"variable {:?} is used but its type has not been declared", .types
var .get(var)
) .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
});
debug_assert_ne!( debug_assert_ne!(
ty, ty,
types::INVALID, types::INVALID,
@@ -322,24 +414,55 @@ impl<'a> FunctionBuilder<'a> {
.use_var(self.func, var, ty, self.position.unwrap()) .use_var(self.func, var, ty, self.position.unwrap())
}; };
self.handle_ssa_side_effects(side_effects); self.handle_ssa_side_effects(side_effects);
val Ok(val)
}
/// Returns the Cranelift IR value corresponding to the utilization at the current program
/// position of a previously defined user variable.
pub fn use_var(&mut self, var: Variable) -> Value {
self.try_use_var(var).unwrap_or_else(|_| {
panic!(
"variable {:?} is used but its type has not been declared",
var
)
})
}
/// Registers a new definition of a user variable. This function will return
/// an error if the value supplied does not match the type the variable was
/// declared to have.
pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
let var_ty = *self
.func_ctx
.types
.get(var)
.ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
if var_ty != self.func.dfg.value_type(val) {
return Err(DefVariableError::TypeMismatch(var, val));
}
self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
Ok(())
} }
/// Register a new definition of a user variable. The type of the value must be /// Register a new definition of a user variable. The type of the value must be
/// the same as the type registered for the variable. /// the same as the type registered for the variable.
pub fn def_var(&mut self, var: Variable, val: Value) { pub fn def_var(&mut self, var: Variable, val: Value) {
debug_assert_eq!( self.try_def_var(var, val)
*self.func_ctx.types.get(var).unwrap_or_else(|| panic!( .unwrap_or_else(|error| match error {
"variable {:?} is used but its type has not been declared", DefVariableError::TypeMismatch(var, val) => {
var panic!(
)), "declared type of variable {:?} doesn't match type of value {}",
self.func.dfg.value_type(val), var, val
"declared type of variable {:?} doesn't match type of value {}", );
var, }
val DefVariableError::DefinedBeforeDeclared(var) => {
); panic!(
"variable {:?} is used but its type has not been declared",
self.func_ctx.ssa.def_var(var, val, self.position.unwrap()); var
);
}
})
} }
/// Set label for Value /// Set label for Value
@@ -971,7 +1094,10 @@ impl<'a> FunctionBuilder<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::greatest_divisible_power_of_two; use super::greatest_divisible_power_of_two;
use crate::frontend::{FunctionBuilder, FunctionBuilderContext}; use crate::frontend::{
DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
UseVariableError,
};
use crate::Variable; use crate::Variable;
use alloc::string::ToString; use alloc::string::ToString;
use cranelift_codegen::entity::EntityRef; use cranelift_codegen::entity::EntityRef;
@@ -1669,4 +1795,41 @@ block0:
assert_eq!(8, greatest_divisible_power_of_two(24)); assert_eq!(8, greatest_divisible_power_of_two(24));
assert_eq!(1, greatest_divisible_power_of_two(25)); assert_eq!(1, greatest_divisible_power_of_two(25));
} }
#[test]
fn try_use_var() {
let sig = Signature::new(CallConv::SystemV);
let mut fn_ctx = FunctionBuilderContext::new();
let mut func = Function::with_name_signature(ExternalName::testcase("sample"), sig);
{
let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
let block0 = builder.create_block();
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);
assert_eq!(
builder.try_use_var(Variable::with_u32(0)),
Err(UseVariableError::UsedBeforeDeclared(Variable::with_u32(0)))
);
let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
assert_eq!(
builder.try_def_var(Variable::with_u32(0), value),
Err(DefVariableError::DefinedBeforeDeclared(Variable::with_u32(
0
)))
);
builder.declare_var(Variable::with_u32(0), cranelift_codegen::ir::types::I32);
assert_eq!(
builder.try_declare_var(Variable::with_u32(0), cranelift_codegen::ir::types::I32),
Err(DeclareVariableError::DeclaredMultipleTimes(
Variable::with_u32(0)
))
);
}
}
} }