From e296b4dae606d1efd4837a834c42c19ed5751463 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 28 Apr 2024 00:05:36 +0200 Subject: [PATCH] Code optimizations in implicit codegen (#523) --- media/test-project/test.spice | 30 +++---------------- src-bootstrap/util/block-allocator.spice | 2 +- src/ast/ASTNodes.h | 23 +++++++------- src/irgenerator/GenImplicit.cpp | 14 ++++----- src/typechecker/FunctionManager.cpp | 2 +- src/typechecker/TypeChecker.cpp | 24 ++++++++++----- src/typechecker/TypeChecker.h | 4 +-- src/typechecker/TypeCheckerImplicit.cpp | 15 ++++++++-- src/typechecker/TypeMatcher.cpp | 3 +- src/typechecker/TypeMatcher.h | 3 +- .../ir-code.ll | 19 ++++++++++++ .../source.spice | 9 ++++++ .../source1.spice | 13 ++++++++ 13 files changed, 96 insertions(+), 65 deletions(-) create mode 100644 test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/ir-code.ll create mode 100644 test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source.spice create mode 100644 test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source1.spice diff --git a/media/test-project/test.spice b/media/test-project/test.spice index 1b830c341..4b8a30feb 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,38 +1,16 @@ -type Visitor struct {} - -type SymbolTable struct {} - -type Visitable interface { - f accept(Visitor*); -} - -type AstNode struct : Visitable {} - -type AstEntryNode struct : Visitable { - AstNode astNode - SymbolTable* extFunctionScope - bool takesArgs -} - -f 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 main() { DefaultMemoryManager defaultMemoryManager; IMemoryManager* memoryManager = &defaultMemoryManager; - memoryManager.allocate(10l); - //BlockAllocator allocator = BlockAllocator(memoryManager, 10l); -}*/ \ No newline at end of file + BlockAllocator allocator = BlockAllocator(memoryManager, 10l); +} \ No newline at end of file diff --git a/src-bootstrap/util/block-allocator.spice b/src-bootstrap/util/block-allocator.spice index 4af700a68..7101acf9a 100644 --- a/src-bootstrap/util/block-allocator.spice +++ b/src-bootstrap/util/block-allocator.spice @@ -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 { + if ptr == nil { panic(Error("Could not allocate memory block for BlockAllocator.")); } diff --git a/src/ast/ASTNodes.h b/src/ast/ASTNodes.h index cfdcbe52a..e71bfbe92 100644 --- a/src/ast/ASTNodes.h +++ b/src/ast/ASTNodes.h @@ -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); @@ -88,14 +88,14 @@ class ASTNode { customItemsInitialization(manifestationCount); } - virtual std::vector> *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> *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> *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 @@ -134,17 +134,17 @@ class ASTNode { [[nodiscard]] virtual std::vector *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 *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 *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; } @@ -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 diff --git a/src/irgenerator/GenImplicit.cpp b/src/irgenerator/GenImplicit.cpp index 87e913a53..4fd3627cb 100644 --- a/src/irgenerator/GenImplicit.cpp +++ b/src/irgenerator/GenImplicit.cpp @@ -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(fieldSymbol->declNode); // Call ctor for struct fields + const SymbolType &fieldType = fieldSymbol->getType(); + auto fieldNode = spice_pointer_cast(fieldSymbol->declNode); if (fieldType.is(TY_STRUCT)) { // Lookup ctor function and call if available Scope *matchScope = fieldType.getBodyScope(); @@ -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(); @@ -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; } diff --git a/src/typechecker/FunctionManager.cpp b/src/typechecker/FunctionManager.cpp index 431187820..f7e76cf90 100644 --- a/src/typechecker/FunctionManager.cpp +++ b/src/typechecker/FunctionManager.cpp @@ -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 diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index 82ca45c82..a8cc2e904 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -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 @@ -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}; @@ -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); } @@ -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 &templateTypes, +bool TypeChecker::visitOrdinaryFctCall(FctCallNode *node, std::vector &templateTypes, const std::string &fqFunctionName) { FctCallNode::FctCallData &data = node->data.at(manIdx); @@ -1856,13 +1856,17 @@ bool TypeChecker::visitOrdinaryFctCall(FctCallNode *node, const std::vectortargetScope; 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); @@ -1891,7 +1895,7 @@ bool TypeChecker::visitFctPtrCall(FctCallNode *node, const SymbolType &functionT return true; } -bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope, const std::vector &templateTypes) const { +bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope, std::vector &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 @@ -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); diff --git a/src/typechecker/TypeChecker.h b/src/typechecker/TypeChecker.h index b523b733a..a8d17ab71 100644 --- a/src/typechecker/TypeChecker.h +++ b/src/typechecker/TypeChecker.h @@ -132,9 +132,9 @@ class TypeChecker : private CompilerPass, public ASTVisitor { bool typeCheckedMainFct = false; // Private methods - bool visitOrdinaryFctCall(FctCallNode *node, const std::vector &templateTypes, const std::string &fqFunctionName); + bool visitOrdinaryFctCall(FctCallNode *node, std::vector &templateTypes, const std::string &fqFunctionName); bool visitFctPtrCall(FctCallNode *node, const SymbolType &functionType) const; - bool visitMethodCall(FctCallNode *node, Scope *structScope, const std::vector &templateTypes) const; + bool visitMethodCall(FctCallNode *node, Scope *structScope, std::vector &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; diff --git a/src/typechecker/TypeCheckerImplicit.cpp b/src/typechecker/TypeCheckerImplicit.cpp index ad41ebb83..e7a313f7d 100644 --- a/src/typechecker/TypeCheckerImplicit.cpp +++ b/src/typechecker/TypeCheckerImplicit.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2021-2024 ChilliBits. All rights reserved. #include "TypeChecker.h" +#include "TypeMatcher.h" #include #include @@ -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(fieldSymbol->declNode); @@ -282,6 +285,8 @@ 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(fieldSymbol->declNode); @@ -289,8 +294,10 @@ void TypeChecker::createCopyCtorBodyPreamble(Scope *bodyScope) { 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); + } } } } @@ -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(fieldSymbol->declNode); diff --git a/src/typechecker/TypeMatcher.cpp b/src/typechecker/TypeMatcher.cpp index c2929b148..bf44d62f5 100644 --- a/src/typechecker/TypeMatcher.cpp +++ b/src/typechecker/TypeMatcher.cpp @@ -26,8 +26,7 @@ bool TypeMatcher::matchRequestedToCandidateTypes(const std::vector & } 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); diff --git a/src/typechecker/TypeMatcher.h b/src/typechecker/TypeMatcher.h index 64bfaed94..ab8ea9acb 100644 --- a/src/typechecker/TypeMatcher.h +++ b/src/typechecker/TypeMatcher.h @@ -23,8 +23,7 @@ class TypeMatcher { const std::vector &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 &symbolTypes, const TypeMapping &typeMapping); static void substantiateTypeWithTypeMapping(SymbolType &symbolType, const TypeMapping &typeMapping); }; diff --git a/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/ir-code.ll b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/ir-code.ll new file mode 100644 index 000000000..c634f7ccb --- /dev/null +++ b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/ir-code.ll @@ -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 } diff --git a/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source.spice b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source.spice new file mode 100644 index 000000000..63612a2ad --- /dev/null +++ b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source.spice @@ -0,0 +1,9 @@ +import "source1"; + +type Outer struct { + int i +} + +f main() { + TestStruct ts = TestStruct(); +} \ No newline at end of file diff --git a/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source1.spice b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source1.spice new file mode 100644 index 000000000..e2c24d514 --- /dev/null +++ b/test/test-files/irgenerator/imports/sucess-struct-backwards-dependency/source1.spice @@ -0,0 +1,13 @@ +type T dyn; + +type TestStruct struct { + T t +} + +f privateFunction(const T& t) { + return t.i; +} + +public p TestStruct.ctor() { + privateFunction(this.t); +} \ No newline at end of file