1use alloc::{format, string::ToString, vec::Vec};
2
3use crate::{
4 AttrPrinter, attributes::AttrParser, derive::DialectAttribute,
5 dialects::debuginfo::DebugInfoDialect, interner::Symbol, parse::ParserExt, print::AsmPrinter,
6};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum FrameBase {
11 Local(u32),
12 Global(u32),
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub enum ResolvedFrameBase {
18 Local(i16),
19 Global(u32),
20}
21
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24#[repr(u8)]
25pub enum ExpressionOp {
26 WasmLocal(u32) = 0,
28 WasmGlobal(u32) = 1,
30 WasmStack(u32) = 2,
32 ConstU64(u64) = 3,
34 ConstS64(i64) = 4,
36 PlusUConst(u64) = 5,
38 Minus = 6,
40 Plus = 7,
42 Deref = 8,
44 StackValue = 9,
46 Piece(u64) = 10,
48 BitPiece { size: u64, offset: u64 } = 11,
50 Address { address: u64 } = 13,
52 FrameBase { base: FrameBase, byte_offset: i64 } = 14,
54 ResolvedFrameBase {
56 base: ResolvedFrameBase,
57 byte_offset: i64,
58 } = 15,
59 Unsupported(Symbol) = u8::MAX,
61}
62
63impl ExpressionOp {
64 const fn tag(&self) -> u8 {
65 unsafe { *(self as *const Self).cast::<u8>() }
72 }
73}
74
75impl miden_core::serde::Serializable for ExpressionOp {
76 fn write_into<W: miden_core::serde::ByteWriter>(&self, target: &mut W) {
77 target.write_u8(self.tag());
78 match self {
79 Self::WasmLocal(idx) | Self::WasmGlobal(idx) | Self::WasmStack(idx) => {
80 target.write_u32(*idx);
81 }
82 Self::ConstU64(val) | Self::PlusUConst(val) | Self::Piece(val) => {
83 target.write_u64(*val);
84 }
85 Self::ConstS64(val) => {
86 target.write_u64(*val as u64);
87 }
88 Self::Minus | Self::Plus | Self::Deref | Self::StackValue => (),
89 Self::BitPiece { size, offset } => {
90 target.write_u64(*size);
91 target.write_u64(*offset);
92 }
93 Self::FrameBase { base, byte_offset } => {
94 match base {
95 FrameBase::Local(index) => {
96 target.write_u8(0);
97 target.write_u32(*index);
98 }
99 FrameBase::Global(index) => {
100 target.write_u8(1);
101 target.write_u32(*index);
102 }
103 }
104 target.write_u64(*byte_offset as u64);
105 }
106 Self::ResolvedFrameBase { base, byte_offset } => {
107 match base {
108 ResolvedFrameBase::Local(offset) => {
109 target.write_u8(0);
110 target.write_bytes(&offset.to_le_bytes());
111 }
112 ResolvedFrameBase::Global(address) => {
113 target.write_u8(1);
114 target.write_u32(*address);
115 }
116 }
117 target.write_u64(*byte_offset as u64);
118 }
119 Self::Address { address } => {
120 target.write_u64(*address);
121 }
122 Self::Unsupported(name) => {
123 target.write_usize(name.as_str().len());
124 target.write_bytes(name.as_str().as_bytes());
125 }
126 }
127 }
128}
129
130impl miden_core::serde::Deserializable for ExpressionOp {
131 fn read_from<R: miden_core::serde::ByteReader>(
132 source: &mut R,
133 ) -> Result<Self, miden_core::serde::DeserializationError> {
134 use miden_core::serde::DeserializationError;
135
136 Ok(match source.read_u8()? {
137 0 => Self::WasmLocal(u32::read_from(source)?),
138 1 => Self::WasmGlobal(u32::read_from(source)?),
139 2 => Self::WasmStack(u32::read_from(source)?),
140 3 => Self::ConstU64(u64::read_from(source)?),
141 4 => Self::ConstS64(u64::read_from(source)? as i64),
142 5 => Self::PlusUConst(u64::read_from(source)?),
143 6 => Self::Minus,
144 7 => Self::Plus,
145 8 => Self::Deref,
146 9 => Self::StackValue,
147 10 => Self::Piece(u64::read_from(source)?),
148 11 => {
149 let size = u64::read_from(source)?;
150 let offset = u64::read_from(source)?;
151 Self::BitPiece { size, offset }
152 }
153 12 => {
154 let global_index = u32::read_from(source)?;
156 let byte_offset = u64::read_from(source)? as i64;
157 Self::FrameBase {
158 base: FrameBase::Global(global_index),
159 byte_offset,
160 }
161 }
162 13 => {
163 let address = u64::read_from(source)?;
164 Self::Address { address }
165 }
166 14 => {
167 let base = match source.read_u8()? {
168 0 => FrameBase::Local(u32::read_from(source)?),
169 1 => FrameBase::Global(u32::read_from(source)?),
170 tag => {
171 return Err(DeserializationError::InvalidValue(format!(
172 "invalid frame-base tag '{tag}'"
173 )));
174 }
175 };
176 let byte_offset = u64::read_from(source)? as i64;
177 Self::FrameBase { base, byte_offset }
178 }
179 15 => {
180 let base = match source.read_u8()? {
181 0 => {
182 let bytes = source.read_array::<2>()?;
183 ResolvedFrameBase::Local(i16::from_le_bytes(bytes))
184 }
185 1 => ResolvedFrameBase::Global(u32::read_from(source)?),
186 tag => {
187 return Err(DeserializationError::InvalidValue(format!(
188 "invalid resolved frame-base tag '{tag}'"
189 )));
190 }
191 };
192 let byte_offset = u64::read_from(source)? as i64;
193 Self::ResolvedFrameBase { base, byte_offset }
194 }
195 u8::MAX => {
196 let len = usize::read_from(source)?;
197 let bytes = source.read_slice(len)?;
198 let s = core::str::from_utf8(bytes)
199 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
200 Self::Unsupported(Symbol::intern(s))
201 }
202 invalid => {
203 return Err(DeserializationError::InvalidValue(format!(
204 "unknown DIExpressionOp tag '{invalid}'"
205 )));
206 }
207 })
208 }
209
210 fn min_serialized_size() -> usize {
211 1
212 }
213}
214
215impl crate::formatter::PrettyPrint for ExpressionOp {
216 fn render(&self) -> crate::formatter::Document {
217 use crate::formatter::*;
218 match self {
219 Self::WasmLocal(idx) => {
220 const_text("DW_OP_WASM_local") + const_text("(") + display(idx) + const_text(")")
221 }
222 Self::WasmGlobal(idx) => {
223 const_text("DW_OP_WASM_global") + const_text("(") + display(idx) + const_text(")")
224 }
225 Self::WasmStack(idx) => {
226 const_text("DW_OP_WASM_stack") + const_text("(") + display(idx) + const_text(")")
227 }
228 Self::ConstU64(val) => {
229 const_text("DW_OP_constu") + const_text("(") + display(val) + const_text(")")
230 }
231 Self::ConstS64(val) => {
232 const_text("DW_OP_consts") + const_text("(") + display(val) + const_text(")")
233 }
234 Self::PlusUConst(val) => {
235 const_text("DW_OP_plus_uconst") + const_text("(") + display(val) + const_text(")")
236 }
237 Self::Minus => const_text("DW_OP_minus"),
238 Self::Plus => const_text("DW_OP_plus"),
239 Self::Deref => const_text("DW_OP_deref"),
240 Self::StackValue => const_text("DW_OP_stack_value"),
241 Self::Piece(size) => {
242 const_text("DW_OP_piece") + const_text("(") + display(*size) + const_text(")")
243 }
244 Self::BitPiece { size, offset } => {
245 const_text("DW_OP_bit_piece")
246 + const_text("(")
247 + display(*size)
248 + const_text(",")
249 + display(*offset)
250 + const_text(")")
251 }
252 Self::FrameBase { base, byte_offset } => match base {
253 FrameBase::Local(index) => {
254 const_text("DW_OP_fbreg(local, ")
255 + text(format!("{index}{byte_offset:+}"))
256 + const_text(")")
257 }
258 FrameBase::Global(index) => {
259 const_text("DW_OP_fbreg(global, ")
260 + text(format!("{index}{byte_offset:+}"))
261 + const_text(")")
262 }
263 },
264 Self::ResolvedFrameBase { base, byte_offset } => match base {
265 ResolvedFrameBase::Local(offset) => {
266 const_text("MIDEN_OP_fbreg(local, ")
267 + text(format!("{offset}{byte_offset:+}"))
268 + const_text(")")
269 }
270 ResolvedFrameBase::Global(address) => {
271 const_text("MIDEN_OP_fbreg(global, ")
272 + text(format!("{address}{byte_offset:+}"))
273 + const_text(")")
274 }
275 },
276 Self::Address { address } => {
277 const_text("DW_OP_addr") + const_text("(") + display(*address) + const_text(")")
278 }
279 Self::Unsupported(name) => const_text(name.as_str()),
280 }
281 }
282}
283
284impl ExpressionOp {
285 fn parse(parser: &mut dyn crate::parse::Parser<'_>) -> crate::parse::ParseResult<Self> {
286 use crate::parse::Token;
287
288 let mut op = parser
289 .token_stream_mut()
290 .expect_map("DIExpression operator", |tok| match tok {
291 Token::BareIdent(id) => match id {
292 "DW_OP_WASM_local" => Some(ExpressionOp::WasmLocal(0)),
293 "DW_OP_WASM_global" => Some(ExpressionOp::WasmGlobal(0)),
294 "DW_OP_WASM_stack" => Some(ExpressionOp::WasmStack(0)),
295 "DW_OP_constu" => Some(ExpressionOp::ConstU64(0)),
296 "DW_OP_consts" => Some(ExpressionOp::ConstS64(0)),
297 "DW_OP_plus_uconst" => Some(ExpressionOp::PlusUConst(0)),
298 "DW_OP_minus" => Some(ExpressionOp::Minus),
299 "DW_OP_plus" => Some(ExpressionOp::Plus),
300 "DW_OP_deref" => Some(ExpressionOp::Deref),
301 "DW_OP_stack_value" => Some(ExpressionOp::StackValue),
302 "DW_OP_piece" => Some(ExpressionOp::Piece(0)),
303 "DW_OP_bit_piece" => Some(ExpressionOp::BitPiece { size: 0, offset: 0 }),
304 "DW_OP_fbreg" => Some(ExpressionOp::FrameBase {
305 base: FrameBase::Global(0),
306 byte_offset: 0,
307 }),
308 "DW_OP_addr" => Some(ExpressionOp::Address { address: 0 }),
309 other => Some(ExpressionOp::Unsupported(Symbol::intern(other))),
310 },
311 _ => None,
312 })?
313 .into_inner();
314 match &mut op {
315 ExpressionOp::WasmLocal(idx)
316 | ExpressionOp::WasmGlobal(idx)
317 | ExpressionOp::WasmStack(idx) => {
318 parser.parse_lparen()?;
319 *idx = parser.parse_decimal_integer::<u32>()?.into_inner();
320 parser.parse_rparen()?;
321 }
322 ExpressionOp::ConstU64(val)
323 | ExpressionOp::PlusUConst(val)
324 | ExpressionOp::Piece(val)
325 | ExpressionOp::Address { address: val } => {
326 parser.parse_lparen()?;
327 *val = parser.parse_decimal_integer::<u64>()?.into_inner();
328 parser.parse_rparen()?;
329 }
330 ExpressionOp::ConstS64(val) => {
331 parser.parse_lparen()?;
332 *val = parser.parse_decimal_integer::<i64>()?.into_inner();
333 parser.parse_rparen()?;
334 }
335 ExpressionOp::Minus
336 | ExpressionOp::Plus
337 | ExpressionOp::Deref
338 | ExpressionOp::StackValue
339 | ExpressionOp::Unsupported(_) => (),
340 ExpressionOp::BitPiece { size, offset } => {
341 parser.parse_lparen()?;
342 *size = parser.parse_decimal_integer::<u64>()?.into_inner();
343 parser.parse_comma()?;
344 *offset = parser.parse_decimal_integer::<u64>()?.into_inner();
345 parser.parse_rparen()?;
346 }
347 ExpressionOp::FrameBase { base, byte_offset } => {
348 parser.parse_lparen()?;
349 let is_local = parser
350 .token_stream_mut()
351 .expect_map("'local' or 'global' modifier", |tok| match tok {
352 Token::BareIdent("local") => Some(true),
353 Token::BareIdent("global") => Some(false),
354 _ => None,
355 })?
356 .into_inner();
357 parser.parse_comma()?;
358 let index = parser.parse_decimal_integer::<u32>()?.into_inner();
359 let negative = parser
361 .token_stream_mut()
362 .expect_map("'+' or '-' offset sign", |tok| match tok {
363 Token::Plus => Some(false),
364 Token::Minus => Some(true),
365 _ => None,
366 })?
367 .into_inner();
368 let (offset_span, magnitude) = parser.parse_decimal_integer::<u64>()?.into_parts();
369 let signed = if negative {
370 -(magnitude as i128)
371 } else {
372 magnitude as i128
373 };
374 *byte_offset = i64::try_from(signed).map_err(|_| {
375 crate::parse::ParserError::InvalidIntegerLiteral {
376 span: offset_span,
377 reason: format!("byte offset '{signed}' is out of range for i64"),
378 }
379 })?;
380 *base = if is_local {
381 FrameBase::Local(index)
382 } else {
383 FrameBase::Global(index)
384 };
385 parser.parse_rparen()?;
386 }
387 ExpressionOp::ResolvedFrameBase { .. } => unreachable!(
388 "resolved frame-base expressions are produced only during MASM lowering"
389 ),
390 }
391
392 Ok(op)
393 }
394}
395
396#[derive(DialectAttribute, Clone, Debug, Default, PartialEq, Eq, Hash)]
398#[attribute(dialect = DebugInfoDialect, implements(AttrPrinter))]
399pub struct Expression {
400 pub operations: Vec<ExpressionOp>,
401}
402
403impl Expression {
404 pub fn new() -> Self {
405 Self {
406 operations: Vec::new(),
407 }
408 }
409
410 pub fn with_ops(operations: Vec<ExpressionOp>) -> Self {
411 Self { operations }
412 }
413
414 pub fn is_empty(&self) -> bool {
415 self.operations.is_empty()
416 }
417}
418
419impl miden_core::serde::Serializable for Expression {
420 fn write_into<W: miden_core::serde::ByteWriter>(&self, target: &mut W) {
421 target.write_usize(self.operations.len());
422 for op in self.operations.iter() {
423 target.write(op);
424 }
425 }
426}
427
428impl miden_core::serde::Deserializable for Expression {
429 fn read_from<R: miden_core::serde::ByteReader>(
430 source: &mut R,
431 ) -> Result<Self, miden_core::serde::DeserializationError> {
432 let len = usize::read_from(source)?;
433 let operations = source.read_many_iter(len)?.collect::<Result<Vec<_>, _>>()?;
434 Ok(Self::with_ops(operations))
435 }
436}
437
438impl AttrPrinter for ExpressionAttr {
439 fn print(&self, printer: &mut AsmPrinter<'_>) {
440 use crate::formatter::*;
441
442 if self.operations.is_empty() {
443 *printer += const_text("[]");
444 return;
445 }
446
447 *printer += const_text("[");
448 for (i, op) in self.operations.iter().enumerate() {
449 if i > 0 {
450 *printer += const_text(", ");
451 }
452 match op {
453 ExpressionOp::WasmLocal(idx) => {
454 *printer += const_text("DW_OP_WASM_local");
455 *printer += const_text("(") + display(*idx) + const_text(")");
456 }
457 ExpressionOp::WasmGlobal(idx) => {
458 *printer += const_text("DW_OP_WASM_global");
459 *printer += const_text("(") + display(*idx) + const_text(")");
460 }
461 ExpressionOp::WasmStack(idx) => {
462 *printer += const_text("DW_OP_WASM_stack");
463 *printer += const_text("(") + display(*idx) + const_text(")");
464 }
465 ExpressionOp::ConstU64(val) => {
466 *printer += const_text("DW_OP_constu");
467 *printer += const_text("(") + display(*val) + const_text(")");
468 }
469 ExpressionOp::ConstS64(val) => {
470 *printer += const_text("DW_OP_consts");
471 *printer += const_text("(") + display(*val) + const_text(")");
472 }
473 ExpressionOp::PlusUConst(val) => {
474 *printer += const_text("DW_OP_plus_uconst");
475 *printer += const_text("(") + display(*val) + const_text(")");
476 }
477 ExpressionOp::Minus => *printer += const_text("DW_OP_minus"),
478 ExpressionOp::Plus => *printer += const_text("DW_OP_plus"),
479 ExpressionOp::Deref => *printer += const_text("DW_OP_deref"),
480 ExpressionOp::StackValue => *printer += const_text("DW_OP_stack_value"),
481 ExpressionOp::Piece(size) => {
482 *printer += const_text("DW_OP_piece");
483 *printer += const_text("(") + display(*size) + const_text(")");
484 }
485 ExpressionOp::BitPiece { size, offset } => {
486 *printer += const_text("DW_OP_bit_piece");
487 *printer += const_text("(")
488 + display(*size)
489 + const_text(",")
490 + display(*offset)
491 + const_text(")");
492 }
493 ExpressionOp::FrameBase { base, byte_offset } => match base {
494 FrameBase::Local(index) => {
495 *printer += const_text("DW_OP_fbreg(local, ");
496 *printer += text(format!("{}{:+}", index, byte_offset));
497 *printer += const_text(")");
498 }
499 FrameBase::Global(index) => {
500 *printer += const_text("DW_OP_fbreg(global, ");
501 *printer += text(format!("{}{:+}", index, byte_offset));
502 *printer += const_text(")");
503 }
504 },
505 ExpressionOp::ResolvedFrameBase { base, byte_offset } => match base {
506 ResolvedFrameBase::Local(offset) => {
507 *printer += const_text("MIDEN_OP_fbreg(local, ");
508 *printer += text(format!("{}{:+}", offset, byte_offset));
509 *printer += const_text(")");
510 }
511 ResolvedFrameBase::Global(address) => {
512 *printer += const_text("MIDEN_OP_fbreg(global, ");
513 *printer += text(format!("{}{:+}", address, byte_offset));
514 *printer += const_text(")");
515 }
516 },
517 ExpressionOp::Address { address } => {
518 *printer += const_text("DW_OP_addr");
519 *printer += const_text("(") + display(*address) + const_text(")");
520 }
521 ExpressionOp::Unsupported(name) => *printer += const_text(name.as_str()),
522 }
523 }
524 *printer += const_text("]");
525 }
526}
527
528impl AttrParser for ExpressionAttr {
529 fn parse(
530 parser: &mut dyn crate::parse::Parser<'_>,
531 ) -> crate::parse::ParseResult<crate::AttributeRef> {
532 use crate::parse::Delimiter;
533
534 let mut ops = Vec::default();
535 parser.parse_comma_separated_list(
536 Delimiter::OptionalBracket,
537 Some("DIExpression"),
538 |parser| {
539 ops.push(ExpressionOp::parse(parser)?);
540
541 Ok(true)
542 },
543 )?;
544
545 let attr = parser
546 .context_rc()
547 .create_attribute::<ExpressionAttr, _>(Expression::with_ops(ops));
548
549 Ok(attr.as_attribute_ref())
550 }
551}