@@ -359,8 +359,8 @@ let gen_print_str (heap_ptr_global : int) (str_ptr_local : int) (fd_write_idx :
359359 Signature: `(envc_out: i32, envbuf_size_out: i32) -> errno: i32`.
360360 Writes the env-var count and the total byte size of the
361361 null-terminated `KEY=VAL\0…` buffer the next call would need.
362- String accessor (`env_at`) is gated on byte-level wasm IR ops,
363- deferred to a follow-up slice . *)
362+ Paired with `environ_get` (created by
363+ {!create_environ_get_import}) for the `env_at` string accessor . *)
364364let create_environ_sizes_get_import () : import * func_type =
365365 let func_type = {
366366 ft_params = [I32 ; I32 ]; (* envc_out_ptr, envbuf_size_out_ptr *)
@@ -413,3 +413,159 @@ let gen_count_via_sizes_get
413413 LocalGet scratch_local;
414414 I32Load (2 , 0 );
415415 ]
416+
417+ (* * Create the WASI `environ_get` import (ADR-015 S5, #180).
418+ Signature: `(environ_ptr_ptr: i32, environ_buf_ptr: i32) -> errno: i32`.
419+ Fills two regions: a vector of pointers (one per env-var, written
420+ at `environ_ptr_ptr`) and a contiguous buffer of null-terminated
421+ `KEY=VAL` strings (written at `environ_buf_ptr`). The sizes that
422+ must be allocated are reported by `environ_sizes_get`. *)
423+ let create_environ_get_import () : import * func_type =
424+ let func_type = {
425+ ft_params = [I32 ; I32 ]; (* environ_ptr_ptr, environ_buf_ptr *)
426+ ft_results = [I32 ]; (* errno *)
427+ } in
428+ let import = {
429+ i_module = " wasi_snapshot_preview1" ;
430+ i_name = " environ_get" ;
431+ i_desc = ImportFunc 0 ;
432+ } in
433+ (import, func_type)
434+
435+ (* * Create the WASI `args_get` import (ADR-015 S5, #180).
436+ Signature: `(argv_ptr_ptr: i32, argv_buf_ptr: i32) -> errno: i32`.
437+ Same shape as `environ_get`. *)
438+ let create_args_get_import () : import * func_type =
439+ let func_type = {
440+ ft_params = [I32 ; I32 ];
441+ ft_results = [I32 ];
442+ } in
443+ let import = {
444+ i_module = " wasi_snapshot_preview1" ;
445+ i_name = " args_get" ;
446+ i_desc = ImportFunc 0 ;
447+ } in
448+ (import, func_type)
449+
450+ (* * Emit `env_at(i)` / `arg_at(i)`: fetch the i-th entry from the WASI
451+ environ/argv vector and return it as a length-prefixed AffineScript
452+ string. Sequence:
453+ 1. `*_sizes_get(&count, &bufsize)`
454+ 2. allocate `count*4` bytes for the pointer vector + `bufsize`
455+ bytes for the string buffer
456+ 3. `*_get(ptrvec, ptrvec + count*4)`
457+ 4. resolve `src = ptrvec[i]`
458+ 5. scan `src` for the null terminator to compute length
459+ 6. allocate `(4 + length)` bytes for the result string,
460+ store length at +0, byte-copy `src..src+length` to `result+4`
461+ 7. leave the result pointer on the stack
462+
463+ The byte loops use `I32Load8U`/`I32Store8` (added with the
464+ byte-level wasm IR extension). The caller has placed the index `i`
465+ on the stack; this helper consumes it via [LocalSet n_local].
466+
467+ All locals must be pre-allocated by the caller (8 in total). The
468+ helper itself does not modify the type or scope context — it only
469+ emits instructions. *)
470+ let gen_str_at_via_get
471+ (heap_ptr_global : int )
472+ (n_local : int )
473+ (scratch_local : int )
474+ (count_local : int )
475+ (bufsize_local : int )
476+ (ptrvec_local : int )
477+ (src_local : int )
478+ (dst_local : int )
479+ (result_local : int )
480+ (sizes_func_idx : int )
481+ (get_func_idx : int )
482+ : instr list =
483+ [
484+ (* Index `i` is on the stack from the caller's arg_code. *)
485+ LocalSet n_local;
486+
487+ (* --- Phase 1: sizes_get -> count, bufsize --- *)
488+ GlobalGet heap_ptr_global;
489+ I32Const 8l ; I32Add ;
490+ GlobalSet heap_ptr_global;
491+ GlobalGet heap_ptr_global;
492+ I32Const 8l ; I32Sub ;
493+ LocalSet scratch_local;
494+ LocalGet scratch_local; (* count_ptr *)
495+ LocalGet scratch_local; I32Const 4l ; I32Add ; (* bufsize_ptr *)
496+ Call sizes_func_idx;
497+ Drop ;
498+ LocalGet scratch_local; I32Load (2 , 0 ); LocalSet count_local;
499+ LocalGet scratch_local; I32Load (2 , 4 ); LocalSet bufsize_local;
500+
501+ (* --- Phase 2: allocate ptrvec (count*4) + bytebuf (bufsize) --- *)
502+ GlobalGet heap_ptr_global;
503+ LocalSet ptrvec_local;
504+ GlobalGet heap_ptr_global;
505+ LocalGet count_local; I32Const 4l ; I32Mul ;
506+ LocalGet bufsize_local; I32Add ;
507+ I32Add ;
508+ GlobalSet heap_ptr_global;
509+
510+ (* --- Phase 3: get(ptrvec, ptrvec + count*4) --- *)
511+ LocalGet ptrvec_local;
512+ LocalGet ptrvec_local; LocalGet count_local; I32Const 4l ; I32Mul ; I32Add ;
513+ Call get_func_idx;
514+ Drop ;
515+
516+ (* --- Phase 4: src = *(ptrvec + i*4) --- *)
517+ LocalGet ptrvec_local;
518+ LocalGet n_local; I32Const 4l ; I32Mul ; I32Add ;
519+ I32Load (2 , 0 );
520+ LocalSet src_local;
521+
522+ (* --- Phase 5: scan for null terminator. Use scratch as cursor. --- *)
523+ LocalGet src_local; LocalSet scratch_local;
524+ Block (BtEmpty , [
525+ Loop (BtEmpty , [
526+ LocalGet scratch_local;
527+ I32Load8U (0 , 0 );
528+ I32Eqz ; BrIf 1 ; (* exit on 0 byte *)
529+ LocalGet scratch_local; I32Const 1l ; I32Add ;
530+ LocalSet scratch_local;
531+ Br 0
532+ ])
533+ ]);
534+ (* length = cursor - src (excludes the null terminator).
535+ Stash it back into count_local, which we are done with. *)
536+ LocalGet scratch_local; LocalGet src_local; I32Sub ;
537+ LocalSet count_local;
538+
539+ (* --- Phase 6: allocate (4 + length) for the AS string --- *)
540+ GlobalGet heap_ptr_global;
541+ LocalSet result_local;
542+ GlobalGet heap_ptr_global;
543+ I32Const 4l ; LocalGet count_local; I32Add ;
544+ I32Add ;
545+ GlobalSet heap_ptr_global;
546+
547+ (* Store length at result+0. *)
548+ LocalGet result_local;
549+ LocalGet count_local;
550+ I32Store (2 , 0 );
551+
552+ (* --- Phase 7: byte-copy src..src+length -> result+4 ---
553+ Reuses scratch as src cursor and count_local as the loop count. *)
554+ LocalGet src_local; LocalSet scratch_local;
555+ LocalGet result_local; I32Const 4l ; I32Add ; LocalSet dst_local;
556+ Block (BtEmpty , [
557+ Loop (BtEmpty , [
558+ LocalGet count_local; I32Eqz ; BrIf 1 ;
559+ LocalGet dst_local;
560+ LocalGet scratch_local; I32Load8U (0 , 0 );
561+ I32Store8 (0 , 0 );
562+ LocalGet scratch_local; I32Const 1l ; I32Add ; LocalSet scratch_local;
563+ LocalGet dst_local; I32Const 1l ; I32Add ; LocalSet dst_local;
564+ LocalGet count_local; I32Const 1l ; I32Sub ; LocalSet count_local;
565+ Br 0
566+ ])
567+ ]);
568+
569+ (* --- Result: leave the string pointer on the stack. --- *)
570+ LocalGet result_local;
571+ ]
0 commit comments