1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! Verify CPU flags values.

use crate::entity::{EntitySet, SecondaryMap};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir;
use crate::ir::instructions::BranchInfo;
use crate::isa;
use crate::packed_option::PackedOption;
use crate::timing;
use crate::verifier::{VerifierErrors, VerifierStepResult};

/// Verify that CPU flags are used correctly.
///
/// The value types `iflags` and `fflags` represent CPU flags which usually live in a
/// special-purpose register, so they can't be used as freely as other value types that can live in
/// any register.
///
/// We verify the following conditions:
///
/// - At most one flags value can be live at a time.
/// - A flags value can not be live across an instruction that clobbers the flags.
///
///
pub fn verify_flags(
    func: &ir::Function,
    cfg: &ControlFlowGraph,
    isa: Option<&dyn isa::TargetIsa>,
    errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
    let _tt = timing::verify_flags();
    let encinfo = if isa.is_none() || isa.unwrap().get_mach_backend().is_some() {
        None
    } else {
        Some(isa.unwrap().encoding_info())
    };
    let mut verifier = FlagsVerifier {
        func,
        cfg,
        encinfo,
        livein: SecondaryMap::new(),
    };
    verifier.check(errors)
}

struct FlagsVerifier<'a> {
    func: &'a ir::Function,
    cfg: &'a ControlFlowGraph,
    encinfo: Option<isa::EncInfo>,

    /// The single live-in flags value (if any) for each block.
    livein: SecondaryMap<ir::Block, PackedOption<ir::Value>>,
}

impl<'a> FlagsVerifier<'a> {
    fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        // List of blocks that need to be processed. blocks may be re-added to this list when we detect
        // that one of their successor blocks needs a live-in flags value.
        let mut worklist = EntitySet::with_capacity(self.func.layout.block_capacity());
        for block in self.func.layout.blocks() {
            worklist.insert(block);
        }

        while let Some(block) = worklist.pop() {
            if let Some(value) = self.visit_block(block, errors)? {
                // The block has live-in flags. Check if the value changed.
                match self.livein[block].expand() {
                    // Revisit any predecessor blocks the first time we see a live-in for `block`.
                    None => {
                        self.livein[block] = value.into();
                        for BlockPredecessor { block: pred, .. } in self.cfg.pred_iter(block) {
                            worklist.insert(pred);
                        }
                    }
                    Some(old) if old != value => {
                        return errors.fatal((
                            block,
                            format!("conflicting live-in CPU flags: {} and {}", old, value),
                        ));
                    }
                    x => assert_eq!(x, Some(value)),
                }
            } else {
                // Existing live-in flags should never be able to disappear.
                assert_eq!(self.livein[block].expand(), None);
            }
        }

        Ok(())
    }

    /// Check flags usage in `block` and return the live-in flags value, if any.
    fn visit_block(
        &self,
        block: ir::Block,
        errors: &mut VerifierErrors,
    ) -> VerifierStepResult<Option<ir::Value>> {
        // The single currently live flags value.
        let mut live_val = None;

        // Visit instructions backwards so we can track liveness accurately.
        for inst in self.func.layout.block_insts(block).rev() {
            // Check if `inst` interferes with existing live flags.
            if let Some(live) = live_val {
                for &res in self.func.dfg.inst_results(inst) {
                    if res == live {
                        // We've reached the def of `live_flags`, so it is no longer live above.
                        live_val = None;
                    } else if self.func.dfg.value_type(res).is_flags() {
                        errors
                            .report((inst, format!("{} clobbers live CPU flags in {}", res, live)));
                        return Err(());
                    }
                }

                // Does the instruction have an encoding that clobbers the CPU flags?
                if self
                    .encinfo
                    .as_ref()
                    .and_then(|ei| ei.operand_constraints(self.func.encodings[inst]))
                    .map_or(false, |c| c.clobbers_flags)
                    && live_val.is_some()
                {
                    errors.report((
                        inst,
                        format!("encoding clobbers live CPU flags in {}", live),
                    ));
                    return Err(());
                }
            }

            // Now look for live ranges of CPU flags that end here.
            for &arg in self.func.dfg.inst_args(inst) {
                if self.func.dfg.value_type(arg).is_flags() {
                    merge(&mut live_val, arg, inst, errors)?;
                }
            }

            // Include live-in flags to successor blocks.
            match self.func.dfg.analyze_branch(inst) {
                BranchInfo::NotABranch => {}
                BranchInfo::SingleDest(dest, _) => {
                    if let Some(val) = self.livein[dest].expand() {
                        merge(&mut live_val, val, inst, errors)?;
                    }
                }
                BranchInfo::Table(jt, dest) => {
                    if let Some(dest) = dest {
                        if let Some(val) = self.livein[dest].expand() {
                            merge(&mut live_val, val, inst, errors)?;
                        }
                    }
                    for dest in self.func.jump_tables[jt].iter() {
                        if let Some(val) = self.livein[*dest].expand() {
                            merge(&mut live_val, val, inst, errors)?;
                        }
                    }
                }
            }
        }

        // Return the required live-in flags value.
        Ok(live_val)
    }
}

// Merge live flags values, or return an error on conflicting values.
fn merge(
    a: &mut Option<ir::Value>,
    b: ir::Value,
    inst: ir::Inst,
    errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
    if let Some(va) = *a {
        if b != va {
            return errors.fatal((
                inst,
                format!("conflicting live CPU flags: {} and {}", va, b),
            ));
        }
    } else {
        *a = Some(b);
    }

    Ok(())
}