Skip to content

Commit

Permalink
Add support for async attribute on lambda functions (#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Jan 13, 2024
1 parent 01d5228 commit a6c852a
Show file tree
Hide file tree
Showing 53 changed files with 665 additions and 569 deletions.
19 changes: 11 additions & 8 deletions docs/docs/language/threads.md
Expand Up @@ -12,24 +12,27 @@ A simple worker thread can be spawned like this:
import "std/os/thread";
f<int> main() {
Thread thread = Thread(p() {
Thread thread = Thread(p() [[async]] {
// Do something
});
thread.run();
}
```

Please always add the `[[async]]` attribute to the thread routine. This will ensure, that the thread routine is executed in a
thread-safe manner. The `run()` method will start the thread. The thread will run until the thread routine returns.

To join a thread to the main thread, use the `join()` method:

```spice
import "std/os/thread";
f<int> main() {
Thread thread1 = Thread(p() {
Thread thread1 = Thread(p() [[async]] {
// Do something
});
thread1.run();
Thread thread2 = Thread(p() {
Thread thread2 = Thread(p() [[async]] {
// Do something
});
thread2.run();
Expand All @@ -46,10 +49,10 @@ To get the ID of the current thread (i.e. within the thread routine), you can ca
import "std/os/thread";
f<int> main() {
Thread thread1 = Thread(p() {
Thread thread1 = Thread(p() [[async]] {
// Do something
});
Thread thread2 = Thread(p() {
Thread thread2 = Thread(p() [[async]] {
// Do something
});
thread2.run();
Expand All @@ -72,15 +75,15 @@ import "std/os/threadpool";
f<int> main() {
ThreadPool tp = ThreadPool(3s); // Create a thread pool with 3 worker threads
// Enque tasks to the thread pool, that will run in parallel
tp.enqueue(p() {
tp.enqueue(p() [[async]] {
delay(50);
printf("Hello from task 1\n");
});
tp.enqueue(p() {
tp.enqueue(p() [[async]] {
delay(100);
printf("Hello from task 2\n");
});
tp.enqueue(p() {
tp.enqueue(p() [[async]] {
delay(150);
printf("Hello from task 3\n");
});
Expand Down
14 changes: 14 additions & 0 deletions media/test-project/test.spice
Expand Up @@ -47,6 +47,20 @@ f<int> main() {
tp.join();
}

/*import "std/os/thread-pool";

f<int> main() {
int testVar = 123;
ThreadPool tp = ThreadPool(3s);
dyn proc = p() [[async]] {
printf("Hello from inside: %d\n", testVar);
};
tp.enqueue(proc);
tp.start();
tp.join();
printf("TestVar: %d\n", testVar);
}*/

/*f<int> main() {
int i = 123; // Captured by ref
int j = 321; // Captured by val
Expand Down
10 changes: 0 additions & 10 deletions src-bootstrap/symboltablebuilder/symbol-table.spice
Expand Up @@ -99,16 +99,6 @@ public p SymbolTable.insertAnonymous(const SymbolType symbolType, const AstNode*
this.lookupAnonymous(declNode.codeLoc).used = true;
}

/**
* Add a capture to the capture list manually
*
* @param name Capture name
* @param capture Capture
*/
public p SymbolTable.insertCapture(const string name, const Capture capture) {
this.captures.insert(name, capture);
}

/**
* Check if a symbol exists in the current or any parent scope and return it if possible
*
Expand Down
4 changes: 4 additions & 0 deletions src/ast/ASTBuilder.cpp
Expand Up @@ -558,6 +558,10 @@ std::any ASTBuilder::visitLambdaAttr(SpiceParser::LambdaAttrContext *ctx) {
// Visit children
visitChildren(ctx);

// Tell the attributes that they are module attributes
for (AttrNode *attr : lambdaAttrNode->attrLst()->attributes())
attr->target = AttrNode::TARGET_LAMBDA;

return concludeNode(lambdaAttrNode);
}

Expand Down
2 changes: 1 addition & 1 deletion src/irgenerator/DebugInfoGenerator.cpp
Expand Up @@ -169,7 +169,7 @@ void DebugInfoGenerator::popLexicalBlock() {
}

llvm::DICompositeType *DebugInfoGenerator::generateCaptureStructDebugInfo(const Function *spiceFunc) {
const std::unordered_map<std::string, Capture> &captures = spiceFunc->bodyScope->symbolTable.captures;
const CaptureMap &captures = spiceFunc->bodyScope->symbolTable.captures;
const ASTNode *node = spiceFunc->declNode;
const size_t lineNo = node->codeLoc.line;

Expand Down
2 changes: 1 addition & 1 deletion src/irgenerator/GenControlStructures.cpp
Expand Up @@ -147,7 +147,7 @@ std::any IRGenerator::visitForeachLoop(const ForeachLoopNode *node) {
llvm::Function *getIdxFct = stdFunctionManager.getIteratorGetIdxFct(node->getIdxFct, currentScope);
llvm::Value *pair = builder.CreateCall(getIdxFct, iterator);
pair->setName("pair");
builder.CreateStore(pair, pairPtr);
insertStore(pair, pairPtr);
// Store idx to idx var
llvm::Value *idxAddrInPair = builder.CreateStructGEP(pairTy, pairPtr, 0, "idx_addr");
LLVMExprResult idxResult = {.ptr = idxAddrInPair};
Expand Down
20 changes: 10 additions & 10 deletions src/irgenerator/GenExpressions.cpp
Expand Up @@ -72,7 +72,7 @@ std::any IRGenerator::visitAssignExpr(const AssignExprNode *node) {
} else if (result.value) { // The operation only updated the value
// Store the result
lhs.value = result.value;
builder.CreateStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
insertStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
}
return LLVMExprResult{.value = lhs.value, .ptr = lhs.ptr, .refPtr = lhs.refPtr, .entry = lhs.entry};
}
Expand Down Expand Up @@ -540,7 +540,7 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {
lhs.ptr = insertAlloca(lhs.value->getType());

// Store the new value
builder.CreateStore(lhs.value, lhs.ptr);
insertStore(lhs.value, lhs.ptr);

break;
}
Expand All @@ -553,14 +553,14 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {

// If this operation happens on a volatile variable, store the value directly
if (lhs.entry && lhs.entry->isVolatile)
builder.CreateStore(lhs.value, lhs.ptr, true);
insertStore(lhs.value, lhs.ptr, true);

// Save to the existing address if possible, otherwise (e.g. for literals) allocate new space
if (!lhs.ptr)
lhs.ptr = insertAlloca(lhs.value->getType());

// Store the new value
builder.CreateStore(lhs.value, lhs.ptr);
insertStore(lhs.value, lhs.ptr);

break;
}
Expand All @@ -573,14 +573,14 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {

// If this operation happens on a volatile variable, store the value directly
if (lhs.entry && lhs.entry->isVolatile)
builder.CreateStore(lhs.value, lhs.ptr, true);
insertStore(lhs.value, lhs.ptr, true);

// Save to the existing address if possible, otherwise (e.g. for literals) allocate new space
if (!lhs.ptr)
lhs.ptr = insertAlloca(lhs.value->getType());

// Store the new value
builder.CreateStore(lhs.value, lhs.ptr);
insertStore(lhs.value, lhs.ptr);

break;
}
Expand All @@ -595,7 +595,7 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {
lhs.ptr = insertAlloca(lhs.value->getType());

// Store the new value
builder.CreateStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
insertStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);

break;
}
Expand All @@ -610,7 +610,7 @@ std::any IRGenerator::visitPrefixUnaryExpr(const PrefixUnaryExprNode *node) {
lhs.ptr = insertAlloca(lhs.value->getType());

// Store the new value
builder.CreateStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
insertStore(lhs.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);

break;
}
Expand Down Expand Up @@ -750,7 +750,7 @@ std::any IRGenerator::visitPostfixUnaryExpr(const PostfixUnaryExprNode *node) {
lhs.value = result.value;
lhs.ptr = result.ptr;
} else {
builder.CreateStore(result.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
insertStore(result.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
lhs.ptr = nullptr;
}
break;
Expand All @@ -773,7 +773,7 @@ std::any IRGenerator::visitPostfixUnaryExpr(const PostfixUnaryExprNode *node) {
lhs.value = result.value;
lhs.ptr = result.ptr;
} else {
builder.CreateStore(result.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
insertStore(result.value, lhs.ptr, lhs.entry && lhs.entry->isVolatile);
lhs.ptr = nullptr;
}
break;
Expand Down
14 changes: 7 additions & 7 deletions src/irgenerator/GenImplicit.cpp
Expand Up @@ -42,7 +42,7 @@ llvm::Value *IRGenerator::doImplicitCast(llvm::Value *src, SymbolType dstSTy, Sy
// Pack the pointers together again
for (; loadCounter > 0; loadCounter--) {
llvm::Value *newActualArg = insertAlloca(src->getType());
builder.CreateStore(src, newActualArg);
insertStore(src, newActualArg);
src = newActualArg;
}
return src;
Expand Down Expand Up @@ -190,7 +190,7 @@ llvm::Function *IRGenerator::generateImplicitFunction(const std::function<void(v
// Generate debug info
diGenerator.generateLocalVarDebugInfo(THIS_VARIABLE_NAME, thisAddress, 1);
// Store the value at the new address
builder.CreateStore(fct->arg_begin(), thisAddress);
insertStore(fct->arg_begin(), thisAddress);
}

// Generate body
Expand Down Expand Up @@ -281,7 +281,7 @@ llvm::Function *IRGenerator::generateImplicitProcedure(const std::function<void(
// Generate debug info
diGenerator.generateLocalVarDebugInfo(THIS_VARIABLE_NAME, thisAddress, 1);
// Store the value at the new address
builder.CreateStore(fct->arg_begin(), thisAddress);
insertStore(fct->arg_begin(), thisAddress);
}

// Generate body
Expand Down Expand Up @@ -325,7 +325,7 @@ void IRGenerator::generateCtorBodyPreamble(Scope *bodyScope) {
thisAddressLoaded = insertLoad(builder.getPtrTy(), thisAddress);
llvm::Value *indices[3] = {builder.getInt32(0), builder.getInt32(0), builder.getInt32(2)};
llvm::Value *gepResult = insertInBoundsGEP(spiceStruct->vtableType, spiceStruct->vtable, indices);
builder.CreateStore(gepResult, thisAddressLoaded);
insertStore(gepResult, thisAddressLoaded);
}

const size_t fieldCount = structScope->getFieldCount();
Expand Down Expand Up @@ -366,7 +366,7 @@ void IRGenerator::generateCtorBodyPreamble(Scope *bodyScope) {
value = getDefaultValueForSymbolType(fieldType);
}
// Store default value
builder.CreateStore(value, fieldAddress);
insertStore(value, fieldAddress);
}
}
}
Expand Down Expand Up @@ -511,7 +511,7 @@ void IRGenerator::generateTestMain() {
// Prepare result variable
llvm::Type *i32Ty = builder.getInt32Ty();
llvm::Value *overallResult = insertAlloca(i32Ty, RETURN_VARIABLE_NAME);
builder.CreateStore(builder.getTrue(), overallResult);
insertStore(builder.getTrue(), overallResult);

// Print start message
const auto accFct = [&](size_t sum, const std::vector<const Function *> *innerVector) { return sum + innerVector->size(); };
Expand Down Expand Up @@ -573,7 +573,7 @@ void IRGenerator::generateTestMain() {
// Update result variable
llvm::Value *oldResult = insertLoad(i32Ty, overallResult);
llvm::Value *newResult = builder.CreateAnd(oldResult, builder.CreateZExt(testCaseResult, i32Ty));
builder.CreateStore(newResult, overallResult);
insertStore(newResult, overallResult);

// Print test case result message
llvm::Value *message = builder.CreateSelect(testCaseResult, successMsg, errorMsg);
Expand Down
2 changes: 1 addition & 1 deletion src/irgenerator/GenStatements.cpp
Expand Up @@ -77,7 +77,7 @@ std::any IRGenerator::visitDeclStmt(const DeclStmtNode *node) {
assert(!node->isCtorCallRequired);
// Retrieve default value for lhs symbol type and store it
llvm::Constant *defaultValue = getDefaultValueForSymbolType(varSymbolType);
builder.CreateStore(defaultValue, varAddress);
insertStore(defaultValue, varAddress);
}
}
assert(varAddress != nullptr);
Expand Down
8 changes: 4 additions & 4 deletions src/irgenerator/GenTopLevelDefinitions.cpp
Expand Up @@ -85,7 +85,7 @@ std::any IRGenerator::visitMainFctDef(const MainFctDefNode *node) {
// Generate debug info
diGenerator.generateLocalVarDebugInfo(RETURN_VARIABLE_NAME, resultAddress, SIZE_MAX);
// Store the default result value
builder.CreateStore(builder.getInt32(0), resultAddress);
insertStore(builder.getInt32(0), resultAddress);

// Store function argument values
for (auto &arg : fct->args()) {
Expand All @@ -101,7 +101,7 @@ std::any IRGenerator::visitMainFctDef(const MainFctDefNode *node) {
// Generate debug info
diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
// Store the value at the new address
builder.CreateStore(&arg, paramAddress);
insertStore(&arg, paramAddress);
}

// Visit function body
Expand Down Expand Up @@ -258,7 +258,7 @@ std::any IRGenerator::visitFctDef(const FctDefNode *node) {
// Generate debug info
diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
// Store the value at the new address
builder.CreateStore(&arg, paramAddress);
insertStore(&arg, paramAddress);
}

// Store the default values for optional function args
Expand Down Expand Up @@ -418,7 +418,7 @@ std::any IRGenerator::visitProcDef(const ProcDefNode *node) {
// Generate debug info
diGenerator.generateLocalVarDebugInfo(paramName, paramAddress, argNumber + 1);
// Store the value at the new address
builder.CreateStore(&arg, paramAddress);
insertStore(&arg, paramAddress);
}

// Store the default values for optional procedure args
Expand Down

0 comments on commit a6c852a

Please sign in to comment.