From ce472a63619ae2b49126338eaf24638b3a1c2b8a Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Mon, 16 Jun 2025 13:05:43 +0530 Subject: [PATCH 1/4] Added dev branch and branch protection rules. --- .github/workflows/test.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fabf604..0f67884 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,16 +2,9 @@ name: CI on: push: - paths: - - "**.zz" - - "src/**" - - "test/zz/**" - - "include/**" - - "main.cpp" - - "**.yml" - - "**.py" - - "CMakeLists.txt" - pull_request: {} + branches: [main] + pull_request: + branches: [main] jobs: test-linux-llvm: From b1f060afe95639b9d3aab262be2bfbb65c02ef78 Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Tue, 17 Jun 2025 10:56:33 +0530 Subject: [PATCH 2/4] Added various test cases and codegen complied to that --- include/all.hpp | 3 +- include/ast/ASTNode.hpp | 2 +- include/codegen/CodeGen.hpp | 8 +- include/lexer/Lexer.hpp | 4 +- include/parser/Parser.hpp | 5 +- include/parser/ScopeContext.hpp | 13 +- main.cpp | 12 +- src/ast/ASTNode.cpp | 51 +++- src/codegen/CodeGenLLVM.cpp | 389 +++++++++++++++----------- src/codegen/CodeGenLinux.cpp | 85 ++++-- src/codegen/CodeGenWindows.cpp | 76 +++-- src/parser/Parser.cpp | 41 ++- src/parser/ScopeContext.cpp | 107 ++++--- src/typechecker/TypeChecker.cpp | 40 ++- tests/zz/comparisons.zz | 4 - tests/zz/conditionals.zz | 6 - tests/zz/conditionals/if-elif-else.zz | 16 ++ tests/zz/conditionals/if-elif.zz | 13 + tests/zz/conditionals/if-else.zz | 24 ++ tests/zz/conditionals/if.zz | 16 ++ tests/zz/operations.zz | 3 - tests/zz/operations/binary.zz | 48 ++++ tests/zz/operations/unary.zz | 18 ++ tests/zz/type_conversion.zz | 8 - tests/zz/typechecker_fails.zz | 2 - tests/zz/variables.zz | 20 +- 26 files changed, 692 insertions(+), 322 deletions(-) delete mode 100644 tests/zz/comparisons.zz delete mode 100644 tests/zz/conditionals.zz create mode 100644 tests/zz/conditionals/if-elif-else.zz create mode 100644 tests/zz/conditionals/if-elif.zz create mode 100644 tests/zz/conditionals/if-else.zz create mode 100644 tests/zz/conditionals/if.zz delete mode 100644 tests/zz/operations.zz create mode 100644 tests/zz/operations/binary.zz create mode 100644 tests/zz/operations/unary.zz delete mode 100644 tests/zz/type_conversion.zz delete mode 100644 tests/zz/typechecker_fails.zz diff --git a/include/all.hpp b/include/all.hpp index bb56311..f48b408 100644 --- a/include/all.hpp +++ b/include/all.hpp @@ -24,4 +24,5 @@ #include #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/include/ast/ASTNode.hpp b/include/ast/ASTNode.hpp index 2ad256a..e45256d 100644 --- a/include/ast/ASTNode.hpp +++ b/include/ast/ASTNode.hpp @@ -39,7 +39,7 @@ namespace zlang : type(t), value(val), scope(sc) {} static std::unique_ptr makeProgramNode(const std::shared_ptr scope); - static std::unique_ptr makeVariableDeclarationNode( + static std::optional> makeVariableDeclarationNode( const std::string &name, std::unique_ptr typeAnnotation, std::unique_ptr initializer, const std::shared_ptr scope); diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 3c96289..187450c 100644 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -164,7 +164,7 @@ namespace zlang public: ~CodeGenLinux() override = default; - CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream){}; + CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream) {}; void generate(std::unique_ptr program) override; }; @@ -194,7 +194,7 @@ namespace zlang public: ~CodeGenWindows() override = default; - CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream){}; + CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; void generate(std::unique_ptr program) override; }; @@ -223,9 +223,11 @@ namespace zlang 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: ~CodeGenLLVM() override = default; - CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream){}; + 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 aac5ed2..a136567 100644 --- a/include/lexer/Lexer.hpp +++ b/include/lexer/Lexer.hpp @@ -35,7 +35,7 @@ namespace zlang size_t line; size_t column; - std::string toString() const + std::string to_string() const { return "Token { kind = " + kindToString(kind) + ", text = \"" + text + @@ -95,7 +95,7 @@ namespace zlang inline std::ostream &operator<<(std::ostream &os, const Token &token) { - return os << token.toString(); + return os << token.to_string(); } using Error = zlang::Error; class Lexer diff --git a/include/parser/Parser.hpp b/include/parser/Parser.hpp index 429bc0e..0f93a1e 100644 --- a/include/parser/Parser.hpp +++ b/include/parser/Parser.hpp @@ -19,6 +19,7 @@ namespace zlang } private: + int blockNumber = 0; Lexer &lexer; Token currentToken; std::shared_ptr currentScope; @@ -32,14 +33,14 @@ namespace zlang std::unique_ptr parseVariableDeclaration(); std::unique_ptr parseVariableReassignment(); std::unique_ptr parseConditionals(); - std::unique_ptr parseExpression(); + std::unique_ptr parseExpression(bool expect_exclaim = false); std::unique_ptr parsePrimary(); int getPrecedence(const std::string &op) const; std::unique_ptr parseUnary(); std::unique_ptr parseBinaryRHS(int exprPrec, std::unique_ptr lhs); - void enterScope(); + void enterScope(std::string name); void exitScope(); std::unique_ptr parseBlock(); }; diff --git a/include/parser/ScopeContext.hpp b/include/parser/ScopeContext.hpp index 90cf318..0d52a50 100644 --- a/include/parser/ScopeContext.hpp +++ b/include/parser/ScopeContext.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace zlang { @@ -36,17 +38,21 @@ namespace zlang } }; + static int variableNumber = 0; + class ScopeContext { 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_; - void defineVariable(const std::string &name, const VariableInfo &info); + bool defineVariable(const std::string &name, const VariableInfo &info); void defineFunction(const std::string &name, const 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; @@ -101,6 +107,7 @@ namespace zlang } std::cout << std::endl; } + std::optional lookupVariableInCurrentContext(const std::string &name) const; private: std::unordered_map vars_; diff --git a/main.cpp b/main.cpp index f070e19..d39b1a9 100644 --- a/main.cpp +++ b/main.cpp @@ -58,15 +58,15 @@ int main(int argc, char *argv[]) return 1; } + if (!program.get()) + { + zlang::logError(Error(ErrorType::Generic, "Parsing Failed")); + return 1; + } if (cli.printAST()) { - if (!program.get()) - { - zlang::logError(Error(ErrorType::Generic, "Parsing Failed")); - return 1; - } + program.get()->print(std::cout); } - program.get()->print(std::cout); // Type checking TypeChecker typeChecker; diff --git a/src/ast/ASTNode.cpp b/src/ast/ASTNode.cpp index 36d6804..ec9187f 100644 --- a/src/ast/ASTNode.cpp +++ b/src/ast/ASTNode.cpp @@ -29,33 +29,58 @@ namespace zlang void ASTNode::setElseBranch(std::unique_ptr elseNode) { - children.push_back(std::move(elseNode)); + if (children.size() < 2) + { + throw std::logic_error("setElseBranch on a non‑conditional node"); + } + + if (children.size() == 2) + { + // no else/elif attached yet: attach here + children.push_back(std::move(elseNode)); + } + else + { + // we already have an elseBranch in children[2]: + // forward the new elseNode into _that_ node + ASTNode *existing = children[2].get(); + existing->setElseBranch(std::move(elseNode)); + } } ASTNode *ASTNode::getElseBranch() const { - if (children.empty() or children.back().get() == nullptr) + if (children.size() < 3 || children[2] == nullptr) return nullptr; - if (children.back().get()->type != NodeType::ElseIfStatement || children.back().get()->type != NodeType::ElseStatement) - { + + auto *branch = children[2].get(); + if (branch->type != NodeType::ElseIfStatement && branch->type != NodeType::ElseStatement) return nullptr; - } - return children.back().get(); + + return branch; } - std::unique_ptr ASTNode::makeVariableDeclarationNode( + std::optional> + ASTNode::makeVariableDeclarationNode( const std::string &name, std::unique_ptr typeAnnotation, std::unique_ptr initializer, const std::shared_ptr scope) { auto node = std::make_unique(NodeType::VariableDeclaration, name, scope); - scope.get()->defineVariable(name, {typeAnnotation.get()->value}); - if (typeAnnotation) - node->addChild(std::move(typeAnnotation)); - if (initializer) - node->addChild(std::move(initializer)); - return node; + bool result = scope.get()->defineVariable(name, {typeAnnotation.get()->value}); + if (result) + { + if (typeAnnotation) + node->addChild(std::move(typeAnnotation)); + if (initializer) + node->addChild(std::move(initializer)); + return node; + } + else + { + return std::nullopt; + } } std::unique_ptr ASTNode::makeSymbolNode(const std::string &name, const std::shared_ptr scope) diff --git a/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index 13b1aea..687e263 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -8,6 +8,87 @@ namespace zlang 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"); + out << " " << tmp << " = sitofp " << 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); + out << " " << tmp << " = fptosi " << 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) + { + out << " " << tmp << " = sext " << fromTy << " " << val << " to " << toTy << "\n"; + } + else if (fromType.bits > toType.bits) + { + out << " " << tmp << " = trunc " << fromTy << " " << val << " to " << toTy << "\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::intToXmm(const std::string ®ister_int, uint32_t bits) { @@ -22,24 +103,25 @@ namespace zlang } std::string CodeGenLLVM::generateFloatLiteral(std::unique_ptr node) { - std::string tmpDouble = fresh(); + std::string tmp = fresh(); std::string val = node->value; bool isF32 = (!val.empty() && (val.back() == 'f' || val.back() == 'F')); if (isF32) val.pop_back(); - out << " " << tmpDouble << " = fadd double 0.0, " << val << "\n"; + + std::string hexVal = toHexFloatFromStr(val, isF32); + if (isF32) { - std::string tmpFloat = fresh(); - out << " " << tmpFloat << " = fptrunc double " << tmpDouble << " to float\n"; - noteType(tmpFloat, node->scope->lookupType("float")); - return tmpFloat; + out << " " << tmp << " = fadd float 0.0, " << hexVal << "\n"; + noteType(tmp, node->scope->lookupType("float")); } else { - noteType(tmpDouble, node->scope->lookupType("double")); - return tmpDouble; + out << " " << tmp << " = fadd double 0.0, " << hexVal << "\n"; + noteType(tmp, node->scope->lookupType("double")); } + return tmp; } std::string CodeGenLLVM::generateStringLiteral(std::unique_ptr node) { @@ -86,7 +168,7 @@ namespace zlang std::string CodeGenLLVM::generateBooleanLiteral(std::unique_ptr node) { std::string name = fresh(); - out << " " << name << " = add i1 0, " + out << " " << name << " = add i8 0, " << (node->value == "true" ? "1" : "0") << "\n"; noteType(name, node->scope->lookupType("boolean")); return name; @@ -104,7 +186,15 @@ namespace zlang // Determine if it's a global or local variable bool isGlobal = scope.isGlobalVariable(name); - std::string ptr = (isGlobal ? "@" : "%") + name; + std::string ptr; + if (!isGlobal) + { + ptr = "%" + node->scope->getMapping(name); + } + else + { + ptr = "@" + name; + } // Generate load instruction std::string loaded = fresh(); @@ -121,42 +211,26 @@ namespace zlang TypeInfo t2 = regType[rhs]; TypeInfo tr = TypeChecker::promoteType(t1, t2); std::string res = fresh(); + std::string L = castValue(lhs, t1, tr); + std::string R = castValue(rhs, t2, tr); if (tr.isFloat) { - auto cast = [&](const std::string &val, const TypeInfo &ti) - { - if (ti.isFloat && ti.bits == 64) - return val; // already double - std::string target = (tr.bits == 64 ? "double" : "float"); - std::string tmp = fresh(); - if (!ti.isFloat) - { - // integer -> double - std::string intTy = "i" + std::to_string(ti.bits); - out << " " << tmp << " = sitofp " << intTy << " " << val << " to " << target << "\n"; - } - else - { - // float32 -> double - out << " " << tmp << " = fpext float " << val << " to " << target << "\n"; - } - noteType(tmp, tr); - return tmp; - }; - std::string L = cast(lhs, t1); - std::string R = cast(rhs, t2); std::string target = (tr.bits == 64 ? "double" : "float"); - // Floating-point operations + if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { - static const std::unordered_map fp_ops = {{"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; + static const std::unordered_map fp_ops = { + {"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; auto op = fp_ops.at(node->value); out << " " << res << " = " << op << " " << target << " " << L << ", " << R << "\n"; + noteType(res, tr); + return res; } else { - static const std::unordered_map fcmp_ops = {{"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; + static const std::unordered_map fcmp_ops = { + {"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; auto cmpop = fcmp_ops.at(node->value); out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; std::string zero = fresh(); @@ -164,33 +238,32 @@ namespace zlang noteType(zero, node->scope->lookupType("boolean")); return zero; } - noteType(res, tr); } else { - // Integer operations on various widths std::string intTy = "i" + std::to_string(tr.bits); if (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") { - static const std::unordered_map int_ops = {{"+", "add"}, {"-", "sub"}, {"*", "mul"}, {"/", "sdiv"}}; + static const std::unordered_map int_ops = { + {"+", "add"}, {"-", "sub"}, {"*", "mul"}, {"/", "sdiv"}}; auto op = int_ops.at(node->value); - out << " " << res << " = " << op << " " << intTy << " " << lhs << ", " << rhs << "\n"; + out << " " << res << " = " << op << " " << intTy << " " << L << ", " << R << "\n"; noteType(res, tr); + return res; } else { - static const std::unordered_map icmp_ops = {{"==", "eq"}, {"!=", "ne"}, {"<", "slt"}, {"<=", "sle"}, {">", "sgt"}, {">=", "sge"}}; + static const std::unordered_map icmp_ops = { + {"==", "eq"}, {"!=", "ne"}, {"<", "slt"}, {"<=", "sle"}, {">", "sgt"}, {">=", "sge"}}; auto cmpop = icmp_ops.at(node->value); - out << " " << res << " = icmp " << cmpop << " " << intTy << " " << lhs << ", " << rhs << "\n"; + out << " " << res << " = icmp " << cmpop << " " << intTy << " " << L << ", " << R << "\n"; std::string zero = fresh(); out << " " << zero << " = zext i1 " << res << " to i8\n"; noteType(zero, node->scope->lookupType("boolean")); return zero; } } - - return res; } std::string CodeGenLLVM::generateUnaryOperation(std::unique_ptr node) { @@ -198,7 +271,8 @@ namespace zlang auto varName = node->children[0]->value; TypeInfo ti = scope.lookupType(scope.lookupVariable(varName).type); - // Use correct LLVM type string + auto val = emitExpression(std::move(node->children[0])); + std::string llvmType; if (ti.isFloat) llvmType = (ti.bits == 32) ? "float" : "double"; @@ -207,7 +281,6 @@ namespace zlang if (node->value == "!") { - auto val = emitExpression(std::move(node->children[0])); std::string res = fresh(); out << " " << res << " = icmp eq " << llvmType << " " << val << ", 0\n"; std::string zero = fresh(); @@ -217,9 +290,16 @@ namespace zlang } else if (node->value == "-") { - auto val = emitExpression(std::move(node->children[0])); std::string res = fresh(); - out << " " << res << " = sub " << llvmType << " 0, " << val << "\n"; + if (ti.isFloat) + { + out << " " << res << " = fsub " << llvmType << " 0.0, " << val << "\n"; + } + else + { + out << " " << res << " = sub " << llvmType << " 0, " << val << "\n"; + } + noteType(res, ti); return res; } else if (node->value == "++" || node->value == "--") @@ -228,7 +308,7 @@ namespace zlang throw std::runtime_error("Increment/Decrement not supported on float"); bool isGlobal = scope.isGlobalVariable(varName); - std::string ptr = (isGlobal ? "@" : "%") + varName; + std::string ptr = (isGlobal ? "@" + varName : "%" + node->scope->getMapping(varName)); std::string cur = fresh(); out << " " << cur << " = load " << llvmType << ", " << llvmType << "* " << ptr << "\n"; @@ -297,6 +377,19 @@ namespace zlang 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."); @@ -311,29 +404,10 @@ namespace zlang // 1) Compute the RHS expression std::string val = emitExpression(std::move(node->children.back())); - TypeInfo tr = regType[val]; // actual computed type + TypeInfo tr = regType[val]; - // 2) Convert value to target type if needed - if (!ti.isFloat && !tr.isFloat && ti.bits != tr.bits) - { - if (tr.bits > ti.bits) - { - // Truncate larger integer to smaller - std::string narrow = fresh(); - out << " " << narrow << " = trunc i" << tr.bits - << " " << val << " to i" << ti.bits << "\n"; - val = narrow; - } - else - { - // Extend smaller integer to larger - std::string extend = fresh(); - std::string op = tr.isSigned ? "sext" : "zext"; - out << " " << extend << " = " << op << " i" << tr.bits - << " " << val << " to i" << ti.bits << "\n"; - val = extend; - } - } + // 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 @@ -342,49 +416,41 @@ namespace zlang if (isGlobal) { - out << " store " << ty << " " << val << ", " << ty << "* @" << name << "\n"; + out << " store " << ty << " " << castedVal << ", " << ty << "* @" << name << "\n"; } else { - out << " store " << ty << " " << val << ", " << ty << "* %" << name << "\n"; + out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(name) << "\n"; } } - void CodeGenLLVM::generateVariableDeclaration( - std::unique_ptr node) + 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); + 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]; - if (!ti.isFloat && !tr.isFloat && ti.bits != tr.bits) - { - if (tr.bits > ti.bits) - { - std::string narrow = fresh(); - out << " " << narrow << " = trunc i" << tr.bits << " " << val << " to i" << ti.bits << "\n"; - val = narrow; - } - else - { - std::string extend = fresh(); - std::string op = tr.isSigned ? "sext" : "zext"; - out << " " << extend << " = " << op << " i" << tr.bits << " " << val << " to i" << ti.bits << "\n"; - val = extend; - } - } + // Use castValue for all type conversions + std::string castedVal = castValue(val, tr, ti); if (isGlobal) { - out << " store " << ty << " " << val << ", " << ty << "* @" << node->value << "\n"; + out << " store " << ty << " " << castedVal << ", " << ty << "* @" << node->value << "\n"; } else { - out << " store " << ty << " " << val << ", " << ty << "* %" << node->value << "\n"; + out << " store " << ty << " " << castedVal << ", " << ty << "* %" << node->scope->getMapping(node->value) << "\n"; } } } @@ -395,83 +461,94 @@ namespace zlang std::string elseLbl = "if.else" + std::to_string(id); std::string endLbl = "if.end" + std::to_string(id); - // Emit the condition - auto condVal = emitExpression(std::move(statement->children[0])); // e.g., %1 + 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"; - std::string temp = fresh(); - out << " " << temp << " = icmp ne i1 " << condBool << ", 0\n"; - out << " br i1 " << temp << ", label %" << thenLbl << ", label %" << elseLbl << "\n"; + << " " << condVal << " to i1\n"; + + out << " br i1 " << condBool + << ", label %" << thenLbl + << ", label %" + << (statement->getElseBranch() ? elseLbl : endLbl) + << "\n\n"; - // Then block 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"; + { + 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"; - // Else or ElseIf - out << elseLbl << ":\n"; ASTNode *branch = statement->getElseBranch(); - while (branch) + if (branch) { - if (branch->type == NodeType::ElseIfStatement) - { - auto elifCond = emitExpression(std::move(branch->children[0])); - TypeInfo condTi = regType[elifCond]; - std::string condBool = fresh(); - out << " " << condBool - << " = trunc i" << condTi.bits - << " " << condVal << " to i1"; - std::string temp = fresh(); - out << " " << temp << " = icmp ne i1 " << condBool << ", 0\n"; - - std::string elifThen = "elif.then" + std::to_string(blockLabelCount); - std::string elifNext = "elif.next" + std::to_string(blockLabelCount); - blockLabelCount++; - - out << " br i1 " << temp << ", label %" << elifThen << ", label %" << elifNext << "\n"; - - // Elif then block - 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"; - - // Prepare for next else - out << elifNext << ":\n"; - branch = branch->getElseBranch(); - } - 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"; - break; - } - else + out << elseLbl << ":\n"; + + while (branch) { - break; + 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"); + } } } - out << " br label %" << endLbl << "\n"; - // End block + + // 5) end label out << endLbl << ":\n"; } + void CodeGenLLVM::generate(std::unique_ptr program) { outGlobal << "; ModuleID = 'zlang'\n"; diff --git a/src/codegen/CodeGenLinux.cpp b/src/codegen/CodeGenLinux.cpp index 558ac43..b4c7b5b 100644 --- a/src/codegen/CodeGenLinux.cpp +++ b/src/codegen/CodeGenLinux.cpp @@ -164,8 +164,8 @@ namespace zlang auto r_bool = alloc.allocate(); out << " movzbq %al, %" << r_bool << "\n"; noteType(r_bool, node->scope->lookupType("boolean")); - alloc.freeXMM(xr); - alloc.freeXMM(xl); + alloc.free(xr); + alloc.free(xl); return r_bool; } else @@ -174,7 +174,7 @@ namespace zlang } noteType(xl, tr); - alloc.freeXMM(xr); + alloc.free(xr); return xl; } @@ -228,12 +228,14 @@ namespace zlang } else if (assembly_comparison_operations.count(op)) { + std::string r_bool = alloc.allocate(); out << " cmp" << suf << " %" << r_r << ", %" << r_l << "\n" << " " << assembly_comparison_operations[op] << " %al\n" - << " movzbq %al, %" << r_l << "\n"; + << " movzbq %al, %" << r_bool << "\n"; alloc.free(rr); - noteType(rl, node->scope->lookupType("boolean")); - return rl; + alloc.free(rl); + noteType(r_bool, node->scope->lookupType("boolean")); + return r_bool; } else if (op == "&&" || op == "||" || op == "&" || op == "|") { @@ -377,6 +379,21 @@ namespace zlang 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."); } @@ -384,7 +401,6 @@ namespace zlang void CodeGenLinux::generateVariableReassignment( std::unique_ptr statement) { - logMessage("Inside Reassignment"); auto &scp = *statement->scope; auto nm = statement->value; auto ti = scp.lookupType(scp.lookupVariable(nm).type); @@ -398,7 +414,7 @@ namespace zlang ? nm + "(%rip)" : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)") << "\n"; - alloc.freeXMM(r); + alloc.free(r); } else { @@ -431,7 +447,7 @@ namespace zlang out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" << "\n"; - alloc.freeXMM(r); + alloc.free(r); } else { @@ -467,7 +483,7 @@ namespace zlang { out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " << nm + "(%rip)" << "\n"; - alloc.freeXMM(r); + alloc.free(r); } else { @@ -499,55 +515,66 @@ namespace zlang void CodeGenLinux::generateIfStatement(std::unique_ptr statement) { int id = blockLabelCount++; - std::string elseLbl = ".Lelse" + std::to_string(id); 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) + { + elseLabels.push_back(".Lelse" + std::to_string(blockLabelCount++)); + branch = branch->getElseBranch(); + } + + size_t elseIdx = 0; + auto condR = emitExpression(std::move(statement->children[0])); out << " cmpq $0, %" << condR << "\n"; - out << " je " << elseLbl << "\n"; + out << " je " << (elseLabels.empty() ? endLbl : elseLabels[elseIdx]) << "\n"; alloc.free(condR); - std::unique_ptr ifBlock = std::move(statement->children[1]); - std::vector> children = std::move(ifBlock->children); + + 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 << " jmp " << endLbl << "\n"; - out << elseLbl << ":\n"; - ASTNode *branch = statement->getElseBranch(); + out << " jmp " << endLbl << "\n"; + + branch = statement->getElseBranch(); while (branch) { + out << elseLabels[elseIdx++] << ":\n"; + if (branch->type == NodeType::ElseIfStatement) { auto r2 = emitExpression(std::move(branch->children[0])); out << " cmpq $0, %" << r2 << "\n"; - out << " je " << elseLbl << "\n"; + out << " je " << (elseIdx < elseLabels.size() ? elseLabels[elseIdx] : endLbl) << "\n"; alloc.free(r2); - std::unique_ptr elifBlock = std::move(branch->children[1]); - std::vector> children = std::move(elifBlock->children); + 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(); - out << " jmp " << endLbl << "\n"; - - branch = branch->getElseBranch(); + out << " jmp " << endLbl << "\n"; } else if (branch->type == NodeType::ElseStatement) { - std::unique_ptr elseBlock = std::move(branch->children[0]); - std::vector> children = std::move(elseBlock->children); + 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(); - break; } + + branch = branch->getElseBranch(); } + out << endLbl << ":\n\n"; - return; } void CodeGenLinux::generate(std::unique_ptr program) { diff --git a/src/codegen/CodeGenWindows.cpp b/src/codegen/CodeGenWindows.cpp index 1cd5fab..47816df 100644 --- a/src/codegen/CodeGenWindows.cpp +++ b/src/codegen/CodeGenWindows.cpp @@ -321,7 +321,7 @@ namespace zlang case NodeType::UnaryOp: return generateUnaryOperation(std::move(node)); default: - throw std::runtime_error("Unknown statement encountered."); + throw std::runtime_error("Unknown expression encountered."); } } @@ -359,6 +359,21 @@ namespace zlang 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."); } @@ -492,53 +507,72 @@ namespace zlang { int id = blockLabelCount++; std::string endLbl = "Lend" + std::to_string(id); - std::string nextLbl = "Lelse" + std::to_string(id); - auto emitBlock = [&](std::unique_ptr block) + std::vector elseLabels; + ASTNode *branch = statement->getElseBranch(); + while (branch) { - std::vector> stmts = std::move(block->children); - emitPrologue(std::move(block)); - for (auto &stmt : stmts) - generateStatement(std::move(stmt)); - emitEpilogue(); - }; + 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"; - out << " je " << nextLbl << "\n"; + if (!elseLabels.empty()) + out << " je " << elseLabels[elseIdx] << "\n"; + else + out << " je " << endLbl << "\n"; alloc.free(condReg); - emitBlock(std::move(statement->children[1])); + 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"; - out << nextLbl << ":\n"; - ASTNode *branch = statement->getElseBranch(); + branch = statement->getElseBranch(); while (branch) { + out << elseLabels[elseIdx++] << ":\n"; + if (branch->type == NodeType::ElseIfStatement) { - std::string elseIfNextLbl = "Lelse" + std::to_string(blockLabelCount++); - auto r = emitExpression(std::move(branch->children[0])); out << " cmp " << r << ", 0\n"; - out << " je " << elseIfNextLbl << "\n"; + if (elseIdx < elseLabels.size()) + out << " je " << elseLabels[elseIdx] << "\n"; + else + out << " je " << endLbl << "\n"; alloc.free(r); - emitBlock(std::move(branch->children[1])); + 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"; - out << elseIfNextLbl << ":\n"; - - branch = branch->getElseBranch(); } else if (branch->type == NodeType::ElseStatement) { - emitBlock(std::move(branch->children[0])); + 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"; diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index 35cc7e0..4a33a73 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -4,7 +4,7 @@ namespace zlang { Parser::Parser(Lexer &lex) : lexer(lex) { - currentScope = std::make_shared(nullptr); + 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 @@ -78,6 +78,8 @@ namespace zlang return parseVariableReassignment(); 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) + return parseExpression(true); logError({ErrorType::Syntax, "Unexpected token '" + currentToken.text + "' at line " + std::to_string(currentToken.line) + @@ -89,7 +91,7 @@ namespace zlang 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(); + enterScope("blockNumber" + std::to_string(blockNumber++)); auto blockNode = std::make_unique(NodeType::Program, "", currentScope); while (!match(Token::Kind::RightBrace) && currentToken.kind != Token::Kind::EndOfFile) { @@ -189,7 +191,17 @@ namespace zlang } } expect(Token::Kind::SemiColon, "Expected ';' after declaration"); - return ASTNode::makeVariableDeclarationNode(name, std::move(typeNode), std::move(initNode), currentScope); + 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 + { + return std::move(result.value()); + } } std::unique_ptr Parser::parseVariableReassignment() @@ -203,10 +215,15 @@ namespace zlang return ASTNode::makeVariableReassignmentNode(name, std::move(expr), currentScope); } - std::unique_ptr Parser::parseExpression() + std::unique_ptr Parser::parseExpression(bool expect_exclaim) { auto lhs = parseUnary(); - return parseBinaryRHS(0, std::move(lhs)); + 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) + "."); + } + return ans; } std::unique_ptr Parser::parsePrimary() @@ -280,18 +297,22 @@ namespace zlang { while (true) { - if (currentToken.kind != Token::Kind::Symbol) + // Only consider tokens that can represent binary operators + if (!(currentToken.kind == Token::Kind::Symbol || (currentToken.kind == Token::Kind::Equal and currentToken.text == "=="))) break; - int tokPrec = getPrecedence(currentToken.text); + + std::string op = currentToken.text; + int tokPrec = getPrecedence(op); + if (tokPrec < exprPrec) return lhs; - std::string op = currentToken.text; advance(); // parse RHS auto rhs = parseUnary(); int nextPrec = getPrecedence(currentToken.text); + if (tokPrec < nextPrec) rhs = parseBinaryRHS(tokPrec + 1, std::move(rhs)); @@ -309,9 +330,9 @@ namespace zlang return it == prec.end() ? -1 : it->second; } - void Parser::enterScope() + void Parser::enterScope(std::string name) { - currentScope = std::make_shared(currentScope); + currentScope = std::make_shared(currentScope, name); } void Parser::exitScope() { diff --git a/src/parser/ScopeContext.cpp b/src/parser/ScopeContext.cpp index 13db936..4a9de70 100644 --- a/src/parser/ScopeContext.cpp +++ b/src/parser/ScopeContext.cpp @@ -2,82 +2,125 @@ namespace zlang { - void ScopeContext::defineVariable(const std::string &name, const VariableInfo &info) + bool ScopeContext::defineVariable(const std::string &varName, const VariableInfo &info) { - - TypeInfo ti = lookupType(info.type); - int size = ti.bits / 8; - int padding = (ti.align - (stackOffset % ti.align)) % ti.align; - stackOffset += padding; - stackOffset += size; - offsetTable[name] = -stackOffset; - vars_[name] = info; + if (lookupVariableInCurrentContext(varName).has_value()) + { + return false; + } + else + { + 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; + return true; + } } - void ScopeContext::defineFunction(const std::string &name, const FunctionInfo &info) + void ScopeContext::defineFunction(const std::string &functionName, const FunctionInfo &info) { - funcs_[name] = info; + funcs_[functionName] = info; } - void ScopeContext::defineType(const std::string &name, const TypeInfo &info) + void ScopeContext::defineType(const std::string &typeName, const TypeInfo &info) { - types_[name] = info; + types_[typeName] = info; } - VariableInfo ScopeContext::lookupVariable(const std::string &name) const + VariableInfo ScopeContext::lookupVariable(const std::string &varName) const { - auto it = vars_.find(name); + auto it = vars_.find(varName); if (it != vars_.end()) return it->second; if (parent_) - return parent_->lookupVariable(name); - throw std::runtime_error("Undefined variable '" + name + "'"); + 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_) + return parent_->getMapping(varName); + } + catch (...) + { + throw std::runtime_error("Undefined variable '" + varName + "'"); + } + return ""; + } + + std::optional ScopeContext::lookupVariableInCurrentContext(const std::string &varName) const + { + auto it = vars_.find(varName); + if (it != vars_.end()) + return it->second; + return std::nullopt; } - FunctionInfo ScopeContext::lookupFunction(const std::string &name) const + FunctionInfo ScopeContext::lookupFunction(const std::string &functionName) const { - auto it = funcs_.find(name); + auto it = funcs_.find(functionName); if (it != funcs_.end()) return it->second; if (parent_) - return parent_->lookupFunction(name); - throw std::runtime_error("Undefined function '" + name + "'"); + return parent_->lookupFunction(functionName); + throw std::runtime_error("Undefined function '" + functionName + "'"); } - TypeInfo ScopeContext::lookupType(const std::string &name) const + TypeInfo ScopeContext::lookupType(const std::string &typeName) const { - auto it = types_.find(name); + auto it = types_.find(typeName); if (it != types_.end()) return it->second; if (parent_) - return parent_->lookupType(name); - throw std::runtime_error("Undefined type '" + name + "'"); + return parent_->lookupType(typeName); + throw std::runtime_error("Undefined type '" + typeName + "'"); } - std::int64_t ScopeContext::getVariableOffset(const std::string &name) const + std::int64_t ScopeContext::getVariableOffset(const std::string &varName) const { - auto it = offsetTable.find(name); + auto it = offsetTable.find(varName); if (it != offsetTable.end()) return it->second; if (parent_) - return parent_->getVariableOffset(name); - throw std::runtime_error("Unknown variable " + name); + return parent_->getVariableOffset(varName); + throw std::runtime_error("Unknown variable " + varName); } - bool ScopeContext::isGlobalVariable(const std::string &name) const + bool ScopeContext::isGlobalVariable(const std::string &varName) const { const ScopeContext *ctx = this; while (ctx) { - auto it = ctx->vars_.find(name); + auto it = ctx->vars_.find(varName); if (it != ctx->vars_.end()) { return (ctx->parent_ == nullptr); } ctx = ctx->parent_.get(); } - throw std::runtime_error("Unknown variable " + name); + throw std::runtime_error("Unknown variable " + varName); } bool ScopeContext::isGlobalScope() const diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index deee72c..fa2b2bf 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -11,10 +11,7 @@ namespace zlang shouldCodegen_ = false; return; } - for (auto &child : program->children) - { - checkNode(child.get()); - } + checkNode(program.get()); } std::string TypeChecker::checkNode(const ASTNode *node) @@ -25,6 +22,13 @@ namespace zlang switch (node->type) { + case NodeType::Program: + for (auto &child : node->children) + { + checkNode(child.get()); + } + return ""; + case NodeType::VariableDeclaration: { std::string annotatedType, initType; @@ -34,7 +38,9 @@ namespace zlang annotatedType = node->children[0]->value; } if (node->children.size() == 2) + { initType = checkNode(node->children[1].get()); + } if (annotatedType.empty() && initType.empty()) { @@ -56,13 +62,13 @@ namespace zlang "' does not match annotation '" + annotatedType + "' on variable '" + node->value + "'"}); shouldCodegen_ = false; + return ""; } } // final type is the annotation if provided, else the inferred std::string finalType = !annotatedType.empty() ? annotatedType : initType; - scope->defineVariable(node->value, VariableInfo{finalType}); return finalType; } @@ -209,18 +215,28 @@ namespace zlang return ""; } - // handle if / else etc by checking their children case NodeType::IfStatement: case NodeType::ElseIfStatement: + { + if (node->children.size() > 0) + checkNode(node->children[0].get()); + + if (node->children.size() > 1) + checkNode(node->children[1].get()); + + if (node->children.size() > 2 && node->children[2]) + checkNode(node->children[2].get()); + + return ""; + } case NodeType::ElseStatement: - for (auto &c : node->children) - checkNode(c.get()); - return ""; // control node itself has no type + { + if (!node->children.empty()) + checkNode(node->children[0].get()); + return ""; + } default: - // propagate into any other children - for (auto &c : node->children) - checkNode(c.get()); return ""; } } diff --git a/tests/zz/comparisons.zz b/tests/zz/comparisons.zz deleted file mode 100644 index 7e01f9c..0000000 --- a/tests/zz/comparisons.zz +++ /dev/null @@ -1,4 +0,0 @@ -let x: integer = 10; -let y: boolean = x > 5; - -let z: boolean = !y; \ No newline at end of file diff --git a/tests/zz/conditionals.zz b/tests/zz/conditionals.zz deleted file mode 100644 index 49f9913..0000000 --- a/tests/zz/conditionals.zz +++ /dev/null @@ -1,6 +0,0 @@ -let a: integer = 10; -if (2 >= 5){ - a = 12; -}else{ - a = 20; -} \ No newline at end of file diff --git a/tests/zz/conditionals/if-elif-else.zz b/tests/zz/conditionals/if-elif-else.zz new file mode 100644 index 0000000..9031254 --- /dev/null +++ b/tests/zz/conditionals/if-elif-else.zz @@ -0,0 +1,16 @@ +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; +} diff --git a/tests/zz/conditionals/if-elif.zz b/tests/zz/conditionals/if-elif.zz new file mode 100644 index 0000000..648bfa4 --- /dev/null +++ b/tests/zz/conditionals/if-elif.zz @@ -0,0 +1,13 @@ +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; +} diff --git a/tests/zz/conditionals/if-else.zz b/tests/zz/conditionals/if-else.zz new file mode 100644 index 0000000..ebc377a --- /dev/null +++ b/tests/zz/conditionals/if-else.zz @@ -0,0 +1,24 @@ +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; +} + +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; +} +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 new file mode 100644 index 0000000..e11c62e --- /dev/null +++ b/tests/zz/conditionals/if.zz @@ -0,0 +1,16 @@ +let a: int64_t = 10; +let b: int64_t = 20; +let f: float = 10.5f; +let u: uint8_t = 10; + +if (a < b) { + let x: int64_t = 1; +} + +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 diff --git a/tests/zz/operations.zz b/tests/zz/operations.zz deleted file mode 100644 index 4e45275..0000000 --- a/tests/zz/operations.zz +++ /dev/null @@ -1,3 +0,0 @@ -let x: integer = 2; -let y: integer = 2 * x - x; -let k: int32_t = ++x; \ No newline at end of file diff --git a/tests/zz/operations/binary.zz b/tests/zz/operations/binary.zz new file mode 100644 index 0000000..061847e --- /dev/null +++ b/tests/zz/operations/binary.zz @@ -0,0 +1,48 @@ +// Integer + Integer +let a: int64_t = 10; +let b: int64_t = 20; +let sum_i: int64_t = a + b; +let diff_i: int64_t = a - b; +let prod_i: int64_t = a * b; +let div_i: int64_t = b / a; + +// Integer + Float (promote to float) +let c: float = 2.5; +let sum_if: float = a + c; +let diff_if: float = c - a; +let prod_if: float = a * c; +let div_if: float = a / c; + +// Float + Float +let d: double = 3.1415; +let e: double = 2.718; +let sum_f: double = d + e; +let diff_f: double = d - e; +let prod_f: double = d * e; +let div_f: double = d / e; + +// Unsigned + Signed +let ua: uint8_t = 5; +let sb: int8_t = -2; +let sum_us: int16_t = ua + sb; + +// Comparisons int +let cmp_lt: boolean = a < b; +let cmp_le: boolean = a <= b; +let cmp_gt: boolean = a > b; +let cmp_ge: boolean = a >= b; +let cmp_eq: boolean = a == b; +let cmp_ne: boolean = a != b; + +// Comparisons float +let cmpf_lt: boolean = d < e; +let cmpf_eq: boolean = d == e; + +// Mixed Comparison +let cmpmix: boolean = a < d; + +// Comparison boolean (only ==, != valid) +let ba: boolean = true; +let bb: boolean = false; +let cmpb_eq: boolean = ba == bb; +let cmpb_ne: boolean = ba != bb; \ No newline at end of file diff --git a/tests/zz/operations/unary.zz b/tests/zz/operations/unary.zz new file mode 100644 index 0000000..108c02d --- /dev/null +++ b/tests/zz/operations/unary.zz @@ -0,0 +1,18 @@ +let x: int64_t = 5; +++x; +--x; + +let y: uint8_t = 255; +++y; +--y; + +let z: size_t = 100; +++z; +--z; + +// Logical NOT on boolean +let b1: boolean = true; +let nb1: boolean = !b1; + +let b2: boolean = false; +let nb2: boolean = !b2; diff --git a/tests/zz/type_conversion.zz b/tests/zz/type_conversion.zz deleted file mode 100644 index 6e36f6b..0000000 --- a/tests/zz/type_conversion.zz +++ /dev/null @@ -1,8 +0,0 @@ -let a: uint32_t = 123; -let b: float = 12.3f; - -let z: uint64_t = 0; - -if(a > b + 10){ - z = 69; -} \ No newline at end of file diff --git a/tests/zz/typechecker_fails.zz b/tests/zz/typechecker_fails.zz deleted file mode 100644 index b513513..0000000 --- a/tests/zz/typechecker_fails.zz +++ /dev/null @@ -1,2 +0,0 @@ -let x: string = "Hello World"; -let y: integer = 10; \ No newline at end of file diff --git a/tests/zz/variables.zz b/tests/zz/variables.zz index 8abdf39..6ddd9e2 100644 --- a/tests/zz/variables.zz +++ b/tests/zz/variables.zz @@ -1,10 +1,14 @@ -let x: integer = 10; -let z: integer; +let x: int64_t = 42; +let y: uint8_t; +let f: float = 3.14f; +let d: double = 2.718; +let flag: boolean = true; +let other: boolean = false; +let s: string = "hello"; +let a: string; -z = 13; -x = 11; - -let k: string = "Hello World"; -let g: double = 32.11; -let h: float = 3.14f; +let x1: int64_t = 10; +x = 20; +let f1: float = 1.5f; +f = 2.5f; \ No newline at end of file From 8942769c2f5eba8afe377dc517a5a82797984ed0 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Tue, 17 Jun 2025 10:19:48 +0530 Subject: [PATCH 3/4] Windows passed, lets see about linux and llvm node --- .vscode/settings.json | 3 +- include/codegen/CodeGen.hpp | 35 +-- include/parser/ScopeContext.hpp | 6 +- src/codegen/CodeGenLLVM.cpp | 107 ++++---- src/codegen/CodeGenLinux.cpp | 418 ++++++++++++++++++++------------ src/codegen/CodeGenWindows.cpp | 318 +++++++++++++++--------- 6 files changed, 535 insertions(+), 352 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0a2b93c..c835cf1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -96,5 +96,6 @@ "xtr1common": "cpp", "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" } \ No newline at end of file diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 187450c..7921c81 100644 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -40,9 +40,9 @@ namespace zlang std::ostringstream outGlobal; std::ostringstream out; - std::string adjustReg(std::string &r64, uint64_t bits) + std::string adjustReg(const std::string &r64, uint64_t bits) { - r64 = RegisterAllocator::getBaseReg(r64); + std::string baseRegister = RegisterAllocator::getBaseReg(r64); static const std::unordered_map> registers_based_on_bytes = { {"rax", {"rax", "eax", "ax", "al"}}, {"rbx", {"rbx", "ebx", "bx", "bl"}}, @@ -61,9 +61,9 @@ namespace zlang {"r14", {"r14", "r14d", "r14w", "r14b"}}, {"r15", {"r15", "r15d", "r15w", "r15b"}}}; - auto it = registers_based_on_bytes.find(r64); + auto it = registers_based_on_bytes.find(baseRegister); if (it == registers_based_on_bytes.end()) - throw std::runtime_error("Unknown register '" + r64 + "'\n\n" + out.str()); + throw std::runtime_error("Unknown register '" + baseRegister + "'\n\n" + out.str()); const auto &ents = it->second; switch (bits) @@ -111,8 +111,6 @@ namespace zlang void noteType(const std::string ®ister_, const TypeInfo &type_) { regType[register_] = type_; } - virtual std::string intToXmm(const std::string &r_int, uint32_t bits) = 0; - virtual void generateStatement(std::unique_ptr statement) = 0; virtual std::string emitExpression(std::unique_ptr node) = 0; @@ -142,15 +140,10 @@ namespace zlang { private: std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - std::string intToXmm(const std::string &r_int, uint32_t bits) override; - 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; @@ -161,10 +154,11 @@ namespace zlang 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) {}; + CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream){}; void generate(std::unique_ptr program) override; }; @@ -172,15 +166,10 @@ namespace zlang { private: std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; - std::string intToXmm(const std::string &r_int, uint32_t bits) override; - 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; @@ -191,10 +180,12 @@ namespace zlang 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; public: ~CodeGenWindows() override = default; - CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; + CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream){}; void generate(std::unique_ptr program) override; }; @@ -203,15 +194,10 @@ namespace zlang private: std::unordered_map integer_suffixes = {{8, 'b'}, {16, 'w'}, {32, 'l'}, {64, 'q'}}; std::unordered_map stringLiterals; - std::string intToXmm(const std::string &r_int, uint32_t bits) override; - 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; @@ -222,12 +208,11 @@ namespace zlang 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: ~CodeGenLLVM() override = default; - CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream) {}; + 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/parser/ScopeContext.hpp b/include/parser/ScopeContext.hpp index 0d52a50..f76cadf 100644 --- a/include/parser/ScopeContext.hpp +++ b/include/parser/ScopeContext.hpp @@ -30,7 +30,7 @@ namespace zlang bool isFloat; bool isSigned; - std::string to_string() + 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"; @@ -44,8 +44,8 @@ namespace zlang { 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) {}; + 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_; diff --git a/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index 687e263..3d3fc65 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -7,7 +7,6 @@ namespace zlang static int cnt = 0; return "%tmp" + std::to_string(cnt++); } - std::string toHexFloatFromStr(const std::string &valStr, bool isF32) { std::ostringstream oss; @@ -24,13 +23,10 @@ namespace zlang } 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(); @@ -38,13 +34,15 @@ namespace zlang { std::string intTy = "i" + std::to_string(fromType.bits); std::string floatTy = (toType.bits == 64 ? "double" : "float"); - out << " " << tmp << " = sitofp " << intTy << " " << val << " to " << floatTy << "\n"; + 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); - out << " " << tmp << " = fptosi " << floatTy << " " << val << " to " << intTy << "\n"; + std::string instr = toType.isSigned ? "fptosi" : "fptoui"; + out << " " << tmp << " = " << instr << " " << floatTy << " " << val << " to " << intTy << "\n"; } else if (!fromType.isFloat && !toType.isFloat) { @@ -52,12 +50,18 @@ namespace zlang std::string toTy = "i" + std::to_string(toType.bits); if (fromType.bits < toType.bits) { - out << " " << tmp << " = sext " << fromTy << " " << val << " to " << toTy << "\n"; + 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; @@ -88,12 +92,6 @@ namespace zlang noteType(tmp, toType); return tmp; } - - std::string CodeGenLLVM::intToXmm(const std::string ®ister_int, - uint32_t bits) - { - return ""; - } std::string CodeGenLLVM::generateIntegerLiteral(std::unique_ptr node) { std::string name = fresh(); @@ -210,59 +208,72 @@ namespace zlang TypeInfo t1 = regType[lhs]; TypeInfo t2 = regType[rhs]; TypeInfo tr = TypeChecker::promoteType(t1, t2); - std::string res = fresh(); 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 (node->value == "+" || node->value == "-" || node->value == "*" || node->value == "/") + if (fp_ops.count(node->value)) { - static const std::unordered_map fp_ops = { - {"+", "fadd"}, {"-", "fsub"}, {"*", "fmul"}, {"/", "fdiv"}}; - auto op = fp_ops.at(node->value); - out << " " << res << " = " << op << " " << target << " " << L << ", " << R << "\n"; + out << " " << res << " = " << fp_ops.at(node->value) << " " << target << " " << L << ", " << R << "\n"; noteType(res, tr); return res; } - else + else { - static const std::unordered_map fcmp_ops = { - {"==", "oeq"}, {"!=", "one"}, {"<", "olt"}, {"<=", "ole"}, {">", "ogt"}, {">=", "oge"}}; - auto cmpop = fcmp_ops.at(node->value); + std::string cmpop = fcmp_ops.at(node->value); out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; - std::string zero = fresh(); - out << " " << zero << " = zext i1 " << res << " to i8\n"; - noteType(zero, node->scope->lookupType("boolean")); - return zero; + 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 == "/") { - static const std::unordered_map int_ops = { - {"+", "add"}, {"-", "sub"}, {"*", "mul"}, {"/", "sdiv"}}; - auto op = int_ops.at(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; } - else + + static const std::unordered_map> cmp_map = { + {"==", {"eq", "ueq"}}, {"!=", {"ne", "une"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; + + auto it = cmp_map.find(node->value); + if (it != cmp_map.end()) { - static const std::unordered_map icmp_ops = { - {"==", "eq"}, {"!=", "ne"}, {"<", "slt"}, {"<=", "sle"}, {">", "sgt"}, {">=", "sge"}}; - auto cmpop = icmp_ops.at(node->value); + 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 zero = fresh(); - out << " " << zero << " = zext i1 " << res << " to i8\n"; - noteType(zero, node->scope->lookupType("boolean")); - return zero; + 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) @@ -281,6 +292,8 @@ namespace zlang 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(); @@ -288,22 +301,11 @@ namespace zlang noteType(zero, node->scope->lookupType("boolean")); return zero; } - else if (node->value == "-") - { - std::string res = fresh(); - if (ti.isFloat) - { - out << " " << res << " = fsub " << llvmType << " 0.0, " << val << "\n"; - } - else - { - out << " " << res << " = sub " << llvmType << " 0, " << val << "\n"; - } - noteType(res, ti); - return res; - } else if (node->value == "++" || node->value == "--") { + if (node->children[0]->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"); @@ -548,7 +550,6 @@ namespace zlang // 5) end label out << endLbl << ":\n"; } - void CodeGenLLVM::generate(std::unique_ptr program) { outGlobal << "; ModuleID = 'zlang'\n"; diff --git a/src/codegen/CodeGenLinux.cpp b/src/codegen/CodeGenLinux.cpp index b4c7b5b..434294d 100644 --- a/src/codegen/CodeGenLinux.cpp +++ b/src/codegen/CodeGenLinux.cpp @@ -2,19 +2,133 @@ namespace zlang { - std::string CodeGenLinux::intToXmm(const std::string ®ister_int, - uint32_t bits) + std::string CodeGenLinux::castValue( + const std::string &val, + const TypeInfo &fromType, + const TypeInfo &toType) { - std::string r_xmm = alloc.allocateXMM(); - const char *cvt = (bits == 32 ? "cvtsi2ss" : "cvtsi2sd"); - out << " " << cvt << " %" << register_int << ", %" << r_xmm << "\n"; - alloc.free(register_int); - noteType(r_xmm, {bits, bits / 8, true, true}); - return r_xmm; + 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(val, fromType.bits) << "\n"; + alloc.free(val); + noteType(dst, toType); + return dst; + } + return RegisterAllocator::getBaseReg(val); + } + + if (fromType.isFloat && toType.isFloat) + { + std::string dstX = alloc.allocateXMM(); + auto instr = (fromType.bits < toType.bits) ? "cvtss2sd" : "cvtsd2ss"; + out << " " << instr << " " << dstX << ", " << val << "\n"; + alloc.free(val); + noteType(dstX, toType); + return dstX; + } + + if (!fromType.isFloat && toType.isFloat) + { + std::string dstX = alloc.allocateXMM(); + auto instr = (toType.bits == 32) ? "cvtsi2ss" : "cvtsi2sd"; + out << " " << instr << " " << dstX << ", " + << adjustReg(val, fromType.bits) << "\n"; + alloc.free(val); + noteType(dstX, toType); + return dstX; + } + + if (fromType.isFloat && !toType.isFloat) + { + std::string dstG = alloc.allocate(); + auto instr = (fromType.bits == 32) ? "cvttss2si" : "cvttsd2si"; + out << " " << instr << " " << adjustReg(dstG, toType.bits) + << ", " << val << "\n"; + alloc.free(val); + noteType(dstG, toType); + return dstG; + } + + if (!fromType.isFloat && !toType.isFloat) + { + std::string dstG = alloc.allocate(); + std::string srcAdj = adjustReg(val, fromType.bits); + std::string dstAdjFull = adjustReg(dstG, toType.bits); + + if (toType.bits > fromType.bits) + { + if (fromType.isSigned) + { + if (fromType.bits == 32 && toType.bits == 64) + { + out << " movsxd " << dstAdjFull << ", " << srcAdj << "\n"; + } + else + { + out << " movsx " << dstAdjFull << ", " << adjustReg(val, fromType.bits) << "\n"; + } + } + else + { + if (fromType.bits == 8 || fromType.bits == 16) + { + out << " movzx " << dstAdjFull << ", " << adjustReg(val, fromType.bits) << "\n"; + } + else if (fromType.bits == 32 && toType.bits == 64) + { + out << " mov " << adjustReg(dstG, 32) << ", " << adjustReg(val, 32) << "\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 " << dstAdjFull << ", " << adjustReg(val, toType.bits) << "\n"; + } + else + { + out << " movzx " << dstAdjFull << ", " << adjustReg(val, toType.bits) << "\n"; + } + } + else if (toType.bits == 32) + { + out << " mov " << adjustReg(dstAdjFull, 32) << ", " << adjustReg(val, 32) << "\n"; + } + else + { + throw std::runtime_error("Unsupported downcast to " + std::to_string(toType.bits)); + } + } + else + { + out << " mov " << dstAdjFull << ", " << srcAdj << "\n"; + } + + alloc.free(val); + noteType(dstG, toType); + return dstG; + } + + throw std::runtime_error( + "Unsupported cast: from " + fromType.to_string() + + " to " + toType.to_string()); } - std::string - CodeGenLinux::generateIntegerLiteral(std::unique_ptr node) + std::string CodeGenLinux::generateIntegerLiteral(std::unique_ptr node) { auto r = alloc.allocate(); out << " movq $" << node->value << ", %" << r << "\n"; @@ -43,18 +157,8 @@ namespace zlang std::string lbl = ".Lstr" + std::to_string(stringLabelCount++); std::string str = node->value; - std::ostringstream escaped; - for (char c : str) - { - if (c == '\\') - escaped << "\\\\"; - else if (c == '\"') - escaped << "\\\""; - else - escaped << c; - } - outGlobal << lbl << ": .string \"" << escaped.str() << "\"\n"; + outGlobal << lbl << ": .string \"" << str << "\"\n"; auto r = alloc.allocate(); out << " leaq " << lbl << "(%rip), %" << r << "\n"; @@ -124,43 +228,33 @@ namespace zlang auto rl = emitExpression(std::move(node->children[0])); auto rr = emitExpression(std::move(node->children[1])); - // Lookup operand types and compute result type auto t1 = regType.at(rl); auto t2 = regType.at(rr); auto tr = TypeChecker::promoteType(t1, t2); const auto &op = node->value; - // --- Floating‑point path --- + rl = castValue(rl, t1, tr); + rr = castValue(rr, t2, tr); + if (tr.isFloat) { bool isF32 = (tr.bits == 32); - std::string xr = rr; - std::string xl = rl; - - std::string suf = isF32 ? "ss" : "sd"; - - if (!t1.isFloat) - { - xl = intToXmm(rl, tr.bits); - } - - if (!t2.isFloat) - { - xr = intToXmm(rr, tr.bits); - } + char suf = isF32 ? 's' : 'd'; // movss/addss vs movsd/addsd + std::string xl = rl, xr = rr; if (op == "+") - out << " add" << suf << " %" << xr << ", %" << xl << "\n"; + out << " add" << suf << suf << " %" << xr << ", %" << xl << "\n"; else if (op == "-") - out << " sub" << suf << " %" << xr << ", %" << xl << "\n"; + out << " sub" << suf << suf << " %" << xr << ", %" << xl << "\n"; else if (op == "*") - out << " mul" << suf << " %" << xr << ", %" << xl << "\n"; + out << " mul" << suf << suf << " %" << xr << ", %" << xl << "\n"; else if (op == "/") - out << " div" << suf << " %" << xr << ", %" << xl << "\n"; + out << " div" << suf << suf << " %" << xr << ", %" << xl << "\n"; else if (assembly_comparison_operations.count(op)) { - out << " ucomi" << suf << " %" << xr << ", %" << xl << "\n" - << " " << assembly_comparison_operations[op] << " %al\n"; + // unordered compare + set + out << " ucomi" << suf << suf << " %" << xr << ", %" << xl << "\n" + << " " << assembly_comparison_operations.at(op) << " %al\n"; auto r_bool = alloc.allocate(); out << " movzbq %al, %" << r_bool << "\n"; noteType(r_bool, node->scope->lookupType("boolean")); @@ -178,7 +272,6 @@ namespace zlang return xl; } - // --- Integer path --- char suf = integer_suffixes[tr.bits]; auto r_l = adjustReg(rl, tr.bits); auto r_r = adjustReg(rr, tr.bits); @@ -197,30 +290,39 @@ namespace zlang } else if (op == "/") { - // Signed division - if (tr.bits == 32) + if (tr.isSigned) { - // dividend in EAX, sign‑extend to EDX:EAX - out << " movl %" << r_l - << ", %eax\n" - " cltd\n" - " idivl %" - << r_r - << "\n" - " movl %eax, %" - << r_l << "\n"; + 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 { - // dividend in RAX, sign‑extend to RDX:RAX - out << " movq %" << r_l - << ", %rax\n" - " cqo\n" - " idivq %" - << r_r - << "\n" - " movq %rax, %" - << r_l << "\n"; + 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"; + } } alloc.free(rr); noteType(r_l, tr); @@ -228,9 +330,14 @@ namespace zlang } else if (assembly_comparison_operations.count(op)) { - std::string r_bool = alloc.allocate(); + 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" - << " " << assembly_comparison_operations[op] << " %al\n" + << " " << map.at(op) << " %al\n" << " movzbq %al, %" << r_bool << "\n"; alloc.free(rr); alloc.free(rl); @@ -245,7 +352,7 @@ namespace zlang else if (op == "||") instr = "or"; else - instr = op; // "&" or "|" + instr = op; out << " " << instr << suf << " %" << r_r << ", %" << r_l << "\n"; } else @@ -260,68 +367,52 @@ namespace zlang std::string CodeGenLinux::generateUnaryOperation(std::unique_ptr node) { const auto &op = node->value; - if (op == "!" || op == "++" || op == "--") - { - // Fetch the operand AST and its variable name (must be VariableAccess) - std::unique_ptr child = std::move(node->children[0]); - if (child->type != NodeType::VariableAccess) - throw std::runtime_error(op + " can only be applied to variables"); + if (op != "!" && op != "++" && op != "--") + throw std::runtime_error("Unsupported unary operator: " + op); - std::string varName = child->value; - auto &scp = *child->scope; - TypeInfo ti = scp.lookupType(scp.lookupVariable(varName).type); + auto child = std::move(node->children[0]); - if (op == "!") - { - auto r = emitExpression(std::move(child)); - out << " cmpq $0, %" << r - << "\n" - " sete %al\n" - " movzbq %al, %" - << r << "\n"; - noteType(r, node->scope->lookupType("boolean")); - return r; - } + const std::string varName = child->value; + auto &scp = *child->scope; + TypeInfo ti = scp.lookupType(scp.lookupVariable(varName).type); - if (ti.isFloat) - throw std::runtime_error("Operator '" + op + - "' not supported on float"); + if (op == "!") + { + auto r = emitExpression(std::move(child)); + TypeInfo boolType = node->scope->lookupType("boolean"); + r = castValue(r, regType.at(r), boolType); + std::string res = alloc.allocate(); + out << " cmpq $0, %" << r << "\n" + << " sete %al\n" + << " movzbq %al, %" << res << "\n"; + noteType(res, boolType); + return res; + } - uint64_t sz = ti.bits / 8; - char suf = integer_suffixes[ti.bits]; + if (child->type != NodeType::VariableAccess) + throw std::runtime_error(op + " can only be applied to variables"); + if (ti.isFloat) + throw std::runtime_error(op + " not supported on float"); - std::string r = alloc.allocate(); - std::string adj = adjustReg(r, ti.bits); - if (scp.isGlobalVariable(varName)) - { - out << " " << getCorrectMove(sz, /*isFloat=*/false) << " " - << varName << "(%rip), %" << adj << "\n"; - } - else - { - int64_t off = scp.getVariableOffset(varName); - out << " " << getCorrectMove(sz, /*isFloat=*/false) << " " << off - << "(%rbp), %" << adj << "\n"; - } + std::string ptr = scp.isGlobalVariable(varName) + ? varName + "(%rip)" + : std::to_string(scp.getVariableOffset(varName)) + "(%rbp)"; + uint64_t sz = ti.bits / 8; + char suf = integer_suffixes[ti.bits]; - out << " " << (op == "++" ? "inc" : "dec") << suf << " %" << adj - << "\n"; + std::string r = alloc.allocate(); + std::string adj = adjustReg(r, ti.bits); + out << " " << getCorrectMove(sz, /*isFloat=*/false) + << " " << ptr << ", %" << adj << "\n"; - if (scp.isGlobalVariable(varName)) - { - out << " " << getCorrectMove(sz, /*isFloat=*/false) << " %" - << adj << ", " << varName << "(%rip)\n"; - } - else - { - int64_t off = scp.getVariableOffset(varName); - out << " " << getCorrectMove(sz, /*isFloat=*/false) << " %" - << adj << ", " << off << "(%rbp)\n"; - } - noteType(adj, {ti.bits, ti.align, /*isFloat=*/false, ti.isSigned}); - return adj; - } - throw std::runtime_error("Unknown Unary Operator"); + out << " " << (op == "++" ? "inc" : "dec") << suf + << " %" << adj << "\n"; + + out << " " << getCorrectMove(sz, /*isFloat=*/false) + << " %" << adj << ", " << ptr << "\n"; + + noteType(adj, ti); + return adj; } std::string CodeGenLinux::emitExpression(std::unique_ptr node) { @@ -398,37 +489,34 @@ namespace zlang throw std::runtime_error("Unknown statement encountered."); } } - void CodeGenLinux::generateVariableReassignment( - std::unique_ptr statement) + void CodeGenLinux::generateVariableReassignment(std::unique_ptr statement) { 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); + + std::string addr = scp.isGlobalVariable(nm) + ? nm + "(%rip)" + : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)"; if (ti.isFloat) { - out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " - << (scp.isGlobalVariable(nm) - ? nm + "(%rip)" - : std::to_string(scp.getVariableOffset(nm)) + "(%rbp)") - << "\n"; - alloc.free(r); + out << " " << (sz == 4 ? "movss %" : "movsd %") << r << ", " << addr << "\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"; - alloc.free(r); + out << " " << getCorrectMove(sz, false) << " %" << adj << ", " << addr << "\n"; } + + alloc.free(r); } - void CodeGenLinux::generateVariableDeclaration( - std::unique_ptr statement) + void CodeGenLinux::generateVariableDeclaration(std::unique_ptr statement) { auto &scp = *statement->scope; auto nm = statement->value; @@ -437,80 +525,90 @@ namespace zlang if (!scp.isGlobalVariable(nm)) { - out << " # Making space for variable named: " << nm << "\n"; - out << " subq $" << sz << ", %rsp\n"; 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"; - alloc.free(r); } else { std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " %" << adj - << ", " + out << " " << getCorrectMove(sz, false) << " %" << adj << ", " << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" << "\n"; - alloc.free(r); } + + alloc.free(r); } else { if (ti.isFloat) { - out << " " - << (sz == 4 ? "movss $0.0, (%rsp)" : "movsd $0.0, (%rsp)") + 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 << " " << getCorrectMove(sz, false) << " $0, (%rsp)" + out << " xorq %rax, %rax\n"; + out << " mov" << integer_suffixes[ti.bits] << " %rax, " + << std::to_string(scp.getVariableOffset(nm)) + "(%rbp)" << "\n"; } } } - else + 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"; - alloc.free(r); + << nm + "(%rip)" + << "\n"; } else { std::string adj = adjustReg(r, ti.bits); - out << " " << getCorrectMove(sz, false) << " %" << adj - << ", " << nm + "(%rip)" << "\n"; - alloc.free(r); + out << " " << getCorrectMove(sz, false) << " %" << adj << ", " + << nm + "(%rip)" + << "\n"; } + + alloc.free(r); } else { if (ti.isFloat) { - out << " " - << (sz == 4 ? "movss $0.0, " + nm + "(%rip)" - : "movsd $0.0, " + nm + "(%rip)") + 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 << " " << getCorrectMove(sz, false) - << " $0, " + nm + "(%rip)" << "\n"; + out << " xorq %rax, %rax\n"; + out << " mov" << integer_suffixes[ti.bits] << " %rax, " + << nm + "(%rip)" + << "\n"; } } } - - return; } void CodeGenLinux::generateIfStatement(std::unique_ptr statement) { diff --git a/src/codegen/CodeGenWindows.cpp b/src/codegen/CodeGenWindows.cpp index 47816df..3b7713e 100644 --- a/src/codegen/CodeGenWindows.cpp +++ b/src/codegen/CodeGenWindows.cpp @@ -2,17 +2,147 @@ namespace zlang { - std::string CodeGenWindows::intToXmm(const std::string ®ister_int, uint32_t bits) + std::string CodeGenWindows::castValue(const std::string &srcReg, const TypeInfo &fromType, const TypeInfo &toType) { - std::string r_xmm = alloc.allocateXMM(); - const char *cvt = (bits == 32 ? "cvtsi2ss" : "cvtsi2sd"); - out << " " << cvt - << " " << r_xmm - << ", " << register_int - << "\n"; - alloc.free(register_int); - noteType(r_xmm, {bits, bits / 8, true, true}); - return r_xmm; + 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) { @@ -53,19 +183,8 @@ namespace zlang std::string CodeGenWindows::generateStringLiteral(std::unique_ptr node) { std::string lbl = "Lstr" + std::to_string(stringLabelCount++); - - std::ostringstream escaped; - for (char c : node->value) - { - if (c == '"') - escaped << "\"\""; // MASM doubles quotes inside strings - else - escaped << c; - } - outGlobal << lbl << " BYTE " - << '"' << escaped.str() << '"' << ", 0\n"; - + << '"' << ((node->value.length() == 0) ? "\\0" : node->value) << '"' << ", 0\n"; auto r = alloc.allocate(); out << " lea " << r << ", [" << lbl << "]\n"; noteType(r, node->scope->lookupType("string")); @@ -87,27 +206,18 @@ namespace zlang uint64_t sz = ti.bits / 8; uint64_t bits = ti.bits; - std::string address; - if (scope.isGlobalVariable(name)) - { - address = name; - } - else - { - int64_t off = scope.getVariableOffset(name); - address = "rbp - " + std::to_string(off); - } + 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"; + out << " movss " << r_xmm << ", DWORD PTR " << address << "\n"; } else if (sz == 8) { - out << " movsd " << r_xmm << ", QWORD PTR [" << address << "]\n"; + out << " movsd " << r_xmm << ", QWORD PTR " << address << "\n"; } else { @@ -140,7 +250,7 @@ namespace zlang throw std::runtime_error("Unsupported integer size: " + std::to_string(sz)); } - out << " mov " << r_adj << ", " << ptrSize << "[" << address << "]\n"; + out << " mov " << r_adj << ", " << ptrSize << address << "\n"; noteType(r_adj, ti); return r_adj; } @@ -156,29 +266,31 @@ namespace zlang 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"); - std::string xl = (t1.isFloat ? rl : intToXmm(rl, bits)); - std::string xr = (t2.isFloat ? rr : intToXmm(rr, bits)); if (op == "+") - out << " add" << suf << " " << xl << ", " << xr << "\n"; + out << " add" << suf << " " << rl << ", " << rr << "\n"; else if (op == "-") - out << " sub" << suf << " " << xl << ", " << xr << "\n"; + out << " sub" << suf << " " << rl << ", " << rr << "\n"; else if (op == "*") - out << " mul" << suf << " " << xl << ", " << xr << "\n"; + out << " mul" << suf << " " << rl << ", " << rr << "\n"; else if (op == "/") - out << " div" << suf << " " << xl << ", " << xr << "\n"; + out << " div" << suf << " " << rl << ", " << rr << "\n"; else if (assembly_comparison_operations.count(op)) { - out << " ucomi" << suf << " " << xl << ", " << xr << "\n"; + 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(xl); - alloc.free(xr); + alloc.free(rl); + alloc.free(rr); noteType(result, node->scope->lookupType("boolean")); return result; } @@ -187,14 +299,14 @@ namespace zlang throw std::runtime_error("Unsupported FP op: " + op); } - alloc.free(xr); - noteType(xl, tr); - return xl; + alloc.free(rr); + noteType(rl, tr); + return rl; } else { - auto dl = adjustReg(rl, bits); - auto dr = adjustReg(rr, bits); + std::string dl = rl; + std::string dr = rr; if (op == "+") out << " add " << dl << ", " << dr << "\n"; @@ -231,6 +343,7 @@ namespace zlang { throw std::runtime_error("Unsupported integer op: " + op); } + alloc.free(rr); noteType(rl, tr); return rl; @@ -244,6 +357,12 @@ namespace zlang 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(); @@ -253,27 +372,29 @@ namespace zlang out << " movzx " << result << ", " << result8 << "\n"; alloc.free(r); - - noteType(result, node->scope->lookupType("boolean")); + 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); - uint64_t bits = ti.bits; if (ti.isFloat) throw std::runtime_error("Increment/Decrement not supported on float"); std::string reg = generateVariableAccess(std::move(child)); - std::string adj = adjustReg(reg, bits); + 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 (bits) + switch (ti.bits) { case 64: ptrSize = "QWORD PTR "; @@ -288,18 +409,16 @@ namespace zlang ptrSize = "BYTE PTR "; break; default: - throw std::runtime_error("Unsupported variable size: " + std::to_string(bits)); + throw std::runtime_error("Unsupported variable size: " + std::to_string(ti.bits)); } - std::string addr = scp.isGlobalVariable(var) - ? var - : "rbp - " + std::to_string(scp.getVariableOffset(var)); - - out << " mov " << ptrSize << "[" << addr << "], " << adj << "\n"; + 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) @@ -324,7 +443,6 @@ namespace zlang throw std::runtime_error("Unknown expression encountered."); } } - void CodeGenWindows::emitPrologue(std::unique_ptr blockNode) { auto size = -blockNode->scope->stackOffset + 32; // +32 for shadow space @@ -333,13 +451,11 @@ namespace zlang 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) @@ -378,35 +494,30 @@ namespace zlang 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); - auto destAddr = [&]() -> std::string - { - if (scope.isGlobalVariable(name)) - return "[" + name + "]"; - else - return "[rbp - " + std::to_string(scope.getVariableOffset(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 r_xmm = emitExpression(std::move(statement->children.back())); - std::string movInstr = (bits == 32) ? "movss" : "movsd"; - - out << " " << movInstr << " " << destAddr() << ", " << r_xmm << "\n"; - alloc.free(r_xmm); + 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 r = emitExpression(std::move(statement->children.back())); std::string adj = adjustReg(r, bits); - std::string ptrPrefix; switch (bits) { @@ -423,29 +534,18 @@ namespace zlang ptrPrefix = "BYTE PTR "; break; default: - throw std::runtime_error("Unsupported variable size in reassignment: " + std::to_string(bits)); + throw std::runtime_error("Unsupported size in reassignment: " + std::to_string(bits)); } - - out << " mov " << ptrPrefix << destAddr() << ", " << adj << "\n"; + 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; - uint64_t sz = bits / 8; - - auto destAddr = [&]() -> std::string - { - if (scope.isGlobalVariable(name)) - return "[" + name + "]"; - else - return "[rbp - " + std::to_string(scope.getVariableOffset(name)) + "]"; - }; auto ptrPrefix = [&]() -> std::string { @@ -464,45 +564,45 @@ namespace zlang } }; - if (!scope.isGlobalVariable(name)) - { - out << " sub rsp, " << sz << "\n"; - } - 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 r_xmm = emitExpression(std::move(statement->children.back())); - std::string movInstr = (bits == 32) ? "movss" : "movsd"; - out << " " << movInstr << " " << destAddr() << ", " << r_xmm << "\n"; - alloc.free(r_xmm); + std::string movInstr = (bits == 32 ? "movss" : "movsd"); + out << " " << movInstr << " " + << ptrPrefix() << addr << ", " << r << "\n"; } else { - std::string r = emitExpression(std::move(statement->children.back())); std::string adj = adjustReg(r, bits); - out << " mov " << ptrPrefix() << destAddr() << ", " << adj << "\n"; - alloc.free(r); + 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(); - out << " pxor " << r_xmm << ", " << r_xmm << "\n"; - out << " movsd " << destAddr() << ", " << r_xmm << "\n"; + 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"; - out << " mov " << ptrPrefix() << destAddr() << ", rax\n"; + out << " xor rax, rax\n" + << " mov " << ptrPrefix() << addr << ", rax\n"; } } } - void CodeGenWindows::generateIfStatement(std::unique_ptr statement) { int id = blockLabelCount++; @@ -577,7 +677,6 @@ namespace zlang out << endLbl << ":\n\n"; } - void CodeGenWindows::generate(std::unique_ptr program) { std::vector globals; @@ -628,5 +727,4 @@ namespace zlang << "\n; ============== Globals End Here ==============\n" << out.str() << "END\n\n"; } - } // namespace zlang \ No newline at end of file From fb131269838469b800ce56a09138a6cd2f4154f4 Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Tue, 17 Jun 2025 16:43:50 +0530 Subject: [PATCH 4/4] FINALLYY, all tests pass. --- include/codegen/CodeGen.hpp | 6 +-- src/codegen/CodeGenLLVM.cpp | 8 ++-- src/codegen/CodeGenLinux.cpp | 80 +++++++++++++++--------------------- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 7921c81..53f396a 100644 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -158,7 +158,7 @@ namespace zlang public: ~CodeGenLinux() override = default; - CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream){}; + CodeGenLinux(std::ostream &outstream) : CodeGen(RegisterAllocator::forSysV(), outstream) {}; void generate(std::unique_ptr program) override; }; @@ -185,7 +185,7 @@ namespace zlang public: ~CodeGenWindows() override = default; - CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream){}; + CodeGenWindows(std::ostream &outstream) : CodeGen(RegisterAllocator::forMSVC(), outstream) {}; void generate(std::unique_ptr program) override; }; @@ -212,7 +212,7 @@ namespace zlang public: ~CodeGenLLVM() override = default; - CodeGenLLVM(std::ostream &outstream) : CodeGen(RegisterAllocator(), outstream){}; + 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/src/codegen/CodeGenLLVM.cpp b/src/codegen/CodeGenLLVM.cpp index 3d3fc65..20f256d 100644 --- a/src/codegen/CodeGenLLVM.cpp +++ b/src/codegen/CodeGenLLVM.cpp @@ -224,7 +224,7 @@ namespace zlang noteType(res, tr); return res; } - else + else { std::string cmpop = fcmp_ops.at(node->value); out << " " << res << " = fcmp " << cmpop << " " << target << " " << L << ", " << R << "\n"; @@ -257,7 +257,7 @@ namespace zlang } static const std::unordered_map> cmp_map = { - {"==", {"eq", "ueq"}}, {"!=", {"ne", "une"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; + {"==", {"eq", "eq"}}, {"!=", {"ne", "ne"}}, {"<", {"slt", "ult"}}, {"<=", {"sle", "ule"}}, {">", {"sgt", "ugt"}}, {">=", {"sge", "uge"}}}; auto it = cmp_map.find(node->value); if (it != cmp_map.end()) @@ -281,7 +281,7 @@ namespace zlang 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; @@ -303,7 +303,7 @@ namespace zlang } else if (node->value == "++" || node->value == "--") { - if (node->children[0]->type != NodeType::VariableAccess) + if (child_type != NodeType::VariableAccess) throw std::runtime_error(node->value + " can only be applied to variables"); if (ti.isFloat) diff --git a/src/codegen/CodeGenLinux.cpp b/src/codegen/CodeGenLinux.cpp index 434294d..78ca774 100644 --- a/src/codegen/CodeGenLinux.cpp +++ b/src/codegen/CodeGenLinux.cpp @@ -9,12 +9,12 @@ namespace zlang { if (fromType.bits == toType.bits && fromType.isFloat == toType.isFloat) { - if (!fromType.isFloat && fromType.bits == toType.bits && - fromType.isSigned != toType.isSigned) + if (!fromType.isFloat && fromType.isSigned != toType.isSigned) { std::string dst = alloc.allocate(); - out << " mov " << adjustReg(dst, fromType.bits) << ", " - << adjustReg(val, fromType.bits) << "\n"; + // AT&T: source, destination + out << " mov %" << adjustReg(val, fromType.bits) + << ", %" << adjustReg(dst, fromType.bits) << "\n"; alloc.free(val); noteType(dst, toType); return dst; @@ -26,7 +26,8 @@ namespace zlang { std::string dstX = alloc.allocateXMM(); auto instr = (fromType.bits < toType.bits) ? "cvtss2sd" : "cvtsd2ss"; - out << " " << instr << " " << dstX << ", " << val << "\n"; + // AT&T: source, destination + out << " " << instr << " %" << val << ", %" << dstX << "\n"; alloc.free(val); noteType(dstX, toType); return dstX; @@ -36,8 +37,8 @@ namespace zlang { std::string dstX = alloc.allocateXMM(); auto instr = (toType.bits == 32) ? "cvtsi2ss" : "cvtsi2sd"; - out << " " << instr << " " << dstX << ", " - << adjustReg(val, fromType.bits) << "\n"; + out << " " << instr << " %" << adjustReg(val, fromType.bits) + << ", %" << dstX << "\n"; alloc.free(val); noteType(dstX, toType); return dstX; @@ -47,75 +48,61 @@ namespace zlang { std::string dstG = alloc.allocate(); auto instr = (fromType.bits == 32) ? "cvttss2si" : "cvttsd2si"; - out << " " << instr << " " << adjustReg(dstG, toType.bits) - << ", " << val << "\n"; + out << " " << instr << " %" << val + << ", %" << adjustReg(dstG, toType.bits) << "\n"; alloc.free(val); noteType(dstG, toType); return dstG; } + // Integer casts if (!fromType.isFloat && !toType.isFloat) { std::string dstG = alloc.allocate(); std::string srcAdj = adjustReg(val, fromType.bits); - std::string dstAdjFull = adjustReg(dstG, toType.bits); + std::string dstAdj = adjustReg(dstG, toType.bits); if (toType.bits > fromType.bits) { + // Extension if (fromType.isSigned) { if (fromType.bits == 32 && toType.bits == 64) { - out << " movsxd " << dstAdjFull << ", " << srcAdj << "\n"; + // AT&T: source, destination + out << " movsxd %" << srcAdj << ", %" << dstAdj << "\n"; } else { - out << " movsx " << dstAdjFull << ", " << adjustReg(val, fromType.bits) << "\n"; + // movsx for 8/16 -> larger + out << " movsx %" << srcAdj << ", %" << dstAdj << "\n"; } } else { if (fromType.bits == 8 || fromType.bits == 16) { - out << " movzx " << dstAdjFull << ", " << adjustReg(val, fromType.bits) << "\n"; - } - else if (fromType.bits == 32 && toType.bits == 64) - { - out << " mov " << adjustReg(dstG, 32) << ", " << adjustReg(val, 32) << "\n"; + // AT&T: source, destination + out << " movzx %" << srcAdj << ", %" << dstAdj << "\n"; } else { - throw std::runtime_error("Unsupported unsigned cast: " + - std::to_string(fromType.bits) + " -> " + - std::to_string(toType.bits)); + // 32->64: normal mov zero-extends + out << " movl %" << adjustReg(val, 32) + << ", %" << adjustReg(dstG, 32) << "\n"; } } } else if (toType.bits < fromType.bits) { - if (toType.bits == 8 || toType.bits == 16) - { - if (toType.isSigned) - { - out << " movsx " << dstAdjFull << ", " << adjustReg(val, toType.bits) << "\n"; - } - else - { - out << " movzx " << dstAdjFull << ", " << adjustReg(val, toType.bits) << "\n"; - } - } - else if (toType.bits == 32) - { - out << " mov " << adjustReg(dstAdjFull, 32) << ", " << adjustReg(val, 32) << "\n"; - } - else - { - throw std::runtime_error("Unsupported downcast to " + std::to_string(toType.bits)); - } + // Truncation: move smaller portion + out << " mov %" << adjustReg(val, toType.bits) + << ", %" << dstAdj << "\n"; } else { - out << " mov " << dstAdjFull << ", " << srcAdj << "\n"; + // Same size: simple move + out << " mov %" << srcAdj << ", %" << dstAdj << "\n"; } alloc.free(val); @@ -239,21 +226,20 @@ namespace zlang if (tr.isFloat) { bool isF32 = (tr.bits == 32); - char suf = isF32 ? 's' : 'd'; // movss/addss vs movsd/addsd + std::string vsuf = isF32 ? "ss" : "sd"; std::string xl = rl, xr = rr; if (op == "+") - out << " add" << suf << suf << " %" << xr << ", %" << xl << "\n"; + out << " add" << vsuf << " %" << xr << ", %" << xl << "\n"; else if (op == "-") - out << " sub" << suf << suf << " %" << xr << ", %" << xl << "\n"; + out << " sub" << vsuf << " %" << xr << ", %" << xl << "\n"; else if (op == "*") - out << " mul" << suf << suf << " %" << xr << ", %" << xl << "\n"; + out << " mul" << vsuf << " %" << xr << ", %" << xl << "\n"; else if (op == "/") - out << " div" << suf << suf << " %" << xr << ", %" << xl << "\n"; + out << " div" << vsuf << " %" << xr << ", %" << xl << "\n"; else if (assembly_comparison_operations.count(op)) { - // unordered compare + set - out << " ucomi" << suf << suf << " %" << xr << ", %" << xl << "\n" + out << " ucomi" << vsuf << " %" << xr << ", %" << xl << "\n" << " " << assembly_comparison_operations.at(op) << " %al\n"; auto r_bool = alloc.allocate(); out << " movzbq %al, %" << r_bool << "\n";