Skip to content

Commit

Permalink
Code optimizations in implicit codegen (#523)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Apr 27, 2024
1 parent 3e5f72c commit e296b4d
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 65 deletions.
30 changes: 4 additions & 26 deletions media/test-project/test.spice
@@ -1,38 +1,16 @@
type Visitor struct {}

type SymbolTable struct {}

type Visitable interface {
f<bool> accept(Visitor*);
}

type AstNode struct : Visitable {}

type AstEntryNode struct : Visitable {
AstNode astNode
SymbolTable* extFunctionScope
bool takesArgs
}

f<int> main() {
AstEntryNode entryNode;
printf("%d", entryNode.takesArgs);
}

/*import "bootstrap/util/block-allocator";
import "bootstrap/util/block-allocator";
import "bootstrap/util/memory";

type ASTNode struct {
int value
}

p ASTNode.dtor() {
public p ASTNode.dtor() {
printf("Dtor called!");
}

f<int> main() {
DefaultMemoryManager defaultMemoryManager;
IMemoryManager* memoryManager = &defaultMemoryManager;
memoryManager.allocate(10l);
//BlockAllocator<ASTNode> allocator = BlockAllocator<ASTNode>(memoryManager, 10l);
}*/
BlockAllocator<ASTNode> allocator = BlockAllocator<ASTNode>(memoryManager, 10l);
}
2 changes: 1 addition & 1 deletion src-bootstrap/util/block-allocator.spice
Expand Up @@ -39,7 +39,7 @@ public p BlockAllocator.dtor() {
public p BlockAllocator.allocateNewBlock() {
// Allocate new block
heap byte* ptr = this.memoryManager.allocate(this.blockSize);
if ptr != nil<heap byte*> {
if ptr == nil<heap byte*> {
panic(Error("Could not allocate memory block for BlockAllocator."));
}

Expand Down
23 changes: 10 additions & 13 deletions src/ast/ASTNodes.h
Expand Up @@ -76,7 +76,7 @@ class ASTNode {
return nodes;
}

virtual void resizeToNumberOfManifestations(size_t manifestationCount) { // NOLINT(misc-no-recursion)
void resizeToNumberOfManifestations(size_t manifestationCount) { // NOLINT(misc-no-recursion)
// Resize children
for (ASTNode *child : children) {
assert(child != nullptr);
Expand All @@ -88,14 +88,14 @@ class ASTNode {
customItemsInitialization(manifestationCount);
}

virtual std::vector<std::vector<const Function *>> *getOpFctPointers() { // LCOV_EXCL_LINE
assert_fail("The given node does not overload the getOpFctPointers function"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
virtual std::vector<std::vector<const Function *>> *getOpFctPointers() { // LCOV_EXCL_LINE
assert_fail("The given node does not overload the getOpFctPointers function"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
[[nodiscard]] virtual const std::vector<std::vector<const Function *>> *getOpFctPointers() const { // LCOV_EXCL_LINE
assert_fail("The given node does not overload the getOpFctPointers function"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

virtual void customItemsInitialization(size_t) {} // Noop

Expand Down Expand Up @@ -134,17 +134,17 @@ class ASTNode {
[[nodiscard]] virtual std::vector<Function *> *getFctManifestations(const std::string &) { // LCOV_EXCL_LINE
assert_fail("Must be called on a FctDefNode, ProcDefNode, ExtDeclNode, StructDefNode or SignatureNode"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

[[nodiscard]] virtual std::vector<Struct *> *getStructManifestations() { // LCOV_EXCL_LINE
assert_fail("Must be called on a StructDefNode"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

[[nodiscard]] virtual std::vector<Interface *> *getInterfaceManifestations() { // LCOV_EXCL_LINE
assert_fail("Must be called on a InterfaceDefNode"); // LCOV_EXCL_LINE
return nullptr; // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

[[nodiscard]] virtual bool isFctOrProcDef() const { return false; }
[[nodiscard]] virtual bool isStructDef() const { return false; }
Expand Down Expand Up @@ -825,10 +825,7 @@ class StmtLstNode : public ASTNode {

// Other methods
[[nodiscard]] bool returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable) const override;
void resizeToNumberOfManifestations(size_t manifestationCount) override {
ASTNode::resizeToNumberOfManifestations(manifestationCount);
dtorFunctions.resize(manifestationCount);
}
void customItemsInitialization(size_t manifestationCount) override { dtorFunctions.resize(manifestationCount); }
[[nodiscard]] bool isStmtLstNode() const override { return true; }

// Public members
Expand Down
14 changes: 7 additions & 7 deletions src/irgenerator/GenImplicit.cpp
Expand Up @@ -334,10 +334,10 @@ void IRGenerator::generateCtorBodyPreamble(Scope *bodyScope) {
assert(fieldSymbol != nullptr && fieldSymbol->isField());
if (fieldSymbol->isImplicitField)
continue;
const SymbolType &fieldType = fieldSymbol->getType();
auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);

// Call ctor for struct fields
const SymbolType &fieldType = fieldSymbol->getType();
auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);
if (fieldType.is(TY_STRUCT)) {
// Lookup ctor function and call if available
Scope *matchScope = fieldType.getBodyScope();
Expand Down Expand Up @@ -398,9 +398,9 @@ void IRGenerator::generateCopyCtorBodyPreamble(const Function *copyCtorFunction)
assert(fieldSymbol != nullptr && fieldSymbol->isField());
if (fieldSymbol->isImplicitField)
continue;
const SymbolType &fieldType = fieldSymbol->getType();

// Call copy ctor for struct fields
const SymbolType &fieldType = fieldSymbol->getType();
if (fieldType.is(TY_STRUCT)) {
// Lookup copy ctor function and call if available
Scope *matchScope = fieldType.getBodyScope();
Expand Down Expand Up @@ -443,14 +443,14 @@ void IRGenerator::generateDtorBodyPreamble(const Function *dtorFunction) {
assert(fieldSymbol != nullptr && fieldSymbol->isField());
if (fieldSymbol->isImplicitField)
continue;
const SymbolType &fieldType = fieldSymbol->getType();

// Call dtor for struct fields
const SymbolType &fieldType = fieldSymbol->getType();
if (fieldType.is(TY_STRUCT)) {
// Lookup dtor function and generate call if found
dtorFunction = FunctionManager::lookupFunction(fieldType.getBodyScope(), DTOR_FUNCTION_NAME, fieldType, {}, false);
if (dtorFunction)
generateCtorOrDtorCall(fieldSymbol, dtorFunction, {});
const Function *dtorFct = FunctionManager::lookupFunction(fieldType.getBodyScope(), DTOR_FUNCTION_NAME, fieldType, {}, false);
if (dtorFct)
generateCtorOrDtorCall(fieldSymbol, dtorFct, {});
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/typechecker/FunctionManager.cpp
Expand Up @@ -413,7 +413,7 @@ bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs,

// Check if the requested param type matches the candidate param type. The type mapping may be extended
if (!TypeMatcher::matchRequestedToCandidateType(candidateParamType, requestedType, typeMapping, genericTypeResolver,
strictSpecifierMatching, isArgTemporary))
strictSpecifierMatching))
return false;

// Substantiate the candidate param type, based on the type mapping
Expand Down
24 changes: 16 additions & 8 deletions src/typechecker/TypeChecker.cpp
Expand Up @@ -1521,7 +1521,8 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) {
// Retrieve scope for the new scope path fragment
if (baseType.is(TY_STRUCT)) {
// Set access scope to struct scope
const NameRegistryEntry *nameRegistryEntry = sourceFile->getNameRegistryEntry(baseType.getSubType());
const std::string &structName = baseType.getSubType();
const NameRegistryEntry *nameRegistryEntry = sourceFile->getNameRegistryEntry(structName);
assert(nameRegistryEntry != nullptr);

// Change the access scope to the struct scope
Expand All @@ -1530,8 +1531,7 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) {
// Check if the entry is public if it is imported
assert(nameRegistryEntry->targetEntry != nullptr);
if (!nameRegistryEntry->targetEntry->getType().isPublic() && accessScope->parent->isImportedBy(rootScope))
SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY,
"Cannot access struct '" + nameRegistryEntry->targetEntry->name + "' due to its private visibility")
SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access struct '" + structName + "' due to its private visibility")
}

return ExprResult{node->setEvaluatedSymbolType(varType, manIdx), varEntry};
Expand Down Expand Up @@ -1778,7 +1778,7 @@ std::any TypeChecker::visitFctCall(FctCallNode *node) {

returnType = mapImportedScopeTypeToLocalType(returnType.getBaseType().getBodyScope(), returnType);

// Add anonymous symbol to keep track of deallocation
// Add anonymous symbol to keep track of de-allocation
if (returnType.is(TY_STRUCT))
anonymousSymbol = currentScope->symbolTable.insertAnonymous(returnType, node);
}
Expand All @@ -1793,7 +1793,7 @@ std::any TypeChecker::visitFctCall(FctCallNode *node) {
return ExprResult{node->setEvaluatedSymbolType(returnType, manIdx), anonymousSymbol};
}

bool TypeChecker::visitOrdinaryFctCall(FctCallNode *node, const std::vector<SymbolType> &templateTypes,
bool TypeChecker::visitOrdinaryFctCall(FctCallNode *node, std::vector<SymbolType> &templateTypes,
const std::string &fqFunctionName) {
FctCallNode::FctCallData &data = node->data.at(manIdx);

Expand Down Expand Up @@ -1856,13 +1856,17 @@ bool TypeChecker::visitOrdinaryFctCall(FctCallNode *node, const std::vector<Symb
if (!data.thisType.is(TY_DYN) && !templateTypes.empty())
data.thisType.setTemplateTypes(templateTypes);

// Map local types to imported types
// Map local arg types to imported types
Scope *matchScope = data.calleeParentScope = functionRegistryEntry->targetScope;
ArgList localArgs;
localArgs.reserve(data.argResults.size());
for (const ExprResult &argResult : data.argResults)
localArgs.emplace_back(mapLocalTypeToImportedScopeType(data.calleeParentScope, argResult.type), argResult.isTemporary());

// Map local template types to imported types
for (SymbolType &templateType : templateTypes)
templateType = mapLocalTypeToImportedScopeType(data.calleeParentScope, templateType);

// Retrieve function object
data.callee = FunctionManager::matchFunction(matchScope, functionName, data.thisType, localArgs, templateTypes, false, node);

Expand Down Expand Up @@ -1891,7 +1895,7 @@ bool TypeChecker::visitFctPtrCall(FctCallNode *node, const SymbolType &functionT
return true;
}

bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope, const std::vector<SymbolType> &templateTypes) const {
bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope, std::vector<SymbolType> &templateTypes) const {
FctCallNode::FctCallData &data = node->data.at(manIdx);

// Traverse through structs - the first fragment is already looked up and the last one is the method name
Expand All @@ -1918,12 +1922,16 @@ bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope, const s
if (data.thisType.is(TY_INTERFACE))
SOFT_ERROR_BOOL(node, INVALID_MEMBER_ACCESS, "Cannot call a method on an interface")

// Map local types to imported types
// Map local arg types to imported types
Scope *matchScope = data.calleeParentScope = structScope;
ArgList localArgs;
for (const ExprResult &argResult : data.argResults)
localArgs.emplace_back(mapLocalTypeToImportedScopeType(data.calleeParentScope, argResult.type), argResult.isTemporary());

// Map local template types to imported types
for (SymbolType &templateType : templateTypes)
templateType = mapLocalTypeToImportedScopeType(data.calleeParentScope, templateType);

// 'this' type
SymbolType localThisType = data.thisType;
autoDeReference(localThisType);
Expand Down
4 changes: 2 additions & 2 deletions src/typechecker/TypeChecker.h
Expand Up @@ -132,9 +132,9 @@ class TypeChecker : private CompilerPass, public ASTVisitor {
bool typeCheckedMainFct = false;

// Private methods
bool visitOrdinaryFctCall(FctCallNode *node, const std::vector<SymbolType> &templateTypes, const std::string &fqFunctionName);
bool visitOrdinaryFctCall(FctCallNode *node, std::vector<SymbolType> &templateTypes, const std::string &fqFunctionName);
bool visitFctPtrCall(FctCallNode *node, const SymbolType &functionType) const;
bool visitMethodCall(FctCallNode *node, Scope *structScope, const std::vector<SymbolType> &templateTypes) const;
bool visitMethodCall(FctCallNode *node, Scope *structScope, std::vector<SymbolType> &templateTypes) const;
bool checkAsyncLambdaCaptureRules(LambdaBaseNode *node, const LambdaAttrNode *attrs) const;
[[nodiscard]] SymbolType mapLocalTypeToImportedScopeType(const Scope *targetScope, const SymbolType &symbolType) const;
[[nodiscard]] SymbolType mapImportedScopeTypeToLocalType(const Scope *sourceScope, const SymbolType &symbolType) const;
Expand Down
15 changes: 12 additions & 3 deletions src/typechecker/TypeCheckerImplicit.cpp
@@ -1,6 +1,7 @@
// Copyright (c) 2021-2024 ChilliBits. All rights reserved.

#include "TypeChecker.h"
#include "TypeMatcher.h"

#include <SourceFile.h>
#include <ast/ASTBuilder.h>
Expand Down Expand Up @@ -252,6 +253,8 @@ void TypeChecker::createCtorBodyPreamble(Scope *bodyScope) {
if (fieldSymbol->isImplicitField)
continue;
SymbolType fieldType = fieldSymbol->getType();
if (fieldType.hasAnyGenericParts())
TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping);

if (fieldType.is(TY_STRUCT)) {
auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);
Expand Down Expand Up @@ -282,15 +285,19 @@ void TypeChecker::createCopyCtorBodyPreamble(Scope *bodyScope) {
if (fieldSymbol->isImplicitField)
continue;
SymbolType fieldType = fieldSymbol->getType();
if (fieldType.hasAnyGenericParts())
TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping);

if (fieldType.is(TY_STRUCT)) {
auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);
// Match ctor function, create the concrete manifestation and set it to used
Scope *matchScope = fieldType.getBodyScope();
const ArgList args = {{fieldType.toConstReference(fieldNode), false /* we always have the field as storage */}};
Function *spiceFunc = FunctionManager::matchFunction(matchScope, CTOR_FUNCTION_NAME, fieldType, args, {}, false, fieldNode);
fieldType.setBodyScope(spiceFunc->thisType.getBodyScope());
fieldSymbol->updateType(fieldType, true);
if (spiceFunc != nullptr) {
fieldType.setBodyScope(spiceFunc->thisType.getBodyScope());
fieldSymbol->updateType(fieldType, true);
}
}
}
}
Expand All @@ -309,7 +316,9 @@ void TypeChecker::createDtorBodyPreamble(Scope *bodyScope) {
assert(fieldSymbol != nullptr && fieldSymbol->isField());
if (fieldSymbol->isImplicitField)
continue;
const SymbolType &fieldType = fieldSymbol->getType();
SymbolType fieldType = fieldSymbol->getType();
if (fieldType.hasAnyGenericParts())
TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping);

if (fieldType.is(TY_STRUCT)) {
auto fieldNode = spice_pointer_cast<FieldNode *>(fieldSymbol->declNode);
Expand Down
3 changes: 1 addition & 2 deletions src/typechecker/TypeMatcher.cpp
Expand Up @@ -26,8 +26,7 @@ bool TypeMatcher::matchRequestedToCandidateTypes(const std::vector<SymbolType> &
}

bool TypeMatcher::matchRequestedToCandidateType(SymbolType candidateType, SymbolType requestedType, TypeMapping &typeMapping,
ResolverFct &resolverFct, bool strictSpecifierMatching,
bool isRequestedValueTemporary) {
ResolverFct &resolverFct, bool strictSpecifierMatching) {
// Unwrap as far as possible and remove reference wrappers if possible
SymbolType::unwrapBoth(candidateType, requestedType);

Expand Down
3 changes: 1 addition & 2 deletions src/typechecker/TypeMatcher.h
Expand Up @@ -23,8 +23,7 @@ class TypeMatcher {
const std::vector<SymbolType> &reqTypes, TypeMapping &typeMapping,
ResolverFct &resolverFct, bool strictSpecifiers);
static bool matchRequestedToCandidateType(SymbolType candidateType, SymbolType requestedType, TypeMapping &typeMapping,
ResolverFct &resolverFct, bool strictSpecifierMatching,
bool isRequestedValueTemporary = false);
ResolverFct &resolverFct, bool strictSpecifierMatching);
static void substantiateTypesWithTypeMapping(std::vector<SymbolType> &symbolTypes, const TypeMapping &typeMapping);
static void substantiateTypeWithTypeMapping(SymbolType &symbolType, const TypeMapping &typeMapping);
};
Expand Down
@@ -0,0 +1,19 @@
; ModuleID = 'source.spice'
source_filename = "source.spice"

%struct.TestStruct = type { %struct.Outer }
%struct.Outer = type { i32 }

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
%result = alloca i32, align 4
%ts = alloca %struct.TestStruct, align 8
store i32 0, ptr %result, align 4
call void @_ZN10TestStructI5OuterE4ctorEv(ptr noundef nonnull align 4 dereferenceable(4) %ts)
%1 = load i32, ptr %result, align 4
ret i32 %1
}

declare void @_ZN10TestStructI5OuterE4ctorEv(ptr)

attributes #0 = { noinline nounwind optnone uwtable }
@@ -0,0 +1,9 @@
import "source1";

type Outer struct {
int i
}

f<int> main() {
TestStruct<Outer> ts = TestStruct<Outer>();
}
@@ -0,0 +1,13 @@
type T dyn;

type TestStruct<T> struct {
T t
}

f<int> privateFunction<T>(const T& t) {
return t.i;
}

public p TestStruct.ctor() {
privateFunction(this.t);
}

0 comments on commit e296b4d

Please sign in to comment.