summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal@nekernel.org>2026-01-18 19:40:59 +0100
committerAmlal El Mahrouss <amlal@nekernel.org>2026-01-18 19:49:20 +0100
commit300eb04e2567a284f23edff7a8eef96bcd267254 (patch)
treefb3c5e1a8bfa74613fddfe345a5f765a97de6c88
parentbfc06a997671134e7b747557251d2434f8cb8c2e (diff)
feat: compiler_kit: improvements on the assembler and linker.
Signed-off-by: Amlal El Mahrouss <amlal@nekernel.org>
-rw-r--r--doc/specs/NECTAR_LANG.md12
-rw-r--r--example/example_02_nectar/example.ncpp4
-rw-r--r--include/CompilerKit/Detail/AMD64.h6
-rw-r--r--include/CompilerKit/Detail/PreConfig.h2
-rw-r--r--src/CompilerKit/src/Assemblers/Assembler+AMD64.cc540
-rw-r--r--src/CompilerKit/src/Compilers/NectarCompiler+AMD64.cc101
-rw-r--r--src/CompilerKit/src/Linkers/DynamicLinker64+MachO.cc14
-rw-r--r--src/CompilerKit/src/Linkers/DynamicLinker64+PEF.cc8
-rw-r--r--test/test_samples/test_printf.ncpp8
9 files changed, 582 insertions, 113 deletions
diff --git a/doc/specs/NECTAR_LANG.md b/doc/specs/NECTAR_LANG.md
index a175f19..d1f168a 100644
--- a/doc/specs/NECTAR_LANG.md
+++ b/doc/specs/NECTAR_LANG.md
@@ -25,8 +25,16 @@
- `struct` Data implementation of `impl` -- useful to store fields and such.
- `let` Pointer/Reference variable declaration.
- `const` and `let` declaration.
-- Functions.
-- Nested Stubs.
+- Functions support.
+- Nested Stubs support.
+
+===================================
+
+# 2: Operators
+
+===================================
+
+- `:=` Equals/Assign To operator.
===================================
diff --git a/example/example_02_nectar/example.ncpp b/example/example_02_nectar/example.ncpp
index 3f40941..d7ec86c 100644
--- a/example/example_02_nectar/example.ncpp
+++ b/example/example_02_nectar/example.ncpp
@@ -1,6 +1,6 @@
const main() {
- if (67 == 67) {
- return 123;
+ if (0x10 == 0x10) {
+ return 0x137;
}
return 0;
diff --git a/include/CompilerKit/Detail/AMD64.h b/include/CompilerKit/Detail/AMD64.h
index f1c0671..449ebd1 100644
--- a/include/CompilerKit/Detail/AMD64.h
+++ b/include/CompilerKit/Detail/AMD64.h
@@ -48,9 +48,9 @@ inline std::vector<CpuOpcodeAMD64> kOpcodesAMD64 = {
CK_ASM_OPCODE("int3", 0xC3) CK_ASM_OPCODE("iret", 0xCF) CK_ASM_OPCODE("retf", 0xCB)
CK_ASM_OPCODE("retn", 0xC3) CK_ASM_OPCODE("ret", 0xC3) CK_ASM_OPCODE("sti", 0xfb)
CK_ASM_OPCODE("cli", 0xfa) CK_ASM_OPCODE("hlt", 0xf4) CK_ASM_OPCODE("nop", 0x90)
- CK_ASM_OPCODE("mov", 0x48) CK_ASM_OPCODE("call", 0xFF)
- CK_ASM_OPCODE("syscall", 0x0F) CK_ASM_OPCODE("xor", 0x48) CK_ASM_OPCODE(
- "push", kAsmPushOpcode) CK_ASM_OPCODE("pop", kAsmPopOpcode)};
+ CK_ASM_OPCODE("mov", 0x48) CK_ASM_OPCODE("call", 0xFF) CK_ASM_OPCODE(
+ "syscall", 0x0F) CK_ASM_OPCODE("xor", 0x48) CK_ASM_OPCODE("cmp", 0x39)
+ CK_ASM_OPCODE("push", kAsmPushOpcode) CK_ASM_OPCODE("pop", kAsmPopOpcode)};
#define kAsmRegisterLimit 16
diff --git a/include/CompilerKit/Detail/PreConfig.h b/include/CompilerKit/Detail/PreConfig.h
index c581c76..093917d 100644
--- a/include/CompilerKit/Detail/PreConfig.h
+++ b/include/CompilerKit/Detail/PreConfig.h
@@ -48,8 +48,8 @@
#include <time.h>
#include <unistd.h>
#include <cassert>
-#include <string>
#include <fstream>
+#include <string>
#define ToString(X) Stringify(X)
#define Stringify(X) #X
diff --git a/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc b/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
index a4c8d1f..d90791c 100644
--- a/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
+++ b/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
@@ -569,17 +569,16 @@ bool CompilerKit::EncoderAMD64::WriteNumber(const std::size_t& pos, std::string&
switch (jump_label[pos + 1]) {
case 'x': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid hex number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_hex");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast64 num =
- CompilerKit::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16));
+ CompilerKit::NumberCast64 num = CompilerKit::NumberCast64(res);
- for (auto& i : num.number) {
+ for (char& i : num.number) {
if (i == 0) continue;
kAppBytes.push_back(i);
@@ -593,15 +592,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber(const std::size_t& pos, std::string&
return true;
}
case 'b': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid binary number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_bin");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast64 num =
- CompilerKit::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2));
+ CompilerKit::NumberCast64 num = CompilerKit::NumberCast64(res);
if (kVerbose) {
kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n";
@@ -616,15 +614,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber(const std::size_t& pos, std::string&
return true;
}
case 'o': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid octal number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_octal");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast64 num =
- CompilerKit::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7));
+ CompilerKit::NumberCast64 num = CompilerKit::NumberCast64(res);
if (kVerbose) {
kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n";
@@ -643,15 +640,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber(const std::size_t& pos, std::string&
}
}
- /* check for errno and stuff like that */
- if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) {
- if (errno != 0) {
- return false;
- }
+ auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast64 num =
- CompilerKit::NumberCast64(strtol(jump_label.substr(pos).c_str(), nullptr, 10));
+ CompilerKit::NumberCast64 num = CompilerKit::NumberCast64(res);
for (char& i : num.number) {
if (i == 0) continue;
@@ -871,15 +867,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber8(const std::size_t& pos, std::string
switch (jump_label[pos + 1]) {
case 'x': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid hex number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_hex");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast8 num =
- CompilerKit::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16));
+ CompilerKit::NumberCast8 num = CompilerKit::NumberCast8(res);
kAppBytes.push_back(num.number);
@@ -891,15 +886,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber8(const std::size_t& pos, std::string
return true;
}
case 'b': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid binary number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_bin");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast8 num =
- CompilerKit::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2));
+ CompilerKit::NumberCast8 num = CompilerKit::NumberCast8(res);
if (kVerbose) {
kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n";
@@ -910,15 +904,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber8(const std::size_t& pos, std::string
return true;
}
case 'o': {
- if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) {
- if (errno != 0) {
- CompilerKit::Detail::print_error("Invalid octal number: " + jump_label, "CompilerKit");
- throw std::runtime_error("invalid_octal");
- }
+ auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast8 num =
- CompilerKit::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7));
+ CompilerKit::NumberCast8 num = CompilerKit::NumberCast8(res);
if (kVerbose) {
kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n";
@@ -933,15 +926,14 @@ bool CompilerKit::EncoderAMD64::WriteNumber8(const std::size_t& pos, std::string
}
}
- /* check for errno and stuff like that */
- if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) {
- if (errno != 0) {
- return false;
- }
+ auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10);
+ res += kOrigin;
+
+ if (errno != 0) {
+ return false;
}
- CompilerKit::NumberCast8 num =
- CompilerKit::NumberCast8(strtol(jump_label.substr(pos).c_str(), nullptr, 10));
+ CompilerKit::NumberCast8 num = CompilerKit::NumberCast8(res);
kAppBytes.push_back(num.number);
@@ -994,6 +986,194 @@ bool CompilerKit::EncoderAMD64::WriteLine(std::string line, std::string file) {
throw std::runtime_error("syntax_err");
}
+ /// Handle [reg+n] or [reg-n] memory addressing for any register
+ if (substr.find('[') != std::string::npos) {
+ // Parse the memory operand
+ auto bracketStart = substr.find('[');
+ auto bracketEnd = substr.find(']');
+
+ if (bracketStart == std::string::npos || bracketEnd == std::string::npos) {
+ CompilerKit::Detail::print_error("Syntax error: malformed memory operand.", file);
+ throw std::runtime_error("syntax_err");
+ }
+
+ std::string memOperand = substr.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
+
+ // Register lookup table
+ struct RegInfo {
+ const char* name;
+ i64_byte_t code;
+ };
+
+ RegInfo regs64[] = {{"rax", 0}, {"rcx", 1}, {"rdx", 2}, {"rbx", 3},
+ {"rsp", 4}, {"rbp", 5}, {"rsi", 6}, {"rdi", 7}};
+
+ // Find base register in memory operand
+ i64_byte_t baseReg = 0;
+ bool foundBase = false;
+
+ for (auto& reg : regs64) {
+ if (memOperand.find(reg.name) != std::string::npos) {
+ baseReg = reg.code;
+ foundBase = true;
+ break;
+ }
+ }
+
+ if (!foundBase) {
+ CompilerKit::Detail::print_error("Invalid base register in memory operand.", file);
+ throw std::runtime_error("invalid_base_reg");
+ }
+
+ bool isRbp = (baseReg == 5);
+ bool isRsp = (baseReg == 4);
+
+ // Parse displacement
+ int32_t displacement = 0;
+ bool hasDisp = false;
+
+ auto plusPos = memOperand.find('+');
+ auto minusPos = memOperand.find('-');
+
+ if (plusPos != std::string::npos) {
+ std::string dispStr = memOperand.substr(plusPos + 1);
+ displacement = static_cast<int32_t>(strtol(dispStr.c_str(), nullptr, 0));
+ hasDisp = true;
+ } else if (minusPos != std::string::npos) {
+ std::string dispStr = memOperand.substr(minusPos + 1);
+ displacement = -static_cast<int32_t>(strtol(dispStr.c_str(), nullptr, 0));
+ hasDisp = true;
+ }
+
+ // Determine if destination is memory or register
+ auto commaPos = substr.find(',');
+ bool destIsMemory = bracketStart < commaPos;
+
+ // Find register in the other operand
+ std::string otherOperand;
+ if (destIsMemory) {
+ otherOperand = substr.substr(commaPos + 1);
+ } else {
+ otherOperand = substr.substr(0, commaPos);
+ }
+
+ // Remove whitespace
+ while (!otherOperand.empty() && (otherOperand[0] == ' ' || otherOperand[0] == '\t')) {
+ otherOperand.erase(0, 1);
+ }
+
+ // Check for register in other operand
+ i64_byte_t regCode = 0;
+ bool foundReg = false;
+ bool isImmediate = false;
+ int64_t immValue = 0;
+
+ for (auto& reg : regs64) {
+ if (otherOperand.find(reg.name) != std::string::npos) {
+ regCode = reg.code;
+ foundReg = true;
+ break;
+ }
+ }
+
+ if (!foundReg) {
+ // Check if it's an immediate value
+ std::string immStr = otherOperand;
+ while (!immStr.empty() && (immStr[0] == ' ' || immStr[0] == '\t')) {
+ immStr.erase(0, 1);
+ }
+ if (!immStr.empty() && (isdigit(immStr[0]) || immStr[0] == '-')) {
+ isImmediate = true;
+ immValue = strtol(immStr.c_str(), nullptr, 0);
+ }
+ }
+
+ // Determine mod field based on displacement size
+ // mod=00: [reg] no displacement (except rbp which requires disp8)
+ // mod=01: [reg+disp8]
+ // mod=10: [reg+disp32]
+ i64_byte_t mod = 0;
+ if (!hasDisp && displacement == 0) {
+ // [rbp] requires disp8 with 0, can't use mod=00 (it means RIP-relative)
+ mod = isRbp ? 0x01 : 0x00;
+ } else if (displacement >= -128 && displacement <= 127) {
+ mod = 0x01; // 8-bit displacement
+ } else {
+ mod = 0x02; // 32-bit displacement
+ }
+
+ if (destIsMemory) {
+ if (foundReg) {
+ // mov [reg+n], reg
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x89); // MOV r/m64, r64
+
+ // ModR/M: mod | reg << 3 | r/m
+ i64_byte_t modrm = (mod << 6) | (regCode << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ // RSP needs SIB byte
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24); // SIB: scale=0, index=4(none), base=4(rsp)
+ }
+ } else if (isImmediate) {
+ // mov qword [reg+n], imm32
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0xC7); // MOV r/m64, imm32
+
+ // ModR/M: mod | 0 << 3 | r/m (reg field is 0 for this opcode)
+ i64_byte_t modrm = (mod << 6) | (0 << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ // RSP needs SIB byte
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24);
+ }
+ } else {
+ CompilerKit::Detail::print_error("Invalid source operand for mov to memory.", file);
+ throw std::runtime_error("invalid_operand");
+ }
+ } else {
+ // mov reg, [reg+n]
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x8B); // MOV r64, r/m64
+
+ // ModR/M: mod | reg << 3 | r/m
+ i64_byte_t modrm = (mod << 6) | (regCode << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ // RSP needs SIB byte
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24);
+ }
+ }
+
+ // Write displacement
+ if (mod == 0x01) {
+ // 8-bit displacement
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(displacement & 0xFF));
+ } else if (mod == 0x02) {
+ // 32-bit displacement
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(displacement & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 8) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 16) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 24) & 0xFF));
+ } else if (isRbp) {
+ // rbp with mod=00 still needs disp8=0
+ kAppBytes.emplace_back(0x00);
+ }
+
+ // Write immediate if present
+ if (destIsMemory && isImmediate) {
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(immValue & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 8) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 16) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 24) & 0xFF));
+ }
+
+ break;
+ }
+
bool onlyOneReg = true;
std::vector<RegMapAMD64> currentRegList;
@@ -1144,6 +1324,252 @@ bool CompilerKit::EncoderAMD64::WriteLine(std::string line, std::string file) {
break;
}
+ /// Compare instruction handler.
+ if (name == "cmp") {
+ std::string substr = line.substr(line.find(name) + name.size());
+
+ if (substr.find(",") == std::string::npos) {
+ CompilerKit::Detail::print_error("Syntax error: missing right operand.", "CompilerKit");
+ throw std::runtime_error("syntax_err");
+ }
+
+ // Register lookup table
+ struct RegInfo {
+ const char* name;
+ i64_byte_t code;
+ };
+
+ RegInfo regs64[] = {{"rax", 0}, {"rcx", 1}, {"rdx", 2}, {"rbx", 3},
+ {"rsp", 4}, {"rbp", 5}, {"rsi", 6}, {"rdi", 7}};
+
+ /// Handle [reg+n] memory addressing
+ if (substr.find('[') != std::string::npos) {
+ auto bracketStart = substr.find('[');
+ auto bracketEnd = substr.find(']');
+
+ if (bracketEnd == std::string::npos) {
+ CompilerKit::Detail::print_error("Syntax error: malformed memory operand.", file);
+ throw std::runtime_error("syntax_err");
+ }
+
+ std::string memOperand = substr.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
+
+ // Find base register
+ i64_byte_t baseReg = 0;
+ bool foundBase = false;
+
+ for (auto& reg : regs64) {
+ if (memOperand.find(reg.name) != std::string::npos) {
+ baseReg = reg.code;
+ foundBase = true;
+ break;
+ }
+ }
+
+ if (!foundBase) {
+ CompilerKit::Detail::print_error("Invalid base register in memory operand.", file);
+ throw std::runtime_error("invalid_base_reg");
+ }
+
+ bool isRbp = (baseReg == 5);
+ bool isRsp = (baseReg == 4);
+
+ // Parse displacement
+ int32_t displacement = 0;
+ bool hasDisp = false;
+
+ auto plusPos = memOperand.find('+');
+ auto minusPos = memOperand.find('-');
+
+ if (plusPos != std::string::npos) {
+ std::string dispStr = memOperand.substr(plusPos + 1);
+ displacement = static_cast<int32_t>(strtol(dispStr.c_str(), nullptr, 0));
+ hasDisp = true;
+ } else if (minusPos != std::string::npos) {
+ std::string dispStr = memOperand.substr(minusPos + 1);
+ displacement = -static_cast<int32_t>(strtol(dispStr.c_str(), nullptr, 0));
+ hasDisp = true;
+ }
+
+ auto commaPos = substr.find(',');
+ bool destIsMemory = bracketStart < commaPos;
+
+ std::string otherOperand;
+ if (destIsMemory) {
+ otherOperand = substr.substr(commaPos + 1);
+ } else {
+ otherOperand = substr.substr(0, commaPos);
+ }
+
+ while (!otherOperand.empty() && (otherOperand[0] == ' ' || otherOperand[0] == '\t')) {
+ otherOperand.erase(0, 1);
+ }
+
+ i64_byte_t regCode = 0;
+ bool foundReg = false;
+ bool isImmediate = false;
+ int64_t immValue = 0;
+
+ for (auto& reg : regs64) {
+ if (otherOperand.find(reg.name) != std::string::npos) {
+ regCode = reg.code;
+ foundReg = true;
+ break;
+ }
+ }
+
+ if (!foundReg) {
+ std::string immStr = otherOperand;
+ while (!immStr.empty() && (immStr[0] == ' ' || immStr[0] == '\t')) {
+ immStr.erase(0, 1);
+ }
+ if (!immStr.empty() && (isdigit(immStr[0]) || immStr[0] == '-')) {
+ isImmediate = true;
+ immValue = strtol(immStr.c_str(), nullptr, 0);
+ }
+ }
+
+ // Determine mod field
+ i64_byte_t mod = 0;
+ if (!hasDisp && displacement == 0) {
+ mod = isRbp ? 0x01 : 0x00;
+ } else if (displacement >= -128 && displacement <= 127) {
+ mod = 0x01;
+ } else {
+ mod = 0x02;
+ }
+
+ if (destIsMemory) {
+ if (foundReg) {
+ // cmp [reg+n], reg
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x39); // CMP r/m64, r64
+
+ i64_byte_t modrm = (mod << 6) | (regCode << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24);
+ }
+ } else if (isImmediate) {
+ // cmp qword [reg+n], imm32
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x81); // CMP r/m64, imm32
+
+ // reg field = 7 for CMP
+ i64_byte_t modrm = (mod << 6) | (7 << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24);
+ }
+ }
+ } else {
+ // cmp reg, [reg+n]
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x3B); // CMP r64, r/m64
+
+ i64_byte_t modrm = (mod << 6) | (regCode << 3) | baseReg;
+ kAppBytes.emplace_back(modrm);
+
+ if (isRsp) {
+ kAppBytes.emplace_back(0x24);
+ }
+ }
+
+ // Write displacement
+ if (mod == 0x01) {
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(displacement & 0xFF));
+ } else if (mod == 0x02) {
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(displacement & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 8) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 16) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((displacement >> 24) & 0xFF));
+ } else if (isRbp) {
+ kAppBytes.emplace_back(0x00);
+ }
+
+ // Write immediate
+ if (destIsMemory && isImmediate) {
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(immValue & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 8) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 16) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 24) & 0xFF));
+ }
+
+ break;
+ }
+
+ // Handle register-to-register and register-to-immediate
+ i64_byte_t reg1Code = 0;
+ i64_byte_t reg2Code = 0;
+ bool foundReg1 = false;
+ bool foundReg2 = false;
+ bool isImmediate = false;
+ int64_t immValue = 0;
+
+ auto commaPos = substr.find(',');
+ std::string leftOperand = substr.substr(0, commaPos);
+ std::string rightOperand = substr.substr(commaPos + 1);
+
+ while (!leftOperand.empty() && (leftOperand[0] == ' ' || leftOperand[0] == '\t')) {
+ leftOperand.erase(0, 1);
+ }
+ while (!rightOperand.empty() && (rightOperand[0] == ' ' || rightOperand[0] == '\t')) {
+ rightOperand.erase(0, 1);
+ }
+
+ for (auto& reg : regs64) {
+ if (leftOperand.find(reg.name) != std::string::npos) {
+ reg1Code = reg.code;
+ foundReg1 = true;
+ break;
+ }
+ }
+
+ for (auto& reg : regs64) {
+ if (rightOperand.find(reg.name) != std::string::npos) {
+ reg2Code = reg.code;
+ foundReg2 = true;
+ break;
+ }
+ }
+
+ if (!foundReg2) {
+ if (!rightOperand.empty() && (isdigit(rightOperand[0]) || rightOperand[0] == '-')) {
+ isImmediate = true;
+ immValue = strtol(rightOperand.c_str(), nullptr, 0);
+ }
+ }
+
+ if (foundReg1 && foundReg2) {
+ // cmp reg1, reg2
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x39); // CMP r/m64, r64
+
+ i64_byte_t modrm = (0x3 << 6) | (reg2Code << 3) | reg1Code;
+ kAppBytes.emplace_back(modrm);
+ } else if (foundReg1 && isImmediate) {
+ // cmp reg, imm
+ kAppBytes.emplace_back(0x48); // REX.W
+ kAppBytes.emplace_back(0x81); // CMP r/m64, imm32
+
+ // reg field = 7 for CMP
+ i64_byte_t modrm = (0x3 << 6) | (7 << 3) | reg1Code;
+ kAppBytes.emplace_back(modrm);
+
+ kAppBytes.emplace_back(static_cast<i64_byte_t>(immValue & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 8) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 16) & 0xFF));
+ kAppBytes.emplace_back(static_cast<i64_byte_t>((immValue >> 24) & 0xFF));
+ } else {
+ CompilerKit::Detail::print_error("Invalid operands for cmp instruction.", file);
+ throw std::runtime_error("invalid_cmp_operands");
+ }
+
+ break;
+ }
+
/// Push instruction handler.
if (name == "push" || name == "pop") {
std::string substr = line.substr(line.find(name) + name.size());
diff --git a/src/CompilerKit/src/Compilers/NectarCompiler+AMD64.cc b/src/CompilerKit/src/Compilers/NectarCompiler+AMD64.cc
index 720fdee..1889035 100644
--- a/src/CompilerKit/src/Compilers/NectarCompiler+AMD64.cc
+++ b/src/CompilerKit/src/Compilers/NectarCompiler+AMD64.cc
@@ -345,33 +345,59 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendNectarAMD64::Compile(
left.erase(left.find(" "), 1);
}
- auto right = left.substr(left.find_first_of("==") + 2);
-
- auto tmp = left.substr(0, left.find_first_of("=="));
- left = std::move(tmp);
-
- syntax_tree.fUserValue +=
- "mov rdi, " +
- (isnumber(left[0])
- ? left
- : (nectar_get_variable_ref(left).empty() ? left : nectar_get_variable_ref(left))) +
- "\n";
-
- syntax_tree.fUserValue += "mov rsi, " +
- (isnumber(right[0]) ? right
- : (nectar_get_variable_ref(right).empty()
- ? right
- : nectar_get_variable_ref(right))) +
- "\n";
+ std::vector<std::pair<CompilerKit::STLString, CompilerKit::STLString>> operators = {
+ {"==", "je"}, {"!=", "jne"}, {">=", "jge"}, {"<=", "jle"}, {">", "jg"}, {"<", "jl"},
+ };
+
+ for (auto& op : operators) {
+ if (left.find(op.first) == CompilerKit::STLString::npos) continue;
+
+ auto right = left.substr(left.find(op.first) + op.first.size());
+
+ auto tmp = left.substr(0, left.find(op.first));
+ left = std::move(tmp);
+
+ if (!nectar_get_variable_ref(left).empty())
+ syntax_tree.fUserValue += "mov rdi, " +
+ (isnumber(left[0]) ? left
+ : (nectar_get_variable_ref(left).empty()
+ ? left
+ : nectar_get_variable_ref(left))) +
+ "\n";
+ else
+ syntax_tree.fUserValue += "mov rdi, " +
+ (isnumber(left[0]) ? left
+ : (nectar_get_variable_ref(left).empty()
+ ? left
+ : nectar_get_variable_ref(left))) +
+ "\n";
+
+ if (!nectar_get_variable_ref(left).empty())
+ syntax_tree.fUserValue +=
+ "lea rsi, " +
+ (isnumber(right[0])
+ ? right
+ : (nectar_get_variable_ref(right).empty() ? right
+ : nectar_get_variable_ref(right))) +
+ "\n";
+ else
+ syntax_tree.fUserValue +=
+ "mov rsi, " +
+ (isnumber(right[0])
+ ? right
+ : (nectar_get_variable_ref(right).empty() ? right
+ : nectar_get_variable_ref(right))) +
+ "\n";
- syntax_tree.fUserValue += "cmp rdi, rsi\n";
+ syntax_tree.fUserValue += "cmp rdi, rsi\n";
- syntax_tree.fUserValue +=
- "jne __ret_" + std::to_string(kOrigin) + "_" + kCurrentFunctionName + "\n";
+ syntax_tree.fUserValue +=
+ op.second + " __ret_" + std::to_string(kOrigin) + "_" + kCurrentFunctionName + "\n";
- kCurrentFunctionName = std::to_string(kOrigin) + "_" + kCurrentFunctionName;
+ kCurrentFunctionName = std::to_string(kOrigin) + "_" + kCurrentFunctionName;
- ++kOrigin;
+ ++kOrigin;
+ }
break;
}
@@ -776,8 +802,12 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendNectarAMD64::Compile(
kExternalSymbols.insert(mangled + valueOfVar);
if (!kNasmOutput) {
- syntax_tree.fUserValue += instr + nectar_get_variable_ref(varName) + ", __thiscall " +
- mangled + valueOfVar + "\n";
+ if (valueOfVar.ends_with(")"))
+ syntax_tree.fUserValue += instr + nectar_get_variable_ref(varName) +
+ ", __thiscall " + mangled + valueOfVar + "\n";
+ else
+ syntax_tree.fUserValue +=
+ instr + nectar_get_variable_ref(varName) + ", " + mangled + valueOfVar + "\n";
} else {
// NASM: Generate call and move result
syntax_tree.fUserValue += "call " + mangled + valueOfVar + "\n";
@@ -816,7 +846,7 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendNectarAMD64::Compile(
auto ref = nectar_get_variable_ref(subText);
- if (ref.empty() == false) syntax_tree.fUserValue += "mov rax, " + ref + "\n";
+ if (ref.empty() == false) syntax_tree.fUserValue += "lea rax, " + ref + "\n";
if (subText.starts_with("'") || isnumber(subText[0]))
syntax_tree.fUserValue += "mov rax, " + subText + "\n";
@@ -841,7 +871,8 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendNectarAMD64::Compile(
}
if (!kNasmOutput)
- syntax_tree.fUserValue += "public_segment .code64 __ret_" + kCurrentFunctionName + "\n";
+ syntax_tree.fUserValue +=
+ "public_segment .code64 __ret_" + kCurrentFunctionName + "\nnop\n";
else
syntax_tree.fUserValue += "__ret_" + kCurrentFunctionName + ":\n";
@@ -1033,12 +1064,12 @@ static CompilerKit::STLString nectar_mangle_name(const CompilerKit::STLString& i
/// \brief Generate function prologue
static CompilerKit::STLString nectar_generate_prologue() {
- return "push rbp\nmov rbp, rsp\n";
+ return "";
}
/// \brief Generate function epilogue
static CompilerKit::STLString nectar_generate_epilogue() {
- return "mov rsp, rbp\npop rbp\n";
+ return "";
}
/// \brief Allocate a variable on the stack
@@ -1107,7 +1138,7 @@ static CompilerKit::STLString nectar_get_variable_ref(const CompilerKit::STLStri
return varInfo->fRegister;
} else {
// Stack or spilled
- return "qword [rbp" + std::to_string(varInfo->fStackOffset) + "]";
+ return "qword [rbp+" + std::to_string(-varInfo->fStackOffset) + "]";
}
}
@@ -1188,10 +1219,10 @@ static CompilerKit::STLString nectar_spill_lru_variable() {
/// if impl init
if (!lruVar->fRegister.ends_with("{}"))
- spillCode =
- "mov qword [rbp" + std::to_string(kContext.fStackOffset) + "], " + lruVar->fRegister + "\n";
+ spillCode = "mov qword [rbp+" + std::to_string(-kContext.fStackOffset) + "], " +
+ lruVar->fRegister + "\n";
else
- spillCode = "mov qword [rbp" + std::to_string(kContext.fStackOffset) + "], rax\n";
+ spillCode = "mov qword [rbp+" + std::to_string(-kContext.fStackOffset) + "], rax\n";
// Update variable info
lruVar->fLocation = VarLocation::kStackSpill;
@@ -1255,7 +1286,7 @@ static CompilerKit::STLString nectar_generate_constructor_call(
nectar_pop_scope();
CompilerKit::STLString code;
- code += "lea r8, [rbp" + std::to_string(offset) + "]\n";
+ code += "lea r8, [rbp+" + std::to_string(offset) + "]\n";
code += "call " + ctor_mangled + "\n";
return code;
}
@@ -1274,7 +1305,7 @@ static CompilerKit::STLString nectar_generate_destructor_call(
CompilerKit::STLString code;
if (varInfo->fLocation == VarLocation::kStack || varInfo->fLocation == VarLocation::kStackSpill) {
- code += "lea r8, [rbp" + std::to_string(varInfo->fStackOffset) + "]\n";
+ code += "lea r8, [rbp+" + std::to_string(varInfo->fStackOffset) + "]\n";
} else {
code += "mov r8, " + varInfo->fRegister + "\n";
}
diff --git a/src/CompilerKit/src/Linkers/DynamicLinker64+MachO.cc b/src/CompilerKit/src/Linkers/DynamicLinker64+MachO.cc
index f87e2fc..f076d3c 100644
--- a/src/CompilerKit/src/Linkers/DynamicLinker64+MachO.cc
+++ b/src/CompilerKit/src/Linkers/DynamicLinker64+MachO.cc
@@ -46,17 +46,17 @@ static std::vector<CompilerKit::Detail::Blob> kTextBytes;
static std::vector<CompilerKit::Detail::Blob> kDataBytes;
/* symbol table */
-static std::vector<nlist_64> kSymbolTable;
-static std::vector<Char> kStringTable;
+static std::vector<nlist_64> kSymbolTable;
+static std::vector<Char> kStringTable;
static std::map<CompilerKit::STLString, UInt64> kSymbolOffsets;
/// @brief Structure to hold section information from AE records
struct SectionInfo {
CompilerKit::STLString name;
- UInt32 kind;
+ UInt32 kind;
std::vector<Char> bytes;
- UInt64 address;
- UInt64 size;
+ UInt64 address;
+ UInt64 size;
};
/// @brief Extract clean symbol name from AE record name
@@ -87,7 +87,7 @@ static CompilerKit::STLString ExtractSymbolName(const CompilerKit::STLString& ae
/// @brief Add a symbol to the symbol table
static UInt32 AddSymbol(const CompilerKit::STLString& name, uint8_t type, uint8_t sect,
- UInt64 value) {
+ UInt64 value) {
// Add name to string table (offset 0 is reserved for empty string)
if (kStringTable.empty()) {
kStringTable.push_back('\0'); // First byte is null
@@ -345,7 +345,7 @@ NECTAR_MODULE(DynamicLinker64MachO) {
using namespace CompilerKit::MachO;
UInt32 numCommands = 8; // __PAGEZERO, LC_BUILD_VERSION, __TEXT, __LINKEDIT, LC_LOAD_DYLINKER,
- // LC_UUID, LC_SYMTAB, LC_DYSYMTAB
+ // LC_UUID, LC_SYMTAB, LC_DYSYMTAB
if (!kIsDylib) {
numCommands += 1; // LC_MAIN
diff --git a/src/CompilerKit/src/Linkers/DynamicLinker64+PEF.cc b/src/CompilerKit/src/Linkers/DynamicLinker64+PEF.cc
index ea513c5..b0a142e 100644
--- a/src/CompilerKit/src/Linkers/DynamicLinker64+PEF.cc
+++ b/src/CompilerKit/src/Linkers/DynamicLinker64+PEF.cc
@@ -202,8 +202,8 @@ NECTAR_MODULE(DynamicLinker64PEF) {
pef_container.Count = 0UL;
pef_container.Kind = is_executable ? CompilerKit::kPefKindExec : CompilerKit::kPefKindDylib;
pef_container.SubCpu = kSubArch;
- pef_container.Linker = kLinkerId; // Amlal El Mahrouss Linker
- pef_container.Abi = static_cast<Int32>(kAbi); // Multi-Processor UX ABI
+ pef_container.Linker = kLinkerId; // Nectar Linker
+ pef_container.Abi = static_cast<Int32>(kAbi); // Multi-Processor UX ABI
pef_container.Magic[0] = kPefMagic[kFatBinaryEnable ? 2 : 0];
pef_container.Magic[1] = kPefMagic[1];
pef_container.Magic[2] = kPefMagic[kFatBinaryEnable ? 0 : 2];
@@ -552,8 +552,8 @@ NECTAR_MODULE(DynamicLinker64PEF) {
// And check for any duplications
for (size_t commandHeaderIndex = 0UL; commandHeaderIndex < command_headers.size();
++commandHeaderIndex) {
- if (CompilerKit::STLString(command_headers[commandHeaderIndex].Name).find(kLinkerDefineSymbol) !=
- CompilerKit::STLString::npos &&
+ if (CompilerKit::STLString(command_headers[commandHeaderIndex].Name)
+ .find(kLinkerDefineSymbol) != CompilerKit::STLString::npos &&
CompilerKit::STLString(command_headers[commandHeaderIndex].Name).find(kLinkerDynamicSym) ==
CompilerKit::STLString::npos) {
// ignore :UndefinedSymbol: headers, they do not contain code.
diff --git a/test/test_samples/test_printf.ncpp b/test/test_samples/test_printf.ncpp
index 60a6dbb..d18c818 100644
--- a/test/test_samples/test_printf.ncpp
+++ b/test/test_samples/test_printf.ncpp
@@ -1,5 +1,9 @@
let main()
{
- let ret := 100;
- return ret;
+ if (0x01 <= 0x100)
+ {
+ return 0x80;
+ }
+
+ return 0x0;
} \ No newline at end of file