Skip to main content

solar_codegen/backend/evm/assembler/
inst.rs

1use solar_data_structures::{index::Idx, newtype_index};
2
3newtype_index! {
4    /// A label identifier.
5    pub struct Label;
6
7    /// A deferred constant identifier.
8    ///
9    /// Deferred constants are immediates whose final value is only known after
10    /// bytecode emission has observed lazy backend state, such as exact spill
11    /// slot allocation. They must be resolved before assembly.
12    pub struct DeferredConst;
13
14    /// An interned push immediate identifier.
15    pub(in crate::backend::evm) struct PushValueId;
16}
17
18pub(super) trait AsmIndex: Idx {
19    const NAME: &'static str;
20
21    fn inst_payload(self) -> u32 {
22        let index =
23            u32::try_from(self.index()).unwrap_or_else(|_| panic!("{} overflow", Self::NAME));
24        assert!(index <= AsmInst::PAYLOAD_MASK, "{} overflow", Self::NAME);
25        index
26    }
27
28    fn from_inst_payload(payload: u32) -> Self {
29        Self::from_usize(payload as usize)
30    }
31}
32
33impl AsmIndex for Label {
34    const NAME: &'static str = "assembler label index";
35}
36
37impl AsmIndex for DeferredConst {
38    const NAME: &'static str = "assembler deferred constant index";
39}
40
41impl AsmIndex for PushValueId {
42    const NAME: &'static str = "assembler push value index";
43}
44
45/// An instruction in the assembler.
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub(in crate::backend::evm) struct AsmInst(u32);
48
49impl AsmInst {
50    pub(in crate::backend::evm) const PLACEHOLDER: Self = Self(0);
51    pub(in crate::backend::evm) const PAYLOAD_MASK: u32 = 0x0fff_ffff;
52    const INLINE_PUSH_MAX: u32 = 0x7fff_ffff;
53    const TAG_MASK: u32 = 0xf000_0000;
54    const TAG_OP: u32 = 0x8000_0000;
55    const TAG_PUSH: u32 = 0x9000_0000;
56    const TAG_PUSH_LABEL: u32 = 0xa000_0000;
57    const TAG_PUSH_DEFERRED: u32 = 0xb000_0000;
58    const TAG_PUSH_IMMUTABLE: u32 = 0xc000_0000;
59    const TAG_LABEL: u32 = 0xd000_0000;
60
61    pub(in crate::backend::evm) fn op(opcode: u8) -> Self {
62        Self(Self::TAG_OP | u32::from(opcode))
63    }
64
65    pub(in crate::backend::evm) fn push_inline(value: u32) -> Option<Self> {
66        (value <= Self::INLINE_PUSH_MAX).then_some(Self(value))
67    }
68
69    pub(in crate::backend::evm) fn push(index: PushValueId) -> Self {
70        Self::tagged(Self::TAG_PUSH, index.inst_payload())
71    }
72
73    pub(in crate::backend::evm) fn push_label(label: Label) -> Self {
74        Self::tagged(Self::TAG_PUSH_LABEL, label.inst_payload())
75    }
76
77    pub(in crate::backend::evm) fn push_deferred(id: DeferredConst) -> Self {
78        Self::tagged(Self::TAG_PUSH_DEFERRED, id.inst_payload())
79    }
80
81    pub(in crate::backend::evm) fn push_immutable(id: u32) -> Self {
82        Self::tagged(Self::TAG_PUSH_IMMUTABLE, id)
83    }
84
85    pub(in crate::backend::evm) fn label(label: Label) -> Self {
86        Self::tagged(Self::TAG_LABEL, label.inst_payload())
87    }
88
89    fn tagged(tag: u32, payload: u32) -> Self {
90        assert!(payload <= Self::PAYLOAD_MASK, "assembler instruction payload overflow");
91        Self(tag | payload)
92    }
93
94    pub(in crate::backend::evm) fn kind(self) -> AsmInstKind {
95        if self.0 <= Self::INLINE_PUSH_MAX {
96            return AsmInstKind::PushInline(self.0);
97        }
98
99        let payload = self.0 & Self::PAYLOAD_MASK;
100        match self.0 & Self::TAG_MASK {
101            Self::TAG_OP => AsmInstKind::Op(payload as u8),
102            Self::TAG_PUSH => AsmInstKind::Push(PushValueId::from_inst_payload(payload)),
103            Self::TAG_PUSH_LABEL => AsmInstKind::PushLabel(Label::from_inst_payload(payload)),
104            Self::TAG_PUSH_DEFERRED => {
105                AsmInstKind::PushDeferred(DeferredConst::from_inst_payload(payload))
106            }
107            Self::TAG_PUSH_IMMUTABLE => AsmInstKind::PushImmutable(payload),
108            Self::TAG_LABEL => AsmInstKind::Label(Label::from_inst_payload(payload)),
109            _ => unreachable!("invalid assembler instruction tag"),
110        }
111    }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub(in crate::backend::evm) enum AsmInstKind {
116    Op(u8),
117    PushInline(u32),
118    Push(PushValueId),
119    PushLabel(Label),
120    PushDeferred(DeferredConst),
121    PushImmutable(u32),
122    Label(Label),
123}