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
// Copyright (c) 2017-2019 Fabian Schuiki

//! Optimization and analysis passes on LLHD IR.
//!
//! This module implements various passes that analyze or mutate an LLHD
//! intermediate representation.

use crate::ir::prelude::*;
use crate::{
    ir::InstData,
    ty::{signal_ty, Type},
};

/// Constant folding
pub struct ConstantFoldingPass;

impl ConstantFoldingPass {
    /// Fold a function.
    pub fn run_on_function(func: &mut Function) -> bool {
        let mut builder = FunctionBuilder::new(func);
        let mut modified = false;
        let mut insts = vec![];
        for bb in builder.func.layout.blocks() {
            for inst in builder.func.layout.insts(bb) {
                insts.push(inst);
            }
        }
        for inst in insts {
            modified |= Self::run_on_inst(&mut builder, inst);
        }
        modified
    }

    /// Fold a process.
    pub fn run_on_process(prok: &mut Process) -> bool {
        let mut builder = ProcessBuilder::new(prok);
        let mut modified = false;
        let mut insts = vec![];
        for bb in builder.prok.layout.blocks() {
            for inst in builder.prok.layout.insts(bb) {
                insts.push(inst);
            }
        }
        for inst in insts {
            modified |= Self::run_on_inst(&mut builder, inst);
        }
        modified
    }

    /// Fold an entity.
    pub fn run_on_entity(entity: &mut Entity) -> bool {
        let mut builder = EntityBuilder::new(entity);
        let mut modified = false;
        for inst in builder.entity.layout.insts().collect::<Vec<_>>() {
            modified |= Self::run_on_inst(&mut builder, inst);
        }
        modified
    }

    /// Fold a single instruction.
    pub fn run_on_inst(builder: &mut impl UnitBuilder, inst: Inst) -> bool {
        if !builder.dfg().has_result(inst) {
            return false;
        }
        builder.insert_after(inst);
        let value = builder.dfg().inst_result(inst);
        let ty = builder.dfg().value_type(value);
        let replacement = match builder.dfg()[inst] {
            InstData::Binary { opcode, args, .. } => {
                Self::fold_binary(builder, opcode, ty.clone(), args)
            }
            _ => None,
        };
        if let Some(replacement) = replacement {
            let new_ty = builder.dfg().value_type(replacement);
            assert!(
                ty == new_ty || ty == signal_ty(new_ty),
                "types before (lhs) and after (rhs) folding must match"
            );
            builder.unit_mut().dfg_mut().replace_use(value, replacement);
            builder.prune_if_unused(inst);
            true
        } else {
            false
        }
    }

    /// Fold a binary instruction.
    fn fold_binary(
        builder: &mut impl UnitBuilder,
        opcode: Opcode,
        ty: Type,
        args: [Value; 2],
    ) -> Option<Value> {
        if ty.is_int() {
            Self::fold_binary_int(builder, opcode, ty.unwrap_int(), args)
        } else if ty.is_signal() && ty.unwrap_signal().is_int() {
            Self::fold_binary_int(builder, opcode, ty.unwrap_signal().unwrap_int(), args)
        } else {
            None
        }
    }

    /// Fold a binary instruction on integers.
    fn fold_binary_int(
        builder: &mut impl UnitBuilder,
        opcode: Opcode,
        width: usize,
        args: [Value; 2],
    ) -> Option<Value> {
        let inst0 = builder.dfg().get_value_inst(args[0])?;
        let inst1 = builder.dfg().get_value_inst(args[1])?;
        let imm0 = builder.dfg()[inst0].get_const_int()?;
        let imm1 = builder.dfg()[inst1].get_const_int()?;
        let result = match opcode {
            Opcode::Add => imm0 + imm1,
            _ => return None,
        };
        Some(builder.ins().const_int(width, result))
    }
}