runtime_wasm.mx raw

   1  //go:build js && wasm
   2  
   3  package runtime
   4  
   5  import "unsafe"
   6  
   7  // putchar writes a single byte to stdout via the JS console.
   8  //
   9  //go:wasm-module wasi_snapshot_preview1
  10  //export fd_write
  11  func fd_write(fd int32, iovs unsafe.Pointer, iovs_len int32, nwritten unsafe.Pointer) int32
  12  
  13  func putchar(c byte) {
  14  	buf := [1]byte{c}
  15  	iov := [2]uintptr{uintptr(unsafe.Pointer(&buf[0])), 1}
  16  	var nw int32
  17  	fd_write(1, unsafe.Pointer(&iov[0]), 1, unsafe.Pointer(&nw))
  18  }
  19  
  20  // abort terminates the program.
  21  //
  22  //export abort
  23  func abort() {
  24  	trap()
  25  }
  26  
  27  // hardwareRand returns a random uint64. On WASM/JS, we use a simple
  28  // xorshift fallback since there's no hardware RNG instruction.
  29  var randState uint64 = 0x853c49e6748fea9b
  30  
  31  func hardwareRand() (n uint64, ok bool) {
  32  	// xorshift64*
  33  	randState ^= randState >> 12
  34  	randState ^= randState << 25
  35  	randState ^= randState >> 27
  36  	return randState * 0x2545F4914F6CDD1D, true
  37  }
  38  
  39  // Heap management for WASM linear memory.
  40  var heapStart, heapEnd uintptr
  41  
  42  // wasm_memory_size returns the current memory size in pages (64KB each).
  43  //
  44  //go:linkname wasm_memory_size llvm.wasm.memory.size.i32
  45  func wasm_memory_size(index int32) int32
  46  
  47  // wasm_memory_grow grows memory by delta pages. Returns previous size or -1.
  48  //
  49  //go:linkname wasm_memory_grow llvm.wasm.memory.grow.i32
  50  func wasm_memory_grow(index int32, delta int32) int32
  51  
  52  const wasmPageSize = 64 * 1024
  53  
  54  // allocateHeap initializes the WASM heap from linear memory.
  55  func allocateHeap() {
  56  	pages := wasm_memory_size(0)
  57  	heapStart = uintptr(pages) * wasmPageSize
  58  	// Start with 8 pages (512KB)
  59  	if wasm_memory_grow(0, 8) == -1 {
  60  		runtimePanic("cannot allocate heap memory")
  61  	}
  62  	heapEnd = heapStart + 8*wasmPageSize
  63  }
  64  
  65  func growHeap() bool {
  66  	oldPages := wasm_memory_grow(0, 8)
  67  	if oldPages == -1 {
  68  		return false
  69  	}
  70  	heapEnd = uintptr(oldPages+8) * wasmPageSize
  71  	return true
  72  }
  73  
  74  // globalsStart/globalsEnd are linker-provided symbols for GC scanning.
  75  //
  76  //go:extern __data_start
  77  var globalsStart [0]byte
  78  
  79  //go:extern __data_end
  80  var globalsEnd [0]byte
  81  
  82  // Timing functions for WASM.
  83  var wasmTickOffset int64
  84  
  85  func ticks() timeUnit {
  86  	return timeUnit(monotime())
  87  }
  88  
  89  //go:wasm-module wasi_snapshot_preview1
  90  //export clock_time_get
  91  func clock_time_get(clockID int32, precision int64, time unsafe.Pointer) int32
  92  
  93  func monotime() int64 {
  94  	var t int64
  95  	clock_time_get(1, 0, unsafe.Pointer(&t)) // CLOCK_MONOTONIC = 1
  96  	return t
  97  }
  98  
  99  func nanosecondsToTicks(ns int64) timeUnit {
 100  	return timeUnit(ns)
 101  }
 102  
 103  func ticksToNanoseconds(t timeUnit) int64 {
 104  	return int64(t)
 105  }
 106  
 107  func sleepTicks(d timeUnit) {
 108  	// No-op on WASM — cooperative scheduling handles yields.
 109  }
 110  
 111  func now() (int64, int32, int64) {
 112  	var t int64
 113  	clock_time_get(0, 0, unsafe.Pointer(&t)) // CLOCK_REALTIME = 0
 114  	sec := t / 1000000000
 115  	nsec := int32(t % 1000000000)
 116  	mono := monotime()
 117  	return sec, nsec, mono
 118  }
 119  
 120  // libc_errno_location is not available on WASM.
 121  func libc_errno_location() *int32 {
 122  	return &wasmErrno
 123  }
 124  
 125  var wasmErrno int32
 126  
 127  // savedStackPointer is used by the GC to save the stack pointer.
 128  var savedStackPointer uintptr
 129  
 130  // _start is the WASI entry point for WASM executables.
 131  // WASM is single-threaded, so we bypass the scheduler and call main directly.
 132  //
 133  //export _start
 134  func _start() {
 135  	// Initialize heap from linear memory.
 136  	allocateHeap()
 137  	initRand()
 138  	initHeap()
 139  	initAll()
 140  	callMain()
 141  }
 142