Skip to content

Commit e2b3534

Browse files
committed
Implement LLVM variables
1 parent 85dd5b8 commit e2b3534

File tree

9 files changed

+344
-16
lines changed

9 files changed

+344
-16
lines changed

src/dev/engine/internal/icodebuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class ICodeBuilder
5454
virtual void createExp() = 0;
5555
virtual void createExp10() = 0;
5656

57+
virtual void createVariableWrite(Variable *variable) = 0;
58+
5759
virtual void beginIfStatement() = 0;
5860
virtual void beginElseBranch() = 0;
5961
virtual void endIf() = 0;

src/dev/engine/internal/llvmcodebuilder.cpp

Lines changed: 186 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
66
#include <llvm/Passes/PassBuilder.h>
77

8+
#include <scratchcpp/stage.h>
9+
#include <scratchcpp/iengine.h>
10+
#include <scratchcpp/variable.h>
11+
812
#include "llvmcodebuilder.h"
913
#include "llvmexecutablecode.h"
1014

@@ -28,6 +32,7 @@ LLVMCodeBuilder::LLVMCodeBuilder(Target *target, const std::string &id, bool war
2832
m_constValues.push_back({});
2933
m_regs.push_back({});
3034
initTypes();
35+
createVariableMap();
3136
}
3237

3338
std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
@@ -41,10 +46,12 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
4146
}
4247

4348
// Create function
44-
// void *f(Target *)
49+
// void *f(Target *, ValueData **)
4550
llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
46-
llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, pointerType, false);
51+
llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, { pointerType, pointerType }, false);
4752
llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
53+
llvm::Value *targetPtr = func->getArg(0);
54+
llvm::Value *targetVariables = func->getArg(1);
4855

4956
llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
5057
m_builder.SetInsertPoint(entry);
@@ -59,6 +66,20 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
5966
std::vector<Loop> loops;
6067
m_heap.clear();
6168

69+
// Create variable pointers
70+
for (auto &[var, varPtr] : m_variablePtrs) {
71+
llvm::Value *ptr = getVariablePtr(targetVariables, var);
72+
73+
// Access variable directly (slow?)
74+
// varPtr.ptr = ptr;
75+
76+
// All variables are currently copied to the stack and synced later (seems to be faster)
77+
// NOTE: Strings are NOT copied, only the pointer and string size are copied
78+
varPtr.ptr = m_builder.CreateAlloca(m_valueDataType);
79+
varPtr.onStack = true;
80+
createValueCopy(ptr, varPtr.ptr);
81+
}
82+
6283
// Execute recorded steps
6384
for (const Step &step : m_steps) {
6485
switch (step.type) {
@@ -67,9 +88,8 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
6788
std::vector<llvm::Value *> args;
6889

6990
// Add target pointer arg
70-
assert(func->arg_size() == 1);
7191
types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
72-
args.push_back(func->getArg(0));
92+
args.push_back(targetPtr);
7393

7494
// Args
7595
for (auto &arg : step.args) {
@@ -395,9 +415,27 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
395415
break;
396416
}
397417

418+
case Step::Type::WriteVariable: {
419+
assert(step.args.size() == 1);
420+
assert(m_variablePtrs.find(step.workVariable) != m_variablePtrs.cend());
421+
const auto &arg = step.args[0];
422+
VariablePtr &varPtr = m_variablePtrs[step.workVariable];
423+
varPtr.changed = true;
424+
createValueStore(arg.second, varPtr.ptr);
425+
break;
426+
}
427+
428+
case Step::Type::ReadVariable: {
429+
assert(step.args.size() == 0);
430+
const VariablePtr &varPtr = m_variablePtrs[step.workVariable];
431+
step.functionReturnReg->value = varPtr.ptr;
432+
break;
433+
}
434+
398435
case Step::Type::Yield:
399436
if (!m_warp) {
400437
freeHeap();
438+
syncVariables(targetVariables);
401439
m_builder.CreateStore(m_builder.getInt1(true), coro.didSuspend);
402440
llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(m_ctx, "", func);
403441
llvm::Value *noneToken = llvm::ConstantTokenNone::get(m_ctx);
@@ -608,6 +646,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
608646
}
609647

610648
freeHeap();
649+
syncVariables(targetVariables);
611650

612651
// Add final suspend point
613652
if (!m_warp) {
@@ -707,6 +746,18 @@ void LLVMCodeBuilder::addConstValue(const Value &value)
707746

708747
void LLVMCodeBuilder::addVariableValue(Variable *variable)
709748
{
749+
// TODO: Implement type prediction
750+
Step step(Step::Type::ReadVariable);
751+
step.workVariable = variable;
752+
m_variablePtrs[variable] = VariablePtr();
753+
754+
auto ret = std::make_shared<Register>(Compiler::StaticType::Unknown);
755+
ret->isRawValue = false;
756+
step.functionReturnReg = ret;
757+
m_regs[m_currentFunction].push_back(ret);
758+
m_tmpRegs.push_back(ret);
759+
760+
m_steps.push_back(step);
710761
}
711762

712763
void LLVMCodeBuilder::addListContents(List *list)
@@ -843,6 +894,13 @@ void LLVMCodeBuilder::createExp10()
843894
createOp(Step::Type::Exp10, Compiler::StaticType::Number, Compiler::StaticType::Number, 1);
844895
}
845896

897+
void LLVMCodeBuilder::createVariableWrite(Variable *variable)
898+
{
899+
Step &step = createOp(Step::Type::WriteVariable, Compiler::StaticType::Void, Compiler::StaticType::Unknown, 1);
900+
step.workVariable = variable;
901+
m_variablePtrs[variable] = VariablePtr();
902+
}
903+
846904
void LLVMCodeBuilder::beginIfStatement()
847905
{
848906
Step step(Step::Type::BeginIf);
@@ -926,6 +984,36 @@ void LLVMCodeBuilder::initTypes()
926984
m_valueDataType->setBody({ unionType, valueType, sizeType });
927985
}
928986

987+
void LLVMCodeBuilder::createVariableMap()
988+
{
989+
if (!m_target)
990+
return;
991+
992+
// Map variable pointers to variable data array indices
993+
const auto &variables = m_target->variables();
994+
ValueData **variableData = m_target->variableData();
995+
const size_t len = variables.size();
996+
m_targetVariableMap.clear();
997+
m_targetVariableMap.reserve(len);
998+
999+
size_t i, j;
1000+
1001+
for (i = 0; i < len; i++) {
1002+
Variable *var = variables[i].get();
1003+
1004+
// Find the data for this variable
1005+
for (j = 0; j < len; j++) {
1006+
if (variableData[j] == &var->valuePtr()->data())
1007+
break;
1008+
}
1009+
1010+
if (j < len)
1011+
m_targetVariableMap[var] = j;
1012+
else
1013+
assert(false);
1014+
}
1015+
}
1016+
9291017
LLVMCodeBuilder::Coroutine LLVMCodeBuilder::initCoroutine(llvm::Function *func)
9301018
{
9311019
// Set presplitcoroutine attribute
@@ -1224,7 +1312,32 @@ llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num)
12241312
return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
12251313
}
12261314

1227-
void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount)
1315+
llvm::Value *LLVMCodeBuilder::getVariablePtr(llvm::Value *targetVariables, Variable *variable)
1316+
{
1317+
if (!m_target->isStage() && variable->target() == m_target) {
1318+
// If this is a local sprite variable, use the variable array at runtime (for clones)
1319+
assert(m_targetVariableMap.find(variable) != m_targetVariableMap.cend());
1320+
const size_t index = m_targetVariableMap[variable];
1321+
llvm::Value *ptr = m_builder.CreateGEP(m_valueDataType->getPointerTo(), targetVariables, m_builder.getInt64(index));
1322+
return m_builder.CreateLoad(m_valueDataType->getPointerTo(), ptr);
1323+
}
1324+
1325+
// Otherwise create a raw pointer at compile time
1326+
llvm::Value *addr = m_builder.getInt64((uintptr_t)&variable->value().data());
1327+
return m_builder.CreateIntToPtr(addr, m_valueDataType->getPointerTo());
1328+
}
1329+
1330+
void LLVMCodeBuilder::syncVariables(llvm::Value *targetVariables)
1331+
{
1332+
for (auto &[var, varPtr] : m_variablePtrs) {
1333+
if (varPtr.onStack && varPtr.changed)
1334+
createValueCopy(varPtr.ptr, getVariablePtr(targetVariables, var));
1335+
1336+
varPtr.changed = false;
1337+
}
1338+
}
1339+
1340+
LLVMCodeBuilder::Step &LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount)
12281341
{
12291342
Step step(type);
12301343

@@ -1236,13 +1349,71 @@ void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, Co
12361349

12371350
m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
12381351

1239-
auto ret = std::make_shared<Register>(retType);
1240-
ret->isRawValue = true;
1241-
step.functionReturnReg = ret;
1242-
m_regs[m_currentFunction].push_back(ret);
1243-
m_tmpRegs.push_back(ret);
1352+
if (retType != Compiler::StaticType::Void) {
1353+
auto ret = std::make_shared<Register>(retType);
1354+
ret->isRawValue = true;
1355+
step.functionReturnReg = ret;
1356+
m_regs[m_currentFunction].push_back(ret);
1357+
m_tmpRegs.push_back(ret);
1358+
}
12441359

12451360
m_steps.push_back(step);
1361+
return m_steps.back();
1362+
}
1363+
1364+
void LLVMCodeBuilder::createValueStore(std::shared_ptr<Register> reg, llvm::Value *targetPtr)
1365+
{
1366+
// TODO: Implement type prediction
1367+
Compiler::StaticType type = reg->type;
1368+
llvm::Value *converted = nullptr;
1369+
1370+
// Optimize string constants that represent numbers
1371+
if (reg->isConstValue && reg->type == Compiler::StaticType::String && reg->constValue.isValidNumber())
1372+
type = Compiler::StaticType::Number;
1373+
1374+
switch (type) {
1375+
case Compiler::StaticType::Number:
1376+
converted = castValue(reg, type);
1377+
m_builder.CreateCall(resolve_value_assign_double(), { targetPtr, converted });
1378+
/*{
1379+
llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0);
1380+
m_builder.CreateStore(converted, ptr);
1381+
}*/
1382+
break;
1383+
1384+
case Compiler::StaticType::Bool:
1385+
converted = castValue(reg, type);
1386+
m_builder.CreateCall(resolve_value_assign_bool(), { targetPtr, converted });
1387+
break;
1388+
1389+
case Compiler::StaticType::String:
1390+
converted = castValue(reg, type);
1391+
m_builder.CreateCall(resolve_value_assign_cstring(), { targetPtr, converted });
1392+
break;
1393+
1394+
case Compiler::StaticType::Unknown:
1395+
m_builder.CreateCall(resolve_value_assign_copy(), { targetPtr, reg->value });
1396+
break;
1397+
1398+
default:
1399+
assert(false);
1400+
break;
1401+
}
1402+
}
1403+
1404+
void LLVMCodeBuilder::createValueCopy(llvm::Value *source, llvm::Value *target)
1405+
{
1406+
// NOTE: This doesn't copy strings, but only the pointers
1407+
copyStructField(source, target, 0, m_valueDataType, m_builder.getInt64Ty()); // value
1408+
copyStructField(source, target, 1, m_valueDataType, m_builder.getInt32Ty()); // type
1409+
copyStructField(source, target, 2, m_valueDataType, m_builder.getInt64Ty()); // string size
1410+
}
1411+
1412+
void LLVMCodeBuilder::copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType)
1413+
{
1414+
llvm::Value *sourceField = m_builder.CreateStructGEP(structType, source, index);
1415+
llvm::Value *targetField = m_builder.CreateStructGEP(structType, target, index);
1416+
m_builder.CreateStore(m_builder.CreateLoad(fieldType, sourceField), targetField);
12461417
}
12471418

12481419
llvm::Value *LLVMCodeBuilder::createValue(std::shared_ptr<Register> reg)
@@ -1505,6 +1676,11 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
15051676
return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
15061677
}
15071678

1679+
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_copy()
1680+
{
1681+
return resolveFunction("value_assign_copy", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_valueDataType->getPointerTo() }, false));
1682+
}
1683+
15081684
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toDouble()
15091685
{
15101686
return resolveFunction("value_toDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), m_valueDataType->getPointerTo(), false));

src/dev/engine/internal/llvmcodebuilder.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class LLVMCodeBuilder : public ICodeBuilder
5656
void createExp() override;
5757
void createExp10() override;
5858

59+
void createVariableWrite(Variable *variable) override;
60+
5961
void beginIfStatement() override;
6062
void beginElseBranch() override;
6163
void endIf() override;
@@ -114,6 +116,8 @@ class LLVMCodeBuilder : public ICodeBuilder
114116
Log10,
115117
Exp,
116118
Exp10,
119+
WriteVariable,
120+
ReadVariable,
117121
Yield,
118122
BeginIf,
119123
BeginElse,
@@ -135,6 +139,7 @@ class LLVMCodeBuilder : public ICodeBuilder
135139
std::vector<std::pair<Compiler::StaticType, std::shared_ptr<Register>>> args; // target type, register
136140
Compiler::StaticType functionReturnType = Compiler::StaticType::Void;
137141
std::shared_ptr<Register> functionReturnReg;
142+
Variable *workVariable = nullptr; // for variable write
138143
};
139144

140145
struct IfStatement
@@ -163,6 +168,14 @@ class LLVMCodeBuilder : public ICodeBuilder
163168
llvm::Value *didSuspend = nullptr;
164169
};
165170

171+
struct VariablePtr
172+
{
173+
llvm::Value *ptr = nullptr;
174+
Compiler::StaticType type = Compiler::StaticType::Unknown;
175+
bool onStack = false;
176+
bool changed = false;
177+
};
178+
166179
struct Procedure
167180
{
168181
// TODO: Implement procedures
@@ -177,6 +190,7 @@ class LLVMCodeBuilder : public ICodeBuilder
177190
};
178191

179192
void initTypes();
193+
void createVariableMap();
180194

181195
Coroutine initCoroutine(llvm::Function *func);
182196
void verifyFunction(llvm::Function *func);
@@ -190,7 +204,14 @@ class LLVMCodeBuilder : public ICodeBuilder
190204
llvm::Value *isNaN(llvm::Value *num);
191205
llvm::Value *removeNaN(llvm::Value *num);
192206

193-
void createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount);
207+
llvm::Value *getVariablePtr(llvm::Value *targetVariables, Variable *variable);
208+
void syncVariables(llvm::Value *targetVariables);
209+
210+
Step &createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount);
211+
212+
void createValueStore(std::shared_ptr<Register> reg, llvm::Value *targetPtr);
213+
void createValueCopy(llvm::Value *source, llvm::Value *target);
214+
void copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType);
194215
llvm::Value *createValue(std::shared_ptr<Register> reg);
195216
llvm::Value *createComparison(std::shared_ptr<Register> arg1, std::shared_ptr<Register> arg2, Comparison type);
196217

@@ -202,6 +223,7 @@ class LLVMCodeBuilder : public ICodeBuilder
202223
llvm::FunctionCallee resolve_value_assign_bool();
203224
llvm::FunctionCallee resolve_value_assign_cstring();
204225
llvm::FunctionCallee resolve_value_assign_special();
226+
llvm::FunctionCallee resolve_value_assign_copy();
205227
llvm::FunctionCallee resolve_value_toDouble();
206228
llvm::FunctionCallee resolve_value_toBool();
207229
llvm::FunctionCallee resolve_value_toCString();
@@ -215,6 +237,8 @@ class LLVMCodeBuilder : public ICodeBuilder
215237
llvm::FunctionCallee resolve_strcasecmp();
216238

217239
Target *m_target = nullptr;
240+
std::unordered_map<Variable *, size_t> m_targetVariableMap;
241+
std::unordered_map<Variable *, VariablePtr> m_variablePtrs;
218242

219243
std::string m_id;
220244
llvm::LLVMContext m_ctx;

0 commit comments

Comments
 (0)