From 956de8cbe41626338b010c4a3b986d7d02564b43 Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Sat, 11 Nov 2023 08:24:41 -0500 Subject: [PATCH] experiment: emulate stable memory in WASIMode and WasmMode (to support testing of stable data structures on non-IC targets) (#4256) This PR exploits wasmtime's support for multiple memory and bulk memory instructions to emulate the IC stable memory API (i.e. ExperimentalStableMemory.mo and Regions.mo). The basic idea is to introduce an extra Wasm memory and emulate the low-level `ic0.stable64_grow/size/read/write` operations, using bulk MemoryCopy instructions for data transfer. The goal is to allow users to easily unit-test stable data structures without having to resort to the full IC environment. For example, the third-party StableBTree implementation went to a lot of trouble and was less efficient as a result, just to support testing outside the IC. @rvanasa would this work with your VSCode unit testing support? It does require enabling some additional features on wasmtime (no idea about compatibility with wasmer, if we still use that). The current implementation only adds a 32-bit memory, but it should be possible to add a 64-bit one instead. I just want to get this working first. 32-bit might be all we can support in browsers anyway (not sure). - [x] enable wasmtime flags in base repo so tests compile (this is one reason CI is failing). Alternatively, only add the stable memory if it's used. - [ ] PR to mops to enable additional wasmtime flags --- Changelog.md | 8 + src/codegen/compile.ml | 330 +++++++++++++----- src/exes/candid_tests.ml | 3 +- src/lang_utils/error_codes.ml | 1 + src/linking/linkModule.ml | 10 +- src/pipeline/pipeline.ml | 5 + src/wasm-exts/ast.ml | 14 +- src/wasm-exts/customModule.ml | 1 + src/wasm-exts/customModuleDecode.ml | 11 +- src/wasm-exts/customModuleEncode.ml | 14 + test/check-error-codes.py | 1 + test/random/Embedder.hs | 2 +- test/run-drun/ok/region0-big-blob.drun-run.ok | 4 - test/run-drun/ok/region0-big-blob.tc.ok | 4 - .../ok/stable-mem-big-blob.drun-run.ok | 4 - test/run-drun/ok/stable-mem-big-blob.tc.ok | 4 - test/run-drun/region0-big-blob.mo | 25 -- test/run-drun/stable-mem-big-blob.mo | 26 -- test/run-drun/stable-mem/StableMemory.mo | 2 +- test/run.sh | 8 +- test/run/ok/region-test.tc.ok | 12 + test/run/ok/region-test.wasm-run.ok | 10 + test/run/ok/region0-big-blob.tc.ok | 4 + test/run/ok/region0-big-blob.wasm-run.ok | 2 + test/run/ok/stable-log.wasm-run.ok | 50 +++ test/run/ok/stable-mem-big-blob.tc.ok | 4 + test/run/ok/stable-mem-big-blob.wasm-run.ok | 2 + test/run/ok/stable-memory-test.tc.ok | 8 + test/run/ok/stable-memory-test.wasm-run.ok | 10 + test/run/ok/stable-memory.tc.ok | 32 ++ test/run/ok/stable-memory.wasm-run.ok | 9 + test/run/region-test.mo | 214 ++++++++++++ test/run/region0-big-blob.mo | 22 ++ test/run/stable-log.mo | 77 ++++ test/run/stable-mem-big-blob.mo | 23 ++ test/run/stable-mem/StableMemory.mo | 38 ++ test/run/stable-memory-test.mo | 212 +++++++++++ test/run/stable-memory.mo | 30 ++ test/run/stable-region/Region.mo | 41 +++ 39 files changed, 1102 insertions(+), 175 deletions(-) delete mode 100644 test/run-drun/ok/region0-big-blob.drun-run.ok delete mode 100644 test/run-drun/ok/region0-big-blob.tc.ok delete mode 100644 test/run-drun/ok/stable-mem-big-blob.drun-run.ok delete mode 100644 test/run-drun/ok/stable-mem-big-blob.tc.ok delete mode 100644 test/run-drun/region0-big-blob.mo delete mode 100644 test/run-drun/stable-mem-big-blob.mo create mode 100644 test/run/ok/region-test.tc.ok create mode 100644 test/run/ok/region-test.wasm-run.ok create mode 100644 test/run/ok/region0-big-blob.tc.ok create mode 100644 test/run/ok/region0-big-blob.wasm-run.ok create mode 100644 test/run/ok/stable-log.wasm-run.ok create mode 100644 test/run/ok/stable-mem-big-blob.tc.ok create mode 100644 test/run/ok/stable-mem-big-blob.wasm-run.ok create mode 100644 test/run/ok/stable-memory-test.tc.ok create mode 100644 test/run/ok/stable-memory-test.wasm-run.ok create mode 100644 test/run/ok/stable-memory.tc.ok create mode 100644 test/run/ok/stable-memory.wasm-run.ok create mode 100644 test/run/region-test.mo create mode 100644 test/run/region0-big-blob.mo create mode 100644 test/run/stable-log.mo create mode 100644 test/run/stable-mem-big-blob.mo create mode 100644 test/run/stable-mem/StableMemory.mo create mode 100644 test/run/stable-memory-test.mo create mode 100644 test/run/stable-memory.mo create mode 100644 test/run/stable-region/Region.mo diff --git a/Changelog.md b/Changelog.md index b94517c8547..402afe505f7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,14 @@ * motoko (`moc`) + * Code compiled for targets WASI (`-wasi-system-api`) and pure Wasm (`-no-system-api`) can now + use up to 4GB of (efficiently emulated) stable memory, enabling more offline testing of, for example, + stable data structures built using libraries `Regions.mo` and `ExperimentalStableMemory.mo`. + Note that any Wasm engine (such as `wasmtime`), used to execute such binaries, must support and enable + Wasm features multi-memory and bulk-memory (as well as the standard NaN canonicalization) (#4256). + + * bugfix: fully implement `Region.loadXXX/storeXXX` for `Int8`, `Int16` and `Float` (#4270). + * BREAKING CHANGE (Minor): values of type `Principal` are now constrained to contain at most 29 bytes, matching the IC's notion of principal (#4268). diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 7d94bb1e5c4..74485f790e7 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -261,6 +261,7 @@ module E = struct module NameEnv = Env.Make(String) module StringEnv = Env.Make(String) module LabSet = Set.Make(String) + module FeatureSet = Set.Make(String) module FunEnv = Env.Make(Int32) type local_names = (int32 * string) list (* For the debug section: Names of locals *) @@ -324,6 +325,11 @@ module E = struct (* Mutable *) locals : value_type list ref; (* Types of locals *) local_names : (int32 * string) list ref; (* Names of locals *) + + features : FeatureSet.t ref; (* Wasm features using wasmtime naming *) + + (* requires stable memory (and emulation on wasm targets) *) + requires_stable_memory : bool ref; } @@ -360,6 +366,8 @@ module E = struct return_arity = 0; locals = ref []; local_names = ref []; + features = ref FeatureSet.empty; + requires_stable_memory = ref false; } (* This wraps Mo_types.Hash.hash to also record which labels we have seen, @@ -634,6 +642,32 @@ module E = struct let get_typtbl_typs (env : t) : Type.typ list = !(env.typtbl_typs) + let add_feature (env : t) f = + env.features := FeatureSet.add f (!(env.features)) + + let get_features (env : t) = FeatureSet.elements (!(env.features)) + + let require_stable_memory (env : t) = + if not !(env.requires_stable_memory) + then + (env.requires_stable_memory := true; + match mode env with + | Flags.ICMode | Flags.RefMode -> + () + | Flags.WASIMode | Flags.WasmMode -> + add_feature env "bulk-memory"; + add_feature env "multi-memory") + + let requires_stable_memory (env : t) = + !(env.requires_stable_memory) + + let get_memories (env : t) = + nr {mtype = MemoryType {min = mem_size env; max = None}} + :: + match mode env with + | Flags.WASIMode | Flags.WasmMode when !(env.requires_stable_memory) -> + [ nr {mtype = MemoryType {min = Int32.zero; max = None}} ] + | _ -> [] end @@ -3957,31 +3991,58 @@ module Region = struct E.call_import env "rts" "region_vec_pages" let new_ env = + E.require_stable_memory env; E.call_import env "rts" "region_new" let size env = + E.require_stable_memory env; E.call_import env "rts" "region_size" let grow env = + E.require_stable_memory env; E.call_import env "rts" "region_grow" - let load_blob env = E.call_import env "rts" "region_load_blob" - let store_blob env = E.call_import env "rts" "region_store_blob" + let load_blob env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_blob" + let store_blob env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_blob" - let load_word8 env = E.call_import env "rts" "region_load_word8" - let store_word8 env = E.call_import env "rts" "region_store_word8" + let load_word8 env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_word8" + let store_word8 env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_word8" - let load_word16 env = E.call_import env "rts" "region_load_word16" - let store_word16 env = E.call_import env "rts" "region_store_word16" + let load_word16 env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_word16" + let store_word16 env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_word16" - let load_word32 env = E.call_import env "rts" "region_load_word32" - let store_word32 env = E.call_import env "rts" "region_store_word32" + let load_word32 env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_word32" + let store_word32 env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_word32" - let load_word64 env = E.call_import env "rts" "region_load_word64" - let store_word64 env = E.call_import env "rts" "region_store_word64" + let load_word64 env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_word64" + let store_word64 env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_word64" - let load_float64 env = E.call_import env "rts" "region_load_float64" - let store_float64 env = E.call_import env "rts" "region_store_float64" + let load_float64 env = + E.require_stable_memory env; + E.call_import env "rts" "region_load_float64" + let store_float64 env = + E.require_stable_memory env; + E.call_import env "rts" "region_store_float64" end @@ -5059,6 +5120,83 @@ end (* Cycles *) *) module StableMem = struct + + let conv_u32 env get_u64 = + get_u64 ^^ + compile_shrU64_const 32L ^^ + G.i (Convert (Wasm.Values.I32 I32Op.WrapI64)) ^^ + E.then_trap_with env "stable64 overflow" ^^ + get_u64 ^^ + G.i (Convert (Wasm.Values.I32 I32Op.WrapI64)) + + (* Raw stable memory API, + using ic0.stable64_xxx or + emulating via (for now) 32-bit memory 1 + *) + let stable64_grow env = + E.require_stable_memory env; + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + IC.system_call env "stable64_grow" + | _ -> + Func.share_code1 Func.Always env "stable64_grow" ("pages", I64Type) [I64Type] + (fun env get_pages -> + let set_old_pages, get_old_pages = new_local env "old_pages" in + conv_u32 env get_pages ^^ + G.i StableGrow ^^ + set_old_pages ^^ + get_old_pages ^^ + compile_unboxed_const (-1l) ^^ + G.i (Compare (Wasm.Values.I32 I32Op.Eq)) ^^ + G.if1 I64Type + begin + compile_const_64 (-1L) + end + begin + get_old_pages ^^ + G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) + end) + + let stable64_size env = + E.require_stable_memory env; + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + IC.system_call env "stable64_size" + | _ -> + Func.share_code0 Func.Always env "stable64_size" [I64Type] + (fun env -> + G.i StableSize ^^ + G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32))) + + let stable64_read env = + E.require_stable_memory env; + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + IC.system_call env "stable64_read" + | _ -> + Func.share_code3 Func.Always env "stable64_read" + (("dst", I64Type), ("offset", I64Type), ("size", I64Type)) [] + (fun env get_dst get_offset get_size -> + conv_u32 env get_dst ^^ + conv_u32 env get_offset ^^ + conv_u32 env get_size ^^ + G.i StableRead) + + let stable64_write env = + E.require_stable_memory env; + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + IC.system_call env "stable64_write" + | _ -> + Func.share_code3 Func.Always env "stable64_write" + (("offset", I64Type), ("src", I64Type), ("size", I64Type)) [] + (fun env get_offset get_src get_size -> + conv_u32 env get_offset ^^ + conv_u32 env get_src ^^ + conv_u32 env get_size ^^ + G.i StableWrite) + + (* Versioning (c.f. Region.rs) *) (* NB: these constants must agree with VERSION_NO_STABLE_MEMORY etc. in Region.rs *) let version_no_stable_memory = Int32.of_int 0 (* never manifest in serialized form *) @@ -5085,21 +5223,16 @@ module StableMem = struct (* stable memory bounds check *) let guard env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> get_mem_size env ^^ compile_const_64 (Int64.of_int page_size_bits) ^^ G.i (Binary (Wasm.Values.I64 I64Op.Shl)) ^^ G.i (Compare (Wasm.Values.I64 I64Op.GeU)) ^^ E.then_trap_with env "StableMemory offset out of bounds" - | _ -> assert false (* check both offset and [offset,.., offset + size) within bounds *) (* c.f. region.rs check_relative_range *) (* TODO: specialize on size *) let guard_range env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code2 Func.Always env "__stablemem_guard_range" (("offset", I64Type), ("size", I32Type)) [] (fun env get_offset get_size -> @@ -5126,7 +5259,6 @@ module StableMem = struct G.i (Compare (Wasm.Values.I64 I64Op.GtU)) ^^ E.then_trap_with env "StableMemory range out of bounds" end) - | _ -> assert false let add_guard env guarded get_offset bytes = if guarded then @@ -5140,8 +5272,6 @@ module StableMem = struct (* TODO: crusso in read/write could avoid stack allocation by reserving and re-using scratch memory instead *) let read env guarded name typ bytes load = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code1 Func.Never env (Printf.sprintf "__stablemem_%sread_%s" (if guarded then "guarded_" else "") name) ("offset", I64Type) [typ] (fun env get_offset -> @@ -5151,13 +5281,10 @@ module StableMem = struct get_temp_ptr ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ get_offset ^^ compile_const_64 (Int64.of_int32 bytes) ^^ - IC.system_call env "stable64_read" ^^ + stable64_read env ^^ get_temp_ptr ^^ load)) - | _ -> assert false let write env guarded name typ bytes store = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code2 Func.Never env (Printf.sprintf "__stablemem_%swrite_%s" (if guarded then "guarded_" else "") name) (("offset", I64Type), ("value", typ)) [] (fun env get_offset get_value -> @@ -5168,8 +5295,7 @@ module StableMem = struct get_offset ^^ get_temp_ptr ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ compile_const_64 (Int64.of_int32 bytes) ^^ - IC.system_call env "stable64_write")) - | _ -> assert false + stable64_write env)) let _read_word32 env = read env false "word32" I32Type 4l load_unskewed_ptr @@ -5179,8 +5305,6 @@ module StableMem = struct (* read and clear word32 from stable mem offset on stack *) let read_and_clear_word32 env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code1 Func.Always env "__stablemem_read_and_clear_word32" ("offset", I64Type) [I32Type] (fun env get_offset -> @@ -5190,7 +5314,7 @@ module StableMem = struct get_temp_ptr ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ get_offset ^^ compile_const_64 4L ^^ - IC.system_call env "stable64_read" ^^ + stable64_read env ^^ get_temp_ptr ^^ load_unskewed_ptr ^^ set_word ^^ (* write 0 *) @@ -5198,24 +5322,21 @@ module StableMem = struct get_offset ^^ get_temp_ptr ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ compile_const_64 4L ^^ - IC.system_call env "stable64_write" ^^ + stable64_write env ^^ (* return word *) get_word )) - | _ -> assert false (* ensure_pages : ensure at least num pages allocated, growing (real) stable memory if needed *) let ensure_pages env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code1 Func.Always env "__stablemem_ensure_pages" ("pages", I64Type) [I64Type] (fun env get_pages -> let (set_size, get_size) = new_local64 env "size" in let (set_pages_needed, get_pages_needed) = new_local64 env "pages_needed" in - IC.system_call env "stable64_size" ^^ + stable64_size env ^^ set_size ^^ get_pages ^^ @@ -5228,14 +5349,11 @@ module StableMem = struct G.i (Compare (Wasm.Values.I64 I64Op.GtS)) ^^ G.if1 I64Type (get_pages_needed ^^ - IC.system_call env "stable64_grow") + stable64_grow env) get_size) - | _ -> assert false (* ensure stable memory includes [offset..offset+size), assumes size > 0 *) let ensure env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code2 Func.Always env "__stablemem_ensure" (("offset", I64Type), ("size", I64Type)) [] (fun env get_offset get_size -> @@ -5259,12 +5377,9 @@ module StableMem = struct compile_const_64 0L ^^ G.i (Compare (Wasm.Values.I64 I64Op.LtS)) ^^ E.then_trap_with env "Out of stable memory.") - | _ -> assert false (* low-level grow, respecting --max-stable-pages *) let grow env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code1 Func.Always env "__stablemem_grow" ("pages", I64Type) [I64Type] (fun env get_pages -> let (set_size, get_size) = new_local64 env "size" in @@ -5308,7 +5423,6 @@ module StableMem = struct (* return old logical size *) get_size) end) - | _ -> assert false let load_word32 env = read env true "word32" I32Type 4l load_unskewed_ptr @@ -5342,8 +5456,6 @@ module StableMem = struct (G.i (Store {ty = F64Type; align = 0; offset = 0l; sz = None})) let load_blob env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code2 Func.Always env "__stablemem_load_blob" (("offset", I64Type), ("len", I32Type)) [I32Type] (fun env get_offset get_len -> @@ -5355,13 +5467,10 @@ module StableMem = struct get_blob ^^ Blob.payload_ptr_unskewed env ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ get_offset ^^ get_len ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ - IC.system_call env "stable64_read" ^^ + stable64_read env ^^ get_blob) - | _ -> assert false let store_blob env = - match E.mode env with - | Flags.ICMode | Flags.RefMode -> Func.share_code2 Func.Always env "__stablemem_store_blob" (("offset", I64Type), ("blob", I32Type)) [] (fun env get_offset get_blob -> @@ -5373,8 +5482,7 @@ module StableMem = struct get_offset ^^ get_blob ^^ Blob.payload_ptr_unskewed env ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ get_len ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ - IC.system_call env "stable64_write") - | _ -> assert false + stable64_write env) end (* StableMem *) @@ -5403,6 +5511,7 @@ module StableMemoryInterface = struct (* Prims *) let size env = + E.require_stable_memory env; Func.share_code0 Func.Always env "__stablememory_size" [I64Type] (fun env -> if_regions env @@ -5412,6 +5521,7 @@ module StableMemoryInterface = struct StableMem.get_mem_size) let grow env = + E.require_stable_memory env; Func.share_code1 Func.Always env "__stablememory_grow" ("pages", I64Type) [I64Type] (fun env get_pages -> if_regions env @@ -5442,6 +5552,7 @@ module StableMemoryInterface = struct get_res)) let load_blob env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_load_blob" (("offset", I64Type), ("len", I32Type)) [I32Type] (fun env offset len -> @@ -5451,6 +5562,7 @@ module StableMemoryInterface = struct Region.load_blob StableMem.load_blob) let store_blob env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_store_blob" (("offset", I64Type), ("blob", I32Type)) [] (fun env offset blob -> @@ -5461,6 +5573,7 @@ module StableMemoryInterface = struct StableMem.store_blob) let load_word8 env = + E.require_stable_memory env; Func.share_code1 Func.Never env "__stablememory_load_word8" ("offset", I64Type) [I32Type] (fun env offset -> @@ -5470,6 +5583,7 @@ module StableMemoryInterface = struct Region.load_word8 StableMem.load_word8) let store_word8 env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_store_word8" (("offset", I64Type), ("value", I32Type)) [] (fun env offset value -> @@ -5480,6 +5594,7 @@ module StableMemoryInterface = struct StableMem.store_word8) let load_word16 env = + E.require_stable_memory env; Func.share_code1 Func.Never env "__stablememory_load_word16" ("offset", I64Type) [I32Type] (fun env offset-> @@ -5489,6 +5604,7 @@ module StableMemoryInterface = struct Region.load_word16 StableMem.load_word16) let store_word16 env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_store_word16" (("offset", I64Type), ("value", I32Type)) [] (fun env offset value -> @@ -5499,6 +5615,7 @@ module StableMemoryInterface = struct StableMem.store_word16) let load_word32 env = + E.require_stable_memory env; Func.share_code1 Func.Never env "__stablememory_load_word32" ("offset", I64Type) [I32Type] (fun env offset -> @@ -5508,6 +5625,7 @@ module StableMemoryInterface = struct Region.load_word32 StableMem.load_word32) let store_word32 env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_store_word32" (("offset", I64Type), ("value", I32Type)) [] (fun env offset value -> @@ -5518,6 +5636,7 @@ module StableMemoryInterface = struct StableMem.store_word32) let load_word64 env = + E.require_stable_memory env; Func.share_code1 Func.Never env "__stablememory_load_word64" ("offset", I64Type) [I64Type] (fun env offset -> if_regions env @@ -5526,6 +5645,7 @@ module StableMemoryInterface = struct Region.load_word64 StableMem.load_word64) let store_word64 env = + E.require_stable_memory env; Func.share_code2 Func.Never env "__stablememory_store_word64" (("offset", I64Type), ("value", I64Type)) [] (fun env offset value -> @@ -5536,6 +5656,7 @@ module StableMemoryInterface = struct StableMem.store_word64) let load_float64 env = + E.require_stable_memory env; Func.share_code1 Func.Never env "__stablememory_load_float64" ("offset", I64Type) [F64Type] (fun env offset -> @@ -5557,6 +5678,9 @@ module StableMemoryInterface = struct end module RTS_Exports = struct + (* Must be called late, after main codegen, to ensure correct generation of + of functioning or unused-but-trapping stable memory exports (as required) + *) let system_exports env = let bigint_trap_fi = E.add_fun env "bigint_trap" ( Func.of_body env [] [] (fun env -> @@ -5621,29 +5745,55 @@ module RTS_Exports = struct }) end; + + (* Stable Memory related exports *) + + let when_stable_memory_required_else_trap env code = + if E.requires_stable_memory env then + code() else + E.trap_with env "unreachable" in + let ic0_stable64_write_fi = - if E.mode env = Flags.WASIMode then + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + E.reuse_import env "ic0" "stable64_write" + | Flags.WASIMode | Flags.WasmMode -> E.add_fun env "ic0_stable64_write" ( - Func.of_body env ["to", I64Type; "from", I64Type; "len", I64Type] [] - (fun env -> - E.trap_with env "ic0_stable64_write is not supposed to be called in WASI" - ) + Func.of_body env ["offset", I64Type; "src", I64Type; "size", I64Type] [] + (fun env -> + when_stable_memory_required_else_trap env (fun () -> + let get_offset = G.i (LocalGet (nr 0l)) in + let get_src = G.i (LocalGet (nr 1l)) in + let get_size = G.i (LocalGet (nr 2l)) in + get_offset ^^ + get_src ^^ + get_size ^^ + StableMem.stable64_write env)) ) - else E.reuse_import env "ic0" "stable64_write" in + in E.add_export env (nr { name = Lib.Utf8.decode "ic0_stable64_write"; edesc = nr (FuncExport (nr ic0_stable64_write_fi)) }); let ic0_stable64_read_fi = - if E.mode env = Flags.WASIMode then + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + E.reuse_import env "ic0" "stable64_read" + | Flags.WASIMode | Flags.WasmMode -> E.add_fun env "ic0_stable64_read" ( - Func.of_body env ["dst", I64Type; "offset", I64Type; "len", I64Type] [] - (fun env -> - E.trap_with env "ic0_stable64_read is not supposed to be called in WASI" - ) + Func.of_body env ["dst", I64Type; "offset", I64Type; "size", I64Type] [] + (fun env -> + when_stable_memory_required_else_trap env (fun () -> + let get_dst = G.i (LocalGet (nr 0l)) in + let get_offset = G.i (LocalGet (nr 1l)) in + let get_size = G.i (LocalGet (nr 2l)) in + get_dst ^^ + get_offset ^^ + get_size ^^ + StableMem.stable64_read env)) ) - else E.reuse_import env "ic0" "stable64_read" in + in E.add_export env (nr { name = Lib.Utf8.decode "ic0_stable64_read"; edesc = nr (FuncExport (nr ic0_stable64_read_fi)) @@ -5653,13 +5803,10 @@ module RTS_Exports = struct E.add_fun env "moc_stable_mem_grow" ( Func.of_body env ["newPages", I64Type] [I64Type] (fun env -> - match E.mode env with - | Flags.ICMode | Flags.RefMode -> - G.i (LocalGet (nr 0l)) ^^ - StableMem.grow env - | _ -> - E.trap_with env "moc_stable_mem_grow is not supposed to be called in WASI" (* improve me *) - )) + when_stable_memory_required_else_trap env (fun () -> + G.i (LocalGet (nr 0l)) ^^ + StableMem.grow env)) + ) in E.add_export env (nr { name = Lib.Utf8.decode "moc_stable_mem_grow"; @@ -5670,12 +5817,8 @@ module RTS_Exports = struct E.add_fun env "moc_stable_mem_size" ( Func.of_body env [] [I64Type] (fun env -> - match E.mode env with - | Flags.ICMode | Flags.RefMode -> - StableMem.get_mem_size env - | _ -> - E.trap_with env "moc_stable_mem_size is not supposed to be called in WASI" (* improve me *) - ) + when_stable_memory_required_else_trap env (fun () -> + StableMem.get_mem_size env)) ) in E.add_export env (nr { @@ -5687,12 +5830,7 @@ module RTS_Exports = struct E.add_fun env "moc_stable_mem_get_version" ( Func.of_body env [] [I32Type] (fun env -> - match E.mode env with - | Flags.ICMode | Flags.RefMode -> - StableMem.get_version env - | _ -> - E.trap_with env "moc_stable_mem_get_version is not supposed to be called in WASI" (* improve me *) - ) + StableMem.get_version env) ) in E.add_export env (nr { @@ -5704,12 +5842,8 @@ module RTS_Exports = struct E.add_fun env "moc_stable_mem_set_version" ( Func.of_body env ["version", I32Type] [] (fun env -> - match E.mode env with - | Flags.ICMode | Flags.RefMode -> - G.i (LocalGet (nr 0l)) ^^ - StableMem.set_version env - | _ -> - E.trap_with env "moc_stable_mem_set_version is not supposed to be called in WASI" (* improve me *) + G.i (LocalGet (nr 0l)) ^^ + StableMem.set_version env ) ) in @@ -7781,7 +7915,7 @@ module Stabilization = struct compile_const_64 4L ^^ extend64 get_dst ^^ extend64 get_len ^^ - IC.system_call env "stable64_write" + StableMem.stable64_write env end end begin @@ -7812,13 +7946,13 @@ module Stabilization = struct compile_add64_const 4L ^^ extend64 get_dst ^^ extend64 get_len ^^ - IC.system_call env "stable64_write" + StableMem.stable64_write env end ^^ (* let M = pagesize * ic0.stable64_size() - 1 *) (* M is beginning of last page *) let (set_M, get_M) = new_local64 env "M" in - IC.system_call env "stable64_size" ^^ + StableMem.stable64_size env ^^ compile_sub64_const 1L ^^ compile_shl64_const (Int64.of_int page_size_bits) ^^ set_M ^^ @@ -7863,7 +7997,7 @@ module Stabilization = struct match E.mode env with | Flags.ICMode | Flags.RefMode -> let (set_pages, get_pages) = new_local64 env "pages" in - IC.system_call env "stable64_size" ^^ + StableMem.stable64_size env ^^ set_pages ^^ get_pages ^^ @@ -7903,7 +8037,7 @@ module Stabilization = struct let (set_version, get_version) = new_local env "version" in let (set_N, get_N) = new_local64 env "N" in - IC.system_call env "stable64_size" ^^ + StableMem.stable64_size env ^^ compile_sub64_const 1L ^^ compile_shl64_const (Int64.of_int page_size_bits) ^^ set_M ^^ @@ -7977,7 +8111,7 @@ module Stabilization = struct extend64 (get_blob ^^ Blob.payload_ptr_unskewed env) ^^ get_offset ^^ extend64 get_len ^^ - IC.system_call env "stable64_read" ^^ + StableMem.stable64_read env ^^ let (set_val, get_val) = new_local env "val" in (* deserialize blob to val *) @@ -7994,7 +8128,7 @@ module Stabilization = struct get_offset ^^ extend64 (get_blob ^^ Blob.payload_ptr_unskewed env) ^^ extend64 (get_blob ^^ Blob.len env) ^^ - IC.system_call env "stable64_write" ^^ + StableMem.stable64_write env ^^ (* return val *) get_val @@ -10611,7 +10745,7 @@ and compile_prim_invocation (env : E.t) ae p es at = | OtherPrim "rts_stable_memory_size", [] -> SR.Vanilla, - IC.ic_system_call "stable64_size" env ^^ BigNum.from_word64 env + StableMem.stable64_size env ^^ BigNum.from_word64 env | OtherPrim "rts_logical_stable_memory_size", [] -> SR.Vanilla, @@ -11920,6 +12054,8 @@ and metadata name value = and conclude_module env set_serialization_globals start_fi_o = + RTS_Exports.system_exports env; + FuncDec.export_async_method env; (* See Note [Candid subtype checks] *) @@ -11958,7 +12094,7 @@ and conclude_module env set_serialization_globals start_fi_o = let other_imports = E.get_other_imports env in - let memories = [nr {mtype = MemoryType {min = E.mem_size env; max = None}} ] in + let memories = E.get_memories env in let funcs = E.get_funcs env in @@ -12007,6 +12143,7 @@ and conclude_module env set_serialization_globals start_fi_o = service = !(env.E.service); }; source_mapping_url = None; + wasm_features = E.get_features env; } in match E.get_rts env with @@ -12027,7 +12164,6 @@ let compile mode rts (prog : Ir.prog) : Wasm_exts.CustomModule.extended_module = IC.system_imports env; RTS.system_imports env; - RTS_Exports.system_exports env; compile_init_func env prog; let start_fi_o = match E.mode env with diff --git a/src/exes/candid_tests.ml b/src/exes/candid_tests.ml index e3718135d69..3d56766b002 100644 --- a/src/exes/candid_tests.ml +++ b/src/exes/candid_tests.ml @@ -14,6 +14,7 @@ let name = "candid-tests" let version = "0.1" let banner = "Candid test suite runner " ^ version ^ "" let usage = "Usage: " ^ name ^ " [ -i path/to/candid/test ]" +let _WASMTIME_OPTIONS_ = "--disable-cache --enable-cranelift-nan-canonicalization --wasm-features multi-memory,bulk-memory" (* Argument handling *) @@ -254,7 +255,7 @@ let () = match run_cmd "moc -Werror -wasi-system-api tmp.mo -o tmp.wasm" with | ((Fail | Timeout), stdout, stderr) -> CantCompile (stdout, stderr, src) | (Ok, _, _) -> - match must_not_trap, run_cmd "timeout 10s wasmtime --disable-cache tmp.wasm" with + match must_not_trap, run_cmd ("timeout 10s wasmtime "^ _WASMTIME_OPTIONS_ ^" tmp.wasm") with | ShouldPass, (Ok, _, _) -> WantedPass | ShouldTrap, (Fail, _, _) -> WantedTrap | ShouldPass, (Fail, stdout, stderr) -> UnwantedTrap (stdout, stderr) diff --git a/src/lang_utils/error_codes.ml b/src/lang_utils/error_codes.ml index 486a7d3791a..d156f900537 100644 --- a/src/lang_utils/error_codes.ml +++ b/src/lang_utils/error_codes.ml @@ -194,4 +194,5 @@ let error_codes : (string * string option) list = "M0188", None; (* Send capability required (calling shared from query) *) "M0189", None; (* Different set of bindings in pattern alternatives *) "M0190", None; (* Types inconsistent for alternative pattern variables, losing information *) + "M0191", None; (* Code requires Wasm features ... to execute *) ] diff --git a/src/linking/linkModule.ml b/src/linking/linkModule.ml index dc37b5e0227..f48f9e67eca 100644 --- a/src/linking/linkModule.ml +++ b/src/linking/linkModule.ml @@ -521,11 +521,17 @@ let set_memory_size new_size_bytes : module_' -> module_' = fun m -> let page_size = Int32.of_int (64*1024) in let new_size_pages = Int32.(add (div new_size_bytes page_size) 1l) in match m.memories with + | [t;t1] -> + { m with + memories = [(phrase (fun m -> + { mtype = MemoryType ({min = new_size_pages; max = None}) } + ) t); t1] + } | [t] -> { m with - memories = [ phrase (fun m -> + memories = [phrase (fun m -> { mtype = MemoryType ({min = new_size_pages; max = None}) } - ) t ] + ) t] } | _ -> raise (LinkError "Expect one memory in first module") diff --git a/src/pipeline/pipeline.ml b/src/pipeline/pipeline.ml index 9867532496e..da9bb1d781e 100644 --- a/src/pipeline/pipeline.ml +++ b/src/pipeline/pipeline.ml @@ -717,6 +717,11 @@ let compile_files mode do_link files : compile_result = | Some (_, ss) -> validate_stab_sig ss | _ -> Diag.return () in + let* () = + if Wasm_exts.CustomModule.(ext_module.wasm_features) <> [] + then Diag.warn Source.no_region "M0191" "compile" (Printf.sprintf "code requires Wasm features %s to execute" (String.concat "," Wasm_exts.CustomModule.(ext_module.wasm_features))) + else Diag.return () + in Diag.return (idl, ext_module) diff --git a/src/wasm-exts/ast.ml b/src/wasm-exts/ast.ml index edf5021810c..d13c26f7a27 100644 --- a/src/wasm-exts/ast.ml +++ b/src/wasm-exts/ast.ml @@ -5,7 +5,8 @@ reference implementation. Base revision: WebAssembly/spec@a7a1856. The changes are: - * None for now + * Pseudo-instruction Meta for debug information + * StableMemory, StableGrow, StableRead, StableWrite instructions. The code is otherwise as untouched as possible, so that we can relatively easily apply diffs from the original code (possibly manually). @@ -116,8 +117,19 @@ and instr' = | Unary of unop (* unary numeric operator *) | Binary of binop (* binary numeric operator *) | Convert of cvtop (* conversion *) + + (* Custom addition for debugging *) | Meta of Dwarf5.Meta.die (* debugging metadata *) + (* Custom additions for emulating stable-memory, special cases + of MemorySize, MemoryGrow and MemoryCopy + requiring wasm features bulk-memory and multi-memory + *) + | StableSize (* size of stable memory *) + | StableGrow (* grow stable memory *) + | StableRead (* read from stable memory *) + | StableWrite (* write to stable memory *) + (* Globals & Functions *) type const = instr list phrase diff --git a/src/wasm-exts/customModule.ml b/src/wasm-exts/customModule.ml index b0db974a0e6..f92e04566ac 100644 --- a/src/wasm-exts/customModule.ml +++ b/src/wasm-exts/customModule.ml @@ -73,4 +73,5 @@ type extended_module = { motoko : motoko_sections; (* source map section *) source_mapping_url : string option; + wasm_features : string list; } diff --git a/src/wasm-exts/customModuleDecode.ml b/src/wasm-exts/customModuleDecode.ml index e2bd0ed02e3..5993362c757 100644 --- a/src/wasm-exts/customModuleDecode.ml +++ b/src/wasm-exts/customModuleDecode.ml @@ -846,13 +846,19 @@ let motoko_stable_types_name = icp_name "motoko:stable-types" let is_icp icp_name n = icp_name n <> None +let is_wasm_features n = (n = Utf8.decode "wasm_features") +let wasm_features_section s = + custom_section is_wasm_features + (fun sec_end s -> let t = utf8 sec_end s in String.split_on_char ',' t) [] s + let is_unknown n = not ( is_dylink n || is_name n || is_motoko n || is_icp candid_service_name n || is_icp candid_args_name n || - is_icp motoko_stable_types_name n) + is_icp motoko_stable_types_name n || + is_wasm_features n) let skip_custom sec_end s = skip (sec_end - pos s) s; @@ -901,6 +907,8 @@ let module_ s = iterate skip_custom_section s; let motoko = motoko_sections s in iterate skip_custom_section s; + let wasm_features = wasm_features_section s in + iterate skip_custom_section s; require (pos s = len s) s (len s) "junk after last section"; require (List.length func_types = List.length func_bodies) s (len s) "function and code section have inconsistent lengths"; @@ -915,6 +923,7 @@ let module_ s = motoko; candid; source_mapping_url = None; + wasm_features = wasm_features; } diff --git a/src/wasm-exts/customModuleEncode.ml b/src/wasm-exts/customModuleEncode.ml index cc4512e26a3..5e2c5feacdd 100644 --- a/src/wasm-exts/customModuleEncode.ml +++ b/src/wasm-exts/customModuleEncode.ml @@ -637,6 +637,15 @@ let encode (em : extended_module) = | Convert (F64 F64Op.DemoteF64) -> assert false | Convert (F64 F64Op.ReinterpretInt) -> op 0xbf + (* Custom encodings for emulating stable-memory, special cases + of MemorySize, MemoryGrow and MemoryCopy + requiring wasm features bulk-memory and multi-memory + *) + | StableSize -> op 0x3f; u8 0x01 + | StableGrow -> op 0x40; u8 0x01 + | StableRead -> op 0xfc; vu32 0x0al; u8 0x00; u8 0x01 + | StableWrite -> op 0xfc; vu32 0x0al; u8 0x01; u8 0x00 + let const c = list (instr ignore) c.it; end_ () @@ -854,6 +863,10 @@ let encode (em : extended_module) = icp_custom_section "candid:service" utf8 candid.service; icp_custom_section "candid:args" utf8 candid.args + let wasm_features_section wasm_features = + let text = String.concat "," wasm_features in + custom_section "wasm_features" utf8 text (text <> "") + let uleb128 n = vu64 (Int64.of_int n) let sleb128 n = vs64 (Int64.of_int n) let close_section () = u8 0x00 @@ -1222,6 +1235,7 @@ let encode (em : extended_module) = name_section em.name; candid_sections em.candid; motoko_sections em.motoko; + wasm_features_section em.wasm_features; source_mapping_url_section em.source_mapping_url; if !Mo_config.Flags.debug_info then begin diff --git a/test/check-error-codes.py b/test/check-error-codes.py index 8e39b9283d8..afb663ef5cc 100755 --- a/test/check-error-codes.py +++ b/test/check-error-codes.py @@ -42,6 +42,7 @@ "M0162", # Candid service constructor type not supported as Motoko type "M0164", # unknown record or variant label in textual representation "M0165", # odd expected type + "M0191", # compiler warning about wasm features (hard to trigger) } def populate_error_codes(): diff --git a/test/random/Embedder.hs b/test/random/Embedder.hs index fc88c429d27..21c28ed17c2 100644 --- a/test/random/Embedder.hs +++ b/test/random/Embedder.hs @@ -36,7 +36,7 @@ addCompilerArgs (WasmTime _) = ("-wasi-system-api" :) addCompilerArgs Drun = id addEmbedderArgs Reference = id -addEmbedderArgs (WasmTime _) = ("--disable-cache" :) +addEmbedderArgs (WasmTime _) = ("--disable-cache" :) . ("--enable-cranelift-nan-canonicalization" :) . ("--wasm-features" :) . ("multi-memory,bulk-memory" :) addEmbedderArgs Drun = ("--extra-batches" :) . ("10" :) embedderInvocation :: Embedder -> [Text] -> [Text] diff --git a/test/run-drun/ok/region0-big-blob.drun-run.ok b/test/run-drun/ok/region0-big-blob.drun-run.ok deleted file mode 100644 index 01dcaede9b7..00000000000 --- a/test/run-drun/ok/region0-big-blob.drun-run.ok +++ /dev/null @@ -1,4 +0,0 @@ -ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 -debug.print: 255 -debug.print: ok -ingress Err: IC0503: Canister rwlgt-iiaaa-aaaaa-aaaaa-cai trapped explicitly: assertion failed at region0-big-blob.mo:18.3-18.15 diff --git a/test/run-drun/ok/region0-big-blob.tc.ok b/test/run-drun/ok/region0-big-blob.tc.ok deleted file mode 100644 index 3042e1965f8..00000000000 --- a/test/run-drun/ok/region0-big-blob.tc.ok +++ /dev/null @@ -1,4 +0,0 @@ -region0-big-blob.mo:6.7-6.8: warning [M0145], this pattern of type - Nat64 -does not cover value - 1 or 2 or _ diff --git a/test/run-drun/ok/stable-mem-big-blob.drun-run.ok b/test/run-drun/ok/stable-mem-big-blob.drun-run.ok deleted file mode 100644 index d0e52bea656..00000000000 --- a/test/run-drun/ok/stable-mem-big-blob.drun-run.ok +++ /dev/null @@ -1,4 +0,0 @@ -ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 -debug.print: 255 -debug.print: ok -ingress Err: IC0503: Canister rwlgt-iiaaa-aaaaa-aaaaa-cai trapped explicitly: assertion failed at stable-mem-big-blob.mo:17.3-17.15 diff --git a/test/run-drun/ok/stable-mem-big-blob.tc.ok b/test/run-drun/ok/stable-mem-big-blob.tc.ok deleted file mode 100644 index b626baec234..00000000000 --- a/test/run-drun/ok/stable-mem-big-blob.tc.ok +++ /dev/null @@ -1,4 +0,0 @@ -stable-mem-big-blob.mo:5.7-5.8: warning [M0145], this pattern of type - Nat64 -does not cover value - 1 or 2 or _ diff --git a/test/run-drun/region0-big-blob.mo b/test/run-drun/region0-big-blob.mo deleted file mode 100644 index 57ec3123afa..00000000000 --- a/test/run-drun/region0-big-blob.mo +++ /dev/null @@ -1,25 +0,0 @@ -//MOC-FLAG --stable-regions -import P "mo:⛔"; -import StableMemory "stable-mem/StableMemory"; -actor { - - let 0 = StableMemory.grow(65535-128); - let n = 2**31+16384; - let o:Nat64 = 2**31+16384 - 1; - StableMemory.storeNat8(o, 255); - let b = StableMemory.loadBlob(0, n); - StableMemory.storeNat8(o, 0); - assert StableMemory.loadNat8(o) == 0; - StableMemory.storeBlob(0, b); - P.debugPrint(debug_show StableMemory.loadNat8(o)); - assert StableMemory.loadNat8(o) == 255; - assert b.size() == n; - P.debugPrint("ok"); - assert false; -} - -//SKIP run -//SKIP run-low -//SKIP run-ir -// too slow on ic-ref-run: -//SKIP comp-ref diff --git a/test/run-drun/stable-mem-big-blob.mo b/test/run-drun/stable-mem-big-blob.mo deleted file mode 100644 index e505558bdfe..00000000000 --- a/test/run-drun/stable-mem-big-blob.mo +++ /dev/null @@ -1,26 +0,0 @@ -import P "mo:⛔"; -import StableMemory "stable-mem/StableMemory"; -actor { - - let 0 = StableMemory.grow(65535-128); - let n = 2**31+16384; - let o:Nat64 = 2**31+16384 - 1; - StableMemory.storeNat8(o, 255); - let b = StableMemory.loadBlob(0, n); - StableMemory.storeNat8(o, 0); - assert StableMemory.loadNat8(o) == 0; - StableMemory.storeBlob(0, b); - P.debugPrint(debug_show StableMemory.loadNat8(o)); - assert StableMemory.loadNat8(o) == 255; - assert b.size() == n; - P.debugPrint("ok"); - assert false; -} - -//SKIP run -//SKIP run-low -//SKIP run-ir -// too slow on ic-ref-run: -//SKIP comp-ref - - diff --git a/test/run-drun/stable-mem/StableMemory.mo b/test/run-drun/stable-mem/StableMemory.mo index a3d6bb2ed44..8570198b7ef 100644 --- a/test/run-drun/stable-mem/StableMemory.mo +++ b/test/run-drun/stable-mem/StableMemory.mo @@ -3,7 +3,7 @@ import Prim "mo:⛔"; module { public let size = Prim.stableMemorySize; - //public let region = Prim.stableMemoryRegion; + public let grow = Prim.stableMemoryGrow; public let loadNat32 = Prim.stableMemoryLoadNat32; diff --git a/test/run.sh b/test/run.sh index 34fd3d36be4..50d81e09fb6 100755 --- a/test/run.sh +++ b/test/run.sh @@ -26,7 +26,7 @@ DTESTS=no IDL=no PERF=no VIPER=no -WASMTIME_OPTIONS="--disable-cache --enable-cranelift-nan-canonicalization" +WASMTIME_OPTIONS="--disable-cache --enable-cranelift-nan-canonicalization --wasm-features multi-memory,bulk-memory" WRAP_drun=$(realpath $(dirname $0)/drun-wrapper.sh) WRAP_ic_ref_run=$(realpath $(dirname $0)/ic-ref-run-wrapper.sh) SKIP_RUNNING=${SKIP_RUNNING:-no} @@ -358,8 +358,8 @@ do if [ "$SKIP_VALIDATE" != yes ] then - run_if wasm valid wasm-validate $out/$base.wasm - run_if ref.wasm valid-ref wasm-validate $out/$base.ref.wasm + run_if wasm valid wasm-validate --enable-multi-memory $out/$base.wasm + run_if ref.wasm valid-ref wasm-validate --enable-multi-memory $out/$base.ref.wasm fi if [ -e $out/$base.wasm ] @@ -370,7 +370,7 @@ do if grep -F -q CHECK $mangled then $ECHO -n " [FileCheck]" - wasm2wat --no-check $out/$base.wasm > $out/$base.wat + wasm2wat --enable-multi-memory --no-check $out/$base.wasm > $out/$base.wat cat $out/$base.wat | FileCheck $mangled > $out/$base.filecheck 2>&1 diff_files="$diff_files $base.filecheck" fi diff --git a/test/run/ok/region-test.tc.ok b/test/run/ok/region-test.tc.ok new file mode 100644 index 00000000000..60fa5bd982d --- /dev/null +++ b/test/run/ok/region-test.tc.ok @@ -0,0 +1,12 @@ +region-test.mo:5.5-5.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ +region-test.mo:6.5-6.7: warning [M0145], this pattern of type + Nat +does not cover value + 0 or 1 or _ +region-test.mo:7.5-7.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ diff --git a/test/run/ok/region-test.wasm-run.ok b/test/run/ok/region-test.wasm-run.ok new file mode 100644 index 00000000000..bb8f39594d0 --- /dev/null +++ b/test/run/ok/region-test.wasm-run.ok @@ -0,0 +1,10 @@ +Nat8 +Nat16 +Nat32 +Nat64 +Int8 +Int16 +Int32 +Int64 +Float +Blob diff --git a/test/run/ok/region0-big-blob.tc.ok b/test/run/ok/region0-big-blob.tc.ok new file mode 100644 index 00000000000..4a3e5f19a97 --- /dev/null +++ b/test/run/ok/region0-big-blob.tc.ok @@ -0,0 +1,4 @@ +region0-big-blob.mo:5.5-5.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ diff --git a/test/run/ok/region0-big-blob.wasm-run.ok b/test/run/ok/region0-big-blob.wasm-run.ok new file mode 100644 index 00000000000..7ed54ca357a --- /dev/null +++ b/test/run/ok/region0-big-blob.wasm-run.ok @@ -0,0 +1,2 @@ +255 +ok diff --git a/test/run/ok/stable-log.wasm-run.ok b/test/run/ok/stable-log.wasm-run.ok new file mode 100644 index 00000000000..1b52ae13b2f --- /dev/null +++ b/test/run/ok/stable-log.wasm-run.ok @@ -0,0 +1,50 @@ +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 +19 +18 +17 +16 +15 +14 +13 +12 +11 +10 +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 +19 +18 +17 +16 +15 +14 +13 +12 +11 +10 +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 diff --git a/test/run/ok/stable-mem-big-blob.tc.ok b/test/run/ok/stable-mem-big-blob.tc.ok new file mode 100644 index 00000000000..422a0c590b6 --- /dev/null +++ b/test/run/ok/stable-mem-big-blob.tc.ok @@ -0,0 +1,4 @@ +stable-mem-big-blob.mo:4.5-4.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ diff --git a/test/run/ok/stable-mem-big-blob.wasm-run.ok b/test/run/ok/stable-mem-big-blob.wasm-run.ok new file mode 100644 index 00000000000..7ed54ca357a --- /dev/null +++ b/test/run/ok/stable-mem-big-blob.wasm-run.ok @@ -0,0 +1,2 @@ +255 +ok diff --git a/test/run/ok/stable-memory-test.tc.ok b/test/run/ok/stable-memory-test.tc.ok new file mode 100644 index 00000000000..3a58a2d5f5d --- /dev/null +++ b/test/run/ok/stable-memory-test.tc.ok @@ -0,0 +1,8 @@ +stable-memory-test.mo:4.5-4.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ +stable-memory-test.mo:5.5-5.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ diff --git a/test/run/ok/stable-memory-test.wasm-run.ok b/test/run/ok/stable-memory-test.wasm-run.ok new file mode 100644 index 00000000000..bb8f39594d0 --- /dev/null +++ b/test/run/ok/stable-memory-test.wasm-run.ok @@ -0,0 +1,10 @@ +Nat8 +Nat16 +Nat32 +Nat64 +Int8 +Int16 +Int32 +Int64 +Float +Blob diff --git a/test/run/ok/stable-memory.tc.ok b/test/run/ok/stable-memory.tc.ok new file mode 100644 index 00000000000..6c48daae888 --- /dev/null +++ b/test/run/ok/stable-memory.tc.ok @@ -0,0 +1,32 @@ +stable-memory.mo:4.5-4.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ +stable-memory.mo:5.5-5.7: warning [M0145], this pattern of type + Nat64 +does not cover value + 0 or 1 or _ +stable-memory.mo:7.5-7.6: warning [M0145], this pattern of type + Nat8 +does not cover value + 1 or 2 or _ +stable-memory.mo:11.5-11.7: warning [M0145], this pattern of type + Nat8 +does not cover value + 0 or 1 or _ +stable-memory.mo:17.5-17.6: warning [M0145], this pattern of type + Nat64 +does not cover value + 1 or 2 or _ +stable-memory.mo:18.5-18.7: warning [M0145], this pattern of type + Nat64 +does not cover value + 0 or 1 or _ +stable-memory.mo:20.5-20.6: warning [M0145], this pattern of type + Nat8 +does not cover value + 1 or 2 or _ +stable-memory.mo:24.5-24.7: warning [M0145], this pattern of type + Nat8 +does not cover value + 0 or 1 or _ diff --git a/test/run/ok/stable-memory.wasm-run.ok b/test/run/ok/stable-memory.wasm-run.ok new file mode 100644 index 00000000000..0c6376ea08b --- /dev/null +++ b/test/run/ok/stable-memory.wasm-run.ok @@ -0,0 +1,9 @@ +{size = 0} +{size = 16} +{read = 0} +{read = 66} +{read = 66} +{size = 16} +{read = 0} +{read = 66} +{read = 66} diff --git a/test/run/region-test.mo b/test/run/region-test.mo new file mode 100644 index 00000000000..a2fc1567c9c --- /dev/null +++ b/test/run/region-test.mo @@ -0,0 +1,214 @@ +import Prim "mo:⛔"; +import Region "stable-region/Region"; + +let r = Region.new(); +let 0 = Region.size(r); +let 16 = Region.id(r); +let 0 = Region.grow(r, 64); +assert (64 == Region.size(r)); + +do { + Prim.debugPrint("Nat8"); + type T = Nat8; + let size : Nat64 = 1; + let mod : Nat64 = 256; + let load = Region.loadNat8; + let store = Region.storeNat8; + func conv(n : Nat64) : Nat8 { Prim.natToNat8(Prim.nat64ToNat(n % mod)) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat16"); + type T = Nat16; + let size : Nat64 = 2; + let mod : Nat64 = 65536; + let load = Region.loadNat16; + let store = Region.storeNat16; + func conv(n : Nat64) : Nat16 { Prim.natToNat16(Prim.nat64ToNat(n % mod)) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat32"); + type T = Nat32; + let size : Nat64 = 4; + let mod : Nat64 = 2**32; + let load = Region.loadNat32; + let store = Region.storeNat32; + func conv(n : Nat64) : Nat32 { Prim.natToNat32(Prim.nat64ToNat(n % mod)) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat64"); + type T = Nat8; + let size : Nat64 = 8; + let load = Region.loadNat64; + let store = Region.storeNat64; + func conv(n : Nat64) : Nat64 { n }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int8"); + type T = Int8; + let size : Nat64 = 1; + let mod : Nat64 = 256; + let load = Region.loadInt8; + let store = Region.storeInt8; + func conv(n : Nat64) : Int8 { Prim.intToInt8(Prim.nat64ToNat(n % mod) - 128) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int16"); + type T = Int16; + let size : Nat64 = 2; + let mod : Nat64 = 65536; + let load = Region.loadInt16; + let store = Region.storeInt16; + func conv(n : Nat64) : Int16 { Prim.intToInt16(Prim.nat64ToNat(n % mod) - 32768 )}; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int32"); + type T = Int32; + let size : Nat64 = 4; + let mod : Nat64 = 2**32; + let load = Region.loadInt32; + let store = Region.storeInt32; + func conv(n : Nat64) : Int32 { Prim.intToInt32(Prim.nat64ToNat(n % mod) - 2147483648) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int64"); + type T = Int8; + let size : Nat64 = 8; + let load = Region.loadInt64; + let store = Region.storeInt64; + func conv(n : Nat64) : Int64 { Prim.nat64ToInt64(n)+(-9223372036854775808) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Float"); + type T = Int8; + let size : Nat64 = 8; + let load = Region.loadFloat; + let store = Region.storeFloat; + func conv(n : Nat64) : Float { Prim.int64ToFloat(Prim.nat64ToInt64(n)+(-9223372036854775808)) }; + let max = Region.size(r)*65536; + var i : Nat64 = 0; + while(i < max) { + store(r, i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(r, i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Blob"); + type T = Int8; + let load = Region.loadBlob; + let store = Region.storeBlob; + func conv(n : Nat64) : Blob { load(r, 0, Prim.nat64ToNat(n)) }; + let max : Nat64 = Region.size(r)*65536; + var i : Nat64 = 1; + while(i < max) { + let b = conv(i); + store(r, 0, b); + assert (b == load(r, 0, Prim.nat64ToNat(i))); + i := i * 2; + }; +}; + +//SKIP run-low +//SKIP run +//SKIP run-ir diff --git a/test/run/region0-big-blob.mo b/test/run/region0-big-blob.mo new file mode 100644 index 00000000000..c941716eb2e --- /dev/null +++ b/test/run/region0-big-blob.mo @@ -0,0 +1,22 @@ +//MOC-FLAG --stable-regions +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; + +let 0 = StableMemory.grow(65535-128); +let n = 2**31+16384; +let o:Nat64 = 2**31+16384 - 1; +StableMemory.storeNat8(o, 255); +let b = StableMemory.loadBlob(0, n); +StableMemory.storeNat8(o, 0); +assert StableMemory.loadNat8(o) == 0; +StableMemory.storeBlob(0, b); +P.debugPrint(debug_show StableMemory.loadNat8(o)); +assert StableMemory.loadNat8(o) == 255; +assert b.size() == n; +P.debugPrint("ok"); + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref diff --git a/test/run/stable-log.mo b/test/run/stable-log.mo new file mode 100644 index 00000000000..0f5d92033cf --- /dev/null +++ b/test/run/stable-log.mo @@ -0,0 +1,77 @@ +import Prim "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; + +func ensure(offset : Nat64) { + let pages = (offset + 65536) >> 16; + if (pages > StableMemory.size()) { + let oldsize = StableMemory.grow(pages - StableMemory.size()); + assert (oldsize != 0xFFFF_FFFF_FFFF_FFFF); + }; +}; + +var base : Nat64 = 0; + +func log(t : Text) { + let blob = Prim.encodeUtf8(t); + let size = Prim.natToNat64(blob.size()); + ensure(base + size + 4); + StableMemory.storeBlob(base, blob); + base += size; + StableMemory.storeNat32(base, Prim.natToNat32(blob.size())); + base += 4; +}; + +func readLast(count : Nat) : [Text] { + let a = Prim.Array_init(count, ""); + var offset = base; + var k = 0; + while (k < count and offset > 0) { + offset -= 4; + let size = StableMemory.loadNat32(offset); + offset -= Prim.natToNat64(Prim.nat32ToNat(size)); + let blob = StableMemory.loadBlob(offset, Prim.nat32ToNat(size)); + switch (Prim.decodeUtf8(blob)) { + case (?t) { a[k] := t }; + case null { assert false }; + }; + k += 1; + }; + return Prim.Array_tabulate(k, func i { a[i] }); +}; + + +// testing +var count = 0; + +func populate() : () { + // populate + let limit = count + 10; + while (count < limit) { + log(debug_show(count)); + count += 1; + }; +}; + +func readAll() : () { + let ts = readLast(count); + for (t in ts.vals()) { + Prim.debugPrint(t); + }; +}; + +func readExtra() : () { + let ts = readLast(2*count); + for (t in ts.vals()) { + Prim.debugPrint(t); + } +}; + +populate(); +readAll(); +populate(); +readAll(); +readExtra(); + +//SKIP run +//SKIP run-low +//SKIP run-ir diff --git a/test/run/stable-mem-big-blob.mo b/test/run/stable-mem-big-blob.mo new file mode 100644 index 00000000000..3531121818c --- /dev/null +++ b/test/run/stable-mem-big-blob.mo @@ -0,0 +1,23 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; + +let 0 = StableMemory.grow(65535-128); +let n = 2**31+16384; +let o:Nat64 = 2**31+16384 - 1; +StableMemory.storeNat8(o, 255); +let b = StableMemory.loadBlob(0, n); +StableMemory.storeNat8(o, 0); +assert StableMemory.loadNat8(o) == 0; +StableMemory.storeBlob(0, b); +P.debugPrint(debug_show StableMemory.loadNat8(o)); +assert StableMemory.loadNat8(o) == 255; +assert b.size() == n; +P.debugPrint("ok"); + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + + diff --git a/test/run/stable-mem/StableMemory.mo b/test/run/stable-mem/StableMemory.mo new file mode 100644 index 00000000000..ef6f485f326 --- /dev/null +++ b/test/run/stable-mem/StableMemory.mo @@ -0,0 +1,38 @@ +import Prim "mo:⛔"; + +module { + + public let size = Prim.stableMemorySize; + public let grow = Prim.stableMemoryGrow; + + public let loadNat32 = Prim.stableMemoryLoadNat32; + public let storeNat32 = Prim.stableMemoryStoreNat32; + + public let loadNat8 = Prim.stableMemoryLoadNat8; + public let storeNat8 = Prim.stableMemoryStoreNat8; + + public let loadNat16 = Prim.stableMemoryLoadNat16; + public let storeNat16 = Prim.stableMemoryStoreNat16; + + public let loadNat64 = Prim.stableMemoryLoadNat64; + public let storeNat64 = Prim.stableMemoryStoreNat64; + + public let loadFloat = Prim.stableMemoryLoadFloat; + public let storeFloat= Prim.stableMemoryStoreFloat; + + public let loadInt32 = Prim.stableMemoryLoadInt32; + public let storeInt32 = Prim.stableMemoryStoreInt32; + + public let loadInt8 = Prim.stableMemoryLoadInt8; + public let storeInt8 = Prim.stableMemoryStoreInt8; + + public let loadInt16 = Prim.stableMemoryLoadInt16; + public let storeInt16 = Prim.stableMemoryStoreInt16; + + public let loadInt64 = Prim.stableMemoryLoadInt64; + public let storeInt64 = Prim.stableMemoryStoreInt64; + + public let loadBlob = Prim.stableMemoryLoadBlob; + public let storeBlob = Prim.stableMemoryStoreBlob; + +} diff --git a/test/run/stable-memory-test.mo b/test/run/stable-memory-test.mo new file mode 100644 index 00000000000..425555f81b8 --- /dev/null +++ b/test/run/stable-memory-test.mo @@ -0,0 +1,212 @@ +import Prim "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; + +let 0 = StableMemory.size(); +let 0 = StableMemory.grow(64); +assert (64 == StableMemory.size()); + +do { + Prim.debugPrint("Nat8"); + type T = Nat8; + let size : Nat64 = 1; + let mod : Nat64 = 256; + let load = StableMemory.loadNat8; + let store = StableMemory.storeNat8; + func conv(n : Nat64) : Nat8 { Prim.natToNat8(Prim.nat64ToNat(n % mod)) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat16"); + type T = Nat16; + let size : Nat64 = 2; + let mod : Nat64 = 65536; + let load = StableMemory.loadNat16; + let store = StableMemory.storeNat16; + func conv(n : Nat64) : Nat16 { Prim.natToNat16(Prim.nat64ToNat(n % mod)) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat32"); + type T = Nat32; + let size : Nat64 = 4; + let mod : Nat64 = 2**32; + let load = StableMemory.loadNat32; + let store = StableMemory.storeNat32; + func conv(n : Nat64) : Nat32 { Prim.natToNat32(Prim.nat64ToNat(n % mod)) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Nat64"); + type T = Nat8; + let size : Nat64 = 8; + let load = StableMemory.loadNat64; + let store = StableMemory.storeNat64; + func conv(n : Nat64) : Nat64 { n }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int8"); + type T = Int8; + let size : Nat64 = 1; + let mod : Nat64 = 256; + let load = StableMemory.loadInt8; + let store = StableMemory.storeInt8; + func conv(n : Nat64) : Int8 { Prim.intToInt8(Prim.nat64ToNat(n % mod) - 128) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int16"); + type T = Int16; + let size : Nat64 = 2; + let mod : Nat64 = 65536; + let load = StableMemory.loadInt16; + let store = StableMemory.storeInt16; + func conv(n : Nat64) : Int16 { Prim.intToInt16(Prim.nat64ToNat(n % mod) - 32768 )}; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int32"); + type T = Int32; + let size : Nat64 = 4; + let mod : Nat64 = 2**32; + let load = StableMemory.loadInt32; + let store = StableMemory.storeInt32; + func conv(n : Nat64) : Int32 { Prim.intToInt32(Prim.nat64ToNat(n % mod) - 2147483648) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Int64"); + type T = Int8; + let size : Nat64 = 8; + let load = StableMemory.loadInt64; + let store = StableMemory.storeInt64; + func conv(n : Nat64) : Int64 { Prim.nat64ToInt64(n)+(-9223372036854775808) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Float"); + type T = Int8; + let size : Nat64 = 8; + let load = StableMemory.loadFloat; + let store = StableMemory.storeFloat; + func conv(n : Nat64) : Float { Prim.int64ToFloat(Prim.nat64ToInt64(n)+(-9223372036854775808)) }; + let max = StableMemory.size()*65536; + var i : Nat64 = 0; + while(i < max) { + store(i, conv(i)); + i += size; + }; + i := 0; + while(i < max) { + assert (conv(i) == load(i)); + i += size; + }; +}; + +do { + Prim.debugPrint("Blob"); + type T = Int8; + let load = StableMemory.loadBlob; + let store = StableMemory.storeBlob; + func conv(n : Nat64) : Blob { load(0, Prim.nat64ToNat(n)) }; + let max : Nat64 = StableMemory.size()*65536; + var i : Nat64 = 1; + while(i < max) { + let b = conv(i); + store(0, b); + assert (b == load(0, Prim.nat64ToNat(i))); + i := i * 2; + }; +}; + +//SKIP run-low +//SKIP run +//SKIP run-ir diff --git a/test/run/stable-memory.mo b/test/run/stable-memory.mo new file mode 100644 index 00000000000..b0a9bdc7299 --- /dev/null +++ b/test/run/stable-memory.mo @@ -0,0 +1,30 @@ +import Prim "mo:prim"; + +Prim.debugPrint(debug_show {size = Prim.stableMemorySize()}); +let 0 = Prim.stableMemoryGrow(16); +let 16 = Prim.stableMemorySize(); +Prim.debugPrint(debug_show {size = Prim.stableMemorySize()}); +let 0 = Prim.stableMemoryLoadNat8(0); +Prim.debugPrint(debug_show {read = Prim.stableMemoryLoadNat8(0)}); +Prim.stableMemoryStoreNat8(0,66); +Prim.debugPrint(debug_show {read = Prim.stableMemoryLoadNat8(0)}); +let 66 = Prim.stableMemoryLoadNat8(0); +Prim.debugPrint(debug_show {read = Prim.stableMemoryLoadNat8(0)}); + +/* regions */ + +let r1 = Prim.regionNew(); +let 0 = Prim.regionGrow(r1, 16); +let 16 = Prim.regionSize(r1); +Prim.debugPrint(debug_show {size = Prim.regionSize(r1)}); +let 0 = Prim.regionLoadNat8(r1, 0); +Prim.debugPrint(debug_show {read = Prim.regionLoadNat8(r1, 0)}); +Prim.regionStoreNat8(r1, 0, 66); +Prim.debugPrint(debug_show {read = Prim.regionLoadNat8(r1, 0)}); +let 66 = Prim.regionLoadNat8(r1, 0); +Prim.debugPrint(debug_show {read = Prim.regionLoadNat8(r1, 0)}); + + +//SKIP run-low +//SKIP run +//SKIP run-ir diff --git a/test/run/stable-region/Region.mo b/test/run/stable-region/Region.mo new file mode 100644 index 00000000000..7422a0a3811 --- /dev/null +++ b/test/run/stable-region/Region.mo @@ -0,0 +1,41 @@ +import Prim "mo:⛔"; + +module { + + public let new = Prim.regionNew; + public let id = Prim.regionId; + + public let size = Prim.regionSize; + public let grow = Prim.regionGrow; + + public let loadNat32 = Prim.regionLoadNat32; + public let storeNat32 = Prim.regionStoreNat32; + + public let loadNat8 = Prim.regionLoadNat8; + public let storeNat8 = Prim.regionStoreNat8; + + public let loadNat16 = Prim.regionLoadNat16; + public let storeNat16 = Prim.regionStoreNat16; + + public let loadNat64 = Prim.regionLoadNat64; + public let storeNat64 = Prim.regionStoreNat64; + + public let loadFloat = Prim.regionLoadFloat; + public let storeFloat= Prim.regionStoreFloat; + + public let loadInt32 = Prim.regionLoadInt32; + public let storeInt32 = Prim.regionStoreInt32; + + public let loadInt8 = Prim.regionLoadInt8; + public let storeInt8 = Prim.regionStoreInt8; + + public let loadInt16 = Prim.regionLoadInt16; + public let storeInt16 = Prim.regionStoreInt16; + + public let loadInt64 = Prim.regionLoadInt64; + public let storeInt64 = Prim.regionStoreInt64; + + public let loadBlob = Prim.regionLoadBlob; + public let storeBlob = Prim.regionStoreBlob; + +}