2022-04-04 12:29:23 +02:00
|
|
|
# Copyright 2022 Mattia Giambirtone & All Contributors
|
|
|
|
#
|
|
|
|
# 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.
|
2022-04-28 18:06:53 +02:00
|
|
|
## The Peon runtime environment
|
2022-08-17 17:31:15 +02:00
|
|
|
{.push checks:off.} # The VM is a critical point where checks are deleterious
|
2022-08-01 10:36:06 +02:00
|
|
|
|
|
|
|
import std/monotimes
|
2022-08-17 17:31:15 +02:00
|
|
|
import std/math
|
2022-08-01 10:36:06 +02:00
|
|
|
|
2022-05-30 22:06:15 +02:00
|
|
|
|
2022-04-28 18:06:53 +02:00
|
|
|
import ../config
|
|
|
|
import ../frontend/meta/bytecode
|
2022-05-20 15:47:04 +02:00
|
|
|
import ../util/multibyte
|
2022-04-04 12:29:23 +02:00
|
|
|
|
2022-05-30 12:31:15 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
import strutils
|
2022-08-01 10:36:06 +02:00
|
|
|
when DEBUG_TRACE_VM:
|
2022-08-17 17:31:15 +02:00
|
|
|
import std/strformat
|
2022-08-01 10:36:06 +02:00
|
|
|
|
2022-07-10 13:19:57 +02:00
|
|
|
|
2022-05-18 13:32:32 +02:00
|
|
|
type
|
2022-04-28 18:06:53 +02:00
|
|
|
PeonVM* = ref object
|
2022-08-17 17:31:15 +02:00
|
|
|
## The Peon Virtual Machine.
|
|
|
|
## Note how the only data
|
|
|
|
## type we handle here is
|
|
|
|
## a 64-bit unsigned integer:
|
|
|
|
## This is to allow the use
|
|
|
|
## of unboxed primitive types.
|
|
|
|
## For more complex types, the
|
|
|
|
## value represents a pointer to
|
|
|
|
## some stack- or heap-allocated
|
|
|
|
## object. The VM has no concept
|
|
|
|
## of type by itself and it relies
|
|
|
|
## on the compiler to produce the
|
|
|
|
## correct results
|
|
|
|
ip: uint64 # Instruction pointer
|
2022-05-18 13:32:32 +02:00
|
|
|
chunk: Chunk # Piece of bytecode to execute
|
2022-08-17 17:31:15 +02:00
|
|
|
calls: seq[uint64] # Our call stack
|
|
|
|
operands: seq[uint64] # Our operand stack
|
|
|
|
cache: array[6, uint64] # Singletons cache
|
|
|
|
frames: seq[uint64] # Stores the bottom of stack frames
|
|
|
|
closedOver: seq[uint64] # Stores variables that do not have stack semantics
|
|
|
|
results: seq[uint64] # Stores function's results (return values)
|
2022-05-02 17:26:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
proc initCache*(self: PeonVM) =
|
|
|
|
## Initializes the VM's
|
|
|
|
## singletons cache
|
2022-08-17 17:31:15 +02:00
|
|
|
self.cache[0] = 0x0 # Nil
|
|
|
|
self.cache[1] = 0x1 # True
|
|
|
|
self.cache[2] = 0x2 # False
|
|
|
|
self.cache[3] = 0x3 # Positive inf
|
|
|
|
self.cache[4] = 0x4 # Negative inf
|
|
|
|
self.cache[5] = 0x5 # NaN
|
2022-04-28 18:06:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
proc newPeonVM*: PeonVM =
|
|
|
|
## Initializes a new, blank VM
|
|
|
|
## for executing Peon bytecode
|
|
|
|
new(result)
|
|
|
|
result.ip = 0
|
2022-05-23 10:49:38 +02:00
|
|
|
result.frames = @[]
|
2022-08-17 17:31:15 +02:00
|
|
|
result.calls = newSeq[uint64]()
|
|
|
|
result.operands = newSeq[uint64]()
|
2022-05-02 17:26:38 +02:00
|
|
|
result.initCache()
|
2022-05-22 17:23:52 +02:00
|
|
|
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-07-10 13:19:57 +02:00
|
|
|
# Getters for singleton types (they are cached!)
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc getNil*(self: PeonVM): uint64 {.inline.} = self.cache[2]
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc getBool*(self: PeonVM, value: bool): uint64 {.inline.} =
|
2022-04-28 18:06:53 +02:00
|
|
|
if value:
|
2022-05-02 23:19:17 +02:00
|
|
|
return self.cache[1]
|
2022-08-17 17:31:15 +02:00
|
|
|
return self.cache[0]
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc getInf*(self: PeonVM, positive: bool): uint64 {.inline.} =
|
2022-04-28 18:06:53 +02:00
|
|
|
if positive:
|
2022-05-02 23:19:17 +02:00
|
|
|
return self.cache[3]
|
|
|
|
return self.cache[4]
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc getNan*(self: PeonVM): uint64 {.inline.} = self.cache[5]
|
2022-04-28 18:06:53 +02:00
|
|
|
|
2022-08-15 19:52:06 +02:00
|
|
|
|
|
|
|
# Thanks to nim's *genius* idea of making x !> y a template
|
|
|
|
# for y < x (which by itself is fine) together with the fact
|
|
|
|
# that the order of evaluation of templates with the same
|
|
|
|
# expression is fucking stupid (see https://nim-lang.org/docs/manual.html#order-of-evaluation
|
|
|
|
# and https://github.com/nim-lang/Nim/issues/10425 and try not to
|
|
|
|
# bang your head against the nearest wall), we need a custom operator
|
|
|
|
# that preserves the natural order of evaluation
|
|
|
|
proc `!>`[T](a, b: T): auto {.inline.} =
|
|
|
|
b < a
|
|
|
|
|
|
|
|
|
|
|
|
proc `!>=`[T](a, b: T): auto {.inline, used.} =
|
|
|
|
b <= a
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
# Stack primitives. Note: all accesses to the call stack
|
|
|
|
# that go through the getc/setc wrappers is frame-relative,
|
2022-07-10 13:19:57 +02:00
|
|
|
# meaning that the index is added to the current stack frame's
|
2022-08-17 17:31:15 +02:00
|
|
|
# bottom to obtain an absolute stack index
|
|
|
|
{.push inline.}
|
|
|
|
proc push(self: PeonVM, obj: uint64) =
|
|
|
|
## Pushes a value object onto the
|
2022-06-02 01:33:56 +02:00
|
|
|
## operand stack
|
|
|
|
self.operands.add(obj)
|
2022-04-28 18:06:53 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc pop(self: PeonVM): uint64 =
|
|
|
|
## Pops a value off the
|
|
|
|
## operand stack and
|
|
|
|
## returns it
|
2022-06-02 01:33:56 +02:00
|
|
|
return self.operands.pop()
|
2022-04-28 18:06:53 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc peekb(self: PeonVM, distance: BackwardsIndex = ^1): uint64 =
|
|
|
|
## Returns the value at the
|
|
|
|
## given (backwards) distance from the top of
|
|
|
|
## the operand stack without consuming it
|
|
|
|
return self.operands[distance]
|
|
|
|
|
|
|
|
|
|
|
|
proc peek(self: PeonVM, distance: int = 0): uint64 =
|
|
|
|
## Returns the value at the
|
2022-06-02 01:33:56 +02:00
|
|
|
## given distance from the top of
|
|
|
|
## the operand stack without consuming it
|
2022-08-17 17:31:15 +02:00
|
|
|
if distance < 0:
|
|
|
|
return self.peekb(^(-distance))
|
2022-06-02 01:33:56 +02:00
|
|
|
return self.operands[self.operands.high() + distance]
|
2022-05-07 10:48:01 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
|
|
|
|
proc pushc(self: PeonVM, val: uint64) =
|
|
|
|
## Pushes a value to the
|
2022-06-02 01:33:56 +02:00
|
|
|
## call stack
|
|
|
|
self.calls.add(val)
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc popc(self: PeonVM): uint64 =
|
|
|
|
## Pops a value off the call
|
2022-06-02 01:33:56 +02:00
|
|
|
## stack and returns it
|
|
|
|
return self.calls.pop()
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} =
|
|
|
|
## Returns the value at the
|
2022-06-02 01:33:56 +02:00
|
|
|
## given distance from the top of
|
|
|
|
## the call stack without consuming it
|
|
|
|
return self.calls[self.calls.high() + distance]
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc getc(self: PeonVM, idx: uint): uint64 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Accessor method that abstracts
|
|
|
|
## indexing our call stack through stack
|
|
|
|
## frames
|
|
|
|
return self.calls[idx + self.frames[^1]]
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc setc(self: PeonVM, idx: uint, val: uint64) =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Setter method that abstracts
|
|
|
|
## indexing our call stack through stack
|
|
|
|
## frames
|
|
|
|
self.calls[idx + self.frames[^1]] = val
|
|
|
|
|
2022-07-10 13:19:57 +02:00
|
|
|
# Byte-level primitives to read and decode
|
|
|
|
# bytecode
|
2022-05-30 22:06:15 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc readByte(self: PeonVM): uint8 =
|
2022-05-02 17:26:38 +02:00
|
|
|
## Reads a single byte from the
|
|
|
|
## bytecode and returns it as an
|
|
|
|
## unsigned 8 bit integer
|
2022-04-28 18:06:53 +02:00
|
|
|
inc(self.ip)
|
2022-05-02 17:26:38 +02:00
|
|
|
return self.chunk.code[self.ip - 1]
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc readShort(self: PeonVM): uint16 =
|
2022-05-02 17:26:38 +02:00
|
|
|
## Reads two bytes from the
|
|
|
|
## bytecode and returns them
|
|
|
|
## as an unsigned 16 bit
|
|
|
|
## integer
|
2022-05-20 15:47:04 +02:00
|
|
|
return [self.readByte(), self.readByte()].fromDouble()
|
2022-05-02 17:26:38 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc readLong(self: PeonVM): uint32 =
|
2022-05-02 17:26:38 +02:00
|
|
|
## Reads three bytes from the
|
|
|
|
## bytecode and returns them
|
|
|
|
## as an unsigned 32 bit
|
|
|
|
## integer. Note however that
|
|
|
|
## the boundary is capped at
|
|
|
|
## 24 bits instead of 32
|
2022-05-20 15:47:04 +02:00
|
|
|
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
2022-05-02 17:26:38 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc readUInt(self: PeonVM): uint32 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads three bytes from the
|
|
|
|
## bytecode and returns them
|
|
|
|
## as an unsigned 32 bit
|
|
|
|
## integer
|
|
|
|
return uint32([self.readByte(), self.readByte(), self.readByte(), self.readByte()].fromQuad())
|
|
|
|
|
|
|
|
|
2022-07-10 13:19:57 +02:00
|
|
|
# Functions to read primitives from the chunk's
|
|
|
|
# constants table
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadInt64(self: PeonVM, idx: int): int64 =
|
2022-05-07 10:48:01 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an int64
|
2022-05-20 15:47:04 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
2022-05-22 17:23:52 +02:00
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
|
|
|
|
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
|
|
|
|
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
|
|
|
|
]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-05-02 23:19:17 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadUInt64(self: PeonVM, idx: int): uint64 =
|
2022-05-02 17:26:38 +02:00
|
|
|
## Reads a constant from the
|
2022-05-02 23:19:17 +02:00
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an uint64
|
2022-05-20 15:47:04 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
2022-05-22 17:23:52 +02:00
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
|
|
|
|
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
|
|
|
|
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
|
|
|
|
]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-04-28 18:06:53 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadUInt32(self: PeonVM, idx: int): uint32 =
|
2022-05-22 17:23:52 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an int32
|
2022-05-22 17:23:52 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-05-22 17:23:52 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadInt32(self: PeonVM, idx: int): int32 =
|
2022-05-23 14:03:17 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an uint32
|
2022-05-23 14:03:17 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-05-23 14:03:17 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadInt16(self: PeonVM, idx: int): int16 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an int16
|
2022-06-02 01:33:56 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadUInt16(self: PeonVM, idx: int): uint16 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an uint16
|
2022-06-02 01:33:56 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadInt8(self: PeonVM, idx: int): int8 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an int8
|
|
|
|
result = int8(self.chunk.consts[idx])
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadUInt8(self: PeonVM, idx: int): uint8 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as an uint8
|
|
|
|
result = self.chunk.consts[idx]
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadFloat32(self: PeonVM, idx: int): float32 =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as a float32
|
2022-06-02 01:33:56 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc constReadFloat64(self: PeonVM, idx: int): float =
|
2022-06-02 01:33:56 +02:00
|
|
|
## Reads a constant from the
|
|
|
|
## chunk's constant table and
|
2022-08-17 17:31:15 +02:00
|
|
|
## returns it as a float
|
2022-06-02 01:33:56 +02:00
|
|
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
|
|
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
|
|
|
|
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
|
|
|
|
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7]]
|
2022-08-17 17:31:15 +02:00
|
|
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
|
|
|
|
|
|
|
{.pop.}
|
2022-06-02 01:33:56 +02:00
|
|
|
|
|
|
|
|
2022-04-28 18:06:53 +02:00
|
|
|
proc dispatch*(self: PeonVM) =
|
|
|
|
## Main bytecode dispatch loop
|
2022-06-02 01:33:56 +02:00
|
|
|
var instruction {.register.}: OpCode
|
2022-04-28 18:06:53 +02:00
|
|
|
while true:
|
2022-07-10 13:19:57 +02:00
|
|
|
{.computedgoto.} # https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
2022-05-22 17:23:52 +02:00
|
|
|
when DEBUG_TRACE_VM:
|
2022-05-23 10:49:38 +02:00
|
|
|
echo &"IP: {self.ip}"
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Instruction: {OpCode(self.chunk.code[self.ip])}"
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.calls.len() !> 0:
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Call Stack: {self.calls}"
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.operands.len() !> 0:
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Operand Stack: {self.operands}"
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.frames.len() !> 0:
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Current Frame: {self.calls[self.frames[^1]..^1]}"
|
|
|
|
echo &"Frames: {self.frames}"
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.closedOver.len() !> 0:
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Closure Array: {self.closedOver}"
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.results.len() !> 0:
|
2022-06-02 01:33:56 +02:00
|
|
|
echo &"Results: {self.results}"
|
2022-05-22 17:23:52 +02:00
|
|
|
discard readLine stdin
|
2022-06-02 01:33:56 +02:00
|
|
|
instruction = OpCode(self.readByte())
|
2022-04-28 18:06:53 +02:00
|
|
|
case instruction:
|
2022-07-10 13:19:57 +02:00
|
|
|
# Constant loading instructions
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadTrue:
|
2022-04-28 18:06:53 +02:00
|
|
|
self.push(self.getBool(true))
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadFalse:
|
2022-04-28 18:06:53 +02:00
|
|
|
self.push(self.getBool(false))
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadNan:
|
2022-04-28 18:06:53 +02:00
|
|
|
self.push(self.getNan())
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadNil:
|
2022-04-28 18:06:53 +02:00
|
|
|
self.push(self.getNil())
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadInf:
|
2022-04-28 18:06:53 +02:00
|
|
|
self.push(self.getInf(true))
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadInt64:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadInt64(int(self.readLong()))))
|
2022-05-07 10:48:01 +02:00
|
|
|
of LoadUInt64:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadUInt64(int(self.readLong()))))
|
2022-05-22 17:23:52 +02:00
|
|
|
of LoadUInt32:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadUInt32(int(self.readLong()))))
|
2022-05-30 22:06:15 +02:00
|
|
|
of LoadInt32:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadInt32(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadInt16:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadInt16(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadUInt16:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadUInt16(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadInt8:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadInt8(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadUInt8:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.constReadUInt8(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadString:
|
2022-08-17 17:31:15 +02:00
|
|
|
# TODO: Use constReadString with own memory manager
|
|
|
|
# Strings are broken rn!!
|
|
|
|
let size = int(self.readLong())
|
|
|
|
let idx = int(self.readLong())
|
|
|
|
var str = self.chunk.consts[idx..<idx + size].fromBytes()
|
|
|
|
self.push(cast[uint64](str.addr))
|
|
|
|
# We cast instead of converting because, unlike with integers,
|
|
|
|
# we don't want nim to touch any of the bits of the underlying
|
|
|
|
# value!
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadFloat32:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadFloat64:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(cast[uint64](self.constReadFloat64(int(self.readLong()))))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadFunction:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Loads a function address onto the operand stack
|
|
|
|
self.push(uint64(self.readLong()))
|
2022-06-02 01:33:56 +02:00
|
|
|
of LoadReturnAddress:
|
2022-07-10 13:19:57 +02:00
|
|
|
# Loads a 32-bit unsigned integer onto the operand stack.
|
|
|
|
# Used to load function return addresses
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(uint64(self.readUInt()))
|
2022-05-23 10:49:38 +02:00
|
|
|
of Call:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Calls a peon function. The calling convention here
|
|
|
|
# is pretty simple: the first value in the frame is
|
|
|
|
# the new instruction pointer to jump to, then a
|
|
|
|
# 32-bit return address follows. After that, all
|
|
|
|
# arguments and locals follow. Note that, due to
|
|
|
|
# how the stack works, all arguments before the call
|
|
|
|
# are in the reverse order in which they are passed
|
|
|
|
# to the function
|
2022-07-09 12:47:53 +02:00
|
|
|
var argc {.used.} = self.readLong().int
|
2022-08-17 17:31:15 +02:00
|
|
|
let retAddr = self.peek(-argc - 1) # Return address
|
|
|
|
let jmpAddr = self.peek(-argc - 2) # Function address
|
|
|
|
self.ip = jmpAddr
|
|
|
|
self.pushc(jmpAddr)
|
2022-07-09 12:47:53 +02:00
|
|
|
self.pushc(retAddr)
|
|
|
|
# Creates a new result slot for the
|
|
|
|
# function's return value
|
2022-06-02 01:33:56 +02:00
|
|
|
self.results.add(self.getNil())
|
2022-07-09 12:47:53 +02:00
|
|
|
# Creates a new call frame
|
2022-08-17 17:31:15 +02:00
|
|
|
self.frames.add(uint64(self.calls.len() - 2))
|
2022-07-09 12:47:53 +02:00
|
|
|
# Loads the arguments onto the stack
|
|
|
|
for _ in 0..<argc:
|
|
|
|
self.pushc(self.pop())
|
2022-08-17 17:31:15 +02:00
|
|
|
# Pops the function and return address
|
|
|
|
# off the operand stack since they're
|
|
|
|
# not needed there anymore
|
2022-07-09 12:47:53 +02:00
|
|
|
discard self.pop()
|
|
|
|
discard self.pop()
|
|
|
|
# TODO: Use the frame's initial size once
|
2022-06-02 01:33:56 +02:00
|
|
|
# we have more control over the
|
|
|
|
# memory
|
2022-08-15 19:52:06 +02:00
|
|
|
#[while argc !> 0:
|
2022-07-09 12:47:53 +02:00
|
|
|
dec(argc)
|
2022-06-02 01:33:56 +02:00
|
|
|
self.pushc(self.getNil())
|
|
|
|
]#
|
2022-07-09 12:47:53 +02:00
|
|
|
of Return:
|
2022-06-02 01:33:56 +02:00
|
|
|
# Returns from a function.
|
|
|
|
# Every peon program is wrapped
|
|
|
|
# in a hidden function, so this
|
|
|
|
# will also exit the VM if we're
|
|
|
|
# at the end of the program
|
2022-08-17 17:31:15 +02:00
|
|
|
while self.calls.len().uint64 !> self.frames[^1] + 2'u64:
|
2022-08-01 10:36:06 +02:00
|
|
|
# Discards the function's local variables,
|
|
|
|
# if there is any
|
|
|
|
discard self.popc()
|
|
|
|
let ret = self.popc() # Return address
|
2022-08-17 17:31:15 +02:00
|
|
|
discard self.popc() # Function address
|
2022-06-02 01:33:56 +02:00
|
|
|
if self.readByte() == 1:
|
|
|
|
# Function is non-void!
|
|
|
|
self.push(self.results.pop())
|
|
|
|
else:
|
|
|
|
discard self.results.pop()
|
2022-08-01 10:36:06 +02:00
|
|
|
# Discard the topmost stack frame
|
2022-05-30 22:06:15 +02:00
|
|
|
discard self.frames.pop()
|
2022-06-02 01:33:56 +02:00
|
|
|
if self.frames.len() == 0:
|
|
|
|
# End of the program!
|
|
|
|
return
|
|
|
|
self.ip = ret.uInt
|
|
|
|
of SetResult:
|
|
|
|
# Sets the result of the
|
2022-07-10 13:19:57 +02:00
|
|
|
# current function. A Return
|
|
|
|
# instruction will pop this
|
|
|
|
# off the results array and
|
|
|
|
# onto the operand stack when
|
|
|
|
# the current function exits.
|
2022-06-02 01:33:56 +02:00
|
|
|
self.results[self.frames.high()] = self.pop()
|
2022-05-23 14:03:17 +02:00
|
|
|
of StoreVar:
|
2022-06-02 01:33:56 +02:00
|
|
|
# Stores the value at the top of the operand stack
|
|
|
|
# into the given call stack index
|
2022-08-17 17:31:15 +02:00
|
|
|
let idx = self.readLong()
|
|
|
|
when DEBUG_TRACE_VM:
|
|
|
|
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
|
|
|
if idx + self.frames[^1] <= self.calls.high().uint:
|
2022-06-02 01:33:56 +02:00
|
|
|
self.setc(idx, self.pop())
|
|
|
|
else:
|
2022-07-09 12:47:53 +02:00
|
|
|
self.pushc(self.pop())
|
2022-05-30 22:06:15 +02:00
|
|
|
of StoreClosure:
|
2022-05-27 14:01:57 +02:00
|
|
|
# Stores/updates the value of a closed-over
|
|
|
|
# variable
|
|
|
|
let idx = self.readLong().int
|
2022-08-15 19:52:06 +02:00
|
|
|
if idx !> self.closedOver.high():
|
2022-06-13 17:28:05 +02:00
|
|
|
# Note: we *peek* the stack, but we
|
|
|
|
# don't pop!
|
|
|
|
self.closedOver.add(self.peek())
|
2022-05-27 14:01:57 +02:00
|
|
|
else:
|
2022-06-13 17:28:05 +02:00
|
|
|
self.closedOver[idx] = self.peek()
|
2022-05-30 22:06:15 +02:00
|
|
|
of LoadClosure:
|
|
|
|
# Loads a closed-over variable onto the
|
|
|
|
# stack
|
2022-06-02 01:33:56 +02:00
|
|
|
self.push(self.closedOver[self.readLong()])
|
2022-05-23 14:03:17 +02:00
|
|
|
of LoadVar:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Pushes a variable onto the operand
|
|
|
|
# stack
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(self.getc(self.readLong()))
|
2022-05-07 10:48:01 +02:00
|
|
|
of NoOp:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Does nothing
|
2022-04-29 23:04:53 +02:00
|
|
|
continue
|
2022-06-02 01:33:56 +02:00
|
|
|
of PopC:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Pops a value off the call stack
|
2022-06-02 01:33:56 +02:00
|
|
|
discard self.popc()
|
2022-05-07 10:48:01 +02:00
|
|
|
of Pop:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Pops a value off the operand stack
|
2022-05-30 22:06:15 +02:00
|
|
|
discard self.pop()
|
2022-06-13 15:04:53 +02:00
|
|
|
of PushC:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Pushes a value from the operand stack
|
|
|
|
# onto the call stack
|
2022-06-13 15:04:53 +02:00
|
|
|
self.pushc(self.pop())
|
2022-05-30 22:06:15 +02:00
|
|
|
of PopRepl:
|
2022-08-04 17:48:56 +02:00
|
|
|
# Pops a peon object off the
|
|
|
|
# operand stack and prints it.
|
|
|
|
# Used in interactive REPL mode
|
2022-08-15 19:52:06 +02:00
|
|
|
if self.frames.len() !> 1:
|
2022-06-13 15:04:53 +02:00
|
|
|
discard self.pop()
|
|
|
|
continue
|
2022-07-09 12:47:53 +02:00
|
|
|
echo self.pop()
|
2022-05-23 11:53:34 +02:00
|
|
|
of PopN:
|
2022-07-09 12:47:53 +02:00
|
|
|
# Pops N elements off the call stack
|
2022-05-30 12:31:15 +02:00
|
|
|
for _ in 0..<int(self.readShort()):
|
2022-06-02 01:33:56 +02:00
|
|
|
discard self.popc()
|
2022-05-30 22:06:15 +02:00
|
|
|
# Jump opcodes
|
2022-05-07 10:48:01 +02:00
|
|
|
of Jump:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Absolute jump
|
2022-06-02 12:05:22 +02:00
|
|
|
self.ip = self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
of JumpForwards:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Relative, forward-jump
|
2022-06-02 12:05:22 +02:00
|
|
|
self.ip += self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
of JumpBackwards:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Relative, backward-jump
|
2022-06-02 12:05:22 +02:00
|
|
|
self.ip -= self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
of JumpIfFalse:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Conditional positive jump
|
|
|
|
if not self.peek().bool:
|
2022-06-02 12:05:22 +02:00
|
|
|
self.ip += self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
of JumpIfTrue:
|
2022-08-17 17:31:15 +02:00
|
|
|
# Conditional positive jump
|
|
|
|
if self.peek().bool:
|
2022-06-02 12:05:22 +02:00
|
|
|
self.ip += self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
of JumpIfFalsePop:
|
2022-06-02 12:05:22 +02:00
|
|
|
let ip = self.readLong()
|
2022-08-17 17:31:15 +02:00
|
|
|
if not self.peek().bool:
|
2022-06-02 12:19:18 +02:00
|
|
|
self.ip += ip
|
2022-05-07 10:48:01 +02:00
|
|
|
discard self.pop()
|
|
|
|
of JumpIfFalseOrPop:
|
2022-08-17 17:31:15 +02:00
|
|
|
if not self.peek().bool:
|
2022-06-02 01:33:56 +02:00
|
|
|
self.ip += self.readLong()
|
2022-05-07 10:48:01 +02:00
|
|
|
else:
|
|
|
|
discard self.pop()
|
2022-08-17 17:31:15 +02:00
|
|
|
# Built-in operations on primitive types.
|
|
|
|
# Note: for operations where the order of
|
|
|
|
# the operands matters, we don't need to
|
|
|
|
# swap the order of the calls to pop: this
|
|
|
|
# is because operators are handled like peon
|
|
|
|
# functions, which means the arguments are
|
|
|
|
# already reversed on the stack when we
|
|
|
|
# execute the instruction
|
|
|
|
of Negate:
|
|
|
|
self.push(uint64(-int64(self.pop())))
|
|
|
|
of NegateFloat64:
|
|
|
|
self.push(cast[uint64](-cast[float](self.pop())))
|
|
|
|
of NegateFloat32:
|
|
|
|
self.push(cast[uint64](-cast[float32](self.pop())))
|
|
|
|
of Add:
|
|
|
|
self.push(self.pop() + self.pop())
|
|
|
|
of Subtract:
|
|
|
|
self.push(self.pop() - self.pop())
|
|
|
|
of Multiply:
|
|
|
|
self.push(self.pop() * self.pop())
|
|
|
|
of Divide:
|
|
|
|
self.push(self.pop() div self.pop())
|
|
|
|
of SignedDivide:
|
|
|
|
self.push(uint64(int64(self.pop()) div int64(self.pop())))
|
2022-06-14 18:10:13 +02:00
|
|
|
of AddFloat64:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(cast[uint64](cast[float](self.pop()) + cast[float](self.pop())))
|
|
|
|
of SubtractFloat64:
|
|
|
|
self.push(cast[uint64](cast[float](self.pop()) - cast[float](self.pop())))
|
|
|
|
of MultiplyFloat64:
|
|
|
|
self.push(cast[uint64](cast[float](self.pop()) * cast[float](self.pop())))
|
|
|
|
of DivideFloat64:
|
|
|
|
self.push(cast[uint64](cast[float](self.pop()) / cast[float](self.pop())))
|
2022-06-14 18:10:13 +02:00
|
|
|
of AddFloat32:
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(cast[uint64](cast[float32](self.pop()) + cast[float32](self.pop())))
|
|
|
|
of SubtractFloat32:
|
|
|
|
self.push(cast[uint64](cast[float32](self.pop()) - cast[float32](self.pop())))
|
|
|
|
of MultiplyFloat32:
|
|
|
|
self.push(cast[uint64](cast[float32](self.pop()) * cast[float32](self.pop())))
|
|
|
|
of DivideFloat32:
|
|
|
|
self.push(cast[uint64](cast[float32](self.pop()) / cast[float32](self.pop())))
|
|
|
|
of Pow:
|
|
|
|
self.push(uint64(self.pop() ^ self.pop()))
|
|
|
|
of SignedPow:
|
|
|
|
self.push(uint64(int64(self.pop()) ^ int64(self.pop())))
|
|
|
|
of PowFloat64:
|
|
|
|
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
|
|
|
of PowFloat32:
|
|
|
|
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
|
|
|
of Mod:
|
|
|
|
self.push(uint64(self.pop() mod self.pop()))
|
|
|
|
of SignedMod:
|
|
|
|
self.push(uint64(int64(self.pop()) mod int64(self.pop())))
|
|
|
|
of ModFloat64:
|
|
|
|
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
|
|
|
of ModFloat32:
|
|
|
|
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
|
|
|
of LShift:
|
|
|
|
self.push(self.pop() shl self.pop())
|
|
|
|
of RShift:
|
|
|
|
self.push(self.pop() shr self.pop())
|
|
|
|
of Xor:
|
|
|
|
self.push(self.pop() xor self.pop())
|
|
|
|
of Not:
|
|
|
|
self.push(not self.pop())
|
|
|
|
of And:
|
|
|
|
self.push(self.pop() and self.pop())
|
|
|
|
of Equal:
|
|
|
|
self.push(self.getBool(self.pop() == self.pop()))
|
|
|
|
of NotEqual:
|
|
|
|
self.push(self.getBool(self.pop() != self.pop()))
|
|
|
|
of GreaterThan:
|
|
|
|
self.push(self.getBool(self.pop() !> self.pop()))
|
|
|
|
of LessThan:
|
|
|
|
self.push(self.getBool(self.pop() < self.pop()))
|
|
|
|
of GreaterOrEqual:
|
|
|
|
self.push(self.getBool(self.pop() !>= self.pop()))
|
|
|
|
of LessOrEqual:
|
|
|
|
self.push(self.getBool(self.pop() <= self.pop()))
|
|
|
|
of PrintInt64:
|
|
|
|
# Prints the value at the top of the stack
|
|
|
|
# as an int64
|
|
|
|
echo int64(self.pop())
|
|
|
|
of PrintUInt64:
|
|
|
|
# Prints the value at the top of the stack
|
|
|
|
echo self.pop()
|
|
|
|
of PrintInt32:
|
|
|
|
echo int32(self.pop())
|
|
|
|
of PrintUInt32:
|
|
|
|
echo uint32(self.pop())
|
|
|
|
of PrintInt16:
|
|
|
|
echo int16(self.pop())
|
|
|
|
of PrintUInt16:
|
|
|
|
echo uint16(self.pop())
|
|
|
|
of PrintInt8:
|
|
|
|
echo int8(self.pop())
|
|
|
|
of PrintUInt8:
|
|
|
|
echo uint8(self.pop())
|
|
|
|
of PrintFloat32:
|
|
|
|
echo cast[float32](self.pop())
|
|
|
|
of PrintFloat64:
|
|
|
|
echo cast[float](self.pop())
|
|
|
|
of PrintHex:
|
|
|
|
# Prints the value at the top of the stack
|
|
|
|
# as a hexadecimal integer
|
|
|
|
echo "0x" & self.pop().toHex().strip(chars={'0'})
|
|
|
|
of PrintBool:
|
|
|
|
if self.pop().bool:
|
|
|
|
echo "true"
|
|
|
|
else:
|
|
|
|
echo "false"
|
|
|
|
of PrintInf:
|
|
|
|
if self.pop() == 0x3:
|
|
|
|
echo "-inf"
|
|
|
|
else:
|
|
|
|
echo "inf"
|
|
|
|
of PrintNan:
|
|
|
|
echo "nan"
|
|
|
|
of PrintString:
|
|
|
|
echo cast[ptr string](self.pop())[] # TODO
|
2022-08-01 10:36:06 +02:00
|
|
|
of SysClock64:
|
2022-08-04 17:48:56 +02:00
|
|
|
# Pushes the value of a monotonic clock
|
2022-08-17 17:31:15 +02:00
|
|
|
# onto the operand stack. This can be used
|
|
|
|
# to track system time accurately, but it
|
|
|
|
# cannot be converted to a date. The number
|
2022-08-04 17:48:56 +02:00
|
|
|
# is in seconds
|
2022-08-17 17:31:15 +02:00
|
|
|
self.push(cast[uint64](getMonoTime().ticks().float() / 1_000_000_000))
|
2022-04-28 18:06:53 +02:00
|
|
|
else:
|
|
|
|
discard
|
|
|
|
|
|
|
|
|
|
|
|
proc run*(self: PeonVM, chunk: Chunk) =
|
2022-06-14 12:12:56 +02:00
|
|
|
## Executes a piece of Peon bytecode
|
2022-04-28 18:06:53 +02:00
|
|
|
self.chunk = chunk
|
2022-06-02 01:33:56 +02:00
|
|
|
self.frames = @[]
|
|
|
|
self.calls = @[]
|
|
|
|
self.operands = @[]
|
2022-04-28 18:06:53 +02:00
|
|
|
self.ip = 0
|
|
|
|
self.dispatch()
|
2022-08-17 17:31:15 +02:00
|
|
|
|
|
|
|
{.pop.}
|