Auto stack size calculation
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- "-I/opt/homebrew/opt/antlr4-cpp-runtime/include/antlr4-runtime"
|
||||
- "-I./generated"
|
||||
- "-std=c++17"
|
||||
|
||||
+37
-55
@@ -1,32 +1,43 @@
|
||||
#include "CodeGenVisitor.h"
|
||||
|
||||
#include "generated/ifccParser.h"
|
||||
|
||||
antlrcpp::Any CodeGenVisitor::visitStmt(ifccParser::StmtContext *ctx) {
|
||||
std::any CodeGenVisitor::visitProg(ifccParser::ProgContext *ctx) {
|
||||
scopeStack.push_back("main");
|
||||
int size = symbolTable->stackSize(currentScope());
|
||||
#ifdef __APPLE__
|
||||
std::cout << ".globl _main\n_main:\n";
|
||||
std::cout << " sub sp, sp, #" << size << "\n";
|
||||
#else
|
||||
std::cout << ".globl main\nmain:\n";
|
||||
std::cout << " subq $" << size << ", %rsp\n";
|
||||
#endif
|
||||
|
||||
this->visitChildren(ctx);
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::cout << " add sp, sp, #" << size << "\n";
|
||||
#else
|
||||
std::cout << " addq $" << size << ", %rsp\n";
|
||||
#endif
|
||||
std::cout << " ret\n";
|
||||
scopeStack.pop_back();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::any CodeGenVisitor::visitStmt(ifccParser::StmtContext *ctx) {
|
||||
std::cout << " ;" << ctx->getText() << "\n";
|
||||
return this->visitChildren(ctx);
|
||||
}
|
||||
|
||||
// Declare a new variable: reserve a slot on the stack, mark as uninitialized.
|
||||
antlrcpp::Any CodeGenVisitor::visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) {
|
||||
std::string name = ctx->VAR_NAME()->getText();
|
||||
if (symbolTable.find(name) != symbolTable.end()) {
|
||||
std::cerr << "error: variable '" << name << "' already declared\n";
|
||||
return 1;
|
||||
}
|
||||
symbolTable[name] = {nextOffset, false};
|
||||
nextOffset += 4;
|
||||
// Declaration pass already filled the symbol table: nothing to do here.
|
||||
std::any CodeGenVisitor::visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Assign a value to a variable: evaluate val, store on stack, mark as initialized.
|
||||
antlrcpp::Any CodeGenVisitor::visitSet_stmt(ifccParser::Set_stmtContext *ctx) {
|
||||
std::any CodeGenVisitor::visitSet_stmt(ifccParser::Set_stmtContext *ctx) {
|
||||
std::string name = ctx->VAR_NAME()->getText();
|
||||
if (symbolTable.find(name) == symbolTable.end()) {
|
||||
std::cerr << "error: variable '" << name << "' undeclared\n";
|
||||
return 1;
|
||||
}
|
||||
int offset = symbolTable[name].offset;
|
||||
int offset = symbolTable->getOffset(currentScope(), name);
|
||||
|
||||
this->visit(ctx->val());
|
||||
|
||||
@@ -36,12 +47,12 @@ antlrcpp::Any CodeGenVisitor::visitSet_stmt(ifccParser::Set_stmtContext *ctx) {
|
||||
std::cout << " movl %eax, " << offset << "(%rsp)\n";
|
||||
#endif
|
||||
|
||||
symbolTable[name].initialized = true;
|
||||
symbolTable->markInitialized(currentScope(), name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load a constant or variable into the accumulator register
|
||||
antlrcpp::Any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) {
|
||||
// Load a constant or variable into the accumulator register.
|
||||
std::any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) {
|
||||
if (ctx->CONST()) {
|
||||
int val = stoi(ctx->CONST()->getText());
|
||||
#ifdef __APPLE__
|
||||
@@ -51,50 +62,21 @@ antlrcpp::Any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) {
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string name = ctx->VAR_NAME()->getText();
|
||||
if (symbolTable.find(name) == symbolTable.end()) {
|
||||
std::cerr << "error: variable '" << name << "' undeclared\n";
|
||||
return 1;
|
||||
}
|
||||
if (!symbolTable[name].initialized) {
|
||||
std::cerr << "warning: variable '" << name << "' used before initialization\n";
|
||||
}
|
||||
int offset = symbolTable[name].offset;
|
||||
int offset = symbolTable->getOffset(currentScope(), name);
|
||||
symbolTable->isInitialized(currentScope(), name); // emits warning if needed
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::cout << " ldr w0, [sp, #" << offset << "]\n";
|
||||
#else
|
||||
std::cout << " movl " << offset << "(%rsp), %eax\n";
|
||||
#endif
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
antlrcpp::Any CodeGenVisitor::visitProg(ifccParser::ProgContext *ctx) {
|
||||
#ifdef __APPLE__
|
||||
std::cout << ".globl _main\n";
|
||||
std::cout << "_main:\n";
|
||||
// Prologue: reserve space for local variables
|
||||
std::cout << " sub sp, sp, #16\n";
|
||||
#else
|
||||
std::cout << ".globl main\n";
|
||||
std::cout << "main:\n";
|
||||
// Prologue: reserve space for local variables
|
||||
std::cout << " subq $16, %rsp\n";
|
||||
#endif
|
||||
|
||||
this->visitChildren(ctx);
|
||||
|
||||
// Epilogue
|
||||
#ifdef __APPLE__
|
||||
std::cout << " add sp, sp, #16\n";
|
||||
#else
|
||||
std::cout << " addq $16, %rsp\n";
|
||||
#endif
|
||||
std::cout << " ret\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
antlrcpp::Any CodeGenVisitor::visitReturn_stmt(ifccParser::Return_stmtContext *ctx) {
|
||||
// Evaluate the return value into the accumulator register
|
||||
std::any CodeGenVisitor::visitReturn_stmt(ifccParser::Return_stmtContext *ctx) {
|
||||
this->visit(ctx->val());
|
||||
return 0;
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,31 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "antlr4-runtime.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "generated/ifccBaseVisitor.h"
|
||||
|
||||
|
||||
class CodeGenVisitor : public ifccBaseVisitor {
|
||||
class CodeGenVisitor : public ifccBaseVisitor {
|
||||
SymbolTable *symbolTable; // shared, not owned
|
||||
std::vector<std::string> scopeStack; // navigation state, owned by this visitor
|
||||
|
||||
struct VarInfo {
|
||||
int offset; // stack offset relative to sp
|
||||
bool initialized; // true after a set_stmt assigns a value
|
||||
};
|
||||
std::string currentScope() const { return scopeStack.back(); }
|
||||
|
||||
// Symbol table: variable name -> VarInfo
|
||||
std::map<std::string, VarInfo> symbolTable;
|
||||
int nextOffset = 0; // first variable at [sp, #0], next at [sp, #4], etc.
|
||||
public:
|
||||
explicit CodeGenVisitor(SymbolTable *st) : symbolTable(st) {
|
||||
}
|
||||
|
||||
public:
|
||||
std::any visitProg(ifccParser::ProgContext *ctx) override ;
|
||||
std::any visitStmt(ifccParser::StmtContext *ctx) override ;
|
||||
std::any visitReturn_stmt(ifccParser::Return_stmtContext *ctx) override ;
|
||||
std::any visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) override ;
|
||||
std::any visitSet_stmt(ifccParser::Set_stmtContext *ctx) override ;
|
||||
std::any visitVal(ifccParser::ValContext *ctx) override ;
|
||||
std::any visitProg(ifccParser::ProgContext *ctx) override;
|
||||
|
||||
std::any visitStmt(ifccParser::StmtContext *ctx) override;
|
||||
|
||||
std::any visitReturn_stmt(ifccParser::Return_stmtContext *ctx) override;
|
||||
|
||||
std::any visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) override;
|
||||
|
||||
std::any visitSet_stmt(ifccParser::Set_stmtContext *ctx) override;
|
||||
|
||||
std::any visitVal(ifccParser::ValContext *ctx) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "DeclarationVisitor.h"
|
||||
|
||||
#include "generated/ifccParser.h"
|
||||
|
||||
std::any DeclarationVisitor::visitProg(ifccParser::ProgContext *ctx) {
|
||||
symbolTable->addScope("main");
|
||||
scopeStack.push_back("main");
|
||||
this->visitChildren(ctx);
|
||||
scopeStack.pop_back();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::any DeclarationVisitor::visitStmt(ifccParser::StmtContext *ctx) {
|
||||
return this->visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any DeclarationVisitor::visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) {
|
||||
std::vector<antlr4::tree::TerminalNode*> vars = ctx->VAR_NAME();
|
||||
for (auto var : vars) {
|
||||
std::string name = var->getText();
|
||||
symbolTable->declare(currentScope(), name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::any DeclarationVisitor::visitSet_stmt(ifccParser::Set_stmtContext *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::any DeclarationVisitor::visitVal(ifccParser::ValContext *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::any DeclarationVisitor::visitReturn_stmt(ifccParser::Return_stmtContext *ctx) {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "antlr4-runtime.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "generated/ifccBaseVisitor.h"
|
||||
|
||||
|
||||
class DeclarationVisitor : public ifccBaseVisitor {
|
||||
SymbolTable *symbolTable = new SymbolTable();
|
||||
|
||||
std::vector<std::string> scopeStack;
|
||||
|
||||
const std::string ¤tScope() const { return scopeStack.back(); }
|
||||
|
||||
public:
|
||||
~DeclarationVisitor() override {
|
||||
delete symbolTable;
|
||||
}
|
||||
|
||||
[[nodiscard]] SymbolTable *getSymbolTable() const {
|
||||
return symbolTable;
|
||||
}
|
||||
|
||||
std::any visitProg(ifccParser::ProgContext *ctx) override;
|
||||
|
||||
std::any visitStmt(ifccParser::StmtContext *ctx) override;
|
||||
|
||||
std::any visitReturn_stmt(ifccParser::Return_stmtContext *ctx) override;
|
||||
|
||||
std::any visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) override;
|
||||
|
||||
std::any visitSet_stmt(ifccParser::Set_stmtContext *ctx) override;
|
||||
|
||||
std::any visitVal(ifccParser::ValContext *ctx) override;
|
||||
};
|
||||
|
||||
|
||||
+3
-1
@@ -18,7 +18,9 @@ OBJECTS=build/ifccBaseVisitor.o \
|
||||
build/ifccVisitor.o \
|
||||
build/ifccParser.o \
|
||||
build/main.o \
|
||||
build/CodeGenVisitor.o
|
||||
build/CodeGenVisitor.o \
|
||||
build/DeclarationVisitor.o \
|
||||
build/SymbolTable.o
|
||||
|
||||
ifcc: $(OBJECTS)
|
||||
@mkdir -p build
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "SymbolTable.h"
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
class SymbolTable {
|
||||
struct VarInfo {
|
||||
int offset;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
std::string functionName;
|
||||
std::map<std::string, VarInfo> vars;
|
||||
int nextOffset = 0;
|
||||
};
|
||||
|
||||
std::map<std::string, Scope> scopes; // name -> Scope
|
||||
|
||||
public:
|
||||
// Creates a new scope (call in DeclarationVisitor).
|
||||
void addScope(const std::string &functionName) {
|
||||
if (scopes.count(functionName)) {
|
||||
std::cerr << "error: scope '" << functionName << "' already exists\n";
|
||||
exit(1);
|
||||
}
|
||||
scopes[functionName] = {functionName, {}, 0};
|
||||
}
|
||||
|
||||
// Declare a variable in a named scope; returns its stack offset.
|
||||
int declare(const std::string &scopeName, const std::string &name) {
|
||||
auto &scope = getScope(scopeName);
|
||||
if (scope.vars.count(name)) {
|
||||
std::cerr << "error: variable '" << name << "' already declared in '" << scopeName << "'\n";
|
||||
exit(1);
|
||||
}
|
||||
int offset = scope.nextOffset;
|
||||
scope.vars[name] = {offset, false};
|
||||
scope.nextOffset += 4;
|
||||
return offset;
|
||||
}
|
||||
|
||||
int getOffset(const std::string &scopeName, const std::string &name) {
|
||||
auto &vars = getScope(scopeName).vars;
|
||||
if (!vars.count(name)) {
|
||||
std::cerr << "error: variable '" << name << "' undeclared in '" << scopeName << "'\n";
|
||||
exit(1);
|
||||
}
|
||||
return vars[name].offset;
|
||||
}
|
||||
|
||||
bool isDeclared(const std::string &scopeName, const std::string &name) const {
|
||||
if (!scopes.count(scopeName)) return false;
|
||||
return scopes.at(scopeName).vars.count(name) > 0;
|
||||
}
|
||||
|
||||
void markInitialized(const std::string &scopeName, const std::string &name) {
|
||||
auto &vars = getScope(scopeName).vars;
|
||||
if (!vars.count(name)) {
|
||||
std::cerr << "error: variable '" << name << "' undeclared in '" << scopeName << "'\n";
|
||||
exit(1);
|
||||
}
|
||||
vars[name].initialized = true;
|
||||
}
|
||||
|
||||
bool isInitialized(const std::string &scopeName, const std::string &name) {
|
||||
if (!isDeclared(scopeName, name)) return false;
|
||||
if (!scopes.at(scopeName).vars[name].initialized) {
|
||||
std::cerr << "warning: variable '" << name << "' used before initialization\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stack size for a given scope, aligned to 16 bytes.
|
||||
int stackSize(const std::string &scopeName) const {
|
||||
int n = getScope(scopeName).nextOffset;
|
||||
return (n + 15) & ~15;
|
||||
}
|
||||
|
||||
private:
|
||||
Scope &getScope(const std::string &name) {
|
||||
if (!scopes.count(name)) {
|
||||
std::cerr << "error: scope '" << name << "' not found\n";
|
||||
exit(1);
|
||||
}
|
||||
return scopes.at(name);
|
||||
}
|
||||
|
||||
const Scope &getScope(const std::string &name) const {
|
||||
if (!scopes.count(name)) {
|
||||
std::cerr << "error: scope '" << name << "' not found\n";
|
||||
exit(1);
|
||||
}
|
||||
return scopes.at(name);
|
||||
}
|
||||
};
|
||||
+1
-1
@@ -8,7 +8,7 @@ stmt: decl_stmt | set_stmt | return_stmt ;
|
||||
|
||||
return_stmt: RETURN val ';' ;
|
||||
|
||||
decl_stmt: 'int' VAR_NAME ';' ;
|
||||
decl_stmt: 'int' (VAR_NAME ',')* VAR_NAME ';' ;
|
||||
set_stmt: VAR_NAME '=' val ';' ;
|
||||
|
||||
val: CONST | VAR_NAME ;
|
||||
|
||||
+31
-34
@@ -9,48 +9,45 @@
|
||||
#include "generated/ifccBaseVisitor.h"
|
||||
|
||||
#include "CodeGenVisitor.h"
|
||||
#include "DeclarationVisitor.h"
|
||||
|
||||
using namespace antlr4;
|
||||
using namespace std;
|
||||
|
||||
int main(int argn, const char **argv)
|
||||
{
|
||||
stringstream in;
|
||||
if (argn==2)
|
||||
{
|
||||
ifstream lecture(argv[1]);
|
||||
if( !lecture.good() )
|
||||
{
|
||||
cerr<<"error: cannot read file: " << argv[1] << endl ;
|
||||
exit(1);
|
||||
}
|
||||
in << lecture.rdbuf();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "usage: ifcc path/to/file.c" << endl ;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ANTLRInputStream input(in.str());
|
||||
int main(int argn, const char **argv) {
|
||||
stringstream in;
|
||||
if (argn == 2) {
|
||||
ifstream lecture(argv[1]);
|
||||
if (!lecture.good()) {
|
||||
cerr << "error: cannot read file: " << argv[1] << endl;
|
||||
exit(1);
|
||||
}
|
||||
in << lecture.rdbuf();
|
||||
} else {
|
||||
cerr << "usage: ifcc path/to/file.c" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ifccLexer lexer(&input);
|
||||
CommonTokenStream tokens(&lexer);
|
||||
ANTLRInputStream input(in.str());
|
||||
|
||||
tokens.fill();
|
||||
ifccLexer lexer(&input);
|
||||
CommonTokenStream tokens(&lexer);
|
||||
|
||||
ifccParser parser(&tokens);
|
||||
tree::ParseTree* tree = parser.axiom();
|
||||
tokens.fill();
|
||||
|
||||
if(parser.getNumberOfSyntaxErrors() != 0)
|
||||
{
|
||||
cerr << "error: syntax error during parsing" << endl;
|
||||
exit(1);
|
||||
}
|
||||
ifccParser parser(&tokens);
|
||||
tree::ParseTree *tree = parser.axiom();
|
||||
|
||||
|
||||
CodeGenVisitor v;
|
||||
v.visit(tree);
|
||||
if (parser.getNumberOfSyntaxErrors() != 0) {
|
||||
cerr << "error: syntax error during parsing" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
DeclarationVisitor dv;
|
||||
dv.visit(tree);
|
||||
|
||||
CodeGenVisitor cgv = CodeGenVisitor(dv.getSymbolTable());
|
||||
cgv.visit(tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user