Skip to main content

vicis_core/ir/function/instruction/
mod.rs

1pub mod builder;
2pub mod parser;
3
4pub use parser::parse;
5use rustc_hash::FxHashMap;
6
7use crate::ir::{
8    function::{basic_block::BasicBlockId, data::Data, param_attrs::ParameterAttribute},
9    module::{attributes::Attribute, metadata::Metadata, name::Name},
10    types::Type,
11    value::{ConstantData, ConstantInt, Value, ValueId},
12};
13use id_arena::Id;
14use std::{fmt, slice};
15
16pub type InstructionId = Id<Instruction>;
17
18pub struct Instruction {
19    pub opcode: Opcode,
20    pub operand: Operand,
21    pub dest: Option<Name>,
22    pub id: Option<InstructionId>,
23    pub parent: BasicBlockId,
24    pub metadata: FxHashMap<String, Metadata>,
25}
26
27#[derive(Clone, Copy, PartialEq)]
28pub enum Opcode {
29    Alloca,
30    Phi,
31    Load,
32    Store,
33    InsertValue,
34    ExtractValue,
35    Add,
36    Sub,
37    Mul,
38    SDiv,
39    SRem,
40    And,
41    LShr,
42    ICmp,
43    Sext,
44    Zext,
45    Bitcast,
46    Trunc,
47    IntToPtr,
48    GetElementPtr,
49    Call,
50    Invoke,
51    LandingPad,
52    Resume,
53    Br,
54    CondBr,
55    Ret,
56    Unreachable,
57    Invalid,
58}
59
60#[derive(Clone, Copy, Eq, PartialEq)]
61pub enum ICmpCond {
62    Eq,
63    Ne,
64    Ugt,
65    Uge,
66    Ult,
67    Ule,
68    Sgt,
69    Sge,
70    Slt,
71    Sle,
72}
73
74#[derive(Debug, Clone)]
75pub struct Alloca {
76    pub tys: [Type; 2],
77    pub num_elements: ConstantData,
78    pub align: u32,
79}
80
81#[derive(Debug, Clone)]
82pub struct Phi {
83    pub ty: Type,
84    pub args: Vec<ValueId>,
85    pub blocks: Vec<BasicBlockId>,
86}
87
88#[derive(Debug, Clone)]
89pub struct Load {
90    pub tys: [Type; 2],
91    pub addr: ValueId,
92    pub align: u32,
93}
94
95#[derive(Debug, Clone)]
96pub struct IntBinary {
97    pub ty: Type,
98    pub nsw: bool,
99    pub nuw: bool,
100    pub exact: bool,
101    pub args: [ValueId; 2],
102}
103
104#[derive(Debug, Clone)]
105pub struct Store {
106    pub tys: [Type; 2],
107    pub args: [ValueId; 2],
108    pub align: u32,
109}
110
111#[derive(Debug, Clone)]
112pub struct InsertValue {
113    pub tys: [Type; 2],
114    pub args: Vec<ValueId>,
115}
116
117#[derive(Debug, Clone)]
118pub struct ExtractValue {
119    pub ty: Type,
120    pub args: Vec<ValueId>,
121}
122
123#[derive(Debug, Clone)]
124pub struct ICmp {
125    pub ty: Type,
126    pub args: [ValueId; 2],
127    pub cond: ICmpCond,
128}
129
130#[derive(Debug, Clone)]
131pub struct Cast {
132    pub tys: [Type; 2], // from, to
133    pub arg: ValueId,
134}
135
136#[derive(Debug, Clone)]
137pub struct GetElementPtr {
138    pub inbounds: bool,
139    pub tys: Vec<Type>,
140    pub args: Vec<ValueId>,
141}
142
143#[derive(Debug, Clone)]
144pub struct Call {
145    pub args: Vec<ValueId>, // args[0] = callee, args[1..] = arguments
146    pub tys: Vec<Type>,     // tys[0] = callee's result type, args[1..] = argument types
147    pub param_attrs: Vec<Vec<ParameterAttribute>>, // param_attrs[0] = attrs of args[1]
148    pub ret_attrs: Vec<ParameterAttribute>,
149    pub func_attrs: Vec<Attribute>,
150}
151
152#[derive(Debug, Clone)]
153pub struct Invoke {
154    pub args: Vec<ValueId>, // args[0] = callee, args[1..] = arguments
155    pub tys: Vec<Type>,     // tys[0] = callee's result type, args[1..] = argument types
156    pub param_attrs: Vec<Vec<ParameterAttribute>>, // param_attrs[0] = attrs of args[1]
157    pub ret_attrs: Vec<ParameterAttribute>,
158    pub func_attrs: Vec<Attribute>,
159    pub blocks: Vec<BasicBlockId>,
160}
161
162#[derive(Debug, Clone)]
163pub struct LandingPad {
164    pub ty: Type,
165}
166
167#[derive(Debug, Clone)]
168pub struct Resume {
169    pub ty: Type,
170    pub arg: ValueId,
171}
172
173#[derive(Debug, Clone)]
174pub struct Br {
175    pub block: BasicBlockId,
176}
177
178#[derive(Debug, Clone)]
179pub struct CondBr {
180    pub arg: ValueId,
181    pub blocks: [BasicBlockId; 2], // iftrue, iffalse
182}
183
184#[derive(Debug, Clone)]
185pub struct Ret {
186    pub ty: Type,
187    pub val: Option<ValueId>,
188}
189
190#[derive(Clone)]
191pub enum Operand {
192    Alloca(Alloca),
193    Phi(Phi),
194    Load(Load),
195    IntBinary(IntBinary),
196    Store(Store),
197    InsertValue(InsertValue),
198    ExtractValue(ExtractValue),
199    ICmp(ICmp),
200    Cast(Cast),
201    GetElementPtr(GetElementPtr),
202    Call(Call),
203    Invoke(Invoke),
204    LandingPad(LandingPad),
205    Resume(Resume),
206    Br(Br),
207    CondBr(CondBr),
208    Ret(Ret),
209    Unreachable,
210    Invalid,
211}
212
213impl Instruction {
214    pub fn replace(&mut self, other: Self) {
215        assert_eq!(self.opcode, Opcode::Invalid);
216        self.opcode = other.opcode;
217        self.operand = other.operand;
218        self.dest = other.dest;
219        self.parent = other.parent;
220    }
221
222    pub fn with_operand(mut self, operand: Operand) -> Self {
223        self.operand = operand;
224        self
225    }
226
227    pub fn with_dest(mut self, dest: Name) -> Self {
228        self.dest = Some(dest);
229        self
230    }
231
232    pub fn with_metadata(mut self, metadata: FxHashMap<String, Metadata>) -> Self {
233        self.metadata = metadata;
234        self
235    }
236
237    pub fn fold_consts(&self, data: &Data) -> Option<ConstantData> {
238        match self.operand {
239            Operand::IntBinary(ref i) => {
240                let args: Vec<&Value> = i.args.iter().map(|&id| data.value_ref(id)).collect();
241                match args.as_slice() {
242                    [Value::Constant(ConstantData::Int(ConstantInt::Int32(x))), Value::Constant(ConstantData::Int(ConstantInt::Int32(y)))] => {
243                        match self.opcode {
244                            Opcode::Add => Some(ConstantData::Int(ConstantInt::Int32(x + y))),
245                            Opcode::Sub => Some(ConstantData::Int(ConstantInt::Int32(x - y))),
246                            Opcode::Mul => Some(ConstantData::Int(ConstantInt::Int32(x * y))),
247                            Opcode::SRem => Some(ConstantData::Int(ConstantInt::Int32(x % y))),
248                            _ => None,
249                        }
250                    }
251                    _ => None,
252                }
253            }
254            Operand::ICmp(ref i) => {
255                let args: Vec<&Value> = i.args.iter().map(|&id| data.value_ref(id)).collect();
256                match args.as_slice() {
257                    [Value::Constant(ConstantData::Int(ConstantInt::Int32(x))), Value::Constant(ConstantData::Int(ConstantInt::Int32(y)))] => {
258                        match i.cond {
259                            ICmpCond::Eq => Some(ConstantData::Int(ConstantInt::Int1(x == y))),
260                            ICmpCond::Ne => Some(ConstantData::Int(ConstantInt::Int1(x != y))),
261                            ICmpCond::Ugt => Some(ConstantData::Int(ConstantInt::Int1(x > y))),
262                            ICmpCond::Uge => Some(ConstantData::Int(ConstantInt::Int1(x >= y))),
263                            ICmpCond::Ult => Some(ConstantData::Int(ConstantInt::Int1(x < y))),
264                            ICmpCond::Ule => Some(ConstantData::Int(ConstantInt::Int1(x <= y))),
265                            ICmpCond::Sgt => Some(ConstantData::Int(ConstantInt::Int1(x > y))),
266                            ICmpCond::Sge => Some(ConstantData::Int(ConstantInt::Int1(x >= y))),
267                            ICmpCond::Slt => Some(ConstantData::Int(ConstantInt::Int1(x < y))),
268                            ICmpCond::Sle => Some(ConstantData::Int(ConstantInt::Int1(x <= y))),
269                        }
270                    }
271                    _ => None,
272                }
273            }
274            Operand::Cast(_) => todo!(),
275            _ => None,
276        }
277    }
278}
279
280impl Opcode {
281    pub fn with_block(self, parent: BasicBlockId) -> Instruction {
282        Instruction {
283            opcode: self,
284            operand: Operand::Invalid,
285            dest: None,
286            id: None,
287            parent,
288            metadata: FxHashMap::default(), // users: FxHashSet::default(),
289        }
290    }
291
292    pub fn is_terminator(&self) -> bool {
293        matches!(
294            self,
295            Self::Ret | Self::Br | Self::CondBr | Self::Invoke | Self::Resume
296        )
297    }
298
299    pub fn is_load(&self) -> bool {
300        self == &Self::Load
301    }
302
303    pub fn is_store(&self) -> bool {
304        self == &Self::Store
305    }
306
307    pub fn is_alloca(&self) -> bool {
308        self == &Self::Alloca
309    }
310
311    pub fn is_phi(&self) -> bool {
312        self == &Self::Phi
313    }
314
315    pub fn is_call(&self) -> bool {
316        self == &Self::Call
317    }
318
319    pub fn is_invoke(&self) -> bool {
320        self == &Self::Invoke
321    }
322
323    pub fn has_side_effects(&self) -> bool {
324        self.is_load()
325            || self.is_store()
326            || self.is_alloca()
327            || self.is_phi()
328            || self.is_call()
329            || self.is_invoke()
330            || self.is_terminator()
331    }
332}
333
334macro_rules! as_inst {
335    ($name:ident, $inst:ident) => {
336        pub fn $name(&self) -> Option<&$inst> {
337            match self {
338                Self::$inst(x) => Some(x),
339                _ => None,
340            }
341        }
342    };
343    (mut $name:ident, $inst:ident) => {
344        pub fn $name(&mut self) -> Option<&mut $inst> {
345            match self {
346                Self::$inst(x) => Some(x),
347                _ => None,
348            }
349        }
350    };
351}
352
353impl Operand {
354    pub fn args(&self) -> &[ValueId] {
355        match self {
356            Self::Alloca(_) => &[],
357            Self::Phi(Phi { args, .. }) => args.as_slice(),
358            Self::Ret(Ret { val, .. }) if val.is_none() => &[],
359            Self::Ret(Ret { val, .. }) => slice::from_ref(val.as_ref().unwrap()),
360            Self::Load(Load { addr, .. }) => slice::from_ref(addr),
361            Self::Store(Store { args, .. }) => args,
362            Self::InsertValue(InsertValue { args, .. }) => args,
363            Self::ExtractValue(ExtractValue { args, .. }) => args,
364            Self::IntBinary(IntBinary { args, .. }) => args,
365            Self::ICmp(ICmp { args, .. }) => args,
366            Self::Cast(Cast { arg, .. }) => slice::from_ref(arg),
367            Self::GetElementPtr(GetElementPtr { args, .. }) => args.as_slice(),
368            Self::Call(Call { args, .. }) | Self::Invoke(Invoke { args, .. }) => args.as_slice(),
369            Self::LandingPad(LandingPad { .. }) => &[],
370            Self::Resume(Resume { arg, .. }) => slice::from_ref(arg),
371            Self::Br(Br { .. }) => &[],
372            Self::CondBr(CondBr { arg, .. }) => slice::from_ref(arg),
373            Self::Unreachable => &[],
374            Self::Invalid => &[],
375        }
376    }
377
378    pub fn args_mut(&mut self) -> &mut [ValueId] {
379        match self {
380            Self::Alloca(_) => &mut [],
381            Self::Phi(Phi { args, .. }) => args.as_mut_slice(),
382            Self::Ret(Ret { val, .. }) if val.is_none() => &mut [],
383            Self::Ret(Ret { val, .. }) => slice::from_mut(val.as_mut().unwrap()),
384            Self::Load(Load { addr, .. }) => slice::from_mut(addr),
385            Self::Store(Store { args, .. }) => args,
386            Self::InsertValue(InsertValue { args, .. }) => args,
387            Self::ExtractValue(ExtractValue { args, .. }) => args,
388            Self::IntBinary(IntBinary { args, .. }) => args,
389            Self::ICmp(ICmp { args, .. }) => args,
390            Self::Cast(Cast { arg, .. }) => slice::from_mut(arg),
391            Self::GetElementPtr(GetElementPtr { args, .. }) => args.as_mut_slice(),
392            Self::Call(Call { args, .. }) | Self::Invoke(Invoke { args, .. }) => args.as_mut(),
393            Self::LandingPad(LandingPad { .. }) => &mut [],
394            Self::Resume(Resume { arg, .. }) => slice::from_mut(arg),
395            Self::Br(Br { .. }) => &mut [],
396            Self::CondBr(CondBr { arg, .. }) => slice::from_mut(arg),
397            Self::Unreachable => &mut [],
398            Self::Invalid => &mut [],
399        }
400    }
401
402    pub fn types(&self) -> &[Type] {
403        match self {
404            Self::Alloca(Alloca { tys, .. }) => tys,
405            Self::Phi(Phi { ty, .. }) => slice::from_ref(ty),
406            Self::Ret(Ret { ty, .. }) => slice::from_ref(ty),
407            Self::Load(Load { tys, .. }) => tys,
408            Self::Store(Store { .. }) => &[],
409            Self::InsertValue(InsertValue { tys, .. }) => tys,
410            Self::ExtractValue(ExtractValue { ty, .. }) => slice::from_ref(ty),
411            Self::IntBinary(IntBinary { ty, .. }) => slice::from_ref(ty),
412            Self::ICmp(ICmp { ty, .. }) => slice::from_ref(ty),
413            Self::Cast(Cast { tys, .. }) => tys,
414            Self::GetElementPtr(GetElementPtr { tys, .. }) => tys.as_slice(),
415            Self::Call(Call { tys, .. }) | Self::Invoke(Invoke { tys, .. }) => tys.as_slice(),
416            Self::LandingPad(LandingPad { ty }) => slice::from_ref(ty),
417            Self::Resume(Resume { ty, .. }) => slice::from_ref(ty),
418            Self::Br(Br { .. }) => &[],
419            Self::CondBr(CondBr { .. }) => &[],
420            Self::Unreachable => &[],
421            Self::Invalid => &[],
422        }
423    }
424
425    pub fn blocks(&self) -> &[BasicBlockId] {
426        match self {
427            Self::Phi(Phi { blocks, .. }) => blocks,
428            Self::Br(Br { block }) => slice::from_ref(block),
429            Self::CondBr(CondBr { blocks, .. }) => blocks,
430            Self::Invoke(Invoke { blocks, .. }) => blocks,
431            _ => &[],
432        }
433    }
434
435    pub fn call_result_ty(&self) -> Option<Type> {
436        match self {
437            Self::Call(Call { tys, .. }) | Self::Invoke(Invoke { tys, .. }) => Some(tys[0]),
438            _ => None,
439        }
440    }
441
442    as_inst!(as_alloca, Alloca);
443    as_inst!(as_store, Store);
444    as_inst!(as_load, Load);
445    as_inst!(as_phi, Phi);
446    as_inst!(mut as_phi_mut, Phi);
447    as_inst!(as_condbr, CondBr);
448}
449
450impl Alloca {
451    pub fn ty(&self) -> Type {
452        self.tys[0]
453    }
454}
455
456impl Store {
457    pub fn dst_val(&self) -> ValueId {
458        self.args[1]
459    }
460
461    pub fn src_val(&self) -> ValueId {
462        self.args[0]
463    }
464}
465
466impl Load {
467    pub fn src_val(&self) -> ValueId {
468        self.addr
469    }
470}
471
472impl Phi {
473    pub fn blocks_mut(&mut self) -> &mut Vec<BasicBlockId> {
474        &mut self.blocks
475    }
476
477    pub fn args_mut(&mut self) -> &mut Vec<ValueId> {
478        &mut self.args
479    }
480}
481
482impl fmt::Debug for Opcode {
483    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484        write!(
485            f,
486            "{}",
487            match self {
488                Opcode::Alloca => "alloca",
489                Opcode::Phi => "phi",
490                Opcode::Load => "load",
491                Opcode::Store => "store",
492                Opcode::InsertValue => "insertvalue",
493                Opcode::ExtractValue => "extractvalue",
494                Opcode::Add => "add",
495                Opcode::Sub => "sub",
496                Opcode::Mul => "mul",
497                Opcode::SDiv => "sdiv",
498                Opcode::SRem => "srem",
499                Opcode::And => "and",
500                Opcode::LShr => "lshr",
501                Opcode::ICmp => "icmp",
502                Opcode::Sext => "sext",
503                Opcode::Zext => "zext",
504                Opcode::Bitcast => "bitcast",
505                Opcode::Trunc => "trunc",
506                Opcode::IntToPtr => "inttoptr",
507                Opcode::GetElementPtr => "getelementptr",
508                Opcode::Call => "call",
509                Opcode::Invoke => "invoke",
510                Opcode::LandingPad => "landingpad",
511                Opcode::Resume => "resume",
512                Opcode::Br | Opcode::CondBr => "br",
513                Opcode::Ret => "ret",
514                Opcode::Unreachable => "unreachable",
515                Opcode::Invalid => "INVALID",
516            }
517        )
518    }
519}
520
521impl fmt::Debug for ICmpCond {
522    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523        write!(
524            f,
525            "{}",
526            match self {
527                Self::Eq => "eq",
528                Self::Ne => "ne",
529                Self::Ugt => "ugt",
530                Self::Uge => "uge",
531                Self::Ult => "ult",
532                Self::Ule => "ule",
533                Self::Sgt => "sgt",
534                Self::Sge => "sge",
535                Self::Slt => "slt",
536                Self::Sle => "sle",
537            }
538        )
539    }
540}