use crate::value::QCodeMut;
use crate::{
context::Context,
error::Result,
space::{MemorySpaceId, Space, SpaceId, SpaceRef, SpaceType},
types::TypeId,
value::{
BlockId, BlockRef, FunctionId, FunctionRef, LocalBlockId, ModuleView, QCodeView, Value,
ValueId,
util::{
base_ref::{BaseRef, WithCtx, WithCtxMut},
named::{Named, Renameable},
},
},
};
use jstd::Identifier;
use std::{
borrow::Cow,
fmt::{Display, Formatter},
marker::PhantomData,
};
mod aggregate;
mod assert;
mod binop;
mod bits;
mod casting;
mod flags;
pub(crate) mod intrinsic;
mod map;
mod memory;
mod mnemonic;
mod pcode_op;
mod scan;
pub mod segment;
mod terminator;
mod unop;
pub use aggregate::{Extract, Gep, Tuple};
pub use assert::Assert;
pub use binop::{Binary, Binop, FloatBinop, IntBinop};
pub use casting::{FloatToFloat, FloatToInt, IntToFloat, Range, Sext, Zext};
pub use flags::{Carry, IsFloatNaN, LzCount, PopCount, SBorrow, SCarry};
pub use intrinsic::{
Intrinsic, IntrinsicApp, IntrinsicId, IntrinsicRegistration, RootOp, Simplified,
recognizers_for,
};
pub use map::Map;
pub use memory::{Load, Store};
pub use mnemonic::Mnemonic;
pub use pcode_op::{PCodeOp, PCodeOpId, VM_INTERRUPT};
pub use scan::Scan;
pub use terminator::{
Apply, BadInsn, Branch, BranchInd, CBranch, Call, CallInd, CallTag, Callee, Return,
ReturnValue, Switch, SwitchArm, TailCall,
};
pub use unop::{Unary, Unop};
#[derive(Identifier)]
pub struct LocalInsnId(u32);
crate::composite_id!(InstructionId, LocalInsnId);
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct Instruction<'str> {
pub(crate) name: Option<Cow<'str, str>>,
pub(crate) type_id: TypeId,
mnemonic: Mnemonic,
pub(crate) parent: Option<LocalBlockId>,
address: Option<u64>,
_marker: std::marker::PhantomData<&'str ()>,
}
impl<'str> Instruction<'str> {
pub(crate) fn new(type_id: TypeId, mnemonic: Mnemonic) -> Self {
Self {
name: None,
parent: None,
type_id,
mnemonic,
address: None,
_marker: std::marker::PhantomData,
}
}
pub fn mnemonic(&self) -> &Mnemonic {
&self.mnemonic
}
pub(crate) fn mnemonic_mut(&mut self) -> &mut Mnemonic {
&mut self.mnemonic
}
pub(crate) fn set_address(&mut self, address: u64) {
self.address = Some(address);
}
pub fn from_id<'ctx>(
ctx: &'ctx Context<'str>,
id: InstructionId,
) -> InstructionRef<'str, 'ctx> {
InstructionRef::new(ModuleView::new(ctx), id)
}
pub fn from_id_mut<'ctx>(
ctx: &'ctx mut Context<'str>,
id: InstructionId,
) -> InstructionMutRef<'str, 'ctx> {
InstructionMutRef::from_id(ctx, id)
}
}
impl<'s, 'ctx: 's, 'str: 'ctx, R> InstructionRef<'str, 'ctx, R>
where
R: QCodeView<'ctx, 'str>,
{
fn inner(&'s self) -> &'ctx Instruction<'str> {
self.view.instruction(self.id)
}
pub fn name(&'s self) -> Option<&'ctx str> {
self.inner().name.as_deref()
}
pub fn type_id(&'s self) -> TypeId {
self.inner().type_id
}
pub fn size(&'s self) -> usize {
self.view.shared().types.size_of(self.inner().type_id)
}
pub fn parent(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
self.inner()
.parent
.map(|local| BlockRef::new(self.view, BlockId::new(self.id.func, local)))
}
pub fn block(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
self.parent()
}
pub fn function(&'s self) -> Option<FunctionRef<'str, 'ctx, R>> {
self.parent().and_then(|block| block.parent())
}
pub fn mnemonic(&'s self) -> &'ctx Mnemonic {
&self.inner().mnemonic
}
pub fn operands(&'s self) -> smallvec::SmallVec<[ValueId; 2]> {
let func = self.id.func;
self.mnemonic()
.args()
.into_iter()
.map(|v| v.qualify(func))
.collect()
}
pub fn address(&'s self) -> Option<u64> {
self.inner().address
}
pub fn space(&'s self) -> Option<SpaceRef<'ctx>> {
self.view
.shared()
.types
.space_of(self.inner().type_id)
.and_then(MemorySpaceId::shared)
.map(|id| Space::from_id(self.view.shared(), id))
}
pub fn memory_space(&'s self) -> Option<MemorySpaceId> {
self.view.shared().types.space_of(self.inner().type_id)
}
pub fn opcode(&'s self) -> &'static str {
self.mnemonic().opcode()
}
pub fn is_terminator(&'s self) -> bool {
self.mnemonic().is_terminator()
}
fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
let ty = self.view.shared().types.type_name(self.type_id());
if let Some(name) = self.name() {
write!(f, "{ty} %{name}")
} else {
let id: usize = self.id.local.into();
write!(f, "{ty} %tmp{id:x}")
}
}
}
impl<'str, H: QCodeMut<'str>> BaseRef<H, InstructionId> {
pub fn set_result_type(&mut self, new_type: TypeId) {
let current = self.ctx.body(self.id.func).insn(self.id).type_id;
let (current_size, new_size) = {
let types = &self.ctx.shr().types;
(types.size_of(current), types.size_of(new_type))
};
assert!(
current_size == 0 || current_size == new_size,
"cannot change instruction result type: size {current_size} → {new_size}",
);
self.ctx.instruction_mut(self.id).type_id = new_type;
}
pub fn rename_local(&mut self, name: Cow<'str, str>) -> Result<()> {
let old_name = self
.ctx
.body(self.id.func)
.insn(self.id)
.name
.as_deref()
.map(str::to_owned);
self.ctx
.register_body_name(self.id.into(), name.clone(), old_name.as_deref())?;
self.ctx.instruction_mut(self.id).name = Some(name);
Ok(())
}
}
#[derive(Clone, Copy)]
pub struct InstructionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
pub id: InstructionId,
pub(in crate::value) view: R,
marker: PhantomData<&'ctx &'str ()>,
}
impl<'str, 'ctx, R> InstructionRef<'str, 'ctx, R> {
pub fn new(view: R, id: InstructionId) -> Self {
Self {
id,
view,
marker: PhantomData,
}
}
pub fn id(&self) -> ValueId {
self.id.into()
}
pub fn as_statement(&self) -> InstructionStatement<'_, 'str, 'ctx, R> {
InstructionStatement(self)
}
}
impl<'str, 'ctx> InstructionRef<'str, 'ctx> {
pub fn from_id(ctx: &'ctx Context<'str>, id: InstructionId) -> Self {
Self::new(ModuleView::new(ctx), id)
}
pub fn from_mnemonic(
ctx: &'ctx mut Context<'str>,
func: FunctionId,
mnemonic: Mnemonic,
size: usize,
) -> Self {
let type_id = ctx.shared.types.get_or_make_int(size);
let insn = Instruction::new(type_id, mnemonic);
let id = ctx.push_insn(func, insn);
InstructionRef::new(ModuleView::new(ctx), id)
}
pub fn from_mnemonic_with_type(
ctx: &'ctx mut Context<'str>,
func: FunctionId,
mnemonic: Mnemonic,
type_id: TypeId,
) -> Self {
let insn = Instruction::new(type_id, mnemonic);
let id = ctx.push_insn(func, insn);
InstructionRef::new(ModuleView::new(ctx), id)
}
pub fn from_mnemonic_with_space(
ctx: &'ctx mut Context<'str>,
func: FunctionId,
mnemonic: Mnemonic,
size: usize,
_space: Option<SpaceId>,
) -> Self {
let type_id = ctx.shared.types.get_or_make_int(size);
let insn = Instruction::new(type_id, mnemonic);
let id = ctx.push_insn(func, insn);
InstructionRef::new(ModuleView::new(ctx), id)
}
}
impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for InstructionRef<'str, 'ctx> {
fn ctx(&'s self) -> &'ctx Context<'str> {
self.view.context()
}
}
impl<'str: 'ctx, 'ctx, R> Named for InstructionRef<'str, 'ctx, R>
where
R: QCodeView<'ctx, 'str>,
{
fn name(&self) -> Option<&str> {
self.view.instruction(self.id).name.as_deref()
}
}
impl<'str: 'ctx, 'ctx, R> Display for InstructionRef<'str, 'ctx, R>
where
R: QCodeView<'ctx, 'str>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
InstructionRef::fmt(self, f)
}
}
impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for InstructionRef<'str, 'ctx, R>
where
R: QCodeView<'ctx, 'str>,
{
fn id(&self) -> ValueId {
self.id()
}
fn size(&self) -> usize {
InstructionRef::size(self)
}
}
pub type InstructionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, InstructionId>;
impl<'str, 'ctx> InstructionMutRef<'str, 'ctx> {
pub fn as_ref(&self) -> InstructionRef<'str, '_> {
InstructionRef::new(ModuleView::new(self.ctx), self.id)
}
fn inner(&self) -> &Instruction<'str> {
self.ctx.instruction(self.id)
}
pub fn inner_mut(&mut self) -> &mut Instruction<'str> {
self.ctx.instruction_mut(self.id)
}
pub fn mnemonic_mut(&mut self) -> &mut Mnemonic {
&mut self.inner_mut().mnemonic
}
pub fn set_mnemonic(&mut self, mnemonic: Mnemonic) {
let old_args = self.inner().mnemonic.args();
let new_args = mnemonic.args();
let func = self.id.func;
for arg in old_args {
if let Some(users) = self.ctx.bodies[func].users.get_mut(&arg) {
users.retain(|&local| local != self.id.localize(func));
}
}
for arg in new_args {
self.ctx.bodies[func]
.users
.entry(arg)
.or_default()
.push(self.id.localize(func));
}
self.inner_mut().mnemonic = mnemonic;
}
pub fn address_mut(&mut self) -> &mut Option<u64> {
&mut self.inner_mut().address
}
pub fn set_address(&mut self, address: u64) {
*self.address_mut() = Some(address);
}
pub fn set_type(&mut self, new_type: TypeId) {
let current = self.inner().type_id;
let current_size = self.ctx.shared.types.size_of(current);
let new_size = self.ctx.shared.types.size_of(new_type);
if current_size != 0 && current_size != new_size {
panic!(
"Cannot change type of instruction {}: size {} → {}",
self, current_size, new_size
);
}
self.inner_mut().type_id = new_type;
}
pub fn set_type_resized(&mut self, new_type: TypeId) {
self.inner_mut().type_id = new_type;
}
pub fn set_space(&mut self, space: SpaceId) {
if matches!(
Space::from_id(&self.ctx.shared, space).ty,
SpaceType::Register
) {
return;
}
let size = self.ctx.shared.types.size_of(self.inner().type_id);
let type_id = self.ctx.shared.types.get_or_make_space_address(size, space);
self.inner_mut().type_id = type_id;
}
}
impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for InstructionMutRef<'str, 'ctx> {
fn ctx(&'s self) -> &'s Context<'str> {
self.ctx
}
}
impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for InstructionMutRef<'str, 'ctx> {
fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
self.ctx
}
}
impl Named for InstructionMutRef<'_, '_> {
fn name(&self) -> Option<&str> {
self.ctx.instruction(self.id).name.as_deref()
}
}
impl Display for InstructionMutRef<'_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.as_ref().fmt(f)
}
}
impl<'str, 'ctx> Value<'str, 'ctx> for InstructionMutRef<'str, 'ctx> {
fn id(&self) -> ValueId {
self.id()
}
fn size(&self) -> usize {
self.as_ref().size()
}
}
impl<'str, 'ctx, H: QCodeMut<'str>> Renameable<'str, 'ctx> for BaseRef<H, InstructionId>
where
Self: Named,
{
fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
self.rename_local(name)
}
}
pub struct InstructionStatement<'a, 'str, 'ctx, R = ModuleView<'ctx, 'str>>(
&'a InstructionRef<'str, 'ctx, R>,
);
impl<'str: 'ctx, 'ctx, R> Display for InstructionStatement<'_, 'str, 'ctx, R>
where
R: QCodeView<'ctx, 'str>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for token in segment::instruction_segments(self.0) {
write!(f, "{}", token.text)?;
}
Ok(())
}
}