Compare commits
8 Commits
0e916c314a
...
c68763f785
| Author | SHA1 | Date | |
|---|---|---|---|
|
c68763f785
|
|||
|
dcefe20d47
|
|||
|
989fcb7199
|
|||
|
4cffd27a33
|
|||
|
f9b910ae13
|
|||
|
7a7a1ec64c
|
|||
|
99dba99db2
|
|||
|
cc88d6b6a0
|
@@ -15,6 +15,7 @@ add_subdirectory(furlang)
|
|||||||
add_subdirectory(furvm)
|
add_subdirectory(furvm)
|
||||||
add_subdirectory(furc)
|
add_subdirectory(furc)
|
||||||
add_subdirectory(furas)
|
add_subdirectory(furas)
|
||||||
|
add_subdirectory(disfuras)
|
||||||
|
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
|
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
file(GLOB_RECURSE DISFURAS_SRCS "src/**.cpp")
|
||||||
|
file(GLOB_RECURSE DISFURAS_HDRS "include/**.hpp")
|
||||||
|
add_executable(disfuras ${DISFURAS_SRCS} ${DISFURAS_HDRS})
|
||||||
|
target_include_directories(disfuras PRIVATE include/)
|
||||||
|
target_link_libraries(disfuras PUBLIC furlang libfurc libfurvm)
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
#include "furvm/module.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <exception>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
void print_type(const furvm::mod_type& type, const furvm::mod& mod) {
|
||||||
|
switch (type.type) {
|
||||||
|
case furvm::mod_type::S8: std::cout << "$s8"; return;
|
||||||
|
case furvm::mod_type::S16: std::cout << "$s16"; return;
|
||||||
|
case furvm::mod_type::S32: std::cout << "$s32"; return;
|
||||||
|
case furvm::mod_type::S64: std::cout << "$s64"; return;
|
||||||
|
case furvm::mod_type::U8: std::cout << "$u8"; return;
|
||||||
|
case furvm::mod_type::U16: std::cout << "$u16"; return;
|
||||||
|
case furvm::mod_type::U32: std::cout << "$u32"; return;
|
||||||
|
case furvm::mod_type::U64: std::cout << "$u64"; return;
|
||||||
|
case furvm::mod_type::Ptr:
|
||||||
|
std::cout << "ptr ";
|
||||||
|
print_type(*mod.type_at(type.value.typeRef), mod);
|
||||||
|
return;
|
||||||
|
case furvm::mod_type::Ref:
|
||||||
|
std::cout << "ref ";
|
||||||
|
print_type(*mod.type_at(type.value.typeRef), mod);
|
||||||
|
return;
|
||||||
|
case furvm::mod_type::Array:
|
||||||
|
std::cout << "array ";
|
||||||
|
print_type(*mod.type_at(type.value.array.typeId), mod);
|
||||||
|
if (type.value.array.size == 0) {
|
||||||
|
std::cout << " dynamic";
|
||||||
|
} else {
|
||||||
|
std::cout << ' ' << type.value.array.size;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case furvm::mod_type::Import: std::cout << "import " << type.value.imprt.modId << "::t" << type.value.imprt.typeId;
|
||||||
|
case furvm::mod_type::Count: break;
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* typeNames[furvm::instruction::Count] = {
|
||||||
|
// NoOperation:
|
||||||
|
"nop",
|
||||||
|
// PushS8:
|
||||||
|
"push $__t0",
|
||||||
|
// PushU8:
|
||||||
|
"push $__t1",
|
||||||
|
// PushS16:
|
||||||
|
"push $__t2",
|
||||||
|
// PushU16:
|
||||||
|
"push $__t3",
|
||||||
|
// PushS32:
|
||||||
|
"push $__t4",
|
||||||
|
// PushU32:
|
||||||
|
"push $__t5",
|
||||||
|
// PushConstant:
|
||||||
|
"push",
|
||||||
|
// Array:
|
||||||
|
"array",
|
||||||
|
// Get:
|
||||||
|
"get",
|
||||||
|
// Set:
|
||||||
|
"set",
|
||||||
|
// Drop:
|
||||||
|
"drop",
|
||||||
|
// Duplicate:
|
||||||
|
"dup",
|
||||||
|
// Swap:
|
||||||
|
"swap",
|
||||||
|
// Clone:
|
||||||
|
"clone",
|
||||||
|
// Reference:
|
||||||
|
"ref",
|
||||||
|
// Add:
|
||||||
|
"add",
|
||||||
|
// Sub:
|
||||||
|
"sub",
|
||||||
|
// Mul:
|
||||||
|
"mul",
|
||||||
|
// Div:
|
||||||
|
"div",
|
||||||
|
// Mod:
|
||||||
|
"mod",
|
||||||
|
// Equals:
|
||||||
|
"eq",
|
||||||
|
// NotEquals:
|
||||||
|
"ne",
|
||||||
|
// LessThan:
|
||||||
|
"lt",
|
||||||
|
// GreaterThan:
|
||||||
|
"gt",
|
||||||
|
// LessEqual:
|
||||||
|
"le",
|
||||||
|
// GreaterEqual:
|
||||||
|
"ge",
|
||||||
|
// Pointerof:
|
||||||
|
"pointerof",
|
||||||
|
// Sizeof:
|
||||||
|
"sizeof",
|
||||||
|
// Lengthof:
|
||||||
|
"lengthof",
|
||||||
|
// Load:
|
||||||
|
"load",
|
||||||
|
// Store:
|
||||||
|
"store",
|
||||||
|
// Call:
|
||||||
|
"call",
|
||||||
|
// Jump:
|
||||||
|
"jmp",
|
||||||
|
// JumpNotZero:
|
||||||
|
"jnz",
|
||||||
|
// Return:
|
||||||
|
"ret",
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " <module.fmod>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
|
||||||
|
|
||||||
|
furvm::mod mod;
|
||||||
|
try {
|
||||||
|
mod = furvm::mod::load(file);
|
||||||
|
} catch (std::exception ex) {
|
||||||
|
std::cerr << "Failed to load module " << argv[1] << ": " << ex.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "; Generated with disfuras\n";
|
||||||
|
|
||||||
|
std::cout << "; Types:\n";
|
||||||
|
for (const auto& ptr : mod.types()) {
|
||||||
|
const auto& [header, type] = *ptr;
|
||||||
|
std::cout << "type __t" << header.id() << " = ";
|
||||||
|
print_type(type, mod);
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<furvm::function_id, std::string> funcNames;
|
||||||
|
std::size_t unnamedCounter = 0;
|
||||||
|
|
||||||
|
std::unordered_map<std::size_t, std::string> labels;
|
||||||
|
std::unordered_set<std::size_t> deadLabels;
|
||||||
|
std::size_t labelCounter = 0;
|
||||||
|
|
||||||
|
std::cout << "; Functions:\n";
|
||||||
|
for (const auto& ptr : mod.functions()) {
|
||||||
|
const auto& [header, func] = *ptr;
|
||||||
|
auto it = mod.function_map().find(header.id());
|
||||||
|
if (it != mod.function_map().end()) {
|
||||||
|
std::cout << "public func " << (funcNames[header.id()] = it->second.first);
|
||||||
|
} else {
|
||||||
|
std::cout << "func " << (funcNames[header.id()] = "__f"s + std::to_string(unnamedCounter++));
|
||||||
|
}
|
||||||
|
for (const auto& param : func.signature().params) {
|
||||||
|
std::cout << " $__t" << param.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << " = ";
|
||||||
|
if (func.signature().returnType.has_value())
|
||||||
|
std::cout << "$__t" << func.signature().returnType->id() << ' '; // NOLINT
|
||||||
|
switch (func.type()) {
|
||||||
|
case furvm::function_t::Normal: {
|
||||||
|
auto it = labels.find(func.position());
|
||||||
|
if (it == labels.end()) it = labels.emplace(func.position(), funcNames[header.id()]).first;
|
||||||
|
deadLabels.insert(func.position());
|
||||||
|
std::cout << '#' << it->second;
|
||||||
|
} break;
|
||||||
|
case furvm::function_t::Native: {
|
||||||
|
std::cout << "native " << func.native();
|
||||||
|
} break;
|
||||||
|
case furvm::function_t::Import: {
|
||||||
|
std::cout << "import " << func.imp().mod << "::" << func.imp().function;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "; Bytecode:\n";
|
||||||
|
// Label pass:
|
||||||
|
for (std::size_t off = 0; off < mod.bytecode().size();) {
|
||||||
|
furvm::instruction instr{};
|
||||||
|
off += instr.read(mod.bytecode_view().subview(off));
|
||||||
|
if (instr.type != furvm::instruction::Jump && instr.type != furvm::instruction::JumpNotZero) continue;
|
||||||
|
if (labels.find(off + instr.arg.s8) == labels.end())
|
||||||
|
labels[off + instr.arg.s8] = "__l"s + std::to_string(labelCounter++);
|
||||||
|
deadLabels.insert(off + instr.arg.s8);
|
||||||
|
}
|
||||||
|
// Actual printing pass:
|
||||||
|
for (std::size_t off = 0; off < mod.bytecode().size();) {
|
||||||
|
if (auto it = labels.find(off); it != labels.end()) {
|
||||||
|
std::cout << it->second << ":\n";
|
||||||
|
deadLabels.erase(off);
|
||||||
|
}
|
||||||
|
furvm::instruction instr{};
|
||||||
|
off += instr.read(mod.bytecode_view().subview(off));
|
||||||
|
std::cout << " " << typeNames[instr.type];
|
||||||
|
switch (instr.arg.type) {
|
||||||
|
case furvm::instruction_argument::None: break;
|
||||||
|
case furvm::instruction_argument::S8: std::cout << ' ' << std::to_string(instr.arg.s8); break;
|
||||||
|
case furvm::instruction_argument::U8: std::cout << ' ' << std::to_string(instr.arg.u8); break;
|
||||||
|
case furvm::instruction_argument::S16: std::cout << ' ' << instr.arg.s16; break;
|
||||||
|
case furvm::instruction_argument::U16: std::cout << ' ' << instr.arg.u16; break;
|
||||||
|
case furvm::instruction_argument::S32: std::cout << ' ' << std::to_string(instr.arg.s8); break;
|
||||||
|
case furvm::instruction_argument::U32: std::cout << ' ' << std::to_string(instr.arg.u8); break;
|
||||||
|
case furvm::instruction_argument::Constant: throw std::runtime_error("unimplemented");
|
||||||
|
case furvm::instruction_argument::Type: std::cout << " $__t" << instr.arg.u16; break;
|
||||||
|
case furvm::instruction_argument::Variable: std::cout << " %" << instr.arg.u16; break;
|
||||||
|
case furvm::instruction_argument::Function: std::cout << ' ' << funcNames[instr.arg.s16]; break;
|
||||||
|
case furvm::instruction_argument::Offset: std::cout << " #" << labels[instr.arg.s16]; break;
|
||||||
|
case furvm::instruction_argument::Count: break;
|
||||||
|
}
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
if (!deadLabels.empty()) {
|
||||||
|
std::cerr << "Malformed module: invalid jumps\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
std::cerr << "Exception uncaught: " << ex.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Disassemble into FIR (furlang's IR)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
func println $s32 = native println
|
||||||
|
|
||||||
|
public func main = #main
|
||||||
|
main:
|
||||||
|
push $s32 69
|
||||||
|
call $s32 println
|
||||||
|
ret
|
||||||
|
|||||||
+59
-67
@@ -21,48 +21,36 @@ using namespace std::string_literals;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct instruction {
|
|
||||||
furvm::instruction_t fur{};
|
|
||||||
enum arg_type {
|
|
||||||
None = 0,
|
|
||||||
Type,
|
|
||||||
Constant,
|
|
||||||
Variable,
|
|
||||||
Function,
|
|
||||||
Label,
|
|
||||||
} arg = None;
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
std::unordered_map<enum token::type, instruction> instructions = {
|
std::unordered_map<enum token::type, furvm::instruction_t> instructions = {
|
||||||
{ token::Array, { furvm::instruction_t::Array, instruction::Type } },
|
{ token::Array, furvm::instruction_t::Array },
|
||||||
{ token::Get, { furvm::instruction_t::Get } },
|
{ token::Get, furvm::instruction_t::Get },
|
||||||
{ token::Set, { furvm::instruction_t::Set } },
|
{ token::Set, furvm::instruction_t::Set },
|
||||||
{ token::Drop, { furvm::instruction_t::Drop } },
|
{ token::Drop, furvm::instruction_t::Drop },
|
||||||
{ token::Dup, { furvm::instruction_t::Duplicate } },
|
{ token::Dup, furvm::instruction_t::Duplicate },
|
||||||
{ token::Swap, { furvm::instruction_t::Swap } },
|
{ token::Swap, furvm::instruction_t::Swap },
|
||||||
{ token::Clone, { furvm::instruction_t::Clone } },
|
{ token::Clone, furvm::instruction_t::Clone },
|
||||||
{ token::Ref, { furvm::instruction_t::Reference } },
|
{ token::Ref, furvm::instruction_t::Reference },
|
||||||
{ token::Add, { furvm::instruction_t::Add } },
|
{ token::Add, furvm::instruction_t::Add },
|
||||||
{ token::Sub, { furvm::instruction_t::Sub } },
|
{ token::Sub, furvm::instruction_t::Sub },
|
||||||
{ token::Mul, { furvm::instruction_t::Mul } },
|
{ token::Mul, furvm::instruction_t::Mul },
|
||||||
{ token::Div, { furvm::instruction_t::Div } },
|
{ token::Div, furvm::instruction_t::Div },
|
||||||
{ token::Mod, { furvm::instruction_t::Mod } },
|
{ token::Mod, furvm::instruction_t::Mod },
|
||||||
{ token::Eq, { furvm::instruction_t::Equals } },
|
{ token::Eq, furvm::instruction_t::Equals },
|
||||||
{ token::Neq, { furvm::instruction_t::NotEquals } },
|
{ token::Neq, furvm::instruction_t::NotEquals },
|
||||||
{ token::Lt, { furvm::instruction_t::LessThan } },
|
{ token::Lt, furvm::instruction_t::LessThan },
|
||||||
{ token::Gt, { furvm::instruction_t::GreaterThan } },
|
{ token::Gt, furvm::instruction_t::GreaterThan },
|
||||||
{ token::Le, { furvm::instruction_t::LessEqual } },
|
{ token::Le, furvm::instruction_t::LessEqual },
|
||||||
{ token::Ge, { furvm::instruction_t::GreaterEqual } },
|
{ token::Ge, furvm::instruction_t::GreaterEqual },
|
||||||
{ token::Ptrof, { furvm::instruction_t::Pointerof } },
|
{ token::Ptrof, furvm::instruction_t::Pointerof },
|
||||||
{ token::Sizeof, { furvm::instruction_t::Sizeof } },
|
{ token::Sizeof, furvm::instruction_t::Sizeof },
|
||||||
{ token::Lenof, { furvm::instruction_t::Lengthof } },
|
{ token::Lenof, furvm::instruction_t::Lengthof },
|
||||||
{ token::Load, { furvm::instruction_t::Load, instruction::Variable } },
|
{ token::Load, furvm::instruction_t::Load },
|
||||||
{ token::Store, { furvm::instruction_t::Store, instruction::Variable } },
|
{ token::Store, furvm::instruction_t::Store },
|
||||||
{ token::Call, { furvm::instruction_t::Call, instruction::Function } },
|
{ token::Call, furvm::instruction_t::Call },
|
||||||
{ token::Jmp, { furvm::instruction_t::Jump, instruction::Label } },
|
{ token::Jmp, furvm::instruction_t::Jump },
|
||||||
{ token::Jnz, { furvm::instruction_t::JumpNotZero, instruction::Label } },
|
{ token::Jnz, furvm::instruction_t::JumpNotZero },
|
||||||
{ token::Ret, { furvm::instruction_t::Return } },
|
{ token::Ret, furvm::instruction_t::Return },
|
||||||
};
|
};
|
||||||
// NOLINTEND
|
// NOLINTEND
|
||||||
|
|
||||||
@@ -214,12 +202,12 @@ struct mod_context {
|
|||||||
handle.dispatch();
|
handle.dispatch();
|
||||||
}
|
}
|
||||||
for (auto unknown : label.unknowns) {
|
for (auto unknown : label.unknowns) {
|
||||||
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(label.offset - unknown);
|
const auto jmpOff = static_cast<std::ptrdiff_t>(label.offset) - static_cast<std::ptrdiff_t>(unknown);
|
||||||
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
assert(false); // TODO: Further jumps are not implemented
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
}
|
}
|
||||||
mod.bytecode()[unknown - 1] = jmpOff;
|
mod.bytecode()[unknown - 1] = static_cast<std::int8_t>(jmpOff);
|
||||||
}
|
}
|
||||||
label.functions = {};
|
label.functions = {};
|
||||||
label.unknowns = {};
|
label.unknowns = {};
|
||||||
@@ -453,10 +441,11 @@ struct mod_context {
|
|||||||
case token::Ret: {
|
case token::Ret: {
|
||||||
auto it = instructions.find(result->type);
|
auto it = instructions.find(result->type);
|
||||||
assert(it != instructions.end());
|
assert(it != instructions.end());
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(it->second.fur));
|
furvm::instruction instr{ it->second };
|
||||||
switch (it->second.arg) {
|
instr.arg.type = furvm::instruction::s_arguments[instr.type];
|
||||||
case instruction::None: break;
|
switch (instr.arg.type) {
|
||||||
case instruction::Type: {
|
case furvm::instruction_argument::None: break;
|
||||||
|
case furvm::instruction_argument::Type: {
|
||||||
result = eat_token(lexer, token::Dolar);
|
result = eat_token(lexer, token::Dolar);
|
||||||
if (!result) return result.error;
|
if (!result) return result.error;
|
||||||
result = eat_token(lexer, token::Identifier);
|
result = eat_token(lexer, token::Identifier);
|
||||||
@@ -464,25 +453,19 @@ struct mod_context {
|
|||||||
auto type = types.find(std::string(result->value.string));
|
auto type = types.find(std::string(result->value.string));
|
||||||
if (type == types.end())
|
if (type == types.end())
|
||||||
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
auto id = type->second.id();
|
instr.arg.u32 = type->second.id();
|
||||||
mod.bytecode().push_back((id >> 0) & 0xFF);
|
|
||||||
mod.bytecode().push_back((id >> 8) & 0xFF);
|
|
||||||
mod.bytecode().push_back((id >> 16) & 0xFF);
|
|
||||||
mod.bytecode().push_back((id >> 24) & 0xFF);
|
|
||||||
} break;
|
} break;
|
||||||
case instruction::Constant: {
|
case furvm::instruction_argument::Constant: {
|
||||||
assert(false); // TODO: Unimplemented
|
assert(false); // TODO: Unimplemented
|
||||||
} break;
|
} break;
|
||||||
case instruction::Variable: {
|
case furvm::instruction_argument::Variable: {
|
||||||
result = eat_token(lexer, token::Percent);
|
result = eat_token(lexer, token::Percent);
|
||||||
if (!result) return result.error;
|
if (!result) return result.error;
|
||||||
result = eat_token(lexer, token::Unsigned);
|
result = eat_token(lexer, token::Unsigned);
|
||||||
if (!result) return result.error;
|
if (!result) return result.error;
|
||||||
std::uint16_t var = result->value.uint;
|
instr.arg.u16 = result->value.uint;
|
||||||
mod.bytecode().push_back((var >> 0) & 0xFF);
|
|
||||||
mod.bytecode().push_back((var >> 8) & 0xFF);
|
|
||||||
} break;
|
} break;
|
||||||
case instruction::Function: {
|
case furvm::instruction_argument::Function: {
|
||||||
furvm::function_sig signature;
|
furvm::function_sig signature;
|
||||||
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
||||||
result->type == token::Dolar) {
|
result->type == token::Dolar) {
|
||||||
@@ -499,11 +482,10 @@ struct mod_context {
|
|||||||
auto func = functions.find(std::make_pair(name, signature));
|
auto func = functions.find(std::make_pair(name, signature));
|
||||||
if (func == functions.end())
|
if (func == functions.end())
|
||||||
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
auto id = func->second.id();
|
auto id = func->second.id();
|
||||||
mod.bytecode().push_back((id >> 0) & 0xFF);
|
instr.arg.u16 = id;
|
||||||
mod.bytecode().push_back((id >> 8) & 0xFF);
|
|
||||||
} break;
|
} break;
|
||||||
case instruction::Label: {
|
case furvm::instruction_argument::Offset: {
|
||||||
result = eat_token(lexer, token::Sha256);
|
result = eat_token(lexer, token::Sha256);
|
||||||
if (!result) return result.error;
|
if (!result) return result.error;
|
||||||
result = eat_token(lexer, token::Identifier);
|
result = eat_token(lexer, token::Identifier);
|
||||||
@@ -511,18 +493,28 @@ struct mod_context {
|
|||||||
auto& label = labels[std::string(result->value.string)];
|
auto& label = labels[std::string(result->value.string)];
|
||||||
auto offset = label.offset;
|
auto offset = label.offset;
|
||||||
if (offset == label_context::INVALID) {
|
if (offset == label_context::INVALID) {
|
||||||
label.unknowns.push_back(mod.bytecode().size() + 1);
|
label.unknowns.push_back(mod.bytecode().size() + 2);
|
||||||
mod.bytecode().push_back(0);
|
instr.arg.s8 = 0;
|
||||||
return { generator_error::Success };
|
break;
|
||||||
}
|
}
|
||||||
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(offset - mod.bytecode().size() - 1);
|
const auto jmpOff =
|
||||||
|
static_cast<std::ptrdiff_t>(offset) - static_cast<std::ptrdiff_t>(mod.bytecode().size()) - 2;
|
||||||
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
assert(false); // TODO: Further jumps are not implemented
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
}
|
}
|
||||||
mod.bytecode().push_back(jmpOff);
|
instr.arg.s8 = static_cast<std::int8_t>(jmpOff);
|
||||||
} break;
|
} break;
|
||||||
|
case furvm::instruction_argument::S8:
|
||||||
|
case furvm::instruction_argument::U8:
|
||||||
|
case furvm::instruction_argument::S16:
|
||||||
|
case furvm::instruction_argument::U16:
|
||||||
|
case furvm::instruction_argument::S32:
|
||||||
|
case furvm::instruction_argument::U32:
|
||||||
|
case furvm::instruction_argument::Count:
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
instr.write(mod.bytecode());
|
||||||
return { generator_error::Success };
|
return { generator_error::Success };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef FURC_BACK_IR_HPP
|
#ifndef FURC_MIDDLE_IR_HPP
|
||||||
#define FURC_BACK_IR_HPP
|
#define FURC_MIDDLE_IR_HPP
|
||||||
|
|
||||||
#include "furc/front/ast.hpp"
|
#include "furc/front/ast.hpp"
|
||||||
#include "furlang/arena.hpp"
|
#include "furlang/arena.hpp"
|
||||||
@@ -403,4 +403,4 @@ private:
|
|||||||
|
|
||||||
} // namespace furc
|
} // namespace furc
|
||||||
|
|
||||||
#endif // FURC_BACK_IR_HPP
|
#endif // FURC_MIDDLE_IR_HPP
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#include "furc/back/ir.hpp"
|
|
||||||
#include "furc/front/lexer.hpp"
|
#include "furc/front/lexer.hpp"
|
||||||
#include "furc/front/parser.hpp"
|
#include "furc/front/parser.hpp"
|
||||||
|
#include "furc/middle/ir.hpp"
|
||||||
#include "furlang/arena.hpp"
|
#include "furlang/arena.hpp"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "furc/back/ir.hpp"
|
#include "furc/middle/ir.hpp"
|
||||||
|
|
||||||
#include "furc/front/ast.hpp"
|
#include "furc/front/ast.hpp"
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef FURLANG_VIEW_HPP
|
||||||
|
#define FURLANG_VIEW_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace furlang {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class view {
|
||||||
|
public:
|
||||||
|
constexpr view() noexcept = default;
|
||||||
|
|
||||||
|
constexpr view(const T* data, std::size_t size) noexcept
|
||||||
|
: m_data(data), m_size(size) {}
|
||||||
|
public:
|
||||||
|
constexpr view subview(std::size_t offset, std::size_t count = std::numeric_limits<std::size_t>::max()) {
|
||||||
|
if (count > 0 && offset >= m_size) throw std::runtime_error("offset too large");
|
||||||
|
return { m_data + offset, std::min(m_size - offset, count) };
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator[](std::size_t offset) const {
|
||||||
|
if (offset >= m_size) throw std::runtime_error("out of bounds");
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* data() const { return m_data; }
|
||||||
|
constexpr std::size_t size() const { return m_size; }
|
||||||
|
private:
|
||||||
|
const T* m_data = nullptr;
|
||||||
|
std::size_t m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furlang
|
||||||
|
|
||||||
|
#endif // FURLANG_VIEW_HPP
|
||||||
@@ -75,11 +75,7 @@ class constant;
|
|||||||
|
|
||||||
// instruction.hpp
|
// instruction.hpp
|
||||||
|
|
||||||
/**
|
struct instruction_argument;
|
||||||
* @enum instruction_t
|
|
||||||
* @brief Furvm's instruction type.
|
|
||||||
*/
|
|
||||||
enum class instruction_t : byte;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct instruction
|
* @struct instruction
|
||||||
|
|||||||
@@ -1,221 +1,100 @@
|
|||||||
#ifndef FURVM_INSTRUCTION_HPP
|
#ifndef FURVM_INSTRUCTION_HPP
|
||||||
#define FURVM_INSTRUCTION_HPP
|
#define FURVM_INSTRUCTION_HPP
|
||||||
|
|
||||||
|
#include "furlang/view.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
enum class instruction_t : byte {
|
struct instruction_argument {
|
||||||
/**
|
enum type_e {
|
||||||
* @brief No operation.
|
None = 0,
|
||||||
*/
|
S8,
|
||||||
NoOperation = 0,
|
U8,
|
||||||
|
S16,
|
||||||
|
U16,
|
||||||
|
S32,
|
||||||
|
U32,
|
||||||
|
Constant,
|
||||||
|
Type,
|
||||||
|
Variable,
|
||||||
|
Function,
|
||||||
|
Offset,
|
||||||
|
|
||||||
/**
|
Count,
|
||||||
* @brief Pushes an s8 integer from a byte onto the stack.
|
} type;
|
||||||
*/
|
union {
|
||||||
PushS8,
|
std::int8_t s8;
|
||||||
|
std::uint8_t u8;
|
||||||
|
std::int16_t s16;
|
||||||
|
std::uint16_t u16;
|
||||||
|
std::int32_t s32;
|
||||||
|
std::uint32_t u32;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
static const std::size_t s_sizes[Count];
|
||||||
* @brief Pushes an u8 integer from a byte onto the stack.
|
static const bool s_signedness[Count];
|
||||||
*/
|
|
||||||
PushU8,
|
|
||||||
|
|
||||||
/**
|
std::size_t size() const { return s_sizes[type]; }
|
||||||
* @brief Pushes an s16 integer from two byte onto the stack.
|
bool is_signed() const { return s_signedness[type]; }
|
||||||
*/
|
|
||||||
PushS16,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes an u16 integer from two byte onto the stack.
|
|
||||||
*/
|
|
||||||
PushU16,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes an s32 integer from a byte onto the stack.
|
|
||||||
*/
|
|
||||||
PushS32,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes an u32 integer from a byte onto the stack.
|
|
||||||
*/
|
|
||||||
PushU32,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a constant onto the stack.
|
|
||||||
*
|
|
||||||
* Pushes a constant from the constant pool denoted by two next bytes in little-endian onto the stack.
|
|
||||||
*/
|
|
||||||
PushConstant,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a new array onto the stack.
|
|
||||||
*
|
|
||||||
* Type is the next 4 bytes in little-endian.
|
|
||||||
* If the type is dynamic the array's size will be popped off of the stack.
|
|
||||||
*/
|
|
||||||
Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes an element from an array onto the stack.
|
|
||||||
*/
|
|
||||||
Get,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets an array element.
|
|
||||||
*/
|
|
||||||
Set,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pops top element from the stack.
|
|
||||||
*/
|
|
||||||
Drop,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Duplicates top element on the stack.
|
|
||||||
*/
|
|
||||||
Duplicate,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Swaps two top elements of the stack.
|
|
||||||
*/
|
|
||||||
Swap,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clones top element on the stack.
|
|
||||||
*/
|
|
||||||
Clone,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a new reference onto the stack.
|
|
||||||
*
|
|
||||||
* Pops the top thing from the stack and pushes its reference.
|
|
||||||
*/
|
|
||||||
Reference,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds two things together on the stack.
|
|
||||||
*/
|
|
||||||
Add,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Subtracts two things together on the stack.
|
|
||||||
*/
|
|
||||||
Sub,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Multiplies two things together on the stack.
|
|
||||||
*/
|
|
||||||
Mul,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Divides two things together on the stack.
|
|
||||||
*/
|
|
||||||
Div,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Modulos two things together on the stack.
|
|
||||||
*/
|
|
||||||
Mod,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares two top-most things from the stack for equality.
|
|
||||||
*/
|
|
||||||
Equals,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares two top-most things from the stack for inequality.
|
|
||||||
*/
|
|
||||||
NotEquals,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares if the first top-most thing is less than the second top-most thing.
|
|
||||||
*/
|
|
||||||
LessThan,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares if the first top-most thing is greater than the second top-most thing.
|
|
||||||
*/
|
|
||||||
GreaterThan,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares if the first top-most thing is less than or equal to the second top-most thing.
|
|
||||||
*/
|
|
||||||
LessEqual,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares if the first top-most thing is greater than or equal to the second top-most thing.
|
|
||||||
*/
|
|
||||||
GreaterEqual,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a pointer of popped-off thing onto the stack.
|
|
||||||
*/
|
|
||||||
Pointerof,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a size of popped-off thing onto the stack.
|
|
||||||
*/
|
|
||||||
Sizeof,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a length of popped-off thing onto the stack.
|
|
||||||
*/
|
|
||||||
Lengthof,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pushes a variable onto the stack.
|
|
||||||
*
|
|
||||||
* Fetches a variable denoted by next two bytes in little-endian and pushes it onto the stack.
|
|
||||||
*/
|
|
||||||
Load,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Stores an element from the stack in a variable.
|
|
||||||
*
|
|
||||||
* Pops a thing from the stack and stores it in a variable denoted by next two bytes in little-endian.
|
|
||||||
*/
|
|
||||||
Store,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calls a function.
|
|
||||||
*
|
|
||||||
* Calls a function denoted by next two bytes in little-endian from current frame's module.
|
|
||||||
*/
|
|
||||||
Call,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Jumps to an instruction relative to the current instruction.
|
|
||||||
*
|
|
||||||
* Jumps to an instruction relative to the current instruction with offset denoted by next byte.
|
|
||||||
*/
|
|
||||||
Jump,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Jumps to an instruction relative to the current instruction if top thing on the stack is not zero.
|
|
||||||
*
|
|
||||||
* Jumps to an instruction relative to the current instruction with offset denoted by next byte if the top thing on
|
|
||||||
* the stack is not zero (is true).
|
|
||||||
*/
|
|
||||||
JumpNotZero,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pops the current call frame.
|
|
||||||
*/
|
|
||||||
Return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using instruction_argument_t = instruction_argument::type_e;
|
||||||
|
|
||||||
struct instruction {
|
struct instruction {
|
||||||
instruction_t type; /**< Instruction type. */
|
enum type_e : byte {
|
||||||
|
NoOperation = 0,
|
||||||
|
PushS8,
|
||||||
|
PushU8,
|
||||||
|
PushS16,
|
||||||
|
PushU16,
|
||||||
|
PushS32,
|
||||||
|
PushU32,
|
||||||
|
PushConstant,
|
||||||
|
Array,
|
||||||
|
Get,
|
||||||
|
Set,
|
||||||
|
Drop,
|
||||||
|
Duplicate,
|
||||||
|
Swap,
|
||||||
|
Clone,
|
||||||
|
Reference,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
Equals,
|
||||||
|
NotEquals,
|
||||||
|
LessThan,
|
||||||
|
GreaterThan,
|
||||||
|
LessEqual,
|
||||||
|
GreaterEqual,
|
||||||
|
Pointerof,
|
||||||
|
Sizeof,
|
||||||
|
Lengthof,
|
||||||
|
Load,
|
||||||
|
Store,
|
||||||
|
Call,
|
||||||
|
Jump,
|
||||||
|
JumpNotZero,
|
||||||
|
Return,
|
||||||
|
|
||||||
/**
|
Count,
|
||||||
* @brief Instruction value.
|
} type;
|
||||||
*/
|
instruction_argument arg;
|
||||||
union value {
|
|
||||||
constant_index constant; /**< Constant instruction argument. */
|
static const instruction_argument_t s_arguments[Count];
|
||||||
} value; /**< Instruction value. */
|
|
||||||
|
std::size_t read(furlang::view<std::uint8_t> in);
|
||||||
|
std::size_t write(std::vector<std::uint8_t>& out) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using instruction_t = instruction::type_e;
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
#endif // FURVM_INSTRUCTION_HPP
|
#endif // FURVM_INSTRUCTION_HPP
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define FURVM_MODULE_HPP
|
#define FURVM_MODULE_HPP
|
||||||
|
|
||||||
#include "furlang/utility/hash.hpp"
|
#include "furlang/utility/hash.hpp"
|
||||||
|
#include "furlang/view.hpp"
|
||||||
#include "furvm/function.hpp"
|
#include "furvm/function.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/handle.hpp"
|
#include "furvm/handle.hpp"
|
||||||
@@ -195,7 +196,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @return A constant reference to the bytecode.
|
* @return A constant reference to the bytecode.
|
||||||
*/
|
*/
|
||||||
constexpr const bytecode_t& bytecode() const { return m_bytecode; }
|
furlang::view<std::uint8_t> bytecode_view() const { return { m_bytecode.data(), m_bytecode.size() }; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Emplaces a function in the module's function container.
|
* @brief Emplaces a function in the module's function container.
|
||||||
@@ -284,6 +285,10 @@ public:
|
|||||||
m_functionMap.erase(it);
|
m_functionMap.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handle_container<function_h>& functions() const { return m_functions; }
|
||||||
|
|
||||||
|
const auto& function_map() const { return m_functionMap; }
|
||||||
public:
|
public:
|
||||||
template <typename NameFwd, typename Func>
|
template <typename NameFwd, typename Func>
|
||||||
void set_native_function(NameFwd&& name, Func&& func) {
|
void set_native_function(NameFwd&& name, Func&& func) {
|
||||||
@@ -341,6 +346,8 @@ public:
|
|||||||
void erase_type(Args&&... args) {
|
void erase_type(Args&&... args) {
|
||||||
m_types.erase(std::forward<Args>(args)...);
|
m_types.erase(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handle_container<mod_type_h>& types() const { return m_types; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Prints the module in a bytecode form to an output stream.
|
* @brief Prints the module in a bytecode form to an output stream.
|
||||||
|
|||||||
+16
-36
@@ -1,5 +1,6 @@
|
|||||||
#include "furvm/executor.hpp"
|
#include "furvm/executor.hpp"
|
||||||
|
|
||||||
|
#include "furlang/view.hpp"
|
||||||
#include "furvm/context.hpp" // IWYU pragma: keep
|
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||||
#include "furvm/exceptions.hpp"
|
#include "furvm/exceptions.hpp"
|
||||||
#include "furvm/function.hpp" // IWYU pragma: keep
|
#include "furvm/function.hpp" // IWYU pragma: keep
|
||||||
@@ -131,45 +132,36 @@ void executor::step() {
|
|||||||
|
|
||||||
struct frame& frame = m_frames.top();
|
struct frame& frame = m_frames.top();
|
||||||
|
|
||||||
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte_at(frame.position++));
|
instruction instr{};
|
||||||
switch (instr) {
|
frame.position += instr.read(frame.mod->bytecode_view().subview(frame.position));
|
||||||
|
switch (instr.type) {
|
||||||
case instruction_t::NoOperation: break;
|
case instruction_t::NoOperation: break;
|
||||||
case instruction_t::PushS8: {
|
case instruction_t::PushS8: {
|
||||||
push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
|
push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
|
||||||
static_cast<thing_type::s8>(frame.mod->byte_at(frame.position++));
|
instr.arg.s8;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU8: {
|
case instruction_t::PushU8: {
|
||||||
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() })->get<thing_type::u8>() =
|
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() })->get<thing_type::u8>() =
|
||||||
static_cast<thing_type::u8>(frame.mod->byte_at(frame.position++));
|
instr.arg.u8;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushS16: {
|
case instruction_t::PushS16: {
|
||||||
thing_type::u16 value = frame.mod->byte_at(frame.position++);
|
|
||||||
value |= static_cast<thing_type::u16>(frame.mod->byte_at(frame.position++) << 8);
|
|
||||||
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() })->get<thing_type::s16>() =
|
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() })->get<thing_type::s16>() =
|
||||||
static_cast<thing_type::s16>(value);
|
instr.arg.s16;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU16: {
|
case instruction_t::PushU16: {
|
||||||
thing_type::u16 value = frame.mod->byte_at(frame.position++);
|
|
||||||
value |= static_cast<thing_type::u16>(frame.mod->byte_at(frame.position++) << 8);
|
|
||||||
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() })->get<thing_type::u16>() =
|
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() })->get<thing_type::u16>() =
|
||||||
value;
|
instr.arg.u16;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushS32: {
|
case instruction_t::PushS32: {
|
||||||
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<thing_type::s32>() =
|
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<thing_type::s32>() =
|
||||||
static_cast<thing_type::s32>(frame.mod->byte_at(frame.position++));
|
instr.arg.s8; // NOLINT
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU32: {
|
case instruction_t::PushU32: {
|
||||||
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() })->get<thing_type::u32>() =
|
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() })->get<thing_type::u32>() =
|
||||||
static_cast<thing_type::u32>(frame.mod->byte_at(frame.position++));
|
static_cast<thing_type::u32>(instr.arg.u8);
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Array: {
|
case instruction_t::Array: {
|
||||||
mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte_at(frame.position)) |
|
const auto& type = *mod_to_thing_type(frame.mod, *frame.mod->type_at(instr.arg.u32));
|
||||||
(static_cast<mod_type_id>(frame.mod->byte_at(frame.position + 1)) << 8) |
|
|
||||||
(static_cast<mod_type_id>(frame.mod->byte_at(frame.position + 2)) << 16) |
|
|
||||||
(static_cast<mod_type_id>(frame.mod->byte_at(frame.position + 3)) << 24);
|
|
||||||
frame.position += 4;
|
|
||||||
|
|
||||||
const auto& type = *mod_to_thing_type(frame.mod, *frame.mod->type_at(typeId));
|
|
||||||
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
||||||
throw std::runtime_error("invalid array type");
|
throw std::runtime_error("invalid array type");
|
||||||
|
|
||||||
@@ -304,31 +296,19 @@ void executor::step() {
|
|||||||
length->get<thing_type::u64>() = thing->length();
|
length->get<thing_type::u64>() = thing->length();
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Load: {
|
case instruction_t::Load: {
|
||||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
push_thing(load_thing(instr.arg.u16));
|
||||||
(static_cast<std::uint16_t>(frame.mod->byte_at(frame.position + 1)) << 8);
|
|
||||||
frame.position += 2;
|
|
||||||
push_thing(load_thing(variable));
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Store: {
|
case instruction_t::Store: {
|
||||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
store_thing(instr.arg.u16, std::move(pop_thing()));
|
||||||
(static_cast<std::uint16_t>(frame.mod->byte_at(frame.position + 1)) << 8);
|
|
||||||
frame.position += 2;
|
|
||||||
store_thing(variable, std::move(pop_thing()));
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Call: {
|
case instruction_t::Call: {
|
||||||
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
push_frame(frame.mod, *frame.mod->function_at(instr.arg.u16));
|
||||||
(static_cast<std::uint16_t>(frame.mod->byte_at(frame.position + 1)) << 8);
|
|
||||||
frame.position += 2;
|
|
||||||
push_frame(frame.mod, *frame.mod->function_at(funcId));
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Jump: {
|
case instruction_t::Jump: {
|
||||||
std::int8_t offset = static_cast<std::int8_t>(frame.mod->byte_at(frame.position++));
|
frame.position += instr.arg.s8;
|
||||||
frame.position += offset;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::JumpNotZero: {
|
case instruction_t::JumpNotZero: {
|
||||||
byte offset = frame.mod->byte_at(frame.position++);
|
if (pop_thing()->integer() != 0) frame.position += instr.arg.s8;
|
||||||
auto cond = pop_thing();
|
|
||||||
if (cond->integer() != 0) frame.position += (std::int8_t)offset;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Return: {
|
case instruction_t::Return: {
|
||||||
pop_frame();
|
pop_frame();
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
|
||||||
|
#include "furlang/view.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
const std::size_t instruction_argument::s_sizes[instruction_argument::Count] = {
|
||||||
|
// None:
|
||||||
|
0,
|
||||||
|
// S8:
|
||||||
|
1,
|
||||||
|
// U8:
|
||||||
|
1,
|
||||||
|
// S16:
|
||||||
|
2,
|
||||||
|
// U16:
|
||||||
|
2,
|
||||||
|
// S32
|
||||||
|
1,
|
||||||
|
// U32
|
||||||
|
1,
|
||||||
|
// Constant:
|
||||||
|
4,
|
||||||
|
// Type:
|
||||||
|
4,
|
||||||
|
// Variable:
|
||||||
|
2,
|
||||||
|
// Function:
|
||||||
|
4,
|
||||||
|
// Offset:
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool instruction_argument::s_signedness[instruction_argument::Count] = {
|
||||||
|
// None:
|
||||||
|
false,
|
||||||
|
// S8:
|
||||||
|
true,
|
||||||
|
// U8:
|
||||||
|
false,
|
||||||
|
// S16:
|
||||||
|
true,
|
||||||
|
// U16:
|
||||||
|
false,
|
||||||
|
// S32
|
||||||
|
true,
|
||||||
|
// U32:
|
||||||
|
false,
|
||||||
|
// Constant:
|
||||||
|
false,
|
||||||
|
// Type:
|
||||||
|
false,
|
||||||
|
// Variable:
|
||||||
|
false,
|
||||||
|
// Function:
|
||||||
|
false,
|
||||||
|
// Offset:
|
||||||
|
true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const instruction_argument_t instruction::s_arguments[instruction::Count] = {
|
||||||
|
// NoOperation:
|
||||||
|
instruction_argument::None,
|
||||||
|
// PushS8:
|
||||||
|
instruction_argument::S8,
|
||||||
|
// PushU8:
|
||||||
|
instruction_argument::U8,
|
||||||
|
// PushS16:
|
||||||
|
instruction_argument::S16,
|
||||||
|
// PushU16:
|
||||||
|
instruction_argument::U16,
|
||||||
|
// PushS32:
|
||||||
|
instruction_argument::S32,
|
||||||
|
// PushU32:
|
||||||
|
instruction_argument::U32,
|
||||||
|
// PushConstant:
|
||||||
|
instruction_argument::Constant,
|
||||||
|
// Array:
|
||||||
|
instruction_argument::Type,
|
||||||
|
// Get:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Set:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Drop:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Duplicate:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Swap:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Clone:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Reference:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Add:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Sub:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Mul:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Div:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Mod:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Equals:
|
||||||
|
instruction_argument::None,
|
||||||
|
// NotEquals:
|
||||||
|
instruction_argument::None,
|
||||||
|
// LessThan:
|
||||||
|
instruction_argument::None,
|
||||||
|
// GreaterThan:
|
||||||
|
instruction_argument::None,
|
||||||
|
// LessEqual:
|
||||||
|
instruction_argument::None,
|
||||||
|
// GreaterEqual:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Pointerof:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Sizeof:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Lengthof:
|
||||||
|
instruction_argument::None,
|
||||||
|
// Load:
|
||||||
|
instruction_argument::Variable,
|
||||||
|
// Store:
|
||||||
|
instruction_argument::Variable,
|
||||||
|
// Call:
|
||||||
|
instruction_argument::Function,
|
||||||
|
// Jump:
|
||||||
|
instruction_argument::Offset,
|
||||||
|
// JumpNotZero:
|
||||||
|
instruction_argument::Offset,
|
||||||
|
// Return:
|
||||||
|
instruction_argument::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::size_t instruction::read(furlang::view<std::uint8_t> in) {
|
||||||
|
type = static_cast<type_e>(in[0]);
|
||||||
|
if (type >= Count) throw std::runtime_error("invalid instruction");
|
||||||
|
arg.type = s_arguments[type];
|
||||||
|
|
||||||
|
furlang::view argView = in.subview(1, arg.size());
|
||||||
|
if (argView.size() != arg.size()) throw std::runtime_error("invalid bytecode");
|
||||||
|
switch (argView.size()) {
|
||||||
|
case 0: break;
|
||||||
|
case 1: arg.u8 = argView[0]; break;
|
||||||
|
case 2: arg.u16 = argView[0] | (argView[1] << 8); break;
|
||||||
|
case 4: arg.u32 = argView[0] | (argView[1] << 8) | (argView[2] << 16) | (argView[3] << 24); break;
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 + argView.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t instruction::write(std::vector<std::uint8_t>& out) const {
|
||||||
|
if (type >= Count) throw std::runtime_error("invalid instruction");
|
||||||
|
if (s_arguments[type] != arg.type) throw std::runtime_error("malformed instruction");
|
||||||
|
out.push_back(static_cast<char>(type));
|
||||||
|
switch (arg.size()) {
|
||||||
|
case 0: break;
|
||||||
|
case 1: out.push_back(arg.u8); break;
|
||||||
|
case 2:
|
||||||
|
out.push_back(arg.u16 & 0xFF);
|
||||||
|
out.push_back((arg.u16 >> 8) & 0xFF);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
out.push_back(arg.u32 & 0xFF);
|
||||||
|
out.push_back((arg.u32 >> 8) & 0xFF);
|
||||||
|
out.push_back((arg.u32 >> 16) & 0xFF);
|
||||||
|
out.push_back((arg.u32 >> 24) & 0xFF);
|
||||||
|
break;
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
Reference in New Issue
Block a user