MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode.cpp
Go to the documentation of this file.
1
6#include "mxvm/icode.hpp"
7#include "mxvm/parser.hpp"
9#include <algorithm>
10#include <cstdint>
11#include <dlfcn.h>
12#include <filesystem>
13#include <iomanip>
14#include <iostream>
15#include <sstream>
16#include <unordered_set>
17
18namespace mxvm {
19
20 std::unordered_map<std::string, Program *> Base::object_map;
21
22 std::string Program::getMangledName(const std::string &var) {
23 auto dot_pos = var.find('.');
24 if (dot_pos != std::string::npos) {
25 std::string sym = var;
26 std::replace(sym.begin(), sym.end(), '.', '_');
27 if (platform == Platform::DARWIN && !sym.empty() && sym[0] != '_') {
28 if (var == "stdout" || var == "stdin" || var == "stderr") {
29 return var;
30 }
31 return sym;
32 } else {
33 return sym;
34 }
35 }
36
37 if (!isVariable(var))
38 return var;
39 Variable &v = getVariable(var);
40 if (!v.obj_name.empty()) {
42 return "_" + v.obj_name + "_" + var;
43 } else {
44 return v.obj_name + "_" + var;
45 }
46 }
47
48 if (platform == Platform::DARWIN && !var.empty() && var[0] != '-') {
49 return "_" + var;
50 } else {
51 return var;
52 }
53 }
54
55 std::string Program::getMangledName(const Operand &op) {
56 if (op.op.find('.') == std::string::npos) {
57 auto it = labels.find(op.op);
58 if (it != labels.end()) {
59 return name + "_" + op.op;
60 }
61 }
62 if (!op.object.empty() && !op.label.empty()) {
63 if (op.op == "stdout" || op.op == "stderr" || op.op == "stdin") {
64 return op.op;
65 }
66 std::string base = op.object + "_" + op.label;
67 return (platform == Platform::DARWIN) ? ("_" + base) : base;
68 }
69 return getMangledName(op.op);
70 }
71
72 Base::Base(Base &&other) noexcept
73 : name(std::move(other.name)), inc(std::move(other.inc)), vars(std::move(other.vars)), labels(std::move(other.labels)), external(std::move(other.external)), external_functions(std::move(other.external_functions)) {
74 }
75
76 Base &Base::operator=(Base &&other) noexcept {
77 if (this != &other) {
78 name = std::move(other.name);
79 inc = std::move(other.inc);
80 vars = std::move(other.vars);
81 labels = std::move(other.labels);
82 external = std::move(other.external);
83 external_functions = std::move(other.external_functions);
84 }
85 return *this;
86 }
87
88 std::unordered_map<std::string, Variable> Base::allocated;
89
90 void Base::add_allocated(const std::string &name, Variable &v) {
91 auto it = allocated.find(name);
92 if (it == allocated.end())
93 allocated[name] = v;
94 }
95
96 Program::Program() : pc(0), running(false) {
97
98 add_extern("main", "strlen", true);
99 add_extern("main", "printf", true);
100 add_extern("main", "calloc", true);
101 add_extern("main", "free", true);
102 add_extern("main", "exit", true);
103 add_extern("main", "atol", true);
104 add_extern("main", "atof", true);
105 }
106
108 Variable vstdout, vstdin, vstderr;
109#ifndef __APPLE__
110 vstdout.setExtern("stdout", stdout);
111 vstdin.setExtern("stdin", stdin);
112 vstderr.setExtern("stderr", stderr);
113 add_variable(name + "." + std::string("stdout"), vstdout);
114 add_variable(name + "." + std::string("stdin"), vstdin);
115 add_variable(name + "." + std::string("stderr"), vstderr);
116#else
117
118 vstdout.setExtern("stdout", stdout);
119 vstdin.setExtern("stdin", stdin);
120 vstderr.setExtern("stderr", stderr);
121 add_variable(name + "." + std::string("stdout"), vstdout);
122 add_variable(name + "." + std::string("stdin"), vstdin);
123 add_variable(name + "." + std::string("stderr"), vstderr);
124
125#endif
126 }
127
128 void Program::setArgs(const std::vector<std::string> &argv) {
129 std::copy(argv.begin(), argv.end(), std::back_inserter(args));
130 }
131
132 void Program::setObject(bool obj) {
133 object = obj;
134 }
135
136 void Base::add_object(const std::string &name, Program *prog) {
137 if (base != nullptr) {
138 auto it = base->object_map.find(name);
139 if (it == base->object_map.end())
140 base->object_map[name] = prog;
141 }
142 }
143
145 std::unordered_set<void *> freed_ptrs;
146 for (auto &i : vars) {
147 if (i.second.var_value.ptr_value != nullptr && i.second.var_value.owns) {
148 if (freed_ptrs.find(i.second.var_value.ptr_value) == freed_ptrs.end()) {
149 freed_ptrs.insert(i.second.var_value.ptr_value);
150 free(i.second.var_value.ptr_value);
151 if (debug_mode) {
152 std::cerr << Col("MXVM: Warning ", mx::Color::RED) << "Possible Memory Leak, Pointer: " << name << "." << i.first << "\n";
153 }
154 }
155 i.second.var_value.ptr_value = nullptr;
156 i.second.var_value.owns = false;
157 }
158 }
159
160 for (auto &h : RuntimeFunction::handles) {
161 if (h.second) {
162 dlclose(h.second);
163 h.second = nullptr;
164 char *errf = dlerror();
165 if (errf != nullptr) {
166 std::cerr << "Error releaseing module: " << errf << "\n";
167 } else {
168 if (mxvm::debug_mode) {
169 std::cout << "Released module: " << h.first << "\n";
170 }
171 }
172 }
173 }
174 }
175 bool Program::isFunctionValid(const std::string &label) {
176 auto dot_pos = label.find('.');
177 if (dot_pos != std::string::npos) {
178 std::string obj_name = label.substr(0, dot_pos);
179 std::string func_name = label.substr(dot_pos + 1);
180
181 auto it = object_map.find(obj_name);
182 if (it != object_map.end() && it->second != nullptr) {
183 Program *obj_prog = it->second;
184 auto label_it = obj_prog->labels.find(func_name);
185 return label_it != obj_prog->labels.end() && label_it->second.second; // second==true means function
186 }
187 return false;
188 }
189
190 auto it = labels.find(label);
191 if (it != labels.end() && it->second.second) {
192 return true;
193 }
194
195 return false;
196 }
197
198 std::unordered_map<std::string, void *> RuntimeFunction::handles;
199 std::string Base::root_name;
200
201 RuntimeFunction::RuntimeFunction(const std::string &mod, const std::string &name) {
202 fname = name;
203 handle = nullptr;
204 if (handles.find(mod) == handles.end()) {
205 handle = dlopen(mod.c_str(), RTLD_LAZY);
206 handles[mod] = handle;
207 } else {
208 handle = handles[mod];
209 }
210 if (handle == nullptr) {
211 throw mx::Exception("Error could not open module: " + mod + " try using --path to point to module path");
212 }
213 func = (void *)dlsym(handle, name.c_str());
214 if (func == nullptr) {
215 char *dl_e = dlerror();
216 throw mx::Exception("Error could not find symbol: " + name + " in: " + mod + " Error: " + ((dl_e != nullptr) ? dl_e : ""));
217 }
218 char *dl_err = dlerror();
219 if (dl_err != nullptr) {
220 throw mx::Exception("Error: " + std::string(dl_err));
221 }
222 }
223
224 void RuntimeFunction::call(Program *program, std::vector<Operand> &operands) {
225 if (!func || !handle) {
226 throw mx::Exception("RuntimeFunction: function: " + this->fname + " pointer is null: " + this->mod_name);
227 }
228 using FuncType = void (*)(Program *program, std::vector<Operand> &);
229 FuncType f = reinterpret_cast<FuncType>(func);
230 if (f != nullptr)
231 f(program, operands);
232 }
233
234 Base *Base::base = nullptr;
235 std::vector<std::string> Base::filenames;
236
238 inc.push_back(i);
239 }
240
241 void Base::add_label(const std::string &name, uint64_t address, bool f) {
242 if (labels.find(name) == labels.end())
243 labels[name] = std::make_pair(address, f);
244 }
245
246 void Base::add_variable(const std::string &name, const Variable &v) {
247 if (vars.find(name) == vars.end()) {
248 vars[name] = v;
249 } else {
250 throw mx::Exception("Duplciate variable name: " + name);
251 }
252 }
253
254 void Base::add_global(const std::string &objname, const std::string &name, const Variable &v) {
255 }
256
257 void Base::add_filename(const std::string &fname) {
258 if (base != nullptr) {
259 auto it = std::find(base->filenames.begin(), base->filenames.end(), fname);
260 if (it == base->filenames.end()) {
261 base->filenames.push_back(fname);
262 }
263 }
264 }
265
266 void Base::add_extern(const std::string &mod, const std::string &name, bool module) {
267 ExternalFunction f = {name, mod, module};
268 auto it = std::find(external.begin(), external.end(), f);
269 if (it == external.end())
270 external.push_back({name, mod, module});
271 }
272
273 void Base::add_runtime_extern(const std::string &mod_name, const std::string &mod, const std::string &func_name, const std::string &name) {
274 if (mod_name.empty() || mod.empty() || func_name.empty() || name.empty()) {
275 throw mx::Exception("External function missing information.");
276 }
277
278 if (base != nullptr) {
279 if (base->external_functions.find(name) == base->external_functions.end()) {
280 base->external_functions[name] = RuntimeFunction(mod, func_name);
281 base->external_functions[name].mod_name = mod_name;
282 }
283 }
284 }
285 Program *Program::getObjectByName(const std::string &searchName) {
286 if (this->name == searchName)
287 return this;
288 for (auto &obj : objects) {
289 Program *found = obj->getObjectByName(searchName);
290 if (found)
291 return found;
292 }
293 return nullptr;
294 }
295
296 std::string Program::escapeNewLines(const std::string &input) {
297 std::string result;
298 for (char c : input) {
299 switch (c) {
300 case '\t':
301 result += "\\t";
302 break;
303 case '\n':
304 result += "\\n";
305 break;
306 case '\r':
307 result += "\\r";
308 break;
309 default:
310 result += c;
311 break;
312 }
313 }
314 return result;
315 }
316 void Program::memoryDump(std::ostream &out) {
317 out << "Current working directory: ";
318 try {
319 out << std::filesystem::current_path().string() << "\n";
320 } catch (const std::exception &e) {
321 out << "(unable to retrieve: " << e.what() << ")\n";
322 }
323
324 std::function<void(Program *, std::ostream &, int)> dumpProgram;
325 dumpProgram = [&](Program *prog, std::ostream &out, int depth = 0) {
326 std::string indent(depth * 2, ' ');
327 out << indent << "=== MEMORY DUMP for " << prog->name << " ===\n";
328 if (!prog->labels.empty()) {
329 out << indent << " Labels:\n";
330 out << indent << " " << std::left << std::setw(20) << "Name"
331 << std::setw(15) << "Address"
332 << std::setw(12) << "IsFunc"
333 << "\n";
334 out << indent << " " << std::string(42, '-') << "\n";
335 for (const auto &label : prog->labels) {
336 out << indent << " " << std::setw(20) << label.first
337 << std::setw(15) << label.second.first
338 << std::setw(10) << (label.second.second ? "yes" : "no")
339 << "\n";
340 }
341 }
342 if (prog->vars.empty()) {
343 out << indent << " (no variables)\n";
344 } else {
345 out << indent << std::left << std::setw(25) << "Name"
346 << std::setw(18) << "Type"
347 << std::setw(30) << "Value"
348 << std::setw(18) << "PtrSize"
349 << std::setw(18) << "PtrCount"
350 << std::setw(12) << "Owns"
351 << "\n";
352 out << indent << std::string(121, '-') << "\n";
353 for (const auto &var : prog->vars) {
354 out << indent << std::setw(25) << var.first;
355 std::string typeStr;
356 switch (var.second.type) {
358 typeStr = "int";
359 break;
361 typeStr = "float";
362 break;
364 typeStr = "string";
365 break;
367 typeStr = "ptr";
368 break;
370 typeStr = "label";
371 break;
373 typeStr = "external";
374 break;
376 typeStr = "array";
377 break;
379 typeStr = "byte";
380 break;
381 default:
382 typeStr = "unknown";
383 break;
384 }
385 out << std::setw(18) << typeStr;
386 switch (var.second.type) {
389 out << std::setw(30) << var.second.var_value.int_value;
390 break;
392 out << std::setw(30) << std::fixed << std::setprecision(6)
393 << var.second.var_value.float_value;
394 break;
396 out << std::setw(30) << ("\"" + Program::escapeNewLines(var.second.var_value.str_value) + "\"");
397 break;
400 if (var.second.var_value.ptr_value == nullptr)
401 out << std::setw(30) << "null";
402 else {
403 std::ostringstream ptr_oss;
404 ptr_oss << var.second.var_value.ptr_value;
405 out << std::setw(30) << ptr_oss.str() << std::dec;
406 }
407 break;
409 out << std::setw(30) << var.second.var_value.label_value;
410 break;
411 default:
412 out << std::setw(30) << var.second.var_value.int_value;
413 break;
414 }
415 if (var.second.type == VarType::VAR_POINTER) {
416 out << std::setw(18) << var.second.var_value.ptr_size
417 << std::setw(18) << var.second.var_value.ptr_count
418 << std::setw(12) << (var.second.var_value.owns ? "yes" : "no");
419 } else {
420 out << std::setw(18) << "-"
421 << std::setw(18) << "-"
422 << std::setw(12) << "-";
423 }
424 out << "\n";
425 }
426 }
427 out << "\n";
428 for (const auto &obj : prog->objects) {
429 if (obj)
430 dumpProgram(obj.get(), out, depth + 1);
431 }
432 };
433 dumpProgram(this, out, 0);
434 }
435
436 void except_assert(std::string reason, bool value) {
437 if (value == false) {
438 throw mx::Exception(reason);
439 }
440 }
441} // namespace mxvm
General-purpose exception with errno-aware factory method.
Definition exception.hpp:38
Base class for instruction storage, variables, labels, and external function registrations.
Definition icode.hpp:121
static std::unordered_map< std::string, Program * > object_map
object name -> Program mapping
Definition icode.hpp:201
void add_global(const std::string &objname, const std::string &name, const Variable &v)
Declare a global variable scoped to an object.
Definition icode.cpp:254
void add_filename(const std::string &fname)
Record a source filename for debug/error reporting.
Definition icode.cpp:257
std::unordered_map< std::string, std::pair< uint64_t, bool > > labels
label -> (address, is_function)
Definition icode.hpp:194
std::vector< Instruction > inc
instruction stream
Definition icode.hpp:192
void add_runtime_extern(const std::string &mod_name, const std::string &mod, const std::string &func_name, const std::string &name)
Register and resolve a runtime-loaded external function.
Definition icode.cpp:273
void add_variable(const std::string &name, const Variable &v)
Declare a local variable in the symbol table.
Definition icode.cpp:246
void add_allocated(const std::string &name, Variable &v)
Track a heap-allocated variable for cleanup.
Definition icode.cpp:90
void add_extern(const std::string &mod, const std::string &name, bool module)
Register an external function import.
Definition icode.cpp:266
void add_object(const std::string &name, Program *prog)
Register a child object program.
Definition icode.cpp:136
std::unordered_map< std::string, Variable > vars
variable symbol table
Definition icode.hpp:193
Base & operator=(Base &&other) noexcept
Move-assignment operator.
Definition icode.cpp:76
void add_label(const std::string &name, uint64_t address, bool f)
Register a label at the given instruction address.
Definition icode.cpp:241
static Base * base
pointer to the main program base
Definition icode.hpp:197
static std::vector< std::string > filenames
Definition icode.hpp:199
std::string name
Definition icode.hpp:191
Base()=default
std::unordered_map< std::string, RuntimeFunction > external_functions
resolved runtime function bindings
Definition icode.hpp:196
static std::unordered_map< std::string, Variable > allocated
heap-allocated variables
Definition icode.hpp:200
void add_instruction(const Instruction &i)
Append an instruction to the instruction stream.
Definition icode.cpp:237
static std::string root_name
Definition icode.hpp:198
std::vector< ExternalFunction > external
declared external function imports
Definition icode.hpp:195
Complete MXVM program: instruction interpreter and native x64 code generator.
Definition icode.hpp:212
Variable & getVariable(const std::string &name)
Look up a variable by name, searching local scope then global.
bool isFunctionValid(const std::string &label)
Check whether a label exists and is marked as a function entry.
Definition icode.cpp:175
size_t pc
program counter
Definition icode.hpp:399
Program * getObjectByName(const std::string &name)
Look up a child object program by name.
Definition icode.cpp:285
void add_standard()
Register standard library runtime functions.
Definition icode.cpp:107
bool isVariable(const std::string &name)
Check whether a variable exists in local or global scope.
std::vector< std::string > args
Definition icode.hpp:407
~Program()
Destructor — frees any heap-allocated variables.
Definition icode.cpp:144
static std::string escapeNewLines(const std::string &text)
Escape newline characters in a string literal for assembly output.
Definition icode.cpp:296
Platform platform
Definition icode.hpp:861
std::vector< std::unique_ptr< Program > > objects
Definition icode.hpp:340
void setObject(bool obj)
Mark this program as an object (no main entry point).
Definition icode.cpp:132
std::string getMangledName(const std::string &var)
Mangle a variable name with its owning object prefix.
Definition icode.cpp:22
void memoryDump(std::ostream &out)
Dump all variable values to a stream for debugging.
Definition icode.cpp:316
std::string name
Definition icode.hpp:306
Operand result
return value operand from last invoke
Definition icode.hpp:859
Program()
Default constructor — initializes execution state (pc, flags, etc.).
Definition icode.cpp:96
void setArgs(const std::vector< std::string > &argv)
Set the command-line arguments available to the program.
Definition icode.cpp:128
bool running
interpreter running flag
Definition icode.hpp:400
Wraps a dynamically loaded native function from a shared library module.
Definition icode.hpp:78
RuntimeFunction()
Default constructor — null function pointer and handle.
Definition icode.hpp:81
std::string fname
function symbol name
Definition icode.hpp:110
void * handle
dlopen handle
Definition icode.hpp:108
void call(Program *program, std::vector< Operand > &operands)
Invoke the loaded function.
Definition icode.cpp:224
static std::unordered_map< std::string, void * > handles
cached library handles
Definition icode.hpp:111
void * func
resolved function pointer
Definition icode.hpp:107
std::string mod_name
module name
Definition icode.hpp:109
Exception class, hex formatting utilities, and terminal color definitions.
std::string Col(const std::string &col, std::string color)
Wrap a string with ANSI color codes if terminal supports color.
Intermediate code representation, execution engine, and native x64 code generation (SysV + Win64).
Parser that tokenizes and parses MXVM source into programs, handles modules/objects,...
const char * argv(int idx)
Return the command-line argument at the given index.
Definition std.c:76
const std::string RED
Definition exception.hpp:70
Definition ast.hpp:14
bool debug_mode
enable verbose debug output during parsing
Definition parser.cpp:96
void except_assert(std::string reason, bool value)
Assert a condition, throwing an Exception with the given reason on failure.
Definition icode.cpp:436
Describes an external function imported from a module or object.
Definition parser.hpp:162
A complete MXVM instruction with opcode, operands, and optional label.
Definition instruct.hpp:168
A single instruction operand (constant value or variable reference).
Definition instruct.hpp:159
std::string op
textual operand value
Definition instruct.hpp:161
std::string object
owning object name for member access
Definition instruct.hpp:164
std::string label
label target for jump/call operands
Definition instruct.hpp:160
A named variable with type, value, and optional object association.
Definition instruct.hpp:292
void setExtern(std::string name, void *value)
Configure this variable as an external pointer reference.
Definition instruct.hpp:319
std::string obj_name
Definition instruct.hpp:297