Skip to content

Commit 2f7dbd6

Browse files
authored
PCC: remove proof-carrying code (for now?). (#12800)
In late 2023, we built out an experimental feature called Proof-Carrying Code (PCC), where we attached "facts" to values in the CLIF IR and built verification of these facts after lowering to machine instructions. We also added "memory types" describing layout of memory and a "checked" flag on memory operations such that we could verify that any checked memory operation accessed valid memory (as defined by memory types attached to pointer values via facts). Wasmtime's Cranelift backend then put appropriate memory types and facts in its IR such that all accesses to memory (aspirationally) could be checked, taking the whole mid-end and lowering backend of Cranelift out of the trusted core that enforces SFI. This basically worked, at the time, for static memories; but never for dynamic memories, and then work on the feature lost prioritization (aka I had to work on other things) and I wasn't able to complete it and put it in fuzzing/enable it as a production option. Unfortunately since then it has bit-rotted significantly -- as we add new backend optimizations and instruction lowerings we haven't kept the PCC framework up to date. Inspired by the discussion in #12497 I think it's time to delete it (hopefully just "for now"?) unless/until we can build it again. And when we do that, we should probably get it to the point of validating robust operation on all combinations of memory configurations before merging. (That implies a big experiment branch rather than a bunch of eager PRs in-tree, but so it goes.) I still believe it is possible to build this (and I have ideas on how to do it!) but not right now.
1 parent 83909b0 commit 2f7dbd6

File tree

220 files changed

+633
-7039
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

220 files changed

+633
-7039
lines changed

cranelift/codegen/meta/src/shared/settings.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,6 @@ pub(crate) fn define() -> SettingGroup {
7777
true,
7878
);
7979

80-
settings.add_bool(
81-
"enable_pcc",
82-
"Enable proof-carrying code translation validation.",
83-
r#"
84-
This adds a proof-carrying-code mode. Proof-carrying code (PCC) is a strategy to verify
85-
that the compiler preserves certain properties or invariants in the compiled code.
86-
For example, a frontend that translates WebAssembly to CLIF can embed PCC facts in
87-
the CLIF, and Cranelift will verify that the final machine code satisfies the stated
88-
facts at each intermediate computed value. Loads and stores can be marked as "checked"
89-
and their memory effects can be verified as safe.
90-
"#,
91-
false,
92-
);
93-
9480
// Note that Cranelift doesn't currently need an is_pie flag, because PIE is
9581
// just PIC where symbols can't be preempted, which can be expressed with the
9682
// `colocated` flag on external functions and global values.

cranelift/codegen/src/context.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@ impl Context {
381381
&self.domtree,
382382
&self.loop_analysis,
383383
&mut alias_analysis,
384-
&fisa.flags,
385384
ctrl_plane,
386385
);
387386
pass.run();

cranelift/codegen/src/egraph/mod.rs

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@ use crate::cursor::{Cursor, CursorPosition, FuncCursor};
77
use crate::dominator_tree::DominatorTree;
88
use crate::egraph::elaborate::Elaborator;
99
use crate::inst_predicates::{is_mergeable_for_egraph, is_pure_for_egraph};
10-
use crate::ir::pcc::Fact;
1110
use crate::ir::{
12-
Block, DataFlowGraph, Function, Inst, InstructionData, Opcode, Type, Value, ValueDef,
13-
ValueListPool,
11+
Block, DataFlowGraph, Function, Inst, InstructionData, Type, Value, ValueDef, ValueListPool,
1412
};
1513
use crate::loop_analysis::LoopAnalysis;
1614
use crate::opts::IsleContext;
1715
use crate::opts::generated_code::SkeletonInstSimplification;
1816
use crate::scoped_hash_map::{Entry as ScopedEntry, ScopedHashMap};
19-
use crate::settings::Flags;
2017
use crate::take_and_replace::TakeAndReplace;
2118
use crate::trace;
2219
use alloc::vec::Vec;
@@ -60,8 +57,6 @@ pub struct EgraphPass<'a> {
6057
/// Loop analysis results, used for built-in LICM during
6158
/// elaboration.
6259
loop_analysis: &'a LoopAnalysis,
63-
/// Compiler flags.
64-
flags: &'a Flags,
6560
/// Chaos-mode control-plane so we can test that we still get
6661
/// correct results when our heuristics make bad decisions.
6762
ctrl_plane: &'a mut ControlPlane,
@@ -95,7 +90,6 @@ where
9590
domtree: &'opt DominatorTree,
9691
pub(crate) alias_analysis: &'opt mut AliasAnalysis<'analysis>,
9792
pub(crate) alias_analysis_state: &'opt mut LastStores,
98-
flags: &'opt Flags,
9993
ctrl_plane: &'opt mut ControlPlane,
10094
// Held locally during optimization of one node (recursively):
10195
pub(crate) rewrite_depth: usize,
@@ -170,7 +164,6 @@ where
170164
let result = self.func.dfg.first_result(inst);
171165
self.value_to_opt_value[result] = orig_result;
172166
self.available_block[result] = self.available_block[orig_result];
173-
self.func.dfg.merge_facts(result, orig_result);
174167
}
175168
orig_result
176169
} else {
@@ -194,8 +187,6 @@ where
194187
}
195188
};
196189

197-
self.attach_constant_fact(inst, result, ty);
198-
199190
self.available_block[result] = self.get_available_block(inst);
200191
let opt_value = self.optimize_pure_enode(inst);
201192
log::trace!("optimizing inst {inst} orig result {result} gave {opt_value}");
@@ -387,7 +378,6 @@ where
387378
ctx.eclass_size[union_value] = eclass_size - 1;
388379
ctx.stats.union += 1;
389380
trace!(" -> union: now {}", union_value);
390-
ctx.func.dfg.merge_facts(old_union_value, optimized_value);
391381
ctx.available_block[union_value] =
392382
ctx.merge_availability(old_union_value, optimized_value);
393383
}
@@ -503,7 +493,6 @@ where
503493
);
504494
self.value_to_opt_value[result] = new_result;
505495
self.available_block[result] = self.available_block[new_result];
506-
self.func.dfg.merge_facts(result, new_result);
507496
Some(SkeletonInstSimplification::Remove)
508497
}
509498
// Otherwise, generic side-effecting op -- always keep it, and
@@ -695,23 +684,6 @@ where
695684
// Return the best simplification!
696685
best
697686
}
698-
699-
/// Helper to propagate facts on constant values: if PCC is
700-
/// enabled, then unconditionally add a fact attesting to the
701-
/// Value's concrete value.
702-
fn attach_constant_fact(&mut self, inst: Inst, value: Value, ty: Type) {
703-
if self.flags.enable_pcc() {
704-
if let InstructionData::UnaryImm {
705-
opcode: Opcode::Iconst,
706-
imm,
707-
} = self.func.dfg.insts[inst]
708-
{
709-
let imm: i64 = imm.into();
710-
self.func.dfg.facts[value] =
711-
Some(Fact::constant(ty.bits().try_into().unwrap(), imm as u64));
712-
}
713-
}
714-
}
715687
}
716688

717689
impl<'a> EgraphPass<'a> {
@@ -721,15 +693,13 @@ impl<'a> EgraphPass<'a> {
721693
domtree: &'a DominatorTree,
722694
loop_analysis: &'a LoopAnalysis,
723695
alias_analysis: &'a mut AliasAnalysis<'a>,
724-
flags: &'a Flags,
725696
ctrl_plane: &'a mut ControlPlane,
726697
) -> Self {
727698
Self {
728699
func,
729700
domtree,
730701
loop_analysis,
731702
alias_analysis,
732-
flags,
733703
ctrl_plane,
734704
stats: Stats::default(),
735705
remat_values: FxHashSet::default(),
@@ -897,7 +867,6 @@ impl<'a> EgraphPass<'a> {
897867
domtree: &self.domtree,
898868
alias_analysis: self.alias_analysis,
899869
alias_analysis_state: &mut alias_analysis_state,
900-
flags: self.flags,
901870
ctrl_plane: self.ctrl_plane,
902871
optimized_values: Default::default(),
903872
optimized_insts: Default::default(),

cranelift/codegen/src/ir/dfg.rs

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::ir;
55
use crate::ir::builder::ReplaceBuilder;
66
use crate::ir::dynamic_type::{DynamicTypeData, DynamicTypes};
77
use crate::ir::instructions::{CallInfo, InstructionData};
8-
use crate::ir::pcc::Fact;
98
use crate::ir::user_stack_maps::{UserStackMapEntry, UserStackMapEntryVec};
109
use crate::ir::{
1110
Block, BlockArg, BlockCall, ConstantData, ConstantPool, DynamicType, ExceptionTables,
@@ -144,9 +143,6 @@ pub struct DataFlowGraph {
144143
/// Primary value table with entries for all values.
145144
values: PrimaryMap<Value, ValueDataPacked>,
146145

147-
/// Facts: proof-carrying-code assertions about values.
148-
pub facts: SecondaryMap<Value, Option<Fact>>,
149-
150146
/// Function signature table. These signatures are referenced by indirect call instructions as
151147
/// well as the external function references.
152148
pub signatures: PrimaryMap<SigRef, Signature>,
@@ -181,7 +177,6 @@ impl DataFlowGraph {
181177
dynamic_types: DynamicTypes::new(),
182178
value_lists: ValueListPool::new(),
183179
values: PrimaryMap::new(),
184-
facts: SecondaryMap::new(),
185180
signatures: PrimaryMap::new(),
186181
ext_funcs: PrimaryMap::new(),
187182
values_labels: None,
@@ -207,7 +202,6 @@ impl DataFlowGraph {
207202
self.constants.clear();
208203
self.immediates.clear();
209204
self.jump_tables.clear();
210-
self.facts.clear();
211205
}
212206

213207
/// Get the total number of instructions created in this function, whether they are currently
@@ -489,21 +483,6 @@ impl DataFlowGraph {
489483
// removed), and unions (but the egraph pass ensures there are no
490484
// aliases before creating unions).
491485

492-
// Merge `facts` from any alias onto the aliased value. Note that if
493-
// there was a chain of aliases, at this point every alias that was in
494-
// the chain points to the same final value, so their facts will all be
495-
// merged together.
496-
for value in self.facts.keys() {
497-
if let ValueData::Alias { original, .. } = self.values[value].into() {
498-
if let Some(new_fact) = self.facts[value].take() {
499-
match &mut self.facts[original] {
500-
Some(old_fact) => *old_fact = Fact::intersect(old_fact, &new_fact),
501-
old_fact => *old_fact = Some(new_fact),
502-
}
503-
}
504-
}
505-
}
506-
507486
// - `signatures` and `ext_funcs` have no values.
508487

509488
if let Some(values_labels) = &mut self.values_labels {
@@ -1075,13 +1054,7 @@ impl DataFlowGraph {
10751054
// Get the controlling type variable.
10761055
let ctrl_typevar = self.ctrl_typevar(inst);
10771056
// Create new result values.
1078-
let num_results = self.make_inst_results(new_inst, ctrl_typevar);
1079-
// Copy over PCC facts, if any.
1080-
for i in 0..num_results {
1081-
let old_result = self.inst_results(inst)[i];
1082-
let new_result = self.inst_results(new_inst)[i];
1083-
self.facts[new_result] = self.facts[old_result].clone();
1084-
}
1057+
self.make_inst_results(new_inst, ctrl_typevar);
10851058
new_inst
10861059
}
10871060

@@ -1416,38 +1389,6 @@ impl DataFlowGraph {
14161389
pub fn detach_inst_results(&mut self, inst: Inst) {
14171390
self.results[inst].clear(&mut self.value_lists);
14181391
}
1419-
1420-
/// Merge the facts for two values. If both values have facts and
1421-
/// they differ, both values get a special "conflict" fact that is
1422-
/// never satisfied.
1423-
pub fn merge_facts(&mut self, a: Value, b: Value) {
1424-
let a = self.resolve_aliases(a);
1425-
let b = self.resolve_aliases(b);
1426-
match (&self.facts[a], &self.facts[b]) {
1427-
(Some(a), Some(b)) if a == b => { /* nothing */ }
1428-
(None, None) => { /* nothing */ }
1429-
(Some(a), None) => {
1430-
self.facts[b] = Some(a.clone());
1431-
}
1432-
(None, Some(b)) => {
1433-
self.facts[a] = Some(b.clone());
1434-
}
1435-
(Some(a_fact), Some(b_fact)) => {
1436-
assert_eq!(self.value_type(a), self.value_type(b));
1437-
let merged = Fact::intersect(a_fact, b_fact);
1438-
crate::trace!(
1439-
"facts merge on {} and {}: {:?}, {:?} -> {:?}",
1440-
a,
1441-
b,
1442-
a_fact,
1443-
b_fact,
1444-
merged,
1445-
);
1446-
self.facts[a] = Some(merged.clone());
1447-
self.facts[b] = Some(merged);
1448-
}
1449-
}
1450-
}
14511392
}
14521393

14531394
/// Contents of a basic block.

cranelift/codegen/src/ir/entities.rs

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -187,25 +187,6 @@ impl GlobalValue {
187187
}
188188
}
189189

190-
/// An opaque reference to a memory type.
191-
///
192-
/// A `MemoryType` is a descriptor of a struct layout in memory, with
193-
/// types and proof-carrying-code facts optionally attached to the
194-
/// fields.
195-
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
196-
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
197-
pub struct MemoryType(u32);
198-
entity_impl!(MemoryType, "mt");
199-
200-
impl MemoryType {
201-
/// Create a new memory type reference from its number.
202-
///
203-
/// This method is for use by the parser.
204-
pub fn with_number(n: u32) -> Option<Self> {
205-
if n < u32::MAX { Some(Self(n)) } else { None }
206-
}
207-
}
208-
209190
/// An opaque reference to a constant.
210191
///
211192
/// You can store [`ConstantData`](super::ConstantData) in a
@@ -400,8 +381,6 @@ pub enum AnyEntity {
400381
DynamicType(DynamicType),
401382
/// A Global value.
402383
GlobalValue(GlobalValue),
403-
/// A memory type.
404-
MemoryType(MemoryType),
405384
/// A jump table.
406385
JumpTable(JumpTable),
407386
/// A constant.
@@ -427,7 +406,6 @@ impl fmt::Display for AnyEntity {
427406
Self::DynamicStackSlot(r) => r.fmt(f),
428407
Self::DynamicType(r) => r.fmt(f),
429408
Self::GlobalValue(r) => r.fmt(f),
430-
Self::MemoryType(r) => r.fmt(f),
431409
Self::JumpTable(r) => r.fmt(f),
432410
Self::Constant(r) => r.fmt(f),
433411
Self::FuncRef(r) => r.fmt(f),
@@ -486,12 +464,6 @@ impl From<GlobalValue> for AnyEntity {
486464
}
487465
}
488466

489-
impl From<MemoryType> for AnyEntity {
490-
fn from(r: MemoryType) -> Self {
491-
Self::MemoryType(r)
492-
}
493-
}
494-
495467
impl From<JumpTable> for AnyEntity {
496468
fn from(r: JumpTable) -> Self {
497469
Self::JumpTable(r)

cranelift/codegen/src/ir/function.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use crate::ir::DebugTags;
99
use crate::ir::{
1010
self, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, DynamicStackSlots,
1111
DynamicType, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Inst, JumpTable,
12-
JumpTableData, Layout, MemoryType, MemoryTypeData, SigRef, Signature, SourceLocs, StackSlot,
13-
StackSlotData, StackSlots, Type, pcc::Fact,
12+
JumpTableData, Layout, SigRef, Signature, SourceLocs, StackSlot, StackSlotData, StackSlots,
13+
Type,
1414
};
1515
use crate::isa::CallConv;
1616
use crate::write::{write_function, write_function_spec};
@@ -173,12 +173,6 @@ pub struct FunctionStencil {
173173
/// Global values referenced.
174174
pub global_values: PrimaryMap<ir::GlobalValue, ir::GlobalValueData>,
175175

176-
/// Global value proof-carrying-code facts.
177-
pub global_value_facts: SecondaryMap<ir::GlobalValue, Option<Fact>>,
178-
179-
/// Memory types for proof-carrying code.
180-
pub memory_types: PrimaryMap<ir::MemoryType, ir::MemoryTypeData>,
181-
182176
/// Data flow graph containing the primary definition of all instructions, blocks and values.
183177
pub dfg: DataFlowGraph,
184178

@@ -221,8 +215,6 @@ impl FunctionStencil {
221215
self.sized_stack_slots.clear();
222216
self.dynamic_stack_slots.clear();
223217
self.global_values.clear();
224-
self.global_value_facts.clear();
225-
self.memory_types.clear();
226218
self.dfg.clear();
227219
self.layout.clear();
228220
self.srclocs.clear();
@@ -257,11 +249,6 @@ impl FunctionStencil {
257249
self.global_values.push(data)
258250
}
259251

260-
/// Declares a memory type for use by the function.
261-
pub fn create_memory_type(&mut self, data: MemoryTypeData) -> MemoryType {
262-
self.memory_types.push(data)
263-
}
264-
265252
/// Find the global dyn_scale value associated with given DynamicType.
266253
pub fn get_dyn_scale(&self, ty: DynamicType) -> GlobalValue {
267254
self.dfg.dynamic_types.get(ty).unwrap().dynamic_scale
@@ -420,8 +407,6 @@ impl Function {
420407
sized_stack_slots: StackSlots::new(),
421408
dynamic_stack_slots: DynamicStackSlots::new(),
422409
global_values: PrimaryMap::new(),
423-
global_value_facts: SecondaryMap::new(),
424-
memory_types: PrimaryMap::new(),
425410
dfg: DataFlowGraph::new(),
426411
layout: Layout::new(),
427412
srclocs: SecondaryMap::new(),

0 commit comments

Comments
 (0)