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.
|
||||
use crate::ssa::{SSABuilder, SideEffects};
|
||||
use crate::variable::Variable;
|
||||
use core::fmt::{self, Debug};
|
||||
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::condcodes::IntCC;
|
||||
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
|
||||
/// all the complexity of its internal representation.
|
||||
///
|
||||
@@ -290,27 +376,33 @@ impl<'a> FunctionBuilder<'a> {
|
||||
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.
|
||||
pub fn declare_var(&mut self, var: Variable, ty: Type) {
|
||||
debug_assert_eq!(
|
||||
self.func_ctx.types[var],
|
||||
types::INVALID,
|
||||
"variable {:?} is declared twice",
|
||||
var
|
||||
);
|
||||
/// Declares the type of a variable, so that it can be used later (by calling
|
||||
/// [`FunctionBuilder::use_var`]). This function will return an error if it
|
||||
/// was not possible to use the variable.
|
||||
pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
|
||||
if self.func_ctx.types[var] != types::INVALID {
|
||||
return Err(DeclareVariableError::DeclaredMultipleTimes(var));
|
||||
}
|
||||
self.func_ctx.types[var] = ty;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// In order to use a variable (by calling [`FunctionBuilder::use_var`]), you need
|
||||
/// to first declare its type with this method.
|
||||
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 ty = *self.func_ctx.types.get(var).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"variable {:?} is used but its type has not been declared",
|
||||
var
|
||||
)
|
||||
});
|
||||
let ty = *self
|
||||
.func_ctx
|
||||
.types
|
||||
.get(var)
|
||||
.ok_or(UseVariableError::UsedBeforeDeclared(var))?;
|
||||
debug_assert_ne!(
|
||||
ty,
|
||||
types::INVALID,
|
||||
@@ -322,24 +414,55 @@ impl<'a> FunctionBuilder<'a> {
|
||||
.use_var(self.func, var, ty, self.position.unwrap())
|
||||
};
|
||||
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
|
||||
/// the same as the type registered for the variable.
|
||||
pub fn def_var(&mut self, var: Variable, val: Value) {
|
||||
debug_assert_eq!(
|
||||
*self.func_ctx.types.get(var).unwrap_or_else(|| panic!(
|
||||
self.try_def_var(var, val)
|
||||
.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",
|
||||
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
|
||||
@@ -971,7 +1094,10 @@ impl<'a> FunctionBuilder<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::greatest_divisible_power_of_two;
|
||||
use crate::frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use crate::frontend::{
|
||||
DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
|
||||
UseVariableError,
|
||||
};
|
||||
use crate::Variable;
|
||||
use alloc::string::ToString;
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
@@ -1669,4 +1795,41 @@ block0:
|
||||
assert_eq!(8, greatest_divisible_power_of_two(24));
|
||||
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