Skip to content

Commit

Permalink
Add first AST optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Dec 29, 2022
1 parent 3e60add commit 42a7a19
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 45 deletions.
34 changes: 22 additions & 12 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
f<int> getAge() {
dyn i;
if (true) {
result = 20;
return;
} else if (i = false) {
result = 19;
}
return 15;
}
/*f<double> calledFunction(int mandatoryArg, dyn optionalArg = true) {
printf("Mandatory: %d\n", mandatoryArg);
printf("Optional: %d\n", optionalArg);
return 0.1;
}*/

/*f<double> calledFunction(string testString) {
printf("String: %s", testString);
return 0.3;
}*/

/*f<int> main() {
dyn res = calledFunction(1, false);
printf("Result: %f\n", res);
//calledFunction("test");
}*/

f<int> main() {
int age = getAge();
printf("The age is: %d", age);
bool i = true;
if (i) {
while (i) {

}
}
}
56 changes: 42 additions & 14 deletions src/ast/ASTNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class ASTNode {
for (const ASTNode *child : children)
delete child;
}
virtual void deleteRecursive(const ASTNode *anchorNode = nullptr) {
for (ASTNode *child : children) {
if (child != anchorNode)
child->deleteRecursive(anchorNode);
}
children.clear();
delete this;
}

// Friend classes
friend class ASTBuilder;
Expand Down Expand Up @@ -80,6 +88,33 @@ class ASTNode {

inline void reserveChildren(size_t numberOfChildren) { children.reserve(numberOfChildren); }

void replaceInParent(ASTNode *replacementNode) {
assert(parent != nullptr);
for (size_t i = 0; i < parent->children.size(); i++) {
if (parent->children.at(i) == this) {
// Replace in children vector
parent->children.at(i) = replacementNode;
// De-allocate subtree without destroying the replacement node
deleteRecursive(replacementNode);
break;
}
}
}

void removeFromParent() {
assert(parent != nullptr);
for (size_t i = 0; i < parent->children.size(); i++) {
ASTNode *child = parent->children.at(i);
if (child == this) {
// Remove from children vector
parent->children.erase(parent->children.begin() + (long)i);
// De-allocate subtree
delete this;
break;
}
}
}

virtual void resizeToNumberOfManifestations(size_t manifestationCount) { // NOLINT(misc-no-recursion)
// Reserve children
for (ASTNode *child : children)
Expand All @@ -90,7 +125,7 @@ class ASTNode {
customItemsInitialization(manifestationCount);
}

virtual void customItemsInitialization(size_t) { /* Noop */}
virtual void customItemsInitialization(size_t) {} // Noop

SymbolType setEvaluatedSymbolType(const SymbolType &symbolType, const size_t idx) {
symbolTypes.insert(symbolTypes.begin() + static_cast<long>(idx), symbolType);
Expand Down Expand Up @@ -142,11 +177,12 @@ class ASTNode {
bool unreachable = false;

protected:
// Private members
// Protected members
CompileTimeValue compileTimeValue = {};
std::string compileTimeStringValue;

private:
// Private members
bool hasDirectCompileTimeValue = false;
};

Expand Down Expand Up @@ -815,9 +851,7 @@ class DeclStmtNode : public ASTNode {

// Util methods
[[nodiscard]] bool isParam() const { return dynamic_cast<ParamLstNode *>(parent); }
void customItemsInitialization(size_t manifestationCount) override {
entries.resize(manifestationCount, nullptr);
}
void customItemsInitialization(size_t manifestationCount) override { entries.resize(manifestationCount, nullptr); }

// Public members
std::string varName;
Expand Down Expand Up @@ -1475,9 +1509,7 @@ class FunctionCallNode : public ASTNode {
[[nodiscard]] ArgLstNode *argLst() const { return getChild<ArgLstNode>(); }

// Util methods
void customItemsInitialization(size_t manifestationCount) override {
data.resize(manifestationCount);
}
void customItemsInitialization(size_t manifestationCount) override { data.resize(manifestationCount); }

// Public members
std::string fqFunctionName;
Expand Down Expand Up @@ -1520,9 +1552,7 @@ class StructInstantiationNode : public ASTNode {
[[nodiscard]] ArgLstNode *fieldLst() const { return getChild<ArgLstNode>(); }

// Util methods
void customItemsInitialization(size_t manifestationCount) override {
instantiatedStructs.resize(manifestationCount, nullptr);
}
void customItemsInitialization(size_t manifestationCount) override { instantiatedStructs.resize(manifestationCount, nullptr); }

// Public members
std::string fqStructName;
Expand Down Expand Up @@ -1614,9 +1644,7 @@ class CustomDataTypeNode : public ASTNode {
[[nodiscard]] TypeLstNode *templateTypeLst() const { return getChild<TypeLstNode>(); }

// Util methods
void customItemsInitialization(size_t manifestationCount) override {
customTypes.resize(manifestationCount, nullptr);
}
void customItemsInitialization(size_t manifestationCount) override { customTypes.resize(manifestationCount, nullptr); }

// Public members
std::string fqTypeName;
Expand Down
153 changes: 151 additions & 2 deletions src/astoptimizer/ASTOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,157 @@

#include <ast/ASTNodes.h>

std::any ASTOptimizer::visitThreadDef(ThreadDefNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitUnsafeBlockDef(UnsafeBlockDefNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitForLoop(ForLoopNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitForeachLoop(ForeachLoopNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitWhileLoop(WhileLoopNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitDoWhileLoop(DoWhileLoopNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

std::any ASTOptimizer::visitIfStmt(IfStmtNode *node) {
// ToDo
// Optimize all children first
visitChildren(node);

const bool thenBranchEmpty = node->thenBody()->children.empty();
const bool elseStmtEmpty = !node->elseStmt();

// Both are empty -> remove the whole node
if (thenBranchEmpty && elseStmtEmpty) {
node->removeFromParent();
return true;
}

// Only the 'then' branch is empty
if (thenBranchEmpty) {
// Check if the else branch is an else if
if (node->elseStmt()->isElseIf) {
// If it is an else if -> replace this node in its parent with the ifStmt of the else if
node->replaceInParent(node->elseStmt()->ifStmt());
return true;
}
// Otherwise, do nothing for now
}

return false;
}

std::any ASTOptimizer::visitElseStmt(ElseStmtNode *node) {
// Optimize all children first
visitChildren(node);

const bool ifStmtEmpty = node->isElseIf && !node->ifStmt();
const bool elseBranchEmpty = !node->isElseIf && node->body()->children.empty();

// This is an else-if and the ifStmt was optimized away -> remove the whole node
if (ifStmtEmpty) {
node->removeFromParent();
return true;
}

// The else branch is empty -> remove the whole node
if (elseBranchEmpty) {
node->removeFromParent();
return true;
}

return false;
}
}

std::any ASTOptimizer::visitAnonymousBlockStmt(AnonymousBlockStmtNode *node) {
// Optimize all children first
visitChildren(node);

const bool isBodyEmpty = node->body()->children.empty();

// The body is empty -> remove the whole node
if (isBodyEmpty) {
node->removeFromParent();
return true;
}

return false;
}

15 changes: 11 additions & 4 deletions src/astoptimizer/ASTOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@
* - Remove obviously unnecessary nodes from the AST
*
* The following optimizations are made:
* - Remove empty if and else branches
* - Remove empty if, else if and else branches
* - Remove empty while, do-while, for and foreach loops
* - Remove empty anonymous, thread and unsafe blocks
*/
class ASTOptimizer : private CompilerPass, public ASTVisitor {
public:
// Constructors
ASTOptimizer(GlobalResourceManager &resourceManager, SourceFile *sourceFile) : CompilerPass(resourceManager, sourceFile) {}

// Public methods
std::any visitThreadDef(ThreadDefNode *node) override;
std::any visitUnsafeBlockDef(UnsafeBlockDefNode *node) override;
std::any visitForLoop(ForLoopNode *node) override;
std::any visitForeachLoop(ForeachLoopNode *node) override;
std::any visitWhileLoop(WhileLoopNode *node) override;
std::any visitDoWhileLoop(DoWhileLoopNode *node) override;
std::any visitIfStmt(IfStmtNode *node) override;

private:
// Private members
std::any visitElseStmt(ElseStmtNode *node) override;
std::any visitAnonymousBlockStmt(AnonymousBlockStmtNode *node) override;
};
7 changes: 7 additions & 0 deletions src/irgenerator/GenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,13 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {
break;
}
case PrefixUnaryExprNode::OP_ADDRESS_OF: {
// If the address is unknown, allocate memory to save the value or constant
if (!lhs.ptr) {
llvm::Value *value = lhs.value ?: lhs.constant;
lhs.ptr = insertAlloca(value->getType());
builder.CreateStore(value, lhs.ptr);
}

// Execute operation
lhs.value = lhs.ptr;
lhs.ptr = insertAlloca(lhs.value->getType());
Expand Down
5 changes: 4 additions & 1 deletion src/irgenerator/GenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ std::any IRGenerator::visitDeclStmt(const DeclStmtNode *node) {
// Get LLVM type of variable
llvm::Type *varTy = varSymbolType.toLLVMType(context, currentScope);

// Check if right side is dyn array. If this is the case we have an empty array initializer and need the default value
const bool rhsIsDynArray = node->hasAssignment && node->assignExpr()->getEvaluatedSymbolType(manIdx).isArrayOf(TY_DYN);

// Check if the declaration is with an assignment or the default value
llvm::Value *varAddress = nullptr;
if (node->hasAssignment) { // Assignment
if (node->hasAssignment && !rhsIsDynArray) { // Assignment
ExprResult assignResult = doAssignment(varEntry, node->assignExpr());
assert(assignResult.entry == varEntry);
assert(assignResult.ptr == varEntry->getAddress());
Expand Down
4 changes: 2 additions & 2 deletions src/irgenerator/IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ ExprResult IRGenerator::doAssignment(SymbolTableEntry *lhsEntry, const ASTNode *
assert(lhsEntry != nullptr);

// Get symbol types of left and right side
const SymbolType lhsSType = lhsEntry->getType();
const SymbolType rhsSType = rhsNode->getEvaluatedSymbolType(manIdx);
const SymbolType &lhsSType = lhsEntry->getType();
const SymbolType &rhsSType = rhsNode->getEvaluatedSymbolType(manIdx);

// Deduce some information about the assignment
const bool isRefAssign = lhsSType.isReference();
Expand Down
Loading

0 comments on commit 42a7a19

Please sign in to comment.