From d47cb71b669cbe312cc200f4513206591a195b1f Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sun, 29 Mar 2026 14:24:40 +1100 Subject: [PATCH] String support (floats are currently segfaulting) --- example/compiler.cpp | 9 ++- src/codegen/x86_64_Linux_libc.cpp | 105 +++++++++++++++++++++++++++--- src/codegen/x86_64_Linux_libc.h | 13 +++- src/tram.h | 18 ++--- 4 files changed, 124 insertions(+), 21 deletions(-) diff --git a/example/compiler.cpp b/example/compiler.cpp index 4669a78..3c11724 100644 --- a/example/compiler.cpp +++ b/example/compiler.cpp @@ -1,14 +1,21 @@ #include +#include int main() { // The outputted program should have an exit status of 10 Tram::Program program; + program.addInstruction(Tram::Instruction(Tram::InstructionType::External, {Tram::Parameter("printf")})); program.addInstruction(Tram::Instruction(Tram::InstructionType::CreateLabel, {Tram::Parameter("main")})); - program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Literal(10))})); + program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Literal("%f"))})); + program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::f0), Tram::Parameter(Tram::Literal(3.14159))})); + program.addInstruction(Tram::Instruction(Tram::InstructionType::Call, {Tram::Parameter("printf")})); + program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Literal(0))})); Tram::Compiler compiler(program); compiler.setTarget(Tram::Target::x86_64_Linux_libc); + compiler.setLogLevel(Tram::LogLevel::All); compiler.compile(); + std::cout << compiler.getAsm().value(); compiler.assembleAndLink("test"); } diff --git a/src/codegen/x86_64_Linux_libc.cpp b/src/codegen/x86_64_Linux_libc.cpp index e9d55b9..5c19193 100644 --- a/src/codegen/x86_64_Linux_libc.cpp +++ b/src/codegen/x86_64_Linux_libc.cpp @@ -4,18 +4,47 @@ namespace Tram { std::optional x86_64_Linux_libc_Compiler::compile(const Program &program, Compiler &compiler) { + // Pre-process which labels are available for (const auto& instruction : program.getInstructions()) { if (instruction.getType() == InstructionType::CreateLabel) { if (instruction.getParameter(0) && instruction.getParameter(0).value().getType() == ParameterType::Variable) { if (std::ranges::find(labels, instruction.getParameter(0).value().getVariable()) == labels.end()) { labels.push_back(instruction.getParameter(0).value().getVariable()); } else { - compiler.errorLog("createlabel: label already exists"); + compiler.errorLog("create label: label already exists"); } } else { - compiler.errorLog("createlabel: expecting a variable argument"); + compiler.errorLog("create label: expecting a variable argument"); } } + + // Pre-process complex data types + for (size_t i = 0; auto arg = instruction.getParameter(i); i++) { + if (arg.value().getType() == ParameterType::Literal) { + if (arg.value().getLiteral().getType() == LiteralType::String) { + if (std::ranges::find(strings, arg.value().getLiteral().getString()) == strings.end()) { + compiler.debugLog("found new string " + arg.value().getLiteral().getString() + ", adding to strings"); + strings.push_back(arg.value().getLiteral().getString()); + } + } + if (arg.value().getLiteral().getType() == LiteralType::Float) { + if (std::ranges::find(floats, arg.value().getLiteral().getFloat()) == floats.end()) { + compiler.debugLog("found new float " + std::to_string(arg.value().getLiteral().getFloat()) + ", adding to strings"); + floats.push_back(arg.value().getLiteral().getFloat()); + } + } + } + } + } + + // Add complex data types to .data section for use in the program + for (size_t i = 0; i < strings.size(); i++) { + compiler.debugLog("adding string " + strings[i] + " to .data section"); + head += " __tram_string_" + std::to_string(i) + " db \"" + strings[i] + "\", 0\n"; + } + for (size_t i = 0; i < floats.size(); i++) { + compiler.debugLog("adding float " + std::to_string(floats[i]) + " to .data section"); + head += " __tram_float_" + std::to_string(i) + ": dq " + std::to_string(floats[i]) + "\n"; } for (const auto& instruction : program.getInstructions()) { @@ -93,6 +122,8 @@ namespace Tram { } LiteralType dataType = LiteralType::None; + // 0 means mov, 1 means movsd, 2 means lea + int instruction = 0; std::string destination; std::string source; @@ -118,16 +149,24 @@ namespace Tram { } case ParameterType::Literal: { dataType = param1.value().getLiteral().getType(); - // FIXME do float/double later switch (dataType) { - case LiteralType::Float: { - compiler.errorLog("put: float is a WIP"); - return {}; - } case LiteralType::Int: { source = std::to_string(param1.value().getLiteral().getInt()); break; } + case LiteralType::Float: { + size_t index = std::ranges::find(floats, param1.value().getLiteral().getFloat()) - floats.begin(); + source = "[__tram_float_" + std::to_string(index) + "]"; + instruction = 1; + break; + } + case LiteralType::String: { + size_t index = std::ranges::find(strings, param1.value().getLiteral().getString()) - strings.begin(); + source = "[rel __tram_string_" + std::to_string(index) + "]"; + instruction = 2; + dataType = LiteralType::Int; + break; + } case LiteralType::None: { compiler.errorLog("put: source literal has no data"); return {}; @@ -173,7 +212,18 @@ namespace Tram { return {}; } } - std::string mnemonic = (dataType == LiteralType::Float) ? "movsd" : "mov"; + std::string mnemonic = "mov"; + switch (instruction) { + case 0: + mnemonic = "mov"; + break; + case 1: + mnemonic = "movsd"; + break; + case 2: + mnemonic = "lea"; + break; + } body += " " + mnemonic + " " + destination + ", " + source + "\n"; break; } @@ -181,11 +231,12 @@ namespace Tram { compiler.allLog("instruction: label"); std::optional param = instruction.getParameter(0); if (!param.has_value()) { - compiler.errorLog("put: expecting at least 1 parameter"); + compiler.errorLog("create label: expecting at least 1 parameter"); return {}; } if (param.value().getType() != ParameterType::Variable) { - compiler.errorLog("put: parameter 0 must be an identifier"); + compiler.errorLog("create label: parameter 0 must be an identifier"); + return {}; } body += param.value().getVariable() + ":\n"; break; @@ -213,6 +264,40 @@ namespace Tram { if (!param.has_value()) { compiler.errorLog("jump if zero: expecting at least 1 parameter"); } + if (param.value().getType() != ParameterType::Variable) { + compiler.errorLog("jump if zero: parameter 0 must be an identifier"); + return {}; + } + if (std::ranges::find(labels, instruction.getParameter(0).value().getVariable()) == labels.end()) { + compiler.errorLog("jump if zero: unknown label " + param.value().getVariable()); + return {}; + } + if (registers[Register::r0] != LiteralType::None) { + // compare Tram r0, which is x86 r8 + body += " cmp r8, 0\n"; + body += " je " + param.value().getVariable() + "\n"; + } + break; + } + case InstructionType::JumpNotZero: { + compiler.allLog("instruction: jump not zero"); + std::optional param = instruction.getParameter(0); + if (!param.has_value()) { + compiler.errorLog("jump not zero: expecting at least 1 parameter"); + } + if (param.value().getType() != ParameterType::Variable) { + compiler.errorLog("jump not zero: parameter 0 must be an identifier"); + return {}; + } + if (std::ranges::find(labels, instruction.getParameter(0).value().getVariable()) == labels.end()) { + compiler.errorLog("jump not zero: unknown label " + param.value().getVariable()); + return {}; + } + if (registers[Register::r0] != LiteralType::None) { + // compare Tram r0, which is x86 r8 + body += " cmp r8, 0\n"; + body += " jne " + param.value().getVariable() + "\n"; + } break; } case InstructionType::None: diff --git a/src/codegen/x86_64_Linux_libc.h b/src/codegen/x86_64_Linux_libc.h index 5b7e88e..b2a758e 100644 --- a/src/codegen/x86_64_Linux_libc.h +++ b/src/codegen/x86_64_Linux_libc.h @@ -18,10 +18,19 @@ namespace Tram { {Register::f12, LiteralType::None}, {Register::f13, LiteralType::None}, {Register::f14, LiteralType::None}, {Register::f15, LiteralType::None} }; + // Map of variables in use std::unordered_map variables = {}; + + // Stores which labels are available std::vector labels = {}; - std::string registerToAsmRegister(Register reg) { + // These vectors store noninteger data types + // Data is mapped to the variable [__tram_DTYPE_X], + // where X is the index of the string, and DTYPE is the type of the data + std::vector strings = {}; + std::vector floats = {}; + + static std::string registerToAsmRegister(Register reg) { if (reg >= Register::r0 && reg <= Register::r15) { /* * allocation model: @@ -39,7 +48,7 @@ namespace Tram { * allocation model: * Tram registers f0-f15 are directly mapped to x64 registers xmm0-xmm15 */ - int reg_idx = static_cast(reg) - static_cast(Register::f0); + const int reg_idx = static_cast(reg) - static_cast(Register::f0); return "xmm" + std::to_string(reg_idx); } return ""; diff --git a/src/tram.h b/src/tram.h index 32db7b3..deb8fc9 100644 --- a/src/tram.h +++ b/src/tram.h @@ -26,7 +26,7 @@ namespace Tram { // General purpose registers, adapts to whichever data type - // r0-r15 - Integer registers + // r0-r15 - Integer registers, also used for pointers (such as to strings) // f0-f15 - Floating point registers enum class Register { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, @@ -54,25 +54,27 @@ namespace Tram { }; enum class LiteralType { - Int, Float, None + Int, Float, String, None }; class Literal { LiteralType type = LiteralType::None; - std::variant value; + std::variant value; public: Literal() = default; - explicit Literal(int64_t value) : type(LiteralType::Int), value(value) {} - explicit Literal(int value) : type(LiteralType::Int), value(value) {} - explicit Literal(double value) : type(LiteralType::Float), value(value) {} + explicit Literal(int64_t value) : type(LiteralType::Int), value(value) {} + explicit Literal(int value) : type(LiteralType::Int), value(value) {} + explicit Literal(double value) : type(LiteralType::Float), value(value) {} + explicit Literal(std::string value) : type(LiteralType::String), value(value) {} Literal(const Literal& self) = default; [[nodiscard]] LiteralType getType() const { return type; } - [[nodiscard]] int64_t getInt() const { return std::get(value); } - [[nodiscard]] double getFloat() const { return std::get(value); } + [[nodiscard]] int64_t getInt() const { return std::get(value); } + [[nodiscard]] double getFloat() const { return std::get(value); } + [[nodiscard]] std::string getString() const { return std::get(value); } };