Skip to content

Commit 69be16c

Browse files
authored
Merge pull request #630 from scratchcpp/llvm_edge_activated_hats
Implement edge activated hats
2 parents 0da8733 + f3924cc commit 69be16c

30 files changed

+607
-53
lines changed

include/scratchcpp/compiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
4949
Target *target() const;
5050
std::shared_ptr<Block> block() const;
5151

52-
std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
52+
std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock, bool isHatPredicate = false);
5353
void preoptimize();
5454

5555
CompilerValue *addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const ArgTypes &argTypes = {}, const Args &args = {});

include/scratchcpp/executablecode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class LIBSCRATCHCPP_EXPORT ExecutableCode
2121
/*! Runs the script until it finishes or yields. */
2222
virtual void run(ExecutionContext *context) = 0;
2323

24+
/*! Runs the hat predicate and returns its return value. */
25+
virtual bool runPredicate(ExecutionContext *context) = 0;
26+
2427
/*! Stops the code. isFinished() will return true. */
2528
virtual void kill(ExecutionContext *context) = 0;
2629

include/scratchcpp/script.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class LIBSCRATCHCPP_EXPORT Script
3131
ExecutableCode *code() const;
3232
void setCode(std::shared_ptr<ExecutableCode> code);
3333

34+
ExecutableCode *hatPredicateCode() const;
35+
void setHatPredicateCode(std::shared_ptr<ExecutableCode> code);
36+
3437
bool runHatPredicate(Target *target);
3538

3639
std::shared_ptr<Thread> start();

include/scratchcpp/thread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class LIBSCRATCHCPP_EXPORT Thread
2727
Script *script() const;
2828

2929
void run();
30+
bool runPredicate();
3031
void kill();
3132
void reset();
3233

src/blocks/eventblocks.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@
33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
55
#include <scratchcpp/block.h>
6+
#include <scratchcpp/input.h>
67
#include <scratchcpp/field.h>
78
#include <scratchcpp/broadcast.h>
89
#include <scratchcpp/executioncontext.h>
910
#include <scratchcpp/thread.h>
1011
#include <scratchcpp/compilerconstant.h>
1112
#include <scratchcpp/promise.h>
1213
#include <scratchcpp/stringptr.h>
14+
#include <scratchcpp/sprite.h>
15+
#include <scratchcpp/itimer.h>
1316
#include <utf8.h>
1417

1518
#include "eventblocks.h"
19+
#include "audio/audioinput.h"
20+
#include "audio/iaudioloudness.h"
1621

1722
using namespace libscratchcpp;
1823

@@ -33,6 +38,7 @@ Rgb EventBlocks::color() const
3338

3439
void EventBlocks::registerBlocks(IEngine *engine)
3540
{
41+
// Blocks
3642
engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject);
3743
engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked);
3844
engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked);
@@ -43,6 +49,10 @@ void EventBlocks::registerBlocks(IEngine *engine)
4349
engine->addCompileFunction(this, "event_broadcast", &compileBroadcast);
4450
engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait);
4551
engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed);
52+
53+
// Hat predicates
54+
engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate);
55+
engine->addHatPredicateCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThanPredicate);
4656
}
4757

4858
CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler)
@@ -51,6 +61,21 @@ CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler)
5161
return nullptr;
5262
}
5363

64+
CompilerValue *EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler)
65+
{
66+
Input *input = compiler->input("TOUCHINGOBJECTMENU");
67+
CompilerValue *name = nullptr;
68+
69+
if (input->pointsToDropdownMenu()) {
70+
std::string value = input->selectedMenuItem();
71+
72+
name = compiler->addConstValue(value);
73+
} else
74+
name = compiler->addInput(input);
75+
76+
return compiler->addTargetFunctionCall("event_whentouchingobject_predicate", Compiler::StaticType::Bool, { Compiler::StaticType::String }, { name });
77+
}
78+
5479
CompilerValue *EventBlocks::compileWhenFlagClicked(Compiler *compiler)
5580
{
5681
compiler->engine()->addGreenFlagScript(compiler->block());
@@ -99,6 +124,27 @@ CompilerValue *EventBlocks::compileWhenGreaterThan(Compiler *compiler)
99124
return nullptr;
100125
}
101126

127+
CompilerValue *EventBlocks::compileWhenGreaterThanPredicate(Compiler *compiler)
128+
{
129+
Field *field = compiler->field("WHENGREATERTHANMENU");
130+
std::string predicate;
131+
132+
if (field) {
133+
const std::string option = field->value().toString();
134+
135+
if (option == "LOUDNESS")
136+
predicate = "event_whengreaterthan_loudness_predicate";
137+
else if (option == "TIMER")
138+
predicate = "event_whengreaterthan_timer_predicate";
139+
else
140+
return compiler->addConstValue(false);
141+
} else
142+
return compiler->addConstValue(false);
143+
144+
CompilerValue *value = compiler->addInput("VALUE");
145+
return compiler->addFunctionCallWithCtx(predicate, Compiler::StaticType::Bool, { Compiler::StaticType::Number }, { value });
146+
}
147+
102148
CompilerValue *EventBlocks::compileBroadcast(Compiler *compiler)
103149
{
104150
auto input = compiler->addInput("BROADCAST_INPUT");
@@ -127,6 +173,43 @@ CompilerValue *EventBlocks::compileWhenKeyPressed(Compiler *compiler)
127173
return nullptr;
128174
}
129175

176+
extern "C" bool event_whentouchingobject_predicate(Target *target, const StringPtr *name)
177+
{
178+
static const StringPtr MOUSE_STR = StringPtr("_mouse_");
179+
static const StringPtr EDGE_STR = StringPtr("_edge_");
180+
181+
IEngine *engine = target->engine();
182+
183+
if (string_compare_case_sensitive(name, &MOUSE_STR) == 0)
184+
return target->touchingPoint(engine->mouseX(), engine->mouseY());
185+
else if (string_compare_case_sensitive(name, &EDGE_STR) == 0)
186+
return target->touchingEdge();
187+
else {
188+
// TODO: Use UTF-16 in engine
189+
const std::string u8name = utf8::utf16to8(std::u16string(name->data));
190+
Target *anotherTarget = engine->targetAt(engine->findTarget(u8name));
191+
192+
if (anotherTarget && !anotherTarget->isStage())
193+
return target->touchingSprite(static_cast<Sprite *>(anotherTarget));
194+
else
195+
return false;
196+
}
197+
}
198+
199+
extern "C" bool event_whengreaterthan_loudness_predicate(ExecutionContext *ctx, double value)
200+
{
201+
if (!EventBlocks::audioInput)
202+
EventBlocks::audioInput = AudioInput::instance().get();
203+
204+
auto audioLoudness = EventBlocks::audioInput->audioLoudness();
205+
return (audioLoudness->getLoudness() > value);
206+
}
207+
208+
extern "C" bool event_whengreaterthan_timer_predicate(ExecutionContext *ctx, double value)
209+
{
210+
return ctx->engine()->timer()->value() > value;
211+
}
212+
130213
extern "C" void event_broadcast(ExecutionContext *ctx, const StringPtr *name, bool wait)
131214
{
132215
Thread *thread = ctx->thread();

src/blocks/eventblocks.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
namespace libscratchcpp
88
{
99

10+
class IAudioInput;
11+
1012
class EventBlocks : public IExtension
1113
{
1214
public:
@@ -16,14 +18,18 @@ class EventBlocks : public IExtension
1618

1719
void registerBlocks(IEngine *engine) override;
1820

21+
static inline IAudioInput *audioInput = nullptr;
22+
1923
private:
2024
static CompilerValue *compileWhenTouchingObject(Compiler *compiler);
25+
static CompilerValue *compileWhenTouchingObjectPredicate(Compiler *compiler);
2126
static CompilerValue *compileWhenFlagClicked(Compiler *compiler);
2227
static CompilerValue *compileWhenThisSpriteClicked(Compiler *compiler);
2328
static CompilerValue *compileWhenStageClicked(Compiler *compiler);
2429
static CompilerValue *compileWhenBroadcastReceived(Compiler *compiler);
2530
static CompilerValue *compileWhenBackdropSwitchesTo(Compiler *compiler);
2631
static CompilerValue *compileWhenGreaterThan(Compiler *compiler);
32+
static CompilerValue *compileWhenGreaterThanPredicate(Compiler *compiler);
2733
static CompilerValue *compileBroadcast(Compiler *compiler);
2834
static CompilerValue *compileBroadcastAndWait(Compiler *compiler);
2935
static CompilerValue *compileWhenKeyPressed(Compiler *compiler);

src/engine/compiler.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ std::shared_ptr<libscratchcpp::Block> Compiler::block() const
4444
}
4545

4646
/*! Compiles the script starting with the given block. */
47-
std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
47+
std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock, bool isHatPredicate)
4848
{
4949
BlockPrototype *procedurePrototype = nullptr;
5050

@@ -60,13 +60,32 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
6060
}
6161
}
6262

63-
impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype);
63+
impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype, isHatPredicate);
6464
impl->substackTree.clear();
6565
impl->substackHit = false;
6666
impl->emptySubstack = false;
6767
impl->warp = false;
6868
impl->block = startBlock;
6969

70+
if (impl->block && isHatPredicate) {
71+
auto f = impl->block->hatPredicateCompileFunction();
72+
73+
if (f) {
74+
CompilerValue *ret = f(this);
75+
assert(ret);
76+
77+
if (!ret)
78+
std::cout << "warning: '" << impl->block->opcode() << "' hat predicate compile function doesn't return a valid value" << std::endl;
79+
} else {
80+
std::cout << "warning: unsupported hat predicate: " << impl->block->opcode() << std::endl;
81+
impl->unsupportedBlocks.insert(impl->block->opcode());
82+
addConstValue(false); // return false if unsupported
83+
}
84+
85+
impl->block = nullptr;
86+
return impl->builder->finalize();
87+
}
88+
7089
while (impl->block) {
7190
if (impl->block->compileFunction()) {
7291
assert(impl->customIfStatementCount == 0);

src/engine/internal/codebuilderfactory.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ std::shared_ptr<CodeBuilderFactory> CodeBuilderFactory::instance()
1313
return m_instance;
1414
}
1515

16-
std::shared_ptr<ICodeBuilder> CodeBuilderFactory::create(CompilerContext *ctx, BlockPrototype *procedurePrototype) const
16+
std::shared_ptr<ICodeBuilder> CodeBuilderFactory::create(CompilerContext *ctx, BlockPrototype *procedurePrototype, bool isPredicate) const
1717
{
1818
assert(dynamic_cast<LLVMCompilerContext *>(ctx));
19-
return std::make_shared<LLVMCodeBuilder>(static_cast<LLVMCompilerContext *>(ctx), procedurePrototype);
19+
return std::make_shared<LLVMCodeBuilder>(static_cast<LLVMCompilerContext *>(ctx), procedurePrototype, isPredicate);
2020
}
2121

2222
std::shared_ptr<CompilerContext> CodeBuilderFactory::createCtx(IEngine *engine, Target *target) const

src/engine/internal/codebuilderfactory.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class CodeBuilderFactory : public ICodeBuilderFactory
1111
{
1212
public:
1313
static std::shared_ptr<CodeBuilderFactory> instance();
14-
std::shared_ptr<ICodeBuilder> create(CompilerContext *ctx, BlockPrototype *procedurePrototype) const override;
14+
std::shared_ptr<ICodeBuilder> create(CompilerContext *ctx, BlockPrototype *procedurePrototype, bool isPredicate) const override;
1515
std::shared_ptr<CompilerContext> createCtx(IEngine *engine, Target *target) const override;
1616

1717
private:

src/engine/internal/engine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ void Engine::compile()
274274
auto script = std::make_shared<Script>(target.get(), block, this);
275275
m_scripts[block] = script;
276276
script->setCode(compiler.compile(block));
277+
278+
if (block->hatPredicateCompileFunction())
279+
script->setHatPredicateCode(compiler.compile(block, true));
277280
} else {
278281
std::cout << "warning: unsupported top level block: " << block->opcode() << std::endl;
279282
m_unsupportedBlocks.insert(block->opcode());

src/engine/internal/icodebuilderfactory.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ICodeBuilderFactory
1818
public:
1919
virtual ~ICodeBuilderFactory() { }
2020

21-
virtual std::shared_ptr<ICodeBuilder> create(CompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr) const = 0;
21+
virtual std::shared_ptr<ICodeBuilder> create(CompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr, bool isPredicate = false) const = 0;
2222
virtual std::shared_ptr<CompilerContext> createCtx(IEngine *engine, Target *target) const = 0;
2323
};
2424

src/engine/internal/llvm/llvmcodebuilder.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ static std::unordered_map<ValueType, Compiler::StaticType>
2626
static const std::unordered_set<LLVMInstruction::Type>
2727
VAR_LIST_READ_INSTRUCTIONS = { LLVMInstruction::Type::ReadVariable, LLVMInstruction::Type::GetListItem, LLVMInstruction::Type::GetListItemIndex, LLVMInstruction::Type::ListContainsItem };
2828

29-
LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype) :
29+
LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype, bool isPredicate) :
3030
m_ctx(ctx),
3131
m_target(ctx->target()),
3232
m_llvmCtx(*ctx->llvmCtx()),
3333
m_module(ctx->module()),
3434
m_builder(m_llvmCtx),
3535
m_procedurePrototype(procedurePrototype),
3636
m_defaultWarp(procedurePrototype ? procedurePrototype->warp() : false),
37-
m_warp(m_defaultWarp)
37+
m_warp(m_defaultWarp),
38+
m_isPredicate(isPredicate)
3839
{
3940
initTypes();
4041
createVariableMap();
@@ -54,6 +55,10 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
5455

5556
if (it == m_instructions.end())
5657
m_warp = true;
58+
59+
// Do not create coroutine in hat predicates
60+
if (m_isPredicate)
61+
m_warp = true;
5762
}
5863

5964
// Set fast math flags
@@ -1314,10 +1319,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
13141319
// End and verify the function
13151320
llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0);
13161321

1317-
if (m_warp)
1318-
m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType));
1319-
else
1320-
coro->end();
1322+
if (m_isPredicate) {
1323+
// Use last instruction return value
1324+
assert(!m_instructions.empty());
1325+
m_builder.CreateRet(m_instructions.back()->functionReturnReg->value);
1326+
} else {
1327+
if (m_warp)
1328+
m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType));
1329+
else
1330+
coro->end();
1331+
}
13211332

13221333
verifyFunction(m_function);
13231334

@@ -1338,7 +1349,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
13381349

13391350
verifyFunction(resumeFunc);
13401351

1341-
return std::make_shared<LLVMExecutableCode>(m_ctx, m_function->getName().str(), resumeFunc->getName().str());
1352+
return std::make_shared<LLVMExecutableCode>(m_ctx, m_function->getName().str(), resumeFunc->getName().str(), m_isPredicate);
13421353
}
13431354

13441355
CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args)
@@ -2008,7 +2019,7 @@ void LLVMCodeBuilder::popLoopScope()
20082019

20092020
std::string LLVMCodeBuilder::getMainFunctionName(BlockPrototype *procedurePrototype)
20102021
{
2011-
return procedurePrototype ? "proc." + procedurePrototype->procCode() : "script";
2022+
return procedurePrototype ? "proc." + procedurePrototype->procCode() : (m_isPredicate ? "predicate" : "script");
20122023
}
20132024

20142025
std::string LLVMCodeBuilder::getResumeFunctionName(BlockPrototype *procedurePrototype)
@@ -2019,7 +2030,8 @@ std::string LLVMCodeBuilder::getResumeFunctionName(BlockPrototype *procedureProt
20192030
llvm::FunctionType *LLVMCodeBuilder::getMainFunctionType(BlockPrototype *procedurePrototype)
20202031
{
20212032
// void *f(ExecutionContext *, Target *, ValueData **, List **, (warp arg), (procedure args...))
2022-
llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0);
2033+
// bool f(...) (hat predicates)
2034+
llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0);
20232035
std::vector<llvm::Type *> argTypes = { pointerType, pointerType, pointerType, pointerType };
20242036

20252037
if (procedurePrototype) {
@@ -2034,7 +2046,7 @@ llvm::FunctionType *LLVMCodeBuilder::getMainFunctionType(BlockPrototype *procedu
20342046
}
20352047
}
20362048

2037-
return llvm::FunctionType::get(pointerType, argTypes, false);
2049+
return llvm::FunctionType::get(m_isPredicate ? m_builder.getInt1Ty() : pointerType, argTypes, false);
20382050
}
20392051

20402052
llvm::Function *LLVMCodeBuilder::getOrCreateFunction(const std::string &name, llvm::FunctionType *type)

src/engine/internal/llvm/llvmcodebuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class LLVMLoopScope;
2525
class LLVMCodeBuilder : public ICodeBuilder
2626
{
2727
public:
28-
LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr);
28+
LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr, bool isPredicate = false);
2929

3030
std::shared_ptr<ExecutableCode> finalize() override;
3131

@@ -239,6 +239,7 @@ class LLVMCodeBuilder : public ICodeBuilder
239239
bool m_defaultWarp = false;
240240
bool m_warp = false;
241241
int m_defaultArgCount = 0;
242+
bool m_isPredicate = false; // for hat predicates
242243

243244
long m_loopScope = -1; // index
244245
std::vector<std::shared_ptr<LLVMLoopScope>> m_loopScopes;

0 commit comments

Comments
 (0)