Skip to content

Commit

Permalink
Add support for syscall builtin (#631)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Aug 11, 2024
1 parent f7955cb commit 570ec64
Show file tree
Hide file tree
Showing 37 changed files with 359 additions and 66 deletions.
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
34 changes: 25 additions & 9 deletions docs/docs/language/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@ printf("Here is a string: %s.\nAnd here is a double: %f", "Demo", 1.123);
Sizeof returns the internal size of a variable, constant or type in bits. To get the size in bytes, simply divide the result by 8.

### Signature
`int sizeof(<any variable>)`
`int sizeof(type <any type>)`
`int sizeof(<any variable>)` or `int sizeof(type <any type>)`

`any variable`: Variable or constant of any type.
`any type`: Any data type
`any variable`: Variable or constant of any type, `any type`: Any data type

### Usage example
```spice
Expand All @@ -68,11 +66,9 @@ sizeof("Hello World!"); // 64 (Strings are Char pointers internally)
Alignof returns the alignment of a variable, constant or type in bytes. To get the alignment in bits, simply multiply the result by 8.

### Signature
`int alignof(<any variable>)`
`int alignof(type <any type>)`
`int alignof(<any variable>)` or `int alignof(type <any type>)`

`any variable`: Variable or constant of any type.
`any type`: Any data type
`any variable`: Variable or constant of any type, `any type`: Any data type

### Usage example
```spice
Expand Down Expand Up @@ -112,4 +108,24 @@ Panic is used to terminate the program with an error message.
### Usage example
```spice
panic(Error("This is an error message")));
```
```

## 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)`

`syscallNumber`: Number of the syscall. See [here](https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/) for all Linux syscalls.

### 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 with the OS via
system calls. Please use `import "std/os/syscall";` instead.
45 changes: 3 additions & 42 deletions media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,45 +1,6 @@
import "std/data/unordered-map";
import "std/data/pair";
import "std/os/syscall";

f<int> main() {
UnorderedMap<int, String> unorderedMap;

// Iterate over empty container
{
foreach Pair<int&, String&> item : unorderedMap {
printf("%d: %s\n", item.getFirst(), item.getSecond());
}
}

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<int&, String&> item : unorderedMap {
printf("%d: %s\n", item.getFirst(), item.getSecond());
}
}


/*f<int> greatestCommonDivisor(int a, int b) {
while b != 0 {
int temp = b;
b = a % b;
a = temp;
}
return a;
string str = "Hello World!\n";
syscallWrite(FileDescriptor::STDOUT, str);
}

f<int> main() {
int a = 56;
int gcd = greatestCommonDivisor(a, 98);
printf("GCD of %d and %d is %d.", a, 98, gcd);
}*/
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/Spice.g4
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -153,6 +154,7 @@ SIZEOF: 'sizeof';
ALIGNOF: 'alignof';
LEN: 'len';
PANIC: 'panic';
SYSCALL: 'syscall';
EXT: 'ext';
TRUE: 'true';
FALSE: 'false';
Expand Down
9 changes: 9 additions & 0 deletions src/ast/ASTBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SysCallNode>(ctx);

// Visit children
visitChildren(ctx);

return concludeNode(sysCallNode);
}

std::any ASTBuilder::visitAssignExpr(SpiceParser::AssignExprContext *ctx) {
const auto assignExprNode = createNode<AssignExprNode>(ctx);

Expand Down
1 change: 1 addition & 0 deletions src/ast/ASTBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/ast/ASTNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssignExprNode *> assignExprs() const { return getChildren<AssignExprNode>(); }
};

// ======================================================= AssignExprNode ========================================================

class AssignExprNode final : public ExprNode {
Expand Down Expand Up @@ -1838,6 +1853,7 @@ class AtomicExprNode final : public ExprNode {
[[nodiscard]] AlignofCallNode *alignofCall() const { return getChild<AlignofCallNode>(); }
[[nodiscard]] LenCallNode *lenCall() const { return getChild<LenCallNode>(); }
[[nodiscard]] PanicCallNode *panicCall() const { return getChild<PanicCallNode>(); }
[[nodiscard]] SysCallNode *sysCall() const { return getChild<SysCallNode>(); }

// Other methods
void customItemsInitialization(size_t manifestationCount) override { data.resize(manifestationCount); }
Expand Down
2 changes: 2 additions & 0 deletions src/ast/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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); }
Expand Down
1 change: 1 addition & 0 deletions src/ast/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/ast/AbstractASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class SizeofCallNode;
class AlignofCallNode;
class LenCallNode;
class PanicCallNode;
class SysCallNode;
class AssignExprNode;
class TernaryExprNode;
class LogicalOrExprNode;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/ast/ParallelizableASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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); }
Expand Down
2 changes: 2 additions & 0 deletions src/ast/ParallelizableASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class SizeofCallNode;
class AlignofCallNode;
class LenCallNode;
class PanicCallNode;
class SysCallNode;
class AssignExprNode;
class TernaryExprNode;
class LogicalOrExprNode;
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/exception/SemanticError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions src/exception/SemanticError.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/global/GlobalResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/global/RuntimeModuleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const std::unordered_map<const char *, RuntimeModule> TYPE_NAME_TO_RT_MODULE_MAP
const std::unordered_map<const char *, RuntimeModule> FCT_NAME_TO_RT_MODULE_MAPPING = {
// Memory RT
{"sAlloc", MEMORY_RT},
{"sAllocUnsafe", MEMORY_RT},
{"sRealloc", MEMORY_RT},
{"sCopy", MEMORY_RT},
{"sDealloc", MEMORY_RT},
Expand Down
36 changes: 36 additions & 0 deletions src/irgenerator/GenBuiltinFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "IRGenerator.h"

#include <ast/ASTNodes.h>
#include <llvm/IR/InlineAsm.h>

#include <llvm/IR/Module.h>

Expand Down Expand Up @@ -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 *constraints = 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, constraints, true);

// Fill arguments array (first argument is syscall number)
const std::vector<AssignExprNode *> 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
2 changes: 2 additions & 0 deletions src/irgenerator/GenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading

0 comments on commit 570ec64

Please sign in to comment.