// +build tinygo.wasm

package runtime

import (
	"unsafe"
)

const GOARCH = "wasm"

// The bitness of the CPU (e.g. 8, 32, 64).
const TargetBits = 32

//go:extern __heap_base
var heapStartSymbol [0]byte

//go:extern __global_base
var globalsStartSymbol [0]byte

//export llvm.wasm.memory.size.i32
func wasm_memory_size(index int32) int32

//export llvm.wasm.memory.grow.i32
func wasm_memory_grow(index int32, delta int32) int32

var (
	heapStart    = uintptr(unsafe.Pointer(&heapStartSymbol))
	heapEnd      = uintptr(wasm_memory_size(0) * wasmPageSize)
	globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol))
	globalsEnd   = uintptr(unsafe.Pointer(&heapStartSymbol))
)

const wasmPageSize = 64 * 1024

func align(ptr uintptr) uintptr {
	// Align to 16, which is the alignment of max_align_t:
	// https://godbolt.org/z/dYqTsWrGq
	const heapAlign = 16
	return (ptr + heapAlign - 1) &^ (heapAlign - 1)
}

func getCurrentStackPointer() uintptr

// growHeap tries to grow the heap size. It returns true if it succeeds, false
// otherwise.
func growHeap() bool {
	// Grow memory by the available size, which means the heap size is doubled.
	memorySize := wasm_memory_size(0)
	result := wasm_memory_grow(0, memorySize)
	if result == -1 {
		// Grow failed.
		return false
	}

	setHeapEnd(uintptr(wasm_memory_size(0) * wasmPageSize))

	// Heap has grown successfully.
	return true
}

// The below functions override the default allocator of wasi-libc.
// Most functions are defined but unimplemented to make sure that if there is
// any code using them, they will get an error instead of (incorrectly) using
// the wasi-libc dlmalloc heap implementation instead. If they are needed by any
// program, they can certainly be implemented.

//export malloc
func libc_malloc(size uintptr) unsafe.Pointer {
	return alloc(size, nil)
}

//export free
func libc_free(ptr unsafe.Pointer) {
	free(ptr)
}

//export calloc
func libc_calloc(nmemb, size uintptr) unsafe.Pointer {
	// Note: we could be even more correct here and check that nmemb * size
	// doesn't overflow. However the current implementation should normally work
	// fine.
	return alloc(nmemb*size, nil)
}

//export realloc
func libc_realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
	return realloc(ptr, size)
}

//export posix_memalign
func libc_posix_memalign(memptr *unsafe.Pointer, alignment, size uintptr) int {
	runtimePanic("unimplemented: posix_memalign")
	return 0
}

//export aligned_alloc
func libc_aligned_alloc(alignment, bytes uintptr) unsafe.Pointer {
	runtimePanic("unimplemented: aligned_alloc")
	return nil
}

//export malloc_usable_size
func libc_malloc_usable_size(ptr unsafe.Pointer) uintptr {
	runtimePanic("unimplemented: malloc_usable_size")
	return 0
}
