Add result values to call instructions too.
The make_inst_results() method now understands direct and indirect calls, and can allocate result values matching the return types of the function call.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef};
|
||||
use ir::entities::{NO_VALUE, ExpandedValue};
|
||||
use ir::instructions::InstructionData;
|
||||
use ir::instructions::{InstructionData, CallInfo};
|
||||
use ir::extfunc::ExtFuncData;
|
||||
use entity_map::{EntityMap, PrimaryEntityData};
|
||||
|
||||
@@ -213,6 +213,7 @@ impl DataFlowGraph {
|
||||
pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize {
|
||||
let constraints = self.insts[inst].opcode().constraints();
|
||||
let fixed_results = constraints.fixed_results();
|
||||
let mut total_results = fixed_results;
|
||||
|
||||
// Additional values form a linked list starting from the second result value. Generate
|
||||
// the list backwards so we don't have to modify value table entries in place. (This
|
||||
@@ -220,20 +221,41 @@ impl DataFlowGraph {
|
||||
// choice, but since it is only visible in extremely rare instructions with 3+ results,
|
||||
// we don't care).
|
||||
let mut head = NO_VALUE;
|
||||
let mut first_type = Type::default();
|
||||
let mut first_type = None;
|
||||
let mut rev_num = 1;
|
||||
|
||||
// TBD: Function call return values for direct and indirect function calls.
|
||||
// Get the call signature if this is a function call.
|
||||
if let Some(sig) = self.call_signature(inst) {
|
||||
// Create result values corresponding to the call return types.
|
||||
let var_results = self.signatures[sig].return_types.len();
|
||||
total_results += var_results;
|
||||
|
||||
if fixed_results > 0 {
|
||||
for res_idx in (1..fixed_results).rev() {
|
||||
for res_idx in (0..var_results).rev() {
|
||||
if let Some(ty) = first_type {
|
||||
head = self.make_value(ValueData::Inst {
|
||||
ty: ty,
|
||||
num: (total_results - rev_num) as u16,
|
||||
inst: inst,
|
||||
next: head,
|
||||
});
|
||||
rev_num += 1;
|
||||
}
|
||||
first_type = Some(self.signatures[sig].return_types[res_idx].value_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Then the fixed results whic will appear at the front of the list.
|
||||
for res_idx in (0..fixed_results).rev() {
|
||||
if let Some(ty) = first_type {
|
||||
head = self.make_value(ValueData::Inst {
|
||||
ty: constraints.result_type(res_idx, ctrl_typevar),
|
||||
num: res_idx as u16,
|
||||
ty: ty,
|
||||
num: (total_results - rev_num) as u16,
|
||||
inst: inst,
|
||||
next: head,
|
||||
});
|
||||
rev_num += 1;
|
||||
}
|
||||
first_type = constraints.result_type(0, ctrl_typevar);
|
||||
first_type = Some(constraints.result_type(res_idx, ctrl_typevar));
|
||||
}
|
||||
|
||||
// Update the second_result pointer in `inst`.
|
||||
@@ -242,9 +264,9 @@ impl DataFlowGraph {
|
||||
.second_result_mut()
|
||||
.expect("instruction format doesn't allow multiple results") = head;
|
||||
}
|
||||
*self.insts[inst].first_type_mut() = first_type;
|
||||
*self.insts[inst].first_type_mut() = first_type.unwrap_or_default();
|
||||
|
||||
fixed_results
|
||||
total_results
|
||||
}
|
||||
|
||||
/// Get the first result of an instruction.
|
||||
@@ -265,6 +287,16 @@ impl DataFlowGraph {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the call signature of a direct or indirect call instruction.
|
||||
/// Returns `None` if `inst` is not a call instruction.
|
||||
pub fn call_signature(&self, inst: Inst) -> Option<SigRef> {
|
||||
match self.insts[inst].analyze_call() {
|
||||
CallInfo::NotACall => None,
|
||||
CallInfo::Direct(f, _) => Some(self.ext_funcs[f].signature),
|
||||
CallInfo::Indirect(s, _) => Some(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow immutable access to instructions via indexing.
|
||||
|
||||
@@ -388,6 +388,21 @@ impl InstructionData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return information about a call instruction.
|
||||
///
|
||||
/// Any instruction that can call another function reveals its call signature here.
|
||||
pub fn analyze_call<'a>(&'a self) -> CallInfo<'a> {
|
||||
match self {
|
||||
&InstructionData::Call { ref data, .. } => {
|
||||
CallInfo::Direct(data.func_ref, &data.varargs)
|
||||
}
|
||||
&InstructionData::IndirectCall { ref data, .. } => {
|
||||
CallInfo::Indirect(data.sig_ref, &data.varargs)
|
||||
}
|
||||
_ => CallInfo::NotACall,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if an instruction is terminating, or false otherwise.
|
||||
pub fn is_terminating<'a>(&'a self) -> bool {
|
||||
match self {
|
||||
@@ -413,6 +428,19 @@ pub enum BranchInfo<'a> {
|
||||
Table(JumpTable),
|
||||
}
|
||||
|
||||
/// Information about call instructions.
|
||||
pub enum CallInfo<'a> {
|
||||
/// This is not a call instruction.
|
||||
NotACall,
|
||||
|
||||
/// This is a direct call to an external function declared in the preamble. See
|
||||
/// `DataFlowGraph.ext_funcs`.
|
||||
Direct(FuncRef, &'a [Value]),
|
||||
|
||||
/// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`.
|
||||
Indirect(SigRef, &'a [Value]),
|
||||
}
|
||||
|
||||
/// Value type constraints for a given opcode.
|
||||
///
|
||||
/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and
|
||||
|
||||
Reference in New Issue
Block a user