Skip to content

Commit 9d3787b

Browse files
committed
Create shims for undefined procedures
1 parent 0309dc9 commit 9d3787b

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

src/engine/internal/llvm/instructions/procedures.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins)
4444
llvm::FunctionType *type = m_utils.scriptFunctionType(ins->procedurePrototype);
4545
std::vector<llvm::Value *> args;
4646

47-
m_utils.compilerCtx()->addUsedProcedure(ins->procedurePrototype, name, type);
47+
m_utils.compilerCtx()->addUsedProcedure(ins->procedurePrototype, name);
4848

4949
llvm::FunctionType *funcType = m_utils.scriptFunctionType(nullptr);
5050
int passArgCount = funcType->getNumParams();

src/engine/internal/llvm/llvmcompilercontext.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <llvm/IR/Verifier.h>
88

99
#include <scratchcpp/target.h>
10+
#include <scratchcpp/blockprototype.h>
1011
#include <iostream>
1112

1213
#include "llvmcompilercontext.h"
@@ -70,9 +71,9 @@ void LLVMCompilerContext::addDefinedProcedure(BlockPrototype *prototype)
7071
m_definedProcedures.insert(prototype);
7172
}
7273

73-
void LLVMCompilerContext::addUsedProcedure(BlockPrototype *prototype, const std::string &functionName, llvm::FunctionType *functionType)
74+
void LLVMCompilerContext::addUsedProcedure(BlockPrototype *prototype, const std::string &functionName)
7475
{
75-
m_usedProcedures[prototype] = { functionName, functionType };
76+
m_usedProcedures[prototype] = functionName;
7677
}
7778

7879
function_id_t LLVMCompilerContext::getNextFunctionId()
@@ -99,6 +100,9 @@ void LLVMCompilerContext::initJit()
99100
std::cout << "==============" << std::endl << std::endl;
100101
#endif
101102

103+
// Define shims for missing procedures
104+
createProcedureShims();
105+
102106
// Optimize
103107
optimize(llvm::OptimizationLevel::O3);
104108

@@ -199,6 +203,27 @@ void LLVMCompilerContext::createTargetMachine()
199203
m_targetMachine = std::unique_ptr<llvm::TargetMachine>(target->createTargetMachine(targetTriple, cpu, features, opt, llvm::Reloc::PIC_));
200204
}
201205

206+
void LLVMCompilerContext::createProcedureShims()
207+
{
208+
llvm::IRBuilder<> builder(*m_llvmCtx);
209+
210+
for (const auto &[prototype, name] : m_usedProcedures) {
211+
if (m_definedProcedures.find(prototype) == m_definedProcedures.cend()) {
212+
std::cout << "warning: procedure \"" << prototype->procCode() << "\" is not defined" << std::endl;
213+
214+
// We need to define shims for undefined procedures (the JIT compiler crashes without them)
215+
llvm::Function *func = m_module->getFunction(name); // since the function is used, it's already declared
216+
llvm::BasicBlock *entry = llvm::BasicBlock::Create(*m_llvmCtx, "entry", func);
217+
llvm::PointerType *pointerType = llvm::PointerType::get(*m_llvmCtx, 0);
218+
llvm::Constant *nullPointer = llvm::ConstantPointerNull::get(pointerType);
219+
builder.SetInsertPoint(entry);
220+
builder.CreateRet(nullPointer);
221+
222+
verifyFunction(func);
223+
}
224+
}
225+
}
226+
202227
void LLVMCompilerContext::optimize(llvm::OptimizationLevel optLevel)
203228
{
204229
llvm::PassBuilder passBuilder(m_targetMachine.get());

src/engine/internal/llvm/llvmcompilercontext.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class LLVMCompilerContext : public CompilerContext
3737
const std::unordered_map<function_id_t, LLVMExecutableCode *> &codeMap() const;
3838

3939
void addDefinedProcedure(BlockPrototype *prototype);
40-
void addUsedProcedure(BlockPrototype *prototype, const std::string &functionName, llvm::FunctionType *functionType);
40+
void addUsedProcedure(BlockPrototype *prototype, const std::string &functionName);
4141

4242
function_id_t getNextFunctionId();
4343

@@ -70,6 +70,7 @@ class LLVMCompilerContext : public CompilerContext
7070

7171
void initTarget();
7272
void createTargetMachine();
73+
void createProcedureShims();
7374
void optimize(llvm::OptimizationLevel optLevel);
7475

7576
llvm::Function *createCoroResumeFunction();
@@ -98,7 +99,7 @@ class LLVMCompilerContext : public CompilerContext
9899
llvm::Type *m_functionIdType = nullptr;
99100

100101
std::unordered_set<BlockPrototype *> m_definedProcedures;
101-
std::unordered_map<BlockPrototype *, std::pair<std::string, llvm::FunctionType *>> m_usedProcedures; // function name, function type
102+
std::unordered_map<BlockPrototype *, std::string> m_usedProcedures;
102103
};
103104

104105
} // namespace libscratchcpp

test/llvm/llvmcodebuilder_test.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3522,6 +3522,36 @@ TEST_F(LLVMCodeBuilderTest, Procedures)
35223522
ASSERT_TRUE(code->isFinished(ctx.get()));
35233523
}
35243524

3525+
TEST_F(LLVMCodeBuilderTest, UndefinedProcedure)
3526+
{
3527+
Sprite sprite;
3528+
3529+
BlockPrototype prototype;
3530+
prototype.setProcCode("procedure 1 %s %s %b");
3531+
prototype.setArgumentNames({ "any type 1", "any type 2", "bool" });
3532+
prototype.setArgumentIds({ "a", "b", "c" });
3533+
prototype.setWarp(false);
3534+
3535+
LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, false);
3536+
builder->createProcedureCall(&prototype, { builder->addConstValue("test"), builder->addConstValue(true), builder->addConstValue(false) });
3537+
3538+
CompilerValue *v = builder->addConstValue("test");
3539+
builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3540+
3541+
std::string expected = "test\n";
3542+
3543+
auto code = builder->build();
3544+
Script script(&sprite, nullptr, nullptr);
3545+
script.setCode(code);
3546+
Thread thread(&sprite, nullptr, &script);
3547+
auto ctx = code->createExecutionContext(&thread);
3548+
3549+
testing::internal::CaptureStdout();
3550+
code->run(ctx.get());
3551+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
3552+
ASSERT_TRUE(code->isFinished(ctx.get()));
3553+
}
3554+
35253555
TEST_F(LLVMCodeBuilderTest, HatPredicates)
35263556
{
35273557
Sprite sprite;

0 commit comments

Comments
 (0)