qcode/value/literal.rs
1//! Compile-time integer constants, optionally with symbolic labels.
2//!
3//! A [`Literal`] stores a raw `u64` value together with an optional
4//! [`SymbolicRef`] that gives it meaning beyond its numeric value — for example
5//! the address of a known block or function. When a literal has a symbolic
6//! reference it is displayed as `&<name>` rather than `0x…`.
7//!
8//! Every literal carries a [`TypeId`] that encodes its byte width (and, for
9//! pointer literals, its space provenance). Type is preserved through constant
10//! folding.
11
12use crate::{
13 context::Shared,
14 types::TypeId,
15 value::{
16 Value, ValueId,
17 block::BlockId,
18 function::FunctionId,
19 util::base_ref::{BaseRef, WithShared},
20 },
21};
22use jstd::Identifier;
23
24#[derive(Identifier)]
25pub struct LiteralId(usize);
26
27/// An optional symbolic meaning attached to a [`Literal`].
28///
29/// When the assembler/lifter knows that a numeric constant is actually the
30/// address of a block, a function, or a string, it stores a `SymbolicRef` so
31/// that the literal can be displayed and reasoned about symbolically.
32///
33#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
34pub enum SymbolicRef {
35 /// The literal is the address of this basic block.
36 Block(BlockId),
37 /// The literal is the entry address of this function.
38 Function(FunctionId),
39 /// The literal is a pointer to this string constant.
40 String(String),
41}
42
43/// A compile-time integer constant stored in a [`Context`](crate::context::Context).
44///
45/// The raw value is a `u64`; [`LiteralRef::value`] masks it to the width
46/// described by the literal's [`TypeId`].
47#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48pub struct Literal {
49 /// Raw integer value (may be wider than the type's size before masking).
50 pub value: u64,
51 /// The type of this constant (encodes size and semantic kind).
52 pub type_id: TypeId,
53 /// Optional symbolic annotation (block address, function address, string).
54 pub symbolic: Option<SymbolicRef>,
55}
56
57pub type LiteralRef<'str, 'ctx> = BaseRef<&'ctx Shared<'str>, LiteralId>;
58
59impl<'s, 'ctx: 's, 'str: 'ctx> WithShared<'s, 'ctx, 'str> for LiteralRef<'str, 'ctx> {
60 fn shared(&'s self) -> &'ctx Shared<'str> {
61 self.ctx
62 }
63}
64
65impl<'s, 'ctx: 's, 'str: 'ctx, Ctx> BaseRef<Ctx, LiteralId>
66where
67 Self: WithShared<'s, 'ctx, 'str>,
68{
69 fn inner(&'s self) -> &'ctx Literal {
70 &self.shared().values.literals[self.id]
71 }
72
73 pub fn mask(&'s self) -> u64 {
74 let size = self.shared().types.size_of(self.inner().type_id);
75 if size >= 8 {
76 u64::MAX
77 } else {
78 (1u64 << (size * 8)) - 1
79 }
80 }
81
82 pub fn value(&'s self) -> u64 {
83 self.inner().value & self.mask()
84 }
85
86 /// Returns the [`TypeId`] of this literal.
87 pub fn type_id(&'s self) -> TypeId {
88 self.inner().type_id
89 }
90}
91
92impl std::fmt::Display for LiteralRef<'_, '_> {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 let literal = &self.ctx.values.literals[self.id];
95 match &literal.symbolic {
96 // Symbolic block/function names live in bodies/interfaces, which a
97 // `&Shared`-backed leaf ref cannot reach; the block/function *name*
98 // rendering is done by the full-context path (`segment::literal_atom`,
99 // used by the instruction renderer and the dataflow graph). Here we
100 // fall back to the numeric form. See context-split 5b-ii item #1.
101 Some(SymbolicRef::Block(_)) | Some(SymbolicRef::Function(_)) => {
102 write!(f, "&<0x{:x}>", literal.value)
103 }
104 Some(SymbolicRef::String(s)) => write!(f, "&{:?}", s),
105 // A `bool` literal prints as `true`/`false`; the `bool` type token is
106 // emitted by the operand's type prefix, so the round-trip is `bool true`.
107 None if self.ctx.types.is_bool(literal.type_id) => {
108 write!(f, "{}", if literal.value != 0 { "true" } else { "false" })
109 }
110 None => write!(f, "0x{:x}", literal.value),
111 }
112 }
113}
114
115impl<'str, 'ctx> Value<'str, 'ctx> for LiteralRef<'str, 'ctx> {
116 fn id(&self) -> ValueId {
117 ValueId::Literal(self.id)
118 }
119
120 fn size(&self) -> usize {
121 self.ctx
122 .types
123 .size_of(self.ctx.values.literals[self.id].type_id)
124 }
125}