Skip to main content

geam_core/plan/
execution.rs

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    /// Seals all entry-reachable host specializations into executable storage.
153    ///
154    /// A linked but unused provider does not participate in sealing.
155    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}