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

@@ -2,6 +2,9 @@
test legalizer
isa riscv
; regex: V=v\d+
; regex: VX=vx\d+
function f(i32) {
sig0 = signature(i32) -> i32
@@ -21,3 +24,11 @@ function f(i32) {
ebb0(v0: i32):
return_reg v0
}
function int_split_args(i64) -> i64 {
ebb0(v0: i64):
; check: $ebb0($(v0l=$VX): i32, $(v0h=$VX): i32):
; check: iconcat_lohi $v0l, $v0h
v1 = iadd_imm v0, 1
return v0
}

View File

@@ -10,8 +10,8 @@ ebb0(v1: i64, v2: i64):
v3 = band v1, v2
return v3
}
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi
; check: [R#ec
; sameln: $(v3l=$V) = band $v1l, $v2l
; check: [R#ec
@@ -23,8 +23,8 @@ ebb0(v1: i64, v2: i64):
v3 = bor v1, v2
return v3
}
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi
; check: [R#cc
; sameln: $(v3l=$V) = bor $v1l, $v2l
; check: [R#cc
@@ -36,8 +36,8 @@ ebb0(v1: i64, v2: i64):
v3 = bxor v1, v2
return v3
}
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi
; check: [R#8c
; sameln: $(v3l=$V) = bxor $v1l, $v2l
; check: [R#8c
@@ -52,8 +52,8 @@ ebb0(v1: i64, v2: i64):
v3 = iadd v1, v2
return v3
}
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi
; check: [R#0c
; sameln: $(v3l=$V) = iadd $v1l, $v2l
; check: $(c=$V) = icmp ult, $v3l, $v1l

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);
}
}

View File

@@ -67,6 +67,17 @@ impl Type {
}
}
/// Get an integer type with the requested number of bits.
pub fn int(bits: u16) -> Option<Type> {
match bits {
8 => Some(I8),
16 => Some(I16),
32 => Some(I32),
64 => Some(I64),
_ => None,
}
}
/// Get a type with the same number of lanes as this type, but with the lanes replaced by
/// booleans of the same size.
///

View File

@@ -5,7 +5,7 @@
//!
//! This doesn't support the soft-float ABI at the moment.
use abi::{ArgAction, ArgAssigner, legalize_args};
use abi::{ArgAction, ValueConversion, ArgAssigner, legalize_args};
use ir::{Signature, ArgumentType, ArgumentLoc};
use isa::riscv::registers::{GPR, FPR};
use settings as shared_settings;
@@ -39,7 +39,7 @@ impl ArgAssigner for Args {
// Check for a legal type.
// RISC-V doesn't have SIMD at all, so break all vectors down.
if !ty.is_scalar() {
return ArgAction::Split;
return ArgAction::Convert(ValueConversion::VectorSplit);
}
// Large integers and booleans are broken down to fit in a register.
@@ -47,7 +47,7 @@ impl ArgAssigner for Args {
// Align registers and stack to a multiple of two pointers.
self.regs = align(self.regs, 2);
self.offset = align(self.offset, 2 * self.pointer_bytes);
return ArgAction::Split;
return ArgAction::Convert(ValueConversion::IntSplit);
}
if self.regs < 8 {

View File

@@ -13,7 +13,9 @@
//! The legalizer does not deal with register allocation constraints. These constraints are derived
//! from the encoding recipes, and solved later by the register allocator.
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, InstBuilder};
use abi::{legalize_abi_value, ValueConversion};
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, InstBuilder, Ebb, Type, Value,
ArgumentType};
use ir::condcodes::IntCC;
use isa::{TargetIsa, Legalize};
@@ -87,4 +89,115 @@ fn legalize_signatures(func: &mut Function, isa: &TargetIsa) {
for sig in func.dfg.signatures.keys() {
isa.legalize_signature(&mut func.dfg.signatures[sig]);
}
if let Some(entry) = func.layout.entry_block() {
legalize_entry_arguments(func, entry);
}
}
/// Legalize the entry block arguments after `func`'s signature has been legalized.
///
/// The legalized signature may contain more arguments than the original signature, and the
/// argument types have been changed. This function goes through the arguments to the entry EBB and
/// replaces them with arguments of the right type for the ABI.
///
/// The original entry EBB arguments are computed from the new ABI arguments by code inserted at
/// the top of the entry block.
fn legalize_entry_arguments(func: &mut Function, entry: Ebb) {
// Insert position for argument conversion code.
// We want to insert instructions before the first instruction in the entry block.
// If the entry block is empty, append instructions to it instead.
let mut pos = Cursor::new(&mut func.layout);
pos.goto_top(entry);
pos.next_inst();
// Keep track of the argument types in the ABI-legalized signature.
let abi_types = &func.signature.argument_types;
let mut abi_arg = 0;
// Process the EBB arguments one at a time, possibly replacing one argument with multiple new
// ones. We do this by detaching the entry EBB arguments first.
let mut next_arg = func.dfg.take_ebb_args(entry);
while let Some(arg) = next_arg {
// Get the next argument before we mutate `arg`.
next_arg = func.dfg.next_ebb_arg(arg);
let arg_type = func.dfg.value_type(arg);
if arg_type == abi_types[abi_arg].value_type {
// No value translation is necessary, this argument matches the ABI type.
// Just use the original EBB argument value. This is the most common case.
func.dfg.put_ebb_arg(entry, arg);
abi_arg += 1;
} else {
// Compute the value we want for `arg` from the legalized ABI arguments.
let converted = convert_from_abi(&mut func.dfg,
&mut pos,
entry,
&mut abi_arg,
abi_types,
arg_type);
// The old `arg` is no longer an attached EBB argument, but there are probably still
// uses of the value. Make it an alias to the converted value.
func.dfg.change_to_alias(arg, converted);
}
}
}
/// Compute original value of type `ty` from the legalized ABI arguments beginning at `abi_arg`.
///
/// Update `abi_arg` to reflect the ABI arguments consumed and return the computed value.
fn convert_from_abi(dfg: &mut DataFlowGraph,
pos: &mut Cursor,
entry: Ebb,
abi_arg: &mut usize,
abi_types: &[ArgumentType],
ty: Type)
-> Value {
// Terminate the recursion when we get the desired type.
if ty == abi_types[*abi_arg].value_type {
return dfg.append_ebb_arg(entry, ty);
}
// Reconstruct how `ty` was legalized into the argument at `abi_arg`.
let conversion = legalize_abi_value(ty, &abi_types[*abi_arg]);
// The conversion describes value to ABI argument. We implement the reverse conversion here.
match conversion {
// Construct a `ty` by concatenating two ABI integers.
ValueConversion::IntSplit => {
let abi_ty = ty.half_width().expect("Invalid type for conversion");
let lo = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
let hi = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
dfg.ins(pos).iconcat_lohi(lo, hi)
}
// Construct a `ty` by concatenating two halves of a vector.
ValueConversion::VectorSplit => {
let abi_ty = ty.half_vector().expect("Invalid type for conversion");
let _lo = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
let _hi = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
unimplemented!()
}
// Construct a `ty` by bit-casting from an integer type.
ValueConversion::IntBits => {
assert!(!ty.is_int());
let abi_ty = Type::int(ty.bits()).expect("Invalid type for conversion");
let arg = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
dfg.ins(pos).bitcast(ty, arg)
}
// ABI argument is a sign-extended version of the value we want.
ValueConversion::Sext(abi_ty) => {
let arg = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
// TODO: Currently, we don't take advantage of the ABI argument being sign-extended.
// We could insert an `assert_sreduce` which would fold with a following `sextend` of
// this value.
dfg.ins(pos).ireduce(ty, arg)
}
ValueConversion::Uext(abi_ty) => {
let arg = convert_from_abi(dfg, pos, entry, abi_arg, abi_types, abi_ty);
// TODO: Currently, we don't take advantage of the ABI argument being sign-extended.
// We could insert an `assert_ureduce` which would fold with a following `uextend` of
// this value.
dfg.ins(pos).ireduce(ty, arg)
}
}
}