Verify the location of outgoing call arguments.

Once a signature has been legalized, the arguments to any call using
that signature must be assigned to the proper stack locations. Outgoing
arguments that are passed on the stack must be assigned to matching
OutgoingArg stack slot locations.

Outgoing arguments that are passed in registers don't need to appear in
the correct registers until after register allocation.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-31 15:04:41 -07:00
parent 7d9edc2d5e
commit ce9e6fe53e

View File

@@ -57,7 +57,7 @@ use flowgraph::ControlFlowGraph;
use ir::entities::AnyEntity;
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo};
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot,
Value, Type, Opcode};
StackSlotKind, Value, Type, Opcode, ValueLoc, ArgumentLoc};
use isa::TargetIsa;
use std::error as std_error;
use std::fmt::{self, Display, Formatter};
@@ -552,6 +552,7 @@ impl<'a> Verifier<'a> {
.iter()
.map(|a| a.value_type);
self.typecheck_variable_args_iterator(inst, arg_types)?;
self.check_outgoing_args(inst, sig_ref)?;
}
CallInfo::Indirect(sig_ref, _) => {
let arg_types = self.func.dfg.signatures[sig_ref]
@@ -559,6 +560,7 @@ impl<'a> Verifier<'a> {
.iter()
.map(|a| a.value_type);
self.typecheck_variable_args_iterator(inst, arg_types)?;
self.check_outgoing_args(inst, sig_ref)?;
}
CallInfo::NotACall => {}
}
@@ -599,6 +601,64 @@ impl<'a> Verifier<'a> {
Ok(())
}
/// Check the locations assigned to outgoing call arguments.
///
/// When a signature has been legalized, all values passed as outgoing arguments on the stack
/// must be assigned to a matching `OutgoingArg` stack slot.
fn check_outgoing_args(&self, inst: Inst, sig_ref: SigRef) -> Result {
let sig = &self.func.dfg.signatures[sig_ref];
// Before legalization, there's nothing to check.
if sig.argument_bytes.is_none() {
return Ok(());
}
let args = self.func.dfg.inst_variable_args(inst);
let expected_args = &sig.argument_types[..];
for (&arg, &abi) in args.iter().zip(expected_args) {
// Value types have already been checked by `typecheck_variable_args_iterator()`.
if let ArgumentLoc::Stack(offset) = abi.location {
let arg_loc = self.func.locations.get_or_default(arg);
if let ValueLoc::Stack(ss) = arg_loc {
// Argument value is assigned to a stack slot as expected.
self.verify_stack_slot(inst, ss)?;
let slot = &self.func.stack_slots[ss];
if slot.kind != StackSlotKind::OutgoingArg {
return err!(inst,
"Outgoing stack argument {} in wrong stack slot: {} = {}",
arg,
ss,
slot);
}
if slot.offset != offset {
return err!(inst,
"Outgoing stack argument {} should have offset {}: {} = {}",
arg,
offset,
ss,
slot);
}
if slot.size != abi.value_type.bytes() {
return err!(inst,
"Outgoing stack argument {} wrong size for {}: {} = {}",
arg,
abi.value_type,
ss,
slot);
}
} else {
let reginfo = self.isa.map(|i| i.register_info());
return err!(inst,
"Outgoing stack argument {} in wrong location: {}",
arg,
arg_loc.display(reginfo.as_ref()));
}
}
}
Ok(())
}
fn typecheck_return(&self, inst: Inst) -> Result {
if self.func.dfg[inst].opcode().is_return() {
let args = self.func.dfg.inst_variable_args(inst);