@@ -80,16 +80,18 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
80
80
for (auto &[var, varPtr] : m_variablePtrs) {
81
81
llvm::Value *ptr = getVariablePtr (targetVariables, var);
82
82
83
- // Access variable directly (slow?)
84
- // varPtr.ptr = ptr;
83
+ // Direct access
84
+ varPtr.heapPtr = ptr;
85
85
86
- // All variables are currently copied to the stack and synced later (seems to be faster)
86
+ // All variables are currently created on the stack and synced later (seems to be faster)
87
87
// NOTE: Strings are NOT copied, only the pointer and string size are copied
88
- varPtr.ptr = m_builder.CreateAlloca (m_valueDataType);
89
- varPtr.onStack = true ;
90
- createValueCopy (ptr, varPtr.ptr );
88
+ varPtr.stackPtr = m_builder.CreateAlloca (m_valueDataType);
89
+ varPtr.onStack = false ; // use heap before the first assignment
91
90
}
92
91
92
+ m_scopeVariables.clear ();
93
+ m_scopeVariables.push_back ({});
94
+
93
95
// Execute recorded steps
94
96
for (const LLVMInstruction &step : m_instructions) {
95
97
switch (step.type ) {
@@ -107,12 +109,13 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
107
109
args.push_back (castValue (arg.second , arg.first ));
108
110
}
109
111
110
- llvm::Value *ret = m_builder.CreateCall (resolveFunction (step.functionName , llvm::FunctionType::get (getType (step.functionReturnType ), types, false )), args);
112
+ llvm::Type *retType = getType (step.functionReturnReg ? step.functionReturnReg ->type : Compiler::StaticType::Void);
113
+ llvm::Value *ret = m_builder.CreateCall (resolveFunction (step.functionName , llvm::FunctionType::get (retType, types, false )), args);
111
114
112
115
if (step.functionReturnReg ) {
113
116
step.functionReturnReg ->value = ret;
114
117
115
- if (step.functionReturnType == Compiler::StaticType::String)
118
+ if (step.functionReturnReg -> type == Compiler::StaticType::String)
116
119
m_heap.push_back (step.functionReturnReg ->value );
117
120
}
118
121
@@ -429,16 +432,41 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
429
432
assert (step.args .size () == 1 );
430
433
assert (m_variablePtrs.find (step.workVariable ) != m_variablePtrs.cend ());
431
434
const auto &arg = step.args [0 ];
435
+ Compiler::StaticType type = optimizeRegisterType (arg.second );
432
436
LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable ];
433
437
varPtr.changed = true ;
434
- createValueStore (arg.second , varPtr.ptr );
438
+
439
+ // Initialize stack variable on first assignment
440
+ if (!varPtr.onStack ) {
441
+ varPtr.onStack = true ;
442
+ varPtr.type = type; // don't care about unknown type on first assignment
443
+
444
+ ValueType mappedType;
445
+
446
+ if (type == Compiler::StaticType::String || type == Compiler::StaticType::Unknown) {
447
+ // Value functions are used for these types, so don't break them
448
+ mappedType = ValueType::Number;
449
+ } else {
450
+ auto it = std::find_if (TYPE_MAP.begin (), TYPE_MAP.end (), [type](const std::pair<ValueType, Compiler::StaticType> &pair) { return pair.second == type; });
451
+ assert (it != TYPE_MAP.cend ());
452
+ mappedType = it->first ;
453
+ }
454
+
455
+ llvm::Value *typeField = m_builder.CreateStructGEP (m_valueDataType, varPtr.stackPtr , 1 );
456
+ m_builder.CreateStore (m_builder.getInt32 (static_cast <uint32_t >(mappedType)), typeField);
457
+ }
458
+
459
+ createValueStore (arg.second , varPtr.stackPtr , type, varPtr.type );
460
+ varPtr.type = type;
461
+ m_scopeVariables.back ()[&varPtr] = varPtr.type ;
435
462
break ;
436
463
}
437
464
438
465
case LLVMInstruction::Type::ReadVariable: {
439
466
assert (step.args .size () == 0 );
440
467
const LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable ];
441
- step.functionReturnReg ->value = varPtr.ptr ;
468
+ step.functionReturnReg ->value = varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr ;
469
+ step.functionReturnReg ->type = varPtr.type ;
442
470
break ;
443
471
}
444
472
@@ -447,6 +475,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
447
475
freeHeap ();
448
476
syncVariables (targetVariables);
449
477
coro->createSuspend ();
478
+ reloadVariables (targetVariables);
450
479
}
451
480
452
481
break ;
@@ -467,13 +496,23 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
467
496
m_builder.SetInsertPoint (statement.body );
468
497
469
498
ifStatements.push_back (statement);
499
+ pushScopeLevel ();
470
500
break ;
471
501
}
472
502
473
503
case LLVMInstruction::Type::BeginElse: {
474
504
assert (!ifStatements.empty ());
475
505
LLVMIfStatement &statement = ifStatements.back ();
476
506
507
+ // Restore types from parent scope
508
+ std::unordered_map<LLVMVariablePtr *, Compiler::StaticType> parentScopeVariables = m_scopeVariables[m_scopeVariables.size () - 2 ]; // no reference!
509
+ popScopeLevel ();
510
+
511
+ for (auto &[ptr, type] : parentScopeVariables)
512
+ ptr->type = type;
513
+
514
+ pushScopeLevel ();
515
+
477
516
// Jump to the branch after the if statement
478
517
assert (!statement.afterIf );
479
518
statement.afterIf = llvm::BasicBlock::Create (m_ctx, " " , func);
@@ -515,6 +554,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
515
554
m_builder.SetInsertPoint (statement.afterIf );
516
555
517
556
ifStatements.pop_back ();
557
+ popScopeLevel ();
518
558
break ;
519
559
}
520
560
@@ -568,6 +608,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
568
608
m_builder.SetInsertPoint (body);
569
609
570
610
loops.push_back (loop);
611
+ pushScopeLevel ();
571
612
break ;
572
613
}
573
614
@@ -589,6 +630,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
589
630
590
631
// Switch to body branch
591
632
m_builder.SetInsertPoint (body);
633
+ pushScopeLevel ();
592
634
break ;
593
635
}
594
636
@@ -610,6 +652,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
610
652
611
653
// Switch to body branch
612
654
m_builder.SetInsertPoint (body);
655
+ pushScopeLevel ();
613
656
break ;
614
657
}
615
658
@@ -643,6 +686,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
643
686
m_builder.SetInsertPoint (loop.afterLoop );
644
687
645
688
loops.pop_back ();
689
+ popScopeLevel ();
646
690
break ;
647
691
}
648
692
}
@@ -705,8 +749,6 @@ void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler:
705
749
706
750
m_tmpRegs.erase (m_tmpRegs.end () - argTypes.size (), m_tmpRegs.end ());
707
751
708
- ins.functionReturnType = returnType;
709
-
710
752
if (returnType != Compiler::StaticType::Void) {
711
753
auto reg = std::make_shared<LLVMRegister>(returnType);
712
754
reg->isRawValue = true ;
@@ -729,7 +771,6 @@ void LLVMCodeBuilder::addConstValue(const Value &value)
729
771
730
772
void LLVMCodeBuilder::addVariableValue (Variable *variable)
731
773
{
732
- // TODO: Implement type prediction
733
774
LLVMInstruction ins (LLVMInstruction::Type::ReadVariable);
734
775
ins.workVariable = variable;
735
776
m_variablePtrs[variable] = LLVMVariablePtr ();
@@ -990,6 +1031,23 @@ void LLVMCodeBuilder::createVariableMap()
990
1031
}
991
1032
}
992
1033
1034
+ void LLVMCodeBuilder::pushScopeLevel ()
1035
+ {
1036
+ m_scopeVariables.push_back ({});
1037
+ }
1038
+
1039
+ void LLVMCodeBuilder::popScopeLevel ()
1040
+ {
1041
+ for (size_t i = 0 ; i < m_scopeVariables.size () - 1 ; i++) {
1042
+ for (auto &[ptr, type] : m_scopeVariables[i]) {
1043
+ if (ptr->type != type)
1044
+ ptr->type = Compiler::StaticType::Unknown;
1045
+ }
1046
+ }
1047
+
1048
+ m_scopeVariables.pop_back ();
1049
+ }
1050
+
993
1051
void LLVMCodeBuilder::verifyFunction (llvm::Function *func)
994
1052
{
995
1053
if (llvm::verifyFunction (*func, &llvm::errs ())) {
@@ -1199,6 +1257,17 @@ llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::St
1199
1257
}
1200
1258
}
1201
1259
1260
+ Compiler::StaticType LLVMCodeBuilder::optimizeRegisterType (LLVMRegisterPtr reg)
1261
+ {
1262
+ Compiler::StaticType ret = reg->type ;
1263
+
1264
+ // Optimize string constants that represent numbers
1265
+ if (reg->isConstValue && reg->type == Compiler::StaticType::String && reg->constValue .isValidNumber ())
1266
+ ret = Compiler::StaticType::Number;
1267
+
1268
+ return ret;
1269
+ }
1270
+
1202
1271
llvm::Type *LLVMCodeBuilder::getType (Compiler::StaticType type)
1203
1272
{
1204
1273
switch (type) {
@@ -1248,14 +1317,25 @@ llvm::Value *LLVMCodeBuilder::getVariablePtr(llvm::Value *targetVariables, Varia
1248
1317
1249
1318
void LLVMCodeBuilder::syncVariables (llvm::Value *targetVariables)
1250
1319
{
1320
+ // Copy stack variables to the actual variables
1251
1321
for (auto &[var, varPtr] : m_variablePtrs) {
1252
1322
if (varPtr.onStack && varPtr.changed )
1253
- createValueCopy (varPtr.ptr , getVariablePtr (targetVariables, var));
1323
+ createValueCopy (varPtr.stackPtr , getVariablePtr (targetVariables, var));
1254
1324
1255
1325
varPtr.changed = false ;
1256
1326
}
1257
1327
}
1258
1328
1329
+ void LLVMCodeBuilder::reloadVariables (llvm::Value *targetVariables)
1330
+ {
1331
+ // Reset variables to use heap
1332
+ for (auto &[var, varPtr] : m_variablePtrs) {
1333
+ varPtr.onStack = false ;
1334
+ varPtr.changed = false ;
1335
+ varPtr.type = Compiler::StaticType::Unknown;
1336
+ }
1337
+ }
1338
+
1259
1339
LLVMInstruction &LLVMCodeBuilder::createOp (LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount)
1260
1340
{
1261
1341
LLVMInstruction ins (type);
@@ -1280,33 +1360,69 @@ LLVMInstruction &LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler:
1280
1360
return m_instructions.back ();
1281
1361
}
1282
1362
1283
- void LLVMCodeBuilder::createValueStore (LLVMRegisterPtr reg, llvm::Value *targetPtr)
1363
+ void LLVMCodeBuilder::createValueStore (LLVMRegisterPtr reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType )
1284
1364
{
1285
- // TODO: Implement type prediction
1286
- Compiler::StaticType type = reg->type ;
1287
1365
llvm::Value *converted = nullptr ;
1288
1366
1289
- // Optimize string constants that represent numbers
1290
- if (reg->isConstValue && reg->type == Compiler::StaticType::String && reg->constValue .isValidNumber ())
1291
- type = Compiler::StaticType::Number;
1367
+ if (sourceType != Compiler::StaticType::Unknown)
1368
+ converted = castValue (reg, sourceType);
1292
1369
1293
- switch (type) {
1370
+ auto it = std::find_if (TYPE_MAP.begin (), TYPE_MAP.end (), [sourceType](const std::pair<ValueType, Compiler::StaticType> &pair) { return pair.second == sourceType; });
1371
+ const ValueType mappedType = it == TYPE_MAP.cend () ? ValueType::Number : it->first ; // unknown type can be ignored
1372
+
1373
+ switch (sourceType) {
1294
1374
case Compiler::StaticType::Number:
1295
- converted = castValue (reg, type);
1296
- m_builder.CreateCall (resolve_value_assign_double (), { targetPtr, converted });
1297
- /* {
1298
- llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0);
1299
- m_builder.CreateStore(converted, ptr);
1300
- }*/
1375
+ switch (targetType) {
1376
+ case Compiler::StaticType::Number: {
1377
+ // Write number to number directly
1378
+ llvm::Value *ptr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1379
+ m_builder.CreateStore (converted, ptr);
1380
+ break ;
1381
+ }
1382
+
1383
+ case Compiler::StaticType::Bool: {
1384
+ // Write number to bool value directly and change type
1385
+ llvm::Value *ptr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1386
+ llvm::Value *typePtr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1387
+ m_builder.CreateStore (converted, ptr);
1388
+ m_builder.CreateStore (m_builder.getInt32 (static_cast <uint32_t >(mappedType)), typePtr);
1389
+ break ;
1390
+ }
1391
+
1392
+ default :
1393
+ m_builder.CreateCall (resolve_value_assign_double (), { targetPtr, converted });
1394
+ break ;
1395
+ }
1396
+
1301
1397
break ;
1302
1398
1303
1399
case Compiler::StaticType::Bool:
1304
- converted = castValue (reg, type);
1305
- m_builder.CreateCall (resolve_value_assign_bool (), { targetPtr, converted });
1400
+ switch (targetType) {
1401
+ case Compiler::StaticType::Number: {
1402
+ // Write bool to number value directly and change type
1403
+ llvm::Value *ptr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1404
+ m_builder.CreateStore (converted, ptr);
1405
+ llvm::Value *typePtr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1406
+ m_builder.CreateStore (converted, ptr);
1407
+ m_builder.CreateStore (m_builder.getInt32 (static_cast <uint32_t >(mappedType)), typePtr);
1408
+ break ;
1409
+ }
1410
+
1411
+ case Compiler::StaticType::Bool: {
1412
+ // Write bool to bool directly
1413
+ llvm::Value *ptr = m_builder.CreateStructGEP (m_valueDataType, targetPtr, 0 );
1414
+ m_builder.CreateStore (converted, ptr);
1415
+ break ;
1416
+ }
1417
+
1418
+ default :
1419
+ m_builder.CreateCall (resolve_value_assign_bool (), { targetPtr, converted });
1420
+ break ;
1421
+ }
1422
+
1306
1423
break ;
1307
1424
1308
1425
case Compiler::StaticType::String:
1309
- converted = castValue (reg, type);
1310
1426
m_builder.CreateCall (resolve_value_assign_cstring (), { targetPtr, converted });
1311
1427
break ;
1312
1428
0 commit comments