From da2884482413e9eee0a16d10d4ce87f4fce5767a Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 11 Aug 2024 05:25:31 +0200 Subject: [PATCH] Add support for syscall builtin --- .run/spice run.run.xml | 2 +- CMakeLists.txt | 9 +++-- docs/docs/language/builtins.md | 20 +++++++++- media/test-project/test.spice | 49 +++++------------------ src/CMakeLists.txt | 4 +- src/Spice.g4 | 4 +- src/ast/ASTBuilder.cpp | 9 +++++ src/ast/ASTBuilder.h | 1 + src/ast/ASTNodes.h | 16 ++++++++ src/ast/ASTVisitor.cpp | 2 + src/ast/ASTVisitor.h | 1 + src/ast/AbstractASTVisitor.h | 2 + src/ast/ParallelizableASTVisitor.cpp | 2 + src/ast/ParallelizableASTVisitor.h | 2 + src/exception/SemanticError.cpp | 6 +++ src/exception/SemanticError.h | 3 ++ src/global/GlobalResourceManager.cpp | 2 + src/global/RuntimeModuleManager.h | 1 + src/irgenerator/GenBuiltinFunctions.cpp | 36 +++++++++++++++++ src/irgenerator/GenExpressions.cpp | 2 + src/irgenerator/GenTargetDependent.cpp | 53 +++++++++++++++++++++++++ src/irgenerator/IRGenerator.h | 5 +++ src/typechecker/TypeChecker.cpp | 31 ++++++++++++++- src/typechecker/TypeChecker.h | 11 ++++- src/visualizer/ASTVisualizer.h | 1 + src/visualizer/CSTVisualizer.h | 1 + std/os/syscall_linux_amd64.spice | 4 -- std/os/syscall_linux_x86_64.spice | 18 +++++++++ 28 files changed, 244 insertions(+), 53 deletions(-) create mode 100644 src/irgenerator/GenTargetDependent.cpp delete mode 100644 std/os/syscall_linux_amd64.spice create mode 100644 std/os/syscall_linux_x86_64.spice diff --git a/.run/spice run.run.xml b/.run/spice run.run.xml index f1152f57e..231eb76aa 100644 --- a/.run/spice run.run.xml +++ b/.run/spice run.run.xml @@ -1,5 +1,5 @@ - + diff --git a/CMakeLists.txt b/CMakeLists.txt index e7e990280..73fddc890 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,12 @@ separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS_LIST}) # Map LLVM components to lib names -llvm_map_components_to_libnames(LLVM_LIBS aarch64codegen amdgpucodegen armcodegen avrcodegen bpfcodegen hexagoncodegen - lanaicodegen loongarchcodegen mcjit mipscodegen msp430codegen nativecodegen nvptxcodegen powerpccodegen riscvcodegen - sparccodegen systemzcodegen target vecodegen webassemblycodegen x86codegen xcorecodegen) +llvm_map_components_to_libnames(LLVM_LIBS aarch64asmparser aarch64codegen amdgpuasmparser amdgpucodegen armasmparser + armcodegen avrasmparser avrcodegen bpfasmparser bpfcodegen hexagonasmparser hexagoncodegen lanaiasmparser + lanaicodegen loongarchasmparser loongarchcodegen mcjit mipsasmparser mipscodegen msp430asmparser msp430codegen + nativecodegen nvptxcodegen powerpcasmparser powerpccodegen riscvasmparser riscvcodegen sparcasmparser + sparccodegen systemzasmparser systemzcodegen target veasmparser vecodegen webassemblyasmparser + webassemblycodegen x86asmparser x86codegen xcorecodegen) # Coverage file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/coverage.bat ${CMAKE_CURRENT_BINARY_DIR}/coverage.bat SYMBOLIC) diff --git a/docs/docs/language/builtins.md b/docs/docs/language/builtins.md index e3e12d4f2..ee5ee78e9 100644 --- a/docs/docs/language/builtins.md +++ b/docs/docs/language/builtins.md @@ -112,4 +112,22 @@ Panic is used to terminate the program with an error message. ### Usage example ```spice panic(Error("This is an error message"))); -``` \ No newline at end of file +``` + +## The `syscall` builtin +Syscall is used to send a specific syscall to the underlying operating system. +Up to six arguments in addition to the syscall number are allowed. + +### Signature +`long syscall(unsigned short syscallNumber, ...args)` + +### Usage example +```spice +// Use write syscall to print "Hello World!" +const string str = "Hello World!"; +syscall(/* syscall no = write */ 1s, /*fd = stdout*/ 1, /* buffer */ str, len(str)); +``` + +!!! warning + It is not recommended to use the syscall builtin directly. There is a std package for interacting via + system calls with the OS. Please use `import "std/os/syscall";` instead. diff --git a/media/test-project/test.spice b/media/test-project/test.spice index 6f3637e56..975954480 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,45 +1,16 @@ -import "std/data/unordered-map"; -import "std/data/pair"; +import "std/os/syscall"; f main() { - UnorderedMap unorderedMap; + string str = "Hello World!\n"; + syscallWrite(FileDescriptor::STDOUT, str); - // Iterate over empty container - { - foreach Pair item : unorderedMap { - printf("%d: %s\n", item.getFirst(), item.getSecond()); - } + /*heap string name = nil; + unsafe { + name = (heap string) sAllocUnsafe(20l); + syscallRead(FileDescriptor::STDIN, name, 4l); } - unorderedMap.upsert(1, String("1")); - unorderedMap.upsert(2, String("2")); - unorderedMap.upsert(3, String("3")); - unorderedMap.upsert(4, String("4")); - unorderedMap.upsert(5, String("5")); - unorderedMap.upsert(99, String("99")); - unorderedMap.upsert(100, String("100")); - unorderedMap.upsert(1265, String("1265")); - unorderedMap.upsert(101, String("101")); - unorderedMap.upsert(102, String("102")); - - // Iterate over filled container - foreach Pair item : unorderedMap { - printf("%d: %s\n", item.getFirst(), item.getSecond()); - } -} - - -/*f greatestCommonDivisor(int a, int b) { - while b != 0 { - int temp = b; - b = a % b; - a = temp; - } - return a; + String nameStr = String("Hello "); + unsafe { nameStr += (string) name; } + syscallWrite(FileDescriptor::STDOUT, nameStr.getRaw());*/ } - -f main() { - int a = 56; - int gcd = greatestCommonDivisor(a, 98); - printf("GCD of %d and %d is %d.", a, 98, gcd); -}*/ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2763250d9..f80c22ce3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ set(SOURCES irgenerator/GenExpressions.cpp irgenerator/GenValues.cpp irgenerator/GenImplicit.cpp + irgenerator/GenTargetDependent.cpp irgenerator/GenVTable.cpp irgenerator/StdFunctionManager.cpp irgenerator/StdFunctionManager.h @@ -159,7 +160,8 @@ set(SOURCES util/RawStringOStream.h ) -add_executable(spice ${SOURCES} ${ANTLR_Spice_CXX_OUTPUTS}) +add_executable(spice ${SOURCES} ${ANTLR_Spice_CXX_OUTPUTS} + irgenerator/GenTargetDependent.cpp) # Enable pedantic warnings target_compile_options(spice PRIVATE -Wpedantic -Wall -Wno-unknown-pragmas) diff --git a/src/Spice.g4 b/src/Spice.g4 index 9ab871aea..7d23ee43b 100644 --- a/src/Spice.g4 +++ b/src/Spice.g4 @@ -59,12 +59,13 @@ fallthroughStmt: FALLTHROUGH; assertStmt: ASSERT assignExpr SEMICOLON; // Builtin functions -builtinCall: printfCall | sizeOfCall | alignOfCall | lenCall | panicCall; +builtinCall: printfCall | sizeOfCall | alignOfCall | lenCall | panicCall | sysCall; printfCall: PRINTF LPAREN STRING_LIT (COMMA assignExpr)* RPAREN; sizeOfCall: SIZEOF LPAREN (assignExpr | TYPE dataType) RPAREN; alignOfCall: ALIGNOF LPAREN (assignExpr | TYPE dataType) RPAREN; lenCall: LEN LPAREN assignExpr RPAREN; panicCall: PANIC LPAREN assignExpr RPAREN; +sysCall: SYSCALL LPAREN assignExpr (COMMA assignExpr)* RPAREN; // Expression loop assignExpr: prefixUnaryExpr assignOp assignExpr | ternaryExpr; @@ -153,6 +154,7 @@ SIZEOF: 'sizeof'; ALIGNOF: 'alignof'; LEN: 'len'; PANIC: 'panic'; +SYSCALL: 'syscall'; EXT: 'ext'; TRUE: 'true'; FALSE: 'false'; diff --git a/src/ast/ASTBuilder.cpp b/src/ast/ASTBuilder.cpp index f071585cd..11592e308 100644 --- a/src/ast/ASTBuilder.cpp +++ b/src/ast/ASTBuilder.cpp @@ -766,6 +766,15 @@ std::any ASTBuilder::visitPanicCall(SpiceParser::PanicCallContext *ctx) { return concludeNode(panicCallNode); } +std::any ASTBuilder::visitSysCall(SpiceParser::SysCallContext *ctx) { + const auto sysCallNode = createNode(ctx); + + // Visit children + visitChildren(ctx); + + return concludeNode(sysCallNode); +} + std::any ASTBuilder::visitAssignExpr(SpiceParser::AssignExprContext *ctx) { const auto assignExprNode = createNode(ctx); diff --git a/src/ast/ASTBuilder.h b/src/ast/ASTBuilder.h index f108a528b..ec6a56fb2 100644 --- a/src/ast/ASTBuilder.h +++ b/src/ast/ASTBuilder.h @@ -93,6 +93,7 @@ class ASTBuilder final : CompilerPass, public SpiceVisitor { std::any visitAlignOfCall(SpiceParser::AlignOfCallContext *ctx) override; std::any visitLenCall(SpiceParser::LenCallContext *ctx) override; std::any visitPanicCall(SpiceParser::PanicCallContext *ctx) override; + std::any visitSysCall(SpiceParser::SysCallContext *ctx) override; std::any visitAssignExpr(SpiceParser::AssignExprContext *ctx) override; std::any visitTernaryExpr(SpiceParser::TernaryExprContext *ctx) override; std::any visitLogicalOrExpr(SpiceParser::LogicalOrExprContext *ctx) override; diff --git a/src/ast/ASTNodes.h b/src/ast/ASTNodes.h index ee56f5ee4..91f9dd1d8 100644 --- a/src/ast/ASTNodes.h +++ b/src/ast/ASTNodes.h @@ -1387,6 +1387,21 @@ class PanicCallNode final : public ExprNode { [[nodiscard]] bool returnsOnAllControlPaths(bool *) const override { return true; } }; +// ========================================================= SysCallNode ========================================================= + +class SysCallNode final : public ExprNode { +public: + // Constructors + using ExprNode::ExprNode; + + // Visitor methods + std::any accept(AbstractASTVisitor *visitor) override { return visitor->visitSysCall(this); } + std::any accept(ParallelizableASTVisitor *visitor) const override { return visitor->visitSysCall(this); } + + // Public get methods + [[nodiscard]] std::vector assignExprs() const { return getChildren(); } +}; + // ======================================================= AssignExprNode ======================================================== class AssignExprNode final : public ExprNode { @@ -1838,6 +1853,7 @@ class AtomicExprNode final : public ExprNode { [[nodiscard]] AlignofCallNode *alignofCall() const { return getChild(); } [[nodiscard]] LenCallNode *lenCall() const { return getChild(); } [[nodiscard]] PanicCallNode *panicCall() const { return getChild(); } + [[nodiscard]] SysCallNode *sysCall() const { return getChild(); } // Other methods void customItemsInitialization(size_t manifestationCount) override { data.resize(manifestationCount); } diff --git a/src/ast/ASTVisitor.cpp b/src/ast/ASTVisitor.cpp index ea2d9bd86..35ec229c7 100644 --- a/src/ast/ASTVisitor.cpp +++ b/src/ast/ASTVisitor.cpp @@ -110,6 +110,8 @@ std::any ASTVisitor::visitLenCall(LenCallNode *node) { return visitChildren(node std::any ASTVisitor::visitPanicCall(PanicCallNode *node) { return visitChildren(node); } +std::any ASTVisitor::visitSysCall(SysCallNode *node) { return visitChildren(node); } + std::any ASTVisitor::visitAssignExpr(AssignExprNode *node) { return visitChildren(node); } std::any ASTVisitor::visitTernaryExpr(TernaryExprNode *node) { return visitChildren(node); } diff --git a/src/ast/ASTVisitor.h b/src/ast/ASTVisitor.h index 7988b2fb9..03d1aa259 100644 --- a/src/ast/ASTVisitor.h +++ b/src/ast/ASTVisitor.h @@ -63,6 +63,7 @@ class ASTVisitor : public AbstractASTVisitor { std::any visitAlignofCall(AlignofCallNode *node) override; std::any visitLenCall(LenCallNode *node) override; std::any visitPanicCall(PanicCallNode *node) override; + std::any visitSysCall(SysCallNode *node) override; std::any visitAssignExpr(AssignExprNode *node) override; std::any visitTernaryExpr(TernaryExprNode *node) override; std::any visitLogicalOrExpr(LogicalOrExprNode *node) override; diff --git a/src/ast/AbstractASTVisitor.h b/src/ast/AbstractASTVisitor.h index 798055d8c..5177da267 100644 --- a/src/ast/AbstractASTVisitor.h +++ b/src/ast/AbstractASTVisitor.h @@ -60,6 +60,7 @@ class SizeofCallNode; class AlignofCallNode; class LenCallNode; class PanicCallNode; +class SysCallNode; class AssignExprNode; class TernaryExprNode; class LogicalOrExprNode; @@ -151,6 +152,7 @@ class AbstractASTVisitor { virtual std::any visitAlignofCall(AlignofCallNode *node) = 0; virtual std::any visitLenCall(LenCallNode *node) = 0; virtual std::any visitPanicCall(PanicCallNode *node) = 0; + virtual std::any visitSysCall(SysCallNode *node) = 0; virtual std::any visitAssignExpr(AssignExprNode *node) = 0; virtual std::any visitTernaryExpr(TernaryExprNode *node) = 0; virtual std::any visitLogicalOrExpr(LogicalOrExprNode *node) = 0; diff --git a/src/ast/ParallelizableASTVisitor.cpp b/src/ast/ParallelizableASTVisitor.cpp index a5c9e4095..3c4ea79b8 100644 --- a/src/ast/ParallelizableASTVisitor.cpp +++ b/src/ast/ParallelizableASTVisitor.cpp @@ -121,6 +121,8 @@ std::any ParallelizableASTVisitor::visitLenCall(const LenCallNode *node) { retur std::any ParallelizableASTVisitor::visitPanicCall(const PanicCallNode *node) { return visitChildren(node); } +std::any ParallelizableASTVisitor::visitSysCall(const SysCallNode *node) { return visitChildren(node); } + std::any ParallelizableASTVisitor::visitAssignExpr(const AssignExprNode *node) { return visitChildren(node); } std::any ParallelizableASTVisitor::visitTernaryExpr(const TernaryExprNode *node) { return visitChildren(node); } diff --git a/src/ast/ParallelizableASTVisitor.h b/src/ast/ParallelizableASTVisitor.h index b9173d77e..4e9b7b4f2 100644 --- a/src/ast/ParallelizableASTVisitor.h +++ b/src/ast/ParallelizableASTVisitor.h @@ -60,6 +60,7 @@ class SizeofCallNode; class AlignofCallNode; class LenCallNode; class PanicCallNode; +class SysCallNode; class AssignExprNode; class TernaryExprNode; class LogicalOrExprNode; @@ -151,6 +152,7 @@ class ParallelizableASTVisitor { virtual std::any visitAlignofCall(const AlignofCallNode *node); virtual std::any visitLenCall(const LenCallNode *node); virtual std::any visitPanicCall(const PanicCallNode *node); + virtual std::any visitSysCall(const SysCallNode *node); virtual std::any visitAssignExpr(const AssignExprNode *node); virtual std::any visitTernaryExpr(const TernaryExprNode *node); virtual std::any visitLogicalOrExpr(const LogicalOrExprNode *node); diff --git a/src/exception/SemanticError.cpp b/src/exception/SemanticError.cpp index 68f78e7bd..0047fb07c 100644 --- a/src/exception/SemanticError.cpp +++ b/src/exception/SemanticError.cpp @@ -168,6 +168,12 @@ std::string SemanticError::getMessagePrefix(SemanticErrorType errorType) { return "Expected array type"; case EXPECTED_ERROR_TYPE: return "Expected error type"; + case INVALID_SYSCALL_NUMBER_TYPE: + return "Invalid syscall number type"; + case SYSCALL_NUMBER_OUT_OF_RANGE: + return "Syscall number out of range"; + case TOO_MANY_SYSCALL_ARGS: + return "Too many syscall args"; case RETURN_WITHOUT_VALUE_RESULT: return "Return without initialization of result variable"; case RETURN_WITH_VALUE_IN_PROCEDURE: diff --git a/src/exception/SemanticError.h b/src/exception/SemanticError.h index da71cb290..2311139de 100644 --- a/src/exception/SemanticError.h +++ b/src/exception/SemanticError.h @@ -83,6 +83,9 @@ enum SemanticErrorType : uint8_t { ARRAY_ITEM_TYPE_NOT_MATCHING, EXPECTED_ARRAY_TYPE, EXPECTED_ERROR_TYPE, + INVALID_SYSCALL_NUMBER_TYPE, + SYSCALL_NUMBER_OUT_OF_RANGE, + TOO_MANY_SYSCALL_ARGS, RETURN_WITHOUT_VALUE_RESULT, RETURN_WITH_VALUE_IN_PROCEDURE, INVALID_STRUCT_INSTANTIATION, diff --git a/src/global/GlobalResourceManager.cpp b/src/global/GlobalResourceManager.cpp index 0ff077e05..a19736ea9 100644 --- a/src/global/GlobalResourceManager.cpp +++ b/src/global/GlobalResourceManager.cpp @@ -21,10 +21,12 @@ GlobalResourceManager::GlobalResourceManager(const CliOptions &cliOptions) if (cliOptions.isNativeTarget) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); } else { // GCOV_EXCL_START llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); } // GCOV_EXCL_STOP // Create cpu name and features strings diff --git a/src/global/RuntimeModuleManager.h b/src/global/RuntimeModuleManager.h index 4e94673c3..3f5bc0db6 100644 --- a/src/global/RuntimeModuleManager.h +++ b/src/global/RuntimeModuleManager.h @@ -37,6 +37,7 @@ const std::unordered_map TYPE_NAME_TO_RT_MODULE_MAP const std::unordered_map FCT_NAME_TO_RT_MODULE_MAPPING = { // Memory RT {"sAlloc", MEMORY_RT}, + {"sAllocUnsafe", MEMORY_RT}, {"sRealloc", MEMORY_RT}, {"sCopy", MEMORY_RT}, {"sDealloc", MEMORY_RT}, diff --git a/src/irgenerator/GenBuiltinFunctions.cpp b/src/irgenerator/GenBuiltinFunctions.cpp index b13b8643e..6f9fa0b99 100644 --- a/src/irgenerator/GenBuiltinFunctions.cpp +++ b/src/irgenerator/GenBuiltinFunctions.cpp @@ -3,6 +3,7 @@ #include "IRGenerator.h" #include +#include #include @@ -120,4 +121,39 @@ std::any IRGenerator::visitPanicCall(const PanicCallNode *node) { return nullptr; } +std::any IRGenerator::visitSysCall(const SysCallNode *node) { + // Create assembly string + static constexpr uint8_t NUM_REGS = 7; + const char *asmString = getSysCallAsmString(); + const char *constaints = getSysCallConstraintString(); + + // Create inline assembly + llvm::Type *int64Ty = builder.getInt64Ty(); + llvm::Type *argTypes[NUM_REGS] = {int64Ty, int64Ty, int64Ty, int64Ty, int64Ty, int64Ty, int64Ty}; + llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), argTypes, false); + llvm::InlineAsm *inlineAsm = llvm::InlineAsm::get(fctType, asmString, constaints, true); + + // Fill arguments array (first argument is syscall number) + const std::vector assignExprs = node->assignExprs(); + llvm::Value *argValues[NUM_REGS]; + for (unsigned short i = 0; i < NUM_REGS; i++) { + if (i < assignExprs.size()) { + const AssignExprNode *argNode = assignExprs.at(i); + const QualType &argType = argNode->getEvaluatedSymbolType(manIdx); + assert(argType.isOneOf({TY_INT, TY_LONG, TY_SHORT, TY_BOOL, TY_BYTE, TY_PTR, TY_STRING})); + if (argType.isOneOf({TY_PTR, TY_STRING})) + argValues[i] = builder.CreatePtrToInt(resolveValue(argNode), builder.getInt64Ty()); + else + argValues[i] = builder.CreateZExt(resolveValue(argNode), builder.getInt64Ty()); + } else { + argValues[i] = builder.getInt64(0); + } + } + + // Generate call + llvm::Value *result = builder.CreateCall(inlineAsm, argValues); + + return LLVMExprResult{.value = result}; +} + } // namespace spice::compiler \ No newline at end of file diff --git a/src/irgenerator/GenExpressions.cpp b/src/irgenerator/GenExpressions.cpp index 3f2f037a5..52035538a 100644 --- a/src/irgenerator/GenExpressions.cpp +++ b/src/irgenerator/GenExpressions.cpp @@ -812,6 +812,8 @@ std::any IRGenerator::visitAtomicExpr(const AtomicExprNode *node) { return visit(node->lenCall()); if (node->panicCall()) return visit(node->panicCall()); + if (node->sysCall()) + return visit(node->sysCall()); // Identifier (local or global variable access) assert(!node->identifierFragments.empty()); diff --git a/src/irgenerator/GenTargetDependent.cpp b/src/irgenerator/GenTargetDependent.cpp new file mode 100644 index 000000000..347500bf5 --- /dev/null +++ b/src/irgenerator/GenTargetDependent.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2021-2024 ChilliBits. All rights reserved. + +#include "IRGenerator.h" + +#include + +namespace spice::compiler { + +const char *IRGenerator::getSysCallAsmString() const { + if (cliOptions.targetArch == "x86_64" || cliOptions.targetArch == "amd64") + return "movq $0, %rax\n" + "movq $1, %rdi\n" + "movq $2, %rsi\n" + "movq $3, %rdx\n" + "movq $4, %r10\n" + "movq $5, %r8\n" + "movq $6, %r9\n" + "syscall\n"; + + if (cliOptions.targetArch == "x86" || cliOptions.targetArch == "i386") // ToDo: Test this on an actual 32 bit x86 CPU + return "movq $0, %eax\n" + "movq $1, %ebx\n" + "movq $2, %ecx\n" + "movq $3, %edx\n" + "movq $4, %esi\n" + "movq $5, %edi\n" + "movq $6, %ebp\n" + "syscall\n"; + + if (cliOptions.targetArch == "aarch64" || cliOptions.targetArch == "arm64") // ToDo: Test this on an actual AArch64 CPU + return "movq $0, %rax\n" + "movq $1, %rdi\n" + "movq $2, %rsi\n" + "movq $3, %rdx\n" + "movq $4, %r10\n" + "movq $5, %r8\n" + "movq $6, %r9\n" + "syscall\n"; + + assert_fail("Unsupported target for inline assembly"); +} + +const char *IRGenerator::getSysCallConstraintString() const { + if (cliOptions.targetArch == "x86_64" || cliOptions.targetArch == "amd64") + return "r,r,r,r,r,r,r,~{rax},~{rdi},~{rsi},~{rdx},~{r10},~{r8},~{r9},~{dirflag},~{fpsr},~{flags}"; + if (cliOptions.targetArch == "x86" || cliOptions.targetArch == "i386") + return "r,r,r,r,r,r,r,~{eax},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{dirflag},~{fpsr},~{flags}"; + if (cliOptions.targetArch == "aarch64" || cliOptions.targetArch == "arm64") + return "r,r,r,r,r,r,r,~{x8},~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{dirflag},~{fpsr},~{flags}"; + assert_fail("Unsupported target for inline assembly"); +} + +} // namespace spice::compiler diff --git a/src/irgenerator/IRGenerator.h b/src/irgenerator/IRGenerator.h index c0f779a31..f04deaa9d 100644 --- a/src/irgenerator/IRGenerator.h +++ b/src/irgenerator/IRGenerator.h @@ -83,6 +83,7 @@ class IRGenerator final : CompilerPass, public ParallelizableASTVisitor { std::any visitAlignofCall(const AlignofCallNode *node) override; std::any visitLenCall(const LenCallNode *node) override; std::any visitPanicCall(const PanicCallNode *node) override; + std::any visitSysCall(const SysCallNode *node) override; // Expressions std::any visitAssignExpr(const AssignExprNode *node) override; std::any visitTernaryExpr(const TernaryExprNode *node) override; @@ -172,6 +173,10 @@ class IRGenerator final : CompilerPass, public ParallelizableASTVisitor { void generateDefaultDtor(const Function *dtorFunction); void generateTestMain(); + // Generate target dependent + const char *getSysCallAsmString() const; + const char *getSysCallConstraintString() const; + // Generate VTable llvm::Constant *generateTypeInfoName(StructBase *spiceStruct) const; llvm::Constant *generateTypeInfo(StructBase *spiceStruct) const; diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index 86bdabc54..ed268ea0b 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -724,7 +724,7 @@ std::any TypeChecker::visitFallthroughStmt(FallthroughStmtNode *node) { std::any TypeChecker::visitAssertStmt(AssertStmtNode *node) { // Visit condition - QualType conditionType = std::any_cast(visit(node->assignExpr())).type; + const QualType conditionType = std::any_cast(visit(node->assignExpr())).type; HANDLE_UNRESOLVED_TYPE_ER(conditionType) // Check if condition evaluates to bool @@ -858,6 +858,33 @@ std::any TypeChecker::visitPanicCall(PanicCallNode *node) { return ExprResult{node->setEvaluatedSymbolType(QualType(TY_DYN), manIdx)}; } +std::any TypeChecker::visitSysCall(SysCallNode *node) { + // Check if the syscall number if of type short + const std::vector assignExprs = node->assignExprs(); + const QualType sysCallNumberType = std::any_cast(visit(assignExprs.front())).type; + if (!sysCallNumberType.is(TY_SHORT)) + SOFT_ERROR_ER(assignExprs.front(), INVALID_SYSCALL_NUMBER_TYPE, "Syscall number must be of type short") + + // Check if the syscall number is out of range + // According to https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/ + if (node->hasCompileTimeValue()) { + const unsigned short sysCallNumber = node->getCompileTimeValue().shortValue; + if (sysCallNumber < 0 || sysCallNumber > 439) + SOFT_ERROR_ER(node, SYSCALL_NUMBER_OUT_OF_RANGE, "Only syscall numbers between 0 and 439 are supported") + } + + // Check if too many syscall args are given + // According to https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/ + if (node->assignExprs().size() > 6) + SOFT_ERROR_ER(node->assignExprs().front(), TOO_MANY_SYSCALL_ARGS, "There are no syscalls that support more than 6 arguments") + + // Visit children + for (size_t i = 1; i < assignExprs.size(); i++) + visit(assignExprs.at(i)); + + return ExprResult{node->setEvaluatedSymbolType(QualType(TY_LONG), manIdx)}; +} + std::any TypeChecker::visitAssignExpr(AssignExprNode *node) { // Check if ternary if (node->ternaryExpr()) { @@ -1454,6 +1481,8 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) { return visit(node->lenCall()); if (node->panicCall()) return visit(node->panicCall()); + if (node->sysCall()) + return visit(node->sysCall()); // Check for assign expression within parentheses if (node->assignExpr()) diff --git a/src/typechecker/TypeChecker.h b/src/typechecker/TypeChecker.h index 86a0e8a0e..bc386ae3b 100644 --- a/src/typechecker/TypeChecker.h +++ b/src/typechecker/TypeChecker.h @@ -35,7 +35,8 @@ class TypeChecker final : CompilerPass, public ASTVisitor { friend class FunctionManager; friend class StructManager; - // Public methods + // Visitor methods + // Top level definitions std::any visitEntry(EntryNode *node) override; std::any visitMainFctDef(MainFctDefNode *node) override; std::any visitMainFctDefPrepare(MainFctDefNode *node); @@ -63,6 +64,7 @@ class TypeChecker final : CompilerPass, public ASTVisitor { std::any visitExtDeclPrepare(ExtDeclNode *node); std::any visitImportDef(ImportDefNode *node) override; std::any visitImportDefPrepare(ImportDefNode *node); + // Control structures std::any visitUnsafeBlock(UnsafeBlockNode *node) override; std::any visitForLoop(ForLoopNode *node) override; std::any visitForeachLoop(ForeachLoopNode *node) override; @@ -73,7 +75,9 @@ class TypeChecker final : CompilerPass, public ASTVisitor { std::any visitSwitchStmt(SwitchStmtNode *node) override; std::any visitCaseBranch(CaseBranchNode *node) override; std::any visitDefaultBranch(DefaultBranchNode *node) override; + std::any visitAssertStmt(AssertStmtNode *node) override; std::any visitAnonymousBlockStmt(AnonymousBlockStmtNode *node) override; + // Statements std::any visitStmtLst(StmtLstNode *node) override; std::any visitParamLst(ParamLstNode *node) override; std::any visitField(FieldNode *node) override; @@ -84,12 +88,14 @@ class TypeChecker final : CompilerPass, public ASTVisitor { std::any visitBreakStmt(BreakStmtNode *node) override; std::any visitContinueStmt(ContinueStmtNode *node) override; std::any visitFallthroughStmt(FallthroughStmtNode *node) override; - std::any visitAssertStmt(AssertStmtNode *node) override; + // Builtin functions std::any visitPrintfCall(PrintfCallNode *node) override; std::any visitSizeofCall(SizeofCallNode *node) override; std::any visitAlignofCall(AlignofCallNode *node) override; std::any visitLenCall(LenCallNode *node) override; std::any visitPanicCall(PanicCallNode *node) override; + std::any visitSysCall(SysCallNode *node) override; + // Expressions std::any visitAssignExpr(AssignExprNode *node) override; std::any visitTernaryExpr(TernaryExprNode *node) override; std::any visitLogicalOrExpr(LogicalOrExprNode *node) override; @@ -106,6 +112,7 @@ class TypeChecker final : CompilerPass, public ASTVisitor { std::any visitPrefixUnaryExpr(PrefixUnaryExprNode *node) override; std::any visitPostfixUnaryExpr(PostfixUnaryExprNode *node) override; std::any visitAtomicExpr(AtomicExprNode *node) override; + // Values and types std::any visitValue(ValueNode *node) override; std::any visitConstant(ConstantNode *node) override; std::any visitFctCall(FctCallNode *node) override; diff --git a/src/visualizer/ASTVisualizer.h b/src/visualizer/ASTVisualizer.h index 48c700817..1ae11b0d1 100644 --- a/src/visualizer/ASTVisualizer.h +++ b/src/visualizer/ASTVisualizer.h @@ -76,6 +76,7 @@ class ASTVisualizer final : CompilerPass, public AbstractASTVisitor { std::any visitAlignofCall(AlignofCallNode *ctx) override { return buildNode(ctx); } std::any visitLenCall(LenCallNode *ctx) override { return buildNode(ctx); } std::any visitPanicCall(PanicCallNode *ctx) override { return buildNode(ctx); } + std::any visitSysCall(SysCallNode *ctx) override { return buildNode(ctx); } std::any visitAssignExpr(AssignExprNode *ctx) override { return buildNode(ctx); } std::any visitTernaryExpr(TernaryExprNode *ctx) override { return buildNode(ctx); } std::any visitLogicalOrExpr(LogicalOrExprNode *ctx) override { return buildNode(ctx); } diff --git a/src/visualizer/CSTVisualizer.h b/src/visualizer/CSTVisualizer.h index d7cf7d63a..1ebc1ec08 100644 --- a/src/visualizer/CSTVisualizer.h +++ b/src/visualizer/CSTVisualizer.h @@ -79,6 +79,7 @@ class CSTVisualizer final : CompilerPass, public SpiceVisitor { std::any visitAlignOfCall(SpiceParser::AlignOfCallContext *ctx) override { return buildRule(ctx); } std::any visitLenCall(SpiceParser::LenCallContext *ctx) override { return buildRule(ctx); } std::any visitPanicCall(SpiceParser::PanicCallContext *ctx) override { return buildRule(ctx); } + std::any visitSysCall(SpiceParser::SysCallContext *ctx) override { return buildRule(ctx); } std::any visitAssignExpr(SpiceParser::AssignExprContext *ctx) override { return buildRule(ctx); } std::any visitTernaryExpr(SpiceParser::TernaryExprContext *ctx) override { return buildRule(ctx); } std::any visitLogicalOrExpr(SpiceParser::LogicalOrExprContext *ctx) override { return buildRule(ctx); } diff --git a/std/os/syscall_linux_amd64.spice b/std/os/syscall_linux_amd64.spice deleted file mode 100644 index d991e8247..000000000 --- a/std/os/syscall_linux_amd64.spice +++ /dev/null @@ -1,4 +0,0 @@ -/* - * Resarch results: LLVM does not support issuing syscalls out of the box. It is required to call an inline assembly instruction (like described here: https://llvm.org/docs/LangRef.html#inline-assembler-expressions) - * An example syscall could look like so: https://notes.eatonphil.com/compiler-basics-llvm-system-calls.html - */ \ No newline at end of file diff --git a/std/os/syscall_linux_x86_64.spice b/std/os/syscall_linux_x86_64.spice new file mode 100644 index 000000000..1bce36db8 --- /dev/null +++ b/std/os/syscall_linux_x86_64.spice @@ -0,0 +1,18 @@ +// Syscall numbers +const unsigned short SYSCALL_READ = 0s; +const unsigned short SYSCALL_WRITE = 1s; + +// Standard file descriptors +public type FileDescriptor enum { + STDIN = 0, + STDOUT = 1, + STDERR = 2 +} + +public p syscallRead(FileDescriptor fd, string buffer, unsigned long length) { + syscall(SYSCALL_READ, fd, buffer, length); +} + +public p syscallWrite(FileDescriptor fd, string buffer) { + syscall(SYSCALL_WRITE, fd, buffer, len(buffer)); +}