String support (floats are currently segfaulting)
This commit is contained in:
@@ -1,14 +1,21 @@
|
|||||||
#include <tram.h>
|
#include <tram.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
// The outputted program should have an exit status of 10
|
// The outputted program should have an exit status of 10
|
||||||
|
|
||||||
Tram::Program program;
|
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::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);
|
Tram::Compiler compiler(program);
|
||||||
compiler.setTarget(Tram::Target::x86_64_Linux_libc);
|
compiler.setTarget(Tram::Target::x86_64_Linux_libc);
|
||||||
|
compiler.setLogLevel(Tram::LogLevel::All);
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
|
std::cout << compiler.getAsm().value();
|
||||||
compiler.assembleAndLink("test");
|
compiler.assembleAndLink("test");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,47 @@
|
|||||||
namespace Tram {
|
namespace Tram {
|
||||||
std::optional<std::string> x86_64_Linux_libc_Compiler::compile(const Program &program, Compiler &compiler) {
|
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()) {
|
for (const auto& instruction : program.getInstructions()) {
|
||||||
if (instruction.getType() == InstructionType::CreateLabel) {
|
if (instruction.getType() == InstructionType::CreateLabel) {
|
||||||
if (instruction.getParameter(0) && instruction.getParameter(0).value().getType() == ParameterType::Variable) {
|
if (instruction.getParameter(0) && instruction.getParameter(0).value().getType() == ParameterType::Variable) {
|
||||||
if (std::ranges::find(labels, instruction.getParameter(0).value().getVariable()) == labels.end()) {
|
if (std::ranges::find(labels, instruction.getParameter(0).value().getVariable()) == labels.end()) {
|
||||||
labels.push_back(instruction.getParameter(0).value().getVariable());
|
labels.push_back(instruction.getParameter(0).value().getVariable());
|
||||||
} else {
|
} else {
|
||||||
compiler.errorLog("createlabel: label already exists");
|
compiler.errorLog("create label: label already exists");
|
||||||
}
|
}
|
||||||
} else {
|
} 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()) {
|
for (const auto& instruction : program.getInstructions()) {
|
||||||
@@ -93,6 +122,8 @@ namespace Tram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LiteralType dataType = LiteralType::None;
|
LiteralType dataType = LiteralType::None;
|
||||||
|
// 0 means mov, 1 means movsd, 2 means lea
|
||||||
|
int instruction = 0;
|
||||||
std::string destination;
|
std::string destination;
|
||||||
std::string source;
|
std::string source;
|
||||||
|
|
||||||
@@ -118,16 +149,24 @@ namespace Tram {
|
|||||||
}
|
}
|
||||||
case ParameterType::Literal: {
|
case ParameterType::Literal: {
|
||||||
dataType = param1.value().getLiteral().getType();
|
dataType = param1.value().getLiteral().getType();
|
||||||
// FIXME do float/double later
|
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case LiteralType::Float: {
|
|
||||||
compiler.errorLog("put: float is a WIP");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
case LiteralType::Int: {
|
case LiteralType::Int: {
|
||||||
source = std::to_string(param1.value().getLiteral().getInt());
|
source = std::to_string(param1.value().getLiteral().getInt());
|
||||||
break;
|
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: {
|
case LiteralType::None: {
|
||||||
compiler.errorLog("put: source literal has no data");
|
compiler.errorLog("put: source literal has no data");
|
||||||
return {};
|
return {};
|
||||||
@@ -173,7 +212,18 @@ namespace Tram {
|
|||||||
return {};
|
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";
|
body += " " + mnemonic + " " + destination + ", " + source + "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -181,11 +231,12 @@ namespace Tram {
|
|||||||
compiler.allLog("instruction: label");
|
compiler.allLog("instruction: label");
|
||||||
std::optional<Parameter> param = instruction.getParameter(0);
|
std::optional<Parameter> param = instruction.getParameter(0);
|
||||||
if (!param.has_value()) {
|
if (!param.has_value()) {
|
||||||
compiler.errorLog("put: expecting at least 1 parameter");
|
compiler.errorLog("create label: expecting at least 1 parameter");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (param.value().getType() != ParameterType::Variable) {
|
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";
|
body += param.value().getVariable() + ":\n";
|
||||||
break;
|
break;
|
||||||
@@ -213,6 +264,40 @@ namespace Tram {
|
|||||||
if (!param.has_value()) {
|
if (!param.has_value()) {
|
||||||
compiler.errorLog("jump if zero: expecting at least 1 parameter");
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case InstructionType::None:
|
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}
|
{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 = {};
|
std::unordered_map<std::string, LiteralType> variables = {};
|
||||||
|
|
||||||
|
// Stores which labels are available
|
||||||
std::vector<std::string> labels = {};
|
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) {
|
if (reg >= Register::r0 && reg <= Register::r15) {
|
||||||
/*
|
/*
|
||||||
* allocation model:
|
* allocation model:
|
||||||
@@ -39,7 +48,7 @@ namespace Tram {
|
|||||||
* allocation model:
|
* allocation model:
|
||||||
* Tram registers f0-f15 are directly mapped to x64 registers xmm0-xmm15
|
* 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 "xmm" + std::to_string(reg_idx);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
18
src/tram.h
18
src/tram.h
@@ -26,7 +26,7 @@
|
|||||||
namespace Tram {
|
namespace Tram {
|
||||||
|
|
||||||
// General purpose registers, adapts to whichever data type
|
// 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
|
// f0-f15 - Floating point registers
|
||||||
enum class Register {
|
enum class Register {
|
||||||
r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15,
|
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 {
|
enum class LiteralType {
|
||||||
Int, Float, None
|
Int, Float, String, None
|
||||||
};
|
};
|
||||||
|
|
||||||
class Literal {
|
class Literal {
|
||||||
LiteralType type = LiteralType::None;
|
LiteralType type = LiteralType::None;
|
||||||
std::variant<int64_t, double> value;
|
std::variant<int64_t, double, std::string> value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Literal() = default;
|
Literal() = default;
|
||||||
|
|
||||||
explicit Literal(int64_t value) : type(LiteralType::Int), value(value) {}
|
explicit Literal(int64_t value) : type(LiteralType::Int), value(value) {}
|
||||||
explicit Literal(int 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(double value) : type(LiteralType::Float), value(value) {}
|
||||||
|
explicit Literal(std::string value) : type(LiteralType::String), value(value) {}
|
||||||
Literal(const Literal& self) = default;
|
Literal(const Literal& self) = default;
|
||||||
|
|
||||||
[[nodiscard]] LiteralType getType() const { return type; }
|
[[nodiscard]] LiteralType getType() const { return type; }
|
||||||
|
|
||||||
[[nodiscard]] int64_t getInt() const { return std::get<int64_t>(value); }
|
[[nodiscard]] int64_t getInt() const { return std::get<int64_t>(value); }
|
||||||
[[nodiscard]] double getFloat() const { return std::get<double>(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