added math instructions and a way to convert to and from floats and ints

This commit is contained in:
2026-04-10 15:33:56 +10:00
parent d31f05e0ae
commit a3f1e188c7
3 changed files with 223 additions and 10 deletions

View File

@@ -6,12 +6,12 @@ int main() {
// The outputted program should have an exit status of 10
Tram::Program program;
program.addInstruction(Tram::Instruction(Tram::InstructionType::External, {Tram::Parameter("puts")}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::CreateLabel, {Tram::Parameter("main")}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::PushAll, {}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::PopAll, {}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Literal("hi"))}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::Call, {Tram::Parameter("puts")}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::Put, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Literal(10))}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::IntToFloat, {Tram::Parameter(Tram::Register::f0), Tram::Parameter(Tram::Register::r0)}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::Divide, {Tram::Parameter(Tram::Register::f0), Tram::Parameter(Tram::Literal(5.0))}));
program.addInstruction(Tram::Instruction(Tram::InstructionType::FloatToInt, {Tram::Parameter(Tram::Register::r0), Tram::Parameter(Tram::Register::f0)}));
Tram::Compiler compiler(program);
compiler.setTarget(Tram::Target::x86_64_Linux_libc);
compiler.setLogLevel(Tram::LogLevel::All);

View File

@@ -1,5 +1,6 @@
#include "../tram.h"
#include <optional>
#include <string>
#include <tram.h>
#include <utility>
#include "x86_64_Linux_libc.h"
@@ -303,11 +304,11 @@ namespace Tram {
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value()) {
compiler.errorLog("put: expecting at least 2 parameters");
compiler.errorLog("add: expecting at least 2 parameters");
return {};
}
if (!param1.has_value()) {
compiler.errorLog("put: expecting at least 2 parameters");
compiler.errorLog("add: expecting at least 2 parameters");
return {};
}
@@ -340,7 +341,7 @@ namespace Tram {
std::string destination = registerToAsmRegister(reg);
std::string source = ({
const auto _param = parameterToAsmString(param1.value(), compiler);
const auto _param = parameterToAsmString(param1.value(), compiler, true);
if (!_param.has_value()) {
return {};
}
@@ -348,7 +349,156 @@ namespace Tram {
});
body += " " + instruction + " " + destination + ", " + source + "\n";
break;
}
case InstructionType::Subtract: {
compiler.allLog("instruction: subtract");
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value()) {
compiler.errorLog("subtract: expecting at least 2 parameters");
return {};
}
if (!param1.has_value()) {
compiler.errorLog("subtract: expecting at least 2 parameters");
return {};
}
if (param0.value().getType() != ParameterType::Register) {
compiler.errorLog("subtract: can only subtract a number from a register");
return {};
}
Register reg = param0.value().getRegister();
LiteralType regType = registers[reg];
if (regType == LiteralType::None) {
compiler.errorLog("subtract: selected register to subtract to is empty");
return {};
}
std::string instruction;
switch (regType) {
case LiteralType::Int: {
instruction = "sub";
break;
}
case LiteralType::Float: {
instruction = "subsd";
break;
}
case LiteralType::String:
case LiteralType::None:
std::unreachable();
}
std::string destination = registerToAsmRegister(reg);
std::string source = ({
const auto _param = parameterToAsmString(param1.value(), compiler, true);
if (!_param.has_value()) {
return {};
}
_param.value();
});
body += " " + instruction + " " + destination + ", " + source + "\n";
break;
}
case InstructionType::Multiply: {
compiler.allLog("instruction: multiply");
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value()) {
compiler.errorLog("multiply: expecting at least 2 parameters");
return {};
}
if (!param1.has_value()) {
compiler.errorLog("multiply: expecting at least 2 parameters");
return {};
}
if (param0.value().getType() != ParameterType::Register) {
compiler.errorLog("multiply: can only multiply a number and a register");
return {};
}
Register reg = param0.value().getRegister();
LiteralType regType = registers[reg];
if (regType == LiteralType::None) {
compiler.errorLog("multiply: selected register to multiply to is empty");
return {};
}
std::string instruction;
switch (regType) {
case LiteralType::Int: {
instruction = "mul";
break;
}
case LiteralType::Float: {
instruction = "mulsd";
break;
}
case LiteralType::String:
case LiteralType::None:
std::unreachable();
}
std::string destination = registerToAsmRegister(reg);
std::string source = ({
const auto _param = parameterToAsmString(param1.value(), compiler, true);
if (!_param.has_value()) {
return {};
}
_param.value();
});
body += " " + instruction + " " + destination + ", " + source + "\n";
break;
}
case InstructionType::Divide: {
compiler.allLog("instruction: divide");
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value()) {
compiler.errorLog("divide: expecting at least 2 parameters");
return {};
}
if (!param1.has_value()) {
compiler.errorLog("divide: expecting at least 2 parameters");
return {};
}
if (param0.value().getType() != ParameterType::Register || param0->getRegister() < Register::f0) {
compiler.errorLog("divide: can only divide a number and a float register");
return {};
}
if (param1.value().getType() == ParameterType::Literal && param1->getLiteral().getType() != LiteralType::Float) {
compiler.errorLog("divide: second argument must be a float literal or float register");
return {};
}
if (param1.value().getType() == ParameterType::Register && param1->getRegister() < Register::f0) {
compiler.errorLog("divide: if the second argument is a register it must be a float register");
return {};
}
Register reg = param0.value().getRegister();
LiteralType regType = registers[reg];
if (regType == LiteralType::None) {
compiler.errorLog("divide: selected register to divide to is empty");
return {};
}
std::string destination = registerToAsmRegister(reg);
std::string source = ({
const auto _param = parameterToAsmString(param1.value(), compiler, true);
if (!_param.has_value()) {
return {};
}
_param.value();
});
body += " divsd " + destination + ", " + source + "\n";
break;
}
case InstructionType::Push: {
compiler.allLog("instruction: push");
@@ -386,7 +536,8 @@ namespace Tram {
break;
}
case InstructionType::PushAll:
case InstructionType::PushAll: {
compiler.allLog("instruction: pushall");
body += " push r8\n";
body += " push r9\n";
body += " push r10\n";
@@ -401,7 +552,9 @@ namespace Tram {
stackSizeBytes += 16;
break;
case InstructionType::PopAll:
}
case InstructionType::PopAll: {
compiler.allLog("instruction: popall");
body += " pop r15\n";
body += " pop r14\n";
body += " pop r13\n";
@@ -415,6 +568,65 @@ namespace Tram {
stackSizeBytes -= 16;
break;
}
case InstructionType::IntToFloat: {
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value() || !param1.has_value()) {
compiler.errorLog("inttofloat: expecting at least 2 parameters");
return {};
}
if (param0->getType() != ParameterType::Register || param1->getType() != ParameterType::Register) {
compiler.errorLog("inttofloat: both parameter must be registers");
return {};
}
if (param0->getRegister() < Register::f0) {
compiler.errorLog("inttofloat: parameter 0 must be a float register");
return {};
}
if (param1->getRegister() > Register::r15) {
compiler.errorLog("inttofloat: parameter 1 must be an int register");
return {};
}
body += " cvtsi2sd " +
parameterToAsmString(param0.value(), compiler).value() + ", " +
parameterToAsmString(param1.value(), compiler).value() + "\n";
registers[param0->getRegister()] = LiteralType::Float;
break;
}
case InstructionType::FloatToInt: {
std::optional param0 = instruction.getParameter(0);
std::optional param1 = instruction.getParameter(1);
if (!param0.has_value() || !param1.has_value()) {
compiler.errorLog("inttofloat: expecting at least 2 parameters");
return {};
}
if (param0->getType() != ParameterType::Register || param1->getType() != ParameterType::Register) {
compiler.errorLog("inttofloat: both parameter must be registers");
return {};
}
if (param0->getRegister() > Register::r15) {
compiler.errorLog("inttofloat: parameter 0 must be an int register");
return {};
}
if (param1->getRegister() < Register::f0) {
compiler.errorLog("inttofloat: parameter 1 must be a float register");
return {};
}
body += " cvttsd2si " +
parameterToAsmString(param0.value(), compiler).value() + ", " +
parameterToAsmString(param1.value(), compiler).value() + "\n";
registers[param0->getRegister()] = LiteralType::Int;
break;
}
case InstructionType::None:
break;
}

View File

@@ -40,6 +40,7 @@ namespace Tram {
External, Call, Put,
CreateLabel, Jump, JumpNotZero, JumpIfZero,
Add, Subtract, Multiply, Divide,
IntToFloat, FloatToInt,
Push, Pop, PushAll, PopAll,
None
};