1use pepl_types::ast::*;
11use wasm_encoder::{BlockType, Function, Instruction, ValType};
12
13use crate::compiler::FuncContext;
14use crate::error::CodegenResult;
15use crate::expr::emit_expr;
16use crate::gas;
17use crate::runtime::*;
18use crate::stmt::emit_stmts;
19use crate::types::*;
20
21const DEFAULT_GAS_LIMIT: i32 = 1_000_000;
27
28pub fn emit_init(
36 state: &StateBlock,
37 derived: Option<&DerivedBlock>,
38 ctx: &mut FuncContext,
39 f: &mut Function,
40) -> CodegenResult<()> {
41 f.instruction(&Instruction::I32Const(DEFAULT_GAS_LIMIT));
43 f.instruction(&Instruction::GlobalSet(GLOBAL_GAS_LIMIT));
44 f.instruction(&Instruction::I32Const(0));
46 f.instruction(&Instruction::GlobalSet(GLOBAL_GAS));
47
48 let field_count = state.fields.len();
50 let entries_local = ctx.alloc_local(ValType::I32);
51
52 f.instruction(&Instruction::I32Const((field_count * 12) as i32));
53 f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
54 f.instruction(&Instruction::LocalSet(entries_local));
55
56 for (i, field) in state.fields.iter().enumerate() {
57 let (key_ptr, key_len) = ctx.intern_string(&field.name.name);
58 let val_local = ctx.alloc_local(ValType::I32);
59 let base = (i * 12) as u64;
60
61 emit_expr(&field.default, ctx, f)?;
63 f.instruction(&Instruction::LocalSet(val_local));
64
65 f.instruction(&Instruction::LocalGet(entries_local));
67 f.instruction(&Instruction::I32Const(key_ptr as i32));
68 f.instruction(&Instruction::I32Store(memarg(base, 2)));
69 f.instruction(&Instruction::LocalGet(entries_local));
71 f.instruction(&Instruction::I32Const(key_len as i32));
72 f.instruction(&Instruction::I32Store(memarg(base + 4, 2)));
73 f.instruction(&Instruction::LocalGet(entries_local));
75 f.instruction(&Instruction::LocalGet(val_local));
76 f.instruction(&Instruction::I32Store(memarg(base + 8, 2)));
77 }
78
79 f.instruction(&Instruction::LocalGet(entries_local));
81 f.instruction(&Instruction::I32Const(field_count as i32));
82 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_RECORD)));
83 f.instruction(&Instruction::GlobalSet(GLOBAL_STATE_PTR));
84
85 if let Some(derived_block) = derived {
87 emit_recompute_derived(derived_block, ctx, f)?;
88 }
89
90 f.instruction(&Instruction::End);
91 Ok(())
92}
93
94pub fn emit_dispatch_action(
106 actions: &[ActionDecl],
107 invariants: &[InvariantDecl],
108 derived: Option<&DerivedBlock>,
109 ctx: &mut FuncContext,
110 f: &mut Function,
111) -> CodegenResult<()> {
112 let snapshot_local = ctx.alloc_local(ValType::I32);
114 f.instruction(&Instruction::GlobalGet(GLOBAL_STATE_PTR));
115 f.instruction(&Instruction::LocalSet(snapshot_local));
116
117 f.instruction(&Instruction::I32Const(0));
119 f.instruction(&Instruction::GlobalSet(GLOBAL_GAS));
120
121 f.instruction(&Instruction::Block(BlockType::Empty)); for (i, action) in actions.iter().enumerate() {
126 f.instruction(&Instruction::LocalGet(0)); f.instruction(&Instruction::I32Const(i as i32));
128 f.instruction(&Instruction::I32Eq);
129 f.instruction(&Instruction::If(BlockType::Empty));
130
131 for (pi, param) in action.params.iter().enumerate() {
135 let param_local = ctx.alloc_local(ValType::I32);
136 f.instruction(&Instruction::LocalGet(1)); f.instruction(&Instruction::I32Const(pi as i32));
138 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST_GET)));
139 f.instruction(&Instruction::LocalSet(param_local));
140 ctx.push_local(¶m.name.name, param_local);
141 }
142
143 emit_stmts(&action.body.stmts, ctx, f)?;
145
146 for param in action.params.iter().rev() {
148 ctx.pop_local(¶m.name.name);
149 }
150
151 f.instruction(&Instruction::Br(0)); f.instruction(&Instruction::End); }
154
155 f.instruction(&Instruction::End); if let Some(derived_block) = derived {
159 emit_recompute_derived(derived_block, ctx, f)?;
160 }
161
162 for inv in invariants {
164 emit_expr(&inv.condition, ctx, f)?;
165 f.instruction(&Instruction::I32Load(memarg(4, 2)));
166 f.instruction(&Instruction::I32Eqz);
167 f.instruction(&Instruction::If(BlockType::Empty));
168 f.instruction(&Instruction::LocalGet(snapshot_local));
170 f.instruction(&Instruction::GlobalSet(GLOBAL_STATE_PTR));
171 let (ptr, len) = ctx.intern_string(&format!("invariant violated: {}", inv.name.name));
173 f.instruction(&Instruction::I32Const(ptr as i32));
174 f.instruction(&Instruction::I32Const(len as i32));
175 f.instruction(&Instruction::Call(IMPORT_TRAP));
176 f.instruction(&Instruction::Unreachable);
177 f.instruction(&Instruction::End);
178 }
179
180 f.instruction(&Instruction::End);
182 Ok(())
183}
184
185pub fn emit_render(
194 views: &[ViewDecl],
195 ctx: &mut FuncContext,
196 f: &mut Function,
197) -> CodegenResult<()> {
198 let result_local = ctx.alloc_local(ValType::I32);
199 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_NIL)));
200 f.instruction(&Instruction::LocalSet(result_local));
201
202 f.instruction(&Instruction::Block(BlockType::Empty));
203
204 for (i, view) in views.iter().enumerate() {
205 f.instruction(&Instruction::LocalGet(0)); f.instruction(&Instruction::I32Const(i as i32));
207 f.instruction(&Instruction::I32Eq);
208 f.instruction(&Instruction::If(BlockType::Empty));
209
210 for param in view.params.iter() {
212 let param_local = ctx.alloc_local(ValType::I32);
213 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_NIL)));
214 f.instruction(&Instruction::LocalSet(param_local));
215 ctx.push_local(¶m.name.name, param_local);
216 }
217
218 emit_ui_block(&view.body, ctx, f)?;
220 f.instruction(&Instruction::LocalSet(result_local));
221
222 for param in view.params.iter().rev() {
224 ctx.pop_local(¶m.name.name);
225 }
226
227 f.instruction(&Instruction::Br(0));
228 f.instruction(&Instruction::End);
229 }
230
231 f.instruction(&Instruction::End);
232
233 f.instruction(&Instruction::LocalGet(result_local));
234 f.instruction(&Instruction::End);
235 Ok(())
236}
237
238fn emit_ui_block(block: &UIBlock, ctx: &mut FuncContext, f: &mut Function) -> CodegenResult<()> {
242 let count = block.elements.len();
244 if count == 0 {
245 f.instruction(&Instruction::I32Const(0));
246 f.instruction(&Instruction::I32Const(0));
247 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST)));
248 return Ok(());
249 }
250
251 let arr_local = ctx.alloc_local(ValType::I32);
252 f.instruction(&Instruction::I32Const((count * 4) as i32));
253 f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
254 f.instruction(&Instruction::LocalSet(arr_local));
255
256 for (i, elem) in block.elements.iter().enumerate() {
257 let node_local = ctx.alloc_local(ValType::I32);
258 emit_ui_element(elem, ctx, f)?;
259 f.instruction(&Instruction::LocalSet(node_local));
260 f.instruction(&Instruction::LocalGet(arr_local));
261 f.instruction(&Instruction::LocalGet(node_local));
262 f.instruction(&Instruction::I32Store(memarg(i as u64 * 4, 2)));
263 }
264
265 f.instruction(&Instruction::LocalGet(arr_local));
266 f.instruction(&Instruction::I32Const(count as i32));
267 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST)));
268 Ok(())
269}
270
271fn emit_ui_element(elem: &UIElement, ctx: &mut FuncContext, f: &mut Function) -> CodegenResult<()> {
273 match elem {
274 UIElement::Component(comp) => emit_component_expr(comp, ctx, f),
275 UIElement::Let(let_bind) => {
276 crate::stmt::emit_stmt(&Stmt::Let(let_bind.clone()), ctx, f)?;
277 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_NIL)));
278 Ok(())
279 }
280 UIElement::If(ui_if) => emit_ui_if(ui_if, ctx, f),
281 UIElement::For(ui_for) => emit_ui_for(ui_for, ctx, f),
282 }
283}
284
285fn emit_component_expr(
289 comp: &ComponentExpr,
290 ctx: &mut FuncContext,
291 f: &mut Function,
292) -> CodegenResult<()> {
293 let entries_local = ctx.alloc_local(ValType::I32);
295 f.instruction(&Instruction::I32Const(3 * 12)); f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
297 f.instruction(&Instruction::LocalSet(entries_local));
298
299 let (ck_ptr, ck_len) = ctx.intern_string("component");
301 let (cn_ptr, cn_len) = ctx.intern_string(&comp.name.name);
302 let comp_val = ctx.alloc_local(ValType::I32);
303 f.instruction(&Instruction::I32Const(cn_ptr as i32));
304 f.instruction(&Instruction::I32Const(cn_len as i32));
305 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_STRING)));
306 f.instruction(&Instruction::LocalSet(comp_val));
307
308 f.instruction(&Instruction::LocalGet(entries_local));
309 f.instruction(&Instruction::I32Const(ck_ptr as i32));
310 f.instruction(&Instruction::I32Store(memarg(0, 2)));
311 f.instruction(&Instruction::LocalGet(entries_local));
312 f.instruction(&Instruction::I32Const(ck_len as i32));
313 f.instruction(&Instruction::I32Store(memarg(4, 2)));
314 f.instruction(&Instruction::LocalGet(entries_local));
315 f.instruction(&Instruction::LocalGet(comp_val));
316 f.instruction(&Instruction::I32Store(memarg(8, 2)));
317
318 let (pk_ptr, pk_len) = ctx.intern_string("props");
320 let props_val = ctx.alloc_local(ValType::I32);
321 emit_props(&comp.props, ctx, f)?;
322 f.instruction(&Instruction::LocalSet(props_val));
323
324 f.instruction(&Instruction::LocalGet(entries_local));
325 f.instruction(&Instruction::I32Const(pk_ptr as i32));
326 f.instruction(&Instruction::I32Store(memarg(12, 2)));
327 f.instruction(&Instruction::LocalGet(entries_local));
328 f.instruction(&Instruction::I32Const(pk_len as i32));
329 f.instruction(&Instruction::I32Store(memarg(16, 2)));
330 f.instruction(&Instruction::LocalGet(entries_local));
331 f.instruction(&Instruction::LocalGet(props_val));
332 f.instruction(&Instruction::I32Store(memarg(20, 2)));
333
334 let (chk_ptr, chk_len) = ctx.intern_string("children");
336 let children_val = ctx.alloc_local(ValType::I32);
337 if let Some(children_block) = &comp.children {
338 emit_ui_block(children_block, ctx, f)?;
339 } else {
340 f.instruction(&Instruction::I32Const(0));
341 f.instruction(&Instruction::I32Const(0));
342 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST)));
343 }
344 f.instruction(&Instruction::LocalSet(children_val));
345
346 f.instruction(&Instruction::LocalGet(entries_local));
347 f.instruction(&Instruction::I32Const(chk_ptr as i32));
348 f.instruction(&Instruction::I32Store(memarg(24, 2)));
349 f.instruction(&Instruction::LocalGet(entries_local));
350 f.instruction(&Instruction::I32Const(chk_len as i32));
351 f.instruction(&Instruction::I32Store(memarg(28, 2)));
352 f.instruction(&Instruction::LocalGet(entries_local));
353 f.instruction(&Instruction::LocalGet(children_val));
354 f.instruction(&Instruction::I32Store(memarg(32, 2)));
355
356 f.instruction(&Instruction::LocalGet(entries_local));
358 f.instruction(&Instruction::I32Const(3));
359 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_RECORD)));
360 Ok(())
361}
362
363fn emit_props(props: &[PropAssign], ctx: &mut FuncContext, f: &mut Function) -> CodegenResult<()> {
365 if props.is_empty() {
366 f.instruction(&Instruction::I32Const(0));
367 f.instruction(&Instruction::I32Const(0));
368 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_RECORD)));
369 return Ok(());
370 }
371
372 let entries_local = ctx.alloc_local(ValType::I32);
373 f.instruction(&Instruction::I32Const((props.len() * 12) as i32));
374 f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
375 f.instruction(&Instruction::LocalSet(entries_local));
376
377 for (i, prop) in props.iter().enumerate() {
378 let (key_ptr, key_len) = ctx.intern_string(&prop.name.name);
379 let val_local = ctx.alloc_local(ValType::I32);
380 emit_expr(&prop.value, ctx, f)?;
381 f.instruction(&Instruction::LocalSet(val_local));
382
383 let base = (i * 12) as u64;
384 f.instruction(&Instruction::LocalGet(entries_local));
385 f.instruction(&Instruction::I32Const(key_ptr as i32));
386 f.instruction(&Instruction::I32Store(memarg(base, 2)));
387 f.instruction(&Instruction::LocalGet(entries_local));
388 f.instruction(&Instruction::I32Const(key_len as i32));
389 f.instruction(&Instruction::I32Store(memarg(base + 4, 2)));
390 f.instruction(&Instruction::LocalGet(entries_local));
391 f.instruction(&Instruction::LocalGet(val_local));
392 f.instruction(&Instruction::I32Store(memarg(base + 8, 2)));
393 }
394
395 f.instruction(&Instruction::LocalGet(entries_local));
396 f.instruction(&Instruction::I32Const(props.len() as i32));
397 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_RECORD)));
398 Ok(())
399}
400
401fn emit_ui_if(ui_if: &UIIf, ctx: &mut FuncContext, f: &mut Function) -> CodegenResult<()> {
402 emit_expr(&ui_if.condition, ctx, f)?;
403 f.instruction(&Instruction::I32Load(memarg(4, 2)));
404 f.instruction(&Instruction::If(BlockType::Result(ValType::I32)));
405 emit_ui_block(&ui_if.then_block, ctx, f)?;
406 f.instruction(&Instruction::Else);
407 match &ui_if.else_block {
408 Some(UIElse::Block(block)) => emit_ui_block(block, ctx, f)?,
409 Some(UIElse::ElseIf(elif)) => emit_ui_if(elif, ctx, f)?,
410 None => {
411 f.instruction(&Instruction::I32Const(0));
412 f.instruction(&Instruction::I32Const(0));
413 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST)));
414 }
415 }
416 f.instruction(&Instruction::End);
417 Ok(())
418}
419
420fn emit_ui_for(ui_for: &UIFor, ctx: &mut FuncContext, f: &mut Function) -> CodegenResult<()> {
421 let list_local = ctx.alloc_local(ValType::I32);
423 let arr_local = ctx.alloc_local(ValType::I32);
424 let count_local = ctx.alloc_local(ValType::I32);
425 let i_local = ctx.alloc_local(ValType::I32);
426 let result_arr = ctx.alloc_local(ValType::I32);
427
428 emit_expr(&ui_for.iterable, ctx, f)?;
429 f.instruction(&Instruction::LocalSet(list_local));
430
431 f.instruction(&Instruction::LocalGet(list_local));
432 f.instruction(&Instruction::I32Load(memarg(4, 2)));
433 f.instruction(&Instruction::LocalSet(arr_local));
434
435 f.instruction(&Instruction::LocalGet(list_local));
436 f.instruction(&Instruction::I32Load(memarg(8, 2)));
437 f.instruction(&Instruction::LocalSet(count_local));
438
439 f.instruction(&Instruction::LocalGet(count_local));
441 f.instruction(&Instruction::I32Const(4));
442 f.instruction(&Instruction::I32Mul);
443 f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
444 f.instruction(&Instruction::LocalSet(result_arr));
445
446 f.instruction(&Instruction::I32Const(0));
447 f.instruction(&Instruction::LocalSet(i_local));
448
449 let item_local = ctx.alloc_local(ValType::I32);
450 let index_local = if ui_for.index.is_some() {
451 Some(ctx.alloc_local(ValType::I32))
452 } else {
453 None
454 };
455
456 ctx.push_local(&ui_for.item.name, item_local);
457 if let (Some(idx_ident), Some(idx_local)) = (&ui_for.index, index_local) {
458 ctx.push_local(&idx_ident.name, idx_local);
459 }
460
461 f.instruction(&Instruction::Block(BlockType::Empty));
462 f.instruction(&Instruction::Loop(BlockType::Empty));
463
464 f.instruction(&Instruction::LocalGet(i_local));
465 f.instruction(&Instruction::LocalGet(count_local));
466 f.instruction(&Instruction::I32GeU);
467 f.instruction(&Instruction::BrIf(1));
468
469 f.instruction(&Instruction::LocalGet(arr_local));
471 f.instruction(&Instruction::LocalGet(i_local));
472 f.instruction(&Instruction::I32Const(4));
473 f.instruction(&Instruction::I32Mul);
474 f.instruction(&Instruction::I32Add);
475 f.instruction(&Instruction::I32Load(memarg(0, 2)));
476 f.instruction(&Instruction::LocalSet(item_local));
477
478 if let Some(idx_local) = index_local {
479 f.instruction(&Instruction::I32Const(VALUE_SIZE as i32));
480 f.instruction(&Instruction::Call(rt_func_idx(RT_ALLOC)));
481 f.instruction(&Instruction::LocalTee(idx_local));
482 f.instruction(&Instruction::I32Const(TAG_NUMBER));
483 f.instruction(&Instruction::I32Store(memarg(0, 2)));
484 f.instruction(&Instruction::LocalGet(idx_local));
485 f.instruction(&Instruction::LocalGet(i_local));
486 f.instruction(&Instruction::F64ConvertI32U);
487 f.instruction(&Instruction::F64Store(memarg(4, 3)));
488 }
489
490 let body_result = ctx.alloc_local(ValType::I32);
492 emit_ui_block(&ui_for.body, ctx, f)?;
493 f.instruction(&Instruction::LocalSet(body_result));
494
495 f.instruction(&Instruction::LocalGet(result_arr));
497 f.instruction(&Instruction::LocalGet(i_local));
498 f.instruction(&Instruction::I32Const(4));
499 f.instruction(&Instruction::I32Mul);
500 f.instruction(&Instruction::I32Add);
501 f.instruction(&Instruction::LocalGet(body_result));
502 f.instruction(&Instruction::I32Store(memarg(0, 2)));
503
504 f.instruction(&Instruction::LocalGet(i_local));
506 f.instruction(&Instruction::I32Const(1));
507 f.instruction(&Instruction::I32Add);
508 f.instruction(&Instruction::LocalSet(i_local));
509 f.instruction(&Instruction::Br(0));
510
511 f.instruction(&Instruction::End);
512 f.instruction(&Instruction::End);
513
514 ctx.pop_local(&ui_for.item.name);
515 if let Some(idx_ident) = &ui_for.index {
516 ctx.pop_local(&idx_ident.name);
517 }
518
519 f.instruction(&Instruction::LocalGet(result_arr));
520 f.instruction(&Instruction::LocalGet(count_local));
521 f.instruction(&Instruction::Call(rt_func_idx(RT_VAL_LIST)));
522 Ok(())
523}
524
525pub fn emit_get_state(f: &mut Function) {
531 f.instruction(&Instruction::GlobalGet(GLOBAL_STATE_PTR));
532 f.instruction(&Instruction::End);
533}
534
535pub fn emit_update(
541 update_decl: &UpdateDecl,
542 derived: Option<&DerivedBlock>,
543 ctx: &mut FuncContext,
544 f: &mut Function,
545) -> CodegenResult<()> {
546 gas::emit_gas_tick(f, ctx.data.gas_exhausted_ptr, ctx.data.gas_exhausted_len);
547
548 let dt_local = ctx.alloc_local(ValType::I32);
550 f.instruction(&Instruction::LocalGet(0));
551 f.instruction(&Instruction::LocalSet(dt_local));
552 ctx.push_local(&update_decl.param.name.name, dt_local);
553
554 emit_stmts(&update_decl.body.stmts, ctx, f)?;
555
556 ctx.pop_local(&update_decl.param.name.name);
557
558 if let Some(derived_block) = derived {
560 emit_recompute_derived(derived_block, ctx, f)?;
561 }
562
563 f.instruction(&Instruction::End);
564 Ok(())
565}
566
567pub fn emit_handle_event(
569 handle_event_decl: &HandleEventDecl,
570 derived: Option<&DerivedBlock>,
571 ctx: &mut FuncContext,
572 f: &mut Function,
573) -> CodegenResult<()> {
574 gas::emit_gas_tick(f, ctx.data.gas_exhausted_ptr, ctx.data.gas_exhausted_len);
575
576 let event_local = ctx.alloc_local(ValType::I32);
577 f.instruction(&Instruction::LocalGet(0));
578 f.instruction(&Instruction::LocalSet(event_local));
579 ctx.push_local(&handle_event_decl.param.name.name, event_local);
580
581 emit_stmts(&handle_event_decl.body.stmts, ctx, f)?;
582
583 ctx.pop_local(&handle_event_decl.param.name.name);
584
585 if let Some(derived_block) = derived {
586 emit_recompute_derived(derived_block, ctx, f)?;
587 }
588
589 f.instruction(&Instruction::End);
590 Ok(())
591}
592
593fn emit_recompute_derived(
599 derived: &DerivedBlock,
600 ctx: &mut FuncContext,
601 f: &mut Function,
602) -> CodegenResult<()> {
603 for field in &derived.fields {
604 let val_local = ctx.alloc_local(ValType::I32);
605 emit_expr(&field.value, ctx, f)?;
606 f.instruction(&Instruction::LocalSet(val_local));
607
608 let field_name = field.name.name.clone();
610 crate::stmt::emit_state_field_set(&field_name, val_local, ctx, f)?;
611 }
612 Ok(())
613}
614
615fn memarg(offset: u64, align: u32) -> wasm_encoder::MemArg {
620 wasm_encoder::MemArg {
621 offset,
622 align,
623 memory_index: 0,
624 }
625}