Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
Utility.cpp
Go to the documentation of this file.
1#ifndef CMAKE_PCH
2#include "VM.hpp"
3#endif
4#include <iostream>
5#include <stdexcept>
6#include <format>
7#include <cassert>
8#include "core/core.h"
9#include <phsint.hpp>
10
11#ifdef TIMING
12#include <chrono>
13#endif
14
15#ifdef _DEBUG
16#ifdef _WIN32
17#include <windows.h>
18inline bool isDebuggerAttached()
19{
20 return IsDebuggerPresent() == TRUE;
21}
22#else
23#include <unistd.h>
24#include <sys/ptrace.h>
25inline bool isDebuggerAttached()
26{
27 return ptrace(PTRACE_TRACEME, 0, 1, 0) == -1;
28}
29#endif
30#endif
31
32namespace Phasor
33{
34
35std::string VM::getVersion()
36{
37 return PHASOR_VERSION_STRING;
38}
39void VM::setStatus(int newStatus)
40{
41 status = newStatus;
42}
44{
45 status = 0;
46}
48{
49 return status;
50}
52{
53 return isError;
54}
55
56void VM::initFFI(const std::filesystem::path &path)
57{
58#ifndef SANDBOXED
59 ffi = std::make_unique<FFI>(path, this);
60#endif
61}
62
63void VM::setup(const Bytecode &bc, const size_t initialPC) {
64 m_bytecode = &bc;
65 pc = initialPC;
66 stack.clear();
67 callStack.clear();
68
70
71#ifdef TRACING
72 log(std::format("\nVM::{}():\n\n{}\n", __func__, getBytecodeInformation()));
73 flush();
74#endif
75
76 registers.fill(Value());
77 variables.resize(m_bytecode->nextVarIndex);
78}
79
80int VM::run(const Bytecode &bc, const size_t startPC)
81{
82 setup(bc, startPC);
83
84#ifdef TRACING
85 log(std::format("\nVM::{}():\n\n", __func__));
86 flush();
87#endif
88
89#ifdef TIMING
90 using clock = std::chrono::high_resolution_clock;
91 auto start = clock::now();
92#endif
93
94 try
95 {
96 evalLoop();
97 return status;
98 }
99 catch (const VM::Halt &)
100 {
101#ifdef TIMING
102 auto end = clock::now();
103 auto us = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
104 log(std::format("VM::{}(): Duration of bytecode execution: {}us\n\n", __func__, us));
105 flush();
106#endif
107#ifdef TRACING
108 log(std::format("\nVM::{}(): HALT (status={})\n\n{}\n", __func__, status, getInformation()));
109 flush();
110#endif
111#ifdef _DEBUG
112 if (isDebuggerAttached())
113 assert(status == 0);
114#endif
115 return status;
116 }
117#if defined(TRACING) || defined(_DEBUG)
118 catch (const std::exception &e)
119#else
120 catch (const std::exception &)
121#endif
122 {
123#ifdef TRACING
124 logerr(std::format("\nVM::{}(): UNCAUGHT EXCEPTION!\n\n{}\n{}\n\n", __func__, e.what(), getInformation()));
125 flusherr();
126#endif
128#ifdef _DEBUG
129 logerr(std::format("{}\n", e.what()));
130 assert(false);
131#endif
132 throw;
133 }
134}
135
136Value VM::runFunction(const std::string &name, const Bytecode &bytecode, const bool &argsInit)
137{
138 isDirectCall = true;
139 setup(bytecode, bytecode.functionEntries.find(name)->second);
140
141 if (!argsInit) push(0);
142
143 try {
144 evalLoop();
145 }
146 catch (const VM::Halt &) {
147 if (isDirectCall) {
148 Value ret = pop();
149 if (ret.isInt()) status = ret.asInt();
150 else status = 0;
151 reset(true, false, true);
152 return ret;
153 } else {
154 throw std::runtime_error("Function call was not properly handled!");
155 }
156 }
157 throw std::runtime_error("Function did not return properly!");
159 isError = true;
160 return Value();
161}
162
164{
165#ifdef TRACING
166 log(std::format("VM::{}()\n", __func__));
167 flush();
168#endif
169 importHandler = handler;
170}
171
173{
174#ifdef TRACING
175 log(std::format("VM::{}()\n", __func__));
176 flush();
177#endif
178 for (auto &i : registers)
179 {
180 i = Value();
181 }
182 for (auto &i : variables)
183 {
184 i = Value();
185 }
186 flush();
187 flusherr();
188 reset(true, true, true);
189}
190
191void VM::reset(const bool &resetStack, const bool &resetFunctions, const bool &resetVariables)
192{
193#ifdef TRACING
194 log(std::format("VM::{}()\n", __func__));
195 flush();
196#endif
197 if (resetStack)
198 {
199 callStack.clear();
200 stack_pool.release();
201 stack = std::pmr::vector<Value>(&stack_pool);
202 }
203 if (resetFunctions)
204 {
205 nativeFunctions.clear();
206 }
207 if (resetVariables)
208 {
209 variables.clear();
210 }
211 pc = 0;
212 status = 0;
213 m_bytecode = nullptr;
214 isDirectCall = false;
215}
216
218{
219 int callStackTop = callStack.empty() ? -1 : callStack.back();
220 std::string info;
221
222 if (!stack.empty())
223 {
224 info = std::format("Stack Top: {:T}\n", peek());
225 }
226
227 std::string registersStr;
228 int regCount = 0;
229
230 for (const auto &reg : registers)
231 {
232 if (reg.getType() != ValueType::Null)
233 {
234 registersStr += std::format("R{}: {:T}\n", regCount, reg);
235 }
236 regCount++;
237 }
238
239 info += std::format("VM INFORMATION:\n{}PC: {}\nCS: {}", registersStr, pc, callStackTop);
240
241 return info;
242}
243
245{
246 std::string info;
247 std::string constants;
248 std::string variables;
249 std::string functions;
250 std::string instructions;
251
252 for (const auto &constant : m_bytecode->constants)
253 {
254 constants += std::format("{:T}\n", constant);
255 }
256 for (const auto &variable : m_bytecode->variables)
257 {
258 variables += std::format("{}\n", variable.first);
259 }
260 for (const auto &function : m_bytecode->functionEntries)
261 {
262 functions += std::format("{}() PC = {}\n", function.first, function.second);
263 }
264#ifdef TRACING
265 for (const auto &instruction : m_bytecode->instructions)
266 {
267 instructions += std::format("{}({}, {}, {})\n", opCodeToString(instruction.op), instruction.operand1,
268 instruction.operand2, instruction.operand3);
269 }
270#endif
271
272 info = std::format(
273 "BYTECODE INFORMATION:\n\nConstants: {}\n{}\nVariables: {}\n{}\nFunctions: {}\n{}\nInstructions: {}\n{}",
274 m_bytecode->constants.size(), constants, m_bytecode->variables.size(), variables,
275 m_bytecode->functionEntries.size(), functions, m_bytecode->instructions.size(), instructions);
276 return info;
277}
278
279void VM::log(const Value &msg)
280{
281 std::string s = msg.toString();
282 c_print_stdout(s.c_str(), (i64)s.length());
283}
284
285void VM::logerr(const Value &msg)
286{
287 std::string s = msg.toString();
288 c_print_stderr(s.c_str(), (i64)s.length());
289}
290
292{
293 fflush(stdout);
294}
295
297{
298 fflush(stderr);
299}
300} // namespace Phasor
void c_print_stderr(const char *s, int64_t len)
Native print error function.
Definition IO.c:105
void c_print_stdout(const char *s, int64_t len)
Native print function.
Definition IO.c:100
#define msg
#define BAD_STATUS
Definition VM.hpp:27
Throws when the HALT opcode is reached.
Definition VM.hpp:52
std::array< Value, MAX_REGISTERS > registers
Virtual registers for register-based operations (v2.0).
Definition VM.hpp:261
ImportHandler importHandler
Import handler for loading modules.
Definition VM.hpp:258
std::vector< Value > variables
Variable storage indexed by variable index, or simply: the managed heap.
Definition VM.hpp:271
Value pop()
Pop a value from the stack.
Definition Stack.cpp:17
void setImportHandler(const ImportHandler &handler)
Set the import handler for importing modules.
Definition Utility.cpp:163
std::function< void(const std::filesystem::path &path)> ImportHandler
Definition VM.hpp:73
void logerr(const Value &msg)
Log a Value to stderr.
Definition Utility.cpp:285
void registerArrayFunctions()
Definition Array.cpp:7
std::string getVersion()
Get Phasor VM version.
Definition Utility.cpp:35
void evalLoop()
Definition Operations.cpp:9
std::unique_ptr< FFI > ffi
FFI.
Definition VM.hpp:249
bool isDirectCall
is a direct call to a function
Definition VM.hpp:245
void setStatus(int newStatus)
Set VM exit code.
Definition Utility.cpp:39
bool isErrorStatus()
Definition Utility.cpp:51
std::vector< int > callStack
Call stack for function calls.
Definition VM.hpp:268
std::string getBytecodeInformation()
Get bytecode information for debugging.
Definition Utility.cpp:244
void log(const Value &msg)
Log a Value to stdout.
Definition Utility.cpp:279
int getStatus()
Definition Utility.cpp:47
void flush()
Flush stdout.
Definition Utility.cpp:291
std::pmr::vector< Value > stack
Definition VM.hpp:265
bool isError
Is status an error code.
Definition VM.hpp:255
void cleanup()
Clean up the virtual machine.
Definition Utility.cpp:172
size_t pc
Program counter.
Definition VM.hpp:277
void push(const Value &value)
Push a value onto the stack.
Definition Stack.cpp:8
void resetStatus()
Definition Utility.cpp:43
int run(const Bytecode &bytecode, const size_t startPC=0)
Run the virtual machine Exits -1 on uncaught exception.
Definition Utility.cpp:80
void initFFI(const std::filesystem::path &path)
Initialize the FFI plugins.
Definition Utility.cpp:56
std::map< std::string, NativeFunction > nativeFunctions
Native function registry.
Definition VM.hpp:280
const Bytecode * m_bytecode
Bytecode to execute.
Definition VM.hpp:274
void setup(const Bytecode &bc, const size_t initialPC)
Definition Utility.cpp:63
std::pmr::monotonic_buffer_resource stack_pool
Stack.
Definition VM.hpp:264
int status
Exit code.
Definition VM.hpp:252
Value peek()
Peek at the top value on the stack.
Definition Stack.cpp:38
void flusherr()
Flush stderr.
Definition Utility.cpp:296
Value runFunction(const std::string &name, const Bytecode &bytecode, const bool &argsInit=false)
Run a function from bytecode on the virtual machine.
Definition Utility.cpp:136
void reset(const bool &resetStack=true, const bool &resetFunctions=true, const bool &resetVariables=true)
Reset the virtual machine.
Definition Utility.cpp:191
std::string getInformation()
Get VM information for debugging.
Definition Utility.cpp:217
A value in the Phasor VM.
Definition Value.hpp:59
bool isInt() const noexcept
Definition Value.hpp:148
i64 asInt() const noexcept
Get the value as an integer.
Definition Value.hpp:164
static Phasor::u64 s[2]
Definition random.cpp:7
The Phasor Programming Language and Runtime.
Definition AST.hpp:13
int64_t i64
Definition phsint.hpp:16
std::string opCodeToString(OpCode op)
Definition map.cpp:132
Complete bytecode structure.
Definition CodeGen.hpp:50
std::unordered_map< std::string, int > functionEntries
Function name -> instruction index mapping.
Definition CodeGen.hpp:54