Legalize return values from call instructions.

Like the entry block arguments, the return values from a call
instruction need to be converted back from their ABI representation.

Add tests of call instruction legalization.
This commit is contained in:
Jakob Stoklund Olesen
2017-03-16 17:50:49 -07:00
parent 56d12b14ff
commit d881ed331b
2 changed files with 148 additions and 1 deletions

View File

@@ -14,6 +14,50 @@ ebb0(v0: i64):
return v1 return v1
} }
function split_call_arg(i32) {
fn1 = function foo(i64)
fn2 = function foo(i32, i64)
ebb0(v0: i32):
v1 = uextend.i64 v0
call fn1(v1)
; check: $(v1l=$V), $(v1h=$V) = isplit_lohi $v1
; check: call $fn1($v1l, $v1h)
call fn2(v0, v1)
; check: call $fn2($v0, $V, $V)
return
}
function split_ret_val() {
fn1 = function foo() -> i64
ebb0:
v1 = call fn1()
; check: $ebb0:
; nextln: $(v1l=$V), $(v1h=$V) = call $fn1()
; check: $(v1new=$V) = iconcat_lohi $v1l, $v1h
; check: $v1 = copy $v1new
jump ebb1(v1)
; check: jump $ebb1($v1)
ebb1(v10: i64):
jump ebb1(v10)
}
; First return value is fine, second one is expanded.
function split_ret_val2() {
fn1 = function foo() -> i32, i64
ebb0:
v1, v2 = call fn1()
; check: $ebb0:
; nextln: $v1, $(v2l=$V), $(v2h=$V) = call $fn1()
; check: $(v2new=$V) = iconcat_lohi $v2l, $v2h
; check: $v2 -> $v2new
jump ebb1(v1, v2)
; check: jump $ebb1($v1, $v2)
ebb1(v9: i32, v10: i64):
jump ebb1(v9, v10)
}
function int_ext(i8, i8 sext, i8 uext) -> i8 uext { function int_ext(i8, i8 sext, i8 uext) -> i8 uext {
ebb0(v1: i8, v2: i8, v3: i8): ebb0(v1: i8, v2: i8, v3: i8):
; check: $ebb0($v1: i8, $(v2x=$V): i32, $(v3x=$V): i32): ; check: $ebb0($v1: i8, $(v2x=$V): i32, $(v3x=$V): i32):
@@ -24,6 +68,22 @@ ebb0(v1: i8, v2: i8, v3: i8):
return v1 return v1
} }
; Function produces single return value, still need to copy.
function ext_ret_val() {
fn1 = function foo() -> i8 sext
ebb0:
v1 = call fn1()
; check: $ebb0:
; nextln: $(rv=$V) = call $fn1()
; check: $(v1new=$V) = ireduce.i8 $rv
; check: $v1 = copy $v1new
jump ebb1(v1)
; check: jump $ebb1($v1)
ebb1(v10: i8):
jump ebb1(v10)
}
function vector_split_args(i64x4) -> i64x4 { function vector_split_args(i64x4) -> i64x4 {
ebb0(v0: i64x4): ebb0(v0: i64x4):
; check: $ebb0($(v0al=$V): i32, $(v0ah=$V): i32, $(v0bl=$V): i32, $(v0bh=$V): i32, $(v0cl=$V): i32, $(v0ch=$V): i32, $(v0dl=$V): i32, $(v0dh=$V): i32): ; check: $ebb0($(v0al=$V): i32, $(v0ah=$V): i32, $(v0bl=$V): i32, $(v0bh=$V): i32, $(v0cl=$V): i32, $(v0ch=$V): i32, $(v0dl=$V): i32, $(v0dh=$V): i32):

View File

@@ -163,6 +163,89 @@ fn legalize_entry_arguments(func: &mut Function, entry: Ebb) {
} }
} }
/// Legalize the results returned from a call instruction to match the ABI signature.
///
/// The cursor `pos` points to a call instruction with at least one return value. The cursor will
/// be left pointing after the instructions inserted to convert the return values.
///
/// This function is very similar to the `legalize_entry_arguments` function above.
fn legalize_inst_results<ResType>(dfg: &mut DataFlowGraph,
pos: &mut Cursor,
mut get_abi_type: ResType)
where ResType: FnMut(&DataFlowGraph, usize) -> ArgumentType
{
let mut call = pos.current_inst().expect("Cursor must point to a call instruction");
// We theoretically allow for call instructions that return a number of fixed results before
// the call return values. In practice, it doesn't happen.
let fixed_results = dfg[call].opcode().constraints().fixed_results();
assert_eq!(fixed_results, 0, "Fixed results on calls not supported");
let mut next_res = dfg.detach_secondary_results(call);
// The currently last result on the call instruction.
let mut last_res = dfg.first_result(call);
let mut abi_res = 0;
// The first result requires special handling.
let first_ty = dfg.value_type(last_res);
if first_ty != get_abi_type(dfg, abi_res).value_type {
// Move the call out of the way, so we can redefine the first result.
let copy = call;
call = dfg.redefine_first_value(pos);
last_res = dfg.first_result(call);
// Set up a closure that can attach new results to `call`.
let mut get_res = |dfg: &mut DataFlowGraph, ty| {
let abi_type = get_abi_type(dfg, abi_res);
if ty == abi_type.value_type {
// Don't append the first result - it's not detachable.
if fixed_results + abi_res == 0 {
*dfg[call].first_type_mut() = ty;
debug_assert_eq!(last_res, dfg.first_result(call));
} else {
last_res = dfg.append_secondary_result(last_res, ty);
}
abi_res += 1;
Ok(last_res)
} else {
Err(abi_type)
}
};
let v = convert_from_abi(dfg, pos, first_ty, &mut get_res);
dfg.replace(copy).copy(v);
}
// Point immediately after the call and any instructions dealing with the first result.
pos.next_inst();
// Now do the secondary results.
while let Some(res) = next_res {
next_res = dfg.next_secondary_result(res);
let res_type = dfg.value_type(res);
if res_type == get_abi_type(dfg, abi_res).value_type {
// No value translation is necessary, this result matches the ABI type.
dfg.attach_secondary_result(last_res, res);
last_res = res;
abi_res += 1;
} else {
let mut get_res = |dfg: &mut DataFlowGraph, ty| {
let abi_type = get_abi_type(dfg, abi_res);
if ty == abi_type.value_type {
last_res = dfg.append_secondary_result(last_res, ty);
abi_res += 1;
Ok(last_res)
} else {
Err(abi_type)
}
};
let v = convert_from_abi(dfg, pos, res_type, &mut get_res);
// The old `res` is no longer an attached result.
dfg.change_to_alias(res, v);
}
}
}
/// Compute original value of type `ty` from the legalized ABI arguments. /// Compute original value of type `ty` from the legalized ABI arguments.
/// ///
/// The conversion is recursive, controlled by the `get_arg` closure which is called to retrieve an /// The conversion is recursive, controlled by the `get_arg` closure which is called to retrieve an
@@ -423,7 +506,11 @@ fn handle_call_abi(dfg: &mut DataFlowGraph, pos: &mut Cursor) -> bool {
abi_args, abi_args,
|dfg, abi_arg| dfg.signatures[sig_ref].argument_types[abi_arg]); |dfg, abi_arg| dfg.signatures[sig_ref].argument_types[abi_arg]);
// TODO: Convert return values. if !dfg.signatures[sig_ref].return_types.is_empty() {
legalize_inst_results(dfg,
pos,
|dfg, abi_res| dfg.signatures[sig_ref].return_types[abi_res]);
}
// Yes, we changed stuff. // Yes, we changed stuff.
true true