llvm_transforms/
constant_fold_pass.rs1use crate::const_prop::{rpo, subst_kind};
8use crate::constant_fold::try_fold;
9use crate::pass::FunctionPass;
10use llvm_ir::{Context, Function, InstrId, ValueRef};
11use std::collections::HashMap;
12
13pub struct ConstantFold;
15
16impl FunctionPass for ConstantFold {
17 fn name(&self) -> &'static str {
18 "constant-fold"
19 }
20
21 fn run_on_function(&mut self, ctx: &mut Context, func: &mut Function) -> bool {
22 if func.blocks.is_empty() {
23 return false;
24 }
25
26 let mut subst: HashMap<InstrId, ValueRef> = HashMap::new();
28
29 for bi in rpo(func) {
30 let body = func.blocks[bi].body.clone();
31 for iid in body {
32 if !subst.is_empty() {
33 let new_kind = subst_kind(func.instr(iid).kind.clone(), &subst);
34 func.instr_mut(iid).kind = new_kind;
35 }
36 let kind = func.instr(iid).kind.clone();
37 if let Some(cid) = try_fold(ctx, &kind) {
38 subst.insert(iid, ValueRef::Constant(cid));
39 }
40 }
41 if let Some(tid) = func.blocks[bi].terminator {
42 if !subst.is_empty() {
43 let new_kind = subst_kind(func.instr(tid).kind.clone(), &subst);
44 func.instr_mut(tid).kind = new_kind;
45 }
46 }
47 }
48
49 if subst.is_empty() {
50 return false;
51 }
52
53 for bb in &mut func.blocks {
54 bb.body.retain(|id| !subst.contains_key(id));
55 }
56 true
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use llvm_ir::{Builder, Context, InstrKind, Linkage, Module, ValueRef};
64
65 fn make_const_add_fn() -> (Context, Module) {
66 let mut ctx = Context::new();
67 let mut module = Module::new("const_add");
68 let mut b = Builder::new(&mut ctx, &mut module);
69 b.add_function("f", b.ctx.i32_ty, vec![], vec![], false, Linkage::External);
70 let entry = b.add_block("entry");
71 b.position_at_end(entry);
72 let c2 = b.const_int(b.ctx.i32_ty, 2);
73 let sum = b.build_add("sum", c2, c2);
74 b.build_ret(sum);
75 (ctx, module)
76 }
77
78 fn make_non_const_add_fn() -> (Context, Module) {
79 let mut ctx = Context::new();
80 let mut module = Module::new("non_const_add");
81 let mut b = Builder::new(&mut ctx, &mut module);
82 b.add_function(
83 "f",
84 b.ctx.i32_ty,
85 vec![b.ctx.i32_ty],
86 vec!["x".into()],
87 false,
88 Linkage::External,
89 );
90 let entry = b.add_block("entry");
91 b.position_at_end(entry);
92 let x = b.get_arg(0);
93 let c2 = b.const_int(b.ctx.i32_ty, 2);
94 let sum = b.build_add("sum", x, c2);
95 b.build_ret(sum);
96 (ctx, module)
97 }
98
99 #[test]
100 fn folds_add_2_plus_2() {
101 let (mut ctx, mut module) = make_const_add_fn();
102 let mut pass = ConstantFold;
103 let changed = pass.run_on_function(&mut ctx, &mut module.functions[0]);
104 assert!(changed);
105 assert_eq!(module.functions[0].blocks[0].body.len(), 0);
106 let func = &module.functions[0];
107 let tid = func.blocks[0].terminator.expect("terminator");
108 match &func.instr(tid).kind {
109 InstrKind::Ret {
110 val: Some(ValueRef::Constant(cid)),
111 } => match ctx.get_const(*cid) {
112 llvm_ir::ConstantData::Int { val, .. } => assert_eq!(*val, 4),
113 other => panic!("unexpected ret constant: {other:?}"),
114 },
115 other => panic!("expected ret constant, got {other:?}"),
116 }
117 }
118
119 #[test]
120 fn does_not_fold_non_constant_expression() {
121 let (mut ctx, mut module) = make_non_const_add_fn();
122 let mut pass = ConstantFold;
123 let changed = pass.run_on_function(&mut ctx, &mut module.functions[0]);
124 assert!(!changed);
125 assert_eq!(module.functions[0].blocks[0].body.len(), 1);
126 }
127}