Auto stack size calculation

This commit is contained in:
Clément Grennerat 2026-03-04 10:54:23 +01:00
parent 11ccd45663
commit 906759a515
15 changed files with 303 additions and 158 deletions

6
Makefile Normal file
View File

@ -0,0 +1,6 @@
test-all:
python ifcc-test.py testfiles
test-last:
python ifcc-test.py -v -v testfiles/$(shell ls -A testfiles | tail -n 1)

6
compiler/.clangd Normal file
View File

@ -0,0 +1,6 @@
CompileFlags:
Add:
- "-I/opt/homebrew/opt/antlr4-cpp-runtime/include/antlr4-runtime"
- "-I./generated"
- "-std=c++17"

View File

@ -1,32 +1,43 @@
#include "CodeGenVisitor.h" #include "CodeGenVisitor.h"
#include "generated/ifccParser.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"; std::cout << " ;" << ctx->getText() << "\n";
return this->visitChildren(ctx); return this->visitChildren(ctx);
} }
// Declare a new variable: reserve a slot on the stack, mark as uninitialized. // Declaration pass already filled the symbol table: nothing to do here.
antlrcpp::Any CodeGenVisitor::visitDecl_stmt(ifccParser::Decl_stmtContext *ctx) { std::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;
return 0; return 0;
} }
// Assign a value to a variable: evaluate val, store on stack, mark as initialized. // 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(); std::string name = ctx->VAR_NAME()->getText();
if (symbolTable.find(name) == symbolTable.end()) { int offset = symbolTable->getOffset(currentScope(), name);
std::cerr << "error: variable '" << name << "' undeclared\n";
return 1;
}
int offset = symbolTable[name].offset;
this->visit(ctx->val()); this->visit(ctx->val());
@ -36,12 +47,12 @@ antlrcpp::Any CodeGenVisitor::visitSet_stmt(ifccParser::Set_stmtContext *ctx) {
std::cout << " movl %eax, " << offset << "(%rsp)\n"; std::cout << " movl %eax, " << offset << "(%rsp)\n";
#endif #endif
symbolTable[name].initialized = true; symbolTable->markInitialized(currentScope(), name);
return 0; return 0;
} }
// Load a constant or variable into the accumulator register // Load a constant or variable into the accumulator register.
antlrcpp::Any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) { std::any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) {
if (ctx->CONST()) { if (ctx->CONST()) {
int val = stoi(ctx->CONST()->getText()); int val = stoi(ctx->CONST()->getText());
#ifdef __APPLE__ #ifdef __APPLE__
@ -51,50 +62,21 @@ antlrcpp::Any CodeGenVisitor::visitVal(ifccParser::ValContext *ctx) {
#endif #endif
return val; return val;
} }
std::string name = ctx->VAR_NAME()->getText(); std::string name = ctx->VAR_NAME()->getText();
if (symbolTable.find(name) == symbolTable.end()) { int offset = symbolTable->getOffset(currentScope(), name);
std::cerr << "error: variable '" << name << "' undeclared\n"; symbolTable->isInitialized(currentScope(), name); // emits warning if needed
return 1;
}
if (!symbolTable[name].initialized) {
std::cerr << "warning: variable '" << name << "' used before initialization\n";
}
int offset = symbolTable[name].offset;
#ifdef __APPLE__ #ifdef __APPLE__
std::cout << " ldr w0, [sp, #" << offset << "]\n"; std::cout << " ldr w0, [sp, #" << offset << "]\n";
#else #else
std::cout << " movl " << offset << "(%rsp), %eax\n"; std::cout << " movl " << offset << "(%rsp), %eax\n";
#endif #endif
return offset; return offset;
} }
antlrcpp::Any CodeGenVisitor::visitProg(ifccParser::ProgContext *ctx) { std::any CodeGenVisitor::visitReturn_stmt(ifccParser::Return_stmtContext *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
this->visit(ctx->val()); this->visit(ctx->val());
return 0; return 0;
} }

View File

@ -1,31 +1,31 @@
#pragma once #pragma once
#include <map> #include <vector>
#include <string> #include <string>
#include "antlr4-runtime.h" #include "antlr4-runtime.h"
#include "SymbolTable.h"
#include "generated/ifccBaseVisitor.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 { std::string currentScope() const { return scopeStack.back(); }
int offset; // stack offset relative to sp
bool initialized; // true after a set_stmt assigns a value
};
// Symbol table: variable name -> VarInfo public:
std::map<std::string, VarInfo> symbolTable; explicit CodeGenVisitor(SymbolTable *st) : symbolTable(st) {
int nextOffset = 0; // first variable at [sp, #0], next at [sp, #4], etc. }
public: std::any visitProg(ifccParser::ProgContext *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 ;
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;
}; };

View File

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

View File

@ -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 &currentScope() 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;
};

View File

@ -18,7 +18,9 @@ OBJECTS=build/ifccBaseVisitor.o \
build/ifccVisitor.o \ build/ifccVisitor.o \
build/ifccParser.o \ build/ifccParser.o \
build/main.o \ build/main.o \
build/CodeGenVisitor.o build/CodeGenVisitor.o \
build/DeclarationVisitor.o \
build/SymbolTable.o
ifcc: $(OBJECTS) ifcc: $(OBJECTS)
@mkdir -p build @mkdir -p build

1
compiler/SymbolTable.cpp Normal file
View File

@ -0,0 +1 @@
#include "SymbolTable.h"

97
compiler/SymbolTable.h Normal file
View File

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

View File

@ -8,7 +8,7 @@ stmt: decl_stmt | set_stmt | return_stmt ;
return_stmt: RETURN val ';' ; return_stmt: RETURN val ';' ;
decl_stmt: 'int' VAR_NAME ';' ; decl_stmt: 'int' (VAR_NAME ',')* VAR_NAME ';' ;
set_stmt: VAR_NAME '=' val ';' ; set_stmt: VAR_NAME '=' val ';' ;
val: CONST | VAR_NAME ; val: CONST | VAR_NAME ;

View File

@ -9,48 +9,45 @@
#include "generated/ifccBaseVisitor.h" #include "generated/ifccBaseVisitor.h"
#include "CodeGenVisitor.h" #include "CodeGenVisitor.h"
#include "DeclarationVisitor.h"
using namespace antlr4; using namespace antlr4;
using namespace std; using namespace std;
int main(int argn, const char **argv) int main(int argn, const char **argv) {
{ stringstream in;
stringstream in; if (argn == 2) {
if (argn==2) ifstream lecture(argv[1]);
{ if (!lecture.good()) {
ifstream lecture(argv[1]); cerr << "error: cannot read file: " << argv[1] << endl;
if( !lecture.good() ) exit(1);
{ }
cerr<<"error: cannot read file: " << argv[1] << endl ; in << lecture.rdbuf();
exit(1); } else {
} cerr << "usage: ifcc path/to/file.c" << endl;
in << lecture.rdbuf(); exit(1);
} }
else
{
cerr << "usage: ifcc path/to/file.c" << endl ;
exit(1);
}
ANTLRInputStream input(in.str());
ifccLexer lexer(&input); ANTLRInputStream input(in.str());
CommonTokenStream tokens(&lexer);
tokens.fill(); ifccLexer lexer(&input);
CommonTokenStream tokens(&lexer);
ifccParser parser(&tokens); tokens.fill();
tree::ParseTree* tree = parser.axiom();
if(parser.getNumberOfSyntaxErrors() != 0) ifccParser parser(&tokens);
{ tree::ParseTree *tree = parser.axiom();
cerr << "error: syntax error during parsing" << endl;
exit(1);
}
if (parser.getNumberOfSyntaxErrors() != 0) {
CodeGenVisitor v; cerr << "error: syntax error during parsing" << endl;
v.visit(tree); exit(1);
}
return 0; DeclarationVisitor dv;
dv.visit(tree);
CodeGenVisitor cgv = CodeGenVisitor(dv.getSymbolTable());
cgv.visit(tree);
return 0;
} }

View File

@ -17,17 +17,17 @@ import subprocess
def run_command(string, logfile=None, toscreen=False): def run_command(string, logfile=None, toscreen=False):
""" execute `string` as a shell command. Maybe write stdout+stderr to `logfile` and/or to the toscreen. """ execute `string` as a shell command. Maybe write stdout+stderr to `logfile` and/or to the toscreen.
return the exit status""" return the exit status"""
if args.debug: if args.debug:
print("ifcc-test.py: "+string) print("ifcc-test.py: "+string)
process=subprocess.Popen(string,shell=True, process=subprocess.Popen(string,shell=True,
stderr=subprocess.STDOUT,stdout=subprocess.PIPE, stderr=subprocess.STDOUT,stdout=subprocess.PIPE,
text=True,bufsize=0) text=True,bufsize=0)
if logfile: if logfile:
logfile=open(logfile,'w') logfile=open(logfile,'w')
while True: while True:
output = process.stdout.readline() output = process.stdout.readline()
if len(output) == 0: # only happens when 'process' has terminated if len(output) == 0: # only happens when 'process' has terminated
@ -45,7 +45,7 @@ def dumpfile(name,quiet=False):
if not quiet: if not quiet:
print(data,end='') print(data,end='')
return data return data
###################################################################################### ######################################################################################
## ARGPARSE step: make sense of our command-line arguments ## ARGPARSE step: make sense of our command-line arguments
@ -56,7 +56,7 @@ import textwrap
import shutil import shutil
width = shutil.get_terminal_size().columns-2 width = shutil.get_terminal_size().columns-2
twf=lambda text: textwrap.fill(text,width,initial_indent=' '*4,subsequent_indent=' '*6) twf=lambda text: textwrap.fill(text,width,initial_indent=' '*4,subsequent_indent=' '*6)
argparser = argparse.ArgumentParser( argparser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
description = "Testing script for the ifcc compiler. operates in one of two modes:\n\n" description = "Testing script for the ifcc compiler. operates in one of two modes:\n\n"
@ -67,7 +67,7 @@ epilog="examples:\n\n"
+twf("python3 ifcc-test.py path/to/some/dir/*.c path/to/some/other/dir")+'\n' +twf("python3 ifcc-test.py path/to/some/dir/*.c path/to/some/other/dir")+'\n'
+'\n' +'\n'
+twf("python3 ifcc-test.py -o ./myprog path/to/some/source.c")+'\n' +twf("python3 ifcc-test.py -o ./myprog path/to/some/source.c")+'\n'
+twf("python3 ifcc-test.py -S -o truc.s truc.c")+'\n' +twf("python3 ifcc-test.py -S -o truc.asm truc.c")+'\n'
, ,
) )
@ -121,7 +121,7 @@ if args.S or args.c or args.output:
print("error: this mode only supports a single input file") print("error: this mode only supports a single input file")
exit(1) exit(1)
inputfilename=args.input[0] inputfilename=args.input[0]
if inputfilename[-2:] != ".c": if inputfilename[-2:] != ".c":
print("error: incorrect filename suffix (should be '.c'): "+inputfilename) print("error: incorrect filename suffix (should be '.c'): "+inputfilename)
exit(1) exit(1)
@ -135,10 +135,10 @@ if args.S or args.c or args.output:
if (args.S or args.c) and not args.output: if (args.S or args.c) and not args.output:
print("error: option '-o filename' is required in this mode") print("error: option '-o filename' is required in this mode")
exit(1) exit(1)
if args.S: # produce assembly if args.S: # produce assembly
if args.output[-2:] != ".s": if args.output[-2:] != ".asm":
print("error: output file name must end with '.s'") print("error: output file name must end with '.asm'")
exit(1) exit(1)
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {args.output}') ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {args.output}')
if ifccstatus: # let's show error messages on screen if ifccstatus: # let's show error messages on screen
@ -150,17 +150,17 @@ if args.S or args.c or args.output:
if args.output[-2:] != ".o": if args.output[-2:] != ".o":
print("error: output file name must end with '.o'") print("error: output file name must end with '.o'")
exit(1) exit(1)
asmname=args.output[:-2]+".s" asmname=args.output[:-2]+".asm"
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}') ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}')
if ifccstatus: # let's show error messages on screen if ifccstatus: # let's show error messages on screen
exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}',toscreen=True)) exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}',toscreen=True))
exit(run_command(f'gcc -c -o {args.output} {asmname}',toscreen=True)) exit(run_command(f'gcc -c -o {args.output} {asmname}',toscreen=True))
else: # produce an executable else: # produce an executable
if args.output[-2:] in [".o",".c",".s"]: if args.output[-2:] in [".o",".c",".asm"]:
print("error: incorrect name for an executable: "+args.output) print("error: incorrect name for an executable: "+args.output)
exit(1) exit(1)
asmname=args.output+".s" asmname=args.output+".asm"
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}') ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}')
if ifccstatus: if ifccstatus:
exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}', toscreen=True)) exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}', toscreen=True))
@ -237,7 +237,7 @@ for inputfilename in inputfilenames:
subdirname=subdirname.replace('/','-') # flatten path to single subdir subdirname=subdirname.replace('/','-') # flatten path to single subdir
if args.debug>=2: if args.debug>=2:
print("debug: subdir="+subdirname) print("debug: subdir="+subdirname)
os.mkdir(pld_base_dir+'/ifcc-test-output/'+subdirname) os.mkdir(pld_base_dir+'/ifcc-test-output/'+subdirname)
shutil.copyfile(inputfilename,pld_base_dir+'/ifcc-test-output/'+subdirname+'/input.c') shutil.copyfile(inputfilename,pld_base_dir+'/ifcc-test-output/'+subdirname+'/input.c')
jobs.append(subdirname) jobs.append(subdirname)
@ -269,20 +269,20 @@ for jobname in jobs:
print('TEST-CASE: '+jobname) print('TEST-CASE: '+jobname)
os.chdir(jobname) os.chdir(jobname)
## Reference compiler = GCC ## Reference compiler = GCC
gccstatus=run_command("gcc -S -o asm-gcc.s input.c", "gcc-compile.txt") gccstatus=run_command("gcc -S -o asm-gcc.asm input.c", "gcc-compile.txt")
if gccstatus == 0: if gccstatus == 0:
# test-case is a valid program. we should run it # test-case is a valid program. we should run it
gccstatus=run_command("gcc -o exe-gcc asm-gcc.s", "gcc-link.txt") gccstatus=run_command("gcc -o exe-gcc asm-gcc.asm", "gcc-link.txt")
if gccstatus == 0: # then both compile and link stage went well if gccstatus == 0: # then both compile and link stage went well
exegccstatus=run_command("./exe-gcc", "gcc-execute.txt") exegccstatus=run_command("./exe-gcc", "gcc-execute.txt")
if args.verbose >=2: if args.verbose >=2:
dumpfile("gcc-execute.txt") dumpfile("gcc-execute.txt")
## IFCC compiler ## IFCC compiler
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc input.c > asm-ifcc.s', 'ifcc-compile.txt') ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc input.c > asm-ifcc.asm', 'ifcc-compile.txt')
if gccstatus != 0 and ifccstatus != 0: if gccstatus != 0 and ifccstatus != 0:
## ifcc correctly rejects invalid program -> test-case ok ## ifcc correctly rejects invalid program -> test-case ok
print("TEST OK") print("TEST OK")
@ -297,23 +297,23 @@ for jobname in jobs:
print("TEST FAIL (your compiler rejects a valid program)") print("TEST FAIL (your compiler rejects a valid program)")
all_ok=False all_ok=False
if args.verbose: if args.verbose:
dumpfile("asm-ifcc.s") # stdout of ifcc dumpfile("asm-ifcc.asm") # stdout of ifcc
dumpfile("ifcc-compile.txt") # stderr of ifcc dumpfile("ifcc-compile.txt") # stderr of ifcc
continue continue
else: else:
## ifcc accepts to compile valid program -> let's link it ## ifcc accepts to compile valid program -> let's link it
ldstatus=run_command("gcc -o exe-ifcc asm-ifcc.s", "ifcc-link.txt") ldstatus=run_command("gcc -o exe-ifcc asm-ifcc.asm", "ifcc-link.txt")
if ldstatus: if ldstatus:
print("TEST FAIL (your compiler produces incorrect assembly)") print("TEST FAIL (your compiler produces incorrect assembly)")
all_ok=False all_ok=False
if args.verbose: if args.verbose:
dumpfile("asm-ifcc.s") dumpfile("asm-ifcc.asm")
dumpfile("ifcc-link.txt") dumpfile("ifcc-link.txt")
continue continue
## both compilers did produce an executable, so now we run both ## both compilers did produce an executable, so now we run both
## these executables and compare the results. ## these executables and compare the results.
run_command("./exe-ifcc", "ifcc-execute.txt") run_command("./exe-ifcc", "ifcc-execute.txt")
if open("gcc-execute.txt").read() != open("ifcc-execute.txt").read() : if open("gcc-execute.txt").read() != open("ifcc-execute.txt").read() :
print("TEST FAIL (different results at execution)") print("TEST FAIL (different results at execution)")

View File

@ -1,11 +0,0 @@
gcc: gcca main-gcc.asm
gcc -o main-gcc.o main-gcc.asm
gcca: main.c
gcc -S -o main-gcc.asm main.c
ifcc: ifcca main-ifcc.asm
gcc -o main-ifcc.o main-ifcc.asm
ifcca: main.c comp-ifcc
../compiler/ifcc main.c > main-ifcc.asm
comp-ifcc:
cd ../compiler && make

View File

@ -1,10 +0,0 @@
int main() {
int z;
int x;
x = 56;
int y;
x = y;
y = 10;
z = x;
return z;
}

View File

@ -1,8 +1,8 @@
int main() { int main() {
int z; int z, x;
int x;
x = 56; x = 56;
int y; int y;
int n, o;
y = 10; y = 10;
x = y; x = y;
z = x; z = x;