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:
@@ -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 {
|
||||||
|
DefVariableError::TypeMismatch(var, val) => {
|
||||||
|
panic!(
|
||||||
|
"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",
|
"variable {:?} is used but its type has not been declared",
|
||||||
var
|
var
|
||||||
)),
|
|
||||||
self.func.dfg.value_type(val),
|
|
||||||
"declared type of variable {:?} doesn't match type of value {}",
|
|
||||||
var,
|
|
||||||
val
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user