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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
//! An `Instruction` holds an `Operation`.

use il::*;
use std::fmt;

/// An `Instruction` represents location, and non-semantical information about
/// an `Operation`.
///
/// An `instruction` gives location to an `Operation`.
///
/// Methods are provided to create individual instructions, as all uses cases
/// cannot be seen beforehand. However, it is generally poor-form to create
/// an `Instruction` manually. You should use the methods on `Block` which 
/// correspond to the `Operation` you wish to create, and the `Instruction`
/// will be created automatically.
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Instruction {
    operation: Operation,
    index: usize,
    comment: Option<String>,
    address: Option<u64>
}


impl Instruction {
    /// Create a new instruction with the given index and operation.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the methods
    /// on `il::Block` which correspond to the operation you wish to append to
    /// that block.
    pub fn new(index: usize, operation: Operation) -> Instruction {
        Instruction {
            operation: operation,
            index: index,
            comment: None,
            address: None
        }
    }


    /// Create a new `Assign` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `assign` method on `il::Block` instead.
    pub fn assign(index: usize, dst: Scalar, src: Expression) -> Instruction {
        Instruction::new(index, Operation::assign(dst, src))
    }


    /// Create a new `Store` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `store` method on `il::Block` instead.
    pub fn store(instruction_index: usize, index: Expression, src: Expression)
        -> Instruction {

        Instruction::new(instruction_index, Operation::store(index, src))
    }


    /// Create a new `Load` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `load` method on `il::Block` instead.
    pub fn load(instruction_index: usize, dst: Scalar, index: Expression)
        -> Instruction {

        Instruction::new(instruction_index, Operation::load(dst, index))
    }


    /// Create a new `Brc` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `brc` method on `il::Block` instead.
    pub fn branch(index: usize, target: Expression) -> Instruction {

        Instruction::new(index, Operation::branch(target))
    }


    /// Create a new `Intrinsic` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `intrinsic` method on `il::Block` instead.
    pub fn intrinsic(index: usize, intrinsic: Intrinsic) -> Instruction {
        Instruction::new(index, Operation::Intrinsic { intrinsic: intrinsic })
    }


    /// Create a new `Nop` instruction.
    ///
    /// # Warning
    /// You almost never want to call this function. You should use the
    /// `nop` method on `il::Block` instead.
    pub fn nop(index: usize) -> Instruction {
        Instruction::new(index, Operation::Nop)
    }


    /// Returns `true` if the `Operation` for this `Instruction` is `Operation::Assign`
    pub fn is_assign(&self) -> bool {
        if let Operation::Assign{..} = self.operation {
            true
        }
        else {
            false
        }
    }

    /// Returns `true` if the `Operation` for this `Instruction` is `Operation::Store`
    pub fn is_store(&self) -> bool {
        if let Operation::Store{..} = self.operation {
            true
        }
        else {
            false
        }
    }

    /// Returns `true` if the `Operation` for this `Instruction` is `Operation::Load`
    pub fn is_load(&self) -> bool {
        if let Operation::Load{..} = self.operation {
            true
        }
        else {
            false
        }
    }

    /// Returns `true` if the `Operation` for this `Instruction` is `Operation::Brc`
    pub fn is_branch(&self) -> bool {
        if let Operation::Branch{..} = self.operation {
            true
        }
        else {
            false
        }
    }

    /// Get the `Operation` for this `Instruction`
    pub fn operation(&self) -> &Operation {
        &self.operation
    }

    /// Get a mutable reference to the `Operation` for this `Instruction`
    pub fn operation_mut(&mut self) -> &mut Operation {
        &mut self.operation
    }

    /// Get the index for this `Instruction`.
    ///
    /// An `Instruction` index is assigned by its parent `Block` and uniquely identifies the
    /// `Instruction` within the `Block`. `Instruction` indices need not be continuous, nor
    /// in order.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Get the optional comment for this `Instruction`
    pub fn comment(&self) -> Option<&str> {
        self.comment.as_ref().map(|s| s.as_str())
    }

    /// Set the optional comment for this `Instruction`
    pub fn set_comment(&mut self, comment: Option<String>) {
        self.comment = comment;
    }

    /// Get the optional address for this `Instruction`
    ///
    /// An `Instruction` will typically have an address if one was given by a translator. It is
    /// not uncommon for there to be a mixture of `Instruction`s with and without comments. For
    /// example, applying SSA to a `Function` will cause `Phi` instructions to be inserted, and
    /// these instructions will not have addresses.
    pub fn address(&self) -> Option<u64> {
        self.address.clone()
    }

    /// Set the optional address for this `Instruction`
    pub fn set_address(&mut self, address: Option<u64>) {
        self.address = address;
    }

    /// Clone this instruction with a new index.
    pub(crate) fn clone_new_index(&self, index: usize) -> Instruction {
        Instruction {
            operation: self.operation.clone(),
            index: index,
            comment: self.comment.clone(),
            address: self.address
        }
    }

    /// Get the `Scalar` which will be written by this `Instruction`.
    pub fn scalars_written(&self) -> Option<Vec<&Scalar>> {
        self.operation.scalars_written()
    }

    /// Get a mutable reference to the `Scalar` which will be written by this
    /// `Instruction`.
    pub fn scalar_written_mut(&mut self) -> Option<Vec<&mut Scalar>> {
        self.operation.scalars_written_mut()
    }

    /// Get a Vec of each `Scalar` read by this `Instruction`.
    pub fn scalars_read(&self) -> Option<Vec<&Scalar>> {
        self.operation.scalars_read()
    }

    /// Get a Vec of mutable references for each `Scalar` read by this
    /// `Instruction`.
    pub fn scalars_read_mut(&mut self) -> Option<Vec<&mut Scalar>> {
        self.operation.scalars_read_mut()
    }

    /// Get all the scalars used in this instruction
    pub fn scalars(&self) -> Option<Vec<&Scalar>> {
        let mut scalars =
            self.scalars_written()?
                .into_iter()
                .chain(self.scalars_read()?.into_iter())
                .collect::<Vec<&Scalar>>();

        scalars.sort();
        scalars.dedup();
        Some(scalars)
    }
}



impl fmt::Display for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let prefix = match self.address {
            Some(address) => 
                format!("{:X} {:02X} {}", address, self.index, self.operation),
            None =>
                format!("{:02X} {}", self.index, self.operation)
        };
        if let Some(ref comment) = self.comment {
            write!(f, "{} // {}", prefix, comment)
        }
        else {
            write!(f, "{}", prefix)
        }
    }
}