String support (floats are currently segfaulting)

This commit is contained in:
2026-03-29 14:24:40 +11:00
parent 3d4caaad40
commit d47cb71b66
4 changed files with 124 additions and 21 deletions

View File

@@ -1,14 +1,21 @@
#include <tram.h>
#include <iostream>
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");
}

View File

@@ -4,18 +4,47 @@
namespace Tram {
std::optional<std::string> 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<Parameter> 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<Parameter> 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:

View File

@@ -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<std::string, LiteralType> variables = {};
// Stores which labels are available
std::vector<std::string> 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<std::string> strings = {};
std::vector<double> 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<int>(reg) - static_cast<int>(Register::f0);
const int reg_idx = static_cast<int>(reg) - static_cast<int>(Register::f0);
return "xmm" + std::to_string(reg_idx);
}
return "";

View File

@@ -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<int64_t, double> value;
std::variant<int64_t, double, std::string> 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<int64_t>(value); }
[[nodiscard]] double getFloat() const { return std::get<double>(value); }
[[nodiscard]] int64_t getInt() const { return std::get<int64_t>(value); }
[[nodiscard]] double getFloat() const { return std::get<double>(value); }
[[nodiscard]] std::string getString() const { return std::get<std::string>(value); }
};