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

//! This module implements basic blocks in a function or process body.

use crate::inst::*;
use std;
use crate::ty::*;
use crate::unit::*;
use crate::value::*;

/// A basic block.
pub struct Block {
    id: BlockRef,
    name: Option<String>,
    insts: Vec<InstRef>,
}

impl Block {
    /// Create a new empty basic block with an optional name (aka label).
    pub fn new(name: Option<String>) -> Block {
        Block {
            id: BlockRef::new(ValueId::alloc()),
            name: name,
            insts: Vec::new(),
        }
    }

    /// Obtain a reference to this block.
    pub fn as_ref(&self) -> BlockRef {
        self.id
    }

    /// Obtain an iterator over the instructions in this block.
    pub fn insts<'a>(&'a self, ctx: &'a UnitContext) -> InstIter<'a> {
        InstIter::new(self.insts.iter(), ctx)
    }

    /// Obtain an iterator over references to the instructions in this block.
    pub fn inst_refs(&self) -> std::slice::Iter<InstRef> {
        self.insts.iter()
    }

    /// Insert an instruction into this block as dictated by the requested
    /// position. `Begin` and `End` are treated as synonyms to `BlockBegin` and
    /// `BlockEnd`. Panics if the referred instruction is not part of this
    /// block.
    pub fn insert_inst(&mut self, inst: InstRef, pos: InstPosition) {
        let index = match pos {
            InstPosition::Begin => 0,
            InstPosition::End => self.insts.len(),
            InstPosition::Before(i) => self.inst_pos(i),
            InstPosition::After(i) => self.inst_pos(i) + 1,
            InstPosition::BlockBegin(b) => {
                assert_eq!(self.id, b);
                0
            }
            InstPosition::BlockEnd(b) => {
                assert_eq!(self.id, b);
                self.insts.len()
            }
        };
        self.insts.insert(index, inst)
    }

    /// Detach an instruction from this block. Panics if the instruction is not
    /// part of this block.
    pub fn detach_inst(&mut self, inst: InstRef) {
        let pos = self.inst_pos(inst);
        self.insts.remove(pos);
    }

    /// Determine the index at which a certain position is located. Panics if
    /// the instruction is not part of the block.
    fn inst_pos(&self, inst: InstRef) -> usize {
        self.insts
            .iter()
            .position(|&i| i == inst)
            .expect("basic block does not contain inst")
    }
}

impl Value for Block {
    fn id(&self) -> ValueId {
        self.id.into()
    }

    fn ty(&self) -> Type {
        void_ty()
    }

    fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|x| x as &str)
    }

    fn is_global(&self) -> bool {
        false
    }
}

/// A relative position of a block. Used to insert or move a block to a position
/// relative to the surrounding unit or another block.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum BlockPosition {
    /// The very first position in the function/process.
    Begin,
    /// The very last position in the function/process.
    End,
    /// The position just before another block.
    Before(BlockRef),
    /// The position just after another block.
    After(BlockRef),
}