diff --git a/media/test-project/os-test.spice b/media/test-project/os-test.spice index cab098535..11a0b0839 100644 --- a/media/test-project/os-test.spice +++ b/media/test-project/os-test.spice @@ -20,8 +20,14 @@ f main() { import "std/runtime/string_rt" as _rt_str; f main() { - _rt_str::String s1 = _rt_str::String('H'); - s1.append("ello"); - - printf("Equals: %d", s1.opEquals("Hell2")); + // Plus + printf("String: %s\n", "Hello " + "World!"); + string s = "Hello " + "World!"; + printf("String: %s\n", s); + // Equals + printf("String: %d\n", "Hello World!" == "Hello Programmers!"); + printf("String: %d\n", "Hello" == "Hell2"); + // Not equals + printf("String: %d\n", "Hello World!" != "Hello Programmers!"); + printf("String: %d\n", "Hello" != "Hell2"); } \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f532bb6ba..7185d2d06 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ set(SOURCES analyzer/OpRuleManager.h generator/GeneratorVisitor.cpp generator/GeneratorVisitor.h + generator/StdFunctionManager.cpp + generator/StdFunctionManager.h generator/OpRuleConversionsManager.cpp generator/OpRuleConversionsManager.h linker/LinkerInterface.cpp diff --git a/src/analyzer/AnalyzerVisitor.cpp b/src/analyzer/AnalyzerVisitor.cpp index f4ffd9729..7d57a8800 100644 --- a/src/analyzer/AnalyzerVisitor.cpp +++ b/src/analyzer/AnalyzerVisitor.cpp @@ -545,7 +545,7 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { } // Build struct specifiers - SymbolType symbolType = SymbolType(TY_STRUCT, node->structName, templateTypes); + SymbolType symbolType = SymbolType(TY_STRUCT, node->structName, {}, templateTypes); auto structSymbolSpecifiers = SymbolSpecifiers(symbolType); if (SpecifierLstNode *specifierLst = node->specifierLst(); specifierLst) { for (const auto &specifier : specifierLst->specifiers()) { @@ -1324,7 +1324,7 @@ std::any AnalyzerVisitor::visitAssignExpr(AssignExprNode *node) { "The variable '" + variableName + "' was referenced before defined"); // Perform type inference - if (lhsTy.is(TY_DYN)) + if (lhsTy.is(TY_DYN) || (lhsTy.is(TY_STRING) && rhsTy.is(TY_STRING))) currentEntry->updateType(rhsTy, false); // Update state in symbol table @@ -2296,7 +2296,7 @@ std::any AnalyzerVisitor::visitCustomDataType(CustomDataTypeNode *node) { throw SemanticError(node->codeLoc, UNKNOWN_DATATYPE, "Unknown datatype '" + identifier + "'"); structSymbol->setUsed(); - return node->setEvaluatedSymbolType(SymbolType(TY_STRUCT, identifier, concreteTemplateTypes)); + return node->setEvaluatedSymbolType(SymbolType(TY_STRUCT, identifier, {.arraySize = 0}, concreteTemplateTypes)); } void AnalyzerVisitor::insertDestructorCall(const CodeLoc &codeLoc, SymbolTableEntry *varEntry) { diff --git a/src/analyzer/OpRuleManager.cpp b/src/analyzer/OpRuleManager.cpp index ad9c7b83f..bcf412dfd 100644 --- a/src/analyzer/OpRuleManager.cpp +++ b/src/analyzer/OpRuleManager.cpp @@ -15,6 +15,9 @@ SymbolType OpRuleManager::getAssignResultType(const CodeLoc &codeLoc, const Symb // Allow char* = string if (lhs.isPointerOf(TY_CHAR) && rhs.is(TY_STRING)) return lhs; + // Allow string = string_object + if (lhs.is(TY_STRING) && rhs.is(TY_STRING)) + return rhs; // Check primitive type combinations return validateBinaryOperation(codeLoc, ASSIGN_OP_RULES, "=", lhs, rhs); } @@ -155,6 +158,10 @@ SymbolType OpRuleManager::getPlusResultType(const CodeLoc &codeLoc, const Symbol throw printErrorMessageUnsafe(codeLoc, "+", lhs, rhs); } + // Allow string + string + if (lhs.is(TY_STRING) && rhs.is(TY_STRING)) + return SymbolType(TY_STRING, "", { .isStringStruct = true }, {}); + return validateBinaryOperation(codeLoc, PLUS_OP_RULES, "+", lhs, rhs); } diff --git a/src/analyzer/OpRuleManager.h b/src/analyzer/OpRuleManager.h index 824b31499..791d76dff 100644 --- a/src/analyzer/OpRuleManager.h +++ b/src/analyzer/OpRuleManager.h @@ -24,7 +24,6 @@ const std::vector ASSIGN_OP_RULES = { BinaryOpRule(TY_LONG, TY_LONG, TY_LONG, false), // long = long -> long BinaryOpRule(TY_BYTE, TY_BYTE, TY_BYTE, false), // byte = byte -> byte BinaryOpRule(TY_CHAR, TY_CHAR, TY_CHAR, false), // char = char -> char - BinaryOpRule(TY_STRING, TY_STRING, TY_STRING, false), // string = string -> string BinaryOpRule(TY_BOOL, TY_BOOL, TY_BOOL, false), // bool = bool -> bool }; @@ -412,7 +411,6 @@ const std::vector PLUS_OP_RULES = { BinaryOpRule(TY_LONG, TY_SHORT, TY_LONG, false), // long + short -> long BinaryOpRule(TY_LONG, TY_LONG, TY_LONG, false), // long + long -> long BinaryOpRule(TY_BYTE, TY_BYTE, TY_BYTE, false), // byte + byte -> byte - BinaryOpRule(TY_STRING, TY_STRING, TY_STRING, false), // string + string -> string }; // Minus op rules diff --git a/src/generator/GeneratorVisitor.cpp b/src/generator/GeneratorVisitor.cpp index ebbceb3a9..1462d4747 100644 --- a/src/generator/GeneratorVisitor.cpp +++ b/src/generator/GeneratorVisitor.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -42,7 +41,10 @@ GeneratorVisitor::GeneratorVisitor(const std::shared_ptr &con // Create LLVM base components module = std::make_unique(FileUtil::getFileName(sourceFile.filePath), *context); - conversionsManager = std::make_unique(context, builder); + + // Initialize generator helper objects + stdFunctionManager = std::make_unique(this); + conversionsManager = std::make_unique(this); // Initialize LLVM llvm::InitializeAllTargetInfos(); @@ -286,7 +288,7 @@ std::any GeneratorVisitor::visitMainFctDef(MainFctDefNode *node) { // Restore stack if necessary if (stackState != nullptr) { - builder->CreateCall(retrieveStackRestoreFct(), {stackState}); + builder->CreateCall(stdFunctionManager->getStackRestoreFct(), {stackState}); stackState = nullptr; } @@ -473,7 +475,7 @@ std::any GeneratorVisitor::visitFctDef(FctDefNode *node) { // Restore stack if necessary if (stackState != nullptr) { - builder->CreateCall(retrieveStackRestoreFct(), {stackState}); + builder->CreateCall(stdFunctionManager->getStackRestoreFct(), {stackState}); stackState = nullptr; } @@ -660,7 +662,7 @@ std::any GeneratorVisitor::visitProcDef(ProcDefNode *node) { // Restore stack if necessary if (stackState != nullptr) { - builder->CreateCall(retrieveStackRestoreFct(), {stackState}); + builder->CreateCall(stdFunctionManager->getStackRestoreFct(), {stackState}); stackState = nullptr; } @@ -1300,12 +1302,12 @@ std::any GeneratorVisitor::visitAssertStmt(AssertStmtNode *node) { parentFct->getBasicBlockList().push_back(bThen); moveInsertPointToBlock(bThen); // Generate IR for assertion error - llvm::Function *printfFct = retrievePrintfFct(); + llvm::Function *printfFct = stdFunctionManager->getPrintfFct(); std::string errorMsg = "Assertion failed: Condition '" + node->expressionString + "' evaluated to false."; llvm::Value *templateString = builder->CreateGlobalStringPtr(errorMsg); builder->CreateCall(printfFct, templateString); // Generate call to exit - llvm::Function *exitFct = retrieveExitFct(); + llvm::Function *exitFct = stdFunctionManager->getExitFct(); builder->CreateCall(exitFct, builder->getInt32(1)); // Create unreachable instruction builder->CreateUnreachable(); @@ -1456,7 +1458,7 @@ std::any GeneratorVisitor::visitContinueStmt(ContinueStmtNode *node) { std::any GeneratorVisitor::visitPrintfCall(PrintfCallNode *node) { // Declare if not declared already - llvm::Function *printfFct = retrievePrintfFct(); + llvm::Function *printfFct = stdFunctionManager->getPrintfFct(); std::vector printfArgs; printfArgs.push_back(builder->CreateGlobalStringPtr(node->templatedString)); @@ -1471,6 +1473,9 @@ std::any GeneratorVisitor::visitPrintfCall(PrintfCallNode *node) { if (argSymbolType.isArray()) { // Convert array type to pointer type llvm::Value *indices[2] = {builder->getInt32(0), builder->getInt32(0)}; argVal = builder->CreateInBoundsGEP(targetType, argValPtr, indices); + } else if (argSymbolType.isStringStruct()) { + argValPtr = materializeString(argValPtr); + argVal = builder->CreateLoad(targetType, argValPtr); } else { argVal = builder->CreateLoad(targetType, argValPtr); } @@ -1982,6 +1987,10 @@ std::any GeneratorVisitor::visitAdditiveExpr(AdditiveExprNode *node) { switch (opQueue.front().first) { case AdditiveExprNode::OP_PLUS: + /*if (lhsSymbolType.isStringStruct()) + lhs = materializeString(lhsPtr); + if (rhsSymbolType.isStringStruct()) + rhs = materializeString(rhsPtr);*/ lhs = conversionsManager->getPlusInst(lhs, rhs, lhsSymbolType, rhsSymbolType, currentScope, node->codeLoc); break; case AdditiveExprNode::OP_MINUS: @@ -2976,7 +2985,7 @@ llvm::Value *GeneratorVisitor::insertAlloca(llvm::Type *llvmType, const std::str llvm::Value *GeneratorVisitor::allocateDynamicallySizedArray(llvm::Type *itemType) { // Call llvm.stacksave intrinsic - llvm::Function *stackSaveFct = retrieveStackSaveFct(); + llvm::Function *stackSaveFct = stdFunctionManager->getStackSaveFct(); if (stackState == nullptr) stackState = builder->CreateCall(stackSaveFct); // Allocate array @@ -3043,48 +3052,10 @@ bool GeneratorVisitor::insertDestructorCall(const CodeLoc &codeLoc, SymbolTableE return true; } -llvm::Function *GeneratorVisitor::retrievePrintfFct() { - std::string printfFctName = "printf"; - llvm::Function *printfFct = module->getFunction(printfFctName); - if (printfFct) - return printfFct; - // Not found -> declare it for linkage - llvm::FunctionType *printfFctTy = llvm::FunctionType::get(builder->getInt32Ty(), builder->getInt8PtrTy(), true); - module->getOrInsertFunction(printfFctName, printfFctTy); - return module->getFunction(printfFctName); -} - -llvm::Function *GeneratorVisitor::retrieveExitFct() { - std::string exitFctName = "exit"; - llvm::Function *exitFct = module->getFunction(exitFctName); - if (exitFct) - return exitFct; - // Not found -> declare it for linkage - llvm::FunctionType *exitFctTy = llvm::FunctionType::get(builder->getVoidTy(), builder->getInt32Ty(), false); - module->getOrInsertFunction(exitFctName, exitFctTy); - return module->getFunction(exitFctName); -} - -llvm::Function *GeneratorVisitor::retrieveStackSaveFct() { - std::string stackSaveFctName = "llvm.stacksave"; - llvm::Function *stackSaveFct = module->getFunction(stackSaveFctName); - if (stackSaveFct) - return stackSaveFct; - // Not found -> declare it for linkage - llvm::FunctionType *stackSaveFctTy = llvm::FunctionType::get(builder->getInt8PtrTy(), {}, false); - module->getOrInsertFunction(stackSaveFctName, stackSaveFctTy); - return module->getFunction(stackSaveFctName); -} - -llvm::Function *GeneratorVisitor::retrieveStackRestoreFct() { - std::string stackRestoreFctName = "llvm.stackrestore"; - llvm::Function *stackRestoreFct = module->getFunction(stackRestoreFctName); - if (stackRestoreFct) - return stackRestoreFct; - // Not found -> declare it for linkage - llvm::FunctionType *stackRestoreFctTy = llvm::FunctionType::get(builder->getVoidTy(), builder->getInt8PtrTy(), false); - module->getOrInsertFunction(stackRestoreFctName, stackRestoreFctTy); - return module->getFunction(stackRestoreFctName); +llvm::Value *GeneratorVisitor::materializeString(llvm::Value *stringStructPtr) { + assert(stringStructPtr->getType()->isPointerTy()); + llvm::Value *rawStringValue = builder->CreateCall(stdFunctionManager->getStringRawFct(), stringStructPtr); + return rawStringValue; } llvm::Constant *GeneratorVisitor::getDefaultValueForSymbolType(const SymbolType &symbolType) { diff --git a/src/generator/GeneratorVisitor.h b/src/generator/GeneratorVisitor.h index d86f90635..f1743db94 100644 --- a/src/generator/GeneratorVisitor.h +++ b/src/generator/GeneratorVisitor.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -22,7 +23,6 @@ class ThreadFactory; class LinkerInterface; struct CliOptions; class LinkerInterface; -class OpRuleConversionsManager; class SymbolTable; class SymbolTableEntry; class Function; @@ -43,6 +43,10 @@ class GeneratorVisitor : public AstVisitor { ThreadFactory &threadFactory, const LinkerInterface &linker, const CliOptions &cliOptions, const SourceFile &sourceFile, const std::string &objectFile); + // Friend classes + friend class StdFunctionManager; + friend class OpRuleConversionsManager; + // Public methods void optimize(); void emit(); @@ -101,6 +105,7 @@ class GeneratorVisitor : public AstVisitor { private: // Members + std::unique_ptr stdFunctionManager; std::unique_ptr conversionsManager; const std::string &objectFile; llvm::TargetMachine *targetMachine{}; @@ -163,10 +168,8 @@ class GeneratorVisitor : public AstVisitor { llvm::Value *allocateDynamicallySizedArray(llvm::Type *itemType); llvm::Value *createGlobalArray(llvm::Constant *constArray); bool insertDestructorCall(const CodeLoc &codeLoc, SymbolTableEntry *varEntry); - llvm::Function *retrievePrintfFct(); - llvm::Function *retrieveExitFct(); - llvm::Function *retrieveStackSaveFct(); - llvm::Function *retrieveStackRestoreFct(); + + llvm::Value *materializeString(llvm::Value *stringStruct); llvm::Constant *getDefaultValueForSymbolType(const SymbolType &symbolType); SymbolTableEntry *initExtGlobal(const std::string &globalName, const std::string &fqGlobalName); llvm::Value *doImplicitCast(llvm::Value *src, llvm::Type *dstTy, SymbolType srcType); diff --git a/src/generator/OpRuleConversionsManager.cpp b/src/generator/OpRuleConversionsManager.cpp index 302151033..035be9205 100644 --- a/src/generator/OpRuleConversionsManager.cpp +++ b/src/generator/OpRuleConversionsManager.cpp @@ -5,8 +5,16 @@ #include #include +#include +#include #include +OpRuleConversionsManager::OpRuleConversionsManager(GeneratorVisitor *generator) : generator(generator) { + builder = generator->builder.get(); + context = generator->context.get(); + stdFunctionManager = generator->stdFunctionManager.get(); +} + llvm::Value *OpRuleConversionsManager::getPlusEqualInst(llvm::Value *lhs, llvm::Value *rhs, const SymbolType &lhsSTy, const SymbolType &rhsSTy, SymbolTable *accessScope, const CodeLoc &codeLoc) { @@ -465,9 +473,12 @@ llvm::Value *OpRuleConversionsManager::getEqualInst(llvm::Value *lhs, llvm::Valu case COMB(TY_BYTE, TY_BYTE): // fallthrough case COMB(TY_CHAR, TY_CHAR): return builder->CreateICmpEQ(lhs, rhs); - case COMB(TY_STRING, TY_STRING): - // ToDo(@marcauberer): Insert call to opEquals in the runtime lib - throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '==' operator for lhs=string and rhs=string yet"); + case COMB(TY_STRING, TY_STRING): { + // Generate call to the function isRawEqual(string, string) of the string std + llvm::Function *opFct = stdFunctionManager->getStringLitEqualsOpStringLitFct(); + llvm::Value *result = builder->CreateCall(opFct, {lhs, rhs}); + return result; + } case COMB(TY_BOOL, TY_BOOL): return builder->CreateICmpEQ(lhs, rhs); } @@ -570,9 +581,13 @@ llvm::Value *OpRuleConversionsManager::getNotEqualInst(llvm::Value *lhs, llvm::V case COMB(TY_BYTE, TY_BYTE): // fallthrough case COMB(TY_CHAR, TY_CHAR): return builder->CreateICmpNE(lhs, rhs); - case COMB(TY_STRING, TY_STRING): - // ToDo(@marcauberer): Insert call to opNotEquals in the runtime lib - throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '!=' operator for lhs=string and rhs=string yet"); + case COMB(TY_STRING, TY_STRING): { + // Generate call to the function isRawEqual(string, string) of the string std + llvm::Function *opFct = stdFunctionManager->getStringLitEqualsOpStringLitFct(); + llvm::Value *result = builder->CreateCall(opFct, {lhs, rhs}); + // Negate the result + return builder->CreateNot(result); + } case COMB(TY_BOOL, TY_BOOL): return builder->CreateICmpNE(lhs, rhs); } @@ -943,9 +958,13 @@ llvm::Value *OpRuleConversionsManager::getPlusInst(llvm::Value *lhs, llvm::Value case COMB(TY_BYTE, TY_BYTE): // fallthrough case COMB(TY_CHAR, TY_CHAR): return builder->CreateAdd(lhs, rhs); - case COMB(TY_STRING, TY_STRING): - // ToDo(@marcauberer): Insert call to append in the runtime lib - throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '+' operator for lhs=string and rhs=string yet"); + case COMB(TY_STRING, TY_STRING): { + // Generate call to the constructor ctor(string, string) of the String struct + llvm::Function *opFct = stdFunctionManager->getStringLitPlusOpStringLitFct(); + llvm::Value *thisPtr = generator->insertAlloca(stdFunctionManager->getStringStructType()); + builder->CreateCall(opFct, {thisPtr, lhs, rhs}); + return thisPtr; + } case COMB(TY_PTR, TY_INT): // fallthrough case COMB(TY_PTR, TY_SHORT): // fallthrough case COMB(TY_PTR, TY_LONG): diff --git a/src/generator/OpRuleConversionsManager.h b/src/generator/OpRuleConversionsManager.h index 25c268154..9c3f3933b 100644 --- a/src/generator/OpRuleConversionsManager.h +++ b/src/generator/OpRuleConversionsManager.h @@ -9,15 +9,16 @@ #include // Forward declarations -class ErrorFactory; +class GeneratorVisitor; +class StdFunctionManager; struct CodeLoc; #define COMB(en1, en2) ((en1) | ((en2) << 16)) class OpRuleConversionsManager { public: - explicit OpRuleConversionsManager(const std::shared_ptr &context, std::shared_ptr> builder) - : context(context), builder(std::move(builder)) {} + // Constructors + explicit OpRuleConversionsManager(GeneratorVisitor *generator); // Public methods llvm::Value *getPlusEqualInst(llvm::Value *lhs, llvm::Value *rhs, const SymbolType &lhsTy, const SymbolType &rhsTy, @@ -64,6 +65,8 @@ class OpRuleConversionsManager { private: // Members - const std::shared_ptr &context; - std::shared_ptr> builder; + GeneratorVisitor *generator; + llvm::LLVMContext *context; + llvm::IRBuilder<> *builder; + const StdFunctionManager *stdFunctionManager; }; \ No newline at end of file diff --git a/src/generator/StdFunctionManager.cpp b/src/generator/StdFunctionManager.cpp new file mode 100644 index 000000000..52c5e9624 --- /dev/null +++ b/src/generator/StdFunctionManager.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2021-2022 ChilliBits. All rights reserved. + +#include "StdFunctionManager.h" + +#include + +StdFunctionManager::StdFunctionManager(GeneratorVisitor *generator) : generator(generator) { + context = generator->context.get(); + builder = generator->builder.get(); + module = generator->module.get(); +} + +llvm::StructType *StdFunctionManager::getStringStructType() const { + std::string structName = "_s__String__charptr_long_long"; + llvm::Type *ptrTy = builder->getPtrTy(); + llvm::Type *int64Ty = builder->getInt64Ty(); + return llvm::StructType::create(*context, {ptrTy, int64Ty, int64Ty}, structName); +} + +llvm::Function *StdFunctionManager::getPrintfFct() const { + std::string printfFctName = "printf"; + llvm::Function *printfFct = module->getFunction(printfFctName); + if (printfFct) + return printfFct; + // Not found -> declare it for linkage + llvm::FunctionType *printfFctTy = llvm::FunctionType::get(builder->getInt32Ty(), builder->getInt8PtrTy(), true); + module->getOrInsertFunction(printfFctName, printfFctTy); + return module->getFunction(printfFctName); +} + +llvm::Function *StdFunctionManager::getExitFct() const { + std::string exitFctName = "exit"; + llvm::Function *exitFct = module->getFunction(exitFctName); + if (exitFct) + return exitFct; + // Not found -> declare it for linkage + llvm::FunctionType *exitFctTy = llvm::FunctionType::get(builder->getVoidTy(), builder->getInt32Ty(), false); + module->getOrInsertFunction(exitFctName, exitFctTy); + return module->getFunction(exitFctName); +} + +llvm::Function *StdFunctionManager::getStackSaveFct() const { + std::string stackSaveFctName = "llvm.stacksave"; + llvm::Function *stackSaveFct = module->getFunction(stackSaveFctName); + if (stackSaveFct) + return stackSaveFct; + // Not found -> declare it for linkage + llvm::FunctionType *stackSaveFctTy = llvm::FunctionType::get(builder->getInt8PtrTy(), {}, false); + module->getOrInsertFunction(stackSaveFctName, stackSaveFctTy); + return module->getFunction(stackSaveFctName); +} + +llvm::Function *StdFunctionManager::getStackRestoreFct() const { + std::string stackRestoreFctName = "llvm.stackrestore"; + llvm::Function *stackRestoreFct = module->getFunction(stackRestoreFctName); + if (stackRestoreFct) + return stackRestoreFct; + // Not found -> declare it for linkage + llvm::FunctionType *stackRestoreFctTy = llvm::FunctionType::get(builder->getVoidTy(), builder->getInt8PtrTy(), false); + module->getOrInsertFunction(stackRestoreFctName, stackRestoreFctTy); + return module->getFunction(stackRestoreFctName); +} + +llvm::Function *StdFunctionManager::getStringRawFct() const { + std::string functionName = "_mf__String__getRaw"; + llvm::Function *opFct = module->getFunction(functionName); + if (opFct) + return opFct; + llvm::Type *structTyPtr = getStringStructType()->getPointerTo(); + llvm::Type *stringTy = builder->getPtrTy(); + llvm::FunctionType *opFctTy = llvm::FunctionType::get(stringTy, structTyPtr, false); + module->getOrInsertFunction(functionName, opFctTy); + return module->getFunction(functionName); +} + +llvm::Function *StdFunctionManager::getStringLitPlusOpStringLitFct() const { + std::string functionName = "_mp__String__ctor__string_string"; + llvm::Function *opFct = module->getFunction(functionName); + if (opFct != nullptr) + return opFct; + llvm::Type *ptrTy = builder->getPtrTy(); + llvm::FunctionType *opFctTy = llvm::FunctionType::get(builder->getVoidTy(), {ptrTy, ptrTy, ptrTy}, false); + module->getOrInsertFunction(functionName, opFctTy); + return module->getFunction(functionName); +} + +llvm::Function *StdFunctionManager::getStringLitEqualsOpStringLitFct() const { + std::string functionName = "_mf__isRawEqual__string_string"; + llvm::Function *opFct = module->getFunction(functionName); + if (opFct != nullptr) + return opFct; + llvm::Type *ptrTy = builder->getPtrTy(); + llvm::FunctionType *opFctTy = llvm::FunctionType::get(builder->getInt1Ty(), {ptrTy, ptrTy}, false); + module->getOrInsertFunction(functionName, opFctTy); + return module->getFunction(functionName); +} \ No newline at end of file diff --git a/src/generator/StdFunctionManager.h b/src/generator/StdFunctionManager.h new file mode 100644 index 000000000..bb932d5f2 --- /dev/null +++ b/src/generator/StdFunctionManager.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021-2022 ChilliBits. All rights reserved. + +#pragma once + +#include +#include + +// Forward declarations +class GeneratorVisitor; + +class StdFunctionManager { +public: + // Constructors + explicit StdFunctionManager(GeneratorVisitor* generator); + + // Public methods for type retrieval + llvm::StructType *getStringStructType() const; + + // Public methods for function retrieval + llvm::Function *getPrintfFct() const; + llvm::Function *getExitFct() const; + llvm::Function *getStackSaveFct() const; + llvm::Function *getStackRestoreFct() const; + llvm::Function *getStringRawFct() const; + llvm::Function *getStringLitPlusOpStringLitFct() const; + llvm::Function *getStringLitEqualsOpStringLitFct() const; + +private: + // Members + GeneratorVisitor *generator; + llvm::LLVMContext *context; + llvm::IRBuilder<> *builder; + llvm::Module *module; +}; \ No newline at end of file diff --git a/src/symbol/GenericType.cpp b/src/symbol/GenericType.cpp index 3cb902e33..474486fae 100644 --- a/src/symbol/GenericType.cpp +++ b/src/symbol/GenericType.cpp @@ -5,11 +5,11 @@ GenericType::GenericType(const SymbolType &type) { this->typeChain = type.getTypeChain(); } GenericType::GenericType(const std::string &name, const std::vector &typeConditions) { - this->typeChain.push({TY_GENERIC, name, {}, nullptr}); + this->typeChain.push({TY_GENERIC, name, 0, {}, nullptr}); this->typeConditions = typeConditions; } -GenericType::GenericType(const std::string &name) { this->typeChain.push({TY_GENERIC, name, {}, nullptr}); } +GenericType::GenericType(const std::string &name) { this->typeChain.push({TY_GENERIC, name, 0, {}, nullptr}); } /** * Checks if the given symbol type matches all conditions to get a manifestation of the current generic type diff --git a/src/symbol/Struct.cpp b/src/symbol/Struct.cpp index b5a673e42..3c85169e6 100644 --- a/src/symbol/Struct.cpp +++ b/src/symbol/Struct.cpp @@ -89,7 +89,7 @@ SymbolType Struct::getSymbolType() const { for (const auto &templateType : templateTypes) concreteTemplateTypes.push_back(templateType); } - return SymbolType(TY_STRUCT, name, concreteTemplateTypes); + return SymbolType(TY_STRUCT, name, { .arraySize = 0 }, concreteTemplateTypes); } /** diff --git a/src/symbol/SymbolTable.cpp b/src/symbol/SymbolTable.cpp index 181a0a59d..cc719166f 100644 --- a/src/symbol/SymbolTable.cpp +++ b/src/symbol/SymbolTable.cpp @@ -354,6 +354,7 @@ void SymbolTable::insertFunction(const Function &function) { * @param callArgTypes Argument types requirement * @param err Error Factory * @param codeLoc Declaration code location for the error message + * * @return Matched function or nullptr */ Function *SymbolTable::matchFunction(SymbolTable *currentScope, const std::string &callFunctionName, diff --git a/src/symbol/SymbolTableEntry.cpp b/src/symbol/SymbolTableEntry.cpp index 295ae7bef..4b1def72a 100644 --- a/src/symbol/SymbolTableEntry.cpp +++ b/src/symbol/SymbolTableEntry.cpp @@ -26,9 +26,13 @@ const SymbolType &SymbolTableEntry::getType() const { return type; } * @param newType New type of the current symbol */ void SymbolTableEntry::updateType(const SymbolType &newType, bool force) { - if (!force && type != SymbolType(TY_DYN)) // GCOV_EXCL_LINE + if (force || type.is(TY_DYN)) { + type = newType; + } else if (type.isBaseType(TY_STRING) && newType.is(TY_STRING)) { + type = type.replaceBaseType(newType); + } else { throw std::runtime_error("Internal compiler error: Cannot change type of non-dyn"); // GCOV_EXCL_LINE - type = newType; + } } /** diff --git a/src/symbol/SymbolType.cpp b/src/symbol/SymbolType.cpp index 2da199ff1..06ed24db8 100644 --- a/src/symbol/SymbolType.cpp +++ b/src/symbol/SymbolType.cpp @@ -28,7 +28,7 @@ SymbolType SymbolType::toPointer(const CodeLoc &codeLoc, llvm::Value *dynamicSiz throw SemanticError(codeLoc, DYN_POINTERS_NOT_ALLOWED, "Just use the dyn type without '*' instead"); TypeChain newTypeChain = typeChain; - newTypeChain.push({TY_PTR, "", {}, dynamicSize}); + newTypeChain.push({TY_PTR, "", 0, {}, dynamicSize}); return SymbolType(newTypeChain); } @@ -43,7 +43,7 @@ SymbolType SymbolType::toArray(const CodeLoc &codeLoc, int size) const { throw SemanticError(codeLoc, DYN_ARRAYS_NOT_ALLOWED, "Just use the dyn type without '[]' instead"); TypeChain newTypeChain = typeChain; - newTypeChain.push({TY_ARRAY, std::to_string(size), {}, nullptr}); + newTypeChain.push({TY_ARRAY, "", {.arraySize = size}, {}, nullptr}); return SymbolType(newTypeChain); } @@ -124,8 +124,16 @@ llvm::Type *SymbolType::toLLVMType(llvm::LLVMContext &context, SymbolTable *acce if (isOneOf({TY_CHAR, TY_BYTE})) return llvm::Type::getInt8Ty(context); - if (is(TY_STRING)) - return llvm::Type::getInt8PtrTy(context); + if (is(TY_STRING)) { + if (typeChain.top().data.isStringStruct) { + std::string structName = "_s__String__charptr_long_long"; + llvm::Type *ptrTy = llvm::PointerType::get(context, 0); + llvm::Type *int64Ty = llvm::Type::getInt64Ty(context); + return llvm::StructType::create(context, {ptrTy, int64Ty, int64Ty}, structName); + } else { + return llvm::Type::getInt8PtrTy(context); + } + } if (is(TY_BOOL)) return llvm::Type::getInt1Ty(context); @@ -251,6 +259,16 @@ bool SymbolType::isOneOf(const std::vector &superTypes) const { return std::any_of(superTypes.begin(), superTypes.end(), [&superType](int type) { return type == superType; }); } +/** + * Check if the current type is a string object type + * + * @return String struct or not + */ +bool SymbolType::isStringStruct() const { + SymbolSuperType superType = getSuperType(); + return superType == TY_STRING && typeChain.top().data.isStringStruct; +} + /** * Retrieve the super type of the current type * @@ -337,20 +355,7 @@ int SymbolType::getArraySize() const { if (typeChain.top().superType != TY_ARRAY) // GCOV_EXCL_LINE throw std::runtime_error("Internal compiler error: Cannot get size of non-array type"); // GCOV_EXCL_LINE - return std::stoi(typeChain.top().subType); -} - -/** - * Set the dynamic array size of the current type - */ -SymbolType SymbolType::setDynamicArraySize(llvm::Value *dynamicArraySize) const { - if (typeChain.top().superType != TY_PTR) // GCOV_EXCL_LINE - throw std::runtime_error("Internal compiler error: Cannot get size of non-array type"); // GCOV_EXCL_LINE - - TypeChain newTypeChain = typeChain; - newTypeChain.pop(); - newTypeChain.push({typeChain.top().superType, typeChain.top().subType, typeChain.top().templateTypes, dynamicArraySize}); - return SymbolType(newTypeChain); + return typeChain.top().data.arraySize; } /** @@ -361,10 +366,16 @@ SymbolType SymbolType::setDynamicArraySize(llvm::Value *dynamicArraySize) const llvm::Value *SymbolType::getDynamicArraySize() const { if (typeChain.top().superType != TY_PTR) // GCOV_EXCL_LINE throw std::runtime_error("Internal compiler error: Cannot get dynamic sized of non-array type"); // GCOV_EXCL_LINE + if (typeChain.top().data.arraySize > 0) // GCOV_EXCL_LINE + throw std::runtime_error("Cannot retrieve dynamic size of non-dynamically-sized array"); // GCOV_EXCL_LINE return typeChain.top().dynamicArraySize; } +bool operator==(const SymbolType &lhs, const SymbolType &rhs) { return lhs.typeChain == rhs.typeChain; } + +bool operator!=(const SymbolType &lhs, const SymbolType &rhs) { return lhs.typeChain != rhs.typeChain; } + /** * Compares the type chains of two symbol types without taking array sizes into account * @@ -379,10 +390,8 @@ bool equalsIgnoreArraySizes(SymbolType lhs, SymbolType rhs) { // Compare stack elements for (int i = 0; i < lhs.typeChain.size(); i++) { - if ((lhs.typeChain.top().superType != TY_ARRAY || rhs.typeChain.top().superType != TY_ARRAY) && - lhs.typeChain.top() != rhs.typeChain.top()) { + if (!equalsIgnoreArraySize(lhs.typeChain.top(), rhs.typeChain.top())) return false; - } lhs.typeChain.pop(); rhs.typeChain.pop(); } @@ -390,10 +399,6 @@ bool equalsIgnoreArraySizes(SymbolType lhs, SymbolType rhs) { return true; } -bool operator==(const SymbolType &lhs, const SymbolType &rhs) { return lhs.typeChain == rhs.typeChain; } - -bool operator!=(const SymbolType &lhs, const SymbolType &rhs) { return lhs.typeChain != rhs.typeChain; } - /** * Set the sub type of the top element * @@ -417,7 +422,7 @@ std::string SymbolType::getNameFromChainElement(const TypeChainElement &chainEle return "array"; if (!withSize || chainElement.subType == "0") return "[]"; - return chainElement.subType == "-1" ? "[size]" : "[" + chainElement.subType + "]"; + return chainElement.subType == "-1" ? "[size]" : "[" + std::to_string(chainElement.data.arraySize) + "]"; } case TY_DOUBLE: return "double"; diff --git a/src/symbol/SymbolType.h b/src/symbol/SymbolType.h index 5ae03f65e..d36cba066 100644 --- a/src/symbol/SymbolType.h +++ b/src/symbol/SymbolType.h @@ -38,18 +38,38 @@ enum SymbolSuperType { TY_IMPORT }; +union TypeChainElementData { + bool isStringStruct; // TY_STRING + int arraySize; // TY_ARRAY +}; + class SymbolType { public: // Structs struct TypeChainElement { SymbolSuperType superType; std::string subType; + TypeChainElementData data; std::vector templateTypes; llvm::Value *dynamicArraySize; friend bool operator==(const TypeChainElement &lhs, const TypeChainElement &rhs) { - return lhs.superType == rhs.superType && lhs.subType == rhs.subType && lhs.templateTypes == rhs.templateTypes && - lhs.dynamicArraySize == rhs.dynamicArraySize; + // Check super type, subtype and template types + if (!equalsIgnoreArraySize(lhs, rhs)) + return false; + // Check data + switch (lhs.superType) { + case TY_STRING: + return lhs.data.isStringStruct == rhs.data.isStringStruct; + case TY_ARRAY: + return lhs.data.arraySize == rhs.data.arraySize; + default: + return true; + } + } + friend bool equalsIgnoreArraySize(const TypeChainElement &lhs, const TypeChainElement &rhs) { + // Check super type, subtype and template types + return lhs.superType == rhs.superType && lhs.subType == rhs.subType && lhs.templateTypes == rhs.templateTypes; } }; @@ -57,10 +77,12 @@ class SymbolType { typedef std::stack TypeChain; // Constructors - explicit SymbolType(SymbolSuperType superType) : typeChain({{superType, "", {}, nullptr}}) {} - explicit SymbolType(SymbolSuperType superType, const std::string &subType) : typeChain({{superType, subType, {}, nullptr}}) {} - explicit SymbolType(SymbolSuperType superType, const std::string &subType, const std::vector &templateTypes) - : typeChain({{superType, subType, templateTypes, nullptr}}) {} + explicit SymbolType(SymbolSuperType superType) : typeChain({{superType, "", {.arraySize = 0}, {}, nullptr}}) {} + explicit SymbolType(SymbolSuperType superType, const std::string &subType) + : typeChain({{superType, subType, {.arraySize = 0}, {}, nullptr}}) {} + explicit SymbolType(SymbolSuperType superType, const std::string &subType, const TypeChainElementData &data, + const std::vector &templateTypes) + : typeChain({{superType, subType, data, templateTypes, nullptr}}) {} explicit SymbolType(TypeChain types) : typeChain(std::move(types)) {} SymbolType() = default; virtual ~SymbolType() = default; @@ -83,6 +105,7 @@ class SymbolType { [[nodiscard]] bool isPrimitive() const; [[nodiscard]] bool isBaseType(SymbolSuperType superType) const; [[nodiscard]] bool isOneOf(const std::vector &superTypes) const; + [[nodiscard]] bool isStringStruct() const; [[nodiscard]] SymbolSuperType getSuperType() const; [[nodiscard]] std::string getSubType() const; [[nodiscard]] SymbolType getBaseType() const; @@ -92,7 +115,6 @@ class SymbolType { [[nodiscard]] bool isSigned() const; [[nodiscard]] std::string getName(bool withSize = false, bool mangledName = false) const; [[nodiscard]] int getArraySize() const; - [[nodiscard]] SymbolType setDynamicArraySize(llvm::Value *dynamicArraySize) const; [[nodiscard]] llvm::Value *getDynamicArraySize() const; friend bool equalsIgnoreArraySizes(SymbolType lhs, SymbolType rhs); friend bool operator==(const SymbolType &lhs, const SymbolType &rhs); diff --git a/std/runtime/string_rt.spice b/std/runtime/string_rt.spice index 4fe0d51ca..6f1f63b01 100644 --- a/std/runtime/string_rt.spice +++ b/std/runtime/string_rt.spice @@ -40,6 +40,28 @@ public p String.ctor(const string value = "") { } } +public p String.ctor(const string value1, const string value2) { + unsigned long value1Length = str.getRawLength(value1); + unsigned long value2Length = str.getRawLength(value2); + this.length = value1Length + value2Length; + this.capacity = this.length > INITIAL_ALLOC_COUNT ? this.length * RESIZE_FACTOR : INITIAL_ALLOC_COUNT; // +1 because of null terminator + + // Allocate space for the initial number of elements + unsafe { + this.contents = (char*) malloc(sizeof(type char) * this.capacity); + } + + // Save initial value + unsafe { + for unsigned long i; i < value1Length; i++ { + this.contents[i] = value1[i]; + } + for unsigned long i; i < value2Length + 1; i++ { // +1 because of null terminator + this.contents[value1Length + i] = value2[i]; + } + } +} + public p String.ctor(const char value) { this.length = 1l; this.capacity = INITIAL_ALLOC_COUNT; @@ -172,17 +194,6 @@ public f String.opEquals(const string operand) { return true; } -/** - * Implements the equals operator for 'string != string' - * - * @param operand String to comare the current string to - * - * @return Equal or not - */ -public inline f String.opNotEquals(const String operand) { - return !this.opEquals(operand.getRaw()); -} - /** * Implements the equals operator for 'string != string' * diff --git a/std/time/time_linux.spice b/std/time/time_linux.spice new file mode 100644 index 000000000..884f7e428 --- /dev/null +++ b/std/time/time_linux.spice @@ -0,0 +1,42 @@ +// Constants +public const int TIMEZONE_UTC = 0; + +// Structs +type TimeVal struct { + unsigned long tvSec + unsigned long tvUsec +} +type TimeZone struct { + int tzMinutewest + int tzDsttime +} + +// Link external functions +ext gettimeofday(TimeVal*, TimeZone*); + +/** + * Retrieve seconds since epoch + */ +public f getCurrentSecs() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return time.tvSec * 1000l; +} + +/** + * Retrieve milliseconds since epoch + */ +public f getCurrentMillis() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return time.tvSec * 1000l + time.tvUsec / 1000l; +} + +/** + * Retrieve microseconds since epoch + */ +public f getCurrentMicros() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return 1000000l * time.tvSec + time.tvUsec; +} \ No newline at end of file diff --git a/std/time/time_windows.spice b/std/time/time_windows.spice new file mode 100644 index 000000000..a3fbeb43c --- /dev/null +++ b/std/time/time_windows.spice @@ -0,0 +1,42 @@ +// Constants +public const int TIMEZONE_UTC = 0; + +// Structs +type TimeVal struct { + unsigned int tvSec + unsigned int tvUsec +} +type TimeZone struct { + int tzMinutewest + int tzDsttime +} + +// Link external functions +ext gettimeofday(TimeVal*, TimeZone*); + +/** + * Retrieve seconds since epoch + */ +public f getCurrentSecs() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return time.tvSec * 1000l; +} + +/** + * Retrieve milliseconds since epoch + */ +public f getCurrentMillis() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return time.tvSec * 1000l + time.tvUsec / 1000l; +} + +/** + * Retrieve microseconds since epoch + */ +public f getCurrentMicros() { + TimeVal time = TimeVal{}; + gettimeofday(&time, nil); + return 1000000l * time.tvSec + time.tvUsec; +} \ No newline at end of file diff --git a/std/type/string.spice b/std/type/string.spice index 146b3d8f4..4fdfb9e71 100644 --- a/std/type/string.spice +++ b/std/type/string.spice @@ -73,4 +73,19 @@ public f getRawLength(string input) { while input[result] != '\0' { result++; } -} \ No newline at end of file +} + +// Returns the equality of two strings +public f isRawEqual(string lhs, string rhs) { + unsigned long lhsLength = getRawLength(lhs); + unsigned long rhsLength = getRawLength(rhs); + // Return false immediately if length does not match + if lhsLength != rhsLength { return false; } + // Compare chars + for int i = 0; i < lhsLength; i++ { + if lhs[i] != rhs[i] { + return false; + } + } + return true; +} diff --git a/test/test-files/generator/pointers/success-pointer/ir-code.ll b/test/test-files/generator/pointers/success-pointer/ir-code.ll index 9b8855b82..a04877078 100644 --- a/test/test-files/generator/pointers/success-pointer/ir-code.ll +++ b/test/test-files/generator/pointers/success-pointer/ir-code.ll @@ -4,10 +4,10 @@ target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16 target triple = "x86_64-w64-windows-gnu" @0 = private unnamed_addr constant [6 x i8] c"Pizza\00", align 1 -@1 = private unnamed_addr constant [31 x i8] c"Pointer address: %p, value: %s\00", align 1 +@1 = private unnamed_addr constant [32 x i8] c"Pointer address: %p, value: %s\0A\00", align 1 @2 = private unnamed_addr constant [7 x i8] c"Burger\00", align 1 -@3 = private unnamed_addr constant [19 x i8] c"Restored value: %s\00", align 1 -@4 = private unnamed_addr constant [28 x i8] c"Restored value address: %p\0A\00", align 1 +@3 = private unnamed_addr constant [20 x i8] c"Restored value: %s\0A\00", align 1 +@4 = private unnamed_addr constant [27 x i8] c"Restored value address: %p\00", align 1 define i32 @main() { entry.l1: diff --git a/test/test-files/generator/pointers/success-pointer/source.spice b/test/test-files/generator/pointers/success-pointer/source.spice index 3bb34004a..579417184 100644 --- a/test/test-files/generator/pointers/success-pointer/source.spice +++ b/test/test-files/generator/pointers/success-pointer/source.spice @@ -2,12 +2,12 @@ f main() { string food = "Pizza"; string* ptr = &food; - printf("Pointer address: %p, value: %s", ptr, *ptr); + printf("Pointer address: %p, value: %s\n", ptr, *ptr); *ptr = "Burger"; dyn restoredFood = *ptr; - printf("Restored value: %s", restoredFood); + printf("Restored value: %s\n", restoredFood); - printf("Restored value address: %p\n", &restoredFood); + printf("Restored value address: %p", &restoredFood); } \ No newline at end of file diff --git a/test/test-files/std/runtime/string-operators/cout.out b/test/test-files/std/runtime/string-operators/cout.out new file mode 100644 index 000000000..6ec815797 --- /dev/null +++ b/test/test-files/std/runtime/string-operators/cout.out @@ -0,0 +1,6 @@ +String: Hello World! +String: Hello World! +String: 0 +String: 0 +String: 1 +String: 1 diff --git a/test/test-files/std/runtime/string-operators/ir-code-O2.ll b/test/test-files/std/runtime/string-operators/ir-code-O2.ll new file mode 100644 index 000000000..34978c969 --- /dev/null +++ b/test/test-files/std/runtime/string-operators/ir-code-O2.ll @@ -0,0 +1,64 @@ +; ModuleID = 'source.spice' +source_filename = "source.spice" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-w64-windows-gnu" + +%_s__String__charptr_long_long.0 = type { ptr, i64, i64 } +%_s__String__charptr_long_long.1 = type { ptr, i64, i64 } +%_s__String__charptr_long_long.4 = type { ptr, i64, i64 } +%_s__String__charptr_long_long.5 = type { ptr, i64, i64 } + +@0 = private unnamed_addr constant [7 x i8] c"Hello \00", align 1 +@1 = private unnamed_addr constant [7 x i8] c"World!\00", align 1 +@2 = private unnamed_addr constant [12 x i8] c"String: %s\0A\00", align 1 +@3 = private unnamed_addr constant [13 x i8] c"Hello World!\00", align 1 +@4 = private unnamed_addr constant [19 x i8] c"Hello Programmers!\00", align 1 +@5 = private unnamed_addr constant [12 x i8] c"String: %d\0A\00", align 1 +@6 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1 +@7 = private unnamed_addr constant [6 x i8] c"Hell2\00", align 1 + +define i32 @main() local_unnamed_addr { +entry.l3: + %0 = alloca %_s__String__charptr_long_long.0, align 8 + call void @_mp__String__ctor__string_string(ptr nonnull %0, ptr nonnull @0, ptr nonnull @1) + %1 = alloca ptr, align 8 + store ptr %0, ptr %1, align 8 + %2 = call ptr @_mf__String__getRaw(ptr nonnull %1) + %.unpack = load ptr, ptr %2, align 8 + %3 = insertvalue %_s__String__charptr_long_long.1 undef, ptr %.unpack, 0 + %.elt4 = getelementptr inbounds %_s__String__charptr_long_long.1, ptr %2, i64 0, i32 1 + %.unpack5 = load i64, ptr %.elt4, align 8 + %4 = insertvalue %_s__String__charptr_long_long.1 %3, i64 %.unpack5, 1 + %.elt6 = getelementptr inbounds %_s__String__charptr_long_long.1, ptr %2, i64 0, i32 2 + %.unpack7 = load i64, ptr %.elt6, align 8 + %5 = insertvalue %_s__String__charptr_long_long.1 %4, i64 %.unpack7, 2 + %6 = call i32 (ptr, ...) @printf(ptr nonnull @2, %_s__String__charptr_long_long.1 %5) + %7 = alloca %_s__String__charptr_long_long.4, align 8 + call void @_mp__String__ctor__string_string(ptr nonnull %7, ptr nonnull @0, ptr nonnull @1) + %8 = alloca ptr, align 8 + store ptr %7, ptr %8, align 8 + %9 = call ptr @_mf__String__getRaw(ptr nonnull %8) + %.unpack8 = load ptr, ptr %9, align 8 + %10 = insertvalue %_s__String__charptr_long_long.5 undef, ptr %.unpack8, 0 + %.elt9 = getelementptr inbounds %_s__String__charptr_long_long.5, ptr %9, i64 0, i32 1 + %.unpack10 = load i64, ptr %.elt9, align 8 + %11 = insertvalue %_s__String__charptr_long_long.5 %10, i64 %.unpack10, 1 + %.elt11 = getelementptr inbounds %_s__String__charptr_long_long.5, ptr %9, i64 0, i32 2 + %.unpack12 = load i64, ptr %.elt11, align 8 + %12 = insertvalue %_s__String__charptr_long_long.5 %11, i64 %.unpack12, 2 + %13 = call i32 (ptr, ...) @printf(ptr nonnull @2, %_s__String__charptr_long_long.5 %12) + %14 = call i32 (ptr, ...) @printf(ptr nonnull @5, i32 zext (i1 icmp eq (ptr @3, ptr @4) to i32)) + %15 = call i32 (ptr, ...) @printf(ptr nonnull @5, i32 zext (i1 icmp eq (ptr @6, ptr @7) to i32)) + %16 = call i32 (ptr, ...) @printf(ptr nonnull @5, i32 zext (i1 icmp ne (ptr @3, ptr @4) to i32)) + %17 = call i32 (ptr, ...) @printf(ptr nonnull @5, i32 zext (i1 icmp ne (ptr @6, ptr @7) to i32)) + ret i32 0 +} + +; Function Attrs: nofree nounwind +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) local_unnamed_addr #0 + +declare void @_mp__String__ctor__string_string(ptr, ptr, ptr) local_unnamed_addr + +declare ptr @_mf__String__getRaw(ptr) local_unnamed_addr + +attributes #0 = { nofree nounwind } diff --git a/test/test-files/std/runtime/string-operators/source.spice b/test/test-files/std/runtime/string-operators/source.spice new file mode 100644 index 000000000..a2f124763 --- /dev/null +++ b/test/test-files/std/runtime/string-operators/source.spice @@ -0,0 +1,14 @@ +import "std/runtime/string_rt" as _rt_str; + +f main() { + // Plus + printf("String: %s\n", "Hello " + "World!"); + string s = "Hello " + "World!"; + printf("String: %s\n", s); + // Equals + printf("String: %d\n", "Hello World!" == "Hello Programmers!"); + printf("String: %d\n", "Hello" == "Hell2"); + // Not equals + printf("String: %d\n", "Hello World!" != "Hello Programmers!"); + printf("String: %d\n", "Hello" != "Hell2"); +} \ No newline at end of file