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 ¶ms.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}