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,
42        from_bits,
43        to_bits
44    );
45    Ok(fact
46        .and_then(|f| ctx.uextend(&f, from_bits, to_bits))
47        .or_else(|| {
48            let result = Fact::Range {
49                bit_width: to_bits,
50                min: 0,
51                max,
52            };
53            trace!(" -> clamping to {:?}", result);
54            Some(result)
55        }))
56}
57
58pub(crate) fn check_subsumes(ctx: &FactContext, subsumer: &Fact, subsumee: &Fact) -> PccResult<()> {
59    check_subsumes_optionals(ctx, Some(subsumer), Some(subsumee))
60}
61
62pub(crate) fn check_subsumes_optionals(
63    ctx: &FactContext,
64    subsumer: Option<&Fact>,
65    subsumee: Option<&Fact>,
66) -> PccResult<()> {
67    trace!(
68        "checking if derived fact {:?} subsumes stated fact {:?}",
69        subsumer,
70        subsumee
71    );
72
73    if ctx.subsumes_fact_optionals(subsumer, subsumee) {
74        Ok(())
75    } else {
76        Err(PccError::UnsupportedFact)
77    }
78}
79
80pub(crate) fn check_output<I: VCodeInst, F: FnOnce(&VCode<I>) -> PccResult<Option<Fact>>>(
81    ctx: &FactContext,
82    vcode: &mut VCode<I>,
83    out: Writable<Reg>,
84    ins: &[Reg],
85    f: F,
86) -> PccResult<()> {
87    if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) {
88        let result = f(vcode)?;
89        check_subsumes_optionals(ctx, result.as_ref(), Some(fact))
90    } else if ins.iter().any(|r| {
91        vcode
92            .vreg_fact(r.into())
93            .map(|fact| fact.propagates())
94            .unwrap_or(false)
95    }) {
96        if let Ok(Some(fact)) = f(vcode) {
97            trace!("setting vreg {:?} to {:?}", out, fact);
98            vcode.set_vreg_fact(out.to_reg().into(), fact);
99        }
100        Ok(())
101    } else {
102        Ok(())
103    }
104}
105
106pub(crate) fn check_unop<I: VCodeInst, F: FnOnce(&Fact) -> PccResult<Option<Fact>>>(
107    ctx: &FactContext,
108    vcode: &mut VCode<I>,
109    reg_width: u16,
110    out: Writable<Reg>,
111    ra: Reg,
112    f: F,
113) -> PccResult<()> {
114    check_output(ctx, vcode, out, &[ra], |vcode| {
115        let ra = get_fact_or_default(vcode, ra, reg_width);
116        f(&ra)
117    })
118}
119
120pub(crate) fn check_binop<I: VCodeInst, F: FnOnce(&Fact, &Fact) -> PccResult<Option<Fact>>>(
121    ctx: &FactContext,
122    vcode: &mut VCode<I>,
123    reg_width: u16,
124    out: Writable<Reg>,
125    ra: Reg,
126    rb: Reg,
127    f: F,
128) -> PccResult<()> {
129    check_output(ctx, vcode, out, &[ra, rb], |vcode| {
130        let ra = get_fact_or_default(vcode, ra, reg_width);
131        let rb = get_fact_or_default(vcode, rb, reg_width);
132        f(&ra, &rb)
133    })
134}
135
136pub(crate) fn check_constant<I: VCodeInst>(
137    ctx: &FactContext,
138    vcode: &mut VCode<I>,
139    out: Writable<Reg>,
140    bit_width: u16,
141    value: u64,
142) -> PccResult<()> {
143    let result = Fact::constant(bit_width, value);
144    if let Some(fact) = vcode.vreg_fact(out.to_reg().into()) {
145        check_subsumes(ctx, &result, fact)
146    } else {
147        trace!("setting vreg {:?} to {:?}", out, result);
148        vcode.set_vreg_fact(out.to_reg().into(), result);
149        Ok(())
150    }
151}
152
153/// The operation we're checking against an amode: either
154///
155/// - a *load*, and we need to validate that the field's fact subsumes
156///   the load result's fact, OR
157///
158/// - a *store*, and we need to validate that the stored data's fact
159///   subsumes the field's fact.
160pub(crate) enum LoadOrStore<'a> {
161    Load {
162        result_fact: Option<&'a Fact>,
163        from_bits: u16,
164        to_bits: u16,
165    },
166    Store {
167        stored_fact: Option<&'a Fact>,
168    },
169}