String support (floats are currently segfaulting)
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 "";
|
||||
|
||||
18
src/tram.h
18
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<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); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user