1use crate::value::QCodeMut;
6use crate::{
7 context::Context,
8 error::Result,
9 space::{MemorySpaceId, Space, SpaceId, SpaceRef, SpaceType},
10 types::TypeId,
11 value::{
12 BlockId, BlockRef, FunctionId, FunctionRef, LocalBlockId, ModuleView, QCodeView, Value,
13 ValueId,
14 util::{
15 base_ref::{BaseRef, WithCtx, WithCtxMut},
16 named::{Named, Renameable},
17 },
18 },
19};
20use jstd::Identifier;
21use std::{
22 borrow::Cow,
23 fmt::{Display, Formatter},
24 marker::PhantomData,
25};
26
27mod aggregate;
28mod assert;
29mod binop;
30mod bits;
31mod casting;
32mod flags;
33pub(crate) mod intrinsic;
34mod map;
35mod memory;
36mod mnemonic;
37mod pcode_op;
38mod scan;
39pub mod segment;
40mod terminator;
41mod unop;
42
43pub use aggregate::{Extract, Gep, Tuple};
44pub use assert::Assert;
45pub use binop::{Binary, Binop, FloatBinop, IntBinop};
46pub use casting::{FloatToFloat, FloatToInt, IntToFloat, Range, Sext, Zext};
47pub use flags::{Carry, IsFloatNaN, LzCount, PopCount, SBorrow, SCarry};
48pub use intrinsic::{
49 Intrinsic, IntrinsicApp, IntrinsicId, IntrinsicRegistration, RootOp, Simplified,
50 recognizers_for,
51};
52pub use map::Map;
53pub use memory::{Load, Store};
54pub use mnemonic::Mnemonic;
55pub use pcode_op::{PCodeOp, PCodeOpId};
56pub use scan::Scan;
57pub use terminator::{
58 Apply, BadInsn, Branch, BranchInd, CBranch, Call, CallInd, CallTag, Callee, Return,
59 ReturnValue, Switch, SwitchArm, TailCall,
60};
61pub use unop::{Unary, Unop};
62
63#[derive(Identifier)]
67pub struct LocalInsnId(u32);
68
69crate::composite_id!(InstructionId, LocalInsnId);
70
71#[derive(Clone, serde::Serialize, serde::Deserialize)]
74pub struct Instruction<'str> {
75 pub(crate) name: Option<Cow<'str, str>>,
77
78 pub(crate) type_id: TypeId,
80
81 mnemonic: Mnemonic,
83
84 pub(crate) parent: Option<LocalBlockId>,
89
90 address: Option<u64>,
92
93 _marker: std::marker::PhantomData<&'str ()>,
94}
95
96impl<'str> Instruction<'str> {
97 pub(crate) fn new(type_id: TypeId, mnemonic: Mnemonic) -> Self {
98 Self {
99 name: None,
100 parent: None,
101 type_id,
102 mnemonic,
103 address: None,
104 _marker: std::marker::PhantomData,
105 }
106 }
107
108 pub fn mnemonic(&self) -> &Mnemonic {
109 &self.mnemonic
110 }
111
112 pub(crate) fn mnemonic_mut(&mut self) -> &mut Mnemonic {
115 &mut self.mnemonic
116 }
117
118 pub(crate) fn set_address(&mut self, address: u64) {
121 self.address = Some(address);
122 }
123
124 pub fn from_id<'ctx>(
125 ctx: &'ctx Context<'str>,
126 id: InstructionId,
127 ) -> InstructionRef<'str, 'ctx> {
128 InstructionRef::new(ModuleView::new(ctx), id)
129 }
130
131 pub fn from_id_mut<'ctx>(
132 ctx: &'ctx mut Context<'str>,
133 id: InstructionId,
134 ) -> InstructionMutRef<'str, 'ctx> {
135 InstructionMutRef::from_id(ctx, id)
136 }
137}
138
139impl<'s, 'ctx: 's, 'str: 'ctx, R> InstructionRef<'str, 'ctx, R>
140where
141 R: QCodeView<'ctx, 'str>,
142{
143 fn inner(&'s self) -> &'ctx Instruction<'str> {
144 self.view.instruction(self.id)
145 }
146
147 pub fn name(&'s self) -> Option<&'ctx str> {
149 self.inner().name.as_deref()
150 }
151
152 pub fn type_id(&'s self) -> TypeId {
154 self.inner().type_id
155 }
156
157 pub fn size(&'s self) -> usize {
159 self.view.shared().types.size_of(self.inner().type_id)
160 }
161
162 pub fn parent(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
164 self.inner()
165 .parent
166 .map(|local| BlockRef::new(self.view, BlockId::new(self.id.func, local)))
167 }
168
169 pub fn block(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
170 self.parent()
171 }
172
173 pub fn function(&'s self) -> Option<FunctionRef<'str, 'ctx, R>> {
175 self.parent().and_then(|block| block.parent())
176 }
177
178 pub fn mnemonic(&'s self) -> &'ctx Mnemonic {
180 &self.inner().mnemonic
181 }
182
183 pub fn operands(&'s self) -> smallvec::SmallVec<[ValueId; 2]> {
193 let func = self.id.func;
194 self.mnemonic()
195 .args()
196 .into_iter()
197 .map(|v| v.qualify(func))
198 .collect()
199 }
200
201 pub fn address(&'s self) -> Option<u64> {
203 self.inner().address
204 }
205
206 pub fn space(&'s self) -> Option<SpaceRef<'ctx>> {
211 self.view
212 .shared()
213 .types
214 .space_of(self.inner().type_id)
215 .and_then(MemorySpaceId::shared)
216 .map(|id| Space::from_id(self.view.shared(), id))
217 }
218
219 pub fn memory_space(&'s self) -> Option<MemorySpaceId> {
222 self.view.shared().types.space_of(self.inner().type_id)
223 }
224
225 pub fn opcode(&'s self) -> &'static str {
227 self.mnemonic().opcode()
228 }
229
230 pub fn is_terminator(&'s self) -> bool {
232 self.mnemonic().is_terminator()
233 }
234
235 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
236 let ty = self.view.shared().types.type_name(self.type_id());
237
238 if let Some(name) = self.name() {
239 write!(f, "{ty} %{name}")
240 } else {
241 let id: usize = self.id.local.into();
244 write!(f, "{ty} %tmp{id:x}")
245 }
246 }
247}
248
249impl<'str, H: QCodeMut<'str>> BaseRef<H, InstructionId> {
257 pub fn set_result_type(&mut self, new_type: TypeId) {
261 let current = self.ctx.body(self.id.func).insn(self.id).type_id;
262 let (current_size, new_size) = {
263 let types = &self.ctx.shr().types;
264 (types.size_of(current), types.size_of(new_type))
265 };
266 assert!(
267 current_size == 0 || current_size == new_size,
268 "cannot change instruction result type: size {current_size} → {new_size}",
269 );
270 self.ctx.instruction_mut(self.id).type_id = new_type;
271 }
272
273 pub fn rename_local(&mut self, name: Cow<'str, str>) -> Result<()> {
276 let old_name = self
277 .ctx
278 .body(self.id.func)
279 .insn(self.id)
280 .name
281 .as_deref()
282 .map(str::to_owned);
283 self.ctx
284 .register_body_name(self.id.into(), name.clone(), old_name.as_deref())?;
285 self.ctx.instruction_mut(self.id).name = Some(name);
286 Ok(())
287 }
288}
289
290#[derive(Clone, Copy)]
291pub struct InstructionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
292 pub id: InstructionId,
293 pub(in crate::value) view: R,
294 marker: PhantomData<&'ctx &'str ()>,
295}
296
297impl<'str, 'ctx, R> InstructionRef<'str, 'ctx, R> {
298 pub fn new(view: R, id: InstructionId) -> Self {
299 Self {
300 id,
301 view,
302 marker: PhantomData,
303 }
304 }
305
306 pub fn id(&self) -> ValueId {
307 self.id.into()
308 }
309
310 pub fn as_statement(&self) -> InstructionStatement<'_, 'str, 'ctx, R> {
312 InstructionStatement(self)
313 }
314}
315
316impl<'str, 'ctx> InstructionRef<'str, 'ctx> {
317 pub fn from_id(ctx: &'ctx Context<'str>, id: InstructionId) -> Self {
318 Self::new(ModuleView::new(ctx), id)
319 }
320
321 pub fn from_mnemonic(
325 ctx: &'ctx mut Context<'str>,
326 func: FunctionId,
327 mnemonic: Mnemonic,
328 size: usize,
329 ) -> Self {
330 let type_id = ctx.shared.types.get_or_make_int(size);
331 let insn = Instruction::new(type_id, mnemonic);
332 let id = ctx.push_insn(func, insn);
333 InstructionRef::new(ModuleView::new(ctx), id)
334 }
335
336 pub fn from_mnemonic_with_type(
342 ctx: &'ctx mut Context<'str>,
343 func: FunctionId,
344 mnemonic: Mnemonic,
345 type_id: TypeId,
346 ) -> Self {
347 let insn = Instruction::new(type_id, mnemonic);
348 let id = ctx.push_insn(func, insn);
349 InstructionRef::new(ModuleView::new(ctx), id)
350 }
351
352 pub fn from_mnemonic_with_space(
357 ctx: &'ctx mut Context<'str>,
358 func: FunctionId,
359 mnemonic: Mnemonic,
360 size: usize,
361 _space: Option<SpaceId>,
362 ) -> Self {
363 let type_id = ctx.shared.types.get_or_make_int(size);
364 let insn = Instruction::new(type_id, mnemonic);
365 let id = ctx.push_insn(func, insn);
366 InstructionRef::new(ModuleView::new(ctx), id)
367 }
368}
369
370impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for InstructionRef<'str, 'ctx> {
371 fn ctx(&'s self) -> &'ctx Context<'str> {
372 self.view.context()
376 }
377}
378
379impl<'str: 'ctx, 'ctx, R> Named for InstructionRef<'str, 'ctx, R>
380where
381 R: QCodeView<'ctx, 'str>,
382{
383 fn name(&self) -> Option<&str> {
384 self.view.instruction(self.id).name.as_deref()
385 }
386}
387
388impl<'str: 'ctx, 'ctx, R> Display for InstructionRef<'str, 'ctx, R>
389where
390 R: QCodeView<'ctx, 'str>,
391{
392 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
393 InstructionRef::fmt(self, f)
394 }
395}
396
397impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for InstructionRef<'str, 'ctx, R>
398where
399 R: QCodeView<'ctx, 'str>,
400{
401 fn id(&self) -> ValueId {
402 self.id()
403 }
404
405 fn size(&self) -> usize {
406 InstructionRef::size(self)
407 }
408}
409
410pub type InstructionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, InstructionId>;
411
412impl<'str, 'ctx> InstructionMutRef<'str, 'ctx> {
413 pub fn as_ref(&self) -> InstructionRef<'str, '_> {
414 InstructionRef::new(ModuleView::new(self.ctx), self.id)
415 }
416
417 fn inner(&self) -> &Instruction<'str> {
418 self.ctx.instruction(self.id)
419 }
420
421 pub fn inner_mut(&mut self) -> &mut Instruction<'str> {
422 self.ctx.instruction_mut(self.id)
423 }
424
425 pub fn mnemonic_mut(&mut self) -> &mut Mnemonic {
426 &mut self.inner_mut().mnemonic
427 }
428
429 pub fn set_mnemonic(&mut self, mnemonic: Mnemonic) {
432 let old_args = self.inner().mnemonic.args();
433 let new_args = mnemonic.args();
434
435 let func = self.id.func;
437 for arg in old_args {
438 if let Some(users) = self.ctx.bodies[func].users.get_mut(&arg) {
439 users.retain(|&local| local != self.id.localize(func));
440 }
441 }
442
443 for arg in new_args {
444 self.ctx.bodies[func]
445 .users
446 .entry(arg)
447 .or_default()
448 .push(self.id.localize(func));
449 }
450
451 self.inner_mut().mnemonic = mnemonic;
452 }
453
454 pub fn address_mut(&mut self) -> &mut Option<u64> {
455 &mut self.inner_mut().address
456 }
457
458 pub fn set_address(&mut self, address: u64) {
459 *self.address_mut() = Some(address);
460 }
461
462 pub fn set_type(&mut self, new_type: TypeId) {
467 let current = self.inner().type_id;
468 let current_size = self.ctx.shared.types.size_of(current);
469 let new_size = self.ctx.shared.types.size_of(new_type);
470 if current_size != 0 && current_size != new_size {
471 panic!(
472 "Cannot change type of instruction {}: size {} → {}",
473 self, current_size, new_size
474 );
475 }
476 self.inner_mut().type_id = new_type;
477 }
478
479 pub fn set_type_resized(&mut self, new_type: TypeId) {
486 self.inner_mut().type_id = new_type;
487 }
488
489 pub fn set_space(&mut self, space: SpaceId) {
496 if matches!(
498 Space::from_id(&self.ctx.shared, space).ty,
499 SpaceType::Register
500 ) {
501 return;
502 }
503 let size = self.ctx.shared.types.size_of(self.inner().type_id);
504 let type_id = self.ctx.shared.types.get_or_make_space_address(size, space);
505 self.inner_mut().type_id = type_id;
506 }
507}
508
509impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for InstructionMutRef<'str, 'ctx> {
510 fn ctx(&'s self) -> &'s Context<'str> {
511 self.ctx
512 }
513}
514
515impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for InstructionMutRef<'str, 'ctx> {
516 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
517 self.ctx
518 }
519}
520
521impl Named for InstructionMutRef<'_, '_> {
525 fn name(&self) -> Option<&str> {
526 self.ctx.instruction(self.id).name.as_deref()
527 }
528}
529
530impl Display for InstructionMutRef<'_, '_> {
531 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
532 self.as_ref().fmt(f)
533 }
534}
535
536impl<'str, 'ctx> Value<'str, 'ctx> for InstructionMutRef<'str, 'ctx> {
537 fn id(&self) -> ValueId {
538 self.id()
539 }
540
541 fn size(&self) -> usize {
542 self.as_ref().size()
543 }
544}
545
546impl<'str, 'ctx, H: QCodeMut<'str>> Renameable<'str, 'ctx> for BaseRef<H, InstructionId>
548where
549 Self: Named,
550{
551 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
552 self.rename_local(name)
553 }
554}
555
556pub struct InstructionStatement<'a, 'str, 'ctx, R = ModuleView<'ctx, 'str>>(
558 &'a InstructionRef<'str, 'ctx, R>,
559);
560
561impl<'str: 'ctx, 'ctx, R> Display for InstructionStatement<'_, 'str, 'ctx, R>
562where
563 R: QCodeView<'ctx, 'str>,
564{
565 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
566 for token in segment::instruction_segments(self.0) {
569 write!(f, "{}", token.text)?;
570 }
571 Ok(())
572 }
573}