1pub(crate) mod constant;
2mod explain;
3pub(crate) mod function;
4pub(crate) mod graph;
5pub(crate) mod host;
6mod lowering;
7pub(crate) mod runtime;
8pub(crate) mod type_;
9
10use self::constant::ProfiledConstantTable;
11#[cfg(test)]
12use self::constant::{ConstantId, ConstantValue};
13#[cfg(test)]
14use self::function::ExecutableFunction;
15#[cfg(test)]
16use self::function::{
17 BitArrayFunctionFunctionId, BitArrayFunctionId, BitArrayListFunctionId, BoolFunctionFunctionId,
18 BoolFunctionId, BoolListFunctionId, CustomFunctionFunctionId, CustomFunctionId,
19 CustomListFunctionId, ExecutionBitArrayFunctionBody, ExecutionBitArrayFunctionFunctionBody,
20 ExecutionBoolFunctionBody, ExecutionBoolFunctionFunctionBody,
21 ExecutionCoreListFunctionFunctionBody, ExecutionCustomFunctionBody,
22 ExecutionCustomFunctionFunctionBody, ExecutionFloatFunctionFunctionBody,
23 ExecutionFunctionFunctionFunctionBody, ExecutionGenericFunctionFunctionBody,
24 ExecutionIntFunctionBody, ExecutionIntFunctionFunctionBody, ExecutionNeverFunctionFunctionBody,
25 ExecutionNilFunctionBody, ExecutionNilFunctionFunctionBody,
26 ExecutionStringFunctionFunctionBody, ExecutionTupleFunctionBody,
27 ExecutionTupleFunctionFunctionBody, ExecutionUtfCodepointFunctionFunctionBody,
28 FloatFunctionFunctionId, FloatListFunctionId, FunctionFunctionFunctionId,
29 FunctionListFunctionId, GenericFunctionFunctionId, IntFunctionFunctionId, IntFunctionId,
30 IntListFunctionId, ListListFunctionId, NeverFunctionFunctionId, NilFunctionFunctionId,
31 NilFunctionId, NilListFunctionId, ParameterListFunctionId, ParameterListListFunctionId,
32 ProfiledListFunctionFunctionId, StringFunctionFunctionId, StringListFunctionId,
33 TupleFunctionFunctionId, TupleFunctionId, TupleListFunctionId, UtfCodepointFunctionFunctionId,
34 UtfCodepointListFunctionId,
35};
36use self::function::{
37 ExecutionGraphProfile, ExecutionProfile, FunctionLabelSource, FunctionTables,
38 ProfiledRuntimeFunctionId,
39};
40#[cfg(test)]
41use self::type_::{
42 CustomConstructorId, CustomConstructorRefinement, CustomTypeId, CustomValueShape,
43 CustomValueShapeId, FunctionListTypeId, FunctionType, ListListTypeId, ListStorageTypeId,
44 ListTypeId, TupleListTypeId, ValueShapeId, ValueType,
45};
46use self::type_::{CustomTypeTable, ExternalTypeTable, ListTypeTable, ValueShapeTable};
47use crate::host::HostProfile;
48use crate::plan::{HostedModulePlan, ModuleId, ModulePlan, SourceContext};
49use ecow::EcoString;
50pub use explain::ExecutionPlanExplanation;
51pub use host::{HostSpecializationError, HostSpecializationErrorReason};
52use std::convert::Infallible;
53
54pub struct ExecutionPlan {
55 program: ExecutionProgram<Infallible>,
56}
57
58pub struct HostedExecution<Profile: HostProfile> {
59 program: ExecutionProgram<host::HostedExecutionProfile>,
60 host_functions: host::HostFunctionTables<Profile>,
61 external_stores: Profile::ExternalStores,
62}
63
64pub(crate) struct ExecutionProgram<Profile: ExecutionProfile> {
65 common: ExecutionProgramCommon<Profile::Graph>,
66 functions: FunctionTables<Profile>,
67}
68
69struct ExecutionProgramCommon<Graph: ExecutionGraphProfile> {
70 root: ModuleId,
71 modules: Box<[ExecutionModuleContext]>,
72 main: ProfiledRuntimeFunctionId<Graph>,
73 constants: ProfiledConstantTable<Graph>,
74 list_types: ListTypeTable,
75 custom_types: CustomTypeTable,
76 external_types: ExternalTypeTable,
77 value_shapes: ValueShapeTable,
78}
79
80struct ExecutionModuleContext {
81 module: EcoString,
82 source_context: Option<SourceContext>,
83}
84
85impl ExecutionModuleContext {
86 fn new(module: EcoString, source_context: Option<SourceContext>) -> Self {
87 Self {
88 module,
89 source_context,
90 }
91 }
92}
93
94impl explain::Explain for ExecutionPlan {
95 fn write_explanation(&self, context: &mut explain::ExplainContext<'_, '_>) {
96 context.push_str("module ");
97 context.push_str(self.module());
98 context.push_str("\nmain ");
99 self.program
100 .common
101 .main
102 .function_label()
103 .write(context.output());
104 context.push('\n');
105 context.write(&self.program.functions);
106 context.write(&self.program.common.constants);
107 }
108}
109
110impl<Profile: HostProfile> explain::Explain for HostedExecution<Profile> {
111 fn write_explanation(&self, context: &mut explain::ExplainContext<'_, '_>) {
112 context.push_str("module ");
113 context.push_str(&self.program.common.modules[self.program.common.root.index()].module);
114 context.push_str("\nmain ");
115 self.program
116 .common
117 .main
118 .function_label()
119 .write(context.output());
120 context.push('\n');
121 context.write(&function::HostedFunctionTablesExplanation::new(
122 &self.program.functions,
123 &self.host_functions,
124 ));
125 context.write(&self.program.common.constants);
126 }
127}
128
129impl ExecutionPlan {
130 pub fn from_module_plan(module_plan: ModulePlan) -> Self {
131 Self {
132 program: lowering::lower(module_plan),
133 }
134 }
135
136 pub fn module(&self) -> &EcoString {
137 &self.program.common.modules[self.program.common.root.index()].module
138 }
139
140 pub fn source_context(&self) -> Option<&SourceContext> {
141 self.program.common.modules[self.program.common.root.index()]
142 .source_context
143 .as_ref()
144 }
145
146 pub fn explain(&self) -> ExecutionPlanExplanation<'_> {
147 ExecutionPlanExplanation::new(self)
148 }
149}
150
151impl<Profile: HostProfile> HostedExecution<Profile> {
152 pub fn try_from_module_plan(
156 module_plan: HostedModulePlan<Profile>,
157 ) -> Result<Self, HostSpecializationError> {
158 let (program, host_functions) = lowering::lower_hosted(module_plan)?;
159 Ok(Self {
160 program,
161 host_functions,
162 external_stores: Profile::ExternalStores::default(),
163 })
164 }
165
166 pub fn run_main(
167 &self,
168 state: &mut Profile::RunState,
169 echo: &mut dyn crate::EchoSink,
170 ) -> Result<crate::Value, crate::ExecutionError> {
171 crate::runtime::run_hosted_main(self, state, echo)
172 }
173
174 pub fn explain(&self) -> ExecutionPlanExplanation<'_> {
175 ExecutionPlanExplanation::new_hosted(self)
176 }
177
178 pub(crate) fn host_value_function<Body>(
179 &self,
180 id: &host::HostFunctionId<Body>,
181 ) -> &host::HostedValueFunction<Profile>
182 where
183 Body: function::ExecutionFunctionBody,
184 {
185 self.host_functions.value(id)
186 }
187
188 pub(crate) fn host_never_function(
189 &self,
190 id: host::HostNeverFunctionId,
191 ) -> &host::HostedNeverFunction<Profile> {
192 self.host_functions.never(id)
193 }
194
195 pub(crate) fn external_stores(&self) -> &Profile::ExternalStores {
196 &self.external_stores
197 }
198}
199
200#[cfg(test)]
201impl ExecutionPlan {
202 pub(crate) fn main_runtime(&self) -> self::function::RuntimeFunctionId {
203 self.program.common.main.runtime_id()
204 }
205
206 pub(crate) fn constant<Return: ConstantValue>(
207 &self,
208 id: ConstantId<Return>,
209 ) -> &self::constant::ProfiledConstantProgram<Return, Infallible> {
210 self.program.common.constants.get(id)
211 }
212
213 pub(crate) fn list_value_type(&self, id: ListTypeId) -> crate::plan::ValueType {
214 self.program.common.list_types.list_value_type(
215 id,
216 &self.program.common.custom_types,
217 &self.program.common.external_types,
218 )
219 }
220
221 #[cfg(test)]
222 pub(crate) fn list_storage_type(&self, id: ListTypeId) -> ListStorageTypeId {
223 self.program.common.list_types.storage_type(id)
224 }
225
226 pub(crate) fn tuple_list_item_type(&self, id: TupleListTypeId) -> Vec<crate::plan::ValueType> {
227 self.program.common.list_types.tuple_item_type(
228 id,
229 &self.program.common.custom_types,
230 &self.program.common.external_types,
231 )
232 }
233
234 pub(crate) fn nested_list_item_type(&self, id: ListListTypeId) -> crate::plan::ValueType {
235 self.program.common.list_types.nested_list_item_type(
236 id,
237 &self.program.common.custom_types,
238 &self.program.common.external_types,
239 )
240 }
241
242 pub(crate) fn function_list_item_type(
243 &self,
244 id: FunctionListTypeId,
245 ) -> crate::plan::FunctionType {
246 self.program.common.list_types.function_item_type(
247 id,
248 &self.program.common.custom_types,
249 &self.program.common.external_types,
250 )
251 }
252
253 pub(crate) fn function_type(&self, type_: &FunctionType) -> crate::plan::FunctionType {
254 self.program.common.list_types.function_type(
255 type_,
256 &self.program.common.custom_types,
257 &self.program.common.external_types,
258 )
259 }
260
261 pub(crate) fn custom_value_type(&self, id: CustomTypeId) -> crate::plan::CustomType {
262 self.program.common.custom_types.value_type(id)
263 }
264
265 #[cfg(test)]
266 pub(crate) fn custom_shape_refinement(
267 &self,
268 shape: &CustomValueShape,
269 ) -> CustomConstructorRefinement {
270 self.program
271 .common
272 .value_shapes
273 .custom(shape.shape_id())
274 .constructor()
275 }
276
277 #[cfg(test)]
278 pub(crate) fn custom_shape_value_type(
279 &self,
280 shape: &CustomValueShape,
281 ) -> crate::plan::CustomType {
282 self.custom_shape_type(shape.shape_id())
283 }
284
285 #[cfg(test)]
286 fn custom_shape_type(&self, id: CustomValueShapeId) -> crate::plan::CustomType {
287 let shape = self.program.common.value_shapes.custom(id);
288 let nominal = self.program.common.custom_types.value_type(shape.type_id());
289 crate::plan::CustomType::new(
290 nominal.type_name().clone(),
291 shape
292 .arguments()
293 .iter()
294 .map(|argument| {
295 self.program.common.list_types.value_type(
296 self.program.common.value_shapes.value_type(*argument),
297 &self.program.common.custom_types,
298 &self.program.common.external_types,
299 )
300 })
301 .collect(),
302 )
303 }
304
305 pub(crate) fn shape_value_type(&self, id: ValueShapeId) -> ValueType {
306 self.program.common.value_shapes.value_type(id).clone()
307 }
308
309 pub(crate) fn custom_constructor(
310 &self,
311 id: CustomConstructorId,
312 ) -> &type_::CustomConstructorDescriptor {
313 self.program.common.custom_types.constructor(id)
314 }
315
316 #[cfg(test)]
317 pub(crate) fn custom_constructor_id(
318 &self,
319 type_index: usize,
320 constructor_index: usize,
321 ) -> CustomConstructorId {
322 self.program
323 .common
324 .custom_types
325 .constructor_id(type_index, constructor_index)
326 }
327
328 pub(crate) fn int_function(
329 &self,
330 id: IntFunctionId,
331 ) -> &ExecutableFunction<ExecutionIntFunctionBody<Infallible>> {
332 self.program.functions.int_function(id)
333 }
334
335 pub(crate) fn bit_array_function(
336 &self,
337 id: BitArrayFunctionId,
338 ) -> &ExecutableFunction<ExecutionBitArrayFunctionBody<Infallible>> {
339 self.program.functions.bit_array_function(id)
340 }
341
342 pub(crate) fn custom_function(
343 &self,
344 id: CustomFunctionId,
345 ) -> &ExecutableFunction<ExecutionCustomFunctionBody<Infallible>> {
346 self.program.functions.custom_function(id)
347 }
348
349 #[cfg(test)]
350 pub(crate) fn custom_function_id(&self, index: usize) -> CustomFunctionId {
351 self.program.functions.custom_function_id(index)
352 }
353
354 pub(crate) fn bool_function(
355 &self,
356 id: BoolFunctionId,
357 ) -> &ExecutableFunction<ExecutionBoolFunctionBody<Infallible>> {
358 self.program.functions.bool_function(id)
359 }
360
361 pub(crate) fn nil_function(
362 &self,
363 id: NilFunctionId,
364 ) -> &ExecutableFunction<ExecutionNilFunctionBody<Infallible>> {
365 self.program.functions.nil_function(id)
366 }
367
368 pub(crate) fn tuple_function(
369 &self,
370 id: TupleFunctionId,
371 ) -> &ExecutableFunction<ExecutionTupleFunctionBody<Infallible>> {
372 self.program.functions.tuple_function(id)
373 }
374
375 #[cfg(test)]
376 pub(crate) fn parameter_list_function_id(&self, index: usize) -> ParameterListFunctionId {
377 self.program.functions.parameter_list_function_id(index)
378 }
379
380 #[cfg(test)]
381 pub(crate) fn parameter_list_list_function_id(
382 &self,
383 index: usize,
384 ) -> ParameterListListFunctionId {
385 self.program
386 .functions
387 .parameter_list_list_function_id(index)
388 }
389
390 #[cfg(test)]
391 pub(crate) fn int_list_function_id(&self, index: usize) -> IntListFunctionId {
392 self.program.functions.int_list_function_id(index)
393 }
394
395 #[cfg(test)]
396 pub(crate) fn string_list_function_id(&self, index: usize) -> StringListFunctionId {
397 self.program.functions.string_list_function_id(index)
398 }
399
400 #[cfg(test)]
401 pub(crate) fn bit_array_list_function_id(&self, index: usize) -> BitArrayListFunctionId {
402 self.program.functions.bit_array_list_function_id(index)
403 }
404
405 #[cfg(test)]
406 pub(crate) fn utf_codepoint_list_function_id(
407 &self,
408 index: usize,
409 ) -> UtfCodepointListFunctionId {
410 self.program.functions.utf_codepoint_list_function_id(index)
411 }
412
413 #[cfg(test)]
414 pub(crate) fn custom_list_function_id(&self, index: usize) -> CustomListFunctionId {
415 self.program.functions.custom_list_function_id(index)
416 }
417
418 #[cfg(test)]
419 pub(crate) fn float_list_function_id(&self, index: usize) -> FloatListFunctionId {
420 self.program.functions.float_list_function_id(index)
421 }
422
423 #[cfg(test)]
424 pub(crate) fn bool_list_function_id(&self, index: usize) -> BoolListFunctionId {
425 self.program.functions.bool_list_function_id(index)
426 }
427
428 #[cfg(test)]
429 pub(crate) fn nil_list_function_id(&self, index: usize) -> NilListFunctionId {
430 self.program.functions.nil_list_function_id(index)
431 }
432
433 #[cfg(test)]
434 pub(crate) fn tuple_list_function_id(&self, index: usize) -> TupleListFunctionId {
435 self.program.functions.tuple_list_function_id(index)
436 }
437
438 #[cfg(test)]
439 pub(crate) fn list_list_function_id(&self, index: usize) -> ListListFunctionId {
440 self.program.functions.list_list_function_id(index)
441 }
442
443 #[cfg(test)]
444 pub(crate) fn function_list_function_id(&self, index: usize) -> FunctionListFunctionId {
445 self.program.functions.function_list_function_id(index)
446 }
447
448 pub(crate) fn int_function_function(
449 &self,
450 id: IntFunctionFunctionId,
451 ) -> &ExecutableFunction<ExecutionIntFunctionFunctionBody<Infallible>> {
452 self.program.functions.int_function_function(id)
453 }
454
455 pub(crate) fn float_function_function(
456 &self,
457 id: FloatFunctionFunctionId,
458 ) -> &ExecutableFunction<ExecutionFloatFunctionFunctionBody<Infallible>> {
459 self.program.functions.float_function_function(id)
460 }
461
462 pub(crate) fn string_function_function(
463 &self,
464 id: StringFunctionFunctionId,
465 ) -> &ExecutableFunction<ExecutionStringFunctionFunctionBody<Infallible>> {
466 self.program.functions.string_function_function(id)
467 }
468
469 pub(crate) fn bit_array_function_function(
470 &self,
471 id: BitArrayFunctionFunctionId,
472 ) -> &ExecutableFunction<ExecutionBitArrayFunctionFunctionBody<Infallible>> {
473 self.program.functions.bit_array_function_function(id)
474 }
475
476 pub(crate) fn utf_codepoint_function_function(
477 &self,
478 id: UtfCodepointFunctionFunctionId,
479 ) -> &ExecutableFunction<ExecutionUtfCodepointFunctionFunctionBody<Infallible>> {
480 self.program.functions.utf_codepoint_function_function(id)
481 }
482
483 pub(crate) fn custom_function_function(
484 &self,
485 id: &CustomFunctionFunctionId,
486 ) -> &ExecutableFunction<ExecutionCustomFunctionFunctionBody<Infallible>> {
487 self.program.functions.custom_function_function(id)
488 }
489
490 pub(crate) fn generic_function_function(
491 &self,
492 id: &GenericFunctionFunctionId,
493 ) -> &ExecutableFunction<ExecutionGenericFunctionFunctionBody<Infallible>> {
494 self.program.functions.generic_function_function(id)
495 }
496
497 pub(crate) fn never_function_function(
498 &self,
499 id: &NeverFunctionFunctionId,
500 ) -> &ExecutableFunction<ExecutionNeverFunctionFunctionBody<Infallible>> {
501 self.program.functions.never_function_function(id)
502 }
503
504 pub(crate) fn bool_function_function(
505 &self,
506 id: BoolFunctionFunctionId,
507 ) -> &ExecutableFunction<ExecutionBoolFunctionFunctionBody<Infallible>> {
508 self.program.functions.bool_function_function(id)
509 }
510
511 pub(crate) fn nil_function_function(
512 &self,
513 id: NilFunctionFunctionId,
514 ) -> &ExecutableFunction<ExecutionNilFunctionFunctionBody<Infallible>> {
515 self.program.functions.nil_function_function(id)
516 }
517
518 pub(crate) fn tuple_function_function(
519 &self,
520 id: TupleFunctionFunctionId,
521 ) -> &ExecutableFunction<ExecutionTupleFunctionFunctionBody<Infallible>> {
522 self.program.functions.tuple_function_function(id)
523 }
524
525 pub(crate) fn core_list_function_function(
526 &self,
527 id: &ProfiledListFunctionFunctionId<Infallible>,
528 ) -> &ExecutableFunction<ExecutionCoreListFunctionFunctionBody<Infallible>> {
529 self.program.functions.core_list_function_function(id)
530 }
531
532 pub(crate) fn function_function_function(
533 &self,
534 id: &FunctionFunctionFunctionId,
535 ) -> &ExecutableFunction<ExecutionFunctionFunctionFunctionBody<Infallible>> {
536 self.program.functions.function_function_function(id)
537 }
538
539 #[cfg(test)]
540 pub(crate) fn function_function_function_id(&self, index: usize) -> FunctionFunctionFunctionId {
541 self.program.functions.function_function_function_id(index)
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::HostedExecution;
548 use crate::plan::execution::explain;
549 use crate::plan::execution::function::IntFunctionId;
550 use crate::{
551 HostModule, HostProviderSet, ModuleSource, PackageSource, compile_typed_host_program,
552 compile_typed_module, plan_host_program, plan_module,
553 };
554 use num_bigint::BigInt;
555 use std::convert::Infallible;
556
557 #[test]
558 fn plain_execution_program_keeps_host_targets_uninhabited() {
559 let typed = compile_typed_module("main", "main.gleam", "pub fn main() { 1 }")
560 .expect("source should compile");
561 let plan = plan_module(typed).expect("source should plan");
562 let execution = super::ExecutionPlan::from_module_plan(plan);
563 let function: &super::ExecutableFunction<
564 super::function::ExecutionIntFunctionBody<Infallible>,
565 > = execution.program.functions.int_function(IntFunctionId(0));
566
567 assert_eq!(function.body().block_graph().blocks().len(), 1);
568 }
569
570 #[test]
571 fn exposes_the_root_module_and_source_context() {
572 let source = "pub fn main() { 1 }";
573 let typed =
574 compile_typed_module("sample", "sample.gleam", source).expect("source should compile");
575 let context = crate::SourceContext::new("sample.gleam", source);
576 let module =
577 crate::plan_module_with_source(typed, context.clone()).expect("source should plan");
578 let execution = super::ExecutionPlan::from_module_plan(module);
579
580 assert_eq!(execution.module(), "sample");
581 assert_eq!(execution.source_context(), Some(&context));
582 }
583
584 #[test]
585 fn writes_the_complete_execution_plan() {
586 let source = "pub fn main() { 1 }";
587 let expected = concat!(
588 "module main\n",
589 "main int#0\n",
590 "\n",
591 "function int#0\n",
592 " entry b0 params=[] captures=[]\n",
593 " block b0 params=[]\n",
594 " %int#0:shape#0(Int) = int.value 1\n",
595 " return %int#0\n",
596 );
597
598 explain::assert_rendered(source, expected, |plan, output| {
599 let mut context = explain::ExplainContext::new(plan, output);
600 context.write(plan);
601 });
602 }
603
604 #[test]
605 fn writes_the_complete_hosted_execution_plan() {
606 let math = HostModule::new("host_support", "host/math")
607 .expect("host module should be valid")
608 .with_function("add", <BigInt as std::ops::Add>::add)
609 .expect("host function should be valid");
610 let hosts = HostProviderSet::new([math]).expect("host modules should be unique");
611 let source = "import host/math\npub fn main() { math.add(1, 2) }";
612 let typed = compile_typed_host_program(
613 "application",
614 "main",
615 [PackageSource::new(
616 "application",
617 ["host_support"],
618 [ModuleSource::new("main", "main.gleam", source)],
619 )],
620 hosts,
621 )
622 .expect("host source should compile");
623 let plan = plan_host_program(typed).expect("host source should plan");
624 let execution =
625 HostedExecution::try_from_module_plan(plan).expect("hosted execution should seal");
626 let expected = concat!(
627 "module main\n",
628 "main int#0\n",
629 "\nfunction int#0\n",
630 " entry b0 params=[] captures=[]\n",
631 " block b0 params=[]\n",
632 " %int#0:shape#0(Int) = int.value 1\n",
633 " %int#1:shape#0(Int) = int.value 2\n",
634 " tail int#1 args=[%int#0, %int#1]\n",
635 "\nfunction int#1\n",
636 " host host_support::host/math.add signature=fn(Int, Int) -> Int\n",
637 );
638
639 assert_eq!(execution.explain().to_string(), expected);
640 }
641}