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(furc)
|
||||
add_subdirectory(furas)
|
||||
add_subdirectory(disfuras)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
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
|
||||
|
||||
+58
-66
@@ -21,48 +21,36 @@ using namespace std::string_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
struct instruction {
|
||||
furvm::instruction_t fur{};
|
||||
enum arg_type {
|
||||
None = 0,
|
||||
Type,
|
||||
Constant,
|
||||
Variable,
|
||||
Function,
|
||||
Label,
|
||||
} arg = None;
|
||||
};
|
||||
|
||||
// NOLINTBEGIN
|
||||
std::unordered_map<enum token::type, instruction> instructions = {
|
||||
{ token::Array, { furvm::instruction_t::Array, instruction::Type } },
|
||||
{ token::Get, { furvm::instruction_t::Get } },
|
||||
{ token::Set, { furvm::instruction_t::Set } },
|
||||
{ token::Drop, { furvm::instruction_t::Drop } },
|
||||
{ token::Dup, { furvm::instruction_t::Duplicate } },
|
||||
{ token::Swap, { furvm::instruction_t::Swap } },
|
||||
{ token::Clone, { furvm::instruction_t::Clone } },
|
||||
{ token::Ref, { furvm::instruction_t::Reference } },
|
||||
{ token::Add, { furvm::instruction_t::Add } },
|
||||
{ token::Sub, { furvm::instruction_t::Sub } },
|
||||
{ token::Mul, { furvm::instruction_t::Mul } },
|
||||
{ token::Div, { furvm::instruction_t::Div } },
|
||||
{ token::Mod, { furvm::instruction_t::Mod } },
|
||||
{ token::Eq, { furvm::instruction_t::Equals } },
|
||||
{ token::Neq, { furvm::instruction_t::NotEquals } },
|
||||
{ token::Lt, { furvm::instruction_t::LessThan } },
|
||||
{ token::Gt, { furvm::instruction_t::GreaterThan } },
|
||||
{ token::Le, { furvm::instruction_t::LessEqual } },
|
||||
{ token::Ge, { furvm::instruction_t::GreaterEqual } },
|
||||
{ token::Ptrof, { furvm::instruction_t::Pointerof } },
|
||||
{ token::Sizeof, { furvm::instruction_t::Sizeof } },
|
||||
{ token::Lenof, { furvm::instruction_t::Lengthof } },
|
||||
{ token::Load, { furvm::instruction_t::Load, instruction::Variable } },
|
||||
{ token::Store, { furvm::instruction_t::Store, instruction::Variable } },
|
||||
{ token::Call, { furvm::instruction_t::Call, instruction::Function } },
|
||||
{ token::Jmp, { furvm::instruction_t::Jump, instruction::Label } },
|
||||
{ token::Jnz, { furvm::instruction_t::JumpNotZero, instruction::Label } },
|
||||
{ token::Ret, { furvm::instruction_t::Return } },
|
||||
std::unordered_map<enum token::type, furvm::instruction_t> instructions = {
|
||||
{ token::Array, furvm::instruction_t::Array },
|
||||
{ token::Get, furvm::instruction_t::Get },
|
||||
{ token::Set, furvm::instruction_t::Set },
|
||||
{ token::Drop, furvm::instruction_t::Drop },
|
||||
{ token::Dup, furvm::instruction_t::Duplicate },
|
||||
{ token::Swap, furvm::instruction_t::Swap },
|
||||
{ token::Clone, furvm::instruction_t::Clone },
|
||||
{ token::Ref, furvm::instruction_t::Reference },
|
||||
{ token::Add, furvm::instruction_t::Add },
|
||||
{ token::Sub, furvm::instruction_t::Sub },
|
||||
{ token::Mul, furvm::instruction_t::Mul },
|
||||
{ token::Div, furvm::instruction_t::Div },
|
||||
{ token::Mod, furvm::instruction_t::Mod },
|
||||
{ token::Eq, furvm::instruction_t::Equals },
|
||||
{ token::Neq, furvm::instruction_t::NotEquals },
|
||||
{ token::Lt, furvm::instruction_t::LessThan },
|
||||
{ token::Gt, furvm::instruction_t::GreaterThan },
|
||||
{ token::Le, furvm::instruction_t::LessEqual },
|
||||
{ token::Ge, furvm::instruction_t::GreaterEqual },
|
||||
{ token::Ptrof, furvm::instruction_t::Pointerof },
|
||||
{ token::Sizeof, furvm::instruction_t::Sizeof },
|
||||
{ token::Lenof, furvm::instruction_t::Lengthof },
|
||||
{ token::Load, furvm::instruction_t::Load },
|
||||
{ token::Store, furvm::instruction_t::Store },
|
||||
{ token::Call, furvm::instruction_t::Call },
|
||||
{ token::Jmp, furvm::instruction_t::Jump },
|
||||
{ token::Jnz, furvm::instruction_t::JumpNotZero },
|
||||
{ token::Ret, furvm::instruction_t::Return },
|
||||
};
|
||||
// NOLINTEND
|
||||
|
||||
@@ -214,12 +202,12 @@ struct mod_context {
|
||||
handle.dispatch();
|
||||
}
|
||||
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() ||
|
||||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||
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.unknowns = {};
|
||||
@@ -453,10 +441,11 @@ struct mod_context {
|
||||
case token::Ret: {
|
||||
auto it = instructions.find(result->type);
|
||||
assert(it != instructions.end());
|
||||
mod.bytecode().push_back(static_cast<furvm::byte>(it->second.fur));
|
||||
switch (it->second.arg) {
|
||||
case instruction::None: break;
|
||||
case instruction::Type: {
|
||||
furvm::instruction instr{ it->second };
|
||||
instr.arg.type = furvm::instruction::s_arguments[instr.type];
|
||||
switch (instr.arg.type) {
|
||||
case furvm::instruction_argument::None: break;
|
||||
case furvm::instruction_argument::Type: {
|
||||
result = eat_token(lexer, token::Dolar);
|
||||
if (!result) return result.error;
|
||||
result = eat_token(lexer, token::Identifier);
|
||||
@@ -464,25 +453,19 @@ struct mod_context {
|
||||
auto type = types.find(std::string(result->value.string));
|
||||
if (type == types.end())
|
||||
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||
auto id = 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);
|
||||
instr.arg.u32 = type->second.id();
|
||||
} break;
|
||||
case instruction::Constant: {
|
||||
case furvm::instruction_argument::Constant: {
|
||||
assert(false); // TODO: Unimplemented
|
||||
} break;
|
||||
case instruction::Variable: {
|
||||
case furvm::instruction_argument::Variable: {
|
||||
result = eat_token(lexer, token::Percent);
|
||||
if (!result) return result.error;
|
||||
result = eat_token(lexer, token::Unsigned);
|
||||
if (!result) return result.error;
|
||||
std::uint16_t var = result->value.uint;
|
||||
mod.bytecode().push_back((var >> 0) & 0xFF);
|
||||
mod.bytecode().push_back((var >> 8) & 0xFF);
|
||||
instr.arg.u16 = result->value.uint;
|
||||
} break;
|
||||
case instruction::Function: {
|
||||
case furvm::instruction_argument::Function: {
|
||||
furvm::function_sig signature;
|
||||
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
||||
result->type == token::Dolar) {
|
||||
@@ -500,10 +483,9 @@ struct mod_context {
|
||||
if (func == functions.end())
|
||||
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||
auto id = func->second.id();
|
||||
mod.bytecode().push_back((id >> 0) & 0xFF);
|
||||
mod.bytecode().push_back((id >> 8) & 0xFF);
|
||||
instr.arg.u16 = id;
|
||||
} break;
|
||||
case instruction::Label: {
|
||||
case furvm::instruction_argument::Offset: {
|
||||
result = eat_token(lexer, token::Sha256);
|
||||
if (!result) return result.error;
|
||||
result = eat_token(lexer, token::Identifier);
|
||||
@@ -511,18 +493,28 @@ struct mod_context {
|
||||
auto& label = labels[std::string(result->value.string)];
|
||||
auto offset = label.offset;
|
||||
if (offset == label_context::INVALID) {
|
||||
label.unknowns.push_back(mod.bytecode().size() + 1);
|
||||
mod.bytecode().push_back(0);
|
||||
return { generator_error::Success };
|
||||
label.unknowns.push_back(mod.bytecode().size() + 2);
|
||||
instr.arg.s8 = 0;
|
||||
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() ||
|
||||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||
assert(false); // TODO: Further jumps are not implemented
|
||||
}
|
||||
mod.bytecode().push_back(jmpOff);
|
||||
instr.arg.s8 = static_cast<std::int8_t>(jmpOff);
|
||||
} 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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef FURC_BACK_IR_HPP
|
||||
#define FURC_BACK_IR_HPP
|
||||
#ifndef FURC_MIDDLE_IR_HPP
|
||||
#define FURC_MIDDLE_IR_HPP
|
||||
|
||||
#include "furc/front/ast.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
@@ -403,4 +403,4 @@ private:
|
||||
|
||||
} // 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/parser.hpp"
|
||||
#include "furc/middle/ir.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
int main(void) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "furc/back/ir.hpp"
|
||||
#include "furc/middle/ir.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
|
||||
|
||||
/**
|
||||
* @enum instruction_t
|
||||
* @brief Furvm's instruction type.
|
||||
*/
|
||||
enum class instruction_t : byte;
|
||||
struct instruction_argument;
|
||||
|
||||
/**
|
||||
* @struct instruction
|
||||
|
||||
@@ -1,221 +1,100 @@
|
||||
#ifndef FURVM_INSTRUCTION_HPP
|
||||
#define FURVM_INSTRUCTION_HPP
|
||||
|
||||
#include "furlang/view.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
enum class instruction_t : byte {
|
||||
/**
|
||||
* @brief No operation.
|
||||
*/
|
||||
NoOperation = 0,
|
||||
struct instruction_argument {
|
||||
enum type_e {
|
||||
None = 0,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
Constant,
|
||||
Type,
|
||||
Variable,
|
||||
Function,
|
||||
Offset,
|
||||
|
||||
/**
|
||||
* @brief Pushes an s8 integer from a byte onto the stack.
|
||||
*/
|
||||
PushS8,
|
||||
Count,
|
||||
} type;
|
||||
union {
|
||||
std::int8_t s8;
|
||||
std::uint8_t u8;
|
||||
std::int16_t s16;
|
||||
std::uint16_t u16;
|
||||
std::int32_t s32;
|
||||
std::uint32_t u32;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Pushes an u8 integer from a byte onto the stack.
|
||||
*/
|
||||
PushU8,
|
||||
static const std::size_t s_sizes[Count];
|
||||
static const bool s_signedness[Count];
|
||||
|
||||
/**
|
||||
* @brief Pushes an s16 integer from two byte onto the stack.
|
||||
*/
|
||||
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,
|
||||
std::size_t size() const { return s_sizes[type]; }
|
||||
bool is_signed() const { return s_signedness[type]; }
|
||||
};
|
||||
|
||||
using instruction_argument_t = instruction_argument::type_e;
|
||||
|
||||
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,
|
||||
|
||||
/**
|
||||
* @brief Instruction value.
|
||||
*/
|
||||
union value {
|
||||
constant_index constant; /**< Constant instruction argument. */
|
||||
} value; /**< Instruction value. */
|
||||
Count,
|
||||
} type;
|
||||
instruction_argument arg;
|
||||
|
||||
static const instruction_argument_t s_arguments[Count];
|
||||
|
||||
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
|
||||
|
||||
#endif // FURVM_INSTRUCTION_HPP
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define FURVM_MODULE_HPP
|
||||
|
||||
#include "furlang/utility/hash.hpp"
|
||||
#include "furlang/view.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
@@ -195,7 +196,7 @@ public:
|
||||
*
|
||||
* @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:
|
||||
/**
|
||||
* @brief Emplaces a function in the module's function container.
|
||||
@@ -284,6 +285,10 @@ public:
|
||||
m_functionMap.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
const handle_container<function_h>& functions() const { return m_functions; }
|
||||
|
||||
const auto& function_map() const { return m_functionMap; }
|
||||
public:
|
||||
template <typename NameFwd, typename Func>
|
||||
void set_native_function(NameFwd&& name, Func&& func) {
|
||||
@@ -341,6 +346,8 @@ public:
|
||||
void erase_type(Args&&... args) {
|
||||
m_types.erase(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const handle_container<mod_type_h>& types() const { return m_types; }
|
||||
public:
|
||||
/**
|
||||
* @brief Prints the module in a bytecode form to an output stream.
|
||||
|
||||
+16
-36
@@ -1,5 +1,6 @@
|
||||
#include "furvm/executor.hpp"
|
||||
|
||||
#include "furlang/view.hpp"
|
||||
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||
#include "furvm/exceptions.hpp"
|
||||
#include "furvm/function.hpp" // IWYU pragma: keep
|
||||
@@ -131,45 +132,36 @@ void executor::step() {
|
||||
|
||||
struct frame& frame = m_frames.top();
|
||||
|
||||
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte_at(frame.position++));
|
||||
switch (instr) {
|
||||
instruction instr{};
|
||||
frame.position += instr.read(frame.mod->bytecode_view().subview(frame.position));
|
||||
switch (instr.type) {
|
||||
case instruction_t::NoOperation: break;
|
||||
case instruction_t::PushS8: {
|
||||
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;
|
||||
case instruction_t::PushU8: {
|
||||
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;
|
||||
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>() =
|
||||
static_cast<thing_type::s16>(value);
|
||||
instr.arg.s16;
|
||||
} break;
|
||||
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>() =
|
||||
value;
|
||||
instr.arg.u16;
|
||||
} break;
|
||||
case instruction_t::PushS32: {
|
||||
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;
|
||||
case instruction_t::PushU32: {
|
||||
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;
|
||||
case instruction_t::Array: {
|
||||
mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte_at(frame.position)) |
|
||||
(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));
|
||||
const auto& type = *mod_to_thing_type(frame.mod, *frame.mod->type_at(instr.arg.u32));
|
||||
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
||||
throw std::runtime_error("invalid array type");
|
||||
|
||||
@@ -304,31 +296,19 @@ void executor::step() {
|
||||
length->get<thing_type::u64>() = thing->length();
|
||||
} break;
|
||||
case instruction_t::Load: {
|
||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
||||
(static_cast<std::uint16_t>(frame.mod->byte_at(frame.position + 1)) << 8);
|
||||
frame.position += 2;
|
||||
push_thing(load_thing(variable));
|
||||
push_thing(load_thing(instr.arg.u16));
|
||||
} break;
|
||||
case instruction_t::Store: {
|
||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
||||
(static_cast<std::uint16_t>(frame.mod->byte_at(frame.position + 1)) << 8);
|
||||
frame.position += 2;
|
||||
store_thing(variable, std::move(pop_thing()));
|
||||
store_thing(instr.arg.u16, std::move(pop_thing()));
|
||||
} break;
|
||||
case instruction_t::Call: {
|
||||
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte_at(frame.position)) |
|
||||
(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));
|
||||
push_frame(frame.mod, *frame.mod->function_at(instr.arg.u16));
|
||||
} break;
|
||||
case instruction_t::Jump: {
|
||||
std::int8_t offset = static_cast<std::int8_t>(frame.mod->byte_at(frame.position++));
|
||||
frame.position += offset;
|
||||
frame.position += instr.arg.s8;
|
||||
} break;
|
||||
case instruction_t::JumpNotZero: {
|
||||
byte offset = frame.mod->byte_at(frame.position++);
|
||||
auto cond = pop_thing();
|
||||
if (cond->integer() != 0) frame.position += (std::int8_t)offset;
|
||||
if (pop_thing()->integer() != 0) frame.position += instr.arg.s8;
|
||||
} break;
|
||||
case instruction_t::Return: {
|
||||
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