cranelift_codegen/machinst/
pcc.rs

1//! Common helpers for ISA-specific proof-carrying-code implementations.
2
3use crate::ir::pcc::{Fact, FactContext, PccError, PccResult};
4use crate::machinst::{Reg, VCode, VCodeInst, Writable};
5use crate::trace;
6
7pub(crate) fn get_fact_or_default<I: VCodeInst>(vcode: &VCode<I>, reg: Reg, width: u16) -> Fact {
8    trace!(
9        "get_fact_or_default: reg {reg:?} -> {:?}",
10        vcode.vreg_fact(reg.into())
11    );
12    vcode
13        .vreg_fact(reg.into())
14        .cloned()
15        .unwrap_or_else(|| Fact::max_range_for_width(width))
16}
17
18pub(crate) fn has_fact<I: VCodeInst>(vcode: &VCode<I>, reg: Reg) -> bool {
19    vcode.vreg_fact(reg.into()).is_some()
20}
21
22pub(crate) fn fail_if_missing(fact: Option<Fact>) -> PccResult<Fact> {
23    fact.ok_or(PccError::UnsupportedFact)
24}
25
26pub(crate) fn clamp_range(
27    ctx: &FactContext,
28    to_bits: u16,
29    from_bits: u16,
30    fact: Option<Fact>,
31) -> PccResult<Option<Fact>> {
32    let max = if from_bits > 64 {
33        return Ok(None);
34    } else if from_bits == 64 {
35        u64::MAX
36    } else {
37        (1u64 << from_bits) - 1
38    };
39    trace!(
40        "clamp_range: fact {:?} from {} to {}",
41        fact, from_bits, to_bits
42    );
43    Ok(fact
44        .and_then(|f| ctx.uextend(&f, from_bits, to_bits))
45        .or_else(|| {
46            let result = Fact::Range {
47                bit_width: to_bits,
48                min: 0,
49                max,
50            };
51            trace!(" -> clamping to {:?}", result);
52            Some(result)
53        }))
54}
55
56pub(crate) fn check_subsumes(ctx: &FactContext, subsumer: &Fact, subsumee: &Fact) -> PccResult<()> {
57    check_subsumes_optionals(ctx, Some(subsumer), Some(subsumee))
58}
59
60pub(crate) fn check_subsumes_optionals(
61    ctx: &FactContext,
62    subsumer: Option<&Fact>,
63    subsumee: Option<&Fact>,
64) -> PccResult<()> {
65    trace!(
66        "checking if derived fact {:?} subsumes stated fact {:?}",
67        subsumer, subsumee
68    );
69
70    if ctx.subsumes_fact_optionals(subsumer, subsumee) {
71        Ok(())
72    } else {
73        Err(PccError::UnsupportedFact)
74    }
75}
76
77pub(crate) fn check_output<I: VCodeInst, F: FnOnce(&VCode<I>) -> PccResult<Option<Fact>>>(
78    ctx: &FactContext,
79    vcode: &mut VCode<I>,
80    out: Writable<Reg>,
81    ins: &[Reg],
82    f: F,
83) -> PccResult<()> {
84    if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) {
85        let result = f(vcode)?;
86        check_subsumes_optionals(ctx, result.as_ref(), Some(fact))
87    } else if ins.iter().any(|r| {
88        vcode
89            .vreg_fact(r.into())
90            .map(|fact| fact.propagates())
91            .unwrap_or(false)
92    }) {
93        if let Ok(Some(fact)) = f(vcode) {
94            trace!("setting vreg {:?} to {:?}", out, fact);
95            vcode.set_vreg_fact(out.to_reg().into(), fact);
96        }
97        Ok(())
98    } else {
99        Ok(())
100    }
101}
102
103pub(crate) fn check_unop<I: VCodeInst, F: FnOnce(&Fact) -> PccResult<Option<Fact>>>(
104    ctx: &FactContext,
105    vcode: &mut VCode<I>,
106    reg_width: u16,
107    out: Writable<Reg>,
108    ra: Reg,
109    f: F,
110) -> PccResult<()> {
111    check_output(ctx, vcode, out, &[ra], |vcode| {
112        let ra = get_fact_or_default(vcode, ra, reg_width);
113        f(&ra)
114    })
115}
116
117pub(crate) fn check_binop<I: VCodeInst, F: FnOnce(&Fact, &Fact) -> PccResult<Option<Fact>>>(
118    ctx: &FactContext,
119    vcode: &mut VCode<I>,
120    reg_width: u16,
121    out: Writable<Reg>,
122    ra: Reg,
123    rb: Reg,
124    f: F,
125) -> PccResult<()> {
126    check_output(ctx, vcode, out, &[ra, rb], |vcode| {
127        let ra = get_fact_or_default(vcode, ra, reg_width);
128        let rb = get_fact_or_default(vcode, rb, reg_width);
129        f(&ra, &rb)
130    })
131}
132
133pub(crate) fn check_constant<I: VCodeInst>(
134    ctx: &FactContext,
135    vcode: &mut VCode<I>,
136    out: Writable<Reg>,
137    bit_width: u16,
138    value: u64,
139) -> PccResult<()> {
140    let result = Fact::constant(bit_width, value);
141    if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) {
142        check_subsumes(ctx, &result, fact)
143    } else {
144        trace!("setting vreg {:?} to {:?}", out, result);
145        vcode.set_vreg_fact(out.to_reg().into(), result);
146        Ok(())
147    }
148}
149
150/// The operation we're checking against an amode: either
151///
152/// - a *load*, and we need to validate that the field's fact subsumes
153///   the load result's fact, OR
154///
155/// - a *store*, and we need to validate that the stored data's fact
156///   subsumes the field's fact.
157pub(crate) enum LoadOrStore<'a> {
158    Load {
159        result_fact: Option<&'a Fact>,
160        from_bits: u16,
161        to_bits: u16,
162    },
163    Store {
164        stored_fact: Option<&'a Fact>,
165    },
166}