Make DataFlowGraph::insts public, but restricted (#5450)

We have some operations defined on DataFlowGraph purely to work around borrow-checker issues with InstructionData and other data on DataFlowGraph. Part of the problem is that indexing the DFG directly hides the fact that we're only indexing the insts field of the DFG.

This PR makes the insts field of the DFG public, but wraps it in a newtype that only allows indexing. This means that the borrow checker is better able to tell when operations on memory held by the DFG won't conflict, which comes up frequently when mutating ValueLists held by InstructionData.
This commit is contained in:
Trevor Elliott
2022-12-16 10:46:09 -08:00
committed by GitHub
parent 6323b0f9f4
commit 25bf8e0e67
31 changed files with 182 additions and 178 deletions

View File

@@ -85,14 +85,14 @@ pub struct LastStores {
impl LastStores {
fn update(&mut self, func: &Function, inst: Inst) {
let opcode = func.dfg[inst].opcode();
let opcode = func.dfg.insts[inst].opcode();
if has_memory_fence_semantics(opcode) {
self.heap = inst.into();
self.table = inst.into();
self.vmctx = inst.into();
self.other = inst.into();
} else if opcode.can_store() {
if let Some(memflags) = func.dfg[inst].memflags() {
if let Some(memflags) = func.dfg.insts[inst].memflags() {
if memflags.heap() {
self.heap = inst.into();
} else if memflags.table() {
@@ -112,7 +112,7 @@ impl LastStores {
}
fn get_last_store(&self, func: &Function, inst: Inst) -> PackedOption<Inst> {
if let Some(memflags) = func.dfg[inst].memflags() {
if let Some(memflags) = func.dfg.insts[inst].memflags() {
if memflags.heap() {
self.heap
} else if memflags.table() {
@@ -122,7 +122,9 @@ impl LastStores {
} else {
self.other
}
} else if func.dfg[inst].opcode().can_load() || func.dfg[inst].opcode().can_store() {
} else if func.dfg.insts[inst].opcode().can_load()
|| func.dfg.insts[inst].opcode().can_store()
{
inst.into()
} else {
PackedOption::default()
@@ -277,13 +279,13 @@ impl<'a> AliasAnalysis<'a> {
"alias analysis: scanning at inst{} with state {:?} ({:?})",
inst.index(),
state,
func.dfg[inst],
func.dfg.insts[inst],
);
let replacing_value = if let Some((address, offset, ty)) = inst_addr_offset_type(func, inst)
{
let address = func.dfg.resolve_aliases(address);
let opcode = func.dfg[inst].opcode();
let opcode = func.dfg.insts[inst].opcode();
if opcode.can_store() {
let store_data = inst_store_data(func, inst).unwrap();

View File

@@ -641,9 +641,9 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut FuncCursor<'f> {
if let CursorPosition::At(_) = self.position() {
if let Some(curr) = self.current_inst() {
if let Some(prev) = self.layout().prev_inst(curr) {
let prev_op = self.data_flow_graph()[prev].opcode();
let inst_op = self.data_flow_graph()[inst].opcode();
let curr_op = self.data_flow_graph()[curr].opcode();
let prev_op = self.data_flow_graph().insts[prev].opcode();
let inst_op = self.data_flow_graph().insts[inst].opcode();
let curr_op = self.data_flow_graph().insts[curr].opcode();
if prev_op.is_branch()
&& !prev_op.is_terminator()
&& !inst_op.is_terminator()

View File

@@ -102,7 +102,7 @@ impl NewOrExistingInst {
NewOrExistingInst::New(data, ty) => (*ty, *data),
NewOrExistingInst::Existing(inst) => {
let ty = dfg.ctrl_typevar(*inst);
(ty, dfg[*inst].clone())
(ty, dfg.insts[*inst].clone())
}
}
}
@@ -191,8 +191,11 @@ where
union_find: self.eclasses,
value_lists: &self.func.dfg.value_lists,
};
self.gvn_map
.insert((ty, self.func.dfg[inst].clone()), opt_value, &gvn_context);
self.gvn_map.insert(
(ty, self.func.dfg.insts[inst].clone()),
opt_value,
&gvn_context,
);
self.value_to_opt_value[result] = opt_value;
opt_value
}
@@ -335,7 +338,7 @@ impl<'a> EgraphPass<'a> {
trace!(" -> {} = {:?}", value, def);
match def {
ValueDef::Result(i, 0) => {
trace!(" -> {} = {:?}", i, self.func.dfg[i]);
trace!(" -> {} = {:?}", i, self.func.dfg.insts[i]);
}
_ => {}
}

View File

@@ -218,7 +218,7 @@ impl<'a> Elaborator<'a> {
}
ValueDef::Result(inst, _) => {
trace!(" -> value {}: result, computing cost", value);
let inst_data = &self.func.dfg[inst];
let inst_data = &self.func.dfg.insts[inst];
let loop_level = self
.func
.layout
@@ -358,7 +358,7 @@ impl<'a> Elaborator<'a> {
trace!(
" -> result {} of inst {:?}",
result_idx,
self.func.dfg[inst]
self.func.dfg.insts[inst]
);
// We're going to need to use this instruction
@@ -600,7 +600,7 @@ impl<'a> Elaborator<'a> {
// elaboration for args of *any* branch must be inserted
// before the *first* branch, because the branch group
// must remain contiguous at the end of the block.
if self.func.dfg[inst].opcode().is_branch() && first_branch == None {
if self.func.dfg.insts[inst].opcode().is_branch() && first_branch == None {
first_branch = Some(inst);
}

View File

@@ -40,7 +40,7 @@ fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool
/// its value is unused?
#[inline(always)]
pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
let data = &func.dfg[inst];
let data = &func.dfg.insts[inst];
let opcode = data.opcode();
trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
}
@@ -51,7 +51,7 @@ pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
/// - Actual pure nodes (arithmetic, etc)
/// - Loads with the `readonly` flag set
pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
let is_readonly_load = match func.dfg[inst] {
let is_readonly_load = match func.dfg.insts[inst] {
InstructionData::Load {
opcode: Opcode::Load,
flags,
@@ -69,7 +69,7 @@ pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
// are, we can always trivially eliminate them with no effect.)
let has_one_result = func.dfg.inst_results(inst).len() == 1;
let op = func.dfg[inst].opcode();
let op = func.dfg.insts[inst].opcode();
has_one_result && (is_readonly_load || (!op.can_load() && !trivially_has_side_effects(op)))
}
@@ -77,14 +77,14 @@ pub fn is_pure_for_egraph(func: &Function, inst: Inst) -> bool {
/// Does the given instruction have any side-effect as per [has_side_effect], or else is a load,
/// but not the get_pinned_reg opcode?
pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
let op = func.dfg[inst].opcode();
let op = func.dfg.insts[inst].opcode();
op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
}
/// Is the given instruction a constant value (`iconst`, `fconst`) that can be
/// represented in 64 bits?
pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
let data = &func.dfg[inst];
let data = &func.dfg.insts[inst];
if data.opcode() == Opcode::Null {
return Some(0);
}
@@ -98,7 +98,7 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
/// Get the address, offset, and access type from the given instruction, if any.
pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offset32, Type)> {
let data = &func.dfg[inst];
let data = &func.dfg.insts[inst];
match data {
InstructionData::Load { arg, offset, .. } => {
let ty = func.dfg.value_type(func.dfg.inst_results(inst)[0]);
@@ -122,7 +122,7 @@ pub fn inst_addr_offset_type(func: &Function, inst: Inst) -> Option<(Value, Offs
/// Get the store data, if any, from an instruction.
pub fn inst_store_data(func: &Function, inst: Inst) -> Option<Value> {
let data = &func.dfg[inst];
let data = &func.dfg.insts[inst];
match data {
InstructionData::Store { args, .. } | InstructionData::StoreNoOffset { args, .. } => {
Some(args[0])
@@ -157,14 +157,14 @@ pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
mut visit: F,
) {
for inst in f.layout.block_likely_branches(block) {
if f.dfg[inst].opcode().is_branch() {
if f.dfg.insts[inst].opcode().is_branch() {
visit_branch_targets(f, inst, &mut visit);
}
}
}
fn visit_branch_targets<F: FnMut(Inst, Block, bool)>(f: &Function, inst: Inst, visit: &mut F) {
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
match f.dfg.insts[inst].analyze_branch(&f.dfg.value_lists) {
BranchInfo::NotABranch => {}
BranchInfo::SingleDest(dest, _) => {
visit(inst, dest, false);

View File

@@ -201,7 +201,7 @@ impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> {
fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'f mut DataFlowGraph) {
// Splat the new instruction on top of the old one.
self.dfg[self.inst] = data;
self.dfg.insts[self.inst] = data;
if !self.dfg.has_results(self.inst) {
// The old result values were either detached or non-existent.

View File

@@ -23,6 +23,27 @@ use alloc::collections::BTreeMap;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
/// Storage for instructions within the DFG.
#[derive(Clone, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Insts(PrimaryMap<Inst, InstructionData>);
/// Allow immutable access to instructions via indexing.
impl Index<Inst> for Insts {
type Output = InstructionData;
fn index(&self, inst: Inst) -> &InstructionData {
self.0.index(inst)
}
}
/// Allow mutable access to instructions via indexing.
impl IndexMut<Inst> for Insts {
fn index_mut(&mut self, inst: Inst) -> &mut InstructionData {
self.0.index_mut(inst)
}
}
/// A data flow graph defines all instructions and basic blocks in a function as well as
/// the data flow dependencies between them. The DFG also tracks values which can be either
/// instruction results or block parameters.
@@ -36,7 +57,7 @@ pub struct DataFlowGraph {
/// Data about all of the instructions in the function, including opcodes and operands.
/// The instructions in this map are not in program order. That is tracked by `Layout`, along
/// with the block containing each instruction.
insts: PrimaryMap<Inst, InstructionData>,
pub insts: Insts,
/// List of result values for each instruction.
///
@@ -89,7 +110,7 @@ impl DataFlowGraph {
/// Create a new empty `DataFlowGraph`.
pub fn new() -> Self {
Self {
insts: PrimaryMap::new(),
insts: Insts(PrimaryMap::new()),
results: SecondaryMap::new(),
blocks: PrimaryMap::new(),
dynamic_types: DynamicTypes::new(),
@@ -106,7 +127,7 @@ impl DataFlowGraph {
/// Clear everything.
pub fn clear(&mut self) {
self.insts.clear();
self.insts.0.clear();
self.results.clear();
self.blocks.clear();
self.dynamic_types.clear();
@@ -125,12 +146,12 @@ impl DataFlowGraph {
///
/// This is intended for use with `SecondaryMap::with_capacity`.
pub fn num_insts(&self) -> usize {
self.insts.len()
self.insts.0.len()
}
/// Returns `true` if the given instruction reference is valid.
pub fn inst_is_valid(&self, inst: Inst) -> bool {
self.insts.is_valid(inst)
self.insts.0.is_valid(inst)
}
/// Get the total number of basic blocks created in this function, whether they are
@@ -619,7 +640,7 @@ impl DataFlowGraph {
pub fn make_inst(&mut self, data: InstructionData) -> Inst {
let n = self.num_insts() + 1;
self.results.resize(n);
self.insts.push(data)
self.insts.0.push(data)
}
/// Declares a dynamic vector type
@@ -656,7 +677,7 @@ impl DataFlowGraph {
/// Get the fixed value arguments on `inst` as a slice.
pub fn inst_fixed_args(&self, inst: Inst) -> &[Value] {
let num_fixed_args = self[inst]
let num_fixed_args = self.insts[inst]
.opcode()
.constraints()
.num_fixed_value_arguments();
@@ -665,7 +686,7 @@ impl DataFlowGraph {
/// Get the fixed value arguments on `inst` as a mutable slice.
pub fn inst_fixed_args_mut(&mut self, inst: Inst) -> &mut [Value] {
let num_fixed_args = self[inst]
let num_fixed_args = self.insts[inst]
.opcode()
.constraints()
.num_fixed_value_arguments();
@@ -674,7 +695,7 @@ impl DataFlowGraph {
/// Get the variable value arguments on `inst` as a slice.
pub fn inst_variable_args(&self, inst: Inst) -> &[Value] {
let num_fixed_args = self[inst]
let num_fixed_args = self.insts[inst]
.opcode()
.constraints()
.num_fixed_value_arguments();
@@ -683,7 +704,7 @@ impl DataFlowGraph {
/// Get the variable value arguments on `inst` as a mutable slice.
pub fn inst_variable_args_mut(&mut self, inst: Inst) -> &mut [Value] {
let num_fixed_args = self[inst]
let num_fixed_args = self.insts[inst]
.opcode()
.constraints()
.num_fixed_value_arguments();
@@ -849,18 +870,17 @@ impl DataFlowGraph {
///
/// Panics if the instruction doesn't support arguments.
pub fn append_inst_arg(&mut self, inst: Inst, new_arg: Value) {
let mut branch_values = self.insts[inst]
.take_value_list()
.expect("the instruction doesn't have value arguments");
branch_values.push(new_arg, &mut self.value_lists);
self.insts[inst].put_value_list(branch_values)
self.insts[inst]
.value_list_mut()
.expect("the instruction doesn't have value arguments")
.push(new_arg, &mut self.value_lists);
}
/// Clone an instruction, attaching new result `Value`s and
/// returning them.
pub fn clone_inst(&mut self, inst: Inst) -> Inst {
// First, add a clone of the InstructionData.
let inst_data = self[inst].clone();
let inst_data = self.insts[inst].clone();
let new_inst = self.make_inst(inst_data);
// Get the controlling type variable.
let ctrl_typevar = self.ctrl_typevar(inst);
@@ -947,7 +967,7 @@ impl DataFlowGraph {
/// Get the controlling type variable, or `INVALID` if `inst` isn't polymorphic.
pub fn ctrl_typevar(&self, inst: Inst) -> Type {
let constraints = self[inst].opcode().constraints();
let constraints = self.insts[inst].opcode().constraints();
if !constraints.is_polymorphic() {
types::INVALID
@@ -955,12 +975,12 @@ impl DataFlowGraph {
// Not all instruction formats have a designated operand, but in that case
// `requires_typevar_operand()` should never be true.
self.value_type(
self[inst]
self.insts[inst]
.typevar_operand(&self.value_lists)
.unwrap_or_else(|| {
panic!(
"Instruction format for {:?} doesn't have a designated operand",
self[inst]
self.insts[inst]
)
}),
)
@@ -970,22 +990,6 @@ impl DataFlowGraph {
}
}
/// Allow immutable access to instructions via indexing.
impl Index<Inst> for DataFlowGraph {
type Output = InstructionData;
fn index(&self, inst: Inst) -> &InstructionData {
&self.insts[inst]
}
}
/// Allow mutable access to instructions via indexing.
impl IndexMut<Inst> for DataFlowGraph {
fn index_mut(&mut self, inst: Inst) -> &mut InstructionData {
&mut self.insts[inst]
}
}
/// basic blocks.
impl DataFlowGraph {
/// Create a new basic block.
@@ -1187,9 +1191,9 @@ impl<'a> fmt::Display for DisplayInst<'a> {
let typevar = dfg.ctrl_typevar(inst);
if typevar.is_invalid() {
write!(f, "{}", dfg[inst].opcode())?;
write!(f, "{}", dfg.insts[inst].opcode())?;
} else {
write!(f, "{}.{}", dfg[inst].opcode(), typevar)?;
write!(f, "{}.{}", dfg.insts[inst].opcode(), typevar)?;
}
write_operands(f, dfg, inst)
}
@@ -1376,7 +1380,7 @@ mod tests {
// Immutable reference resolution.
{
let immdfg = &dfg;
let ins = &immdfg[inst];
let ins = &immdfg.insts[inst];
assert_eq!(ins.opcode(), Opcode::Iconst);
}

View File

@@ -282,7 +282,7 @@ impl FunctionStencil {
///
/// Note that this method ignores multi-destination branches like `br_table`.
pub fn change_branch_destination(&mut self, inst: Inst, new_dest: Block) {
match self.dfg[inst].branch_destination_mut() {
match self.dfg.insts[inst].branch_destination_mut() {
None => (),
Some(inst_dest) => *inst_dest = new_dest,
}
@@ -309,7 +309,7 @@ impl FunctionStencil {
});
if default_dest == Some(old_dest) {
match &mut self.dfg[inst] {
match &mut self.dfg.insts[inst] {
InstructionData::BranchTable { destination, .. } => {
*destination = new_dest;
}
@@ -333,13 +333,13 @@ impl FunctionStencil {
let inst_iter = self.layout.block_insts(block);
// Ignore all instructions prior to the first branch.
let mut inst_iter = inst_iter.skip_while(|&inst| !dfg[inst].opcode().is_branch());
let mut inst_iter = inst_iter.skip_while(|&inst| !dfg.insts[inst].opcode().is_branch());
// A conditional branch is permitted in a basic block only when followed
// by a terminal jump instruction.
if let Some(_branch) = inst_iter.next() {
if let Some(next) = inst_iter.next() {
match dfg[next].opcode() {
match dfg.insts[next].opcode() {
Opcode::Jump => (),
_ => return Err((next, "post-branch instruction not jump")),
}
@@ -377,7 +377,7 @@ impl FunctionStencil {
.zip(self.dfg.inst_results(src))
.all(|(a, b)| self.dfg.value_type(*a) == self.dfg.value_type(*b)));
self.dfg[dst] = self.dfg[src];
self.dfg.insts[dst] = self.dfg.insts[src];
self.layout.remove_inst(src);
}

View File

@@ -600,7 +600,7 @@ impl Layout {
// If two, the former is conditional and the latter is unconditional.
let last = self.last_inst(block)?;
if let Some(prev) = self.prev_inst(last) {
if dfg[prev].opcode().is_branch() {
if dfg.insts[prev].opcode().is_branch() {
return Some(prev);
}
}

View File

@@ -53,7 +53,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
while let Some(_block) = pos.next_block() {
let mut prev_pos = pos.position();
while let Some(inst) = pos.next_inst() {
match pos.func.dfg[inst] {
match pos.func.dfg.insts[inst] {
// control flow
InstructionData::CondTrap {
opcode:

View File

@@ -153,11 +153,11 @@ fn is_unsafe_load(inst_data: &InstructionData) -> bool {
/// Test whether the given instruction is loop-invariant.
fn is_loop_invariant(inst: Inst, dfg: &DataFlowGraph, loop_values: &FxHashSet<Value>) -> bool {
if trivially_unsafe_for_licm(dfg[inst].opcode()) {
if trivially_unsafe_for_licm(dfg.insts[inst].opcode()) {
return false;
}
if is_unsafe_load(&dfg[inst]) {
if is_unsafe_load(&dfg.insts[inst]) {
return false;
}

View File

@@ -251,7 +251,7 @@ impl BlockLoweringOrder {
block_succ_range[block] = (block_succ_start, block_succ_end);
for inst in f.layout.block_likely_branches(block) {
if f.dfg[inst].opcode() == Opcode::Return {
if f.dfg.insts[inst].opcode() == Opcode::Return {
// Implicit output edge for any return.
block_out_count[block] += 1;
}
@@ -279,7 +279,7 @@ impl BlockLoweringOrder {
for inst in f.layout.block_likely_branches(block) {
// If the block has a branch with any "fixed args"
// (not blockparam args) ...
if f.dfg[inst].opcode().is_branch() && f.dfg.inst_fixed_args(inst).len() > 0 {
if f.dfg.insts[inst].opcode().is_branch() && f.dfg.inst_fixed_args(inst).len() > 0 {
// ... then force a minimum successor count of
// two, so the below algorithm cannot put
// edge-moves on the end of the block.

View File

@@ -221,7 +221,7 @@ macro_rules! isle_lower_prelude_methods {
#[inline]
fn inst_data(&mut self, inst: Inst) -> InstructionData {
self.lower_ctx.dfg()[inst]
self.lower_ctx.dfg().insts[inst]
}
#[inline]

View File

@@ -368,7 +368,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
"bb {} inst {} ({:?}): result {} regs {:?}",
bb,
inst,
f.dfg[inst],
f.dfg.insts[inst],
result,
regs,
);
@@ -705,7 +705,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
// then reverse these and append to the VCode at the end of
// each IR instruction.
for inst in self.f.layout.block_insts(block).rev() {
let data = &self.f.dfg[inst];
let data = &self.f.dfg.insts[inst];
let has_side_effect = has_lowering_side_effect(self.f, inst);
// If inst has been sunk to another location, skip it.
if self.is_inst_sunk(inst) {
@@ -736,14 +736,14 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
// Skip lowering branches; these are handled separately
// (see `lower_clif_branches()` below).
if self.f.dfg[inst].opcode().is_branch() {
if self.f.dfg.insts[inst].opcode().is_branch() {
continue;
}
// Normal instruction: codegen if the instruction is side-effecting
// or any of its outputs its used.
if has_side_effect || value_needed {
trace!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
trace!("lowering: inst {}: {:?}", inst, self.f.dfg.insts[inst]);
let temp_regs = backend.lower(self, inst).unwrap_or_else(|| {
let ty = if self.num_outputs(inst) > 0 {
Some(self.output_ty(inst, 0))
@@ -975,7 +975,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
if last_inst != Some(inst) {
branches.push(inst);
} else {
debug_assert!(self.f.dfg[inst].opcode() == Opcode::BrTable);
debug_assert!(self.f.dfg.insts[inst].opcode() == Opcode::BrTable);
debug_assert!(branches.len() == 1);
}
last_inst = Some(inst);
@@ -1104,7 +1104,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
impl<'func, I: VCodeInst> Lower<'func, I> {
/// Get the instdata for a given IR instruction.
pub fn data(&self, ir_inst: Inst) -> &InstructionData {
&self.f.dfg[ir_inst]
&self.f.dfg.insts[ir_inst]
}
/// Likewise, but starting with a GlobalValue identifier.
@@ -1129,7 +1129,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
/// Returns the memory flags of a given memory access.
pub fn memflags(&self, ir_inst: Inst) -> Option<MemFlags> {
match &self.f.dfg[ir_inst] {
match &self.f.dfg.insts[ir_inst] {
&InstructionData::AtomicCas { flags, .. } => Some(flags),
&InstructionData::AtomicRmw { flags, .. } => Some(flags),
&InstructionData::Load { flags, .. }

View File

@@ -30,7 +30,7 @@ pub fn do_nan_canonicalization(func: &mut Function) {
/// arithmetic operation. This ignores operations like `fneg`, `fabs`, or
/// `fcopysign` that only operate on the sign bit of a floating point value.
fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
match pos.func.dfg[inst] {
match pos.func.dfg.insts[inst] {
InstructionData::Unary { opcode, .. } => {
opcode == Opcode::Ceil
|| opcode == Opcode::Floor

View File

@@ -76,7 +76,7 @@ where
ValueDef::Result(inst, _) if ctx.ctx.func.dfg.inst_results(inst).len() == 1 => {
let ty = ctx.ctx.func.dfg.value_type(value);
trace!(" -> value of type {}", ty);
return Some((ty, ctx.ctx.func.dfg[inst].clone()));
return Some((ty, ctx.ctx.func.dfg.insts[inst].clone()));
}
_ => {}
}

View File

@@ -232,7 +232,7 @@ pub fn do_remove_constant_phis(func: &mut Function, domtree: &mut DominatorTree)
let mut summary = BlockSummary::new(&bump, formals);
for inst in func.layout.block_insts(b) {
let idetails = &func.dfg[inst];
let idetails = &func.dfg.insts[inst];
// Note that multi-dest transfers (i.e., branch tables) don't
// carry parameters in our IR, so we only have to care about
// `SingleDest` here.
@@ -378,9 +378,9 @@ pub fn do_remove_constant_phis(func: &mut Function, domtree: &mut DominatorTree)
continue;
}
let old_actuals = func.dfg[edge.inst].take_value_list().unwrap();
let old_actuals = func.dfg.insts[edge.inst].value_list().unwrap();
let num_old_actuals = old_actuals.len(&func.dfg.value_lists);
let num_fixed_actuals = func.dfg[edge.inst]
let num_fixed_actuals = func.dfg.insts[edge.inst]
.opcode()
.constraints()
.num_fixed_value_arguments();
@@ -412,7 +412,7 @@ pub fn do_remove_constant_phis(func: &mut Function, domtree: &mut DominatorTree)
new_actuals.push(actual_i, &mut func.dfg.value_lists);
}
}
func.dfg[edge.inst].put_value_list(new_actuals);
*func.dfg.insts[edge.inst].value_list_mut().unwrap() = new_actuals;
}
}

View File

@@ -96,7 +96,7 @@ pub fn do_simple_gvn(func: &mut Function, domtree: &mut DominatorTree) {
let func = Ref::map(pos.borrow(), |pos| &pos.func);
let opcode = func.dfg[inst].opcode();
let opcode = func.dfg.insts[inst].opcode();
if opcode.is_branch() && !opcode.is_terminator() {
scope_stack.push(func.layout.next_inst(inst).unwrap());
@@ -108,13 +108,13 @@ pub fn do_simple_gvn(func: &mut Function, domtree: &mut DominatorTree) {
}
// These are split up to separate concerns.
if is_load_and_not_readonly(&func.dfg[inst]) {
if is_load_and_not_readonly(&func.dfg.insts[inst]) {
continue;
}
let ctrl_typevar = func.dfg.ctrl_typevar(inst);
let key = HashKey {
inst: func.dfg[inst],
inst: func.dfg.insts[inst],
ty: ctrl_typevar,
pos: &pos,
};

View File

@@ -142,7 +142,7 @@ fn package_up_divrem_info(
/// Examine `inst` to see if it is a div or rem by a constant, and if so return the operands,
/// signedness, operation size and div-vs-rem-ness in a handy bundle.
fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option<DivRemByConstInfo> {
if let InstructionData::BinaryImm64 { opcode, arg, imm } = dfg[inst] {
if let InstructionData::BinaryImm64 { opcode, arg, imm } = dfg.insts[inst] {
let (is_signed, is_rem) = match opcode {
Opcode::UdivImm => (false, false),
Opcode::UremImm => (false, true),
@@ -478,7 +478,7 @@ enum BranchOrderKind {
/// layout-wise. The unconditional jump can then become a fallthrough.
fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, block: Block, inst: Inst) {
let (term_inst, term_inst_args, term_dest, cond_inst, cond_inst_args, cond_dest, kind) =
match pos.func.dfg[inst] {
match pos.func.dfg.insts[inst] {
InstructionData::Jump {
opcode: Opcode::Jump,
destination,
@@ -500,7 +500,7 @@ fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, block: Block,
return;
};
let prev_inst_data = &pos.func.dfg[prev_inst];
let prev_inst_data = &pos.func.dfg.insts[prev_inst];
if let Some(prev_dest) = prev_inst_data.branch_destination() {
if prev_dest != next_block {
@@ -609,7 +609,7 @@ mod simplify {
if let InstructionData::UnaryImm {
opcode: Opcode::Iconst,
imm,
} = dfg[candidate_inst]
} = dfg.insts[candidate_inst]
{
return Some(imm);
}
@@ -631,7 +631,7 @@ mod simplify {
opcode: Opcode::IshlImm,
arg: prev_arg,
imm: prev_imm,
} = &pos.func.dfg[arg_inst]
} = &pos.func.dfg.insts[arg_inst]
{
if imm != *prev_imm {
return false;
@@ -677,7 +677,7 @@ mod simplify {
/// would likely be expanded back into an instruction on smaller types with the same initial
/// opcode, creating unnecessary churn.
fn simplify(pos: &mut FuncCursor, inst: Inst, native_word_width: u32) {
match pos.func.dfg[inst] {
match pos.func.dfg.insts[inst] {
InstructionData::Binary { opcode, args } => {
if let Some(mut imm) = resolve_imm64_value(&pos.func.dfg, args[1]) {
let new_opcode = match opcode {
@@ -748,7 +748,7 @@ mod simplify {
opcode: prev_opcode,
arg: prev_arg,
imm: prev_imm,
} = &pos.func.dfg[arg_inst]
} = &pos.func.dfg.insts[arg_inst]
{
if opcode == *prev_opcode
&& ty == pos.func.dfg.ctrl_typevar(arg_inst)
@@ -846,7 +846,7 @@ mod simplify {
opcode: br_opcode,
args: ref br_args,
..
} = pos.func.dfg[inst]
} = pos.func.dfg.insts[inst]
{
let first_arg = {
let args = pos.func.dfg.inst_args(inst);
@@ -865,7 +865,7 @@ mod simplify {
arg: cmp_arg,
cond: cmp_cond,
imm: cmp_imm,
} = pos.func.dfg[icmp_inst]
} = pos.func.dfg.insts[icmp_inst]
{
let cmp_imm: i64 = cmp_imm.into();
if cmp_imm != 0 {
@@ -900,7 +900,7 @@ mod simplify {
};
info.args.as_mut_slice(&mut pos.func.dfg.value_lists)[0] = info.cmp_arg;
if let InstructionData::Branch { ref mut opcode, .. } = pos.func.dfg[info.br_inst] {
if let InstructionData::Branch { ref mut opcode, .. } = pos.func.dfg.insts[info.br_inst] {
*opcode = info.new_opcode;
} else {
panic!();

View File

@@ -93,7 +93,7 @@ fn harvest_candidate_lhs(
// Should we keep tracing through the given `val`? Only if it is defined
// by an instruction that we can translate to Souper IR.
let should_trace = |val| match func.dfg.value_def(val) {
ir::ValueDef::Result(inst, 0) => match func.dfg[inst].opcode() {
ir::ValueDef::Result(inst, 0) => match func.dfg.insts[inst].opcode() {
ir::Opcode::Iadd
| ir::Opcode::IaddImm
| ir::Opcode::IrsubImm
@@ -157,7 +157,7 @@ fn harvest_candidate_lhs(
// `iconst`s into souper operands here,
// when they are actually used.
match func.dfg.value_def(arg) {
ir::ValueDef::Result(inst, 0) => match func.dfg[inst] {
ir::ValueDef::Result(inst, 0) => match func.dfg.insts[inst] {
ir::InstructionData::UnaryImm { opcode, imm } => {
debug_assert_eq!(opcode, ir::Opcode::Iconst);
let imm: i64 = imm.into();
@@ -179,7 +179,7 @@ fn harvest_candidate_lhs(
}
};
match (func.dfg[inst].opcode(), &func.dfg[inst]) {
match (func.dfg.insts[inst].opcode(), &func.dfg.insts[inst]) {
(ir::Opcode::Iadd, _) => {
let a = arg(allocs, 0);
let b = arg(allocs, 1);

View File

@@ -470,7 +470,7 @@ impl<'a> Verifier<'a> {
inst: Inst,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let is_terminator = self.func.dfg[inst].opcode().is_terminator();
let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
if is_terminator && !is_last_inst {
@@ -520,7 +520,7 @@ impl<'a> Verifier<'a> {
inst: Inst,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let inst_data = &self.func.dfg[inst];
let inst_data = &self.func.dfg.insts[inst];
let dfg = &self.func.dfg;
// The instruction format matches the opcode
@@ -580,7 +580,7 @@ impl<'a> Verifier<'a> {
self.verify_inst_result(inst, res, errors)?;
}
match self.func.dfg[inst] {
match self.func.dfg.insts[inst] {
MultiAry { ref args, .. } => {
self.verify_value_list(inst, args, errors)?;
}
@@ -1181,7 +1181,7 @@ impl<'a> Verifier<'a> {
}
fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
let inst_data = &self.func.dfg[inst];
let inst_data = &self.func.dfg.insts[inst];
let constraints = inst_data.opcode().constraints();
let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
@@ -1261,7 +1261,7 @@ impl<'a> Verifier<'a> {
ctrl_type: Type,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let constraints = self.func.dfg[inst].opcode().constraints();
let constraints = self.func.dfg.insts[inst].opcode().constraints();
for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
let arg_type = self.func.dfg.value_type(arg);
@@ -1341,7 +1341,7 @@ impl<'a> Verifier<'a> {
BranchInfo::NotABranch => {}
}
match self.func.dfg[inst].analyze_call(&self.func.dfg.value_lists) {
match self.func.dfg.insts[inst].analyze_call(&self.func.dfg.value_lists) {
CallInfo::Direct(func_ref, _) => {
let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
let arg_types = self.func.dfg.signatures[sig_ref]
@@ -1407,7 +1407,7 @@ impl<'a> Verifier<'a> {
}
fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
if self.func.dfg[inst].opcode().is_return() {
if self.func.dfg.insts[inst].opcode().is_return() {
let args = self.func.dfg.inst_variable_args(inst);
let expected_types = &self.func.signature.returns;
if args.len() != expected_types.len() {
@@ -1442,7 +1442,7 @@ impl<'a> Verifier<'a> {
ctrl_type: Type,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
match self.func.dfg[inst] {
match self.func.dfg.insts[inst] {
ir::InstructionData::Unary { opcode, arg } => {
let arg_type = self.func.dfg.value_type(arg);
match opcode {
@@ -1604,7 +1604,7 @@ impl<'a> Verifier<'a> {
inst: Inst,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let inst_data = &self.func.dfg[inst];
let inst_data = &self.func.dfg.insts[inst];
match *inst_data {
ir::InstructionData::Store { flags, .. } => {

View File

@@ -276,7 +276,7 @@ fn decorate_block<FW: FuncWriter>(
// if it can't be trivially inferred.
//
fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
let inst_data = &func.dfg[inst];
let inst_data = &func.dfg.insts[inst];
let constraints = inst_data.opcode().constraints();
if !constraints.is_polymorphic() {
@@ -357,7 +357,7 @@ fn write_instruction(
}
// Then the opcode, possibly with a '.type' suffix.
let opcode = func.dfg[inst].opcode();
let opcode = func.dfg.insts[inst].opcode();
match type_suffix(func, inst) {
Some(suf) => write!(w, "{}.{}", opcode, suf)?,
@@ -378,7 +378,7 @@ fn write_instruction(
pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
let pool = &dfg.value_lists;
use crate::ir::instructions::InstructionData::*;
match dfg[inst] {
match dfg.insts[inst] {
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg),
@@ -487,7 +487,7 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
let mut sep = " ; ";
for &arg in dfg.inst_args(inst) {
if let ValueDef::Result(src, _) = dfg.value_def(arg) {
let imm = match dfg[src] {
let imm = match dfg.insts[src] {
UnaryImm { imm, .. } => imm.to_string(),
UnaryIeee32 { imm, .. } => imm.to_string(),
UnaryIeee64 { imm, .. } => imm.to_string(),