Skip to content

Commit 4e8f623

Browse files
authored
Merge pull request #605 from scratchcpp/llvm_promises
LLVM: Implement promises
2 parents b88a148 + 4477e02 commit 4e8f623

35 files changed

+635
-156
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ if (LIBSCRATCHCPP_USE_LLVM)
7676
include/scratchcpp/dev/compilerconstant.h
7777
include/scratchcpp/dev/executablecode.h
7878
include/scratchcpp/dev/executioncontext.h
79+
include/scratchcpp/dev/promise.h
7980
)
8081

8182
if(LIBSCRATCHCPP_PRINT_LLVM_IR)

include/scratchcpp/dev/compiler.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class LIBSCRATCHCPP_EXPORT Compiler
4848
std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
4949

5050
CompilerValue *addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});
51+
CompilerValue *addTargetFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});
52+
CompilerValue *addFunctionCallWithCtx(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});
5153
CompilerConstant *addConstValue(const Value &value);
5254
CompilerValue *addVariableValue(Variable *variable);
5355
CompilerValue *addListContents(List *list);
@@ -101,12 +103,16 @@ class LIBSCRATCHCPP_EXPORT Compiler
101103
void beginElseBranch();
102104
void endIf();
103105

106+
void beginWhileLoop(CompilerValue *cond);
107+
void beginRepeatUntilLoop(CompilerValue *cond);
108+
void beginLoopCondition();
109+
void endLoop();
110+
104111
void moveToIf(CompilerValue *cond, std::shared_ptr<Block> substack);
105112
void moveToIfElse(CompilerValue *cond, std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
106113
void moveToRepeatLoop(CompilerValue *count, std::shared_ptr<Block> substack);
107114
void moveToWhileLoop(CompilerValue *cond, std::shared_ptr<Block> substack);
108115
void moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr<Block> substack);
109-
void beginLoopCondition();
110116
void warp();
111117

112118
Input *input(const std::string &name) const;

include/scratchcpp/dev/executablecode.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@ class LIBSCRATCHCPP_EXPORT ExecutableCode
3030
/*! Returns true if the code is stopped or finished. */
3131
virtual bool isFinished(ExecutionContext *context) const = 0;
3232

33-
/*! Pauses the script (when it's executed using run() again) until resolvePromise() is called. */
34-
virtual void promise() = 0;
35-
36-
/*! Resolves the promise and resumes the script. */
37-
virtual void resolvePromise() = 0;
38-
3933
/*! Creates an execution context for the given Target. */
4034
virtual std::shared_ptr<ExecutionContext> createExecutionContext(Target *target) const = 0;
4135
};

include/scratchcpp/dev/executioncontext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace libscratchcpp
99
{
1010

1111
class Target;
12+
class Promise;
1213
class ExecutionContextPrivate;
1314

1415
/*! \brief The ExecutionContext represents the execution context of a target (can be a clone) with variables, lists, etc. */
@@ -21,6 +22,9 @@ class LIBSCRATCHCPP_EXPORT ExecutionContext
2122

2223
Target *target() const;
2324

25+
std::shared_ptr<Promise> promise() const;
26+
void setPromise(std::shared_ptr<Promise> promise);
27+
2428
private:
2529
spimpl::unique_impl_ptr<ExecutionContextPrivate> impl;
2630
};

include/scratchcpp/dev/promise.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "../global.h"
6+
#include "../spimpl.h"
7+
8+
namespace libscratchcpp
9+
{
10+
11+
class PromisePrivate;
12+
13+
/*! \brief The Promise class represents the eventual completion of an asynchronous operation. */
14+
class LIBSCRATCHCPP_EXPORT Promise
15+
{
16+
public:
17+
Promise();
18+
Promise(const Promise &) = delete;
19+
20+
bool isResolved() const;
21+
void resolve();
22+
23+
private:
24+
spimpl::unique_impl_ptr<PromisePrivate> impl;
25+
};
26+
27+
} // namespace libscratchcpp

include/scratchcpp/thread.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace libscratchcpp
1010

1111
class VirtualMachine;
1212
class Target;
13+
#ifdef USE_LLVM
14+
class Promise;
15+
#endif
1316
class IEngine;
1417
class Script;
1518
class ThreadPrivate;
@@ -32,8 +35,13 @@ class LIBSCRATCHCPP_EXPORT Thread
3235

3336
bool isFinished() const;
3437

38+
#ifdef USE_LLVM
39+
std::shared_ptr<Promise> promise() const;
40+
void setPromise(std::shared_ptr<Promise> promise);
41+
#else
3542
void promise();
3643
void resolvePromise();
44+
#endif
3745

3846
private:
3947
spimpl::unique_impl_ptr<ThreadPrivate> impl;

src/dev/engine/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ target_sources(scratchcpp
1212
executioncontext.cpp
1313
executioncontext_p.cpp
1414
executioncontext_p.h
15+
promise.cpp
16+
promise_p.cpp
17+
promise_p.h
1518
internal/icodebuilder.h
1619
internal/icodebuilderfactory.h
1720
internal/codebuilderfactory.cpp

src/dev/engine/compiler.cpp

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
5454
std::cerr << "error: if statement created by block '" << impl->block->opcode() << "' not terminated" << std::endl;
5555
assert(false);
5656
}
57+
58+
if (impl->customLoopCount > 0) {
59+
std::cerr << "error: loop created by block '" << impl->block->opcode() << "' not terminated" << std::endl;
60+
assert(false);
61+
}
5762
} else {
5863
std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl;
5964
impl->unsupportedBlocks.insert(impl->block->opcode());
@@ -76,14 +81,34 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
7681

7782
/*!
7883
* Adds a call to the given function.\n
79-
* For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2)
84+
* For example: extern "C" bool some_block(double arg1, const char *arg2)
8085
*/
8186
CompilerValue *Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args)
8287
{
8388
assert(argTypes.size() == args.size());
8489
return impl->builder->addFunctionCall(functionName, returnType, argTypes, args);
8590
}
8691

92+
/*!
93+
* Adds a call to the given function with a target parameter.\n
94+
* For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2)
95+
*/
96+
CompilerValue *Compiler::addTargetFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args)
97+
{
98+
assert(argTypes.size() == args.size());
99+
return impl->builder->addTargetFunctionCall(functionName, returnType, argTypes, args);
100+
}
101+
102+
/*!
103+
* Adds a call to the given function with an execution context parameter.\n
104+
* For example: extern "C" bool some_block(ExecutionContext *ctx, double arg1, const char *arg2)
105+
*/
106+
CompilerValue *Compiler::addFunctionCallWithCtx(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args)
107+
{
108+
assert(argTypes.size() == args.size());
109+
return impl->builder->addFunctionCallWithCtx(functionName, returnType, argTypes, args);
110+
}
111+
87112
/*! Adds the given constant to the compiled code. */
88113
CompilerConstant *Compiler::addConstValue(const Value &value)
89114
{
@@ -362,6 +387,45 @@ void Compiler::endIf()
362387
impl->customIfStatementCount--;
363388
}
364389

390+
/*!
391+
* Begins a custom while loop.
392+
* \note The loop must be terminated with endLoop() after compiling your block.
393+
*/
394+
void Compiler::beginWhileLoop(CompilerValue *cond)
395+
{
396+
impl->builder->beginWhileLoop(cond);
397+
impl->customLoopCount++;
398+
}
399+
400+
/*!
401+
* Begins a custom repeat until loop.
402+
* \note The loop must be terminated with endLoop() after compiling your block.
403+
*/
404+
void Compiler::beginRepeatUntilLoop(CompilerValue *cond)
405+
{
406+
impl->builder->beginRepeatUntilLoop(cond);
407+
impl->customLoopCount++;
408+
}
409+
410+
/*! Begins a while/until loop condition. */
411+
void Compiler::beginLoopCondition()
412+
{
413+
impl->builder->beginLoopCondition();
414+
}
415+
416+
/*! Ends custom loop. */
417+
void Compiler::endLoop()
418+
{
419+
if (impl->customLoopCount == 0) {
420+
std::cerr << "error: called Compiler::endLoop() without a loop";
421+
assert(false);
422+
return;
423+
}
424+
425+
impl->builder->endLoop();
426+
impl->customLoopCount--;
427+
}
428+
365429
/*! Jumps to the given if substack. */
366430
void Compiler::moveToIf(CompilerValue *cond, std::shared_ptr<Block> substack)
367431
{
@@ -425,12 +489,6 @@ void Compiler::moveToRepeatUntilLoop(CompilerValue *cond, std::shared_ptr<Block>
425489
impl->substackEnd();
426490
}
427491

428-
/*! Begins a while/until loop condition. */
429-
void Compiler::beginLoopCondition()
430-
{
431-
impl->builder->beginLoopCondition();
432-
}
433-
434492
/*! Makes current script run without screen refresh. */
435493
void Compiler::warp()
436494
{

src/dev/engine/compiler_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct CompilerPrivate
3333

3434
std::shared_ptr<Block> block;
3535
int customIfStatementCount = 0;
36+
int customLoopCount = 0;
3637
std::vector<std::pair<std::pair<std::shared_ptr<Block>, std::shared_ptr<Block>>, SubstackType>> substackTree;
3738
bool substackHit = false;
3839
bool warp = false;

src/dev/engine/executioncontext.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,15 @@ Target *ExecutionContext::target() const
1717
{
1818
return impl->target;
1919
}
20+
21+
/*! Returns the script promise. */
22+
std::shared_ptr<Promise> ExecutionContext::promise() const
23+
{
24+
return impl->promise;
25+
}
26+
27+
/*! Sets the script promise (yields until the promise is resolved). */
28+
void ExecutionContext::setPromise(std::shared_ptr<Promise> promise)
29+
{
30+
impl->promise = promise;
31+
}

src/dev/engine/executioncontext_p.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
#pragma once
44

5+
#include <memory>
6+
57
namespace libscratchcpp
68
{
79

810
class Target;
11+
class Promise;
912

1013
struct ExecutionContextPrivate
1114
{
1215
ExecutionContextPrivate(Target *target);
1316

1417
Target *target = nullptr;
18+
std::shared_ptr<Promise> promise;
1519
};
1620

1721
} // namespace libscratchcpp

src/dev/engine/internal/icodebuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class ICodeBuilder
2020
virtual std::shared_ptr<ExecutableCode> finalize() = 0;
2121

2222
virtual CompilerValue *addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) = 0;
23+
virtual CompilerValue *addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) = 0;
24+
virtual CompilerValue *addFunctionCallWithCtx(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) = 0;
2325
virtual CompilerConstant *addConstValue(const Value &value) = 0;
2426
virtual CompilerValue *addVariableValue(Variable *variable) = 0;
2527
virtual CompilerValue *addListContents(List *list) = 0;

src/dev/engine/internal/llvm/llvmcodebuilder.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,14 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
5858
m_builder.setFastMathFlags(fmf);
5959

6060
// Create function
61-
// void *f(Target *, ValueData **, List **)
61+
// void *f(ExecutionContext *, Target *, ValueData **, List **)
6262
llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
63-
llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, { pointerType, pointerType, pointerType }, false);
63+
llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, { pointerType, pointerType, pointerType, pointerType }, false);
6464
llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
65-
llvm::Value *targetPtr = func->getArg(0);
66-
llvm::Value *targetVariables = func->getArg(1);
67-
llvm::Value *targetLists = func->getArg(2);
65+
llvm::Value *executionContextPtr = func->getArg(0);
66+
llvm::Value *targetPtr = func->getArg(1);
67+
llvm::Value *targetVariables = func->getArg(2);
68+
llvm::Value *targetLists = func->getArg(3);
6869

6970
llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
7071
m_builder.SetInsertPoint(entry);
@@ -117,9 +118,17 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
117118
std::vector<llvm::Type *> types;
118119
std::vector<llvm::Value *> args;
119120

121+
// Add execution context arg
122+
if (step.functionCtxArg) {
123+
types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
124+
args.push_back(executionContextPtr);
125+
}
126+
120127
// Add target pointer arg
121-
types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
122-
args.push_back(targetPtr);
128+
if (step.functionTargetArg) {
129+
types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
130+
args.push_back(targetPtr);
131+
}
123132

124133
// Args
125134
for (auto &arg : step.args) {
@@ -950,6 +959,20 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName,
950959
return nullptr;
951960
}
952961

962+
CompilerValue *LLVMCodeBuilder::addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args)
963+
{
964+
CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args);
965+
m_instructions.back().functionTargetArg = true;
966+
return ret;
967+
}
968+
969+
CompilerValue *LLVMCodeBuilder::addFunctionCallWithCtx(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args)
970+
{
971+
CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args);
972+
m_instructions.back().functionCtxArg = true;
973+
return ret;
974+
}
975+
953976
CompilerConstant *LLVMCodeBuilder::addConstValue(const Value &value)
954977
{
955978
auto constReg = std::make_shared<LLVMConstantRegister>(TYPE_MAP[value.type()], value);

src/dev/engine/internal/llvm/llvmcodebuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class LLVMCodeBuilder : public ICodeBuilder
2727
std::shared_ptr<ExecutableCode> finalize() override;
2828

2929
CompilerValue *addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) override;
30+
CompilerValue *addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) override;
31+
CompilerValue *addFunctionCallWithCtx(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) override;
3032
CompilerConstant *addConstValue(const Value &value) override;
3133
CompilerValue *addVariableValue(Variable *variable) override;
3234
CompilerValue *addListContents(List *list) override;

0 commit comments

Comments
 (0)