summaryrefslogtreecommitdiff
path: root/wasm2c/wasm-rt.h
blob: c489c85099c7fd9f326abe7a6ed472816fc2c039 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/*
 * Copyright 2018 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef WASM_RT_H_
#define WASM_RT_H_

#include <setjmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef __has_builtin
#define __has_builtin(x) 0 /** Compatibility with non-clang compilers. */
#endif

#if __has_builtin(__builtin_expect)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#define LIKELY(x) __builtin_expect(!!(x), 1)
#else
#define UNLIKELY(x) (x)
#define LIKELY(x) (x)
#endif

#if __has_builtin(__builtin_memcpy)
#define wasm_rt_memcpy __builtin_memcpy
#else
#define wasm_rt_memcpy memcpy
#endif

#if __has_builtin(__builtin_unreachable)
#define wasm_rt_unreachable __builtin_unreachable
#else
#define wasm_rt_unreachable abort
#endif

#ifdef __STDC_VERSION__
#if __STDC_VERSION__ >= 201112L
#define WASM_RT_C11_AVAILABLE
#endif
#endif

/**
 * Many devices don't implement the C11 threads.h. We use CriticalSection APIs
 * for Windows and pthreads on other platforms where threads are not available.
 */
#ifdef WASM_RT_C11_AVAILABLE

#if defined(_WIN32)
#include <windows.h>
#define WASM_RT_MUTEX CRITICAL_SECTION
#define WASM_RT_USE_CRITICALSECTION 1
#elif defined(__APPLE__) || defined(__STDC_NO_THREADS__)
#include <pthread.h>
#define WASM_RT_MUTEX pthread_mutex_t
#define WASM_RT_USE_PTHREADS 1
#else
#include <threads.h>
#define WASM_RT_MUTEX mtx_t
#define WASM_RT_USE_C11THREADS 1
#endif

#endif

#ifdef _MSC_VER
#define WASM_RT_THREAD_LOCAL __declspec(thread)
#elif defined(WASM_RT_C11_AVAILABLE)
#define WASM_RT_THREAD_LOCAL _Thread_local
#else
#define WASM_RT_THREAD_LOCAL
#endif

/**
 * If enabled, perform additional sanity checks in the generated wasm2c code and
 * wasm2c runtime. This is useful to enable on debug builds.
 */
#ifndef WASM_RT_SANITY_CHECKS
#define WASM_RT_SANITY_CHECKS 0
#endif

/**
 * Backward compatibility: Convert the previously exposed
 * WASM_RT_MEMCHECK_SIGNAL_HANDLER macro to the ALLOCATION and CHECK macros that
 * are now used.
 */
#if defined(WASM_RT_MEMCHECK_SIGNAL_HANDLER)

#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
#define WASM_RT_USE_MMAP 1
#define WASM_RT_MEMCHECK_GUARD_PAGES 1
#else
#define WASM_RT_USE_MMAP 0
#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1
#endif

#warning \
    "WASM_RT_MEMCHECK_SIGNAL_HANDLER has been deprecated in favor of WASM_RT_USE_MMAP and WASM_RT_MEMORY_CHECK_* macros"
#endif

/**
 * Specify if we use OR mmap/mprotect (+ Windows equivalents) OR malloc/realloc
 * for the Wasm memory allocation and growth. mmap/mprotect guarantees memory
 * will grow without being moved, while malloc ensures the virtual memory is
 * consumed only as needed, but may relocate the memory to handle memory
 * fragmentation.
 *
 * This defaults to malloc on 32-bit platforms or if memory64 support is needed.
 * It defaults to mmap on 64-bit platforms assuming memory64 support is not
 * needed (so we can use the guard based range checks below).
 */
#ifndef WASM_RT_USE_MMAP
#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64
#define WASM_RT_USE_MMAP 1
#else
#define WASM_RT_USE_MMAP 0
#endif
#endif

/**
 * Set the range checking strategy for Wasm memories.
 *
 * GUARD_PAGES:  memory accesses rely on unmapped pages/guard pages to trap
 * out-of-bound accesses.
 *
 * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks.
 *
 * This defaults to GUARD_PAGES as this is the fasest option, iff the
 * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy,
 * no 64-bit memories, no big-endian --- are met. This falls back to BOUNDS
 * otherwise.
 */

/** Check if Guard checks are supported */
#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 && \
    !WABT_BIG_ENDIAN
#define WASM_RT_GUARD_PAGES_SUPPORTED 1
#else
#define WASM_RT_GUARD_PAGES_SUPPORTED 0
#endif

/** Specify defaults for memory checks if unspecified */
#if !defined(WASM_RT_MEMCHECK_GUARD_PAGES) && \
    !defined(WASM_RT_MEMCHECK_BOUNDS_CHECK)
#if WASM_RT_GUARD_PAGES_SUPPORTED
#define WASM_RT_MEMCHECK_GUARD_PAGES 1
#else
#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1
#endif
#endif

/** Ensure the macros are defined */
#ifndef WASM_RT_MEMCHECK_GUARD_PAGES
#define WASM_RT_MEMCHECK_GUARD_PAGES 0
#endif
#ifndef WASM_RT_MEMCHECK_BOUNDS_CHECK
#define WASM_RT_MEMCHECK_BOUNDS_CHECK 0
#endif

/** Sanity check the use of guard pages */
#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_GUARD_PAGES_SUPPORTED
#error \
    "WASM_RT_MEMCHECK_GUARD_PAGES not supported on this platform/configuration"
#endif

#if WASM_RT_MEMCHECK_GUARD_PAGES && WASM_RT_MEMCHECK_BOUNDS_CHECK
#error \
    "Cannot use both WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK"

#elif !WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_MEMCHECK_BOUNDS_CHECK
#error \
    "Must choose at least one from WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK"
#endif

/**
 * Some configurations above require the Wasm runtime to install a signal
 * handler. However, this can be explicitly disallowed by the host using
 * WASM_RT_SKIP_SIGNAL_RECOVERY. In this case, when the wasm code encounters an
 * OOB access, it may either trap or abort.
 */
#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY
#define WASM_RT_SKIP_SIGNAL_RECOVERY 0
#endif

#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_SKIP_SIGNAL_RECOVERY
#define WASM_RT_INSTALL_SIGNAL_HANDLER 1
#else
#define WASM_RT_INSTALL_SIGNAL_HANDLER 0
#endif

/**
 * This macro, if defined to 1 (i.e., allows the "segue" optimization), allows
 * Wasm2c to use segment registers to speedup access to the linear heap. Note
 * that even if allowed in this way, the segment registers would only be used if
 * Wasm2c output is compiled for a suitable architecture and OS and the produces
 * C file is compiled by supported compilers. The extact restrictions are listed
 * in detail in src/template/wasm2c.declarations.c
 */
#ifndef WASM_RT_ALLOW_SEGUE
#define WASM_RT_ALLOW_SEGUE 0
#endif

/**
 * This macro, if defined, allows the embedder to disable all stack exhaustion
 * checks. This a non conformant configuration, i.e., this does not respect
 * Wasm's specification, and may compromise security. Use with caution.
 */
#ifndef WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION
#define WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION 0
#endif

/**
 * We need to detect and trap stack overflows. If we use a signal handler on
 * POSIX systems, this can detect call stack overflows. On windows, or platforms
 * without a signal handler, we use stack depth counting.
 */
#if !defined(WASM_RT_STACK_DEPTH_COUNT) &&        \
    !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \
    !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION

#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32)
#define WASM_RT_STACK_EXHAUSTION_HANDLER 1
#else
#define WASM_RT_STACK_DEPTH_COUNT 1
#endif

#endif

/** Ensure the stack macros are defined */
#ifndef WASM_RT_STACK_DEPTH_COUNT
#define WASM_RT_STACK_DEPTH_COUNT 0
#endif
#ifndef WASM_RT_STACK_EXHAUSTION_HANDLER
#define WASM_RT_STACK_EXHAUSTION_HANDLER 0
#endif

#if WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION

#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) != 0
#error \
    "Cannot specify WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION along with WASM_RT_STACK_EXHAUSTION_HANDLER or WASM_RT_STACK_DEPTH_COUNT"
#endif

#else

#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) > 1
#error \
    "Cannot specify multiple options from WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT"
#elif (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) == 0
#error \
    "Must specify one of WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT"
#endif

#endif

#if WASM_RT_STACK_EXHAUSTION_HANDLER && !WASM_RT_INSTALL_SIGNAL_HANDLER
#error \
    "WASM_RT_STACK_EXHAUSTION_HANDLER  can only be used if WASM_RT_INSTALL_SIGNAL_HANDLER is enabled"
#endif

#if WASM_RT_STACK_DEPTH_COUNT
/**
 * When the signal handler cannot be used to detect stack overflows, stack depth
 * is limited explicitly. The maximum stack depth before trapping can be
 * configured by defining this symbol before including wasm-rt when building the
 * generated c files, for example:
 *
 * ```
 *   cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o
 * ```
 */
#ifndef WASM_RT_MAX_CALL_STACK_DEPTH
#define WASM_RT_MAX_CALL_STACK_DEPTH 500
#endif

/** Current call stack depth. */
extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth;

#endif

#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE
/**
 * The segue optimization uses x86 segments to point to a linear memory. If
 * used, the runtime must query whether it can use the fast userspace wrgsbase
 * instructions or whether it must invoke syscalls to set the segment base,
 * depending on the supported CPU features. The result of this query is saved in
 * this variable.
 */
extern bool wasm_rt_fsgsbase_inst_supported;
/**
 * If fast userspace wrgsbase instructions don't exist, the runtime most provide
 * a function that invokes the OS' underlying syscall to set the segment base.
 */
void wasm_rt_syscall_set_segue_base(void* base);
/**
 * If fast userspace rdgsbase instructions don't exist, the runtime most provide
 * a function that invokes the OS' underlying syscall to get the segment base.
 */
void* wasm_rt_syscall_get_segue_base();
#endif

#if defined(_MSC_VER)
#define WASM_RT_NO_RETURN __declspec(noreturn)
#else
#define WASM_RT_NO_RETURN __attribute__((noreturn))
#endif

#if defined(__APPLE__) && WASM_RT_STACK_EXHAUSTION_HANDLER
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1
#else
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0
#endif

/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */
typedef enum {
  WASM_RT_TRAP_NONE, /** No error. */
  WASM_RT_TRAP_OOB,  /** Out-of-bounds access in linear memory or a table. */
  WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */
  WASM_RT_TRAP_DIV_BY_ZERO,  /** Integer divide by zero. */
  WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */
  WASM_RT_TRAP_UNREACHABLE,        /** Unreachable instruction executed. */
  WASM_RT_TRAP_CALL_INDIRECT,      /** Invalid call_indirect, for any reason. */
  WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */
  WASM_RT_TRAP_UNALIGNED,          /** Unaligned atomic instruction executed. */
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
  WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB,
#else
  WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */
#endif
} wasm_rt_trap_t;

/** Value types. Used to define function signatures. */
typedef enum {
  WASM_RT_I32,
  WASM_RT_I64,
  WASM_RT_F32,
  WASM_RT_F64,
  WASM_RT_V128,
  WASM_RT_FUNCREF,
  WASM_RT_EXTERNREF,
} wasm_rt_type_t;

/**
 * A generic function pointer type, both for Wasm functions (`code`)
 * and host functions (`hostcode`). All function pointers are stored
 * in this canonical form, but must be cast to their proper signature
 * to call.
 */
typedef void (*wasm_rt_function_ptr_t)(void);

/**
 * A pointer to a "tail-callee" function, called by a tail-call
 * trampoline or by another tail-callee function. (The definition uses a
 * single-member struct to allow a recursive definition.)
 */
typedef struct wasm_rt_tailcallee_t {
  void (*fn)(void** instance_ptr,
             void* tail_call_stack,
             struct wasm_rt_tailcallee_t* next);
} wasm_rt_tailcallee_t;

/**
 * The type of a function (an arbitrary number of param and result types).
 * This is represented as an opaque 256-bit ID.
 */
typedef const char* wasm_rt_func_type_t;

/**
 * A function instance (the runtime representation of a function).
 * These can be stored in tables of type funcref, or used as values.
 */
typedef struct {
  /** The function's type. */
  wasm_rt_func_type_t func_type;
  /**
   * The function. The embedder must know the actual C signature of the function
   * and cast to it before calling.
   */
  wasm_rt_function_ptr_t func;
  /** An alternate version of the function to be used when tail-called. */
  wasm_rt_tailcallee_t func_tailcallee;
  /**
   * A function instance is a closure of the function over an instance
   * of the originating module. The module_instance element will be passed into
   * the function at runtime.
   */
  void* module_instance;
} wasm_rt_funcref_t;

/** Default (null) value of a funcref */
#define wasm_rt_funcref_null_value \
  ((wasm_rt_funcref_t){NULL, NULL, {NULL}, NULL})

/** The type of an external reference (opaque to WebAssembly). */
typedef void* wasm_rt_externref_t;

/** Default (null) value of an externref */
#define wasm_rt_externref_null_value ((wasm_rt_externref_t){NULL})

/** A Memory object. */
typedef struct {
  /** The linear memory data, with a byte length of `size`. */
  uint8_t* data;
  /** The current page count for this Memory object. */
  uint64_t pages;
  /**
   * The maximum page count for this Memory object. If there is no maximum,
   * `max_pages` is 0xffffffffu (i.e. UINT32_MAX).
   */
  uint64_t max_pages;
  /** The current size of the linear memory, in bytes. */
  uint64_t size;
  /** Is this memory indexed by u64 (as opposed to default u32) */
  bool is64;
} wasm_rt_memory_t;

#ifdef WASM_RT_C11_AVAILABLE
/** A shared Memory object. */
typedef struct {
  /**
   * The linear memory data, with a byte length of `size`. The memory is marked
   * atomic as it is shared and may have to be accessed with different memory
   * orders --- sequential when being accessed atomically, relaxed otherwise.
   * Unfortunately, the C standard does not state what happens if there are
   * overlaps in two memory accesses which have a memory order, e.g., an
   * atomic32 being read from the same location an atomic64 is read. One way to
   * prevent optimizations from assuming non-overlapping behavior as typically
   * done in C is to mark the memory as volatile. Thus the memory is atomic and
   * volatile.
   */
  _Atomic volatile uint8_t* data;
  /** The current page count for this Memory object. */
  uint64_t pages;
  /**
   * The maximum page count for this Memory object. If there is no maximum,
   * `max_pages` is 0xffffffffu (i.e. UINT32_MAX).
   */
  uint64_t max_pages;
  /** The current size of the linear memory, in bytes. */
  uint64_t size;
  /** Is this memory indexed by u64 (as opposed to default u32) */
  bool is64;
  /** Lock used to ensure operations such as memory grow are threadsafe */
  WASM_RT_MUTEX mem_lock;
} wasm_rt_shared_memory_t;
#endif

/** A Table of type funcref. */
typedef struct {
  /** The table element data, with an element count of `size`. */
  wasm_rt_funcref_t* data;
  /**
   * The maximum element count of this Table object. If there is no maximum,
   * `max_size` is 0xffffffffu (i.e. UINT32_MAX).
   */
  uint32_t max_size;
  /** The current element count of the table. */
  uint32_t size;
} wasm_rt_funcref_table_t;

/** A Table of type externref. */
typedef struct {
  /** The table element data, with an element count of `size`. */
  wasm_rt_externref_t* data;
  /**
   * The maximum element count of this Table object. If there is no maximum,
   * `max_size` is 0xffffffffu (i.e. UINT32_MAX).
   */
  uint32_t max_size;
  /** The current element count of the table. */
  uint32_t size;
} wasm_rt_externref_table_t;

/** Initialize the runtime. */
void wasm_rt_init(void);

/** Is the runtime initialized? */
bool wasm_rt_is_initialized(void);

/** Free the runtime's state. */
void wasm_rt_free(void);

/*
 * Initialize the multithreaded runtime for a given thread. Must be
 * called by each thread (other than the one that called wasm_rt_init)
 * before initializing a Wasm module or calling an exported
 * function.
 */
void wasm_rt_init_thread(void);

/*
 * Free the individual thread's state.
 */
void wasm_rt_free_thread(void);

/** A hardened jmp_buf that allows checking for initialization before use */
typedef struct {
  /** Is the jmp buf intialized? */
  bool initialized;
  /** jmp_buf contents */
  jmp_buf buffer;
} wasm_rt_jmp_buf;

#ifndef _WIN32
#define WASM_RT_SETJMP_SETBUF(buf) sigsetjmp(buf, 1)
#else
#define WASM_RT_SETJMP_SETBUF(buf) setjmp(buf)
#endif

#define WASM_RT_SETJMP(buf) \
  ((buf).initialized = true, WASM_RT_SETJMP_SETBUF((buf).buffer))

#ifndef _WIN32
#define WASM_RT_LONGJMP_UNCHECKED(buf, val) siglongjmp(buf, val)
#else
#define WASM_RT_LONGJMP_UNCHECKED(buf, val) longjmp(buf, val)
#endif

#define WASM_RT_LONGJMP(buf, val)                                   \
  /** Abort on failure as this may be called in the trap handler */ \
  if (!((buf).initialized))                                         \
    abort();                                                        \
  (buf).initialized = false;                                        \
  WASM_RT_LONGJMP_UNCHECKED((buf).buffer, val)

/**
 * Stop execution immediately and jump back to the call to `wasm_rt_impl_try`.
 * The result of `wasm_rt_impl_try` will be the provided trap reason.
 *
 * This is typically called by the generated code, and not the embedder.
 */
WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t);

/** Return a human readable error string based on a trap type. */
const char* wasm_rt_strerror(wasm_rt_trap_t trap);

#define wasm_rt_try(target) WASM_RT_SETJMP(target)

/**
 * Initialize a Memory object with an initial page size of `initial_pages` and
 * a maximum page size of `max_pages`, indexed with an i32 or i64.
 *
 *  ```
 *    wasm_rt_memory_t my_memory;
 *    // 1 initial page (65536 bytes), and a maximum of 2 pages,
 *    // indexed with an i32
 *    wasm_rt_allocate_memory(&my_memory, 1, 2, false);
 *  ```
 */
void wasm_rt_allocate_memory(wasm_rt_memory_t*,
                             uint64_t initial_pages,
                             uint64_t max_pages,
                             bool is64);

/**
 * Grow a Memory object by `pages`, and return the previous page count. If
 * this new page count is greater than the maximum page count, the grow fails
 * and 0xffffffffu (UINT32_MAX) is returned instead.
 *
 *  ```
 *    wasm_rt_memory_t my_memory;
 *    ...
 *    // Grow memory by 10 pages.
 *    uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10);
 *    if (old_page_size == UINT32_MAX) {
 *      // Failed to grow memory.
 *    }
 *  ```
 */
uint64_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint64_t pages);

/** Free a Memory object. */
void wasm_rt_free_memory(wasm_rt_memory_t*);

#ifdef WASM_RT_C11_AVAILABLE
/** Shared memory version of wasm_rt_allocate_memory */
void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*,
                                    uint64_t initial_pages,
                                    uint64_t max_pages,
                                    bool is64);

/** Shared memory version of wasm_rt_grow_memory */
uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages);

/** Shared memory version of wasm_rt_free_memory */
void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*);
#endif

/**
 * Initialize a funcref Table object with an element count of `elements` and a
 * maximum size of `max_elements`.
 *
 *  ```
 *    wasm_rt_funcref_table_t my_table;
 *    // 5 elements and a maximum of 10 elements.
 *    wasm_rt_allocate_funcref_table(&my_table, 5, 10);
 *  ```
 */
void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*,
                                    uint32_t elements,
                                    uint32_t max_elements);

/** Free a funcref Table object. */
void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*);

/**
 * Initialize an externref Table object with an element count
 * of `elements` and a maximum size of `max_elements`.
 * Usage as per wasm_rt_allocate_funcref_table.
 */
void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*,
                                      uint32_t elements,
                                      uint32_t max_elements);

/** Free an externref Table object. */
void wasm_rt_free_externref_table(wasm_rt_externref_table_t*);

/**
 * Grow a Table object by `delta` elements (giving the new elements the value
 * `init`), and return the previous element count. If this new element count is
 * greater than the maximum element count, the grow fails and 0xffffffffu
 * (UINT32_MAX) is returned instead.
 */
uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*,
                                    uint32_t delta,
                                    wasm_rt_funcref_t init);
uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*,
                                      uint32_t delta,
                                      wasm_rt_externref_t init);

#ifdef __cplusplus
}
#endif

#endif /* WASM_RT_H_ */