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:
Jakob Stoklund Olesen
2016-10-18 12:31:37 -07:00
parent 1bcb8e25a2
commit cdb63a547e
2 changed files with 70 additions and 10 deletions

View File

@@ -2,7 +2,7 @@
use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef}; use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef};
use ir::entities::{NO_VALUE, ExpandedValue}; use ir::entities::{NO_VALUE, ExpandedValue};
use ir::instructions::InstructionData; use ir::instructions::{InstructionData, CallInfo};
use ir::extfunc::ExtFuncData; use ir::extfunc::ExtFuncData;
use entity_map::{EntityMap, PrimaryEntityData}; use entity_map::{EntityMap, PrimaryEntityData};
@@ -213,6 +213,7 @@ impl DataFlowGraph {
pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize { pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize {
let constraints = self.insts[inst].opcode().constraints(); let constraints = self.insts[inst].opcode().constraints();
let fixed_results = constraints.fixed_results(); 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 // 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 // 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, // choice, but since it is only visible in extremely rare instructions with 3+ results,
// we don't care). // we don't care).
let mut head = NO_VALUE; 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 (0..var_results).rev() {
for res_idx in (1..fixed_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 { head = self.make_value(ValueData::Inst {
ty: constraints.result_type(res_idx, ctrl_typevar), ty: ty,
num: res_idx as u16, num: (total_results - rev_num) as u16,
inst: inst, inst: inst,
next: head, 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`. // Update the second_result pointer in `inst`.
@@ -242,9 +264,9 @@ impl DataFlowGraph {
.second_result_mut() .second_result_mut()
.expect("instruction format doesn't allow multiple results") = head; .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. /// 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. /// Allow immutable access to instructions via indexing.

View File

@@ -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. /// Return true if an instruction is terminating, or false otherwise.
pub fn is_terminating<'a>(&'a self) -> bool { pub fn is_terminating<'a>(&'a self) -> bool {
match self { match self {
@@ -413,6 +428,19 @@ pub enum BranchInfo<'a> {
Table(JumpTable), 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. /// Value type constraints for a given opcode.
/// ///
/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and