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:
parent
6681b1be78
commit
d6301ccb5f
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -6,9 +6,12 @@
|
|||
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";
|
||||
|
|
@ -16,7 +19,8 @@ void LiteralNode::log(int indent) {
|
|||
|
||||
std::string LiteralNode::toJson(int 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;
|
||||
ss << "{\n"
|
||||
<< in << " \"kind\": \"Literal\",\n"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue