Legalize entry block arguments to match ABI types.

Insert conversion code that reconstructs the original function argument
types from the legalized ABI signature.

Add abi::legalize_abi_value(). This function is used when adapting code
to a legalized function signature.
This commit is contained in:
Jakob Stoklund Olesen
2017-03-06 13:57:36 -08:00
parent 77492aa463
commit 37b2e94c72
6 changed files with 288 additions and 27 deletions

View File

@@ -3,23 +3,65 @@
//! This module provides functions and data structures that are useful for implementing the
//! `TargetIsa::legalize_signature()` method.
use ir::{ArgumentLoc, ArgumentType, Type};
use ir::{ArgumentLoc, ArgumentType, ArgumentExtension, Type};
use std::cmp::Ordering;
/// Legalization action to perform on a single argument or return value.
/// Legalization action to perform on a single argument or return value when converting a
/// signature.
///
/// An argument may go through a sequence of legalization steps before it reaches the final
/// `Assign` action.
#[derive(Clone, Copy)]
pub enum ArgAction {
/// Assign the argument to the given location.
Assign(ArgumentLoc),
/// Split the argument into smaller parts, then call again.
/// Convert the argument, then call again.
///
/// This action can split an integer type into two smaller integer arguments, or it can split a
/// SIMD vector into halves.
///
/// Floating point scalar types can't be split.
Split,
Convert(ValueConversion),
}
/// Legalization action to be applied to a value that is being passed to or from a legalized ABI.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ValueConversion {
/// Split an integer types into low and high parts, using `isplit_lohi`.
IntSplit,
/// Split a vector type into halves with identical lane types.
VectorSplit,
/// Bit-cast to an integer type of the same size.
IntBits,
/// Sign-extend integer value to the required type.
Sext(Type),
/// Unsigned zero-extend value to the required type.
Uext(Type),
}
impl ValueConversion {
/// Apply this conversion to a type, return the converted type.
pub fn apply(self, ty: Type) -> Type {
match self {
ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
ValueConversion::Sext(nty) => nty,
ValueConversion::Uext(nty) => nty,
}
}
/// Is this a split conversion that results in two arguments?
pub fn is_split(self) -> bool {
match self {
ValueConversion::IntSplit => true,
ValueConversion::VectorSplit => true,
_ => false,
}
}
}
/// Common trait for assigning arguments to registers or stack locations.
@@ -53,20 +95,104 @@ pub fn legalize_args<AA: ArgAssigner>(args: &mut Vec<ArgumentType>, aa: &mut AA)
argno += 1;
}
// Split this argument into two smaller ones. Then revisit both.
ArgAction::Split => {
let new_arg = ArgumentType { value_type: split_type(arg.value_type), ..arg };
ArgAction::Convert(conv) => {
let new_arg = ArgumentType { value_type: conv.apply(arg.value_type), ..arg };
args[argno].value_type = new_arg.value_type;
args.insert(argno + 1, new_arg);
if conv.is_split() {
args.insert(argno + 1, new_arg);
}
}
}
}
}
/// Given a value type that isn't legal, compute a replacement type half the size.
fn split_type(ty: Type) -> Type {
if ty.is_int() {
ty.half_width().expect("Integer type too small to split")
} else {
ty.half_vector().expect("Can only split integers and vectors")
/// Determine the right action to take when passing a `have` value type to a call signature where
/// the next argument is `arg` which has a different value type.
///
/// The signature legalization process in `legalize_args` above can replace a single argument value
/// with multiple arguments of smaller types. It can also change the type of an integer argument to
/// a larger integer type, requiring the smaller value to be sign- or zero-extended.
///
/// The legalizer needs to repair the values at all ABI boundaries:
///
/// - Incoming function arguments to the entry EBB.
/// - Function arguments passed to a call.
/// - Return values from a call.
/// - Return values passed to a return instruction.
///
/// The `legalize_abi_value` function helps the legalizer with the process. When the legalizer
/// needs to pass a pre-legalized `have` argument, but the ABI argument `arg` has a different value
/// type, `legalize_abi_value(have, arg)` tells the legalizer how to create the needed value type
/// for the argument.
///
/// It may be necessary to call `legalize_abi_value` more than once for a given argument before the
/// desired argument type appears. This will happen when a vector or integer type needs to be split
/// more than once, for example.
pub fn legalize_abi_value(have: Type, arg: &ArgumentType) -> ValueConversion {
let have_bits = have.bits();
let arg_bits = arg.value_type.bits();
match have_bits.cmp(&arg_bits) {
// We have fewer bits than the ABI argument.
Ordering::Less => {
assert!(have.is_int() && arg.value_type.is_int(),
"Can only extend integer values");
match arg.extension {
ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
_ => panic!("No argument extension specified"),
}
}
// We have the same number of bits as the argument.
Ordering::Equal => {
// This must be an integer vector that is split and then extended.
assert!(arg.value_type.is_int());
assert!(!have.is_scalar());
ValueConversion::VectorSplit
}
// We have more bits than the argument.
Ordering::Greater => {
if have.is_scalar() {
if have.is_float() {
// Convert a float to int so it can be split the next time.
// ARM would do this to pass an `f64` in two registers.
ValueConversion::IntBits
} else {
ValueConversion::IntSplit
}
} else {
ValueConversion::VectorSplit
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ir::types;
use ir::ArgumentType;
#[test]
fn legalize() {
let mut arg = ArgumentType::new(types::I32);
assert_eq!(legalize_abi_value(types::I64X2, &arg),
ValueConversion::VectorSplit);
assert_eq!(legalize_abi_value(types::I64, &arg),
ValueConversion::IntSplit);
// Vector of integers is broken down, then sign-extended.
arg.extension = ArgumentExtension::Sext;
assert_eq!(legalize_abi_value(types::I16X4, &arg),
ValueConversion::VectorSplit);
assert_eq!(legalize_abi_value(types::I16.by(2).unwrap(), &arg),
ValueConversion::VectorSplit);
assert_eq!(legalize_abi_value(types::I16, &arg),
ValueConversion::Sext(types::I32));
// 64-bit float is split as an integer.
assert_eq!(legalize_abi_value(types::F64, &arg),
ValueConversion::IntBits);
}
}