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..a820007 --- /dev/null +++ b/examples/functions.zz @@ -0,0 +1,17 @@ + +extern fn printf(fmt: string, ...) -> int32_t; + +let a: int64_t = 10; +let f: float = 20.5; +let u: uint8_t = 10; + +fn print_int(x: int64_t) -> none { + printf("%d\n", x); +} + +fn main() { + if (a > 100) { + let res: int64_t = a * 2; + print_int(res); + } +} 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..f98b88f 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, 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); 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..6cabc18 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, std::ostringstream &out) = 0; - virtual std::string emitExpression(std::unique_ptr node) = 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 void emitEpilogue() = 0; - virtual void emitPrologue(std::unique_ptr blockNode) = 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 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 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,25 +134,39 @@ 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); + + 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: ~CodeGenLinux() override = default; @@ -162,26 +174,38 @@ namespace zlang void generate(std::unique_ptr program) override; }; - class CodeGenWindows : public CodeGen - { + 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; + + 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; @@ -189,30 +213,38 @@ namespace zlang void generate(std::unique_ptr program) override; }; - class CodeGenLLVM : public CodeGen - { + 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 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 +} // 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..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, @@ -17,15 +15,20 @@ namespace zlang StringLiteral, BoolLiteral, Symbol, - Keyword, + Comma, + Arrow, EndOfFile, SemiColon, Equal, + Return, + Dot, + Ellipsis, If, ElseIf, Else, LeftBrace, RightBrace, + Function, LeftParen, RightParen, Unknown @@ -35,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) + @@ -44,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: @@ -64,8 +64,6 @@ namespace zlang return "StringLiteral"; case Kind::Symbol: return "Symbol"; - case Kind::Keyword: - return "Keyword"; case Kind::EndOfFile: return "EndOfFile"; case Kind::SemiColon: @@ -88,18 +86,28 @@ 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"; + 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); @@ -122,4 +130,4 @@ namespace zlang Token scanSymbol(); }; -} // namespace zlang +} // namespace zlang diff --git a/include/parser/NameMapper.hpp b/include/parser/NameMapper.hpp new file mode 100644 index 0000000..afd8b8e --- /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 = scopeName + "___" + name + "___v" + std::to_string(varCounter_++); + return mangled; + } + + inline std::string mapFunction(const std::string &name, const std::string &scopeName) + { + 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 = scopeName + "___" + name + "___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..e40df1c 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; + bool isVariadic; + 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); + 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(); + 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_) { + this->canary = canary_; + } + std::uint64_t getCanary() { + return this->canary; + } + std::string allocateSpillSlot(std::int64_t size, CodegenOutputFormat format); + 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..c268145 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,52 +44,56 @@ 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()) - { + switch (cli.getFormat()) { case CodegenOutputFormat::Default: #ifdef _WIN64 cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); @@ -105,20 +103,17 @@ int main(int argc, char *argv[]) #endif break; - case CodegenOutputFormat::X86_64_MSWIN: - { + case CodegenOutputFormat::X86_64_MSWIN: { cg = CodeGen::create(TargetTriple::X86_64_WINDOWS, *outstream); break; } - case CodegenOutputFormat::X86_64_LINUX: - { + case CodegenOutputFormat::X86_64_LINUX: { cg = CodeGen::create(TargetTriple::X86_64_LINUX, *outstream); break; } - case CodegenOutputFormat::LLVM_IR: - { + case CodegenOutputFormat::LLVM_IR: { cg = CodeGen::create(TargetTriple::LLVM_IR, *outstream); break; } @@ -127,12 +122,9 @@ int main(int argc, char *argv[]) std::cerr << "This should not happen, ACP Pradhyumn...\n"; exit(1); } - try - { + 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..94961db 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, 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, .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, 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); + 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, .isVariadic = isVariadic}); + 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..bff6770 100644 --- a/src/codegen/CodeGen.cpp +++ b/src/codegen/CodeGen.cpp @@ -1,12 +1,9 @@ #include "all.hpp" -namespace zlang -{ +namespace zlang { CodeGen::~CodeGen() = default; - std::unique_ptr CodeGen::create(TargetTriple target, std::ostream &outstream) - { - switch (target) - { + 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: diff --git a/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index 20f256d..4704735 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -1,178 +1,217 @@ #include "all.hpp" -namespace zlang -{ - static std::string fresh() - { +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::string toHexFloatFromStr(const std::string &valStr, bool isF32, std::ostringstream &out) { std::ostringstream oss; oss << std::scientific << std::setprecision(16); - if (isF32) - { + if (isF32) { float v = std::stof(valStr); oss << v; - } - else - { + } 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) + 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) - { + 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) - { + } 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) - { + } 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) - { + 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) - { + } else if (fromType.bits > toType.bits) { out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\n"; - } - else if (fromType.isSigned != toType.isSigned) - { + } 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 - { + } else { return val; } - } - else if (fromType.isFloat && toType.isFloat) - { + } 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) - { + if (fromType.bits < toType.bits) { out << " " << tmp << " = fpext " << fromTy << " " << val << " to " << toTy << "\n"; - } - else if (fromType.bits > toType.bits) - { + } else if (fromType.bits > toType.bits) { out << " " << tmp << " = fptrunc " << fromTy << " " << val << " to " << toTy << "\n"; - } - else - { + } else { return val; } - } - else - { + } 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 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::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); + std::string hexVal = toHexFloatFromStr(val, isF32, out); - if (isF32) - { + if (isF32) { out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; noteType(tmp, node->scope->lookupType("float")); - } - else - { + } 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 CodeGenLLVM::generateStringLiteral(std::unique_ptr node, std::ostringstream &out) { 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; + std::string unescaped = unescapeString(value); + size_t total_bytes = unescaped.size() + 1; // +1 for null terminator - for (unsigned char c : value) - { - if (isprint(c) && c != '"' && c != '\\') - { + std::string llvmEscaped; + for (unsigned char c : unescaped) { + if (isprint(c) && c != '"' && c != '\\') { llvmEscaped += c; - } - else - { + } else { char buf[5]; - snprintf(buf, sizeof(buf), "\\%02X", c); // Use uppercase hex + snprintf(buf, sizeof(buf), "\\%02x", c); llvmEscaped += buf; } } llvmEscaped += "\\00"; - outGlobal << name << " = private unnamed_addr constant [" - << len << " x i8] c\"" << llvmEscaped << "\"\n"; + + outGlobalStream << name << " = private unnamed_addr constant [" + << total_bytes << " x i8] c\"" << llvmEscaped << "\"\n"; std::string ptr = fresh(); out << " " << ptr << " = getelementptr inbounds [" - << len << " x i8], [" << len << " x i8]* " + << 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 + 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 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::string CodeGenLLVM::generateVariableAccess(std::unique_ptr node, std::ostringstream &out) { auto &scope = *node->scope; auto name = node->value; @@ -185,12 +224,9 @@ namespace zlang // Determine if it's a global or local variable bool isGlobal = scope.isGlobalVariable(name); std::string ptr; - if (!isGlobal) - { + if (!isGlobal) { ptr = "%" + node->scope->getMapping(name); - } - else - { + } else { ptr = "@" + name; } @@ -201,31 +237,26 @@ namespace zlang 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])); + 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); - std::string R = castValue(rhs, t2, tr); + std::string L = castValue(lhs, t1, tr, out); + std::string R = castValue(rhs, t2, tr, out); std::string res = fresh(); - if (tr.isFloat) - { + 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)) - { + if (fp_ops.count(node->value)) { out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; noteType(res, tr); return res; - } - else - { + } else { std::string cmpop = fcmp_ops.at(node->value); out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; std::string zext = fresh(); @@ -233,14 +264,11 @@ namespace zlang noteType(zext, node->scope->lookupType("boolean")); return zext; } - } - else - { + } else { std::string intTy = "i" + std::to_string(tr.bits); bool isSigned = tr.isSigned; - if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") - { + if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { std::string op; if (node->value == "+") op = "add"; @@ -260,8 +288,7 @@ namespace zlang {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; auto it = cmp_map.find(node->value); - if (it != cmp_map.end()) - { + if (it != cmp_map.end()) { std::string signedOp = it->second.first; std::string unsignedOp = it->second.second; std::string cmpop = isSigned ? signedOp : unsignedOp; @@ -276,13 +303,12 @@ namespace zlang throw std::runtime_error("Unsupported integer op " + node->value); } } - std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node) - { + 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])); + auto val = emitExpression(std::move(node->children[0]), out); std::string llvmType; if (ti.isFloat) @@ -290,19 +316,16 @@ namespace zlang else llvmType = "i" + std::to_string(ti.bits); - if (node->value == "!") - { + if (node->value == "!") { TypeInfo boolType = node->scope->lookupType("boolean"); - val = castValue(val, regType.at(val), boolType); + 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 == "--") - { + } else if (node->value == "++" || node->value == "--") { if (child_type != NodeType::VariableAccess) throw std::runtime_error(node->value + " can only be applied to variables"); @@ -324,72 +347,83 @@ namespace zlang noteType(updated, ti); return updated; - } - else - { + } else { throw std::runtime_error("Unsupported unary: " + node->value); } } - std::string CodeGenLLVM::emitExpression(std::unique_ptr node) - { - switch (node->type) - { + std::string CodeGenLLVM::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: - node->print(std::cout, 0); - throw std::runtime_error("Unknown expression encountered."); + throw std::runtime_error("Unknown statement encountered."); } } - void CodeGenLLVM::emitEpilogue() - { + void CodeGenLLVM::emitEpilogue(std::shared_ptr scope, std::ostringstream &out, bool clearRax) { out << " ; Block ends\n"; } - void CodeGenLLVM::emitPrologue(std::unique_ptr blockNode) - { + void CodeGenLLVM::emitPrologue(std::shared_ptr scope, std::ostringstream &out) { } - void CodeGenLLVM::generateStatement(std::unique_ptr statement) - { - switch (statement->type) - { - case NodeType::VariableReassignment: - { - generateVariableReassignment(std::move(statement)); + 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)); + 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); + 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. + 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: @@ -397,73 +431,61 @@ namespace zlang throw std::runtime_error("Unknown statement encountered."); } } - void CodeGenLLVM::generateVariableReassignment(std::unique_ptr node) - { + 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())); + 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); + 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) - { + if (isGlobal) { out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; - } - else - { + } else { out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; } } - void CodeGenLLVM::generateVariableDeclaration(std::unique_ptr node) - { + 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) - { + if (!isGlobal) { out << " %" << node->scope->getMapping(node->value) << " = alloca " << ty << "\n"; } - if (node->children.size() >= 2) - { - auto val = emitExpression(std::move(node->children.back())); + if (node->children.size() >= 2) { + auto val = emitExpression(std::move(node->children.back()), out); TypeInfo tr = regType[val]; - // Use castValue for all type conversions - std::string castedVal = castValue(val, tr, ti); + std::string castedVal = castValue(val, tr, ti, out); - if (isGlobal) - { + if (isGlobal) { out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; - } - else - { + } else { out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; } } } - void CodeGenLLVM::generateIfStatement(std::unique_ptr statement) - { + 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])); + auto condVal = emitExpression(std::move(statement->children[0]), out); TypeInfo condTi = regType[condVal]; std::string condBool = fresh(); @@ -481,27 +503,24 @@ namespace zlang { auto ifBlock = std::move(statement->children[1]); auto children = std::move(ifBlock->children); - emitPrologue(std::move(ifBlock)); + emitPrologue(ifBlock->scope, out); for (auto &stmt : children) - generateStatement(std::move(stmt)); - emitEpilogue(); + generateStatement(std::move(stmt), out); + emitEpilogue(ifBlock->scope, out); } out << " br label %" << endLbl << "\n\n"; ASTNode *branch = statement->getElseBranch(); - if (branch) - { + if (branch) { out << elseLbl << ":\n"; - while (branch) - { - if (branch->type == NodeType::ElseIfStatement) - { + 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])); + auto elifCond = emitExpression(std::move(branch->children[0]), out); TypeInfo elifTi = regType[elifCond]; std::string elifBool = fresh(); out << " " << elifBool @@ -516,32 +535,27 @@ namespace zlang { auto elifBlock = std::move(branch->children[1]); auto elifChildren = std::move(elifBlock->children); - emitPrologue(std::move(elifBlock)); + emitPrologue(elifBlock->scope, out); for (auto &stmt : elifChildren) - generateStatement(std::move(stmt)); - emitEpilogue(); + generateStatement(std::move(stmt), out); + emitEpilogue(elifBlock->scope, out); } out << " br label %" << endLbl << "\n\n"; out << elifNext << ":\n"; branch = branch->getElseBranch(); - if (!branch) - { + if (!branch) { out << " br label %" << endLbl << "\n\n"; } - } - else if (branch->type == NodeType::ElseStatement) - { + } else if (branch->type == NodeType::ElseStatement) { auto elseBlock = std::move(branch->children[0]); auto elseChildren = std::move(elseBlock->children); - emitPrologue(std::move(elseBlock)); + emitPrologue(elseBlock->scope, out); for (auto &stmt : elseChildren) - generateStatement(std::move(stmt)); - emitEpilogue(); + generateStatement(std::move(stmt), out); + emitEpilogue(elseBlock->scope, out); out << " br label %" << endLbl << "\n\n"; branch = nullptr; - } - else - { + } else { throw std::runtime_error("Unexpected node type in else chain"); } } @@ -550,40 +564,225 @@ namespace zlang // 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"; + 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); } } - 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)); + // Emit main + outStream << "define i32 @main() {\n"; + // Decls in main + for (auto &s : declarationsAndReassignments) { + generateStatement(std::move(s), outStream); } - out << " ret i32 0\n"; - out - << "}\n"; - outfinal << outGlobal.str() + "\n\n" - << out.str() << "\n\n"; + 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 @