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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
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")
|
||||
/// 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 {
|
||||
ty.half_vector().expect("Can only split integers and vectors")
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user