1use super::cfg::{assemble_dispatch_loop, Blocks};
16use super::encode::*;
17use super::kind::{self, FieldLayout, FieldNested, Kind, KindTable, ParamShape};
18use super::regsplit;
19use super::WasmLowerError;
20use crate::semantics::builtins::BuiltinId;
21use crate::vm::instruction::{BoundaryType, CompiledFunction, CompiledProgram, Constant, EnumTypeDef, Op, StructTypeDef};
22use crate::Interner;
23use logicaffeine_language::analysis::{PolicyCondition, PolicyRegistry};
24
25type R<T> = std::result::Result<T, WasmLowerError>;
26
27const HEAP_PAGES: u32 = 64;
31
32const NET_INBOX_ADDR: i32 = 8;
36
37#[derive(Clone, Copy, PartialEq, Eq)]
39enum HostFn {
40 PrintI64,
41 PrintBool,
42 PrintChar,
46 PrintF64,
47 PrintDate,
50 PrintMoment,
51 PrintDuration,
54 PrintTime,
55 PowFf,
57 PowFi,
60 Today,
63 Now,
64 PrintSeqI64,
69 PrintSeqBool,
73 PrintSeqWord32,
78 PrintSeqWord64,
79 PrintSeqF64,
80 PrintText,
84 PrintSeqText,
88 PrintSetI64,
94 PrintSetText,
98 FmtI64Into,
103 FmtF64Into,
108 FmtBoolInto,
110 FmtF64PrecInto,
114 FmtAlignInto,
120 Args,
124 ParseInt,
127 ParseFloat,
130 ParseTimestamp,
133 TemporalComponent,
138 TemporalComponentDate,
145 FmtSeqI64Into,
150 FmtSeqBoolInto,
154 FmtSetI64Into,
157 PrintRational,
160 PrintNothing,
164 PrintWord,
168 BigintFromI64,
173 BigintPow,
176 BigintToText,
180 BigintMul,
183 BigintAdd,
185 BigintSub,
188 BigintDiv,
191 BigintMod,
194 ComplexFromI64,
199 ComplexAdd,
200 ComplexSub,
201 ComplexMul,
202 ComplexToText,
203 ModularFromI64,
206 ModularAdd,
207 ModularSub,
208 ModularMul,
209 ModularToText,
210 DecimalFromText,
213 DecimalFromI64,
214 DecimalAdd,
215 DecimalSub,
216 DecimalMul,
217 DecimalToText,
218 MoneyFromDecimal,
221 MoneyFromI64,
222 MoneyAdd,
223 MoneySub,
224 MoneyToText,
225 QuantityOfI64,
226 QuantityConvert,
227 QuantityAdd,
228 QuantitySub,
229 QuantityMul,
230 QuantityDiv,
231 QuantityToText,
232 RationalFromI64,
233 RationalFromBigint,
234 RationalAdd,
235 RationalSub,
236 RationalMul,
237 RationalDiv,
238 RationalToText,
239 RationalFloor,
240 RationalCeil,
241 RationalRound,
242 RationalAbs,
243 UuidParse,
244 UuidNil,
245 UuidMax,
246 UuidDns,
247 UuidUrl,
248 UuidOid,
249 UuidX500,
250 UuidVersion,
251 UuidEq,
252 UuidToText,
253 UuidFromPtr,
254 PrintSpan,
255 MomentAddSpan,
256 DateAddSpan,
257 FormatTimestampRt,
258 MonthsBetweenRt,
259 YearsBetweenRt,
260 InZoneRt,
261 LocalInstantRt,
262 Lanes16FromBytes,
263 Lanes8FromWords,
264 Lanes4W64FromWords,
265 LanesSplat16,
266 LanesSplat8,
267 LanesToSeq,
268 LanesShuffle,
269 LanesInterleaveLo,
270 LanesInterleaveHi,
271 LanesByteAdd,
272 LanesMaddubs,
273 LanesPackus,
274 LanesShrBytes,
275 DecimalToRational,
276 MoneySetRate,
277 MoneyToCurrency,
278 MoneySetRatesInt,
279 MoneySetRatesRational,
280 MoneySetRatesDecimal,
281 WriteWireResidual,
282 WireBytesInt,
283 WireBytesBool,
284 WireBytesFloat,
285 WireBytesText,
286 ReadWireFrame,
287 ReadWireProgramRt,
288 DynamicToText,
289 RunAccepted,
290 Sha1Rnds4,
291 Sha1Msg1,
292 Sha1Msg2,
293 Sha1Nexte,
294 Lanes4Add,
295 Lanes4Xor,
296 RtAlloc,
300}
301
302fn set_rates_value_kind(ops: &[Op], target: u16, kinds: &KindTable) -> Option<Kind> {
307 let mut aliases = std::collections::HashSet::new();
308 aliases.insert(target);
309 let mut changed = true;
310 while changed {
311 changed = false;
312 for op in ops {
313 if let Op::Move { dst, src } = op {
314 if aliases.contains(dst) && aliases.insert(*src) {
315 changed = true;
316 }
317 if aliases.contains(src) && aliases.insert(*dst) {
318 changed = true;
319 }
320 }
321 }
322 }
323 ops.iter().find_map(|op| match op {
324 Op::SetIndex { collection, value, .. } if aliases.contains(collection) => kinds.get(*value as usize),
325 _ => None,
326 })
327}
328
329fn wire_bytes_host_fn(arg_kind: Option<Kind>) -> Option<HostFn> {
333 match arg_kind {
334 Some(Kind::Int) => Some(HostFn::WireBytesInt),
335 Some(Kind::Bool) => Some(HostFn::WireBytesBool),
336 Some(Kind::Float) => Some(HostFn::WireBytesFloat),
337 Some(Kind::Text) => Some(HostFn::WireBytesText),
338 _ => None,
339 }
340}
341
342fn lanes_v_host_fn(b: BuiltinId) -> Option<HostFn> {
346 Some(match b {
347 BuiltinId::Lanes16Word8Make => HostFn::Lanes16FromBytes,
348 BuiltinId::Lanes8Word32 => HostFn::Lanes8FromWords,
349 BuiltinId::Lanes4Word64 => HostFn::Lanes4W64FromWords,
350 BuiltinId::Splat16Word8 => HostFn::LanesSplat16,
351 BuiltinId::Splat8Word32 => HostFn::LanesSplat8,
352 BuiltinId::SeqOfLanes16W8 | BuiltinId::SeqOfLanes8 => HostFn::LanesToSeq,
353 BuiltinId::Shuffle16 => HostFn::LanesShuffle,
354 BuiltinId::InterleaveLo16 => HostFn::LanesInterleaveLo,
355 BuiltinId::InterleaveHi16 => HostFn::LanesInterleaveHi,
356 BuiltinId::ByteAdd16 => HostFn::LanesByteAdd,
357 BuiltinId::Maddubs16 => HostFn::LanesMaddubs,
358 BuiltinId::Packus16 => HostFn::LanesPackus,
359 BuiltinId::ShrBytes16 => HostFn::LanesShrBytes,
360 _ => return None,
361 })
362}
363
364const HOST_FNS: [HostFn; 139] = [
365 HostFn::PrintI64,
366 HostFn::PrintBool,
367 HostFn::PrintChar,
368 HostFn::PrintF64,
369 HostFn::PrintDate,
370 HostFn::PrintMoment,
371 HostFn::PrintDuration,
372 HostFn::PrintTime,
373 HostFn::PowFf,
374 HostFn::PowFi,
375 HostFn::Today,
376 HostFn::Now,
377 HostFn::PrintSeqI64,
378 HostFn::PrintSeqBool,
379 HostFn::PrintSeqWord32,
380 HostFn::PrintSeqWord64,
381 HostFn::PrintSeqF64,
382 HostFn::PrintText,
383 HostFn::PrintSeqText,
384 HostFn::PrintSetI64,
385 HostFn::FmtI64Into,
386 HostFn::FmtF64Into,
387 HostFn::FmtBoolInto,
388 HostFn::FmtF64PrecInto,
389 HostFn::FmtAlignInto,
390 HostFn::Args,
391 HostFn::ParseInt,
392 HostFn::FmtSeqI64Into,
393 HostFn::FmtSeqBoolInto,
394 HostFn::FmtSetI64Into,
395 HostFn::PrintSetText,
396 HostFn::PrintRational,
397 HostFn::PrintNothing,
398 HostFn::ParseFloat,
399 HostFn::ParseTimestamp,
400 HostFn::TemporalComponent,
401 HostFn::TemporalComponentDate,
402 HostFn::PrintWord,
403 HostFn::BigintFromI64,
404 HostFn::BigintPow,
405 HostFn::BigintToText,
406 HostFn::BigintMul,
407 HostFn::BigintAdd,
408 HostFn::BigintSub,
409 HostFn::BigintDiv,
410 HostFn::BigintMod,
411 HostFn::ComplexFromI64,
412 HostFn::ComplexAdd,
413 HostFn::ComplexSub,
414 HostFn::ComplexMul,
415 HostFn::ComplexToText,
416 HostFn::ModularFromI64,
417 HostFn::ModularAdd,
418 HostFn::ModularSub,
419 HostFn::ModularMul,
420 HostFn::ModularToText,
421 HostFn::DecimalFromText,
422 HostFn::DecimalFromI64,
423 HostFn::DecimalAdd,
424 HostFn::DecimalSub,
425 HostFn::DecimalMul,
426 HostFn::DecimalToText,
427 HostFn::MoneyFromDecimal,
428 HostFn::MoneyFromI64,
429 HostFn::MoneyAdd,
430 HostFn::MoneySub,
431 HostFn::MoneyToText,
432 HostFn::QuantityOfI64,
433 HostFn::QuantityConvert,
434 HostFn::QuantityAdd,
435 HostFn::QuantitySub,
436 HostFn::QuantityMul,
437 HostFn::QuantityDiv,
438 HostFn::QuantityToText,
439 HostFn::RationalFromI64,
440 HostFn::RationalFromBigint,
441 HostFn::RationalAdd,
442 HostFn::RationalSub,
443 HostFn::RationalMul,
444 HostFn::RationalDiv,
445 HostFn::RationalToText,
446 HostFn::RationalFloor,
447 HostFn::RationalCeil,
448 HostFn::RationalRound,
449 HostFn::RationalAbs,
450 HostFn::UuidParse,
451 HostFn::UuidNil,
452 HostFn::UuidMax,
453 HostFn::UuidDns,
454 HostFn::UuidUrl,
455 HostFn::UuidOid,
456 HostFn::UuidX500,
457 HostFn::UuidVersion,
458 HostFn::UuidEq,
459 HostFn::UuidToText,
460 HostFn::UuidFromPtr,
461 HostFn::PrintSpan,
462 HostFn::MomentAddSpan,
463 HostFn::DateAddSpan,
464 HostFn::FormatTimestampRt,
465 HostFn::MonthsBetweenRt,
466 HostFn::YearsBetweenRt,
467 HostFn::InZoneRt,
468 HostFn::LocalInstantRt,
469 HostFn::Lanes16FromBytes,
470 HostFn::Lanes8FromWords,
471 HostFn::Lanes4W64FromWords,
472 HostFn::LanesSplat16,
473 HostFn::LanesSplat8,
474 HostFn::LanesToSeq,
475 HostFn::LanesShuffle,
476 HostFn::LanesInterleaveLo,
477 HostFn::LanesInterleaveHi,
478 HostFn::LanesByteAdd,
479 HostFn::LanesMaddubs,
480 HostFn::LanesPackus,
481 HostFn::LanesShrBytes,
482 HostFn::DecimalToRational,
483 HostFn::MoneySetRate,
484 HostFn::MoneyToCurrency,
485 HostFn::MoneySetRatesInt,
486 HostFn::MoneySetRatesRational,
487 HostFn::MoneySetRatesDecimal,
488 HostFn::WriteWireResidual,
489 HostFn::WireBytesInt,
490 HostFn::WireBytesBool,
491 HostFn::WireBytesFloat,
492 HostFn::WireBytesText,
493 HostFn::ReadWireFrame,
494 HostFn::ReadWireProgramRt,
495 HostFn::DynamicToText,
496 HostFn::RunAccepted,
497 HostFn::Sha1Rnds4,
498 HostFn::Sha1Msg1,
499 HostFn::Sha1Msg2,
500 HostFn::Sha1Nexte,
501 HostFn::Lanes4Add,
502 HostFn::Lanes4Xor,
503 HostFn::RtAlloc,
504];
505
506impl HostFn {
507 fn field(self) -> &'static str {
508 match self {
509 HostFn::PrintI64 => "print_i64",
510 HostFn::PrintBool => "print_bool",
511 HostFn::PrintChar => "print_char",
512 HostFn::PrintF64 => "print_f64",
513 HostFn::PrintDate => "print_date",
514 HostFn::PrintMoment => "print_moment",
515 HostFn::PrintDuration => "print_duration",
516 HostFn::PrintTime => "print_time",
517 HostFn::PowFf => "pow_ff",
518 HostFn::PowFi => "pow_fi",
519 HostFn::Today => "today",
520 HostFn::Now => "now",
521 HostFn::PrintSeqI64 => "print_seq_i64",
522 HostFn::PrintSeqBool => "print_seq_bool",
523 HostFn::PrintSeqWord32 => "print_seq_word32",
524 HostFn::PrintSeqWord64 => "print_seq_word64",
525 HostFn::PrintSeqF64 => "print_seq_f64",
526 HostFn::PrintText => "print_text",
527 HostFn::PrintSeqText => "print_seq_text",
528 HostFn::PrintSetI64 => "print_set_i64",
529 HostFn::PrintSetText => "print_set_text",
530 HostFn::FmtI64Into => "fmt_i64_into",
531 HostFn::FmtF64Into => "fmt_f64_into",
532 HostFn::FmtBoolInto => "fmt_bool_into",
533 HostFn::FmtF64PrecInto => "fmt_f64_prec_into",
534 HostFn::FmtAlignInto => "fmt_align_into",
535 HostFn::Args => "args",
536 HostFn::ParseInt => "parse_int",
537 HostFn::ParseFloat => "parse_float",
538 HostFn::ParseTimestamp => "parse_timestamp",
539 HostFn::TemporalComponent => "temporal_component",
540 HostFn::TemporalComponentDate => "temporal_component_date",
541 HostFn::PrintWord => "print_word",
542 HostFn::FmtSeqI64Into => "fmt_seq_i64_into",
543 HostFn::FmtSeqBoolInto => "fmt_seq_bool_into",
544 HostFn::FmtSetI64Into => "fmt_set_i64_into",
545 HostFn::PrintRational => "print_rational",
546 HostFn::PrintNothing => "print_nothing",
547 HostFn::BigintFromI64 => "logos_rt_bigint_from_i64",
548 HostFn::BigintPow => "logos_rt_bigint_pow",
549 HostFn::BigintToText => "logos_rt_bigint_to_text",
550 HostFn::BigintMul => "logos_rt_bigint_mul",
551 HostFn::BigintAdd => "logos_rt_bigint_add",
552 HostFn::BigintSub => "logos_rt_bigint_sub",
553 HostFn::BigintDiv => "logos_rt_bigint_div",
554 HostFn::BigintMod => "logos_rt_bigint_mod",
555 HostFn::ComplexFromI64 => "logos_rt_complex_from_i64",
556 HostFn::ComplexAdd => "logos_rt_complex_add",
557 HostFn::ComplexSub => "logos_rt_complex_sub",
558 HostFn::ComplexMul => "logos_rt_complex_mul",
559 HostFn::ComplexToText => "logos_rt_complex_to_text",
560 HostFn::ModularFromI64 => "logos_rt_modular_from_i64",
561 HostFn::ModularAdd => "logos_rt_modular_add",
562 HostFn::ModularSub => "logos_rt_modular_sub",
563 HostFn::ModularMul => "logos_rt_modular_mul",
564 HostFn::ModularToText => "logos_rt_modular_to_text",
565 HostFn::DecimalFromText => "logos_rt_decimal_from_text",
566 HostFn::DecimalFromI64 => "logos_rt_decimal_from_i64",
567 HostFn::DecimalAdd => "logos_rt_decimal_add",
568 HostFn::DecimalSub => "logos_rt_decimal_sub",
569 HostFn::DecimalMul => "logos_rt_decimal_mul",
570 HostFn::DecimalToText => "logos_rt_decimal_to_text",
571 HostFn::MoneyFromDecimal => "logos_rt_money_from_decimal",
572 HostFn::MoneyFromI64 => "logos_rt_money_from_i64",
573 HostFn::MoneyAdd => "logos_rt_money_add",
574 HostFn::MoneySub => "logos_rt_money_sub",
575 HostFn::MoneyToText => "logos_rt_money_to_text",
576 HostFn::QuantityOfI64 => "logos_rt_quantity_of_i64",
577 HostFn::QuantityConvert => "logos_rt_quantity_convert",
578 HostFn::QuantityAdd => "logos_rt_quantity_add",
579 HostFn::QuantitySub => "logos_rt_quantity_sub",
580 HostFn::QuantityMul => "logos_rt_quantity_mul",
581 HostFn::QuantityDiv => "logos_rt_quantity_div",
582 HostFn::QuantityToText => "logos_rt_quantity_to_text",
583 HostFn::RationalFromI64 => "logos_rt_rational_from_i64",
584 HostFn::RationalFromBigint => "logos_rt_rational_from_bigint",
585 HostFn::RationalAdd => "logos_rt_rational_add",
586 HostFn::RationalSub => "logos_rt_rational_sub",
587 HostFn::RationalMul => "logos_rt_rational_mul",
588 HostFn::RationalDiv => "logos_rt_rational_div",
589 HostFn::RationalToText => "logos_rt_rational_to_text",
590 HostFn::RationalFloor => "logos_rt_rational_floor",
591 HostFn::RationalCeil => "logos_rt_rational_ceil",
592 HostFn::RationalRound => "logos_rt_rational_round",
593 HostFn::RationalAbs => "logos_rt_rational_abs",
594 HostFn::UuidParse => "logos_rt_uuid_parse",
595 HostFn::UuidNil => "logos_rt_uuid_nil",
596 HostFn::UuidMax => "logos_rt_uuid_max",
597 HostFn::UuidDns => "logos_rt_uuid_dns",
598 HostFn::UuidUrl => "logos_rt_uuid_url",
599 HostFn::UuidOid => "logos_rt_uuid_oid",
600 HostFn::UuidX500 => "logos_rt_uuid_x500",
601 HostFn::UuidVersion => "logos_rt_uuid_version",
602 HostFn::UuidEq => "logos_rt_uuid_eq",
603 HostFn::UuidToText => "logos_rt_uuid_to_text",
604 HostFn::UuidFromPtr => "logos_rt_uuid_from_ptr",
605 HostFn::PrintSpan => "print_span",
606 HostFn::MomentAddSpan => "logos_rt_moment_add_span",
607 HostFn::DateAddSpan => "logos_rt_date_add_span",
608 HostFn::FormatTimestampRt => "logos_rt_format_timestamp",
609 HostFn::MonthsBetweenRt => "logos_rt_months_between",
610 HostFn::YearsBetweenRt => "logos_rt_years_between",
611 HostFn::InZoneRt => "logos_rt_in_zone",
612 HostFn::LocalInstantRt => "logos_rt_local_instant",
613 HostFn::Lanes16FromBytes => "logos_rt_lanes16_from_bytes",
614 HostFn::Lanes8FromWords => "logos_rt_lanes8_from_words",
615 HostFn::Lanes4W64FromWords => "logos_rt_lanes4w64_from_words",
616 HostFn::LanesSplat16 => "logos_rt_lanes_splat16",
617 HostFn::LanesSplat8 => "logos_rt_lanes_splat8",
618 HostFn::LanesToSeq => "logos_rt_lanes_to_seq",
619 HostFn::LanesShuffle => "logos_rt_lanes_shuffle",
620 HostFn::LanesInterleaveLo => "logos_rt_lanes_interleave_lo",
621 HostFn::LanesInterleaveHi => "logos_rt_lanes_interleave_hi",
622 HostFn::LanesByteAdd => "logos_rt_lanes_byte_add",
623 HostFn::LanesMaddubs => "logos_rt_lanes_maddubs",
624 HostFn::LanesPackus => "logos_rt_lanes_packus",
625 HostFn::LanesShrBytes => "logos_rt_lanes_shr_bytes",
626 HostFn::DecimalToRational => "logos_rt_decimal_to_rational",
627 HostFn::MoneySetRate => "logos_rt_set_rate",
628 HostFn::MoneyToCurrency => "logos_rt_to_currency",
629 HostFn::MoneySetRatesInt => "logos_rt_set_rates_int",
630 HostFn::MoneySetRatesRational => "logos_rt_set_rates_rational",
631 HostFn::MoneySetRatesDecimal => "logos_rt_set_rates_decimal",
632 HostFn::WriteWireResidual => "write_wire_residual",
633 HostFn::WireBytesInt => "logos_rt_wire_bytes_int",
634 HostFn::WireBytesBool => "logos_rt_wire_bytes_bool",
635 HostFn::WireBytesFloat => "logos_rt_wire_bytes_float",
636 HostFn::WireBytesText => "logos_rt_wire_bytes_text",
637 HostFn::ReadWireFrame => "read_wire_frame",
638 HostFn::ReadWireProgramRt => "logos_rt_read_wire_program",
639 HostFn::DynamicToText => "logos_rt_dynamic_to_text",
640 HostFn::RunAccepted => "logos_rt_run_accepted",
641 HostFn::Sha1Rnds4 => "logos_rt_sha1rnds4",
642 HostFn::Sha1Msg1 => "logos_rt_sha1msg1",
643 HostFn::Sha1Msg2 => "logos_rt_sha1msg2",
644 HostFn::Sha1Nexte => "logos_rt_sha1nexte",
645 HostFn::Lanes4Add => "logos_rt_lanes4_add",
646 HostFn::Lanes4Xor => "logos_rt_lanes4_xor",
647 HostFn::RtAlloc => "logos_rt_alloc",
648 }
649 }
650
651 fn params(self) -> Vec<u8> {
653 match self {
654 HostFn::PrintI64 | HostFn::PrintChar => vec![I64],
655 HostFn::PrintBool | HostFn::PrintDate => vec![I32],
656 HostFn::PrintF64 => vec![F64],
657 HostFn::PrintMoment | HostFn::PrintDuration | HostFn::PrintTime | HostFn::PrintSpan => vec![I64],
658 HostFn::MomentAddSpan => vec![I64, I32, I32],
659 HostFn::DateAddSpan => vec![I32, I32, I32],
660 HostFn::FormatTimestampRt => vec![I64],
661 HostFn::MonthsBetweenRt | HostFn::YearsBetweenRt => vec![I64, I64],
662 HostFn::InZoneRt | HostFn::LocalInstantRt => vec![I64, I32],
663 HostFn::Lanes16FromBytes | HostFn::Lanes8FromWords | HostFn::Lanes4W64FromWords | HostFn::LanesToSeq => vec![I32],
664 HostFn::LanesSplat16 | HostFn::LanesSplat8 => vec![I64],
665 HostFn::LanesShuffle | HostFn::LanesInterleaveLo | HostFn::LanesInterleaveHi | HostFn::LanesByteAdd | HostFn::LanesMaddubs | HostFn::LanesPackus => vec![I32, I32],
666 HostFn::LanesShrBytes => vec![I32, I64],
667 HostFn::DecimalToRational | HostFn::MoneySetRatesInt | HostFn::MoneySetRatesRational | HostFn::MoneySetRatesDecimal => vec![I32],
668 HostFn::MoneySetRate | HostFn::MoneyToCurrency => vec![I32, I32],
669 HostFn::WriteWireResidual => vec![I32, I32],
670 HostFn::WireBytesInt | HostFn::WireBytesBool => vec![I64],
671 HostFn::WireBytesFloat => vec![F64],
672 HostFn::WireBytesText => vec![I32],
673 HostFn::ReadWireFrame | HostFn::ReadWireProgramRt => vec![I32, I32],
674 HostFn::DynamicToText => vec![I32],
675 HostFn::RunAccepted => vec![I32, I64, I64, I64],
676 HostFn::Sha1Rnds4 => vec![I32, I32, I64],
677 HostFn::Sha1Msg1 | HostFn::Sha1Msg2 | HostFn::Sha1Nexte | HostFn::Lanes4Add | HostFn::Lanes4Xor => vec![I32, I32],
678 HostFn::PowFf => vec![F64, F64],
679 HostFn::PowFi => vec![F64, I64],
680 HostFn::Today | HostFn::Now => vec![],
681 HostFn::PrintSeqI64 | HostFn::PrintSeqBool | HostFn::PrintSeqWord32 | HostFn::PrintSeqWord64 | HostFn::PrintSeqF64 | HostFn::PrintText | HostFn::PrintSeqText | HostFn::PrintSetI64 | HostFn::PrintSetText => vec![I32],
682 HostFn::FmtI64Into => vec![I32, I64],
683 HostFn::FmtF64Into => vec![I32, F64],
684 HostFn::FmtBoolInto => vec![I32, I64], HostFn::FmtF64PrecInto => vec![I32, F64, I32],
686 HostFn::FmtAlignInto => vec![I32, I32, I32, I32],
687 HostFn::FmtSeqI64Into | HostFn::FmtSeqBoolInto | HostFn::FmtSetI64Into => vec![I32, I32],
688 HostFn::Args => vec![],
689 HostFn::ParseInt | HostFn::ParseFloat | HostFn::ParseTimestamp => vec![I32],
690 HostFn::PrintRational => vec![I64, I64],
691 HostFn::PrintNothing => vec![],
692 HostFn::TemporalComponent => vec![I64, I32],
693 HostFn::TemporalComponentDate => vec![I32, I32],
694 HostFn::PrintWord => vec![I64],
695 HostFn::BigintFromI64 => vec![I64],
696 HostFn::BigintPow => vec![I32, I64],
697 HostFn::BigintToText => vec![I32],
698 HostFn::BigintMul | HostFn::BigintAdd | HostFn::BigintSub | HostFn::BigintDiv | HostFn::BigintMod => vec![I32, I32],
699 HostFn::ComplexFromI64 => vec![I64, I64],
700 HostFn::ComplexAdd | HostFn::ComplexSub | HostFn::ComplexMul => vec![I32, I32],
701 HostFn::ComplexToText => vec![I32],
702 HostFn::ModularFromI64 => vec![I64, I64],
703 HostFn::ModularAdd | HostFn::ModularSub | HostFn::ModularMul => vec![I32, I32],
704 HostFn::ModularToText => vec![I32],
705 HostFn::DecimalFromI64 => vec![I64],
706 HostFn::DecimalFromText | HostFn::DecimalToText => vec![I32],
707 HostFn::DecimalAdd | HostFn::DecimalSub | HostFn::DecimalMul => vec![I32, I32],
708 HostFn::MoneyFromI64 => vec![I64, I32],
709 HostFn::MoneyFromDecimal | HostFn::MoneyAdd | HostFn::MoneySub => vec![I32, I32],
710 HostFn::MoneyToText => vec![I32],
711 HostFn::QuantityOfI64 => vec![I64, I32],
712 HostFn::QuantityConvert
713 | HostFn::QuantityAdd
714 | HostFn::QuantitySub
715 | HostFn::QuantityMul
716 | HostFn::QuantityDiv => vec![I32, I32],
717 HostFn::QuantityToText => vec![I32],
718 HostFn::RationalFromI64 => vec![I64],
719 HostFn::RationalFromBigint
720 | HostFn::RationalToText
721 | HostFn::RationalFloor
722 | HostFn::RationalCeil
723 | HostFn::RationalRound
724 | HostFn::RationalAbs => vec![I32],
725 HostFn::RationalAdd | HostFn::RationalSub | HostFn::RationalMul | HostFn::RationalDiv => vec![I32, I32],
726 HostFn::UuidNil | HostFn::UuidMax | HostFn::UuidDns | HostFn::UuidUrl | HostFn::UuidOid | HostFn::UuidX500 => vec![],
727 HostFn::UuidParse | HostFn::UuidVersion | HostFn::UuidToText | HostFn::UuidFromPtr => vec![I32],
728 HostFn::UuidEq => vec![I32, I32],
729 HostFn::RtAlloc => vec![I32],
730 }
731 }
732
733 fn results(self) -> Vec<u8> {
735 match self {
736 HostFn::PrintI64
737 | HostFn::PrintBool
738 | HostFn::PrintChar
739 | HostFn::PrintF64
740 | HostFn::PrintDate
741 | HostFn::PrintMoment
742 | HostFn::PrintDuration
743 | HostFn::PrintTime
744 | HostFn::PrintSpan
745 | HostFn::PrintSeqI64
746 | HostFn::PrintSeqBool
747 | HostFn::PrintSeqWord32
748 | HostFn::PrintSeqWord64
749 | HostFn::PrintSeqF64
750 | HostFn::PrintText
751 | HostFn::PrintSeqText
752 | HostFn::PrintSetI64
753 | HostFn::PrintSetText
754 | HostFn::PrintRational
755 | HostFn::PrintNothing
756 | HostFn::PrintWord => vec![],
757 HostFn::PowFf | HostFn::PowFi | HostFn::ParseFloat => vec![F64],
758 HostFn::Today | HostFn::FmtI64Into | HostFn::FmtF64Into | HostFn::FmtBoolInto | HostFn::FmtF64PrecInto | HostFn::FmtAlignInto | HostFn::FmtSeqI64Into | HostFn::FmtSeqBoolInto | HostFn::FmtSetI64Into | HostFn::Args
759 | HostFn::BigintFromI64 | HostFn::BigintPow | HostFn::BigintToText | HostFn::BigintMul | HostFn::BigintAdd | HostFn::BigintSub | HostFn::BigintDiv | HostFn::BigintMod | HostFn::ComplexFromI64 | HostFn::ComplexAdd | HostFn::ComplexSub | HostFn::ComplexMul | HostFn::ComplexToText | HostFn::ModularFromI64 | HostFn::ModularAdd | HostFn::ModularSub | HostFn::ModularMul | HostFn::ModularToText | HostFn::DecimalFromText | HostFn::DecimalFromI64 | HostFn::DecimalAdd | HostFn::DecimalSub | HostFn::DecimalMul | HostFn::DecimalToText | HostFn::MoneyFromDecimal | HostFn::MoneyFromI64 | HostFn::MoneyAdd | HostFn::MoneySub | HostFn::MoneyToText | HostFn::QuantityOfI64 | HostFn::QuantityConvert | HostFn::QuantityAdd | HostFn::QuantitySub | HostFn::QuantityMul | HostFn::QuantityDiv | HostFn::QuantityToText | HostFn::RationalFromI64 | HostFn::RationalFromBigint | HostFn::RationalAdd | HostFn::RationalSub | HostFn::RationalMul | HostFn::RationalDiv | HostFn::RationalToText | HostFn::RationalFloor | HostFn::RationalCeil | HostFn::RationalRound | HostFn::RationalAbs | HostFn::UuidParse | HostFn::UuidNil | HostFn::UuidMax | HostFn::UuidDns | HostFn::UuidUrl | HostFn::UuidOid | HostFn::UuidX500 | HostFn::UuidEq | HostFn::UuidToText | HostFn::UuidFromPtr | HostFn::DateAddSpan | HostFn::Sha1Rnds4 | HostFn::Sha1Msg1 | HostFn::Sha1Msg2 | HostFn::Sha1Nexte | HostFn::Lanes4Add | HostFn::Lanes4Xor | HostFn::RtAlloc | HostFn::FormatTimestampRt | HostFn::InZoneRt | HostFn::Lanes16FromBytes | HostFn::Lanes8FromWords | HostFn::Lanes4W64FromWords | HostFn::LanesSplat16 | HostFn::LanesSplat8 | HostFn::LanesToSeq | HostFn::LanesShuffle | HostFn::LanesInterleaveLo | HostFn::LanesInterleaveHi | HostFn::LanesByteAdd | HostFn::LanesMaddubs | HostFn::LanesPackus | HostFn::LanesShrBytes | HostFn::DecimalToRational | HostFn::MoneySetRate | HostFn::MoneyToCurrency | HostFn::MoneySetRatesInt | HostFn::MoneySetRatesRational | HostFn::MoneySetRatesDecimal | HostFn::WireBytesInt | HostFn::WireBytesBool | HostFn::WireBytesFloat | HostFn::WireBytesText | HostFn::ReadWireFrame | HostFn::ReadWireProgramRt | HostFn::DynamicToText => vec![I32],
760 HostFn::Now | HostFn::ParseInt | HostFn::ParseTimestamp | HostFn::TemporalComponent | HostFn::TemporalComponentDate | HostFn::UuidVersion | HostFn::MomentAddSpan | HostFn::MonthsBetweenRt | HostFn::YearsBetweenRt | HostFn::LocalInstantRt | HostFn::WriteWireResidual | HostFn::RunAccepted => vec![I64],
761 }
762 }
763
764 fn for_show(k: Kind) -> Option<HostFn> {
768 Some(match k {
769 Kind::Int => HostFn::PrintI64,
770 Kind::Bool => HostFn::PrintBool,
771 Kind::Char => HostFn::PrintChar,
772 Kind::Float => HostFn::PrintF64,
773 Kind::Date => HostFn::PrintDate,
774 Kind::Moment => HostFn::PrintMoment,
775 Kind::Duration => HostFn::PrintDuration,
776 Kind::Time => HostFn::PrintTime,
777 Kind::Span => HostFn::PrintSpan,
778 Kind::SeqInt => HostFn::PrintSeqI64,
779 Kind::SeqBool => HostFn::PrintSeqBool,
780 Kind::SeqWord32 => HostFn::PrintSeqWord32,
781 Kind::SeqWord64 => HostFn::PrintSeqWord64,
782 Kind::SeqFloat => HostFn::PrintSeqF64,
783 Kind::SeqText => HostFn::PrintSeqText,
784 Kind::SeqAny => HostFn::PrintSeqI64,
786 Kind::Text => HostFn::PrintText,
787 Kind::Struct => return None,
791 Kind::Set => HostFn::PrintSetI64,
794 Kind::SetText | Kind::CrdtSetText => HostFn::PrintSetText,
795 Kind::Map => return None,
799 Kind::Enum => return None,
801 Kind::Closure => return None,
803 Kind::BigInt => return None,
806 Kind::Complex => return None,
808 Kind::Modular => return None,
810 Kind::Decimal => return None,
812 Kind::Money => return None,
814 Kind::Quantity => return None,
815 Kind::Uuid => return None,
816 Kind::Lanes => return None,
817 Kind::LanesV => return None,
819 Kind::Dynamic => return None,
822 Kind::Tuple => return None,
825 Kind::SeqStruct => return None,
828 Kind::SeqEnum => return None,
831 Kind::SeqSeqInt => return None,
834 Kind::Rational => return None,
837 Kind::Optional => return None,
840 Kind::Word32 | Kind::Word64 => return None,
843 })
844 }
845}
846
847#[derive(Default)]
849struct TypeTable {
850 sigs: Vec<(Vec<u8>, Vec<u8>)>,
851}
852
853impl TypeTable {
854 fn intern(&mut self, params: Vec<u8>, results: Vec<u8>) -> u32 {
855 if let Some(i) = self.sigs.iter().position(|(p, r)| *p == params && *r == results) {
856 return i as u32;
857 }
858 self.sigs.push((params, results));
859 (self.sigs.len() - 1) as u32
860 }
861
862 fn encode(&self) -> Vec<u8> {
863 let mut out = Vec::new();
864 leb_u32(&mut out, self.sigs.len() as u32);
865 for (params, results) in &self.sigs {
866 out.push(0x60); leb_u32(&mut out, params.len() as u32);
868 out.extend_from_slice(params);
869 leb_u32(&mut out, results.len() as u32);
870 out.extend_from_slice(results);
871 }
872 out
873 }
874}
875
876struct Plan {
881 ops: Vec<Op>,
882 kinds: KindTable,
883 num_params: u32,
884 num_regs: u32,
885 result: Option<Kind>,
886 reachable_blocks: Vec<bool>,
887 structs: kind::StructLayout,
890 cow_inserts: Vec<bool>,
893 reg_shape: std::collections::HashMap<u16, kind::ParamShape>,
897 return_closure: Option<u16>,
901 stub: bool,
906}
907
908impl Plan {
909 fn stub() -> Plan {
911 Plan {
912 ops: Vec::new(),
913 kinds: KindTable::empty(),
914 num_params: 0,
915 num_regs: 0,
916 result: None,
917 reachable_blocks: Vec::new(),
918 structs: kind::StructLayout::default(),
919 cow_inserts: Vec::new(),
920 reg_shape: std::collections::HashMap::new(),
921 return_closure: None,
922 stub: true,
923 }
924 }
925}
926
927fn infer_with_reachability(
932 ops: &[Op],
933 constants: &[Constant],
934 struct_types: &[StructTypeDef],
935 enum_types: &[EnumTypeDef],
936 fn_return_types: &[Option<BoundaryType>],
937 num_regs: usize,
938 seeds: &[Option<Kind>],
939 ret_of: &dyn Fn(usize) -> Option<Kind>,
940 global_of: &dyn Fn(u16) -> Option<Kind>,
941 closure_ret: &dyn Fn(usize) -> Option<Kind>,
942 ret_layout: &dyn Fn(u16) -> Option<FieldLayout>,
943 fn_return_closure: &dyn Fn(u16) -> Option<u16>,
944 param_layouts: &[(u16, ParamShape)],
945 param_closures: &[(u16, u16)],
946 linked: bool,
947 functions: &[crate::vm::instruction::CompiledFunction],
948) -> R<(KindTable, Vec<bool>, Vec<bool>)> {
949 let blocks = Blocks::new(ops).ok_or(WasmLowerError::Unsupported("jump target escapes the function"))?;
950 let (pc_reach, block_reach) = reachability(ops, &blocks);
951 let bigint_demand = kind::bigint_demanded_regs(ops, functions, linked);
954 let kinds = kind::infer(ops, constants, struct_types, enum_types, fn_return_types, num_regs, seeds, ret_of, global_of, closure_ret, ret_layout, fn_return_closure, param_layouts, param_closures, &pc_reach, linked, &bigint_demand)?;
955 Ok((kinds, pc_reach, block_reach))
956}
957
958fn reachability(ops: &[Op], blocks: &Blocks) -> (Vec<bool>, Vec<bool>) {
961 let nb = blocks.num_blocks();
962 let mut block_reach = vec![false; nb];
963 let mut stack = vec![0usize];
964 while let Some(k) = stack.pop() {
965 if block_reach[k] {
966 continue;
967 }
968 block_reach[k] = true;
969 for s in block_successors(ops, blocks, k) {
970 if !block_reach[s] {
971 stack.push(s);
972 }
973 }
974 }
975 let mut pc_reach = vec![false; ops.len()];
976 for (k, &live) in block_reach.iter().enumerate() {
977 if live {
978 for pc in blocks.start(k)..blocks.end(k) {
979 pc_reach[pc] = true;
980 }
981 }
982 }
983 (pc_reach, block_reach)
984}
985
986fn block_successors(ops: &[Op], blocks: &Blocks, k: usize) -> Vec<usize> {
988 let n = ops.len();
989 let fallthrough = |pc: usize| -> Vec<usize> {
990 if pc + 1 < n {
991 vec![blocks.block_of(pc + 1)]
992 } else {
993 vec![]
994 }
995 };
996 for pc in blocks.start(k)..blocks.end(k) {
997 match ops[pc] {
998 Op::Jump { target } => return vec![blocks.block_of(target)],
999 Op::JumpIfFalse { target, .. } | Op::JumpIfTrue { target, .. } => {
1000 let mut s = vec![blocks.block_of(target)];
1001 s.extend(fallthrough(pc));
1002 return s;
1003 }
1004 Op::IterNext { exit, .. } => {
1007 let mut s = vec![blocks.block_of(exit)];
1008 s.extend(fallthrough(pc));
1009 return s;
1010 }
1011 Op::Return { .. } | Op::ReturnNothing | Op::Halt | Op::FailWith { .. } => return vec![],
1012 _ => {}
1013 }
1014 }
1015 if blocks.end(k) < n {
1017 vec![blocks.block_of(blocks.end(k))]
1018 } else {
1019 vec![]
1020 }
1021}
1022
1023pub fn assemble_program(program: &CompiledProgram, policies: &PolicyRegistry, interner: &Interner) -> R<Vec<u8>> {
1027 assemble_program_impl(program, policies, interner, false)
1028}
1029
1030pub(crate) fn assemble_program_linked(program: &CompiledProgram, policies: &PolicyRegistry, interner: &Interner) -> R<Vec<u8>> {
1038 assemble_program_impl(program, policies, interner, true)
1039}
1040
1041fn assemble_program_impl(program: &CompiledProgram, policies: &PolicyRegistry, interner: &Interner, linked: bool) -> R<Vec<u8>> {
1042 let layout = code_layout(program)?;
1044 let ret_of = |fi: usize| -> Option<Kind> { declared_result(program, fi) };
1045 let no_closure = |_: usize| -> Option<Kind> { None };
1046 let no_ret_layout = |_: u16| -> Option<FieldLayout> { None };
1047 let no_ret_closure = |_: u16| -> Option<u16> { None };
1048 let no_param_origin = |_: usize, _: usize| -> Option<u16> { None };
1049
1050 let num_globals = program.globals.len();
1053 let no_globals = |_: u16| None;
1054 let no_caps: Vec<Vec<Option<Kind>>> = Vec::new();
1055 let main_p1 = plan_main(program, &layout, &ret_of, &no_globals, &no_closure, &no_ret_layout, &no_ret_closure, linked)?;
1056 let global_kinds: Vec<Option<Kind>> = {
1057 let mut gk = vec![None; num_globals];
1058 for op in &main_p1.ops {
1059 if let Op::GlobalSet { idx, src } = *op {
1060 if let Some(k) = main_p1.kinds.get(src as usize) {
1061 gk[idx as usize] = Some(k);
1062 }
1063 }
1064 }
1065 gk
1066 };
1067 let global_of = |idx: u16| -> Option<Kind> { global_kinds.get(idx as usize).copied().flatten() };
1068 let global_closures: Vec<Option<u16>> = {
1072 let mut gc = vec![None; num_globals];
1073 for op in &main_p1.ops {
1074 if let Op::GlobalSet { idx, src } = *op {
1075 if let Some(&c) = main_p1.structs.closure_of.get(&src) {
1076 gc[idx as usize] = Some(c);
1077 }
1078 }
1079 }
1080 gc
1081 };
1082 let global_closure_of = |idx: u16| -> Option<u16> { global_closures.get(idx as usize).copied().flatten() };
1083
1084 let no_shapes: Vec<Vec<Option<ParamShape>>> = Vec::new();
1095 let no_capture_closures: Vec<Vec<Option<u16>>> = Vec::new();
1096 let mut plans: Vec<Option<Plan>> = (0..program.functions.len()).map(|_| None).collect();
1097 for fi in 0..program.functions.len() {
1098 if !layout.is_closure[fi] {
1099 plans[fi] = Some(plan_function(program, fi, &layout, &ret_of, &global_of, &no_closure, &no_ret_layout, &no_ret_closure, &no_param_origin, &no_caps, &no_shapes, &no_capture_closures, false, linked)?);
1100 }
1101 }
1102 let mut capture_kinds: Vec<Vec<Option<Kind>>> = Vec::new();
1103 let mut capture_shapes: Vec<Vec<Option<ParamShape>>> = Vec::new();
1104 let mut capture_closures: Vec<Vec<Option<u16>>> = Vec::new();
1105 for _ in 0..(program.functions.len() + 4) {
1106 let (ck, cs, cc) = extract_capture_kinds(program, &main_p1, &plans, &global_of, &global_closure_of);
1107 capture_kinds = ck;
1108 capture_shapes = cs;
1109 capture_closures = cc;
1110 let cur_results: Vec<Option<Kind>> = plans.iter().map(|p| p.as_ref().and_then(|p| p.result)).collect();
1111 let cur_layouts: Vec<Option<FieldLayout>> = plans.iter().map(|p| p.as_ref().and_then(fn_return_struct_layout)).collect();
1112 let cur_ret_closures: Vec<Option<u16>> = plans.iter().map(|p| p.as_ref().and_then(|p| p.return_closure)).collect();
1113 let closure_ret = |fi: usize| cur_results.get(fi).copied().flatten();
1114 let ret_of_i =
1115 |fi: usize| cur_results.get(fi).copied().flatten().or_else(|| declared_result(program, fi));
1116 let ret_layout_of = |func: u16| cur_layouts.get(func as usize).cloned().flatten();
1117 let ret_closure_of = |func: u16| cur_ret_closures.get(func as usize).copied().flatten();
1118 let main_iter = plan_main(program, &layout, &ret_of_i, &global_of, &closure_ret, &ret_layout_of, &ret_closure_of, linked).ok();
1123 let mut scan_plans: Vec<&Plan> = vec![main_iter.as_ref().unwrap_or(&main_p1)];
1124 scan_plans.extend(plans.iter().flatten());
1125 let param_origins = compute_param_origins(program, &scan_plans);
1126 let param_origin = |fi: usize, pi: usize| param_origins.get(fi).and_then(|v| v.get(pi)).copied().flatten();
1127 let mut progress = false;
1128 for fi in 0..program.functions.len() {
1129 if let Ok(p) = plan_function(program, fi, &layout, &ret_of_i, &global_of, &closure_ret, &ret_layout_of, &ret_closure_of, ¶m_origin, &capture_kinds, &capture_shapes, &capture_closures, false, linked) {
1130 let changed = plans[fi]
1131 .as_ref()
1132 .map_or(true, |x| x.result != p.result || x.return_closure != p.return_closure);
1133 if changed {
1134 progress = true;
1135 }
1136 plans[fi] = Some(p);
1137 }
1138 }
1139 if !progress {
1140 break;
1141 }
1142 }
1143 let fns1: Vec<Plan> = plans.into_iter().map(|p| p.expect("every function planned by the fixpoint")).collect();
1144
1145 let fn_results_p1: Vec<Option<Kind>> = fns1.iter().map(|p| p.result).collect();
1150 let ret_layouts: Vec<Option<FieldLayout>> = fns1.iter().map(fn_return_struct_layout).collect();
1151 let ret_closures: Vec<Option<u16>> = fns1.iter().map(|p| p.return_closure).collect();
1152 let closure_ret = |fi: usize| -> Option<Kind> { fn_results_p1.get(fi).copied().flatten() };
1153 let ret_of_inferred =
1154 |fi: usize| -> Option<Kind> { fn_results_p1.get(fi).copied().flatten().or_else(|| declared_result(program, fi)) };
1155 let ret_layout_of = |func: u16| -> Option<FieldLayout> { ret_layouts.get(func as usize).cloned().flatten() };
1156 let ret_closure_of = |func: u16| -> Option<u16> { ret_closures.get(func as usize).copied().flatten() };
1157 let main = plan_main(program, &layout, &ret_of_inferred, &global_of, &closure_ret, &ret_layout_of, &ret_closure_of, linked)?;
1160 let scan_plans: Vec<&Plan> = std::iter::once(&main).chain(fns1.iter()).collect();
1161 let param_origins = compute_param_origins(program, &scan_plans);
1162 let param_origin = |fi: usize, pi: usize| param_origins.get(fi).and_then(|v| v.get(pi)).copied().flatten();
1163 let reachable = {
1169 let main_end = layout.main_end.min(program.code.len());
1176 let mut r = vec![false; program.functions.len()];
1177 let mut stack: Vec<usize> = Vec::new();
1178 let collect = |ops: &[Op], stack: &mut Vec<usize>| {
1182 for op in ops {
1183 if let Op::Call { func, .. }
1184 | Op::MakeClosure { func, .. }
1185 | Op::Spawn { func, .. }
1186 | Op::SpawnHandle { func, .. } = op
1187 {
1188 stack.push(*func as usize);
1189 }
1190 }
1191 };
1192 collect(&program.code[0..main_end], &mut stack);
1193 while let Some(fi) = stack.pop() {
1194 if fi >= r.len() || r[fi] {
1195 continue;
1196 }
1197 r[fi] = true;
1198 let (s, e) = layout.func_region[fi];
1199 if s <= e && e <= program.code.len() {
1200 collect(&program.code[s..e], &mut stack);
1201 }
1202 }
1203 r
1204 };
1205 let fns: Vec<Plan> = {
1206 let mut v = Vec::with_capacity(program.functions.len());
1207 for fi in 0..program.functions.len() {
1208 if !reachable[fi] {
1209 v.push(Plan::stub());
1210 continue;
1211 }
1212 v.push(plan_function(program, fi, &layout, &ret_of_inferred, &global_of, &closure_ret, &ret_layout_of, &ret_closure_of, ¶m_origin, &capture_kinds, &capture_shapes, &capture_closures, true, linked)?);
1213 }
1214 v
1215 };
1216
1217 let loads_text = |op: &Op| matches!(op, Op::LoadConst { idx, .. } if matches!(program.constants.get(*idx as usize), Some(Constant::Text(_))));
1222 let concats_text = |p: &Plan, op: &Op| match *op {
1225 Op::Add { dst, .. } | Op::AddAssign { dst, .. } => p.kinds.get(dst as usize) == Some(Kind::Text),
1226 _ => false,
1227 };
1228 let uses_heap = std::iter::once(&main)
1229 .chain(fns.iter())
1230 .any(|p| p.ops.iter().any(|op| op_uses_heap(op) || loads_text(op) || concats_text(p, op)));
1231 let uses_iter = std::iter::once(&main)
1232 .chain(fns.iter())
1233 .any(|p| p.ops.iter().any(|op| matches!(op, Op::IterPrepare { .. })));
1234 let has_closures = layout.is_closure.iter().any(|&c| c);
1240
1241 let mut used = Vec::new();
1243 let note = |h: HostFn, used: &mut Vec<HostFn>| {
1244 if !used.contains(&h) {
1245 used.push(h);
1246 }
1247 };
1248 let note_fmt_kind = |k: Option<Kind>, used: &mut Vec<HostFn>| match k {
1252 Some(Kind::Int) => note(HostFn::FmtI64Into, used),
1253 Some(Kind::Float) => note(HostFn::FmtF64Into, used),
1254 Some(Kind::Bool) => note(HostFn::FmtBoolInto, used),
1255 Some(Kind::SeqInt | Kind::SeqAny) => note(HostFn::FmtSeqI64Into, used),
1257 Some(Kind::SeqBool) => note(HostFn::FmtSeqBoolInto, used),
1259 Some(Kind::Set) => note(HostFn::FmtSetI64Into, used),
1260 Some(Kind::BigInt) => note(HostFn::BigintToText, used),
1262 _ => {}
1263 };
1264 let note_operand_fmt = |plan: &Plan, r: u16, used: &mut Vec<HostFn>| note_fmt_kind(plan.kinds.get(r as usize), used);
1266 for plan in std::iter::once(&main).chain(fns.iter()) {
1267 for op in &plan.ops {
1268 match *op {
1269 Op::Show { src } => {
1275 if let Some(elems) = plan.structs.tuple_layouts.get(&src) {
1276 note(HostFn::PrintText, &mut used);
1279 for &e in elems {
1280 note_operand_fmt(plan, e, &mut used);
1281 }
1282 } else if plan.kinds.get(src as usize) == Some(Kind::Enum) {
1283 note(HostFn::PrintText, &mut used);
1287 if let Some(def) = plan
1288 .structs
1289 .ind_type_of
1290 .get(&src)
1291 .and_then(|tn| program.enum_types.iter().find(|e| &e.name == tn))
1292 {
1293 for v in &def.variants {
1294 for ft in &v.field_types {
1295 note_fmt_kind(kind::boundary_to_kind(ft), &mut used);
1296 }
1297 }
1298 }
1299 } else if plan.kinds.get(src as usize) == Some(Kind::Map) {
1300 note(HostFn::PrintText, &mut used);
1303 if let Some(&kr) = plan.structs.map_set_key.get(&src) {
1304 note_fmt_kind(plan.kinds.get(kr as usize), &mut used);
1305 }
1306 if let Some(&vr) = plan.structs.map_set_value.get(&src) {
1307 note_fmt_kind(plan.kinds.get(vr as usize), &mut used);
1308 }
1309 } else if plan.kinds.get(src as usize) == Some(Kind::SeqSeqInt) {
1310 note(HostFn::PrintText, &mut used);
1313 note(HostFn::FmtSeqI64Into, &mut used);
1314 } else if plan.kinds.get(src as usize) == Some(Kind::SeqEnum) {
1315 note(HostFn::PrintText, &mut used);
1318 if let Some(def) = plan
1319 .structs
1320 .seq_elem_ind_type
1321 .get(&src)
1322 .and_then(|tn| program.enum_types.iter().find(|e| &e.name == tn))
1323 {
1324 for v in &def.variants {
1325 for ft in &v.field_types {
1326 note_fmt_kind(kind::boundary_to_kind(ft), &mut used);
1327 }
1328 }
1329 }
1330 } else if plan.kinds.get(src as usize) == Some(Kind::Struct) {
1331 note(HostFn::PrintText, &mut used);
1334 if let Some(def) = plan
1335 .structs
1336 .struct_name_of
1337 .get(&src)
1338 .and_then(|tn| program.struct_types.iter().find(|s| &s.name == tn))
1339 {
1340 for (_, bt) in &def.fields {
1341 note_fmt_kind(kind::boundary_to_kind(bt), &mut used);
1342 }
1343 }
1344 } else if plan.kinds.get(src as usize) == Some(Kind::SeqStruct) {
1345 note(HostFn::PrintText, &mut used);
1348 if let Some(def) = plan
1349 .structs
1350 .seq_elem_struct_name
1351 .get(&src)
1352 .and_then(|tn| program.struct_types.iter().find(|s| &s.name == tn))
1353 {
1354 for (_, bt) in &def.fields {
1355 note_fmt_kind(kind::boundary_to_kind(bt), &mut used);
1356 }
1357 }
1358 } else if plan.kinds.get(src as usize) == Some(Kind::Rational) {
1359 if linked {
1363 note(HostFn::RationalToText, &mut used);
1364 note(HostFn::PrintText, &mut used);
1365 } else {
1366 note(HostFn::PrintRational, &mut used);
1367 }
1368 } else if plan.kinds.get(src as usize) == Some(Kind::Optional) {
1369 note(HostFn::PrintNothing, &mut used);
1372 let inner = plan.structs.opt_inner.get(&src).copied().unwrap_or(Kind::Int);
1373 if let Some(h) = HostFn::for_show(inner) {
1374 note(h, &mut used);
1375 }
1376 } else if matches!(plan.kinds.get(src as usize), Some(Kind::Word32) | Some(Kind::Word64)) {
1377 note(HostFn::PrintWord, &mut used);
1379 } else if plan.kinds.get(src as usize) == Some(Kind::BigInt) {
1380 note(HostFn::BigintToText, &mut used);
1382 note(HostFn::PrintText, &mut used);
1383 } else if plan.kinds.get(src as usize) == Some(Kind::Complex) {
1384 note(HostFn::ComplexToText, &mut used);
1386 note(HostFn::PrintText, &mut used);
1387 } else if plan.kinds.get(src as usize) == Some(Kind::Modular) {
1388 note(HostFn::ModularToText, &mut used);
1389 note(HostFn::PrintText, &mut used);
1390 } else if plan.kinds.get(src as usize) == Some(Kind::Decimal) {
1391 note(HostFn::DecimalToText, &mut used);
1392 note(HostFn::PrintText, &mut used);
1393 } else if plan.kinds.get(src as usize) == Some(Kind::Money) {
1394 note(HostFn::MoneyToText, &mut used);
1395 note(HostFn::PrintText, &mut used);
1396 } else if plan.kinds.get(src as usize) == Some(Kind::Quantity) {
1397 note(HostFn::QuantityToText, &mut used);
1398 note(HostFn::PrintText, &mut used);
1399 } else if plan.kinds.get(src as usize) == Some(Kind::Uuid) {
1400 note(HostFn::UuidToText, &mut used);
1401 note(HostFn::PrintText, &mut used);
1402 } else if plan.kinds.get(src as usize) == Some(Kind::Dynamic) {
1403 note(HostFn::DynamicToText, &mut used);
1405 note(HostFn::PrintText, &mut used);
1406 } else if let Some(h) = plan.kinds.get(src as usize).and_then(HostFn::for_show) {
1407 note(h, &mut used);
1408 }
1409 }
1410 Op::CallBuiltin { builtin: BuiltinId::Pow, args_start, .. } => {
1411 let bk = plan.kinds.get(args_start as usize);
1412 let ek = plan.kinds.get((args_start + 1) as usize);
1413 if let Some(h) = pow_host_for(bk, ek) {
1414 note(h, &mut used);
1415 }
1416 }
1417 Op::Pow { lhs, rhs, .. } => {
1421 let bk = plan.kinds.get(lhs as usize);
1422 let ek = plan.kinds.get(rhs as usize);
1423 if linked && bk == Some(Kind::Int) && ek == Some(Kind::Int) {
1424 note(HostFn::BigintFromI64, &mut used);
1425 note(HostFn::BigintPow, &mut used);
1426 } else if let Some(h) = pow_host_for(bk, ek) {
1427 note(h, &mut used);
1428 }
1429 }
1430 Op::Mul { dst, lhs, rhs }
1434 | Op::Add { dst, lhs, rhs }
1435 | Op::Sub { dst, lhs, rhs }
1436 | Op::Div { dst, lhs, rhs }
1437 | Op::Mod { dst, lhs, rhs }
1438 if linked && plan.kinds.get(dst as usize) == Some(Kind::BigInt) =>
1439 {
1440 note(
1441 match *op {
1442 Op::Add { .. } => HostFn::BigintAdd,
1443 Op::Sub { .. } => HostFn::BigintSub,
1444 Op::Div { .. } => HostFn::BigintDiv,
1445 Op::Mod { .. } => HostFn::BigintMod,
1446 _ => HostFn::BigintMul,
1447 },
1448 &mut used,
1449 );
1450 if plan.kinds.get(lhs as usize) == Some(Kind::Int) || plan.kinds.get(rhs as usize) == Some(Kind::Int) {
1451 note(HostFn::BigintFromI64, &mut used);
1452 }
1453 }
1454 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs }
1457 if linked && plan.kinds.get(dst as usize) == Some(Kind::Complex) =>
1458 {
1459 note(
1460 match *op {
1461 Op::Add { .. } => HostFn::ComplexAdd,
1462 Op::Sub { .. } => HostFn::ComplexSub,
1463 _ => HostFn::ComplexMul,
1464 },
1465 &mut used,
1466 );
1467 if plan.kinds.get(lhs as usize) == Some(Kind::Int) || plan.kinds.get(rhs as usize) == Some(Kind::Int) {
1468 note(HostFn::ComplexFromI64, &mut used);
1469 }
1470 }
1471 Op::CallBuiltin { builtin: BuiltinId::Complex, .. } if linked => note(HostFn::ComplexFromI64, &mut used),
1473 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs }
1474 if linked && plan.kinds.get(dst as usize) == Some(Kind::Modular) =>
1475 {
1476 note(match *op { Op::Add { .. } => HostFn::ModularAdd, Op::Sub { .. } => HostFn::ModularSub, _ => HostFn::ModularMul }, &mut used);
1477 if plan.kinds.get(lhs as usize) == Some(Kind::Int) || plan.kinds.get(rhs as usize) == Some(Kind::Int) { note(HostFn::ModularFromI64, &mut used); }
1478 }
1479 Op::CallBuiltin { builtin: BuiltinId::Modular, .. } if linked => note(HostFn::ModularFromI64, &mut used),
1480 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs }
1481 if linked && plan.kinds.get(dst as usize) == Some(Kind::Decimal) =>
1482 {
1483 note(match *op { Op::Add { .. } => HostFn::DecimalAdd, Op::Sub { .. } => HostFn::DecimalSub, _ => HostFn::DecimalMul }, &mut used);
1484 if plan.kinds.get(lhs as usize) == Some(Kind::Int) || plan.kinds.get(rhs as usize) == Some(Kind::Int) { note(HostFn::DecimalFromI64, &mut used); }
1485 }
1486 Op::CallBuiltin { builtin: BuiltinId::Decimal, .. } if linked => note(HostFn::DecimalFromText, &mut used),
1487 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs }
1488 if linked && plan.kinds.get(dst as usize) == Some(Kind::Money) =>
1489 {
1490 let _ = (lhs, rhs);
1491 note(match *op { Op::Add { .. } => HostFn::MoneyAdd, _ => HostFn::MoneySub }, &mut used);
1492 }
1493 Op::CallBuiltin { builtin: BuiltinId::Money, .. } if linked => { note(HostFn::MoneyFromDecimal, &mut used); note(HostFn::MoneyFromI64, &mut used); }
1494 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs } | Op::Div { dst, lhs, rhs }
1495 if linked && plan.kinds.get(dst as usize) == Some(Kind::Quantity) =>
1496 {
1497 let _ = (lhs, rhs);
1498 note(match *op {
1499 Op::Add { .. } => HostFn::QuantityAdd,
1500 Op::Sub { .. } => HostFn::QuantitySub,
1501 Op::Mul { .. } => HostFn::QuantityMul,
1502 _ => HostFn::QuantityDiv,
1503 }, &mut used);
1504 }
1505 Op::CallBuiltin { builtin: BuiltinId::Quantity, .. } if linked => note(HostFn::QuantityOfI64, &mut used),
1506 Op::CallBuiltin { builtin: BuiltinId::Convert, .. } if linked => note(HostFn::QuantityConvert, &mut used),
1507 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs } | Op::Div { dst, lhs, rhs }
1511 if linked && plan.kinds.get(dst as usize) == Some(Kind::Rational) =>
1512 {
1513 note(match *op {
1514 Op::Add { .. } => HostFn::RationalAdd,
1515 Op::Sub { .. } => HostFn::RationalSub,
1516 Op::Mul { .. } => HostFn::RationalMul,
1517 _ => HostFn::RationalDiv,
1518 }, &mut used);
1519 for r in [lhs, rhs] {
1520 match plan.kinds.get(r as usize) {
1521 Some(Kind::Int) => note(HostFn::RationalFromI64, &mut used),
1522 Some(Kind::BigInt) => note(HostFn::RationalFromBigint, &mut used),
1523 _ => {}
1524 }
1525 }
1526 }
1527 Op::ExactDiv { lhs, rhs, .. } if linked => {
1530 note(HostFn::RationalDiv, &mut used);
1531 for r in [lhs, rhs] {
1532 match plan.kinds.get(r as usize) {
1533 Some(Kind::Int) => note(HostFn::RationalFromI64, &mut used),
1534 Some(Kind::BigInt) => note(HostFn::RationalFromBigint, &mut used),
1535 _ => {}
1536 }
1537 }
1538 }
1539 Op::CallBuiltin { builtin: b @ (BuiltinId::Floor | BuiltinId::Ceil | BuiltinId::Round | BuiltinId::Abs), args_start, .. }
1541 if linked && plan.kinds.get(args_start as usize) == Some(Kind::Rational) =>
1542 {
1543 note(match b {
1544 BuiltinId::Floor => HostFn::RationalFloor,
1545 BuiltinId::Ceil => HostFn::RationalCeil,
1546 BuiltinId::Round => HostFn::RationalRound,
1547 _ => HostFn::RationalAbs,
1548 }, &mut used);
1549 }
1550 Op::CallBuiltin { builtin: b @ (BuiltinId::Uuid | BuiltinId::UuidNil | BuiltinId::UuidMax | BuiltinId::UuidDns | BuiltinId::UuidUrl | BuiltinId::UuidOid | BuiltinId::UuidX500 | BuiltinId::UuidVersion), .. } if linked => {
1552 note(match b {
1553 BuiltinId::Uuid => HostFn::UuidParse,
1554 BuiltinId::UuidNil => HostFn::UuidNil,
1555 BuiltinId::UuidMax => HostFn::UuidMax,
1556 BuiltinId::UuidDns => HostFn::UuidDns,
1557 BuiltinId::UuidUrl => HostFn::UuidUrl,
1558 BuiltinId::UuidOid => HostFn::UuidOid,
1559 BuiltinId::UuidX500 => HostFn::UuidX500,
1560 _ => HostFn::UuidVersion,
1561 }, &mut used);
1562 }
1563 Op::CallBuiltin { builtin: BuiltinId::UuidFromBytes, .. } if linked => note(HostFn::UuidFromPtr, &mut used),
1565 Op::CallBuiltin { builtin: b @ (BuiltinId::Sha1Rnds4 | BuiltinId::Sha1Msg1 | BuiltinId::Sha1Msg2 | BuiltinId::Sha1Nexte), .. } if linked => {
1567 note(match b {
1568 BuiltinId::Sha1Rnds4 => HostFn::Sha1Rnds4,
1569 BuiltinId::Sha1Msg1 => HostFn::Sha1Msg1,
1570 BuiltinId::Sha1Msg2 => HostFn::Sha1Msg2,
1571 _ => HostFn::Sha1Nexte,
1572 }, &mut used);
1573 }
1574 Op::Add { lhs, .. } if linked && plan.kinds.get(lhs as usize) == Some(Kind::Lanes) => note(HostFn::Lanes4Add, &mut used),
1576 Op::AddAssign { dst, .. } if linked && plan.kinds.get(dst as usize) == Some(Kind::Lanes) => note(HostFn::Lanes4Add, &mut used),
1577 Op::BitXor { lhs, .. } if linked && plan.kinds.get(lhs as usize) == Some(Kind::Lanes) => note(HostFn::Lanes4Xor, &mut used),
1579 Op::Add { lhs, rhs, .. } | Op::Sub { lhs, rhs, .. }
1581 if linked && (plan.kinds.get(lhs as usize) == Some(Kind::Span) || plan.kinds.get(rhs as usize) == Some(Kind::Span)) =>
1582 {
1583 let base = if plan.kinds.get(lhs as usize) == Some(Kind::Span) { rhs } else { lhs };
1584 note(if plan.kinds.get(base as usize) == Some(Kind::Date) { HostFn::DateAddSpan } else { HostFn::MomentAddSpan }, &mut used);
1585 }
1586 Op::Eq { lhs, rhs, .. } | Op::NotEq { lhs, rhs, .. }
1588 if linked && plan.kinds.get(lhs as usize) == Some(Kind::Uuid) && plan.kinds.get(rhs as usize) == Some(Kind::Uuid) =>
1589 {
1590 note(HostFn::UuidEq, &mut used);
1591 }
1592 Op::LoadToday { .. } => note(HostFn::Today, &mut used),
1593 Op::LoadNow { .. } => note(HostFn::Now, &mut used),
1594 Op::Args { .. } => note(HostFn::Args, &mut used),
1595 Op::CallBuiltin { builtin: BuiltinId::ParseInt, .. } => note(HostFn::ParseInt, &mut used),
1596 Op::CallBuiltin { builtin: BuiltinId::ParseFloat, .. } => note(HostFn::ParseFloat, &mut used),
1597 Op::CallBuiltin { builtin: BuiltinId::ParseTimestamp, .. } => note(HostFn::ParseTimestamp, &mut used),
1598 Op::CallBuiltin { builtin: BuiltinId::WriteWireResidual, .. } => note(HostFn::WriteWireResidual, &mut used),
1600 Op::CallBuiltin { builtin: BuiltinId::FormatTimestamp, .. } if linked => note(HostFn::FormatTimestampRt, &mut used),
1603 Op::CallBuiltin { builtin: BuiltinId::MonthsBetween, .. } if linked => note(HostFn::MonthsBetweenRt, &mut used),
1604 Op::CallBuiltin { builtin: BuiltinId::YearsBetween, .. } if linked => note(HostFn::YearsBetweenRt, &mut used),
1605 Op::CallBuiltin { builtin: BuiltinId::InZone, .. } if linked => note(HostFn::InZoneRt, &mut used),
1607 Op::CallBuiltin { builtin: BuiltinId::LocalInstant, .. } if linked => note(HostFn::LocalInstantRt, &mut used),
1608 Op::CallBuiltin { builtin: b, .. } if linked && lanes_v_host_fn(b).is_some() => {
1611 note(lanes_v_host_fn(b).unwrap(), &mut used)
1612 }
1613 Op::CallBuiltin { builtin: BuiltinId::ToCurrency, .. } if linked => note(HostFn::MoneyToCurrency, &mut used),
1618 Op::CallBuiltin { builtin: BuiltinId::SetRate, args_start, .. } if linked => {
1619 note(HostFn::MoneySetRate, &mut used);
1620 match plan.kinds.get((args_start + 1) as usize) {
1621 Some(Kind::Int) => note(HostFn::RationalFromI64, &mut used),
1622 Some(Kind::Decimal) => note(HostFn::DecimalToRational, &mut used),
1623 _ => {}
1624 }
1625 }
1626 Op::CallBuiltin { builtin: BuiltinId::SetRates, .. } if linked => {
1627 note(HostFn::MoneySetRatesInt, &mut used);
1628 note(HostFn::MoneySetRatesRational, &mut used);
1629 note(HostFn::MoneySetRatesDecimal, &mut used);
1630 }
1631 Op::CallBuiltin { builtin: BuiltinId::WireBytes, args_start, .. } if linked => {
1633 if let Some(h) = wire_bytes_host_fn(plan.kinds.get(args_start as usize)) {
1634 note(h, &mut used);
1635 }
1636 }
1637 Op::CallBuiltin { builtin: BuiltinId::ReadWireProgram, .. } if linked => {
1640 note(HostFn::ReadWireFrame, &mut used);
1641 note(HostFn::ReadWireProgramRt, &mut used);
1642 note(HostFn::RtAlloc, &mut used);
1646 }
1647 Op::CallBuiltin { builtin: BuiltinId::RunAccepted, .. } if linked => note(HostFn::RunAccepted, &mut used),
1648 Op::CallBuiltin {
1649 builtin:
1650 BuiltinId::YearOf | BuiltinId::MonthOf | BuiltinId::DayOf | BuiltinId::WeekdayOf | BuiltinId::HourOf
1651 | BuiltinId::MinuteOf | BuiltinId::SecondOf | BuiltinId::WeekOf | BuiltinId::QuarterOf,
1652 args_start,
1653 ..
1654 } => {
1655 if plan.kinds.get(args_start as usize) == Some(Kind::Date) {
1658 note(HostFn::TemporalComponentDate, &mut used);
1659 } else {
1660 note(HostFn::TemporalComponent, &mut used);
1661 }
1662 }
1663 Op::CallBuiltin { builtin: BuiltinId::Format, args_start, arg_count, .. } if arg_count > 0 => {
1665 note_operand_fmt(plan, args_start, &mut used);
1666 }
1667 Op::Concat { lhs, rhs, .. } => {
1670 note_operand_fmt(plan, lhs, &mut used);
1671 note_operand_fmt(plan, rhs, &mut used);
1672 }
1673 Op::Add { dst, lhs, rhs } if plan.kinds.get(dst as usize) == Some(Kind::Text) => {
1674 note_operand_fmt(plan, lhs, &mut used);
1675 note_operand_fmt(plan, rhs, &mut used);
1676 }
1677 Op::AddAssign { dst, src } if plan.kinds.get(dst as usize) == Some(Kind::Text) => {
1678 note_operand_fmt(plan, dst, &mut used);
1679 note_operand_fmt(plan, src, &mut used);
1680 }
1681 Op::FormatValue { src, spec, .. } => {
1686 let spec_s = match spec {
1687 u32::MAX => None,
1688 idx => match program.constants.get(idx as usize) {
1689 Some(Constant::Text(s)) => Some(s.as_str()),
1690 _ => None,
1691 },
1692 };
1693 if matches!(spec_s, Some(s) if s.starts_with('.')) {
1694 note(HostFn::FmtF64PrecInto, &mut used);
1695 } else {
1696 note(HostFn::FmtAlignInto, &mut used);
1697 note_fmt_kind(plan.kinds.get(src as usize), &mut used);
1698 }
1699 }
1700 _ => {}
1701 }
1702 }
1703 }
1704 if linked && (uses_heap || uses_iter) {
1707 note(HostFn::RtAlloc, &mut used);
1708 }
1709 let imports: Vec<HostFn> = HOST_FNS.iter().copied().filter(|h| used.contains(h)).collect();
1711 let host_index = |h: HostFn| -> Option<u32> { imports.iter().position(|x| *x == h).map(|i| i as u32) };
1712 let num_imports = imports.len() as u32;
1713 let main_index = num_imports;
1714 let fn_base = num_imports + 1; let mut types = TypeTable::default();
1718 let host_type: Vec<u32> = imports.iter().map(|h| types.intern(h.params(), h.results())).collect();
1719 let main_type = types.intern(vec![], result_valtypes(main.result));
1720 let mut fn_types: Vec<u32> = Vec::with_capacity(fns.len());
1721 let mut fn_param_valtypes: Vec<Vec<u8>> = Vec::with_capacity(fns.len());
1722 for p in &fns {
1723 let params: Vec<u8> = (0..p.num_params).map(|r| p.kinds.valtype(r as usize)).collect();
1724 fn_types.push(types.intern(params.clone(), result_valtypes(p.result)));
1725 fn_param_valtypes.push(params);
1726 }
1727
1728 let heap_global = num_globals as u32; let iter_global = heap_global + u32::from(uses_heap); let fn_results: Vec<Option<Kind>> = fns.iter().map(|p| p.result).collect();
1732 let rt_alloc = if linked && uses_heap { host_index(HostFn::RtAlloc) } else { None };
1736 let ctx = Ctx { constants: &program.constants, host_index: &host_index, fn_base, heap_global, iter_global, fn_type: &fn_types, fn_param_valtypes: &fn_param_valtypes, fn_results: &fn_results, functions: &program.functions, capture_kinds: &capture_kinds, enum_types: &program.enum_types, struct_types: &program.struct_types, policies, interner, linked, rt_alloc };
1737 let mut main_body = emit_body(&main, &ctx)?;
1738 if linked && uses_iter {
1745 const SLAB: i32 = (HEAP_PAGES * 65536) as i32;
1746 let rt_alloc = host_index(HostFn::RtAlloc).ok_or(WasmLowerError::Unsupported("logos_rt_alloc not imported"))?;
1747 let locals_len = encode_locals(&main).len();
1748 let mut prologue = Vec::new();
1749 i32_const(&mut prologue, SLAB);
1750 prologue.push(0x10); leb_u32(&mut prologue, rt_alloc);
1752 i32_const(&mut prologue, SLAB);
1753 prologue.push(0x6A); prologue.push(0x24); leb_u32(&mut prologue, iter_global);
1756 main_body.splice(locals_len..locals_len, prologue);
1757 }
1758 let mut fn_bodies = Vec::with_capacity(fns.len());
1759 for p in &fns {
1760 fn_bodies.push(emit_body(p, &ctx)?);
1761 }
1762
1763 let mut module = vec![0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00];
1765 section(&mut module, 1, &types.encode());
1766
1767 let mut imp = Vec::new();
1772 leb_u32(&mut imp, imports.len() as u32 + u32::from(linked));
1773 for (i, h) in imports.iter().enumerate() {
1774 encode_name(&mut imp, "env");
1775 encode_name(&mut imp, h.field());
1776 imp.push(0x00); leb_u32(&mut imp, host_type[i]);
1778 }
1779 if linked {
1780 encode_name(&mut imp, "env");
1781 encode_name(&mut imp, "__linear_memory");
1782 imp.push(0x02); imp.push(0x00); leb_u32(&mut imp, 0); }
1786 section(&mut module, 2, &imp);
1787
1788 let mut func = Vec::new();
1790 leb_u32(&mut func, 1 + fns.len() as u32);
1791 leb_u32(&mut func, main_type);
1792 for t in &fn_types {
1793 leb_u32(&mut func, *t);
1794 }
1795 section(&mut module, 3, &func);
1796
1797 if has_closures && !linked {
1801 let mut table = Vec::new();
1802 leb_u32(&mut table, 1); table.push(0x70); table.push(0x00); leb_u32(&mut table, fns.len() as u32); section(&mut module, 4, &table);
1807 }
1808
1809 if uses_heap && !linked {
1817 let mut mem = Vec::new();
1818 leb_u32(&mut mem, 1); mem.push(0x00); leb_u32(&mut mem, HEAP_PAGES); section(&mut module, 5, &mem);
1822 }
1823
1824 if num_globals > 0 || uses_heap || uses_iter {
1827 let mut glob = Vec::new();
1828 leb_u32(&mut glob, num_globals as u32 + u32::from(uses_heap) + u32::from(uses_iter));
1829 for gk in &global_kinds {
1830 let vt = gk.map(Kind::wasm_valtype).unwrap_or(I64);
1831 glob.push(vt);
1832 glob.push(0x01); if vt == F64 {
1837 glob.push(0x44); glob.extend_from_slice(&0f64.to_le_bytes());
1839 } else if vt == I32 {
1840 i32_const(&mut glob, 0);
1841 } else {
1842 glob.push(0x42); leb_i64(&mut glob, 0);
1844 }
1845 glob.push(0x0B); }
1847 if uses_heap {
1848 glob.push(I32);
1852 glob.push(0x01); i32_const(&mut glob, if linked { 0 } else { 16 });
1854 glob.push(0x0B);
1855 }
1856 if uses_iter {
1857 glob.push(I32);
1861 glob.push(0x01); i32_const(&mut glob, if linked { 0 } else { (HEAP_PAGES * 65536) as i32 });
1863 glob.push(0x0B);
1864 }
1865 section(&mut module, 6, &glob);
1866 }
1867
1868 let export_memory = uses_heap && !linked;
1871 let mut export = Vec::new();
1872 leb_u32(&mut export, 1 + u32::from(export_memory));
1873 encode_name(&mut export, "main");
1874 export.push(0x00); leb_u32(&mut export, main_index);
1876 if export_memory {
1877 encode_name(&mut export, "memory");
1878 export.push(0x02); leb_u32(&mut export, 0); }
1881 section(&mut module, 7, &export);
1882
1883 if has_closures && !linked {
1887 let mut elem = Vec::new();
1888 leb_u32(&mut elem, 1); leb_u32(&mut elem, 0); elem.push(0x41); leb_i32(&mut elem, 0);
1892 elem.push(0x0B); leb_u32(&mut elem, fns.len() as u32);
1894 for i in 0..fns.len() as u32 {
1895 leb_u32(&mut elem, fn_base + i);
1896 }
1897 section(&mut module, 9, &elem);
1898 }
1899
1900 let mut code = Vec::new();
1902 leb_u32(&mut code, 1 + fn_bodies.len() as u32);
1903 for entry in std::iter::once(&main_body).chain(fn_bodies.iter()) {
1904 leb_u32(&mut code, entry.len() as u32);
1905 code.extend_from_slice(entry);
1906 }
1907 section(&mut module, 10, &code);
1908
1909 Ok(module)
1910}
1911
1912fn declared_result(program: &CompiledProgram, fi: usize) -> Option<Kind> {
1915 program.functions.get(fi).and_then(|f| f.ret_kind).map(Kind::from_slot)
1916}
1917
1918struct CodeLayout {
1924 main_end: usize,
1926 func_region: Vec<(usize, usize)>,
1928 closure_ranges: Vec<(usize, usize)>,
1930 is_closure: Vec<bool>,
1932}
1933
1934fn code_layout(program: &CompiledProgram) -> R<CodeLayout> {
1938 let code_len = program.code.len();
1939 let nf = program.functions.len();
1940 let mut is_closure = vec![false; nf];
1941 for op in &program.code {
1942 if let Op::MakeClosure { func, .. } = *op {
1943 if (func as usize) < nf {
1944 is_closure[func as usize] = true;
1945 }
1946 }
1947 }
1948 let mut func_region = vec![(0usize, 0usize); nf];
1949 let mut closure_ranges = Vec::new();
1950 for (fi, f) in program.functions.iter().enumerate() {
1951 if is_closure[fi] {
1952 let entry = f.entry_pc;
1953 let end = match (entry >= 1).then(|| program.code.get(entry - 1)).flatten() {
1954 Some(Op::Jump { target }) if *target > entry && *target <= code_len => *target,
1955 _ => return Err(WasmLowerError::Unsupported("closure body without a jump-over")),
1956 };
1957 func_region[fi] = (entry, end);
1958 closure_ranges.push((entry, end));
1959 }
1960 }
1961 let mut regular: Vec<usize> = (0..nf).filter(|&fi| !is_closure[fi]).collect();
1964 regular.sort_by_key(|&fi| program.functions[fi].entry_pc);
1965 let main_end = regular.first().map(|&fi| program.functions[fi].entry_pc).unwrap_or(code_len);
1966 for w in 0..regular.len() {
1967 let fi = regular[w];
1968 let start = program.functions[fi].entry_pc;
1969 let end = regular.get(w + 1).map(|&g| program.functions[g].entry_pc).unwrap_or(code_len);
1970 func_region[fi] = (start, end);
1971 }
1972 Ok(CodeLayout { main_end, func_region, closure_ranges, is_closure })
1973}
1974
1975fn child_holes(closure_ranges: &[(usize, usize)], start: usize, end: usize) -> Vec<(usize, usize)> {
1978 closure_ranges
1979 .iter()
1980 .copied()
1981 .filter(|&(e, t)| {
1982 e >= start
1983 && t <= end
1984 && (e, t) != (start, end)
1985 && !closure_ranges
1986 .iter()
1987 .any(|&(e2, t2)| (e2, t2) != (e, t) && e2 <= e && t <= t2 && e2 >= start && t2 <= end)
1988 })
1989 .collect()
1990}
1991
1992fn extract_region(code: &[Op], start: usize, end: usize, holes: &[(usize, usize)]) -> R<Vec<Op>> {
1996 let in_hole = |pc: usize| holes.iter().any(|&(s, e)| pc >= s && pc < e);
1997 let mut new_index = vec![usize::MAX; end];
1998 let mut kept = Vec::new();
1999 for pc in start..end {
2000 if !in_hole(pc) {
2001 new_index[pc] = kept.len();
2002 kept.push(pc);
2003 }
2004 }
2005 let rebase = |t: usize| -> R<usize> {
2006 if t >= start && t < end && new_index[t] != usize::MAX {
2007 Ok(new_index[t])
2008 } else {
2009 Err(WasmLowerError::Unsupported("jump target escapes the function"))
2010 }
2011 };
2012 let mut ops = Vec::with_capacity(kept.len());
2013 for &pc in &kept {
2014 ops.push(match code[pc] {
2015 Op::Jump { target } => Op::Jump { target: rebase(target)? },
2016 Op::JumpIfFalse { cond, target } => Op::JumpIfFalse { cond, target: rebase(target)? },
2017 Op::JumpIfTrue { cond, target } => Op::JumpIfTrue { cond, target: rebase(target)? },
2018 Op::IterNext { dst, exit } => Op::IterNext { dst, exit: rebase(exit)? },
2019 other => other,
2020 });
2021 }
2022 Ok(ops)
2023}
2024
2025fn plan_main(
2028 program: &CompiledProgram,
2029 layout: &CodeLayout,
2030 ret_of: &dyn Fn(usize) -> Option<Kind>,
2031 global_of: &dyn Fn(u16) -> Option<Kind>,
2032 closure_ret: &dyn Fn(usize) -> Option<Kind>,
2033 ret_layout: &dyn Fn(u16) -> Option<FieldLayout>,
2034 fn_return_closure: &dyn Fn(u16) -> Option<u16>,
2035 linked: bool,
2036) -> R<Plan> {
2037 let holes = child_holes(&layout.closure_ranges, 0, layout.main_end);
2038 let ops = extract_region(&program.code, 0, layout.main_end, &holes)?;
2039 let num_regs = program.register_count as u32;
2040 let (ops, num_regs) = regsplit::split_registers(&ops, num_regs, 0, &program.functions);
2043 let fn_return_types = fn_return_types(program);
2044 let (kinds, _pc_reach, reachable_blocks) =
2045 infer_with_reachability(&ops, &program.constants, &program.struct_types, &program.enum_types, &fn_return_types, num_regs as usize, &[], ret_of, global_of, closure_ret, ret_layout, fn_return_closure, &[], &[], linked, &program.functions)?;
2046 let structs = kind::struct_layout(&ops, &program.constants, &program.struct_types, &program.enum_types, &fn_return_types, ret_layout, fn_return_closure, &[], &[]);
2047 let cow_inserts = cow_struct_inserts(&ops, num_regs, &program.functions);
2048 let reg_shape = complete_reg_shape(&structs, &kinds, program);
2049 Ok(Plan { ops, kinds, num_params: 0, num_regs, result: None, reachable_blocks, structs, cow_inserts, reg_shape, return_closure: None, stub: false })
2050}
2051
2052fn fn_return_types(program: &CompiledProgram) -> Vec<Option<BoundaryType>> {
2055 program.functions.iter().map(|f| f.return_type.clone()).collect()
2056}
2057
2058fn complete_reg_shape(
2063 structs: &kind::StructLayout,
2064 kinds: &KindTable,
2065 program: &CompiledProgram,
2066) -> std::collections::HashMap<u16, ParamShape> {
2067 let mut out = structs.reg_shape.clone();
2068 for (reg, value_reg) in &structs.map_set_value {
2069 if !out.contains_key(reg) {
2070 if let Some(vk) = kinds.get(*value_reg as usize) {
2071 out.insert(*reg, ParamShape::Map(vk));
2072 }
2073 }
2074 }
2075 for (reg, elems) in &structs.tuple_layouts {
2076 if !out.contains_key(reg) {
2077 if let Some(ks) = elems.iter().map(|e| kinds.get(*e as usize)).collect::<Option<Vec<Kind>>>() {
2078 if !ks.windows(2).all(|w| w[0] == w[1]) {
2080 out.insert(*reg, ParamShape::Tuple(ks));
2081 }
2082 }
2083 }
2084 }
2085 for (reg, name) in &structs.ind_type_of {
2086 if !out.contains_key(reg) {
2087 if let Some(variants) = kind::resolve_enum_variants(name, &program.enum_types) {
2088 out.insert(*reg, ParamShape::Enum(variants));
2089 }
2090 }
2091 }
2092 out
2093}
2094
2095fn extract_capture_kinds(
2102 program: &CompiledProgram,
2103 main_p1: &Plan,
2104 region_plans: &[Option<Plan>],
2105 global_of: &dyn Fn(u16) -> Option<Kind>,
2106 global_closure_of: &dyn Fn(u16) -> Option<u16>,
2107) -> (Vec<Vec<Option<Kind>>>, Vec<Vec<Option<ParamShape>>>, Vec<Vec<Option<u16>>>) {
2108 let mut kinds: Vec<Vec<Option<Kind>>> = program
2109 .functions
2110 .iter()
2111 .map(|f| f.captures.iter().map(|(_, g)| g.and_then(|gi| global_of(gi))).collect())
2112 .collect();
2113 let mut shapes: Vec<Vec<Option<ParamShape>>> =
2114 program.functions.iter().map(|f| vec![None; f.captures.len()]).collect();
2115 let mut closures: Vec<Vec<Option<u16>>> = program
2120 .functions
2121 .iter()
2122 .map(|f| f.captures.iter().map(|(_, g)| g.and_then(|gi| global_closure_of(gi))).collect())
2123 .collect();
2124 for plan in std::iter::once(main_p1).chain(region_plans.iter().flatten()) {
2125 for op in &plan.ops {
2126 if let Op::MakeClosure { func, locals_start, .. } = *op {
2127 let fi = func as usize;
2128 let mut local_k: u16 = 0;
2129 for (k, (_sym, global)) in program.functions[fi].captures.iter().enumerate() {
2130 if global.is_none() {
2131 let reg = locals_start + local_k;
2132 local_k += 1;
2133 if let Some(kind) = plan.kinds.get(reg as usize) {
2134 kinds[fi][k] = Some(kind);
2135 }
2136 if let Some(shape) = plan.reg_shape.get(®) {
2142 shapes[fi][k] = Some(shape.clone());
2143 }
2144 if let Some(&c) = plan.structs.closure_of.get(®) {
2147 closures[fi][k] = Some(c);
2148 }
2149 }
2150 }
2151 }
2152 }
2153 }
2154 (kinds, shapes, closures)
2155}
2156
2157#[allow(clippy::too_many_arguments)]
2163fn plan_function(
2164 program: &CompiledProgram,
2165 fi: usize,
2166 layout: &CodeLayout,
2167 ret_of: &dyn Fn(usize) -> Option<Kind>,
2168 global_of: &dyn Fn(u16) -> Option<Kind>,
2169 closure_ret: &dyn Fn(usize) -> Option<Kind>,
2170 ret_layout: &dyn Fn(u16) -> Option<FieldLayout>,
2171 fn_return_closure: &dyn Fn(u16) -> Option<u16>,
2172 param_origin: &dyn Fn(usize, usize) -> Option<u16>,
2173 capture_kinds: &[Vec<Option<Kind>>],
2174 capture_shapes: &[Vec<Option<ParamShape>>],
2175 capture_closures: &[Vec<Option<u16>>],
2176 strict: bool,
2177 linked: bool,
2178) -> R<Plan> {
2179 let f = &program.functions[fi];
2180 let (start, end) = layout.func_region[fi];
2181 if start >= end || end > program.code.len() {
2182 return Err(WasmLowerError::Unsupported("malformed function bounds"));
2183 }
2184 let holes = child_holes(&layout.closure_ranges, start, end);
2185 let ops = extract_region(&program.code, start, end, &holes)?;
2186
2187 let cap_n = f.captures.len() as u32;
2195 let num_params = f.param_count as u32 + 2 * cap_n;
2196 let mut seeds: Vec<Option<Kind>> = if cap_n > 0 {
2197 let mut s = vec![Some(Kind::Int); num_params as usize];
2198 for i in 0..f.param_count as usize {
2202 if let Some(Some(bt)) = f.param_types.get(i) {
2203 if let Some(k) = kind::boundary_to_kind(bt) {
2204 s[i] = Some(k);
2205 }
2206 }
2207 }
2208 let caps = capture_kinds.get(fi).map(|v| v.as_slice()).unwrap_or(&[]);
2209 for k in 0..f.captures.len() {
2210 if let Some(kind) = caps.get(k).copied().flatten() {
2211 s[f.param_count as usize + k] = Some(kind);
2212 }
2213 }
2214 s
2215 } else {
2216 kind::function_param_seeds(f)
2217 };
2218 let num_regs = f.register_count as u32;
2219 let (ops, num_regs) = regsplit::split_registers(&ops, num_regs, num_params, &program.functions);
2223 let mut param_layouts = if cap_n == 0 { param_seeds(f, program) } else { Vec::new() };
2227 let local_shapes = capture_shapes.get(fi).map(|v| v.as_slice()).unwrap_or(&[]);
2232 for (k, (_sym, global)) in f.captures.iter().enumerate() {
2233 let shape = if let Some(gidx) = global {
2234 program
2236 .global_types
2237 .get(*gidx as usize)
2238 .and_then(|t| t.as_ref())
2239 .and_then(|bt| boundary_to_param_shape(bt, program))
2240 } else {
2241 local_shapes.get(k).cloned().flatten()
2243 };
2244 if let Some(shape) = shape {
2245 param_layouts.push((f.param_count + k as u16, shape));
2246 }
2247 }
2248 let mut param_closures: Vec<(u16, u16)> =
2253 (0..f.param_count).filter_map(|i| param_origin(fi, i as usize).map(|c| (i, c))).collect();
2254 if let Some(caps) = capture_closures.get(fi) {
2257 for (k, c) in caps.iter().enumerate() {
2258 if let Some(c) = c {
2259 param_closures.push((f.param_count + k as u16, *c));
2260 }
2261 }
2262 }
2263 for &(reg, _) in ¶m_closures {
2267 if let Some(slot) = seeds.get_mut(reg as usize) {
2268 *slot = Some(Kind::Closure);
2269 }
2270 }
2271 let fn_rt = fn_return_types(program);
2272 let (kinds, pc_reach, reachable_blocks) =
2273 infer_with_reachability(&ops, &program.constants, &program.struct_types, &program.enum_types, &fn_rt, num_regs as usize, &seeds, ret_of, global_of, closure_ret, ret_layout, fn_return_closure, ¶m_layouts, ¶m_closures, linked, &program.functions)?;
2274
2275 let inferred = infer_result(&ops, &kinds, &pc_reach, strict)?;
2279 let result = match (f.ret_kind.map(Kind::from_slot), inferred) {
2280 (Some(d), Some(i)) if d.wasm_valtype() != i.wasm_valtype() => Some(i),
2281 (Some(d), _) => Some(d),
2282 (None, i) => i,
2283 };
2284 let structs = kind::struct_layout(&ops, &program.constants, &program.struct_types, &program.enum_types, &fn_rt, ret_layout, fn_return_closure, ¶m_layouts, ¶m_closures);
2285 let cow_inserts = cow_struct_inserts(&ops, num_regs, &program.functions);
2286 let reg_shape = complete_reg_shape(&structs, &kinds, program);
2287 let return_closure = infer_return_closure(&ops, &kinds, &structs, &pc_reach);
2288 Ok(Plan { ops, kinds, num_params, num_regs, result, reachable_blocks, structs, cow_inserts, reg_shape, return_closure, stub: false })
2289}
2290
2291fn infer_return_closure(ops: &[Op], kinds: &KindTable, structs: &kind::StructLayout, pc_reach: &[bool]) -> Option<u16> {
2297 let mut found: Option<u16> = None;
2298 for (pc, op) in ops.iter().enumerate() {
2299 if !pc_reach.get(pc).copied().unwrap_or(true) {
2300 continue;
2301 }
2302 if let Op::Return { src } = *op {
2303 if kinds.get(src as usize) != Some(Kind::Closure) {
2304 continue;
2305 }
2306 match structs.closure_of.get(&src) {
2307 Some(&c) if found.is_none() => found = Some(c),
2308 Some(&c) if found == Some(c) => {}
2309 _ => return None,
2310 }
2311 }
2312 }
2313 found
2314}
2315
2316fn compute_param_origins(program: &CompiledProgram, plans: &[&Plan]) -> Vec<Vec<Option<u16>>> {
2325 use std::collections::{HashMap, HashSet};
2326 let mut obs: HashMap<(usize, usize), HashSet<Option<u16>>> = HashMap::new();
2327 for plan in plans {
2328 for (pc, op) in plan.ops.iter().enumerate() {
2329 let (func, args_start, arg_count) = match *op {
2330 Op::Call { func, args_start, arg_count, .. } => (Some(func as usize), args_start, arg_count),
2331 Op::CallValue { args_start, arg_count, .. } => {
2332 (plan.structs.callee_func.get(pc).copied().flatten().map(|f| f as usize), args_start, arg_count)
2333 }
2334 _ => continue,
2335 };
2336 let Some(func) = func else { continue };
2337 for i in 0..arg_count {
2338 let origin = plan.structs.closure_of.get(&(args_start + i)).copied();
2339 obs.entry((func, i as usize)).or_default().insert(origin);
2340 }
2341 }
2342 }
2343 (0..program.functions.len())
2344 .map(|fi| {
2345 let pc = program.functions[fi].param_count as usize;
2346 (0..pc)
2347 .map(|i| match obs.get(&(fi, i)) {
2348 Some(set) if set.len() == 1 => set.iter().next().copied().flatten(),
2349 _ => None,
2350 })
2351 .collect()
2352 })
2353 .collect()
2354}
2355
2356fn boundary_to_param_shape(bt: &BoundaryType, program: &CompiledProgram) -> Option<ParamShape> {
2361 match bt {
2362 BoundaryType::Struct(name) => kind::resolve_named_layout(name, &program.struct_types, &program.constants)
2363 .map(ParamShape::Struct),
2364 BoundaryType::Map(_, value) => kind::boundary_to_kind(value).map(ParamShape::Map),
2365 BoundaryType::Enum(name) => kind::resolve_enum_variants(name, &program.enum_types).map(ParamShape::Enum),
2366 BoundaryType::Tuple(elems) => {
2367 elems.iter().map(kind::boundary_to_kind).collect::<Option<Vec<Kind>>>().map(ParamShape::Tuple)
2368 }
2369 _ => None,
2370 }
2371}
2372
2373fn param_seeds(f: &CompiledFunction, program: &CompiledProgram) -> Vec<(u16, ParamShape)> {
2378 let mut out = Vec::new();
2379 for (i, pt) in f.param_types.iter().enumerate() {
2380 if let Some(shape) = pt.as_ref().and_then(|bt| boundary_to_param_shape(bt, program)) {
2381 out.push((i as u16, shape));
2382 }
2383 }
2384 out
2385}
2386
2387fn infer_result(ops: &[Op], kinds: &KindTable, pc_reach: &[bool], strict: bool) -> R<Option<Kind>> {
2393 let mut result = None;
2394 for (pc, op) in ops.iter().enumerate() {
2395 if !pc_reach.get(pc).copied().unwrap_or(true) {
2399 continue;
2400 }
2401 if let Op::Return { src } = *op {
2402 let k = match kinds.get(src as usize) {
2403 Some(k) => k,
2404 None if !strict => continue,
2405 None => return Err(WasmLowerError::Unsupported("return of an unknown-kind value")),
2406 };
2407 match result {
2408 None => result = Some(k),
2409 Some(prev) if prev == k => {}
2410 Some(Kind::SeqAny) if k.is_seq() => result = Some(k),
2416 Some(prev) if prev.is_seq() && k == Kind::SeqAny => {}
2417 Some(_) => return Err(WasmLowerError::Unsupported("function returns mixed kinds")),
2418 }
2419 }
2420 }
2421 Ok(result)
2422}
2423
2424fn fn_return_struct_layout(plan: &Plan) -> Option<FieldLayout> {
2429 for op in &plan.ops {
2430 if let Op::Return { src } = *op {
2431 if plan.kinds.get(src as usize) == Some(Kind::Struct) {
2432 let layout = plan.structs.reg_layout.get(&src)?;
2433 let resolved: FieldLayout = layout
2439 .iter()
2440 .filter_map(|&(f, vr)| {
2441 plan.kinds.get(vr as usize).map(|k| {
2442 let nested = match (k, plan.structs.struct_name_of.get(&vr)) {
2443 (Kind::Struct, Some(name)) => FieldNested::Struct(name.clone()),
2444 _ => FieldNested::None,
2445 };
2446 (f, k, nested)
2447 })
2448 })
2449 .collect();
2450 return (resolved.len() == layout.len()).then_some(resolved);
2451 }
2452 }
2453 }
2454 None
2455}
2456
2457fn result_valtypes(result: Option<Kind>) -> Vec<u8> {
2459 match result {
2460 Some(k) => vec![k.wasm_valtype()],
2461 None => vec![],
2462 }
2463}
2464
2465struct Ctx<'a> {
2469 constants: &'a [Constant],
2470 host_index: &'a dyn Fn(HostFn) -> Option<u32>,
2471 fn_base: u32,
2472 heap_global: u32,
2475 iter_global: u32,
2479 fn_type: &'a [u32],
2482 fn_param_valtypes: &'a [Vec<u8>],
2486 fn_results: &'a [Option<Kind>],
2489 functions: &'a [crate::vm::instruction::CompiledFunction],
2492 capture_kinds: &'a [Vec<Option<Kind>>],
2496 enum_types: &'a [EnumTypeDef],
2499 struct_types: &'a [StructTypeDef],
2502 policies: &'a PolicyRegistry,
2505 interner: &'a Interner,
2508 linked: bool,
2512 rt_alloc: Option<u32>,
2516}
2517
2518#[derive(PartialEq, Eq)]
2520enum Flow {
2521 Straight,
2522 Terminated,
2523}
2524
2525fn emit_body(plan: &Plan, _ctx: &Ctx) -> R<Vec<u8>> {
2528 if plan.stub {
2532 return Ok(vec![0x00 , 0x00 , 0x0B ]);
2533 }
2534 let ctx = _ctx;
2535 let blocks = Blocks::new(&plan.ops).ok_or(WasmLowerError::Unsupported("jump target escapes the function"))?;
2536 let pc_local = plan.num_regs;
2537
2538 let mut blocks_code: Vec<Vec<u8>> = Vec::with_capacity(blocks.num_blocks());
2539 for k in 0..blocks.num_blocks() {
2540 if !plan.reachable_blocks.get(k).copied().unwrap_or(true) {
2544 blocks_code.push(vec![0x00]); continue;
2546 }
2547 let mut code = Vec::new();
2548 let mut terminated = false;
2549 for pc in blocks.start(k)..blocks.end(k) {
2550 if lower_op(pc, plan, ctx, &blocks, k, &mut code)? == Flow::Terminated {
2551 terminated = true;
2552 break;
2553 }
2554 }
2555 if !terminated {
2556 let end = blocks.end(k);
2557 if end >= plan.ops.len() {
2558 match plan.result {
2560 None => code.push(0x0F), Some(_) => code.push(0x00), }
2563 } else {
2564 let next = blocks.block_of(end);
2565 code.push(0x41); leb_u32(&mut code, next as u32);
2567 local_set(&mut code, pc_local);
2568 code.push(0x0C); leb_u32(&mut code, blocks.br_loop(k));
2570 }
2571 }
2572 blocks_code.push(code);
2573 }
2574
2575 let mut body = assemble_dispatch_loop(pc_local, &blocks_code);
2576 if plan.result.is_some() {
2577 body.push(0x00); }
2579 body.push(0x0B); let mut entry = encode_locals(plan);
2582 entry.extend(body);
2583 Ok(entry)
2584}
2585
2586fn lower_op(pc: usize, plan: &Plan, ctx: &Ctx, blocks: &Blocks, k: usize, code: &mut Vec<u8>) -> R<Flow> {
2588 let kinds = &plan.kinds;
2589 match plan.ops[pc] {
2590 Op::LoadConst { dst, idx } => {
2591 let dst_float = kinds.get(dst as usize) == Some(Kind::Float);
2595 match ctx.constants.get(idx as usize).ok_or(WasmLowerError::Unsupported("constant index out of range"))? {
2596 Constant::Int(v) if dst_float => {
2597 code.push(0x44); code.extend_from_slice(&(*v as f64).to_le_bytes());
2599 }
2600 Constant::Int(v) => {
2601 code.push(0x42); leb_i64(code, *v);
2603 }
2604 Constant::Bool(b) if dst_float => {
2605 code.push(0x44); code.extend_from_slice(&(if *b { 1.0f64 } else { 0.0f64 }).to_le_bytes());
2607 }
2608 Constant::Bool(b) => {
2609 code.push(0x42); leb_i64(code, i64::from(*b));
2611 }
2612 Constant::Char(c) => {
2613 code.push(0x42); leb_i64(code, i64::from(*c as u32));
2615 }
2616 Constant::Nothing => {
2617 code.push(0x42); leb_i64(code, 0);
2623 }
2624 Constant::Float(f) => {
2625 code.push(0x44); code.extend_from_slice(&f.to_le_bytes());
2627 }
2628 Constant::Duration(v) | Constant::Moment(v) | Constant::Time(v) => {
2633 code.push(0x42); leb_i64(code, *v);
2635 }
2636 Constant::Date(v) => {
2637 i32_const(code, *v);
2642 }
2643 Constant::Span { months, days } => {
2646 let packed = ((*months as i64) << 32) | ((*days as u32) as i64);
2647 code.push(0x42); leb_i64(code, packed);
2649 }
2650 Constant::Text(s) => {
2651 lower_text_literal(code, ctx, plan.num_regs, s.as_bytes());
2654 }
2655 _ => return Err(WasmLowerError::Unsupported("non-scalar constant")),
2656 }
2657 local_set(code, dst as u32);
2658 Ok(Flow::Straight)
2659 }
2660 Op::EnsureOwned { .. } => Ok(Flow::Straight),
2666 Op::Move { dst, src } => {
2667 if kinds.get(dst as usize) == Some(Kind::Float) && kinds.get(src as usize) != Some(Kind::Float) {
2670 push_as_f64(code, src, kinds.get(src as usize))?;
2671 } else {
2672 local_get(code, src as u32);
2673 }
2674 local_set(code, dst as u32);
2675 if cow_clonable(kinds.get(dst as usize)) {
2678 emit_retain(code, dst);
2679 }
2680 Ok(Flow::Straight)
2681 }
2682 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::BigInt) => {
2688 lower_bigint_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::BigintAdd)
2689 }
2690 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::BigInt) => {
2691 lower_bigint_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::BigintSub)
2692 }
2693 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::BigInt) => {
2694 lower_bigint_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::BigintMul)
2695 }
2696 Op::Div { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::BigInt) => {
2697 lower_bigint_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::BigintDiv)
2698 }
2699 Op::Mod { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::BigInt) => {
2700 lower_bigint_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::BigintMod)
2701 }
2702 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Complex) => {
2703 lower_complex_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ComplexAdd)
2704 }
2705 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Complex) => {
2706 lower_complex_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ComplexSub)
2707 }
2708 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Complex) => {
2709 lower_complex_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ComplexMul)
2710 }
2711 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Modular) => {
2712 lower_modular_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ModularAdd)
2713 }
2714 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Modular) => {
2715 lower_modular_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ModularSub)
2716 }
2717 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Modular) => {
2718 lower_modular_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::ModularMul)
2719 }
2720 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Decimal) => {
2721 lower_decimal_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::DecimalAdd)
2722 }
2723 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Decimal) => {
2724 lower_decimal_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::DecimalSub)
2725 }
2726 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Decimal) => {
2727 lower_decimal_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::DecimalMul)
2728 }
2729 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Money) => {
2730 lower_money_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::MoneyAdd)
2731 }
2732 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Money) => {
2733 lower_money_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::MoneySub)
2734 }
2735 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Quantity) => {
2736 lower_quantity_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::QuantityAdd)
2737 }
2738 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Quantity) => {
2739 lower_quantity_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::QuantitySub)
2740 }
2741 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Quantity) => {
2742 lower_quantity_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::QuantityMul)
2743 }
2744 Op::Div { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Quantity) => {
2745 lower_quantity_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::QuantityDiv)
2746 }
2747 Op::Add { dst, lhs, rhs }
2751 if ctx.linked && (kinds.get(lhs as usize) == Some(Kind::Span) || kinds.get(rhs as usize) == Some(Kind::Span)) =>
2752 {
2753 let (base, span) = if kinds.get(lhs as usize) == Some(Kind::Span) { (rhs, lhs) } else { (lhs, rhs) };
2754 let is_date = kinds.get(base as usize) == Some(Kind::Date);
2755 return lower_span_add(code, ctx, plan.num_regs, dst, base, span, is_date, false);
2756 }
2757 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(rhs as usize) == Some(Kind::Span) => {
2759 let is_date = kinds.get(lhs as usize) == Some(Kind::Date);
2760 return lower_span_add(code, ctx, plan.num_regs, dst, lhs, rhs, is_date, true);
2761 }
2762 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Lanes) => {
2764 let idx = (ctx.host_index)(HostFn::Lanes4Add).ok_or(WasmLowerError::Unsupported("lanes4_add not imported"))?;
2765 local_get(code, lhs as u32);
2766 local_get(code, rhs as u32);
2767 code.push(0x10);
2768 leb_u32(code, idx);
2769 local_set(code, dst as u32);
2770 Ok(Flow::Straight)
2771 }
2772 Op::Add { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Rational) => {
2773 lower_rational_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::RationalAdd)
2774 }
2775 Op::Sub { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Rational) => {
2776 lower_rational_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::RationalSub)
2777 }
2778 Op::Mul { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Rational) => {
2779 lower_rational_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::RationalMul)
2780 }
2781 Op::Div { dst, lhs, rhs } if ctx.linked && kinds.get(dst as usize) == Some(Kind::Rational) => {
2782 lower_rational_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::RationalDiv)
2783 }
2784 Op::Add { dst, lhs, rhs } => {
2785 if kinds.get(dst as usize) == Some(Kind::Text) {
2786 lower_concat(code, kinds, ctx, plan.num_regs, dst, lhs, rhs)?;
2787 Ok(Flow::Straight)
2788 } else {
2789 lower_arith(code, kinds, dst, lhs, rhs, ArithOp::Add)
2790 }
2791 }
2792 Op::Sub { dst, lhs, rhs } => lower_arith(code, kinds, dst, lhs, rhs, ArithOp::Sub),
2793 Op::Mul { dst, lhs, rhs } => lower_arith(code, kinds, dst, lhs, rhs, ArithOp::Mul),
2794 Op::Div { dst, lhs, rhs } => lower_arith(code, kinds, dst, lhs, rhs, ArithOp::Div),
2795 Op::FloorDiv { dst, lhs, rhs } => lower_floordiv_regs(code, kinds, plan.num_regs, dst, lhs, rhs),
2796 Op::Mod { dst, lhs, rhs } => lower_arith(code, kinds, dst, lhs, rhs, ArithOp::Mod),
2797 Op::Pow { dst, lhs, rhs } => lower_pow_regs(code, kinds, ctx, plan.num_regs, dst, lhs, rhs),
2801 Op::DivPow2 { dst, lhs, k } => {
2805 local_get(code, lhs as u32);
2806 code.push(0x42); leb_i64(code, 1i64 << k);
2808 code.push(0x7F); local_set(code, dst as u32);
2810 Ok(Flow::Straight)
2811 }
2812 Op::MagicDivU { dst, lhs, magic, more, mul_back } => {
2815 lower_magic_div(code, plan.num_regs, dst, lhs, magic, more, mul_back);
2816 Ok(Flow::Straight)
2817 }
2818 Op::ExactDiv { dst, lhs, rhs } if ctx.linked => {
2822 lower_rational_binop(code, kinds, ctx, dst, lhs, rhs, HostFn::RationalDiv)
2823 }
2824 Op::ExactDiv { dst, lhs, rhs } => {
2825 lower_exact_div(code, ctx, plan.num_regs, dst, lhs, rhs);
2826 Ok(Flow::Straight)
2827 }
2828 Op::RegionBoundsGuard { .. } => Ok(Flow::Straight),
2831 Op::AddAssign { dst, src } => {
2832 if kinds.get(dst as usize) == Some(Kind::Text) {
2833 lower_concat(code, kinds, ctx, plan.num_regs, dst, dst, src)?;
2834 Ok(Flow::Straight)
2835 } else if ctx.linked && kinds.get(dst as usize) == Some(Kind::Lanes) {
2836 let idx = (ctx.host_index)(HostFn::Lanes4Add).ok_or(WasmLowerError::Unsupported("lanes4_add not imported"))?;
2838 local_get(code, dst as u32);
2839 local_get(code, src as u32);
2840 code.push(0x10);
2841 leb_u32(code, idx);
2842 local_set(code, dst as u32);
2843 Ok(Flow::Straight)
2844 } else {
2845 lower_arith(code, kinds, dst, dst, src, ArithOp::Add)
2846 }
2847 }
2848 Op::BitXor { dst, lhs, rhs } => match kinds.get(lhs as usize) {
2853 Some(Kind::Int) | Some(Kind::Word64) => {
2854 arith(code, 0x85, dst, lhs, rhs); Ok(Flow::Straight)
2856 }
2857 Some(Kind::Word32) => {
2858 arith(code, 0x73, dst, lhs, rhs); Ok(Flow::Straight)
2860 }
2861 Some(Kind::Lanes) if ctx.linked => {
2863 let idx = (ctx.host_index)(HostFn::Lanes4Xor).ok_or(WasmLowerError::Unsupported("lanes4_xor not imported"))?;
2864 local_get(code, lhs as u32);
2865 local_get(code, rhs as u32);
2866 code.push(0x10);
2867 leb_u32(code, idx);
2868 local_set(code, dst as u32);
2869 Ok(Flow::Straight)
2870 }
2871 _ => Err(WasmLowerError::Unsupported("`^` of a non-Int/Word value")),
2872 },
2873 Op::BitAnd { dst, lhs, rhs } => match kinds.get(lhs as usize) {
2874 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Word64) => {
2875 arith(code, 0x83, dst, lhs, rhs); Ok(Flow::Straight)
2877 }
2878 Some(Kind::Word32) => {
2879 arith(code, 0x71, dst, lhs, rhs); Ok(Flow::Straight)
2881 }
2882 _ => Err(WasmLowerError::Unsupported("`&` of a non-Int/Bool/Word value")),
2883 },
2884 Op::BitOr { dst, lhs, rhs } => match kinds.get(lhs as usize) {
2885 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Word64) => {
2886 arith(code, 0x84, dst, lhs, rhs); Ok(Flow::Straight)
2888 }
2889 Some(Kind::Word32) => {
2890 arith(code, 0x72, dst, lhs, rhs); Ok(Flow::Straight)
2892 }
2893 _ => Err(WasmLowerError::Unsupported("`|` of a non-Int/Bool/Word value")),
2894 },
2895 Op::Shl { dst, lhs, rhs } => {
2896 arith(code, 0x86, dst, lhs, rhs); Ok(Flow::Straight)
2898 }
2899 Op::Shr { dst, lhs, rhs } => {
2900 arith(code, 0x87, dst, lhs, rhs); Ok(Flow::Straight)
2902 }
2903 Op::Not { dst, src } => {
2904 match kinds.get(src as usize) {
2908 Some(Kind::Bool) | Some(Kind::Int) => {
2909 local_get(code, src as u32);
2910 code.push(0x50); code.push(0xAD); local_set(code, dst as u32);
2913 }
2914 _ => return Err(WasmLowerError::Unsupported("Not of a non-Int/Bool value")),
2915 }
2916 Ok(Flow::Straight)
2917 }
2918 Op::Lt { dst, lhs, rhs } => lower_compare(code, kinds, dst, lhs, rhs, Cmp::Lt),
2919 Op::Gt { dst, lhs, rhs } => lower_compare(code, kinds, dst, lhs, rhs, Cmp::Gt),
2920 Op::LtEq { dst, lhs, rhs } => lower_compare(code, kinds, dst, lhs, rhs, Cmp::Le),
2921 Op::GtEq { dst, lhs, rhs } => lower_compare(code, kinds, dst, lhs, rhs, Cmp::Ge),
2922 Op::ApproxEq { dst, lhs, rhs } => lower_approx_eq(code, kinds, dst, lhs, rhs),
2923 Op::Eq { dst, lhs, rhs } => {
2924 if kinds.get(lhs as usize) == Some(Kind::Text) && kinds.get(rhs as usize) == Some(Kind::Text) {
2925 lower_text_eq(code, plan.num_regs, dst, lhs, rhs, false);
2926 Ok(Flow::Straight)
2927 } else if ctx.linked && kinds.get(lhs as usize) == Some(Kind::Uuid) && kinds.get(rhs as usize) == Some(Kind::Uuid) {
2928 lower_uuid_eq(code, ctx, dst, lhs, rhs, false)
2929 } else {
2930 lower_compare(code, kinds, dst, lhs, rhs, Cmp::Eq)
2931 }
2932 }
2933 Op::NotEq { dst, lhs, rhs } => {
2934 if kinds.get(lhs as usize) == Some(Kind::Text) && kinds.get(rhs as usize) == Some(Kind::Text) {
2935 lower_text_eq(code, plan.num_regs, dst, lhs, rhs, true);
2936 Ok(Flow::Straight)
2937 } else if ctx.linked && kinds.get(lhs as usize) == Some(Kind::Uuid) && kinds.get(rhs as usize) == Some(Kind::Uuid) {
2938 lower_uuid_eq(code, ctx, dst, lhs, rhs, true)
2939 } else {
2940 lower_compare(code, kinds, dst, lhs, rhs, Cmp::Ne)
2941 }
2942 }
2943 Op::Jump { target } => {
2944 code.push(0x41);
2945 leb_u32(code, blocks.block_of(target) as u32);
2946 local_set(code, plan.num_regs);
2947 code.push(0x0C);
2948 leb_u32(code, blocks.br_loop(k));
2949 Ok(Flow::Terminated)
2950 }
2951 Op::JumpIfFalse { cond, target } => {
2952 emit_cond_jump(code, cond, true, blocks.block_of(target), blocks.block_of(pc + 1), plan.num_regs, blocks.br_loop(k));
2953 Ok(Flow::Terminated)
2954 }
2955 Op::JumpIfTrue { cond, target } => {
2956 emit_cond_jump(code, cond, false, blocks.block_of(target), blocks.block_of(pc + 1), plan.num_regs, blocks.br_loop(k));
2957 Ok(Flow::Terminated)
2958 }
2959 Op::GlobalGet { dst, idx } => {
2960 code.push(0x23); leb_u32(code, idx as u32);
2962 local_set(code, dst as u32);
2963 if cow_clonable(kinds.get(dst as usize)) {
2966 emit_retain(code, dst);
2967 }
2968 Ok(Flow::Straight)
2969 }
2970 Op::LoadToday { dst } => {
2971 let idx = (ctx.host_index)(HostFn::Today).ok_or(WasmLowerError::Unsupported("today not imported"))?;
2972 code.push(0x10); leb_u32(code, idx);
2974 local_set(code, dst as u32);
2975 Ok(Flow::Straight)
2976 }
2977 Op::NewEmptyList { dst } | Op::NewEmptyListI32 { dst } | Op::NewEmptyMap { dst } | Op::NewEmptySet { dst } => {
2983 emit_empty_header(code, ctx, plan.num_regs, dst as u32);
2984 Ok(Flow::Straight)
2985 }
2986 Op::Length { dst, collection } => {
2988 local_get(code, collection as u32);
2989 i32_load(code, 0); code.push(0xAD); local_set(code, dst as u32);
2992 Ok(Flow::Straight)
2993 }
2994 Op::ListPush { list, value } => {
2995 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, list)?;
2996 lower_list_push(code, kinds, ctx, plan.num_regs, list, value)?;
2997 if cow_clonable(kinds.get(value as usize)) {
3000 emit_retain(code, value);
3001 }
3002 Ok(Flow::Straight)
3003 }
3004 Op::ListPushField { obj, field, src } => {
3005 lower_list_push_field(code, plan, kinds, ctx, obj, field, src)?;
3006 if cow_clonable(kinds.get(src as usize)) {
3007 emit_retain(code, src);
3008 }
3009 Ok(Flow::Straight)
3010 }
3011 Op::ListPop { list, dst } => {
3012 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, list)?;
3013 lower_list_pop(code, kinds, dst, list)?;
3014 if cow_clonable(kinds.get(dst as usize)) {
3018 emit_retain(code, dst);
3019 }
3020 Ok(Flow::Straight)
3021 }
3022 Op::Index { dst, collection, index } | Op::IndexUnchecked { dst, collection, index } => {
3026 match kinds.get(collection as usize) {
3027 Some(Kind::Map) => lower_map_get(code, kinds, plan.num_regs, dst, collection, index)?,
3028 Some(Kind::Text) => lower_text_index(code, ctx, plan.num_regs, dst, collection, index)?,
3029 _ => lower_index(code, kinds, dst, collection, index)?,
3030 }
3031 if cow_clonable(kinds.get(dst as usize)) {
3034 emit_retain(code, dst);
3035 }
3036 Ok(Flow::Straight)
3037 }
3038 Op::SetIndex { collection, index, value } | Op::SetIndexUnchecked { collection, index, value } => {
3041 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, collection)?;
3042 if kinds.get(collection as usize) == Some(Kind::Map) {
3043 lower_map_insert(code, kinds, ctx, plan.num_regs, collection, index, value)?;
3044 } else {
3045 lower_set_index(code, kinds, collection, index, value)?;
3046 }
3047 if cow_clonable(kinds.get(value as usize)) {
3050 emit_retain(code, value);
3051 }
3052 Ok(Flow::Straight)
3053 }
3054 Op::NewRange { dst, start, end } => {
3055 lower_new_range(code, ctx, plan.num_regs, dst, start, end);
3056 Ok(Flow::Straight)
3057 }
3058 Op::NewList { dst, start, count } => {
3062 lower_new_list(code, kinds, ctx, plan.num_regs, dst, start, count)?;
3063 Ok(Flow::Straight)
3064 }
3065 Op::NewTuple { dst, start, count } => {
3066 if kinds.get(dst as usize) == Some(Kind::Tuple) {
3067 lower_new_tuple_het(code, kinds, ctx, plan.num_regs, dst, start, count)?;
3068 } else {
3069 lower_new_list(code, kinds, ctx, plan.num_regs, dst, start, count)?;
3070 }
3071 Ok(Flow::Straight)
3072 }
3073 Op::DestructureTuple { src, start, count } => {
3074 lower_destructure_tuple(code, kinds, src, start, count)?;
3075 Ok(Flow::Straight)
3076 }
3077 Op::IterPrepare { iterable } => {
3078 lower_iter_prepare(code, kinds, ctx, plan.num_regs, iterable)?;
3079 Ok(Flow::Straight)
3080 }
3081 Op::IterNext { dst, exit } => {
3082 lower_iter_next(code, kinds, ctx, blocks, k, plan.num_regs, dst, exit, pc);
3083 Ok(Flow::Terminated)
3084 }
3085 Op::SetAdd { set, value } => {
3086 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, set)?;
3087 lower_set_add(code, kinds, ctx, plan.num_regs, set, value)?;
3088 Ok(Flow::Straight)
3089 }
3090 Op::RemoveFrom { collection, value } => {
3091 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, collection)?;
3092 lower_remove_from(code, kinds, plan.num_regs, collection, value)?;
3093 Ok(Flow::Straight)
3094 }
3095 Op::UnionOp { dst, lhs, rhs } => {
3096 lower_union(code, kinds, ctx, plan.num_regs, dst, lhs, rhs)?;
3097 Ok(Flow::Straight)
3098 }
3099 Op::IntersectOp { dst, lhs, rhs } => {
3100 lower_intersect(code, kinds, ctx, plan.num_regs, dst, lhs, rhs)?;
3101 Ok(Flow::Straight)
3102 }
3103 Op::Contains { dst, collection, value } => {
3104 if kinds.get(collection as usize) == Some(Kind::Map) {
3105 lower_map_contains(code, kinds, plan.num_regs, dst, collection, value)?;
3106 } else {
3107 lower_contains(code, kinds, plan.num_regs, dst, collection, value)?;
3108 }
3109 Ok(Flow::Straight)
3110 }
3111 Op::SliceOp { dst, collection, start, end } => {
3112 lower_slice(code, kinds, ctx, plan.num_regs, dst, collection, start, end)?;
3113 Ok(Flow::Straight)
3114 }
3115 Op::SeqConcat { dst, lhs, rhs } => {
3116 lower_seq_concat(code, kinds, ctx, plan.num_regs, dst, lhs, rhs)?;
3117 Ok(Flow::Straight)
3118 }
3119 Op::Concat { dst, lhs, rhs } => {
3120 lower_concat(code, kinds, ctx, plan.num_regs, dst, lhs, rhs)?;
3121 Ok(Flow::Straight)
3122 }
3123 Op::FormatValue { dst, src, spec, debug_prefix } => {
3124 lower_format_value(code, kinds, ctx, plan.num_regs, dst, src, spec, debug_prefix)?;
3125 Ok(Flow::Straight)
3126 }
3127 Op::DeepClone { dst, src } => {
3128 lower_deep_clone(code, kinds, &plan.structs, ctx, plan.num_regs, dst, src)?;
3129 Ok(Flow::Straight)
3130 }
3131 Op::CallBuiltin { dst, builtin: BuiltinId::Copy, args_start, .. } => {
3134 lower_deep_clone(code, kinds, &plan.structs, ctx, plan.num_regs, dst, args_start)?;
3135 Ok(Flow::Straight)
3136 }
3137 Op::NewStruct { dst, .. } => {
3138 let count = plan.structs.count.get(pc).copied().flatten().unwrap_or(0);
3139 lower_new_struct(code, ctx, plan.num_regs, count, dst);
3140 Ok(Flow::Straight)
3141 }
3142 Op::StructInsert { obj, value, .. } => {
3143 let slot = plan.structs.slot.get(pc).copied().flatten().ok_or(WasmLowerError::Unsupported("struct insert with no static slot"))?;
3144 let cow = plan.cow_inserts.get(pc).copied().unwrap_or(true);
3145 lower_struct_insert(code, kinds, ctx, plan.num_regs, slot, obj, value, cow)?;
3146 Ok(Flow::Straight)
3147 }
3148 Op::GetField { dst, obj, .. } => {
3149 let slot = plan.structs.slot.get(pc).copied().flatten().ok_or(WasmLowerError::Unsupported("field access with no static slot"))?;
3150 lower_get_field(code, kinds, slot, dst, obj)?;
3151 if cow_clonable(kinds.get(dst as usize)) {
3154 emit_retain(code, dst);
3155 }
3156 Ok(Flow::Straight)
3157 }
3158 Op::CheckPolicy { subject, predicate, is_capability, object, .. } => {
3159 lower_check_policy(code, plan, ctx, subject, predicate, is_capability, object)?;
3160 Ok(Flow::Straight)
3161 }
3162 Op::CrdtBump { obj, field, amount, negate } => {
3163 lower_crdt_bump(code, plan, ctx, obj, field, amount, negate)?;
3164 Ok(Flow::Straight)
3165 }
3166 Op::CrdtMerge { target, source } => {
3167 lower_crdt_merge(code, plan, target, source)?;
3168 Ok(Flow::Straight)
3169 }
3170 Op::NewCrdt { dst, kind } => {
3171 match kind {
3179 0 | 1 | 3 => emit_empty_header(code, ctx, plan.num_regs, dst as u32),
3180 _ => {
3181 lower_text_literal(code, ctx, plan.num_regs, b"");
3182 local_set(code, dst as u32);
3183 }
3184 }
3185 Ok(Flow::Straight)
3186 }
3187 Op::CrdtResolve { obj, field, value } => {
3188 lower_crdt_resolve(code, plan, kinds, obj, field, value)?;
3189 Ok(Flow::Straight)
3190 }
3191 Op::CrdtAppend { seq, value } => {
3192 lower_crdt_append(code, plan, kinds, ctx, seq, value)?;
3193 Ok(Flow::Straight)
3194 }
3195 Op::ChanNew { dst, .. } => {
3200 emit_empty_header(code, ctx, plan.num_regs, dst as u32);
3201 Ok(Flow::Straight)
3202 }
3203 Op::ChanSend { chan, val } => {
3204 lower_list_push(code, kinds, ctx, plan.num_regs, chan, val)?;
3205 Ok(Flow::Straight)
3206 }
3207 Op::ChanRecv { dst, chan } => {
3208 lower_chan_recv(code, kinds, plan.num_regs, dst, chan)?;
3209 Ok(Flow::Straight)
3210 }
3211 Op::ChanTryRecv { dst, chan } => {
3215 lower_chan_try_recv(code, kinds, ctx, plan.num_regs, dst, chan)?;
3216 Ok(Flow::Straight)
3217 }
3218 Op::ChanTrySend { dst, chan, val } => {
3222 lower_list_push(code, kinds, ctx, plan.num_regs, chan, val)?;
3223 code.push(0x42); leb_i64(code, 1);
3225 local_set(code, dst as u32);
3226 Ok(Flow::Straight)
3227 }
3228 Op::ChanClose { .. } => Ok(Flow::Straight),
3232 Op::Spawn { func, args_start, arg_count } => {
3236 if emit_sync_call(code, kinds, ctx, func, args_start, arg_count)? {
3237 code.push(0x1A); }
3239 Ok(Flow::Straight)
3240 }
3241 Op::SpawnHandle { dst, func, args_start, arg_count } => {
3242 if emit_sync_call(code, kinds, ctx, func, args_start, arg_count)? {
3243 code.push(0x1A); }
3245 code.push(0x42); leb_i64(code, 0);
3247 local_set(code, dst as u32);
3248 Ok(Flow::Straight)
3249 }
3250 Op::TaskAbort { .. } => Ok(Flow::Straight),
3252 Op::TaskAwait { dst, handle } => {
3257 local_get(code, handle as u32);
3258 local_set(code, dst as u32);
3259 Ok(Flow::Straight)
3260 }
3261 Op::Sleep { .. } => Ok(Flow::Straight),
3265 Op::SelectArmRecv { .. } | Op::SelectArmTimeout { .. } => Ok(Flow::Straight),
3272 Op::SelectWait { dst_arm } => {
3273 lower_select_wait(code, plan, kinds, blocks, k, pc, dst_arm)?;
3274 Ok(Flow::Straight)
3275 }
3276 Op::NetConnect { .. } | Op::NetSync { .. } => Ok(Flow::Straight),
3283 Op::NetListen { .. } => {
3284 emit_empty_header(code, ctx, plan.num_regs, plan.num_regs + 8);
3286 i32_const(code, NET_INBOX_ADDR);
3287 local_get(code, plan.num_regs + 8);
3288 i32_store(code, 0);
3289 Ok(Flow::Straight)
3290 }
3291 Op::NetSend { msg, .. } => {
3292 let elem = kinds.get(msg as usize).ok_or(WasmLowerError::Unsupported("Send of an unknown-kind message"))?;
3293 i32_const(code, NET_INBOX_ADDR);
3294 i32_load(code, 0);
3295 local_set(code, plan.num_regs + 8);
3296 lower_list_push_at(code, elem, ctx, plan.num_regs, plan.num_regs + 8, msg)?;
3297 if cow_clonable(kinds.get(msg as usize)) {
3298 emit_retain(code, msg);
3299 }
3300 Ok(Flow::Straight)
3301 }
3302 Op::NetStream { values, .. } => {
3303 let elem = kinds.get(values as usize).ok_or(WasmLowerError::Unsupported("Stream of an unknown-kind list"))?;
3306 i32_const(code, NET_INBOX_ADDR);
3307 i32_load(code, 0);
3308 local_set(code, plan.num_regs + 8);
3309 lower_list_push_at(code, elem, ctx, plan.num_regs, plan.num_regs + 8, values)?;
3310 if cow_clonable(kinds.get(values as usize)) {
3311 emit_retain(code, values);
3312 }
3313 Ok(Flow::Straight)
3314 }
3315 Op::NetAwait { dst, .. } => {
3316 let elem = kinds.get(dst as usize).ok_or(WasmLowerError::Unsupported("Await of an unknown-kind message"))?;
3317 i32_const(code, NET_INBOX_ADDR);
3318 i32_load(code, 0);
3319 local_set(code, plan.num_regs + 8);
3320 emit_pop_front(code, elem, plan.num_regs + 8, plan.num_regs + 5, dst as u32)?;
3321 if cow_clonable(kinds.get(dst as usize)) {
3322 emit_retain(code, dst);
3323 }
3324 Ok(Flow::Straight)
3325 }
3326 Op::NetMakePeer { dst, addr } => {
3327 local_get(code, addr as u32);
3328 local_set(code, dst as u32);
3329 Ok(Flow::Straight)
3330 }
3331 Op::NewInductive { dst, ctor, args_start, count, .. } => {
3332 lower_new_inductive(code, kinds, ctx, plan.num_regs, dst, ctor, args_start, count)?;
3333 Ok(Flow::Straight)
3334 }
3335 Op::TestArm { dst, target, variant } => {
3336 lower_test_arm(code, dst, target, variant);
3337 Ok(Flow::Straight)
3338 }
3339 Op::BindArm { dst, target, index, .. } => {
3340 lower_bind_arm(code, kinds, dst, target, index)?;
3341 Ok(Flow::Straight)
3342 }
3343 Op::MakeClosure { dst, func, locals_start } => {
3344 lower_make_closure(code, ctx, plan.num_regs, dst, func, locals_start)?;
3345 Ok(Flow::Straight)
3346 }
3347 Op::CallValue { dst, callee, args_start, arg_count, .. } => {
3348 for a in 0..arg_count {
3351 let arg = args_start + a;
3352 if cow_clonable(kinds.get(arg as usize)) {
3353 emit_retain(code, arg);
3354 }
3355 }
3356 lower_call_value(code, plan, ctx, pc, dst, callee, args_start, arg_count)?;
3357 Ok(Flow::Straight)
3358 }
3359 Op::IterPop => {
3360 global_get(code, ctx.iter_global);
3362 i32_const(code, 12);
3363 code.push(0x6A); global_set(code, ctx.iter_global);
3365 Ok(Flow::Straight)
3366 }
3367 Op::LoadNow { dst } => {
3368 let idx = (ctx.host_index)(HostFn::Now).ok_or(WasmLowerError::Unsupported("now not imported"))?;
3369 code.push(0x10); leb_u32(code, idx);
3371 local_set(code, dst as u32);
3372 Ok(Flow::Straight)
3373 }
3374 Op::GlobalSet { idx, src } => {
3375 if cow_clonable(kinds.get(src as usize)) {
3378 emit_retain(code, src);
3379 }
3380 local_get(code, src as u32);
3381 code.push(0x24); leb_u32(code, idx as u32);
3383 Ok(Flow::Straight)
3384 }
3385 Op::Call { dst, func, args_start, arg_count } => {
3386 for a in 0..arg_count {
3390 let arg = args_start + a;
3391 if cow_clonable(kinds.get(arg as usize)) {
3392 emit_retain(code, arg);
3393 }
3394 }
3395 let pvts = ctx.fn_param_valtypes.get(func as usize).ok_or(WasmLowerError::Unsupported("call of unknown function"))?;
3399 for a in 0..arg_count {
3400 let arg = args_start + a;
3401 let arg_vt = kinds.valtype(arg as usize);
3402 let param_vt = pvts.get(a as usize).copied().unwrap_or(I64);
3403 if arg_vt == param_vt {
3404 local_get(code, arg as u32);
3405 } else if arg_vt == I64 && param_vt == F64 {
3406 push_as_f64(code, arg, kinds.get(arg as usize))?;
3407 } else {
3408 return Err(WasmLowerError::Unsupported("call argument type does not match the parameter"));
3409 }
3410 }
3411 code.push(0x10); leb_u32(code, ctx.fn_base + func as u32);
3413 if ctx.fn_results.get(func as usize).copied().flatten().is_some() {
3415 local_set(code, dst as u32);
3416 }
3417 Ok(Flow::Straight)
3418 }
3419 Op::CallBuiltin { dst, builtin: BuiltinId::Pow, args_start, arg_count } => {
3420 lower_pow(code, kinds, ctx, plan.num_regs, dst, args_start, arg_count)
3421 }
3422 Op::CallBuiltin { dst, builtin: BuiltinId::ParseInt, args_start, .. } => {
3424 let idx = (ctx.host_index)(HostFn::ParseInt).ok_or(WasmLowerError::Unsupported("parse_int host not imported"))?;
3425 local_get(code, args_start as u32);
3426 code.push(0x10); leb_u32(code, idx);
3428 local_set(code, dst as u32);
3429 Ok(Flow::Straight)
3430 }
3431 Op::CallBuiltin { dst, builtin: BuiltinId::ParseFloat, args_start, .. } => {
3434 let idx = (ctx.host_index)(HostFn::ParseFloat).ok_or(WasmLowerError::Unsupported("parse_float host not imported"))?;
3435 local_get(code, args_start as u32);
3436 code.push(0x10); leb_u32(code, idx);
3438 local_set(code, dst as u32);
3439 Ok(Flow::Straight)
3440 }
3441 Op::CallBuiltin { dst, builtin: BuiltinId::Chr, args_start, .. } => {
3444 lower_chr(code, ctx, plan.num_regs, dst, args_start);
3445 Ok(Flow::Straight)
3446 }
3447 Op::CallBuiltin { dst, builtin: BuiltinId::ParseTimestamp, args_start, .. } => {
3449 let idx = (ctx.host_index)(HostFn::ParseTimestamp).ok_or(WasmLowerError::Unsupported("parse_timestamp host not imported"))?;
3450 local_get(code, args_start as u32);
3451 code.push(0x10); leb_u32(code, idx);
3453 local_set(code, dst as u32);
3454 Ok(Flow::Straight)
3455 }
3456 Op::CallBuiltin { dst, builtin: BuiltinId::WriteWireResidual, args_start, .. } => {
3461 if kinds.get(args_start as usize) != Some(Kind::Text) {
3462 return Err(WasmLowerError::Unsupported("writeWireResidual requires a Text argument"));
3463 }
3464 let idx = (ctx.host_index)(HostFn::WriteWireResidual).ok_or(WasmLowerError::Unsupported("write_wire_residual host not imported"))?;
3465 local_get(code, args_start as u32);
3466 i32_load(code, 8); local_get(code, args_start as u32);
3468 i32_load(code, 0); code.push(0x10); leb_u32(code, idx);
3471 local_set(code, dst as u32);
3472 Ok(Flow::Straight)
3473 }
3474 Op::CallBuiltin {
3480 dst,
3481 builtin:
3482 b @ (BuiltinId::YearOf
3483 | BuiltinId::MonthOf
3484 | BuiltinId::DayOf
3485 | BuiltinId::WeekdayOf
3486 | BuiltinId::HourOf
3487 | BuiltinId::MinuteOf
3488 | BuiltinId::SecondOf
3489 | BuiltinId::WeekOf
3490 | BuiltinId::QuarterOf),
3491 args_start,
3492 ..
3493 } => {
3494 let which: i32 = match b {
3495 BuiltinId::YearOf => 0,
3496 BuiltinId::MonthOf => 1,
3497 BuiltinId::DayOf => 2,
3498 BuiltinId::HourOf => 3,
3499 BuiltinId::MinuteOf => 4,
3500 BuiltinId::SecondOf => 5,
3501 BuiltinId::WeekdayOf => 6,
3502 BuiltinId::WeekOf => 7,
3503 BuiltinId::QuarterOf => 8,
3504 _ => unreachable!(),
3505 };
3506 match kinds.get(args_start as usize) {
3507 Some(Kind::Moment) => {
3508 let idx = (ctx.host_index)(HostFn::TemporalComponent).ok_or(WasmLowerError::Unsupported("temporal_component host not imported"))?;
3509 local_get(code, args_start as u32); i32_const(code, which);
3511 code.push(0x10); leb_u32(code, idx);
3513 local_set(code, dst as u32);
3514 }
3515 Some(Kind::Date) => {
3519 if matches!(b, BuiltinId::HourOf | BuiltinId::MinuteOf | BuiltinId::SecondOf) {
3520 return Err(WasmLowerError::Unsupported("clock component (hour/minute/second) of a Date — a Date has no time-of-day"));
3521 }
3522 let idx = (ctx.host_index)(HostFn::TemporalComponentDate).ok_or(WasmLowerError::Unsupported("temporal_component_date host not imported"))?;
3523 local_get(code, args_start as u32); i32_const(code, which);
3525 code.push(0x10); leb_u32(code, idx);
3527 local_set(code, dst as u32);
3528 }
3529 _ => {
3530 return Err(WasmLowerError::Unsupported("temporal component of a non-temporal value"));
3531 }
3532 }
3533 Ok(Flow::Straight)
3534 }
3535 Op::CallBuiltin {
3545 dst,
3546 builtin: b @ (BuiltinId::SecondsBetween | BuiltinId::AddSeconds | BuiltinId::DateOf | BuiltinId::TimeOf),
3547 args_start,
3548 ..
3549 } => {
3550 const NANOS_PER_DAY: i64 = 86_400_000_000_000;
3551 const NANOS_PER_SEC: i64 = 1_000_000_000;
3552 if kinds.get(args_start as usize) != Some(Kind::Moment) {
3553 return Err(WasmLowerError::Unsupported("temporal arithmetic requires a Moment argument"));
3554 }
3555 let i64c = |code: &mut Vec<u8>, v: i64| {
3556 code.push(0x42); leb_i64(code, v);
3558 };
3559 match b {
3560 BuiltinId::SecondsBetween => {
3561 local_get(code, (args_start + 1) as u32); local_get(code, args_start as u32); code.push(0x7D); i64c(code, NANOS_PER_SEC);
3565 code.push(0x7F); }
3567 BuiltinId::AddSeconds => {
3568 local_get(code, args_start as u32); local_get(code, (args_start + 1) as u32); i64c(code, NANOS_PER_SEC);
3571 code.push(0x7E); code.push(0x7C); }
3574 BuiltinId::DateOf => {
3575 local_get(code, args_start as u32);
3576 i64c(code, NANOS_PER_DAY);
3577 code.push(0x7F); local_get(code, args_start as u32);
3579 i64c(code, NANOS_PER_DAY);
3580 code.push(0x81); i64c(code, 0);
3582 code.push(0x53); code.push(0xAD); code.push(0x7D); code.push(0xA7); }
3587 BuiltinId::TimeOf => {
3588 local_get(code, args_start as u32);
3589 i64c(code, NANOS_PER_DAY);
3590 code.push(0x81); local_get(code, args_start as u32);
3592 i64c(code, NANOS_PER_DAY);
3593 code.push(0x81); i64c(code, 0);
3595 code.push(0x53); code.push(0xAD); i64c(code, NANOS_PER_DAY);
3598 code.push(0x7E); code.push(0x7C); }
3601 _ => unreachable!(),
3602 }
3603 local_set(code, dst as u32);
3604 Ok(Flow::Straight)
3605 }
3606 Op::CallBuiltin {
3611 dst,
3612 builtin: b @ (BuiltinId::FormatTimestamp | BuiltinId::MonthsBetween | BuiltinId::YearsBetween | BuiltinId::InZone | BuiltinId::LocalInstant),
3613 args_start,
3614 ..
3615 } => {
3616 if !ctx.linked {
3617 return Err(WasmLowerError::Unsupported("format_timestamp/months_between/years_between/in_zone/local_instant need the linked runtime (base::temporal)"));
3618 }
3619 if kinds.get(args_start as usize) != Some(Kind::Moment) {
3620 return Err(WasmLowerError::Unsupported("extended temporal requires a Moment argument"));
3621 }
3622 let (rt, two_args) = match b {
3623 BuiltinId::FormatTimestamp => (HostFn::FormatTimestampRt, false),
3624 BuiltinId::MonthsBetween => (HostFn::MonthsBetweenRt, true),
3625 BuiltinId::YearsBetween => (HostFn::YearsBetweenRt, true),
3626 BuiltinId::InZone => (HostFn::InZoneRt, true),
3629 BuiltinId::LocalInstant => (HostFn::LocalInstantRt, true),
3630 _ => unreachable!(),
3631 };
3632 if matches!(b, BuiltinId::InZone | BuiltinId::LocalInstant)
3633 && kinds.get((args_start + 1) as usize) != Some(Kind::Text)
3634 {
3635 return Err(WasmLowerError::Unsupported("in_zone/local_instant require a Text zone name"));
3636 }
3637 let idx = (ctx.host_index)(rt).ok_or(WasmLowerError::Unsupported("extended temporal runtime fn not imported"))?;
3638 local_get(code, args_start as u32);
3639 if two_args {
3640 local_get(code, (args_start + 1) as u32);
3641 }
3642 code.push(0x10); leb_u32(code, idx);
3644 local_set(code, dst as u32);
3645 Ok(Flow::Straight)
3646 }
3647 Op::CallBuiltin { dst, builtin: b, args_start, .. } if ctx.linked && lanes_v_host_fn(b).is_some() => {
3653 let rt = lanes_v_host_fn(b).expect("guarded by lanes_v_host_fn(b).is_some()");
3654 let idx = (ctx.host_index)(rt).ok_or(WasmLowerError::Unsupported("lane-vector runtime fn not imported"))?;
3655 let two = matches!(
3656 b,
3657 BuiltinId::Shuffle16
3658 | BuiltinId::InterleaveLo16
3659 | BuiltinId::InterleaveHi16
3660 | BuiltinId::ByteAdd16
3661 | BuiltinId::Maddubs16
3662 | BuiltinId::Packus16
3663 | BuiltinId::ShrBytes16
3664 );
3665 local_get(code, args_start as u32);
3666 if two {
3667 local_get(code, (args_start + 1) as u32);
3668 }
3669 code.push(0x10); leb_u32(code, idx);
3671 local_set(code, dst as u32);
3672 Ok(Flow::Straight)
3673 }
3674 Op::CallBuiltin { dst, builtin: BuiltinId::SetRate, args_start, .. } if ctx.linked => {
3678 let set_idx = (ctx.host_index)(HostFn::MoneySetRate).ok_or(WasmLowerError::Unsupported("set_rate runtime fn not imported"))?;
3679 local_get(code, args_start as u32); match kinds.get((args_start + 1) as usize) {
3681 Some(Kind::Rational) => local_get(code, (args_start + 1) as u32),
3682 Some(Kind::Int) => {
3683 let c = (ctx.host_index)(HostFn::RationalFromI64).ok_or(WasmLowerError::Unsupported("rational_from_i64 not imported"))?;
3684 local_get(code, (args_start + 1) as u32);
3685 code.push(0x10);
3686 leb_u32(code, c);
3687 }
3688 Some(Kind::Decimal) => {
3689 let c = (ctx.host_index)(HostFn::DecimalToRational).ok_or(WasmLowerError::Unsupported("decimal_to_rational not imported"))?;
3690 local_get(code, (args_start + 1) as u32);
3691 code.push(0x10);
3692 leb_u32(code, c);
3693 }
3694 _ => return Err(WasmLowerError::Unsupported("set_rate rate must be an Int, Decimal, or Rational")),
3695 }
3696 code.push(0x10); leb_u32(code, set_idx);
3698 local_set(code, dst as u32);
3699 Ok(Flow::Straight)
3700 }
3701 Op::CallBuiltin { dst, builtin: BuiltinId::ToCurrency, args_start, .. } if ctx.linked => {
3703 let idx = (ctx.host_index)(HostFn::MoneyToCurrency).ok_or(WasmLowerError::Unsupported("to_currency runtime fn not imported"))?;
3704 local_get(code, args_start as u32); local_get(code, (args_start + 1) as u32); code.push(0x10);
3707 leb_u32(code, idx);
3708 local_set(code, dst as u32);
3709 Ok(Flow::Straight)
3710 }
3711 Op::CallBuiltin { dst, builtin: BuiltinId::SetRates, args_start, .. } if ctx.linked => {
3714 let vk = set_rates_value_kind(&plan.ops, args_start, kinds);
3715 let rt = match vk {
3716 Some(Kind::Int) => HostFn::MoneySetRatesInt,
3717 Some(Kind::Rational) => HostFn::MoneySetRatesRational,
3718 Some(Kind::Decimal) => HostFn::MoneySetRatesDecimal,
3719 _ => return Err(WasmLowerError::Unsupported("set_rates needs a Map of currency code to an Int/Decimal/Rational rate whose value kind is statically known")),
3720 };
3721 let idx = (ctx.host_index)(rt).ok_or(WasmLowerError::Unsupported("set_rates runtime fn not imported"))?;
3722 local_get(code, args_start as u32); code.push(0x10);
3724 leb_u32(code, idx);
3725 local_set(code, dst as u32);
3726 Ok(Flow::Straight)
3727 }
3728 Op::CallBuiltin { dst, builtin: BuiltinId::WireBytes, args_start, .. } if ctx.linked => {
3734 let h = wire_bytes_host_fn(kinds.get(args_start as usize))
3735 .ok_or(WasmLowerError::Unsupported("wireBytes of this value kind is not yet reconstructed for the wire codec"))?;
3736 let idx = (ctx.host_index)(h).ok_or(WasmLowerError::Unsupported("wire_bytes runtime fn not imported"))?;
3737 local_get(code, args_start as u32); code.push(0x10);
3739 leb_u32(code, idx);
3740 local_set(code, dst as u32);
3741 Ok(Flow::Straight)
3742 }
3743 Op::CallBuiltin { dst, builtin: BuiltinId::ReadWireProgram, .. } if ctx.linked => {
3748 const WIRE_BUF: i32 = 1 << 16;
3749 let frame = (ctx.host_index)(HostFn::ReadWireFrame).ok_or(WasmLowerError::Unsupported("read_wire_frame host not imported"))?;
3750 let decode = (ctx.host_index)(HostFn::ReadWireProgramRt).ok_or(WasmLowerError::Unsupported("read_wire_program runtime fn not imported"))?;
3751 let buf = plan.num_regs + 8; i32_const(code, WIRE_BUF);
3753 emit_alloc(code, ctx, buf); local_get(code, buf); local_get(code, buf);
3756 i32_const(code, WIRE_BUF);
3757 code.push(0x10); leb_u32(code, frame);
3759 code.push(0x10); leb_u32(code, decode);
3761 local_set(code, dst as u32);
3762 Ok(Flow::Straight)
3763 }
3764 Op::CallBuiltin { dst, builtin: BuiltinId::RunAccepted, args_start, .. } if ctx.linked => {
3769 if kinds.get(args_start as usize) != Some(Kind::Dynamic) {
3770 return Err(WasmLowerError::Unsupported("run_accepted requires a wire-received (dynamic) shipped function"));
3771 }
3772 let idx = (ctx.host_index)(HostFn::RunAccepted).ok_or(WasmLowerError::Unsupported("run_accepted runtime fn not imported"))?;
3773 local_get(code, args_start as u32); local_get(code, (args_start + 1) as u32); local_get(code, (args_start + 2) as u32); local_get(code, (args_start + 3) as u32); code.push(0x10); leb_u32(code, idx);
3779 local_set(code, dst as u32);
3780 Ok(Flow::Straight)
3781 }
3782 Op::CallBuiltin { dst, builtin: BuiltinId::Format, args_start, arg_count } => {
3785 if arg_count == 0 {
3786 lower_text_literal(code, ctx, plan.num_regs, b""); local_set(code, dst as u32);
3788 } else {
3789 emit_stringify(code, ctx, plan.num_regs, args_start as u32, kinds.get(args_start as usize), dst as u32)?;
3791 }
3792 Ok(Flow::Straight)
3793 }
3794 Op::CallBuiltin { dst, builtin: BuiltinId::RepeatSeq, args_start, arg_count } => {
3796 if arg_count != 2 {
3797 return Err(WasmLowerError::Unsupported("repeatSeq arity"));
3798 }
3799 lower_repeat_seq(code, kinds, ctx, plan.num_regs, dst, args_start)?;
3800 Ok(Flow::Straight)
3801 }
3802 Op::CallBuiltin { dst, builtin: BuiltinId::Money, args_start, arg_count } if ctx.linked && arg_count == 2 => {
3805 if kinds.get((args_start + 1) as usize) != Some(Kind::Text) {
3806 return Err(WasmLowerError::Unsupported("money(amount, currency) with a non-Text currency"));
3807 }
3808 let host = match kinds.get(args_start as usize) {
3809 Some(Kind::Decimal) => HostFn::MoneyFromDecimal,
3810 Some(Kind::Int) => HostFn::MoneyFromI64,
3811 _ => return Err(WasmLowerError::Unsupported("money amount must be an Int or Decimal")),
3812 };
3813 let make = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("money constructor not imported"))?;
3814 local_get(code, args_start as u32);
3815 local_get(code, (args_start + 1) as u32);
3816 code.push(0x10);
3817 leb_u32(code, make);
3818 local_set(code, dst as u32);
3819 return Ok(Flow::Straight);
3820 }
3821 Op::CallBuiltin { dst, builtin: BuiltinId::Quantity, args_start, arg_count } if ctx.linked && arg_count == 2 => {
3824 if kinds.get((args_start + 1) as usize) != Some(Kind::Text) {
3825 return Err(WasmLowerError::Unsupported("quantity(value, unit) with a non-Text unit"));
3826 }
3827 if kinds.get(args_start as usize) != Some(Kind::Int) {
3828 return Err(WasmLowerError::Unsupported("quantity magnitude must be an Int"));
3829 }
3830 let make = (ctx.host_index)(HostFn::QuantityOfI64).ok_or(WasmLowerError::Unsupported("quantity constructor not imported"))?;
3831 local_get(code, args_start as u32);
3832 local_get(code, (args_start + 1) as u32);
3833 code.push(0x10);
3834 leb_u32(code, make);
3835 local_set(code, dst as u32);
3836 return Ok(Flow::Straight);
3837 }
3838 Op::CallBuiltin { dst, builtin: BuiltinId::Convert, args_start, arg_count } if ctx.linked && arg_count == 2 => {
3841 if kinds.get(args_start as usize) != Some(Kind::Quantity) {
3842 return Err(WasmLowerError::Unsupported("convert() requires a Quantity"));
3843 }
3844 if kinds.get((args_start + 1) as usize) != Some(Kind::Text) {
3845 return Err(WasmLowerError::Unsupported("convert(q, unit) with a non-Text unit"));
3846 }
3847 let make = (ctx.host_index)(HostFn::QuantityConvert).ok_or(WasmLowerError::Unsupported("quantity convert not imported"))?;
3848 local_get(code, args_start as u32);
3849 local_get(code, (args_start + 1) as u32);
3850 code.push(0x10);
3851 leb_u32(code, make);
3852 local_set(code, dst as u32);
3853 return Ok(Flow::Straight);
3854 }
3855 Op::CallBuiltin { dst, builtin: BuiltinId::Decimal, args_start, arg_count } if ctx.linked && arg_count == 1 => {
3857 if kinds.get(args_start as usize) != Some(Kind::Text) {
3858 return Err(WasmLowerError::Unsupported("decimal(x) with a non-Text argument"));
3859 }
3860 let from = (ctx.host_index)(HostFn::DecimalFromText).ok_or(WasmLowerError::Unsupported("decimal_from_text not imported"))?;
3861 local_get(code, args_start as u32);
3862 code.push(0x10);
3863 leb_u32(code, from);
3864 local_set(code, dst as u32);
3865 return Ok(Flow::Straight);
3866 }
3867 Op::CallBuiltin { dst, builtin: BuiltinId::Modular, args_start, arg_count } if ctx.linked && arg_count == 2 => {
3869 if kinds.get(args_start as usize) != Some(Kind::Int) || kinds.get((args_start + 1) as usize) != Some(Kind::Int) {
3870 return Err(WasmLowerError::Unsupported("modular(v, n) with non-Int components"));
3871 }
3872 let from = (ctx.host_index)(HostFn::ModularFromI64).ok_or(WasmLowerError::Unsupported("modular_from_i64 not imported"))?;
3873 local_get(code, args_start as u32);
3874 local_get(code, (args_start + 1) as u32);
3875 code.push(0x10);
3876 leb_u32(code, from);
3877 local_set(code, dst as u32);
3878 return Ok(Flow::Straight);
3879 }
3880 Op::CallBuiltin { dst, builtin: BuiltinId::Complex, args_start, arg_count } if ctx.linked && arg_count == 2 => {
3881 if kinds.get(args_start as usize) != Some(Kind::Int) || kinds.get((args_start + 1) as usize) != Some(Kind::Int) {
3882 return Err(WasmLowerError::Unsupported("complex(re, im) with non-Int components"));
3883 }
3884 let from = (ctx.host_index)(HostFn::ComplexFromI64).ok_or(WasmLowerError::Unsupported("complex_from_i64 not imported"))?;
3885 local_get(code, args_start as u32);
3886 local_get(code, (args_start + 1) as u32);
3887 code.push(0x10); leb_u32(code, from);
3889 local_set(code, dst as u32);
3890 Ok(Flow::Straight)
3891 }
3892 Op::CallBuiltin { dst, builtin: b @ (BuiltinId::Floor | BuiltinId::Ceil | BuiltinId::Round | BuiltinId::Abs), args_start, .. }
3896 if ctx.linked && kinds.get(args_start as usize) == Some(Kind::Rational) =>
3897 {
3898 let host = match b {
3899 BuiltinId::Floor => HostFn::RationalFloor,
3900 BuiltinId::Ceil => HostFn::RationalCeil,
3901 BuiltinId::Round => HostFn::RationalRound,
3902 _ => HostFn::RationalAbs,
3903 };
3904 lower_rational_unary(code, ctx, dst, args_start, host)
3905 }
3906 Op::CallBuiltin { dst, builtin: b @ (BuiltinId::Uuid | BuiltinId::UuidNil | BuiltinId::UuidMax | BuiltinId::UuidDns | BuiltinId::UuidUrl | BuiltinId::UuidOid | BuiltinId::UuidX500 | BuiltinId::UuidVersion), args_start, .. }
3909 if ctx.linked =>
3910 {
3911 let host = match b {
3912 BuiltinId::Uuid => HostFn::UuidParse,
3913 BuiltinId::UuidNil => HostFn::UuidNil,
3914 BuiltinId::UuidMax => HostFn::UuidMax,
3915 BuiltinId::UuidDns => HostFn::UuidDns,
3916 BuiltinId::UuidUrl => HostFn::UuidUrl,
3917 BuiltinId::UuidOid => HostFn::UuidOid,
3918 BuiltinId::UuidX500 => HostFn::UuidX500,
3919 _ => HostFn::UuidVersion,
3920 };
3921 let idx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("uuid builtin not imported"))?;
3922 if matches!(b, BuiltinId::Uuid | BuiltinId::UuidVersion) {
3924 local_get(code, args_start as u32);
3925 }
3926 code.push(0x10);
3927 leb_u32(code, idx);
3928 local_set(code, dst as u32);
3929 return Ok(Flow::Straight);
3930 }
3931 Op::CallBuiltin { dst, builtin: BuiltinId::TextBytes, args_start, .. } => {
3935 lower_text_bytes(code, ctx, plan.num_regs, dst, args_start);
3936 return Ok(Flow::Straight);
3937 }
3938 Op::CallBuiltin { dst, builtin: BuiltinId::UuidBytes, args_start, .. } if ctx.linked => {
3939 lower_uuid_bytes(code, ctx, plan.num_regs, dst, args_start);
3940 return Ok(Flow::Straight);
3941 }
3942 Op::CallBuiltin { dst, builtin: BuiltinId::TextFromBytes, args_start, .. } => {
3943 lower_text_from_bytes(code, ctx, plan.num_regs, dst, args_start);
3944 return Ok(Flow::Straight);
3945 }
3946 Op::CallBuiltin { dst, builtin: BuiltinId::UuidFromBytes, args_start, .. } if ctx.linked => {
3947 return lower_uuid_from_bytes(code, ctx, plan.num_regs, dst, args_start);
3948 }
3949 Op::CallBuiltin { dst, builtin: BuiltinId::Lanes4Of, args_start, .. } if ctx.linked => {
3952 lower_lanes4_of(code, ctx, plan.num_regs, dst, [args_start, args_start + 1, args_start + 2, args_start + 3]);
3953 return Ok(Flow::Straight);
3954 }
3955 Op::CallBuiltin { dst, builtin: BuiltinId::Lanes4Word32Make, args_start, .. } if ctx.linked => {
3956 lower_lanes4_word32(code, ctx, plan.num_regs, dst, args_start);
3957 return Ok(Flow::Straight);
3958 }
3959 Op::CallBuiltin { dst, builtin: BuiltinId::SeqOfLanes4W32, args_start, .. } if ctx.linked => {
3960 lower_seq_of_lanes4(code, ctx, plan.num_regs, dst, args_start);
3961 return Ok(Flow::Straight);
3962 }
3963 Op::CallBuiltin { dst, builtin: b @ (BuiltinId::Sha1Rnds4 | BuiltinId::Sha1Msg1 | BuiltinId::Sha1Msg2 | BuiltinId::Sha1Nexte), args_start, .. } if ctx.linked => {
3964 let (host, ternary) = match b {
3965 BuiltinId::Sha1Rnds4 => (HostFn::Sha1Rnds4, true),
3966 BuiltinId::Sha1Msg1 => (HostFn::Sha1Msg1, false),
3967 BuiltinId::Sha1Msg2 => (HostFn::Sha1Msg2, false),
3968 _ => (HostFn::Sha1Nexte, false),
3969 };
3970 return lower_sha1_op(code, ctx, dst, args_start, host, ternary);
3971 }
3972 Op::CallBuiltin { dst, builtin, args_start, arg_count } => {
3973 lower_builtin(code, kinds, dst, builtin, args_start, arg_count)
3974 }
3975 Op::Args { dst } => {
3977 let idx = (ctx.host_index)(HostFn::Args).ok_or(WasmLowerError::Unsupported("args host not imported"))?;
3978 code.push(0x10); leb_u32(code, idx);
3980 local_set(code, dst as u32);
3981 Ok(Flow::Straight)
3982 }
3983 Op::Show { src } => {
3984 if plan.structs.tuple_layouts.contains_key(&src) {
3991 lower_show_tuple(code, plan, ctx, src)?;
3992 return Ok(Flow::Straight);
3993 }
3994 let kind = kinds.get(src as usize).ok_or(WasmLowerError::Unsupported("Show of an unknown-kind value"))?;
3995 if kind == Kind::Enum {
3998 lower_show_enum(code, plan, ctx, src)?;
3999 return Ok(Flow::Straight);
4000 }
4001 if kind == Kind::Struct {
4004 lower_show_struct(code, plan, ctx, src)?;
4005 return Ok(Flow::Straight);
4006 }
4007 if kind == Kind::SeqStruct {
4010 lower_show_seqstruct(code, plan, ctx, src)?;
4011 return Ok(Flow::Straight);
4012 }
4013 if kind == Kind::SeqSeqInt {
4020 lower_show_seqseq(code, ctx, plan.num_regs, src)?;
4021 return Ok(Flow::Straight);
4022 }
4023 if kind == Kind::SeqEnum {
4026 lower_show_seqenum(code, plan, ctx, src)?;
4027 return Ok(Flow::Straight);
4028 }
4029 if kind == Kind::Map {
4030 lower_show_map(code, plan, kinds, ctx, src)?;
4031 return Ok(Flow::Straight);
4032 }
4033 if kind == Kind::Rational {
4038 if ctx.linked {
4039 let to_text = (ctx.host_index)(HostFn::RationalToText).ok_or(WasmLowerError::Unsupported("rational_to_text not imported"))?;
4040 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4041 local_get(code, src as u32);
4042 code.push(0x10);
4043 leb_u32(code, to_text);
4044 code.push(0x10);
4045 leb_u32(code, print_text);
4046 return Ok(Flow::Straight);
4047 }
4048 let idx = (ctx.host_index)(HostFn::PrintRational).ok_or(WasmLowerError::Unsupported("Show sink not imported"))?;
4049 local_get(code, src as u32);
4050 i64_load(code, 0);
4051 local_get(code, src as u32);
4052 i64_load(code, 8);
4053 code.push(0x10); leb_u32(code, idx);
4055 return Ok(Flow::Straight);
4056 }
4057 if kind == Kind::BigInt {
4061 let to_text = (ctx.host_index)(HostFn::BigintToText).ok_or(WasmLowerError::Unsupported("bigint_to_text not imported"))?;
4062 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4063 local_get(code, src as u32); code.push(0x10);
4065 leb_u32(code, to_text); code.push(0x10);
4067 leb_u32(code, print_text); return Ok(Flow::Straight);
4069 }
4070 if kind == Kind::Complex {
4072 let to_text = (ctx.host_index)(HostFn::ComplexToText).ok_or(WasmLowerError::Unsupported("complex_to_text not imported"))?;
4073 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4074 local_get(code, src as u32); code.push(0x10);
4076 leb_u32(code, to_text); code.push(0x10);
4078 leb_u32(code, print_text);
4079 return Ok(Flow::Straight);
4080 }
4081 if kind == Kind::Modular {
4083 let to_text = (ctx.host_index)(HostFn::ModularToText).ok_or(WasmLowerError::Unsupported("modular_to_text not imported"))?;
4084 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4085 local_get(code, src as u32);
4086 code.push(0x10);
4087 leb_u32(code, to_text);
4088 code.push(0x10);
4089 leb_u32(code, print_text);
4090 return Ok(Flow::Straight);
4091 }
4092 if kind == Kind::Decimal {
4093 let to_text = (ctx.host_index)(HostFn::DecimalToText).ok_or(WasmLowerError::Unsupported("decimal_to_text not imported"))?;
4094 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4095 local_get(code, src as u32);
4096 code.push(0x10);
4097 leb_u32(code, to_text);
4098 code.push(0x10);
4099 leb_u32(code, print_text);
4100 return Ok(Flow::Straight);
4101 }
4102 if kind == Kind::Money {
4103 let to_text = (ctx.host_index)(HostFn::MoneyToText).ok_or(WasmLowerError::Unsupported("money_to_text not imported"))?;
4104 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4105 local_get(code, src as u32);
4106 code.push(0x10);
4107 leb_u32(code, to_text);
4108 code.push(0x10);
4109 leb_u32(code, print_text);
4110 return Ok(Flow::Straight);
4111 }
4112 if kind == Kind::Quantity {
4115 let to_text = (ctx.host_index)(HostFn::QuantityToText).ok_or(WasmLowerError::Unsupported("quantity_to_text not imported"))?;
4116 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4117 local_get(code, src as u32);
4118 code.push(0x10);
4119 leb_u32(code, to_text);
4120 code.push(0x10);
4121 leb_u32(code, print_text);
4122 return Ok(Flow::Straight);
4123 }
4124 if kind == Kind::Uuid {
4126 let to_text = (ctx.host_index)(HostFn::UuidToText).ok_or(WasmLowerError::Unsupported("uuid_to_text not imported"))?;
4127 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4128 local_get(code, src as u32);
4129 code.push(0x10);
4130 leb_u32(code, to_text);
4131 code.push(0x10);
4132 leb_u32(code, print_text);
4133 return Ok(Flow::Straight);
4134 }
4135 if kind == Kind::Dynamic {
4137 let to_text = (ctx.host_index)(HostFn::DynamicToText).ok_or(WasmLowerError::Unsupported("dynamic_to_text not imported"))?;
4138 let print_text = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("print_text not imported"))?;
4139 local_get(code, src as u32);
4140 code.push(0x10);
4141 leb_u32(code, to_text);
4142 code.push(0x10);
4143 leb_u32(code, print_text);
4144 return Ok(Flow::Straight);
4145 }
4146 if kind == Kind::Optional {
4150 let nothing = (ctx.host_index)(HostFn::PrintNothing).ok_or(WasmLowerError::Unsupported("Show sink not imported"))?;
4151 let inner = plan.structs.opt_inner.get(&src).copied().unwrap_or(Kind::Int);
4152 let some_host = HostFn::for_show(inner).ok_or(WasmLowerError::Unsupported("Show of an optional with a non-scalar inner"))?;
4153 let some_idx = (ctx.host_index)(some_host).ok_or(WasmLowerError::Unsupported("Show sink not imported"))?;
4154 local_get(code, src as u32);
4155 code.push(0x45); code.push(0x04);
4157 code.push(0x40); code.push(0x10); leb_u32(code, nothing);
4160 code.push(0x05); local_get(code, src as u32);
4162 emit_slot_load(code, Some(inner), 0)?; if inner == Kind::Bool {
4164 code.push(0xA7); }
4166 code.push(0x10); leb_u32(code, some_idx);
4168 code.push(0x0B); return Ok(Flow::Straight);
4170 }
4171 if kind == Kind::Word32 || kind == Kind::Word64 {
4174 let idx = (ctx.host_index)(HostFn::PrintWord).ok_or(WasmLowerError::Unsupported("Show sink not imported"))?;
4175 local_get(code, src as u32);
4176 if kind == Kind::Word32 {
4177 code.push(0xAD); }
4179 code.push(0x10); leb_u32(code, idx);
4181 return Ok(Flow::Straight);
4182 }
4183 let host = HostFn::for_show(kind).ok_or(WasmLowerError::Unsupported("Show of a non-scalar value"))?;
4184 let idx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("Show sink not imported"))?;
4185 local_get(code, src as u32);
4186 if kind == Kind::Bool {
4187 code.push(0xA7); }
4189 code.push(0x10); leb_u32(code, idx);
4191 Ok(Flow::Straight)
4192 }
4193 Op::Return { src } => {
4194 if plan.result.is_none() {
4195 return Err(WasmLowerError::Unsupported("value return from a void function"));
4196 }
4197 local_get(code, src as u32);
4198 code.push(0x0F); Ok(Flow::Terminated)
4200 }
4201 Op::ReturnNothing => {
4202 match plan.result {
4203 None => code.push(0x0F), Some(_) => code.push(0x00), }
4206 Ok(Flow::Terminated)
4207 }
4208 Op::Halt => {
4209 code.push(0x0F); Ok(Flow::Terminated)
4211 }
4212 Op::FailWith { .. } => {
4217 code.push(0x00); Ok(Flow::Terminated)
4219 }
4220 other => Err(unsupported_op(&other)),
4221 }
4222}
4223
4224#[derive(Clone, Copy)]
4225enum ArithOp {
4226 Add,
4227 Sub,
4228 Mul,
4229 Div,
4230 Mod,
4231}
4232
4233fn lower_arith(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, lhs: u16, rhs: u16, op: ArithOp) -> R<Flow> {
4237 match kinds.get(dst as usize) {
4238 Some(Kind::Int) => {
4239 if kinds.valtype(lhs as usize) != I64 || kinds.valtype(rhs as usize) != I64 {
4242 return Err(WasmLowerError::Unsupported("integer arithmetic with a non-integer operand"));
4243 }
4244 match op {
4245 ArithOp::Add => emit_checked_addsub(code, false, dst, lhs, rhs),
4246 ArithOp::Sub => emit_checked_addsub(code, true, dst, lhs, rhs),
4247 ArithOp::Mul => emit_checked_mul(code, dst, lhs, rhs),
4248 ArithOp::Div => arith(code, 0x7F, dst, lhs, rhs), ArithOp::Mod => arith(code, 0x81, dst, lhs, rhs), }
4251 }
4252 Some(Kind::Word32) => {
4255 let opcode = match op {
4256 ArithOp::Add => 0x6A, ArithOp::Sub => 0x6B, ArithOp::Mul => 0x6C, ArithOp::Div => 0x6E, ArithOp::Mod => 0x70, };
4262 arith(code, opcode, dst, lhs, rhs);
4263 }
4264 Some(Kind::Word64) => {
4265 let opcode = match op {
4266 ArithOp::Add => 0x7C, ArithOp::Sub => 0x7D, ArithOp::Mul => 0x7E, ArithOp::Div => 0x80, ArithOp::Mod => 0x82, };
4272 arith(code, opcode, dst, lhs, rhs);
4273 }
4274 Some(Kind::Float) => {
4275 let opcode = match op {
4276 ArithOp::Add => 0xA0,
4277 ArithOp::Sub => 0xA1,
4278 ArithOp::Mul => 0xA2,
4279 ArithOp::Div => 0xA3,
4280 ArithOp::Mod => return Err(WasmLowerError::Unsupported("float modulo")),
4281 };
4282 push_as_f64(code, lhs, kinds.get(lhs as usize))?;
4285 push_as_f64(code, rhs, kinds.get(rhs as usize))?;
4286 code.push(opcode);
4287 local_set(code, dst as u32);
4288 }
4289 Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Moment) => {
4293 let opcode = match op {
4294 ArithOp::Add => 0x7C, ArithOp::Sub => 0x7D, _ => return Err(WasmLowerError::Unsupported("only + and - on temporal values")),
4297 };
4298 arith(code, opcode, dst, lhs, rhs);
4299 }
4300 _ => return Err(WasmLowerError::Unsupported("arithmetic on a non-numeric value")),
4301 }
4302 Ok(Flow::Straight)
4303}
4304
4305#[derive(Clone, Copy)]
4306enum Cmp {
4307 Lt,
4308 Gt,
4309 Le,
4310 Ge,
4311 Eq,
4312 Ne,
4313}
4314
4315fn lower_compare(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, lhs: u16, rhs: u16, cmp: Cmp) -> R<Flow> {
4318 let lf = kinds.valtype(lhs as usize) == F64;
4319 let rf = kinds.valtype(rhs as usize) == F64;
4320 if lf || rf {
4321 if lf != rf {
4328 if matches!(cmp, Cmp::Eq | Cmp::Ne) {
4329 let (int_reg, float_reg) = if lf { (rhs, lhs) } else { (lhs, rhs) };
4330 local_get(code, int_reg as u32);
4332 code.push(0xB9); local_get(code, float_reg as u32);
4334 code.push(0x61); local_get(code, int_reg as u32);
4337 local_get(code, float_reg as u32);
4338 code.push(0xFC); code.push(0x06); code.push(0x51); code.push(0x71); local_get(code, float_reg as u32);
4344 code.push(0x44); code.extend_from_slice(&9223372036854775808.0f64.to_le_bytes());
4346 code.push(0x63); code.push(0x71); if matches!(cmp, Cmp::Ne) {
4349 code.push(0x45); }
4351 code.push(0xAD); local_set(code, dst as u32);
4353 return Ok(Flow::Straight);
4354 }
4355 }
4356 let opcode = match cmp {
4357 Cmp::Lt => 0x63, Cmp::Gt => 0x64, Cmp::Le => 0x65, Cmp::Ge => 0x66, Cmp::Eq => 0x61, Cmp::Ne => 0x62, };
4364 push_as_f64(code, lhs, kinds.get(lhs as usize))?;
4365 push_as_f64(code, rhs, kinds.get(rhs as usize))?;
4366 code.push(opcode);
4367 code.push(0xAD); local_set(code, dst as u32);
4369 return Ok(Flow::Straight);
4370 }
4371 let (lk, rk) = (kinds.get(lhs as usize), kinds.get(rhs as usize));
4376 if lk == Some(Kind::Optional) || rk == Some(Kind::Optional) {
4377 let opt = if lk == Some(Kind::Optional) { lhs } else { rhs };
4378 let opcode = match cmp {
4379 Cmp::Eq => 0x46, Cmp::Ne => 0x47, _ => return Err(WasmLowerError::Unsupported("ordering comparison of optional values")),
4382 };
4383 local_get(code, opt as u32);
4384 i32_const(code, 0);
4385 code.push(opcode);
4386 code.push(0xAD); local_set(code, dst as u32);
4388 return Ok(Flow::Straight);
4389 }
4390 let operand = kinds.get(lhs as usize).or_else(|| kinds.get(rhs as usize));
4391 match operand {
4392 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Char) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) => {
4395 let opcode = match cmp {
4396 Cmp::Lt => 0x53, Cmp::Gt => 0x55, Cmp::Le => 0x57, Cmp::Ge => 0x59, Cmp::Eq => 0x51, Cmp::Ne => 0x52, };
4403 compare(code, opcode, dst, lhs, rhs);
4404 }
4405 Some(Kind::Word32) => {
4408 let opcode = match cmp {
4409 Cmp::Lt => 0x49, Cmp::Gt => 0x4B, Cmp::Le => 0x4D, Cmp::Ge => 0x4F, Cmp::Eq => 0x46, Cmp::Ne => 0x47, };
4416 compare(code, opcode, dst, lhs, rhs);
4417 }
4418 Some(Kind::Word64) => {
4419 let opcode = match cmp {
4420 Cmp::Lt => 0x54, Cmp::Gt => 0x56, Cmp::Le => 0x58, Cmp::Ge => 0x5A, Cmp::Eq => 0x51, Cmp::Ne => 0x52, };
4427 compare(code, opcode, dst, lhs, rhs);
4428 }
4429 Some(Kind::Float) => unreachable!("float comparison handled by the f64 promotion path"),
4431 Some(Kind::Date) | Some(Kind::Moment) => {
4432 return Err(WasmLowerError::Unsupported("comparison of temporal values"))
4433 }
4434 Some(Kind::SeqInt) | Some(Kind::SeqBool) | Some(Kind::SeqFloat) | Some(Kind::SeqText) | Some(Kind::SeqStruct) | Some(Kind::SeqEnum) | Some(Kind::SeqSeqInt) | Some(Kind::SeqAny) | Some(Kind::SeqWord32) | Some(Kind::SeqWord64) => {
4435 return Err(WasmLowerError::Unsupported("comparison of sequences"))
4436 }
4437 Some(Kind::Text) => return Err(WasmLowerError::Unsupported("comparison of text values")),
4438 Some(Kind::Struct) => return Err(WasmLowerError::Unsupported("comparison of struct values")),
4439 Some(Kind::Map) => return Err(WasmLowerError::Unsupported("comparison of map values")),
4440 Some(Kind::Set) | Some(Kind::SetText) | Some(Kind::CrdtSetText) => return Err(WasmLowerError::Unsupported("comparison of set values")),
4441 Some(Kind::Enum) => return Err(WasmLowerError::Unsupported("comparison of enum values")),
4442 Some(Kind::Closure) => return Err(WasmLowerError::Unsupported("comparison of closure values")),
4443 Some(Kind::Tuple) => return Err(WasmLowerError::Unsupported("comparison of tuple values")),
4444 Some(Kind::Rational) => return Err(WasmLowerError::Unsupported("comparison of rational values")),
4445 Some(Kind::BigInt) => return Err(WasmLowerError::Unsupported("comparison of bigint values")),
4446 Some(Kind::Complex) => return Err(WasmLowerError::Unsupported("comparison of complex values")),
4447 Some(Kind::Modular) => return Err(WasmLowerError::Unsupported("comparison of modular values")),
4448 Some(Kind::Decimal) => return Err(WasmLowerError::Unsupported("comparison of decimal values")),
4449 Some(Kind::Money) => return Err(WasmLowerError::Unsupported("comparison of money values")),
4450 Some(Kind::Quantity) => return Err(WasmLowerError::Unsupported("comparison of quantity values")),
4451 Some(Kind::Uuid) => return Err(WasmLowerError::Unsupported("ordering comparison of uuid values")),
4452 Some(Kind::LanesV) => return Err(WasmLowerError::Unsupported("comparison of lane-vector values")),
4453 Some(Kind::Dynamic) => return Err(WasmLowerError::Unsupported("comparison of dynamic wire values")),
4454 Some(Kind::Lanes) => return Err(WasmLowerError::Unsupported("comparison of lane vectors")),
4455 Some(Kind::Optional) => unreachable!("optional comparisons handled by the is-nothing path above"),
4458 None => return Err(WasmLowerError::Unsupported("comparison of unknown-kind values")),
4459 }
4460 Ok(Flow::Straight)
4461}
4462
4463fn lower_approx_eq(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, lhs: u16, rhs: u16) -> R<Flow> {
4470 let lk = kinds.get(lhs as usize);
4471 let rk = kinds.get(rhs as usize);
4472 push_as_f64(code, lhs, lk)?;
4474 push_as_f64(code, rhs, rk)?;
4475 code.push(0x61); push_as_f64(code, lhs, lk)?;
4478 push_as_f64(code, rhs, rk)?;
4479 code.push(0xA1); code.push(0x99); push_as_f64(code, lhs, lk)?;
4483 code.push(0x99); push_as_f64(code, rhs, rk)?;
4485 code.push(0x99); code.push(0xA5); code.push(0x44); code.extend_from_slice(&1e-9f64.to_le_bytes());
4489 code.push(0xA2); code.push(0x44); code.extend_from_slice(&1e-12f64.to_le_bytes());
4492 code.push(0xA5); code.push(0x65); code.push(0x72); code.push(0xAD); local_set(code, dst as u32);
4498 Ok(Flow::Straight)
4499}
4500
4501fn push_as_f64(code: &mut Vec<u8>, reg: u16, kind: Option<Kind>) -> R<()> {
4502 local_get(code, reg as u32);
4503 match kind {
4504 Some(Kind::Float) => {}
4505 Some(Kind::Int) => code.push(0xB9), _ => return Err(WasmLowerError::Unsupported("numeric builtin on a non-number")),
4507 }
4508 Ok(())
4509}
4510
4511fn lower_builtin(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, builtin: BuiltinId, args_start: u16, arg_count: u16) -> R<Flow> {
4513 let arg = args_start;
4514 let ak = kinds.get(arg as usize);
4515 match builtin {
4516 BuiltinId::Sqrt => {
4518 push_as_f64(code, arg, ak)?;
4519 code.push(0x9F); local_set(code, dst as u32);
4521 Ok(Flow::Straight)
4522 }
4523 BuiltinId::Floor => lower_floor_ceil(code, ak, dst, arg, 0x9C), BuiltinId::Ceil => lower_floor_ceil(code, ak, dst, arg, 0x9B), BuiltinId::Round => match ak {
4531 Some(Kind::Float) => {
4532 local_get(code, arg as u32);
4533 code.push(0x44); code.extend_from_slice(&0.5f64.to_le_bytes());
4535 local_get(code, arg as u32);
4536 code.push(0xA6); code.push(0xA0); code.push(0x9D); code.push(0xFC); leb_u32(code, 6); local_set(code, dst as u32);
4542 Ok(Flow::Straight)
4543 }
4544 Some(Kind::Int) => {
4545 local_get(code, arg as u32);
4546 local_set(code, dst as u32);
4547 Ok(Flow::Straight)
4548 }
4549 _ => Err(WasmLowerError::Unsupported("round of a non-number")),
4550 },
4551 BuiltinId::Abs => match ak {
4555 Some(Kind::Float) => {
4556 local_get(code, arg as u32);
4557 code.push(0x99); local_set(code, dst as u32);
4559 Ok(Flow::Straight)
4560 }
4561 Some(Kind::Int) => {
4562 local_get(code, arg as u32);
4564 code.push(0x42); leb_i64(code, i64::MIN);
4566 code.push(0x51); code.push(0x04);
4568 code.push(0x40); code.push(0x00); code.push(0x0B); code.push(0x42);
4573 leb_i64(code, 0); local_get(code, arg as u32);
4575 code.push(0x7D); local_get(code, arg as u32); local_get(code, arg as u32);
4578 code.push(0x42);
4579 leb_i64(code, 0);
4580 code.push(0x53); code.push(0x1B); local_set(code, dst as u32);
4583 Ok(Flow::Straight)
4584 }
4585 _ => Err(WasmLowerError::Unsupported("abs of a non-number")),
4586 },
4587 BuiltinId::Min => lower_minmax(code, kinds, dst, args_start, arg_count, false),
4588 BuiltinId::Max => lower_minmax(code, kinds, dst, args_start, arg_count, true),
4589 BuiltinId::CountOnes => {
4592 local_get(code, arg as u32);
4593 code.push(0x7B); local_set(code, dst as u32);
4595 Ok(Flow::Straight)
4596 }
4597 BuiltinId::Word32 => {
4601 local_get(code, arg as u32);
4602 code.push(0xA7); local_set(code, dst as u32);
4604 Ok(Flow::Straight)
4605 }
4606 BuiltinId::Word64 => {
4607 local_get(code, arg as u32);
4608 local_set(code, dst as u32);
4609 Ok(Flow::Straight)
4610 }
4611 BuiltinId::IntOfWord32 => {
4614 local_get(code, arg as u32);
4615 code.push(0xAD); local_set(code, dst as u32);
4617 Ok(Flow::Straight)
4618 }
4619 BuiltinId::IntOfWord64 => {
4620 local_get(code, arg as u32);
4621 local_set(code, dst as u32);
4622 Ok(Flow::Straight)
4623 }
4624 BuiltinId::Rotl | BuiltinId::Rotr => {
4627 let is_l = matches!(builtin, BuiltinId::Rotl);
4628 match kinds.get(args_start as usize) {
4629 Some(Kind::Word32) => {
4630 local_get(code, args_start as u32);
4631 local_get(code, (args_start + 1) as u32);
4632 code.push(0xA7); code.push(if is_l { 0x77 } else { 0x78 }); }
4635 Some(Kind::Word64) => {
4636 local_get(code, args_start as u32);
4637 local_get(code, (args_start + 1) as u32);
4638 code.push(if is_l { 0x89 } else { 0x8A }); }
4640 _ => return Err(WasmLowerError::Unsupported("rotate of a non-Word value")),
4641 }
4642 local_set(code, dst as u32);
4643 Ok(Flow::Straight)
4644 }
4645 BuiltinId::Wand | BuiltinId::Wor => {
4647 let is_and = matches!(builtin, BuiltinId::Wand);
4648 let op32 = if is_and { 0x71 } else { 0x72 }; let op64 = if is_and { 0x83 } else { 0x84 }; match kinds.get(args_start as usize) {
4651 Some(Kind::Word32) => {
4652 local_get(code, args_start as u32);
4653 local_get(code, (args_start + 1) as u32);
4654 code.push(op32);
4655 }
4656 Some(Kind::Word64) => {
4657 local_get(code, args_start as u32);
4658 local_get(code, (args_start + 1) as u32);
4659 code.push(op64);
4660 }
4661 _ => return Err(WasmLowerError::Unsupported("word_and/or of a non-Word value")),
4662 }
4663 local_set(code, dst as u32);
4664 Ok(Flow::Straight)
4665 }
4666 BuiltinId::Wnot => {
4668 match kinds.get(arg as usize) {
4669 Some(Kind::Word32) => {
4670 local_get(code, arg as u32);
4671 i32_const(code, -1);
4672 code.push(0x73); }
4674 Some(Kind::Word64) => {
4675 local_get(code, arg as u32);
4676 i64c(code, -1);
4677 code.push(0x85); }
4679 _ => return Err(WasmLowerError::Unsupported("word_not of a non-Word value")),
4680 }
4681 local_set(code, dst as u32);
4682 Ok(Flow::Straight)
4683 }
4684 BuiltinId::Word64Shl | BuiltinId::Word64Shr | BuiltinId::Word64And => {
4686 local_get(code, args_start as u32);
4687 local_get(code, (args_start + 1) as u32);
4688 code.push(match builtin {
4689 BuiltinId::Word64Shl => 0x86, BuiltinId::Word64Shr => 0x88, _ => 0x83, });
4693 local_set(code, dst as u32);
4694 Ok(Flow::Straight)
4695 }
4696 BuiltinId::Word32Shr => {
4699 local_get(code, args_start as u32);
4700 local_get(code, (args_start + 1) as u32);
4701 code.push(0xA7); code.push(0x76); local_set(code, dst as u32);
4704 Ok(Flow::Straight)
4705 }
4706 _ => Err(WasmLowerError::Unsupported("builtin not yet lowered")),
4707 }
4708}
4709
4710fn lower_floor_ceil(code: &mut Vec<u8>, arg_kind: Option<Kind>, dst: u16, arg: u16, fop: u8) -> R<Flow> {
4713 match arg_kind {
4714 Some(Kind::Float) => {
4715 local_get(code, arg as u32);
4716 code.push(fop); code.push(0xFC); leb_u32(code, 6); local_set(code, dst as u32);
4720 }
4721 Some(Kind::Int) => {
4722 local_get(code, arg as u32);
4723 local_set(code, dst as u32);
4724 }
4725 _ => return Err(WasmLowerError::Unsupported("floor/ceil of a non-number")),
4726 }
4727 Ok(Flow::Straight)
4728}
4729
4730fn lower_minmax(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, args_start: u16, arg_count: u16, is_max: bool) -> R<Flow> {
4735 if arg_count != 2 {
4736 return Err(WasmLowerError::Unsupported("min/max arity"));
4737 }
4738 let (a, b) = (args_start, args_start + 1);
4739 let (ak, bk) = (kinds.get(a as usize), kinds.get(b as usize));
4740 match (ak, bk) {
4741 (Some(Kind::Int), Some(Kind::Int)) => {
4742 local_get(code, a as u32); local_get(code, b as u32); local_get(code, a as u32);
4745 local_get(code, b as u32);
4746 code.push(if is_max { 0x55 } else { 0x53 }); code.push(0x1B); local_set(code, dst as u32);
4749 Ok(Flow::Straight)
4750 }
4751 (a_some, b_some) if a_some.is_some() && b_some.is_some() => {
4752 let fop = if is_max { 0x98 } else { 0x97 }; push_as_f64(code, b, bk)?; push_as_f64(code, a, ak)?; push_as_f64(code, a, ak)?;
4757 push_as_f64(code, b, bk)?;
4758 code.push(fop); push_as_f64(code, b, bk)?;
4760 push_as_f64(code, b, bk)?;
4761 code.push(0x62); code.push(0x1B); push_as_f64(code, a, ak)?;
4764 push_as_f64(code, a, ak)?;
4765 code.push(0x62); code.push(0x1B); local_set(code, dst as u32);
4768 Ok(Flow::Straight)
4769 }
4770 _ => Err(WasmLowerError::Unsupported("min/max of non-numbers")),
4771 }
4772}
4773
4774fn pow_host_for(base: Option<Kind>, exp: Option<Kind>) -> Option<HostFn> {
4778 match (base, exp) {
4779 (_, Some(Kind::Float)) => Some(HostFn::PowFf),
4780 (Some(Kind::Float), Some(Kind::Int)) => Some(HostFn::PowFi),
4781 _ => None,
4782 }
4783}
4784
4785fn lower_pow(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, args_start: u16, arg_count: u16) -> R<Flow> {
4790 if arg_count != 2 {
4791 return Err(WasmLowerError::Unsupported("pow arity"));
4792 }
4793 lower_pow_regs(code, kinds, ctx, num_regs, dst, args_start, args_start + 1)
4794}
4795
4796fn push_bigint_operand(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, reg: u16) -> R<()> {
4803 match kinds.get(reg as usize) {
4804 Some(Kind::BigInt) => local_get(code, reg as u32),
4805 Some(Kind::Int) => {
4806 let from_i64 = (ctx.host_index)(HostFn::BigintFromI64).ok_or(WasmLowerError::Unsupported("bigint_from_i64 not imported"))?;
4807 local_get(code, reg as u32);
4808 code.push(0x10);
4809 leb_u32(code, from_i64);
4810 }
4811 _ => return Err(WasmLowerError::Unsupported("bigint arithmetic operand must be a BigInt or an Int")),
4812 }
4813 Ok(())
4814}
4815
4816fn lower_bigint_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4820 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("bigint op not imported"))?;
4821 push_bigint_operand(code, kinds, ctx, lhs)?;
4822 push_bigint_operand(code, kinds, ctx, rhs)?;
4823 code.push(0x10);
4824 leb_u32(code, op);
4825 local_set(code, dst as u32);
4826 Ok(Flow::Straight)
4827}
4828
4829fn push_complex_operand(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, reg: u16) -> R<()> {
4832 match kinds.get(reg as usize) {
4833 Some(Kind::Complex) => local_get(code, reg as u32),
4834 Some(Kind::Int) => {
4835 let from = (ctx.host_index)(HostFn::ComplexFromI64).ok_or(WasmLowerError::Unsupported("complex_from_i64 not imported"))?;
4836 local_get(code, reg as u32);
4837 code.push(0x42); leb_i64(code, 0);
4839 code.push(0x10);
4840 leb_u32(code, from);
4841 }
4842 _ => return Err(WasmLowerError::Unsupported("complex arithmetic operand must be a Complex or an Int")),
4843 }
4844 Ok(())
4845}
4846
4847fn lower_complex_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4850 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("complex op not imported"))?;
4851 push_complex_operand(code, kinds, ctx, lhs)?;
4852 push_complex_operand(code, kinds, ctx, rhs)?;
4853 code.push(0x10);
4854 leb_u32(code, op);
4855 local_set(code, dst as u32);
4856 Ok(Flow::Straight)
4857}
4858
4859fn push_modular_operand(code: &mut Vec<u8>, kinds: &KindTable, reg: u16) -> R<()> {
4862 match kinds.get(reg as usize) {
4863 Some(Kind::Modular) => local_get(code, reg as u32),
4864 _ => return Err(WasmLowerError::Unsupported("modular arithmetic operand must be a Modular")),
4865 }
4866 Ok(())
4867}
4868
4869fn lower_modular_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4871 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("modular op not imported"))?;
4872 push_modular_operand(code, kinds, lhs)?;
4873 push_modular_operand(code, kinds, rhs)?;
4874 code.push(0x10);
4875 leb_u32(code, op);
4876 local_set(code, dst as u32);
4877 Ok(Flow::Straight)
4878}
4879
4880fn push_decimal_operand(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, reg: u16) -> R<()> {
4883 match kinds.get(reg as usize) {
4884 Some(Kind::Decimal) => local_get(code, reg as u32),
4885 Some(Kind::Int) => {
4886 let from = (ctx.host_index)(HostFn::DecimalFromI64).ok_or(WasmLowerError::Unsupported("decimal_from_i64 not imported"))?;
4887 local_get(code, reg as u32);
4888 code.push(0x10);
4889 leb_u32(code, from);
4890 }
4891 _ => return Err(WasmLowerError::Unsupported("decimal arithmetic operand must be a Decimal or an Int")),
4892 }
4893 Ok(())
4894}
4895
4896fn lower_decimal_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4898 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("decimal op not imported"))?;
4899 push_decimal_operand(code, kinds, ctx, lhs)?;
4900 push_decimal_operand(code, kinds, ctx, rhs)?;
4901 code.push(0x10);
4902 leb_u32(code, op);
4903 local_set(code, dst as u32);
4904 Ok(Flow::Straight)
4905}
4906
4907fn push_money_operand(code: &mut Vec<u8>, kinds: &KindTable, _ctx: &Ctx, reg: u16) -> R<()> {
4908 match kinds.get(reg as usize) {
4909 Some(Kind::Money) => local_get(code, reg as u32),
4910 _ => return Err(WasmLowerError::Unsupported("money arithmetic operand must be a Money value")),
4911 }
4912 Ok(())
4913}
4914
4915fn lower_money_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4917 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("money op not imported"))?;
4918 push_money_operand(code, kinds, ctx, lhs)?;
4919 push_money_operand(code, kinds, ctx, rhs)?;
4920 code.push(0x10);
4921 leb_u32(code, op);
4922 local_set(code, dst as u32);
4923 Ok(Flow::Straight)
4924}
4925
4926fn push_quantity_operand(code: &mut Vec<u8>, kinds: &KindTable, _ctx: &Ctx, reg: u16) -> R<()> {
4927 match kinds.get(reg as usize) {
4928 Some(Kind::Quantity) => local_get(code, reg as u32),
4929 _ => return Err(WasmLowerError::Unsupported("quantity arithmetic operand must be a Quantity value")),
4932 }
4933 Ok(())
4934}
4935
4936fn lower_quantity_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4939 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("quantity op not imported"))?;
4940 push_quantity_operand(code, kinds, ctx, lhs)?;
4941 push_quantity_operand(code, kinds, ctx, rhs)?;
4942 code.push(0x10);
4943 leb_u32(code, op);
4944 local_set(code, dst as u32);
4945 Ok(Flow::Straight)
4946}
4947
4948fn push_rational_operand(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, reg: u16) -> R<()> {
4952 match kinds.get(reg as usize) {
4953 Some(Kind::Rational) => local_get(code, reg as u32),
4954 Some(Kind::Int) => {
4955 let from = (ctx.host_index)(HostFn::RationalFromI64).ok_or(WasmLowerError::Unsupported("rational_from_i64 not imported"))?;
4956 local_get(code, reg as u32);
4957 code.push(0x10);
4958 leb_u32(code, from);
4959 }
4960 Some(Kind::BigInt) => {
4961 let from = (ctx.host_index)(HostFn::RationalFromBigint).ok_or(WasmLowerError::Unsupported("rational_from_bigint not imported"))?;
4962 local_get(code, reg as u32);
4963 code.push(0x10);
4964 leb_u32(code, from);
4965 }
4966 _ => return Err(WasmLowerError::Unsupported("rational arithmetic operand must be a Rational, Int, or BigInt")),
4967 }
4968 Ok(())
4969}
4970
4971fn lower_rational_binop(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, host: HostFn) -> R<Flow> {
4974 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("rational op not imported"))?;
4975 push_rational_operand(code, kinds, ctx, lhs)?;
4976 push_rational_operand(code, kinds, ctx, rhs)?;
4977 code.push(0x10);
4978 leb_u32(code, op);
4979 local_set(code, dst as u32);
4980 Ok(Flow::Straight)
4981}
4982
4983fn lower_rational_unary(code: &mut Vec<u8>, ctx: &Ctx, dst: u16, arg: u16, host: HostFn) -> R<Flow> {
4986 let op = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("rational unary op not imported"))?;
4987 local_get(code, arg as u32);
4988 code.push(0x10);
4989 leb_u32(code, op);
4990 local_set(code, dst as u32);
4991 Ok(Flow::Straight)
4992}
4993
4994fn lower_uuid_eq(code: &mut Vec<u8>, ctx: &Ctx, dst: u16, lhs: u16, rhs: u16, negate: bool) -> R<Flow> {
4998 let eq = (ctx.host_index)(HostFn::UuidEq).ok_or(WasmLowerError::Unsupported("uuid_eq not imported"))?;
4999 local_get(code, lhs as u32);
5000 local_get(code, rhs as u32);
5001 code.push(0x10);
5002 leb_u32(code, eq); if negate {
5004 code.push(0x45); }
5006 code.push(0xAD); local_set(code, dst as u32);
5008 Ok(Flow::Straight)
5009}
5010
5011fn lower_span_add(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, base: u16, span: u16, is_date: bool, negate: bool) -> R<Flow> {
5015 let host = if is_date { HostFn::DateAddSpan } else { HostFn::MomentAddSpan };
5016 let idx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("span add not imported"))?;
5017 let (months, days) = (num_regs + 5, num_regs + 6);
5018 local_get(code, span as u32);
5020 code.push(0x42);
5021 leb_i64(code, 32);
5022 code.push(0x87); code.push(0xA7); local_set(code, months);
5025 local_get(code, span as u32);
5027 code.push(0xA7); local_set(code, days);
5029 if negate {
5030 for r in [months, days] {
5031 i32_const(code, 0);
5032 local_get(code, r);
5033 code.push(0x6B); local_set(code, r);
5035 }
5036 }
5037 local_get(code, base as u32);
5039 local_get(code, months);
5040 local_get(code, days);
5041 code.push(0x10);
5042 leb_u32(code, idx);
5043 local_set(code, dst as u32);
5044 Ok(Flow::Straight)
5045}
5046
5047fn lower_pow_regs(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, base: u16, exp: u16) -> R<Flow> {
5048 let (bk, ek) = (kinds.get(base as usize), kinds.get(exp as usize));
5049 match (bk, ek) {
5050 (_, Some(Kind::Float)) => {
5051 push_as_f64(code, base, bk)?;
5052 local_get(code, exp as u32); let idx = (ctx.host_index)(HostFn::PowFf).ok_or(WasmLowerError::Unsupported("pow_ff not imported"))?;
5054 code.push(0x10);
5055 leb_u32(code, idx);
5056 local_set(code, dst as u32);
5057 Ok(Flow::Straight)
5058 }
5059 (Some(Kind::Float), Some(Kind::Int)) => {
5060 local_get(code, base as u32);
5061 local_get(code, exp as u32);
5062 let idx = (ctx.host_index)(HostFn::PowFi).ok_or(WasmLowerError::Unsupported("pow_fi not imported"))?;
5063 code.push(0x10);
5064 leb_u32(code, idx);
5065 local_set(code, dst as u32);
5066 Ok(Flow::Straight)
5067 }
5068 (Some(Kind::Int), Some(Kind::Int)) if ctx.linked => {
5069 let from_i64 = (ctx.host_index)(HostFn::BigintFromI64).ok_or(WasmLowerError::Unsupported("bigint_from_i64 not imported"))?;
5074 let pow = (ctx.host_index)(HostFn::BigintPow).ok_or(WasmLowerError::Unsupported("bigint_pow not imported"))?;
5075 local_get(code, base as u32); code.push(0x10);
5077 leb_u32(code, from_i64); local_get(code, exp as u32); code.push(0x10);
5080 leb_u32(code, pow); local_set(code, dst as u32);
5082 Ok(Flow::Straight)
5083 }
5084 (Some(Kind::Int), Some(Kind::Int)) => {
5085 lower_int_pow(code, num_regs, dst, base, exp);
5086 Ok(Flow::Straight)
5087 }
5088 _ => Err(WasmLowerError::Unsupported("pow of non-numbers")),
5089 }
5090}
5091
5092fn lower_floordiv_regs(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, dst: u16, lhs: u16, rhs: u16) -> R<Flow> {
5099 match kinds.get(dst as usize) {
5100 Some(Kind::Int) => {
5101 if kinds.valtype(lhs as usize) != I64 || kinds.valtype(rhs as usize) != I64 {
5102 return Err(WasmLowerError::Unsupported("integer floor division with a non-integer operand"));
5103 }
5104 let s_r = num_regs + 1; local_get(code, lhs as u32);
5107 local_get(code, rhs as u32);
5108 code.push(0x81); local_set(code, s_r);
5110 local_get(code, lhs as u32);
5112 local_get(code, rhs as u32);
5113 code.push(0x7F); local_get(code, s_r);
5116 code.push(0x42);
5117 leb_i64(code, 0); code.push(0x52); local_get(code, s_r);
5120 local_get(code, rhs as u32);
5121 code.push(0x85); code.push(0x42);
5123 leb_i64(code, 0); code.push(0x53); code.push(0x71); code.push(0xAD); code.push(0x7D); local_set(code, dst as u32);
5129 }
5130 Some(Kind::Word32) => arith(code, 0x6E, dst, lhs, rhs), Some(Kind::Word64) => arith(code, 0x80, dst, lhs, rhs), Some(Kind::Float) => {
5133 push_as_f64(code, lhs, kinds.get(lhs as usize))?;
5134 push_as_f64(code, rhs, kinds.get(rhs as usize))?;
5135 code.push(0xA3); code.push(0x9C); local_set(code, dst as u32);
5138 }
5139 _ => return Err(WasmLowerError::Unsupported("floor division on a non-numeric value")),
5140 }
5141 Ok(Flow::Straight)
5142}
5143
5144fn lower_int_pow(code: &mut Vec<u8>, num_regs: u32, dst: u16, base: u16, exp: u16) {
5148 let result = (num_regs + 1) as u16;
5149 let base_s = (num_regs + 2) as u16;
5150 let exp_s = (num_regs + 3) as u16;
5151 let tmp = (num_regs + 4) as u16;
5155 local_get(code, exp as u32);
5157 code.push(0x42);
5158 leb_i64(code, 0);
5159 code.push(0x53); code.push(0x04);
5161 code.push(0x40); code.push(0x00); code.push(0x0B); code.push(0x42);
5166 leb_i64(code, 1);
5167 local_set(code, result as u32);
5168 local_get(code, base as u32);
5169 local_set(code, base_s as u32);
5170 local_get(code, exp as u32);
5171 local_set(code, exp_s as u32);
5172 code.push(0x02);
5174 code.push(0x40);
5175 code.push(0x03);
5176 code.push(0x40);
5177 local_get(code, exp_s as u32);
5179 code.push(0x50); code.push(0x0D);
5181 leb_u32(code, 1); local_get(code, exp_s as u32);
5184 code.push(0x42);
5185 leb_i64(code, 1);
5186 code.push(0x83); code.push(0xA7); code.push(0x04);
5189 code.push(0x40); emit_checked_mul(code, tmp, result, base_s); local_get(code, tmp as u32);
5192 local_set(code, result as u32); code.push(0x0B); local_get(code, exp_s as u32);
5196 code.push(0x42);
5197 leb_i64(code, 1);
5198 code.push(0x87); local_set(code, exp_s as u32);
5200 local_get(code, exp_s as u32);
5202 code.push(0x50); code.push(0x45); code.push(0x04);
5205 code.push(0x40); emit_checked_mul(code, tmp, base_s, base_s); local_get(code, tmp as u32);
5208 local_set(code, base_s as u32); code.push(0x0B); code.push(0x0C);
5211 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); local_get(code, result as u32);
5216 local_set(code, dst as u32);
5217}
5218
5219fn emit_alloc(code: &mut Vec<u8>, ctx: &Ctx, dst_scratch: u32) {
5223 if let Some(rt_alloc) = ctx.rt_alloc {
5225 code.push(0x10); leb_u32(code, rt_alloc);
5230 local_set(code, dst_scratch); } else {
5232 global_get(code, ctx.heap_global);
5234 i32_const(code, 7);
5235 code.push(0x6A); i32_const(code, -8);
5237 code.push(0x71); local_tee(code, dst_scratch); code.push(0x6A); global_set(code, ctx.heap_global);
5241 }
5242}
5243
5244fn lower_list_push(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, list: u16, value: u16) -> R<()> {
5248 let elem = kinds.get(list as usize).and_then(Kind::seq_elem).ok_or(WasmLowerError::Unsupported("push to a sequence of unknown element kind"))?;
5249 lower_list_push_at(code, elem, ctx, num_regs, list as u32, value)
5250}
5251
5252fn lower_list_push_at(code: &mut Vec<u8>, elem: Kind, ctx: &Ctx, num_regs: u32, lst: u32, value: u16) -> R<()> {
5256 let elem_load = seq_elem_load(elem)?;
5258 let elem_store = seq_elem_store(elem)?;
5259 let (hs_new, hs_i, hs_cap) = (num_regs + 5, num_regs + 6, num_regs + 7);
5260 local_get(code, lst);
5268 i32_load(code, 0); local_get(code, lst);
5270 i32_load(code, 4); code.push(0x48); code.push(0x04);
5273 code.push(0x40); {
5275 local_get(code, lst);
5277 i32_load(code, 8); local_get(code, lst);
5279 i32_load(code, 0); i32_const(code, 8);
5281 code.push(0x6C);
5282 code.push(0x6A); local_get(code, value as u32);
5284 elem_store(code, 0);
5285 }
5286 code.push(0x05); {
5288 i32_const(code, 4);
5290 local_get(code, lst);
5291 i32_load(code, 4);
5292 i32_const(code, 2);
5293 code.push(0x6C); local_get(code, lst);
5295 i32_load(code, 4);
5296 code.push(0x45); code.push(0x1B); local_set(code, hs_cap);
5299 local_get(code, hs_cap);
5301 i32_const(code, 8);
5302 code.push(0x6C);
5303 emit_alloc(code, ctx,hs_new);
5304 i32_const(code, 0);
5306 local_set(code, hs_i);
5307 code.push(0x02);
5308 code.push(0x40); code.push(0x03);
5310 code.push(0x40); local_get(code, hs_i);
5312 local_get(code, lst);
5313 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
5316 leb_u32(code, 1); local_get(code, hs_new);
5318 local_get(code, hs_i);
5319 i32_const(code, 8);
5320 code.push(0x6C);
5321 code.push(0x6A); local_get(code, lst);
5323 i32_load(code, 8); local_get(code, hs_i);
5325 i32_const(code, 8);
5326 code.push(0x6C);
5327 code.push(0x6A); elem_load(code, 0);
5329 elem_store(code, 0); local_get(code, hs_i);
5331 i32_const(code, 1);
5332 code.push(0x6A);
5333 local_set(code, hs_i);
5334 code.push(0x0C);
5335 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); local_get(code, hs_new);
5340 local_get(code, lst);
5341 i32_load(code, 0); i32_const(code, 8);
5343 code.push(0x6C);
5344 code.push(0x6A);
5345 local_get(code, value as u32);
5346 elem_store(code, 0);
5347 local_get(code, lst);
5349 local_get(code, hs_new);
5350 i32_store(code, 8);
5351 local_get(code, lst);
5353 local_get(code, hs_cap);
5354 i32_store(code, 4);
5355 }
5356 code.push(0x0B); local_get(code, lst);
5359 local_get(code, lst);
5360 i32_load(code, 0);
5361 i32_const(code, 1);
5362 code.push(0x6A);
5363 i32_store(code, 0);
5364 Ok(())
5365}
5366
5367fn lower_list_pop(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, list: u16) -> R<()> {
5374 let elem = kinds.get(list as usize).and_then(Kind::seq_elem).ok_or(WasmLowerError::Unsupported("pop from a sequence of unknown element kind"))?;
5375 let elem_load = seq_elem_load(elem)?;
5376 let vt = elem.wasm_valtype();
5377 let lst = list as u32;
5378 local_get(code, lst);
5380 i32_load(code, 0); i32_const(code, 0);
5382 code.push(0x4A); code.push(0x04);
5384 code.push(vt); {
5386 local_get(code, lst);
5388 i32_load(code, 8); local_get(code, lst);
5390 i32_load(code, 0); i32_const(code, 1);
5392 code.push(0x6B); i32_const(code, 8);
5394 code.push(0x6C); code.push(0x6A); elem_load(code, 0);
5397 local_get(code, lst);
5399 local_get(code, lst);
5400 i32_load(code, 0);
5401 i32_const(code, 1);
5402 code.push(0x6B); i32_store(code, 0);
5404 }
5405 code.push(0x05); match vt {
5407 F64 => {
5408 code.push(0x44);
5409 code.extend_from_slice(&0.0f64.to_le_bytes());
5410 }
5411 I64 => {
5412 code.push(0x42);
5413 leb_i64(code, 0);
5414 }
5415 _ => i32_const(code, 0),
5416 }
5417 code.push(0x0B); local_set(code, dst as u32);
5419 Ok(())
5420}
5421
5422fn emit_seq_elem_addr(code: &mut Vec<u8>, kinds: &KindTable, collection: u16, index: u16) -> R<()> {
5426 match kinds.get(collection as usize) {
5430 Some(Kind::Tuple) => {}
5431 other if other.and_then(Kind::seq_elem).is_some() => {}
5432 _ => return Err(WasmLowerError::Unsupported("index of a non-scalar sequence")),
5433 }
5434 let col = collection as u32;
5435 local_get(code, index as u32);
5437 code.push(0x42);
5438 leb_i64(code, 1);
5439 code.push(0x53); local_get(code, index as u32);
5441 local_get(code, col);
5442 i32_load(code, 0);
5443 code.push(0xAD); code.push(0x55); code.push(0x72); code.push(0x04);
5447 code.push(0x40); code.push(0x00); code.push(0x0B); local_get(code, col);
5452 i32_load(code, 8); local_get(code, index as u32);
5454 code.push(0x42);
5455 leb_i64(code, 1);
5456 code.push(0x7D); code.push(0xA7); i32_const(code, 8);
5459 code.push(0x6C); code.push(0x6A); Ok(())
5462}
5463
5464fn seq_elem_kind(kinds: &KindTable, collection: u16) -> R<Kind> {
5466 kinds.get(collection as usize).and_then(Kind::seq_elem).ok_or(WasmLowerError::Unsupported("sequence of unknown element kind"))
5467}
5468
5469fn seq_elem_load(elem: Kind) -> R<fn(&mut Vec<u8>, u32)> {
5473 Ok(match elem.wasm_valtype() {
5474 F64 => f64_load,
5475 I64 => i64_load,
5476 _ => i32_load,
5477 })
5478}
5479
5480fn seq_elem_store(elem: Kind) -> R<fn(&mut Vec<u8>, u32)> {
5482 Ok(match elem.wasm_valtype() {
5483 F64 => f64_store,
5484 I64 => i64_store,
5485 _ => i32_store,
5486 })
5487}
5488
5489fn lower_index(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, collection: u16, index: u16) -> R<()> {
5493 let elem = if kinds.get(collection as usize) == Some(Kind::Tuple) {
5494 kinds.get(dst as usize).ok_or(WasmLowerError::Unsupported("tuple element of unknown kind"))?
5495 } else {
5496 seq_elem_kind(kinds, collection)?
5497 };
5498 let load = seq_elem_load(elem)?;
5499 emit_seq_elem_addr(code, kinds, collection, index)?;
5500 load(code, 0);
5501 local_set(code, dst as u32);
5502 Ok(())
5503}
5504
5505fn lower_text_index(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, collection: u16, index: u16) -> R<()> {
5512 let (hdr, data) = (num_regs + 5, num_regs + 6);
5513 let col = collection as u32;
5514 local_get(code, index as u32);
5516 code.push(0x42);
5517 leb_i64(code, 1);
5518 code.push(0x53); local_get(code, index as u32);
5520 local_get(code, col);
5521 i32_load(code, 0); code.push(0xAD); code.push(0x55); code.push(0x72); code.push(0x04);
5526 code.push(0x40); code.push(0x00); code.push(0x0B); i32_const(code, 16);
5531 emit_alloc(code, ctx,hdr);
5532 i32_const(code, 1);
5533 emit_alloc(code, ctx,data);
5534 local_get(code, data); local_get(code, col);
5537 i32_load(code, 8); local_get(code, index as u32);
5539 code.push(0x42);
5540 leb_i64(code, 1);
5541 code.push(0x7D); code.push(0xA7); code.push(0x6A); i32_load8_u(code, 0);
5545 i32_store8(code, 0);
5546 local_get(code, hdr);
5548 i32_const(code, 1);
5549 i32_store(code, 0);
5550 local_get(code, hdr);
5551 i32_const(code, 1);
5552 i32_store(code, 4);
5553 local_get(code, hdr);
5554 local_get(code, data);
5555 i32_store(code, 8);
5556 local_get(code, hdr);
5557 local_set(code, dst as u32);
5558 Ok(())
5559}
5560
5561fn emit_bytes_to_seq(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, base_reg: u32, len_reg: u32) {
5565 let (hdr, data, i) = (num_regs + 7, num_regs + 8, num_regs + 9);
5566 i32_const(code, 16);
5567 emit_alloc(code, ctx, hdr);
5568 local_get(code, len_reg);
5570 i32_const(code, 8);
5571 code.push(0x6C); emit_alloc(code, ctx, data);
5573 local_get(code, hdr);
5575 local_get(code, len_reg);
5576 i32_store(code, 0);
5577 local_get(code, hdr);
5578 local_get(code, len_reg);
5579 i32_store(code, 4);
5580 local_get(code, hdr);
5581 local_get(code, data);
5582 i32_store(code, 8);
5583 i32_const(code, 0);
5585 local_set(code, i);
5586 code.push(0x02);
5587 code.push(0x40); code.push(0x03);
5589 code.push(0x40); local_get(code, i);
5591 local_get(code, len_reg);
5592 code.push(0x4E); code.push(0x0D);
5594 leb_u32(code, 1); local_get(code, data);
5597 local_get(code, i);
5598 i32_const(code, 8);
5599 code.push(0x6C); code.push(0x6A); local_get(code, base_reg);
5603 local_get(code, i);
5604 code.push(0x6A); i32_load8_u(code, 0);
5606 code.push(0xAD); i64_store(code, 0);
5608 local_get(code, i);
5609 i32_const(code, 1);
5610 code.push(0x6A); local_set(code, i);
5612 code.push(0x0C);
5613 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); local_get(code, hdr);
5617 local_set(code, dst as u32);
5618}
5619
5620fn lower_text_bytes(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, text: u16) {
5622 let (base, len) = (num_regs + 5, num_regs + 6);
5623 local_get(code, text as u32);
5624 i32_load(code, 8); local_set(code, base);
5626 local_get(code, text as u32);
5627 i32_load(code, 0); local_set(code, len);
5629 emit_bytes_to_seq(code, ctx, num_regs, dst, base, len);
5630}
5631
5632fn lower_uuid_bytes(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, uuid: u16) {
5634 let (base, len) = (num_regs + 5, num_regs + 6);
5635 local_get(code, uuid as u32);
5636 local_set(code, base);
5637 i32_const(code, 16);
5638 local_set(code, len);
5639 emit_bytes_to_seq(code, ctx, num_regs, dst, base, len);
5640}
5641
5642fn lower_text_from_bytes(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, seq: u16) {
5644 let (hdr, data, i, len, src) = (num_regs + 7, num_regs + 8, num_regs + 9, num_regs + 6, num_regs + 5);
5645 local_get(code, seq as u32);
5646 i32_load(code, 0); local_set(code, len);
5648 local_get(code, seq as u32);
5649 i32_load(code, 8); local_set(code, src);
5651 i32_const(code, 16);
5652 emit_alloc(code, ctx, hdr);
5653 local_get(code, len); emit_alloc(code, ctx, data);
5655 local_get(code, hdr);
5656 local_get(code, len);
5657 i32_store(code, 0);
5658 local_get(code, hdr);
5659 local_get(code, len);
5660 i32_store(code, 4);
5661 local_get(code, hdr);
5662 local_get(code, data);
5663 i32_store(code, 8);
5664 i32_const(code, 0);
5666 local_set(code, i);
5667 code.push(0x02);
5668 code.push(0x40);
5669 code.push(0x03);
5670 code.push(0x40);
5671 local_get(code, i);
5672 local_get(code, len);
5673 code.push(0x4E);
5674 code.push(0x0D);
5675 leb_u32(code, 1);
5676 local_get(code, data);
5677 local_get(code, i);
5678 code.push(0x6A); local_get(code, src);
5680 local_get(code, i);
5681 i32_const(code, 8);
5682 code.push(0x6C);
5683 code.push(0x6A); i32_load8_u(code, 0); i32_store8(code, 0);
5686 local_get(code, i);
5687 i32_const(code, 1);
5688 code.push(0x6A);
5689 local_set(code, i);
5690 code.push(0x0C);
5691 leb_u32(code, 0);
5692 code.push(0x0B);
5693 code.push(0x0B);
5694 local_get(code, hdr);
5695 local_set(code, dst as u32);
5696}
5697
5698fn lower_uuid_from_bytes(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, seq: u16) -> R<Flow> {
5701 let from_ptr = (ctx.host_index)(HostFn::UuidFromPtr).ok_or(WasmLowerError::Unsupported("uuid_from_ptr not imported"))?;
5702 let (block, i, src) = (num_regs + 7, num_regs + 9, num_regs + 5);
5703 local_get(code, seq as u32);
5704 i32_load(code, 8); local_set(code, src);
5706 i32_const(code, 16);
5707 emit_alloc(code, ctx, block);
5708 i32_const(code, 0);
5710 local_set(code, i);
5711 code.push(0x02);
5712 code.push(0x40);
5713 code.push(0x03);
5714 code.push(0x40);
5715 local_get(code, i);
5716 i32_const(code, 16);
5717 code.push(0x4E);
5718 code.push(0x0D);
5719 leb_u32(code, 1);
5720 local_get(code, block);
5721 local_get(code, i);
5722 code.push(0x6A);
5723 local_get(code, src);
5724 local_get(code, i);
5725 i32_const(code, 8);
5726 code.push(0x6C);
5727 code.push(0x6A);
5728 i32_load8_u(code, 0);
5729 i32_store8(code, 0);
5730 local_get(code, i);
5731 i32_const(code, 1);
5732 code.push(0x6A);
5733 local_set(code, i);
5734 code.push(0x0C);
5735 leb_u32(code, 0);
5736 code.push(0x0B);
5737 code.push(0x0B);
5738 local_get(code, block);
5740 code.push(0x10);
5741 leb_u32(code, from_ptr);
5742 local_set(code, dst as u32);
5743 Ok(Flow::Straight)
5744}
5745
5746fn lower_lanes4_of(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, words: [u16; 4]) {
5749 let block = num_regs + 7;
5750 i32_const(code, 16);
5751 emit_alloc(code, ctx, block);
5752 for (i, w) in words.iter().enumerate() {
5753 local_get(code, block);
5754 local_get(code, *w as u32);
5755 i32_store(code, (i * 4) as u32);
5756 }
5757 local_get(code, block);
5758 local_set(code, dst as u32);
5759}
5760
5761fn lower_lanes4_word32(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, seq: u16) {
5764 let block = num_regs + 7;
5765 i32_const(code, 16);
5766 emit_alloc(code, ctx, block);
5767 for i in 0..4u32 {
5768 local_get(code, block);
5769 local_get(code, seq as u32);
5771 i32_load(code, 8);
5772 i32_const(code, (i * 8) as i32);
5773 code.push(0x6A); i32_load(code, 0);
5775 i32_store(code, i * 4);
5776 }
5777 local_get(code, block);
5778 local_set(code, dst as u32);
5779}
5780
5781fn lower_seq_of_lanes4(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, lanes: u16) {
5784 let (hdr, data) = (num_regs + 7, num_regs + 8);
5785 i32_const(code, 16);
5786 emit_alloc(code, ctx, hdr);
5787 i32_const(code, 32); emit_alloc(code, ctx, data);
5789 local_get(code, hdr);
5790 i32_const(code, 4);
5791 i32_store(code, 0);
5792 local_get(code, hdr);
5793 i32_const(code, 4);
5794 i32_store(code, 4);
5795 local_get(code, hdr);
5796 local_get(code, data);
5797 i32_store(code, 8);
5798 for i in 0..4u32 {
5799 local_get(code, data);
5800 local_get(code, lanes as u32);
5801 i32_load(code, i * 4); i32_store(code, i * 8); }
5804 local_get(code, hdr);
5805 local_set(code, dst as u32);
5806}
5807
5808fn lower_sha1_op(code: &mut Vec<u8>, ctx: &Ctx, dst: u16, args_start: u16, host: HostFn, ternary: bool) -> R<Flow> {
5811 let idx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("sha1 op not imported"))?;
5812 local_get(code, args_start as u32);
5813 local_get(code, (args_start + 1) as u32);
5814 if ternary {
5815 local_get(code, (args_start + 2) as u32); }
5817 code.push(0x10);
5818 leb_u32(code, idx);
5819 local_set(code, dst as u32);
5820 Ok(Flow::Straight)
5821}
5822
5823fn lower_set_index(code: &mut Vec<u8>, kinds: &KindTable, collection: u16, index: u16, value: u16) -> R<()> {
5825 let elem = seq_elem_kind(kinds, collection)?;
5826 let store = seq_elem_store(elem)?;
5827 emit_seq_elem_addr(code, kinds, collection, index)?; local_get(code, value as u32); store(code, 0);
5830 Ok(())
5831}
5832
5833fn lower_new_range(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, start: u16, end: u16) {
5836 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7); let cnt = num_regs + 1; local_get(code, end as u32);
5840 local_get(code, start as u32);
5841 code.push(0x7D); code.push(0x42);
5843 leb_i64(code, 1);
5844 code.push(0x7C); local_set(code, cnt);
5846 local_get(code, cnt);
5847 code.push(0x42);
5848 leb_i64(code, 0);
5849 code.push(0x53); code.push(0x04);
5851 code.push(0x40); code.push(0x42);
5853 leb_i64(code, 0);
5854 local_set(code, cnt); code.push(0x0B);
5856 i32_const(code, 16);
5858 emit_alloc(code, ctx,hdr);
5859 local_get(code, cnt);
5860 code.push(0xA7); i32_const(code, 8);
5862 code.push(0x6C); emit_alloc(code, ctx,data);
5864 local_get(code, hdr);
5866 local_get(code, cnt);
5867 code.push(0xA7);
5868 i32_store(code, 0);
5869 local_get(code, hdr);
5870 local_get(code, cnt);
5871 code.push(0xA7);
5872 i32_store(code, 4);
5873 local_get(code, hdr);
5874 local_get(code, data);
5875 i32_store(code, 8);
5876 i32_const(code, 0);
5878 local_set(code, idx);
5879 code.push(0x02);
5880 code.push(0x40); code.push(0x03);
5882 code.push(0x40); local_get(code, idx);
5884 local_get(code, cnt);
5885 code.push(0xA7);
5886 code.push(0x4E); code.push(0x0D);
5888 leb_u32(code, 1); local_get(code, data);
5890 local_get(code, idx);
5891 i32_const(code, 8);
5892 code.push(0x6C);
5893 code.push(0x6A); local_get(code, start as u32);
5895 local_get(code, idx);
5896 code.push(0xAC); code.push(0x7C); i64_store(code, 0);
5899 local_get(code, idx);
5900 i32_const(code, 1);
5901 code.push(0x6A);
5902 local_set(code, idx); code.push(0x0C);
5904 leb_u32(code, 0); code.push(0x0B);
5906 code.push(0x0B); local_get(code, hdr);
5908 local_set(code, dst as u32);
5909}
5910
5911fn lower_repeat_seq(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, args_start: u16) -> R<()> {
5916 let value = args_start; let n = args_start + 1; let is_float = matches!(kinds.get(value as usize), Some(Kind::Float));
5919 if !matches!(kinds.get(value as usize), Some(Kind::Int) | Some(Kind::Float)) {
5920 return Err(WasmLowerError::Unsupported("repeatSeq of a non-scalar element (deep-copy)"));
5921 }
5922 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7); let cnt = num_regs + 1; local_get(code, n as u32);
5926 local_set(code, cnt);
5927 local_get(code, cnt);
5928 code.push(0x42);
5929 leb_i64(code, 0);
5930 code.push(0x53); code.push(0x04);
5932 code.push(0x40); code.push(0x42);
5934 leb_i64(code, 0);
5935 local_set(code, cnt); code.push(0x0B);
5937 i32_const(code, 16);
5939 emit_alloc(code, ctx, hdr);
5940 local_get(code, cnt);
5941 code.push(0xA7); i32_const(code, 8);
5943 code.push(0x6C); emit_alloc(code, ctx, data);
5945 local_get(code, hdr);
5947 local_get(code, cnt);
5948 code.push(0xA7);
5949 i32_store(code, 0);
5950 local_get(code, hdr);
5951 local_get(code, cnt);
5952 code.push(0xA7);
5953 i32_store(code, 4);
5954 local_get(code, hdr);
5955 local_get(code, data);
5956 i32_store(code, 8);
5957 i32_const(code, 0);
5959 local_set(code, idx);
5960 code.push(0x02);
5961 code.push(0x40); code.push(0x03);
5963 code.push(0x40); local_get(code, idx);
5965 local_get(code, cnt);
5966 code.push(0xA7);
5967 code.push(0x4E); code.push(0x0D);
5969 leb_u32(code, 1); local_get(code, data);
5971 local_get(code, idx);
5972 i32_const(code, 8);
5973 code.push(0x6C);
5974 code.push(0x6A); local_get(code, value as u32);
5976 if is_float {
5977 f64_store(code, 0);
5978 } else {
5979 i64_store(code, 0);
5980 }
5981 local_get(code, idx);
5982 i32_const(code, 1);
5983 code.push(0x6A);
5984 local_set(code, idx); code.push(0x0C);
5986 leb_u32(code, 0); code.push(0x0B);
5988 code.push(0x0B); local_get(code, hdr);
5990 local_set(code, dst as u32);
5991 Ok(())
5992}
5993
5994fn lower_new_list(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, start: u16, count: u16) -> R<()> {
5998 let want = if count > 0 {
5999 kinds.get(start as usize).ok_or(WasmLowerError::Unsupported("list literal of unknown element kind"))?
6000 } else {
6001 Kind::Int
6002 };
6003 let elem_store = seq_elem_store(want)?;
6004 for j in 0..count {
6005 if kinds.get((start + j) as usize) != Some(want) {
6006 return Err(WasmLowerError::Unsupported("list literal with mixed element kinds"));
6007 }
6008 }
6009 let (hdr, data) = (num_regs + 5, num_regs + 6);
6010 i32_const(code, 16);
6011 emit_alloc(code, ctx,hdr);
6012 i32_const(code, i32::from(count) * 8);
6013 emit_alloc(code, ctx,data);
6014 local_get(code, hdr);
6016 i32_const(code, i32::from(count));
6017 i32_store(code, 0);
6018 local_get(code, hdr);
6019 i32_const(code, i32::from(count));
6020 i32_store(code, 4);
6021 local_get(code, hdr);
6022 local_get(code, data);
6023 i32_store(code, 8);
6024 for j in 0..count {
6026 local_get(code, data);
6027 local_get(code, (start + j) as u32);
6028 elem_store(code, u32::from(j) * 8);
6029 }
6030 local_get(code, hdr);
6031 local_set(code, dst as u32);
6032 Ok(())
6033}
6034
6035fn lower_destructure_tuple(code: &mut Vec<u8>, kinds: &KindTable, src: u16, start: u16, count: u16) -> R<()> {
6040 for i in 0..count {
6041 let dst = start + i;
6042 local_get(code, src as u32);
6043 i32_load(code, 8); emit_slot_load(code, kinds.get(dst as usize), u32::from(i) * 8)?;
6045 local_set(code, dst as u32);
6046 }
6047 Ok(())
6048}
6049
6050fn lower_new_tuple_het(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, start: u16, count: u16) -> R<()> {
6054 let (hdr, data) = (num_regs + 5, num_regs + 6);
6055 i32_const(code, 16);
6056 emit_alloc(code, ctx,hdr);
6057 i32_const(code, i32::from(count) * 8);
6058 emit_alloc(code, ctx,data);
6059 for (off, v) in [(0u32, i32::from(count)), (4, i32::from(count))] {
6060 local_get(code, hdr);
6061 i32_const(code, v);
6062 i32_store(code, off);
6063 }
6064 local_get(code, hdr);
6065 local_get(code, data);
6066 i32_store(code, 8);
6067 for j in 0..count {
6068 local_get(code, data);
6069 local_get(code, (start + j) as u32);
6070 emit_slot_store(code, kinds.get((start + j) as usize), u32::from(j) * 8)?;
6071 }
6072 local_get(code, hdr);
6073 local_set(code, dst as u32);
6074 Ok(())
6075}
6076
6077fn lower_new_inductive(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, ctor: u32, args_start: u16, count: u16) -> R<()> {
6083 let hs = num_regs + 5;
6084 i32_const(code, 8 * (1 + i32::from(count)));
6085 emit_alloc(code, ctx,hs);
6086 local_get(code, hs);
6087 i32_const(code, ctor as i32); i32_store(code, 0);
6089 for k in 0..count {
6090 let arg = args_start + k;
6091 local_get(code, hs);
6092 local_get(code, arg as u32);
6093 emit_slot_store(code, kinds.get(arg as usize), 8 * (1 + u32::from(k)))?;
6094 }
6095 local_get(code, hs);
6096 local_set(code, dst as u32);
6097 Ok(())
6098}
6099
6100fn lower_bind_arm(code: &mut Vec<u8>, kinds: &KindTable, dst: u16, target: u16, index: u16) -> R<()> {
6105 if kinds.get(target as usize) != Some(Kind::Enum) {
6106 return Err(WasmLowerError::Unsupported("payload binding on a non-enum target"));
6107 }
6108 let kind = kinds.get(dst as usize).or(Some(Kind::Int));
6115 local_get(code, target as u32);
6116 emit_slot_load(code, kind, 8 * (1 + u32::from(index)))?;
6117 local_set(code, dst as u32);
6118 Ok(())
6119}
6120
6121fn capture_valtype(ctx: &Ctx, func: u16, k: usize) -> u8 {
6131 ctx.capture_kinds
6132 .get(func as usize)
6133 .and_then(|v| v.get(k))
6134 .copied()
6135 .flatten()
6136 .map(Kind::wasm_valtype)
6137 .unwrap_or(I64)
6138}
6139
6140fn lower_make_closure(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, func: u16, locals_start: u16) -> R<()> {
6141 let caps = ctx
6142 .functions
6143 .get(func as usize)
6144 .map(|f| f.captures.as_slice())
6145 .ok_or(WasmLowerError::Unsupported("closure of unknown function"))?;
6146 let cap_n = caps.len() as u32;
6147 let hs = num_regs + 5;
6148 i32_const(code, 8 * (1 + 2 * cap_n) as i32);
6149 emit_alloc(code, ctx,hs);
6150 local_get(code, hs);
6151 i32_const(code, i32::from(func)); i32_store(code, 0);
6153 let mut local_k: u16 = 0;
6154 for (k, (_sym, global)) in caps.iter().enumerate() {
6155 local_get(code, hs);
6158 match global {
6159 Some(gidx) => global_get(code, u32::from(*gidx)),
6160 None => {
6161 local_get(code, (locals_start + local_k) as u32);
6162 local_k += 1;
6163 }
6164 }
6165 let off = 8 + 8 * k as u32;
6166 match capture_valtype(ctx, func, k) {
6167 I32 => i32_store(code, off),
6168 F64 => f64_store(code, off),
6169 _ => i64_store(code, off),
6170 }
6171 local_get(code, hs);
6173 code.push(0x42); leb_i64(code, 1);
6175 i64_store(code, 8 + 8 * cap_n + 8 * k as u32);
6176 }
6177 local_get(code, hs);
6178 local_set(code, dst as u32);
6179 Ok(())
6180}
6181
6182fn lower_call_value(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, pc: usize, dst: u16, callee: u16, args_start: u16, arg_count: u16) -> R<()> {
6188 let func = plan
6189 .structs
6190 .callee_func
6191 .get(pc)
6192 .copied()
6193 .flatten()
6194 .ok_or(WasmLowerError::Unsupported("indirect call to a closure of unknown origin"))?;
6195 let type_idx = *ctx
6196 .fn_type
6197 .get(func as usize)
6198 .ok_or(WasmLowerError::Unsupported("closure call: unknown callee type"))?;
6199 let cap_n = ctx.functions.get(func as usize).map(|f| f.captures.len() as u32).unwrap_or(0);
6200 for k in 0..arg_count {
6201 local_get(code, (args_start + k) as u32);
6202 }
6203 for k in 0..cap_n {
6205 local_get(code, callee as u32);
6206 let off = 8 + 8 * k;
6207 match capture_valtype(ctx, func, k as usize) {
6208 I32 => i32_load(code, off),
6209 F64 => f64_load(code, off),
6210 _ => i64_load(code, off),
6211 }
6212 }
6213 for k in 0..cap_n {
6214 local_get(code, callee as u32);
6215 i64_load(code, 8 + 8 * cap_n + 8 * k);
6216 }
6217 if ctx.linked {
6222 code.push(0x10); leb_u32(code, ctx.fn_base + func as u32);
6224 } else {
6225 local_get(code, callee as u32);
6226 i32_load(code, 0); code.push(0x11); leb_u32(code, type_idx);
6229 leb_u32(code, 0); }
6231 if ctx.fn_results.get(func as usize).copied().flatten().is_some() {
6233 local_set(code, dst as u32);
6234 }
6235 Ok(())
6236}
6237
6238fn lower_test_arm(code: &mut Vec<u8>, dst: u16, target: u16, variant: u32) {
6242 local_get(code, target as u32);
6243 i32_load(code, 0); i32_const(code, variant as i32);
6245 code.push(0x46); code.push(0xAD); local_set(code, dst as u32);
6248}
6249
6250fn emit_empty_header(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u32) {
6254 let hs = num_regs + 5;
6255 global_get(code, ctx.heap_global);
6256 i32_const(code, 7);
6257 code.push(0x6A);
6258 i32_const(code, -8);
6259 code.push(0x71);
6260 local_tee(code, hs);
6261 i32_const(code, 16);
6262 code.push(0x6A);
6263 global_set(code, ctx.heap_global);
6264 for off in [0u32, 4, 8] {
6265 local_get(code, hs);
6266 i32_const(code, 0);
6267 i32_store(code, off);
6268 }
6269 local_get(code, hs);
6270 local_set(code, dst);
6271}
6272
6273fn emit_retain(code: &mut Vec<u8>, reg: u16) {
6281 local_get(code, reg as u32);
6282 local_get(code, reg as u32);
6283 i32_load(code, 12);
6284 i32_const(code, 1);
6285 code.push(0x6A); i32_store(code, 12);
6287}
6288
6289fn cow_clonable(k: Option<Kind>) -> bool {
6293 matches!(k, Some(Kind::SeqInt) | Some(Kind::SeqBool) | Some(Kind::SeqFloat) | Some(Kind::SeqAny) | Some(Kind::Set) | Some(Kind::SetText) | Some(Kind::SeqSeqInt) | Some(Kind::Map))
6294}
6295
6296fn emit_cow(code: &mut Vec<u8>, kinds: &KindTable, structs: &kind::StructLayout, ctx: &Ctx, num_regs: u32, reg: u16) -> R<()> {
6301 if !cow_clonable(kinds.get(reg as usize)) {
6302 return Ok(());
6303 }
6304 local_get(code, reg as u32);
6305 i32_load(code, 12); code.push(0x04);
6307 code.push(0x40); lower_deep_clone(code, kinds, structs, ctx, num_regs, reg, reg)?;
6309 code.push(0x0B); Ok(())
6311}
6312
6313fn emit_text_eq_to(code: &mut Vec<u8>, a: u32, b: u32, out: u32, idx: u32) {
6317 i32_const(code, 1);
6318 local_set(code, out); local_get(code, a);
6320 i32_load(code, 0);
6321 local_get(code, b);
6322 i32_load(code, 0);
6323 code.push(0x47); code.push(0x04);
6325 code.push(0x40); i32_const(code, 0);
6327 local_set(code, out);
6328 code.push(0x05); i32_const(code, 0);
6330 local_set(code, idx);
6331 code.push(0x02);
6332 code.push(0x40);
6333 code.push(0x03);
6334 code.push(0x40);
6335 local_get(code, idx);
6336 local_get(code, a);
6337 i32_load(code, 0);
6338 code.push(0x4E); code.push(0x0D);
6340 leb_u32(code, 1); local_get(code, a);
6342 i32_load(code, 8);
6343 local_get(code, idx);
6344 code.push(0x6A);
6345 i32_load8_u(code, 0);
6346 local_get(code, b);
6347 i32_load(code, 8);
6348 local_get(code, idx);
6349 code.push(0x6A);
6350 i32_load8_u(code, 0);
6351 code.push(0x47); code.push(0x04);
6353 code.push(0x40); i32_const(code, 0);
6355 local_set(code, out);
6356 code.push(0x0C);
6357 leb_u32(code, 2); code.push(0x0B);
6359 local_get(code, idx);
6360 i32_const(code, 1);
6361 code.push(0x6A);
6362 local_set(code, idx);
6363 code.push(0x0C);
6364 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); code.push(0x0B); }
6369
6370fn map_key_text(kinds: &KindTable, key: u16) -> R<bool> {
6372 match kinds.get(key as usize) {
6373 Some(Kind::Int) => Ok(false),
6374 Some(Kind::Text) => Ok(true),
6375 _ => Err(WasmLowerError::Unsupported("map key must be Int or Text")),
6376 }
6377}
6378
6379fn emit_map_key_compare(code: &mut Vec<u8>, num_regs: u32, key_text: bool, m: u32, idx_local: u32, key: u16) {
6383 if key_text {
6384 let (entry_key, eq, tidx) = (num_regs + 8, num_regs + 9, num_regs + 10);
6385 emit_map_entry_addr(code, m, idx_local);
6386 i32_load(code, 0); local_set(code, entry_key);
6388 emit_text_eq_to(code, key as u32, entry_key, eq, tidx);
6389 local_get(code, eq); } else {
6391 emit_map_entry_addr(code, m, idx_local);
6392 i64_load(code, 0); local_get(code, key as u32);
6394 code.push(0x51); }
6396}
6397
6398fn map_value_store(k: Option<Kind>) -> R<fn(&mut Vec<u8>, u32)> {
6401 match k {
6402 Some(Kind::Float) => Ok(f64_store),
6403 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Moment) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) => Ok(i64_store),
6404 Some(vk) if vk.wasm_valtype() == I32 => Ok(i32_store),
6407 _ => Err(WasmLowerError::Unsupported("map value of an unsupported kind")),
6408 }
6409}
6410
6411fn map_value_load(k: Option<Kind>) -> R<fn(&mut Vec<u8>, u32)> {
6413 match k {
6414 Some(Kind::Float) => Ok(f64_load),
6415 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Moment) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) => Ok(i64_load),
6416 Some(vk) if vk.wasm_valtype() == I32 => Ok(i32_load),
6417 _ => Err(WasmLowerError::Unsupported("map value of unknown/unsupported kind")),
6418 }
6419}
6420
6421fn emit_map_entry_addr(code: &mut Vec<u8>, m: u32, idx: u32) {
6424 local_get(code, m);
6425 i32_load(code, 8); local_get(code, idx);
6427 i32_const(code, 16);
6428 code.push(0x6C); code.push(0x6A); }
6431
6432fn lower_map_insert(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, map: u16, key: u16, value: u16) -> R<()> {
6436 let key_text = map_key_text(kinds, key)?;
6437 let val_store = map_value_store(kinds.get(value as usize))?;
6438 let m = map as u32;
6439 let (idx, found, new) = (num_regs + 5, num_regs + 6, num_regs + 7);
6440 i32_const(code, 0);
6442 local_set(code, idx);
6443 i32_const(code, 0);
6444 local_set(code, found);
6445 code.push(0x02);
6446 code.push(0x40); code.push(0x03);
6448 code.push(0x40); local_get(code, idx);
6450 local_get(code, m);
6451 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6454 leb_u32(code, 1); emit_map_key_compare(code, num_regs, key_text, m, idx, key);
6456 code.push(0x04);
6457 code.push(0x40); emit_map_entry_addr(code, m, idx);
6459 local_get(code, value as u32);
6460 val_store(code, 8); i32_const(code, 1);
6462 local_set(code, found);
6463 code.push(0x0C);
6464 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
6467 i32_const(code, 1);
6468 code.push(0x6A);
6469 local_set(code, idx);
6470 code.push(0x0C);
6471 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); local_get(code, found);
6476 code.push(0x45); code.push(0x04);
6478 code.push(0x40); local_get(code, m);
6481 i32_load(code, 0);
6482 i32_const(code, 1);
6483 code.push(0x6A);
6484 i32_const(code, 16);
6485 code.push(0x6C);
6486 emit_alloc(code, ctx,new);
6487 i32_const(code, 0);
6489 local_set(code, idx);
6490 code.push(0x02);
6491 code.push(0x40);
6492 code.push(0x03);
6493 code.push(0x40);
6494 local_get(code, idx);
6495 local_get(code, m);
6496 i32_load(code, 0);
6497 code.push(0x4E);
6498 code.push(0x0D);
6499 leb_u32(code, 1);
6500 for field in [0u32, 8] {
6501 local_get(code, new);
6503 local_get(code, idx);
6504 i32_const(code, 16);
6505 code.push(0x6C);
6506 code.push(0x6A); emit_map_entry_addr(code, m, idx); i64_load(code, field);
6509 i64_store(code, field);
6510 }
6511 local_get(code, idx);
6512 i32_const(code, 1);
6513 code.push(0x6A);
6514 local_set(code, idx);
6515 code.push(0x0C);
6516 leb_u32(code, 0);
6517 code.push(0x0B);
6518 code.push(0x0B);
6519 local_get(code, new);
6521 local_get(code, m);
6522 i32_load(code, 0);
6523 i32_const(code, 16);
6524 code.push(0x6C);
6525 code.push(0x6A); local_get(code, key as u32);
6527 if key_text {
6528 i32_store(code, 0);
6529 } else {
6530 i64_store(code, 0);
6531 }
6532 local_get(code, new);
6533 local_get(code, m);
6534 i32_load(code, 0);
6535 i32_const(code, 16);
6536 code.push(0x6C);
6537 code.push(0x6A);
6538 local_get(code, value as u32);
6539 val_store(code, 8);
6540 local_get(code, m);
6542 local_get(code, new);
6543 i32_store(code, 8);
6544 local_get(code, m);
6545 local_get(code, m);
6546 i32_load(code, 0);
6547 i32_const(code, 1);
6548 code.push(0x6A);
6549 i32_store(code, 0);
6550 code.push(0x0B); Ok(())
6552}
6553
6554fn lower_map_get(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, dst: u16, map: u16, key: u16) -> R<()> {
6557 let key_text = map_key_text(kinds, key)?;
6558 let val_load = map_value_load(kinds.get(dst as usize))?;
6559 let m = map as u32;
6560 let (idx, found) = (num_regs + 5, num_regs + 6);
6561 i32_const(code, 0);
6562 local_set(code, idx);
6563 i32_const(code, 0);
6564 local_set(code, found);
6565 code.push(0x02);
6566 code.push(0x40);
6567 code.push(0x03);
6568 code.push(0x40);
6569 local_get(code, idx);
6570 local_get(code, m);
6571 i32_load(code, 0);
6572 code.push(0x4E);
6573 code.push(0x0D);
6574 leb_u32(code, 1);
6575 emit_map_key_compare(code, num_regs, key_text, m, idx, key);
6576 code.push(0x04);
6577 code.push(0x40); emit_map_entry_addr(code, m, idx);
6579 val_load(code, 8); local_set(code, dst as u32);
6581 i32_const(code, 1);
6582 local_set(code, found);
6583 code.push(0x0C);
6584 leb_u32(code, 2);
6585 code.push(0x0B); local_get(code, idx);
6587 i32_const(code, 1);
6588 code.push(0x6A);
6589 local_set(code, idx);
6590 code.push(0x0C);
6591 leb_u32(code, 0);
6592 code.push(0x0B);
6593 code.push(0x0B);
6594 local_get(code, found);
6596 code.push(0x45); code.push(0x04);
6598 code.push(0x40);
6599 code.push(0x00); code.push(0x0B);
6601 Ok(())
6602}
6603
6604fn lower_map_contains(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, dst: u16, map: u16, key: u16) -> R<()> {
6606 let key_text = map_key_text(kinds, key)?;
6607 let m = map as u32;
6608 let idx = num_regs + 5;
6609 code.push(0x42);
6610 leb_i64(code, 0);
6611 local_set(code, dst as u32); i32_const(code, 0);
6613 local_set(code, idx);
6614 code.push(0x02);
6615 code.push(0x40);
6616 code.push(0x03);
6617 code.push(0x40);
6618 local_get(code, idx);
6619 local_get(code, m);
6620 i32_load(code, 0);
6621 code.push(0x4E);
6622 code.push(0x0D);
6623 leb_u32(code, 1);
6624 emit_map_key_compare(code, num_regs, key_text, m, idx, key);
6625 code.push(0x04);
6626 code.push(0x40); code.push(0x42);
6628 leb_i64(code, 1);
6629 local_set(code, dst as u32); code.push(0x0C);
6631 leb_u32(code, 2); code.push(0x0B);
6633 local_get(code, idx);
6634 i32_const(code, 1);
6635 code.push(0x6A);
6636 local_set(code, idx);
6637 code.push(0x0C);
6638 leb_u32(code, 0);
6639 code.push(0x0B);
6640 code.push(0x0B);
6641 Ok(())
6642}
6643
6644fn emit_text_handles_eq(code: &mut Vec<u8>, num_regs: u32, a: u32, b: u32) {
6649 let (idx, res) = (num_regs + 9, num_regs + 10);
6650 i32_const(code, 1);
6651 local_set(code, res); local_get(code, a);
6654 i32_load(code, 0);
6655 local_get(code, b);
6656 i32_load(code, 0);
6657 code.push(0x47); code.push(0x04);
6659 code.push(0x40); i32_const(code, 0);
6661 local_set(code, res);
6662 code.push(0x05); i32_const(code, 0);
6664 local_set(code, idx);
6665 code.push(0x02);
6666 code.push(0x40); code.push(0x03);
6668 code.push(0x40); local_get(code, idx);
6670 local_get(code, a);
6671 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6674 leb_u32(code, 1); local_get(code, a);
6677 i32_load(code, 8);
6678 local_get(code, idx);
6679 code.push(0x6A);
6680 i32_load8_u(code, 0);
6681 local_get(code, b);
6682 i32_load(code, 8);
6683 local_get(code, idx);
6684 code.push(0x6A);
6685 i32_load8_u(code, 0);
6686 code.push(0x47); code.push(0x04);
6688 code.push(0x40); i32_const(code, 0);
6690 local_set(code, res);
6691 code.push(0x0C);
6692 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
6695 i32_const(code, 1);
6696 code.push(0x6A);
6697 local_set(code, idx);
6698 code.push(0x0C);
6699 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); code.push(0x0B); local_get(code, res); }
6705
6706fn emit_set_elem_eq(code: &mut Vec<u8>, num_regs: u32, set: u32, idx: u32, value: u32, is_text: bool) {
6711 local_get(code, set);
6712 i32_load(code, 8); local_get(code, idx);
6714 i32_const(code, 8);
6715 code.push(0x6C);
6716 code.push(0x6A); if is_text {
6718 i32_load(code, 0); local_set(code, num_regs + 8);
6720 emit_text_handles_eq(code, num_regs, num_regs + 8, value);
6721 } else {
6722 i64_load(code, 0); local_get(code, value);
6724 code.push(0x51); }
6726}
6727
6728fn lower_remove_from(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, collection: u16, value: u16) -> R<()> {
6734 let set_text = matches!(kinds.get(collection as usize), Some(Kind::SetText) | Some(Kind::CrdtSetText));
6735 if !set_text && kinds.get(value as usize) != Some(Kind::Int) {
6736 return Err(WasmLowerError::Unsupported("remove of a non-Int value"));
6737 }
6738 let stride: i32 = match kinds.get(collection as usize) {
6739 Some(Kind::Set) | Some(Kind::SetText) | Some(Kind::CrdtSetText) => 8,
6740 Some(Kind::Map) => 16,
6741 _ => return Err(WasmLowerError::Unsupported("remove from a non-set/map collection")),
6742 };
6743 let c = collection as u32;
6744 let (idx, found) = (num_regs + 5, num_regs + 6);
6745 i32_const(code, 0);
6746 local_set(code, idx);
6747 i32_const(code, 0);
6748 local_set(code, found);
6749 code.push(0x02);
6750 code.push(0x40);
6751 code.push(0x03);
6752 code.push(0x40);
6753 local_get(code, idx);
6754 local_get(code, c);
6755 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6758 leb_u32(code, 1); if set_text {
6762 emit_set_elem_eq(code, num_regs, c, idx, value as u32, true);
6763 } else {
6764 local_get(code, c);
6765 i32_load(code, 8);
6766 local_get(code, idx);
6767 i32_const(code, stride);
6768 code.push(0x6C);
6769 code.push(0x6A);
6770 i64_load(code, 0);
6771 local_get(code, value as u32);
6772 code.push(0x51); }
6774 code.push(0x04);
6775 code.push(0x40); let offs: &[u32] = if stride == 16 { &[0, 8] } else { &[0] };
6778 for &off in offs {
6779 local_get(code, c);
6780 i32_load(code, 8);
6781 local_get(code, idx);
6782 i32_const(code, stride);
6783 code.push(0x6C);
6784 code.push(0x6A); local_get(code, c);
6786 i32_load(code, 8);
6787 local_get(code, c);
6788 i32_load(code, 0);
6789 i32_const(code, 1);
6790 code.push(0x6B); i32_const(code, stride);
6792 code.push(0x6C);
6793 code.push(0x6A); i64_load(code, off);
6795 i64_store(code, off);
6796 }
6797 local_get(code, c);
6799 local_get(code, c);
6800 i32_load(code, 0);
6801 i32_const(code, 1);
6802 code.push(0x6B);
6803 i32_store(code, 0);
6804 i32_const(code, 1);
6805 local_set(code, found);
6806 code.push(0x0C);
6807 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
6810 i32_const(code, 1);
6811 code.push(0x6A);
6812 local_set(code, idx);
6813 code.push(0x0C);
6814 leb_u32(code, 0);
6815 code.push(0x0B);
6816 code.push(0x0B);
6817 Ok(())
6818}
6819
6820fn lower_set_add(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, set: u16, value: u16) -> R<()> {
6823 let is_text = matches!(kinds.get(set as usize), Some(Kind::SetText) | Some(Kind::CrdtSetText));
6824 if is_text {
6825 if kinds.get(value as usize) != Some(Kind::Text) {
6826 return Err(WasmLowerError::Unsupported("adding a non-Text value to a Set of Text"));
6827 }
6828 } else if kinds.get(value as usize) != Some(Kind::Int) {
6829 return Err(WasmLowerError::Unsupported("set with a non-Int value (only Set of Int/Text)"));
6830 }
6831 emit_set_add_elem(code, ctx, num_regs, set as u32, value as u32, is_text);
6832 Ok(())
6833}
6834
6835fn emit_set_add_elem(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, s: u32, elem: u32, is_text: bool) {
6842 let (idx, found, new) = (num_regs + 5, num_regs + 6, num_regs + 7);
6843 i32_const(code, 0);
6845 local_set(code, idx);
6846 i32_const(code, 0);
6847 local_set(code, found);
6848 code.push(0x02);
6849 code.push(0x40);
6850 code.push(0x03);
6851 code.push(0x40);
6852 local_get(code, idx);
6853 local_get(code, s);
6854 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6857 leb_u32(code, 1); emit_set_elem_eq(code, num_regs, s, idx, elem, is_text); code.push(0x04);
6860 code.push(0x40); i32_const(code, 1);
6862 local_set(code, found);
6863 code.push(0x0C);
6864 leb_u32(code, 2); code.push(0x0B);
6866 local_get(code, idx);
6867 i32_const(code, 1);
6868 code.push(0x6A);
6869 local_set(code, idx);
6870 code.push(0x0C);
6871 leb_u32(code, 0);
6872 code.push(0x0B);
6873 code.push(0x0B);
6874 local_get(code, found);
6876 code.push(0x45); code.push(0x04);
6878 code.push(0x40); local_get(code, s);
6880 i32_load(code, 0);
6881 i32_const(code, 1);
6882 code.push(0x6A);
6883 i32_const(code, 8);
6884 code.push(0x6C);
6885 emit_alloc(code, ctx,new); emit_seq_copy(code, idx, new, s, s, false); local_get(code, new);
6888 local_get(code, s);
6889 i32_load(code, 0);
6890 i32_const(code, 8);
6891 code.push(0x6C);
6892 code.push(0x6A);
6893 local_get(code, elem);
6894 if is_text {
6895 i32_store(code, 0); } else {
6899 i64_store(code, 0); }
6901 local_get(code, s);
6902 local_get(code, new);
6903 i32_store(code, 8); local_get(code, s);
6905 local_get(code, s);
6906 i32_load(code, 0);
6907 i32_const(code, 1);
6908 code.push(0x6A);
6909 i32_store(code, 0); code.push(0x0B); }
6912
6913fn emit_set_contains_to(code: &mut Vec<u8>, set: u32, elem: u32, out: u32, idx: u32) {
6917 i32_const(code, 0);
6918 local_set(code, out); i32_const(code, 0);
6920 local_set(code, idx);
6921 code.push(0x02);
6922 code.push(0x40); code.push(0x03);
6924 code.push(0x40); local_get(code, idx);
6926 local_get(code, set);
6927 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6930 leb_u32(code, 1); local_get(code, set);
6932 i32_load(code, 8); local_get(code, idx);
6934 i32_const(code, 8);
6935 code.push(0x6C);
6936 code.push(0x6A);
6937 i64_load(code, 0); local_get(code, elem);
6939 code.push(0x51); code.push(0x04);
6941 code.push(0x40); i32_const(code, 1);
6943 local_set(code, out);
6944 code.push(0x0C);
6945 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
6948 i32_const(code, 1);
6949 code.push(0x6A);
6950 local_set(code, idx);
6951 code.push(0x0C);
6952 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); }
6956
6957fn emit_set_copy_loop(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, result: u32, src: u32, filter: Option<u32>) {
6963 let outer = num_regs + 9;
6964 let elem = num_regs + 1;
6965 let (cidx, cfound) = (num_regs + 10, num_regs + 11);
6966 i32_const(code, 0);
6967 local_set(code, outer);
6968 code.push(0x02);
6969 code.push(0x40); code.push(0x03);
6971 code.push(0x40); local_get(code, outer);
6973 local_get(code, src);
6974 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
6977 leb_u32(code, 1); local_get(code, src);
6980 i32_load(code, 8);
6981 local_get(code, outer);
6982 i32_const(code, 8);
6983 code.push(0x6C);
6984 code.push(0x6A);
6985 i64_load(code, 0);
6986 local_set(code, elem);
6987 match filter {
6988 None => emit_set_add_elem(code, ctx, num_regs, result, elem, false),
6989 Some(other) => {
6990 emit_set_contains_to(code, other, elem, cfound, cidx);
6991 local_get(code, cfound);
6992 code.push(0x04);
6993 code.push(0x40); emit_set_add_elem(code, ctx, num_regs, result, elem, false);
6995 code.push(0x0B); }
6997 }
6998 local_get(code, outer);
7000 i32_const(code, 1);
7001 code.push(0x6A);
7002 local_set(code, outer);
7003 code.push(0x0C);
7004 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); }
7008
7009fn lower_union(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, lhs: u16, rhs: u16) -> R<()> {
7015 require_set(kinds, lhs)?;
7016 require_set(kinds, rhs)?;
7017 let result = num_regs + 8;
7018 emit_empty_header(code, ctx, num_regs, result);
7019 emit_set_copy_loop(code, ctx, num_regs, result, lhs as u32, None);
7020 emit_set_copy_loop(code, ctx, num_regs, result, rhs as u32, None);
7021 local_get(code, result);
7022 local_set(code, dst as u32);
7023 Ok(())
7024}
7025
7026fn lower_intersect(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, lhs: u16, rhs: u16) -> R<()> {
7030 require_set(kinds, lhs)?;
7031 require_set(kinds, rhs)?;
7032 let result = num_regs + 8;
7033 emit_empty_header(code, ctx, num_regs, result);
7034 emit_set_copy_loop(code, ctx, num_regs, result, lhs as u32, Some(rhs as u32));
7035 local_get(code, result);
7036 local_set(code, dst as u32);
7037 Ok(())
7038}
7039
7040fn require_set(kinds: &KindTable, r: u16) -> R<()> {
7043 if kinds.get(r as usize) == Some(Kind::Set) {
7044 Ok(())
7045 } else {
7046 Err(WasmLowerError::Unsupported("union/intersect of a non-Set value"))
7047 }
7048}
7049
7050fn lower_contains(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, dst: u16, collection: u16, value: u16) -> R<()> {
7054 let set_text = matches!(kinds.get(collection as usize), Some(Kind::SetText) | Some(Kind::CrdtSetText));
7055 let elem = match kinds.get(collection as usize) {
7056 Some(Kind::Set) => Kind::Int, Some(Kind::SetText) | Some(Kind::CrdtSetText) => Kind::Text, _ => seq_elem_kind(kinds, collection)?,
7059 };
7060 if elem == Kind::Text && !set_text {
7063 return Err(WasmLowerError::Unsupported("contains over a sequence of Text (needs byte-equality)"));
7064 }
7065 let (elem_load, elem_eq): (fn(&mut Vec<u8>, u32), u8) =
7066 if elem == Kind::Float { (f64_load, 0x61) } else { (i64_load, 0x51) };
7067 let idx = num_regs + 5; let col = collection as u32;
7069 code.push(0x42);
7071 leb_i64(code, 0);
7072 local_set(code, dst as u32);
7073 i32_const(code, 0);
7075 local_set(code, idx);
7076 code.push(0x02);
7077 code.push(0x40); code.push(0x03);
7079 code.push(0x40); local_get(code, idx);
7082 local_get(code, col);
7083 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
7086 leb_u32(code, 1); if set_text {
7089 emit_set_elem_eq(code, num_regs, col, idx, value as u32, true);
7090 } else {
7091 local_get(code, col);
7092 i32_load(code, 8); local_get(code, idx);
7094 i32_const(code, 8);
7095 code.push(0x6C);
7096 code.push(0x6A);
7097 elem_load(code, 0);
7098 local_get(code, value as u32);
7099 code.push(elem_eq);
7100 }
7101 code.push(0x04);
7102 code.push(0x40); code.push(0x42);
7104 leb_i64(code, 1);
7105 local_set(code, dst as u32); code.push(0x0C);
7107 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
7111 i32_const(code, 1);
7112 code.push(0x6A);
7113 local_set(code, idx);
7114 code.push(0x0C);
7115 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); Ok(())
7119}
7120
7121fn lower_concat(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, lhs: u16, rhs: u16) -> R<()> {
7126 let (a, b) = (num_regs + 8, num_regs + 9);
7130 emit_stringify(code, ctx, num_regs, lhs as u32, kinds.get(lhs as usize), a)?;
7131 emit_stringify(code, ctx, num_regs, rhs as u32, kinds.get(rhs as usize), b)?;
7132 emit_text_concat(code, ctx, num_regs, a, b, dst as u32);
7133 Ok(())
7134}
7135
7136fn parse_align_spec(spec: &str) -> Option<(i32, u32)> {
7140 if let Some(first) = spec.as_bytes().first() {
7141 let align = match first {
7142 b'>' => Some(0),
7143 b'<' => Some(1),
7144 b'^' => Some(2),
7145 _ => None,
7146 };
7147 if let Some(a) = align {
7148 return spec[1..].parse::<u32>().ok().map(|w| (a, w));
7149 }
7150 }
7151 spec.parse::<u32>().ok().map(|w| (0, w))
7153}
7154
7155fn lower_format_value(
7161 code: &mut Vec<u8>,
7162 kinds: &KindTable,
7163 ctx: &Ctx,
7164 num_regs: u32,
7165 dst: u16,
7166 src: u16,
7167 spec: u32,
7168 debug_prefix: u32,
7169) -> R<()> {
7170 if debug_prefix != u32::MAX {
7171 return Err(WasmLowerError::Unsupported("formatted value with a debug prefix"));
7172 }
7173 let spec_s = match spec {
7174 u32::MAX => return Err(WasmLowerError::Unsupported("formatted value without a spec")),
7175 idx => match ctx.constants.get(idx as usize) {
7176 Some(Constant::Text(s)) => s.as_str(),
7177 _ => return Err(WasmLowerError::Unsupported("format spec is not a text constant")),
7178 },
7179 };
7180 if !spec_s.starts_with('.') {
7183 let (align, width) = parse_align_spec(spec_s).ok_or(WasmLowerError::Unsupported("unsupported format spec"))?;
7184 let fidx = (ctx.host_index)(HostFn::FmtAlignInto).ok_or(WasmLowerError::Unsupported("align formatter not imported"))?;
7185 let (hdr, data, len, text) = (num_regs + 5, num_regs + 6, num_regs + 7, num_regs + 9);
7186 emit_stringify(code, ctx, num_regs, src as u32, kinds.get(src as usize), text)?;
7188 local_get(code, text);
7190 i32_load(code, 0);
7191 i32_const(code, width as i32);
7192 code.push(0x6A); emit_alloc(code, ctx, data);
7194 local_get(code, data);
7196 local_get(code, text);
7197 i32_const(code, width as i32);
7198 i32_const(code, align);
7199 code.push(0x10); leb_u32(code, fidx);
7201 local_set(code, len);
7202 i32_const(code, 16);
7204 emit_alloc(code, ctx, hdr);
7205 for off in [0u32, 4] {
7206 local_get(code, hdr);
7207 local_get(code, len);
7208 i32_store(code, off);
7209 }
7210 local_get(code, hdr);
7211 local_get(code, data);
7212 i32_store(code, 8);
7213 local_get(code, hdr);
7214 local_set(code, dst as u32);
7215 return Ok(());
7216 }
7217 let prec = spec_s
7218 .strip_prefix('.')
7219 .and_then(|r| r.parse::<u32>().ok())
7220 .ok_or(WasmLowerError::Unsupported("unsupported format spec (only `.N` precision)"))?;
7221 match kinds.get(src as usize) {
7222 Some(Kind::Float) | Some(Kind::Int) | Some(Kind::Bool) => {}
7223 _ => return Err(WasmLowerError::Unsupported("format `.N` on a non-numeric value")),
7224 }
7225 let (hdr, data, len) = (num_regs + 5, num_regs + 6, num_regs + 7);
7226 i32_const(code, 16);
7228 emit_alloc(code, ctx,hdr);
7229 i32_const(code, (340 + prec) as i32);
7230 emit_alloc(code, ctx,data);
7231 let fidx = (ctx.host_index)(HostFn::FmtF64PrecInto).ok_or(WasmLowerError::Unsupported("precision formatter not imported"))?;
7233 local_get(code, data);
7234 push_as_f64(code, src, kinds.get(src as usize))?;
7235 i32_const(code, prec as i32);
7236 code.push(0x10); leb_u32(code, fidx);
7238 local_set(code, len);
7239 for off in [0u32, 4] {
7241 local_get(code, hdr);
7242 local_get(code, len);
7243 i32_store(code, off);
7244 }
7245 local_get(code, hdr);
7246 local_get(code, data);
7247 i32_store(code, 8);
7248 local_get(code, hdr);
7249 local_set(code, dst as u32);
7250 Ok(())
7251}
7252
7253fn emit_text_concat(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, a: u32, b: u32, out: u32) {
7257 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7);
7258 i32_const(code, 16);
7259 emit_alloc(code, ctx,hdr);
7260 local_get(code, a);
7263 i32_load(code, 0);
7264 local_get(code, b);
7265 i32_load(code, 0);
7266 code.push(0x6A); emit_alloc(code, ctx,data);
7268 emit_byte_copy(code, idx, data, a, a, false); emit_byte_copy(code, idx, data, a, b, true); for off in [0u32, 4] {
7271 local_get(code, hdr);
7272 local_get(code, a);
7273 i32_load(code, 0);
7274 local_get(code, b);
7275 i32_load(code, 0);
7276 code.push(0x6A);
7277 i32_store(code, off);
7278 }
7279 local_get(code, hdr);
7280 local_get(code, data);
7281 i32_store(code, 8);
7282 local_get(code, hdr);
7283 local_set(code, out);
7284}
7285
7286fn emit_slot_store(code: &mut Vec<u8>, k: Option<Kind>, off: u32) -> R<()> {
7289 match k {
7290 Some(Kind::Float) => f64_store(code, off),
7291 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Char) | Some(Kind::Moment) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) | Some(Kind::Word64) => i64_store(code, off),
7292 Some(Kind::Date) | Some(Kind::SeqInt) | Some(Kind::SeqBool) | Some(Kind::SeqFloat) | Some(Kind::SeqText) | Some(Kind::SeqStruct) | Some(Kind::SeqEnum) | Some(Kind::SeqSeqInt) | Some(Kind::SeqAny) | Some(Kind::Text) | Some(Kind::Struct) | Some(Kind::Map) | Some(Kind::Set) | Some(Kind::SetText) | Some(Kind::CrdtSetText) | Some(Kind::Enum) | Some(Kind::Closure) | Some(Kind::Tuple) | Some(Kind::Rational) | Some(Kind::Optional) | Some(Kind::Word32) | Some(Kind::SeqWord32) | Some(Kind::SeqWord64) | Some(Kind::BigInt) | Some(Kind::Complex) | Some(Kind::Modular) | Some(Kind::Decimal) | Some(Kind::Money) | Some(Kind::Quantity) | Some(Kind::Uuid) | Some(Kind::Lanes) | Some(Kind::LanesV) | Some(Kind::Dynamic) => {
7293 i32_store(code, off)
7294 }
7295 None => return Err(WasmLowerError::Unsupported("struct field of unknown kind")),
7296 }
7297 Ok(())
7298}
7299
7300fn emit_slot_load(code: &mut Vec<u8>, k: Option<Kind>, off: u32) -> R<()> {
7302 match k {
7303 Some(Kind::Float) => f64_load(code, off),
7304 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Char) | Some(Kind::Moment) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) | Some(Kind::Word64) => i64_load(code, off),
7305 Some(Kind::Date) | Some(Kind::SeqInt) | Some(Kind::SeqBool) | Some(Kind::SeqFloat) | Some(Kind::SeqText) | Some(Kind::SeqStruct) | Some(Kind::SeqEnum) | Some(Kind::SeqSeqInt) | Some(Kind::SeqAny) | Some(Kind::Text) | Some(Kind::Struct) | Some(Kind::Map) | Some(Kind::Set) | Some(Kind::SetText) | Some(Kind::CrdtSetText) | Some(Kind::Enum) | Some(Kind::Closure) | Some(Kind::Tuple) | Some(Kind::Rational) | Some(Kind::Optional) | Some(Kind::Word32) | Some(Kind::SeqWord32) | Some(Kind::SeqWord64) | Some(Kind::BigInt) | Some(Kind::Complex) | Some(Kind::Modular) | Some(Kind::Decimal) | Some(Kind::Money) | Some(Kind::Quantity) | Some(Kind::Uuid) | Some(Kind::Lanes) | Some(Kind::LanesV) | Some(Kind::Dynamic) => {
7306 i32_load(code, off)
7307 }
7308 None => return Err(WasmLowerError::Unsupported("struct field of unknown kind")),
7309 }
7310 Ok(())
7311}
7312
7313fn lower_new_struct(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, count: u16, dst: u16) {
7317 let (hdr, data) = (num_regs + 5, num_regs + 6);
7318 i32_const(code, 16);
7319 emit_alloc(code, ctx,hdr);
7320 i32_const(code, i32::from(count) * 8);
7321 emit_alloc(code, ctx,data);
7322 local_get(code, hdr);
7323 i32_const(code, i32::from(count));
7324 i32_store(code, 0); local_get(code, hdr);
7326 i32_const(code, i32::from(count));
7327 i32_store(code, 4); local_get(code, hdr);
7329 local_get(code, data);
7330 i32_store(code, 8); local_get(code, hdr);
7332 local_set(code, dst as u32);
7333}
7334
7335fn cow_struct_inserts(ops: &[Op], num_regs: u32, functions: &[CompiledFunction]) -> Vec<bool> {
7353 let mut cow = vec![false; ops.len()];
7354 let Some(blocks) = Blocks::new(ops) else {
7355 for (pc, op) in ops.iter().enumerate() {
7357 if matches!(op, Op::StructInsert { .. }) {
7358 cow[pc] = true;
7359 }
7360 }
7361 return cow;
7362 };
7363 let mut owned = vec![false; num_regs as usize];
7364 let is_owned = |owned: &[bool], r: u16| (r as usize) < owned.len() && owned[r as usize];
7365 let set_owned = |owned: &mut [bool], r: u16, v: bool| {
7366 if (r as usize) < owned.len() {
7367 owned[r as usize] = v;
7368 }
7369 };
7370 for (pc, op) in ops.iter().enumerate() {
7371 if blocks.is_leader(pc) {
7372 owned.iter_mut().for_each(|o| *o = false);
7373 }
7374 match op {
7375 Op::NewStruct { dst, .. } | Op::DeepClone { dst, .. } => set_owned(&mut owned, *dst, true),
7376 Op::StructInsert { obj, value, .. } => {
7377 if !is_owned(&owned, *obj) {
7378 cow[pc] = true; }
7380 set_owned(&mut owned, *value, false); set_owned(&mut owned, *obj, true); }
7383 Op::GetField { dst, .. } => set_owned(&mut owned, *dst, false), _ => {
7385 let (defs, uses) = regsplit::op_def_uses(op, functions);
7386 for r in defs.into_iter().chain(uses) {
7387 set_owned(&mut owned, r, false);
7388 }
7389 }
7390 }
7391 }
7392 cow
7393}
7394
7395fn lower_struct_insert(
7399 code: &mut Vec<u8>,
7400 kinds: &KindTable,
7401 ctx: &Ctx,
7402 num_regs: u32,
7403 slot: u16,
7404 obj: u16,
7405 value: u16,
7406 cow: bool,
7407) -> R<()> {
7408 if kinds.get(obj as usize) != Some(Kind::Struct) {
7412 return Err(WasmLowerError::Unsupported("struct field set on a value with no known struct layout"));
7413 }
7414 if cow {
7415 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7);
7416 emit_buffer_clone(code, ctx, hdr, data, idx, obj as u32, false);
7417 local_get(code, hdr);
7418 local_set(code, obj as u32);
7419 }
7420 let off = u32::from(slot) * 8;
7421 local_get(code, obj as u32);
7422 i32_load(code, 8); local_get(code, value as u32);
7424 emit_slot_store(code, kinds.get(value as usize), off)
7425}
7426
7427fn lower_get_field(code: &mut Vec<u8>, kinds: &KindTable, slot: u16, dst: u16, obj: u16) -> R<()> {
7430 if kinds.get(obj as usize) != Some(Kind::Struct) {
7431 return Err(WasmLowerError::Unsupported("field access on a value with no known struct layout"));
7432 }
7433 let off = u32::from(slot) * 8;
7434 local_get(code, obj as u32);
7435 i32_load(code, 8); emit_slot_load(code, kinds.get(dst as usize), off)?;
7437 local_set(code, dst as u32);
7438 Ok(())
7439}
7440
7441fn is_clone_trivial(kind: Option<Kind>) -> bool {
7444matches!(kind, Some(Kind::Int | Kind::Bool | Kind::Char | Kind::Float | Kind::Date | Kind::Moment | Kind::Duration | Kind::Time | Kind::Span))
7445}
7446
7447fn field_clone_ok(kind: Option<Kind>) -> bool {
7452 is_clone_trivial(kind)
7453 || matches!(kind, Some(Kind::Text | Kind::SeqInt | Kind::SeqBool | Kind::SeqFloat | Kind::SeqAny | Kind::Set))
7454}
7455
7456fn emit_buffer_clone(code: &mut Vec<u8>, ctx: &Ctx, hdr: u32, data: u32, idx: u32, src_local: u32, is_text: bool) {
7461 i32_const(code, 16);
7462 emit_alloc(code, ctx,hdr);
7463 local_get(code, src_local);
7464 i32_load(code, 0); if !is_text {
7466 i32_const(code, 8);
7467 code.push(0x6C); }
7469 emit_alloc(code, ctx,data);
7470 if is_text {
7471 emit_byte_copy(code, idx, data, src_local, src_local, false);
7472 } else {
7473 emit_seq_copy(code, idx, data, src_local, src_local, false);
7474 }
7475 for off in [0u32, 4] {
7476 local_get(code, hdr);
7477 local_get(code, src_local);
7478 i32_load(code, 0); i32_store(code, off);
7480 }
7481 local_get(code, hdr);
7482 local_get(code, data);
7483 i32_store(code, 8); }
7485
7486fn emit_map_clone(code: &mut Vec<u8>, ctx: &Ctx, hdr: u32, data: u32, idx: u32, src_local: u32) {
7493 i32_const(code, 16);
7494 emit_alloc(code, ctx,hdr);
7495 local_get(code, src_local);
7497 i32_load(code, 0); i32_const(code, 16);
7499 code.push(0x6C);
7500 emit_alloc(code, ctx,data);
7501 i32_const(code, 0);
7503 local_set(code, idx);
7504 code.push(0x02);
7505 code.push(0x40); code.push(0x03);
7507 code.push(0x40); local_get(code, idx);
7509 local_get(code, src_local);
7510 i32_load(code, 0);
7511 i32_const(code, 2);
7512 code.push(0x6C); code.push(0x4E); code.push(0x0D);
7515 leb_u32(code, 1); local_get(code, data);
7517 local_get(code, idx);
7518 i32_const(code, 8);
7519 code.push(0x6C);
7520 code.push(0x6A); local_get(code, src_local);
7522 i32_load(code, 8);
7523 local_get(code, idx);
7524 i32_const(code, 8);
7525 code.push(0x6C);
7526 code.push(0x6A); i64_load(code, 0);
7528 i64_store(code, 0);
7529 local_get(code, idx);
7530 i32_const(code, 1);
7531 code.push(0x6A);
7532 local_set(code, idx);
7533 code.push(0x0C);
7534 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); for off in [0u32, 4] {
7538 local_get(code, hdr);
7539 local_get(code, src_local);
7540 i32_load(code, 0);
7541 i32_store(code, off);
7542 }
7543 local_get(code, hdr);
7544 local_get(code, data);
7545 i32_store(code, 8);
7546}
7547
7548#[allow(clippy::too_many_arguments)]
7555fn emit_clone_each_element(
7556 code: &mut Vec<u8>,
7557 ctx: &Ctx,
7558 outer_hdr: u32,
7559 data: u32,
7560 counter: u32,
7561 isrc: u32,
7562 ihdr: u32,
7563 idata: u32,
7564 iidx: u32,
7565 is_text: bool,
7566) {
7567 i32_const(code, 0);
7568 local_set(code, counter);
7569 code.push(0x02);
7570 code.push(0x40); code.push(0x03);
7572 code.push(0x40); local_get(code, counter);
7574 local_get(code, outer_hdr);
7575 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
7578 leb_u32(code, 1); local_get(code, data);
7581 local_get(code, counter);
7582 i32_const(code, 8);
7583 code.push(0x6C); code.push(0x6A); i32_load(code, 0);
7586 local_set(code, isrc);
7587 emit_buffer_clone(code, ctx, ihdr, idata, iidx, isrc, is_text);
7588 local_get(code, data);
7590 local_get(code, counter);
7591 i32_const(code, 8);
7592 code.push(0x6C); code.push(0x6A); local_get(code, ihdr);
7595 i32_store(code, 0);
7596 local_get(code, counter);
7598 i32_const(code, 1);
7599 code.push(0x6A); local_set(code, counter);
7601 code.push(0x0C);
7602 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); }
7606
7607fn lower_deep_clone(code: &mut Vec<u8>, kinds: &KindTable, structs: &kind::StructLayout, ctx: &Ctx, num_regs: u32, dst: u16, src: u16) -> R<()> {
7614 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7);
7615 let s = src as u32;
7616 match kinds.get(src as usize) {
7617 Some(Kind::Int) | Some(Kind::Bool) | Some(Kind::Float) | Some(Kind::Date) | Some(Kind::Moment) | Some(Kind::Duration) | Some(Kind::Time) | Some(Kind::Span) => {
7619 local_get(code, s);
7620 local_set(code, dst as u32);
7621 return Ok(());
7622 }
7623 Some(Kind::Struct)
7630 if structs
7631 .reg_layout
7632 .get(&src)
7633 .is_some_and(|l| l.iter().all(|&(_, vr)| field_clone_ok(kinds.get(vr as usize)))) =>
7634 {
7635 i32_const(code, 16);
7636 emit_alloc(code, ctx,hdr);
7637 local_get(code, s);
7638 i32_load(code, 0); i32_const(code, 8);
7640 code.push(0x6C); emit_alloc(code, ctx,data);
7642 emit_seq_copy(code, idx, data, s, s, false); for off in [0u32, 4] {
7644 local_get(code, hdr);
7645 local_get(code, s);
7646 i32_load(code, 0);
7647 i32_store(code, off);
7648 }
7649 local_get(code, hdr);
7650 local_get(code, data);
7651 i32_store(code, 8);
7652 let (fsrc, fhdr, fdata, fidx) = (num_regs + 8, num_regs + 9, num_regs + 10, num_regs + 11);
7656 let layout = structs.reg_layout.get(&src).expect("guarded by is_some_and above");
7657 for (slot, &(_, vr)) in layout.iter().enumerate() {
7658 let fk = kinds.get(vr as usize);
7659 if is_clone_trivial(fk) {
7660 continue; }
7662 let off = slot as u32 * 8;
7663 local_get(code, data);
7664 i32_load(code, off); local_set(code, fsrc);
7666 emit_buffer_clone(code, ctx, fhdr, fdata, fidx, fsrc, fk == Some(Kind::Text));
7667 local_get(code, data);
7668 local_get(code, fhdr); i32_store(code, off);
7670 }
7671 local_get(code, hdr);
7672 local_set(code, dst as u32);
7673 return Ok(());
7674 }
7675 Some(k @ (Kind::Text | Kind::SeqInt | Kind::SeqBool | Kind::SeqFloat | Kind::SeqAny | Kind::Set | Kind::SetText)) => {
7680 emit_buffer_clone(code, ctx, hdr, data, idx, s, k == Kind::Text);
7681 local_get(code, hdr);
7682 local_set(code, dst as u32);
7683 Ok(())
7684 }
7685 Some(Kind::Map) => {
7687 emit_map_clone(code, ctx, hdr, data, idx, s);
7688 local_get(code, hdr);
7689 local_set(code, dst as u32);
7690 Ok(())
7691 }
7692 Some(Kind::SeqSeqInt) => {
7696 emit_buffer_clone(code, ctx, hdr, data, idx, s, false);
7697 let (isrc, ihdr, idata, iidx) = (num_regs + 8, num_regs + 9, num_regs + 10, num_regs + 11);
7698 emit_clone_each_element(code, ctx, hdr, data, idx, isrc, ihdr, idata, iidx, false);
7699 local_get(code, hdr);
7700 local_set(code, dst as u32);
7701 Ok(())
7702 }
7703 _ => Err(WasmLowerError::Unsupported("deep clone of an unsupported value kind")),
7704 }
7705}
7706
7707fn lower_text_eq(code: &mut Vec<u8>, num_regs: u32, dst: u16, lhs: u16, rhs: u16, negate: bool) {
7710 let (a, b) = (lhs as u32, rhs as u32);
7711 let idx = num_regs + 7;
7712 code.push(0x42);
7714 leb_i64(code, 1);
7715 local_set(code, dst as u32);
7716 local_get(code, a);
7718 i32_load(code, 0);
7719 local_get(code, b);
7720 i32_load(code, 0);
7721 code.push(0x47); code.push(0x04);
7723 code.push(0x40); code.push(0x42);
7725 leb_i64(code, 0);
7726 local_set(code, dst as u32); code.push(0x05); i32_const(code, 0);
7729 local_set(code, idx);
7730 code.push(0x02);
7731 code.push(0x40); code.push(0x03);
7733 code.push(0x40); local_get(code, idx);
7735 local_get(code, a);
7736 i32_load(code, 0);
7737 code.push(0x4E); code.push(0x0D);
7739 leb_u32(code, 1); local_get(code, a);
7742 i32_load(code, 8);
7743 local_get(code, idx);
7744 code.push(0x6A);
7745 i32_load8_u(code, 0);
7746 local_get(code, b);
7747 i32_load(code, 8);
7748 local_get(code, idx);
7749 code.push(0x6A);
7750 i32_load8_u(code, 0);
7751 code.push(0x47); code.push(0x04);
7753 code.push(0x40); code.push(0x42);
7755 leb_i64(code, 0);
7756 local_set(code, dst as u32); code.push(0x0C);
7758 leb_u32(code, 2); code.push(0x0B); local_get(code, idx);
7761 i32_const(code, 1);
7762 code.push(0x6A);
7763 local_set(code, idx);
7764 code.push(0x0C);
7765 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); code.push(0x0B); if negate {
7770 local_get(code, dst as u32);
7771 code.push(0x50); code.push(0xAD); local_set(code, dst as u32);
7774 }
7775}
7776
7777fn emit_stringify(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, src: u32, kind: Option<Kind>, out: u32) -> R<()> {
7783 match kind {
7784 Some(Kind::Text) => {
7785 local_get(code, src);
7786 local_set(code, out);
7787 }
7788 Some(Kind::BigInt) => {
7791 let to_text = (ctx.host_index)(HostFn::BigintToText).ok_or(WasmLowerError::Unsupported("bigint_to_text not imported"))?;
7792 local_get(code, src);
7793 code.push(0x10); leb_u32(code, to_text);
7795 local_set(code, out);
7796 }
7797 Some(k @ (Kind::Int | Kind::Float | Kind::Bool)) => {
7798 let (host, bufsize) = match k {
7800 Kind::Int => (HostFn::FmtI64Into, 24), Kind::Float => (HostFn::FmtF64Into, 340), _ => (HostFn::FmtBoolInto, 8), };
7804 let (h, data, tmp) = (num_regs + 5, num_regs + 6, num_regs + 7);
7805 i32_const(code, 16);
7806 emit_alloc(code, ctx,h);
7807 i32_const(code, bufsize);
7808 emit_alloc(code, ctx,data);
7809 let fidx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("text formatter not imported"))?;
7811 local_get(code, data);
7812 local_get(code, src);
7813 code.push(0x10); leb_u32(code, fidx);
7815 local_set(code, tmp); local_get(code, h);
7818 local_get(code, tmp);
7819 i32_store(code, 0);
7820 local_get(code, h);
7821 local_get(code, tmp);
7822 i32_store(code, 4);
7823 local_get(code, h);
7824 local_get(code, data);
7825 i32_store(code, 8);
7826 local_get(code, h);
7827 local_set(code, out);
7828 }
7829 Some(k @ (Kind::SeqInt | Kind::SeqBool | Kind::SeqAny | Kind::Set)) => {
7834 let host = match k {
7835 Kind::Set => HostFn::FmtSetI64Into,
7836 Kind::SeqBool => HostFn::FmtSeqBoolInto, _ => HostFn::FmtSeqI64Into,
7838 };
7839 let (h, data, tmp) = (num_regs + 5, num_regs + 6, num_regs + 7);
7840 i32_const(code, 16);
7841 emit_alloc(code, ctx,h);
7842 local_get(code, src);
7844 i32_load(code, 0); i32_const(code, 24);
7846 code.push(0x6C); i32_const(code, 8);
7848 code.push(0x6A); emit_alloc(code, ctx,data);
7850 let fidx = (ctx.host_index)(host).ok_or(WasmLowerError::Unsupported("collection formatter not imported"))?;
7852 local_get(code, data);
7853 local_get(code, src);
7854 code.push(0x10); leb_u32(code, fidx);
7856 local_set(code, tmp);
7857 local_get(code, h);
7858 local_get(code, tmp);
7859 i32_store(code, 0);
7860 local_get(code, h);
7861 local_get(code, tmp);
7862 i32_store(code, 4);
7863 local_get(code, h);
7864 local_get(code, data);
7865 i32_store(code, 8);
7866 local_get(code, h);
7867 local_set(code, out);
7868 }
7869 _ => return Err(WasmLowerError::Unsupported("concat operand kind cannot be stringified yet")),
7870 }
7871 Ok(())
7872}
7873
7874fn emit_struct_display(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, def: &StructTypeDef, handle: u32, out: u32, part: u32, field_i32: u32) -> R<()> {
7881 let mut order: Vec<usize> = (0..def.fields.len()).collect();
7882 order.sort_by(|&a, &b| def.fields[a].0.cmp(&def.fields[b].0));
7883 if def.fields.is_empty() {
7884 lower_text_literal(code, ctx, num_regs, def.name.as_bytes());
7885 local_set(code, out);
7886 return Ok(());
7887 }
7888 lower_text_literal(code, ctx, num_regs, format!("{} {{ ", def.name).as_bytes());
7889 local_set(code, out);
7890 let n = order.len();
7891 for (j, &i) in order.iter().enumerate() {
7892 let (fname, bt) = &def.fields[i];
7893 let ek = kind::boundary_to_kind(bt);
7894 lower_text_literal(code, ctx, num_regs, format!("{fname}: ").as_bytes());
7895 local_set(code, part);
7896 emit_text_concat(code, ctx, num_regs, out, part, out);
7897 let elem_tmp = match ek.map(Kind::wasm_valtype) {
7899 Some(F64) => num_regs + 12,
7900 Some(I64) => num_regs + 1,
7901 _ => field_i32,
7902 };
7903 local_get(code, handle);
7904 i32_load(code, 8); emit_slot_load(code, ek, (i as u32) * 8)?;
7906 local_set(code, elem_tmp);
7907 emit_stringify(code, ctx, num_regs, elem_tmp, ek, part)?;
7908 emit_text_concat(code, ctx, num_regs, out, part, out);
7909 if j + 1 < n {
7910 lower_text_literal(code, ctx, num_regs, b", ");
7911 local_set(code, part);
7912 emit_text_concat(code, ctx, num_regs, out, part, out);
7913 }
7914 }
7915 lower_text_literal(code, ctx, num_regs, b" }");
7916 local_set(code, part);
7917 emit_text_concat(code, ctx, num_regs, out, part, out);
7918 Ok(())
7919}
7920
7921fn lower_show_struct(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, src: u16) -> R<()> {
7922 let type_name = plan
7923 .structs
7924 .struct_name_of
7925 .get(&src)
7926 .ok_or(WasmLowerError::Unsupported("Show of a struct whose type is not statically known"))?;
7927 let def = ctx
7928 .struct_types
7929 .iter()
7930 .find(|s| &s.name == type_name)
7931 .ok_or(WasmLowerError::Unsupported("Show of an unknown struct type"))?;
7932 let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show struct: print_text not imported"))?;
7933 let num_regs = plan.num_regs;
7934 let out = num_regs + 8;
7935 emit_struct_display(code, ctx, num_regs, def, src as u32, out, num_regs + 9, num_regs + 10)?;
7936 local_get(code, out);
7937 code.push(0x10); leb_u32(code, print_idx);
7939 Ok(())
7940}
7941
7942fn lower_show_seqstruct(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, src: u16) -> R<()> {
7947 let type_name = plan
7948 .structs
7949 .seq_elem_struct_name
7950 .get(&src)
7951 .ok_or(WasmLowerError::Unsupported("Show of a Seq of Struct whose element type is not statically known"))?;
7952 let def = ctx
7953 .struct_types
7954 .iter()
7955 .find(|s| &s.name == type_name)
7956 .ok_or(WasmLowerError::Unsupported("Show seq-struct: unknown element struct type"))?;
7957 let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show seq-struct: print_text not imported"))?;
7958 let num_regs = plan.num_regs;
7959 let m = src as u32;
7960 let (outer_acc, out, i, elem) = (num_regs + 8, num_regs + 9, num_regs + 10, num_regs + 11);
7961 let (part, field_i32) = (num_regs + 13, num_regs + 14);
7962 lower_text_literal(code, ctx, num_regs, b"[");
7963 local_set(code, outer_acc);
7964 i32_const(code, 0);
7965 local_set(code, i);
7966 code.push(0x02);
7967 code.push(0x40); code.push(0x03);
7969 code.push(0x40); local_get(code, i);
7971 local_get(code, m);
7972 i32_load(code, 0);
7973 code.push(0x4E); code.push(0x0D);
7975 leb_u32(code, 1);
7976 local_get(code, i);
7978 code.push(0x45);
7979 code.push(0x04);
7980 code.push(0x40);
7981 code.push(0x05);
7982 lower_text_literal(code, ctx, num_regs, b", ");
7983 local_set(code, part);
7984 emit_text_concat(code, ctx, num_regs, outer_acc, part, outer_acc);
7985 code.push(0x0B); local_get(code, m);
7988 i32_load(code, 8);
7989 local_get(code, i);
7990 i32_const(code, 8);
7991 code.push(0x6C);
7992 code.push(0x6A);
7993 i32_load(code, 0);
7994 local_set(code, elem);
7995 emit_struct_display(code, ctx, num_regs, def, elem, out, part, field_i32)?;
7996 emit_text_concat(code, ctx, num_regs, outer_acc, out, outer_acc);
7997 local_get(code, i);
7998 i32_const(code, 1);
7999 code.push(0x6A);
8000 local_set(code, i);
8001 code.push(0x0C);
8002 leb_u32(code, 0);
8003 code.push(0x0B); code.push(0x0B); lower_text_literal(code, ctx, num_regs, b"]");
8006 local_set(code, part);
8007 emit_text_concat(code, ctx, num_regs, outer_acc, part, outer_acc);
8008 local_get(code, outer_acc);
8009 code.push(0x10); leb_u32(code, print_idx);
8011 Ok(())
8012}
8013
8014fn lower_show_seqseq(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, src: u16) -> R<()> {
8020 let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show seq-of-seq: print_text not imported"))?;
8021 let m = src as u32;
8022 let (acc, part, i, inner) = (num_regs + 8, num_regs + 9, num_regs + 10, num_regs + 11);
8025 lower_text_literal(code, ctx, num_regs, b"[");
8027 local_set(code, acc);
8028 i32_const(code, 0);
8029 local_set(code, i);
8030 code.push(0x02);
8031 code.push(0x40); code.push(0x03);
8033 code.push(0x40); local_get(code, i);
8036 local_get(code, m);
8037 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
8040 leb_u32(code, 1); local_get(code, i);
8043 code.push(0x45); code.push(0x04);
8045 code.push(0x40); code.push(0x05); lower_text_literal(code, ctx, num_regs, b", ");
8048 local_set(code, part);
8049 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8050 code.push(0x0B); local_get(code, m);
8053 i32_load(code, 8); local_get(code, i);
8055 i32_const(code, 8);
8056 code.push(0x6C); code.push(0x6A); i32_load(code, 0); local_set(code, inner);
8060 emit_stringify(code, ctx, num_regs, inner, Some(Kind::SeqInt), part)?;
8062 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8063 local_get(code, i);
8065 i32_const(code, 1);
8066 code.push(0x6A);
8067 local_set(code, i);
8068 code.push(0x0C);
8069 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); lower_text_literal(code, ctx, num_regs, b"]");
8074 local_set(code, part);
8075 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8076 local_get(code, acc);
8077 code.push(0x10); leb_u32(code, print_idx);
8079 Ok(())
8080}
8081
8082fn lower_show_map(code: &mut Vec<u8>, plan: &Plan, kinds: &KindTable, ctx: &Ctx, src: u16) -> R<()> {
8090 let key_reg = plan
8091 .structs
8092 .map_set_key
8093 .get(&src)
8094 .copied()
8095 .ok_or(WasmLowerError::Unsupported("Show of a map whose key kind is not statically known"))?;
8096 let val_reg = plan
8097 .structs
8098 .map_set_value
8099 .get(&src)
8100 .copied()
8101 .ok_or(WasmLowerError::Unsupported("Show of a map whose value kind is not statically known"))?;
8102 let key_kind = kinds.get(key_reg as usize);
8103 let val_kind = kinds.get(val_reg as usize);
8104 let key_text = match key_kind {
8105 Some(Kind::Text) => true,
8106 Some(Kind::Int) => false,
8107 _ => return Err(WasmLowerError::Unsupported("Show of a map with a non-Int/Text key")),
8108 };
8109 let val_load = map_value_load(val_kind)?; let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show map: print_text not imported"))?;
8111 let num_regs = plan.num_regs;
8112 let m = src as u32;
8113 let (acc, part, i) = (num_regs + 8, num_regs + 9, num_regs + 10);
8117 let key_tmp = if key_text { num_regs + 11 } else { num_regs + 1 };
8118 let val_tmp = match val_kind.map(Kind::wasm_valtype) {
8119 Some(F64) => num_regs + 12,
8120 Some(I64) => num_regs + 1,
8121 _ => num_regs + 11,
8122 };
8123 lower_text_literal(code, ctx, num_regs, b"{");
8125 local_set(code, acc);
8126 i32_const(code, 0);
8128 local_set(code, i);
8129 code.push(0x02);
8130 code.push(0x40); code.push(0x03);
8132 code.push(0x40); local_get(code, i);
8135 local_get(code, m);
8136 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
8139 leb_u32(code, 1); local_get(code, i);
8142 code.push(0x45); code.push(0x04);
8144 code.push(0x40); code.push(0x05); lower_text_literal(code, ctx, num_regs, b", ");
8147 local_set(code, part);
8148 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8149 code.push(0x0B); emit_map_entry_addr(code, m, i);
8152 if key_text {
8153 i32_load(code, 0);
8154 } else {
8155 i64_load(code, 0);
8156 }
8157 local_set(code, key_tmp);
8158 emit_stringify(code, ctx, num_regs, key_tmp, key_kind, part)?;
8159 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8160 lower_text_literal(code, ctx, num_regs, b": ");
8162 local_set(code, part);
8163 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8164 emit_map_entry_addr(code, m, i);
8166 val_load(code, 8);
8167 local_set(code, val_tmp);
8168 emit_stringify(code, ctx, num_regs, val_tmp, val_kind, part)?;
8169 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8170 local_get(code, i);
8172 i32_const(code, 1);
8173 code.push(0x6A);
8174 local_set(code, i);
8175 code.push(0x0C);
8176 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); lower_text_literal(code, ctx, num_regs, b"}");
8181 local_set(code, part);
8182 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8183 local_get(code, acc);
8184 code.push(0x10); leb_u32(code, print_idx);
8186 Ok(())
8187}
8188
8189fn lower_show_tuple(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, src: u16) -> R<()> {
8199 let elems = plan
8200 .structs
8201 .tuple_layouts
8202 .get(&src)
8203 .ok_or(WasmLowerError::Unsupported("Show of a tuple with no static layout"))?;
8204 let num_regs = plan.num_regs;
8205 let (acc, part) = (num_regs + 8, num_regs + 9);
8206 lower_text_literal(code, ctx, num_regs, b"(");
8208 local_set(code, acc);
8209 let n = elems.len();
8210 for (i, &elem_reg) in elems.iter().enumerate() {
8211 let ek = plan.kinds.get(elem_reg as usize);
8212 let elem_tmp = match ek.map(Kind::wasm_valtype) {
8214 Some(F64) => num_regs + 12, Some(I64) => num_regs + 1, _ => num_regs + 10, };
8218 local_get(code, src as u32);
8219 i32_load(code, 8); emit_slot_load(code, ek, (i as u32) * 8)?;
8221 local_set(code, elem_tmp);
8222 emit_stringify(code, ctx, num_regs, elem_tmp, ek, part)?;
8224 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8225 if i + 1 < n {
8227 lower_text_literal(code, ctx, num_regs, b", ");
8228 local_set(code, part);
8229 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8230 }
8231 }
8232 lower_text_literal(code, ctx, num_regs, b")");
8234 local_set(code, part);
8235 emit_text_concat(code, ctx, num_regs, acc, part, acc);
8236 let idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show tuple: print_text not imported"))?;
8238 local_get(code, acc);
8239 code.push(0x10); leb_u32(code, idx);
8241 Ok(())
8242}
8243
8244fn emit_enum_display(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, def: &EnumTypeDef, handle: u32, out: u32, part: u32, field_i32: u32) -> R<()> {
8260 for v in &def.variants {
8261 let Some(tag) = ctx.constants.iter().position(|c| matches!(c, Constant::Text(n) if *n == v.name)) else {
8265 continue;
8266 };
8267 let tag = tag as i32;
8268 local_get(code, handle);
8269 i32_load(code, 0); i32_const(code, tag);
8271 code.push(0x46); code.push(0x04);
8273 code.push(0x40); lower_text_literal(code, ctx, num_regs, v.name.as_bytes());
8276 local_set(code, out);
8277 if !v.field_types.is_empty() {
8278 lower_text_literal(code, ctx, num_regs, b"(");
8280 local_set(code, part);
8281 emit_text_concat(code, ctx, num_regs, out, part, out);
8282 let n = v.field_types.len();
8283 for (i, ft) in v.field_types.iter().enumerate() {
8284 let ek = kind::boundary_to_kind(ft);
8285 let elem_tmp = match ek.map(Kind::wasm_valtype) {
8286 Some(F64) => num_regs + 12,
8287 Some(I64) => num_regs + 1,
8288 _ => field_i32,
8289 };
8290 local_get(code, handle);
8291 emit_slot_load(code, ek, 8 * (1 + i as u32))?;
8292 local_set(code, elem_tmp);
8293 emit_stringify(code, ctx, num_regs, elem_tmp, ek, part)?;
8294 emit_text_concat(code, ctx, num_regs, out, part, out);
8295 if i + 1 < n {
8296 lower_text_literal(code, ctx, num_regs, b", ");
8297 local_set(code, part);
8298 emit_text_concat(code, ctx, num_regs, out, part, out);
8299 }
8300 }
8301 lower_text_literal(code, ctx, num_regs, b")");
8302 local_set(code, part);
8303 emit_text_concat(code, ctx, num_regs, out, part, out);
8304 }
8305 code.push(0x0B); }
8307 Ok(())
8308}
8309
8310fn lower_show_enum(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, src: u16) -> R<()> {
8311 let type_name = plan
8312 .structs
8313 .ind_type_of
8314 .get(&src)
8315 .ok_or(WasmLowerError::Unsupported("Show of an enum whose type is not statically known"))?;
8316 let def = ctx
8317 .enum_types
8318 .iter()
8319 .find(|e| &e.name == type_name)
8320 .ok_or(WasmLowerError::Unsupported("Show of an unknown enum type"))?;
8321 let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show enum: print_text not imported"))?;
8322 let num_regs = plan.num_regs;
8323 let out = num_regs + 8;
8325 emit_enum_display(code, ctx, num_regs, def, src as u32, out, num_regs + 9, num_regs + 10)?;
8326 local_get(code, out);
8327 code.push(0x10); leb_u32(code, print_idx);
8329 Ok(())
8330}
8331
8332fn lower_show_seqenum(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, src: u16) -> R<()> {
8337 let type_name = plan
8338 .structs
8339 .seq_elem_ind_type
8340 .get(&src)
8341 .ok_or(WasmLowerError::Unsupported("Show of a Seq of Enum whose element type is not statically known"))?;
8342 let def = ctx
8343 .enum_types
8344 .iter()
8345 .find(|e| &e.name == type_name)
8346 .ok_or(WasmLowerError::Unsupported("Show seq-enum: unknown element enum type"))?;
8347 let print_idx = (ctx.host_index)(HostFn::PrintText).ok_or(WasmLowerError::Unsupported("Show seq-enum: print_text not imported"))?;
8348 let num_regs = plan.num_regs;
8349 let m = src as u32;
8350 let (outer_acc, out, i, elem) = (num_regs + 8, num_regs + 9, num_regs + 10, num_regs + 11);
8353 let (part, field_i32) = (num_regs + 13, num_regs + 14);
8354 lower_text_literal(code, ctx, num_regs, b"[");
8355 local_set(code, outer_acc);
8356 i32_const(code, 0);
8357 local_set(code, i);
8358 code.push(0x02);
8359 code.push(0x40); code.push(0x03);
8361 code.push(0x40); local_get(code, i);
8364 local_get(code, m);
8365 i32_load(code, 0);
8366 code.push(0x4E); code.push(0x0D);
8368 leb_u32(code, 1);
8369 local_get(code, i);
8371 code.push(0x45); code.push(0x04);
8373 code.push(0x40);
8374 code.push(0x05); lower_text_literal(code, ctx, num_regs, b", ");
8376 local_set(code, part);
8377 emit_text_concat(code, ctx, num_regs, outer_acc, part, outer_acc);
8378 code.push(0x0B); local_get(code, m);
8381 i32_load(code, 8);
8382 local_get(code, i);
8383 i32_const(code, 8);
8384 code.push(0x6C); code.push(0x6A); i32_load(code, 0);
8387 local_set(code, elem);
8388 emit_enum_display(code, ctx, num_regs, def, elem, out, part, field_i32)?;
8390 emit_text_concat(code, ctx, num_regs, outer_acc, out, outer_acc);
8391 local_get(code, i);
8393 i32_const(code, 1);
8394 code.push(0x6A);
8395 local_set(code, i);
8396 code.push(0x0C);
8397 leb_u32(code, 0);
8398 code.push(0x0B); code.push(0x0B); lower_text_literal(code, ctx, num_regs, b"]");
8401 local_set(code, part);
8402 emit_text_concat(code, ctx, num_regs, outer_acc, part, outer_acc);
8403 local_get(code, outer_acc);
8404 code.push(0x10); leb_u32(code, print_idx);
8406 Ok(())
8407}
8408
8409fn policy_field_slot<'a>(ctx: &'a Ctx, type_name: &str, field_name: &str) -> Option<(u16, &'a BoundaryType)> {
8412 let def = ctx.struct_types.iter().find(|s| s.name == type_name)?;
8413 def.fields.iter().position(|(n, _)| n == field_name).map(|i| (i as u16, &def.fields[i].1))
8414}
8415
8416fn emit_load_text_field(code: &mut Vec<u8>, reg: u16, slot: u16, dst_local: u32) {
8418 local_get(code, reg as u32);
8419 i32_load(code, 8); i32_load(code, u32::from(slot) * 8); local_set(code, dst_local);
8422}
8423
8424fn emit_policy_condition(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, cond: &PolicyCondition, subject: u16, object: u16) -> R<()> {
8429 let (f8, f11) = (plan.num_regs + 8, plan.num_regs + 11);
8430 match cond {
8431 PolicyCondition::FieldEquals { field, value, is_string_literal } => {
8432 if !is_string_literal {
8433 return Err(WasmLowerError::Unsupported("policy: non-Text field comparison"));
8434 }
8435 let subj_type = plan.structs.struct_name_of.get(&subject).ok_or(WasmLowerError::Unsupported("policy: subject has no struct type"))?;
8436 let field_name = ctx.interner.resolve(*field);
8437 let (slot, bt) = policy_field_slot(ctx, subj_type, field_name).ok_or(WasmLowerError::Unsupported("policy: field not in subject type"))?;
8438 if !matches!(bt, BoundaryType::Text) {
8439 return Err(WasmLowerError::Unsupported("policy: field is not Text"));
8440 }
8441 let value_str = ctx.interner.resolve(*value).to_string();
8442 emit_load_text_field(code, subject, slot, f8);
8443 lower_text_literal(code, ctx, plan.num_regs, value_str.as_bytes());
8444 local_set(code, f11);
8445 emit_text_handles_eq(code, plan.num_regs, f8, f11);
8446 }
8447 PolicyCondition::Predicate { predicate, .. } => {
8448 let subj_type = plan.structs.struct_name_of.get(&subject).ok_or(WasmLowerError::Unsupported("policy: subject has no struct type"))?;
8449 let subj_sym = ctx.interner.lookup(subj_type).ok_or(WasmLowerError::Unsupported("policy: subject type not interned"))?;
8450 let preds = ctx.policies.get_predicates(subj_sym).ok_or(WasmLowerError::Unsupported("policy: no predicates for subject type"))?;
8451 let pred = preds.iter().find(|p| p.predicate_name == *predicate).ok_or(WasmLowerError::Unsupported("policy: referenced predicate not found"))?;
8452 emit_policy_condition(code, plan, ctx, &pred.condition, subject, object)?;
8453 }
8454 PolicyCondition::SubjectFieldEqualsObjectField { subject_field, object_field, .. }
8455 | PolicyCondition::ObjectFieldEquals { subject: subject_field, field: object_field, .. } => {
8456 if object == u16::MAX {
8457 return Err(WasmLowerError::Unsupported("policy: cross-field compare needs an object"));
8458 }
8459 let subj_type = plan.structs.struct_name_of.get(&subject).ok_or(WasmLowerError::Unsupported("policy: subject has no struct type"))?;
8460 let obj_type = plan.structs.struct_name_of.get(&object).ok_or(WasmLowerError::Unsupported("policy: object has no struct type"))?;
8461 let sf = ctx.interner.resolve(*subject_field);
8462 let of = ctx.interner.resolve(*object_field);
8463 let (s_slot, s_bt) = policy_field_slot(ctx, subj_type, sf).ok_or(WasmLowerError::Unsupported("policy: subject field not found"))?;
8464 let (o_slot, o_bt) = policy_field_slot(ctx, obj_type, of).ok_or(WasmLowerError::Unsupported("policy: object field not found"))?;
8465 if !matches!(s_bt, BoundaryType::Text) || !matches!(o_bt, BoundaryType::Text) {
8466 return Err(WasmLowerError::Unsupported("policy: cross-field compare of non-Text fields"));
8467 }
8468 emit_load_text_field(code, subject, s_slot, f8);
8469 emit_load_text_field(code, object, o_slot, f11);
8470 emit_text_handles_eq(code, plan.num_regs, f8, f11);
8471 }
8472 PolicyCondition::Or(l, r) => {
8473 emit_policy_condition(code, plan, ctx, l, subject, object)?;
8474 emit_policy_condition(code, plan, ctx, r, subject, object)?;
8475 code.push(0x72); }
8477 PolicyCondition::And(l, r) => {
8478 emit_policy_condition(code, plan, ctx, l, subject, object)?;
8479 emit_policy_condition(code, plan, ctx, r, subject, object)?;
8480 code.push(0x71); }
8482 PolicyCondition::FieldBool { .. } => {
8483 return Err(WasmLowerError::Unsupported("policy: boolean field condition"));
8484 }
8485 }
8486 Ok(())
8487}
8488
8489fn lower_check_policy(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, subject: u16, predicate: crate::Symbol, is_capability: bool, object: u16) -> R<()> {
8494 let subj_type = plan.structs.struct_name_of.get(&subject).ok_or(WasmLowerError::Unsupported("CheckPolicy on a non-struct subject"))?;
8495 let subj_sym = ctx.interner.lookup(subj_type).ok_or(WasmLowerError::Unsupported("CheckPolicy subject type not interned"))?;
8496 let cond = if is_capability {
8497 let caps = ctx.policies.get_capabilities(subj_sym).ok_or(WasmLowerError::Unsupported("CheckPolicy: no capabilities for subject type"))?;
8498 caps.iter().find(|c| c.action == predicate).map(|c| &c.condition).ok_or(WasmLowerError::Unsupported("CheckPolicy: capability not found"))?
8499 } else {
8500 let preds = ctx.policies.get_predicates(subj_sym).ok_or(WasmLowerError::Unsupported("CheckPolicy: no predicates for subject type"))?;
8501 preds.iter().find(|p| p.predicate_name == predicate).map(|p| &p.condition).ok_or(WasmLowerError::Unsupported("CheckPolicy: predicate not found"))?
8502 };
8503 emit_policy_condition(code, plan, ctx, cond, subject, object)?;
8504 code.push(0x45); code.push(0x04);
8506 code.push(0x40); code.push(0x00); code.push(0x0B); Ok(())
8510}
8511
8512fn lower_crdt_bump(code: &mut Vec<u8>, plan: &Plan, ctx: &Ctx, obj: u16, field_const: u32, amount: u16, negate: bool) -> R<()> {
8518 let _ = ctx;
8519 let layout = plan.structs.reg_layout.get(&obj).ok_or(WasmLowerError::Unsupported("CrdtBump on a value with no struct layout"))?;
8523 let slot = layout.iter().position(|(fc, _)| *fc == field_const).ok_or(WasmLowerError::Unsupported("CrdtBump: field not in struct layout"))? as u32;
8524 let off = slot * 8;
8525 local_get(code, obj as u32);
8527 i32_load(code, 8); local_get(code, obj as u32);
8529 i32_load(code, 8);
8530 i64_load(code, off); local_get(code, amount as u32);
8532 code.push(if negate { 0x7D } else { 0x7C }); i64_store(code, off);
8534 Ok(())
8535}
8536
8537fn lower_crdt_merge(code: &mut Vec<u8>, plan: &Plan, target: u16, source: u16) -> R<()> {
8544 let layout = plan.structs.reg_layout.get(&target).ok_or(WasmLowerError::Unsupported("CrdtMerge on a value with no struct layout"))?;
8545 let fields: Vec<(u32, u16)> = layout.clone();
8546 for (slot, (_fc, value_reg)) in fields.iter().enumerate() {
8547 if plan.kinds.get(*value_reg as usize) != Some(Kind::Int) {
8548 return Err(WasmLowerError::Unsupported("CrdtMerge of a non-Int counter field"));
8549 }
8550 let off = (slot as u32) * 8;
8551 local_get(code, target as u32);
8553 i32_load(code, 8); local_get(code, target as u32);
8555 i32_load(code, 8);
8556 i64_load(code, off); local_get(code, source as u32);
8558 i32_load(code, 8);
8559 i64_load(code, off); code.push(0x7C); i64_store(code, off);
8562 }
8563 Ok(())
8564}
8565
8566fn lower_crdt_resolve(code: &mut Vec<u8>, plan: &Plan, kinds: &KindTable, obj: u16, field_const: u32, value: u16) -> R<()> {
8571 let layout = plan.structs.reg_layout.get(&obj).ok_or(WasmLowerError::Unsupported("CrdtResolve on a value with no struct layout"))?;
8572 let slot = layout.iter().position(|(fc, _)| *fc == field_const).ok_or(WasmLowerError::Unsupported("CrdtResolve: field not in struct layout"))? as u32;
8573 let off = slot * 8;
8574 local_get(code, obj as u32);
8575 i32_load(code, 8); local_get(code, value as u32);
8577 emit_slot_store(code, kinds.get(value as usize), off)?;
8578 Ok(())
8579}
8580
8581fn lower_crdt_append(code: &mut Vec<u8>, plan: &Plan, kinds: &KindTable, ctx: &Ctx, seq: u16, value: u16) -> R<()> {
8587 match kinds.get(seq as usize) {
8588 Some(Kind::SeqText) | Some(Kind::SeqInt) | Some(Kind::SeqBool) | Some(Kind::SeqFloat) | Some(Kind::SeqAny) => {
8589 lower_list_push(code, kinds, ctx, plan.num_regs, seq, value)
8590 }
8591 Some(Kind::SetText) => {
8592 emit_set_add_elem(code, ctx, plan.num_regs, seq as u32, value as u32, true);
8593 Ok(())
8594 }
8595 Some(Kind::Set) => {
8596 emit_set_add_elem(code, ctx, plan.num_regs, seq as u32, value as u32, false);
8597 Ok(())
8598 }
8599 _ => Err(WasmLowerError::Unsupported("CrdtAppend to a non-collection CRDT")),
8600 }
8601}
8602
8603fn lower_list_push_field(code: &mut Vec<u8>, plan: &Plan, kinds: &KindTable, ctx: &Ctx, obj: u16, field_const: u32, src: u16) -> R<()> {
8610 emit_cow(code, kinds, &plan.structs, ctx, plan.num_regs, obj)?;
8611 let layout = plan.structs.reg_layout.get(&obj).ok_or(WasmLowerError::Unsupported("ListPushField on a value with no struct layout"))?;
8612 let slot = layout
8613 .iter()
8614 .position(|(fc, _)| *fc == field_const)
8615 .ok_or(WasmLowerError::Unsupported("ListPushField: field not in struct layout"))?;
8616 let elem = kinds.get(src as usize).ok_or(WasmLowerError::Unsupported("ListPushField: unknown pushed-value kind"))?;
8620 let off = (slot as u32) * 8;
8621 let handle = plan.num_regs + 8; local_get(code, obj as u32);
8624 i32_load(code, 8);
8625 i32_load(code, off);
8626 local_set(code, handle);
8627 lower_list_push_at(code, elem, ctx, plan.num_regs, handle, src)
8628}
8629
8630fn emit_sync_call(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, func: u16, args_start: u16, arg_count: u16) -> R<bool> {
8635 for a in 0..arg_count {
8636 let arg = args_start + a;
8637 if cow_clonable(kinds.get(arg as usize)) {
8638 emit_retain(code, arg);
8639 }
8640 }
8641 let pvts = ctx.fn_param_valtypes.get(func as usize).ok_or(WasmLowerError::Unsupported("call of unknown function"))?;
8642 for a in 0..arg_count {
8643 let arg = args_start + a;
8644 let arg_vt = kinds.valtype(arg as usize);
8645 let param_vt = pvts.get(a as usize).copied().unwrap_or(I64);
8646 if arg_vt == param_vt {
8647 local_get(code, arg as u32);
8648 } else if arg_vt == I64 && param_vt == F64 {
8649 push_as_f64(code, arg, kinds.get(arg as usize))?;
8650 } else {
8651 return Err(WasmLowerError::Unsupported("call argument type does not match the parameter"));
8652 }
8653 }
8654 code.push(0x10); leb_u32(code, ctx.fn_base + func as u32);
8656 Ok(ctx.fn_results.get(func as usize).copied().flatten().is_some())
8657}
8658
8659fn lower_chan_try_recv(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, chan: u16) -> R<()> {
8666 let elem = kinds
8667 .get(chan as usize)
8668 .and_then(Kind::seq_elem)
8669 .ok_or(WasmLowerError::Unsupported("try-receive from a channel of unknown element kind"))?;
8670 let ch = chan as u32;
8671 let idx = num_regs + 5; let boxp = num_regs + 6; let val = match elem {
8675 Kind::Float => num_regs + 12, Kind::Int | Kind::Bool | Kind::Char | Kind::Moment | Kind::Duration | Kind::Time | Kind::Span => num_regs + 1, _ => num_regs + 7, };
8679 local_get(code, ch);
8681 i32_load(code, 0);
8682 code.push(0x45); code.push(0x04);
8684 code.push(0x40); i32_const(code, 0);
8686 local_set(code, dst as u32); code.push(0x05); emit_pop_front(code, elem, ch, idx, val)?;
8689 i32_const(code, 8);
8690 emit_alloc(code, ctx,boxp);
8691 local_get(code, boxp);
8692 local_get(code, val);
8693 emit_slot_store(code, Some(elem), 0)?;
8694 local_get(code, boxp);
8695 local_set(code, dst as u32);
8696 code.push(0x0B); Ok(())
8698}
8699
8700fn lower_chan_recv(code: &mut Vec<u8>, kinds: &KindTable, num_regs: u32, dst: u16, chan: u16) -> R<()> {
8704 let elem = kinds.get(chan as usize).and_then(Kind::seq_elem).ok_or(WasmLowerError::Unsupported("receive from a channel of unknown element kind"))?;
8705 let ch = chan as u32;
8706 let idx = num_regs + 5;
8707 local_get(code, ch);
8709 i32_load(code, 0);
8710 code.push(0x45); code.push(0x04);
8712 code.push(0x40);
8713 code.push(0x00); code.push(0x0B);
8715 emit_pop_front(code, elem, ch, idx, dst as u32)
8716}
8717
8718fn emit_pop_front(code: &mut Vec<u8>, elem: Kind, ch: u32, idx: u32, dst: u32) -> R<()> {
8722 let elem_load = seq_elem_load(elem)?;
8723 local_get(code, ch);
8725 i32_load(code, 8);
8726 elem_load(code, 0);
8727 local_set(code, dst);
8728 i32_const(code, 0);
8730 local_set(code, idx);
8731 code.push(0x02);
8732 code.push(0x40);
8733 code.push(0x03);
8734 code.push(0x40);
8735 local_get(code, idx);
8736 local_get(code, ch);
8737 i32_load(code, 0);
8738 i32_const(code, 1);
8739 code.push(0x6B);
8740 code.push(0x4E); code.push(0x0D);
8742 leb_u32(code, 1);
8743 local_get(code, ch);
8744 i32_load(code, 8);
8745 local_get(code, idx);
8746 i32_const(code, 8);
8747 code.push(0x6C);
8748 code.push(0x6A);
8749 local_get(code, ch);
8750 i32_load(code, 8);
8751 local_get(code, idx);
8752 i32_const(code, 1);
8753 code.push(0x6A);
8754 i32_const(code, 8);
8755 code.push(0x6C);
8756 code.push(0x6A);
8757 i64_load(code, 0);
8758 i64_store(code, 0);
8759 local_get(code, idx);
8760 i32_const(code, 1);
8761 code.push(0x6A);
8762 local_set(code, idx);
8763 code.push(0x0C);
8764 leb_u32(code, 0);
8765 code.push(0x0B);
8766 code.push(0x0B);
8767 local_get(code, ch);
8769 local_get(code, ch);
8770 i32_load(code, 0);
8771 i32_const(code, 1);
8772 code.push(0x6B);
8773 i32_store(code, 0);
8774 Ok(())
8775}
8776
8777fn lower_select_wait(code: &mut Vec<u8>, plan: &Plan, kinds: &KindTable, blocks: &Blocks, k: usize, pc: usize, dst_arm: u16) -> R<()> {
8789 #[derive(Clone, Copy)]
8790 enum Arm {
8791 Recv { chan: u16, var: u16 },
8792 Timeout,
8793 }
8794 let mut arms: Vec<Arm> = Vec::new();
8795 for j in blocks.start(k)..pc {
8796 match plan.ops[j] {
8797 Op::SelectArmRecv { chan, var } => arms.push(Arm::Recv { chan, var }),
8798 Op::SelectArmTimeout { .. } => arms.push(Arm::Timeout),
8799 Op::SelectWait { .. } => arms.clear(),
8800 _ => {}
8801 }
8802 }
8803 let da = dst_arm as u32;
8804 let timeout_idx = arms.iter().position(|a| matches!(a, Arm::Timeout));
8805
8806 code.push(0x42); leb_i64(code, -1);
8809 local_set(code, da);
8810
8811 for (i, arm) in arms.iter().enumerate() {
8813 if let Arm::Recv { chan, var } = *arm {
8814 let elem = kinds
8815 .get(chan as usize)
8816 .and_then(Kind::seq_elem)
8817 .ok_or(WasmLowerError::Unsupported("select recv arm on a channel of unknown element kind"))?;
8818 local_get(code, da);
8819 code.push(0x42); leb_i64(code, -1);
8821 code.push(0x51); local_get(code, chan as u32);
8823 i32_load(code, 0); i32_const(code, 0);
8825 code.push(0x4A); code.push(0x71); code.push(0x04);
8828 code.push(0x40); emit_pop_front(code, elem, chan as u32, plan.num_regs + 5, var as u32)?;
8830 code.push(0x42); leb_i64(code, i as i64);
8832 local_set(code, da);
8833 code.push(0x0B); }
8835 }
8836
8837 local_get(code, da);
8839 code.push(0x42); leb_i64(code, -1);
8841 code.push(0x51); code.push(0x04);
8843 code.push(0x40); match timeout_idx {
8845 Some(ti) => {
8846 code.push(0x42); leb_i64(code, ti as i64);
8848 local_set(code, da);
8849 }
8850 None => code.push(0x00), }
8852 code.push(0x0B); Ok(())
8854}
8855
8856fn i64c(code: &mut Vec<u8>, v: i64) {
8858 code.push(0x42);
8859 leb_i64(code, v);
8860}
8861
8862fn lower_magic_div(code: &mut Vec<u8>, num_regs: u32, dst: u16, lhs: u16, magic: u64, more: u8, mul_back: i64) {
8868 const SHIFT_MASK: u8 = 0x3F;
8869 const ADD_MARKER: u8 = 0x40;
8870 const SHIFT_PATH: u8 = 0x80;
8871 let shift = (more & SHIFT_MASK) as i64;
8872 let q = num_regs + 1; let n_lo = num_regs + 2;
8874 let n_hi = num_regs + 3;
8875 let hi = num_regs + 4;
8876 let mask: i64 = 0xFFFF_FFFF;
8877
8878 if more & SHIFT_PATH != 0 {
8879 local_get(code, lhs as u32);
8881 i64c(code, shift);
8882 code.push(0x88); local_set(code, q);
8884 } else {
8885 local_get(code, lhs as u32);
8887 i64c(code, mask);
8888 code.push(0x83); local_set(code, n_lo);
8890 local_get(code, lhs as u32);
8891 i64c(code, 32);
8892 code.push(0x88); local_set(code, n_hi);
8894 let m_lo = (magic & 0xFFFF_FFFF) as i64;
8895 let m_hi = (magic >> 32) as i64;
8896 i64c(code, m_lo);
8898 local_get(code, n_lo);
8899 code.push(0x7E); i64c(code, 32);
8901 code.push(0x88); i64c(code, m_hi);
8903 local_get(code, n_lo);
8904 code.push(0x7E);
8905 i64c(code, mask);
8906 code.push(0x83); code.push(0x7C); i64c(code, m_lo);
8909 local_get(code, n_hi);
8910 code.push(0x7E);
8911 i64c(code, mask);
8912 code.push(0x83);
8913 code.push(0x7C); local_set(code, q); i64c(code, m_hi);
8917 local_get(code, n_hi);
8918 code.push(0x7E); i64c(code, m_hi);
8920 local_get(code, n_lo);
8921 code.push(0x7E);
8922 i64c(code, 32);
8923 code.push(0x88);
8924 code.push(0x7C); i64c(code, m_lo);
8926 local_get(code, n_hi);
8927 code.push(0x7E);
8928 i64c(code, 32);
8929 code.push(0x88);
8930 code.push(0x7C); local_get(code, q);
8932 i64c(code, 32);
8933 code.push(0x88);
8934 code.push(0x7C); local_set(code, hi);
8936 if more & ADD_MARKER != 0 {
8937 local_get(code, lhs as u32);
8939 local_get(code, hi);
8940 code.push(0x7D); i64c(code, 1);
8942 code.push(0x88); local_get(code, hi);
8944 code.push(0x7C); i64c(code, shift);
8946 code.push(0x88); local_set(code, q);
8948 } else {
8949 local_get(code, hi);
8951 i64c(code, shift);
8952 code.push(0x88);
8953 local_set(code, q);
8954 }
8955 }
8956
8957 if mul_back == 0 {
8958 local_get(code, q);
8959 local_set(code, dst as u32);
8960 } else {
8961 local_get(code, lhs as u32);
8963 local_get(code, q);
8964 i64c(code, mul_back);
8965 code.push(0x7E); code.push(0x7D); local_set(code, dst as u32);
8968 }
8969}
8970
8971fn lower_exact_div(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, lhs: u16, rhs: u16) {
8976 let (num, den, a, b) = (num_regs + 1, num_regs + 2, num_regs + 3, num_regs + 4);
8977 let handle = num_regs + 5;
8978 local_get(code, rhs as u32);
8980 code.push(0x50); code.push(0x04);
8982 code.push(0x40);
8983 code.push(0x00); code.push(0x0B);
8985 local_get(code, lhs as u32);
8987 local_set(code, num);
8988 local_get(code, rhs as u32);
8989 local_set(code, den);
8990 local_get(code, den);
8992 i64c(code, 0);
8993 code.push(0x53); code.push(0x04);
8995 code.push(0x40);
8996 i64c(code, 0);
8997 local_get(code, num);
8998 code.push(0x7D); local_set(code, num);
9000 i64c(code, 0);
9001 local_get(code, den);
9002 code.push(0x7D);
9003 local_set(code, den);
9004 code.push(0x0B); local_get(code, num);
9007 local_set(code, a);
9008 local_get(code, a);
9009 i64c(code, 0);
9010 code.push(0x53); code.push(0x04);
9012 code.push(0x40);
9013 i64c(code, 0);
9014 local_get(code, a);
9015 code.push(0x7D);
9016 local_set(code, a);
9017 code.push(0x0B);
9018 local_get(code, den);
9020 local_set(code, b);
9021 code.push(0x02);
9022 code.push(0x40); code.push(0x03);
9024 code.push(0x40); local_get(code, b);
9026 code.push(0x50); code.push(0x0D);
9028 leb_u32(code, 1); local_get(code, a);
9030 local_get(code, b);
9031 code.push(0x81); local_get(code, b);
9033 local_set(code, a); local_set(code, b); code.push(0x0C);
9036 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); local_get(code, num);
9041 local_get(code, a);
9042 code.push(0x7F); local_set(code, num);
9044 local_get(code, den);
9045 local_get(code, a);
9046 code.push(0x7F);
9047 local_set(code, den);
9048 i32_const(code, 16);
9050 emit_alloc(code, ctx,handle);
9051 local_get(code, handle);
9052 local_get(code, num);
9053 i64_store(code, 0);
9054 local_get(code, handle);
9055 local_get(code, den);
9056 i64_store(code, 8);
9057 local_get(code, handle);
9058 local_set(code, dst as u32);
9059}
9060
9061fn emit_byte_copy(code: &mut Vec<u8>, idx: u32, dest_data: u32, a_for_offset: u32, src: u32, offset_by_a: bool) {
9065 i32_const(code, 0);
9066 local_set(code, idx);
9067 code.push(0x02);
9068 code.push(0x40); code.push(0x03);
9070 code.push(0x40); local_get(code, idx);
9072 local_get(code, src);
9073 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
9076 leb_u32(code, 1); local_get(code, dest_data);
9079 if offset_by_a {
9080 local_get(code, a_for_offset);
9081 i32_load(code, 0); local_get(code, idx);
9083 code.push(0x6A);
9084 } else {
9085 local_get(code, idx);
9086 }
9087 code.push(0x6A);
9088 local_get(code, src);
9090 i32_load(code, 8); local_get(code, idx);
9092 code.push(0x6A);
9093 i32_load8_u(code, 0);
9094 i32_store8(code, 0);
9095 local_get(code, idx);
9097 i32_const(code, 1);
9098 code.push(0x6A);
9099 local_set(code, idx);
9100 code.push(0x0C);
9101 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); }
9105
9106fn lower_text_literal(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, bytes: &[u8]) {
9112 let len = bytes.len();
9113 let cap8 = (len + 7) & !7; let (hdr, data) = (num_regs + 5, num_regs + 6);
9115 i32_const(code, 16);
9116 emit_alloc(code, ctx,hdr);
9117 i32_const(code, cap8 as i32);
9118 emit_alloc(code, ctx,data);
9119 for c in 0..(cap8 / 8) {
9120 let mut v: u64 = 0;
9121 for j in 0..8 {
9122 if let Some(&b) = bytes.get(c * 8 + j) {
9123 v |= (b as u64) << (j * 8);
9124 }
9125 }
9126 local_get(code, data);
9127 code.push(0x42); leb_i64(code, v as i64);
9129 i64_store(code, (c * 8) as u32);
9130 }
9131 local_get(code, hdr);
9132 i32_const(code, len as i32);
9133 i32_store(code, 0); local_get(code, hdr);
9135 i32_const(code, len as i32);
9136 i32_store(code, 4); local_get(code, hdr);
9138 local_get(code, data);
9139 i32_store(code, 8); local_get(code, hdr); }
9142
9143fn lower_chr(code: &mut Vec<u8>, ctx: &Ctx, num_regs: u32, dst: u16, arg: u16) {
9149 let c = num_regs + 1; let packed = num_regs + 2; let len = num_regs + 7; let (hdr, data) = (num_regs + 5, num_regs + 6);
9153 let cont = |code: &mut Vec<u8>, shift: i64, pos: i64| {
9155 local_get(code, c);
9156 i64c(code, shift);
9157 code.push(0x88); i64c(code, 0x3F);
9159 code.push(0x83); i64c(code, 0x80);
9161 code.push(0x84); i64c(code, pos);
9163 code.push(0x86); };
9165 let lead = |code: &mut Vec<u8>, shift: i64, mask: i64| {
9167 local_get(code, c);
9168 i64c(code, shift);
9169 code.push(0x88); i64c(code, mask);
9171 code.push(0x84); };
9173 local_get(code, arg as u32);
9175 local_set(code, c);
9176 local_get(code, c);
9178 i64c(code, 0x10FFFF);
9179 code.push(0x56); local_get(code, c);
9181 i64c(code, 0xD800);
9182 code.push(0x5A); local_get(code, c);
9184 i64c(code, 0xDFFF);
9185 code.push(0x58); code.push(0x71); code.push(0x72); code.push(0x04);
9189 code.push(0x40); code.push(0x00); code.push(0x0B); local_get(code, c);
9194 i64c(code, 0x80);
9195 code.push(0x54); code.push(0x04);
9197 code.push(0x40); local_get(code, c);
9199 local_set(code, packed);
9200 i32_const(code, 1);
9201 local_set(code, len);
9202 code.push(0x05); local_get(code, c);
9205 i64c(code, 0x800);
9206 code.push(0x54); code.push(0x04);
9208 code.push(0x40); lead(code, 6, 0xC0);
9210 cont(code, 0, 8);
9211 code.push(0x84); local_set(code, packed);
9213 i32_const(code, 2);
9214 local_set(code, len);
9215 code.push(0x05); local_get(code, c);
9218 i64c(code, 0x10000);
9219 code.push(0x54); code.push(0x04);
9221 code.push(0x40); lead(code, 12, 0xE0);
9223 cont(code, 6, 8);
9224 code.push(0x84); cont(code, 0, 16);
9226 code.push(0x84); local_set(code, packed);
9228 i32_const(code, 3);
9229 local_set(code, len);
9230 code.push(0x05); lead(code, 18, 0xF0);
9232 cont(code, 12, 8);
9233 code.push(0x84); cont(code, 6, 16);
9235 code.push(0x84); cont(code, 0, 24);
9237 code.push(0x84); local_set(code, packed);
9239 i32_const(code, 4);
9240 local_set(code, len);
9241 code.push(0x0B); code.push(0x0B); code.push(0x0B); i32_const(code, 16);
9246 emit_alloc(code, ctx,hdr);
9247 i32_const(code, 8);
9248 emit_alloc(code, ctx,data);
9249 local_get(code, data);
9250 local_get(code, packed);
9251 i64_store(code, 0); local_get(code, hdr);
9253 local_get(code, len);
9254 i32_store(code, 0); local_get(code, hdr);
9256 local_get(code, len);
9257 i32_store(code, 4); local_get(code, hdr);
9259 local_get(code, data);
9260 i32_store(code, 8); local_get(code, hdr);
9262 local_set(code, dst as u32);
9263}
9264
9265fn lower_seq_concat(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, lhs: u16, rhs: u16) -> R<()> {
9268 seq_elem_kind(kinds, lhs)?;
9269 seq_elem_kind(kinds, rhs)?;
9270 let (a, b) = (lhs as u32, rhs as u32);
9271 let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7);
9272 i32_const(code, 16);
9274 emit_alloc(code, ctx,hdr);
9275 local_get(code, a);
9276 i32_load(code, 0);
9277 local_get(code, b);
9278 i32_load(code, 0);
9279 code.push(0x6A); i32_const(code, 8);
9281 code.push(0x6C);
9282 emit_alloc(code, ctx,data);
9283 emit_seq_copy(code, idx, data, a, a, false);
9285 emit_seq_copy(code, idx, data, a, b, true);
9287 for off in [0u32, 4] {
9289 local_get(code, hdr);
9290 local_get(code, a);
9291 i32_load(code, 0);
9292 local_get(code, b);
9293 i32_load(code, 0);
9294 code.push(0x6A);
9295 i32_store(code, off);
9296 }
9297 local_get(code, hdr);
9298 local_get(code, data);
9299 i32_store(code, 8);
9300 local_get(code, hdr);
9301 local_set(code, dst as u32);
9302 Ok(())
9303}
9304
9305fn emit_seq_copy(code: &mut Vec<u8>, idx: u32, dest_data: u32, a_for_offset: u32, src: u32, offset_by_a: bool) {
9309 i32_const(code, 0);
9310 local_set(code, idx);
9311 code.push(0x02);
9312 code.push(0x40); code.push(0x03);
9314 code.push(0x40); local_get(code, idx);
9316 local_get(code, src);
9317 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
9320 leb_u32(code, 1); local_get(code, dest_data);
9323 if offset_by_a {
9324 local_get(code, a_for_offset);
9325 i32_load(code, 0); local_get(code, idx);
9327 code.push(0x6A); } else {
9329 local_get(code, idx);
9330 }
9331 i32_const(code, 8);
9332 code.push(0x6C);
9333 code.push(0x6A);
9334 local_get(code, src);
9336 i32_load(code, 8);
9337 local_get(code, idx);
9338 i32_const(code, 8);
9339 code.push(0x6C);
9340 code.push(0x6A);
9341 i64_load(code, 0);
9342 i64_store(code, 0);
9343 local_get(code, idx);
9345 i32_const(code, 1);
9346 code.push(0x6A);
9347 local_set(code, idx);
9348 code.push(0x0C);
9349 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); }
9353
9354fn lower_slice(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, dst: u16, collection: u16, start: u16, end: u16) -> R<()> {
9360 seq_elem_kind(kinds, collection)?; let col = collection as u32;
9362 let (s0, ee) = (num_regs + 1, num_regs + 2); let (hdr, data, idx) = (num_regs + 5, num_regs + 6, num_regs + 7); i32_const(code, 16);
9366 emit_alloc(code, ctx,hdr);
9367 code.push(0x42);
9369 leb_i64(code, 0); local_get(code, start as u32);
9371 code.push(0x42);
9372 leb_i64(code, 1);
9373 code.push(0x7D); local_get(code, start as u32);
9375 code.push(0x50); code.push(0x1B); local_set(code, s0);
9378 local_get(code, end as u32);
9380 local_set(code, ee);
9381 local_get(code, s0);
9383 local_get(code, ee);
9384 code.push(0x54); local_get(code, ee);
9386 local_get(code, col);
9387 i32_load(code, 0); code.push(0xAD); code.push(0x58); code.push(0x71); code.push(0x04);
9392 code.push(0x40); {
9394 local_get(code, ee);
9396 local_get(code, s0);
9397 code.push(0x7D);
9398 code.push(0xA7); i32_const(code, 8);
9400 code.push(0x6C);
9401 emit_alloc(code, ctx,data);
9402 i32_const(code, 0);
9404 local_set(code, idx);
9405 code.push(0x02);
9406 code.push(0x40); code.push(0x03);
9408 code.push(0x40); local_get(code, idx);
9410 local_get(code, ee);
9411 local_get(code, s0);
9412 code.push(0x7D);
9413 code.push(0xA7); code.push(0x4E); code.push(0x0D);
9416 leb_u32(code, 1); local_get(code, data);
9419 local_get(code, idx);
9420 i32_const(code, 8);
9421 code.push(0x6C);
9422 code.push(0x6A);
9423 local_get(code, col);
9425 i32_load(code, 8);
9426 local_get(code, s0);
9427 code.push(0xA7); local_get(code, idx);
9429 code.push(0x6A); i32_const(code, 8);
9431 code.push(0x6C);
9432 code.push(0x6A);
9433 i64_load(code, 0);
9434 i64_store(code, 0);
9435 local_get(code, idx);
9437 i32_const(code, 1);
9438 code.push(0x6A);
9439 local_set(code, idx);
9440 code.push(0x0C);
9441 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); for off in [0u32, 4] {
9446 local_get(code, hdr);
9447 local_get(code, ee);
9448 local_get(code, s0);
9449 code.push(0x7D);
9450 code.push(0xA7);
9451 i32_store(code, off);
9452 }
9453 local_get(code, hdr);
9454 local_get(code, data);
9455 i32_store(code, 8);
9456 }
9457 code.push(0x05); for off in [0u32, 4, 8] {
9459 local_get(code, hdr);
9460 i32_const(code, 0);
9461 i32_store(code, off);
9462 }
9463 code.push(0x0B); local_get(code, hdr);
9465 local_set(code, dst as u32);
9466 Ok(())
9467}
9468
9469fn lower_iter_prepare(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, num_regs: u32, iterable: u16) -> R<()> {
9474 if !kinds.get(iterable as usize).map(Kind::is_seq).unwrap_or(false) {
9475 return Err(WasmLowerError::Unsupported("iteration over a non-sequence value"));
9476 }
9477 let it = iterable as u32;
9478 let (snap, idx) = (num_regs + 5, num_regs + 6); local_get(code, it);
9481 i32_load(code, 0); i32_const(code, 8);
9483 code.push(0x6C); emit_alloc(code, ctx,snap);
9485 i32_const(code, 0);
9487 local_set(code, idx);
9488 code.push(0x02);
9489 code.push(0x40); code.push(0x03);
9491 code.push(0x40); local_get(code, idx);
9493 local_get(code, it);
9494 i32_load(code, 0); code.push(0x4E); code.push(0x0D);
9497 leb_u32(code, 1); local_get(code, snap);
9500 local_get(code, idx);
9501 i32_const(code, 8);
9502 code.push(0x6C);
9503 code.push(0x6A);
9504 local_get(code, it);
9506 i32_load(code, 8); local_get(code, idx);
9508 i32_const(code, 8);
9509 code.push(0x6C);
9510 code.push(0x6A);
9511 i64_load(code, 0);
9512 i64_store(code, 0);
9513 local_get(code, idx);
9515 i32_const(code, 1);
9516 code.push(0x6A);
9517 local_set(code, idx);
9518 code.push(0x0C);
9519 leb_u32(code, 0); code.push(0x0B); code.push(0x0B); global_get(code, ctx.iter_global);
9524 i32_const(code, 12);
9525 code.push(0x6B); global_set(code, ctx.iter_global);
9527 global_get(code, ctx.iter_global);
9529 local_get(code, snap);
9530 i32_store(code, 0);
9531 global_get(code, ctx.iter_global);
9532 i32_const(code, 0);
9533 i32_store(code, 4);
9534 global_get(code, ctx.iter_global);
9535 local_get(code, it);
9536 i32_load(code, 0);
9537 i32_store(code, 8);
9538 Ok(())
9539}
9540
9541#[allow(clippy::too_many_arguments)]
9546fn lower_iter_next(code: &mut Vec<u8>, kinds: &KindTable, ctx: &Ctx, blocks: &Blocks, k: usize, num_regs: u32, dst: u16, exit: usize, pc: usize) {
9547 let elem_load: fn(&mut Vec<u8>, u32) = match kinds.get(dst as usize).map(Kind::wasm_valtype) {
9550 Some(F64) => f64_load,
9551 Some(I64) => i64_load,
9552 _ => i32_load,
9553 };
9554 let ig = ctx.iter_global;
9555 let pc_local = num_regs;
9556 let fallthrough = blocks.block_of(pc + 1) as u32;
9557 let exit_block = blocks.block_of(exit) as u32;
9558 global_get(code, ig);
9560 i32_load(code, 4); global_get(code, ig);
9562 i32_load(code, 8); code.push(0x48); code.push(0x04);
9565 code.push(0x40); global_get(code, ig);
9568 i32_load(code, 0); global_get(code, ig);
9570 i32_load(code, 4); i32_const(code, 8);
9572 code.push(0x6C); code.push(0x6A); elem_load(code, 0);
9575 local_set(code, dst as u32);
9576 global_get(code, ig);
9578 global_get(code, ig);
9579 i32_load(code, 4);
9580 i32_const(code, 1);
9581 code.push(0x6A); i32_store(code, 4);
9583 code.push(0x41); leb_u32(code, fallthrough);
9586 local_set(code, pc_local);
9587 code.push(0x05); code.push(0x41); leb_u32(code, exit_block);
9591 local_set(code, pc_local);
9592 code.push(0x0B); code.push(0x0C); leb_u32(code, blocks.br_loop(k));
9595}
9596
9597fn op_uses_heap(op: &Op) -> bool {
9600 matches!(
9601 op,
9602 Op::NewEmptyList { .. }
9603 | Op::NewEmptyListI32 { .. }
9604 | Op::Length { .. }
9605 | Op::ListPush { .. }
9606 | Op::ListPop { .. }
9607 | Op::Index { .. }
9608 | Op::IndexUnchecked { .. }
9609 | Op::SetIndex { .. }
9610 | Op::SetIndexUnchecked { .. }
9611 | Op::ListPushField { .. }
9612 | Op::ExactDiv { .. }
9614 | Op::NewRange { .. }
9615 | Op::NewList { .. }
9616 | Op::IterPrepare { .. }
9617 | Op::IterNext { .. }
9618 | Op::IterPop
9619 | Op::Contains { .. }
9620 | Op::SliceOp { .. }
9621 | Op::SeqConcat { .. }
9622 | Op::Concat { .. }
9623 | Op::FormatValue { .. }
9624 | Op::DeepClone { .. }
9625 | Op::NewStruct { .. }
9626 | Op::StructInsert { .. }
9627 | Op::GetField { .. }
9628 | Op::CheckPolicy { .. }
9629 | Op::CrdtBump { .. }
9630 | Op::CrdtMerge { .. }
9631 | Op::NewCrdt { .. }
9632 | Op::CrdtResolve { .. }
9633 | Op::CrdtAppend { .. }
9634 | Op::ChanNew { .. }
9635 | Op::ChanSend { .. }
9636 | Op::ChanRecv { .. }
9637 | Op::ChanTrySend { .. }
9639 | Op::ChanTryRecv { .. }
9640 | Op::SelectWait { .. }
9642 | Op::NetListen { .. }
9644 | Op::NetSend { .. }
9645 | Op::NetStream { .. }
9646 | Op::NetAwait { .. }
9647 | Op::NewEmptyMap { .. }
9648 | Op::NewEmptySet { .. }
9649 | Op::SetAdd { .. }
9650 | Op::RemoveFrom { .. }
9651 | Op::UnionOp { .. }
9652 | Op::IntersectOp { .. }
9653 | Op::NewInductive { .. }
9654 | Op::BindArm { .. }
9655 | Op::NewTuple { .. }
9656 | Op::DestructureTuple { .. }
9657 | Op::MakeClosure { .. }
9658 | Op::CallValue { .. }
9659 | Op::Args { .. }
9662 | Op::CallBuiltin { builtin: BuiltinId::Chr, .. }
9664 | Op::CallBuiltin { builtin: BuiltinId::RepeatSeq, .. }
9666 | Op::CallBuiltin { builtin: BuiltinId::TextBytes, .. }
9668 | Op::CallBuiltin { builtin: BuiltinId::UuidBytes, .. }
9669 | Op::CallBuiltin { builtin: BuiltinId::TextFromBytes, .. }
9670 | Op::CallBuiltin { builtin: BuiltinId::UuidFromBytes, .. }
9671 | Op::CallBuiltin { builtin: BuiltinId::Lanes4Of, .. }
9672 | Op::CallBuiltin { builtin: BuiltinId::Lanes4Word32Make, .. }
9673 | Op::CallBuiltin { builtin: BuiltinId::SeqOfLanes4W32, .. }
9674 | Op::CallBuiltin { builtin: BuiltinId::ReadWireProgram, .. }
9678 )
9679}
9680
9681fn encode_locals(plan: &Plan) -> Vec<u8> {
9685 let mut groups: Vec<(u32, u8)> = Vec::new();
9686 let mut push = |vt: u8, groups: &mut Vec<(u32, u8)>| match groups.last_mut() {
9687 Some((count, t)) if *t == vt => *count += 1,
9688 _ => groups.push((1, vt)),
9689 };
9690 for r in plan.num_params..plan.num_regs {
9691 push(plan.kinds.valtype(r as usize), &mut groups);
9692 }
9693 push(I32, &mut groups); push(I64, &mut groups);
9697 push(I64, &mut groups);
9698 push(I64, &mut groups);
9699 push(I64, &mut groups);
9700 push(I32, &mut groups);
9704 push(I32, &mut groups);
9705 push(I32, &mut groups);
9706 push(I32, &mut groups);
9707 push(I32, &mut groups);
9708 push(I32, &mut groups);
9709 push(I32, &mut groups);
9710 push(F64, &mut groups);
9713 push(I32, &mut groups);
9717 push(I32, &mut groups);
9718
9719 let mut out = Vec::new();
9720 leb_u32(&mut out, groups.len() as u32);
9721 for (count, vt) in groups {
9722 leb_u32(&mut out, count);
9723 out.push(vt);
9724 }
9725 out
9726}
9727
9728fn encode_name(out: &mut Vec<u8>, name: &str) {
9730 leb_u32(out, name.len() as u32);
9731 out.extend_from_slice(name.as_bytes());
9732}
9733
9734fn unsupported_op(op: &Op) -> WasmLowerError {
9737 let what = match op {
9738 Op::ExactDiv { .. } => "exact division (Rational)",
9739 Op::DivPow2 { .. } | Op::MagicDivU { .. } => "oracle division op",
9740 Op::Concat { .. } => "text op",
9741 Op::IndexUnchecked { .. } => "unchecked index (IndexUnchecked)",
9742 Op::CallValue { .. } | Op::MakeClosure { .. } => "closure call",
9743 _ => "op",
9744 };
9745 WasmLowerError::Unsupported(what)
9746}