Skip to content

Commit 5e3cbac

Browse files
committed
feat: add exit operation to prelude
panic ends all shots, exit ends current shot and signals next shot can still run By default they are handled identically with an abort in codegen - I am not sure if this is the right thing to do.
1 parent 907acca commit 5e3cbac

File tree

6 files changed

+253
-2
lines changed

6 files changed

+253
-2
lines changed

hugr-core/src/extension/prelude.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,27 @@ lazy_static! {
102102
PolyFuncTypeRV::new(
103103
[TypeParam::new_list(TypeBound::Any), TypeParam::new_list(TypeBound::Any)],
104104
FuncValueType::new(
105-
vec![TypeRV::new_extension(error_type), TypeRV::new_row_var_use(0, TypeBound::Any)],
105+
vec![TypeRV::new_extension(error_type.clone()), TypeRV::new_row_var_use(0, TypeBound::Any)],
106106
vec![TypeRV::new_row_var_use(1, TypeBound::Any)],
107107
),
108108
),
109109
extension_ref,
110110
)
111111
.unwrap();
112+
prelude
113+
.add_op(
114+
EXIT_OP_ID,
115+
"Exit with input error".to_string(),
116+
PolyFuncTypeRV::new(
117+
[TypeParam::new_list(TypeBound::Any), TypeParam::new_list(TypeBound::Any)],
118+
FuncValueType::new(
119+
vec![TypeRV::new_extension(error_type), TypeRV::new_row_var_use(0, TypeBound::Any)],
120+
vec![TypeRV::new_row_var_use(1, TypeBound::Any)],
121+
),
122+
),
123+
extension_ref,
124+
)
125+
.unwrap();
112126

113127
TupleOpDef::load_all_ops(prelude, extension_ref).unwrap();
114128
NoopDef.add_to_extension(prelude, extension_ref).unwrap();
@@ -163,8 +177,25 @@ pub fn bool_t() -> Type {
163177
/// second sequence of types in its instantiation. Note that the inputs and
164178
/// outputs only exist so that structural constraints such as linearity can be
165179
/// satisfied.
180+
///
181+
/// Panic immediately halts a multi-shot program. It is intended to be used in
182+
/// cases where an unrecoverable error is detected.
166183
pub const PANIC_OP_ID: OpName = OpName::new_inline("panic");
167184

185+
/// Name of the prelude exit operation.
186+
///
187+
/// This operation can have any input and any output wires; it is instantiated
188+
/// with two [TypeArg::Sequence]s representing these. The first input to the
189+
/// operation is always an error type; the remaining inputs correspond to the
190+
/// first sequence of types in its instantiation; the outputs correspond to the
191+
/// second sequence of types in its instantiation. Note that the inputs and
192+
/// outputs only exist so that structural constraints such as linearity can be
193+
/// satisfied.
194+
///
195+
/// Exit immediately halts a single shot's execution. It is intended to be used
196+
/// when the user wants to exit the program early.
197+
pub const EXIT_OP_ID: OpName = OpName::new_inline("exit");
198+
168199
/// Name of the string type.
169200
pub const STRING_TYPE_NAME: TypeName = TypeName::new_inline("string");
170201

@@ -1077,7 +1108,7 @@ mod test {
10771108

10781109
const TYPE_ARG_NONE: TypeArg = TypeArg::Sequence { elems: vec![] };
10791110
let op = PRELUDE
1080-
.instantiate_extension_op(&PANIC_OP_ID, [TYPE_ARG_NONE, TYPE_ARG_NONE])
1111+
.instantiate_extension_op(&EXIT_OP_ID, [TYPE_ARG_NONE, TYPE_ARG_NONE])
10811112
.unwrap();
10821113

10831114
b.add_dataflow_op(op, [err]).unwrap();

hugr-llvm/src/extension/prelude.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,26 @@ pub fn add_prelude_extensions<'a, H: HugrView<Node = Node> + 'a>(
340340
args.outputs.finish(context.builder(), returns)
341341
}
342342
})
343+
.extension_op(prelude::PRELUDE_ID, prelude::EXIT_OP_ID, {
344+
// by default treat an exit like a panic
345+
let pcg = pcg.clone();
346+
move |context, args| {
347+
let err = args.inputs[0];
348+
ensure!(
349+
err.get_type()
350+
== pcg
351+
.error_type(&context.typing_session())?
352+
.as_basic_type_enum()
353+
);
354+
pcg.emit_panic(context, err)?;
355+
let returns = args
356+
.outputs
357+
.get_types()
358+
.map(|ty| ty.const_zero())
359+
.collect_vec();
360+
args.outputs.finish(context.builder(), returns)
361+
}
362+
})
343363
.extension_op(prelude::PRELUDE_ID, generic::LOAD_NAT_OP_ID, {
344364
let pcg = pcg.clone();
345365
move |context, args| {
@@ -362,6 +382,7 @@ pub fn add_prelude_extensions<'a, H: HugrView<Node = Node> + 'a>(
362382
#[cfg(test)]
363383
mod test {
364384
use hugr_core::builder::{Dataflow, DataflowSubContainer};
385+
use hugr_core::extension::prelude::EXIT_OP_ID;
365386
use hugr_core::extension::PRELUDE;
366387
use hugr_core::types::{Type, TypeArg};
367388
use hugr_core::{type_row, Hugr};
@@ -522,6 +543,34 @@ mod test {
522543
check_emission!(hugr, prelude_llvm_ctx);
523544
}
524545

546+
#[rstest]
547+
fn prelude_exit(prelude_llvm_ctx: TestContext) {
548+
let error_val = ConstError::new(42, "EXIT");
549+
let type_arg_q: TypeArg = TypeArg::Type { ty: qb_t() };
550+
let type_arg_2q: TypeArg = TypeArg::Sequence {
551+
elems: vec![type_arg_q.clone(), type_arg_q],
552+
};
553+
let exit_op = PRELUDE
554+
.instantiate_extension_op(&EXIT_OP_ID, [type_arg_2q.clone(), type_arg_2q.clone()])
555+
.unwrap();
556+
557+
let hugr = SimpleHugrConfig::new()
558+
.with_ins(vec![qb_t(), qb_t()])
559+
.with_outs(vec![qb_t(), qb_t()])
560+
.with_extensions(prelude::PRELUDE_REGISTRY.to_owned())
561+
.finish(|mut builder| {
562+
let [q0, q1] = builder.input_wires_arr();
563+
let err = builder.add_load_value(error_val);
564+
let [q0, q1] = builder
565+
.add_dataflow_op(exit_op, [err, q0, q1])
566+
.unwrap()
567+
.outputs_arr();
568+
builder.finish_with_outputs([q0, q1]).unwrap()
569+
});
570+
571+
check_emission!(hugr, prelude_llvm_ctx);
572+
}
573+
525574
#[rstest]
526575
fn prelude_print(prelude_llvm_ctx: TestContext) {
527576
let greeting: ConstString = ConstString::new("Hello, world!".into());
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
source: hugr-llvm/src/extension/prelude.rs
3+
expression: mod_str
4+
---
5+
; ModuleID = 'test_context'
6+
source_filename = "test_context"
7+
8+
@0 = private unnamed_addr constant [5 x i8] c"EXIT\00", align 1
9+
@prelude.panic_template = private unnamed_addr constant [34 x i8] c"Program panicked (signal %i): %s\0A\00", align 1
10+
11+
define { i16, i16 } @_hl.main.1(i16 %0, i16 %1) {
12+
alloca_block:
13+
br label %entry_block
14+
15+
entry_block: ; preds = %alloca_block
16+
%2 = extractvalue { i32, i8* } { i32 42, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0) }, 0
17+
%3 = extractvalue { i32, i8* } { i32 42, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0) }, 1
18+
%4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %2, i8* %3)
19+
call void @abort()
20+
%mrv = insertvalue { i16, i16 } undef, i16 0, 0
21+
%mrv8 = insertvalue { i16, i16 } %mrv, i16 0, 1
22+
ret { i16, i16 } %mrv8
23+
}
24+
25+
declare i32 @printf(i8*, ...)
26+
27+
declare void @abort()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
source: hugr-llvm/src/extension/prelude.rs
3+
expression: mod_str
4+
---
5+
; ModuleID = 'test_context'
6+
source_filename = "test_context"
7+
8+
@0 = private unnamed_addr constant [5 x i8] c"EXIT\00", align 1
9+
@prelude.panic_template = private unnamed_addr constant [34 x i8] c"Program panicked (signal %i): %s\0A\00", align 1
10+
11+
define { i16, i16 } @_hl.main.1(i16 %0, i16 %1) {
12+
alloca_block:
13+
%"0" = alloca i16, align 2
14+
%"1" = alloca i16, align 2
15+
%"5_0" = alloca { i32, i8* }, align 8
16+
%"2_0" = alloca i16, align 2
17+
%"2_1" = alloca i16, align 2
18+
%"6_0" = alloca i16, align 2
19+
%"6_1" = alloca i16, align 2
20+
br label %entry_block
21+
22+
entry_block: ; preds = %alloca_block
23+
store { i32, i8* } { i32 42, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"5_0", align 8
24+
store i16 %0, i16* %"2_0", align 2
25+
store i16 %1, i16* %"2_1", align 2
26+
%"5_01" = load { i32, i8* }, { i32, i8* }* %"5_0", align 8
27+
%"2_02" = load i16, i16* %"2_0", align 2
28+
%"2_13" = load i16, i16* %"2_1", align 2
29+
%2 = extractvalue { i32, i8* } %"5_01", 0
30+
%3 = extractvalue { i32, i8* } %"5_01", 1
31+
%4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %2, i8* %3)
32+
call void @abort()
33+
store i16 0, i16* %"6_0", align 2
34+
store i16 0, i16* %"6_1", align 2
35+
%"6_04" = load i16, i16* %"6_0", align 2
36+
%"6_15" = load i16, i16* %"6_1", align 2
37+
store i16 %"6_04", i16* %"0", align 2
38+
store i16 %"6_15", i16* %"1", align 2
39+
%"06" = load i16, i16* %"0", align 2
40+
%"17" = load i16, i16* %"1", align 2
41+
%mrv = insertvalue { i16, i16 } undef, i16 %"06", 0
42+
%mrv8 = insertvalue { i16, i16 } %mrv, i16 %"17", 1
43+
ret { i16, i16 } %mrv8
44+
}
45+
46+
declare i32 @printf(i8*, ...)
47+
48+
declare void @abort()

hugr-py/src/hugr/std/_json_defs/prelude.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,54 @@
195195
},
196196
"binary": false
197197
},
198+
"exit": {
199+
"extension": "prelude",
200+
"name": "exit",
201+
"description": "Exit with input error",
202+
"signature": {
203+
"params": [
204+
{
205+
"tp": "List",
206+
"param": {
207+
"tp": "Type",
208+
"b": "A"
209+
}
210+
},
211+
{
212+
"tp": "List",
213+
"param": {
214+
"tp": "Type",
215+
"b": "A"
216+
}
217+
}
218+
],
219+
"body": {
220+
"input": [
221+
{
222+
"t": "Opaque",
223+
"extension": "prelude",
224+
"id": "error",
225+
"args": [],
226+
"bound": "C"
227+
},
228+
{
229+
"t": "R",
230+
"i": 0,
231+
"b": "A"
232+
}
233+
],
234+
"output": [
235+
{
236+
"t": "R",
237+
"i": 1,
238+
"b": "A"
239+
}
240+
],
241+
"runtime_reqs": []
242+
}
243+
},
244+
"binary": false
245+
},
198246
"load_nat": {
199247
"extension": "prelude",
200248
"name": "load_nat",

specification/std_extensions/prelude.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,54 @@
195195
},
196196
"binary": false
197197
},
198+
"exit": {
199+
"extension": "prelude",
200+
"name": "exit",
201+
"description": "Exit with input error",
202+
"signature": {
203+
"params": [
204+
{
205+
"tp": "List",
206+
"param": {
207+
"tp": "Type",
208+
"b": "A"
209+
}
210+
},
211+
{
212+
"tp": "List",
213+
"param": {
214+
"tp": "Type",
215+
"b": "A"
216+
}
217+
}
218+
],
219+
"body": {
220+
"input": [
221+
{
222+
"t": "Opaque",
223+
"extension": "prelude",
224+
"id": "error",
225+
"args": [],
226+
"bound": "C"
227+
},
228+
{
229+
"t": "R",
230+
"i": 0,
231+
"b": "A"
232+
}
233+
],
234+
"output": [
235+
{
236+
"t": "R",
237+
"i": 1,
238+
"b": "A"
239+
}
240+
],
241+
"runtime_reqs": []
242+
}
243+
},
244+
"binary": false
245+
},
198246
"load_nat": {
199247
"extension": "prelude",
200248
"name": "load_nat",

0 commit comments

Comments
 (0)