1use crate::CompilerError;
4use crate::compiler::Executable;
5use arrayvec::ArrayVec;
6use dynasmrt::mmap::MutableBuffer;
7use dynasmrt::{
8 AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, components::PatchLoc,
9 relocations::Relocation,
10};
11use std::marker::PhantomData;
12
13pub(crate) use dynasmrt::mmap::ExecutableBuffer;
14
15#[derive(Debug, Clone)]
25pub(crate) struct Assembler<R: Relocation, const S: usize> {
26 buffer: ArrayVec<u8, S>,
28 target: Option<AssemblyOffset>,
30 phantom: PhantomData<R>,
32}
33
34impl<R: Relocation, const S: usize> Assembler<R, S> {
35 #[inline(always)]
37 pub(crate) fn entry() -> AssemblyOffset {
38 AssemblyOffset(0)
39 }
40
41 #[inline(always)]
43 pub(crate) fn len(&self) -> usize {
44 self.buffer.len()
45 }
46
47 #[inline(always)]
49 pub(crate) fn new() -> Self {
50 Self {
51 buffer: ArrayVec::new(),
52 target: None,
53 phantom: PhantomData,
54 }
55 }
56
57 #[inline(always)]
69 pub(crate) fn finalize(&self) -> Result<Executable, CompilerError> {
70 let mut mut_buf = MutableBuffer::new(self.buffer.len())?;
77 mut_buf.set_len(self.buffer.len());
78 mut_buf[..].copy_from_slice(&self.buffer);
79 Ok(Executable {
80 buffer: mut_buf.make_exec()?,
81 })
82 }
83}
84
85impl std::fmt::Debug for Executable {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 write!(
89 f,
90 "Executable[{}; {} ]",
91 std::env::consts::ARCH,
92 hex::encode(&self.buffer[..])
93 )
94 }
95}
96
97impl<R: Relocation, const S: usize> DynasmLabelApi for Assembler<R, S> {
99 type Relocation = R;
100
101 #[inline(always)]
102 fn local_label(&mut self, name: &'static str) {
103 debug_assert_eq!(name, "target");
104 self.target = Some(self.offset());
105 }
106
107 #[inline(always)]
108 fn backward_relocation(
109 &mut self,
110 name: &'static str,
111 target_offset: isize,
112 field_offset: u8,
113 ref_offset: u8,
114 kind: R,
115 ) {
116 debug_assert_eq!(name, "target");
117 let target = self
118 .target
119 .expect("generated programs always have a target before branch");
120 let loc = PatchLoc::new(self.offset(), target_offset, field_offset, ref_offset, kind);
122 let buf = &mut self.buffer[loc.range(0)];
123 loc.patch(buf, 0, target.0)
124 .expect("program relocations are always in range");
125 }
126
127 fn global_label(&mut self, _name: &'static str) {
128 unreachable!();
129 }
130
131 fn dynamic_label(&mut self, _id: DynamicLabel) {
132 unreachable!();
133 }
134
135 fn value_relocation(&mut self, _target: usize, _field_offset: u8, _ref_offset: u8, _kind: R) {
136 unreachable!();
137 }
138
139 fn global_relocation(
140 &mut self,
141 _name: &'static str,
142 _target_offset: isize,
143 _field_offset: u8,
144 _ref_offset: u8,
145 _kind: R,
146 ) {
147 unreachable!();
148 }
149
150 fn dynamic_relocation(
151 &mut self,
152 _id: DynamicLabel,
153 _target_offset: isize,
154 _field_offset: u8,
155 _ref_offset: u8,
156 _kind: R,
157 ) {
158 unreachable!();
159 }
160
161 fn forward_relocation(
162 &mut self,
163 _name: &'static str,
164 _target_offset: isize,
165 _field_offset: u8,
166 _ref_offset: u8,
167 _kind: R,
168 ) {
169 unreachable!();
170 }
171}
172
173impl<R: Relocation, const S: usize> Extend<u8> for Assembler<R, S> {
174 #[inline(always)]
175 fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
176 self.buffer.extend(iter);
177 }
178}
179
180impl<'a, R: Relocation, const S: usize> Extend<&'a u8> for Assembler<R, S> {
181 #[inline(always)]
182 fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
183 for byte in iter {
184 self.buffer.push(*byte);
185 }
186 }
187}
188
189impl<R: Relocation, const S: usize> DynasmApi for Assembler<R, S> {
190 #[inline(always)]
191 fn offset(&self) -> AssemblyOffset {
192 AssemblyOffset(self.buffer.len())
193 }
194
195 #[inline(always)]
196 fn push(&mut self, byte: u8) {
197 self.buffer.push(byte);
198 }
199
200 fn align(&mut self, _alignment: usize, _with: u8) {
201 unreachable!();
202 }
203}