From 2405d8f29f09fe1b2bd0d49ee45be1f4051cb244 Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Fri, 20 Jun 2025 16:45:38 +0530 Subject: [PATCH 1/3] Linux external + user defined functions now work. --- .gitignore | 2 + .vscode/settings.json | 5 +- examples/functions.zz | 11 + include/all.hpp | 7 +- include/ast/ASTNode.hpp | 42 +- include/codegen/Canaries.hpp | 12 + include/codegen/CodeGen.hpp | 246 +++-- include/codegen/RegisterAllocator.hpp | 96 +- include/lexer/Lexer.hpp | 15 +- include/parser/NameMapper.hpp | 30 + include/parser/Parser.hpp | 17 +- include/parser/ScopeContext.hpp | 233 +++-- include/typechecker/TypeChecker.hpp | 87 +- main.cpp | 120 +-- src/ast/ASTNode.cpp | 172 ++-- src/codegen/CodeGen.cpp | 24 +- src/codegen/CodeGenLLVM.cpp | 1090 +++++++++----------- src/codegen/CodeGenLinux.cpp | 1171 +++++++++++++-------- src/codegen/CodeGenWindows.cpp | 1357 ++++++++++++------------- src/codegen/RegisterAllocator.cpp | 192 +++- src/lexer/Lexer.cpp | 19 +- src/parser/Parser.cpp | 600 ++++++++--- src/parser/ScopeContext.cpp | 298 ++++-- src/typechecker/TypeChecker.cpp | 214 ++-- tests/zz/conditionals/if-elif-else.zz | 24 +- tests/zz/conditionals/if-elif.zz | 20 +- tests/zz/conditionals/if-else.zz | 36 +- tests/zz/conditionals/if.zz | 20 +- 28 files changed, 3575 insertions(+), 2585 deletions(-) create mode 100644 examples/functions.zz create mode 100644 include/codegen/Canaries.hpp create mode 100644 include/parser/NameMapper.hpp diff --git a/.gitignore b/.gitignore index 96fc89e..cce68ad 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ tests/object/ **.obj **.exe + +zlang-support/ diff --git a/.vscode/settings.json b/.vscode/settings.json index c835cf1..59164ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -97,5 +97,8 @@ "xtree": "cpp", "xutility": "cpp" }, - "C_Cpp.default.compilerPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx86\\x64\\cl.exe" + "C_Cpp.formatting": "clangFormat", + "C_Cpp.clang_format_style": "{ BasedOnStyle: Google, IndentWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: false, ColumnLimit: 0, AccessModifierOffset: -4, NamespaceIndentation: All, FixNamespaceComments: false}", + "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: false, ColumnLimit: 0, AccessModifierOffset: -4, NamespaceIndentation: All, FixNamespaceComments: false}", + "C_Cpp.clang_format_path": "/usr/bin/clang-format", } \ No newline at end of file diff --git a/examples/functions.zz b/examples/functions.zz new file mode 100644 index 0000000..6bc2156 --- /dev/null +++ b/examples/functions.zz @@ -0,0 +1,11 @@ +extern fn putchar(c: int32_t) -> int32_t; + +fn add(a: int32_t, b: int32_t) -> int32_t { + return a + b; +} + +fn main() { + let sum: int32_t = add(1, add(3, 4)); + let ch: int32_t = sum + 48; + putchar(ch); +} \ No newline at end of file diff --git a/include/all.hpp b/include/all.hpp index f48b408..9d1bc2e 100644 --- a/include/all.hpp +++ b/include/all.hpp @@ -1,4 +1,5 @@ #pragma once + #include "common/Colors.hpp" #include "common/Errors.hpp" #include "common/Logging.hpp" @@ -11,6 +12,7 @@ #include "parser/Parser.hpp" #include "parser/ScopeContext.hpp" +#include "parser/NameMapper.hpp" #include "lexer/Lexer.hpp" @@ -18,6 +20,7 @@ #include "codegen/CodeGen.hpp" #include "codegen/RegisterAllocator.hpp" +#include "codegen/Canaries.hpp" #include #include @@ -25,4 +28,6 @@ #include #include #include -#include \ No newline at end of file +#include + +static NameMapper GLOBAL_NAME_MAPPER; \ No newline at end of file diff --git a/include/ast/ASTNode.hpp b/include/ast/ASTNode.hpp index e45256d..e1ce31f 100644 --- a/include/ast/ASTNode.hpp +++ b/include/ast/ASTNode.hpp @@ -1,20 +1,19 @@ #pragma once +#include +#include #include #include -#include -#include + #include "parser/ScopeContext.hpp" -namespace zlang -{ - enum class NodeType - { +namespace zlang { + enum class NodeType { Program, - VariableDeclaration, // let x: int; or let x = 10; - VariableReassignment, // x = 42; - VariableAccess, // just x - IntegerLiteral, // 10, 42 + VariableDeclaration, // let x: int; or let x = 10; + VariableReassignment, // x = 42; + VariableAccess, // just x + IntegerLiteral, // 10, 42 FloatLiteral, StringLiteral, BooleanLiteral, @@ -24,26 +23,30 @@ namespace zlang BinaryOp, UnaryOp, Symbol, + Function, + ExternFunction, + FunctionParameter, + FunctionParameterList, + FunctionReturnType, + ReturnStatement, + FunctionCall, + FunctionCallArgumentList }; - class ASTNode - { + class ASTNode { public: NodeType type; std::string value; std::vector> children; std::shared_ptr scope; - ASTNode() = default; ASTNode(NodeType t, const std::string &val = "", std::shared_ptr sc = nullptr) : type(t), value(val), scope(sc) {} - static std::unique_ptr makeProgramNode(const std::shared_ptr scope); static std::optional> makeVariableDeclarationNode( const std::string &name, std::unique_ptr typeAnnotation, std::unique_ptr initializer, const std::shared_ptr scope); - static std::unique_ptr makeVariableReassignmentNode( const std::string &name, std::unique_ptr expr, const std::shared_ptr scope); @@ -58,9 +61,16 @@ namespace zlang static std::unique_ptr makeIfStatement(std::unique_ptr condition, std::unique_ptr program, const std::shared_ptr scope); static std::unique_ptr makeElseIfStatement(std::unique_ptr condition, std::unique_ptr program, const std::shared_ptr scope); static std::unique_ptr makeElseStatement(std::unique_ptr program, const std::shared_ptr scope); + static std::unique_ptr makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType); + static std::unique_ptr makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body); + static std::unique_ptr makeFunctionCall(std::string name, std::vector> arguments, const std::shared_ptr scope); + static std::unique_ptr makeFunctionParameterList(const std::vector params, const std::shared_ptr scope); void addChild(std::unique_ptr child); void setElseBranch(std::unique_ptr elseNode); ASTNode *getElseBranch() const; + ASTNode *getFunctionParamList() const; + ASTNode *getFunctionParamReturnType() const; + ASTNode *getFunctionBody() const; void print(std::ostream &out, int indent = 0) const; }; -} +} // namespace zlang diff --git a/include/codegen/Canaries.hpp b/include/codegen/Canaries.hpp new file mode 100644 index 0000000..a0dcd8e --- /dev/null +++ b/include/codegen/Canaries.hpp @@ -0,0 +1,12 @@ +#include +#include + +class CanaryGenerator { +public: + static std::uint64_t generate() { + std::random_device rd; // Cryptographically secure random source + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dis; + return dis(gen); + } +}; \ No newline at end of file diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 53f396a..05704c7 100644 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -1,25 +1,24 @@ #pragma once +#include +#include +#include #include #include -#include -#include +#include + #include "ast/ASTNode.hpp" #include "codegen/RegisterAllocator.hpp" #include "typechecker/TypeChecker.hpp" -#include -namespace zlang -{ - enum class TargetTriple - { +namespace zlang { + enum class TargetTriple { X86_64_LINUX, X86_64_WINDOWS, LLVM_IR }; - class CodeGen - { + class CodeGen { protected: std::map assembly_comparison_operations = { {">=", "setge"}, @@ -37,11 +36,10 @@ namespace zlang RegisterAllocator alloc; std::ostream &outfinal; - std::ostringstream outGlobal; - std::ostringstream out; + std::ostringstream outGlobalStream; + std::ostringstream outStream; - std::string adjustReg(const std::string &r64, uint64_t bits) - { + std::string adjustReg(const std::string &r64, uint64_t bits) { std::string baseRegister = RegisterAllocator::getBaseReg(r64); static const std::unordered_map> registers_based_on_bytes = { {"rax", {"rax", "eax", "ax", "al"}}, @@ -63,11 +61,10 @@ namespace zlang auto it = registers_based_on_bytes.find(baseRegister); if (it == registers_based_on_bytes.end()) - throw std::runtime_error("Unknown register '" + baseRegister + "'\n\n" + out.str()); + throw std::runtime_error("Unknown register '" + baseRegister + "'\n\n"); const auto &ents = it->second; - switch (bits) - { + switch (bits) { case 64: return ents[0]; case 32: @@ -81,21 +78,16 @@ namespace zlang } } - static std::string getCorrectMove(uint64_t size_bytes, bool isfloat) - { - if (isfloat) - { + static std::string getCorrectMove(uint64_t size_bytes, bool isfloat) { + if (isfloat) { if (size_bytes == 4) return "movss"; else if (size_bytes == 8) return "movsd"; else throw std::runtime_error("Bad float move size"); - } - else - { - switch (size_bytes) - { + } else { + switch (size_bytes) { case 8: return "movq"; case 4: @@ -111,23 +103,29 @@ namespace zlang void noteType(const std::string ®ister_, const TypeInfo &type_) { regType[register_] = type_; } - virtual void generateStatement(std::unique_ptr statement) = 0; + virtual void generateStatement(std::unique_ptr statement, std::ostringstream &out) = 0; - virtual std::string emitExpression(std::unique_ptr node) = 0; + virtual std::string emitExpression(std::unique_ptr node, std::ostringstream &out) = 0; - virtual void emitEpilogue() = 0; - virtual void emitPrologue(std::unique_ptr blockNode) = 0; + virtual void emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax = false) = 0; + virtual void emitPrologue(std::shared_ptr scope, std::ostringstream &out) = 0; - virtual std::string generateIntegerLiteral(std::unique_ptr node) = 0; - virtual std::string generateFloatLiteral(std::unique_ptr node) = 0; - virtual std::string generateStringLiteral(std::unique_ptr node) = 0; - virtual std::string generateBooleanLiteral(std::unique_ptr node) = 0; - virtual std::string generateVariableAccess(std::unique_ptr node) = 0; - virtual void generateVariableReassignment(std::unique_ptr node) = 0; - virtual void generateVariableDeclaration(std::unique_ptr node) = 0; - virtual void generateIfStatement(std::unique_ptr node) = 0; - virtual std::string generateBinaryOperation(std::unique_ptr node) = 0; - virtual std::string generateUnaryOperation(std::unique_ptr node) = 0; + virtual std::string generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) = 0; + virtual std::string generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) = 0; + virtual std::string generateStringLiteral(std::unique_ptr node, std::ostringstream &out) = 0; + virtual std::string generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) = 0; + virtual std::string generateVariableAccess(std::unique_ptr node, std::ostringstream &out) = 0; + + virtual void generateVariableReassignment(std::unique_ptr node, std::ostringstream &out) = 0; + virtual void generateVariableDeclaration(std::unique_ptr node, std::ostringstream &out) = 0; + virtual void generateIfStatement(std::unique_ptr node, std::ostringstream &out) = 0; + + virtual std::string generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) = 0; + virtual std::string generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) = 0; + + virtual std::string generateFunctionCall(std::unique_ptr node, std::ostringstream &out) = 0; // Expression + virtual void generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force = false) = 0; // Statement + virtual void generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) = 0; // Statement public: CodeGen(RegisterAllocator alloc, std::ostream &outstream) : stringLabelCount(0), blockLabelCount(0), doubleLabelCount(0), floatLabelCount(0), alloc(alloc), outfinal(outstream) {} @@ -136,83 +134,113 @@ namespace zlang static std::unique_ptr create(TargetTriple target, std::ostream &outstream); }; - class CodeGenLinux : public CodeGen - { + class CodeGenLinux : public CodeGen { private: std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - void generateStatement(std::unique_ptr statement) override; - std::string emitExpression(std::unique_ptr node) override; - void emitEpilogue() override; - void emitPrologue(std::unique_ptr blockNode) override; - std::string generateIntegerLiteral(std::unique_ptr node) override; - std::string generateFloatLiteral(std::unique_ptr node) override; - std::string generateStringLiteral(std::unique_ptr node) override; - std::string generateBooleanLiteral(std::unique_ptr node) override; - std::string generateVariableAccess(std::unique_ptr node) override; - void generateVariableReassignment(std::unique_ptr node) override; - void generateVariableDeclaration(std::unique_ptr node) override; - void generateIfStatement(std::unique_ptr node) override; - std::string generateBinaryOperation(std::unique_ptr node) override; - std::string generateUnaryOperation(std::unique_ptr node) override; - std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType); - public: - ~CodeGenLinux() override = default; - CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream) {}; - void generate(std::unique_ptr program) override; - }; + std::string allocateOrSpill(bool isXMM, std::shared_ptr scope, std::ostringstream &out); + void restoreIfSpilled(const std::string ®, std::shared_ptr scope, std::ostringstream &out); - class CodeGenWindows : public CodeGen - { - private: - std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - void generateStatement(std::unique_ptr statement) override; - std::string emitExpression(std::unique_ptr node) override; - void emitEpilogue() override; - void emitPrologue(std::unique_ptr blockNode) override; - std::string generateIntegerLiteral(std::unique_ptr node) override; - std::string generateFloatLiteral(std::unique_ptr node) override; - std::string generateStringLiteral(std::unique_ptr node) override; - std::string generateBooleanLiteral(std::unique_ptr node) override; - std::string generateVariableAccess(std::unique_ptr node) override; - void generateVariableReassignment(std::unique_ptr node) override; - void generateVariableDeclaration(std::unique_ptr node) override; - void generateIfStatement(std::unique_ptr node) override; - std::string generateBinaryOperation(std::unique_ptr node) override; - std::string generateUnaryOperation(std::unique_ptr node) override; - std::string castValue(const std::string ®, const TypeInfo &fromType, const TypeInfo &toType); - std::string getVariableAddress(const ScopeContext &scope, const std::string &name) const; + void generateStatement(std::unique_ptr statement, std::ostringstream &out) override; - public: - ~CodeGenWindows() override = default; - CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; - void generate(std::unique_ptr program) override; - }; + std::string emitExpression(std::unique_ptr node, std::ostringstream &out) override; - class CodeGenLLVM : public CodeGen - { - private: - std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - std::unordered_map stringLiterals; - void generateStatement(std::unique_ptr statement) override; - std::string emitExpression(std::unique_ptr node) override; - void emitEpilogue() override; - void emitPrologue(std::unique_ptr blockNode) override; - std::string generateIntegerLiteral(std::unique_ptr node) override; - std::string generateFloatLiteral(std::unique_ptr node) override; - std::string generateStringLiteral(std::unique_ptr node) override; - std::string generateBooleanLiteral(std::unique_ptr node) override; - std::string generateVariableAccess(std::unique_ptr node) override; - void generateVariableReassignment(std::unique_ptr node) override; - void generateVariableDeclaration(std::unique_ptr node) override; - void generateIfStatement(std::unique_ptr node) override; - std::string generateBinaryOperation(std::unique_ptr node) override; - std::string generateUnaryOperation(std::unique_ptr node) override; - std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType); + void emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax = false) override; + void emitPrologue(std::shared_ptr scope, std::ostringstream &out) override; + + std::string generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateStringLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateVariableAccess(std::unique_ptr node, std::ostringstream &out) override; + + void generateVariableReassignment(std::unique_ptr node, std::ostringstream &out) override; + void generateVariableDeclaration(std::unique_ptr node, std::ostringstream &out) override; + void generateIfStatement(std::unique_ptr node, std::ostringstream &out) override; + + std::string generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) override; + std::string generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) override; + + std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType, const std::shared_ptr currentScope, std::ostringstream &out); + + std::string generateFunctionCall(std::unique_ptr node, std::ostringstream &out) override; // Expression + void generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force = false) override; // Statement + void generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) override; // Statement + void generateReturnstatement(std::unique_ptr node, std::ostringstream &out); // Statement public: - ~CodeGenLLVM() override = default; - CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream) {}; + ~CodeGenLinux() override = default; + CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream) {}; void generate(std::unique_ptr program) override; }; -} // namespace zlang \ No newline at end of file + + // class CodeGenWindows : public CodeGen { + // private: + // std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; + + // void generateStatement(std::unique_ptr statement) override; + // std::string emitExpression(std::unique_ptr node) override; + + // void emitEpilogue(std::shared_ptr scope) override; + // void emitPrologue(std::shared_ptr scope) override; + + // std::string generateIntegerLiteral(std::unique_ptr node) override; + // std::string generateFloatLiteral(std::unique_ptr node) override; + // std::string generateStringLiteral(std::unique_ptr node) override; + // std::string generateBooleanLiteral(std::unique_ptr node) override; + // std::string generateVariableAccess(std::unique_ptr node) override; + + // void generateVariableReassignment(std::unique_ptr node) override; + // void generateVariableDeclaration(std::unique_ptr node) override; + // void generateIfStatement(std::unique_ptr node) override; + + // std::string generateBinaryOperation(std::unique_ptr node) override; + // std::string generateUnaryOperation(std::unique_ptr node) override; + // std::string castValue(const std::string ®, const TypeInfo &fromType, const TypeInfo &toType); + // std::string getVariableAddress(const ScopeContext &scope, const std::string &name) const; + + // std::string generateFunctionCall(std::unique_ptr node) override; // Expression + // void generateFunctionDeclaration(std::unique_ptr node) override; // Statement + // void generateExternFunctionDeclaration(std::unique_ptr node) override; // Statement + + // public: + // ~CodeGenWindows() override = default; + // CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; + // void generate(std::unique_ptr program) override; + // }; + + // class CodeGenLLVM : public CodeGen { + // private: + // std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; + // std::unordered_map stringLiterals; + + // void generateStatement(std::unique_ptr statement) override; + // std::string emitExpression(std::unique_ptr node) override; + + // void emitEpilogue(std::shared_ptr scope) override; + // void emitPrologue(std::shared_ptr scope) override; + + // std::string generateIntegerLiteral(std::unique_ptr node) override; + // std::string generateFloatLiteral(std::unique_ptr node) override; + // std::string generateStringLiteral(std::unique_ptr node) override; + // std::string generateBooleanLiteral(std::unique_ptr node) override; + // std::string generateVariableAccess(std::unique_ptr node) override; + + // void generateVariableReassignment(std::unique_ptr node) override; + // void generateVariableDeclaration(std::unique_ptr node) override; + // void generateIfStatement(std::unique_ptr node) override; + + // std::string generateBinaryOperation(std::unique_ptr node) override; + // std::string generateUnaryOperation(std::unique_ptr node) override; + // std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType); + + // std::string generateFunctionCall(std::unique_ptr node) override; // Expression + // void generateFunctionDeclaration(std::unique_ptr node) override; // Statement + // void generateExternFunctionDeclaration(std::unique_ptr node) override; // Statement + + // public: + // ~CodeGenLLVM() override = default; + // CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream) {}; + // void generate(std::unique_ptr program) override; + // }; +} // namespace zlang \ No newline at end of file diff --git a/include/codegen/RegisterAllocator.hpp b/include/codegen/RegisterAllocator.hpp index 5415961..a4345a8 100644 --- a/include/codegen/RegisterAllocator.hpp +++ b/include/codegen/RegisterAllocator.hpp @@ -1,45 +1,105 @@ #pragma once +#include +#include +#include #include -#include #include -#include +#include + +namespace zlang { + + // Tips for noobs: CALLER saved are saved by the caller, CALLEE saved are restored by the function/routine + // So CALLER Union CALLEE should be equal to the set of all the registers, so the current state will never be corrupted. + + // Time wasted here: 3days and still counting.. And I am still unemployed. + + // GPRs on LINUX (SysV ABI) + static const std::vector SCRATCH_GPR_LINUX = {"rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11"}; + static const std::vector ARG_GPR_LINUX = {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}; + static const std::vector CALLER_GPR_LINUX = {"rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11"}; + static const std::vector CALLEE_GPR_LINUX = {"rbx", "r12", "r13", "r14", "r15"}; + // XMMs on LINUX (SysV ABI) + static const std::vector SCRATCH_XMM_LINUX = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; + static const std::vector ARG_XMM_LINUX = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"}; + static const std::vector CALLER_XMM_LINUX = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; // Will be used freely by function + static const std::vector CALLEE_XMM_LINUX = {/* None */}; // Why would you do this linus ? // The function is responsible to restore the state + // GPRs on WINDOWS (MSVC) + static const std::vector SCRATCH_GPR_MSVC = {"rax", "rcx", "rdx", "r8", "r9", "r10", "r11"}; + static const std::vector ARG_GPR_MSVC = {"rcx", "rdx", "r8", "r9"}; + static const std::vector CALLER_GPR_MSVC = {"rax", "rcx", "rdx", "r8", "r9", "r10", "r11"}; + static const std::vector CALLEE_GPR_MSVC = {"rbx", "rdi", "rsi", "r12", "r13", "r14", "r15"}; + // XMMs on WINDOWS (MSVC) + static const std::vector SCRATCH_XMM_MSVC = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"}; + static const std::vector ARG_XMM_MSVC = {"xmm0", "xmm1", "xmm2", "xmm3"}; + static const std::vector CALLER_XMM_MSVC = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"}; + static const std::vector CALLEE_XMM_MSVC = {"xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; + + class RegisterAllocator { + struct SpillInfo { + std::string spillSlot; // e.g. -16(%rbp) or [rbp - 16] + bool isXMM; + }; + + std::unordered_map spilledRegs; + std::unordered_map spilledArgumentRegs; -namespace zlang -{ - /// Simple register allocator for x86_64 - class RegisterAllocator - { public: /// Initialize allocator with target-specific registers - static RegisterAllocator forSysV(); // Linux ABI - static RegisterAllocator forMSVC(); // Windows ABI + static RegisterAllocator forSysV(); // Linux ABI + static RegisterAllocator forMSVC(); // Windows ABI - /// Allocate a free register; throws if none available std::string allocate(); + bool isInUse(std::string reg); + bool isInUseXMM(std::string reg); - /// Free a previously allocated register; throws if invalid void free(const std::string ®); - /// Allocate a free XMM register; throws if none available std::string allocateXMM(); - /// Free a previously allocated XMM register; throws if invalid bool freeXMM(const std::string ®); - /// Reset allocator state (mark all as free) + std::string allocateArgument(uint8_t position); + std::string allocateArgumentXMM(uint8_t position); + void freeArgument(const std::string ®); + bool freeArgumentXMM(const std::string ®); + bool isInUseArgument(const std::string ®) const; + bool isInUseArgumentXMM(const std::string ®) const; + void reset(); RegisterAllocator() = default; static std::string getBaseReg(const std::string ®); + std::string pickVictimXMM(); + std::string pickVictim(); + void markSpilledXMM(const std::string ®, const std::string &spillSlot); + void markSpilled(const std::string ®, const std::string &spillSlot); + bool isSpilled(const std::string ®) const; + std::string spillSlotFor(const std::string ®) const; + void unSpillXMM(const std::string ®, zlang::CodegenOutputFormat format, std::ostream &out); + void unSpill(const std::string ®, zlang::CodegenOutputFormat format, std::ostream &out); + void touch(const std::string ®); + void touchXMM(const std::string ®); + void emitSpillRestore(const std::string ®, const std::string &slot, bool isXMM, zlang::CodegenOutputFormat format, std::ostream &out); + private: - RegisterAllocator(std::vector regs); + RegisterAllocator(std::vector regs, std::vector XMMregs, std::vector argumentRegs, std::vector argumentXMMRegs); std::vector available; std::unordered_set inUse; - std::vector availableXMM = { // Default XMMs - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; + std::vector availableXMM; std::unordered_set inUseXMM; + + std::unordered_set inUseArgumentXMM; + std::unordered_set inUseArgument; + + std::list lruRegs; + std::list lruXMMRegs; + + public: + const std::vector availableArgumentRegs; + const std::vector availableArgumentRegsXMM; }; } diff --git a/include/lexer/Lexer.hpp b/include/lexer/Lexer.hpp index a136567..2813bd6 100644 --- a/include/lexer/Lexer.hpp +++ b/include/lexer/Lexer.hpp @@ -17,15 +17,18 @@ namespace zlang StringLiteral, BoolLiteral, Symbol, - Keyword, + Comma, + Arrow, EndOfFile, SemiColon, Equal, + Return, If, ElseIf, Else, LeftBrace, RightBrace, + Function, LeftParen, RightParen, Unknown @@ -64,8 +67,6 @@ namespace zlang return "StringLiteral"; case Kind::Symbol: return "Symbol"; - case Kind::Keyword: - return "Keyword"; case Kind::EndOfFile: return "EndOfFile"; case Kind::SemiColon: @@ -88,6 +89,14 @@ namespace zlang return "RightParen"; case Kind::LeftParen: return "LeftParen"; + case Kind::Arrow: + return "Arrow"; + case Kind::Comma: + return "Comma"; + case Kind::Function: + return "Function"; + case Kind::Return: + return "Return"; } return "Invalid"; } diff --git a/include/parser/NameMapper.hpp b/include/parser/NameMapper.hpp new file mode 100644 index 0000000..98ac5a0 --- /dev/null +++ b/include/parser/NameMapper.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +class NameMapper + { + public: + inline std::string mapVariable(const std::string &name, const std::string &scopeName) + { + std::string mangled = name + "___" + scopeName + "___v" + std::to_string(varCounter_++); + return mangled; + } + + inline std::string mapFunction(const std::string &name, const std::string &scopeName) + { + std::string mangled = name + "___" + scopeName + "___f" + std::to_string(funcCounter_++); + return mangled; + } + + inline std::string mapType(const std::string &name, const std::string &scopeName) + { + std::string mangled = name + "___" + scopeName + "___t" + std::to_string(typeCounter_++); + return mangled; + } + + private: + size_t varCounter_ = 0; + size_t funcCounter_ = 0; + size_t typeCounter_ = 0; + }; diff --git a/include/parser/Parser.hpp b/include/parser/Parser.hpp index 0f93a1e..decbbfe 100644 --- a/include/parser/Parser.hpp +++ b/include/parser/Parser.hpp @@ -1,20 +1,18 @@ #pragma once #include -#include "parser/ScopeContext.hpp" -#include "../lexer/Lexer.hpp" + #include "../ast/ASTNode.hpp" +#include "../lexer/Lexer.hpp" #include "common/Errors.hpp" #include "common/Logging.hpp" +#include "parser/ScopeContext.hpp" -namespace zlang -{ - class Parser - { +namespace zlang { + class Parser { public: explicit Parser(Lexer &lexer); std::unique_ptr parse(); - bool isCorrect() - { + bool isCorrect() { return shouldTypecheck; } @@ -35,12 +33,13 @@ namespace zlang std::unique_ptr parseConditionals(); std::unique_ptr parseExpression(bool expect_exclaim = false); std::unique_ptr parsePrimary(); + std::unique_ptr parseFunctionDeclaration(); int getPrecedence(const std::string &op) const; std::unique_ptr parseUnary(); std::unique_ptr parseBinaryRHS(int exprPrec, std::unique_ptr lhs); - void enterScope(std::string name); + void enterScope(const std::string &name, bool isFunction); void exitScope(); std::unique_ptr parseBlock(); }; diff --git a/include/parser/ScopeContext.hpp b/include/parser/ScopeContext.hpp index f76cadf..4e3155d 100644 --- a/include/parser/ScopeContext.hpp +++ b/include/parser/ScopeContext.hpp @@ -1,117 +1,174 @@ #pragma once -#include -#include +#include #include +#include #include +#include +#include #include -#include -#include -namespace zlang -{ +namespace zlang { + struct VariableInfo { + std::string type; + }; + + struct TypeInfo { + std::uint32_t bits = 0; + std::uint32_t align = 0; + bool isFloat = false; + bool isSigned = false; + bool isString = false; + bool isBoolean = false; + bool isPointer = false; + bool isUserDefined = false; + bool isFunction = false; - struct VariableInfo - { + std::string name; + + std::string to_string() const; + }; + + struct ParamInfo { + std::string name; std::string type; + std::string to_string() const; }; - struct FunctionInfo - { - std::vector paramTypes; + inline std::string ParamInfo::to_string() const { + std::string ret = "ParamInfo( .name: " + name + ", .type: " + type + ")"; + return ret; + } + + struct FunctionInfo { + std::vector paramTypes; std::string returnType; + std::string name; + std::string label; + bool isExtern; + std::string to_string() const; }; - struct TypeInfo - { - // details of user-defined types (e.g., struct/class definitions) - // TODO - std::uint32_t bits; - std::uint32_t align; - bool isFloat; - bool isSigned; - - std::string to_string() const - { - std::string ret = ""; - ret += "TypeInfo( .bits: " + std::to_string(bits) + ", .align: " + std::to_string(align) + ", .isFloat: " + (isFloat ? "true" : "false") + ", .isSigned: " + (isSigned ? "true" : "false") + " )\n"; - return ret; + inline std::string FunctionInfo::to_string() const { + std::string ret = "FunctionInfo( params: ("; + for (size_t i = 0; i < paramTypes.size(); ++i) { + ret += paramTypes[i].to_string(); + if (i != paramTypes.size() - 1) + ret += ", "; } - }; + ret += "), return: " + returnType + " )"; + return ret; + } + + inline std::string TypeInfo::to_string() const { + std::string ret = + "TypeInfo( .name: " + name + ", .bits: " + std::to_string(bits) + + ", .align: " + std::to_string(align) + + ", .isFloat: " + (isFloat ? "true" : "false") + + ", .isSigned: " + (isSigned ? "true" : "false") + + ", .isBoolean: " + (isBoolean ? "true" : "false") + + ", .isString: " + (isString ? "true" : "false") + + ", .isPointer: " + (isPointer ? "true" : "false") + + ", .isUserDefined: " + (isUserDefined ? "true" : "false") + + ", .isFunction: " + (isFunction ? "true" : "false") + " )"; + return ret; + } - static int variableNumber = 0; + class FunctionScope; - class ScopeContext - { + class ScopeContext : public std::enable_shared_from_this { public: - std::string name; - ScopeContext(std::shared_ptr parent = nullptr) : stackOffset(0), parent_(parent){}; - ScopeContext(std::shared_ptr parent = nullptr, std::string name = "") : name(name), stackOffset(0), parent_(parent){}; - std::unordered_map name_mappings; - std::int64_t stackOffset; - std::shared_ptr parent_; + ScopeContext(std::string name, + std::shared_ptr parent = nullptr) + : name_(std::move(name)), parent_(std::move(parent)) {} + virtual ~ScopeContext() = default; + virtual std::string kind() const = 0; bool defineVariable(const std::string &name, const VariableInfo &info); - void defineFunction(const std::string &name, const FunctionInfo &info); + void defineFunction(const std::string &name, FunctionInfo info); void defineType(const std::string &name, const TypeInfo &info); - std::string getMapping(std::string varName); VariableInfo lookupVariable(const std::string &name) const; FunctionInfo lookupFunction(const std::string &name) const; TypeInfo lookupType(const std::string &name) const; - std::unordered_map offsetTable; + std::optional + lookupVariableInCurrentContext(const std::string &name) const; + virtual std::int64_t allocateStack(const std::string &varName, + const TypeInfo &type); std::int64_t getVariableOffset(const std::string &name) const; bool isGlobalScope() const; bool isGlobalVariable(const std::string &name) const; - void printGlobalContext() const - { - const ScopeContext *ctx = this; - while (ctx->parent_) - ctx = ctx->parent_.get(); - - std::cout << "=== Global Scope ===\n"; - // Variables - if (!ctx->vars_.empty()) - { - std::cout << "Variables:\n"; - for (auto &kv : ctx->vars_) - { - std::cout << " " << kv.first << ": " - << kv.second.type << "\n"; - } - } - // Functions - if (!ctx->funcs_.empty()) - { - std::cout << "Functions:\n"; - for (auto &kv : ctx->funcs_) - { - std::cout << " " << kv.first << "(\n"; - for (size_t i = 0; i < kv.second.paramTypes.size(); ++i) - { - std::cout << kv.second.paramTypes[i]; - if (i + 1 < kv.second.paramTypes.size()) - std::cout << ", "; - } - std::cout << ") -> " << kv.second.returnType << "\n"; - } - } - // Types - if (!ctx->types_.empty()) - { - std::cout << "Types:\n"; - for (auto &kv : ctx->types_) - { - std::cout << " " << kv.first - << " (" << kv.second.bits << " bits, align=" << kv.second.align - << ", " << (kv.second.isFloat ? "float" : "int") - << ")\n"; - } - } - std::cout << std::endl; - } - std::optional lookupVariableInCurrentContext(const std::string &name) const; + std::shared_ptr parent() const { return parent_; } + const std::string &name() const { return name_; } + std::string getMapping(std::string name); + virtual void printScope(std::ostream &out, int indent = 0) const; + std::shared_ptr findEnclosingFunctionScope(); + std::shared_ptr getGlobal(); + std::string returnType = "none"; - private: + protected: + std::string name_; + std::shared_ptr parent_; std::unordered_map vars_; std::unordered_map funcs_; std::unordered_map types_; + std::unordered_map offsetTable_; + std::unordered_map variable_name_mappings; + }; + + class FunctionScope : public ScopeContext { + public: + FunctionScope(std::string name, + std::shared_ptr parent = nullptr); + ~FunctionScope() override; + void printScope(std::ostream &out, int indent = 0) const override; + inline std::string kind() const override { return "Function"; } + std::int64_t allocateStack(const std::string &varName, + const TypeInfo &type) override; + std::int64_t getStackOffset() const; + void setCanary(std::uint64_t canary) { + logMessage("Setting canary of: " + name_); + this->canary = canary; + } + std::uint64_t getCanary() { + logMessage("Getting canary of: " + name_); + return this->canary; + } + std::string allocateSpillSlot(std::int64_t size); + std::int64_t getSpillSize() const; + void freeSpillSlot(const std::string &slot, std::int64_t size); + + private: + std::int64_t stackOffset_; + std::uint64_t canary; + std::int64_t nextSpillOffset_ = 0; + std::vector> freeSpillSlots_; + inline static std::int64_t alignSize(const TypeInfo &type) { + return std::max(type.align, 8); + } }; -} + class BlockScope : public ScopeContext { + public: + BlockScope(std::string name, std::shared_ptr funcScope, + std::shared_ptr parent); + ~BlockScope() override; + void printScope(std::ostream &out, int indent = 0) const override; + inline std::string kind() const override { return "Block"; } + std::int64_t allocateStack(const std::string &varName, + const TypeInfo &type) override; + + private: + std::shared_ptr funcScope_; + }; + + class NamespaceScope : public ScopeContext { + public: + NamespaceScope(std::string name, std::shared_ptr parent = nullptr) + : ScopeContext(std::move(name), std::move(parent)) {} + + std::string kind() const override { return "Namespace"; } + + std::int64_t allocateStack(const std::string &, const TypeInfo &) override { + // namespaces don’t allocate stack space + throw std::runtime_error("Namespaces cannot define stack variables"); + } + }; + +} // namespace zlang diff --git a/include/typechecker/TypeChecker.hpp b/include/typechecker/TypeChecker.hpp index f88e2cd..94f8951 100644 --- a/include/typechecker/TypeChecker.hpp +++ b/include/typechecker/TypeChecker.hpp @@ -1,64 +1,81 @@ #pragma once #include +#include #include + #include "ast/ASTNode.hpp" -#include "parser/ScopeContext.hpp" #include "common/Errors.hpp" -#include +#include "parser/ScopeContext.hpp" + +// TODO: Check that all paths inside the function return appropriate value. -namespace zlang -{ +namespace zlang { static const std::set numeric_types = {"integer", "size_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "int8_t", "int16_t", "int32_t", "int64_t", "float", "double"}; static const std::set integral_types = {"integer", "size_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "int8_t", "int16_t", "int32_t", "int64_t"}; - class TypeChecker - { + class TypeChecker { public: - /// Walks the whole AST, logs any errors found. void check(const std::unique_ptr &program); - bool shouldCodegen() - { + bool shouldCodegen() { return shouldCodegen_; } - - static inline std::string typeName(const TypeInfo &info) - { - if (info.isFloat) - return info.bits == 32 ? "float" : "double"; - return (info.isSigned ? "int" : "uint") + std::to_string(info.bits) + "_t"; + static inline std::string typeName(const TypeInfo &info) { + return info.name; } - static inline TypeInfo promoteType(const TypeInfo &a, const TypeInfo &b) - { - if (a.isFloat || b.isFloat) - { + static inline TypeInfo promoteType(const TypeInfo &a, const TypeInfo &b) { + if (a.isString || b.isString || + a.isPointer || b.isPointer || + a.isUserDefined || b.isUserDefined) { + throw std::runtime_error( + "Invalid type promotion between: " + typeName(a) + " and " + typeName(b)); + } + + if (a.isFloat || b.isFloat) { if (a.isFloat && b.isFloat) return a.bits > b.bits ? a : b; - if (a.isFloat) - { + + if (a.isFloat) { if (b.bits > a.bits) - { - return {b.bits, b.bits / 8, true, true}; - } + return TypeInfo{ + .bits = b.bits, + .align = b.bits / 8, + .isFloat = true, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = (b.bits == 64 ? "double" : "float")}; else - { return a; - } - } - else - { + } else { if (a.bits > b.bits) - { - return {a.bits, a.bits / 8, true, true}; - } + return TypeInfo{ + .bits = a.bits, + .align = a.bits / 8, + .isFloat = true, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = (a.bits == 64 ? "double" : "float")}; else - { return b; - } } } + if (a.bits != b.bits) return a.bits > b.bits ? a : b; + + if (a.isSigned && !b.isSigned) + return a; + if (!a.isSigned && b.isSigned) + return b; + return a; } @@ -72,4 +89,4 @@ namespace zlang bool shouldCodegen_ = true; }; -} // namespace zlang \ No newline at end of file +} // namespace zlang \ No newline at end of file diff --git a/main.cpp b/main.cpp index d39b1a9..7fc8e28 100644 --- a/main.cpp +++ b/main.cpp @@ -2,28 +2,24 @@ using namespace zlang; -int main(int argc, char *argv[]) -{ - if (argc < 2) - { +int main(int argc, char *argv[]) { + if (argc < 2) { CommandLine::printUsage(argv[0]); return 0; } CommandLine cli(argc, argv); - if (cli.hasError()) - { + + if (cli.hasError()) { return 1; } - if (cli.showHelp()) - { + if (cli.showHelp()) { CommandLine::printUsage(argv[0]); return 0; } - if (cli.showFormats()) - { + if (cli.showFormats()) { CommandLine::printFormats(); return 0; } @@ -32,16 +28,14 @@ int main(int argc, char *argv[]) assert(inputFile.ends_with(".zz")); - if (inputFile.empty()) - { + if (inputFile.empty()) { logError(zlang::Error(zlang::ErrorType::Generic, "No input files.")); CommandLine::printUsage(argv[0]); return 1; } std::optional source = zlang::File::readAllText(inputFile); - if (!source) - { + if (!source) { logError(zlang::Error(zlang::ErrorType::Generic, "Failed to read from " + inputFile)); return 1; @@ -50,89 +44,87 @@ int main(int argc, char *argv[]) // Parse source Lexer lexer(source.value()); Parser parser(lexer); - + logMessage("Parsing"); std::unique_ptr program = parser.parse(); - if (!parser.isCorrect()) - { + if (!parser.isCorrect()) { return 1; } - if (!program.get()) - { + if (!program.get()) { zlang::logError(Error(ErrorType::Generic, "Parsing Failed")); return 1; } - if (cli.printAST()) - { + if (cli.printAST()) { program.get()->print(std::cout); } + logMessage("TypeChecking"); + // Type checking TypeChecker typeChecker; typeChecker.check(program); + logMessage("Code Genning"); + if (!typeChecker.shouldCodegen()) return 1; + try { + program->scope->lookupFunction("main"); + } catch (...) { + logError(Error(ErrorType::Generic, "Main Function does'nt exist in program scope (GLOBALLY)")); + exit(1); + } std::ostream *outstream = &std::cout; std::ofstream ofs; // Only open the file if requested: - if (!cli.getOutputFile().empty()) - { + if (!cli.getOutputFile().empty()) { ofs.open(cli.getOutputFile()); - if (!ofs) - { + if (!ofs) { std::cerr << "Error: cannot open output file: " << cli.getOutputFile() << "\n"; std::exit(1); } - outstream = &ofs; // now point at the file + outstream = &ofs; // now point at the file } std::unique_ptr cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); - switch (cli.getFormat()) - { - case CodegenOutputFormat::Default: -#ifdef _WIN64 - cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); -#endif -#ifdef __linux__ - cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); -#endif - break; - - case CodegenOutputFormat::X86_64_MSWIN: - { - cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); - break; - } - - case CodegenOutputFormat::X86_64_LINUX: - { - cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); - break; - } - - case CodegenOutputFormat::LLVM_IR: - { - cg = CodeGen::create(TargetTriple::LLVM_IR, *outstream); - break; - } - - default: - std::cerr << "This should not happen, ACP Pradhyumn...\n"; - exit(1); - } - try - { + // switch (cli.getFormat()) { + // case CodegenOutputFormat::Default: + // #ifdef _WIN64 + // cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); + // #endif + // #ifdef __linux__ + // cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); + // #endif + // break; + + // case CodegenOutputFormat::X86_64_MSWIN: { + // cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); + // break; + // } + + // case CodegenOutputFormat::X86_64_LINUX: { + // cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); + // break; + // } + + // case CodegenOutputFormat::LLVM_IR: { + // cg = CodeGen::create(TargetTriple::LLVM_IR, *outstream); + // break; + // } + + // default: + // std::cerr << "This should not happen, ACP Pradhyumn...\n"; + // exit(1); + // } + try { cg->generate(std::move(program)); - } - catch (std::exception const &exc) - { + } catch (std::exception const &exc) { std::cerr << "ERROR: " << exc.what() << "\n"; } diff --git a/src/ast/ASTNode.cpp b/src/ast/ASTNode.cpp index ec9187f..6833ba2 100644 --- a/src/ast/ASTNode.cpp +++ b/src/ast/ASTNode.cpp @@ -1,46 +1,36 @@ #include "all.hpp" -namespace zlang -{ - std::unique_ptr ASTNode::makeProgramNode(const std::shared_ptr scope) - { +namespace zlang { + std::unique_ptr ASTNode::makeProgramNode(const std::shared_ptr scope) { return std::make_unique(NodeType::Program, "", scope); } std::unique_ptr ASTNode::makeVariableReassignmentNode( const std::string &name, - std::unique_ptr expr, const std::shared_ptr scope) - { + std::unique_ptr expr, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::VariableReassignment, name, scope); if (expr) node->addChild(std::move(expr)); return node; } - std::unique_ptr ASTNode::makeVariableAccessNode(const std::string &name, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeVariableAccessNode(const std::string &name, const std::shared_ptr scope) { return std::make_unique(NodeType::VariableAccess, name, scope); } - std::unique_ptr ASTNode::makeIntegerLiteralNode(const std::string &literal, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeIntegerLiteralNode(const std::string &literal, const std::shared_ptr scope) { return std::make_unique(NodeType::IntegerLiteral, literal, scope); } - void ASTNode::setElseBranch(std::unique_ptr elseNode) - { - if (children.size() < 2) - { + void ASTNode::setElseBranch(std::unique_ptr elseNode) { + if (children.size() < 2) { throw std::logic_error("setElseBranch on a non‑conditional node"); } - if (children.size() == 2) - { + if (children.size() == 2) { // no else/elif attached yet: attach here children.push_back(std::move(elseNode)); - } - else - { + } else { // we already have an elseBranch in children[2]: // forward the new elseNode into _that_ node ASTNode *existing = children[2].get(); @@ -48,8 +38,7 @@ namespace zlang } } - ASTNode *ASTNode::getElseBranch() const - { + ASTNode *ASTNode::getElseBranch() const { if (children.size() < 3 || children[2] == nullptr) return nullptr; @@ -65,45 +54,34 @@ namespace zlang const std::string &name, std::unique_ptr typeAnnotation, std::unique_ptr initializer, - const std::shared_ptr scope) - { + const std::shared_ptr scope) { auto node = std::make_unique(NodeType::VariableDeclaration, name, scope); bool result = scope.get()->defineVariable(name, {typeAnnotation.get()->value}); - if (result) - { + if (result) { if (typeAnnotation) node->addChild(std::move(typeAnnotation)); if (initializer) node->addChild(std::move(initializer)); return node; - } - else - { + } else { return std::nullopt; } } - std::unique_ptr ASTNode::makeSymbolNode(const std::string &name, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeSymbolNode(const std::string &name, const std::shared_ptr scope) { return std::make_unique(NodeType::Symbol, name, scope); } - std::unique_ptr ASTNode::makeFloatLiteralNode(const std::string &lit, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeFloatLiteralNode(const std::string &lit, const std::shared_ptr scope) { return std::make_unique(NodeType::FloatLiteral, lit, scope); } - std::unique_ptr ASTNode::makeStringLiteralNode(const std::string &lit, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeStringLiteralNode(const std::string &lit, const std::shared_ptr scope) { return std::make_unique(NodeType::StringLiteral, lit, scope); } - std::unique_ptr ASTNode::makeBooleanLiteralNode(const bool value, const std::shared_ptr scope) - { - if (value) - { + std::unique_ptr ASTNode::makeBooleanLiteralNode(const bool value, const std::shared_ptr scope) { + if (value) { return std::make_unique(NodeType::BooleanLiteral, "true", scope); - } - else - { + } else { return std::make_unique(NodeType::BooleanLiteral, "false", scope); } } @@ -111,8 +89,7 @@ namespace zlang std::unique_ptr ASTNode::makeBinaryOp( const std::string &op, std::unique_ptr lhs, - std::unique_ptr rhs, const std::shared_ptr scope) - { + std::unique_ptr rhs, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::BinaryOp, op, scope); node->addChild(std::move(lhs)); node->addChild(std::move(rhs)); @@ -121,16 +98,14 @@ namespace zlang std::unique_ptr ASTNode::makeUnaryOp( const std::string &op, - std::unique_ptr operand, const std::shared_ptr scope) - { + std::unique_ptr operand, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::UnaryOp, op, scope); node->addChild(std::move(operand)); return node; } std::unique_ptr ASTNode::makeIfStatement(std::unique_ptr cond, - std::unique_ptr thenBlock, const std::shared_ptr scope) - { + std::unique_ptr thenBlock, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::IfStatement, "", scope); node->addChild(std::move(cond)); node->addChild(std::move(thenBlock)); @@ -138,33 +113,89 @@ namespace zlang } std::unique_ptr ASTNode::makeElseIfStatement(std::unique_ptr cond, - std::unique_ptr thenBlock, const std::shared_ptr scope) - { + std::unique_ptr thenBlock, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::ElseIfStatement, "", scope); node->addChild(std::move(cond)); node->addChild(std::move(thenBlock)); return node; } - std::unique_ptr ASTNode::makeElseStatement(std::unique_ptr elseBlock, const std::shared_ptr scope) - { + std::unique_ptr ASTNode::makeElseStatement(std::unique_ptr elseBlock, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::ElseStatement, "", scope); node->addChild(std::move(elseBlock)); return node; } - void ASTNode::addChild(std::unique_ptr child) - { + std::unique_ptr ASTNode::makeFunctionParameterList(const std::vector params, std::shared_ptr scope) { + auto node = std::make_unique(NodeType::FunctionParameterList, "", scope); + for (ParamInfo pi : params) { + auto child = std::make_unique(NodeType::FunctionParameter, "", scope); + auto name = std::make_unique(NodeType::Symbol, pi.name, scope); + auto type = std::make_unique(NodeType::Symbol, pi.type, scope); + child->children.push_back(std::move(name)); + child->children.push_back(std::move(type)); + node->children.push_back(std::move(child)); + } + return node; + } + + std::unique_ptr ASTNode::makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType) { + auto node = std::make_unique(NodeType::ExternFunction, name, scope); + auto paramsList = ASTNode::makeFunctionParameterList(params, scope); + auto returnType_ = std::make_unique(NodeType::FunctionReturnType, returnType, scope); + node->children.push_back(std::move(paramsList)); + node->children.push_back(std::move(returnType_)); + scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = true}); + return node; + } + + std::unique_ptr ASTNode::makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body) { + auto node = std::make_unique(NodeType::Function, name, scope); + auto paramsList = ASTNode::makeFunctionParameterList(params, scope); + auto returnType_ = std::make_unique(NodeType::FunctionReturnType, returnType, scope); + node->children.push_back(std::move(paramsList)); + node->children.push_back(std::move(returnType_)); + body->scope->returnType = returnType; + for (ParamInfo pi : params) { + body->scope->defineVariable(pi.name, VariableInfo{.type = pi.type}); + } + node->children.push_back(std::move(body)); + scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = false}); + return node; + } + + ASTNode *ASTNode::getFunctionParamList() const { + assert(type == NodeType::Function || type == NodeType::ExternFunction); + return children[0].get(); + } + ASTNode *ASTNode::getFunctionParamReturnType() const { + assert(type == NodeType::Function || type == NodeType::ExternFunction); + return children[1].get(); + } + ASTNode *ASTNode::getFunctionBody() const { + assert(type == NodeType::Function); + return children[2].get(); + } + + std::unique_ptr ASTNode::makeFunctionCall(std::string name, std::vector> arguments, const std::shared_ptr scope) { + auto node = std::make_unique(NodeType::FunctionCall, name, scope); + auto args = std::make_unique(NodeType::FunctionCallArgumentList, "", scope); + for (auto &arg : arguments) { + args->addChild(std::move(arg)); + } + node->addChild(std::move(args)); + return node; + } + + void ASTNode::addChild(std::unique_ptr child) { children.push_back(std::move(child)); } - void ASTNode::print(std::ostream &out, int indent) const - { + void ASTNode::print(std::ostream &out, int indent) const { for (int i = 0; i < indent; ++i) out << ' '; - switch (type) - { + switch (type) { case NodeType::Program: out << "Program"; break; @@ -207,13 +238,36 @@ namespace zlang case NodeType::ElseStatement: out << "Else(" << value << ")"; break; + case NodeType::ExternFunction: + out << "ExternFunction(" << value << ")"; + break; + case NodeType::Function: + out << "Functions(" << value << ")"; + break; + case NodeType::FunctionParameterList: + out << "ParameterList(" << value << ")"; + break; + case NodeType::FunctionParameter: + out << "Parameter(" << value << ")"; + break; + case NodeType::FunctionReturnType: + out << "ReturnType(" << value << ")"; + break; + case NodeType::FunctionCall: + out << "FunctionCall(" << value << ")"; + break; + case NodeType::FunctionCallArgumentList: + out << "Arguments(" << value << ")"; + break; + case NodeType::ReturnStatement: + out << "Return(" << value << ")"; + break; default: out << "Unknown"; } out << '\n'; - for (const auto &child : children) - { + for (const auto &child : children) { child->print(out, indent + 2); } } diff --git a/src/codegen/CodeGen.cpp b/src/codegen/CodeGen.cpp index ed2d78d..8290966 100644 --- a/src/codegen/CodeGen.cpp +++ b/src/codegen/CodeGen.cpp @@ -1,19 +1,17 @@ #include "all.hpp" -namespace zlang -{ +namespace zlang { CodeGen::~CodeGen() = default; - std::unique_ptr CodeGen::create(TargetTriple target, std::ostream &outstream) - { - switch (target) - { - case TargetTriple::X86_64_LINUX: - return std::make_unique(outstream); - case TargetTriple::X86_64_WINDOWS: - return std::make_unique(outstream); - case TargetTriple::LLVM_IR: - return std::make_unique(outstream); - } + std::unique_ptr CodeGen::create(TargetTriple target, std::ostream &outstream) { + // switch (target) { + // case TargetTriple::X86_64_LINUX: + // return std::make_unique(outstream); + // case TargetTriple::X86_64_WINDOWS: + // return std::make_unique(outstream); + // case TargetTriple::LLVM_IR: + // return std::make_unique(outstream); + // } + return std::make_unique(outstream); throw std::runtime_error("Unknown target"); } diff --git a/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index 20f256d..f4fa37d 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -1,589 +1,501 @@ -#include "all.hpp" - -namespace zlang -{ - static std::string fresh() - { - static int cnt = 0; - return "%tmp" + std::to_string(cnt++); - } - std::string toHexFloatFromStr(const std::string &valStr, bool isF32) - { - std::ostringstream oss; - oss << std::scientific << std::setprecision(16); - if (isF32) - { - float v = std::stof(valStr); - oss << v; - } - else - { - double v = std::stod(valStr); - oss << v; - } - return oss.str(); - } - std::string CodeGenLLVM::castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType) - { - if (fromType.isFloat == toType.isFloat && fromType.bits == toType.bits) - return val; - - std::string tmp = fresh(); - - if (!fromType.isFloat && toType.isFloat) - { - std::string intTy = "i" + std::to_string(fromType.bits); - std::string floatTy = (toType.bits == 64 ? "double" : "float"); - std::string instr = fromType.isSigned ? "sitofp" : "uitofp"; - out << " " << tmp << " = " << instr << " " << intTy << " " << val << " to " << floatTy << "\n"; - } - else if (fromType.isFloat && !toType.isFloat) - { - std::string floatTy = (fromType.bits == 64 ? "double" : "float"); - std::string intTy = "i" + std::to_string(toType.bits); - std::string instr = toType.isSigned ? "fptosi" : "fptoui"; - out << " " << tmp << " = " << instr << " " << floatTy << " " << val << " to " << intTy << "\n"; - } - else if (!fromType.isFloat && !toType.isFloat) - { - std::string fromTy = "i" + std::to_string(fromType.bits); - std::string toTy = "i" + std::to_string(toType.bits); - if (fromType.bits < toType.bits) - { - std::string instr = fromType.isSigned ? "sext" : "zext"; - out << " " << tmp << " = " << instr << " " << fromTy << " " << val << " to " << toTy << "\n"; - } - else if (fromType.bits > toType.bits) - { - out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\n"; - } - else if (fromType.isSigned != toType.isSigned) - { - // Retag value if signedness differs but bit-width is the same - out << " " << tmp << " = add " << fromTy << " " << val << ", 0\n"; - } - else - { - return val; - } - } - else if (fromType.isFloat && toType.isFloat) - { - std::string fromTy = (fromType.bits == 64 ? "double" : "float"); - std::string toTy = (toType.bits == 64 ? "double" : "float"); - if (fromType.bits < toType.bits) - { - out << " " << tmp << " = fpext " << fromTy << " " << val << " to " << toTy << "\n"; - } - else if (fromType.bits > toType.bits) - { - out << " " << tmp << " = fptrunc " << fromTy << " " << val << " to " << toTy << "\n"; - } - else - { - return val; - } - } - else - { - throw std::runtime_error("Unsupported cast from type to type"); - } - - noteType(tmp, toType); - return tmp; - } - std::string CodeGenLLVM::generateIntegerLiteral(std::unique_ptr node) - { - std::string name = fresh(); - out << " " << name << " = add i64 0, " << node->value << "\n"; - noteType(name, node->scope->lookupType("int64_t")); - return name; - } - std::string CodeGenLLVM::generateFloatLiteral(std::unique_ptr node) - { - std::string tmp = fresh(); - std::string val = node->value; - bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); - if (isF32) - val.pop_back(); - - std::string hexVal = toHexFloatFromStr(val, isF32); - - if (isF32) - { - out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; - noteType(tmp, node->scope->lookupType("float")); - } - else - { - out << " " << tmp << " = fadd double 0.0, " << hexVal << "\n"; - noteType(tmp, node->scope->lookupType("double")); - } - return tmp; - } - std::string CodeGenLLVM::generateStringLiteral(std::unique_ptr node) - { - std::string value = node->value; - - auto it = stringLiterals.find(value); - if (it != stringLiterals.end()) - return it->second; - - std::string name = "@.str" + std::to_string(stringLabelCount++); - size_t len = value.size() + 1; // Include null terminator - std::string llvmEscaped; - - for (unsigned char c : value) - { - if (isprint(c) && c != '"' && c != '\\') - { - llvmEscaped += c; - } - else - { - char buf[5]; - snprintf(buf, sizeof(buf), "\\%02X", c); // Use uppercase hex - llvmEscaped += buf; - } - } - llvmEscaped += "\\00"; - outGlobal << name << " = private unnamed_addr constant [" - << len << " x i8] c\"" << llvmEscaped << "\"\n"; - - std::string ptr = fresh(); - out << " " << ptr << " = getelementptr inbounds [" - << len << " x i8], [" << len << " x i8]* " - << name << ", i64 0, i64 0\n"; - - // Cast pointer to i64 if storing in i64* variable - std::string casted = fresh(); - out << " " << casted << " = ptrtoint i8* " << ptr << " to i64\n"; - - noteType(casted, node->scope->lookupType("int64_t")); // casted is now i64 - stringLiterals[value] = casted; - return casted; - } - std::string CodeGenLLVM::generateBooleanLiteral(std::unique_ptr node) - { - std::string name = fresh(); - out << " " << name << " = add i8 0, " - << (node->value == "true" ? "1" : "0") << "\n"; - noteType(name, node->scope->lookupType("boolean")); - return name; - } - std::string CodeGenLLVM::generateVariableAccess(std::unique_ptr node) - { - auto &scope = *node->scope; - auto name = node->value; - - // Lookup type info - auto ti = scope.lookupType(scope.lookupVariable(name).type); - std::string ty = ti.isFloat - ? (ti.bits == 32 ? "float" : "double") - : "i" + std::to_string(ti.bits); - - // Determine if it's a global or local variable - bool isGlobal = scope.isGlobalVariable(name); - std::string ptr; - if (!isGlobal) - { - ptr = "%" + node->scope->getMapping(name); - } - else - { - ptr = "@" + name; - } - - // Generate load instruction - std::string loaded = fresh(); - out << " " << loaded << " = load " << ty << ", " << ty << "* " << ptr << "\n"; - - noteType(loaded, ti); - return loaded; - } - std::string CodeGenLLVM::generateBinaryOperation(std::unique_ptr node) - { - auto lhs = emitExpression(std::move(node->children[0])); - auto rhs = emitExpression(std::move(node->children[1])); - TypeInfo t1 = regType[lhs]; - TypeInfo t2 = regType[rhs]; - TypeInfo tr = TypeChecker::promoteType(t1, t2); - std::string L = castValue(lhs, t1, tr); - std::string R = castValue(rhs, t2, tr); - std::string res = fresh(); - - if (tr.isFloat) - { - std::string target = (tr.bits == 64 ? "double" : "float"); - static const std::unordered_map fp_ops = {{"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; - static const std::unordered_map fcmp_ops = {{"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; - - if (fp_ops.count(node->value)) - { - out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; - noteType(res, tr); - return res; - } - else - { - std::string cmpop = fcmp_ops.at(node->value); - out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; - std::string zext = fresh(); - out << " " << zext << " = zext i1 " << res << " to i8\n"; - noteType(zext, node->scope->lookupType("boolean")); - return zext; - } - } - else - { - std::string intTy = "i" + std::to_string(tr.bits); - bool isSigned = tr.isSigned; - - if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") - { - std::string op; - if (node->value == "+") - op = "add"; - else if (node->value == "-") - op = "sub"; - else if (node->value == "*") - op = "mul"; - else /* "/" */ - op = (isSigned ? "sdiv" : "udiv"); - - out << " " << res << " = " << op << " " << intTy << " " << L << ", " << R << "\n"; - noteType(res, tr); - return res; - } - - static const std::unordered_map> cmp_map = { - {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; - - auto it = cmp_map.find(node->value); - if (it != cmp_map.end()) - { - std::string signedOp = it->second.first; - std::string unsignedOp = it->second.second; - std::string cmpop = isSigned ? signedOp : unsignedOp; - - out << " " << res << " = icmp " << cmpop << " " << intTy << " " << L << ", " << R << "\n"; - std::string zext = fresh(); - out << " " << zext << " = zext i1 " << res << " to i8\n"; - noteType(zext, node->scope->lookupType("boolean")); - return zext; - } - - throw std::runtime_error("Unsupported integer op " + node->value); - } - } - std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node) - { - auto &scope = *node->children[0]->scope; - auto varName = node->children[0]->value; - TypeInfo ti = scope.lookupType(scope.lookupVariable(varName).type); - NodeType child_type = node->children[0]->type; - auto val = emitExpression(std::move(node->children[0])); - - std::string llvmType; - if (ti.isFloat) - llvmType = (ti.bits == 32) ? "float" : "double"; - else - llvmType = "i" + std::to_string(ti.bits); - - if (node->value == "!") - { - TypeInfo boolType = node->scope->lookupType("boolean"); - val = castValue(val, regType.at(val), boolType); - std::string res = fresh(); - out << " " << res << " = icmp eq " << llvmType << " " << val << ", 0\n"; - std::string zero = fresh(); - out << " " << zero << " = zext i1 " << res << " to i8\n"; - noteType(zero, node->scope->lookupType("boolean")); - return zero; - } - else if (node->value == "++" || node->value == "--") - { - if (child_type != NodeType::VariableAccess) - throw std::runtime_error(node->value + " can only be applied to variables"); - - if (ti.isFloat) - throw std::runtime_error("Increment/Decrement not supported on float"); - - bool isGlobal = scope.isGlobalVariable(varName); - std::string ptr = (isGlobal ? "@" + varName : "%" + node->scope->getMapping(varName)); - - std::string cur = fresh(); - out << " " << cur << " = load " << llvmType << ", " << llvmType << "* " << ptr << "\n"; - - std::string updated = fresh(); - out << " " << updated << " = " - << (node->value == "++" ? "add" : "sub") - << " " << llvmType << " " << cur << ", 1\n"; - - out << " store " << llvmType << " " << updated << ", " << llvmType << "* " << ptr << "\n"; - - noteType(updated, ti); - return updated; - } - else - { - throw std::runtime_error("Unsupported unary: " + node->value); - } - } - std::string CodeGenLLVM::emitExpression(std::unique_ptr node) - { - switch (node->type) - { - case NodeType::IntegerLiteral: - return generateIntegerLiteral(std::move(node)); - case NodeType::FloatLiteral: - return generateFloatLiteral(std::move(node)); - case NodeType::StringLiteral: - return generateStringLiteral(std::move(node)); - case NodeType::BooleanLiteral: - return generateBooleanLiteral(std::move(node)); - case NodeType::VariableAccess: - return generateVariableAccess(std::move(node)); - case NodeType::BinaryOp: - return generateBinaryOperation(std::move(node)); - case NodeType::UnaryOp: - return generateUnaryOperation(std::move(node)); - default: - node->print(std::cout, 0); - throw std::runtime_error("Unknown expression encountered."); - } - } - void CodeGenLLVM::emitEpilogue() - { - out << " ; Block ends\n"; - } - void CodeGenLLVM::emitPrologue(std::unique_ptr blockNode) - { - } - void CodeGenLLVM::generateStatement(std::unique_ptr statement) - { - switch (statement->type) - { - case NodeType::VariableReassignment: - { - generateVariableReassignment(std::move(statement)); - break; - } - case NodeType::VariableDeclaration: - { - generateVariableDeclaration(std::move(statement)); - break; - } - case NodeType::IfStatement: - { - generateIfStatement(std::move(statement)); - break; - } - case NodeType::UnaryOp: - { - if (statement->value == "--" or statement->value == "++") - { - std::string reg = emitExpression(std::move(statement)); - } - break; - } - case NodeType::BinaryOp: - { - std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. - break; - } - default: - statement->print(std::cout, 0); - throw std::runtime_error("Unknown statement encountered."); - } - } - void CodeGenLLVM::generateVariableReassignment(std::unique_ptr node) - { - auto &scope = *node->scope; - auto name = node->value; - TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); - bool isGlobal = scope.isGlobalVariable(name); - - // 1) Compute the RHS expression - std::string val = emitExpression(std::move(node->children.back())); - TypeInfo tr = regType[val]; - - // 2) Convert value to target type (ti) using castValue - std::string castedVal = castValue(val, tr, ti); - - // 3) Emit the store to the correct location - std::string ty = ti.isFloat - ? (ti.bits == 32 ? "float" : "double") - : "i" + std::to_string(ti.bits); - - if (isGlobal) - { - out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; - } - else - { - out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; - } - } - void CodeGenLLVM::generateVariableDeclaration(std::unique_ptr node) - { - bool isGlobal = node->scope->isGlobalVariable(node->value); - TypeInfo ti = node->scope->lookupType(node->scope->lookupVariable(node->value).type); - std::string ty = ti.isFloat - ? (ti.bits == 32 ? "float" : "double") - : "i" + std::to_string(ti.bits); - - if (!isGlobal) - { - out << " %" << node->scope->getMapping(node->value) << " = alloca " << ty << "\n"; - } - - if (node->children.size() >= 2) - { - auto val = emitExpression(std::move(node->children.back())); - TypeInfo tr = regType[val]; - - // Use castValue for all type conversions - std::string castedVal = castValue(val, tr, ti); - - if (isGlobal) - { - out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; - } - else - { - out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; - } - } - } - void CodeGenLLVM::generateIfStatement(std::unique_ptr statement) - { - int id = blockLabelCount++; - std::string thenLbl = "if.then" + std::to_string(id); - std::string elseLbl = "if.else" + std::to_string(id); - std::string endLbl = "if.end" + std::to_string(id); - - auto condVal = emitExpression(std::move(statement->children[0])); - TypeInfo condTi = regType[condVal]; - - std::string condBool = fresh(); - out << " " << condBool - << " = trunc i" << condTi.bits - << " " << condVal << " to i1\n"; - - out << " br i1 " << condBool - << ", label %" << thenLbl - << ", label %" - << (statement->getElseBranch() ? elseLbl : endLbl) - << "\n\n"; - - out << thenLbl << ":\n"; - { - auto ifBlock = std::move(statement->children[1]); - auto children = std::move(ifBlock->children); - emitPrologue(std::move(ifBlock)); - for (auto &stmt : children) - generateStatement(std::move(stmt)); - emitEpilogue(); - } - out << " br label %" << endLbl << "\n\n"; - - ASTNode *branch = statement->getElseBranch(); - if (branch) - { - out << elseLbl << ":\n"; - - while (branch) - { - if (branch->type == NodeType::ElseIfStatement) - { - int elifId = blockLabelCount++; - std::string elifThen = "elif.then" + std::to_string(elifId); - std::string elifNext = "elif.next" + std::to_string(elifId); - - auto elifCond = emitExpression(std::move(branch->children[0])); - TypeInfo elifTi = regType[elifCond]; - std::string elifBool = fresh(); - out << " " << elifBool - << " = trunc i" << elifTi.bits - << " " << elifCond << " to i1\n"; - out << " br i1 " << elifBool - << ", label %" << elifThen - << ", label %" << elifNext - << "\n\n"; - - out << elifThen << ":\n"; - { - auto elifBlock = std::move(branch->children[1]); - auto elifChildren = std::move(elifBlock->children); - emitPrologue(std::move(elifBlock)); - for (auto &stmt : elifChildren) - generateStatement(std::move(stmt)); - emitEpilogue(); - } - out << " br label %" << endLbl << "\n\n"; - out << elifNext << ":\n"; - branch = branch->getElseBranch(); - if (!branch) - { - out << " br label %" << endLbl << "\n\n"; - } - } - else if (branch->type == NodeType::ElseStatement) - { - auto elseBlock = std::move(branch->children[0]); - auto elseChildren = std::move(elseBlock->children); - emitPrologue(std::move(elseBlock)); - for (auto &stmt : elseChildren) - generateStatement(std::move(stmt)); - emitEpilogue(); - out << " br label %" << endLbl << "\n\n"; - branch = nullptr; - } - else - { - throw std::runtime_error("Unexpected node type in else chain"); - } - } - } - - // 5) end label - out << endLbl << ":\n"; - } - void CodeGenLLVM::generate(std::unique_ptr program) - { - outGlobal << "; ModuleID = 'zlang'\n"; - outGlobal << "source_filename = \"zlang\"\n"; - // Emit global variable definitions - for (auto &statement : program->children) - { - if (statement->type == NodeType::VariableDeclaration) - { - auto &name = statement->value; - auto ti = statement->scope->lookupType( - statement->scope->lookupVariable(name).type); - std::string ty = ti.isFloat - ? (ti.bits == 32 ? "float" : "double") - : "i" + std::to_string(ti.bits); - outGlobal << "@" << name << " = global " << ty << (ti.isFloat ? " 0.0" : " 0") << "\n"; - } - } - outGlobal << "\n"; - - // Define main function - out - << "define i32 @main() {\n"; - emitPrologue(nullptr); - // Generate each top-level statement - for (auto &stmt : program->children) - { - generateStatement(std::move(stmt)); - } - out << " ret i32 0\n"; - out - << "}\n"; - - outfinal << outGlobal.str() + "\n\n" - << out.str() << "\n\n"; - } -} // namespace zlang \ No newline at end of file +// #include "all.hpp" + +// namespace zlang { +// static std::string fresh() { +// static int cnt = 0; +// return "%tmp" + std::to_string(cnt++); +// } +// std::string toHexFloatFromStr(const std::string &valStr, bool isF32) { +// std::ostringstream oss; +// oss << std::scientific << std::setprecision(16); +// if (isF32) { +// float v = std::stof(valStr); +// oss << v; +// } else { +// double v = std::stod(valStr); +// oss << v; +// } +// return oss.str(); +// } +// std::string CodeGenLLVM::castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType) { +// if (fromType.isFloat == toType.isFloat && fromType.bits == toType.bits) +// return val; + +// std::string tmp = fresh(); + +// if (!fromType.isFloat && toType.isFloat) { +// std::string intTy = "i" + std::to_string(fromType.bits); +// std::string floatTy = (toType.bits == 64 ? "double" : "float"); +// std::string instr = fromType.isSigned ? "sitofp" : "uitofp"; +// out << " " << tmp << " = " << instr << " " << intTy << " " << val << " to " << floatTy << "\n"; +// } else if (fromType.isFloat && !toType.isFloat) { +// std::string floatTy = (fromType.bits == 64 ? "double" : "float"); +// std::string intTy = "i" + std::to_string(toType.bits); +// std::string instr = toType.isSigned ? "fptosi" : "fptoui"; +// out << " " << tmp << " = " << instr << " " << floatTy << " " << val << " to " << intTy << "\n"; +// } else if (!fromType.isFloat && !toType.isFloat) { +// std::string fromTy = "i" + std::to_string(fromType.bits); +// std::string toTy = "i" + std::to_string(toType.bits); +// if (fromType.bits < toType.bits) { +// std::string instr = fromType.isSigned ? "sext" : "zext"; +// out << " " << tmp << " = " << instr << " " << fromTy << " " << val << " to " << toTy << "\n"; +// } else if (fromType.bits > toType.bits) { +// out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\n"; +// } else if (fromType.isSigned != toType.isSigned) { +// // Retag value if signedness differs but bit-width is the same +// out << " " << tmp << " = add " << fromTy << " " << val << ", 0\n"; +// } else { +// return val; +// } +// } else if (fromType.isFloat && toType.isFloat) { +// std::string fromTy = (fromType.bits == 64 ? "double" : "float"); +// std::string toTy = (toType.bits == 64 ? "double" : "float"); +// if (fromType.bits < toType.bits) { +// out << " " << tmp << " = fpext " << fromTy << " " << val << " to " << toTy << "\n"; +// } else if (fromType.bits > toType.bits) { +// out << " " << tmp << " = fptrunc " << fromTy << " " << val << " to " << toTy << "\n"; +// } else { +// return val; +// } +// } else { +// throw std::runtime_error("Unsupported cast from type to type"); +// } + +// noteType(tmp, toType); +// return tmp; +// } +// std::string CodeGenLLVM::generateIntegerLiteral(std::unique_ptr node) { +// std::string name = fresh(); +// out << " " << name << " = add i64 0, " << node->value << "\n"; +// noteType(name, node->scope->lookupType("int64_t")); +// return name; +// } +// std::string CodeGenLLVM::generateFloatLiteral(std::unique_ptr node) { +// std::string tmp = fresh(); +// std::string val = node->value; +// bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); +// if (isF32) +// val.pop_back(); + +// std::string hexVal = toHexFloatFromStr(val, isF32); + +// if (isF32) { +// out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; +// noteType(tmp, node->scope->lookupType("float")); +// } else { +// out << " " << tmp << " = fadd double 0.0, " << hexVal << "\n"; +// noteType(tmp, node->scope->lookupType("double")); +// } +// return tmp; +// } +// std::string CodeGenLLVM::generateStringLiteral(std::unique_ptr node) { +// std::string value = node->value; + +// auto it = stringLiterals.find(value); +// if (it != stringLiterals.end()) +// return it->second; + +// std::string name = "@.str" + std::to_string(stringLabelCount++); +// size_t len = value.size() + 1; // Include null terminator +// std::string llvmEscaped; + +// for (unsigned char c : value) { +// if (isprint(c) && c != '"' && c != '\\') { +// llvmEscaped += c; +// } else { +// char buf[5]; +// snprintf(buf, sizeof(buf), "\\%02X", c); // Use uppercase hex +// llvmEscaped += buf; +// } +// } +// llvmEscaped += "\\00"; +// outGlobal << name << " = private unnamed_addr constant [" +// << len << " x i8] c\"" << llvmEscaped << "\"\n"; + +// std::string ptr = fresh(); +// out << " " << ptr << " = getelementptr inbounds [" +// << len << " x i8], [" << len << " x i8]* " +// << name << ", i64 0, i64 0\n"; + +// // Cast pointer to i64 if storing in i64* variable +// std::string casted = fresh(); +// out << " " << casted << " = ptrtoint i8* " << ptr << " to i64\n"; + +// noteType(casted, node->scope->lookupType("int64_t")); // casted is now i64 +// stringLiterals[value] = casted; +// return casted; +// } +// std::string CodeGenLLVM::generateBooleanLiteral(std::unique_ptr node) { +// std::string name = fresh(); +// out << " " << name << " = add i8 0, " +// << (node->value == "true" ? "1" : "0") << "\n"; +// noteType(name, node->scope->lookupType("boolean")); +// return name; +// } +// std::string CodeGenLLVM::generateVariableAccess(std::unique_ptr node) { +// auto &scope = *node->scope; +// auto name = node->value; + +// // Lookup type info +// auto ti = scope.lookupType(scope.lookupVariable(name).type); +// std::string ty = ti.isFloat +// ? (ti.bits == 32 ? "float" : "double") +// : "i" + std::to_string(ti.bits); + +// // Determine if it's a global or local variable +// bool isGlobal = scope.isGlobalVariable(name); +// std::string ptr; +// if (!isGlobal) { +// ptr = "%" + node->scope->getMapping(name); +// } else { +// ptr = "@" + name; +// } + +// // Generate load instruction +// std::string loaded = fresh(); +// out << " " << loaded << " = load " << ty << ", " << ty << "* " << ptr << "\n"; + +// noteType(loaded, ti); +// return loaded; +// } +// std::string CodeGenLLVM::generateBinaryOperation(std::unique_ptr node) { +// auto lhs = emitExpression(std::move(node->children[0])); +// auto rhs = emitExpression(std::move(node->children[1])); +// TypeInfo t1 = regType[lhs]; +// TypeInfo t2 = regType[rhs]; +// TypeInfo tr = TypeChecker::promoteType(t1, t2); +// std::string L = castValue(lhs, t1, tr); +// std::string R = castValue(rhs, t2, tr); +// std::string res = fresh(); + +// if (tr.isFloat) { +// std::string target = (tr.bits == 64 ? "double" : "float"); +// static const std::unordered_map fp_ops = {{"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; +// static const std::unordered_map fcmp_ops = {{"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; + +// if (fp_ops.count(node->value)) { +// out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; +// noteType(res, tr); +// return res; +// } else { +// std::string cmpop = fcmp_ops.at(node->value); +// out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; +// std::string zext = fresh(); +// out << " " << zext << " = zext i1 " << res << " to i8\n"; +// noteType(zext, node->scope->lookupType("boolean")); +// return zext; +// } +// } else { +// std::string intTy = "i" + std::to_string(tr.bits); +// bool isSigned = tr.isSigned; + +// if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { +// std::string op; +// if (node->value == "+") +// op = "add"; +// else if (node->value == "-") +// op = "sub"; +// else if (node->value == "*") +// op = "mul"; +// else /* "/" */ +// op = (isSigned ? "sdiv" : "udiv"); + +// out << " " << res << " = " << op << " " << intTy << " " << L << ", " << R << "\n"; +// noteType(res, tr); +// return res; +// } + +// static const std::unordered_map> cmp_map = { +// {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; + +// auto it = cmp_map.find(node->value); +// if (it != cmp_map.end()) { +// std::string signedOp = it->second.first; +// std::string unsignedOp = it->second.second; +// std::string cmpop = isSigned ? signedOp : unsignedOp; + +// out << " " << res << " = icmp " << cmpop << " " << intTy << " " << L << ", " << R << "\n"; +// std::string zext = fresh(); +// out << " " << zext << " = zext i1 " << res << " to i8\n"; +// noteType(zext, node->scope->lookupType("boolean")); +// return zext; +// } + +// throw std::runtime_error("Unsupported integer op " + node->value); +// } +// } +// std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node) { +// auto &scope = *node->children[0]->scope; +// auto varName = node->children[0]->value; +// TypeInfo ti = scope.lookupType(scope.lookupVariable(varName).type); +// NodeType child_type = node->children[0]->type; +// auto val = emitExpression(std::move(node->children[0])); + +// std::string llvmType; +// if (ti.isFloat) +// llvmType = (ti.bits == 32) ? "float" : "double"; +// else +// llvmType = "i" + std::to_string(ti.bits); + +// if (node->value == "!") { +// TypeInfo boolType = node->scope->lookupType("boolean"); +// val = castValue(val, regType.at(val), boolType); +// std::string res = fresh(); +// out << " " << res << " = icmp eq " << llvmType << " " << val << ", 0\n"; +// std::string zero = fresh(); +// out << " " << zero << " = zext i1 " << res << " to i8\n"; +// noteType(zero, node->scope->lookupType("boolean")); +// return zero; +// } else if (node->value == "++" || node->value == "--") { +// if (child_type != NodeType::VariableAccess) +// throw std::runtime_error(node->value + " can only be applied to variables"); + +// if (ti.isFloat) +// throw std::runtime_error("Increment/Decrement not supported on float"); + +// bool isGlobal = scope.isGlobalVariable(varName); +// std::string ptr = (isGlobal ? "@" + varName : "%" + node->scope->getMapping(varName)); + +// std::string cur = fresh(); +// out << " " << cur << " = load " << llvmType << ", " << llvmType << "* " << ptr << "\n"; + +// std::string updated = fresh(); +// out << " " << updated << " = " +// << (node->value == "++" ? "add" : "sub") +// << " " << llvmType << " " << cur << ", 1\n"; + +// out << " store " << llvmType << " " << updated << ", " << llvmType << "* " << ptr << "\n"; + +// noteType(updated, ti); +// return updated; +// } else { +// throw std::runtime_error("Unsupported unary: " + node->value); +// } +// } +// std::string CodeGenLLVM::emitExpression(std::unique_ptr node) { +// switch (node->type) { +// case NodeType::IntegerLiteral: +// return generateIntegerLiteral(std::move(node)); +// case NodeType::FloatLiteral: +// return generateFloatLiteral(std::move(node)); +// case NodeType::StringLiteral: +// return generateStringLiteral(std::move(node)); +// case NodeType::BooleanLiteral: +// return generateBooleanLiteral(std::move(node)); +// case NodeType::VariableAccess: +// return generateVariableAccess(std::move(node)); +// case NodeType::BinaryOp: +// return generateBinaryOperation(std::move(node)); +// case NodeType::UnaryOp: +// return generateUnaryOperation(std::move(node)); +// default: +// node->print(std::cout, 0); +// throw std::runtime_error("Unknown expression encountered."); +// } +// } +// void CodeGenLLVM::emitEpilogue(std::unique_ptr blockNode) { +// out << " ; Block ends\n"; +// } +// void CodeGenLLVM::emitPrologue(std::unique_ptr blockNode) { +// } +// void CodeGenLLVM::generateStatement(std::unique_ptr statement) { +// switch (statement->type) { +// case NodeType::VariableReassignment: { +// generateVariableReassignment(std::move(statement)); +// break; +// } +// case NodeType::VariableDeclaration: { +// generateVariableDeclaration(std::move(statement)); +// break; +// } +// case NodeType::IfStatement: { +// generateIfStatement(std::move(statement)); +// break; +// } +// case NodeType::UnaryOp: { +// if (statement->value == "--" or statement->value == "++") { +// std::string reg = emitExpression(std::move(statement)); +// } +// break; +// } +// case NodeType::BinaryOp: { +// std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. +// break; +// } +// default: +// statement->print(std::cout, 0); +// throw std::runtime_error("Unknown statement encountered."); +// } +// } +// void CodeGenLLVM::generateVariableReassignment(std::unique_ptr node) { +// auto &scope = *node->scope; +// auto name = node->value; +// TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); +// bool isGlobal = scope.isGlobalVariable(name); + +// // 1) Compute the RHS expression +// std::string val = emitExpression(std::move(node->children.back())); +// TypeInfo tr = regType[val]; + +// // 2) Convert value to target type (ti) using castValue +// std::string castedVal = castValue(val, tr, ti); + +// // 3) Emit the store to the correct location +// std::string ty = ti.isFloat +// ? (ti.bits == 32 ? "float" : "double") +// : "i" + std::to_string(ti.bits); + +// if (isGlobal) { +// out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; +// } else { +// out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; +// } +// } +// void CodeGenLLVM::generateVariableDeclaration(std::unique_ptr node) { +// bool isGlobal = node->scope->isGlobalVariable(node->value); +// TypeInfo ti = node->scope->lookupType(node->scope->lookupVariable(node->value).type); +// std::string ty = ti.isFloat +// ? (ti.bits == 32 ? "float" : "double") +// : "i" + std::to_string(ti.bits); + +// if (!isGlobal) { +// out << " %" << node->scope->getMapping(node->value) << " = alloca " << ty << "\n"; +// } + +// if (node->children.size() >= 2) { +// auto val = emitExpression(std::move(node->children.back())); +// TypeInfo tr = regType[val]; + +// // Use castValue for all type conversions +// std::string castedVal = castValue(val, tr, ti); + +// if (isGlobal) { +// out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; +// } else { +// out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; +// } +// } +// } +// void CodeGenLLVM::generateIfStatement(std::unique_ptr statement) { +// int id = blockLabelCount++; +// std::string thenLbl = "if.then" + std::to_string(id); +// std::string elseLbl = "if.else" + std::to_string(id); +// std::string endLbl = "if.end" + std::to_string(id); + +// auto condVal = emitExpression(std::move(statement->children[0])); +// TypeInfo condTi = regType[condVal]; + +// std::string condBool = fresh(); +// out << " " << condBool +// << " = trunc i" << condTi.bits +// << " " << condVal << " to i1\n"; + +// out << " br i1 " << condBool +// << ", label %" << thenLbl +// << ", label %" +// << (statement->getElseBranch() ? elseLbl : endLbl) +// << "\n\n"; + +// out << thenLbl << ":\n"; +// { +// auto ifBlock = std::move(statement->children[1]); +// auto children = std::move(ifBlock->children); +// emitPrologue(std::move(ifBlock)); +// for (auto &stmt : children) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// } +// out << " br label %" << endLbl << "\n\n"; + +// ASTNode *branch = statement->getElseBranch(); +// if (branch) { +// out << elseLbl << ":\n"; + +// while (branch) { +// if (branch->type == NodeType::ElseIfStatement) { +// int elifId = blockLabelCount++; +// std::string elifThen = "elif.then" + std::to_string(elifId); +// std::string elifNext = "elif.next" + std::to_string(elifId); + +// auto elifCond = emitExpression(std::move(branch->children[0])); +// TypeInfo elifTi = regType[elifCond]; +// std::string elifBool = fresh(); +// out << " " << elifBool +// << " = trunc i" << elifTi.bits +// << " " << elifCond << " to i1\n"; +// out << " br i1 " << elifBool +// << ", label %" << elifThen +// << ", label %" << elifNext +// << "\n\n"; + +// out << elifThen << ":\n"; +// { +// auto elifBlock = std::move(branch->children[1]); +// auto elifChildren = std::move(elifBlock->children); +// emitPrologue(std::move(elifBlock)); +// for (auto &stmt : elifChildren) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// } +// out << " br label %" << endLbl << "\n\n"; +// out << elifNext << ":\n"; +// branch = branch->getElseBranch(); +// if (!branch) { +// out << " br label %" << endLbl << "\n\n"; +// } +// } else if (branch->type == NodeType::ElseStatement) { +// auto elseBlock = std::move(branch->children[0]); +// auto elseChildren = std::move(elseBlock->children); +// emitPrologue(std::move(elseBlock)); +// for (auto &stmt : elseChildren) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// out << " br label %" << endLbl << "\n\n"; +// branch = nullptr; +// } else { +// throw std::runtime_error("Unexpected node type in else chain"); +// } +// } +// } + +// // 5) end label +// out << endLbl << ":\n"; +// } +// void CodeGenLLVM::generate(std::unique_ptr program) { +// outGlobal << "; ModuleID = 'zlang'\n"; +// outGlobal << "source_filename = \"zlang\"\n"; +// // Emit global variable definitions +// for (auto &statement : program->children) { +// if (statement->type == NodeType::VariableDeclaration) { +// auto &name = statement->value; +// auto ti = statement->scope->lookupType( +// statement->scope->lookupVariable(name).type); +// std::string ty = ti.isFloat +// ? (ti.bits == 32 ? "float" : "double") +// : "i" + std::to_string(ti.bits); +// outGlobal << "@" << name << " = global " << ty << (ti.isFloat ? " 0.0" : " 0") << "\n"; +// } +// } +// outGlobal << "\n"; + +// // Define main function +// out +// << "define i32 @main() {\n"; +// emitPrologue(nullptr); +// // Generate each top-level statement +// for (auto &stmt : program->children) { +// generateStatement(std::move(stmt)); +// } +// out << " ret i32 0\n"; +// out +// << "}\n"; + +// outfinal << outGlobal.str() + "\n\n" +// << out.str() << "\n\n"; +// } +// std::string CodeGenLLVM::generateFunctionCall(std::unique_ptr node) { +// return ""; +// } +// void CodeGenLLVM::generateFunctionDeclaration(std::unique_ptr node) {} +// void CodeGenLLVM::generateExternFunctionDeclaration(std::unique_ptr node) {} +// } // namespace zlang \ No newline at end of file diff --git a/src/codegen/CodeGenLinux.cpp b/src/codegen/CodeGenLinux.cpp index 78ca774..15614f0 100644 --- a/src/codegen/CodeGenLinux.cpp +++ b/src/codegen/CodeGenLinux.cpp @@ -1,18 +1,22 @@ #include "all.hpp" -namespace zlang -{ +// TODO: Generate a main label, scrap start, make main function compulsory. +// TODO: Link with GCC, please. + +namespace zlang { std::string CodeGenLinux::castValue( const std::string &val, const TypeInfo &fromType, - const TypeInfo &toType) - { - if (fromType.bits == toType.bits && fromType.isFloat == toType.isFloat) - { - if (!fromType.isFloat && fromType.isSigned != toType.isSigned) - { - std::string dst = alloc.allocate(); - // AT&T: source, destination + const TypeInfo &toType, const std::shared_ptr currentScope, std::ostringstream &out) { + // Ensure input value is in a register (restore if previously spilled) + restoreIfSpilled(val, currentScope, out); + + // No-op cast except sign adjustment + if (fromType.bits == toType.bits && fromType.isFloat == toType.isFloat) { + if (!fromType.isFloat && fromType.isSigned != toType.isSigned) { + // Allocate or spill an integer register + std::string dst = allocateOrSpill(false, currentScope, out); + // AT&T syntax: source, destination out << " mov %" << adjustReg(val, fromType.bits) << ", %" << adjustReg(dst, fromType.bits) << "\n"; alloc.free(val); @@ -22,20 +26,19 @@ namespace zlang return RegisterAllocator::getBaseReg(val); } - if (fromType.isFloat && toType.isFloat) - { - std::string dstX = alloc.allocateXMM(); + // Float-to-float conversion + if (fromType.isFloat && toType.isFloat) { + std::string dstX = allocateOrSpill(true, currentScope, out); auto instr = (fromType.bits < toType.bits) ? "cvtss2sd" : "cvtsd2ss"; - // AT&T: source, destination out << " " << instr << " %" << val << ", %" << dstX << "\n"; alloc.free(val); noteType(dstX, toType); return dstX; } - if (!fromType.isFloat && toType.isFloat) - { - std::string dstX = alloc.allocateXMM(); + // Integer-to-float conversion + if (!fromType.isFloat && toType.isFloat) { + std::string dstX = allocateOrSpill(true, currentScope, out); auto instr = (toType.bits == 32) ? "cvtsi2ss" : "cvtsi2sd"; out << " " << instr << " %" << adjustReg(val, fromType.bits) << ", %" << dstX << "\n"; @@ -44,9 +47,9 @@ namespace zlang return dstX; } - if (fromType.isFloat && !toType.isFloat) - { - std::string dstG = alloc.allocate(); + // Float-to-integer conversion + if (fromType.isFloat && !toType.isFloat) { + std::string dstG = allocateOrSpill(false, currentScope, out); auto instr = (fromType.bits == 32) ? "cvttss2si" : "cvttsd2si"; out << " " << instr << " %" << val << ", %" << adjustReg(dstG, toType.bits) << "\n"; @@ -55,53 +58,34 @@ namespace zlang return dstG; } - // Integer casts - if (!fromType.isFloat && !toType.isFloat) - { - std::string dstG = alloc.allocate(); + // Integer-to-integer cast (extend/truncate/move) + if (!fromType.isFloat && !toType.isFloat) { + std::string dstG = allocateOrSpill(false, currentScope, out); std::string srcAdj = adjustReg(val, fromType.bits); std::string dstAdj = adjustReg(dstG, toType.bits); - if (toType.bits > fromType.bits) - { - // Extension - if (fromType.isSigned) - { - if (fromType.bits == 32 && toType.bits == 64) - { - // AT&T: source, destination + if (toType.bits > fromType.bits) { + // Sign or zero extension + if (fromType.isSigned) { + if (fromType.bits == 32 && toType.bits == 64) { out << " movsxd %" << srcAdj << ", %" << dstAdj << "\n"; - } - else - { - // movsx for 8/16 -> larger + } else { out << " movsx %" << srcAdj << ", %" << dstAdj << "\n"; } - } - else - { - if (fromType.bits == 8 || fromType.bits == 16) - { - // AT&T: source, destination + } else { + if (fromType.bits == 8 || fromType.bits == 16) { out << " movzx %" << srcAdj << ", %" << dstAdj << "\n"; - } - else - { - // 32->64: normal mov zero-extends + } else { out << " movl %" << adjustReg(val, 32) << ", %" << adjustReg(dstG, 32) << "\n"; } } - } - else if (toType.bits < fromType.bits) - { - // Truncation: move smaller portion + } else if (toType.bits < fromType.bits) { + // Truncate out << " mov %" << adjustReg(val, toType.bits) << ", %" << dstAdj << "\n"; - } - else - { - // Same size: simple move + } else { + // Simple move out << " mov %" << srcAdj << ", %" << dstAdj << "\n"; } @@ -115,268 +99,298 @@ namespace zlang " to " + toType.to_string()); } - std::string CodeGenLinux::generateIntegerLiteral(std::unique_ptr node) - { - auto r = alloc.allocate(); + std::string CodeGenLinux::allocateOrSpill(bool isXMM, std::shared_ptr scope, std::ostringstream &out) { + try { + std::string reg = isXMM ? alloc.allocateXMM() : alloc.allocate(); + if (!reg.empty()) + return reg; + } catch (...) { + auto result_scope = scope->findEnclosingFunctionScope(); + if (!result_scope) { + throw std::runtime_error("Cannot spill in global context"); + } + + std::shared_ptr funcScope = std::static_pointer_cast(result_scope); + + std::string victim = isXMM ? alloc.pickVictimXMM() : alloc.pickVictim(); + if (victim.empty()) + throw std::runtime_error("No available registers and no victim found"); + + std::string spillSlot = funcScope->allocateSpillSlot(isXMM ? 16 : 8); + + if (isXMM) { + alloc.markSpilledXMM(victim, spillSlot); + out << " movdqu " << victim << ", " << spillSlot << " # Spill XMM\n"; + } else { + char suffix = integer_suffixes.at(64); + out << " mov" << suffix << " " << victim << ", " << spillSlot << " # Spill GPR\n"; + alloc.markSpilled(victim, spillSlot); + } + + return victim; + } + return ""; + } + void CodeGenLinux::restoreIfSpilled(const std::string ®, std::shared_ptr scope, std::ostringstream &out) { + if (!alloc.isSpilled(reg)) + return; + + std::string slot = alloc.spillSlotFor(reg); + + if (reg.starts_with("xm")) { + alloc.unSpillXMM(reg, zlang::CodegenOutputFormat::X86_64_LINUX, out); + } else { + alloc.unSpill(reg, zlang::CodegenOutputFormat::X86_64_LINUX, out); + } + + auto result_scope = scope->findEnclosingFunctionScope(); + if (result_scope) { + std::shared_ptr funcScope = std::static_pointer_cast(result_scope); + funcScope->freeSpillSlot(slot, reg.starts_with("xm") ? 16 : 8); + } + } + + std::string CodeGenLinux::generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) { + TypeInfo intType = node->scope->lookupType("int64_t"); + std::string r = allocateOrSpill(false, node->scope, out); out << " movq $" << node->value << ", %" << r << "\n"; - noteType(r, node->scope->lookupType("int64_t")); + noteType(r, intType); return r; } - std::string CodeGenLinux::generateFloatLiteral(std::unique_ptr node) - { + std::string CodeGenLinux::generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) { std::string lbl = ".Lfloat" + std::to_string(floatLabelCount++); std::string val = node->value; bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); - if (isF32) val.pop_back(); - - outGlobal << lbl << ": ." << (isF32 ? "float" : "double") << " " << val << "\n"; - - auto r_xmm = alloc.allocateXMM(); - out << " " << (isF32 ? "movss " : "movsd ") << lbl << "(%rip), %" << r_xmm << "\n"; - noteType(r_xmm, (isF32 ? node->scope->lookupType("float") - : node->scope->lookupType("double"))); + outGlobalStream << " .align " << (isF32 ? 4 : 8) << "\n" + << lbl << ": ." << (isF32 ? "float" : "double") + << " " << val << "\n"; + TypeInfo floatType = isF32 + ? node->scope->lookupType("float") + : node->scope->lookupType("double"); + std::string r_xmm = allocateOrSpill(true, node->scope, out); + out << " " << (isF32 ? "movss" : "movsd") + << " " << lbl << "(%rip), %" << r_xmm << "\n"; + noteType(r_xmm, floatType); return r_xmm; } - std::string CodeGenLinux::generateStringLiteral(std::unique_ptr node) - { + std::string CodeGenLinux::generateStringLiteral(std::unique_ptr node, std::ostringstream &out) { std::string lbl = ".Lstr" + std::to_string(stringLabelCount++); - std::string str = node->value; - - outGlobal << lbl << ": .string \"" << str << "\"\n"; - - auto r = alloc.allocate(); + outGlobalStream << " .align 1\n" + << lbl << ": .asciz \"" << str << "\"\n"; + TypeInfo strType = node->scope->lookupType("string"); + std::string r = allocateOrSpill(false, node->scope, out); out << " leaq " << lbl << "(%rip), %" << r << "\n"; - noteType(r, node->scope->lookupType("string")); + noteType(r, strType); return r; } - std::string CodeGenLinux::generateBooleanLiteral(std::unique_ptr node) - { - auto r = alloc.allocate(); - out << " movq $" << (node->value == "true" ? "1" : "0") << ", %" << r - << "\n"; - noteType(r, node->scope->lookupType("boolean")); + std::string CodeGenLinux::generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) { + TypeInfo boolType = node->scope->lookupType("boolean"); + std::string r = allocateOrSpill(false, node->scope, out); + + // Move immediate 1 or 0 + out << " movq $" << (node->value == "true" ? "1" : "0") + << ", %" << r << "\n"; + noteType(r, boolType); return r; } - std::string CodeGenLinux::generateVariableAccess(std::unique_ptr node) - { + std::string CodeGenLinux::generateVariableAccess(std::unique_ptr node, std::ostringstream &out) { auto &scope = *node->scope; - auto name = node->value; - auto ti = scope.lookupType(scope.lookupVariable(name).type); + std::string name = node->value; + TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); uint64_t sz = ti.bits / 8; - if (scope.isGlobalVariable(name)) - { - if (ti.isFloat) - { - std::string r_xmm = alloc.allocateXMM(); - out << " " << (sz == 4 ? "movss " : "movsd ") << name - << "(%rip), %" << r_xmm << "\n"; + if (scope.isGlobalVariable(name)) { + if (ti.isFloat) { + // Allocate or spill an XMM reg + std::string r_xmm = allocateOrSpill(true, node->scope, out); + out << " " << (sz == 4 ? "movss" : "movsd") + << " " << name << "(%rip), %" << r_xmm << "\n"; noteType(r_xmm, ti); return r_xmm; - } - else - { - std::string r = alloc.allocate(); + } else { + // Allocate or spill a GPR + std::string r = allocateOrSpill(false, node->scope, out); std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " " << name - << "(%rip), %" << adj << "\n"; + out << " " << getCorrectMove(sz, false) + << " " << name << "(%rip), %" << adj << "\n"; noteType(r, ti); return r; } - } - else - { + } else { int64_t off = scope.getVariableOffset(name); - if (ti.isFloat) - { - std::string r = alloc.allocateXMM(); - out << " " << (sz == 4 ? "movss " : "movsd ") << off - << "(%rbp), %" << r << "\n"; - noteType(r, (sz == 4 ? node->scope->lookupType("float") - : node->scope->lookupType("double"))); - return r; - } - else - { - std::string r = alloc.allocate(); + if (ti.isFloat) { + std::string r_xmm = allocateOrSpill(true, node->scope, out); + out << " " << (sz == 4 ? "movss" : "movsd") + << " " << std::to_string(off) << "(%rbp), %" << r_xmm << "\n"; + noteType(r_xmm, ti); + return r_xmm; + } else { + std::string r = allocateOrSpill(false, node->scope, out); std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " " << off - << "(%rbp), %" << adj << "\n"; + out << " " << getCorrectMove(sz, false) + << " " << std::to_string(off) << "(%rbp), %" << adj << "\n"; noteType(r, ti); return r; } } } - std::string CodeGenLinux::generateBinaryOperation(std::unique_ptr node) - { - auto rl = emitExpression(std::move(node->children[0])); - auto rr = emitExpression(std::move(node->children[1])); - - auto t1 = regType.at(rl); - auto t2 = regType.at(rr); - auto tr = TypeChecker::promoteType(t1, t2); - const auto &op = node->value; + std::string CodeGenLinux::generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) { + // Evaluate operands + std::string rl = emitExpression(std::move(node->children[0]), out); + restoreIfSpilled(rl, node->scope, out); + + std::string rr = emitExpression(std::move(node->children[1]), out); + restoreIfSpilled(rr, node->scope, out); + + // Determine promoted type and operator + TypeInfo t1 = regType.at(rl); + TypeInfo t2 = regType.at(rr); + TypeInfo tr = TypeChecker::promoteType(t1, t2); + const std::string &op = node->value; - rl = castValue(rl, t1, tr); - rr = castValue(rr, t2, tr); + // Cast operands to common type + rl = castValue(rl, t1, tr, node->scope, out); + rr = castValue(rr, t2, tr, node->scope, out); - if (tr.isFloat) - { + if (tr.isFloat) { bool isF32 = (tr.bits == 32); std::string vsuf = isF32 ? "ss" : "sd"; - std::string xl = rl, xr = rr; - + // Perform floating-point operation if (op == "+") - out << " add" << vsuf << " %" << xr << ", %" << xl << "\n"; + out << " add" << vsuf << " %" << rr << ", %" << rl << "\n"; else if (op == "-") - out << " sub" << vsuf << " %" << xr << ", %" << xl << "\n"; + out << " sub" << vsuf << " %" << rr << ", %" << rl << "\n"; else if (op == "*") - out << " mul" << vsuf << " %" << xr << ", %" << xl << "\n"; + out << " mul" << vsuf << " %" << rr << ", %" << rl << "\n"; else if (op == "/") - out << " div" << vsuf << " %" << xr << ", %" << xl << "\n"; - else if (assembly_comparison_operations.count(op)) - { - out << " ucomi" << vsuf << " %" << xr << ", %" << xl << "\n" + out << " div" << vsuf << " %" << rr << ", %" << rl << "\n"; + else if (assembly_comparison_operations.count(op)) { + out << " ucomi" << vsuf << " %" << rr << ", %" << rl << "\n" << " " << assembly_comparison_operations.at(op) << " %al\n"; - auto r_bool = alloc.allocate(); + // Allocate boolean result + TypeInfo boolType = node->scope->lookupType("boolean"); + std::string r_bool = allocateOrSpill(false, node->scope, out); out << " movzbq %al, %" << r_bool << "\n"; - noteType(r_bool, node->scope->lookupType("boolean")); - alloc.free(xr); - alloc.free(xl); + noteType(r_bool, boolType); + // Free operand regs + alloc.free(rr); + alloc.free(rl); return r_bool; - } - else - { + } else { throw std::runtime_error("Unsupported FP op " + op); } + // Free right operand, result in rl + alloc.free(rr); + noteType(rl, tr); + return rl; + } - noteType(xl, tr); - alloc.free(xr); - return xl; - } - - char suf = integer_suffixes[tr.bits]; - auto r_l = adjustReg(rl, tr.bits); - auto r_r = adjustReg(rr, tr.bits); - - if (op == "+") - { - out << " add" << suf << " %" << r_r << ", %" << r_l << "\n"; - } - else if (op == "-") - { - out << " sub" << suf << " %" << r_r << ", %" << r_l << "\n"; - } - else if (op == "*") - { - out << " imul" << suf << " %" << r_r << ", %" << r_l << "\n"; - } - else if (op == "/") - { - if (tr.isSigned) - { - if (tr.bits == 32) - { - out << " movl %" << r_l << ", %eax\n" - << " cltd\n" - << " idivl %" << r_r << "\n" - << " movl %eax, %" << r_l << "\n"; - } - else - { - out << " movq %" << r_l << ", %rax\n" - << " cqo\n" - << " idivq %" << r_r << "\n" - << " movq %rax, %" << r_l << "\n"; - } - } - else - { - if (tr.bits == 32) - { - out << " movl %" << r_l << ", %eax\n" - << " clrl %edx\n" - << " divl %" << r_r << "\n" - << " movl %eax, %" << r_l << "\n"; - } - else - { - out << " movq %" << r_l << ", %rax\n" - << " xorq %rdx, %rdx\n" - << " divq %" << r_r << "\n" - << " movq %rax, %" << r_l << "\n"; + // Integer operations + char suf = integer_suffixes.at(tr.bits); + std::string adj_l = adjustReg(rl, tr.bits); + std::string adj_r = adjustReg(rr, tr.bits); + + if (op == "+" || op == "-" || op == "*" || op == "/") { + if (op == "+") + out << " add" << suf << " %" << adj_r << ", %" << adj_l << "\n"; + else if (op == "-") + out << " sub" << suf << " %" << adj_r << ", %" << adj_l << "\n"; + else if (op == "*") + out << " imul" << suf << " %" << adj_r << ", %" << adj_l << "\n"; + else /* div */ { + // division requires special setup + if (tr.isSigned) { + if (tr.bits == 32) { + out << " movl %" << adj_l << ", %eax\n" + << " cltd\n" + << " idivl %" << adj_r << "\n" + << " movl %eax, %" << adj_l << "\n"; + } else { + out << " movq %" << adj_l << ", %rax\n" + << " cqo\n" + << " idivq %" << adj_r << "\n" + << " movq %rax, %" << adj_l << "\n"; + } + } else { + if (tr.bits == 32) { + out << " movl %" << adj_l << ", %eax\n" + << " clrl %edx\n" + << " divl %" << adj_r << "\n" + << " movl %eax, %" << adj_l << "\n"; + } else { + out << " movq %" << adj_l << ", %rax\n" + << " xorq %rdx, %rdx\n" + << " divq %" << adj_r << "\n" + << " movq %rax, %" << adj_l << "\n"; + } } + alloc.free(rr); + noteType(rl, tr); + return rl; } alloc.free(rr); - noteType(r_l, tr); + noteType(rl, tr); return rl; } - else if (assembly_comparison_operations.count(op)) - { - static const std::unordered_map signedMap = { - {"==", "sete"}, {"!=", "setne"}, {"<", "setl"}, {"<=", "setle"}, {">", "setg"}, {">=", "setge"}}; - static const std::unordered_map unsignedMap = { - {"==", "sete"}, {"!=", "setne"}, {"<", "setb"}, {"<=", "setbe"}, {">", "seta"}, {">=", "setae"}}; - auto &map = tr.isSigned ? signedMap : unsignedMap; - auto r_bool = alloc.allocate(); - out << " cmp" << suf << " %" << r_r << ", %" << r_l << "\n" - << " " << map.at(op) << " %al\n" - << " movzbq %al, %" << r_bool << "\n"; + + // Integer comparison + if (assembly_comparison_operations.count(op)) { + auto &map = tr.isSigned ? assembly_comparison_operations : /* unsigned map here */ assembly_comparison_operations; + out << " cmp" << suf << " %" << adj_r << ", %" << adj_l << "\n" + << " " << map.at(op) << " %al\n"; + TypeInfo boolType = node->scope->lookupType("boolean"); + std::string r_bool = allocateOrSpill(false, node->scope, out); + out << " movzbq %al, %" << r_bool << "\n"; + noteType(r_bool, boolType); alloc.free(rr); alloc.free(rl); - noteType(r_bool, node->scope->lookupType("boolean")); return r_bool; } - else if (op == "&&" || op == "||" || op == "&" || op == "|") - { - std::string instr; - if (op == "&&") - instr = "and"; - else if (op == "||") - instr = "or"; - else - instr = op; - out << " " << instr << suf << " %" << r_r << ", %" << r_l << "\n"; - } - else - { - throw std::runtime_error("Unsupported int op " + op); - } - - alloc.free(rr); - noteType(rl, tr); - return rl; + + // Bitwise/logical ops: &&, ||, &, | + if (op == "&&" || op == "||" || op == "&" || op == "|") { + std::string instr = (op == "&&") ? "and" : (op == "||") ? "or" + : op; + out << " " << instr << suf << " %" << adj_r << ", %" << adj_l << "\n"; + alloc.free(rr); + noteType(rl, tr); + return rl; + } + + throw std::runtime_error("Unsupported int op " + op); } - std::string CodeGenLinux::generateUnaryOperation(std::unique_ptr node) - { + std::string CodeGenLinux::generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) { const auto &op = node->value; if (op != "!" && op != "++" && op != "--") throw std::runtime_error("Unsupported unary operator: " + op); auto child = std::move(node->children[0]); - const std::string varName = child->value; - auto &scp = *child->scope; - TypeInfo ti = scp.lookupType(scp.lookupVariable(varName).type); - - if (op == "!") - { - auto r = emitExpression(std::move(child)); + if (op == "!") { + auto r = emitExpression(std::move(child), out); + restoreIfSpilled(r, node->scope, out); TypeInfo boolType = node->scope->lookupType("boolean"); - r = castValue(r, regType.at(r), boolType); - std::string res = alloc.allocate(); + r = castValue(r, regType.at(r), boolType, node->scope, out); + + auto res = allocateOrSpill(false, node->scope, out); out << " cmpq $0, %" << r << "\n" << " sete %al\n" << " movzbq %al, %" << res << "\n"; noteType(res, boolType); + + alloc.free(r); return res; } if (child->type != NodeType::VariableAccess) throw std::runtime_error(op + " can only be applied to variables"); + + auto &scp = *child->scope; + const std::string varName = child->value; + TypeInfo ti = scp.lookupType(scp.lookupVariable(varName).type); if (ti.isFloat) throw std::runtime_error(op + " not supported on float"); @@ -384,275 +398,284 @@ namespace zlang ? varName + "(%rip)" : std::to_string(scp.getVariableOffset(varName)) + "(%rbp)"; uint64_t sz = ti.bits / 8; - char suf = integer_suffixes[ti.bits]; + char suf = integer_suffixes.at(ti.bits); - std::string r = alloc.allocate(); + auto r = allocateOrSpill(false, node->scope, out); std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, /*isFloat=*/false) - << " " << ptr << ", %" << adj << "\n"; + out << " " << getCorrectMove(sz, false) << " " << ptr << ", %" << adj << "\n"; - out << " " << (op == "++" ? "inc" : "dec") << suf - << " %" << adj << "\n"; + out << " " << (op == "++" ? "inc" : "dec") << suf << " %" << adj << "\n"; - out << " " << getCorrectMove(sz, /*isFloat=*/false) - << " %" << adj << ", " << ptr << "\n"; + out << " " << getCorrectMove(sz, false) << " %" << adj << ", " << ptr << "\n"; noteType(adj, ti); return adj; } - std::string CodeGenLinux::emitExpression(std::unique_ptr node) - { - switch (node->type) - { + std::string CodeGenLinux::emitExpression(std::unique_ptr node, std::ostringstream &out) { + switch (node->type) { case NodeType::IntegerLiteral: - return generateIntegerLiteral(std::move(node)); + return generateIntegerLiteral(std::move(node), out); case NodeType::FloatLiteral: - return generateFloatLiteral(std::move(node)); + return generateFloatLiteral(std::move(node), out); case NodeType::StringLiteral: - return generateStringLiteral(std::move(node)); + return generateStringLiteral(std::move(node), out); case NodeType::BooleanLiteral: - return generateBooleanLiteral(std::move(node)); + return generateBooleanLiteral(std::move(node), out); case NodeType::VariableAccess: - return generateVariableAccess(std::move(node)); + return generateVariableAccess(std::move(node), out); case NodeType::BinaryOp: - return generateBinaryOperation(std::move(node)); + return generateBinaryOperation(std::move(node), out); case NodeType::UnaryOp: - return generateUnaryOperation(std::move(node)); + return generateUnaryOperation(std::move(node), out); + case NodeType::FunctionCall: + return generateFunctionCall(std::move(node), out); default: throw std::runtime_error("Unknown statement encountered."); } } - void CodeGenLinux::emitEpilogue() - { - out << " # Block Ends (scope exit)\n"; - out << " leave\n"; + + void CodeGenLinux::emitPrologue(std::shared_ptr scope, std::ostringstream &out) { + if (scope->kind() == "Function") { + out << " # Function Prologue\n"; + out << " push %rbp\n"; + out << " mov %rsp, %rbp\n"; + + // --- save all callee-saved GPRs --- + for (const auto ® : CALLEE_GPR_LINUX) { + out << " push %" << reg << "\n"; + } + + auto funcScope = std::dynamic_pointer_cast(scope); + if (!funcScope) + throw std::runtime_error("Expected FunctionScope for function prologue"); + + // compute aligned subSize exactly as before... + std::uint64_t spillSize = std::abs(funcScope->getSpillSize()); + std::uint64_t rawSize = std::uint64_t(std::abs(funcScope->getStackOffset())) + 8 + spillSize; + std::uint64_t alignedRaw = (rawSize + 15) & ~15ULL; + std::uint64_t subSize = (alignedRaw + 8) & ~15ULL; + if (subSize > 0) { + out << " sub $" << subSize << ", %rsp\n"; + } + + // load + store canary + std::string canaryReg = alloc.allocate(); + out << std::hex + << " movabs $0x" << funcScope->getCanary() << ", %" << canaryReg << " # load canary value\n" + << std::dec + << " movq %" << canaryReg << ", -8(%rbp) # store canary at -8(%rbp)\n"; + alloc.free(canaryReg); + } } - void CodeGenLinux::emitPrologue(std::unique_ptr blockNode) - { - auto blkScope = blockNode->scope; - int allocSize = -blkScope->stackOffset + 8; - // +8 to cover the pushed RBP - out << " # Block Starts (scope enter)\n"; - out << " push %rbp\n"; - out << " mov %rsp, %rbp\n"; - out << " sub $" << allocSize << ", %rsp\n"; + void CodeGenLinux::emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax) { + if (scope->kind() == "Function") { + auto funcScope = std::dynamic_pointer_cast(scope); + out << " # Function Epilogue with Canary Check\n"; + + // canary check + std::string regStored = alloc.allocate(); + std::string regExpected = alloc.allocate(); + out << " mov -8(%rbp), %" << regStored << " # load stored canary\n" + << std::hex + << " movabs $0x" << funcScope->getCanary() << ", %" << regExpected << " # load expected canary\n" + << std::dec + << " cmp %" << regExpected << ", %" << regStored << " # compare canary\n" + << " jne __stack_smash_detected # mismatch -> abort\n"; + alloc.free(regStored); + alloc.free(regExpected); + + if (clearRax) { + out << " xor %rax, %rax\n"; + } + + // pop in reverse order + for (auto it = CALLEE_GPR_LINUX.rbegin(); it != CALLEE_GPR_LINUX.rend(); ++it) { + out << " pop %" << *it << "\n"; + } + + // restore stack frame and pop callee-saved GPRs + out << " leave\n"; // restores RSP and pops RBP + out << " ret\n"; + } } - void CodeGenLinux::generateStatement(std::unique_ptr statement) - { - switch (statement->type) - { - case NodeType::VariableReassignment: - { - generateVariableReassignment(std::move(statement)); + + void CodeGenLinux::generateStatement(std::unique_ptr statement, std::ostringstream &out) { + switch (statement->type) { + case NodeType::VariableReassignment: { + generateVariableReassignment(std::move(statement), out); + out << "\n"; break; } - case NodeType::VariableDeclaration: - { - generateVariableDeclaration(std::move(statement)); + case NodeType::VariableDeclaration: { + generateVariableDeclaration(std::move(statement), out); + out << "\n"; break; } - case NodeType::IfStatement: - { - generateIfStatement(std::move(statement)); + case NodeType::IfStatement: { + generateIfStatement(std::move(statement), out); + out << "\n"; break; } - case NodeType::UnaryOp: - { - if (statement->value == "--" or statement->value == "++") - { - std::string reg = emitExpression(std::move(statement)); + case NodeType::UnaryOp: { + std::string op = statement->value; + std::string reg = emitExpression(std::move(statement), out); + if (op == "--" or op == "++") { alloc.free(reg); } + out << "\n"; break; } - case NodeType::BinaryOp: - { - std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. + case NodeType::BinaryOp: { + std::string reg = emitExpression(std::move(statement), out); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. + alloc.free(reg); + out << "\n"; + break; + } + case NodeType::FunctionCall: { + std::string reg = generateFunctionCall(std::move(statement), out); alloc.free(reg); + out << "\n"; + break; + } + case NodeType::Function: { + generateFunctionDeclaration(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::ExternFunction: { + generateExternFunctionDeclaration(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::ReturnStatement: { + generateReturnstatement(std::move(statement), out); + out << "\n"; break; } default: throw std::runtime_error("Unknown statement encountered."); } } - void CodeGenLinux::generateVariableReassignment(std::unique_ptr statement) - { + void CodeGenLinux::generateVariableReassignment(std::unique_ptr statement, std::ostringstream &out) { auto &scp = *statement->scope; auto nm = statement->value; auto ti = scp.lookupType(scp.lookupVariable(nm).type); uint64_t sz = ti.bits / 8; - auto r = emitExpression(std::move(statement->children.back())); - // Ensure the result is cast to the target variable's type - r = castValue(r, regType.at(r), ti); + auto r = emitExpression(std::move(statement->children.back()), out); + restoreIfSpilled(r, statement->scope, out); + + r = castValue(r, regType.at(r), ti, statement->scope, out); std::string addr = scp.isGlobalVariable(nm) ? nm + "(%rip)" : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)"; - if (ti.isFloat) - { + if (ti.isFloat) { out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " << addr << "\n"; - } - else - { + } else { std::string adj = adjustReg(r, ti.bits); out << " " << getCorrectMove(sz, false) << " %" << adj << ", " << addr << "\n"; } alloc.free(r); } - void CodeGenLinux::generateVariableDeclaration(std::unique_ptr statement) - { + void CodeGenLinux::generateVariableDeclaration(std::unique_ptr statement, std::ostringstream &out) { auto &scp = *statement->scope; auto nm = statement->value; auto ti = scp.lookupType(scp.lookupVariable(nm).type); uint64_t sz = ti.bits / 8; - if (!scp.isGlobalVariable(nm)) - { - if (statement->children.size() >= 2) - { - auto r = emitExpression(std::move(statement->children.back())); - r = castValue(r, regType.at(r), ti); // <- Added casting - - if (ti.isFloat) - { - out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " - << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" - << "\n"; - } - else - { - std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " %" << adj << ", " - << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" - << "\n"; - } - - alloc.free(r); - } - else - { - if (ti.isFloat) - { - std::string r_xmm = alloc.allocateXMM(); - out << " pxor %" << r_xmm << ", %" << r_xmm << "\n"; - out << " " << (sz == 4 ? "movss %" : "movsd %") << r_xmm << ", " - << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" - << "\n"; - alloc.free(r_xmm); - } - else - { - out << " xorq %rax, %rax\n"; - out << " mov" << integer_suffixes[ti.bits] << " %rax, " - << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" - << "\n"; - } - } - } - else // Global variable path - { - if (statement->children.size() >= 2) - { - auto r = emitExpression(std::move(statement->children.back())); - r = castValue(r, regType.at(r), ti); // <- Added casting - - if (ti.isFloat) - { - out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " - << nm + "(%rip)" - << "\n"; - } - else - { - std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " %" << adj << ", " - << nm + "(%rip)" - << "\n"; - } - - alloc.free(r); + auto emitStore = [&](const std::string &r) { + if (ti.isFloat) { + out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " + << (scp.isGlobalVariable(nm) ? nm + "(%rip)" : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)") + << "\n"; + } else { + std::string adj = adjustReg(r, ti.bits); + out << " " << getCorrectMove(sz, false) << " %" << adj << ", " + << (scp.isGlobalVariable(nm) ? nm + "(%rip)" : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)") + << "\n"; } - else - { - if (ti.isFloat) - { - std::string r_xmm = alloc.allocateXMM(); - out << " pxor %" << r_xmm << ", %" << r_xmm << "\n"; - out << " " << (sz == 4 ? "movss %" : "movsd %") << r_xmm << ", " - << nm + "(%rip)" - << "\n"; - alloc.free(r_xmm); - } - else - { - out << " xorq %rax, %rax\n"; - out << " mov" << integer_suffixes[ti.bits] << " %rax, " - << nm + "(%rip)" - << "\n"; - } + }; + + if (statement->children.size() >= 2) { + auto r = emitExpression(std::move(statement->children.back()), out); + restoreIfSpilled(r, statement->scope, out); + r = castValue(r, regType.at(r), ti, statement->scope, out); + + emitStore(r); + alloc.free(r); + } else { + if (ti.isFloat) { + auto r_xmm = allocateOrSpill(true, statement->scope, out); + out << " pxor %" << r_xmm << ", %" << r_xmm << "\n"; + emitStore(r_xmm); + alloc.free(r_xmm); + } else { + out << " xorq %rax, %rax\n"; + out << " mov" << integer_suffixes.at(ti.bits) << " %rax, " + << (scp.isGlobalVariable(nm) ? nm + "(%rip)" : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)") + << "\n"; } } } - void CodeGenLinux::generateIfStatement(std::unique_ptr statement) - { + void CodeGenLinux::generateIfStatement(std::unique_ptr statement, std::ostringstream &out) { int id = blockLabelCount++; std::string endLbl = ".Lend" + std::to_string(id); std::vector elseLabels; // Precompute labels for each else-if / else branch ASTNode *branch = statement->getElseBranch(); - while (branch) - { + while (branch) { elseLabels.push_back(".Lelse" + std::to_string(blockLabelCount++)); branch = branch->getElseBranch(); } size_t elseIdx = 0; - auto condR = emitExpression(std::move(statement->children[0])); + // IF branch + auto condR = emitExpression(std::move(statement->children[0]), out); + restoreIfSpilled(condR, statement->scope, out); out << " cmpq $0, %" << condR << "\n"; out << " je " << (elseLabels.empty() ? endLbl : elseLabels[elseIdx]) << "\n"; alloc.free(condR); auto ifBlock = std::move(statement->children[1]); - auto children = std::move(ifBlock->children); - emitPrologue(std::move(ifBlock)); - for (auto &stmt : children) - generateStatement(std::move(stmt)); - emitEpilogue(); + emitPrologue(ifBlock->scope, out); + for (auto &stmt : ifBlock->children) { + generateStatement(std::move(stmt), out); + } + emitEpilogue(ifBlock->scope, out); out << " jmp " << endLbl << "\n"; + // ELSE-IF / ELSE branches branch = statement->getElseBranch(); - while (branch) - { + while (branch) { out << elseLabels[elseIdx++] << ":\n"; - if (branch->type == NodeType::ElseIfStatement) - { - auto r2 = emitExpression(std::move(branch->children[0])); + if (branch->type == NodeType::ElseIfStatement) { + auto r2 = emitExpression(std::move(branch->children[0]), out); + restoreIfSpilled(r2, branch->scope, out); out << " cmpq $0, %" << r2 << "\n"; out << " je " << (elseIdx < elseLabels.size() ? elseLabels[elseIdx] : endLbl) << "\n"; alloc.free(r2); auto elifBlock = std::move(branch->children[1]); - auto children = std::move(elifBlock->children); - emitPrologue(std::move(elifBlock)); - for (auto &stmt : children) - generateStatement(std::move(stmt)); - emitEpilogue(); + emitPrologue(elifBlock->scope, out); + for (auto &stmt : elifBlock->children) { + generateStatement(std::move(stmt), out); + } + emitEpilogue(elifBlock->scope, out); out << " jmp " << endLbl << "\n"; - } - else if (branch->type == NodeType::ElseStatement) - { + + } else if (branch->type == NodeType::ElseStatement) { auto elseBlock = std::move(branch->children[0]); - auto children = std::move(elseBlock->children); - emitPrologue(std::move(elseBlock)); - for (auto &stmt : children) - generateStatement(std::move(stmt)); - emitEpilogue(); + emitPrologue(elseBlock->scope, out); + for (auto &stmt : elseBlock->children) { + generateStatement(std::move(stmt), out); + } + emitEpilogue(elseBlock->scope, out); } branch = branch->getElseBranch(); @@ -660,44 +683,308 @@ namespace zlang out << endLbl << ":\n\n"; } - void CodeGenLinux::generate(std::unique_ptr program) - { + void CodeGenLinux::generate(std::unique_ptr program) { std::vector globals; - for (auto &statement : program->children) - if (statement->type == NodeType::VariableDeclaration) + + // Collect global variable declarations + for (auto &statement : program->children) { + if (statement->type == NodeType::VariableDeclaration) { globals.push_back(statement.get()); - outGlobal << ".data\n\n"; - for (auto &g : globals) - { + } + } + + // Emit .data section for globals + outGlobalStream << ".data\n\n"; + for (auto *g : globals) { TypeInfo info = g->scope->lookupType(g->children[0]->value); - switch (info.bits / 8) - { + switch (info.bits / 8) { case 8: - outGlobal << g->value << ": .quad 0\n"; + outGlobalStream << g->value << ": .quad 0\n"; break; case 4: - outGlobal << g->value << ": .long 0\n"; + outGlobalStream << g->value << ": .long 0\n"; break; case 2: - outGlobal << g->value << ": .word 0\n"; + outGlobalStream << g->value << ": .word 0\n"; break; case 1: - outGlobal << g->value << ": .byte 0\n"; + outGlobalStream << g->value << ": .byte 0\n"; break; default: - throw std::runtime_error("Unsupported global size"); + throw std::runtime_error("Unsupported global size: " + std::to_string(info.bits / 8)); + } + } + + // Emit rodata and stack protection code + outGlobalStream << "\n.section .rodata\n"; + + outStream << ".text\n"; + outStream << ".globl __stack_smash_detected\n"; + outStream << "__stack_smash_detected:\n"; + outStream << " mov $60, %rax # syscall: exit\n"; + outStream << " mov $69, %rdi # exit code\n"; + outStream << " syscall\n"; + + // Entry point + outStream << ".global main\n"; + + std::unique_ptr mainFunction; + + // Generate code for all top-level statements + for (auto &statement : program->children) { + if (statement->type == NodeType::Function and statement->value == "main") { + mainFunction = std::move(statement); + } else { + generateStatement(std::move(statement), outStream); + } + } + + outStream << "main:\n"; + generateFunctionDeclaration(std::move(mainFunction), outStream, true); + + // Final output + outfinal << outGlobalStream.str() + << "\n# ============== Globals End Here ==============\n" + << outStream.str() << "\n"; + } + + std::string CodeGenLinux::generateFunctionCall(std::unique_ptr node, std::ostringstream &out) { + auto fnInfo = node->scope->lookupFunction(node->value); + const std::string &label = fnInfo.isExtern ? node->value : fnInfo.label; + auto &args = node->children[0]->children; + const auto &functionParams = fnInfo.paramTypes; + + // --- Save caller-saved GPRs --- + std::vector savedGPR; + for (const auto ® : CALLER_GPR_LINUX) { + if (alloc.isInUse(reg)) { + out << " push %" << reg << " # save caller-saved GPR\n"; + savedGPR.push_back(reg); + } + } + + // --- Save caller-saved XMMs --- + std::vector> savedXMM; + for (const auto ® : CALLER_XMM_LINUX) { + if (alloc.isInUseXMM(reg)) { + auto funcScope = std::static_pointer_cast(node->scope->findEnclosingFunctionScope()); + std::string slot = funcScope->allocateSpillSlot(16); + alloc.markSpilledXMM(reg, slot); + out << " movdqu %" << reg << ", " << slot << " # save caller-saved XMM\n"; + savedXMM.emplace_back(reg, slot); } } - outGlobal << "\n.section .rodata\n"; // Ensure rodata section exists - out << ".text\n.global _start\n_start:\n\n"; - for (std::unique_ptr &statement : program.get()->children) - { - generateStatement(std::move(statement)); + + // calculate stack space for overflow arguments + uint64_t gpCount = 0, xmmCount = 0, stackOffset = 0; + for (size_t i = 0; i < args.size(); ++i) { + bool isFloat = node->scope->lookupType(functionParams[i].type).isFloat; + if (!isFloat) { + if (gpCount < ARG_GPR_LINUX.size()) + gpCount++; + else + stackOffset += 8; + } else { + if (xmmCount < ARG_XMM_LINUX.size()) + xmmCount++; + else + stackOffset += 8; + } + } + uint64_t stackReserve = (stackOffset + 15) & ~15ULL; + if (stackReserve) + out << " sub $" << stackReserve << ", %rsp # reserve stack for args\n"; + + // emit argument moves + gpCount = xmmCount = 0; + stackOffset = 0; + std::vector reservedArgumentRegs; + std::vector spilledArgumentRegs; + + for (size_t i = 0; i < args.size(); ++i) { + std::string src = emitExpression(std::move(args[i]), out); + restoreIfSpilled(src, node->scope, out); + + TypeInfo passed = regType.at(src); + TypeInfo expect = node->scope->lookupType(functionParams[i].type); + std::string cvt = castValue(src, passed, expect, node->scope, out); + restoreIfSpilled(cvt, node->scope, out); + + bool isFloat = expect.isFloat; + if (!isFloat && gpCount < ARG_GPR_LINUX.size()) { + std::string dst = ARG_GPR_LINUX[gpCount]; + if (alloc.isInUseArgument(dst)) { + auto funcScope = std::static_pointer_cast(node->scope->findEnclosingFunctionScope()); + std::string slot = funcScope->allocateSpillSlot(8); + alloc.markSpilled(dst, slot); + spilledArgumentRegs.push_back(dst); + out << " movq %" << dst << ", " << slot << " # spill GPR\n"; + } else { + dst = alloc.allocateArgument(gpCount); + reservedArgumentRegs.emplace_back(dst); + } + gpCount++; + out << " movq %" << cvt << ", %" << dst << "\n"; + } else if (isFloat && xmmCount < ARG_XMM_LINUX.size()) { + std::string dst = ARG_XMM_LINUX[xmmCount]; + if (alloc.isInUseArgumentXMM(dst)) { + auto funcScope = std::static_pointer_cast(node->scope->findEnclosingFunctionScope()); + std::string slot = funcScope->allocateSpillSlot(16); + alloc.markSpilledXMM(dst, slot); + spilledArgumentRegs.push_back(dst); + out << " movdqu %" << dst << ", " << slot << " # spill XMM\n"; + } else { + dst = alloc.allocateArgumentXMM(xmmCount); + reservedArgumentRegs.emplace_back(dst); + } + xmmCount++; + out << " movss %" << cvt << ", %" << dst << "\n"; + } else { + out << " mov" << (isFloat ? "ss %" : "q %") << cvt << ", " << stackOffset << "(%rsp)\n"; + stackOffset += 8; + } + alloc.free(cvt); } - out << " movq $60, %rax\n movq $0, %rdi\n syscall\n"; + // --- Call --- + out << " call " << label << " # function call\n"; - outfinal << outGlobal.str() + "\n# ==============Globals End Here==============\n" - << out.str() << "\n\n"; + for (std::string ® : spilledArgumentRegs) { + restoreIfSpilled(reg, node->scope, out); + } + + for (std::string ® : reservedArgumentRegs) { + alloc.freeArgument(reg); + } + + // restore caller-saved XMMs + for (auto it = savedXMM.rbegin(); it != savedXMM.rend(); ++it) { + out << " movdqu " << it->second << ", %" << it->first << " # restore XMM\n"; + } + // restore caller-saved GPRs + for (auto it = savedGPR.rbegin(); it != savedGPR.rend(); ++it) { + out << " pop %" << *it << " # restore GPR\n"; + } + + // restore stack + if (stackReserve) + out << " add $" << stackReserve << ", %rsp # free args stack\n"; + + // return value + bool isFloatRet = fnInfo.returnType == "float" || fnInfo.returnType == "double"; + std::string holder = allocateOrSpill(isFloatRet, node->scope, out); + std::string abiReg = isFloatRet ? "xmm0" : "rax"; + noteType(holder, node->scope->lookupType(fnInfo.returnType)); + if (isFloatRet) { + const char *mov = fnInfo.returnType == "float" ? "movss" : "movsd"; + out << " " << mov << " %" << abiReg << ", %" << holder << "\n"; + } else { + out << " movq %" << abiReg << ", %" << holder << "\n"; + } + return holder; + } + + void CodeGenLinux::generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force) { + if (node->value == "main" && !force) { + return; + } + auto fnInfo = node->scope->lookupFunction(node->value); + auto bodyNode = node->getFunctionBody(); + auto funcScope = std::dynamic_pointer_cast( + bodyNode->scope->findEnclosingFunctionScope()); + + // Function label and prologue with canary and stack frame setup + if (!force) { + out << fnInfo.label << ":\n"; + } + std::ostringstream prologue; + std::ostringstream body; + + // Canary setup + std::uint64_t canary = CanaryGenerator::generate(); + funcScope->setCanary(canary); + + // SysV argument register order + const auto &ARG_GPR_LINUX = alloc.availableArgumentRegs; // {"rdi","rsi","rdx","rcx","r8","r9"} + const auto &ARG_XMM_LINUX = alloc.availableArgumentRegsXMM; // {"xmm0","xmm1",...,"xmm7"} + + auto ¶ms = fnInfo.paramTypes; + size_t gpIdx = 0, xmmIdx = 0, stackArgOffset = 0; + + // Move incoming args into their stack slots + for (size_t i = 0; i < params.size(); ++i) { + const auto &p = params[i]; + TypeInfo ti = node->scope->lookupType(p.type); + bool isFloat = ti.isFloat; + int64_t slotOff = bodyNode->scope->getVariableOffset(p.name); + + // Determine correct mov instruction + std::string movInst; + if (isFloat) { + movInst = (ti.bits == 64) ? "movsd" : "movss"; + } else { + movInst = "mov" + std::string(1, integer_suffixes.at(64)); // "movq" + } + + if (!isFloat && gpIdx < ARG_GPR_LINUX.size()) { + // integer in GPR + body << " " << movInst + << " %" << ARG_GPR_LINUX[gpIdx++] // prefix register + << ", " << std::to_string(slotOff) << "( %rbp)\n"; + + } else if (isFloat && xmmIdx < ARG_XMM_LINUX.size()) { + // float in XMM reg + body << " " << movInst + << " %" << ARG_XMM_LINUX[xmmIdx++] + << ", " << std::to_string(slotOff) << "( %rbp)\n"; + + } else { + // spilled argument on caller's stack + uint64_t callerDisp = 16 + stackArgOffset; + body << " " << movInst + << " " << callerDisp << "(%rbp)" + << ", " << std::to_string(slotOff) << "( %rbp)\n"; + stackArgOffset += 8; + } + } + + // Generate the function body + for (auto &stmt : bodyNode->children) { + generateStatement(std::move(stmt), body); + } + emitPrologue(funcScope, prologue); + if (bodyNode->scope->returnType == "none") { + emitEpilogue(funcScope, body, force); + } + out << prologue.str() + body.str(); + } + void CodeGenLinux::generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) { + auto fnInfo = node->scope->lookupFunction(node->value); + const std::string &label = node->value; + outGlobalStream << " .global " << label << "\n"; + outGlobalStream << " .type " << label << ", @function\n"; + } + void CodeGenLinux::generateReturnstatement(std::unique_ptr node, std::ostringstream &out) { + // TODO: Ensure that we are not returning a local type, at this point I assume that typechecker handles this. + std::string result = emitExpression(std::move(node->children[0]), out); + std::shared_ptr funcScope = node->scope->findEnclosingFunctionScope(); + restoreIfSpilled(result, funcScope, out); + TypeInfo actual = regType.at(result); + TypeInfo expected = funcScope->lookupType(funcScope->returnType); + std::string casted = castValue(result, actual, expected, funcScope, out); + restoreIfSpilled(casted, funcScope, out); + std::string retReg = expected.isFloat ? "xmm0" : "rax"; + if (expected.name == "none") { + out << " xor %rax, %rax\n"; + } else if (!expected.isFloat) { + out << " mov" << integer_suffixes.at(64) + << " %" << casted << ", %" << retReg << "\n"; + } else { + const char *mov = (expected.bits == 32 ? "movss" : "movsd"); + out << " " << mov + << " %" << casted << ", %" << retReg << "\n"; + } + alloc.free(casted); + emitEpilogue(funcScope, out); } -} // namespace zlang \ No newline at end of file +} // namespace zlang \ No newline at end of file diff --git a/src/codegen/CodeGenWindows.cpp b/src/codegen/CodeGenWindows.cpp index 3b7713e..0ba7664 100644 --- a/src/codegen/CodeGenWindows.cpp +++ b/src/codegen/CodeGenWindows.cpp @@ -1,730 +1,627 @@ -#include "all.hpp" - -namespace zlang -{ - std::string CodeGenWindows::castValue(const std::string &srcReg, const TypeInfo &fromType, const TypeInfo &toType) - { - if (fromType.bits == toType.bits && fromType.isFloat == toType.isFloat) - { - if (!fromType.isFloat && fromType.bits == toType.bits && - fromType.isSigned != toType.isSigned) - { - std::string dst = alloc.allocate(); - out << " mov " << adjustReg(dst, fromType.bits) - << ", " << adjustReg(srcReg, fromType.bits) << "\n"; - alloc.free(srcReg); - noteType(dst, toType); - return dst; - } - return RegisterAllocator::getBaseReg(srcReg); - } - - if (fromType.isFloat && toType.isFloat) - { - std::string dst = alloc.allocateXMM(); - auto instr = (fromType.bits < toType.bits) - ? "cvtss2sd" - : "cvtsd2ss"; - out << " " << instr << " " << dst << ", " << srcReg << "\n"; - alloc.free(srcReg); - noteType(dst, toType); - return dst; - } - - if (!fromType.isFloat && toType.isFloat) - { - std::string dst = alloc.allocateXMM(); - auto instr = (toType.bits == 32) - ? "cvtsi2ss" - : "cvtsi2sd"; - out << " " << instr << " " << dst - << ", " << adjustReg(srcReg, fromType.bits) << "\n"; - alloc.free(srcReg); - noteType(dst, toType); - return dst; - } - - if (fromType.isFloat && !toType.isFloat) - { - std::string dst = alloc.allocate(); - auto instr = (fromType.bits == 32) - ? "cvttss2si" - : "cvttsd2si"; - out << " " << instr << " " << dst << ", " << srcReg << "\n"; - alloc.free(srcReg); - noteType(dst, toType); - return dst; - } - - if (!fromType.isFloat && !toType.isFloat) - { - std::string dst = alloc.allocate(); - std::string srcAdj = adjustReg(srcReg, fromType.bits); - std::string dstAdj = dst; - - if (toType.bits > fromType.bits) - { - if (fromType.isSigned) - { - if (fromType.bits == 32 && toType.bits == 64) - { - out << " movsxd " << dstAdj << ", " << srcAdj << "\n"; - } - else - { - out << " movsx " << dstAdj << ", " << srcAdj << "\n"; - } - } - else - { - if (fromType.bits == 8 || fromType.bits == 16) - { - out << " movzx " << dstAdj << ", " << srcAdj << "\n"; - } - else if (fromType.bits == 32 && toType.bits == 64) - { - out << " mov " << dstAdj << "d, " << srcAdj << "\n"; - } - else - { - throw std::runtime_error("Unsupported unsigned cast: " + std::to_string(fromType.bits) + " -> " + std::to_string(toType.bits)); - } - } - } - else if (toType.bits < fromType.bits) - { - if (toType.bits == 8 || toType.bits == 16) - { - if (toType.isSigned) - { - out << " movsx " << dstAdj << ", " << adjustReg(srcReg, toType.bits) << "\n"; - } - else - { - out << " movzx " << dstAdj << ", " << adjustReg(srcReg, toType.bits) << "\n"; - } - } - else if (toType.bits == 32) - { - out << " mov " << adjustReg(dstAdj, 32) << ", " << adjustReg(srcReg, 32) << "\n"; - } - else - { - throw std::runtime_error("Unsupported downcast to " + std::to_string(toType.bits)); - } - } - else - { - out << " mov " << adjustReg(dstAdj, toType.bits) << ", " << srcAdj << "\n"; - } - - alloc.free(srcReg); - noteType(dst, toType); - return dst; - } - - throw std::runtime_error( - "Unsupported cast from " + - fromType.to_string() + - " to " + - toType.to_string()); - } - - std::string CodeGenWindows::getVariableAddress(const ScopeContext &scope, const std::string &name) const - { - if (scope.isGlobalVariable(name)) - { - return "[" + name + "]"; - } - else - { - int offset = scope.getVariableOffset(name); - return (offset < 0) - ? "[rbp - " + std::to_string(-offset) + "]" - : "[rbp + " + std::to_string(offset) + "]"; - } - } - std::string CodeGenWindows::generateIntegerLiteral(std::unique_ptr node) - { - TypeInfo ti = node->scope->lookupType("int64_t"); - auto r = alloc.allocate(); - std::string adj = adjustReg(r, ti.bits); - - out << " mov " << adj << ", " << node->value << "\n"; - noteType(r, ti); - - return r; - } - std::string CodeGenWindows::generateFloatLiteral(std::unique_ptr node) - { - std::string lbl = "Lfloat" + std::to_string(floatLabelCount++); - std::string val = node->value; - bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); - - if (isF32) - val.pop_back(); - - if (isF32) - outGlobal << lbl << " DWORD " << val << "\n"; - else - outGlobal << lbl << " QWORD " << val << "\n"; - - auto r_xmm = alloc.allocateXMM(); - - out << " " << (isF32 ? "movss" : "movsd") - << " " << r_xmm << ", " - << (isF32 ? "DWORD PTR " : "QWORD PTR ") - << "[" << lbl << "]\n"; - - noteType(r_xmm, node->scope->lookupType(isF32 ? "float" : "double")); - - return r_xmm; - } - std::string CodeGenWindows::generateStringLiteral(std::unique_ptr node) - { - std::string lbl = "Lstr" + std::to_string(stringLabelCount++); - outGlobal << lbl << " BYTE " - << '"' << ((node->value.length() == 0) ? "\\0" : node->value) << '"' << ", 0\n"; - auto r = alloc.allocate(); - out << " lea " << r << ", [" << lbl << "]\n"; - noteType(r, node->scope->lookupType("string")); - - return r; - } - std::string CodeGenWindows::generateBooleanLiteral(std::unique_ptr node) - { - auto r = alloc.allocate(); - out << " mov " << r << ", " << (node->value == "true" ? "1" : "0") << "\n"; - noteType(r, node->scope->lookupType("boolean")); - return r; - } - std::string CodeGenWindows::generateVariableAccess(std::unique_ptr node) - { - auto &scope = *node->scope; - const std::string name = node->value; - TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); - uint64_t sz = ti.bits / 8; - uint64_t bits = ti.bits; - - std::string address = getVariableAddress(scope, name); // Use helper - - if (ti.isFloat) - { - auto r_xmm = alloc.allocateXMM(); - if (sz == 4) - { - out << " movss " << r_xmm << ", DWORD PTR " << address << "\n"; - } - else if (sz == 8) - { - out << " movsd " << r_xmm << ", QWORD PTR " << address << "\n"; - } - else - { - throw std::runtime_error("Unsupported float size: " + std::to_string(sz)); - } - noteType(r_xmm, ti); - return r_xmm; - } - else - { - auto r64 = alloc.allocate(); - auto r_adj = adjustReg(r64, bits); - - std::string ptrSize; - switch (sz) - { - case 8: - ptrSize = "QWORD PTR "; - break; - case 4: - ptrSize = "DWORD PTR "; - break; - case 2: - ptrSize = "WORD PTR "; - break; - case 1: - ptrSize = "BYTE PTR "; - break; - default: - throw std::runtime_error("Unsupported integer size: " + std::to_string(sz)); - } - - out << " mov " << r_adj << ", " << ptrSize << address << "\n"; - noteType(r_adj, ti); - return r_adj; - } - } - std::string CodeGenWindows::generateBinaryOperation(std::unique_ptr node) - { - std::string rl = emitExpression(std::move(node->children[0])); - std::string rr = emitExpression(std::move(node->children[1])); - - TypeInfo t1 = regType.at(rl); - TypeInfo t2 = regType.at(rr); - TypeInfo tr = TypeChecker::promoteType(t1, t2); - uint64_t bits = tr.bits; - const std::string &op = node->value; - - // Cast operands to the promoted type - rl = castValue(rl, t1, tr); - rr = castValue(rr, t2, tr); - - if (tr.isFloat) - { - const char *suf = (bits == 32 ? "ss" : "sd"); - - if (op == "+") - out << " add" << suf << " " << rl << ", " << rr << "\n"; - else if (op == "-") - out << " sub" << suf << " " << rl << ", " << rr << "\n"; - else if (op == "*") - out << " mul" << suf << " " << rl << ", " << rr << "\n"; - else if (op == "/") - out << " div" << suf << " " << rl << ", " << rr << "\n"; - else if (assembly_comparison_operations.count(op)) - { - out << " ucomi" << suf << " " << rl << ", " << rr << "\n"; - auto result = alloc.allocate(); - auto result8 = adjustReg(result, 8); - out << " " << assembly_comparison_operations.at(op) << " " << result8 << "\n"; - out << " movzx " << result << ", " << result8 << "\n"; - alloc.free(rl); - alloc.free(rr); - noteType(result, node->scope->lookupType("boolean")); - return result; - } - else - { - throw std::runtime_error("Unsupported FP op: " + op); - } - - alloc.free(rr); - noteType(rl, tr); - return rl; - } - else - { - std::string dl = rl; - std::string dr = rr; - - if (op == "+") - out << " add " << dl << ", " << dr << "\n"; - else if (op == "-") - out << " sub " << dl << ", " << dr << "\n"; - else if (op == "*") - out << " imul " << dl << ", " << dr << "\n"; - else if (op == "/") - { - auto temp = alloc.allocate(); - out << " mov " << temp << ", rax\n"; - out << " mov rax, " << dl << "\n"; - out << " cqo\n"; - out << " idiv " << dr << "\n"; - out << " mov " << dl << ", rax\n"; - out << " mov rax, " << temp << "\n"; - alloc.free(temp); - alloc.free(rr); - noteType(dl, tr); - return dl; - } - else if (assembly_comparison_operations.count(op)) - { - out << " cmp " << dl << ", " << dr << "\n"; - auto result = alloc.allocate(); - auto result8 = adjustReg(result, 8); - out << " " << assembly_comparison_operations.at(op) << " " << result8 << "\n"; - out << " movzx " << result << ", " << result8 << "\n"; - alloc.free(rr); - noteType(result, node->scope->lookupType("boolean")); - return result; - } - else - { - throw std::runtime_error("Unsupported integer op: " + op); - } - - alloc.free(rr); - noteType(rl, tr); - return rl; - } - } - std::string CodeGenWindows::generateUnaryOperation(std::unique_ptr node) - { - const std::string &op = node->value; - auto child = std::move(node->children[0]); - - if (op == "!") - { - std::string r = emitExpression(std::move(child)); - TypeInfo t = regType.at(r); - - // Cast value to boolean-compatible type (e.g., integer of any size) - TypeInfo boolType = node->scope->lookupType("boolean"); - r = castValue(r, t, boolType); - - out << " cmp " << r << ", 0\n"; - - std::string result = alloc.allocate(); - std::string result8 = adjustReg(result, 8); - - out << " sete " << result8 << "\n"; - out << " movzx " << result << ", " << result8 << "\n"; - - alloc.free(r); - noteType(result, boolType); - return result; - } - else if (op == "++" || op == "--") - { - if (child->type != NodeType::VariableAccess) - throw std::runtime_error(op + " can only be applied to variables"); - - std::string var = child->value; - auto &scp = *child->scope; - TypeInfo ti = scp.lookupType(scp.lookupVariable(var).type); - - if (ti.isFloat) - throw std::runtime_error("Increment/Decrement not supported on float"); - - std::string reg = generateVariableAccess(std::move(child)); - reg = castValue(reg, regType.at(reg), ti); // Ensure reg is properly typed - std::string adj = adjustReg(reg, ti.bits); - - out << " " << (op == "++" ? "inc" : "dec") << " " << adj << "\n"; - - std::string ptrSize; - switch (ti.bits) - { - case 64: - ptrSize = "QWORD PTR "; - break; - case 32: - ptrSize = "DWORD PTR "; - break; - case 16: - ptrSize = "WORD PTR "; - break; - case 8: - ptrSize = "BYTE PTR "; - break; - default: - throw std::runtime_error("Unsupported variable size: " + std::to_string(ti.bits)); - } - - std::string addr = getVariableAddress(scp, var); - out << " mov " << ptrSize << addr << ", " << adj << "\n"; - - noteType(reg, ti); - return reg; - } - - throw std::runtime_error("Unknown Unary Operator: " + op); - } - std::string CodeGenWindows::emitExpression(std::unique_ptr node) - { - switch (node->type) - { - case NodeType::IntegerLiteral: - return generateIntegerLiteral(std::move(node)); - case NodeType::FloatLiteral: - return generateFloatLiteral(std::move(node)); - case NodeType::StringLiteral: - return generateStringLiteral(std::move(node)); - case NodeType::BooleanLiteral: - return generateBooleanLiteral(std::move(node)); - case NodeType::VariableAccess: - return generateVariableAccess(std::move(node)); - case NodeType::BinaryOp: - return generateBinaryOperation(std::move(node)); - case NodeType::UnaryOp: - return generateUnaryOperation(std::move(node)); - default: - throw std::runtime_error("Unknown expression encountered."); - } - } - void CodeGenWindows::emitPrologue(std::unique_ptr blockNode) - { - auto size = -blockNode->scope->stackOffset + 32; // +32 for shadow space - out << " ; Block Starts (scope enter)\n"; - out << " push rbp\n"; - out << " mov rbp, rsp\n"; - out << " sub rsp, " << size << "\n"; - } - void CodeGenWindows::emitEpilogue() - { - out << " ; Block Ends (scope exit)\n"; - out << " leave\n"; - } - void CodeGenWindows::generateStatement(std::unique_ptr statement) - { - switch (statement->type) - { - case NodeType::VariableReassignment: - { - generateVariableReassignment(std::move(statement)); - break; - } - case NodeType::VariableDeclaration: - { - generateVariableDeclaration(std::move(statement)); - break; - } - case NodeType::IfStatement: - { - generateIfStatement(std::move(statement)); - break; - } - case NodeType::UnaryOp: - { - if (statement->value == "--" or statement->value == "++") - { - std::string reg = emitExpression(std::move(statement)); - alloc.free(reg); - } - break; - } - case NodeType::BinaryOp: - { - std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. - alloc.free(reg); - break; - } - default: - throw std::runtime_error("Unknown statement encountered."); - } - } - void CodeGenWindows::generateVariableReassignment(std::unique_ptr statement) - { - auto &scope = *statement->scope; - const std::string &name = statement->value; - TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); - uint64_t bits = ti.bits; - std::string dest = getVariableAddress(scope, name); - - std::string r = emitExpression(std::move(statement->children.back())); - TypeInfo rt = regType.at(r); - - r = castValue(r, rt, ti); - - if (ti.isFloat) - { - std::string movInstr = (bits == 32 ? "movss" : "movsd"); - std::string ptrPrefix = (bits == 32 ? "DWORD PTR " : "QWORD PTR "); - out << " " << movInstr << " " - << ptrPrefix << dest << ", " << r << "\n"; - alloc.free(r); - } - else - { - std::string adj = adjustReg(r, bits); - std::string ptrPrefix; - switch (bits) - { - case 64: - ptrPrefix = "QWORD PTR "; - break; - case 32: - ptrPrefix = "DWORD PTR "; - break; - case 16: - ptrPrefix = "WORD PTR "; - break; - case 8: - ptrPrefix = "BYTE PTR "; - break; - default: - throw std::runtime_error("Unsupported size in reassignment: " + std::to_string(bits)); - } - out << " mov " << ptrPrefix << dest << ", " << adj << "\n"; - alloc.free(r); - } - } - void CodeGenWindows::generateVariableDeclaration(std::unique_ptr statement) - { - auto &scope = *statement->scope; - const std::string &name = statement->value; - TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); - uint64_t bits = ti.bits; - - auto ptrPrefix = [&]() -> std::string - { - switch (bits) - { - case 64: - return "QWORD PTR "; - case 32: - return "DWORD PTR "; - case 16: - return "WORD PTR "; - case 8: - return "BYTE PTR "; - default: - throw std::runtime_error("Unsupported size in declaration: " + std::to_string(bits)); - } - }; - - if (statement->children.size() >= 2) - { - std::string r = emitExpression(std::move(statement->children.back())); - TypeInfo rt = regType.at(r); - r = castValue(r, rt, ti); - - std::string addr = getVariableAddress(scope, name); - if (ti.isFloat) - { - std::string movInstr = (bits == 32 ? "movss" : "movsd"); - out << " " << movInstr << " " - << ptrPrefix() << addr << ", " << r << "\n"; - } - else - { - std::string adj = adjustReg(r, bits); - out << " mov " << ptrPrefix() << addr << ", " << adj << "\n"; - } - alloc.free(r); - } - else - { - std::string addr = getVariableAddress(scope, name); - if (ti.isFloat) - { - std::string r_xmm = alloc.allocateXMM(); - std::string movInstr = (bits == 32 ? "movss" : "movsd"); - out << " pxor " << r_xmm << ", " << r_xmm << "\n" - << " " << movInstr << " " - << ptrPrefix() << addr << ", " << r_xmm << "\n"; - alloc.free(r_xmm); - } - else - { - out << " xor rax, rax\n" - << " mov " << ptrPrefix() << addr << ", rax\n"; - } - } - } - void CodeGenWindows::generateIfStatement(std::unique_ptr statement) - { - int id = blockLabelCount++; - std::string endLbl = "Lend" + std::to_string(id); - - std::vector elseLabels; - ASTNode *branch = statement->getElseBranch(); - while (branch) - { - elseLabels.push_back("Lelse" + std::to_string(blockLabelCount++)); - branch = branch->getElseBranch(); - } - - size_t elseIdx = 0; - - auto condReg = emitExpression(std::move(statement->children[0])); - out << " cmp " << condReg << ", 0\n"; - if (!elseLabels.empty()) - out << " je " << elseLabels[elseIdx] << "\n"; - else - out << " je " << endLbl << "\n"; - alloc.free(condReg); - - auto ifBlock = std::move(statement->children[1]); - auto stmts = std::move(ifBlock->children); - emitPrologue(std::move(ifBlock)); - for (auto &stmt : stmts) - generateStatement(std::move(stmt)); - emitEpilogue(); - out << " jmp " << endLbl << "\n"; - - branch = statement->getElseBranch(); - while (branch) - { - out << elseLabels[elseIdx++] << ":\n"; - - if (branch->type == NodeType::ElseIfStatement) - { - auto r = emitExpression(std::move(branch->children[0])); - out << " cmp " << r << ", 0\n"; - if (elseIdx < elseLabels.size()) - out << " je " << elseLabels[elseIdx] << "\n"; - else - out << " je " << endLbl << "\n"; - alloc.free(r); - - auto elifBlock = std::move(branch->children[1]); - auto stmts = std::move(elifBlock->children); - emitPrologue(std::move(elifBlock)); - for (auto &stmt : stmts) - generateStatement(std::move(stmt)); - emitEpilogue(); - out << " jmp " << endLbl << "\n"; - } - else if (branch->type == NodeType::ElseStatement) - { - auto elseBlock = std::move(branch->children[0]); - auto stmts = std::move(elseBlock->children); - emitPrologue(std::move(elseBlock)); - for (auto &stmt : stmts) - generateStatement(std::move(stmt)); - emitEpilogue(); - break; - } - else - { - throw std::runtime_error("Unexpected node type in else chain"); - } - - branch = branch->getElseBranch(); - } - - out << endLbl << ":\n\n"; - } - void CodeGenWindows::generate(std::unique_ptr program) - { - std::vector globals; - for (auto &statement : program->children) - if (statement->type == NodeType::VariableDeclaration) - globals.push_back(statement.get()); - - outGlobal << ".data\n\n"; - - for (auto &g : globals) - { - TypeInfo info = g->scope->lookupType(g->children[0]->value); - switch (info.bits / 8) - { - case 8: - outGlobal << g->value << " QWORD 0\n"; - break; - case 4: - outGlobal << g->value << " DWORD 0\n"; - break; - case 2: - outGlobal << g->value << " WORD 0\n"; - break; - case 1: - outGlobal << g->value << " BYTE 0\n"; - break; - default: - throw std::runtime_error("Unsupported global size"); - } - } - - outGlobal << "\n.const\n"; - - out << ".code\n"; - out << "main PROC\n"; - - for (std::unique_ptr &statement : program.get()->children) - { - generateStatement(std::move(statement)); - } - - out << " xor eax, eax\n"; - out << " ret\n"; - - out << "main ENDP\n"; - - outfinal << outGlobal.str() - << "\n; ============== Globals End Here ==============\n" - << out.str() << "END\n\n"; - } -} // namespace zlang \ No newline at end of file +// #include "all.hpp" + +// namespace zlang { +// std::string CodeGenWindows::castValue(const std::string &srcReg, const TypeInfo &fromType, const TypeInfo &toType) { +// if (fromType.bits == toType.bits && fromType.isFloat == toType.isFloat) { +// if (!fromType.isFloat && fromType.bits == toType.bits && +// fromType.isSigned != toType.isSigned) { +// std::string dst = alloc.allocate(); +// out << " mov " << adjustReg(dst, fromType.bits) +// << ", " << adjustReg(srcReg, fromType.bits) << "\n"; +// alloc.free(srcReg); +// noteType(dst, toType); +// return dst; +// } +// return RegisterAllocator::getBaseReg(srcReg); +// } + +// if (fromType.isFloat && toType.isFloat) { +// std::string dst = alloc.allocateXMM(); +// auto instr = (fromType.bits < toType.bits) +// ? "cvtss2sd" +// : "cvtsd2ss"; +// out << " " << instr << " " << dst << ", " << srcReg << "\n"; +// alloc.free(srcReg); +// noteType(dst, toType); +// return dst; +// } + +// if (!fromType.isFloat && toType.isFloat) { +// std::string dst = alloc.allocateXMM(); +// auto instr = (toType.bits == 32) +// ? "cvtsi2ss" +// : "cvtsi2sd"; +// out << " " << instr << " " << dst +// << ", " << adjustReg(srcReg, fromType.bits) << "\n"; +// alloc.free(srcReg); +// noteType(dst, toType); +// return dst; +// } + +// if (fromType.isFloat && !toType.isFloat) { +// std::string dst = alloc.allocate(); +// auto instr = (fromType.bits == 32) +// ? "cvttss2si" +// : "cvttsd2si"; +// out << " " << instr << " " << dst << ", " << srcReg << "\n"; +// alloc.free(srcReg); +// noteType(dst, toType); +// return dst; +// } + +// if (!fromType.isFloat && !toType.isFloat) { +// std::string dst = alloc.allocate(); +// std::string srcAdj = adjustReg(srcReg, fromType.bits); +// std::string dstAdj = dst; + +// if (toType.bits > fromType.bits) { +// if (fromType.isSigned) { +// if (fromType.bits == 32 && toType.bits == 64) { +// out << " movsxd " << dstAdj << ", " << srcAdj << "\n"; +// } else { +// out << " movsx " << dstAdj << ", " << srcAdj << "\n"; +// } +// } else { +// if (fromType.bits == 8 || fromType.bits == 16) { +// out << " movzx " << dstAdj << ", " << srcAdj << "\n"; +// } else if (fromType.bits == 32 && toType.bits == 64) { +// out << " mov " << dstAdj << "d, " << srcAdj << "\n"; +// } else { +// throw std::runtime_error("Unsupported unsigned cast: " + std::to_string(fromType.bits) + " -> " + std::to_string(toType.bits)); +// } +// } +// } else if (toType.bits < fromType.bits) { +// if (toType.bits == 8 || toType.bits == 16) { +// if (toType.isSigned) { +// out << " movsx " << dstAdj << ", " << adjustReg(srcReg, toType.bits) << "\n"; +// } else { +// out << " movzx " << dstAdj << ", " << adjustReg(srcReg, toType.bits) << "\n"; +// } +// } else if (toType.bits == 32) { +// out << " mov " << adjustReg(dstAdj, 32) << ", " << adjustReg(srcReg, 32) << "\n"; +// } else { +// throw std::runtime_error("Unsupported downcast to " + std::to_string(toType.bits)); +// } +// } else { +// out << " mov " << adjustReg(dstAdj, toType.bits) << ", " << srcAdj << "\n"; +// } + +// alloc.free(srcReg); +// noteType(dst, toType); +// return dst; +// } + +// throw std::runtime_error( +// "Unsupported cast from " + +// fromType.to_string() + +// " to " + +// toType.to_string()); +// } + +// std::string CodeGenWindows::getVariableAddress(const ScopeContext &scope, const std::string &name) const { +// if (scope.isGlobalVariable(name)) { +// return "[" + name + "]"; +// } else { +// int offset = scope.getVariableOffset(name); +// return (offset < 0) +// ? "[rbp - " + std::to_string(-offset) + "]" +// : "[rbp + " + std::to_string(offset) + "]"; +// } +// } +// std::string CodeGenWindows::generateIntegerLiteral(std::unique_ptr node) { +// TypeInfo ti = node->scope->lookupType("int64_t"); +// auto r = alloc.allocate(); +// std::string adj = adjustReg(r, ti.bits); + +// out << " mov " << adj << ", " << node->value << "\n"; +// noteType(r, ti); + +// return r; +// } +// std::string CodeGenWindows::generateFloatLiteral(std::unique_ptr node) { +// std::string lbl = "Lfloat" + std::to_string(floatLabelCount++); +// std::string val = node->value; +// bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); + +// if (isF32) +// val.pop_back(); + +// if (isF32) +// outGlobal << lbl << " DWORD " << val << "\n"; +// else +// outGlobal << lbl << " QWORD " << val << "\n"; + +// auto r_xmm = alloc.allocateXMM(); + +// out << " " << (isF32 ? "movss" : "movsd") +// << " " << r_xmm << ", " +// << (isF32 ? "DWORD PTR " : "QWORD PTR ") +// << "[" << lbl << "]\n"; + +// noteType(r_xmm, node->scope->lookupType(isF32 ? "float" : "double")); + +// return r_xmm; +// } +// std::string CodeGenWindows::generateStringLiteral(std::unique_ptr node) { +// std::string lbl = "Lstr" + std::to_string(stringLabelCount++); +// outGlobal << lbl << " BYTE " +// << '"' << ((node->value.length() == 0) ? "\\0" : node->value) << '"' << ", 0\n"; +// auto r = alloc.allocate(); +// out << " lea " << r << ", [" << lbl << "]\n"; +// noteType(r, node->scope->lookupType("string")); + +// return r; +// } +// std::string CodeGenWindows::generateBooleanLiteral(std::unique_ptr node) { +// auto r = alloc.allocate(); +// out << " mov " << r << ", " << (node->value == "true" ? "1" : "0") << "\n"; +// noteType(r, node->scope->lookupType("boolean")); +// return r; +// } +// std::string CodeGenWindows::generateVariableAccess(std::unique_ptr node) { +// auto &scope = *node->scope; +// const std::string name = node->value; +// TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); +// uint64_t sz = ti.bits / 8; +// uint64_t bits = ti.bits; + +// std::string address = getVariableAddress(scope, name); // Use helper + +// if (ti.isFloat) { +// auto r_xmm = alloc.allocateXMM(); +// if (sz == 4) { +// out << " movss " << r_xmm << ", DWORD PTR " << address << "\n"; +// } else if (sz == 8) { +// out << " movsd " << r_xmm << ", QWORD PTR " << address << "\n"; +// } else { +// throw std::runtime_error("Unsupported float size: " + std::to_string(sz)); +// } +// noteType(r_xmm, ti); +// return r_xmm; +// } else { +// auto r64 = alloc.allocate(); +// auto r_adj = adjustReg(r64, bits); + +// std::string ptrSize; +// switch (sz) { +// case 8: +// ptrSize = "QWORD PTR "; +// break; +// case 4: +// ptrSize = "DWORD PTR "; +// break; +// case 2: +// ptrSize = "WORD PTR "; +// break; +// case 1: +// ptrSize = "BYTE PTR "; +// break; +// default: +// throw std::runtime_error("Unsupported integer size: " + std::to_string(sz)); +// } + +// out << " mov " << r_adj << ", " << ptrSize << address << "\n"; +// noteType(r_adj, ti); +// return r_adj; +// } +// } +// std::string CodeGenWindows::generateBinaryOperation(std::unique_ptr node) { +// std::string rl = emitExpression(std::move(node->children[0])); +// std::string rr = emitExpression(std::move(node->children[1])); + +// TypeInfo t1 = regType.at(rl); +// TypeInfo t2 = regType.at(rr); +// TypeInfo tr = TypeChecker::promoteType(t1, t2); +// uint64_t bits = tr.bits; +// const std::string &op = node->value; + +// // Cast operands to the promoted type +// rl = castValue(rl, t1, tr); +// rr = castValue(rr, t2, tr); + +// if (tr.isFloat) { +// const char *suf = (bits == 32 ? "ss" : "sd"); + +// if (op == "+") +// out << " add" << suf << " " << rl << ", " << rr << "\n"; +// else if (op == "-") +// out << " sub" << suf << " " << rl << ", " << rr << "\n"; +// else if (op == "*") +// out << " mul" << suf << " " << rl << ", " << rr << "\n"; +// else if (op == "/") +// out << " div" << suf << " " << rl << ", " << rr << "\n"; +// else if (assembly_comparison_operations.count(op)) { +// out << " ucomi" << suf << " " << rl << ", " << rr << "\n"; +// auto result = alloc.allocate(); +// auto result8 = adjustReg(result, 8); +// out << " " << assembly_comparison_operations.at(op) << " " << result8 << "\n"; +// out << " movzx " << result << ", " << result8 << "\n"; +// alloc.free(rl); +// alloc.free(rr); +// noteType(result, node->scope->lookupType("boolean")); +// return result; +// } else { +// throw std::runtime_error("Unsupported FP op: " + op); +// } + +// alloc.free(rr); +// noteType(rl, tr); +// return rl; +// } else { +// std::string dl = rl; +// std::string dr = rr; + +// if (op == "+") +// out << " add " << dl << ", " << dr << "\n"; +// else if (op == "-") +// out << " sub " << dl << ", " << dr << "\n"; +// else if (op == "*") +// out << " imul " << dl << ", " << dr << "\n"; +// else if (op == "/") { +// auto temp = alloc.allocate(); +// out << " mov " << temp << ", rax\n"; +// out << " mov rax, " << dl << "\n"; +// out << " cqo\n"; +// out << " idiv " << dr << "\n"; +// out << " mov " << dl << ", rax\n"; +// out << " mov rax, " << temp << "\n"; +// alloc.free(temp); +// alloc.free(rr); +// noteType(dl, tr); +// return dl; +// } else if (assembly_comparison_operations.count(op)) { +// out << " cmp " << dl << ", " << dr << "\n"; +// auto result = alloc.allocate(); +// auto result8 = adjustReg(result, 8); +// out << " " << assembly_comparison_operations.at(op) << " " << result8 << "\n"; +// out << " movzx " << result << ", " << result8 << "\n"; +// alloc.free(rr); +// noteType(result, node->scope->lookupType("boolean")); +// return result; +// } else { +// throw std::runtime_error("Unsupported integer op: " + op); +// } + +// alloc.free(rr); +// noteType(rl, tr); +// return rl; +// } +// } +// std::string CodeGenWindows::generateUnaryOperation(std::unique_ptr node) { +// const std::string &op = node->value; +// auto child = std::move(node->children[0]); + +// if (op == "!") { +// std::string r = emitExpression(std::move(child)); +// TypeInfo t = regType.at(r); + +// // Cast value to boolean-compatible type (e.g., integer of any size) +// TypeInfo boolType = node->scope->lookupType("boolean"); +// r = castValue(r, t, boolType); + +// out << " cmp " << r << ", 0\n"; + +// std::string result = alloc.allocate(); +// std::string result8 = adjustReg(result, 8); + +// out << " sete " << result8 << "\n"; +// out << " movzx " << result << ", " << result8 << "\n"; + +// alloc.free(r); +// noteType(result, boolType); +// return result; +// } else if (op == "++" || op == "--") { +// if (child->type != NodeType::VariableAccess) +// throw std::runtime_error(op + " can only be applied to variables"); + +// std::string var = child->value; +// auto &scp = *child->scope; +// TypeInfo ti = scp.lookupType(scp.lookupVariable(var).type); + +// if (ti.isFloat) +// throw std::runtime_error("Increment/Decrement not supported on float"); + +// std::string reg = generateVariableAccess(std::move(child)); +// reg = castValue(reg, regType.at(reg), ti); // Ensure reg is properly typed +// std::string adj = adjustReg(reg, ti.bits); + +// out << " " << (op == "++" ? "inc" : "dec") << " " << adj << "\n"; + +// std::string ptrSize; +// switch (ti.bits) { +// case 64: +// ptrSize = "QWORD PTR "; +// break; +// case 32: +// ptrSize = "DWORD PTR "; +// break; +// case 16: +// ptrSize = "WORD PTR "; +// break; +// case 8: +// ptrSize = "BYTE PTR "; +// break; +// default: +// throw std::runtime_error("Unsupported variable size: " + std::to_string(ti.bits)); +// } + +// std::string addr = getVariableAddress(scp, var); +// out << " mov " << ptrSize << addr << ", " << adj << "\n"; + +// noteType(reg, ti); +// return reg; +// } + +// throw std::runtime_error("Unknown Unary Operator: " + op); +// } +// std::string CodeGenWindows::emitExpression(std::unique_ptr node) { +// switch (node->type) { +// case NodeType::IntegerLiteral: +// return generateIntegerLiteral(std::move(node)); +// case NodeType::FloatLiteral: +// return generateFloatLiteral(std::move(node)); +// case NodeType::StringLiteral: +// return generateStringLiteral(std::move(node)); +// case NodeType::BooleanLiteral: +// return generateBooleanLiteral(std::move(node)); +// case NodeType::VariableAccess: +// return generateVariableAccess(std::move(node)); +// case NodeType::BinaryOp: +// return generateBinaryOperation(std::move(node)); +// case NodeType::UnaryOp: +// return generateUnaryOperation(std::move(node)); +// default: +// throw std::runtime_error("Unknown expression encountered."); +// } +// } +// void CodeGenWindows::emitPrologue(std::unique_ptr blockNode) { +// auto size = -blockNode->scope->stackOffset + 32; // +32 for shadow space +// out << " ; Block Starts (scope enter)\n"; +// out << " push rbp\n"; +// out << " mov rbp, rsp\n"; +// out << " sub rsp, " << size << "\n"; +// } +// void CodeGenWindows::emitEpilogue(std::unique_ptr blockNode) { +// out << " ; Block Ends (scope exit)\n"; +// out << " leave\n"; +// } +// void CodeGenWindows::generateStatement(std::unique_ptr statement) { +// switch (statement->type) { +// case NodeType::VariableReassignment: { +// generateVariableReassignment(std::move(statement)); +// break; +// } +// case NodeType::VariableDeclaration: { +// generateVariableDeclaration(std::move(statement)); +// break; +// } +// case NodeType::IfStatement: { +// generateIfStatement(std::move(statement)); +// break; +// } +// case NodeType::UnaryOp: { +// if (statement->value == "--" or statement->value == "++") { +// std::string reg = emitExpression(std::move(statement)); +// alloc.free(reg); +// } +// break; +// } +// case NodeType::BinaryOp: { +// std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. +// alloc.free(reg); +// break; +// } +// default: +// throw std::runtime_error("Unknown statement encountered."); +// } +// } +// void CodeGenWindows::generateVariableReassignment(std::unique_ptr statement) { +// auto &scope = *statement->scope; +// const std::string &name = statement->value; +// TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); +// uint64_t bits = ti.bits; +// std::string dest = getVariableAddress(scope, name); + +// std::string r = emitExpression(std::move(statement->children.back())); +// TypeInfo rt = regType.at(r); + +// r = castValue(r, rt, ti); + +// if (ti.isFloat) { +// std::string movInstr = (bits == 32 ? "movss" : "movsd"); +// std::string ptrPrefix = (bits == 32 ? "DWORD PTR " : "QWORD PTR "); +// out << " " << movInstr << " " +// << ptrPrefix << dest << ", " << r << "\n"; +// alloc.free(r); +// } else { +// std::string adj = adjustReg(r, bits); +// std::string ptrPrefix; +// switch (bits) { +// case 64: +// ptrPrefix = "QWORD PTR "; +// break; +// case 32: +// ptrPrefix = "DWORD PTR "; +// break; +// case 16: +// ptrPrefix = "WORD PTR "; +// break; +// case 8: +// ptrPrefix = "BYTE PTR "; +// break; +// default: +// throw std::runtime_error("Unsupported size in reassignment: " + std::to_string(bits)); +// } +// out << " mov " << ptrPrefix << dest << ", " << adj << "\n"; +// alloc.free(r); +// } +// } +// void CodeGenWindows::generateVariableDeclaration(std::unique_ptr statement) { +// auto &scope = *statement->scope; +// const std::string &name = statement->value; +// TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); +// uint64_t bits = ti.bits; + +// auto ptrPrefix = [&]() -> std::string { +// switch (bits) { +// case 64: +// return "QWORD PTR "; +// case 32: +// return "DWORD PTR "; +// case 16: +// return "WORD PTR "; +// case 8: +// return "BYTE PTR "; +// default: +// throw std::runtime_error("Unsupported size in declaration: " + std::to_string(bits)); +// } +// }; + +// if (statement->children.size() >= 2) { +// std::string r = emitExpression(std::move(statement->children.back())); +// TypeInfo rt = regType.at(r); +// r = castValue(r, rt, ti); + +// std::string addr = getVariableAddress(scope, name); +// if (ti.isFloat) { +// std::string movInstr = (bits == 32 ? "movss" : "movsd"); +// out << " " << movInstr << " " +// << ptrPrefix() << addr << ", " << r << "\n"; +// } else { +// std::string adj = adjustReg(r, bits); +// out << " mov " << ptrPrefix() << addr << ", " << adj << "\n"; +// } +// alloc.free(r); +// } else { +// std::string addr = getVariableAddress(scope, name); +// if (ti.isFloat) { +// std::string r_xmm = alloc.allocateXMM(); +// std::string movInstr = (bits == 32 ? "movss" : "movsd"); +// out << " pxor " << r_xmm << ", " << r_xmm << "\n" +// << " " << movInstr << " " +// << ptrPrefix() << addr << ", " << r_xmm << "\n"; +// alloc.free(r_xmm); +// } else { +// out << " xor rax, rax\n" +// << " mov " << ptrPrefix() << addr << ", rax\n"; +// } +// } +// } +// void CodeGenWindows::generateIfStatement(std::unique_ptr statement) { +// int id = blockLabelCount++; +// std::string endLbl = "Lend" + std::to_string(id); + +// std::vector elseLabels; +// ASTNode *branch = statement->getElseBranch(); +// while (branch) { +// elseLabels.push_back("Lelse" + std::to_string(blockLabelCount++)); +// branch = branch->getElseBranch(); +// } + +// size_t elseIdx = 0; + +// auto condReg = emitExpression(std::move(statement->children[0])); +// out << " cmp " << condReg << ", 0\n"; +// if (!elseLabels.empty()) +// out << " je " << elseLabels[elseIdx] << "\n"; +// else +// out << " je " << endLbl << "\n"; +// alloc.free(condReg); + +// auto ifBlock = std::move(statement->children[1]); +// auto stmts = std::move(ifBlock->children); +// emitPrologue(std::move(ifBlock)); +// for (auto &stmt : stmts) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// out << " jmp " << endLbl << "\n"; + +// branch = statement->getElseBranch(); +// while (branch) { +// out << elseLabels[elseIdx++] << ":\n"; + +// if (branch->type == NodeType::ElseIfStatement) { +// auto r = emitExpression(std::move(branch->children[0])); +// out << " cmp " << r << ", 0\n"; +// if (elseIdx < elseLabels.size()) +// out << " je " << elseLabels[elseIdx] << "\n"; +// else +// out << " je " << endLbl << "\n"; +// alloc.free(r); + +// auto elifBlock = std::move(branch->children[1]); +// auto stmts = std::move(elifBlock->children); +// emitPrologue(std::move(elifBlock)); +// for (auto &stmt : stmts) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// out << " jmp " << endLbl << "\n"; +// } else if (branch->type == NodeType::ElseStatement) { +// auto elseBlock = std::move(branch->children[0]); +// auto stmts = std::move(elseBlock->children); +// emitPrologue(std::move(elseBlock)); +// for (auto &stmt : stmts) +// generateStatement(std::move(stmt)); +// emitEpilogue(); +// break; +// } else { +// throw std::runtime_error("Unexpected node type in else chain"); +// } + +// branch = branch->getElseBranch(); +// } + +// out << endLbl << ":\n\n"; +// } +// void CodeGenWindows::generate(std::unique_ptr program) { +// std::vector globals; +// for (auto &statement : program->children) +// if (statement->type == NodeType::VariableDeclaration) +// globals.push_back(statement.get()); + +// outGlobal << ".data\n\n"; + +// for (auto &g : globals) { +// TypeInfo info = g->scope->lookupType(g->children[0]->value); +// switch (info.bits / 8) { +// case 8: +// outGlobal << g->value << " QWORD 0\n"; +// break; +// case 4: +// outGlobal << g->value << " DWORD 0\n"; +// break; +// case 2: +// outGlobal << g->value << " WORD 0\n"; +// break; +// case 1: +// outGlobal << g->value << " BYTE 0\n"; +// break; +// default: +// throw std::runtime_error("Unsupported global size"); +// } +// } + +// outGlobal << "\n.const\n"; + +// out << ".code\n"; +// out << "main PROC\n"; + +// for (std::unique_ptr &statement : program.get()->children) { +// generateStatement(std::move(statement)); +// } + +// out << " xor eax, eax\n"; +// out << " ret\n"; + +// out << "main ENDP\n"; + +// outfinal << outGlobal.str() +// << "\n; ============== Globals End Here ==============\n" +// << out.str() << "END\n\n"; +// } +// std::string CodeGenWindows::generateFunctionCall(std::unique_ptr node) { +// return ""; +// } +// void CodeGenWindows::generateFunctionDeclaration(std::unique_ptr node) {} +// void CodeGenWindows::generateExternFunctionDeclaration(std::unique_ptr node) {} +// } // namespace zlang \ No newline at end of file diff --git a/src/codegen/RegisterAllocator.cpp b/src/codegen/RegisterAllocator.cpp index 5d1c627..8857dc4 100644 --- a/src/codegen/RegisterAllocator.cpp +++ b/src/codegen/RegisterAllocator.cpp @@ -1,41 +1,50 @@ #include "all.hpp" -namespace zlang -{ - RegisterAllocator::RegisterAllocator(std::vector regs) - : available(std::move(regs)) - { +namespace zlang { + RegisterAllocator::RegisterAllocator(std::vector regs, std::vector XMMregs, std::vector argumentRegs, std::vector argumentXMMRegs) + : available(std::move(regs)), availableXMM(std::move(XMMregs)), availableArgumentRegs(std::move(argumentRegs)), availableArgumentRegsXMM(std::move(argumentXMMRegs)) { } - RegisterAllocator RegisterAllocator::forSysV() - { - // Exclude %rsp, %rbp; use caller-saved first - return RegisterAllocator({"rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11"}); + RegisterAllocator RegisterAllocator::forSysV() { + return RegisterAllocator( + SCRATCH_GPR_LINUX, + SCRATCH_XMM_LINUX, + ARG_GPR_LINUX, + ARG_XMM_LINUX); } - RegisterAllocator RegisterAllocator::forMSVC() - { - // Windows: volatile registers - return RegisterAllocator({"rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}); + RegisterAllocator RegisterAllocator::forMSVC() { + return RegisterAllocator( + SCRATCH_GPR_MSVC, + SCRATCH_XMM_MSVC, + ARG_GPR_MSVC, + ARG_XMM_MSVC); } - std::string RegisterAllocator::allocate() - { - if (available.empty()) - throw std::runtime_error("RegisterAllocator: no registers available"); + std::string RegisterAllocator::allocate() { + if (available.empty()) { + throw std::runtime_error("No free general-purpose registers available"); + } std::string reg = available.back(); available.pop_back(); inUse.insert(reg); + lruRegs.push_back(reg); return reg; } - std::string RegisterAllocator::getBaseReg(const std::string ®) - { + bool RegisterAllocator::isInUse(std::string reg) { + return inUse.find(reg) != inUse.end(); + } + + bool RegisterAllocator::isInUseXMM(std::string reg) { + return inUseXMM.find(reg) != inUseXMM.end(); + } + + std::string RegisterAllocator::getBaseReg(const std::string ®) { static const std::unordered_map reg_to_base = { {"rax", "rax"}, {"eax", "rax"}, {"ax", "rax"}, {"al", "rax"}, {"rbx", "rbx"}, {"ebx", "rbx"}, {"bx", "rbx"}, {"bl", "rbx"}, {"rcx", "rcx"}, {"ecx", "rcx"}, {"cx", "rcx"}, {"cl", "rcx"}, {"rdx", "rdx"}, {"edx", "rdx"}, {"dx", "rdx"}, {"dl", "rdx"}, {"rsi", "rsi"}, {"esi", "rsi"}, {"si", "rsi"}, {"sil", "rsi"}, {"rdi", "rdi"}, {"edi", "rdi"}, {"di", "rdi"}, {"dil", "rdi"}, {"rbp", "rbp"}, {"ebp", "rbp"}, {"bp", "rbp"}, {"bpl", "rbp"}, {"rsp", "rsp"}, {"esp", "rsp"}, {"sp", "rsp"}, {"spl", "rsp"}, {"r8", "r8"}, {"r8d", "r8"}, {"r8w", "r8"}, {"r8b", "r8"}, {"r9", "r9"}, {"r9d", "r9"}, {"r9w", "r9"}, {"r9b", "r9"}, {"r10", "r10"}, {"r10d", "r10"}, {"r10w", "r10"}, {"r10b", "r10"}, {"r11", "r11"}, {"r11d", "r11"}, {"r11w", "r11"}, {"r11b", "r11"}, {"r12", "r12"}, {"r12d", "r12"}, {"r12w", "r12"}, {"r12b", "r12"}, {"r13", "r13"}, {"r13d", "r13"}, {"r13w", "r13"}, {"r13b", "r13"}, {"r14", "r14"}, {"r14d", "r14"}, {"r14w", "r14"}, {"r14b", "r14"}, {"r15", "r15"}, {"r15d", "r15"}, {"r15w", "r15"}, {"r15b", "r15"}}; - if (reg.starts_with("xmm")) - { + if (reg.starts_with("xmm")) { return reg; } @@ -45,51 +54,158 @@ namespace zlang return it->second; } - void RegisterAllocator::free(const std::string ®) - { + void RegisterAllocator::free(const std::string ®) { auto it = inUse.find(getBaseReg(reg)); - if (it == inUse.end()) - { - if (freeXMM(reg)) - { + if (it == inUse.end()) { + if (freeXMM(getBaseReg(reg))) { return; } std::cout << "Free Normal" << std::endl; throw std::runtime_error("RegisterAllocator: attempted to free unallocated register '" + reg + "'"); } inUse.erase(it); + lruRegs.remove(reg); available.push_back(getBaseReg(reg)); } - void RegisterAllocator::reset() - { + void RegisterAllocator::reset() { // Move all inUse back to available for (auto ® : inUse) available.push_back(reg); inUse.clear(); } - std::string RegisterAllocator::allocateXMM() - { - if (availableXMM.empty()) - { - std::cout << "Free XMM" << std::endl; - throw std::runtime_error("RegisterAllocator: no XMM registers available"); + std::string RegisterAllocator::allocateXMM() { + if (availableXMM.empty()) { + throw std::runtime_error("No free XMM registers available"); } std::string reg = availableXMM.back(); availableXMM.pop_back(); inUseXMM.insert(reg); + lruXMMRegs.push_back(reg); return reg; } - bool RegisterAllocator::freeXMM(const std::string ®) - { + bool RegisterAllocator::freeXMM(const std::string ®) { auto it = inUseXMM.find(reg); if (it == inUseXMM.end()) return false; inUseXMM.erase(it); availableXMM.push_back(reg); + lruXMMRegs.remove(reg); + return true; + } + + std::string RegisterAllocator::allocateArgument(uint8_t position) { + std::string reg = availableArgumentRegs[position]; + inUseArgument.insert(reg); + return reg; + } + std::string RegisterAllocator::allocateArgumentXMM(uint8_t position) { + std::string reg = availableArgumentRegsXMM[position]; + inUseArgumentXMM.insert(reg); + return reg; + } + void RegisterAllocator::freeArgument(const std::string ®) { + auto it = inUseArgument.find(getBaseReg(reg)); + if (it == inUseArgument.end()) { + if (freeArgumentXMM(getBaseReg(reg))) { + return; + } + throw std::runtime_error("RegisterAllocator: attempted to free unallocated register '" + reg + "'"); + } + inUseArgument.erase(it); + } + bool RegisterAllocator::freeArgumentXMM(const std::string ®) { + auto it = inUseArgumentXMM.find(getBaseReg(reg)); + if (it == inUseArgumentXMM.end()) { + return false; + } + inUseArgumentXMM.erase(it); return true; } + bool RegisterAllocator::isInUseArgument(const std::string ®) const { + return inUseArgument.find(reg) != inUseArgument.end(); + } + bool RegisterAllocator::isInUseArgumentXMM(const std::string ®) const { + return inUseArgumentXMM.find(reg) != inUseArgumentXMM.end(); + } + std::string RegisterAllocator::pickVictimXMM() { + if (lruXMMRegs.empty()) { + throw std::runtime_error("No victim available for XMM registers"); + } + return lruXMMRegs.front(); + } + std::string RegisterAllocator::pickVictim() { + if (lruRegs.empty()) { + throw std::runtime_error("No victim available for general-purpose registers"); + } + return lruRegs.front(); + } + + void RegisterAllocator::markSpilledXMM(const std::string ®, const std::string &spillSlot) { + spilledRegs[reg] = {spillSlot, true}; + } + + void RegisterAllocator::markSpilled(const std::string ®, const std::string &spillSlot) { + spilledRegs[reg] = {spillSlot, false}; + } + + bool RegisterAllocator::isSpilled(const std::string ®) const { + return spilledRegs.find(reg) != spilledRegs.end(); + } + + std::string RegisterAllocator::spillSlotFor(const std::string ®) const { + auto it = spilledRegs.find(reg); + if (it == spilledRegs.end()) + throw std::runtime_error("Register not spilled: " + reg); + return it->second.spillSlot; + } + + void RegisterAllocator::unSpillXMM(const std::string ®, zlang::CodegenOutputFormat format, std::ostream &out) { + auto it = spilledRegs.find(reg); + if (it == spilledRegs.end()) + throw std::runtime_error("XMM reg not spilled: " + reg); + + emitSpillRestore(reg, it->second.spillSlot, true, format, out); + spilledRegs.erase(it); + } -} // namespace zlang \ No newline at end of file + void RegisterAllocator::unSpill(const std::string ®, zlang::CodegenOutputFormat format, std::ostream &out) { + auto it = spilledRegs.find(reg); + if (it == spilledRegs.end()) + throw std::runtime_error("GPR reg not spilled: " + reg); + + emitSpillRestore(reg, it->second.spillSlot, false, format, out); + spilledRegs.erase(it); + } + + void RegisterAllocator::emitSpillRestore(const std::string ®, const std::string &slot, bool isXMM, zlang::CodegenOutputFormat format, std::ostream &out) { + if (format == CodegenOutputFormat::X86_64_LINUX) { + if (isXMM) + out << " movdqu " << slot << ", %" << reg << "\n"; + else + out << " mov " << slot << ", %" << reg << "\n"; + } + if (format == CodegenOutputFormat::X86_64_MSWIN) { + if (isXMM) + out << " movdqu " << reg << ", " << slot << "\n"; + else + out << " mov " << reg << ", " << slot << "\n"; + } + } + + void RegisterAllocator::touch(const std::string ®) { + if (inUse.count(reg)) { + lruRegs.remove(reg); + lruRegs.push_back(reg); + } + } + + void RegisterAllocator::touchXMM(const std::string ®) { + if (inUseXMM.count(reg)) { + lruXMMRegs.remove(reg); + lruXMMRegs.push_back(reg); + } + } +} // namespace zlang \ No newline at end of file diff --git a/src/lexer/Lexer.cpp b/src/lexer/Lexer.cpp index c33ba91..c40ec11 100644 --- a/src/lexer/Lexer.cpp +++ b/src/lexer/Lexer.cpp @@ -118,6 +118,15 @@ namespace zlang if (text == "else") return {Token::Kind::Else, text, startLine, startCol}; + if (text == "fn") + return {Token::Kind::Function, text, startLine, startCol}; + + if (text == "extern") + return {Token::Kind::Symbol, text, startLine, startCol}; + + if (text == "return") + return {Token::Kind::Return, text, startLine, startCol}; + return Token{Token::Kind::Identifier, text, startLine, startCol}; } @@ -168,6 +177,13 @@ namespace zlang char c = advance(); std::string text(1, c); char next = peekChar(); + + if (c == '-' && next == '>') + { + text.push_back(advance()); + return Token{Token::Kind::Arrow, text, startLine, startCol}; + } + // Multi-char operators if ((c == '&' && next == '&') || (c == '|' && next == '|') || @@ -199,6 +215,8 @@ namespace zlang return Token{Token::Kind::LeftParen, text, startLine, startCol}; case ')': return Token{Token::Kind::RightParen, text, startLine, startCol}; + case ',': + return Token{Token::Kind::Comma, text, startLine, startCol}; case '+': case '-': case '*': @@ -213,5 +231,4 @@ namespace zlang return Token{Token::Kind::Symbol, text, startLine, startCol}; } } - } // namespace zlang \ No newline at end of file diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index 4a33a73..f5b4634 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -1,68 +1,201 @@ #include "all.hpp" -namespace zlang -{ - Parser::Parser(Lexer &lex) : lexer(lex) - { - currentScope = std::make_shared(nullptr, "GLOBAL SCOPE"); - // TODO: This is controversial, lets make something in the near future that gets these (size_t, integer, float, double, etc) sizes dynamically - currentScope->defineType("boolean", TypeInfo{8, 1, false, false}); - currentScope->defineType("string", TypeInfo{64, 8, false, false}); // Assuming pointer to heap - currentScope->defineType("size_t", TypeInfo{64, 8, false, false}); - currentScope->defineType("integer", TypeInfo{64, 8, false, true}); // Default int type +namespace zlang { + Parser::Parser(Lexer& lex) : lexer(lex) { + currentScope = std::make_shared("GLOBAL__SCOPE", nullptr); + // TODO: This is controversial, lets make something in the near future that + // gets these (size_t, integer, float, double, etc) sizes dynamically + currentScope->defineType("boolean", TypeInfo{.bits = 8, + .align = 1, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = true, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "boolean"}); + currentScope->defineType("none", TypeInfo{.bits = 0, + .align = 0, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "none"}); + currentScope->defineType("string", TypeInfo{.bits = 64, + .align = 8, + .isFloat = false, + .isSigned = false, + .isString = true, + .isBoolean = false, + .isPointer = true, + .isUserDefined = false, + .isFunction = false, + .name = "string"}); + currentScope->defineType("size_t", TypeInfo{.bits = 64, + .align = 8, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "size_t"}); + currentScope->defineType("integer", TypeInfo{.bits = 64, + .align = 8, + .isFloat = false, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "integer"}); // Unsigned integers - currentScope->defineType("uint8_t", TypeInfo{8, 1, false, false}); - currentScope->defineType("uint16_t", TypeInfo{16, 2, false, false}); - currentScope->defineType("uint32_t", TypeInfo{32, 4, false, false}); - currentScope->defineType("uint64_t", TypeInfo{64, 8, false, false}); + currentScope->defineType("uint8_t", TypeInfo{.bits = 8, + .align = 1, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "uint8_t"}); + + currentScope->defineType("uint16_t", TypeInfo{.bits = 16, + .align = 2, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "uint16_t"}); + + currentScope->defineType("uint32_t", TypeInfo{.bits = 32, + .align = 4, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "uint32_t"}); + + currentScope->defineType("uint64_t", TypeInfo{.bits = 64, + .align = 8, + .isFloat = false, + .isSigned = false, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "uint64_t"}); // Signed integers - currentScope->defineType("int8_t", TypeInfo{8, 1, false, true}); - currentScope->defineType("int16_t", TypeInfo{16, 2, false, true}); - currentScope->defineType("int32_t", TypeInfo{32, 4, false, true}); - currentScope->defineType("int64_t", TypeInfo{64, 8, false, true}); + currentScope->defineType("int8_t", TypeInfo{.bits = 8, + .align = 1, + .isFloat = false, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "int8_t"}); + + currentScope->defineType("int16_t", TypeInfo{.bits = 16, + .align = 2, + .isFloat = false, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "int16_t"}); + + currentScope->defineType("int32_t", TypeInfo{.bits = 32, + .align = 4, + .isFloat = false, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "int32_t"}); + + currentScope->defineType("int64_t", TypeInfo{.bits = 64, + .align = 8, + .isFloat = false, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "int64_t"}); // Floating-point types - currentScope->defineType("float", TypeInfo{32, 4, true, true}); - currentScope->defineType("double", TypeInfo{64, 8, true, true}); + currentScope->defineType("float", TypeInfo{.bits = 32, + .align = 8, + .isFloat = true, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "float"}); + + currentScope->defineType("double", TypeInfo{.bits = 64, + .align = 16, + .isFloat = true, + .isSigned = true, + .isString = false, + .isBoolean = false, + .isPointer = false, + .isUserDefined = false, + .isFunction = false, + .name = "double"}); shouldTypecheck = true; advance(); } - void Parser::advance() - { - currentToken = lexer.nextToken(); - } + void Parser::advance() { currentToken = lexer.nextToken(); } - bool Parser::match(Token::Token::Kind kind) - { - if (currentToken.kind == kind) - { + bool Parser::match(Token::Token::Kind kind) { + if (currentToken.kind == kind) { advance(); return true; } return false; } - void Parser::expect(Token::Token::Kind kind, const std::string &errMsg) - { - if (!match(kind)) - { + void Parser::expect(Token::Token::Kind kind, const std::string& errMsg) { + if (!match(kind)) { logError({ErrorType::Syntax, - errMsg + - " at line " + std::to_string(currentToken.line) + + errMsg + " at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column)}); shouldTypecheck = false; + exit(0); return; } } - std::unique_ptr Parser::parse() - { + std::unique_ptr Parser::parse() { auto program = ASTNode::makeProgramNode(currentScope); - while (currentToken.kind != Token::Token::Kind::EndOfFile) - { + while (currentToken.kind != Token::Token::Kind::EndOfFile) { auto stmt = parseStatement(); if (stmt) program->addChild(std::move(stmt)); @@ -70,119 +203,270 @@ namespace zlang return program; } - std::unique_ptr Parser::parseStatement() - { + std::unique_ptr Parser::parseStatement() { + if (currentToken.kind == Token::Kind::Return) { + if (currentScope->isGlobalScope()) { + logError({ErrorType::Syntax, + "Unexpected keyword '" + currentToken.text + + "' outside any function at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."}); + shouldTypecheck = false; + advance(); + return nullptr; + } else { + advance(); + auto node = std::make_unique(NodeType::ReturnStatement, "", + currentScope); + if (currentToken.kind == Token::Kind::SemiColon) { + auto ret = std::make_unique(NodeType::Symbol, "none", + currentScope); + node->addChild(std::move(ret)); + } else { + node->addChild(parseExpression(true)); + } + return node; + } + } + if ((currentToken.kind == Token::Kind::Symbol and + currentToken.text == "extern") || + currentToken.kind == Token::Kind::Function) + return parseFunctionDeclaration(); if (match(Token::Token::Kind::Let)) return parseVariableDeclaration(); - if (currentToken.kind == Token::Token::Kind::Identifier) + if (currentToken.kind == Token::Token::Kind::Identifier and lexer.peek().kind != Token::Kind::LeftParen) return parseVariableReassignment(); - if (currentToken.kind == Token::Kind::If || currentToken.kind == Token::Kind::ElseIf || currentToken.kind == Token::Kind::Else) + if (currentToken.kind == Token::Kind::If || + currentToken.kind == Token::Kind::ElseIf || + currentToken.kind == Token::Kind::Else) return parseConditionals(); - if (currentToken.kind == Token::Kind::Symbol || currentToken.kind == Token::Kind::Identifier) + if (currentToken.kind == Token::Kind::Symbol || + currentToken.kind == Token::Kind::Identifier) return parseExpression(true); logError({ErrorType::Syntax, - "Unexpected token '" + currentToken.text + - "' at line " + std::to_string(currentToken.line) + - ", column " + std::to_string(currentToken.column) + "."}); + "Unexpected token '" + currentToken.text + "' at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."}); shouldTypecheck = false; return nullptr; } - std::unique_ptr Parser::parseBlock() - { - expect(Token::Kind::LeftBrace, "Expected a '{' to open a scope, at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); - enterScope("blockNumber" + std::to_string(blockNumber++)); - auto blockNode = std::make_unique(NodeType::Program, "", currentScope); - while (!match(Token::Kind::RightBrace) && currentToken.kind != Token::Kind::EndOfFile) - { - if (currentToken.kind == Token::Kind::EndOfFile) - { + std::unique_ptr Parser::parseFunctionDeclaration() { + bool isExtern = false; + if (currentToken.kind == Token::Kind::Symbol && + currentToken.text == "extern") { + isExtern = true; + advance(); + } + + // Expect 'fn' keyword + expect(Token::Kind::Function, + "Expected 'fn' to start function declaration"); + + // Function name + std::string name = currentToken.text; + expect(Token::Kind::Identifier, "Expected function name after 'fn'"); + + // Parameter list + expect(Token::Kind::LeftParen, "Expected '(' after function name"); + + std::vector params; + if (currentToken.kind != Token::Kind::RightParen) { + // Parse first parameter + std::string paramName = currentToken.text; + expect(Token::Kind::Identifier, "Expected parameter name"); + + expect(Token::Kind::Colon, "Expected ':' after parameter name"); + + if (currentToken.kind != Token::Kind::Identifier) { + logError(Error{ErrorType::Syntax, + "Expected data type after ':' at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column)}); + shouldTypecheck = false; + return nullptr; + } + std::string typeName = currentToken.text; + advance(); + + params.push_back(ParamInfo{.name = paramName, .type = typeName}); + + while (match(Token::Kind::Comma)) { + std::string nextName = currentToken.text; + expect(Token::Kind::Identifier, + "Expected parameter name after ','"); + + expect(Token::Kind::Colon, "Expected ':' after parameter name"); + + if (currentToken.kind != Token::Kind::Identifier) { + logError(Error{ErrorType::Syntax, + "Expected data type after ':' at line " + + std::to_string(currentToken.line) + + ", column " + + std::to_string(currentToken.column)}); + shouldTypecheck = false; + return nullptr; + } + std::string nextType = currentToken.text; + advance(); + + params.push_back(ParamInfo{.name = nextName, .type = nextType}); + } + } + expect(Token::Kind::RightParen, "Expected ')' after parameters"); + + // Return type (optional) + std::string returnTypeName = "none"; + if (match(Token::Kind::Arrow)) { + if (currentToken.kind != Token::Kind::Identifier) { + logError( + Error{ErrorType::Syntax, + "Expected return type identifier after '->' at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column)}); + shouldTypecheck = false; + return nullptr; + } + returnTypeName = currentToken.text; + advance(); + } + + if (isExtern) { + expect(Token::Kind::SemiColon, + "Expected ';' after extern function declaration"); + return ASTNode::makeExternFunctionDeclaration(name, currentScope, + params, returnTypeName); + } + enterScope(name, true); + auto body = parseBlock(); + exitScope(); + if (name == "main") { + if (currentScope->parent() == nullptr) { + // Ok go ahead + } else { + throw std::runtime_error("Main should be in global scope"); + } + } + return ASTNode::makeFunctionDeclaration(name, currentScope, params, + returnTypeName, std::move(body)); + } + + std::unique_ptr Parser::parseBlock() { + expect(Token::Kind::LeftBrace, + "Expected a '{' to open a scope, at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."); + auto blockNode = + std::make_unique(NodeType::Program, "", currentScope); + while (!match(Token::Kind::RightBrace) && + currentToken.kind != Token::Kind::EndOfFile) { + if (currentToken.kind == Token::Kind::EndOfFile) { logError({ErrorType::Syntax, - "Expected token '}' found 'End Of File' at line " + std::to_string(currentToken.line) + - ", column " + std::to_string(currentToken.column) + "."}); + "Expected token '}' found 'End Of File' at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."}); shouldTypecheck = false; return nullptr; } blockNode->addChild(parseStatement()); } - exitScope(); return blockNode; } - std::unique_ptr Parser::parseConditionals() - { - if (!match(Token::Kind::If)) - { + std::unique_ptr Parser::parseConditionals() { + if (!match(Token::Kind::If)) { logError({ErrorType::Syntax, "Expected 'if'"}); shouldTypecheck = false; exit(1); } - expect(Token::Kind::LeftParen, "Expected a '(' after if block at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); + expect(Token::Kind::LeftParen, + "Expected a '(' after if block at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."); auto condition = parseExpression(); - expect(Token::Kind::RightParen, "Expected a ')' after condition at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); + expect(Token::Kind::RightParen, + "Expected a ')' after condition at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."); + enterScope("Block__" + std::to_string(++blockNumber), false); auto ifBlock = parseBlock(); - auto root = ASTNode::makeIfStatement(std::move(condition), std::move(ifBlock), currentScope); - ASTNode *current = root.get(); - - while (match(Token::Kind::ElseIf)) - { - expect(Token::Kind::LeftParen, "Expected a '(' after if block at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); + exitScope(); + auto root = ASTNode::makeIfStatement(std::move(condition), + std::move(ifBlock), currentScope); + ASTNode* current = root.get(); + + while (match(Token::Kind::ElseIf)) { + expect(Token::Kind::LeftParen, + "Expected a '(' after if block at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."); auto condition = parseExpression(); - expect(Token::Kind::RightParen, "Expected a ')' after condition at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); + expect(Token::Kind::RightParen, + "Expected a ')' after condition at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column) + "."); + enterScope("Block__" + std::to_string(++blockNumber), false); auto elifBlock = parseBlock(); - auto elifNode = ASTNode::makeElseIfStatement(std::move(condition), std::move(elifBlock), currentScope); + exitScope(); + auto elifNode = ASTNode::makeElseIfStatement( + std::move(condition), std::move(elifBlock), currentScope); current->setElseBranch(std::move(elifNode)); current = current->getElseBranch(); } // optional 'else' - if (match(Token::Kind::Else)) - { + if (match(Token::Kind::Else)) { + enterScope("Block__" + std::to_string(++blockNumber), false); auto elseBlock = parseBlock(); - auto elseNode = ASTNode::makeElseStatement(std::move(elseBlock), currentScope); + exitScope(); + auto elseNode = + ASTNode::makeElseStatement(std::move(elseBlock), currentScope); current->setElseBranch(std::move(elseNode)); } return root; } - std::unique_ptr Parser::parseVariableDeclaration() - { + std::unique_ptr Parser::parseVariableDeclaration() { if (currentToken.kind != Token::Token::Kind::Identifier) - expect(Token::Token::Kind::Identifier, "Expected variable name after 'let'"); + expect(Token::Token::Kind::Identifier, + "Expected variable name after 'let'"); std::string name = currentToken.text; advance(); std::unique_ptr typeNode, initNode; - if (match(Token::Kind::Colon)) - { + if (match(Token::Kind::Colon)) { typeNode = ASTNode::makeSymbolNode(currentToken.text, currentScope); advance(); + } else { + // TODO: Handle type deduction but its a far far task. + logError( + Error( + ErrorType::Generic, "Expected ':' followed by a type name for declaring variables at line " + std::to_string(currentToken.line) + ", column: " + std::to_string(currentToken.column))); + shouldTypecheck = false; + advance(); + return nullptr; } - if (match(Token::Kind::Equal)) - { + if (match(Token::Kind::Equal)) { initNode = parseExpression(); } - if (initNode == nullptr) - { + if (initNode == nullptr) { TypeInfo ty = currentScope->lookupType(typeNode->value); - if (zlang::numeric_types.find(typeNode->value) != zlang::numeric_types.end()) - { - if (ty.isFloat) - { + if (zlang::numeric_types.find(typeNode->value) != + zlang::numeric_types.end()) { + if (ty.isFloat) { if (ty.bits == 32) - initNode = ASTNode::makeFloatLiteralNode("0.0F", currentScope); + initNode = + ASTNode::makeFloatLiteralNode("0.0F", currentScope); else - initNode = ASTNode::makeFloatLiteralNode("0.0", currentScope); - } - else + initNode = + ASTNode::makeFloatLiteralNode("0.0", currentScope); + } else initNode = ASTNode::makeIntegerLiteralNode("0", currentScope); - } - else - { + } else { if (typeNode->value == "boolean") initNode = ASTNode::makeBooleanLiteralNode(true, currentScope); @@ -191,99 +475,105 @@ namespace zlang } } expect(Token::Kind::SemiColon, "Expected ';' after declaration"); - std::optional> result = ASTNode::makeVariableDeclarationNode(name, std::move(typeNode), std::move(initNode), currentScope); - if (!result.has_value()) - { - logError(Error(ErrorType::Generic, "Variable '" + name + "' already defined in current scope.")); + std::optional> result = + ASTNode::makeVariableDeclarationNode(name, std::move(typeNode), + std::move(initNode), currentScope); + if (!result.has_value()) { + logError( + Error(ErrorType::Generic, + "Variable '" + name + "' already defined in current scope.")); shouldTypecheck = false; return nullptr; - } - else - { + } else { return std::move(result.value()); } } - std::unique_ptr Parser::parseVariableReassignment() - { + std::unique_ptr Parser::parseVariableReassignment() { std::string name = currentToken.text; advance(); expect(Token::Token::Kind::Equal, "Expected '=' for variable reassignment"); auto expr = parseExpression(); expect(Token::Token::Kind::SemiColon, "Expected ';' after reassignment"); - return ASTNode::makeVariableReassignmentNode(name, std::move(expr), currentScope); + return ASTNode::makeVariableReassignmentNode(name, std::move(expr), + currentScope); } - std::unique_ptr Parser::parseExpression(bool expect_exclaim) - { + std::unique_ptr Parser::parseExpression(bool expect_exclaim) { auto lhs = parseUnary(); auto ans = parseBinaryRHS(0, std::move(lhs)); - if (expect_exclaim) - { - expect(Token::Kind::SemiColon, "';' Expected at the end of statement at line " + std::to_string(currentToken.line) + ", column " + std::to_string(currentToken.column) + "."); + if (expect_exclaim) { + expect(Token::Kind::SemiColon, "';' Expected at the end of statement."); } return ans; } - std::unique_ptr Parser::parsePrimary() - { - if (currentToken.kind == Token::Kind::BoolLiteral) - { + std::unique_ptr Parser::parsePrimary() { + if (currentToken.kind == Token::Kind::BoolLiteral) { bool val = (currentToken.text == "true"); advance(); return ASTNode::makeBooleanLiteralNode(val, currentScope); } - if (currentToken.kind == Token::Token::Kind::StringLiteral) - { + if (currentToken.kind == Token::Token::Kind::StringLiteral) { std::string s = currentToken.text; advance(); return ASTNode::makeStringLiteralNode(s, currentScope); } - if (currentToken.kind == Token::Token::Kind::FloatLiteral) - { + if (currentToken.kind == Token::Token::Kind::FloatLiteral) { std::string f = currentToken.text; advance(); return ASTNode::makeFloatLiteralNode(f, currentScope); } - if (currentToken.kind == Token::Token::Kind::IntegerLiteral) - { + if (currentToken.kind == Token::Token::Kind::IntegerLiteral) { std::string val = currentToken.text; advance(); return ASTNode::makeIntegerLiteralNode(val, currentScope); } - if (currentToken.kind == Token::Token::Kind::Identifier) - { + if (currentToken.kind == Token::Token::Kind::Identifier) { std::string name = currentToken.text; advance(); - // 2) make the VariableAccess node - auto node = ASTNode::makeVariableAccessNode(name, currentScope); + if (currentToken.kind == Token::Kind::LeftParen) { + advance(); + std::vector> arguments; + + if (currentToken.kind != Token::Kind::RightParen) { + while (true) { + arguments.push_back(parseExpression(false)); + if (currentToken.kind == Token::Kind::Comma) { + advance(); + } else { + break; + } + } + } + expect(Token::Kind::RightParen, "Expected ')' after function arguments"); + return ASTNode::makeFunctionCall(name, std::move(arguments), currentScope); + } - // 3) check if the *current* token is ++ or -- (postfix) + auto node = ASTNode::makeVariableAccessNode(name, currentScope); if (currentToken.kind == Token::Kind::Symbol && - (currentToken.text == "++" || currentToken.text == "--")) - { - std::string op = currentToken.text; // "++" or "--" - advance(); // consume the ++/-- + (currentToken.text == "++" || currentToken.text == "--")) { + std::string op = currentToken.text; + advance(); node = ASTNode::makeUnaryOp(op, std::move(node), currentScope); } return node; } logError({ErrorType::Syntax, - "Expected expression, got '" + currentToken.text + - "' at line " + std::to_string(currentToken.line) + - ", column " + std::to_string(currentToken.column)}); + "Expected expression, got '" + currentToken.text + "' at line " + + std::to_string(currentToken.line) + ", column " + + std::to_string(currentToken.column)}); shouldTypecheck = false; return nullptr; } - std::unique_ptr Parser::parseUnary() - { + std::unique_ptr Parser::parseUnary() { if (currentToken.kind == Token::Kind::Symbol && - (currentToken.text == "++" || currentToken.text == "--" || currentToken.text == "!")) - { + (currentToken.text == "++" || currentToken.text == "--" || + currentToken.text == "!")) { std::string op = currentToken.text; advance(); auto operand = parseUnary(); @@ -293,12 +583,12 @@ namespace zlang } std::unique_ptr Parser::parseBinaryRHS(int exprPrec, - std::unique_ptr lhs) - { - while (true) - { + std::unique_ptr lhs) { + while (true) { // Only consider tokens that can represent binary operators - if (!(currentToken.kind == Token::Kind::Symbol || (currentToken.kind == Token::Kind::Equal and currentToken.text == "=="))) + if (!(currentToken.kind == Token::Kind::Symbol || + (currentToken.kind == Token::Kind::Equal and + currentToken.text == "=="))) break; std::string op = currentToken.text; @@ -316,13 +606,13 @@ namespace zlang if (tokPrec < nextPrec) rhs = parseBinaryRHS(tokPrec + 1, std::move(rhs)); - lhs = ASTNode::makeBinaryOp(op, std::move(lhs), std::move(rhs), currentScope); + lhs = ASTNode::makeBinaryOp(op, std::move(lhs), std::move(rhs), + currentScope); } return lhs; } - int Parser::getPrecedence(const std::string &op) const - { + int Parser::getPrecedence(const std::string& op) const { static std::map prec = { {"||", 1}, {"&&", 2}, {"==", 3}, {"!=", 3}, {"<", 4}, {">", 4}, {"<=", 4}, {">=", 4}, {"+", 5}, {"-", 5}, {"*", 6}, {"/", 6}}; @@ -330,14 +620,20 @@ namespace zlang return it == prec.end() ? -1 : it->second; } - void Parser::enterScope(std::string name) - { - currentScope = std::make_shared(currentScope, name); + void Parser::enterScope(const std::string& name, bool isFunction) { + if (isFunction) { + currentScope = std::make_shared(name, currentScope); + } else { + auto funcScope = currentScope->findEnclosingFunctionScope(); + if (!funcScope) { + throw std::runtime_error("Block scope must be inside a function scope"); + } + currentScope = std::make_shared(name, funcScope, currentScope); + } } - void Parser::exitScope() - { - if (!currentScope->parent_) + void Parser::exitScope() { + if (!currentScope->parent()) throw std::runtime_error("Scope underflow"); - currentScope = currentScope->parent_; + currentScope = currentScope->parent(); } -} // namespace zlang +} // namespace zlang diff --git a/src/parser/ScopeContext.cpp b/src/parser/ScopeContext.cpp index 4a9de70..9598273 100644 --- a/src/parser/ScopeContext.cpp +++ b/src/parser/ScopeContext.cpp @@ -1,130 +1,224 @@ #include -namespace zlang -{ - bool ScopeContext::defineVariable(const std::string &varName, const VariableInfo &info) - { - if (lookupVariableInCurrentContext(varName).has_value()) - { - return false; - } - else - { +namespace zlang { + bool ScopeContext::defineVariable(const std::string &name, + const VariableInfo &info) { + if (!parent_ || (parent_->kind() == "Namespace" && kind() != "Function")) { + if (lookupVariableInCurrentContext(name).has_value()) { + return false; + } + TypeInfo ti = lookupType(info.type); + vars_[name] = info; + variable_name_mappings[name] = GLOBAL_NAME_MAPPER.mapVariable(name, name_); + return true; + } else { + if (lookupVariableInCurrentContext(name).has_value()) { + return false; + } TypeInfo ti = lookupType(info.type); - int size = ti.bits / 8; - name_mappings[varName] = varName + "___" + this->name + "___" + std::to_string(variableNumber++); - int padding = (ti.align - (stackOffset % ti.align)) % ti.align; - stackOffset += padding; - stackOffset += size; - offsetTable[varName] = -stackOffset; - vars_[varName] = info; + std::int64_t offset = allocateStack(name, ti); + vars_[name] = info; + offsetTable_[name] = offset; + variable_name_mappings[name] = GLOBAL_NAME_MAPPER.mapVariable(name, name_); return true; } } - - void ScopeContext::defineFunction(const std::string &functionName, const FunctionInfo &info) - { - funcs_[functionName] = info; + void ScopeContext::defineFunction(const std::string &name, FunctionInfo info) { + if (!info.isExtern) { + info.label = GLOBAL_NAME_MAPPER.mapFunction(name, name_); + } + funcs_[name] = info; } - - void ScopeContext::defineType(const std::string &typeName, const TypeInfo &info) - { - types_[typeName] = info; + void ScopeContext::defineType(const std::string &name, const TypeInfo &info) { + types_[name] = info; } - - VariableInfo ScopeContext::lookupVariable(const std::string &varName) const - { - auto it = vars_.find(varName); - if (it != vars_.end()) + VariableInfo ScopeContext::lookupVariable(const std::string &name) const { + auto it = vars_.find(name); + if (it != vars_.end()) { return it->second; - if (parent_) - return parent_->lookupVariable(varName); - throw std::runtime_error("Undefined variable '" + varName + "'"); - } - - std::string ScopeContext::getMapping(std::string varName) - { - try - { - lookupVariable(varName); - auto it = vars_.find(varName); - if (it != vars_.end()) - { - if (name_mappings.find(varName) != name_mappings.end()) - { - return name_mappings[varName]; - } - else - { - throw std::runtime_error("Mapping not found for variable '" + varName + "', in scope named '" + name + "'."); - } + } + if (parent_) { + // Skip outer function locals if crossing function boundary + if (this->kind() == "Function" && parent_->kind() == "Function" && + parent_->parent_) { + return parent_->parent_->lookupVariable(name); } - if (parent_) - return parent_->getMapping(varName); + return parent_->lookupVariable(name); } - catch (...) - { - throw std::runtime_error("Undefined variable '" + varName + "'"); + throw std::runtime_error("Undefined variable: " + name); + } + FunctionInfo ScopeContext::lookupFunction(const std::string &name) const { + auto it = funcs_.find(name); + if (it != funcs_.end()) { + return it->second; + } + if (parent_) { + return parent_->lookupFunction(name); } - return ""; + throw std::runtime_error("Undefined function: " + name); } - - std::optional ScopeContext::lookupVariableInCurrentContext(const std::string &varName) const - { - auto it = vars_.find(varName); - if (it != vars_.end()) + TypeInfo ScopeContext::lookupType(const std::string &name) const { + auto it = types_.find(name); + if (it != types_.end()) { return it->second; - return std::nullopt; + } + if (parent_) { + return parent_->lookupType(name); + } + throw std::runtime_error("Undefined type: " + name); } - - FunctionInfo ScopeContext::lookupFunction(const std::string &functionName) const - { - auto it = funcs_.find(functionName); - if (it != funcs_.end()) + std::optional + ScopeContext::lookupVariableInCurrentContext(const std::string &name) const { + auto it = vars_.find(name); + if (it != vars_.end()) { return it->second; - if (parent_) - return parent_->lookupFunction(functionName); - throw std::runtime_error("Undefined function '" + functionName + "'"); + } + return std::nullopt; } - - TypeInfo ScopeContext::lookupType(const std::string &typeName) const - { - auto it = types_.find(typeName); - if (it != types_.end()) + std::int64_t ScopeContext::allocateStack(const std::string & /*varName*/, + const TypeInfo & /*type*/) { + throw std::runtime_error("allocateStack not implemented for scope: " + + kind()); + } + std::int64_t ScopeContext::getVariableOffset(const std::string &name) const { + auto it = offsetTable_.find(name); + if (it != offsetTable_.end()) { return it->second; - if (parent_) - return parent_->lookupType(typeName); - throw std::runtime_error("Undefined type '" + typeName + "'"); + } + if (parent_) { + if (this->kind() == "Function" && parent_->kind() == "Function" && + parent_->parent_) { + return parent_->parent_->getVariableOffset(name); + } + return parent_->getVariableOffset(name); + } + throw std::runtime_error("Unknown variable: " + name); } - - std::int64_t ScopeContext::getVariableOffset(const std::string &varName) const - { - auto it = offsetTable.find(varName); - if (it != offsetTable.end()) + bool ScopeContext::isGlobalVariable(const std::string &name) const { + const ScopeContext *ctx = this; + while (ctx) { + auto it = ctx->vars_.find(name); + if (it != ctx->vars_.end()) { + return ctx->isGlobalScope(); + } + ctx = ctx->parent_.get(); + } + throw std::runtime_error("Unknown variable: " + name); + } + bool ScopeContext::isGlobalScope() const { return parent_ == nullptr; } + std::string ScopeContext::getMapping(std::string name) { + auto it = variable_name_mappings.find(name); + if (it != variable_name_mappings.end()) { return it->second; - if (parent_) - return parent_->getVariableOffset(varName); - throw std::runtime_error("Unknown variable " + varName); + } + if (parent_) { + if (this->kind() == "Function" && parent_->kind() == "Function" && + parent_->parent_) { + return parent_->parent_->getMapping(name); + } + return parent_->getMapping(name); + } + throw std::runtime_error("Mapping not found for variable: " + name); } + void ScopeContext::printScope(std::ostream &out, int indent) const { + std::string pad(indent, ' '); + out << pad << kind() << " Scope: " << name_ << "\n"; + + if (!vars_.empty()) { + out << pad << " Variables:\n"; + for (const auto &kv : vars_) { + out << pad << " " << kv.first << ": " << kv.second.type << "\n"; + } + } - bool ScopeContext::isGlobalVariable(const std::string &varName) const - { + if (!funcs_.empty()) { + out << pad << " Functions:\n"; + for (const auto &kv : funcs_) { + out << pad << " " << kv.first << " -> " << kv.second.returnType + << "\n"; + } + } - const ScopeContext *ctx = this; - while (ctx) - { - auto it = ctx->vars_.find(varName); - if (it != ctx->vars_.end()) - { - return (ctx->parent_ == nullptr); + if (!types_.empty()) { + out << pad << " Types:\n"; + for (const auto &kv : types_) { + out << pad << " " << kv.first << "\n"; + } + } + } + std::shared_ptr ScopeContext::findEnclosingFunctionScope() { + std::shared_ptr current = shared_from_this(); + while (current) { + if (current->kind() == "Function") { + return std::static_pointer_cast(current); + } + current = current->parent_; + } + return nullptr; + } + std::shared_ptr ScopeContext::getGlobal() { + if (!parent_) { + return shared_from_this(); + } else { + return parent_->getGlobal(); + } + } + + FunctionScope::FunctionScope(std::string name, + std::shared_ptr parent) + : ScopeContext(std::move(name), std::move(parent)), stackOffset_(-8) {} + FunctionScope::~FunctionScope() = default; + std::int64_t FunctionScope::allocateStack(const std::string &varName, + const TypeInfo &type) { + std::int64_t size = alignSize(type); + stackOffset_ -= size; + offsetTable_[varName] = stackOffset_; + return stackOffset_; + } + void FunctionScope::printScope(std::ostream &out, int indent) const { + // Use base implementation for printing variables, functions, and types + ScopeContext::printScope(out, indent); + } + std::int64_t FunctionScope::getStackOffset() const { + return stackOffset_; + } + std::string FunctionScope::allocateSpillSlot(std::int64_t size) { + for (auto it = freeSpillSlots_.begin(); it != freeSpillSlots_.end(); ++it) { + if (it->second == size) { + std::int64_t offset = it->first; + freeSpillSlots_.erase(it); + return "-" + std::to_string(offset) + " (%rbp)"; } - ctx = ctx->parent_.get(); } - throw std::runtime_error("Unknown variable " + varName); + nextSpillOffset_ -= size; + std::int64_t offset = nextSpillOffset_; + return std::to_string(stackOffset_ + offset) + " (%rbp)"; + } + std::int64_t FunctionScope::getSpillSize() const { + return nextSpillOffset_; + } + void FunctionScope::freeSpillSlot(const std::string &slot, std::int64_t size) { + auto pos = slot.find('('); + if (pos == std::string::npos) + throw std::runtime_error("Invalid spill slot format: " + slot); + std::string offsetStr = slot.substr(1, pos - 1); // skip '-' + std::int64_t offset = std::stoll(offsetStr); + freeSpillSlots_.emplace_back(offset, size); } - bool ScopeContext::isGlobalScope() const - { - return !this->parent_.get(); + BlockScope::BlockScope(std::string name, + std::shared_ptr funcScope, + std::shared_ptr parent) + : ScopeContext(std::move(name), std::move(parent)), + funcScope_(std::move(funcScope)) {} + BlockScope::~BlockScope() = default; + std::int64_t BlockScope::allocateStack(const std::string &varName, + const TypeInfo &type) { + return funcScope_->allocateStack(varName, type); + } + void BlockScope::printScope(std::ostream &out, int indent) const { + std::string pad(indent, ' '); + out << pad << kind() << " Scope: " << name_ << "\n"; + ScopeContext::printScope(out, indent + 2); } -} +} // namespace zlang diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index fa2b2bf..8113871 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -1,11 +1,8 @@ #include "all.hpp" -namespace zlang -{ - void TypeChecker::check(const std::unique_ptr &program) - { - if (!program || program->type != NodeType::Program) - { +namespace zlang { + void TypeChecker::check(const std::unique_ptr &program) { + if (!program || program->type != NodeType::Program) { logError({ErrorType::Type, "TypeChecker error: root is not a Program node"}); shouldCodegen_ = false; @@ -14,36 +11,138 @@ namespace zlang checkNode(program.get()); } - std::string TypeChecker::checkNode(const ASTNode *node) - { + std::string TypeChecker::checkNode(const ASTNode *node) { if (!node) return ""; auto scope = node->scope; - switch (node->type) - { - + switch (node->type) { case NodeType::Program: - for (auto &child : node->children) - { + for (auto &child : node->children) { checkNode(child.get()); } return ""; - case NodeType::VariableDeclaration: - { + case NodeType::ExternFunction: { + if (node->value == "main") { + logError({ErrorType::Generic, "Function named 'main' cannot be extern"}); + shouldCodegen_ = false; + return ""; + } + ASTNode *params = node->getFunctionParamList(); + for (const auto &childPtr : params->children) { + ASTNode *param = childPtr.get(); + try { + node->scope->lookupType(param->children[1]->value); + } catch (...) { + logError(Error{ErrorType::Type, "Undefined type '" + (param->children[1]->value) + "' for parameter named '" + param->children[0]->value + "' in declaration of function named '" + node->value + "'."}); + shouldCodegen_ = false; + return ""; + } + } + try { + node->scope->lookupType(node->getFunctionParamReturnType()->value); + } catch (...) { + logError(Error{ErrorType::Type, "Undefined type '" + (node->getFunctionParamReturnType()->value) + "' for return type in declaration of function named '" + node->value + "'."}); + shouldCodegen_ = false; + } + return ""; + } + + case NodeType::FunctionCall: { + if (node->value == "main") { + logError({ErrorType::Generic, "Cannot call 'main' function explicitly."}); + shouldCodegen_ = false; + return ""; + } + FunctionInfo functionInfo; + try { + functionInfo = node->scope->lookupFunction(node->value); + } catch (...) { + logError(Error{ErrorType::Type, "Undefined functions '" + (node->value) + "'."}); + shouldCodegen_ = false; + return ""; + } + const std::vector &functionParams = functionInfo.paramTypes; + const std::vector> &functionArguments = node->children[0]->children; + + if (functionArguments.size() != functionParams.size()) { + logError(Error{ + ErrorType::Type, + "Function '" + node->value + "' expects " + + std::to_string(functionParams.size()) + " arguments, got " + + std::to_string(functionArguments.size()) + "."}); + shouldCodegen_ = false; + return ""; + } + + for (size_t i = 0; i < functionArguments.size(); ++i) { + std::string argType = checkNode(functionArguments[i].get()); + std::string paramType = functionParams[i].type; + + if (argType != paramType) { + if (!(isNumeric(argType) && isNumeric(paramType))) { + logError(Error{ + ErrorType::Type, + "Function '" + node->value + "' argument " + + std::to_string(i + 1) + " expects '" + paramType + + "', got '" + argType + "'."}); + shouldCodegen_ = false; + return ""; + } + } + } + return functionInfo.returnType; + } + + case NodeType::Function: { + ASTNode *params = node->getFunctionParamList(); + for (const auto &childPtr : params->children) { + ASTNode *param = childPtr.get(); + try { + node->scope->lookupType(param->children[1]->value); + } catch (...) { + logError(Error{ErrorType::Type, "Undefined type '" + (param->children[1]->value) + "' for parameter named '" + param->children[0]->value + "' in declaration of function named '" + node->value + "'."}); + shouldCodegen_ = false; + return ""; + } + } + try { + node->scope->lookupType(node->getFunctionParamReturnType()->value); + } catch (...) { + logError(Error{ErrorType::Type, "Undefined type '" + (node->getFunctionParamReturnType()->value) + "' for return type in declaration of function named '" + node->value + "'."}); + shouldCodegen_ = false; + } + return ""; + } + + case NodeType::ReturnStatement: { + std::string expectedRet = scope->returnType; + std::string actualRet = "none"; + if (!node->children.empty()) { + actualRet = checkNode(node->children[0].get()); + } + + if (actualRet != expectedRet && + !(isNumeric(actualRet) && isNumeric(expectedRet))) { + logError({ErrorType::Type, + "Return type mismatch: expected '" + expectedRet + + "', got '" + actualRet + "'"}); + shouldCodegen_ = false; + } + return actualRet; + } + + case NodeType::VariableDeclaration: { std::string annotatedType, initType; if (node->children.size() >= 1 && - node->children[0]->type == NodeType::Symbol) - { + node->children[0]->type == NodeType::Symbol) { annotatedType = node->children[0]->value; } - if (node->children.size() == 2) - { + if (node->children.size() == 2) { initType = checkNode(node->children[1].get()); } - if (annotatedType.empty() && initType.empty()) - { + if (annotatedType.empty() && initType.empty()) { logError({ErrorType::Type, "Declaration of '" + node->value + "' needs a type annotation or initializer"}); @@ -53,10 +152,8 @@ namespace zlang // If both present but mismatch, only error if *non‑numeric* if (!annotatedType.empty() && !initType.empty() && - annotatedType != initType) - { - if (!(isNumeric(annotatedType) && isNumeric(initType))) - { + annotatedType != initType) { + if (!(isNumeric(annotatedType) && isNumeric(initType))) { logError({ErrorType::Type, "Initializer type '" + initType + "' does not match annotation '" + annotatedType + @@ -72,13 +169,11 @@ namespace zlang return finalType; } - case NodeType::VariableReassignment: - { + case NodeType::VariableReassignment: { VariableInfo info = scope->lookupVariable(node->value); std::string expected = info.type; std::string actual = checkNode(node->children[0].get()); - if (!(expected == actual or isNumeric(expected) == isNumeric(actual))) - { + if (!(expected == actual or isNumeric(expected) == isNumeric(actual))) { logError({ErrorType::Type, "Reassignment of '" + node->value + "' expects '" + expected + @@ -88,14 +183,10 @@ namespace zlang return expected; } - case NodeType::VariableAccess: - { - try - { + case NodeType::VariableAccess: { + try { return scope->lookupVariable(node->value).type; - } - catch (...) - { + } catch (...) { logError({ErrorType::Type, "Unknown variable " + node->value}); shouldCodegen_ = false; return ""; @@ -104,28 +195,27 @@ namespace zlang case NodeType::IntegerLiteral: return "integer"; + case NodeType::FloatLiteral: if (!node->value.empty() && (node->value.back() == 'f' || node->value.back() == 'F')) return "float"; else return "double"; + case NodeType::StringLiteral: return "string"; case NodeType::BooleanLiteral: return "boolean"; - case NodeType::BinaryOp: - { + case NodeType::BinaryOp: { std::string lhs = checkNode(node->children[0].get()); std::string rhs = checkNode(node->children[1].get()); const auto &op = node->value; // Arithmetic + - * / - if (op == "+" || op == "-" || op == "*" || op == "/") - { - if (isNumeric(lhs) && isNumeric(rhs)) - { + if (op == "+" || op == "-" || op == "*" || op == "/") { + if (isNumeric(lhs) && isNumeric(rhs)) { const TypeInfo &tL = node->scope->lookupType(lhs); const TypeInfo &tR = node->scope->lookupType(rhs); @@ -142,8 +232,7 @@ namespace zlang } // Logical || && - if (op == "||" || op == "&&") - { + if (op == "||" || op == "&&") { if (lhs == "boolean" && rhs == "boolean") return "boolean"; logError({ErrorType::Type, @@ -155,8 +244,7 @@ namespace zlang } // Comparisons - if (op == "==" || op == "!=" || op == ">=" || op == ">" || op == "<=" || op == "<") - { + if (op == "==" || op == "!=" || op == ">=" || op == ">" || op == "<=" || op == "<") { // numeric vs numeric → OK if (isNumeric(lhs) && isNumeric(rhs)) return "boolean"; @@ -184,25 +272,19 @@ namespace zlang shouldCodegen_ = false; return ""; } - - case NodeType::UnaryOp: - { + case NodeType::UnaryOp: { std::string ty = checkNode(node->children[0].get()); const auto &op = node->value; - if (op == "!") - { - if (ty != "boolean") - { + if (op == "!") { + if (ty != "boolean") { logError({ErrorType::Type, "Logical '!' needs boolean, got '" + ty + "'"}); shouldCodegen_ = false; } return "boolean"; } - if (op == "++" || op == "--") - { - if (!isInteger(ty)) - { + if (op == "++" || op == "--") { + if (!isInteger(ty)) { logError({ErrorType::Type, "Unary '" + op + "' needs Integral type, got '" + ty + "'"}); shouldCodegen_ = false; @@ -214,10 +296,8 @@ namespace zlang shouldCodegen_ = false; return ""; } - case NodeType::IfStatement: - case NodeType::ElseIfStatement: - { + case NodeType::ElseIfStatement: { if (node->children.size() > 0) checkNode(node->children[0].get()); @@ -229,8 +309,7 @@ namespace zlang return ""; } - case NodeType::ElseStatement: - { + case NodeType::ElseStatement: { if (!node->children.empty()) checkNode(node->children[0].get()); return ""; @@ -241,19 +320,16 @@ namespace zlang } } - bool TypeChecker::isInteger(const std::string &ty) - { + bool TypeChecker::isInteger(const std::string &ty) { return integral_types.find(ty) != integral_types.end(); } - bool TypeChecker::isNumeric(const std::string &ty) - { + bool TypeChecker::isNumeric(const std::string &ty) { return numeric_types.find(ty) != numeric_types.end(); } - bool TypeChecker::isComparable(const std::string &ty) - { + bool TypeChecker::isComparable(const std::string &ty) { return isNumeric(ty); } -} // namespace zlang \ No newline at end of file +} // namespace zlang \ No newline at end of file diff --git a/tests/zz/conditionals/if-elif-else.zz b/tests/zz/conditionals/if-elif-else.zz index 9031254..bd213af 100644 --- a/tests/zz/conditionals/if-elif-else.zz +++ b/tests/zz/conditionals/if-elif-else.zz @@ -2,15 +2,17 @@ let a: int64_t = 10; let f: float = 20.5; let u: uint8_t = 10; -if (a > 100) { - let res: int64_t = a * 2; -} -elif (f < 10.0) { - let res: float = f + 1.0; -} -elif (u == a) { - let res: uint8_t = u + 1; -} -else { - let res: int64_t = -1; +fn main() { + if (a > 100) { + let res: int64_t = a * 2; + } + elif (f < 10.0) { + let res: float = f + 1.0; + } + elif (u == a) { + let res: uint8_t = u + 1; + } + else { + let res: int64_t = -1; + } } diff --git a/tests/zz/conditionals/if-elif.zz b/tests/zz/conditionals/if-elif.zz index 648bfa4..ace4f83 100644 --- a/tests/zz/conditionals/if-elif.zz +++ b/tests/zz/conditionals/if-elif.zz @@ -2,12 +2,14 @@ let a: int64_t = 50; let f: double = 25.25; let u: uint32_t = 50; -if (a < 0) { - let res: int64_t = a - 1; -} -elif (f >= 25.0) { - let res: double = f * 1.1; -} -elif (u != a) { - let res: uint32_t = u + 10; -} +fn main() { + if (a < 0) { + let res: int64_t = a - 1; + } + elif (f >= 25.0) { + let res: double = f * 1.1; + } + elif (u != a) { + let res: uint32_t = u + 10; + } +} \ No newline at end of file diff --git a/tests/zz/conditionals/if-else.zz b/tests/zz/conditionals/if-else.zz index ebc377a..fc53f79 100644 --- a/tests/zz/conditionals/if-else.zz +++ b/tests/zz/conditionals/if-else.zz @@ -2,23 +2,25 @@ let a: int64_t = 15; let f: double = 12.34; let u: uint16_t = 15; -if (a >= 20) { - let res: int64_t = a - 5; -} -else { - let res: int64_t = a + 5; -} +fn main() { + if (a >= 20) { + let res: int64_t = a - 5; + } + else { + let res: int64_t = a + 5; + } -if (f != 0.0) { - let val: double = f / 2.0; -} -else { - let val: double = 0.0; -} + if (f != 0.0) { + let val: double = f / 2.0; + } + else { + let val: double = 0.0; + } -if (u < a) { - let x: uint16_t = u * 2; + if (u < a) { + let x: uint16_t = u * 2; + } + else { + let x: uint16_t = u / 2; + } } -else { - let x: uint16_t = u / 2; -} \ No newline at end of file diff --git a/tests/zz/conditionals/if.zz b/tests/zz/conditionals/if.zz index e11c62e..a1a9706 100644 --- a/tests/zz/conditionals/if.zz +++ b/tests/zz/conditionals/if.zz @@ -3,14 +3,16 @@ let b: int64_t = 20; let f: float = 10.5f; let u: uint8_t = 10; -if (a < b) { - let x: int64_t = 1; -} +fn main() { + if (a < b) { + let x: int64_t = 1; + } -if (f > 5.5) { - let y: float = f * 2.0; -} + if (f > 5.5) { + let y: float = f * 2.0; + } -if (u == a) { - let z: uint8_t = u + 1; -} \ No newline at end of file + if (u == a) { + let z: uint8_t = u + 1; + } +} From ec252167492c19df869a0840acfb3f0a4f0d310b Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Sat, 21 Jun 2025 18:52:03 +0530 Subject: [PATCH 2/3] Let windows take over. --- examples/functions.zz | 21 +- include/ast/ASTNode.hpp | 4 +- include/codegen/CodeGen.hpp | 142 +- include/lexer/Lexer.hpp | 33 +- include/parser/NameMapper.hpp | 6 +- include/parser/ScopeContext.hpp | 6 +- main.cpp | 58 +- src/ast/ASTNode.cpp | 8 +- src/codegen/CodeGen.cpp | 17 +- src/codegen/CodeGenLLVM.cpp | 1289 ++++++++++------- src/codegen/CodeGenLinux.cpp | 171 ++- src/codegen/CodeGenWindows.cpp | 1861 ++++++++++++++++--------- src/codegen/RegisterAllocator.cpp | 12 +- src/lexer/Lexer.cpp | 91 +- src/parser/Parser.cpp | 36 +- src/parser/ScopeContext.cpp | 24 +- src/support/CommandLine.cpp | 127 +- test_runner.py | 5 +- tests/zz/conditionals/if-elif-else.zz | 15 + tests/zz/functions/basics.zz | 39 + tests/zz/functions/recursive.zz | 40 + tests/zz/functions/scopes.zz | 39 + tests/zz/operations/binary.zz | 63 +- tests/zz/operations/unary.zz | 38 +- tests/zz/types.zz | 24 + tests/zz/variables.zz | 13 +- 26 files changed, 2691 insertions(+), 1491 deletions(-) create mode 100644 tests/zz/functions/basics.zz create mode 100644 tests/zz/functions/recursive.zz create mode 100644 tests/zz/functions/scopes.zz create mode 100644 tests/zz/types.zz diff --git a/examples/functions.zz b/examples/functions.zz index 6bc2156..352ac5d 100644 --- a/examples/functions.zz +++ b/examples/functions.zz @@ -1,11 +1,18 @@ -extern fn putchar(c: int32_t) -> int32_t; +extern fn printf(fmt: string, ...) -> int32_t; +extern fn putchar(ch: int32_t) -> int32_t; -fn add(a: int32_t, b: int32_t) -> int32_t { - return a + b; +fn get_factorial(a: uint32_t) -> uint64_t{ + fn factorial(a: uint32_t) -> uint32_t{ + if(a <= 1){ + return 1; + }else{ + return a * factorial(a - 1); + } + } + let ans: int32_t = factorial(a); + return ans; } fn main() { - let sum: int32_t = add(1, add(3, 4)); - let ch: int32_t = sum + 48; - putchar(ch); -} \ No newline at end of file + printf("Factorial: %d\n", get_factorial(10)); +} diff --git a/include/ast/ASTNode.hpp b/include/ast/ASTNode.hpp index e1ce31f..f98b88f 100644 --- a/include/ast/ASTNode.hpp +++ b/include/ast/ASTNode.hpp @@ -61,8 +61,8 @@ namespace zlang { static std::unique_ptr makeIfStatement(std::unique_ptr condition, std::unique_ptr program, const std::shared_ptr scope); static std::unique_ptr makeElseIfStatement(std::unique_ptr condition, std::unique_ptr program, const std::shared_ptr scope); static std::unique_ptr makeElseStatement(std::unique_ptr program, const std::shared_ptr scope); - static std::unique_ptr makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType); - static std::unique_ptr makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body); + static std::unique_ptr makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, bool isVariadic); + static std::unique_ptr makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body, bool isVariadic); static std::unique_ptr makeFunctionCall(std::string name, std::vector> arguments, const std::shared_ptr scope); static std::unique_ptr makeFunctionParameterList(const std::vector params, const std::shared_ptr scope); void addChild(std::unique_ptr child); diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 05704c7..6cabc18 100644 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -174,73 +174,77 @@ namespace zlang { void generate(std::unique_ptr program) override; }; - // class CodeGenWindows : public CodeGen { - // private: - // std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - - // void generateStatement(std::unique_ptr statement) override; - // std::string emitExpression(std::unique_ptr node) override; - - // void emitEpilogue(std::shared_ptr scope) override; - // void emitPrologue(std::shared_ptr scope) override; - - // std::string generateIntegerLiteral(std::unique_ptr node) override; - // std::string generateFloatLiteral(std::unique_ptr node) override; - // std::string generateStringLiteral(std::unique_ptr node) override; - // std::string generateBooleanLiteral(std::unique_ptr node) override; - // std::string generateVariableAccess(std::unique_ptr node) override; - - // void generateVariableReassignment(std::unique_ptr node) override; - // void generateVariableDeclaration(std::unique_ptr node) override; - // void generateIfStatement(std::unique_ptr node) override; - - // std::string generateBinaryOperation(std::unique_ptr node) override; - // std::string generateUnaryOperation(std::unique_ptr node) override; - // std::string castValue(const std::string ®, const TypeInfo &fromType, const TypeInfo &toType); - // std::string getVariableAddress(const ScopeContext &scope, const std::string &name) const; - - // std::string generateFunctionCall(std::unique_ptr node) override; // Expression - // void generateFunctionDeclaration(std::unique_ptr node) override; // Statement - // void generateExternFunctionDeclaration(std::unique_ptr node) override; // Statement - - // public: - // ~CodeGenWindows() override = default; - // CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; - // void generate(std::unique_ptr program) override; - // }; - - // class CodeGenLLVM : public CodeGen { - // private: - // std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - // std::unordered_map stringLiterals; - - // void generateStatement(std::unique_ptr statement) override; - // std::string emitExpression(std::unique_ptr node) override; - - // void emitEpilogue(std::shared_ptr scope) override; - // void emitPrologue(std::shared_ptr scope) override; - - // std::string generateIntegerLiteral(std::unique_ptr node) override; - // std::string generateFloatLiteral(std::unique_ptr node) override; - // std::string generateStringLiteral(std::unique_ptr node) override; - // std::string generateBooleanLiteral(std::unique_ptr node) override; - // std::string generateVariableAccess(std::unique_ptr node) override; - - // void generateVariableReassignment(std::unique_ptr node) override; - // void generateVariableDeclaration(std::unique_ptr node) override; - // void generateIfStatement(std::unique_ptr node) override; - - // std::string generateBinaryOperation(std::unique_ptr node) override; - // std::string generateUnaryOperation(std::unique_ptr node) override; - // std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType); - - // std::string generateFunctionCall(std::unique_ptr node) override; // Expression - // void generateFunctionDeclaration(std::unique_ptr node) override; // Statement - // void generateExternFunctionDeclaration(std::unique_ptr node) override; // Statement - - // public: - // ~CodeGenLLVM() override = default; - // CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream) {}; - // void generate(std::unique_ptr program) override; - // }; + class CodeGenWindows : public CodeGen { + private: + std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; + + std::string allocateOrSpill(bool isXMM, std::shared_ptr scope, std::ostringstream &out); + void restoreIfSpilled(const std::string ®, std::shared_ptr scope, std::ostringstream &out); + + void generateStatement(std::unique_ptr statement, std::ostringstream &out) override; + std::string emitExpression(std::unique_ptr node, std::ostringstream &out) override; + + void emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax = false) override; + void emitPrologue(std::shared_ptr scope, std::ostringstream &out) override; + + std::string generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateStringLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateVariableAccess(std::unique_ptr node, std::ostringstream &out) override; + + void generateVariableReassignment(std::unique_ptr node, std::ostringstream &out) override; + void generateVariableDeclaration(std::unique_ptr node, std::ostringstream &out) override; + void generateIfStatement(std::unique_ptr node, std::ostringstream &out) override; + + std::string generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) override; + std::string generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) override; + + std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType, const std::shared_ptr currentScope, std::ostringstream &out); + + std::string generateFunctionCall(std::unique_ptr node, std::ostringstream &out) override; // Expression + void generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force = false) override; // Statement + void generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) override; // Statement + void generateReturnstatement(std::unique_ptr node, std::ostringstream &out); // Statement + + public: + ~CodeGenWindows() override = default; + CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; + void generate(std::unique_ptr program) override; + }; + + class CodeGenLLVM : public CodeGen { + private: + std::unordered_map stringLiterals; + + void generateStatement(std::unique_ptr statement, std::ostringstream &out) override; + std::string emitExpression(std::unique_ptr node, std::ostringstream &out) override; + + void emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax = false) override; + void emitPrologue(std::shared_ptr scope, std::ostringstream &out) override; + + std::string generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateStringLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) override; + std::string generateVariableAccess(std::unique_ptr node, std::ostringstream &out) override; + + void generateVariableReassignment(std::unique_ptr node, std::ostringstream &out) override; + void generateVariableDeclaration(std::unique_ptr node, std::ostringstream &out) override; + void generateIfStatement(std::unique_ptr node, std::ostringstream &out) override; + + std::string generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) override; + std::string generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) override; + std::string castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType, std::ostringstream &out); + + std::string generateFunctionCall(std::unique_ptr node, std::ostringstream &out) override; // Expression + void generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force = false) override; // Statement + void generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) override; // Statement + void generateReturnstatement(std::unique_ptr node, std::ostringstream &out); // Statement + + public: + ~CodeGenLLVM() override = default; + CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream) {}; + void generate(std::unique_ptr program) override; + }; } // namespace zlang \ No newline at end of file diff --git a/include/lexer/Lexer.hpp b/include/lexer/Lexer.hpp index 2813bd6..d1fb69f 100644 --- a/include/lexer/Lexer.hpp +++ b/include/lexer/Lexer.hpp @@ -1,14 +1,12 @@ #pragma once #include #include + #include "../common/Errors.hpp" -namespace zlang -{ - struct Token - { - enum class Kind - { +namespace zlang { + struct Token { + enum class Kind { Let, Identifier, Colon, @@ -23,6 +21,8 @@ namespace zlang SemiColon, Equal, Return, + Dot, + Ellipsis, If, ElseIf, Else, @@ -38,8 +38,7 @@ namespace zlang size_t line; size_t column; - std::string to_string() const - { + std::string to_string() const { return "Token { kind = " + kindToString(kind) + ", text = \"" + text + "\", line = " + std::to_string(line) + @@ -47,10 +46,8 @@ namespace zlang } private: - static std::string kindToString(Kind k) - { - switch (k) - { + static std::string kindToString(Kind k) { + switch (k) { case Kind::Equal: return "Equal"; case Kind::Colon: @@ -97,18 +94,20 @@ namespace zlang return "Function"; case Kind::Return: return "Return"; + case Kind::Dot: + return "Dot"; + case Kind::Ellipsis: + return "Ellipsis"; } return "Invalid"; } }; - inline std::ostream &operator<<(std::ostream &os, const Token &token) - { + inline std::ostream &operator<<(std::ostream &os, const Token &token) { return os << token.to_string(); } using Error = zlang::Error; - class Lexer - { + class Lexer { public: explicit Lexer(const std::string &source); @@ -131,4 +130,4 @@ namespace zlang Token scanSymbol(); }; -} // namespace zlang +} // namespace zlang diff --git a/include/parser/NameMapper.hpp b/include/parser/NameMapper.hpp index 98ac5a0..afd8b8e 100644 --- a/include/parser/NameMapper.hpp +++ b/include/parser/NameMapper.hpp @@ -7,19 +7,19 @@ class NameMapper public: inline std::string mapVariable(const std::string &name, const std::string &scopeName) { - std::string mangled = name + "___" + scopeName + "___v" + std::to_string(varCounter_++); + std::string mangled = scopeName + "___" + name + "___v" + std::to_string(varCounter_++); return mangled; } inline std::string mapFunction(const std::string &name, const std::string &scopeName) { - std::string mangled = name + "___" + scopeName + "___f" + std::to_string(funcCounter_++); + std::string mangled = scopeName + "___" + name + "___f" + std::to_string(funcCounter_++); return mangled; } inline std::string mapType(const std::string &name, const std::string &scopeName) { - std::string mangled = name + "___" + scopeName + "___t" + std::to_string(typeCounter_++); + std::string mangled = scopeName + "___" + name + "___t" + std::to_string(typeCounter_++); return mangled; } diff --git a/include/parser/ScopeContext.hpp b/include/parser/ScopeContext.hpp index 4e3155d..84561b6 100644 --- a/include/parser/ScopeContext.hpp +++ b/include/parser/ScopeContext.hpp @@ -45,6 +45,7 @@ namespace zlang { std::string name; std::string label; bool isExtern; + bool isVariadic; std::string to_string() const; }; @@ -98,6 +99,7 @@ namespace zlang { std::shared_ptr parent() const { return parent_; } const std::string &name() const { return name_; } std::string getMapping(std::string name); + void setMapping(const std::string &name, const std::string &llvmName); virtual void printScope(std::ostream &out, int indent = 0) const; std::shared_ptr findEnclosingFunctionScope(); std::shared_ptr getGlobal(); @@ -124,14 +126,12 @@ namespace zlang { const TypeInfo &type) override; std::int64_t getStackOffset() const; void setCanary(std::uint64_t canary) { - logMessage("Setting canary of: " + name_); this->canary = canary; } std::uint64_t getCanary() { - logMessage("Getting canary of: " + name_); return this->canary; } - std::string allocateSpillSlot(std::int64_t size); + std::string allocateSpillSlot(std::int64_t size, CodegenOutputFormat format); std::int64_t getSpillSize() const; void freeSpillSlot(const std::string &slot, std::int64_t size); diff --git a/main.cpp b/main.cpp index 7fc8e28..c268145 100644 --- a/main.cpp +++ b/main.cpp @@ -93,35 +93,35 @@ int main(int argc, char *argv[]) { std::unique_ptr cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); - // switch (cli.getFormat()) { - // case CodegenOutputFormat::Default: - // #ifdef _WIN64 - // cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); - // #endif - // #ifdef __linux__ - // cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); - // #endif - // break; - - // case CodegenOutputFormat::X86_64_MSWIN: { - // cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); - // break; - // } - - // case CodegenOutputFormat::X86_64_LINUX: { - // cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); - // break; - // } - - // case CodegenOutputFormat::LLVM_IR: { - // cg = CodeGen::create(TargetTriple::LLVM_IR, *outstream); - // break; - // } - - // default: - // std::cerr << "This should not happen, ACP Pradhyumn...\n"; - // exit(1); - // } + switch (cli.getFormat()) { + case CodegenOutputFormat::Default: +#ifdef _WIN64 + cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); +#endif +#ifdef __linux__ + cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); +#endif + break; + + case CodegenOutputFormat::X86_64_MSWIN: { + cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); + break; + } + + case CodegenOutputFormat::X86_64_LINUX: { + cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); + break; + } + + case CodegenOutputFormat::LLVM_IR: { + cg = CodeGen::create(TargetTriple::LLVM_IR, *outstream); + break; + } + + default: + std::cerr << "This should not happen, ACP Pradhyumn...\n"; + exit(1); + } try { cg->generate(std::move(program)); } catch (std::exception const &exc) { diff --git a/src/ast/ASTNode.cpp b/src/ast/ASTNode.cpp index 6833ba2..94961db 100644 --- a/src/ast/ASTNode.cpp +++ b/src/ast/ASTNode.cpp @@ -139,17 +139,17 @@ namespace zlang { return node; } - std::unique_ptr ASTNode::makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType) { + std::unique_ptr ASTNode::makeExternFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, bool isVariadic) { auto node = std::make_unique(NodeType::ExternFunction, name, scope); auto paramsList = ASTNode::makeFunctionParameterList(params, scope); auto returnType_ = std::make_unique(NodeType::FunctionReturnType, returnType, scope); node->children.push_back(std::move(paramsList)); node->children.push_back(std::move(returnType_)); - scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = true}); + scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = true, .isVariadic = isVariadic}); return node; } - std::unique_ptr ASTNode::makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body) { + std::unique_ptr ASTNode::makeFunctionDeclaration(std::string name, const std::shared_ptr scope, std::vector params, std::string returnType, std::unique_ptr body, bool isVariadic) { auto node = std::make_unique(NodeType::Function, name, scope); auto paramsList = ASTNode::makeFunctionParameterList(params, scope); auto returnType_ = std::make_unique(NodeType::FunctionReturnType, returnType, scope); @@ -160,7 +160,7 @@ namespace zlang { body->scope->defineVariable(pi.name, VariableInfo{.type = pi.type}); } node->children.push_back(std::move(body)); - scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = false}); + scope->defineFunction(name, FunctionInfo{.paramTypes = params, .returnType = returnType, .name = name, .label = "", .isExtern = false, .isVariadic = isVariadic}); return node; } diff --git a/src/codegen/CodeGen.cpp b/src/codegen/CodeGen.cpp index 8290966..bff6770 100644 --- a/src/codegen/CodeGen.cpp +++ b/src/codegen/CodeGen.cpp @@ -3,15 +3,14 @@ namespace zlang { CodeGen::~CodeGen() = default; std::unique_ptr CodeGen::create(TargetTriple target, std::ostream &outstream) { - // switch (target) { - // case TargetTriple::X86_64_LINUX: - // return std::make_unique(outstream); - // case TargetTriple::X86_64_WINDOWS: - // return std::make_unique(outstream); - // case TargetTriple::LLVM_IR: - // return std::make_unique(outstream); - // } - return std::make_unique(outstream); + switch (target) { + case TargetTriple::X86_64_LINUX: + return std::make_unique(outstream); + case TargetTriple::X86_64_WINDOWS: + return std::make_unique(outstream); + case TargetTriple::LLVM_IR: + return std::make_unique(outstream); + } throw std::runtime_error("Unknown target"); } diff --git a/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index f4fa37d..4704735 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -1,501 +1,788 @@ -// #include "all.hpp" - -// namespace zlang { -// static std::string fresh() { -// static int cnt = 0; -// return "%tmp" + std::to_string(cnt++); -// } -// std::string toHexFloatFromStr(const std::string &valStr, bool isF32) { -// std::ostringstream oss; -// oss << std::scientific << std::setprecision(16); -// if (isF32) { -// float v = std::stof(valStr); -// oss << v; -// } else { -// double v = std::stod(valStr); -// oss << v; -// } -// return oss.str(); -// } -// std::string CodeGenLLVM::castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType) { -// if (fromType.isFloat == toType.isFloat && fromType.bits == toType.bits) -// return val; - -// std::string tmp = fresh(); - -// if (!fromType.isFloat && toType.isFloat) { -// std::string intTy = "i" + std::to_string(fromType.bits); -// std::string floatTy = (toType.bits == 64 ? "double" : "float"); -// std::string instr = fromType.isSigned ? "sitofp" : "uitofp"; -// out << " " << tmp << " = " << instr << " " << intTy << " " << val << " to " << floatTy << "\n"; -// } else if (fromType.isFloat && !toType.isFloat) { -// std::string floatTy = (fromType.bits == 64 ? "double" : "float"); -// std::string intTy = "i" + std::to_string(toType.bits); -// std::string instr = toType.isSigned ? "fptosi" : "fptoui"; -// out << " " << tmp << " = " << instr << " " << floatTy << " " << val << " to " << intTy << "\n"; -// } else if (!fromType.isFloat && !toType.isFloat) { -// std::string fromTy = "i" + std::to_string(fromType.bits); -// std::string toTy = "i" + std::to_string(toType.bits); -// if (fromType.bits < toType.bits) { -// std::string instr = fromType.isSigned ? "sext" : "zext"; -// out << " " << tmp << " = " << instr << " " << fromTy << " " << val << " to " << toTy << "\n"; -// } else if (fromType.bits > toType.bits) { -// out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\n"; -// } else if (fromType.isSigned != toType.isSigned) { -// // Retag value if signedness differs but bit-width is the same -// out << " " << tmp << " = add " << fromTy << " " << val << ", 0\n"; -// } else { -// return val; -// } -// } else if (fromType.isFloat && toType.isFloat) { -// std::string fromTy = (fromType.bits == 64 ? "double" : "float"); -// std::string toTy = (toType.bits == 64 ? "double" : "float"); -// if (fromType.bits < toType.bits) { -// out << " " << tmp << " = fpext " << fromTy << " " << val << " to " << toTy << "\n"; -// } else if (fromType.bits > toType.bits) { -// out << " " << tmp << " = fptrunc " << fromTy << " " << val << " to " << toTy << "\n"; -// } else { -// return val; -// } -// } else { -// throw std::runtime_error("Unsupported cast from type to type"); -// } - -// noteType(tmp, toType); -// return tmp; -// } -// std::string CodeGenLLVM::generateIntegerLiteral(std::unique_ptr node) { -// std::string name = fresh(); -// out << " " << name << " = add i64 0, " << node->value << "\n"; -// noteType(name, node->scope->lookupType("int64_t")); -// return name; -// } -// std::string CodeGenLLVM::generateFloatLiteral(std::unique_ptr node) { -// std::string tmp = fresh(); -// std::string val = node->value; -// bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); -// if (isF32) -// val.pop_back(); - -// std::string hexVal = toHexFloatFromStr(val, isF32); - -// if (isF32) { -// out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; -// noteType(tmp, node->scope->lookupType("float")); -// } else { -// out << " " << tmp << " = fadd double 0.0, " << hexVal << "\n"; -// noteType(tmp, node->scope->lookupType("double")); -// } -// return tmp; -// } -// std::string CodeGenLLVM::generateStringLiteral(std::unique_ptr node) { -// std::string value = node->value; - -// auto it = stringLiterals.find(value); -// if (it != stringLiterals.end()) -// return it->second; - -// std::string name = "@.str" + std::to_string(stringLabelCount++); -// size_t len = value.size() + 1; // Include null terminator -// std::string llvmEscaped; - -// for (unsigned char c : value) { -// if (isprint(c) && c != '"' && c != '\\') { -// llvmEscaped += c; -// } else { -// char buf[5]; -// snprintf(buf, sizeof(buf), "\\%02X", c); // Use uppercase hex -// llvmEscaped += buf; -// } -// } -// llvmEscaped += "\\00"; -// outGlobal << name << " = private unnamed_addr constant [" -// << len << " x i8] c\"" << llvmEscaped << "\"\n"; - -// std::string ptr = fresh(); -// out << " " << ptr << " = getelementptr inbounds [" -// << len << " x i8], [" << len << " x i8]* " -// << name << ", i64 0, i64 0\n"; - -// // Cast pointer to i64 if storing in i64* variable -// std::string casted = fresh(); -// out << " " << casted << " = ptrtoint i8* " << ptr << " to i64\n"; - -// noteType(casted, node->scope->lookupType("int64_t")); // casted is now i64 -// stringLiterals[value] = casted; -// return casted; -// } -// std::string CodeGenLLVM::generateBooleanLiteral(std::unique_ptr node) { -// std::string name = fresh(); -// out << " " << name << " = add i8 0, " -// << (node->value == "true" ? "1" : "0") << "\n"; -// noteType(name, node->scope->lookupType("boolean")); -// return name; -// } -// std::string CodeGenLLVM::generateVariableAccess(std::unique_ptr node) { -// auto &scope = *node->scope; -// auto name = node->value; - -// // Lookup type info -// auto ti = scope.lookupType(scope.lookupVariable(name).type); -// std::string ty = ti.isFloat -// ? (ti.bits == 32 ? "float" : "double") -// : "i" + std::to_string(ti.bits); - -// // Determine if it's a global or local variable -// bool isGlobal = scope.isGlobalVariable(name); -// std::string ptr; -// if (!isGlobal) { -// ptr = "%" + node->scope->getMapping(name); -// } else { -// ptr = "@" + name; -// } - -// // Generate load instruction -// std::string loaded = fresh(); -// out << " " << loaded << " = load " << ty << ", " << ty << "* " << ptr << "\n"; - -// noteType(loaded, ti); -// return loaded; -// } -// std::string CodeGenLLVM::generateBinaryOperation(std::unique_ptr node) { -// auto lhs = emitExpression(std::move(node->children[0])); -// auto rhs = emitExpression(std::move(node->children[1])); -// TypeInfo t1 = regType[lhs]; -// TypeInfo t2 = regType[rhs]; -// TypeInfo tr = TypeChecker::promoteType(t1, t2); -// std::string L = castValue(lhs, t1, tr); -// std::string R = castValue(rhs, t2, tr); -// std::string res = fresh(); - -// if (tr.isFloat) { -// std::string target = (tr.bits == 64 ? "double" : "float"); -// static const std::unordered_map fp_ops = {{"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; -// static const std::unordered_map fcmp_ops = {{"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; - -// if (fp_ops.count(node->value)) { -// out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; -// noteType(res, tr); -// return res; -// } else { -// std::string cmpop = fcmp_ops.at(node->value); -// out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; -// std::string zext = fresh(); -// out << " " << zext << " = zext i1 " << res << " to i8\n"; -// noteType(zext, node->scope->lookupType("boolean")); -// return zext; -// } -// } else { -// std::string intTy = "i" + std::to_string(tr.bits); -// bool isSigned = tr.isSigned; - -// if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { -// std::string op; -// if (node->value == "+") -// op = "add"; -// else if (node->value == "-") -// op = "sub"; -// else if (node->value == "*") -// op = "mul"; -// else /* "/" */ -// op = (isSigned ? "sdiv" : "udiv"); - -// out << " " << res << " = " << op << " " << intTy << " " << L << ", " << R << "\n"; -// noteType(res, tr); -// return res; -// } - -// static const std::unordered_map> cmp_map = { -// {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; - -// auto it = cmp_map.find(node->value); -// if (it != cmp_map.end()) { -// std::string signedOp = it->second.first; -// std::string unsignedOp = it->second.second; -// std::string cmpop = isSigned ? signedOp : unsignedOp; - -// out << " " << res << " = icmp " << cmpop << " " << intTy << " " << L << ", " << R << "\n"; -// std::string zext = fresh(); -// out << " " << zext << " = zext i1 " << res << " to i8\n"; -// noteType(zext, node->scope->lookupType("boolean")); -// return zext; -// } - -// throw std::runtime_error("Unsupported integer op " + node->value); -// } -// } -// std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node) { -// auto &scope = *node->children[0]->scope; -// auto varName = node->children[0]->value; -// TypeInfo ti = scope.lookupType(scope.lookupVariable(varName).type); -// NodeType child_type = node->children[0]->type; -// auto val = emitExpression(std::move(node->children[0])); - -// std::string llvmType; -// if (ti.isFloat) -// llvmType = (ti.bits == 32) ? "float" : "double"; -// else -// llvmType = "i" + std::to_string(ti.bits); - -// if (node->value == "!") { -// TypeInfo boolType = node->scope->lookupType("boolean"); -// val = castValue(val, regType.at(val), boolType); -// std::string res = fresh(); -// out << " " << res << " = icmp eq " << llvmType << " " << val << ", 0\n"; -// std::string zero = fresh(); -// out << " " << zero << " = zext i1 " << res << " to i8\n"; -// noteType(zero, node->scope->lookupType("boolean")); -// return zero; -// } else if (node->value == "++" || node->value == "--") { -// if (child_type != NodeType::VariableAccess) -// throw std::runtime_error(node->value + " can only be applied to variables"); - -// if (ti.isFloat) -// throw std::runtime_error("Increment/Decrement not supported on float"); - -// bool isGlobal = scope.isGlobalVariable(varName); -// std::string ptr = (isGlobal ? "@" + varName : "%" + node->scope->getMapping(varName)); - -// std::string cur = fresh(); -// out << " " << cur << " = load " << llvmType << ", " << llvmType << "* " << ptr << "\n"; - -// std::string updated = fresh(); -// out << " " << updated << " = " -// << (node->value == "++" ? "add" : "sub") -// << " " << llvmType << " " << cur << ", 1\n"; - -// out << " store " << llvmType << " " << updated << ", " << llvmType << "* " << ptr << "\n"; - -// noteType(updated, ti); -// return updated; -// } else { -// throw std::runtime_error("Unsupported unary: " + node->value); -// } -// } -// std::string CodeGenLLVM::emitExpression(std::unique_ptr node) { -// switch (node->type) { -// case NodeType::IntegerLiteral: -// return generateIntegerLiteral(std::move(node)); -// case NodeType::FloatLiteral: -// return generateFloatLiteral(std::move(node)); -// case NodeType::StringLiteral: -// return generateStringLiteral(std::move(node)); -// case NodeType::BooleanLiteral: -// return generateBooleanLiteral(std::move(node)); -// case NodeType::VariableAccess: -// return generateVariableAccess(std::move(node)); -// case NodeType::BinaryOp: -// return generateBinaryOperation(std::move(node)); -// case NodeType::UnaryOp: -// return generateUnaryOperation(std::move(node)); -// default: -// node->print(std::cout, 0); -// throw std::runtime_error("Unknown expression encountered."); -// } -// } -// void CodeGenLLVM::emitEpilogue(std::unique_ptr blockNode) { -// out << " ; Block ends\n"; -// } -// void CodeGenLLVM::emitPrologue(std::unique_ptr blockNode) { -// } -// void CodeGenLLVM::generateStatement(std::unique_ptr statement) { -// switch (statement->type) { -// case NodeType::VariableReassignment: { -// generateVariableReassignment(std::move(statement)); -// break; -// } -// case NodeType::VariableDeclaration: { -// generateVariableDeclaration(std::move(statement)); -// break; -// } -// case NodeType::IfStatement: { -// generateIfStatement(std::move(statement)); -// break; -// } -// case NodeType::UnaryOp: { -// if (statement->value == "--" or statement->value == "++") { -// std::string reg = emitExpression(std::move(statement)); -// } -// break; -// } -// case NodeType::BinaryOp: { -// std::string reg = emitExpression(std::move(statement)); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. -// break; -// } -// default: -// statement->print(std::cout, 0); -// throw std::runtime_error("Unknown statement encountered."); -// } -// } -// void CodeGenLLVM::generateVariableReassignment(std::unique_ptr node) { -// auto &scope = *node->scope; -// auto name = node->value; -// TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); -// bool isGlobal = scope.isGlobalVariable(name); - -// // 1) Compute the RHS expression -// std::string val = emitExpression(std::move(node->children.back())); -// TypeInfo tr = regType[val]; - -// // 2) Convert value to target type (ti) using castValue -// std::string castedVal = castValue(val, tr, ti); - -// // 3) Emit the store to the correct location -// std::string ty = ti.isFloat -// ? (ti.bits == 32 ? "float" : "double") -// : "i" + std::to_string(ti.bits); - -// if (isGlobal) { -// out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; -// } else { -// out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; -// } -// } -// void CodeGenLLVM::generateVariableDeclaration(std::unique_ptr node) { -// bool isGlobal = node->scope->isGlobalVariable(node->value); -// TypeInfo ti = node->scope->lookupType(node->scope->lookupVariable(node->value).type); -// std::string ty = ti.isFloat -// ? (ti.bits == 32 ? "float" : "double") -// : "i" + std::to_string(ti.bits); - -// if (!isGlobal) { -// out << " %" << node->scope->getMapping(node->value) << " = alloca " << ty << "\n"; -// } - -// if (node->children.size() >= 2) { -// auto val = emitExpression(std::move(node->children.back())); -// TypeInfo tr = regType[val]; - -// // Use castValue for all type conversions -// std::string castedVal = castValue(val, tr, ti); - -// if (isGlobal) { -// out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; -// } else { -// out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; -// } -// } -// } -// void CodeGenLLVM::generateIfStatement(std::unique_ptr statement) { -// int id = blockLabelCount++; -// std::string thenLbl = "if.then" + std::to_string(id); -// std::string elseLbl = "if.else" + std::to_string(id); -// std::string endLbl = "if.end" + std::to_string(id); - -// auto condVal = emitExpression(std::move(statement->children[0])); -// TypeInfo condTi = regType[condVal]; - -// std::string condBool = fresh(); -// out << " " << condBool -// << " = trunc i" << condTi.bits -// << " " << condVal << " to i1\n"; - -// out << " br i1 " << condBool -// << ", label %" << thenLbl -// << ", label %" -// << (statement->getElseBranch() ? elseLbl : endLbl) -// << "\n\n"; - -// out << thenLbl << ":\n"; -// { -// auto ifBlock = std::move(statement->children[1]); -// auto children = std::move(ifBlock->children); -// emitPrologue(std::move(ifBlock)); -// for (auto &stmt : children) -// generateStatement(std::move(stmt)); -// emitEpilogue(); -// } -// out << " br label %" << endLbl << "\n\n"; - -// ASTNode *branch = statement->getElseBranch(); -// if (branch) { -// out << elseLbl << ":\n"; - -// while (branch) { -// if (branch->type == NodeType::ElseIfStatement) { -// int elifId = blockLabelCount++; -// std::string elifThen = "elif.then" + std::to_string(elifId); -// std::string elifNext = "elif.next" + std::to_string(elifId); - -// auto elifCond = emitExpression(std::move(branch->children[0])); -// TypeInfo elifTi = regType[elifCond]; -// std::string elifBool = fresh(); -// out << " " << elifBool -// << " = trunc i" << elifTi.bits -// << " " << elifCond << " to i1\n"; -// out << " br i1 " << elifBool -// << ", label %" << elifThen -// << ", label %" << elifNext -// << "\n\n"; - -// out << elifThen << ":\n"; -// { -// auto elifBlock = std::move(branch->children[1]); -// auto elifChildren = std::move(elifBlock->children); -// emitPrologue(std::move(elifBlock)); -// for (auto &stmt : elifChildren) -// generateStatement(std::move(stmt)); -// emitEpilogue(); -// } -// out << " br label %" << endLbl << "\n\n"; -// out << elifNext << ":\n"; -// branch = branch->getElseBranch(); -// if (!branch) { -// out << " br label %" << endLbl << "\n\n"; -// } -// } else if (branch->type == NodeType::ElseStatement) { -// auto elseBlock = std::move(branch->children[0]); -// auto elseChildren = std::move(elseBlock->children); -// emitPrologue(std::move(elseBlock)); -// for (auto &stmt : elseChildren) -// generateStatement(std::move(stmt)); -// emitEpilogue(); -// out << " br label %" << endLbl << "\n\n"; -// branch = nullptr; -// } else { -// throw std::runtime_error("Unexpected node type in else chain"); -// } -// } -// } - -// // 5) end label -// out << endLbl << ":\n"; -// } -// void CodeGenLLVM::generate(std::unique_ptr program) { -// outGlobal << "; ModuleID = 'zlang'\n"; -// outGlobal << "source_filename = \"zlang\"\n"; -// // Emit global variable definitions -// for (auto &statement : program->children) { -// if (statement->type == NodeType::VariableDeclaration) { -// auto &name = statement->value; -// auto ti = statement->scope->lookupType( -// statement->scope->lookupVariable(name).type); -// std::string ty = ti.isFloat -// ? (ti.bits == 32 ? "float" : "double") -// : "i" + std::to_string(ti.bits); -// outGlobal << "@" << name << " = global " << ty << (ti.isFloat ? " 0.0" : " 0") << "\n"; -// } -// } -// outGlobal << "\n"; - -// // Define main function -// out -// << "define i32 @main() {\n"; -// emitPrologue(nullptr); -// // Generate each top-level statement -// for (auto &stmt : program->children) { -// generateStatement(std::move(stmt)); -// } -// out << " ret i32 0\n"; -// out -// << "}\n"; - -// outfinal << outGlobal.str() + "\n\n" -// << out.str() << "\n\n"; -// } -// std::string CodeGenLLVM::generateFunctionCall(std::unique_ptr node) { -// return ""; -// } -// void CodeGenLLVM::generateFunctionDeclaration(std::unique_ptr node) {} -// void CodeGenLLVM::generateExternFunctionDeclaration(std::unique_ptr node) {} -// } // namespace zlang \ No newline at end of file +#include "all.hpp" + +namespace zlang { + + std::string llvmTypeName(const std::string &typeName) { + if (typeName == "boolean") + return "i8"; // Boolean as 1-bit integer + if (typeName == "none") + return "void"; // No type + if (typeName == "string") + return "i8*"; // Pointer to characters + if (typeName == "size_t") + return "i64"; // Platform word size + if (typeName == "integer") + return "i64"; // Default signed integer + + // Unsigned integers + if (typeName == "uint8_t") + return "i8"; + if (typeName == "uint16_t") + return "i16"; + if (typeName == "uint32_t") + return "i32"; + if (typeName == "uint64_t") + return "i64"; + + // Signed integers + if (typeName == "int8_t") + return "i8"; + if (typeName == "int16_t") + return "i16"; + if (typeName == "int32_t") + return "i32"; + if (typeName == "int64_t") + return "i64"; + + // Floating-point types + if (typeName == "float") + return "float"; + if (typeName == "double") + return "double"; + + // Fallback for user-defined or pointer types + return typeName; + } + + std::string unescapeString(const std::string &input) { + std::string result; + result.reserve(input.size()); + + for (size_t i = 0; i < input.size(); ++i) { + if (input[i] == '\\' && i + 1 < input.size()) { + switch (input[++i]) { + case 'n': + result += '\n'; + break; + case 't': + result += '\t'; + break; + case 'r': + result += '\r'; + break; + case '\\': + result += '\\'; + break; + case '"': + result += '"'; + break; + case '0': + result += '\0'; + break; // Null byte + default: // Keep unrecognized escapes as-is + result += '\\'; + result += input[i]; + break; + } + } else { + result += input[i]; + } + } + return result; + } + + static std::string fresh() { + static int cnt = 0; + return "%tmp" + std::to_string(cnt++); + } + std::string toHexFloatFromStr(const std::string &valStr, bool isF32, std::ostringstream &out) { + std::ostringstream oss; + oss << std::scientific << std::setprecision(16); + if (isF32) { + float v = std::stof(valStr); + oss << v; + } else { + double v = std::stod(valStr); + oss << v; + } + return oss.str(); + } + std::string CodeGenLLVM::castValue(const std::string &val, const TypeInfo &fromType, const TypeInfo &toType, std::ostringstream &out) { + if (fromType.isFloat == toType.isFloat && fromType.bits == toType.bits) { + return val; + } + + std::string tmp = fresh(); + + if (!fromType.isFloat && toType.isFloat) { + std::string intTy = "i" + std::to_string(fromType.bits); + std::string floatTy = (toType.bits == 64 ? "double" : "float"); + std::string instr = fromType.isSigned ? "sitofp" : "uitofp"; + out << " " << tmp << " = " << instr << " " << intTy << " " << val << " to " << floatTy << "\n"; + } else if (fromType.isFloat && !toType.isFloat) { + std::string floatTy = (fromType.bits == 64 ? "double" : "float"); + std::string intTy = "i" + std::to_string(toType.bits); + std::string instr = toType.isSigned ? "fptosi" : "fptoui"; + out << " " << tmp << " = " << instr << " " << floatTy << " " << val << " to " << intTy << "\n"; + } else if (!fromType.isFloat && !toType.isFloat) { + std::string fromTy = "i" + std::to_string(fromType.bits); + std::string toTy = "i" + std::to_string(toType.bits); + if (fromType.bits < toType.bits) { + std::string instr = fromType.isSigned ? "sext" : "zext"; + out << " " << tmp << " = " << instr << " " << fromTy << " " << val << " to " << toTy << "\n"; + } else if (fromType.bits > toType.bits) { + out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\n"; + } else if (fromType.isSigned != toType.isSigned) { + // Retag value if signedness differs but bit-width is the same + out << " " << tmp << " = add " << fromTy << " " << val << ", 0\n"; + } else { + return val; + } + } else if (fromType.isFloat && toType.isFloat) { + std::string fromTy = (fromType.bits == 64 ? "double" : "float"); + std::string toTy = (toType.bits == 64 ? "double" : "float"); + if (fromType.bits < toType.bits) { + out << " " << tmp << " = fpext " << fromTy << " " << val << " to " << toTy << "\n"; + } else if (fromType.bits > toType.bits) { + out << " " << tmp << " = fptrunc " << fromTy << " " << val << " to " << toTy << "\n"; + } else { + return val; + } + } else { + throw std::runtime_error("Unsupported cast from type to type"); + } + + noteType(tmp, toType); + return tmp; + } + std::string CodeGenLLVM::generateIntegerLiteral(std::unique_ptr node, std::ostringstream &out) { + std::string name = fresh(); + out << " " << name << " = add i64 0, " << node->value << "\n"; + noteType(name, node->scope->lookupType("int64_t")); + return name; + } + std::string CodeGenLLVM::generateFloatLiteral(std::unique_ptr node, std::ostringstream &out) { + std::string tmp = fresh(); + std::string val = node->value; + bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); + if (isF32) + val.pop_back(); + + std::string hexVal = toHexFloatFromStr(val, isF32, out); + + if (isF32) { + out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; + noteType(tmp, node->scope->lookupType("float")); + } else { + out << " " << tmp << " = fadd double 0.0, " << hexVal << "\n"; + noteType(tmp, node->scope->lookupType("double")); + } + return tmp; + } + std::string CodeGenLLVM::generateStringLiteral(std::unique_ptr node, std::ostringstream &out) { + std::string value = node->value; + + std::string name = "@.str" + std::to_string(stringLabelCount++); + std::string unescaped = unescapeString(value); + size_t total_bytes = unescaped.size() + 1; // +1 for null terminator + + std::string llvmEscaped; + for (unsigned char c : unescaped) { + if (isprint(c) && c != '"' && c != '\\') { + llvmEscaped += c; + } else { + char buf[5]; + snprintf(buf, sizeof(buf), "\\%02x", c); + llvmEscaped += buf; + } + } + llvmEscaped += "\\00"; + + outGlobalStream << name << " = private unnamed_addr constant [" + << total_bytes << " x i8] c\"" << llvmEscaped << "\"\n"; + + std::string ptr = fresh(); + out << " " << ptr << " = getelementptr inbounds [" + << total_bytes << " x i8], [" << total_bytes << " x i8]* " + << name << ", i64 0, i64 0\n"; + + // Cast pointer to i64 if storing in i64* variable + std::string casted = fresh(); + out << " " << casted << " = ptrtoint i8* " << ptr << " to i64\n"; + + noteType(casted, node->scope->lookupType("int64_t")); // casted is now i64 + stringLiterals[value] = casted; + return casted; + } + std::string CodeGenLLVM::generateBooleanLiteral(std::unique_ptr node, std::ostringstream &out) { + std::string name = fresh(); + out << " " << name << " = add i8 0, " + << (node->value == "true" ? "1" : "0") << "\n"; + noteType(name, node->scope->lookupType("boolean")); + return name; + } + std::string CodeGenLLVM::generateVariableAccess(std::unique_ptr node, std::ostringstream &out) { + auto &scope = *node->scope; + auto name = node->value; + + // Lookup type info + auto ti = scope.lookupType(scope.lookupVariable(name).type); + std::string ty = ti.isFloat + ? (ti.bits == 32 ? "float" : "double") + : "i" + std::to_string(ti.bits); + + // Determine if it's a global or local variable + bool isGlobal = scope.isGlobalVariable(name); + std::string ptr; + if (!isGlobal) { + ptr = "%" + node->scope->getMapping(name); + } else { + ptr = "@" + name; + } + + // Generate load instruction + std::string loaded = fresh(); + out << " " << loaded << " = load " << ty << ", " << ty << "* " << ptr << "\n"; + + noteType(loaded, ti); + return loaded; + } + std::string CodeGenLLVM::generateBinaryOperation(std::unique_ptr node, std::ostringstream &out) { + auto lhs = emitExpression(std::move(node->children[0]), out); + auto rhs = emitExpression(std::move(node->children[1]), out); + TypeInfo t1 = regType[lhs]; + TypeInfo t2 = regType[rhs]; + TypeInfo tr = TypeChecker::promoteType(t1, t2); + std::string L = castValue(lhs, t1, tr, out); + std::string R = castValue(rhs, t2, tr, out); + std::string res = fresh(); + + if (tr.isFloat) { + std::string target = (tr.bits == 64 ? "double" : "float"); + static const std::unordered_map fp_ops = {{"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; + static const std::unordered_map fcmp_ops = {{"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; + + if (fp_ops.count(node->value)) { + out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; + noteType(res, tr); + return res; + } else { + std::string cmpop = fcmp_ops.at(node->value); + out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; + std::string zext = fresh(); + out << " " << zext << " = zext i1 " << res << " to i8\n"; + noteType(zext, node->scope->lookupType("boolean")); + return zext; + } + } else { + std::string intTy = "i" + std::to_string(tr.bits); + bool isSigned = tr.isSigned; + + if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { + std::string op; + if (node->value == "+") + op = "add"; + else if (node->value == "-") + op = "sub"; + else if (node->value == "*") + op = "mul"; + else /* "/" */ + op = (isSigned ? "sdiv" : "udiv"); + + out << " " << res << " = " << op << " " << intTy << " " << L << ", " << R << "\n"; + noteType(res, tr); + return res; + } + + static const std::unordered_map> cmp_map = { + {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; + + auto it = cmp_map.find(node->value); + if (it != cmp_map.end()) { + std::string signedOp = it->second.first; + std::string unsignedOp = it->second.second; + std::string cmpop = isSigned ? signedOp : unsignedOp; + + out << " " << res << " = icmp " << cmpop << " " << intTy << " " << L << ", " << R << "\n"; + std::string zext = fresh(); + out << " " << zext << " = zext i1 " << res << " to i8\n"; + noteType(zext, node->scope->lookupType("boolean")); + return zext; + } + + throw std::runtime_error("Unsupported integer op " + node->value); + } + } + std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node, std::ostringstream &out) { + auto &scope = *node->children[0]->scope; + auto varName = node->children[0]->value; + TypeInfo ti = scope.lookupType(scope.lookupVariable(varName).type); + NodeType child_type = node->children[0]->type; + auto val = emitExpression(std::move(node->children[0]), out); + + std::string llvmType; + if (ti.isFloat) + llvmType = (ti.bits == 32) ? "float" : "double"; + else + llvmType = "i" + std::to_string(ti.bits); + + if (node->value == "!") { + TypeInfo boolType = node->scope->lookupType("boolean"); + val = castValue(val, regType.at(val), boolType, out); + std::string res = fresh(); + out << " " << res << " = icmp eq " << llvmType << " " << val << ", 0\n"; + std::string zero = fresh(); + out << " " << zero << " = zext i1 " << res << " to i8\n"; + noteType(zero, node->scope->lookupType("boolean")); + return zero; + } else if (node->value == "++" || node->value == "--") { + if (child_type != NodeType::VariableAccess) + throw std::runtime_error(node->value + " can only be applied to variables"); + + if (ti.isFloat) + throw std::runtime_error("Increment/Decrement not supported on float"); + + bool isGlobal = scope.isGlobalVariable(varName); + std::string ptr = (isGlobal ? "@" + varName : "%" + node->scope->getMapping(varName)); + + std::string cur = fresh(); + out << " " << cur << " = load " << llvmType << ", " << llvmType << "* " << ptr << "\n"; + + std::string updated = fresh(); + out << " " << updated << " = " + << (node->value == "++" ? "add" : "sub") + << " " << llvmType << " " << cur << ", 1\n"; + + out << " store " << llvmType << " " << updated << ", " << llvmType << "* " << ptr << "\n"; + + noteType(updated, ti); + return updated; + } else { + throw std::runtime_error("Unsupported unary: " + node->value); + } + } + std::string CodeGenLLVM::emitExpression(std::unique_ptr node, std::ostringstream &out) { + switch (node->type) { + case NodeType::IntegerLiteral: + return generateIntegerLiteral(std::move(node), out); + case NodeType::FloatLiteral: + return generateFloatLiteral(std::move(node), out); + case NodeType::StringLiteral: + return generateStringLiteral(std::move(node), out); + case NodeType::BooleanLiteral: + return generateBooleanLiteral(std::move(node), out); + case NodeType::VariableAccess: + return generateVariableAccess(std::move(node), out); + case NodeType::BinaryOp: + return generateBinaryOperation(std::move(node), out); + case NodeType::UnaryOp: + return generateUnaryOperation(std::move(node), out); + case NodeType::FunctionCall: + return generateFunctionCall(std::move(node), out); + default: + throw std::runtime_error("Unknown statement encountered."); + } + } + void CodeGenLLVM::emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax) { + out << " ; Block ends\n"; + } + void CodeGenLLVM::emitPrologue(std::shared_ptr scope, std::ostringstream &out) { + } + void CodeGenLLVM::generateStatement(std::unique_ptr statement, std::ostringstream &out) { + switch (statement->type) { + case NodeType::VariableReassignment: { + generateVariableReassignment(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::VariableDeclaration: { + generateVariableDeclaration(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::IfStatement: { + generateIfStatement(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::UnaryOp: { + std::string op = statement->value; + std::string reg = emitExpression(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::BinaryOp: { + std::string reg = emitExpression(std::move(statement), out); // I am doing this just so the increments/decrements work in x + y-- -> this itself must not have any result, but y-- should still be effective. + out << "\n"; + break; + } + case NodeType::FunctionCall: { + std::string reg = generateFunctionCall(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::Function: { + generateFunctionDeclaration(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::ExternFunction: { + generateExternFunctionDeclaration(std::move(statement), out); + out << "\n"; + break; + } + case NodeType::ReturnStatement: { + generateReturnstatement(std::move(statement), out); + out << "\n"; + break; + } + default: + statement->print(std::cout, 0); + throw std::runtime_error("Unknown statement encountered."); + } + } + void CodeGenLLVM::generateVariableReassignment(std::unique_ptr node, std::ostringstream &out) { + auto &scope = *node->scope; + auto name = node->value; + TypeInfo ti = scope.lookupType(scope.lookupVariable(name).type); + bool isGlobal = scope.isGlobalVariable(name); + + // 1) Compute the RHS expression + std::string val = emitExpression(std::move(node->children.back()), out); + TypeInfo tr = regType[val]; + + // 2) Convert value to target type (ti) using castValue + std::string castedVal = castValue(val, tr, ti, out); + + // 3) Emit the store to the correct location + std::string ty = ti.isFloat + ? (ti.bits == 32 ? "float" : "double") + : "i" + std::to_string(ti.bits); + + if (isGlobal) { + out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; + } else { + out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; + } + } + void CodeGenLLVM::generateVariableDeclaration(std::unique_ptr node, std::ostringstream &out) { + bool isGlobal = node->scope->isGlobalVariable(node->value); + TypeInfo ti = node->scope->lookupType(node->scope->lookupVariable(node->value).type); + std::string ty = ti.isFloat + ? (ti.bits == 32 ? "float" : "double") + : "i" + std::to_string(ti.bits); + + if (!isGlobal) { + out << " %" << node->scope->getMapping(node->value) << " = alloca " << ty << "\n"; + } + + if (node->children.size() >= 2) { + auto val = emitExpression(std::move(node->children.back()), out); + TypeInfo tr = regType[val]; + + std::string castedVal = castValue(val, tr, ti, out); + + if (isGlobal) { + out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; + } else { + out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; + } + } + } + void CodeGenLLVM::generateIfStatement(std::unique_ptr statement, std::ostringstream &out) { + int id = blockLabelCount++; + std::string thenLbl = "if.then" + std::to_string(id); + std::string elseLbl = "if.else" + std::to_string(id); + std::string endLbl = "if.end" + std::to_string(id); + + auto condVal = emitExpression(std::move(statement->children[0]), out); + TypeInfo condTi = regType[condVal]; + + std::string condBool = fresh(); + out << " " << condBool + << " = trunc i" << condTi.bits + << " " << condVal << " to i1\n"; + + out << " br i1 " << condBool + << ", label %" << thenLbl + << ", label %" + << (statement->getElseBranch() ? elseLbl : endLbl) + << "\n\n"; + + out << thenLbl << ":\n"; + { + auto ifBlock = std::move(statement->children[1]); + auto children = std::move(ifBlock->children); + emitPrologue(ifBlock->scope, out); + for (auto &stmt : children) + generateStatement(std::move(stmt), out); + emitEpilogue(ifBlock->scope, out); + } + out << " br label %" << endLbl << "\n\n"; + + ASTNode *branch = statement->getElseBranch(); + if (branch) { + out << elseLbl << ":\n"; + + while (branch) { + if (branch->type == NodeType::ElseIfStatement) { + int elifId = blockLabelCount++; + std::string elifThen = "elif.then" + std::to_string(elifId); + std::string elifNext = "elif.next" + std::to_string(elifId); + + auto elifCond = emitExpression(std::move(branch->children[0]), out); + TypeInfo elifTi = regType[elifCond]; + std::string elifBool = fresh(); + out << " " << elifBool + << " = trunc i" << elifTi.bits + << " " << elifCond << " to i1\n"; + out << " br i1 " << elifBool + << ", label %" << elifThen + << ", label %" << elifNext + << "\n\n"; + + out << elifThen << ":\n"; + { + auto elifBlock = std::move(branch->children[1]); + auto elifChildren = std::move(elifBlock->children); + emitPrologue(elifBlock->scope, out); + for (auto &stmt : elifChildren) + generateStatement(std::move(stmt), out); + emitEpilogue(elifBlock->scope, out); + } + out << " br label %" << endLbl << "\n\n"; + out << elifNext << ":\n"; + branch = branch->getElseBranch(); + if (!branch) { + out << " br label %" << endLbl << "\n\n"; + } + } else if (branch->type == NodeType::ElseStatement) { + auto elseBlock = std::move(branch->children[0]); + auto elseChildren = std::move(elseBlock->children); + emitPrologue(elseBlock->scope, out); + for (auto &stmt : elseChildren) + generateStatement(std::move(stmt), out); + emitEpilogue(elseBlock->scope, out); + out << " br label %" << endLbl << "\n\n"; + branch = nullptr; + } else { + throw std::runtime_error("Unexpected node type in else chain"); + } + } + } + + // 5) end label + out << endLbl << ":\n"; + } + void CodeGenLLVM::generate(std::unique_ptr program) { + outGlobalStream << "; ModuleID = 'zlang'\n" + << "source_filename = \"zlang\"\n\n"; + + // Globals + for (auto &stmt : program->children) { + if (stmt->type == NodeType::VariableDeclaration) { + const auto &name = stmt->value; + auto vi = stmt->scope->lookupVariable(name); + TypeInfo ti = stmt->scope->lookupType(vi.type); + std::string ty = llvmTypeName(ti.name); + outGlobalStream << "@" << name << " = global " << ty + << (ti.isFloat ? " 0.0" : (ti.name == "string" ? " null" : " 0")) + << "\n"; + } + } + outGlobalStream << "\n"; + + // Split main and decls + std::unique_ptr mainFunction; + std::vector> declarationsAndReassignments; + for (auto &stmt : program->children) { + if (stmt->type == NodeType::Function && stmt->value == "main") { + mainFunction = std::move(stmt); + } else if (stmt->type == NodeType::VariableDeclaration || stmt->type == NodeType::VariableReassignment || (stmt->type == NodeType::UnaryOp and (stmt->value == "++" || stmt->value == "--"))) { + declarationsAndReassignments.push_back(std::move(stmt)); + } else { + generateStatement(std::move(stmt), outStream); + } + } + + // Emit main + outStream << "define i32 @main() {\n"; + // Decls in main + for (auto &s : declarationsAndReassignments) { + generateStatement(std::move(s), outStream); + } + + for (auto &c : mainFunction->getFunctionBody()->children) generateStatement(std::move(c), outStream); + outStream << " ret i32 0\n"; + outStream << "}\n"; + + outfinal << outGlobalStream.str() << "\n" + << outStream.str() << "\n"; + } + + std::string CodeGenLLVM::generateFunctionCall(std::unique_ptr node, std::ostringstream &out) { + // Lookup function metadata + auto fnInfo = node->scope->lookupFunction(node->value); + const std::string &funcName = fnInfo.isExtern ? node->value : fnInfo.label; + auto &args = node->children[0]->children; + const auto ¶mTypes = fnInfo.paramTypes; + + // Emit each argument, casting to expected type + std::vector> argList; + for (size_t i = 0; i < args.size(); ++i) { + // Evaluate expression + std::string src = emitExpression(std::move(args[i]), out); + + out << "; Source: " << src << " \n"; + + // Determine types + TypeInfo passed = regType.at(src); + bool passedIsFloat = passed.isFloat; + TypeInfo expect; + if (i < paramTypes.size()) { + expect = node->scope->lookupType(paramTypes[i].type); + } else if (fnInfo.isVariadic) { + expect = node->scope->lookupType(passedIsFloat ? "double" : "int64_t"); + } else { + throw std::runtime_error("Too many arguments for function '" + node->value + "'"); + } + + // Cast value + std::string cvt = castValue(src, passed, expect, out); + + // Determine LLVM type + std::string ty = llvmTypeName(expect.name); + if (expect.isPointer) { + std::string tmpPtr = fresh(); + out << " " << tmpPtr << " = inttoptr i64 " << cvt << " to " << llvmTypeName(expect.name) << "\n"; + cvt = tmpPtr; + } + + argList.emplace_back(ty, cvt); + } + + // Prepare call result + TypeInfo rti = node->scope->lookupType(fnInfo.returnType); + std::string retTy = llvmTypeName(rti.name); + bool isVariadic = fnInfo.isVariadic; + + std::string callRes; + if (retTy != "void") { + callRes = fresh(); + out << " " << callRes << " = "; + } else { + out << " "; + } + + // Emit call + out << "call " << retTy; + if (isVariadic && !argList.empty()) { + out << " (" << argList[0].first << ", ...)"; + } + out << " @" << funcName << "("; + for (size_t i = 0; i < argList.size(); ++i) { + if (i) + out << ", "; + out << argList[i].first << " " << argList[i].second; + } + out << ")\n"; + + noteType(callRes, node->scope->lookupType(fnInfo.returnType)); + + return callRes; + } + void CodeGenLLVM::generateFunctionDeclaration(std::unique_ptr node, std::ostringstream &out, bool force) { + const std::string &name = node->value; + bool isMain = (name == "main"); + if (isMain && !force) + return; // skip main if not forced + node->print(std::cout, 0); + auto fnInfo = node->scope->lookupFunction(name); + auto bodyNode = node->getFunctionBody(); + auto funcScope = std::static_pointer_cast(bodyNode->scope->findEnclosingFunctionScope()); + + // Function signature + TypeInfo rti = node->scope->lookupType(fnInfo.returnType); + std::string retTy = llvmTypeName(rti.name); + out << "define " << retTy << " @" << fnInfo.label << "("; + // Parameters + for (size_t i = 0; i < fnInfo.paramTypes.size(); ++i) { + const auto &p = fnInfo.paramTypes[i]; + TypeInfo pti = node->scope->lookupType(p.type); + out << llvmTypeName(pti.name) << " %" << bodyNode->scope->getMapping(p.name); + if (i + 1 < fnInfo.paramTypes.size()) + out << ", "; + } + out << ") {\n"; + + // Emit prologue + emitPrologue(funcScope, out); + + // TODO: We were here last night, now we take one day break. + + // Map arguments into LLVM locals + for (const auto &p : fnInfo.paramTypes) { + TypeInfo pti = node->scope->lookupType(p.type); + std::string allocaReg = fresh() + "___arg___"; + out << " " << allocaReg << " = alloca " + << llvmTypeName(pti.name) << "\n"; + out << " store " << llvmTypeName(pti.name) + << " %" << bodyNode->scope->getMapping(p.name) + << ", " << llvmTypeName(pti.name) + << "* " << allocaReg << "\n"; + funcScope->setMapping(p.name, allocaReg.substr(1)); // or pass without '%' if your getMapping adds it + } + + // Function body + std::ostringstream body; + std::vector> nested; + for (auto &stmt : bodyNode->children) { + if (stmt->type == NodeType::Function) { + nested.push_back(std::move(stmt)); + } else { + generateStatement(std::move(stmt), body); + } + } + out << body.str(); + + // Return or void + if (retTy != "void") { + } else { + out << " ret void\n"; + } + out << "}\n\n"; + + // Nested functions + for (auto &nf : nested) { + generateFunctionDeclaration(std::move(nf), out, false); + } + } + void CodeGenLLVM::generateExternFunctionDeclaration(std::unique_ptr node, std::ostringstream &out) { + auto fnInfo = node->scope->lookupFunction(node->value); + // Build a `declare` line: + // declare @