//go:build tinygo.wasm

package runtime

import (
	"unsafe"
)

const GOARCH = "wasm"

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

// zeroSizedAlloc a sentinel that gets returned when allocating 0 bytes.
// Using this instead of a constant value since I can't easily find a memory
// location that is definitely not going to end up as a valid pointer.
var zeroSizedAlloc uint8

var zeroSizeAllocPtr = &zeroSizedAlloc

const deferExtraRegs = 0

const callInstSize = 1 // unknown and irrelevant (llvm.returnaddress doesn't work), so make something up

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

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

const (
	// wasmMemoryIndex is always zero until the multi-memory feature is used.
	//
	// See https://github.com/WebAssembly/multi-memory
	wasmMemoryIndex = 0

	// wasmPageSize is the size of a page in WebAssembly's 32-bit memory. This
	// is also its only unit of change.
	//
	// See https://www.w3.org/TR/wasm-core-1/#page-size
	wasmPageSize = 64 * 1024
)

// wasm_memory_size invokes the "memory.size" instruction, which returns the
// current size to the memory at the given index (always wasmMemoryIndex), in
// pages.
//
//export llvm.wasm.memory.size.i32
func wasm_memory_size(index int32) int32

// wasm_memory_grow invokes the "memory.grow" instruction, which attempts to
// increase the size of the memory at the given index (always wasmMemoryIndex),
// by the delta (in pages). This returns the previous size on success of -1 on
// failure.
//
//export llvm.wasm.memory.grow.i32
func wasm_memory_grow(index int32, delta int32) int32

var (
	// heapStart is the current memory offset which starts the heap. The heap
	// extends from this offset until heapEnd (exclusive).
	heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))

	// heapEnd is the current memory length in bytes.
	heapEnd = uintptr(wasm_memory_size(wasmMemoryIndex) * wasmPageSize)

	globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol))
	globalsEnd   = uintptr(unsafe.Pointer(&heapStartSymbol))

	stackTop = uintptr(unsafe.Pointer(&globalsStartSymbol))
)

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)
}

//export tinygo_getCurrentStackPointer
func getCurrentStackPointer() uintptr

// savedStackPointer is used to save the system stack pointer while in a goroutine.
var savedStackPointer uintptr

// saveStackPointer is called by internal/task.state.Resume() to save the stack pointer before entering a goroutine from the system stack.
func saveStackPointer() {
	savedStackPointer = getCurrentStackPointer()
}

// 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(wasmMemoryIndex)
	result := wasm_memory_grow(wasmMemoryIndex, memorySize)
	if result == -1 {
		// Grow failed.
		return false
	}

	setHeapEnd(uintptr(wasm_memory_size(wasmMemoryIndex) * wasmPageSize))

	// Heap has grown successfully.
	return true
}
