Skip to main content

yulang_native/
value_cranelift.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt;
3
4use cranelift_codegen::ir::{self, AbiParam, InstBuilder, types};
5use cranelift_codegen::settings;
6use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
7use cranelift_jit::{JITBuilder, JITModule};
8use cranelift_module::{DataDescription, FuncId, Linkage, Module};
9use cranelift_object::{ObjectBuilder, ObjectModule};
10use yulang_runtime as runtime;
11
12use crate::abi::{NativeAbiBlock, NativeAbiFunction, NativeAbiModule, NativeAbiStmt};
13use crate::abi_lane::{NativeAbiRepr, analyze_abi_reprs};
14use crate::abi_validate::{NativeAbiValidateError, validate_abi_module};
15use crate::control_ir::{BlockId, NativeLiteral, NativeTerminator, ValueId};
16use crate::native_runtime::{
17    NATIVE_PRIMITIVE_BOOL_EQ, NATIVE_PRIMITIVE_BOOL_NOT, NATIVE_PRIMITIVE_BOOL_TO_STRING,
18    NATIVE_PRIMITIVE_FLOAT_ADD, NATIVE_PRIMITIVE_FLOAT_DIV, NATIVE_PRIMITIVE_FLOAT_EQ,
19    NATIVE_PRIMITIVE_FLOAT_GE, NATIVE_PRIMITIVE_FLOAT_GT, NATIVE_PRIMITIVE_FLOAT_LE,
20    NATIVE_PRIMITIVE_FLOAT_LT, NATIVE_PRIMITIVE_FLOAT_MUL, NATIVE_PRIMITIVE_FLOAT_SUB,
21    NATIVE_PRIMITIVE_FLOAT_TO_STRING, NATIVE_PRIMITIVE_INT_ADD, NATIVE_PRIMITIVE_INT_DIV,
22    NATIVE_PRIMITIVE_INT_EQ, NATIVE_PRIMITIVE_INT_GE, NATIVE_PRIMITIVE_INT_GT,
23    NATIVE_PRIMITIVE_INT_LE, NATIVE_PRIMITIVE_INT_LT, NATIVE_PRIMITIVE_INT_MUL,
24    NATIVE_PRIMITIVE_INT_SUB, NATIVE_PRIMITIVE_INT_TO_HEX, NATIVE_PRIMITIVE_INT_TO_STRING,
25    NATIVE_PRIMITIVE_INT_TO_UPPER_HEX, NATIVE_PRIMITIVE_STRING_EQ, NATIVE_PRIMITIVE_STRING_INDEX,
26    NATIVE_PRIMITIVE_STRING_LEN, NativeRuntimeContext, yulang_native_bool_and,
27    yulang_native_bool_is_true, yulang_native_closure_env_get, yulang_native_closure_new,
28    yulang_native_closure_push_env, yulang_native_closure_target_id, yulang_native_concat_string,
29    yulang_native_list_empty, yulang_native_list_index, yulang_native_list_index_range,
30    yulang_native_list_index_range_raw, yulang_native_list_len, yulang_native_list_merge,
31    yulang_native_list_singleton, yulang_native_list_splice, yulang_native_list_splice_raw,
32    yulang_native_list_view_raw, yulang_native_make_bool, yulang_native_make_float,
33    yulang_native_make_int, yulang_native_make_string, yulang_native_make_unit,
34    yulang_native_primitive_binary, yulang_native_primitive_unary, yulang_native_record_empty,
35    yulang_native_record_insert, yulang_native_record_select, yulang_native_record_without_field,
36    yulang_native_string_index_range, yulang_native_string_index_range_raw,
37    yulang_native_string_splice, yulang_native_string_splice_raw, yulang_native_tuple_empty,
38    yulang_native_tuple_get, yulang_native_tuple_push, yulang_native_value_eq,
39    yulang_native_variant, yulang_native_variant_payload, yulang_native_variant_tag_eq,
40};
41
42pub type NativeValueCraneliftResult<T> = Result<T, NativeValueCraneliftError>;
43
44#[derive(Debug)]
45pub enum NativeValueCraneliftError {
46    AbiInvalid(NativeAbiValidateError),
47    UnsupportedFunction {
48        function: String,
49        reason: &'static str,
50    },
51    UnsupportedStmt {
52        function: String,
53        kind: &'static str,
54    },
55    UnsupportedLiteral {
56        function: String,
57        literal: NativeLiteral,
58    },
59    MissingBlock {
60        function: String,
61        block: BlockId,
62    },
63    MissingValue {
64        function: String,
65        value: ValueId,
66    },
67    InvalidReturnArity {
68        function: String,
69        arity: usize,
70    },
71    Cranelift(String),
72}
73
74impl fmt::Display for NativeValueCraneliftError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            NativeValueCraneliftError::AbiInvalid(error) => write!(f, "{error}"),
78            NativeValueCraneliftError::UnsupportedFunction { function, reason } => {
79                write!(
80                    f,
81                    "native value Cranelift prototype does not support `{function}` yet: {reason}"
82                )
83            }
84            NativeValueCraneliftError::UnsupportedStmt { function, kind } => write!(
85                f,
86                "native value Cranelift prototype does not support {kind} statements in `{function}` yet"
87            ),
88            NativeValueCraneliftError::UnsupportedLiteral { function, literal } => write!(
89                f,
90                "native value Cranelift prototype does not support literal {literal:?} in `{function}` yet"
91            ),
92            NativeValueCraneliftError::MissingBlock { function, block } => {
93                write!(
94                    f,
95                    "native value Cranelift block {block:?} is missing in `{function}`"
96                )
97            }
98            NativeValueCraneliftError::MissingValue { function, value } => write!(
99                f,
100                "native value Cranelift value {value:?} is missing in `{function}`"
101            ),
102            NativeValueCraneliftError::InvalidReturnArity { function, arity } => write!(
103                f,
104                "native value Cranelift function `{function}` has {arity} return values"
105            ),
106            NativeValueCraneliftError::Cranelift(error) => write!(f, "{error}"),
107        }
108    }
109}
110
111impl std::error::Error for NativeValueCraneliftError {}
112
113impl From<NativeAbiValidateError> for NativeValueCraneliftError {
114    fn from(error: NativeAbiValidateError) -> Self {
115        NativeValueCraneliftError::AbiInvalid(error)
116    }
117}
118
119pub struct NativeValueJitModule {
120    module: JITModule,
121    roots: Vec<FuncId>,
122    strings: Vec<Box<str>>,
123}
124
125impl NativeValueJitModule {
126    pub fn run_roots(&mut self) -> NativeValueCraneliftResult<Vec<runtime::VmValue>> {
127        self.module
128            .finalize_definitions()
129            .map_err(cranelift_error)?;
130        let _string_literals_are_kept_alive = self.strings.len();
131        self.roots
132            .iter()
133            .map(|root| {
134                let ptr = self.module.get_finalized_function(*root);
135                let call = unsafe {
136                    std::mem::transmute::<
137                        _,
138                        extern "C" fn(*mut NativeRuntimeContext) -> *mut runtime::VmValue,
139                    >(ptr)
140                };
141                let mut context = NativeRuntimeContext::new();
142                let value = call(&mut context);
143                if value.is_null() {
144                    return Err(NativeValueCraneliftError::Cranelift(
145                        "native value root returned null".to_string(),
146                    ));
147                }
148                if context.is_closure_handle(value) {
149                    return Err(NativeValueCraneliftError::UnsupportedStmt {
150                        function: "native value root".to_string(),
151                        kind: "closure root value",
152                    });
153                }
154                Ok(unsafe { (*value).clone() })
155            })
156            .collect()
157    }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct NativeValueObjectModule {
162    bytes: Vec<u8>,
163    roots: Vec<String>,
164}
165
166impl NativeValueObjectModule {
167    pub fn bytes(&self) -> &[u8] {
168        &self.bytes
169    }
170
171    pub fn roots(&self) -> &[String] {
172        &self.roots
173    }
174
175    pub fn into_bytes(self) -> Vec<u8> {
176        self.bytes
177    }
178}
179
180pub fn compile_value_abi_module(
181    module: &NativeAbiModule,
182) -> NativeValueCraneliftResult<NativeValueJitModule> {
183    validate_abi_module(module)?;
184    validate_value_prototype_subset(module)?;
185    validate_value_runtime_value_uses(module)?;
186
187    let mut builder =
188        JITBuilder::new(cranelift_module::default_libcall_names()).map_err(cranelift_error)?;
189    builder.symbol(
190        "yulang_native_make_string",
191        yulang_native_make_string as *const u8,
192    );
193    builder.symbol(
194        "yulang_native_make_int",
195        yulang_native_make_int as *const u8,
196    );
197    builder.symbol(
198        "yulang_native_make_float",
199        yulang_native_make_float as *const u8,
200    );
201    builder.symbol(
202        "yulang_native_make_bool",
203        yulang_native_make_bool as *const u8,
204    );
205    builder.symbol(
206        "yulang_native_bool_is_true",
207        yulang_native_bool_is_true as *const u8,
208    );
209    builder.symbol(
210        "yulang_native_value_eq",
211        yulang_native_value_eq as *const u8,
212    );
213    builder.symbol(
214        "yulang_native_bool_and",
215        yulang_native_bool_and as *const u8,
216    );
217    builder.symbol(
218        "yulang_native_make_unit",
219        yulang_native_make_unit as *const u8,
220    );
221    builder.symbol(
222        "yulang_native_closure_new",
223        yulang_native_closure_new as *const u8,
224    );
225    builder.symbol(
226        "yulang_native_closure_push_env",
227        yulang_native_closure_push_env as *const u8,
228    );
229    builder.symbol(
230        "yulang_native_closure_target_id",
231        yulang_native_closure_target_id as *const u8,
232    );
233    builder.symbol(
234        "yulang_native_closure_env_get",
235        yulang_native_closure_env_get as *const u8,
236    );
237    builder.symbol(
238        "yulang_native_concat_string",
239        yulang_native_concat_string as *const u8,
240    );
241    builder.symbol(
242        "yulang_native_primitive_unary",
243        yulang_native_primitive_unary as *const u8,
244    );
245    builder.symbol(
246        "yulang_native_primitive_binary",
247        yulang_native_primitive_binary as *const u8,
248    );
249    builder.symbol(
250        "yulang_native_list_empty",
251        yulang_native_list_empty as *const u8,
252    );
253    builder.symbol(
254        "yulang_native_list_singleton",
255        yulang_native_list_singleton as *const u8,
256    );
257    builder.symbol(
258        "yulang_native_list_merge",
259        yulang_native_list_merge as *const u8,
260    );
261    builder.symbol(
262        "yulang_native_list_len",
263        yulang_native_list_len as *const u8,
264    );
265    builder.symbol(
266        "yulang_native_list_index",
267        yulang_native_list_index as *const u8,
268    );
269    builder.symbol(
270        "yulang_native_list_index_range",
271        yulang_native_list_index_range as *const u8,
272    );
273    builder.symbol(
274        "yulang_native_list_splice",
275        yulang_native_list_splice as *const u8,
276    );
277    builder.symbol(
278        "yulang_native_list_index_range_raw",
279        yulang_native_list_index_range_raw as *const u8,
280    );
281    builder.symbol(
282        "yulang_native_list_splice_raw",
283        yulang_native_list_splice_raw as *const u8,
284    );
285    builder.symbol(
286        "yulang_native_list_view_raw",
287        yulang_native_list_view_raw as *const u8,
288    );
289    builder.symbol(
290        "yulang_native_string_index_range",
291        yulang_native_string_index_range as *const u8,
292    );
293    builder.symbol(
294        "yulang_native_string_splice",
295        yulang_native_string_splice as *const u8,
296    );
297    builder.symbol(
298        "yulang_native_string_index_range_raw",
299        yulang_native_string_index_range_raw as *const u8,
300    );
301    builder.symbol(
302        "yulang_native_string_splice_raw",
303        yulang_native_string_splice_raw as *const u8,
304    );
305    builder.symbol(
306        "yulang_native_tuple_empty",
307        yulang_native_tuple_empty as *const u8,
308    );
309    builder.symbol(
310        "yulang_native_tuple_push",
311        yulang_native_tuple_push as *const u8,
312    );
313    builder.symbol(
314        "yulang_native_tuple_get",
315        yulang_native_tuple_get as *const u8,
316    );
317    builder.symbol(
318        "yulang_native_record_empty",
319        yulang_native_record_empty as *const u8,
320    );
321    builder.symbol(
322        "yulang_native_record_insert",
323        yulang_native_record_insert as *const u8,
324    );
325    builder.symbol(
326        "yulang_native_record_select",
327        yulang_native_record_select as *const u8,
328    );
329    builder.symbol(
330        "yulang_native_record_without_field",
331        yulang_native_record_without_field as *const u8,
332    );
333    builder.symbol("yulang_native_variant", yulang_native_variant as *const u8);
334    builder.symbol(
335        "yulang_native_variant_tag_eq",
336        yulang_native_variant_tag_eq as *const u8,
337    );
338    builder.symbol(
339        "yulang_native_variant_payload",
340        yulang_native_variant_payload as *const u8,
341    );
342    let mut jit = JITModule::new(builder);
343
344    let helpers = declare_helpers(&mut jit)?;
345    let mut strings = Vec::new();
346    let functions = declare_functions(&mut jit, module)?;
347    let mut literals = HostLiteralStore {
348        strings: &mut strings,
349    };
350    define_functions(&mut jit, module, &functions, &helpers, &mut literals)?;
351    Ok(NativeValueJitModule {
352        module: jit,
353        roots: module
354            .roots
355            .iter()
356            .map(|root| {
357                functions.id(&root.name).ok_or_else(|| {
358                    NativeValueCraneliftError::UnsupportedFunction {
359                        function: root.name.clone(),
360                        reason: "root was not declared",
361                    }
362                })
363            })
364            .collect::<NativeValueCraneliftResult<Vec<_>>>()?,
365        strings,
366    })
367}
368
369pub fn compile_value_abi_module_to_object(
370    module: &NativeAbiModule,
371) -> NativeValueCraneliftResult<NativeValueObjectModule> {
372    validate_abi_module(module)?;
373    validate_value_prototype_subset(module)?;
374    validate_value_runtime_value_uses(module)?;
375
376    let isa_builder = cranelift_native::builder().map_err(cranelift_error)?;
377    let flags = settings::Flags::new(settings::builder());
378    let isa = isa_builder.finish(flags).map_err(cranelift_error)?;
379    let builder = ObjectBuilder::new(
380        isa,
381        "yulang_native_value_object".to_string(),
382        cranelift_module::default_libcall_names(),
383    )
384    .map_err(cranelift_error)?;
385    let mut object = ObjectModule::new(builder);
386
387    let helpers = declare_helpers(&mut object)?;
388    let functions = declare_functions(&mut object, module)?;
389    let mut literals = ObjectLiteralStore::default();
390    define_functions(&mut object, module, &functions, &helpers, &mut literals)?;
391    let roots = module
392        .roots
393        .iter()
394        .map(|root| root.name.clone())
395        .collect::<Vec<_>>();
396    let product = object.finish();
397    let bytes = product.emit().map_err(cranelift_error)?;
398    Ok(NativeValueObjectModule { bytes, roots })
399}
400
401fn validate_value_prototype_subset(module: &NativeAbiModule) -> NativeValueCraneliftResult<()> {
402    let function_env_slots = module
403        .functions
404        .iter()
405        .chain(&module.roots)
406        .map(|function| (function.name.as_str(), function.environment_slots))
407        .collect::<HashMap<_, _>>();
408    for function in module.functions.iter().chain(&module.roots) {
409        for block in &function.blocks {
410            for stmt in &block.stmts {
411                match stmt {
412                    NativeAbiStmt::Literal {
413                        literal:
414                            NativeLiteral::Int(_)
415                            | NativeLiteral::Float(_)
416                            | NativeLiteral::Bool(_)
417                            | NativeLiteral::Unit,
418                        ..
419                    } => {}
420                    NativeAbiStmt::Literal {
421                        literal: NativeLiteral::String(_),
422                        ..
423                    } => {}
424                    NativeAbiStmt::Primitive {
425                        op: yulang_typed_ir::PrimitiveOp::StringConcat,
426                        ..
427                    } => {}
428                    NativeAbiStmt::Primitive {
429                        op:
430                            yulang_typed_ir::PrimitiveOp::ListEmpty
431                            | yulang_typed_ir::PrimitiveOp::ListSingleton
432                            | yulang_typed_ir::PrimitiveOp::ListMerge
433                            | yulang_typed_ir::PrimitiveOp::ListLen
434                            | yulang_typed_ir::PrimitiveOp::ListIndex
435                            | yulang_typed_ir::PrimitiveOp::ListIndexRange
436                            | yulang_typed_ir::PrimitiveOp::ListSplice
437                            | yulang_typed_ir::PrimitiveOp::ListIndexRangeRaw
438                            | yulang_typed_ir::PrimitiveOp::ListSpliceRaw
439                            | yulang_typed_ir::PrimitiveOp::ListViewRaw
440                            | yulang_typed_ir::PrimitiveOp::StringIndexRange
441                            | yulang_typed_ir::PrimitiveOp::StringSplice
442                            | yulang_typed_ir::PrimitiveOp::StringIndexRangeRaw
443                            | yulang_typed_ir::PrimitiveOp::StringSpliceRaw,
444                        ..
445                    } => {}
446                    NativeAbiStmt::Primitive { op, .. }
447                        if primitive_unary_code(*op).is_some()
448                            || primitive_binary_code(*op).is_some() => {}
449                    NativeAbiStmt::Primitive { .. } => {
450                        return Err(NativeValueCraneliftError::UnsupportedStmt {
451                            function: function.name.clone(),
452                            kind: "primitive",
453                        });
454                    }
455                    NativeAbiStmt::DirectCall { target, .. } => {
456                        if function_env_slots
457                            .get(target.as_str())
458                            .copied()
459                            .unwrap_or(0)
460                            != 0
461                        {
462                            return Err(NativeValueCraneliftError::UnsupportedStmt {
463                                function: function.name.clone(),
464                                kind: "direct call with hidden environment",
465                            });
466                        }
467                    }
468                    NativeAbiStmt::Tuple { .. }
469                    | NativeAbiStmt::Record { .. }
470                    | NativeAbiStmt::RecordWithoutFields { .. }
471                    | NativeAbiStmt::Variant { .. }
472                    | NativeAbiStmt::Select { .. }
473                    | NativeAbiStmt::TupleGet { .. }
474                    | NativeAbiStmt::VariantTagEq { .. }
475                    | NativeAbiStmt::VariantPayload { .. }
476                    | NativeAbiStmt::ValueEq { .. }
477                    | NativeAbiStmt::BoolAnd { .. } => {}
478                    NativeAbiStmt::LoadEnv { .. }
479                    | NativeAbiStmt::AllocateClosure { .. }
480                    | NativeAbiStmt::IndirectClosureCall { .. } => {}
481                }
482            }
483        }
484    }
485    Ok(())
486}
487
488fn validate_value_runtime_value_uses(module: &NativeAbiModule) -> NativeValueCraneliftResult<()> {
489    let analysis = analyze_abi_reprs(module);
490    for function in module.functions.iter().chain(&module.roots) {
491        let values = analysis.values.get(&function.name).ok_or_else(|| {
492            NativeValueCraneliftError::UnsupportedFunction {
493                function: function.name.clone(),
494                reason: "missing value representation analysis",
495            }
496        })?;
497        for block in &function.blocks {
498            for stmt in &block.stmts {
499                validate_stmt_runtime_value_uses(function, values, stmt)?;
500            }
501            if module.roots.iter().any(|root| root.name == function.name)
502                && let NativeTerminator::Return(value) = &block.terminator
503            {
504                let repr = values.get(value).unwrap_or(&NativeAbiRepr::Unknown);
505                if contains_closure_repr(repr) {
506                    return Err(NativeValueCraneliftError::UnsupportedStmt {
507                        function: function.name.clone(),
508                        kind: "closure root value",
509                    });
510                }
511            }
512        }
513    }
514    Ok(())
515}
516
517fn validate_stmt_runtime_value_uses(
518    function: &NativeAbiFunction,
519    values: &HashMap<ValueId, NativeAbiRepr>,
520    stmt: &NativeAbiStmt,
521) -> NativeValueCraneliftResult<()> {
522    match stmt {
523        NativeAbiStmt::Primitive { args, .. } => {
524            reject_closure_args(function, values, args, "closure primitive argument")
525        }
526        NativeAbiStmt::Tuple { items, .. } => {
527            reject_closure_args(function, values, items, "closure tuple item")
528        }
529        NativeAbiStmt::Record { base, fields, .. } => {
530            if let Some(base) = base {
531                reject_closure_args(function, values, &[*base], "closure record base")?;
532            }
533            reject_closure_args(
534                function,
535                values,
536                &fields.iter().map(|field| field.value).collect::<Vec<_>>(),
537                "closure record field",
538            )
539        }
540        NativeAbiStmt::RecordWithoutFields { base, .. } => {
541            reject_closure_args(function, values, &[*base], "closure record base")
542        }
543        NativeAbiStmt::Variant { value, .. } => value
544            .map(|value| reject_closure_args(function, values, &[value], "closure variant payload"))
545            .transpose()
546            .map(|_| ()),
547        NativeAbiStmt::Select { base, .. } => {
548            reject_closure_args(function, values, &[*base], "closure record select base")
549        }
550        NativeAbiStmt::TupleGet { tuple, .. } => {
551            reject_closure_args(function, values, &[*tuple], "closure tuple select base")
552        }
553        NativeAbiStmt::VariantTagEq { variant, .. } => {
554            reject_closure_args(function, values, &[*variant], "closure variant tag base")
555        }
556        NativeAbiStmt::VariantPayload { variant, .. } => reject_closure_args(
557            function,
558            values,
559            &[*variant],
560            "closure variant payload base",
561        ),
562        NativeAbiStmt::ValueEq { left, right, .. } => reject_closure_args(
563            function,
564            values,
565            &[*left, *right],
566            "closure equality operand",
567        ),
568        NativeAbiStmt::BoolAnd { left, right, .. } => {
569            reject_closure_args(function, values, &[*left, *right], "closure bool operand")
570        }
571        NativeAbiStmt::Literal { .. }
572        | NativeAbiStmt::DirectCall { .. }
573        | NativeAbiStmt::LoadEnv { .. }
574        | NativeAbiStmt::AllocateClosure { .. }
575        | NativeAbiStmt::IndirectClosureCall { .. } => Ok(()),
576    }
577}
578
579fn reject_closure_args(
580    function: &NativeAbiFunction,
581    values: &HashMap<ValueId, NativeAbiRepr>,
582    args: &[ValueId],
583    kind: &'static str,
584) -> NativeValueCraneliftResult<()> {
585    if args.iter().any(|arg| {
586        values
587            .get(arg)
588            .is_some_and(|repr| contains_closure_repr(repr))
589    }) {
590        return Err(NativeValueCraneliftError::UnsupportedStmt {
591            function: function.name.clone(),
592            kind,
593        });
594    }
595    Ok(())
596}
597
598fn contains_closure_repr(repr: &NativeAbiRepr) -> bool {
599    match repr {
600        NativeAbiRepr::ClosurePtr => true,
601        NativeAbiRepr::List(element) => contains_closure_repr(element),
602        NativeAbiRepr::Tuple(items) => items.iter().any(contains_closure_repr),
603        NativeAbiRepr::Record(fields) => fields
604            .iter()
605            .any(|field| contains_closure_repr(&field.value)),
606        NativeAbiRepr::Variant(cases) => cases
607            .iter()
608            .any(|case| case.value.as_ref().is_some_and(contains_closure_repr)),
609        NativeAbiRepr::Unit
610        | NativeAbiRepr::Bool
611        | NativeAbiRepr::Int
612        | NativeAbiRepr::Float
613        | NativeAbiRepr::RuntimeValuePtr(_)
614        | NativeAbiRepr::Unknown => false,
615    }
616}
617
618#[derive(Clone)]
619struct ValueFunctions {
620    ids: HashMap<String, FuncId>,
621    closure_targets: Vec<ValueClosureTarget>,
622}
623
624#[derive(Clone)]
625struct ValueClosureTarget {
626    name: String,
627    id: FuncId,
628    target_id: i64,
629    environment_slots: usize,
630    params: usize,
631}
632
633impl ValueFunctions {
634    fn id(&self, name: &str) -> Option<FuncId> {
635        self.ids.get(name).copied()
636    }
637
638    fn target_id(&self, name: &str) -> Option<i64> {
639        self.closure_targets
640            .iter()
641            .find(|target| target.name == name)
642            .map(|target| target.target_id)
643    }
644
645    fn call_candidates(&self, arity: usize) -> impl Iterator<Item = &ValueClosureTarget> {
646        self.closure_targets
647            .iter()
648            .filter(move |target| target.params == arity)
649    }
650}
651
652fn declare_functions<M: Module>(
653    module_backend: &mut M,
654    module: &NativeAbiModule,
655) -> NativeValueCraneliftResult<ValueFunctions> {
656    let mut ids = HashMap::new();
657    let mut closure_targets = Vec::new();
658    for (index, function) in module.functions.iter().chain(&module.roots).enumerate() {
659        let sig = value_function_signature(module_backend, function);
660        let id = module_backend
661            .declare_function(&function.name, Linkage::Export, &sig)
662            .map_err(cranelift_error)?;
663        ids.insert(function.name.clone(), id);
664        closure_targets.push(ValueClosureTarget {
665            name: function.name.clone(),
666            id,
667            target_id: index as i64,
668            environment_slots: function.environment_slots,
669            params: function.params.len(),
670        });
671    }
672    Ok(ValueFunctions {
673        ids,
674        closure_targets,
675    })
676}
677
678fn define_functions<M: Module, L: ValueLiteralStore>(
679    module_backend: &mut M,
680    module: &NativeAbiModule,
681    functions: &ValueFunctions,
682    helpers: &ValueHelpers,
683    literals: &mut L,
684) -> NativeValueCraneliftResult<()> {
685    for function in module.functions.iter().chain(&module.roots) {
686        let id = functions.id(&function.name).ok_or_else(|| {
687            NativeValueCraneliftError::UnsupportedFunction {
688                function: function.name.clone(),
689                reason: "function was not declared",
690            }
691        })?;
692        let mut ctx = module_backend.make_context();
693        ctx.func.signature = value_function_signature(module_backend, function);
694        lower_value_function(
695            module_backend,
696            &mut ctx,
697            function,
698            functions,
699            helpers,
700            literals,
701        )?;
702        module_backend
703            .define_function(id, &mut ctx)
704            .map_err(cranelift_error)?;
705        module_backend.clear_context(&mut ctx);
706    }
707    Ok(())
708}
709
710#[derive(Debug, Clone, Copy)]
711struct ValueHelpers {
712    make_int: FuncId,
713    make_float: FuncId,
714    make_bool: FuncId,
715    bool_is_true: FuncId,
716    value_eq: FuncId,
717    bool_and: FuncId,
718    make_unit: FuncId,
719    make_string: FuncId,
720    concat_string: FuncId,
721    primitive_unary: FuncId,
722    primitive_binary: FuncId,
723    closure_new: FuncId,
724    closure_push_env: FuncId,
725    closure_target_id: FuncId,
726    closure_env_get: FuncId,
727    list_empty: FuncId,
728    list_singleton: FuncId,
729    list_merge: FuncId,
730    list_len: FuncId,
731    list_index: FuncId,
732    list_index_range: FuncId,
733    list_splice: FuncId,
734    list_index_range_raw: FuncId,
735    list_splice_raw: FuncId,
736    list_view_raw: FuncId,
737    string_index_range: FuncId,
738    string_splice: FuncId,
739    string_index_range_raw: FuncId,
740    string_splice_raw: FuncId,
741    tuple_empty: FuncId,
742    tuple_push: FuncId,
743    tuple_get: FuncId,
744    record_empty: FuncId,
745    record_insert: FuncId,
746    record_select: FuncId,
747    record_without_field: FuncId,
748    variant: FuncId,
749    variant_tag_eq: FuncId,
750    variant_payload: FuncId,
751}
752
753fn declare_helpers<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<ValueHelpers> {
754    Ok(ValueHelpers {
755        make_int: declare_make_int(module_backend)?,
756        make_float: declare_make_float(module_backend)?,
757        make_bool: declare_make_bool(module_backend)?,
758        bool_is_true: declare_bool_is_true(module_backend)?,
759        value_eq: declare_value_eq(module_backend)?,
760        bool_and: declare_bool_and(module_backend)?,
761        make_unit: declare_make_unit(module_backend)?,
762        make_string: declare_make_string(module_backend)?,
763        concat_string: declare_concat_string(module_backend)?,
764        primitive_unary: declare_primitive_unary(module_backend)?,
765        primitive_binary: declare_primitive_binary(module_backend)?,
766        closure_new: declare_closure_new(module_backend)?,
767        closure_push_env: declare_closure_push_env(module_backend)?,
768        closure_target_id: declare_closure_target_id(module_backend)?,
769        closure_env_get: declare_closure_env_get(module_backend)?,
770        list_empty: declare_list_empty(module_backend)?,
771        list_singleton: declare_list_singleton(module_backend)?,
772        list_merge: declare_list_merge(module_backend)?,
773        list_len: declare_list_len(module_backend)?,
774        list_index: declare_list_index(module_backend)?,
775        list_index_range: declare_list_index_range(module_backend)?,
776        list_splice: declare_list_splice(module_backend)?,
777        list_index_range_raw: declare_list_index_range_raw(module_backend)?,
778        list_splice_raw: declare_list_splice_raw(module_backend)?,
779        list_view_raw: declare_list_view_raw(module_backend)?,
780        string_index_range: declare_string_index_range(module_backend)?,
781        string_splice: declare_string_splice(module_backend)?,
782        string_index_range_raw: declare_string_index_range_raw(module_backend)?,
783        string_splice_raw: declare_string_splice_raw(module_backend)?,
784        tuple_empty: declare_tuple_empty(module_backend)?,
785        tuple_push: declare_tuple_push(module_backend)?,
786        tuple_get: declare_tuple_get(module_backend)?,
787        record_empty: declare_record_empty(module_backend)?,
788        record_insert: declare_record_insert(module_backend)?,
789        record_select: declare_record_select(module_backend)?,
790        record_without_field: declare_record_without_field(module_backend)?,
791        variant: declare_variant(module_backend)?,
792        variant_tag_eq: declare_variant_tag_eq(module_backend)?,
793        variant_payload: declare_variant_payload(module_backend)?,
794    })
795}
796
797fn declare_make_int<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
798    let mut sig = module_backend.make_signature();
799    sig.params.push(AbiParam::new(types::I64));
800    sig.params.push(AbiParam::new(types::I64));
801    sig.params.push(AbiParam::new(types::I64));
802    sig.returns.push(AbiParam::new(types::I64));
803    module_backend
804        .declare_function("yulang_native_make_int", Linkage::Import, &sig)
805        .map_err(cranelift_error)
806}
807
808fn declare_make_float<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
809    let mut sig = module_backend.make_signature();
810    sig.params.push(AbiParam::new(types::I64));
811    sig.params.push(AbiParam::new(types::I64));
812    sig.params.push(AbiParam::new(types::I64));
813    sig.returns.push(AbiParam::new(types::I64));
814    module_backend
815        .declare_function("yulang_native_make_float", Linkage::Import, &sig)
816        .map_err(cranelift_error)
817}
818
819fn declare_make_bool<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
820    let mut sig = module_backend.make_signature();
821    sig.params.push(AbiParam::new(types::I64));
822    sig.params.push(AbiParam::new(types::I64));
823    sig.returns.push(AbiParam::new(types::I64));
824    module_backend
825        .declare_function("yulang_native_make_bool", Linkage::Import, &sig)
826        .map_err(cranelift_error)
827}
828
829fn declare_bool_is_true<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
830    let mut sig = module_backend.make_signature();
831    sig.params.push(AbiParam::new(types::I64));
832    sig.returns.push(AbiParam::new(types::I64));
833    module_backend
834        .declare_function("yulang_native_bool_is_true", Linkage::Import, &sig)
835        .map_err(cranelift_error)
836}
837
838fn declare_value_eq<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
839    let mut sig = module_backend.make_signature();
840    sig.params.push(AbiParam::new(types::I64));
841    sig.params.push(AbiParam::new(types::I64));
842    sig.params.push(AbiParam::new(types::I64));
843    sig.returns.push(AbiParam::new(types::I64));
844    module_backend
845        .declare_function("yulang_native_value_eq", Linkage::Import, &sig)
846        .map_err(cranelift_error)
847}
848
849fn declare_bool_and<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
850    let mut sig = module_backend.make_signature();
851    sig.params.push(AbiParam::new(types::I64));
852    sig.params.push(AbiParam::new(types::I64));
853    sig.params.push(AbiParam::new(types::I64));
854    sig.returns.push(AbiParam::new(types::I64));
855    module_backend
856        .declare_function("yulang_native_bool_and", Linkage::Import, &sig)
857        .map_err(cranelift_error)
858}
859
860fn declare_make_unit<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
861    let mut sig = module_backend.make_signature();
862    sig.params.push(AbiParam::new(types::I64));
863    sig.returns.push(AbiParam::new(types::I64));
864    module_backend
865        .declare_function("yulang_native_make_unit", Linkage::Import, &sig)
866        .map_err(cranelift_error)
867}
868
869fn declare_make_string<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
870    let mut sig = module_backend.make_signature();
871    sig.params.push(AbiParam::new(types::I64));
872    sig.params.push(AbiParam::new(types::I64));
873    sig.params.push(AbiParam::new(types::I64));
874    sig.returns.push(AbiParam::new(types::I64));
875    module_backend
876        .declare_function("yulang_native_make_string", Linkage::Import, &sig)
877        .map_err(cranelift_error)
878}
879
880fn declare_list_empty<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
881    let mut sig = module_backend.make_signature();
882    sig.params.push(AbiParam::new(types::I64));
883    sig.returns.push(AbiParam::new(types::I64));
884    module_backend
885        .declare_function("yulang_native_list_empty", Linkage::Import, &sig)
886        .map_err(cranelift_error)
887}
888
889fn declare_list_singleton<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
890    let mut sig = module_backend.make_signature();
891    sig.params.push(AbiParam::new(types::I64));
892    sig.params.push(AbiParam::new(types::I64));
893    sig.returns.push(AbiParam::new(types::I64));
894    module_backend
895        .declare_function("yulang_native_list_singleton", Linkage::Import, &sig)
896        .map_err(cranelift_error)
897}
898
899fn declare_list_merge<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
900    let mut sig = module_backend.make_signature();
901    sig.params.push(AbiParam::new(types::I64));
902    sig.params.push(AbiParam::new(types::I64));
903    sig.params.push(AbiParam::new(types::I64));
904    sig.returns.push(AbiParam::new(types::I64));
905    module_backend
906        .declare_function("yulang_native_list_merge", Linkage::Import, &sig)
907        .map_err(cranelift_error)
908}
909
910fn declare_list_len<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
911    let mut sig = module_backend.make_signature();
912    sig.params.push(AbiParam::new(types::I64));
913    sig.params.push(AbiParam::new(types::I64));
914    sig.returns.push(AbiParam::new(types::I64));
915    module_backend
916        .declare_function("yulang_native_list_len", Linkage::Import, &sig)
917        .map_err(cranelift_error)
918}
919
920fn declare_list_index<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
921    let mut sig = module_backend.make_signature();
922    sig.params.push(AbiParam::new(types::I64));
923    sig.params.push(AbiParam::new(types::I64));
924    sig.params.push(AbiParam::new(types::I64));
925    sig.returns.push(AbiParam::new(types::I64));
926    module_backend
927        .declare_function("yulang_native_list_index", Linkage::Import, &sig)
928        .map_err(cranelift_error)
929}
930
931fn declare_list_index_range<M: Module>(
932    module_backend: &mut M,
933) -> NativeValueCraneliftResult<FuncId> {
934    let mut sig = module_backend.make_signature();
935    sig.params.push(AbiParam::new(types::I64));
936    sig.params.push(AbiParam::new(types::I64));
937    sig.params.push(AbiParam::new(types::I64));
938    sig.returns.push(AbiParam::new(types::I64));
939    module_backend
940        .declare_function("yulang_native_list_index_range", Linkage::Import, &sig)
941        .map_err(cranelift_error)
942}
943
944fn declare_list_splice<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
945    let mut sig = module_backend.make_signature();
946    sig.params.push(AbiParam::new(types::I64));
947    sig.params.push(AbiParam::new(types::I64));
948    sig.params.push(AbiParam::new(types::I64));
949    sig.params.push(AbiParam::new(types::I64));
950    sig.returns.push(AbiParam::new(types::I64));
951    module_backend
952        .declare_function("yulang_native_list_splice", Linkage::Import, &sig)
953        .map_err(cranelift_error)
954}
955
956fn declare_list_index_range_raw<M: Module>(
957    module_backend: &mut M,
958) -> NativeValueCraneliftResult<FuncId> {
959    let mut sig = module_backend.make_signature();
960    sig.params.push(AbiParam::new(types::I64));
961    sig.params.push(AbiParam::new(types::I64));
962    sig.params.push(AbiParam::new(types::I64));
963    sig.params.push(AbiParam::new(types::I64));
964    sig.returns.push(AbiParam::new(types::I64));
965    module_backend
966        .declare_function("yulang_native_list_index_range_raw", Linkage::Import, &sig)
967        .map_err(cranelift_error)
968}
969
970fn declare_list_splice_raw<M: Module>(
971    module_backend: &mut M,
972) -> NativeValueCraneliftResult<FuncId> {
973    let mut sig = module_backend.make_signature();
974    sig.params.push(AbiParam::new(types::I64));
975    sig.params.push(AbiParam::new(types::I64));
976    sig.params.push(AbiParam::new(types::I64));
977    sig.params.push(AbiParam::new(types::I64));
978    sig.params.push(AbiParam::new(types::I64));
979    sig.returns.push(AbiParam::new(types::I64));
980    module_backend
981        .declare_function("yulang_native_list_splice_raw", Linkage::Import, &sig)
982        .map_err(cranelift_error)
983}
984
985fn declare_list_view_raw<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
986    let mut sig = module_backend.make_signature();
987    sig.params.push(AbiParam::new(types::I64));
988    sig.params.push(AbiParam::new(types::I64));
989    sig.returns.push(AbiParam::new(types::I64));
990    module_backend
991        .declare_function("yulang_native_list_view_raw", Linkage::Import, &sig)
992        .map_err(cranelift_error)
993}
994
995fn declare_string_index_range<M: Module>(
996    module_backend: &mut M,
997) -> NativeValueCraneliftResult<FuncId> {
998    let mut sig = module_backend.make_signature();
999    sig.params.push(AbiParam::new(types::I64));
1000    sig.params.push(AbiParam::new(types::I64));
1001    sig.params.push(AbiParam::new(types::I64));
1002    sig.returns.push(AbiParam::new(types::I64));
1003    module_backend
1004        .declare_function("yulang_native_string_index_range", Linkage::Import, &sig)
1005        .map_err(cranelift_error)
1006}
1007
1008fn declare_string_splice<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1009    let mut sig = module_backend.make_signature();
1010    sig.params.push(AbiParam::new(types::I64));
1011    sig.params.push(AbiParam::new(types::I64));
1012    sig.params.push(AbiParam::new(types::I64));
1013    sig.params.push(AbiParam::new(types::I64));
1014    sig.returns.push(AbiParam::new(types::I64));
1015    module_backend
1016        .declare_function("yulang_native_string_splice", Linkage::Import, &sig)
1017        .map_err(cranelift_error)
1018}
1019
1020fn declare_string_index_range_raw<M: Module>(
1021    module_backend: &mut M,
1022) -> NativeValueCraneliftResult<FuncId> {
1023    let mut sig = module_backend.make_signature();
1024    sig.params.push(AbiParam::new(types::I64));
1025    sig.params.push(AbiParam::new(types::I64));
1026    sig.params.push(AbiParam::new(types::I64));
1027    sig.params.push(AbiParam::new(types::I64));
1028    sig.returns.push(AbiParam::new(types::I64));
1029    module_backend
1030        .declare_function(
1031            "yulang_native_string_index_range_raw",
1032            Linkage::Import,
1033            &sig,
1034        )
1035        .map_err(cranelift_error)
1036}
1037
1038fn declare_string_splice_raw<M: Module>(
1039    module_backend: &mut M,
1040) -> NativeValueCraneliftResult<FuncId> {
1041    let mut sig = module_backend.make_signature();
1042    sig.params.push(AbiParam::new(types::I64));
1043    sig.params.push(AbiParam::new(types::I64));
1044    sig.params.push(AbiParam::new(types::I64));
1045    sig.params.push(AbiParam::new(types::I64));
1046    sig.params.push(AbiParam::new(types::I64));
1047    sig.returns.push(AbiParam::new(types::I64));
1048    module_backend
1049        .declare_function("yulang_native_string_splice_raw", Linkage::Import, &sig)
1050        .map_err(cranelift_error)
1051}
1052
1053fn declare_tuple_empty<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1054    let mut sig = module_backend.make_signature();
1055    sig.params.push(AbiParam::new(types::I64));
1056    sig.returns.push(AbiParam::new(types::I64));
1057    module_backend
1058        .declare_function("yulang_native_tuple_empty", Linkage::Import, &sig)
1059        .map_err(cranelift_error)
1060}
1061
1062fn declare_tuple_push<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1063    let mut sig = module_backend.make_signature();
1064    sig.params.push(AbiParam::new(types::I64));
1065    sig.params.push(AbiParam::new(types::I64));
1066    sig.params.push(AbiParam::new(types::I64));
1067    sig.returns.push(AbiParam::new(types::I64));
1068    module_backend
1069        .declare_function("yulang_native_tuple_push", Linkage::Import, &sig)
1070        .map_err(cranelift_error)
1071}
1072
1073fn declare_tuple_get<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1074    let mut sig = module_backend.make_signature();
1075    sig.params.push(AbiParam::new(types::I64));
1076    sig.params.push(AbiParam::new(types::I64));
1077    sig.params.push(AbiParam::new(types::I64));
1078    sig.returns.push(AbiParam::new(types::I64));
1079    module_backend
1080        .declare_function("yulang_native_tuple_get", Linkage::Import, &sig)
1081        .map_err(cranelift_error)
1082}
1083
1084fn declare_record_empty<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1085    let mut sig = module_backend.make_signature();
1086    sig.params.push(AbiParam::new(types::I64));
1087    sig.returns.push(AbiParam::new(types::I64));
1088    module_backend
1089        .declare_function("yulang_native_record_empty", Linkage::Import, &sig)
1090        .map_err(cranelift_error)
1091}
1092
1093fn declare_record_insert<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1094    let mut sig = module_backend.make_signature();
1095    sig.params.push(AbiParam::new(types::I64));
1096    sig.params.push(AbiParam::new(types::I64));
1097    sig.params.push(AbiParam::new(types::I64));
1098    sig.params.push(AbiParam::new(types::I64));
1099    sig.params.push(AbiParam::new(types::I64));
1100    sig.returns.push(AbiParam::new(types::I64));
1101    module_backend
1102        .declare_function("yulang_native_record_insert", Linkage::Import, &sig)
1103        .map_err(cranelift_error)
1104}
1105
1106fn declare_record_select<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1107    let mut sig = module_backend.make_signature();
1108    sig.params.push(AbiParam::new(types::I64));
1109    sig.params.push(AbiParam::new(types::I64));
1110    sig.params.push(AbiParam::new(types::I64));
1111    sig.params.push(AbiParam::new(types::I64));
1112    sig.returns.push(AbiParam::new(types::I64));
1113    module_backend
1114        .declare_function("yulang_native_record_select", Linkage::Import, &sig)
1115        .map_err(cranelift_error)
1116}
1117
1118fn declare_record_without_field<M: Module>(
1119    module_backend: &mut M,
1120) -> NativeValueCraneliftResult<FuncId> {
1121    let mut sig = module_backend.make_signature();
1122    sig.params.push(AbiParam::new(types::I64));
1123    sig.params.push(AbiParam::new(types::I64));
1124    sig.params.push(AbiParam::new(types::I64));
1125    sig.params.push(AbiParam::new(types::I64));
1126    sig.returns.push(AbiParam::new(types::I64));
1127    module_backend
1128        .declare_function("yulang_native_record_without_field", Linkage::Import, &sig)
1129        .map_err(cranelift_error)
1130}
1131
1132fn declare_variant<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1133    let mut sig = module_backend.make_signature();
1134    sig.params.push(AbiParam::new(types::I64));
1135    sig.params.push(AbiParam::new(types::I64));
1136    sig.params.push(AbiParam::new(types::I64));
1137    sig.params.push(AbiParam::new(types::I64));
1138    sig.returns.push(AbiParam::new(types::I64));
1139    module_backend
1140        .declare_function("yulang_native_variant", Linkage::Import, &sig)
1141        .map_err(cranelift_error)
1142}
1143
1144fn declare_variant_tag_eq<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1145    let mut sig = module_backend.make_signature();
1146    sig.params.push(AbiParam::new(types::I64));
1147    sig.params.push(AbiParam::new(types::I64));
1148    sig.params.push(AbiParam::new(types::I64));
1149    sig.params.push(AbiParam::new(types::I64));
1150    sig.returns.push(AbiParam::new(types::I64));
1151    module_backend
1152        .declare_function("yulang_native_variant_tag_eq", Linkage::Import, &sig)
1153        .map_err(cranelift_error)
1154}
1155
1156fn declare_variant_payload<M: Module>(
1157    module_backend: &mut M,
1158) -> NativeValueCraneliftResult<FuncId> {
1159    let mut sig = module_backend.make_signature();
1160    sig.params.push(AbiParam::new(types::I64));
1161    sig.params.push(AbiParam::new(types::I64));
1162    sig.returns.push(AbiParam::new(types::I64));
1163    module_backend
1164        .declare_function("yulang_native_variant_payload", Linkage::Import, &sig)
1165        .map_err(cranelift_error)
1166}
1167
1168fn declare_concat_string<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1169    let mut sig = module_backend.make_signature();
1170    sig.params.push(AbiParam::new(types::I64));
1171    sig.params.push(AbiParam::new(types::I64));
1172    sig.params.push(AbiParam::new(types::I64));
1173    sig.returns.push(AbiParam::new(types::I64));
1174    module_backend
1175        .declare_function("yulang_native_concat_string", Linkage::Import, &sig)
1176        .map_err(cranelift_error)
1177}
1178
1179fn declare_primitive_unary<M: Module>(
1180    module_backend: &mut M,
1181) -> NativeValueCraneliftResult<FuncId> {
1182    let mut sig = module_backend.make_signature();
1183    sig.params.push(AbiParam::new(types::I64));
1184    sig.params.push(AbiParam::new(types::I64));
1185    sig.params.push(AbiParam::new(types::I64));
1186    sig.returns.push(AbiParam::new(types::I64));
1187    module_backend
1188        .declare_function("yulang_native_primitive_unary", Linkage::Import, &sig)
1189        .map_err(cranelift_error)
1190}
1191
1192fn declare_primitive_binary<M: Module>(
1193    module_backend: &mut M,
1194) -> NativeValueCraneliftResult<FuncId> {
1195    let mut sig = module_backend.make_signature();
1196    sig.params.push(AbiParam::new(types::I64));
1197    sig.params.push(AbiParam::new(types::I64));
1198    sig.params.push(AbiParam::new(types::I64));
1199    sig.params.push(AbiParam::new(types::I64));
1200    sig.returns.push(AbiParam::new(types::I64));
1201    module_backend
1202        .declare_function("yulang_native_primitive_binary", Linkage::Import, &sig)
1203        .map_err(cranelift_error)
1204}
1205
1206fn declare_closure_new<M: Module>(module_backend: &mut M) -> NativeValueCraneliftResult<FuncId> {
1207    let mut sig = module_backend.make_signature();
1208    sig.params.push(AbiParam::new(types::I64));
1209    sig.params.push(AbiParam::new(types::I64));
1210    sig.returns.push(AbiParam::new(types::I64));
1211    module_backend
1212        .declare_function("yulang_native_closure_new", Linkage::Import, &sig)
1213        .map_err(cranelift_error)
1214}
1215
1216fn declare_closure_push_env<M: Module>(
1217    module_backend: &mut M,
1218) -> NativeValueCraneliftResult<FuncId> {
1219    let mut sig = module_backend.make_signature();
1220    sig.params.push(AbiParam::new(types::I64));
1221    sig.params.push(AbiParam::new(types::I64));
1222    sig.returns.push(AbiParam::new(types::I64));
1223    module_backend
1224        .declare_function("yulang_native_closure_push_env", Linkage::Import, &sig)
1225        .map_err(cranelift_error)
1226}
1227
1228fn declare_closure_target_id<M: Module>(
1229    module_backend: &mut M,
1230) -> NativeValueCraneliftResult<FuncId> {
1231    let mut sig = module_backend.make_signature();
1232    sig.params.push(AbiParam::new(types::I64));
1233    sig.returns.push(AbiParam::new(types::I64));
1234    module_backend
1235        .declare_function("yulang_native_closure_target_id", Linkage::Import, &sig)
1236        .map_err(cranelift_error)
1237}
1238
1239fn declare_closure_env_get<M: Module>(
1240    module_backend: &mut M,
1241) -> NativeValueCraneliftResult<FuncId> {
1242    let mut sig = module_backend.make_signature();
1243    sig.params.push(AbiParam::new(types::I64));
1244    sig.params.push(AbiParam::new(types::I64));
1245    sig.returns.push(AbiParam::new(types::I64));
1246    module_backend
1247        .declare_function("yulang_native_closure_env_get", Linkage::Import, &sig)
1248        .map_err(cranelift_error)
1249}
1250
1251fn value_function_signature<M: Module>(
1252    module_backend: &M,
1253    function: &NativeAbiFunction,
1254) -> ir::Signature {
1255    let mut sig = module_backend.make_signature();
1256    sig.params.push(AbiParam::new(types::I64));
1257    sig.params
1258        .extend((0..function.environment_slots).map(|_| AbiParam::new(types::I64)));
1259    sig.params
1260        .extend(function.params.iter().map(|_| AbiParam::new(types::I64)));
1261    sig.returns.push(AbiParam::new(types::I64));
1262    sig
1263}
1264
1265fn lower_value_function<M: Module, L: ValueLiteralStore>(
1266    module_backend: &mut M,
1267    ctx: &mut cranelift_codegen::Context,
1268    function: &NativeAbiFunction,
1269    functions: &ValueFunctions,
1270    helpers: &ValueHelpers,
1271    literals: &mut L,
1272) -> NativeValueCraneliftResult<()> {
1273    let mut builder_context = FunctionBuilderContext::new();
1274    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
1275    let blocks = create_value_blocks(&mut builder, function);
1276    declare_value_variables(&mut builder, function);
1277    let params = bind_value_function_params(&mut builder, function, &blocks)?;
1278    let block_start_values = function_block_start_values(function);
1279
1280    for block in &function.blocks {
1281        let clif_block = value_block_ref(function, &blocks, block.id)?;
1282        builder.switch_to_block(clif_block);
1283        bind_value_block_params(&mut builder, function, block, clif_block)?;
1284        let mut values = block_start_values
1285            .get(&block.id)
1286            .cloned()
1287            .unwrap_or_else(HashMap::new);
1288        for stmt in &block.stmts {
1289            let dest = lower_value_stmt(
1290                module_backend,
1291                &mut builder,
1292                function,
1293                stmt,
1294                functions,
1295                helpers,
1296                params.context,
1297                &params.environment,
1298                &values,
1299                literals,
1300            )?;
1301            values.insert(dest, ());
1302        }
1303        lower_value_terminator(
1304            module_backend,
1305            &mut builder,
1306            function,
1307            helpers,
1308            &blocks,
1309            &values,
1310            &block.terminator,
1311        )?;
1312    }
1313    builder.seal_all_blocks();
1314    builder.finalize();
1315    Ok(())
1316}
1317
1318fn create_value_blocks(
1319    builder: &mut FunctionBuilder<'_>,
1320    function: &NativeAbiFunction,
1321) -> HashMap<BlockId, ir::Block> {
1322    let entry = function.blocks.first().map(|block| block.id);
1323    function
1324        .blocks
1325        .iter()
1326        .map(|block| {
1327            let clif_block = builder.create_block();
1328            let params = if Some(block.id) == entry && block.params.starts_with(&function.params) {
1329                &block.params[function.params.len()..]
1330            } else {
1331                block.params.as_slice()
1332            };
1333            for _ in params {
1334                builder.append_block_param(clif_block, types::I64);
1335            }
1336            (block.id, clif_block)
1337        })
1338        .collect()
1339}
1340
1341fn declare_value_variables(builder: &mut FunctionBuilder<'_>, function: &NativeAbiFunction) {
1342    let mut declared = HashSet::new();
1343    for value in function_value_ids(function) {
1344        if declared.insert(value) {
1345            builder.declare_var(variable(value), types::I64);
1346        }
1347    }
1348}
1349
1350fn bind_value_function_params(
1351    builder: &mut FunctionBuilder<'_>,
1352    function: &NativeAbiFunction,
1353    blocks: &HashMap<BlockId, ir::Block>,
1354) -> NativeValueCraneliftResult<ValueFunctionParams> {
1355    let entry = function
1356        .blocks
1357        .first()
1358        .ok_or_else(|| NativeValueCraneliftError::MissingBlock {
1359            function: function.name.clone(),
1360            block: BlockId(0),
1361        })?;
1362    let entry_block = value_block_ref(function, blocks, entry.id)?;
1363    builder.append_block_params_for_function_params(entry_block);
1364    builder.switch_to_block(entry_block);
1365    let block_params = builder.block_params(entry_block).to_vec();
1366    let context = block_params[0];
1367    let environment = block_params
1368        .iter()
1369        .skip(1)
1370        .take(function.environment_slots)
1371        .copied()
1372        .collect::<Vec<_>>();
1373    for (param, value) in function.params.iter().zip(
1374        block_params
1375            .into_iter()
1376            .skip(1 + function.environment_slots),
1377    ) {
1378        builder.def_var(variable(*param), value);
1379    }
1380    Ok(ValueFunctionParams {
1381        context,
1382        environment,
1383    })
1384}
1385
1386fn bind_value_block_params(
1387    builder: &mut FunctionBuilder<'_>,
1388    function: &NativeAbiFunction,
1389    block: &NativeAbiBlock,
1390    clif_block: ir::Block,
1391) -> NativeValueCraneliftResult<()> {
1392    let clif_params = builder.block_params(clif_block).to_vec();
1393    let offset = if function
1394        .blocks
1395        .first()
1396        .is_some_and(|entry| entry.id == block.id)
1397    {
1398        1 + function.environment_slots + function.params.len()
1399    } else {
1400        0
1401    };
1402    for (param, value) in block
1403        .params
1404        .iter()
1405        .zip(clif_params.into_iter().skip(offset))
1406    {
1407        builder.def_var(variable(*param), value);
1408    }
1409    Ok(())
1410}
1411
1412struct ValueFunctionParams {
1413    context: ir::Value,
1414    environment: Vec<ir::Value>,
1415}
1416
1417fn function_block_start_values(
1418    function: &NativeAbiFunction,
1419) -> HashMap<BlockId, HashMap<ValueId, ()>> {
1420    let mut start = function
1421        .blocks
1422        .iter()
1423        .map(|block| {
1424            (
1425                block.id,
1426                block
1427                    .params
1428                    .iter()
1429                    .copied()
1430                    .map(|param| (param, ()))
1431                    .collect::<HashMap<_, _>>(),
1432            )
1433        })
1434        .collect::<HashMap<_, _>>();
1435    if let Some(entry) = function.blocks.first() {
1436        start
1437            .entry(entry.id)
1438            .or_default()
1439            .extend(function.params.iter().copied().map(|param| (param, ())));
1440    }
1441
1442    let mut changed = true;
1443    while changed {
1444        changed = false;
1445        for block in &function.blocks {
1446            let mut out = start.get(&block.id).cloned().unwrap_or_default();
1447            for stmt in &block.stmts {
1448                out.insert(stmt_dest(stmt), ());
1449            }
1450            for successor in terminator_successors(&block.terminator) {
1451                let entry = start.entry(successor).or_default();
1452                let old_len = entry.len();
1453                entry.extend(out.keys().copied().map(|value| (value, ())));
1454                changed |= entry.len() != old_len;
1455            }
1456        }
1457    }
1458    start
1459}
1460
1461fn stmt_dest(stmt: &NativeAbiStmt) -> ValueId {
1462    match stmt {
1463        NativeAbiStmt::Literal { dest, .. }
1464        | NativeAbiStmt::Primitive { dest, .. }
1465        | NativeAbiStmt::DirectCall { dest, .. }
1466        | NativeAbiStmt::Tuple { dest, .. }
1467        | NativeAbiStmt::Record { dest, .. }
1468        | NativeAbiStmt::RecordWithoutFields { dest, .. }
1469        | NativeAbiStmt::Variant { dest, .. }
1470        | NativeAbiStmt::Select { dest, .. }
1471        | NativeAbiStmt::TupleGet { dest, .. }
1472        | NativeAbiStmt::VariantTagEq { dest, .. }
1473        | NativeAbiStmt::VariantPayload { dest, .. }
1474        | NativeAbiStmt::ValueEq { dest, .. }
1475        | NativeAbiStmt::BoolAnd { dest, .. }
1476        | NativeAbiStmt::LoadEnv { dest, .. }
1477        | NativeAbiStmt::AllocateClosure { dest, .. }
1478        | NativeAbiStmt::IndirectClosureCall { dest, .. } => *dest,
1479    }
1480}
1481
1482fn terminator_successors(terminator: &NativeTerminator) -> Vec<BlockId> {
1483    match terminator {
1484        NativeTerminator::Return(_) => Vec::new(),
1485        NativeTerminator::Jump { target, .. } => vec![*target],
1486        NativeTerminator::Branch {
1487            then_block,
1488            else_block,
1489            ..
1490        } => vec![*then_block, *else_block],
1491    }
1492}
1493
1494fn lower_value_terminator<M: Module>(
1495    module_backend: &mut M,
1496    builder: &mut FunctionBuilder<'_>,
1497    function: &NativeAbiFunction,
1498    helpers: &ValueHelpers,
1499    blocks: &HashMap<BlockId, ir::Block>,
1500    values: &HashMap<ValueId, ()>,
1501    terminator: &NativeTerminator,
1502) -> NativeValueCraneliftResult<()> {
1503    match terminator {
1504        NativeTerminator::Return(value) => {
1505            let value = read_value(builder, function, values, *value)?;
1506            builder.ins().return_(&[value]);
1507        }
1508        NativeTerminator::Jump { target, args } => {
1509            let target = value_block_ref(function, blocks, *target)?;
1510            let args = read_block_args(builder, function, values, args)?;
1511            builder.ins().jump(target, &args);
1512        }
1513        NativeTerminator::Branch {
1514            cond,
1515            then_block,
1516            else_block,
1517        } => {
1518            let cond = read_value(builder, function, values, *cond)?;
1519            let cond = lower_value_bool_condition(module_backend, builder, helpers, cond)?;
1520            let then_block = value_block_ref(function, blocks, *then_block)?;
1521            let else_block = value_block_ref(function, blocks, *else_block)?;
1522            builder.ins().brif(cond, then_block, &[], else_block, &[]);
1523        }
1524    }
1525    Ok(())
1526}
1527
1528fn lower_value_bool_condition<M: Module>(
1529    module_backend: &mut M,
1530    builder: &mut FunctionBuilder<'_>,
1531    helpers: &ValueHelpers,
1532    value: ir::Value,
1533) -> NativeValueCraneliftResult<ir::Value> {
1534    let callee = module_backend.declare_func_in_func(helpers.bool_is_true, builder.func);
1535    let call = builder.ins().call(callee, &[value]);
1536    let value = single_call_result(builder, call, "yulang_native_bool_is_true")?;
1537    Ok(builder
1538        .ins()
1539        .icmp_imm(ir::condcodes::IntCC::NotEqual, value, 0))
1540}
1541
1542fn value_block_ref(
1543    function: &NativeAbiFunction,
1544    blocks: &HashMap<BlockId, ir::Block>,
1545    block: BlockId,
1546) -> NativeValueCraneliftResult<ir::Block> {
1547    blocks
1548        .get(&block)
1549        .copied()
1550        .ok_or(NativeValueCraneliftError::MissingBlock {
1551            function: function.name.clone(),
1552            block,
1553        })
1554}
1555
1556fn lower_value_stmt<M: Module, L: ValueLiteralStore>(
1557    module_backend: &mut M,
1558    builder: &mut FunctionBuilder<'_>,
1559    function: &NativeAbiFunction,
1560    stmt: &NativeAbiStmt,
1561    functions: &ValueFunctions,
1562    helpers: &ValueHelpers,
1563    context: ir::Value,
1564    environment: &[ir::Value],
1565    defined: &HashMap<ValueId, ()>,
1566    literals: &mut L,
1567) -> NativeValueCraneliftResult<ValueId> {
1568    match stmt {
1569        NativeAbiStmt::Literal {
1570            dest,
1571            literal: NativeLiteral::Int(value),
1572        } => {
1573            let (ptr, len) = literals.literal_bytes(module_backend, builder, value.as_bytes())?;
1574            let callee = module_backend.declare_func_in_func(helpers.make_int, builder.func);
1575            let call = builder.ins().call(callee, &[context, ptr, len]);
1576            let results = builder.inst_results(call);
1577            if results.len() != 1 {
1578                return Err(NativeValueCraneliftError::InvalidReturnArity {
1579                    function: "yulang_native_make_int".to_string(),
1580                    arity: results.len(),
1581                });
1582            }
1583            builder.def_var(variable(*dest), results[0]);
1584            Ok(*dest)
1585        }
1586        NativeAbiStmt::Literal {
1587            dest,
1588            literal: NativeLiteral::Float(value),
1589        } => {
1590            let (ptr, len) = literals.literal_bytes(module_backend, builder, value.as_bytes())?;
1591            let callee = module_backend.declare_func_in_func(helpers.make_float, builder.func);
1592            let call = builder.ins().call(callee, &[context, ptr, len]);
1593            let results = builder.inst_results(call);
1594            if results.len() != 1 {
1595                return Err(NativeValueCraneliftError::InvalidReturnArity {
1596                    function: "yulang_native_make_float".to_string(),
1597                    arity: results.len(),
1598                });
1599            }
1600            builder.def_var(variable(*dest), results[0]);
1601            Ok(*dest)
1602        }
1603        NativeAbiStmt::Literal {
1604            dest,
1605            literal: NativeLiteral::Bool(value),
1606        } => {
1607            let raw = builder.ins().iconst(types::I64, i64::from(*value));
1608            let callee = module_backend.declare_func_in_func(helpers.make_bool, builder.func);
1609            let call = builder.ins().call(callee, &[context, raw]);
1610            let results = builder.inst_results(call);
1611            if results.len() != 1 {
1612                return Err(NativeValueCraneliftError::InvalidReturnArity {
1613                    function: "yulang_native_make_bool".to_string(),
1614                    arity: results.len(),
1615                });
1616            }
1617            builder.def_var(variable(*dest), results[0]);
1618            Ok(*dest)
1619        }
1620        NativeAbiStmt::Literal {
1621            dest,
1622            literal: NativeLiteral::Unit,
1623        } => {
1624            let callee = module_backend.declare_func_in_func(helpers.make_unit, builder.func);
1625            let call = builder.ins().call(callee, &[context]);
1626            let results = builder.inst_results(call);
1627            if results.len() != 1 {
1628                return Err(NativeValueCraneliftError::InvalidReturnArity {
1629                    function: "yulang_native_make_unit".to_string(),
1630                    arity: results.len(),
1631                });
1632            }
1633            builder.def_var(variable(*dest), results[0]);
1634            Ok(*dest)
1635        }
1636        NativeAbiStmt::Literal {
1637            dest,
1638            literal: NativeLiteral::String(value),
1639        } => {
1640            let (ptr, len) = literals.literal_bytes(module_backend, builder, value.as_bytes())?;
1641            let callee = module_backend.declare_func_in_func(helpers.make_string, builder.func);
1642            let call = builder.ins().call(callee, &[context, ptr, len]);
1643            let results = builder.inst_results(call);
1644            if results.len() != 1 {
1645                return Err(NativeValueCraneliftError::InvalidReturnArity {
1646                    function: "yulang_native_make_string".to_string(),
1647                    arity: results.len(),
1648                });
1649            }
1650            builder.def_var(variable(*dest), results[0]);
1651            Ok(*dest)
1652        }
1653        NativeAbiStmt::Primitive {
1654            dest,
1655            op: yulang_typed_ir::PrimitiveOp::StringConcat,
1656            args,
1657        } => {
1658            let args = read_values(builder, function, defined, args)?;
1659            let callee = module_backend.declare_func_in_func(helpers.concat_string, builder.func);
1660            let call = builder.ins().call(callee, &[context, args[0], args[1]]);
1661            let results = builder.inst_results(call);
1662            if results.len() != 1 {
1663                return Err(NativeValueCraneliftError::InvalidReturnArity {
1664                    function: "yulang_native_concat_string".to_string(),
1665                    arity: results.len(),
1666                });
1667            }
1668            builder.def_var(variable(*dest), results[0]);
1669            Ok(*dest)
1670        }
1671        NativeAbiStmt::Primitive {
1672            dest,
1673            op: yulang_typed_ir::PrimitiveOp::ListEmpty,
1674            args,
1675        } => {
1676            if args.len() > 1 {
1677                return Err(NativeValueCraneliftError::UnsupportedStmt {
1678                    function: function.name.clone(),
1679                    kind: "list empty arity",
1680                });
1681            }
1682            read_values(builder, function, defined, args)?;
1683            let callee = module_backend.declare_func_in_func(helpers.list_empty, builder.func);
1684            let call = builder.ins().call(callee, &[context]);
1685            let results = builder.inst_results(call);
1686            if results.len() != 1 {
1687                return Err(NativeValueCraneliftError::InvalidReturnArity {
1688                    function: "yulang_native_list_empty".to_string(),
1689                    arity: results.len(),
1690                });
1691            }
1692            builder.def_var(variable(*dest), results[0]);
1693            Ok(*dest)
1694        }
1695        NativeAbiStmt::Primitive {
1696            dest,
1697            op: yulang_typed_ir::PrimitiveOp::ListSingleton,
1698            args,
1699        } => {
1700            let args = read_values(builder, function, defined, args)?;
1701            let callee = module_backend.declare_func_in_func(helpers.list_singleton, builder.func);
1702            let call = builder.ins().call(callee, &[context, args[0]]);
1703            let results = builder.inst_results(call);
1704            if results.len() != 1 {
1705                return Err(NativeValueCraneliftError::InvalidReturnArity {
1706                    function: "yulang_native_list_singleton".to_string(),
1707                    arity: results.len(),
1708                });
1709            }
1710            builder.def_var(variable(*dest), results[0]);
1711            Ok(*dest)
1712        }
1713        NativeAbiStmt::Primitive {
1714            dest,
1715            op: yulang_typed_ir::PrimitiveOp::ListMerge,
1716            args,
1717        } => {
1718            let args = read_values(builder, function, defined, args)?;
1719            let callee = module_backend.declare_func_in_func(helpers.list_merge, builder.func);
1720            let call = builder.ins().call(callee, &[context, args[0], args[1]]);
1721            let results = builder.inst_results(call);
1722            if results.len() != 1 {
1723                return Err(NativeValueCraneliftError::InvalidReturnArity {
1724                    function: "yulang_native_list_merge".to_string(),
1725                    arity: results.len(),
1726                });
1727            }
1728            builder.def_var(variable(*dest), results[0]);
1729            Ok(*dest)
1730        }
1731        NativeAbiStmt::Primitive {
1732            dest,
1733            op: yulang_typed_ir::PrimitiveOp::ListLen,
1734            args,
1735        } => {
1736            let args = read_values(builder, function, defined, args)?;
1737            let callee = module_backend.declare_func_in_func(helpers.list_len, builder.func);
1738            let call = builder.ins().call(callee, &[context, args[0]]);
1739            let results = builder.inst_results(call);
1740            if results.len() != 1 {
1741                return Err(NativeValueCraneliftError::InvalidReturnArity {
1742                    function: "yulang_native_list_len".to_string(),
1743                    arity: results.len(),
1744                });
1745            }
1746            builder.def_var(variable(*dest), results[0]);
1747            Ok(*dest)
1748        }
1749        NativeAbiStmt::Primitive {
1750            dest,
1751            op: yulang_typed_ir::PrimitiveOp::ListIndex,
1752            args,
1753        } => {
1754            let args = read_values(builder, function, defined, args)?;
1755            let callee = module_backend.declare_func_in_func(helpers.list_index, builder.func);
1756            let call = builder.ins().call(callee, &[context, args[0], args[1]]);
1757            let results = builder.inst_results(call);
1758            if results.len() != 1 {
1759                return Err(NativeValueCraneliftError::InvalidReturnArity {
1760                    function: "yulang_native_list_index".to_string(),
1761                    arity: results.len(),
1762                });
1763            }
1764            builder.def_var(variable(*dest), results[0]);
1765            Ok(*dest)
1766        }
1767        NativeAbiStmt::Primitive {
1768            dest,
1769            op: yulang_typed_ir::PrimitiveOp::ListIndexRange,
1770            args,
1771        } => {
1772            let args = read_values(builder, function, defined, args)?;
1773            let callee =
1774                module_backend.declare_func_in_func(helpers.list_index_range, builder.func);
1775            let call = builder.ins().call(callee, &[context, args[0], args[1]]);
1776            let results = builder.inst_results(call);
1777            if results.len() != 1 {
1778                return Err(NativeValueCraneliftError::InvalidReturnArity {
1779                    function: "yulang_native_list_index_range".to_string(),
1780                    arity: results.len(),
1781                });
1782            }
1783            builder.def_var(variable(*dest), results[0]);
1784            Ok(*dest)
1785        }
1786        NativeAbiStmt::Primitive {
1787            dest,
1788            op: yulang_typed_ir::PrimitiveOp::ListIndexRangeRaw,
1789            args,
1790        } => {
1791            let args = read_values(builder, function, defined, args)?;
1792            let callee =
1793                module_backend.declare_func_in_func(helpers.list_index_range_raw, builder.func);
1794            let call = builder
1795                .ins()
1796                .call(callee, &[context, args[0], args[1], args[2]]);
1797            let results = builder.inst_results(call);
1798            if results.len() != 1 {
1799                return Err(NativeValueCraneliftError::InvalidReturnArity {
1800                    function: "yulang_native_list_index_range_raw".to_string(),
1801                    arity: results.len(),
1802                });
1803            }
1804            builder.def_var(variable(*dest), results[0]);
1805            Ok(*dest)
1806        }
1807        NativeAbiStmt::Primitive { dest, op, args }
1808            if value_runtime_helper(*op, helpers).is_some() =>
1809        {
1810            let (helper, helper_name) = value_runtime_helper(*op, helpers).expect("checked");
1811            let args = read_values(builder, function, defined, args)?;
1812            let mut call_args = Vec::with_capacity(args.len() + 1);
1813            call_args.push(context);
1814            call_args.extend(args);
1815            let callee = module_backend.declare_func_in_func(helper, builder.func);
1816            let call = builder.ins().call(callee, &call_args);
1817            let result = single_call_result(builder, call, helper_name)?;
1818            builder.def_var(variable(*dest), result);
1819            Ok(*dest)
1820        }
1821        NativeAbiStmt::Primitive { dest, op, args } if primitive_unary_code(*op).is_some() => {
1822            let [arg] = args.as_slice() else {
1823                return Err(NativeValueCraneliftError::UnsupportedStmt {
1824                    function: function.name.clone(),
1825                    kind: "primitive unary arity",
1826                });
1827            };
1828            let arg = read_values(builder, function, defined, &[*arg])?;
1829            let op_code = builder
1830                .ins()
1831                .iconst(types::I64, primitive_unary_code(*op).expect("checked"));
1832            let callee = module_backend.declare_func_in_func(helpers.primitive_unary, builder.func);
1833            let call = builder.ins().call(callee, &[context, op_code, arg[0]]);
1834            let result = single_call_result(builder, call, "yulang_native_primitive_unary")?;
1835            builder.def_var(variable(*dest), result);
1836            Ok(*dest)
1837        }
1838        NativeAbiStmt::Primitive { dest, op, args } if primitive_binary_code(*op).is_some() => {
1839            let [left, right] = args.as_slice() else {
1840                return Err(NativeValueCraneliftError::UnsupportedStmt {
1841                    function: function.name.clone(),
1842                    kind: "primitive binary arity",
1843                });
1844            };
1845            let args = read_values(builder, function, defined, &[*left, *right])?;
1846            let op_code = builder
1847                .ins()
1848                .iconst(types::I64, primitive_binary_code(*op).expect("checked"));
1849            let callee =
1850                module_backend.declare_func_in_func(helpers.primitive_binary, builder.func);
1851            let call = builder
1852                .ins()
1853                .call(callee, &[context, op_code, args[0], args[1]]);
1854            let result = single_call_result(builder, call, "yulang_native_primitive_binary")?;
1855            builder.def_var(variable(*dest), result);
1856            Ok(*dest)
1857        }
1858        NativeAbiStmt::Primitive { .. } => Err(NativeValueCraneliftError::UnsupportedStmt {
1859            function: function.name.clone(),
1860            kind: "primitive",
1861        }),
1862        NativeAbiStmt::DirectCall { dest, target, args } => {
1863            let id = functions.id(target).ok_or_else(|| {
1864                NativeValueCraneliftError::UnsupportedFunction {
1865                    function: target.clone(),
1866                    reason: "target was not declared",
1867                }
1868            })?;
1869            let callee = module_backend.declare_func_in_func(id, builder.func);
1870            let mut call_args = vec![context];
1871            call_args.extend(read_values(builder, function, defined, args)?);
1872            let call = builder.ins().call(callee, &call_args);
1873            let results = builder.inst_results(call);
1874            if results.len() != 1 {
1875                return Err(NativeValueCraneliftError::InvalidReturnArity {
1876                    function: target.clone(),
1877                    arity: results.len(),
1878                });
1879            }
1880            builder.def_var(variable(*dest), results[0]);
1881            Ok(*dest)
1882        }
1883        NativeAbiStmt::Tuple { dest, items } => {
1884            let callee = module_backend.declare_func_in_func(helpers.tuple_empty, builder.func);
1885            let call = builder.ins().call(callee, &[context]);
1886            let mut tuple = single_call_result(builder, call, "yulang_native_tuple_empty")?;
1887            for item in read_values(builder, function, defined, items)? {
1888                let callee = module_backend.declare_func_in_func(helpers.tuple_push, builder.func);
1889                let call = builder.ins().call(callee, &[context, tuple, item]);
1890                tuple = single_call_result(builder, call, "yulang_native_tuple_push")?;
1891            }
1892            builder.def_var(variable(*dest), tuple);
1893            Ok(*dest)
1894        }
1895        NativeAbiStmt::Record { dest, base, fields } => {
1896            let mut record = if let Some(base) = base {
1897                if !defined.contains_key(base) {
1898                    return Err(NativeValueCraneliftError::MissingValue {
1899                        function: function.name.clone(),
1900                        value: *base,
1901                    });
1902                }
1903                builder.use_var(variable(*base))
1904            } else {
1905                let callee =
1906                    module_backend.declare_func_in_func(helpers.record_empty, builder.func);
1907                let call = builder.ins().call(callee, &[context]);
1908                single_call_result(builder, call, "yulang_native_record_empty")?
1909            };
1910            for field in fields {
1911                if !defined.contains_key(&field.value) {
1912                    return Err(NativeValueCraneliftError::MissingValue {
1913                        function: function.name.clone(),
1914                        value: field.value,
1915                    });
1916                }
1917                let value = builder.use_var(variable(field.value));
1918                let (name_ptr, name_len) =
1919                    literals.literal_bytes(module_backend, builder, field.name.0.as_bytes())?;
1920                let callee =
1921                    module_backend.declare_func_in_func(helpers.record_insert, builder.func);
1922                let call = builder
1923                    .ins()
1924                    .call(callee, &[context, record, name_ptr, name_len, value]);
1925                record = single_call_result(builder, call, "yulang_native_record_insert")?;
1926            }
1927            builder.def_var(variable(*dest), record);
1928            Ok(*dest)
1929        }
1930        NativeAbiStmt::RecordWithoutFields { dest, base, fields } => {
1931            if !defined.contains_key(base) {
1932                return Err(NativeValueCraneliftError::MissingValue {
1933                    function: function.name.clone(),
1934                    value: *base,
1935                });
1936            }
1937            let mut record = builder.use_var(variable(*base));
1938            for field in fields {
1939                let (name_ptr, name_len) =
1940                    literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
1941                let callee =
1942                    module_backend.declare_func_in_func(helpers.record_without_field, builder.func);
1943                let call = builder
1944                    .ins()
1945                    .call(callee, &[context, record, name_ptr, name_len]);
1946                record = single_call_result(builder, call, "yulang_native_record_without_field")?;
1947            }
1948            builder.def_var(variable(*dest), record);
1949            Ok(*dest)
1950        }
1951        NativeAbiStmt::Variant { dest, tag, value } => {
1952            let (tag_ptr, tag_len) =
1953                literals.literal_bytes(module_backend, builder, tag.0.as_bytes())?;
1954            let value = if let Some(value) = value {
1955                if !defined.contains_key(value) {
1956                    return Err(NativeValueCraneliftError::MissingValue {
1957                        function: function.name.clone(),
1958                        value: *value,
1959                    });
1960                }
1961                builder.use_var(variable(*value))
1962            } else {
1963                builder.ins().iconst(types::I64, 0)
1964            };
1965            let callee = module_backend.declare_func_in_func(helpers.variant, builder.func);
1966            let call = builder
1967                .ins()
1968                .call(callee, &[context, tag_ptr, tag_len, value]);
1969            let result = single_call_result(builder, call, "yulang_native_variant")?;
1970            builder.def_var(variable(*dest), result);
1971            Ok(*dest)
1972        }
1973        NativeAbiStmt::Select { dest, base, field } => {
1974            if !defined.contains_key(base) {
1975                return Err(NativeValueCraneliftError::MissingValue {
1976                    function: function.name.clone(),
1977                    value: *base,
1978                });
1979            }
1980            let base = builder.use_var(variable(*base));
1981            let (field_ptr, field_len) =
1982                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
1983            let callee = module_backend.declare_func_in_func(helpers.record_select, builder.func);
1984            let call = builder
1985                .ins()
1986                .call(callee, &[context, base, field_ptr, field_len]);
1987            let result = single_call_result(builder, call, "yulang_native_record_select")?;
1988            builder.def_var(variable(*dest), result);
1989            Ok(*dest)
1990        }
1991        NativeAbiStmt::TupleGet { dest, tuple, index } => {
1992            if !defined.contains_key(tuple) {
1993                return Err(NativeValueCraneliftError::MissingValue {
1994                    function: function.name.clone(),
1995                    value: *tuple,
1996                });
1997            }
1998            let tuple = builder.use_var(variable(*tuple));
1999            let index = builder.ins().iconst(types::I64, *index as i64);
2000            let callee = module_backend.declare_func_in_func(helpers.tuple_get, builder.func);
2001            let call = builder.ins().call(callee, &[context, tuple, index]);
2002            let result = single_call_result(builder, call, "yulang_native_tuple_get")?;
2003            builder.def_var(variable(*dest), result);
2004            Ok(*dest)
2005        }
2006        NativeAbiStmt::VariantTagEq { dest, variant, tag } => {
2007            if !defined.contains_key(variant) {
2008                return Err(NativeValueCraneliftError::MissingValue {
2009                    function: function.name.clone(),
2010                    value: *variant,
2011                });
2012            }
2013            let variant = builder.use_var(variable(*variant));
2014            let (tag_ptr, tag_len) =
2015                literals.literal_bytes(module_backend, builder, tag.0.as_bytes())?;
2016            let callee = module_backend.declare_func_in_func(helpers.variant_tag_eq, builder.func);
2017            let call = builder
2018                .ins()
2019                .call(callee, &[context, variant, tag_ptr, tag_len]);
2020            let result = single_call_result(builder, call, "yulang_native_variant_tag_eq")?;
2021            builder.def_var(variable(*dest), result);
2022            Ok(*dest)
2023        }
2024        NativeAbiStmt::VariantPayload { dest, variant } => {
2025            if !defined.contains_key(variant) {
2026                return Err(NativeValueCraneliftError::MissingValue {
2027                    function: function.name.clone(),
2028                    value: *variant,
2029                });
2030            }
2031            let variant = builder.use_var(variable(*variant));
2032            let callee = module_backend.declare_func_in_func(helpers.variant_payload, builder.func);
2033            let call = builder.ins().call(callee, &[context, variant]);
2034            let result = single_call_result(builder, call, "yulang_native_variant_payload")?;
2035            builder.def_var(variable(*dest), result);
2036            Ok(*dest)
2037        }
2038        NativeAbiStmt::ValueEq { dest, left, right } => {
2039            if !defined.contains_key(left) {
2040                return Err(NativeValueCraneliftError::MissingValue {
2041                    function: function.name.clone(),
2042                    value: *left,
2043                });
2044            }
2045            if !defined.contains_key(right) {
2046                return Err(NativeValueCraneliftError::MissingValue {
2047                    function: function.name.clone(),
2048                    value: *right,
2049                });
2050            }
2051            let left = builder.use_var(variable(*left));
2052            let right = builder.use_var(variable(*right));
2053            let callee = module_backend.declare_func_in_func(helpers.value_eq, builder.func);
2054            let call = builder.ins().call(callee, &[context, left, right]);
2055            let result = single_call_result(builder, call, "yulang_native_value_eq")?;
2056            builder.def_var(variable(*dest), result);
2057            Ok(*dest)
2058        }
2059        NativeAbiStmt::BoolAnd { dest, left, right } => {
2060            if !defined.contains_key(left) {
2061                return Err(NativeValueCraneliftError::MissingValue {
2062                    function: function.name.clone(),
2063                    value: *left,
2064                });
2065            }
2066            if !defined.contains_key(right) {
2067                return Err(NativeValueCraneliftError::MissingValue {
2068                    function: function.name.clone(),
2069                    value: *right,
2070                });
2071            }
2072            let left = builder.use_var(variable(*left));
2073            let right = builder.use_var(variable(*right));
2074            let callee = module_backend.declare_func_in_func(helpers.bool_and, builder.func);
2075            let call = builder.ins().call(callee, &[context, left, right]);
2076            let result = single_call_result(builder, call, "yulang_native_bool_and")?;
2077            builder.def_var(variable(*dest), result);
2078            Ok(*dest)
2079        }
2080        NativeAbiStmt::LoadEnv { dest, slot } => {
2081            let value = environment.get(*slot).copied().ok_or_else(|| {
2082                NativeValueCraneliftError::UnsupportedFunction {
2083                    function: function.name.clone(),
2084                    reason: "environment slot out of range",
2085                }
2086            })?;
2087            builder.def_var(variable(*dest), value);
2088            Ok(*dest)
2089        }
2090        NativeAbiStmt::AllocateClosure {
2091            dest,
2092            target,
2093            environment,
2094        } => {
2095            let target_id = functions.target_id(target).ok_or_else(|| {
2096                NativeValueCraneliftError::UnsupportedFunction {
2097                    function: target.clone(),
2098                    reason: "closure target was not declared",
2099                }
2100            })?;
2101            let target_id = builder.ins().iconst(types::I64, target_id);
2102            let callee = module_backend.declare_func_in_func(helpers.closure_new, builder.func);
2103            let call = builder.ins().call(callee, &[context, target_id]);
2104            let mut closure = single_call_result(builder, call, "yulang_native_closure_new")?;
2105            for value in read_values(builder, function, defined, environment)? {
2106                let callee =
2107                    module_backend.declare_func_in_func(helpers.closure_push_env, builder.func);
2108                let call = builder.ins().call(callee, &[closure, value]);
2109                closure = single_call_result(builder, call, "yulang_native_closure_push_env")?;
2110            }
2111            builder.def_var(variable(*dest), closure);
2112            Ok(*dest)
2113        }
2114        NativeAbiStmt::IndirectClosureCall { dest, callee, args } => {
2115            let closure = read_value(builder, function, defined, *callee)?;
2116            let result = lower_indirect_closure_call(
2117                module_backend,
2118                builder,
2119                function,
2120                functions,
2121                helpers,
2122                context,
2123                closure,
2124                args,
2125                defined,
2126            )?;
2127            builder.def_var(variable(*dest), result);
2128            Ok(*dest)
2129        }
2130    }
2131}
2132
2133fn lower_indirect_closure_call<M: Module>(
2134    module_backend: &mut M,
2135    builder: &mut FunctionBuilder<'_>,
2136    function: &NativeAbiFunction,
2137    functions: &ValueFunctions,
2138    helpers: &ValueHelpers,
2139    context: ir::Value,
2140    closure: ir::Value,
2141    args: &[ValueId],
2142    defined: &HashMap<ValueId, ()>,
2143) -> NativeValueCraneliftResult<ir::Value> {
2144    let target_helper =
2145        module_backend.declare_func_in_func(helpers.closure_target_id, builder.func);
2146    let target_call = builder.ins().call(target_helper, &[closure]);
2147    let target_id = single_call_result(builder, target_call, "yulang_native_closure_target_id")?;
2148    let args = read_values(builder, function, defined, args)?;
2149    let candidates = functions
2150        .call_candidates(args.len())
2151        .cloned()
2152        .collect::<Vec<_>>();
2153    if candidates.is_empty() {
2154        return Err(NativeValueCraneliftError::UnsupportedStmt {
2155            function: function.name.clone(),
2156            kind: "indirect closure call arity",
2157        });
2158    }
2159
2160    let miss_block = builder.create_block();
2161    let merge_block = builder.create_block();
2162    builder.append_block_param(merge_block, types::I64);
2163
2164    for (index, target) in candidates.iter().enumerate() {
2165        let call_block = builder.create_block();
2166        let next_block = if index + 1 == candidates.len() {
2167            miss_block
2168        } else {
2169            builder.create_block()
2170        };
2171        let matches =
2172            builder
2173                .ins()
2174                .icmp_imm(ir::condcodes::IntCC::Equal, target_id, target.target_id);
2175        builder
2176            .ins()
2177            .brif(matches, call_block, &[], next_block, &[]);
2178
2179        builder.switch_to_block(call_block);
2180        let callee = module_backend.declare_func_in_func(target.id, builder.func);
2181        let mut call_args = Vec::with_capacity(1 + target.environment_slots + args.len());
2182        call_args.push(context);
2183        for slot in 0..target.environment_slots {
2184            let slot = builder.ins().iconst(types::I64, slot as i64);
2185            let helper = module_backend.declare_func_in_func(helpers.closure_env_get, builder.func);
2186            let call = builder.ins().call(helper, &[closure, slot]);
2187            call_args.push(single_call_result(
2188                builder,
2189                call,
2190                "yulang_native_closure_env_get",
2191            )?);
2192        }
2193        call_args.extend(args.iter().copied());
2194        let call = builder.ins().call(callee, &call_args);
2195        let result = single_call_result(builder, call, &target.name)?;
2196        builder
2197            .ins()
2198            .jump(merge_block, &[ir::BlockArg::Value(result)]);
2199
2200        builder.switch_to_block(next_block);
2201    }
2202
2203    builder.switch_to_block(miss_block);
2204    let null = builder.ins().iconst(types::I64, 0);
2205    builder
2206        .ins()
2207        .jump(merge_block, &[ir::BlockArg::Value(null)]);
2208
2209    builder.switch_to_block(merge_block);
2210    Ok(builder.block_params(merge_block)[0])
2211}
2212
2213fn single_call_result(
2214    builder: &FunctionBuilder<'_>,
2215    call: cranelift_codegen::ir::Inst,
2216    function: &str,
2217) -> NativeValueCraneliftResult<ir::Value> {
2218    let results = builder.inst_results(call);
2219    if results.len() != 1 {
2220        return Err(NativeValueCraneliftError::InvalidReturnArity {
2221            function: function.to_string(),
2222            arity: results.len(),
2223        });
2224    }
2225    Ok(results[0])
2226}
2227
2228fn read_values(
2229    builder: &mut FunctionBuilder<'_>,
2230    function: &NativeAbiFunction,
2231    defined: &HashMap<ValueId, ()>,
2232    values: &[ValueId],
2233) -> NativeValueCraneliftResult<Vec<ir::Value>> {
2234    values
2235        .iter()
2236        .map(|value| read_value(builder, function, defined, *value))
2237        .collect()
2238}
2239
2240fn read_value(
2241    builder: &mut FunctionBuilder<'_>,
2242    function: &NativeAbiFunction,
2243    defined: &HashMap<ValueId, ()>,
2244    value: ValueId,
2245) -> NativeValueCraneliftResult<ir::Value> {
2246    if !defined.contains_key(&value) {
2247        return Err(NativeValueCraneliftError::MissingValue {
2248            function: function.name.clone(),
2249            value,
2250        });
2251    }
2252    Ok(builder.use_var(variable(value)))
2253}
2254
2255fn read_block_args(
2256    builder: &mut FunctionBuilder<'_>,
2257    function: &NativeAbiFunction,
2258    defined: &HashMap<ValueId, ()>,
2259    values: &[ValueId],
2260) -> NativeValueCraneliftResult<Vec<ir::BlockArg>> {
2261    Ok(read_values(builder, function, defined, values)?
2262        .into_iter()
2263        .map(ir::BlockArg::Value)
2264        .collect())
2265}
2266
2267fn function_value_ids(function: &NativeAbiFunction) -> Vec<ValueId> {
2268    let mut values = Vec::new();
2269    values.extend(function.params.iter().copied());
2270    for block in &function.blocks {
2271        values.extend(block.params.iter().copied());
2272        for stmt in &block.stmts {
2273            match stmt {
2274                NativeAbiStmt::Literal { dest, .. }
2275                | NativeAbiStmt::Primitive { dest, .. }
2276                | NativeAbiStmt::DirectCall { dest, .. }
2277                | NativeAbiStmt::Tuple { dest, .. }
2278                | NativeAbiStmt::Record { dest, .. }
2279                | NativeAbiStmt::RecordWithoutFields { dest, .. }
2280                | NativeAbiStmt::Variant { dest, .. }
2281                | NativeAbiStmt::Select { dest, .. }
2282                | NativeAbiStmt::TupleGet { dest, .. }
2283                | NativeAbiStmt::VariantTagEq { dest, .. }
2284                | NativeAbiStmt::VariantPayload { dest, .. }
2285                | NativeAbiStmt::ValueEq { dest, .. }
2286                | NativeAbiStmt::BoolAnd { dest, .. }
2287                | NativeAbiStmt::LoadEnv { dest, .. }
2288                | NativeAbiStmt::AllocateClosure { dest, .. }
2289                | NativeAbiStmt::IndirectClosureCall { dest, .. } => values.push(*dest),
2290            }
2291        }
2292    }
2293    values
2294}
2295
2296fn variable(value: ValueId) -> Variable {
2297    Variable::from_u32(value.0 as u32)
2298}
2299
2300fn value_runtime_helper(
2301    op: yulang_typed_ir::PrimitiveOp,
2302    helpers: &ValueHelpers,
2303) -> Option<(FuncId, &'static str)> {
2304    match op {
2305        yulang_typed_ir::PrimitiveOp::ListSplice => {
2306            Some((helpers.list_splice, "yulang_native_list_splice"))
2307        }
2308        yulang_typed_ir::PrimitiveOp::ListSpliceRaw => {
2309            Some((helpers.list_splice_raw, "yulang_native_list_splice_raw"))
2310        }
2311        yulang_typed_ir::PrimitiveOp::ListViewRaw => {
2312            Some((helpers.list_view_raw, "yulang_native_list_view_raw"))
2313        }
2314        yulang_typed_ir::PrimitiveOp::StringIndexRange => Some((
2315            helpers.string_index_range,
2316            "yulang_native_string_index_range",
2317        )),
2318        yulang_typed_ir::PrimitiveOp::StringSplice => {
2319            Some((helpers.string_splice, "yulang_native_string_splice"))
2320        }
2321        yulang_typed_ir::PrimitiveOp::StringIndexRangeRaw => Some((
2322            helpers.string_index_range_raw,
2323            "yulang_native_string_index_range_raw",
2324        )),
2325        yulang_typed_ir::PrimitiveOp::StringSpliceRaw => {
2326            Some((helpers.string_splice_raw, "yulang_native_string_splice_raw"))
2327        }
2328        _ => None,
2329    }
2330}
2331
2332fn primitive_unary_code(op: yulang_typed_ir::PrimitiveOp) -> Option<i64> {
2333    match op {
2334        yulang_typed_ir::PrimitiveOp::BoolNot => Some(NATIVE_PRIMITIVE_BOOL_NOT),
2335        yulang_typed_ir::PrimitiveOp::IntToString => Some(NATIVE_PRIMITIVE_INT_TO_STRING),
2336        yulang_typed_ir::PrimitiveOp::IntToHex => Some(NATIVE_PRIMITIVE_INT_TO_HEX),
2337        yulang_typed_ir::PrimitiveOp::IntToUpperHex => Some(NATIVE_PRIMITIVE_INT_TO_UPPER_HEX),
2338        yulang_typed_ir::PrimitiveOp::FloatToString => Some(NATIVE_PRIMITIVE_FLOAT_TO_STRING),
2339        yulang_typed_ir::PrimitiveOp::BoolToString => Some(NATIVE_PRIMITIVE_BOOL_TO_STRING),
2340        yulang_typed_ir::PrimitiveOp::StringLen => Some(NATIVE_PRIMITIVE_STRING_LEN),
2341        _ => None,
2342    }
2343}
2344
2345fn primitive_binary_code(op: yulang_typed_ir::PrimitiveOp) -> Option<i64> {
2346    match op {
2347        yulang_typed_ir::PrimitiveOp::BoolEq => Some(NATIVE_PRIMITIVE_BOOL_EQ),
2348        yulang_typed_ir::PrimitiveOp::IntAdd => Some(NATIVE_PRIMITIVE_INT_ADD),
2349        yulang_typed_ir::PrimitiveOp::IntSub => Some(NATIVE_PRIMITIVE_INT_SUB),
2350        yulang_typed_ir::PrimitiveOp::IntMul => Some(NATIVE_PRIMITIVE_INT_MUL),
2351        yulang_typed_ir::PrimitiveOp::IntDiv => Some(NATIVE_PRIMITIVE_INT_DIV),
2352        yulang_typed_ir::PrimitiveOp::IntEq => Some(NATIVE_PRIMITIVE_INT_EQ),
2353        yulang_typed_ir::PrimitiveOp::IntLt => Some(NATIVE_PRIMITIVE_INT_LT),
2354        yulang_typed_ir::PrimitiveOp::IntLe => Some(NATIVE_PRIMITIVE_INT_LE),
2355        yulang_typed_ir::PrimitiveOp::IntGt => Some(NATIVE_PRIMITIVE_INT_GT),
2356        yulang_typed_ir::PrimitiveOp::IntGe => Some(NATIVE_PRIMITIVE_INT_GE),
2357        yulang_typed_ir::PrimitiveOp::FloatAdd => Some(NATIVE_PRIMITIVE_FLOAT_ADD),
2358        yulang_typed_ir::PrimitiveOp::FloatSub => Some(NATIVE_PRIMITIVE_FLOAT_SUB),
2359        yulang_typed_ir::PrimitiveOp::FloatMul => Some(NATIVE_PRIMITIVE_FLOAT_MUL),
2360        yulang_typed_ir::PrimitiveOp::FloatDiv => Some(NATIVE_PRIMITIVE_FLOAT_DIV),
2361        yulang_typed_ir::PrimitiveOp::FloatEq => Some(NATIVE_PRIMITIVE_FLOAT_EQ),
2362        yulang_typed_ir::PrimitiveOp::FloatLt => Some(NATIVE_PRIMITIVE_FLOAT_LT),
2363        yulang_typed_ir::PrimitiveOp::FloatLe => Some(NATIVE_PRIMITIVE_FLOAT_LE),
2364        yulang_typed_ir::PrimitiveOp::FloatGt => Some(NATIVE_PRIMITIVE_FLOAT_GT),
2365        yulang_typed_ir::PrimitiveOp::FloatGe => Some(NATIVE_PRIMITIVE_FLOAT_GE),
2366        yulang_typed_ir::PrimitiveOp::StringIndex => Some(NATIVE_PRIMITIVE_STRING_INDEX),
2367        yulang_typed_ir::PrimitiveOp::StringEq => Some(NATIVE_PRIMITIVE_STRING_EQ),
2368        _ => None,
2369    }
2370}
2371
2372trait ValueLiteralStore {
2373    fn literal_bytes<M: Module>(
2374        &mut self,
2375        module_backend: &mut M,
2376        builder: &mut FunctionBuilder<'_>,
2377        bytes: &[u8],
2378    ) -> NativeValueCraneliftResult<(ir::Value, ir::Value)>;
2379}
2380
2381struct HostLiteralStore<'a> {
2382    strings: &'a mut Vec<Box<str>>,
2383}
2384
2385impl ValueLiteralStore for HostLiteralStore<'_> {
2386    fn literal_bytes<M: Module>(
2387        &mut self,
2388        _module_backend: &mut M,
2389        builder: &mut FunctionBuilder<'_>,
2390        bytes: &[u8],
2391    ) -> NativeValueCraneliftResult<(ir::Value, ir::Value)> {
2392        let text = unsafe { std::str::from_utf8_unchecked(bytes) }
2393            .to_string()
2394            .into_boxed_str();
2395        let ptr = text.as_ptr() as i64;
2396        let len = text.len() as i64;
2397        self.strings.push(text);
2398        Ok((
2399            builder.ins().iconst(types::I64, ptr),
2400            builder.ins().iconst(types::I64, len),
2401        ))
2402    }
2403}
2404
2405#[derive(Default)]
2406struct ObjectLiteralStore {
2407    next_id: usize,
2408}
2409
2410impl ValueLiteralStore for ObjectLiteralStore {
2411    fn literal_bytes<M: Module>(
2412        &mut self,
2413        module_backend: &mut M,
2414        builder: &mut FunctionBuilder<'_>,
2415        bytes: &[u8],
2416    ) -> NativeValueCraneliftResult<(ir::Value, ir::Value)> {
2417        let name = format!("__yulang_lit_{}", self.next_id);
2418        self.next_id += 1;
2419        let data_id = module_backend
2420            .declare_data(&name, Linkage::Local, false, false)
2421            .map_err(cranelift_error)?;
2422        let mut data = DataDescription::new();
2423        data.define(bytes.to_vec().into_boxed_slice());
2424        module_backend
2425            .define_data(data_id, &data)
2426            .map_err(cranelift_error)?;
2427        let global = module_backend.declare_data_in_func(data_id, builder.func);
2428        Ok((
2429            builder.ins().symbol_value(types::I64, global),
2430            builder.ins().iconst(types::I64, bytes.len() as i64),
2431        ))
2432    }
2433}
2434
2435fn cranelift_error(error: impl fmt::Display) -> NativeValueCraneliftError {
2436    NativeValueCraneliftError::Cranelift(error.to_string())
2437}
2438
2439#[cfg(test)]
2440mod tests {
2441    use crate::abi::{NativeAbiBlock, NativeAbiFunction, NativeAbiModule, NativeAbiStmt};
2442
2443    use super::*;
2444
2445    #[test]
2446    fn jit_runs_string_literal_root() {
2447        let mut module = compile_value_abi_module(&NativeAbiModule {
2448            functions: Vec::new(),
2449            roots: vec![NativeAbiFunction {
2450                name: "root".to_string(),
2451                params: Vec::new(),
2452                environment_slots: 0,
2453                blocks: vec![NativeAbiBlock {
2454                    id: BlockId(0),
2455                    params: Vec::new(),
2456                    stmts: vec![NativeAbiStmt::Literal {
2457                        dest: ValueId(0),
2458                        literal: NativeLiteral::String("hello".to_string()),
2459                    }],
2460                    terminator: NativeTerminator::Return(ValueId(0)),
2461                }],
2462            }],
2463        })
2464        .expect("compiled");
2465
2466        assert_eq!(
2467            module.run_roots().expect("ran"),
2468            vec![runtime::VmValue::String(
2469                runtime::runtime::string_tree::StringTree::from_str("hello")
2470            )]
2471        );
2472    }
2473
2474    #[test]
2475    fn jit_runs_string_concat_direct_call() {
2476        let mut module = compile_value_abi_module(&NativeAbiModule {
2477            functions: vec![NativeAbiFunction {
2478                name: "concat".to_string(),
2479                params: vec![ValueId(0), ValueId(1)],
2480                environment_slots: 0,
2481                blocks: vec![NativeAbiBlock {
2482                    id: BlockId(0),
2483                    params: Vec::new(),
2484                    stmts: vec![NativeAbiStmt::Primitive {
2485                        dest: ValueId(2),
2486                        op: yulang_typed_ir::PrimitiveOp::StringConcat,
2487                        args: vec![ValueId(0), ValueId(1)],
2488                    }],
2489                    terminator: NativeTerminator::Return(ValueId(2)),
2490                }],
2491            }],
2492            roots: vec![NativeAbiFunction {
2493                name: "root".to_string(),
2494                params: Vec::new(),
2495                environment_slots: 0,
2496                blocks: vec![NativeAbiBlock {
2497                    id: BlockId(0),
2498                    params: Vec::new(),
2499                    stmts: vec![
2500                        NativeAbiStmt::Literal {
2501                            dest: ValueId(0),
2502                            literal: NativeLiteral::String("hello, ".to_string()),
2503                        },
2504                        NativeAbiStmt::Literal {
2505                            dest: ValueId(1),
2506                            literal: NativeLiteral::String("world".to_string()),
2507                        },
2508                        NativeAbiStmt::DirectCall {
2509                            dest: ValueId(2),
2510                            target: "concat".to_string(),
2511                            args: vec![ValueId(0), ValueId(1)],
2512                        },
2513                    ],
2514                    terminator: NativeTerminator::Return(ValueId(2)),
2515                }],
2516            }],
2517        })
2518        .expect("compiled");
2519
2520        assert_eq!(
2521            module.run_roots().expect("ran"),
2522            vec![runtime::VmValue::String(
2523                runtime::runtime::string_tree::StringTree::from_str("hello, world")
2524            )]
2525        );
2526    }
2527
2528    #[test]
2529    fn jit_runs_closure_call_with_environment() {
2530        let mut module = compile_value_abi_module(&NativeAbiModule {
2531            functions: vec![add_capture_function()],
2532            roots: vec![NativeAbiFunction {
2533                name: "root".to_string(),
2534                params: Vec::new(),
2535                environment_slots: 0,
2536                blocks: vec![NativeAbiBlock {
2537                    id: BlockId(0),
2538                    params: Vec::new(),
2539                    stmts: vec![
2540                        NativeAbiStmt::Literal {
2541                            dest: ValueId(0),
2542                            literal: NativeLiteral::Int("10".to_string()),
2543                        },
2544                        NativeAbiStmt::Literal {
2545                            dest: ValueId(1),
2546                            literal: NativeLiteral::Int("32".to_string()),
2547                        },
2548                        NativeAbiStmt::AllocateClosure {
2549                            dest: ValueId(2),
2550                            target: "add_capture".to_string(),
2551                            environment: vec![ValueId(0)],
2552                        },
2553                        NativeAbiStmt::IndirectClosureCall {
2554                            dest: ValueId(3),
2555                            callee: ValueId(2),
2556                            args: vec![ValueId(1)],
2557                        },
2558                    ],
2559                    terminator: NativeTerminator::Return(ValueId(3)),
2560                }],
2561            }],
2562        })
2563        .expect("compiled");
2564
2565        assert_eq!(
2566            module.run_roots().expect("ran"),
2567            vec![runtime::VmValue::Int("42".to_string())]
2568        );
2569    }
2570
2571    #[test]
2572    fn jit_passes_closure_values_through_block_params() {
2573        let mut module = compile_value_abi_module(&NativeAbiModule {
2574            functions: vec![add_capture_function()],
2575            roots: vec![NativeAbiFunction {
2576                name: "root".to_string(),
2577                params: Vec::new(),
2578                environment_slots: 0,
2579                blocks: vec![
2580                    NativeAbiBlock {
2581                        id: BlockId(0),
2582                        params: Vec::new(),
2583                        stmts: vec![
2584                            NativeAbiStmt::Literal {
2585                                dest: ValueId(0),
2586                                literal: NativeLiteral::Int("10".to_string()),
2587                            },
2588                            NativeAbiStmt::AllocateClosure {
2589                                dest: ValueId(1),
2590                                target: "add_capture".to_string(),
2591                                environment: vec![ValueId(0)],
2592                            },
2593                        ],
2594                        terminator: NativeTerminator::Jump {
2595                            target: BlockId(1),
2596                            args: vec![ValueId(1)],
2597                        },
2598                    },
2599                    NativeAbiBlock {
2600                        id: BlockId(1),
2601                        params: vec![ValueId(2)],
2602                        stmts: vec![
2603                            NativeAbiStmt::Literal {
2604                                dest: ValueId(3),
2605                                literal: NativeLiteral::Int("32".to_string()),
2606                            },
2607                            NativeAbiStmt::IndirectClosureCall {
2608                                dest: ValueId(4),
2609                                callee: ValueId(2),
2610                                args: vec![ValueId(3)],
2611                            },
2612                        ],
2613                        terminator: NativeTerminator::Return(ValueId(4)),
2614                    },
2615                ],
2616            }],
2617        })
2618        .expect("compiled");
2619
2620        assert_eq!(
2621            module.run_roots().expect("ran"),
2622            vec![runtime::VmValue::Int("42".to_string())]
2623        );
2624    }
2625
2626    #[test]
2627    fn jit_rejects_closure_root_value_without_dereferencing_it_as_vm_value() {
2628        let error = match compile_value_abi_module(&NativeAbiModule {
2629            functions: vec![add_capture_function()],
2630            roots: vec![NativeAbiFunction {
2631                name: "root".to_string(),
2632                params: Vec::new(),
2633                environment_slots: 0,
2634                blocks: vec![NativeAbiBlock {
2635                    id: BlockId(0),
2636                    params: Vec::new(),
2637                    stmts: vec![
2638                        NativeAbiStmt::Literal {
2639                            dest: ValueId(0),
2640                            literal: NativeLiteral::Int("10".to_string()),
2641                        },
2642                        NativeAbiStmt::AllocateClosure {
2643                            dest: ValueId(1),
2644                            target: "add_capture".to_string(),
2645                            environment: vec![ValueId(0)],
2646                        },
2647                    ],
2648                    terminator: NativeTerminator::Return(ValueId(1)),
2649                }],
2650            }],
2651        }) {
2652            Ok(_) => panic!("closure roots stay unsupported"),
2653            Err(error) => error,
2654        };
2655        assert!(
2656            error.to_string().contains("closure root value statements"),
2657            "{error}"
2658        );
2659    }
2660
2661    #[test]
2662    fn jit_rejects_closure_values_inside_structural_vm_values() {
2663        let error = match compile_value_abi_module(&NativeAbiModule {
2664            functions: vec![add_capture_function()],
2665            roots: vec![NativeAbiFunction {
2666                name: "root".to_string(),
2667                params: Vec::new(),
2668                environment_slots: 0,
2669                blocks: vec![
2670                    NativeAbiBlock {
2671                        id: BlockId(0),
2672                        params: Vec::new(),
2673                        stmts: vec![
2674                            NativeAbiStmt::Literal {
2675                                dest: ValueId(0),
2676                                literal: NativeLiteral::Int("10".to_string()),
2677                            },
2678                            NativeAbiStmt::AllocateClosure {
2679                                dest: ValueId(1),
2680                                target: "add_capture".to_string(),
2681                                environment: vec![ValueId(0)],
2682                            },
2683                        ],
2684                        terminator: NativeTerminator::Jump {
2685                            target: BlockId(1),
2686                            args: vec![ValueId(1)],
2687                        },
2688                    },
2689                    NativeAbiBlock {
2690                        id: BlockId(1),
2691                        params: vec![ValueId(2)],
2692                        stmts: vec![NativeAbiStmt::Tuple {
2693                            dest: ValueId(3),
2694                            items: vec![ValueId(2)],
2695                        }],
2696                        terminator: NativeTerminator::Return(ValueId(3)),
2697                    },
2698                ],
2699            }],
2700        }) {
2701            Ok(_) => panic!("closure tuple items stay unsupported"),
2702            Err(error) => error,
2703        };
2704        assert!(error.to_string().contains("closure tuple item"), "{error}");
2705    }
2706
2707    #[test]
2708    fn jit_runs_list_literal_root() {
2709        let mut module = compile_value_abi_module(&NativeAbiModule {
2710            functions: Vec::new(),
2711            roots: vec![NativeAbiFunction {
2712                name: "root".to_string(),
2713                params: Vec::new(),
2714                environment_slots: 0,
2715                blocks: vec![NativeAbiBlock {
2716                    id: BlockId(0),
2717                    params: Vec::new(),
2718                    stmts: vec![
2719                        NativeAbiStmt::Literal {
2720                            dest: ValueId(0),
2721                            literal: NativeLiteral::Int("1".to_string()),
2722                        },
2723                        NativeAbiStmt::Primitive {
2724                            dest: ValueId(1),
2725                            op: yulang_typed_ir::PrimitiveOp::ListSingleton,
2726                            args: vec![ValueId(0)],
2727                        },
2728                        NativeAbiStmt::Literal {
2729                            dest: ValueId(2),
2730                            literal: NativeLiteral::Int("2".to_string()),
2731                        },
2732                        NativeAbiStmt::Primitive {
2733                            dest: ValueId(3),
2734                            op: yulang_typed_ir::PrimitiveOp::ListSingleton,
2735                            args: vec![ValueId(2)],
2736                        },
2737                        NativeAbiStmt::Primitive {
2738                            dest: ValueId(4),
2739                            op: yulang_typed_ir::PrimitiveOp::ListMerge,
2740                            args: vec![ValueId(1), ValueId(3)],
2741                        },
2742                    ],
2743                    terminator: NativeTerminator::Return(ValueId(4)),
2744                }],
2745            }],
2746        })
2747        .expect("compiled");
2748
2749        let values = module.run_roots().expect("ran");
2750        let [runtime::VmValue::List(list)] = values.as_slice() else {
2751            panic!("expected one list value");
2752        };
2753        assert_eq!(
2754            list.to_vec()
2755                .into_iter()
2756                .map(|value| value.as_ref().clone())
2757                .collect::<Vec<_>>(),
2758            vec![
2759                runtime::VmValue::Int("1".to_string()),
2760                runtime::VmValue::Int("2".to_string())
2761            ]
2762        );
2763    }
2764
2765    #[test]
2766    fn jit_runs_scalar_literal_roots() {
2767        let mut module = compile_value_abi_module(&NativeAbiModule {
2768            functions: Vec::new(),
2769            roots: vec![
2770                literal_root("bool_root", NativeLiteral::Bool(true)),
2771                literal_root("unit_root", NativeLiteral::Unit),
2772                literal_root("float_root", NativeLiteral::Float("1.5".to_string())),
2773            ],
2774        })
2775        .expect("compiled");
2776
2777        assert_eq!(
2778            module.run_roots().expect("ran"),
2779            vec![
2780                runtime::VmValue::Bool(true),
2781                runtime::VmValue::Unit,
2782                runtime::VmValue::Float("1.5".to_string())
2783            ]
2784        );
2785    }
2786
2787    #[test]
2788    fn jit_runs_list_len_and_index() {
2789        let mut module = compile_value_abi_module(&NativeAbiModule {
2790            functions: Vec::new(),
2791            roots: vec![
2792                NativeAbiFunction {
2793                    name: "len_root".to_string(),
2794                    params: Vec::new(),
2795                    environment_slots: 0,
2796                    blocks: vec![NativeAbiBlock {
2797                        id: BlockId(0),
2798                        params: Vec::new(),
2799                        stmts: list_with_len_or_index_stmts(ValueId(6), None),
2800                        terminator: NativeTerminator::Return(ValueId(6)),
2801                    }],
2802                },
2803                NativeAbiFunction {
2804                    name: "index_root".to_string(),
2805                    params: Vec::new(),
2806                    environment_slots: 0,
2807                    blocks: vec![NativeAbiBlock {
2808                        id: BlockId(0),
2809                        params: Vec::new(),
2810                        stmts: list_with_len_or_index_stmts(ValueId(7), Some(ValueId(5))),
2811                        terminator: NativeTerminator::Return(ValueId(7)),
2812                    }],
2813                },
2814            ],
2815        })
2816        .expect("compiled");
2817
2818        assert_eq!(
2819            module.run_roots().expect("ran"),
2820            vec![
2821                runtime::VmValue::Int("2".to_string()),
2822                runtime::VmValue::Int("2".to_string())
2823            ]
2824        );
2825    }
2826
2827    #[test]
2828    fn jit_runs_list_index_range_raw() {
2829        let mut module = compile_value_abi_module(&NativeAbiModule {
2830            functions: Vec::new(),
2831            roots: vec![NativeAbiFunction {
2832                name: "range_root".to_string(),
2833                params: Vec::new(),
2834                environment_slots: 0,
2835                blocks: vec![NativeAbiBlock {
2836                    id: BlockId(0),
2837                    params: Vec::new(),
2838                    stmts: list_index_range_raw_stmts(),
2839                    terminator: NativeTerminator::Return(ValueId(10)),
2840                }],
2841            }],
2842        })
2843        .expect("compiled");
2844
2845        let values = module.run_roots().expect("ran");
2846        let [runtime::VmValue::List(value)] = values.as_slice() else {
2847            panic!("expected one list value, got {values:?}");
2848        };
2849        let items = value
2850            .to_vec()
2851            .into_iter()
2852            .map(|value| match value.as_ref() {
2853                runtime::VmValue::Int(value) => value.clone(),
2854                value => panic!("expected int value, got {value:?}"),
2855            })
2856            .collect::<Vec<_>>();
2857        assert_eq!(items, vec!["2", "3"]);
2858    }
2859
2860    fn literal_root(name: &str, literal: NativeLiteral) -> NativeAbiFunction {
2861        NativeAbiFunction {
2862            name: name.to_string(),
2863            params: Vec::new(),
2864            environment_slots: 0,
2865            blocks: vec![NativeAbiBlock {
2866                id: BlockId(0),
2867                params: Vec::new(),
2868                stmts: vec![NativeAbiStmt::Literal {
2869                    dest: ValueId(0),
2870                    literal,
2871                }],
2872                terminator: NativeTerminator::Return(ValueId(0)),
2873            }],
2874        }
2875    }
2876
2877    fn list_with_len_or_index_stmts(dest: ValueId, index: Option<ValueId>) -> Vec<NativeAbiStmt> {
2878        let mut stmts = vec![
2879            NativeAbiStmt::Literal {
2880                dest: ValueId(0),
2881                literal: NativeLiteral::Int("1".to_string()),
2882            },
2883            NativeAbiStmt::Primitive {
2884                dest: ValueId(1),
2885                op: yulang_typed_ir::PrimitiveOp::ListSingleton,
2886                args: vec![ValueId(0)],
2887            },
2888            NativeAbiStmt::Literal {
2889                dest: ValueId(2),
2890                literal: NativeLiteral::Int("2".to_string()),
2891            },
2892            NativeAbiStmt::Primitive {
2893                dest: ValueId(3),
2894                op: yulang_typed_ir::PrimitiveOp::ListSingleton,
2895                args: vec![ValueId(2)],
2896            },
2897            NativeAbiStmt::Primitive {
2898                dest: ValueId(4),
2899                op: yulang_typed_ir::PrimitiveOp::ListMerge,
2900                args: vec![ValueId(1), ValueId(3)],
2901            },
2902        ];
2903        if let Some(index) = index {
2904            stmts.push(NativeAbiStmt::Literal {
2905                dest: index,
2906                literal: NativeLiteral::Int("1".to_string()),
2907            });
2908            stmts.push(NativeAbiStmt::Primitive {
2909                dest,
2910                op: yulang_typed_ir::PrimitiveOp::ListIndex,
2911                args: vec![ValueId(4), index],
2912            });
2913        } else {
2914            stmts.push(NativeAbiStmt::Primitive {
2915                dest,
2916                op: yulang_typed_ir::PrimitiveOp::ListLen,
2917                args: vec![ValueId(4)],
2918            });
2919        }
2920        stmts
2921    }
2922
2923    fn list_index_range_raw_stmts() -> Vec<NativeAbiStmt> {
2924        let mut stmts = list_with_len_or_index_stmts(ValueId(6), None);
2925        stmts.pop();
2926        stmts.extend([
2927            NativeAbiStmt::Literal {
2928                dest: ValueId(5),
2929                literal: NativeLiteral::Int("3".to_string()),
2930            },
2931            NativeAbiStmt::Primitive {
2932                dest: ValueId(6),
2933                op: yulang_typed_ir::PrimitiveOp::ListSingleton,
2934                args: vec![ValueId(5)],
2935            },
2936            NativeAbiStmt::Primitive {
2937                dest: ValueId(7),
2938                op: yulang_typed_ir::PrimitiveOp::ListMerge,
2939                args: vec![ValueId(4), ValueId(6)],
2940            },
2941            NativeAbiStmt::Literal {
2942                dest: ValueId(8),
2943                literal: NativeLiteral::Int("1".to_string()),
2944            },
2945            NativeAbiStmt::Literal {
2946                dest: ValueId(9),
2947                literal: NativeLiteral::Int("3".to_string()),
2948            },
2949            NativeAbiStmt::Primitive {
2950                dest: ValueId(10),
2951                op: yulang_typed_ir::PrimitiveOp::ListIndexRangeRaw,
2952                args: vec![ValueId(7), ValueId(8), ValueId(9)],
2953            },
2954        ]);
2955        stmts
2956    }
2957
2958    fn add_capture_function() -> NativeAbiFunction {
2959        NativeAbiFunction {
2960            name: "add_capture".to_string(),
2961            params: vec![ValueId(1)],
2962            environment_slots: 1,
2963            blocks: vec![NativeAbiBlock {
2964                id: BlockId(0),
2965                params: vec![ValueId(1)],
2966                stmts: vec![
2967                    NativeAbiStmt::LoadEnv {
2968                        dest: ValueId(0),
2969                        slot: 0,
2970                    },
2971                    NativeAbiStmt::Primitive {
2972                        dest: ValueId(2),
2973                        op: yulang_typed_ir::PrimitiveOp::IntAdd,
2974                        args: vec![ValueId(0), ValueId(1)],
2975                    },
2976                ],
2977                terminator: NativeTerminator::Return(ValueId(2)),
2978            }],
2979        }
2980    }
2981}