1use crate::value::QCodeMut;
2use crate::{
3 context::Context,
4 error::Result,
5 types::TypeId,
6 value::{
7 LocalBlockId, LocalValueId, ModuleView, QCodeView, Value, ValueId,
8 block::{BlockId, BlockRef},
9 util::{
10 base_ref::{BaseRef, WithCtx, WithCtxMut},
11 named::{Named, Renameable},
12 },
13 },
14};
15use jstd::Identifier;
16use std::{
17 borrow::Cow,
18 fmt::{Display, Formatter},
19 marker::PhantomData,
20};
21
22#[derive(Identifier)]
25pub struct LocalParamId(u32);
26
27crate::composite_id!(BlockParamId, LocalParamId);
28
29impl BlockParamId {
30 pub fn id(self) -> ValueId {
32 ValueId::BlockParam(self)
33 }
34}
35
36#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
47pub struct BlockParam<'str> {
48 pub index: usize,
50
51 pub type_id: TypeId,
53
54 pub(crate) parent: Option<LocalBlockId>,
59
60 pub name: Option<Cow<'str, str>>,
62
63 pub origin: Option<LocalValueId>,
68}
69
70impl<'str> BlockParam<'str> {
71 pub fn make<'ctx>(
75 ctx: &'ctx mut Context<'str>,
76 block_id: BlockId,
77 size: usize,
78 ) -> BlockParamMutRef<'str, 'ctx> {
79 let type_id = ctx.shared.types.get_or_make_int(size);
80 let index = ctx.block(block_id).params.len();
81 let id = ctx.push_block_param(
82 block_id.func,
83 BlockParam {
84 index,
85 type_id,
86 parent: Some(block_id.local),
87 name: None,
88 origin: None,
89 },
90 );
91 BlockParamMutRef::from_id(ctx, id)
92 }
93
94 pub fn new(index: usize, type_id: TypeId, parent: LocalBlockId) -> Self {
99 Self {
100 index,
101 type_id,
102 parent: Some(parent),
103 name: None,
104 origin: None,
105 }
106 }
107
108 pub fn from_id<'ctx>(ctx: &'ctx Context<'str>, id: BlockParamId) -> BlockParamRef<'str, 'ctx> {
109 BlockParamRef::new(ModuleView::new(ctx), id)
110 }
111
112 pub fn from_id_mut<'ctx>(
113 ctx: &'ctx mut Context<'str>,
114 id: BlockParamId,
115 ) -> BlockParamMutRef<'str, 'ctx> {
116 BlockParamMutRef::from_id(ctx, id)
117 }
118
119 pub fn parent_id(&self) -> Option<LocalBlockId> {
123 self.parent
124 }
125
126 pub fn set_parent(&mut self, block: LocalBlockId) {
128 self.parent = Some(block);
129 }
130
131 pub fn origin_id(&self) -> Option<LocalValueId> {
135 self.origin
136 }
137
138 pub fn set_origin_id(&mut self, origin: LocalValueId) {
141 self.origin = Some(origin);
142 }
143}
144
145impl<'s, 'ctx: 's, 'str: 'ctx, R> BlockParamRef<'str, 'ctx, R>
147where
148 R: QCodeView<'ctx, 'str>,
149{
150 fn inner(&'s self) -> &'ctx BlockParam<'str> {
151 self.view.block_param(self.id)
152 }
153
154 pub fn index(&'s self) -> usize {
156 self.inner().index
157 }
158
159 pub fn type_id(&'s self) -> TypeId {
161 self.inner().type_id
162 }
163
164 pub fn size(&'s self) -> usize {
166 self.view.shared().types.size_of(self.inner().type_id)
167 }
168
169 pub fn parent(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
171 self.inner()
172 .parent
173 .map(|local| BlockRef::new(self.view, BlockId::new(self.id.func, local)))
174 }
175
176 pub fn name(&'s self) -> Option<&'ctx str> {
177 self.inner().name.as_deref()
178 }
179
180 pub fn origin(&'s self) -> Option<ValueId> {
182 self.inner()
183 .origin
184 .map(|origin| origin.qualify(self.id.func))
185 }
186
187 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
188 let types = &self.view.shared().types;
192 let ty = types.type_name(self.type_id());
193 if types.pointee_of(self.type_id()).is_some()
194 || types.struct_name_of(self.type_id()).is_some()
195 {
196 write!(f, "{ty} ")?;
197 }
198 if let Some(name) = self.name() {
199 write!(f, "@{name}")
200 } else {
201 let id: usize = self.id.local.into();
202 write!(f, "@param{id:x}")
203 }
204 }
205
206 pub(crate) fn fmt_decl(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
213 let types = &self.view.shared().types;
214 let tid = self.type_id();
215 let is_scalar = types.pointee_of(tid).is_none() && types.struct_name_of(tid).is_none();
216 match (self.name(), is_scalar && self.size() > 0) {
217 (Some(name), true) => write!(f, "@{name}:{}", types.type_name(tid)),
218 _ => self.fmt(f),
219 }
220 }
221}
222
223#[derive(Clone, Copy)]
224pub struct BlockParamRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
225 pub id: BlockParamId,
226 pub(in crate::value) view: R,
227 marker: PhantomData<&'ctx &'str ()>,
228}
229
230impl<'str, 'ctx, R> BlockParamRef<'str, 'ctx, R> {
231 pub fn new(view: R, id: BlockParamId) -> Self {
232 Self {
233 id,
234 view,
235 marker: PhantomData,
236 }
237 }
238
239 pub fn id(&self) -> ValueId {
240 self.id.into()
241 }
242}
243
244impl<'str, 'ctx> BlockParamRef<'str, 'ctx> {
245 pub fn from_id(ctx: &'ctx Context<'str>, id: BlockParamId) -> Self {
246 Self::new(ModuleView::new(ctx), id)
247 }
248}
249
250impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for BlockParamRef<'str, 'ctx> {
251 fn ctx(&'s self) -> &'ctx Context<'str> {
252 self.view.context()
256 }
257}
258
259impl<'str: 'ctx, 'ctx, R> Named for BlockParamRef<'str, 'ctx, R>
260where
261 R: QCodeView<'ctx, 'str>,
262{
263 fn name(&self) -> Option<&str> {
264 self.view.block_param(self.id).name.as_deref()
265 }
266}
267
268impl<'str: 'ctx, 'ctx, R> Display for BlockParamRef<'str, 'ctx, R>
269where
270 R: QCodeView<'ctx, 'str>,
271{
272 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
273 BlockParamRef::fmt(self, f)
274 }
275}
276
277impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for BlockParamRef<'str, 'ctx, R>
278where
279 R: QCodeView<'ctx, 'str>,
280{
281 fn id(&self) -> ValueId {
282 self.id()
283 }
284
285 fn size(&self) -> usize {
286 BlockParamRef::size(self)
287 }
288}
289
290pub type BlockParamMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, BlockParamId>;
291
292impl<'str, 'ctx> BlockParamMutRef<'str, 'ctx> {
293 fn inner_mut(&mut self) -> &mut BlockParam<'str> {
294 self.ctx.block_param_mut(self.id)
295 }
296
297 pub fn set_origin(&mut self, origin: ValueId) {
299 let func = self.id.func;
300 self.inner_mut().origin = Some(origin.localize(func));
301 }
302
303 pub fn constrain_size(&mut self, size: usize) {
304 let current = self.size();
305 if current == 0 {
306 self.set_size(size);
307 } else {
308 assert_eq!(
309 current, size,
310 "block parameter size mismatch for {}: existing {} bytes, new {} bytes",
311 self, current, size
312 );
313 }
314 }
315
316 pub fn as_ref(&self) -> BlockParamRef<'str, '_> {
317 BlockParamRef::new(ModuleView::new(self.ctx), self.id)
318 }
319}
320
321impl<'str, H: QCodeMut<'str>> BaseRef<H, BlockParamId> {
324 pub fn set_size(&mut self, size: usize) {
327 let type_id = self.ctx.shr().types.get_or_make_int(size);
328 self.ctx.block_param_mut(self.id).type_id = type_id;
329 }
330
331 pub fn rename_local(&mut self, name: Cow<'str, str>) -> Result<()> {
334 let old_name = self
335 .ctx
336 .body(self.id.func)
337 .block_param(self.id)
338 .name
339 .as_deref()
340 .map(str::to_owned);
341 self.ctx
342 .register_body_name(self.id.into(), name.clone(), old_name.as_deref())?;
343 self.ctx.block_param_mut(self.id).name = Some(name);
344 Ok(())
345 }
346}
347
348impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for BlockParamMutRef<'str, 'ctx> {
349 fn ctx(&'s self) -> &'s Context<'str> {
350 self.ctx
351 }
352}
353
354impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for BlockParamMutRef<'str, 'ctx> {
355 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
356 self.ctx
357 }
358}
359
360impl Named for BlockParamMutRef<'_, '_> {
361 fn name(&self) -> Option<&str> {
362 self.ctx.block_param(self.id).name.as_deref()
363 }
364}
365
366impl Display for BlockParamMutRef<'_, '_> {
367 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
368 self.as_ref().fmt(f)
369 }
370}
371
372impl<'str, 'ctx> Value<'str, 'ctx> for BlockParamMutRef<'str, 'ctx> {
373 fn id(&self) -> ValueId {
374 self.id()
375 }
376
377 fn size(&self) -> usize {
378 self.as_ref().size()
379 }
380}
381
382impl<'str, 'ctx, H: QCodeMut<'str>> Renameable<'str, 'ctx> for BaseRef<H, BlockParamId>
386where
387 Self: Named,
388{
389 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
390 self.rename_local(name)
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use crate::{
398 context::Context,
399 value::{BasicBlock, FunctionBody},
400 };
401
402 #[test]
403 fn block_param_storage_is_local_and_refs_qualify_with_param_function() {
404 let mut ctx = Context::new();
405 let func = FunctionBody::make(&mut ctx, "local_param_storage".into())
406 .unwrap()
407 .id;
408 let block_id = BasicBlock::make(&mut ctx, func).id;
409 let param_id = BasicBlock::from_id_mut(&mut ctx, block_id).push_param(8).id;
410
411 BlockParam::from_id_mut(&mut ctx, param_id).set_origin(ValueId::BlockParam(param_id));
412
413 let raw = ctx.block_param(param_id);
414 assert_eq!(raw.parent_id(), Some(block_id.local));
415 assert_eq!(
416 raw.origin_id(),
417 Some(LocalValueId::BlockParam(param_id.local))
418 );
419
420 let param = BlockParam::from_id(&ctx, param_id);
421 assert_eq!(param.parent().map(|block| block.id), Some(block_id));
422 assert_eq!(param.origin(), Some(ValueId::BlockParam(param_id)));
423 }
424
425 #[test]
426 #[cfg(debug_assertions)]
427 #[should_panic(expected = "localize: foreign block-param operand")]
428 fn block_param_origin_rejects_foreign_function_value() {
429 let mut ctx = Context::new();
430 let a = FunctionBody::make(&mut ctx, "origin_a".into()).unwrap().id;
431 let b = FunctionBody::make(&mut ctx, "origin_b".into()).unwrap().id;
432 let a_block = BasicBlock::make(&mut ctx, a).id;
433 let b_block = BasicBlock::make(&mut ctx, b).id;
434 let a_param = BasicBlock::from_id_mut(&mut ctx, a_block).push_param(8).id;
435 let b_param = BasicBlock::from_id_mut(&mut ctx, b_block).push_param(8).id;
436
437 BlockParam::from_id_mut(&mut ctx, b_param).set_origin(ValueId::BlockParam(a_param));
438 }
439
440 #[test]
441 fn make_block_param_sets_index_and_size() {
442 let mut ctx = Context::new();
443 let block_id = {
444 let __f = ctx.anon_function();
445 BasicBlock::make(&mut ctx, __f)
446 }
447 .id;
448
449 let p0_id = BasicBlock::from_id_mut(&mut ctx, block_id).push_param(8).id;
450 let p1_id = BasicBlock::from_id_mut(&mut ctx, block_id).push_param(4).id;
451
452 let p0 = BlockParam::from_id(&ctx, p0_id);
453 let p1 = BlockParam::from_id(&ctx, p1_id);
454 assert_eq!(p0.index(), 0);
455 assert_eq!(p0.size(), 8);
456 assert_eq!(p1.index(), 1);
457 assert_eq!(p1.size(), 4);
458 }
459
460 #[test]
461 fn block_param_display_uses_name_when_set() {
462 let mut ctx = Context::new();
463 let block_id = {
464 let __f = ctx.anon_function();
465 BasicBlock::make(&mut ctx, __f)
466 }
467 .id;
468 let p_id = BasicBlock::from_id_mut(&mut ctx, block_id).push_param(8).id;
469
470 let mut p = BlockParam::from_id_mut(&mut ctx, p_id);
471 p.rename("myval".into()).expect("rename ok");
472 assert_eq!(p.to_string(), "@myval");
473 }
474
475 #[test]
476 fn block_param_display_fallback_when_unnamed() {
477 let mut ctx = Context::new();
478 let block_id = {
479 let __f = ctx.anon_function();
480 BasicBlock::make(&mut ctx, __f)
481 }
482 .id;
483 let p_id = BasicBlock::from_id_mut(&mut ctx, block_id).push_param(4).id;
484 let p = BlockParam::from_id(&ctx, p_id);
485 let s = p.to_string();
486 assert!(s.starts_with("@param"), "expected @param<hex>, got {s}");
487 }
488}