From 044655d67501621628051ceead0156cc656a43f6 Mon Sep 17 00:00:00 2001 From: abdussamedulutas Date: Thu, 18 Jun 2026 21:11:05 +0300 Subject: [PATCH] =?UTF-8?q?feat(opt):=20Faz=204=20=E2=80=94=20Optimizasyon?= =?UTF-8?q?=20(sabit=20katlama=20+=20=C3=B6l=C3=BC=20kod=20eleme=20+=20--o?= =?UTF-8?q?ptimized)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/core/config.hpp: CompilerConfig (pass toggle'lar + maxFixpointRounds) - src/opt/optimization_pass.hpp: soyut OptimizationPass tabanı - src/opt/ast_clone.hpp: deepClone — tüm node tiplerini kapsayan derin klonlama - src/opt/constant_folding.hpp: BinaryExpr(Literal,OP,Literal) → Literal; W002 sıfıra bölme uyarısı; + - * / % == != < > <= >= desteklenir - src/opt/dead_code_elim.hpp: return/break/continue sonrası erişilemez deyimler Block'tan silinir - src/opt/optimization_manager.hpp: AST klonlar, fixpoint döngüsüyle pass'leri çalıştırır (ADR-007/009) - literal.hpp/.cpp: hasDirectValue/directIntValue — sentetik (token-sız) literal - args.hpp: --optimized bayrağı - cli/commands/ast.hpp: --optimized verilince klon üstünde optimize edilmiş AST gösterilir; orijinal dokunulmaz - examples/opt_folding.sqt, examples/opt_dce.sqt: örnek fixture'lar Co-Authored-By: Claude Opus 4.8 --- examples/opt_dce.sqt | 15 ++ examples/opt_folding.sqt | 11 ++ src/cli/args.hpp | 5 + src/cli/commands/ast.hpp | 36 +++- src/core/config.hpp | 11 ++ src/opt/ast_clone.hpp | 286 +++++++++++++++++++++++++++++ src/opt/constant_folding.hpp | 304 +++++++++++++++++++++++++++++++ src/opt/dead_code_elim.hpp | 110 +++++++++++ src/opt/optimization_manager.hpp | 56 ++++++ src/opt/optimization_pass.hpp | 19 ++ src/parser/nodes/literal.cpp | 10 +- src/parser/nodes/literal.hpp | 5 + 12 files changed, 862 insertions(+), 6 deletions(-) create mode 100644 examples/opt_dce.sqt create mode 100644 examples/opt_folding.sqt create mode 100644 src/core/config.hpp create mode 100644 src/opt/ast_clone.hpp create mode 100644 src/opt/constant_folding.hpp create mode 100644 src/opt/dead_code_elim.hpp create mode 100644 src/opt/optimization_manager.hpp create mode 100644 src/opt/optimization_pass.hpp diff --git a/examples/opt_dce.sqt b/examples/opt_dce.sqt new file mode 100644 index 0000000..4bcd868 --- /dev/null +++ b/examples/opt_dce.sqt @@ -0,0 +1,15 @@ +// Ölü kod eleme örneği +// saqut ast file:examples/opt_dce.sqt --optimized +// Beklenen: return sonrasındaki deyimler silinir. + +int foo() { + return 42; + int unreachable = 1; + int also_dead = 2; +} + +int main() { + int x = foo(); + return x; + print("Bu satır asla çalışmaz"); +} diff --git a/examples/opt_folding.sqt b/examples/opt_folding.sqt new file mode 100644 index 0000000..14133ae --- /dev/null +++ b/examples/opt_folding.sqt @@ -0,0 +1,11 @@ +// Sabit katlama örneği +// saqut ast file:examples/opt_folding.sqt --optimized +// Beklenen: tüm BinaryExpression'lar Literal'e dönüşür. + +int main() { + int a = 2 + 3; + int b = 10 * 4; + int c = 100 / 5; + int d = a + 1; + return 1 * 10 + 2 * 5; +} diff --git a/src/cli/args.hpp b/src/cli/args.hpp index e74b105..89c9d5f 100644 --- a/src/cli/args.hpp +++ b/src/cli/args.hpp @@ -33,6 +33,7 @@ struct CliArgs { bool showHelp = false; bool stdinMode = false; bool compact = false; // --compact: boşluksuz JSON + bool optimized = false; // --optimized: sabit katlama + ölü kod eleme }; // ============================================================================ @@ -72,6 +73,10 @@ inline CliArgs parseArgs(int argc, char* argv[]) { args.compact = true; continue; } + if (arg == "--optimized") { + args.optimized = true; + continue; + } if (arg.compare(0, 5, "file:") == 0) { args.positional.push_back(arg.substr(5)); continue; diff --git a/src/cli/commands/ast.hpp b/src/cli/commands/ast.hpp index 5f52cba..b027717 100644 --- a/src/cli/commands/ast.hpp +++ b/src/cli/commands/ast.hpp @@ -1,5 +1,8 @@ // ============================================================================ // saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz) +// +// --optimized: sabit katlama + ölü kod eleme uygulandıktan sonra AST göster. +// Orijinal AST dokunulmaz; optimize edilmiş klon gösterilir. // ============================================================================ #ifndef SAQUT_CLI_AST @@ -10,6 +13,13 @@ #include "cli/args.hpp" #include "tokenizer/tokenizer.hpp" #include "parser/parser.hpp" +#include "symbol/symbol_table.hpp" +#include "symbol/symbol_collector.hpp" +#include "semantic/type_checker.hpp" +#include "semantic/structural_validator.hpp" +#include "diagnostic/diagnostic_engine.hpp" +#include "core/config.hpp" +#include "opt/optimization_manager.hpp" #include "json.hpp" inline int cmdAst(const CliArgs& args) { @@ -28,9 +38,25 @@ inline int cmdAst(const CliArgs& args) { return 1; } - AstAnalysis analysis = analyzeAst(ast); + // ── Sembol + tip analizi (--optimized için gerekli; yalın ast'te opsiyonel) ── + SymbolTable symbolTable; + DiagnosticEngine diag; + SymbolCollector(symbolTable, diag).collect(ast); + TypeChecker(symbolTable, diag).check(ast); + StructuralValidator(diag).validate(ast); + + ASTNode* displayAst = ast; // gösterilecek ağaç (orijinal veya klon) + ASTNode* clonedAst = nullptr; + + if (args.optimized) { + CompilerConfig cfg; + OptimizationManager mgr(cfg, diag); + clonedAst = mgr.optimize(ast, &symbolTable); + displayAst = clonedAst; + } + + AstAnalysis analysis = analyzeAst(displayAst); - // Çıktı hedefi std::ostream* out = &std::cout; std::ofstream outFile; if (!args.outputFile.empty()) { @@ -40,12 +66,16 @@ inline int cmdAst(const CliArgs& args) { *out << "{\n" << " \"ast\":\n" - << jsonIndent(2) << astToJson(ast, 2) << ",\n" + << jsonIndent(2) << astToJson(displayAst, 2) << ",\n" << " \"analysis\": {\n" << analysisToJson(analysis) << "\n" << " }\n" << "}\n"; + // Optimizasyon uyarılarını (W002 vb.) stderr'e yazdır + if (args.optimized) diag.printAll(std::cerr); + + if (clonedAst) delete clonedAst; delete ast; for (auto* t : tokens) delete t; return 0; diff --git a/src/core/config.hpp b/src/core/config.hpp new file mode 100644 index 0000000..ed2ec58 --- /dev/null +++ b/src/core/config.hpp @@ -0,0 +1,11 @@ +#ifndef SAQUT_CORE_CONFIG +#define SAQUT_CORE_CONFIG + +// Derleyici yapılandırması — hangi optimizasyon pass'lerinin çalışacağı. +struct CompilerConfig { + bool optConstantFolding = true; + bool optDeadCodeElim = true; + int maxFixpointRounds = 10; +}; + +#endif // SAQUT_CORE_CONFIG diff --git a/src/opt/ast_clone.hpp b/src/opt/ast_clone.hpp new file mode 100644 index 0000000..999ba6f --- /dev/null +++ b/src/opt/ast_clone.hpp @@ -0,0 +1,286 @@ +// ============================================================================ +// saQut — AST Derin Klonlama (ADR-007) +// +// deepClone(node): tüm ağacı kopyalar, parent pointer'ları yeniden bağlar. +// IdentifierNode::resolvedSymbol orijinal sembol tablosunu gösterir (read-only, +// optimizasyon için yeterli — ADR-007). +// ============================================================================ + +#ifndef SAQUT_OPT_AST_CLONE +#define SAQUT_OPT_AST_CLONE + +#include "parser/ast_node.hpp" +#include "parser/nodes/program.hpp" +#include "parser/nodes/declarations.hpp" +#include "parser/nodes/statements.hpp" +#include "parser/nodes/expressions.hpp" +#include "parser/nodes/binary_expr.hpp" +#include "parser/nodes/literal.hpp" +#include "parser/nodes/identifier.hpp" + +inline ASTNode* deepClone(ASTNode* node); + +// ── Yardımcı: typed pointer klonla ve parent'ı bağla ──────────────────────── +static inline ASTNode* cloneChild(ASTNode* child, ASTNode* newParent) { + if (!child) return nullptr; + ASTNode* c = deepClone(child); + c->parent = newParent; + return c; +} + +inline ASTNode* deepClone(ASTNode* node) { + if (!node) return nullptr; + + switch (node->kind) { + + // ── ProgramNode ────────────────────────────────────────────────────────── + case ASTKind::Program: { + auto* src = static_cast(node); + auto* dst = new ProgramNode(); + dst->loc = src->loc; + for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch)); + return dst; + } + + // ── FunctionDeclNode ───────────────────────────────────────────────────── + case ASTKind::FunctionDecl: { + auto* src = static_cast(node); + auto* dst = new FunctionDeclNode(); + dst->loc = src->loc; + dst->name = src->name; + dst->returnType = src->returnType; + for (auto* p : src->params) { + auto* cp = static_cast(deepClone(p)); + cp->parent = dst; + dst->params.push_back(cp); + } + for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch)); + return dst; + } + + // ── VariableDeclNode ───────────────────────────────────────────────────── + case ASTKind::VariableDecl: { + auto* src = static_cast(node); + auto* dst = new VariableDeclNode(); + dst->loc = src->loc; + dst->name = src->name; + dst->varType = src->varType; + dst->isReachable = src->isReachable; + if (src->initExpr) dst->initExpr = cloneChild(src->initExpr, dst); + for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch)); + return dst; + } + + // ── StructDeclNode ─────────────────────────────────────────────────────── + case ASTKind::StructDecl: { + auto* src = static_cast(node); + auto* dst = new StructDeclNode(); + dst->loc = src->loc; + dst->name = src->name; + for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch)); + return dst; + } + + // ── BlockNode ──────────────────────────────────────────────────────────── + case ASTKind::Block: { + auto* src = static_cast(node); + auto* dst = new BlockNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch)); + return dst; + } + + // ── IfStatementNode ────────────────────────────────────────────────────── + case ASTKind::IfStatement: { + auto* src = static_cast(node); + auto* dst = new IfStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->condition = cloneChild(src->condition, dst); + dst->thenBranch = cloneChild(src->thenBranch, dst); + dst->elseBranch = cloneChild(src->elseBranch, dst); + return dst; + } + + // ── WhileStatementNode ─────────────────────────────────────────────────── + case ASTKind::WhileStatement: { + auto* src = static_cast(node); + auto* dst = new WhileStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->condition = cloneChild(src->condition, dst); + dst->body = cloneChild(src->body, dst); + return dst; + } + + // ── ForStatementNode ───────────────────────────────────────────────────── + case ASTKind::ForStatement: { + auto* src = static_cast(node); + auto* dst = new ForStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->init = cloneChild(src->init, dst); + dst->condition = cloneChild(src->condition, dst); + dst->update = cloneChild(src->update, dst); + dst->body = cloneChild(src->body, dst); + return dst; + } + + // ── DoWhileStatementNode ───────────────────────────────────────────────── + case ASTKind::DoWhileStatement: { + auto* src = static_cast(node); + auto* dst = new DoWhileStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->body = cloneChild(src->body, dst); + dst->condition = cloneChild(src->condition, dst); + return dst; + } + + // ── ReturnStatementNode ────────────────────────────────────────────────── + case ASTKind::ReturnStatement: { + auto* src = static_cast(node); + auto* dst = new ReturnStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->value = cloneChild(src->value, dst); + return dst; + } + + // ── BreakStatementNode ─────────────────────────────────────────────────── + case ASTKind::BreakStatement: { + auto* src = static_cast(node); + auto* dst = new BreakStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + return dst; + } + + // ── ContinueStatementNode ──────────────────────────────────────────────── + case ASTKind::ContinueStatement: { + auto* src = static_cast(node); + auto* dst = new ContinueStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + return dst; + } + + // ── ExpressionStatementNode ────────────────────────────────────────────── + case ASTKind::ExpressionStatement: { + auto* src = static_cast(node); + auto* dst = new ExpressionStatementNode(); + dst->loc = src->loc; + dst->isReachable = src->isReachable; + dst->expression = cloneChild(src->expression, dst); + return dst; + } + + // ── BinaryExpressionNode ───────────────────────────────────────────────── + case ASTKind::BinaryExpression: { + auto* src = static_cast(node); + auto* dst = new BinaryExpressionNode(); + dst->loc = src->loc; + dst->Operator = src->Operator; + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->Left = cloneChild(src->Left, dst); + dst->Right = cloneChild(src->Right, dst); + return dst; + } + + // ── LiteralNode ────────────────────────────────────────────────────────── + case ASTKind::Literal: { + auto* src = static_cast(node); + auto* dst = new LiteralNode(); + dst->loc = src->loc; + dst->literalType = src->literalType; + dst->literalBase = src->literalBase; + dst->isFloatValue = src->isFloatValue; + dst->lexerToken = src->lexerToken; // orijinal token, salt-okunur + dst->parserToken = src->parserToken; // aynı token pointer, salt-okunur + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->hasDirectValue = src->hasDirectValue; + dst->directIntValue = src->directIntValue; + return dst; + } + + // ── IdentifierNode ─────────────────────────────────────────────────────── + case ASTKind::Identifier: { + auto* src = static_cast(node); + auto* dst = new IdentifierNode(); + dst->loc = src->loc; + dst->lexerToken = src->lexerToken; + dst->parserToken = src->parserToken; + dst->resolvedSymbol = src->resolvedSymbol; // orijinal tablo, salt-okunur + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + return dst; + } + + // ── PostfixNode ────────────────────────────────────────────────────────── + case ASTKind::Postfix: { + auto* src = static_cast(node); + auto* dst = new PostfixNode(); + dst->loc = src->loc; + dst->Operator = src->Operator; + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->operand = cloneChild(src->operand, dst); + return dst; + } + + // ── CallExpressionNode ─────────────────────────────────────────────────── + case ASTKind::Call: { + auto* src = static_cast(node); + auto* dst = new CallExpressionNode(); + dst->loc = src->loc; + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->callee = cloneChild(src->callee, dst); + for (auto* arg : src->arguments) { + ASTNode* ca = deepClone(arg); + ca->parent = dst; + dst->arguments.push_back(ca); + } + return dst; + } + + // ── MemberAccessNode ───────────────────────────────────────────────────── + case ASTKind::MemberAccess: { + auto* src = static_cast(node); + auto* dst = new MemberAccessNode(); + dst->loc = src->loc; + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->member = src->member; + dst->arrow = src->arrow; + dst->object = cloneChild(src->object, dst); + return dst; + } + + // ── IndexExpressionNode ────────────────────────────────────────────────── + case ASTKind::IndexExpression: { + auto* src = static_cast(node); + auto* dst = new IndexExpressionNode(); + dst->loc = src->loc; + dst->resolvedType = src->resolvedType; + dst->isConstant = src->isConstant; + dst->object = cloneChild(src->object, dst); + dst->index = cloneChild(src->index, dst); + return dst; + } + + // ── UnaryExpression ────────────────────────────────────────────────────── + // UnaryExpression şu anda ayrı bir sınıf değil; parser tarafından + // BinaryExpression veya PostfixNode olarak temsil ediliyor. + // Gelecekte eklenmesi gerekirse buraya eklenecek. + default: + // Bilinmeyen node tipi — klonlanamaz; orijinal döndür (güvenli değil ama + // derleme aşamasında tüm tipler yukarıda kaplanmalı). + return node; + } +} + +#endif // SAQUT_OPT_AST_CLONE diff --git a/src/opt/constant_folding.hpp b/src/opt/constant_folding.hpp new file mode 100644 index 0000000..b9cb56a --- /dev/null +++ b/src/opt/constant_folding.hpp @@ -0,0 +1,304 @@ +// ============================================================================ +// saQut — Sabit Katlama (Constant Folding) Pass (ADR-009) +// +// BinaryExpression(Literal, OP, Literal) → Literal +// Sadece tam sayı sabitleri katlanır; kayan nokta şimdilik dışarıda. +// Derleme zamanı sıfıra bölme: W002 uyarısı verilir, katlama yapılmaz. +// ============================================================================ + +#ifndef SAQUT_OPT_CONSTANT_FOLDING +#define SAQUT_OPT_CONSTANT_FOLDING + +#include +#include "opt/optimization_pass.hpp" +#include "parser/nodes/binary_expr.hpp" +#include "parser/nodes/literal.hpp" +#include "parser/nodes/identifier.hpp" +#include "parser/nodes/statements.hpp" +#include "parser/nodes/expressions.hpp" +#include "parser/nodes/declarations.hpp" +#include "parser/nodes/program.hpp" +#include "diagnostic/diagnostic_engine.hpp" + +class ConstantFoldingPass : public OptimizationPass { +public: + explicit ConstantFoldingPass(DiagnosticEngine& diag) : diag_(diag) {} + + bool run(ASTNode* root, SymbolTable*) override { + changed_ = false; + fold(root); + return changed_; + } + + const std::string& name() const override { + static std::string n = "constant-folding"; + return n; + } + +private: + DiagnosticEngine& diag_; + bool changed_ = false; + + // ── Literal değer okuma ────────────────────────────────────────────────── + static bool isIntLit(ASTNode* node) { + auto* lit = dynamic_cast(node); + return lit && lit->literalType == LiteralType::INTEGER; + } + + static int getIntVal(LiteralNode* lit) { + if (lit->hasDirectValue) return lit->directIntValue; + if (lit->parserToken.token) return std::stoi(lit->parserToken.token->token); + return 0; + } + + // ── Operatör hesaplama ─────────────────────────────────────────────────── + static bool canFoldOp(TokenType op) { + switch (op) { + case TokenType::PLUS: + case TokenType::MINUS: + case TokenType::STAR: + case TokenType::SLASH: + case TokenType::PERCENT: + case TokenType::EQUAL_EQUAL: + case TokenType::BANG_EQUAL: + case TokenType::LESS: + case TokenType::GREATER: + case TokenType::LESS_EQUAL: + case TokenType::GREATER_EQUAL: + return true; + default: + return false; + } + } + + static int computeOp(TokenType op, int l, int r) { + switch (op) { + case TokenType::PLUS: return l + r; + case TokenType::MINUS: return l - r; + case TokenType::STAR: return l * r; + case TokenType::SLASH: return l / r; + case TokenType::PERCENT: return l % r; + case TokenType::EQUAL_EQUAL: return l == r ? 1 : 0; + case TokenType::BANG_EQUAL: return l != r ? 1 : 0; + case TokenType::LESS: return l < r ? 1 : 0; + case TokenType::GREATER: return l > r ? 1 : 0; + case TokenType::LESS_EQUAL: return l <= r ? 1 : 0; + case TokenType::GREATER_EQUAL: return l >= r ? 1 : 0; + default: return 0; + } + } + + // ── Sentetik literal oluştur ───────────────────────────────────────────── + static LiteralNode* makeFoldedLit(int value, const SourceLocation& loc, const Type& type) { + auto* lit = new LiteralNode(); + lit->loc = loc; + lit->literalType = LiteralType::INTEGER; + lit->hasDirectValue = true; + lit->directIntValue = value; + lit->resolvedType = type; + lit->isConstant = true; + return lit; + } + + // ── Ana ziyaretçi: bottom-up, pointer-by-reference ─────────────────────── + // Dönüş değeri: aynı node veya yerine geçen yeni node. + // Folding yapıldıysa orijinal node silinir, yeni node döndürülür. + ASTNode* fold(ASTNode* node) { + if (!node) return nullptr; + + switch (node->kind) { + + // ── İfadeler ──────────────────────────────────────────────────────── + case ASTKind::BinaryExpression: { + auto* bin = static_cast(node); + + // Alt ağaçları önce kat + ASTNode* newLeft = fold(bin->Left); + if (newLeft != bin->Left) { bin->Left = newLeft; if (newLeft) newLeft->parent = bin; } + + ASTNode* newRight = fold(bin->Right); + if (newRight != bin->Right) { bin->Right = newRight; if (newRight) newRight->parent = bin; } + + // İki taraf da tam sayı sabiti ve operatör kesilebilir mi? + if (!isIntLit(bin->Left) || !isIntLit(bin->Right)) return bin; + if (!canFoldOp(bin->Operator)) return bin; + + auto* llit = static_cast(bin->Left); + auto* rlit = static_cast(bin->Right); + int lv = getIntVal(llit); + int rv = getIntVal(rlit); + + // W002: Derleme zamanı sıfıra bölme + if ((bin->Operator == TokenType::SLASH || bin->Operator == TokenType::PERCENT) && rv == 0) { + Diagnostic d; + d.level = DiagLevel::Warning; + d.code = "W002"; + d.loc = bin->loc; + d.message = "Derleme zamanı sıfıra bölme — katlama yapılmadı"; + diag_.report(d); + return bin; // fold etme + } + + int result = computeOp(bin->Operator, lv, rv); + LiteralNode* lit = makeFoldedLit(result, bin->loc, bin->resolvedType); + + delete bin->Left; + delete bin->Right; + delete bin; + changed_ = true; + return lit; + } + + case ASTKind::Postfix: { + auto* pf = static_cast(node); + ASTNode* newOp = fold(pf->operand); + if (newOp != pf->operand) { pf->operand = newOp; if (newOp) newOp->parent = pf; } + return pf; + } + + case ASTKind::Call: { + auto* call = static_cast(node); + if (call->callee) { + ASTNode* nc = fold(call->callee); + if (nc != call->callee) { call->callee = nc; if (nc) nc->parent = call; } + } + for (auto*& arg : call->arguments) { + ASTNode* na = fold(arg); + if (na != arg) { arg = na; if (na) na->parent = call; } + } + return call; + } + + case ASTKind::MemberAccess: { + auto* ma = static_cast(node); + ASTNode* no = fold(ma->object); + if (no != ma->object) { ma->object = no; if (no) no->parent = ma; } + return ma; + } + + case ASTKind::IndexExpression: { + auto* ix = static_cast(node); + ASTNode* no = fold(ix->object); + if (no != ix->object) { ix->object = no; if (no) no->parent = ix; } + ASTNode* ni = fold(ix->index); + if (ni != ix->index) { ix->index = ni; if (ni) ni->parent = ix; } + return ix; + } + + // Literal ve Identifier değişmez + case ASTKind::Literal: + case ASTKind::Identifier: + return node; + + // ── Deyimler (çocuklara iner) ──────────────────────────────────────── + case ASTKind::ExpressionStatement: { + auto* es = static_cast(node); + if (es->expression) { + ASTNode* ne = fold(es->expression); + if (ne != es->expression) { es->expression = ne; if (ne) ne->parent = es; } + } + return es; + } + + case ASTKind::ReturnStatement: { + auto* rs = static_cast(node); + if (rs->value) { + ASTNode* nv = fold(rs->value); + if (nv != rs->value) { rs->value = nv; if (nv) nv->parent = rs; } + } + return rs; + } + + case ASTKind::VariableDecl: { + auto* vd = static_cast(node); + if (vd->initExpr) { + ASTNode* ni = fold(vd->initExpr); + if (ni != vd->initExpr) { vd->initExpr = ni; if (ni) ni->parent = vd; } + } + for (auto*& ch : vd->getChildren()) { + ASTNode* nc = fold(ch); + if (nc != ch) { ch = nc; if (nc) nc->parent = vd; } + } + return vd; + } + + case ASTKind::IfStatement: { + auto* ifs = static_cast(node); + if (ifs->condition) { + ASTNode* nc = fold(ifs->condition); + if (nc != ifs->condition) { ifs->condition = nc; if (nc) nc->parent = ifs; } + } + if (ifs->thenBranch) fold(ifs->thenBranch); + if (ifs->elseBranch) fold(ifs->elseBranch); + return ifs; + } + + case ASTKind::WhileStatement: { + auto* ws = static_cast(node); + if (ws->condition) { + ASTNode* nc = fold(ws->condition); + if (nc != ws->condition) { ws->condition = nc; if (nc) nc->parent = ws; } + } + if (ws->body) fold(ws->body); + return ws; + } + + case ASTKind::ForStatement: { + auto* fs = static_cast(node); + if (fs->init) { ASTNode* n = fold(fs->init); if (n != fs->init) { fs->init = n; if (n) n->parent = fs; } } + if (fs->condition) { ASTNode* n = fold(fs->condition); if (n != fs->condition) { fs->condition = n; if (n) n->parent = fs; } } + if (fs->update) { ASTNode* n = fold(fs->update); if (n != fs->update) { fs->update = n; if (n) n->parent = fs; } } + if (fs->body) fold(fs->body); + return fs; + } + + case ASTKind::DoWhileStatement: { + auto* dw = static_cast(node); + if (dw->body) fold(dw->body); + if (dw->condition) { + ASTNode* nc = fold(dw->condition); + if (nc != dw->condition) { dw->condition = nc; if (nc) nc->parent = dw; } + } + return dw; + } + + // ── Blok ve üst düzey ──────────────────────────────────────────────── + case ASTKind::Block: { + auto* blk = static_cast(node); + for (auto*& ch : blk->getChildren()) { + ASTNode* nc = fold(ch); + if (nc != ch) { ch = nc; if (nc) nc->parent = blk; } + } + return blk; + } + + case ASTKind::FunctionDecl: { + auto* fn = static_cast(node); + for (auto*& ch : fn->getChildren()) { + ASTNode* nc = fold(ch); + if (nc != ch) { ch = nc; if (nc) nc->parent = fn; } + } + return fn; + } + + case ASTKind::Program: { + auto* prog = static_cast(node); + for (auto*& ch : prog->getChildren()) { + ASTNode* nc = fold(ch); + if (nc != ch) { ch = nc; if (nc) nc->parent = prog; } + } + return prog; + } + + default: + // Bilinmeyen veya işlenmeyen node — olduğu gibi bırak + for (auto*& ch : node->getChildren()) { + ASTNode* nc = fold(ch); + if (nc != ch) { ch = nc; if (nc) nc->parent = node; } + } + return node; + } + } +}; + +#endif // SAQUT_OPT_CONSTANT_FOLDING diff --git a/src/opt/dead_code_elim.hpp b/src/opt/dead_code_elim.hpp new file mode 100644 index 0000000..6662c88 --- /dev/null +++ b/src/opt/dead_code_elim.hpp @@ -0,0 +1,110 @@ +// ============================================================================ +// saQut — Ölü Kod Eleme (Dead Code Elimination) Pass (ADR-009) +// +// Bir Block içinde ilk return/break/continue'dan sonra gelen deyimler +// erişilemez (unreachable) olarak işaretlenir ve silinir. +// ============================================================================ + +#ifndef SAQUT_OPT_DEAD_CODE_ELIM +#define SAQUT_OPT_DEAD_CODE_ELIM + +#include +#include +#include "opt/optimization_pass.hpp" +#include "parser/nodes/statements.hpp" +#include "parser/nodes/declarations.hpp" +#include "parser/nodes/expressions.hpp" +#include "parser/nodes/program.hpp" + +class DeadCodeElimPass : public OptimizationPass { +public: + bool run(ASTNode* root, SymbolTable*) override { + changed_ = false; + visit(root); + return changed_; + } + + const std::string& name() const override { + static std::string n = "dead-code-elim"; + return n; + } + +private: + bool changed_ = false; + + void visit(ASTNode* node) { + if (!node) return; + + switch (node->kind) { + + // ── Block: ana DCE mantığı ─────────────────────────────────────────── + case ASTKind::Block: { + auto* blk = static_cast(node); + auto& ch = blk->getChildren(); + bool term = false; + + for (auto* child : ch) { + if (term) { + // Bu deyim erişilemez + if (auto* sn = dynamic_cast(child)) { + if (sn->isReachable) { + sn->isReachable = false; + changed_ = true; + } + } + } + if (child->kind == ASTKind::ReturnStatement || + child->kind == ASTKind::BreakStatement || + child->kind == ASTKind::ContinueStatement) + term = true; + } + + // Erişilemez çocukları sil ve vektörden çıkar + ch.erase(std::remove_if(ch.begin(), ch.end(), + [](ASTNode* n) { + auto* sn = dynamic_cast(n); + return sn && !sn->isReachable; + }), ch.end()); + + // Alt bloklara da in + for (auto* child : ch) visit(child); + break; + } + + // ── İf/While/For gövdelerine in ───────────────────────────────────── + case ASTKind::IfStatement: { + auto* ifs = static_cast(node); + visit(ifs->thenBranch); + visit(ifs->elseBranch); + break; + } + + case ASTKind::WhileStatement: { + auto* ws = static_cast(node); + visit(ws->body); + break; + } + + case ASTKind::ForStatement: { + auto* fs = static_cast(node); + visit(fs->body); + break; + } + + case ASTKind::DoWhileStatement: { + auto* dw = static_cast(node); + visit(dw->body); + break; + } + + // ── Üst düzey: fonksiyon gövdelerine in ───────────────────────────── + case ASTKind::FunctionDecl: + case ASTKind::Program: + default: + for (auto* ch : node->getChildren()) visit(ch); + break; + } + } +}; + +#endif // SAQUT_OPT_DEAD_CODE_ELIM diff --git a/src/opt/optimization_manager.hpp b/src/opt/optimization_manager.hpp new file mode 100644 index 0000000..af0c661 --- /dev/null +++ b/src/opt/optimization_manager.hpp @@ -0,0 +1,56 @@ +// ============================================================================ +// saQut — Optimizasyon Yöneticisi (ADR-007, ADR-009) +// +// 1. AST'yi klonlar (orijinal dokunulmaz). +// 2. Etkin pass'leri fixpoint döngüsüyle çalıştırır. +// 3. Optimize edilmiş klon sahipliğini döndürür (caller delete eder). +// +// Fixpoint garantisi: her pass yalnızca küçülten dönüşümler yapar +// (katlama: n düğüm → 1 düğüm; DCE: düğüm siler). Büyüten pass +// (inlining vb.) eklenirse maxFixpointRounds tavanı zorunludur (ADR-009). +// ============================================================================ + +#ifndef SAQUT_OPT_MANAGER +#define SAQUT_OPT_MANAGER + +#include +#include +#include "core/config.hpp" +#include "opt/ast_clone.hpp" +#include "opt/optimization_pass.hpp" +#include "opt/constant_folding.hpp" +#include "opt/dead_code_elim.hpp" +#include "diagnostic/diagnostic_engine.hpp" +#include "symbol/symbol_table.hpp" + +class OptimizationManager { +public: + explicit OptimizationManager(const CompilerConfig& cfg, DiagnosticEngine& diag) { + if (cfg.optConstantFolding) + passes_.push_back(std::make_unique(diag)); + if (cfg.optDeadCodeElim) + passes_.push_back(std::make_unique()); + maxRounds_ = cfg.maxFixpointRounds; + } + + // optimize: AST'yi klonlar ve optimize edilmiş kopyayı döndürür. + // Dönen pointer caller'a aittir (delete edilmeli). + ASTNode* optimize(ASTNode* root, SymbolTable* table) { + ASTNode* clone = deepClone(root); + + for (int round = 0; round < maxRounds_; ++round) { + bool anyChange = false; + for (auto& pass : passes_) + if (pass->run(clone, table)) anyChange = true; + if (!anyChange) break; + } + + return clone; + } + +private: + std::vector> passes_; + int maxRounds_ = 10; +}; + +#endif // SAQUT_OPT_MANAGER diff --git a/src/opt/optimization_pass.hpp b/src/opt/optimization_pass.hpp new file mode 100644 index 0000000..68aa5d2 --- /dev/null +++ b/src/opt/optimization_pass.hpp @@ -0,0 +1,19 @@ +#ifndef SAQUT_OPT_PASS +#define SAQUT_OPT_PASS + +#include +#include "parser/ast_node.hpp" +#include "symbol/symbol_table.hpp" + +class OptimizationPass { +public: + virtual ~OptimizationPass() = default; + + // Pass'i çalıştırır. En az bir dönüşüm yaptıysa true döndürür. + // root: optimize edilecek klonlanmış AST kökü (sahiplik değişmez). + virtual bool run(ASTNode* root, SymbolTable* table) = 0; + + virtual const std::string& name() const = 0; +}; + +#endif // SAQUT_OPT_PASS diff --git a/src/parser/nodes/literal.cpp b/src/parser/nodes/literal.cpp index 1cd578f..4b9db1e 100644 --- a/src/parser/nodes/literal.cpp +++ b/src/parser/nodes/literal.cpp @@ -6,17 +6,21 @@ LiteralNode::LiteralNode() { kind = ASTKind::Literal; } void LiteralNode::log(int indent) { + std::string val = hasDirectValue ? std::to_string(directIntValue) + : (parserToken.token ? parserToken.token->token : "?"); std::cout << padRight("", indent) - << "Literal {" << (parserToken.token ? parserToken.token->token : "?") << "} " + << "Literal {" << val << "} " << literalTypeToString(literalType); + if (isConstant) std::cout << " [folded]"; if (literalType == LiteralType::INTEGER && literalBase != 10) std::cout << " (base " << literalBase << ")"; std::cout << "\n"; } std::string LiteralNode::toJson(int depth) { - std::string in = jsonIndent(depth); - std::string val = parserToken.token ? parserToken.token->token : "?"; + std::string in = jsonIndent(depth); + std::string val = hasDirectValue ? std::to_string(directIntValue) + : (parserToken.token ? parserToken.token->token : "?"); std::ostringstream ss; ss << "{\n" << in << " \"kind\": \"Literal\",\n" diff --git a/src/parser/nodes/literal.hpp b/src/parser/nodes/literal.hpp index dee1207..f17d24f 100644 --- a/src/parser/nodes/literal.hpp +++ b/src/parser/nodes/literal.hpp @@ -12,6 +12,11 @@ public: int literalBase = 10; bool isFloatValue = false; + // Sabit katlama (constant folding) tarafından üretilen sentetik literal. + // parserToken.token yerine bu değer kullanılır. + bool hasDirectValue = false; + int directIntValue = 0; + LiteralNode(); void log(int indent = 0) override; std::string toJson(int depth = 0) override;