diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e97efd..8f808cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/disfuras/CMakeLists.txt b/disfuras/CMakeLists.txt new file mode 100644 index 0000000..ca41323 --- /dev/null +++ b/disfuras/CMakeLists.txt @@ -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) diff --git a/disfuras/src/main.cpp b/disfuras/src/main.cpp new file mode 100644 index 0000000..b03b875 --- /dev/null +++ b/disfuras/src/main.cpp @@ -0,0 +1,238 @@ +#include "furvm/instruction.hpp" +#include "furvm/module.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +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] << " \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 funcNames; + std::size_t unnamedCounter = 0; + + std::unordered_map labels; + std::unordered_set 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)