feat(opt): Faz 4 — Optimizasyon (sabit katlama + ölü kod eleme + --optimized)

- 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
This commit is contained in:
saqut 2026-06-18 21:11:05 +03:00
parent 6681b1be78
commit d6301ccb5f
12 changed files with 862 additions and 6 deletions

15
examples/opt_dce.sqt Normal file
View File

@ -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");
}

11
examples/opt_folding.sqt Normal file
View File

@ -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;
}

View File

@ -33,6 +33,7 @@ struct CliArgs {
bool showHelp = false; bool showHelp = false;
bool stdinMode = false; bool stdinMode = false;
bool compact = false; // --compact: boşluksuz JSON 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; args.compact = true;
continue; continue;
} }
if (arg == "--optimized") {
args.optimized = true;
continue;
}
if (arg.compare(0, 5, "file:") == 0) { if (arg.compare(0, 5, "file:") == 0) {
args.positional.push_back(arg.substr(5)); args.positional.push_back(arg.substr(5));
continue; continue;

View File

@ -1,5 +1,8 @@
// ============================================================================ // ============================================================================
// saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz) // 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 #ifndef SAQUT_CLI_AST
@ -10,6 +13,13 @@
#include "cli/args.hpp" #include "cli/args.hpp"
#include "tokenizer/tokenizer.hpp" #include "tokenizer/tokenizer.hpp"
#include "parser/parser.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" #include "json.hpp"
inline int cmdAst(const CliArgs& args) { inline int cmdAst(const CliArgs& args) {
@ -28,9 +38,25 @@ inline int cmdAst(const CliArgs& args) {
return 1; 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::ostream* out = &std::cout;
std::ofstream outFile; std::ofstream outFile;
if (!args.outputFile.empty()) { if (!args.outputFile.empty()) {
@ -40,12 +66,16 @@ inline int cmdAst(const CliArgs& args) {
*out << "{\n" *out << "{\n"
<< " \"ast\":\n" << " \"ast\":\n"
<< jsonIndent(2) << astToJson(ast, 2) << ",\n" << jsonIndent(2) << astToJson(displayAst, 2) << ",\n"
<< " \"analysis\": {\n" << " \"analysis\": {\n"
<< analysisToJson(analysis) << "\n" << analysisToJson(analysis) << "\n"
<< " }\n" << " }\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; delete ast;
for (auto* t : tokens) delete t; for (auto* t : tokens) delete t;
return 0; return 0;

11
src/core/config.hpp Normal file
View File

@ -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

286
src/opt/ast_clone.hpp Normal file
View File

@ -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<ProgramNode*>(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<FunctionDeclNode*>(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<VariableDeclNode*>(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<VariableDeclNode*>(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<StructDeclNode*>(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<BlockNode*>(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<IfStatementNode*>(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<WhileStatementNode*>(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<ForStatementNode*>(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<DoWhileStatementNode*>(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<ReturnStatementNode*>(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<BreakStatementNode*>(node);
auto* dst = new BreakStatementNode();
dst->loc = src->loc;
dst->isReachable = src->isReachable;
return dst;
}
// ── ContinueStatementNode ────────────────────────────────────────────────
case ASTKind::ContinueStatement: {
auto* src = static_cast<ContinueStatementNode*>(node);
auto* dst = new ContinueStatementNode();
dst->loc = src->loc;
dst->isReachable = src->isReachable;
return dst;
}
// ── ExpressionStatementNode ──────────────────────────────────────────────
case ASTKind::ExpressionStatement: {
auto* src = static_cast<ExpressionStatementNode*>(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<BinaryExpressionNode*>(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<LiteralNode*>(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<IdentifierNode*>(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<PostfixNode*>(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<CallExpressionNode*>(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<MemberAccessNode*>(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<IndexExpressionNode*>(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

View File

@ -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 <string>
#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<LiteralNode*>(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<BinaryExpressionNode*>(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<LiteralNode*>(bin->Left);
auto* rlit = static_cast<LiteralNode*>(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<PostfixNode*>(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<CallExpressionNode*>(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<MemberAccessNode*>(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<IndexExpressionNode*>(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<ExpressionStatementNode*>(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<ReturnStatementNode*>(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<VariableDeclNode*>(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<IfStatementNode*>(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<WhileStatementNode*>(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<ForStatementNode*>(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<DoWhileStatementNode*>(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<BlockNode*>(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<FunctionDeclNode*>(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<ProgramNode*>(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

110
src/opt/dead_code_elim.hpp Normal file
View File

@ -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 <string>
#include <algorithm>
#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<BlockNode*>(node);
auto& ch = blk->getChildren();
bool term = false;
for (auto* child : ch) {
if (term) {
// Bu deyim erişilemez
if (auto* sn = dynamic_cast<StatementNode*>(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<StatementNode*>(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<IfStatementNode*>(node);
visit(ifs->thenBranch);
visit(ifs->elseBranch);
break;
}
case ASTKind::WhileStatement: {
auto* ws = static_cast<WhileStatementNode*>(node);
visit(ws->body);
break;
}
case ASTKind::ForStatement: {
auto* fs = static_cast<ForStatementNode*>(node);
visit(fs->body);
break;
}
case ASTKind::DoWhileStatement: {
auto* dw = static_cast<DoWhileStatementNode*>(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

View File

@ -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 <memory>
#include <vector>
#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<ConstantFoldingPass>(diag));
if (cfg.optDeadCodeElim)
passes_.push_back(std::make_unique<DeadCodeElimPass>());
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<std::unique_ptr<OptimizationPass>> passes_;
int maxRounds_ = 10;
};
#endif // SAQUT_OPT_MANAGER

View File

@ -0,0 +1,19 @@
#ifndef SAQUT_OPT_PASS
#define SAQUT_OPT_PASS
#include <string>
#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

View File

@ -6,17 +6,21 @@
LiteralNode::LiteralNode() { kind = ASTKind::Literal; } LiteralNode::LiteralNode() { kind = ASTKind::Literal; }
void LiteralNode::log(int indent) { void LiteralNode::log(int indent) {
std::string val = hasDirectValue ? std::to_string(directIntValue)
: (parserToken.token ? parserToken.token->token : "?");
std::cout << padRight("", indent) std::cout << padRight("", indent)
<< "Literal {" << (parserToken.token ? parserToken.token->token : "?") << "} " << "Literal {" << val << "} "
<< literalTypeToString(literalType); << literalTypeToString(literalType);
if (isConstant) std::cout << " [folded]";
if (literalType == LiteralType::INTEGER && literalBase != 10) if (literalType == LiteralType::INTEGER && literalBase != 10)
std::cout << " (base " << literalBase << ")"; std::cout << " (base " << literalBase << ")";
std::cout << "\n"; std::cout << "\n";
} }
std::string LiteralNode::toJson(int depth) { std::string LiteralNode::toJson(int depth) {
std::string in = jsonIndent(depth); std::string in = jsonIndent(depth);
std::string val = parserToken.token ? parserToken.token->token : "?"; std::string val = hasDirectValue ? std::to_string(directIntValue)
: (parserToken.token ? parserToken.token->token : "?");
std::ostringstream ss; std::ostringstream ss;
ss << "{\n" ss << "{\n"
<< in << " \"kind\": \"Literal\",\n" << in << " \"kind\": \"Literal\",\n"

View File

@ -12,6 +12,11 @@ public:
int literalBase = 10; int literalBase = 10;
bool isFloatValue = false; 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(); LiteralNode();
void log(int indent = 0) override; void log(int indent = 0) override;
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;