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], 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>, pub tys: Vec<Type>, pub param_attrs: Vec<Vec<ParameterAttribute>>, 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>, pub tys: Vec<Type>, pub param_attrs: Vec<Vec<ParameterAttribute>>, 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], }
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(), }
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}