Skip to content

Commit 92747e0

Browse files
committed
Implement LLVM variables
1 parent 375de14 commit 92747e0

File tree

9 files changed

+342
-16
lines changed

9 files changed

+342
-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: 183 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,26 @@ 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+
const VariablePtr &varPtr = m_variablePtrs[step.workVariable];
423+
createValueStore(arg.second, varPtr.ptr);
424+
break;
425+
}
426+
427+
case Step::Type::ReadVariable: {
428+
assert(step.args.size() == 0);
429+
const VariablePtr &varPtr = m_variablePtrs[step.workVariable];
430+
step.functionReturnReg->value = varPtr.ptr;
431+
break;
432+
}
433+
398434
case Step::Type::Yield:
399435
if (!m_warp) {
400436
freeHeap();
437+
syncVariables(targetVariables);
401438
m_builder.CreateStore(m_builder.getInt1(true), coro.didSuspend);
402439
llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(m_ctx, "", func);
403440
llvm::Value *noneToken = llvm::ConstantTokenNone::get(m_ctx);
@@ -608,6 +645,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
608645
}
609646

610647
freeHeap();
648+
syncVariables(targetVariables);
611649

612650
// Add final suspend point
613651
if (!m_warp) {
@@ -707,6 +745,18 @@ void LLVMCodeBuilder::addConstValue(const Value &value)
707745

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

712762
void LLVMCodeBuilder::addListContents(List *list)
@@ -843,6 +893,13 @@ void LLVMCodeBuilder::createExp10()
843893
createOp(Step::Type::Exp10, Compiler::StaticType::Number, Compiler::StaticType::Number, 1);
844894
}
845895

896+
void LLVMCodeBuilder::createVariableWrite(Variable *variable)
897+
{
898+
Step &step = createOp(Step::Type::WriteVariable, Compiler::StaticType::Void, Compiler::StaticType::Unknown, 1);
899+
step.workVariable = variable;
900+
m_variablePtrs[variable] = VariablePtr();
901+
}
902+
846903
void LLVMCodeBuilder::beginIfStatement()
847904
{
848905
Step step(Step::Type::BeginIf);
@@ -926,6 +983,36 @@ void LLVMCodeBuilder::initTypes()
926983
m_valueDataType->setBody({ unionType, valueType, sizeType });
927984
}
928985

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

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

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

12371347
m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
12381348

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);
1349+
if (retType != Compiler::StaticType::Void) {
1350+
auto ret = std::make_shared<Register>(retType);
1351+
ret->isRawValue = true;
1352+
step.functionReturnReg = ret;
1353+
m_regs[m_currentFunction].push_back(ret);
1354+
m_tmpRegs.push_back(ret);
1355+
}
12441356

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

12481416
llvm::Value *LLVMCodeBuilder::createValue(std::shared_ptr<Register> reg)
@@ -1505,6 +1673,11 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
15051673
return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
15061674
}
15071675

1676+
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_copy()
1677+
{
1678+
return resolveFunction("value_assign_copy", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_valueDataType->getPointerTo() }, false));
1679+
}
1680+
15081681
llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toDouble()
15091682
{
15101683
return resolveFunction("value_toDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), m_valueDataType->getPointerTo(), false));

src/dev/engine/internal/llvmcodebuilder.h

Lines changed: 26 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,13 @@ 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+
};
177+
166178
struct Procedure
167179
{
168180
// TODO: Implement procedures
@@ -177,6 +189,7 @@ class LLVMCodeBuilder : public ICodeBuilder
177189
};
178190

179191
void initTypes();
192+
void createVariableMap();
180193

181194
Coroutine initCoroutine(llvm::Function *func);
182195
void verifyFunction(llvm::Function *func);
@@ -190,7 +203,14 @@ class LLVMCodeBuilder : public ICodeBuilder
190203
llvm::Value *isNaN(llvm::Value *num);
191204
llvm::Value *removeNaN(llvm::Value *num);
192205

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

@@ -202,6 +222,7 @@ class LLVMCodeBuilder : public ICodeBuilder
202222
llvm::FunctionCallee resolve_value_assign_bool();
203223
llvm::FunctionCallee resolve_value_assign_cstring();
204224
llvm::FunctionCallee resolve_value_assign_special();
225+
llvm::FunctionCallee resolve_value_assign_copy();
205226
llvm::FunctionCallee resolve_value_toDouble();
206227
llvm::FunctionCallee resolve_value_toBool();
207228
llvm::FunctionCallee resolve_value_toCString();
@@ -214,6 +235,10 @@ class LLVMCodeBuilder : public ICodeBuilder
214235
llvm::FunctionCallee resolve_value_lower();
215236
llvm::FunctionCallee resolve_strcasecmp();
216237

238+
Target *m_target = nullptr;
239+
std::unordered_map<Variable *, size_t> m_targetVariableMap;
240+
std::unordered_map<Variable *, VariablePtr> m_variablePtrs;
241+
217242
std::string m_id;
218243
llvm::LLVMContext m_ctx;
219244
std::unique_ptr<llvm::Module> m_module;

0 commit comments

Comments
 (0)