Skip to content

Commit 7812512

Browse files
committed
[Semantic, LLVM] Applied changes from the Compiler to the corresponding directories
1 parent aea066e commit 7812512

File tree

4 files changed

+242
-134
lines changed

4 files changed

+242
-134
lines changed

llvm/ast.hpp

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class AST
8181
// This function will be used to check thre type of return case in functions.
8282
// Default is NO_RETURN so we don't have to override it in every class.
8383
virtual ReturnInfo check_return_case() const { return ReturnInfo(NO_RETURN, false, false); }
84+
virtual bool contains_loop() const { return false; }
8485

8586
virtual llvm::Value *igen() { return nullptr; }
8687
virtual void LLVM_IR_gen();
@@ -211,6 +212,11 @@ class AST
211212
{
212213
return llvm::ConstantInt::get(TheContext, llvm::APInt(32, n, true));
213214
}
215+
static llvm::ConstantInt *c64(int64_t n)
216+
{
217+
return llvm::ConstantInt::get(TheContext, llvm::APInt(64, n, true));
218+
}
219+
214220
static llvm::AllocaInst *createEntryBlockAlloca(
215221
llvm::Function *function,
216222
const std::string &name,
@@ -844,7 +850,15 @@ class LValue : public Expr
844850
{
845851
if (stringLiteral)
846852
{
847-
this->type = TYPE_BYTE_ARRAY;
853+
if(!indices.empty()) {
854+
if(indices.size() > 1) {
855+
semantic_error(line, column, "String literal can only be indexed once.");
856+
}
857+
indices[0]->check_type(TYPE_INT);
858+
this->type = TYPE_BYTE;
859+
} else {
860+
this->type = TYPE_BYTE_ARRAY;
861+
}
848862
return;
849863
}
850864

@@ -884,10 +898,6 @@ class LValue : public Expr
884898
this->type = symbolTypeFromArray(var->type);
885899
else
886900
this->type = var->type; // multi-dimensional arrays keep their type
887-
888-
// printf("LValue entry %s array dimensions: ", name);
889-
// for(int d : var->arrayDimensions) printf("%d ", d);
890-
// printf("\n");
891901
}
892902
else
893903
{
@@ -920,15 +930,33 @@ class LValue : public Expr
920930
strConst,
921931
globalName);
922932

923-
gvar->setAlignment(llvm::Align(1));
924-
925-
// Pointer to first character
926-
llvm::Constant *zero = AST::c32(0);
927-
std::vector<llvm::Constant *> idxList = {zero, zero};
928-
llvm::Constant *strPtr = llvm::ConstantExpr::getGetElementPtr(
929-
strConst->getType(), gvar, idxList);
930-
931-
return strPtr;
933+
gvar->setAlignment(llvm::MaybeAlign(1));
934+
gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
935+
936+
// Indices for GEP into [N x i8]: {0, idx}
937+
llvm::Value *idx0 =AST::c64(0); // First index is always 0 (select the array, not an element past it)
938+
llvm::Value *idx1;
939+
940+
if (!indices.empty()) {
941+
// Dynamic/variable index
942+
llvm::Value *v = indices[0]->igen();
943+
// Cast/truncate/extend to i64 for GEP
944+
if (!v->getType()->isIntegerTy(64))
945+
v = AST::Builder.CreateSExtOrTrunc(v, AST::i64, "idx64");
946+
idx1 = v;
947+
} else {
948+
// No indexing -> point at first char
949+
idx1 = c64(0);
950+
}
951+
952+
llvm::Value *chPtr = AST::Builder.CreateInBoundsGEP(
953+
gvar->getValueType(), // [N x i8]
954+
gvar, // [N x i8]*
955+
{idx0, idx1}, // {0, i}
956+
"str.chptr"
957+
);
958+
// return Value of type i8*
959+
return chPtr; // i8*
932960
}
933961

934962
// === Case 2 & 3: Variable lookup (function arg or local) ===
@@ -1589,6 +1617,13 @@ class Block : public Stmt
15891617
return result;
15901618
}
15911619

1620+
bool contains_loop() const override {
1621+
for(Stmt* stmt : stmt_list) {
1622+
if (stmt->contains_loop()) return true;
1623+
}
1624+
return false;
1625+
}
1626+
15921627
llvm::Value *igen() override
15931628
{
15941629
// Save current variable mapping
@@ -1904,6 +1939,15 @@ class IfStmt : public Stmt
19041939
return combine_if(branches);
19051940
}
19061941

1942+
bool contains_loop() const override {
1943+
if(then_block->contains_loop()) return true;
1944+
for(IfStmt* i : elifs) {
1945+
if(i->contains_loop()) return true;
1946+
}
1947+
if(else_block && else_block->contains_loop()) return true;
1948+
return false;
1949+
}
1950+
19071951
llvm::Value *igen() override
19081952
{
19091953
llvm::Function *function = AST::Builder.GetInsertBlock()->getParent();
@@ -2041,6 +2085,10 @@ class LoopStmt : public Stmt
20412085
return ReturnInfo(rc, false, false);
20422086
}
20432087

2088+
bool contains_loop() const override {
2089+
return true;
2090+
}
2091+
20442092
llvm::Value *igen() override
20452093
{
20462094
llvm::Function *function = AST::Builder.GetInsertBlock()->getParent();
@@ -2577,10 +2625,12 @@ class FParDef : public AST
25772625
if (type->getDataType() == DataType::INT)
25782626
{
25792627
entry->type = TYPE_INT_ARRAY;
2628+
entry->isAssigned = true;
25802629
}
25812630
else if (type->getDataType() == DataType::BYTE)
25822631
{
25832632
entry->type = TYPE_BYTE_ARRAY;
2633+
entry->isAssigned = true;
25842634
}
25852635
else
25862636
{
@@ -2591,7 +2641,11 @@ class FParDef : public AST
25912641
entry->isRef = true; // all arrays are passed by reference
25922642
}
25932643
entry->persistent = true; // Parameters should be persistent because they are needed later
2594-
entry->isVar = true;
2644+
2645+
if(entry->isRef) {
2646+
entry->isVar = true;
2647+
}
2648+
25952649
st.insert(id, entry);
25962650
}
25972651
}
@@ -2845,6 +2899,14 @@ class FuncDecl : public AST
28452899
void sem_analyze() override
28462900
{
28472901
header->sem_analyze();
2902+
STEntry *he = st.lookup(header->getName());
2903+
2904+
if (header->getParams().empty() && he->functionReturnType == TYPE_VOID)
2905+
{
2906+
std::ostringstream sout;
2907+
sout << "Function \"" << header->getName() << "\" declaration has no return type or parameters.";
2908+
semantic_warning(sout.str());
2909+
}
28482910

28492911
// Let's create a new scope for the decleration so
28502912
// its parameters are entered into the SymbolTable
@@ -2990,17 +3052,19 @@ class VarDef : public AST
29903052
oss << "Duplicate variable definition: variable \"" << varName << "\" is already defined in the current scope.";
29913053
semantic_error(line, column, oss.str());
29923054
}
2993-
3055+
29943056
STEntry *entry = new STEntry();
29953057
if (!type->getParenthesisList().empty())
29963058
{
29973059
if (type->getDataType() == DataType::INT)
29983060
{
29993061
entry->type = TYPE_INT_ARRAY;
3062+
entry->isAssigned = true;
30003063
}
30013064
else if (type->getDataType() == DataType::BYTE)
30023065
{
30033066
entry->type = TYPE_BYTE_ARRAY;
3067+
entry->isAssigned = true;
30043068
}
30053069
else
30063070
{
@@ -3142,7 +3206,24 @@ class FuncDef : public AST
31423206
}
31433207

31443208
ReturnInfo check_return_case() const override {
3145-
return block->check_return_case();
3209+
ReturnInfo ri = block->check_return_case();
3210+
3211+
// Added after discussion with Professor Sagonas on issue #43
3212+
if(ri.rc == WEAK_RETURN && contains_loop()) {
3213+
ri.rc = STRONG_RETURN;
3214+
3215+
std::ostringstream oss;
3216+
oss << "Function \"" << header->getName() << "\" contains a loop and may not always return a value."
3217+
<< " Please add a return statement at the end of the function to ensure all possible paths return a value.";
3218+
3219+
semantic_warning_wline(line, column, oss.str());
3220+
}
3221+
3222+
return ri;
3223+
}
3224+
3225+
bool contains_loop() const override {
3226+
return block->contains_loop();
31463227
}
31473228

31483229
Header *getHeader() const
@@ -3155,6 +3236,14 @@ class FuncDef : public AST
31553236
// Check for previous declaration before inserting the new entry of definition
31563237
STEntry *previousDecl = st.getCurrentScope().searchPreviousDeclaration(header->getName());
31573238

3239+
// Check for previous definition -> error: no duplicate defs
3240+
STEntry *existingDef = st.lookup(header->getName());
3241+
if(existingDef && existingDef->type == TYPE_FUNCTION && !existingDef->isDeclaration) {
3242+
std::ostringstream oss;
3243+
oss << "Duplicate definition of function \"" << header->getName() << "\" is not allowed!";
3244+
semantic_error(line, column, oss.str());
3245+
}
3246+
31583247
// 1st: insert the function's own header into the parent scope
31593248
header->sem_analyze(); // FParDefs are inserted here... No!
31603249

@@ -3292,7 +3381,20 @@ class FuncDef : public AST
32923381
}
32933382
}
32943383
}
3384+
3385+
const Scope& scope = st.getCurrentScope();
3386+
const std::map<std::string, STEntry*>& entriesMap = scope.getLocals();
3387+
32953388
block->sem_analyze();
3389+
3390+
for(auto it = entriesMap.begin(); it != entriesMap.end(); it++) {
3391+
if(!it->second->isAssigned && it->second->isVar && it->second->arrayDimensions.empty()) {
3392+
std::ostringstream sout;
3393+
sout << "Variable \"" << it->first << "\" is declared but never assigned a value!";
3394+
semantic_warning(sout.str());
3395+
}
3396+
}
3397+
32963398
st.exitScope();
32973399
}
32983400

llvm/symbol.hpp

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "lexer.hpp" // for yyerror
1010
#include "error.hpp" // for semantic_error
1111

12-
// New implementation: use TYPE_INT_ARRAY and TYPE_BYTE_ARRAY
1312
// Strings will be TYPE_BYTE_ARRAY with only one dimension
1413
enum SymbolType {
1514
TYPE_INT,
@@ -45,14 +44,14 @@ struct STEntry {
4544

4645
// Fields for function entries
4746
SymbolType functionReturnType;
48-
std::vector<STEntry*> functionParams; // new implementation
47+
std::vector<STEntry*> functionParams;
4948

5049
// Functions with decl keyword
5150
bool isDeclaration = false;
5251

5352
// Fields for array and ref entries
54-
std::vector<int> arrayDimensions; // dimensions for arrays
55-
bool isRef = false; // true if this is a reference type
53+
std::vector<int> arrayDimensions; // dimensions for arrays
54+
bool isRef = false; // true if this is a reference type
5655

5756
// If an entry is persistent, it will not be deleted when a scope is removed from the stack
5857
bool persistent = false;
@@ -131,15 +130,10 @@ class Scope {
131130
locals[name] = entry;
132131
}
133132

134-
bool isThereNameInLocals(const std::string& name) const {
135-
return locals.find(name) != locals.end();
136-
}
137-
138133
STEntry* lookup(const std::string& name) {
139134
if (locals.find(name) == locals.end()) {
140135
return nullptr;
141136
}
142-
//printf("Found symbol: %s\n", name.c_str());
143137
return locals[name];
144138
}
145139

@@ -171,11 +165,14 @@ class Scope {
171165
return locals;
172166
}
173167

168+
bool isThereNameInLocals(const std::string& name) const {
169+
return locals.find(name) != locals.end();
170+
}
171+
174172
STEntry* searchPreviousDeclaration(const std::string& name) const {
175-
//std::cout << "Searching for decl " << name << "...\n";
176173
if(locals.find(name) == locals.end())
177174
return nullptr;
178-
//std::cout << "Found it?\n";
175+
179176
STEntry* entry = locals.at(name);
180177
if(entry->isDeclaration && entry->type == TYPE_FUNCTION)
181178
return entry;
@@ -186,10 +183,10 @@ class Scope {
186183
private:
187184
std::map<std::string, STEntry*> locals;
188185
int offset;
189-
ScopeType scopeType = SCOPE_DEFAULT; // default scope type
186+
ScopeType scopeType = SCOPE_DEFAULT; // default scope type
190187

191188
// Fields only for function scopes
192-
SymbolType funcType = TYPE_VOID; // default function type
189+
SymbolType funcType = TYPE_VOID; // default function type
193190

194191
bool canInsert(const std::string& name, SymbolType toInsert) const {
195192
if(locals.find(name) == locals.end())
@@ -260,12 +257,10 @@ class SymbolTable {
260257

261258
void insert(const std::string& name, SymbolType type) {
262259
scopes.back().insert(name, type);
263-
//std::cout << "Inserted Scope: " << name << " of type: " << symbolTypeToString(type) << std::endl;
264260
}
265261

266262
void insert(const std::string& name, STEntry* entry) {
267263
scopes.back().insert(name, entry);
268-
//std::cout << "Inserted symbol: " << name << " of type: " << symbolTypeToString(entry->type) << std::endl;
269264
}
270265

271266
STEntry* lookup(const std::string& name) {
@@ -275,7 +270,6 @@ class SymbolTable {
275270
return entry;
276271
}
277272
}
278-
//yyerror(("Symbol not found: " + name).c_str());
279273
return nullptr; // not found
280274
}
281275

@@ -311,34 +305,22 @@ class SymbolTable {
311305
void enterScope(ScopeType scopeType = SCOPE_DEFAULT, SymbolType funcType = TYPE_VOID) {
312306
int offset = scopes.empty() ? 0 : scopes.back().getOffset();
313307
scopes.push_back(Scope(offset, scopeType, funcType));
314-
//std::cout << "Entering scope with type: "
315-
//<< scopeTypeToString(scopeType)
316-
//<< " and function type: "
317-
//<< symbolTypeToString(funcType)
318-
//<< std::endl;
319-
320-
//printf("Entered scope!\n");
321308
}
322309

323310
void exitScope() {
324311
// clean the locals but for the persistent ones
325312
if(scopes.back().getLocals().empty()) {
326313
//printf("Exiting empty scope\n");
327314
} else {
328-
//printf("Exiting scope with %ld symbols\n", scopes.back().getLocals().size());
329315
for(auto it = scopes.back().getLocals().begin(); it != scopes.back().getLocals().end(); it++) {
330-
//printf("it second is null ? %d\n", it->second == nullptr);
331316
if(!it->second->persistent) {
332-
//printf("Deleting symbol: %s\n", it->first.c_str());
333-
delete it->second; // delete the entry
334-
it->second = nullptr; // set to null to avoid dangling pointer
317+
delete it->second; // delete the entry
318+
it->second = nullptr; // set to null to avoid dangling pointer
335319
}
336320
}
337321
}
338322

339-
//printf("Before pop scope...\n");
340323
scopes.pop_back();
341-
//printf("Successfully exited scope\n");
342324
}
343325

344326
void changeCurrentScopeType(ScopeType scopeType) {
@@ -353,6 +335,7 @@ class SymbolTable {
353335
return scopes.back();
354336
}
355337

338+
// For debugging purposes
356339
void printScopes() const {
357340
std::cout << "=== Symbol Table Scopes ===" << std::endl;
358341
for (size_t i = 0; i < scopes.size(); ++i) {

0 commit comments

Comments
 (0)