.globl emscripten_stack_init
.globl emscripten_stack_set_limits
.globl emscripten_stack_get_free
.globl emscripten_stack_get_base
.globl emscripten_stack_get_end

#ifdef __wasm64__
#define PTR i64
#define ALIGN 3
#define PTRSTORE .int64
#else
#define PTR i32
#define ALIGN 2
#define PTRSTORE .int32
#endif

.globaltype __stack_pointer, PTR

# TODO(sbc): It would be nice if these we initialized directly
# using PTR.const rather than using the `emscripten_stack_init`
.globaltype __stack_end, PTR
__stack_end:
.globaltype __stack_base, PTR
__stack_base:

emscripten_stack_get_base:
  .functype emscripten_stack_get_base () -> (PTR)
  global.get __stack_base
  end_function

emscripten_stack_get_end:
  .functype emscripten_stack_get_end () -> (PTR)
  global.get __stack_end
  end_function

emscripten_stack_init:
  # Initialize __stack_end and __stack_base.
  # This must be called before emscripten_stack_get_end,
  # emscripten_stack_get_base, or emscripten_stack_get_free are called
  .functype emscripten_stack_init () -> ()

  # What llvm calls __stack_high is the high address from where is grows
  # downwards.  We call this the stack base here in emscripten.
#ifdef __PIC__
  global.get __stack_high@GOT
#else
  PTR.const __stack_high
#endif
  global.set __stack_base

  # What llvm calls __stack_low is that end of the stack
#ifdef __PIC__
  global.get __stack_low@GOT
#else
  PTR.const __stack_low
#endif
  # Align up to 16 bytes
  PTR.const 0xf
  PTR.add
  PTR.const -0x10
  PTR.and
  global.set __stack_end

  end_function

emscripten_stack_set_limits:
  .functype emscripten_stack_set_limits (PTR, PTR) -> ()
  local.get 0
  global.set __stack_base
  local.get 1
  global.set __stack_end
  end_function

emscripten_stack_get_free:
  .functype emscripten_stack_get_free () -> (PTR)
  global.get __stack_pointer
  global.get __stack_end
  PTR.sub
  end_function

#ifdef __EMSCRIPTEN_WASM_WORKERS__
# TODO: Relocate the following to its own file wasm_worker.S, but need to figure out how to reference
# __stack_base and __stack_end globals from a separate file as externs in order for that to work.
.globl emscripten_wasm_worker_initialize
emscripten_wasm_worker_initialize:
  .functype emscripten_wasm_worker_initialize (PTR /*stackLowestAddress*/, i32 /*stackSize*/) -> ()

  // __stack_end = stackLowestAddress + (__builtin_wasm_tls_size() + 15) & -16;
  local.get 0
  .globaltype __tls_size, PTR, immutable
  global.get __tls_size
  PTR.add
  PTR.const 0xf
  PTR.add
  PTR.const -0x10
  PTR.and
  global.set __stack_end

  // __stack_base = stackLowestAddress + stackSize;
  local.get 0
  local.get 1
#ifdef __wasm64__
  i64.extend_i32_u
#endif
  PTR.add
  global.set __stack_base

// TODO: We'd like to do this here to avoid JS side calls to __set_stack_limits.
//       (or even better, we'd like to avoid duplicate versions of the stack variables)
// See https://github.com/emscripten-core/emscripten/issues/16496
//  global.get __stack_base
//  global.get __stack_end
//  .functype __set_stack_limits (PTR, PTR) -> ()
//  call __set_stack_limits

  // __wasm_init_tls(stackLowestAddress);
  local.get 0
  .functype __wasm_init_tls (PTR) -> ()
  call __wasm_init_tls

  // N.b. The function __wasm_init_tls above does not need
  // __stack_pointer initialized, and it destroys the value it was set to.
  // So we must initialize __stack_pointer only *after* completing __wasm_init_tls:

  // __stack_pointer = __stack_base;
  global.get __stack_base
  global.set __stack_pointer

  end_function
#endif

# Add emscripten_stack_init to static ctors
.section .init_array.1,"",@
.p2align ALIGN
PTRSTORE emscripten_stack_init
