1use anyhow::{Ok, Result, anyhow, bail, ensure};
2use hugr_core::Node;
3use hugr_core::extension::prelude::generic::LoadNat;
4use hugr_core::extension::prelude::{
5 self, ConstError, ConstExternalSymbol, ConstString, ConstUsize, MakeTuple, TupleOpDef,
6 UnpackTuple, error_type, generic,
7};
8use hugr_core::extension::prelude::{ERROR_TYPE_NAME, STRING_TYPE_NAME};
9use hugr_core::ops::ExtensionOp;
10use hugr_core::types::TypeArg;
11use hugr_core::{
12 HugrView, extension::simple_op::MakeExtensionOp as _, ops::constant::CustomConst,
13 types::SumType,
14};
15use inkwell::{
16 AddressSpace,
17 types::{BasicType, IntType, PointerType},
18 values::{BasicValue as _, BasicValueEnum, StructValue},
19};
20use itertools::Itertools;
21
22use crate::emit::EmitOpArgs;
23use crate::{
24 custom::{CodegenExtension, CodegenExtsBuilder},
25 emit::{
26 func::EmitFuncContext,
27 libc::{emit_libc_abort, emit_libc_printf},
28 },
29 sum::LLVMSumValue,
30 types::TypingSession,
31};
32
33pub trait PreludeCodegen: Clone {
40 fn usize_type<'c>(&self, session: &TypingSession<'c, '_>) -> IntType<'c> {
43 session.iw_context().i64_type()
44 }
45
46 fn qubit_type<'c>(&self, session: &TypingSession<'c, '_>) -> impl BasicType<'c> {
48 session.iw_context().i16_type()
49 }
50
51 fn error_type<'c>(&self, session: &TypingSession<'c, '_>) -> Result<impl BasicType<'c>> {
59 let ctx = session.iw_context();
60 Ok(session.iw_context().struct_type(
61 &[
62 ctx.i32_type().into(),
63 ctx.i8_type().ptr_type(AddressSpace::default()).into(),
64 ],
65 false,
66 ))
67 }
68
69 fn string_type<'c>(&self, session: &TypingSession<'c, '_>) -> Result<impl BasicType<'c>> {
76 Ok(session
77 .iw_context()
78 .i8_type()
79 .ptr_type(AddressSpace::default()))
80 }
81
82 fn emit_print<H: HugrView<Node = Node>>(
84 &self,
85 ctx: &mut EmitFuncContext<H>,
86 text: BasicValueEnum,
87 ) -> Result<()> {
88 let format_str = ctx
89 .builder()
90 .build_global_string_ptr("%s\n", "prelude.print_template")?
91 .as_basic_value_enum();
92 emit_libc_printf(ctx, &[format_str.into(), text.into()])
93 }
94
95 fn emit_const_error<'c, H: HugrView<Node = Node>>(
102 &self,
103 ctx: &mut EmitFuncContext<'c, '_, H>,
104 err: &ConstError,
105 ) -> Result<BasicValueEnum<'c>> {
106 let builder = ctx.builder();
107 let err_ty = ctx.llvm_type(&error_type())?.into_struct_type();
108 let signal = err_ty
109 .get_field_type_at_index(0)
110 .unwrap()
111 .into_int_type()
112 .const_int(u64::from(err.signal), false);
113 let message = builder
114 .build_global_string_ptr(&err.message, "")?
115 .as_basic_value_enum();
116 let err = err_ty.const_named_struct(&[signal.into(), message]);
117 Ok(err.into())
118 }
119
120 fn emit_make_error<'c, H: HugrView<Node = Node>>(
126 &self,
127 ctx: &mut EmitFuncContext<'c, '_, H>,
128 signal: BasicValueEnum<'c>,
129 message: BasicValueEnum<'c>,
130 ) -> Result<BasicValueEnum<'c>> {
131 let builder = ctx.builder();
132
133 let i32_type = ctx.typing_session().iw_context().i32_type();
135 let signal_int = signal.into_int_value();
136 let signal_truncated = builder.build_int_truncate(signal_int, i32_type, "")?;
137
138 let err_ty = ctx.llvm_type(&error_type())?.into_struct_type();
140 let undef = err_ty.get_undef();
141 let err_with_sig = builder
142 .build_insert_value(undef, signal_truncated, 0, "")?
143 .into_struct_value();
144 let err_complete = builder
145 .build_insert_value(err_with_sig, message, 1, "")?
146 .into_struct_value();
147
148 Ok(err_complete.into())
149 }
150
151 fn emit_panic<H: HugrView<Node = Node>>(
160 &self,
161 ctx: &mut EmitFuncContext<H>,
162 err: BasicValueEnum,
163 ) -> Result<()> {
164 let format_str = ctx
165 .builder()
166 .build_global_string_ptr(
167 "Program panicked (signal %i): %s\n",
168 "prelude.panic_template",
169 )?
170 .as_basic_value_enum();
171 let Some(err) = StructValue::try_from(err).ok() else {
172 bail!("emit_panic: Expected err value to be a struct type")
173 };
174 ensure!(err.get_type().count_fields() == 2);
175 let signal = ctx.builder().build_extract_value(err, 0, "")?;
176 ensure!(signal.get_type() == ctx.iw_context().i32_type().as_basic_type_enum());
177 let msg = ctx.builder().build_extract_value(err, 1, "")?;
178 ensure!(PointerType::try_from(msg.get_type()).is_ok());
179 emit_libc_printf(ctx, &[format_str.into(), signal.into(), msg.into()])?;
180 emit_libc_abort(ctx)
181 }
182
183 fn emit_exit<H: HugrView<Node = Node>>(
193 &self,
194 ctx: &mut EmitFuncContext<H>,
195 err: BasicValueEnum,
196 ) -> Result<()> {
197 self.emit_panic(ctx, err)
198 }
199
200 fn emit_const_string<'c, H: HugrView<Node = Node>>(
206 &self,
207 ctx: &mut EmitFuncContext<'c, '_, H>,
208 str: &ConstString,
209 ) -> Result<BasicValueEnum<'c>> {
210 let default_str_type = ctx
211 .iw_context()
212 .i8_type()
213 .ptr_type(AddressSpace::default())
214 .as_basic_type_enum();
215 let str_type = ctx.llvm_type(&str.get_type())?.as_basic_type_enum();
216 ensure!(
217 str_type == default_str_type,
218 "The default implementation of PreludeCodegen::string_type was overridden, but the default implementation of emit_const_string was not. String type is: {str_type}"
219 );
220 let s = ctx.builder().build_global_string_ptr(str.value(), "")?;
221 Ok(s.as_basic_value_enum())
222 }
223
224 fn emit_barrier<'c, H: HugrView<Node = Node>>(
225 &self,
226 ctx: &mut EmitFuncContext<'c, '_, H>,
227 args: EmitOpArgs<'c, '_, ExtensionOp, H>,
228 ) -> Result<()> {
229 args.outputs.finish(ctx.builder(), args.inputs)
231 }
232}
233
234#[derive(Default, Clone)]
237pub struct DefaultPreludeCodegen;
238
239impl PreludeCodegen for DefaultPreludeCodegen {}
240
241#[derive(Clone, Debug, Default)]
242pub struct PreludeCodegenExtension<PCG>(PCG);
243
244impl<PCG: PreludeCodegen> PreludeCodegenExtension<PCG> {
245 pub fn new(pcg: PCG) -> Self {
246 Self(pcg)
247 }
248}
249
250impl<PCG: PreludeCodegen> From<PCG> for PreludeCodegenExtension<PCG> {
251 fn from(pcg: PCG) -> Self {
252 Self::new(pcg)
253 }
254}
255
256impl<PCG: PreludeCodegen> CodegenExtension for PreludeCodegenExtension<PCG> {
257 fn add_extension<'a, H: HugrView<Node = Node> + 'a>(
258 self,
259 builder: CodegenExtsBuilder<'a, H>,
260 ) -> CodegenExtsBuilder<'a, H>
261 where
262 Self: 'a,
263 {
264 add_prelude_extensions(builder, self.0)
265 }
266}
267
268impl<'a, H: HugrView<Node = Node> + 'a> CodegenExtsBuilder<'a, H> {
269 #[must_use]
272 pub fn add_default_prelude_extensions(self) -> Self {
273 self.add_prelude_extensions(DefaultPreludeCodegen)
274 }
275
276 pub fn add_prelude_extensions(self, pcg: impl PreludeCodegen + 'a) -> Self {
279 self.add_extension(PreludeCodegenExtension::from(pcg))
280 }
281}
282
283pub fn add_prelude_extensions<'a, H: HugrView<Node = Node> + 'a>(
286 cem: CodegenExtsBuilder<'a, H>,
287 pcg: impl PreludeCodegen + 'a,
288) -> CodegenExtsBuilder<'a, H> {
289 cem.custom_type((prelude::PRELUDE_ID, "qubit".into()), {
290 let pcg = pcg.clone();
291 move |ts, _| Ok(pcg.qubit_type(&ts).as_basic_type_enum())
292 })
293 .custom_type((prelude::PRELUDE_ID, "usize".into()), {
294 let pcg = pcg.clone();
295 move |ts, _| Ok(pcg.usize_type(&ts).as_basic_type_enum())
296 })
297 .custom_type((prelude::PRELUDE_ID, ERROR_TYPE_NAME.clone()), {
298 let pcg = pcg.clone();
299 move |ts, _| Ok(pcg.error_type(&ts)?.as_basic_type_enum())
300 })
301 .custom_type((prelude::PRELUDE_ID, STRING_TYPE_NAME.clone()), {
302 let pcg = pcg.clone();
303 move |ts, _| Ok(pcg.string_type(&ts)?.as_basic_type_enum())
304 })
305 .custom_const::<ConstUsize>(|context, k| {
306 let ty: IntType = context
307 .llvm_type(&k.get_type())?
308 .try_into()
309 .map_err(|()| anyhow!("Failed to get ConstUsize as IntType"))?;
310 Ok(ty.const_int(k.value(), false).into())
311 })
312 .custom_const::<ConstExternalSymbol>(|context, k| {
313 let llvm_type = context.llvm_type(&k.get_type())?;
316 let global = context.get_global(&k.symbol, llvm_type, k.constant)?;
317 Ok(context
318 .builder()
319 .build_load(global.as_pointer_value(), &k.symbol)?)
320 })
321 .custom_const::<ConstString>({
322 let pcg = pcg.clone();
323 move |context, k| {
324 let err = pcg.emit_const_string(context, k)?;
325 ensure!(
326 err.get_type()
327 == pcg
328 .string_type(&context.typing_session())?
329 .as_basic_type_enum()
330 );
331 Ok(err)
332 }
333 })
334 .custom_const::<ConstError>({
335 let pcg = pcg.clone();
336 move |context, k| {
337 let err = pcg.emit_const_error(context, k)?;
338 ensure!(
339 err.get_type()
340 == pcg
341 .error_type(&context.typing_session())?
342 .as_basic_type_enum()
343 );
344 Ok(err)
345 }
346 })
347 .extension_op(prelude::PRELUDE_ID, prelude::NOOP_OP_ID, |context, args| {
348 args.outputs.finish(context.builder(), args.inputs)
349 })
350 .simple_extension_op::<TupleOpDef>(|context, args, op| match op {
351 TupleOpDef::UnpackTuple => {
352 let unpack_tuple = UnpackTuple::from_extension_op(args.node().as_ref())?;
353 let llvm_sum_type = context.llvm_sum_type(SumType::new([unpack_tuple.0]))?;
354 let llvm_sum_value = args
355 .inputs
356 .into_iter()
357 .exactly_one()
358 .map_err(|_| anyhow!("UnpackTuple does not have exactly one input"))
359 .and_then(|v| LLVMSumValue::try_new(v, llvm_sum_type))?;
360 let rs = llvm_sum_value.build_untag(context.builder(), 0)?;
361 args.outputs.finish(context.builder(), rs)
362 }
363 TupleOpDef::MakeTuple => {
364 let make_tuple = MakeTuple::from_extension_op(args.node().as_ref())?;
365 let llvm_sum_type = context.llvm_sum_type(SumType::new([make_tuple.0]))?;
366 let r = llvm_sum_type.build_tag(context.builder(), 0, args.inputs)?;
367 args.outputs.finish(context.builder(), [r.into()])
368 }
369 _ => Err(anyhow!("Unsupported TupleOpDef")),
370 })
371 .extension_op(prelude::PRELUDE_ID, prelude::PRINT_OP_ID, {
372 let pcg = pcg.clone();
373 move |context, args| {
374 let text = args.inputs[0];
375 pcg.emit_print(context, text)?;
376 args.outputs.finish(context.builder(), [])
377 }
378 })
379 .extension_op(prelude::PRELUDE_ID, prelude::MAKE_ERROR_OP_ID, {
380 let pcg = pcg.clone();
381 move |context, args| {
382 let signal = args.inputs[0];
383 let message = args.inputs[1];
384 ensure!(
385 message.get_type()
386 == pcg
387 .string_type(&context.typing_session())?
388 .as_basic_type_enum(),
389 signal.get_type() == pcg.usize_type(&context.typing_session()).into()
390 );
391 let err = pcg.emit_make_error(context, signal, message)?;
392 args.outputs.finish(context.builder(), [err])
393 }
394 })
395 .extension_op(prelude::PRELUDE_ID, prelude::PANIC_OP_ID, {
396 let pcg = pcg.clone();
397 move |context, args| {
398 let err = args.inputs[0];
399 ensure!(
400 err.get_type()
401 == pcg
402 .error_type(&context.typing_session())?
403 .as_basic_type_enum()
404 );
405 pcg.emit_panic(context, err)?;
406 let returns = args
407 .outputs
408 .get_types()
409 .map(inkwell::types::BasicTypeEnum::const_zero)
410 .collect_vec();
411 args.outputs.finish(context.builder(), returns)
412 }
413 })
414 .extension_op(prelude::PRELUDE_ID, prelude::EXIT_OP_ID, {
415 let pcg = pcg.clone();
417 move |context, args| {
418 let err = args.inputs[0];
419 ensure!(
420 err.get_type()
421 == pcg
422 .error_type(&context.typing_session())?
423 .as_basic_type_enum()
424 );
425 pcg.emit_exit(context, err)?;
426 let returns = args
427 .outputs
428 .get_types()
429 .map(inkwell::types::BasicTypeEnum::const_zero)
430 .collect_vec();
431 args.outputs.finish(context.builder(), returns)
432 }
433 })
434 .extension_op(prelude::PRELUDE_ID, generic::LOAD_NAT_OP_ID.clone(), {
435 let pcg = pcg.clone();
436 move |context, args| {
437 let load_nat = LoadNat::from_extension_op(args.node().as_ref())?;
438 let v = match load_nat.get_nat() {
439 TypeArg::BoundedNat(n) => pcg
440 .usize_type(&context.typing_session())
441 .const_int(n, false),
442 arg => bail!("Unexpected type arg for LoadNat: {}", arg),
443 };
444 args.outputs.finish(context.builder(), vec![v.into()])
445 }
446 })
447 .extension_op(prelude::PRELUDE_ID, prelude::BARRIER_OP_ID, {
448 let pcg = pcg.clone();
449 move |context, args| pcg.emit_barrier(context, args)
450 })
451}
452
453#[cfg(test)]
454mod test {
455 use hugr_core::builder::{Dataflow, DataflowHugr};
456 use hugr_core::extension::PRELUDE;
457 use hugr_core::extension::prelude::{EXIT_OP_ID, MAKE_ERROR_OP_ID, Noop};
458 use hugr_core::types::{Term, Type};
459 use hugr_core::{Hugr, type_row};
460 use prelude::{PANIC_OP_ID, PRINT_OP_ID, bool_t, qb_t, usize_t};
461 use rstest::{fixture, rstest};
462
463 use crate::check_emission;
464 use crate::custom::CodegenExtsBuilder;
465 use crate::emit::test::SimpleHugrConfig;
466 use crate::test::{TestContext, exec_ctx, llvm_ctx};
467 use crate::types::HugrType;
468
469 use super::*;
470
471 #[derive(Clone)]
472 struct TestPreludeCodegen;
473 impl PreludeCodegen for TestPreludeCodegen {
474 fn usize_type<'c>(&self, session: &TypingSession<'c, '_>) -> IntType<'c> {
475 session.iw_context().i32_type()
476 }
477
478 fn qubit_type<'c>(&self, session: &TypingSession<'c, '_>) -> impl BasicType<'c> {
479 session.iw_context().f64_type()
480 }
481 }
482
483 #[rstest]
484 fn prelude_extension_types(llvm_ctx: TestContext) {
485 let iw_context = llvm_ctx.iw_context();
486 let type_converter = CodegenExtsBuilder::<Hugr>::default()
487 .add_prelude_extensions(TestPreludeCodegen)
488 .finish()
489 .type_converter;
490 let session = type_converter.session(iw_context);
491
492 assert_eq!(
493 iw_context.i32_type().as_basic_type_enum(),
494 session.llvm_type(&usize_t()).unwrap()
495 );
496 assert_eq!(
497 iw_context.f64_type().as_basic_type_enum(),
498 session.llvm_type(&qb_t()).unwrap()
499 );
500 }
501
502 #[rstest]
503 fn prelude_extension_types_in_test_context(mut llvm_ctx: TestContext) {
504 llvm_ctx.add_extensions(|x| x.add_prelude_extensions(TestPreludeCodegen));
505 let tc = llvm_ctx.get_typing_session();
506 assert_eq!(
507 llvm_ctx.iw_context().i32_type().as_basic_type_enum(),
508 tc.llvm_type(&usize_t()).unwrap()
509 );
510 assert_eq!(
511 llvm_ctx.iw_context().f64_type().as_basic_type_enum(),
512 tc.llvm_type(&qb_t()).unwrap()
513 );
514 }
515
516 #[rstest::fixture]
517 fn prelude_llvm_ctx(mut llvm_ctx: TestContext) -> TestContext {
518 llvm_ctx.add_extensions(CodegenExtsBuilder::add_default_prelude_extensions);
519 llvm_ctx
520 }
521
522 #[rstest]
523 fn prelude_const_usize(prelude_llvm_ctx: TestContext) {
524 let hugr = SimpleHugrConfig::new()
525 .with_outs(usize_t())
526 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
527 .finish(|mut builder| {
528 let k = builder.add_load_value(ConstUsize::new(17));
529 builder.finish_hugr_with_outputs([k]).unwrap()
530 });
531 check_emission!(hugr, prelude_llvm_ctx);
532 }
533
534 #[rstest]
535 fn prelude_const_external_symbol(prelude_llvm_ctx: TestContext) {
536 let konst1 = ConstExternalSymbol::new("sym1", usize_t(), true);
537 let konst2 = ConstExternalSymbol::new(
538 "sym2",
539 HugrType::new_sum([
540 vec![usize_t(), HugrType::new_unit_sum(3)].into(),
541 type_row![],
542 ]),
543 false,
544 );
545
546 let hugr = SimpleHugrConfig::new()
547 .with_outs(vec![konst1.get_type(), konst2.get_type()])
548 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
549 .finish(|mut builder| {
550 let k1 = builder.add_load_value(konst1);
551 let k2 = builder.add_load_value(konst2);
552 builder.finish_hugr_with_outputs([k1, k2]).unwrap()
553 });
554 check_emission!(hugr, prelude_llvm_ctx);
555 }
556
557 #[rstest]
558 fn prelude_noop(prelude_llvm_ctx: TestContext) {
559 let hugr = SimpleHugrConfig::new()
560 .with_ins(usize_t())
561 .with_outs(usize_t())
562 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
563 .finish(|mut builder| {
564 let in_wires = builder.input_wires();
565 let r = builder
566 .add_dataflow_op(Noop::new(usize_t()), in_wires)
567 .unwrap()
568 .outputs();
569 builder.finish_hugr_with_outputs(r).unwrap()
570 });
571 check_emission!(hugr, prelude_llvm_ctx);
572 }
573
574 #[rstest]
575 fn prelude_make_tuple(prelude_llvm_ctx: TestContext) {
576 let hugr = SimpleHugrConfig::new()
577 .with_ins(vec![bool_t(), bool_t()])
578 .with_outs(Type::new_tuple(vec![bool_t(), bool_t()]))
579 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
580 .finish(|mut builder| {
581 let in_wires = builder.input_wires();
582 let r = builder.make_tuple(in_wires).unwrap();
583 builder.finish_hugr_with_outputs([r]).unwrap()
584 });
585 check_emission!(hugr, prelude_llvm_ctx);
586 }
587
588 #[rstest]
589 fn prelude_unpack_tuple(prelude_llvm_ctx: TestContext) {
590 let hugr = SimpleHugrConfig::new()
591 .with_ins(Type::new_tuple(vec![bool_t(), bool_t()]))
592 .with_outs(vec![bool_t(), bool_t()])
593 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
594 .finish(|mut builder| {
595 let unpack = builder
596 .add_dataflow_op(
597 UnpackTuple::new(vec![bool_t(), bool_t()].into()),
598 builder.input_wires(),
599 )
600 .unwrap();
601 builder.finish_hugr_with_outputs(unpack.outputs()).unwrap()
602 });
603 check_emission!(hugr, prelude_llvm_ctx);
604 }
605
606 #[rstest]
607 fn prelude_panic(prelude_llvm_ctx: TestContext) {
608 let error_val = ConstError::new(42, "PANIC");
609 let type_arg_q: Term = qb_t().into();
610 let type_arg_2q = Term::new_list([type_arg_q.clone(), type_arg_q]);
611 let panic_op = PRELUDE
612 .instantiate_extension_op(&PANIC_OP_ID, [type_arg_2q.clone(), type_arg_2q.clone()])
613 .unwrap();
614
615 let hugr = SimpleHugrConfig::new()
616 .with_ins(vec![qb_t(), qb_t()])
617 .with_outs(vec![qb_t(), qb_t()])
618 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
619 .finish(|mut builder| {
620 let [q0, q1] = builder.input_wires_arr();
621 let err = builder.add_load_value(error_val);
622 let [q0, q1] = builder
623 .add_dataflow_op(panic_op, [err, q0, q1])
624 .unwrap()
625 .outputs_arr();
626 builder.finish_hugr_with_outputs([q0, q1]).unwrap()
627 });
628
629 check_emission!(hugr, prelude_llvm_ctx);
630 }
631
632 #[rstest]
633 fn prelude_exit(prelude_llvm_ctx: TestContext) {
634 let error_val = ConstError::new(42, "EXIT");
635 let type_arg_q: Term = qb_t().into();
636 let type_arg_2q = Term::new_list([type_arg_q.clone(), type_arg_q]);
637 let exit_op = PRELUDE
638 .instantiate_extension_op(&EXIT_OP_ID, [type_arg_2q.clone(), type_arg_2q.clone()])
639 .unwrap();
640
641 let hugr = SimpleHugrConfig::new()
642 .with_ins(vec![qb_t(), qb_t()])
643 .with_outs(vec![qb_t(), qb_t()])
644 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
645 .finish(|mut builder| {
646 let [q0, q1] = builder.input_wires_arr();
647 let err = builder.add_load_value(error_val);
648 let [q0, q1] = builder
649 .add_dataflow_op(exit_op, [err, q0, q1])
650 .unwrap()
651 .outputs_arr();
652 builder.finish_hugr_with_outputs([q0, q1]).unwrap()
653 });
654
655 check_emission!(hugr, prelude_llvm_ctx);
656 }
657
658 #[rstest]
659 fn prelude_print(prelude_llvm_ctx: TestContext) {
660 let greeting: ConstString = ConstString::new("Hello, world!".into());
661 let print_op = PRELUDE.instantiate_extension_op(&PRINT_OP_ID, []).unwrap();
662
663 let hugr = SimpleHugrConfig::new()
664 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
665 .finish(|mut builder| {
666 let greeting_out = builder.add_load_value(greeting);
667 builder.add_dataflow_op(print_op, [greeting_out]).unwrap();
668 builder.finish_hugr_with_outputs([]).unwrap()
669 });
670
671 check_emission!(hugr, prelude_llvm_ctx);
672 }
673
674 #[rstest]
675 fn prelude_make_error(prelude_llvm_ctx: TestContext) {
676 let sig: ConstUsize = ConstUsize::new(100);
677 let msg: ConstString = ConstString::new("Error!".into());
678
679 let make_error_op = PRELUDE
680 .instantiate_extension_op(&MAKE_ERROR_OP_ID, [])
681 .unwrap();
682
683 let hugr = SimpleHugrConfig::new()
684 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
685 .with_outs(error_type())
686 .finish(|mut builder| {
687 let sig_out = builder.add_load_value(sig);
688 let msg_out = builder.add_load_value(msg);
689 let [err] = builder
690 .add_dataflow_op(make_error_op, [sig_out, msg_out])
691 .unwrap()
692 .outputs_arr();
693 builder.finish_hugr_with_outputs([err]).unwrap()
694 });
695
696 check_emission!(hugr, prelude_llvm_ctx);
697 }
698
699 #[rstest]
700 fn prelude_make_error_and_panic(prelude_llvm_ctx: TestContext) {
701 let sig: ConstUsize = ConstUsize::new(100);
702 let msg: ConstString = ConstString::new("Error!".into());
703
704 let make_error_op = PRELUDE
705 .instantiate_extension_op(&MAKE_ERROR_OP_ID, [])
706 .unwrap();
707
708 let panic_op = PRELUDE
709 .instantiate_extension_op(&PANIC_OP_ID, [Term::new_list([]), Term::new_list([])])
710 .unwrap();
711
712 let hugr = SimpleHugrConfig::new()
713 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
714 .finish(|mut builder| {
715 let sig_out = builder.add_load_value(sig);
716 let msg_out = builder.add_load_value(msg);
717 let [err] = builder
718 .add_dataflow_op(make_error_op, [sig_out, msg_out])
719 .unwrap()
720 .outputs_arr();
721 builder.add_dataflow_op(panic_op, [err]).unwrap();
722 builder.finish_hugr_with_outputs([]).unwrap()
723 });
724
725 check_emission!(hugr, prelude_llvm_ctx);
726 }
727
728 #[rstest]
729 fn prelude_load_nat(prelude_llvm_ctx: TestContext) {
730 let hugr = SimpleHugrConfig::new()
731 .with_outs(usize_t())
732 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
733 .finish(|mut builder| {
734 let v = builder
735 .add_dataflow_op(LoadNat::new(42u64.into()), vec![])
736 .unwrap()
737 .out_wire(0);
738 builder.finish_hugr_with_outputs([v]).unwrap()
739 });
740 check_emission!(hugr, prelude_llvm_ctx);
741 }
742
743 #[fixture]
744 fn barrier_hugr() -> Hugr {
745 SimpleHugrConfig::new()
746 .with_outs(vec![usize_t()])
747 .with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
748 .finish(|mut builder| {
749 let i = builder.add_load_value(ConstUsize::new(42));
750 let [w1, _w2] = builder.add_barrier([i, i]).unwrap().outputs_arr();
751 builder.finish_hugr_with_outputs([w1]).unwrap()
752 })
753 }
754
755 #[rstest]
756 fn prelude_barrier(prelude_llvm_ctx: TestContext, barrier_hugr: Hugr) {
757 check_emission!(barrier_hugr, prelude_llvm_ctx);
758 }
759 #[rstest]
760 fn prelude_barrier_exec(mut exec_ctx: TestContext, barrier_hugr: Hugr) {
761 exec_ctx.add_extensions(|cem| add_prelude_extensions(cem, TestPreludeCodegen));
762 assert_eq!(exec_ctx.exec_hugr_u64(barrier_hugr, "main"), 42);
763 }
764}