hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
llvmCodegenContext.cpp
Go to the documentation of this file.
1//
2// Created by XIaokang00010 on 2024/10/9.
3//
4
8#include "compiler/ir/IR.h"
11#include "share/def.hpp"
12#include "llvm/IR/Attributes.h"
13#include "llvm/IR/Constant.h"
14#include "llvm/IR/Constants.h"
15#include "llvm/IR/DerivedTypes.h"
16#include "llvm/IR/Function.h"
17#include "llvm/IR/Intrinsics.h"
18#include "llvm/IR/Value.h"
19#include "llvm/TargetParser/Triple.h"
20#include <algorithm>
21#include <cstddef>
22#include <iostream>
23#include <llvm/Passes/PassBuilder.h>
24#include <llvm/Passes/OptimizationLevel.h>
25#include <llvm/MC/TargetRegistry.h>
26#include <llvm/TargetParser/SubtargetFeature.h>
27#include <llvm/TargetParser/Host.h>
28#include <llvm/Support/FileSystem.h>
29#include <llvm/Support/raw_ostream.h>
30#include <llvm/Support/TargetSelect.h>
31#include <llvm/TargetParser/Host.h>
32#include <llvm/IR/DataLayout.h>
33#include <llvm/Target/TargetOptions.h>
34#include <llvm/Target/TargetMachine.h>
35#include <llvm/IR/LegacyPassManager.h>
36#include <llvm/Support/raw_ostream.h>
37#include <llvm/Support/Error.h>
38#include <llvm/Support/CodeGen.h>
39#include <memory>
40#include <string>
41#include <tuple>
42#include <filesystem>
43
44namespace yoi {
45
46 LLVMCodegen::LLVMCodegen(std::shared_ptr<compilerContext> compilerCtx, const std::shared_ptr<IRModule> &yoiModule)
47 : compilerCtx(std::move(compilerCtx)),
48 yoiModule(yoiModule) {
49
50 codegenObjectCache.setCompilerCtx(this->compilerCtx);
51
52 auto cache_path = std::filesystem::path(this->compilerCtx->getBuildConfig()->buildCachePath);
53 if (std::filesystem::exists(cache_path / "hoshi.cache.tsuki")) {
54 FILE* cache_file = fopen((cache_path / "hoshi.cache.tsuki").string().c_str(), "rb");
55 yoi_assert(cache_file != nullptr, 0, 0, "failed to open cache file");
56
58 fclose(cache_file);
59
60 yoi::vec<yoi::wstr> source_files;
61 for (auto &module : this->compilerCtx->getCompiledModules()) {
62 if (module.second->modulePath == L"builtin")
63 continue;
64 source_files.push_back(module.second->modulePath);
65 }
67 }
68 }
69
71 // void* runtime_object_alloc(unsigned long long sizeOfObject) -> i8* (i64)
72 llvm::Type* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
73 llvm::Type* sizeTy = llvmModCtx.Builder->getInt64Ty();
74
75 llvm::FunctionType *mallocFuncType = llvm::FunctionType::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0), {sizeTy, sizeTy}, false);
76 llvmModCtx.runtimeFunctions[L"mi_calloc"] = llvm::Function::Create(mallocFuncType, llvm::Function::ExternalLinkage, "mi_calloc", llvmModCtx.TheModule.get());
77 llvmModCtx.runtimeFunctions[L"mi_calloc"]->setCallingConv(llvm::CallingConv::C);
78
79 llvm::FunctionType *freeFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {i8PtrTy}, false);
80 llvmModCtx.runtimeFunctions[L"mi_free"] = llvm::Function::Create(freeFuncType, llvm::Function::ExternalLinkage, "mi_free", llvmModCtx.TheModule.get());
81 llvmModCtx.runtimeFunctions[L"mi_free"]->setCallingConv(llvm::CallingConv::C);
82
83 // Weak reference runtime functions
84 // WeakSlot* runtime_weak_slot_alloc(void* target, WeakSlot** weak_slots_head_ptr)
85 llvm::FunctionType* weakSlotAllocType = llvm::FunctionType::get(i8PtrTy, {i8PtrTy, llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
86 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_alloc"] = llvm::Function::Create(weakSlotAllocType, llvm::Function::ExternalLinkage, "runtime_weak_slot_alloc", llvmModCtx.TheModule.get());
87 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_alloc"]->setCallingConv(llvm::CallingConv::C);
88
89 // void runtime_weak_slot_free(WeakSlot* slot, WeakSlot** weak_slots_head_ptr)
90 llvm::FunctionType* weakSlotFreeType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {i8PtrTy, llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
91 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_free"] = llvm::Function::Create(weakSlotFreeType, llvm::Function::ExternalLinkage, "runtime_weak_slot_free", llvmModCtx.TheModule.get());
92 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_free"]->setCallingConv(llvm::CallingConv::C);
93
94 // void runtime_weak_slot_nullify_all(WeakSlot** weak_slots_head_ptr)
95 llvm::FunctionType* weakSlotNullifyType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
96 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_nullify_all"] = llvm::Function::Create(weakSlotNullifyType, llvm::Function::ExternalLinkage, "runtime_weak_slot_nullify_all", llvmModCtx.TheModule.get());
97 llvmModCtx.runtimeFunctions[L"runtime_weak_slot_nullify_all"]->setCallingConv(llvm::CallingConv::C);
98
99 llvm::FunctionType* allocType = llvm::FunctionType::get(i8PtrTy, {sizeTy, i8PtrTy}, false);
100 llvm::FunctionType* funcType = llvm::FunctionType::get(i8PtrTy, {sizeTy}, false);
101 llvmModCtx.runtimeFunctions[L"runtime_object_alloc_report"] = llvm::Function::Create(allocType, llvm::Function::ExternalLinkage, "runtime_object_alloc_report", llvmModCtx.TheModule.get());
102 llvmModCtx.runtimeFunctions[L"object_alloc"] = llvm::Function::Create(funcType, llvm::Function::LinkOnceODRLinkage, "object_alloc", llvmModCtx.TheModule.get());
103 llvmModCtx.runtimeFunctions[L"object_alloc"]->addFnAttr(llvm::Attribute::AlwaysInline);
104
105 // void runtime_finalize_object(void* objectPtr) -> void (i8*)
106 llvm::FunctionType* finalizeType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {i8PtrTy}, false);
107 llvmModCtx.runtimeFunctions[L"runtime_finalize_object_report"] = llvm::Function::Create(finalizeType, llvm::Function::ExternalLinkage, "runtime_finalize_object_report", llvmModCtx.TheModule.get());
108 llvmModCtx.runtimeFunctions[L"finalize_object"] = llvm::Function::Create(finalizeType, llvm::Function::LinkOnceODRLinkage, "finalize_object", llvmModCtx.TheModule.get());
109 llvmModCtx.runtimeFunctions[L"finalize_object"]->addFnAttr(llvm::Attribute::AlwaysInline);
110
111 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
112 // void runtime_debug_report_current_function(const char *function_name);
113 llvm::Type* constCharPtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
114 llvm::FunctionType* debugReportType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {constCharPtrTy}, false);
115 llvmModCtx.runtimeFunctions[L"runtime_debug_report_current_function"] = llvm::Function::Create(debugReportType, llvm::Function::ExternalLinkage, "runtime_debug_report_current_function", llvmModCtx.TheModule.get());
116 llvmModCtx.runtimeFunctions[L"runtime_debug_report_current_function"]->setCallingConv(llvm::CallingConv::C);
117
118 // void runtime_debug_report_leave_function(const char *function_name);
119 llvmModCtx.runtimeFunctions[L"runtime_debug_report_leave_function"] = llvm::Function::Create(debugReportType, llvm::Function::ExternalLinkage, "runtime_debug_report_leave_function", llvmModCtx.TheModule.get());
120 llvmModCtx.runtimeFunctions[L"runtime_debug_report_leave_function"]->setCallingConv(llvm::CallingConv::C);
121
122 // void runtime_debug_print(const char *message);
123 llvm::FunctionType* debugPrintType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {constCharPtrTy}, false);
124 llvmModCtx.runtimeFunctions[L"runtime_debug_print"] = llvm::Function::Create(debugPrintType, llvm::Function::ExternalLinkage, "runtime_debug_print", llvmModCtx.TheModule.get());
125 llvmModCtx.runtimeFunctions[L"runtime_debug_print"]->setCallingConv(llvm::CallingConv::C);
126
127 // void runtime_debug_print_address(void *address);
128 llvm::FunctionType* debugPrintAddressType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {i8PtrTy}, false);
129 llvmModCtx.runtimeFunctions[L"runtime_debug_print_address"] = llvm::Function::Create(debugPrintAddressType, llvm::Function::ExternalLinkage, "runtime_debug_print_address", llvmModCtx.TheModule.get());
130 llvmModCtx.runtimeFunctions[L"runtime_debug_print_address"]->setCallingConv(llvm::CallingConv::C);
131
132 // void runtime_debug_print_int(int value);
133 llvm::FunctionType* debugPrintIntType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmModCtx.Builder->getInt64Ty()}, false);
134 llvmModCtx.runtimeFunctions[L"runtime_debug_print_int"] = llvm::Function::Create(debugPrintIntType, llvm::Function::ExternalLinkage, "runtime_debug_print_int", llvmModCtx.TheModule.get());
135 llvmModCtx.runtimeFunctions[L"runtime_debug_print_int"]->setCallingConv(llvm::CallingConv::C);
136
137 // void runtime_debug_print_deci(double value);
138 llvm::FunctionType* debugPrintDeciType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmModCtx.Builder->getDoubleTy()}, false);
139 llvmModCtx.runtimeFunctions[L"runtime_debug_print_deci"] = llvm::Function::Create(debugPrintDeciType, llvm::Function::ExternalLinkage, "runtime_debug_print_deci", llvmModCtx.TheModule.get());
140 llvmModCtx.runtimeFunctions[L"runtime_debug_print_deci"]->setCallingConv(llvm::CallingConv::C);
141
142 llvm::FunctionType *debugPrintCurrentAllocatedMemoryType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {}, false);
143 llvmModCtx.runtimeFunctions[L"runtime_debug_print_current_allocated_memory"] = llvm::Function::Create(debugPrintCurrentAllocatedMemoryType, llvm::Function::ExternalLinkage, "runtime_debug_print_current_allocated_memory", llvmModCtx.TheModule.get());
144 llvmModCtx.runtimeFunctions[L"runtime_debug_print_current_allocated_memory"]->setCallingConv(llvm::CallingConv::C);
145
146 if (llvmModCtx.compileUnits.find(L"builtin") == llvmModCtx.compileUnits.end()) {
147 llvmModCtx.compileUnits[L"builtin"] = llvmModCtx.DBuilder->createCompileUnit(
148 llvm::dwarf::DW_LANG_C,
149 llvmModCtx.DBuilder->createFile("builtin", "."),
150 "hoshi-lang",
151 false,
152 "",
153 0
154 );
155 }
156 }
157 }
158
160 llvm::Type* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
161 llvm::Type* sizeTy = llvmModCtx.Builder->getInt64Ty();
162
163 // object_alloc
164 auto* objAllocFunc = llvmModCtx.runtimeFunctions.at(L"object_alloc");
165 llvm::BasicBlock *entryOA = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", objAllocFunc);
166 llvmModCtx.Builder->SetInsertPoint(entryOA);
167 llvm::Value* sizeOfObject = objAllocFunc->arg_begin();
168 llvm::Value *mem = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"mi_calloc"), {llvm::ConstantInt::get(sizeTy, 1), sizeOfObject});
169 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
170 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_object_alloc_report"), {sizeOfObject, mem});
171 }
172 llvmModCtx.Builder->CreateRet(mem);
173
174 // finalize_object
175 auto* finalizeObjFunc = llvmModCtx.runtimeFunctions.at(L"finalize_object");
176 llvm::BasicBlock *entryFO = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", finalizeObjFunc);
177 llvmModCtx.Builder->SetInsertPoint(entryFO);
178 llvm::Value* objectPtr = finalizeObjFunc->arg_begin();
179 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
180 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_finalize_object_report"), {objectPtr});
181 }
182 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"mi_free"), {objectPtr});
183 llvmModCtx.Builder->CreateRetVoid();
184 }
185
187 TIMER("generateDeclarations", generateDeclarations(llvmModCtx));
188 TIMER("generateForeignStructTypes", generateForeignStructTypes(llvmModCtx));
189 TIMER("generateImportFunctionImplementations", generateImportFunctionImplementations(llvmModCtx));
190 TIMER("generateImplementations", generateImplementations(llvmModCtx));
191 TIMER("generateDescription", generateDescription(llvmModCtx));
192 TIMER("generateExportFunctionDecls", generateExportFunctionDecls(llvmModCtx));
193 TIMER("generateMainFunction", generateMainFunction(llvmModCtx));
194 TIMER("generateRTTIImplmentation", generateRTTIImplmentation(llvmModCtx));
195 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
196 TIMER("llvmModCtx.DBuilder->finalize()", llvmModCtx.DBuilder->finalize());
197 }
198 }
199
200 llvm::Module* LLVMCodegen::getModule(LLVMModuleContext &llvmModCtx) {
201 return llvmModCtx.TheModule.get();
202 }
203
205 // --- Declare Basic Object Struct Types ---
206 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Type*>> basicTypes = {
207 {compilerCtx->getIntObjectType(), llvmModCtx.Builder->getInt64Ty()},
208 {compilerCtx->getDeciObjectType(), llvmModCtx.Builder->getDoubleTy()},
209 {compilerCtx->getBoolObjectType(), llvmModCtx.Builder->getInt1Ty()},
210 {compilerCtx->getCharObjectType(), llvmModCtx.Builder->getInt8Ty()},
211 {compilerCtx->getStrObjectType(), llvm::PointerType::get(*llvmModCtx.TheContext, 0)},
212 {compilerCtx->getUnsignedObjectType(), llvmModCtx.Builder->getInt64Ty()},
213 {compilerCtx->getShortObjectType(), llvmModCtx.Builder->getInt16Ty()}
214 };
215
216 for (const auto& pair : basicTypes) {
217 auto yoiType = pair.first;
218 auto rawType = pair.second;
219 auto key = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex);
220 auto name = "yoi.basic." + wstring2string(yoiType->to_string());
221 auto typeName = wstring2string(yoiType->to_string());
222 auto* structType = llvm::StructType::create(*llvmModCtx.TheContext, {llvmModCtx.Builder->getInt64Ty(), llvmModCtx.Builder->getInt64Ty(), rawType}, name);
223 auto* llvmStructPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
224 llvmModCtx.structTypeMap[key] = structType;
225 llvmModCtx.foreignTypeMap[key] = rawType;
226 auto typeIdKey = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex, 0);
227 llvmModCtx.typeIDMap[typeIdKey] = llvmModCtx.nextTypeId++;
228
229 auto incFuncName = "basic_" + typeName + "_gc_refcount_increase";
230 auto* incFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmStructPtrType}, false);
231 auto* incFunction = llvm::Function::Create(incFuncType, llvm::Function::LinkOnceODRLinkage, incFuncName, llvmModCtx.TheModule.get());
232 incFunction->addFnAttr(llvm::Attribute::AlwaysInline);
233#ifdef _WIN32
234 llvm::Comdat *incC = llvmModCtx.TheModule->getOrInsertComdat(incFuncName);
235 incC->setSelectionKind(llvm::Comdat::Any);
236 incFunction->setComdat(incC);
237#endif
238 llvmModCtx.functionMap[string2wstring(incFuncName)] = incFunction;
239
240 auto decFuncName = "basic_" + typeName + "_gc_refcount_decrease";
241 auto* decFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmStructPtrType}, false);
242 auto* decFunction = llvm::Function::Create(decFuncType, llvm::Function::LinkOnceODRLinkage, decFuncName, llvmModCtx.TheModule.get());
243 decFunction->addFnAttr(llvm::Attribute::AlwaysInline);
244#ifdef _WIN32
245 llvm::Comdat *decC = llvmModCtx.TheModule->getOrInsertComdat(decFuncName);
246 decC->setSelectionKind(llvm::Comdat::Any);
247 decFunction->setComdat(decC);
248#endif
249 llvmModCtx.functionMap[string2wstring(decFuncName)] = decFunction;
250
251 // generate basic type dyn array function
252 getArrayLLVMType(llvmModCtx, managedPtr(pair.first->getDynamicArrayType()));
253 }
254
255 // --- Handle 'none' type as a special singleton object ---
256 auto noneYoiType = compilerCtx->getNoneObjectType();
257 auto noneKey = std::make_tuple(noneYoiType->type, noneYoiType->typeAffiliateModule, noneYoiType->typeIndex);
258 llvmModCtx.foreignTypeMap[noneKey] = llvm::Type::getVoidTy(*llvmModCtx.TheContext);
259 }
260
262 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Type*>> basicTypes = {
263 {compilerCtx->getIntObjectType(), llvmModCtx.Builder->getInt64Ty()},
264 {compilerCtx->getDeciObjectType(), llvmModCtx.Builder->getDoubleTy()},
265 {compilerCtx->getBoolObjectType(), llvmModCtx.Builder->getInt1Ty()},
266 {compilerCtx->getCharObjectType(), llvmModCtx.Builder->getInt8Ty()},
267 {compilerCtx->getStrObjectType(), llvm::PointerType::get(*llvmModCtx.TheContext, 0)},
268 {compilerCtx->getUnsignedObjectType(), llvmModCtx.Builder->getInt64Ty()},
269 {compilerCtx->getShortObjectType(), llvmModCtx.Builder->getInt16Ty()}
270 };
271
272 // --- Generate GC Functions for Other Basic Types ---
273 for (const auto& pair : basicTypes) {
274 auto yoiType = pair.first;
275 auto key = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex);
276 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
277 auto* llvmStructPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
278 auto typeName = wstring2string(yoiType->to_string());
279
280 // --- Generate gc_refcount_increase ---
281 auto incFuncName = "basic_" + typeName + "_gc_refcount_increase";
282 auto* incFunction = llvmModCtx.functionMap.at(string2wstring(incFuncName));
283
284 auto* incBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", incFunction);
285 llvmModCtx.Builder->SetInsertPoint(incBlock);
286 llvm::Value* thisPtr = incFunction->arg_begin();
287
288 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
289 std::string debugStr = "Increasing refcount of " + typeName + " object";
290 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, debugStr, true);
291 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalValue::PrivateLinkage, debugStrConst, "debug_str");
292 auto* debugStrPtr = llvmModCtx.Builder->CreateBitCast(debugStrGlobal, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
293 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print"), debugStrPtr);
294 // address
295 auto* castedPtr = llvmModCtx.Builder->CreateBitCast(thisPtr, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
296 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print_address"), castedPtr);
297 }
298
299 llvm::Value* incRefCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, 0, "refcount_ptr");
300 auto beforeInc = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Add, incRefCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
301 llvmModCtx.Builder->CreateRetVoid();
302
303 // --- Generate gc_refcount_decrease ---
304 auto decFuncName = "basic_" + typeName + "_gc_refcount_decrease";
305 auto* decFunction = llvmModCtx.functionMap.at(string2wstring(decFuncName));
306
307 auto* entryBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", decFunction);
308 auto* finalizeBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "finalize", decFunction);
309 auto* continueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue", decFunction);
310
311 llvmModCtx.Builder->SetInsertPoint(entryBlock);
312 thisPtr = decFunction->arg_begin();
313
314 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
315 std::string debugStr = "Decreasing refcount of " + typeName + " object";
316 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, debugStr, true);
317 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalValue::PrivateLinkage, debugStrConst, "debug_str");
318 auto* debugStrPtr = llvmModCtx.Builder->CreateBitCast(debugStrGlobal, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
319 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print"), debugStrPtr);
320 // address
321 auto* castedPtr = llvmModCtx.Builder->CreateBitCast(thisPtr, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
322 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print_address"), castedPtr);
323 }
324 llvm::Value* decRefCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, 0, "refcount_ptr");
325 llvm::Value* decOldRefCount = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), decRefCountPtr, "old_refcount");
326 auto beforeDec = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Sub, decRefCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
327
328 llvm::Value* shouldFinalize = llvmModCtx.Builder->CreateICmpSLE(beforeDec, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), "should_finalize");
329 llvmModCtx.Builder->CreateCondBr(shouldFinalize, finalizeBlock, continueBlock);
330
331 llvm::Value* castedPtr = llvmModCtx.Builder->CreateBitCast(thisPtr, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
332 llvmModCtx.Builder->SetInsertPoint(finalizeBlock);
333 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"finalize_object"), castedPtr);
334 llvmModCtx.Builder->CreateBr(continueBlock);
335
336 llvmModCtx.Builder->SetInsertPoint(continueBlock);
337 llvmModCtx.Builder->CreateRetVoid();
338 }
339 }
340
341
342 // --- DECLARATION PHASE ---
343
358
360 for (auto& structDefPair : yoiModule->structTable) {
361 auto structDef = structDefPair.second;
362 auto key = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, yoiModule->structTable.getIndex(structDef->name));
363 auto structName = "struct." + std::to_string(yoiModule->identifier) + "." + wstring2string(structDef->name);
364 llvmModCtx.structTypeMap[key] = llvm::StructType::create(*llvmModCtx.TheContext, structName);
365 auto typeIdKey = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, yoiModule->structTable.getIndex(structDef->name), 0);
366 llvmModCtx.typeIDMap[typeIdKey] = llvmModCtx.nextTypeId++;
367 }
368 for (auto& interfaceDefPair : yoiModule->interfaceTable) {
369 auto interfaceDef = interfaceDefPair.second;
370 auto key = std::make_tuple(IRValueType::valueType::interfaceObject, yoiModule->identifier, yoiModule->interfaceTable.getIndex(interfaceDef->name));
371 auto interfaceName = "interface." + std::to_string(yoiModule->identifier) + "." + wstring2string(interfaceDef->name);
372 llvmModCtx.structTypeMap[key] = llvm::StructType::create(*llvmModCtx.TheContext, interfaceName);
373 auto typeIdKey = std::make_tuple(IRValueType::valueType::interfaceObject, yoiModule->identifier, yoiModule->interfaceTable.getIndex(interfaceDef->name), 0);
374 llvmModCtx.typeIDMap[typeIdKey] = llvmModCtx.nextTypeId++;
375 }
376 }
377
379 for (auto& globalPair : yoiModule->globalVariables) {
380 auto globalName = wstring2string(globalPair.first);
381 // All globals are pointers to objects.
382 auto globalType = yoiTypeToLLVMType(llvmModCtx, globalPair.second);
383 auto* globalVar = new llvm::GlobalVariable(*llvmModCtx.TheModule, globalType, false, llvm::GlobalValue::ExternalLinkage, nullptr, globalName);
384 llvmModCtx.globalValues[yoiModule->globalVariables.getIndex(globalPair.first)] = globalVar;
385 }
386 }
387
389 for (auto& globalPair : yoiModule->globalVariables) {
390 auto linkMetadata = globalPair.second->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"linkMetadata");
391 if (compilerCtx->getImportedModule(linkMetadata.first)->modulePath != llvmModCtx.absolute_path) {
392 continue;
393 }
394 // printf("global(%llu): %s initialized\n", yoiModule->globalVariables.getIndex(globalPair.first), wstring2string(globalPair.first).c_str());
395 // null initializer
396 auto initializer = llvm::Constant::getNullValue(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
397 llvmModCtx.globalValues[yoiModule->globalVariables.getIndex(globalPair.first)]->setInitializer(initializer);
398 }
399 }
400
402 for (auto& funcPair : yoiModule->functionTable) {
403 if (funcPair.second->hasAttribute(IRFunctionDefinition::FunctionAttrs::Unreachable))
404 continue;
405
406 auto funcDef = funcPair.second;
407 auto funcName = wstring2string(funcDef->name);
408 auto* funcType = getFunctionType(llvmModCtx, funcDef);
409 auto* function = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, llvmModCtx.TheModule.get());
410
411 if (funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::Generator)) {
412 function->addFnAttr(llvm::Attribute::PresplitCoroutine);
413 }
414
415 if (funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::AlwaysInline)) {
416 function->addFnAttr(llvm::Attribute::AlwaysInline);
417 }
418
419 llvmModCtx.functionMap[funcDef->name] = function;
420 }
421 }
422
423 // --- IMPLEMENTATION PHASE ---
424
433
435 for (auto& structDefPair : yoiModule->structTable) {
436 auto structDef = structDefPair.second;
437 auto key = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, yoiModule->structTable.getIndex(structDef->name));
438 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
439
440 std::vector<llvm::Type*> fieldTypes;
441 fieldTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // gc_refcount
442 fieldTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // typeid
443 for (const auto& fieldType : structDef->fieldTypes) {
444 fieldTypes.push_back(yoiTypeToLLVMType(llvmModCtx, fieldType, fieldType->isBasicType() && fieldType->hasAttribute(IRValueType::ValueAttr::Raw)));
445 }
446 fieldTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0)); // weak_slots_head
447 if (llvmStructType->isOpaque()) {
448 llvmStructType->setBody(fieldTypes);
449 }
450 }
451 for (auto& interfaceDefPair : yoiModule->interfaceTable) {
452 auto interfaceDef = interfaceDefPair.second;
453 auto key = std::make_tuple(IRValueType::valueType::interfaceObject, yoiModule->identifier, yoiModule->interfaceTable.getIndex(interfaceDef->name));
454 auto* llvmInterfaceType = llvmModCtx.structTypeMap.at(key);
455
456 std::vector<llvm::Type*> memberTypes;
457 memberTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // [0] refcount
458 memberTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // [1] typeid
459 memberTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0)); // [2] this ptr
460 auto* gcFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), { llvm::PointerType::get(*llvmModCtx.TheContext, 0) }, false);
461 auto* gcFuncPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
462 memberTypes.push_back(gcFuncPtrType); // [3] gc_refcount_increase vptr
463 memberTypes.push_back(gcFuncPtrType); // [4] gc_refcount_decrease vptr
464
465 for (const auto& methodPair : interfaceDef->methodMap) {
466 auto funcType = getFunctionType(llvmModCtx, methodPair.second);
467 std::vector<llvm::Type*> virtualArgTypes;
468 virtualArgTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0)); // 'this' is always i8*
469 for(size_t i = 1; i < funcType->getNumParams(); ++i) {
470 virtualArgTypes.push_back(funcType->getParamType(i));
471 }
472 auto virtualFuncType = llvm::FunctionType::get(funcType->getReturnType(), virtualArgTypes, false);
473 memberTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
474 }
475 memberTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0)); // weak_slots_head
476
477 if (llvmInterfaceType->isOpaque()) {
478 llvmInterfaceType->setBody(memberTypes);
479 }
480 }
481 }
482
484 for (auto& structDefPair : yoiModule->structTable) {
485 auto structDef = structDefPair.second;
486 auto structIdx = yoiModule->structTable.getIndex(structDef->name);
487 auto moduleID = yoiModule->identifier;
488 auto key = std::make_tuple(IRValueType::valueType::structObject, moduleID, structIdx);
489 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
490 auto* llvmStructPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
491
492 // --- Generate gc_refcount_increase ---
493 auto incFuncName = "struct_" + std::to_string(moduleID) + "_" + std::to_string(structIdx) + "_gc_refcount_increase";
494 auto* incFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmStructPtrType}, false);
495 auto* incFunction = llvm::Function::Create(incFuncType, llvm::Function::LinkOnceODRLinkage, incFuncName, llvmModCtx.TheModule.get());
496 incFunction->addFnAttr(llvm::Attribute::AlwaysInline);
497#ifdef _WIN32
498 llvm::Comdat *incC = llvmModCtx.TheModule->getOrInsertComdat(incFuncName);
499 incC->setSelectionKind(llvm::Comdat::Any);
500 incFunction->setComdat(incC);
501#endif
502 llvmModCtx.functionMap[string2wstring(incFuncName)] = incFunction;
503
504 // --- Generate gc_refcount_decrease ---
505 auto decFuncName = "struct_" + std::to_string(moduleID) + "_" + std::to_string(structIdx) + "_gc_refcount_decrease";
506 auto* decFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmStructPtrType}, false);
507 auto* decFunction = llvm::Function::Create(decFuncType, llvm::Function::LinkOnceODRLinkage, decFuncName, llvmModCtx.TheModule.get());
508 decFunction->addFnAttr(llvm::Attribute::AlwaysInline);
509#ifdef _WIN32
510 llvm::Comdat *decC = llvmModCtx.TheModule->getOrInsertComdat(decFuncName);
511 decC->setSelectionKind(llvm::Comdat::Any);
512 decFunction->setComdat(decC);
513#endif
514 llvmModCtx.functionMap[string2wstring(decFuncName)] = decFunction;
515 }
516 }
517
519 for (auto& structDefPair : yoiModule->structTable) {
520 auto moduleID = yoiModule->identifier;
521 auto structDef = structDefPair.second;
522 // if (compilerCtx->getImportedModule(structDef->linkedModuleId)->modulePath != llvmModCtx.absolute_path)
523 // continue;
524 auto structIdx = yoiModule->structTable.getIndex(structDef->name);
525 auto key = std::make_tuple(IRValueType::valueType::structObject, moduleID, structIdx);
526 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
527 auto* llvmStructPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
528
529 // --- Generate gc_refcount_increase ---
530 auto incFuncName = "struct_" + std::to_string(moduleID) + "_" + std::to_string(structIdx) + "_gc_refcount_increase";
531 auto incFunction = llvmModCtx.functionMap[string2wstring(incFuncName)];
532
533 auto* incBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", incFunction);
534 llvmModCtx.Builder->SetInsertPoint(incBlock);
535 llvm::Value* thisPtr = incFunction->arg_begin();
536 llvm::Value* refCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, 0, "refcount_ptr");
537 auto beforeInc = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Add, refCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
538 llvmModCtx.Builder->CreateRetVoid();
539
540 // --- Generate gc_refcount_decrease ---
541 auto decFuncName = "struct_" + std::to_string(moduleID) + "_" + std::to_string(structIdx) + "_gc_refcount_decrease";
542 auto decFunction = llvmModCtx.functionMap[string2wstring(decFuncName)];
543
544 auto* entryBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", decFunction);
545 auto* finalizeBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "finalize", decFunction);
546 auto* continueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue", decFunction);
547
548 llvmModCtx.Builder->SetInsertPoint(entryBlock);
549 thisPtr = decFunction->arg_begin();
550
551 refCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, 0, "refcount_ptr");
552 auto beforeDec = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Sub, refCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
553
554 llvm::Value* shouldFinalize = llvmModCtx.Builder->CreateICmpSLE(beforeDec, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), "should_finalize");
555 llvmModCtx.Builder->CreateCondBr(shouldFinalize, finalizeBlock, continueBlock);
556
557 llvmModCtx.Builder->SetInsertPoint(finalizeBlock);
558 llvm::Value* castedPtr = llvmModCtx.Builder->CreateBitCast(thisPtr, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
559
560 // Nullify all weak slots pointing to this struct before field finalization
561 auto weakSlotsHeadIdx = 2 + structDef->fieldTypes.size();
562 auto* weakSlotsHeadPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, weakSlotsHeadIdx, "weak_slots_head_ptr");
563 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_weak_slot_nullify_all"), {weakSlotsHeadPtr});
564
565 // if any finalizer presents, call it
566 if (auto funcName = structDef->name + L"::finalizer"; llvmModCtx.functionMap[funcName] != nullptr && yoiModule->functionTable[funcName]->hasAttribute(IRFunctionDefinition::FunctionAttrs::Finalizer)) {
567 llvmModCtx.Builder->CreateCall(llvmModCtx.functionMap[funcName], {castedPtr});
568 }
569 // call dec for inner object (if any)
570 for (yoi::indexT innerIdx = 0; innerIdx < structDef->fieldTypes.size(); ++innerIdx) {
571 // create gep
572 auto fieldPtr = llvmModCtx.Builder->CreateStructGEP(llvmStructType, thisPtr, innerIdx + 2, "field_ptr"); // skip refcount at index 0, and typeid at index 1
573 auto fieldType = structDef->fieldTypes[innerIdx];
574 // Load the field value before calling its GC function
575 llvm::Value* loadedField = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, fieldType), fieldPtr, "loaded_field_for_gc");
576 callGcFunction(llvmModCtx, loadedField, fieldType, false); // Decrease refcount of member
577 }
578 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"finalize_object"), castedPtr);
579 llvmModCtx.Builder->CreateBr(continueBlock);
580
581 llvmModCtx.Builder->SetInsertPoint(continueBlock);
582 llvmModCtx.Builder->CreateRetVoid();
583 }
584 }
585
586 void LLVMCodegen::generateInterfaceObjectGCFunctionDeclarations(LLVMModuleContext &llvmModCtx) {
587 // These are the top-level GC wrappers for the interface objects themselves.
588 // They manage the interface object's own refcount and dispatch to the interfaceImpl wrappers.
589 for (const auto& interfaceDefPair : yoiModule->interfaceTable) {
590 auto interfaceDef = interfaceDefPair.second;
591 auto interfaceIdx = yoiModule->interfaceTable.getIndex(interfaceDef->name);
592 auto moduleID = yoiModule->identifier;
593 auto key = std::make_tuple(IRValueType::valueType::interfaceObject, moduleID, interfaceIdx);
594 auto* llvmInterfaceType = llvmModCtx.structTypeMap.at(key);
595 auto* llvmInterfacePtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
596 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
597 auto* gcFuncTypeForDispatch = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), { i8PtrTy }, false);
598 auto* gcFuncPtrTypeForDispatch = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
599
600 auto incFuncName = "interface_" + std::to_string(moduleID) + "_" + std::to_string(interfaceIdx) + "_gc_refcount_increase";
601 auto* incFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmInterfacePtrType}, false);
602 auto* incFunction = llvm::Function::Create(incFuncType, llvm::Function::LinkOnceODRLinkage, incFuncName, llvmModCtx.TheModule.get());
603 incFunction->addFnAttr(llvm::Attribute::AlwaysInline);
604#ifdef _WIN32
605 llvm::Comdat *incC = llvmModCtx.TheModule->getOrInsertComdat(incFuncName);
606 incC->setSelectionKind(llvm::Comdat::Any);
607 incFunction->setComdat(incC);
608#endif
609 llvmModCtx.functionMap[string2wstring(incFuncName)] = incFunction;
610
611 auto decFuncName = "interface_" + std::to_string(moduleID) + "_" + std::to_string(interfaceIdx) + "_gc_refcount_decrease";
612 auto* decFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvmInterfacePtrType}, false);
613 auto* decFunction = llvm::Function::Create(decFuncType, llvm::Function::LinkOnceODRLinkage, decFuncName, llvmModCtx.TheModule.get());
614 decFunction->addFnAttr(llvm::Attribute::AlwaysInline);
615#ifdef _WIN32
616 llvm::Comdat *decC = llvmModCtx.TheModule->getOrInsertComdat(decFuncName);
617 decC->setSelectionKind(llvm::Comdat::Any);
618 decFunction->setComdat(decC);
619#endif
620 llvmModCtx.functionMap[string2wstring(decFuncName)] = decFunction;
621 }
622 }
623
624 void LLVMCodegen::generateInterfaceObjectGCFunctionImplementations(LLVMModuleContext &llvmModCtx) {
625 for (const auto& interfaceDefPair : yoiModule->interfaceTable) {
626 auto interfaceDef = interfaceDefPair.second;
627 // if (compilerCtx->getImportedModule(interfaceDef->linkedModuleId)->modulePath != llvmModCtx.absolute_path)
628 // continue;
629 auto interfaceIdx = yoiModule->interfaceTable.getIndex(interfaceDef->name);
630 auto moduleID = yoiModule->identifier;
631 auto key = std::make_tuple(IRValueType::valueType::interfaceObject, moduleID, interfaceIdx);
632 auto* llvmInterfaceType = llvmModCtx.structTypeMap.at(key);
633 auto* llvmInterfacePtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
634 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
635 auto* gcFuncTypeForDispatch = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), { i8PtrTy }, false);
636 auto* gcFuncPtrTypeForDispatch = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
637
638 auto incFuncName = "interface_" + std::to_string(moduleID) + "_" + std::to_string(interfaceIdx) + "_gc_refcount_increase";
639 auto incFunction = llvmModCtx.functionMap[string2wstring(incFuncName)];
640
641 auto* incEntryBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", incFunction);
642
643 llvmModCtx.Builder->SetInsertPoint(incEntryBlock);
644 llvm::Value* thisPtr = incFunction->arg_begin();
645
646 llvm::Value* incRefCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmInterfaceType, thisPtr, 0, "refcount_ptr");
647 // llvm::Value* incOldRefCount = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), incRefCountPtr, "old_refcount");
648 auto beforeInc = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Add, incRefCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
649 llvmModCtx.Builder->CreateRetVoid();
650
651 auto decFuncName = "interface_" + std::to_string(moduleID) + "_" + std::to_string(interfaceIdx) + "_gc_refcount_decrease";
652 auto decFunction = llvmModCtx.functionMap[string2wstring(decFuncName)];
653 llvmModCtx.functionMap[string2wstring(decFuncName)] = decFunction;
654
655 auto* decEntryBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", decFunction);
656 auto* decFinalizeBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "finalize", decFunction);
657 auto* decContinueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue", decFunction);
658
659 llvmModCtx.Builder->SetInsertPoint(decEntryBlock);
660 thisPtr = decFunction->arg_begin();
661
662 llvm::Value* decRefCountPtr = llvmModCtx.Builder->CreateStructGEP(llvmInterfaceType, thisPtr, 0, "refcount_ptr");
663 // llvm::Value* decOldRefCount = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), decRefCountPtr, "old_refcount");
664 // llvm::Value* decNewRefCount = llvmModCtx.Builder->CreateSub(decOldRefCount, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), "new_refcount");
665 // llvmModCtx.Builder->CreateStore(decNewRefCount, decRefCountPtr);
666 auto beforeDec = llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::Sub, decRefCountPtr, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
667
668 llvm::Value* shouldFinalize = llvmModCtx.Builder->CreateICmpSLE(beforeDec, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), "should_finalize");
669 llvmModCtx.Builder->CreateCondBr(shouldFinalize, decFinalizeBlock, decContinueBlock);
670
671 llvmModCtx.Builder->SetInsertPoint(decFinalizeBlock);
672
673 // Nullify all weak slots pointing to this interface before freeing
674 auto weakSlotsHeadIdx = llvmInterfaceType->getNumElements() - 1;
675 auto* weakSlotsHeadPtr = llvmModCtx.Builder->CreateStructGEP(llvmInterfaceType, thisPtr, weakSlotsHeadIdx, "weak_slots_head_ptr");
676 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_weak_slot_nullify_all"), {weakSlotsHeadPtr});
677
678 auto* concreteThisPtr = llvmModCtx.Builder->CreateStructGEP(llvmInterfaceType, thisPtr, 2, "this_ptr_field");
679 auto* loadedConcreteThis = llvmModCtx.Builder->CreateLoad(i8PtrTy, concreteThisPtr, "concrete_this");
680
681 llvm::Value* gcDecSlotPtr = llvmModCtx.Builder->CreateStructGEP(llvmInterfaceType, thisPtr, 4, "gc_dec_slot");
682 llvm::Value* gcDecFuncPtr = llvmModCtx.Builder->CreateLoad(gcFuncPtrTypeForDispatch, gcDecSlotPtr, "gc_func_ptr");
683 llvmModCtx.Builder->CreateCall(gcFuncTypeForDispatch, gcDecFuncPtr, {loadedConcreteThis});
684
685 llvm::Value* castedInterfacePtr = llvmModCtx.Builder->CreateBitCast(thisPtr, i8PtrTy);
686 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"finalize_object"), castedInterfacePtr);
687 llvmModCtx.Builder->CreateBr(decContinueBlock);
688 llvmModCtx.Builder->SetInsertPoint(decContinueBlock);
689 llvmModCtx.Builder->CreateRetVoid();
690 }
691 }
692
693
694 void LLVMCodegen::generateFunctionImplementations(LLVMModuleContext &llvmModCtx) {
695 for (auto& funcPair : yoiModule->functionTable) {
696 if (funcPair.second->hasAttribute(IRFunctionDefinition::FunctionAttrs::Unreachable))
697 continue;
698
699 if (compilerCtx->getImportedModule(funcPair.second->linkedModuleId)->modulePath != llvmModCtx.absolute_path) {
700 continue;
701 }
702
703 if (!funcPair.second->codeBlock.empty()) {
704 llvmModCtx.currentFunctionDef = funcPair.second;
705 generateFunction(llvmModCtx, *funcPair.second);
706 }
707 }
708 }
709
710 void LLVMCodegen::generateFunction(LLVMModuleContext &llvmModCtx, IRFunctionDefinition& funcDef) {
711 llvmModCtx.currentFunction = llvmModCtx.functionMap.at(funcDef.name);
712 if (funcDef.codeBlock.empty()) return;
713
714 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
715 auto sourceFileKey = funcDef.debugInfo.sourceFile == L"<entry>" ? L"builtin" : funcDef.debugInfo.sourceFile;
716 if (auto it = llvmModCtx.compileUnits.find(sourceFileKey); it == llvmModCtx.compileUnits.end()) {
717 std::filesystem::path sourceFile = std::filesystem::path(sourceFileKey);
718
719 llvmModCtx.compileUnits[sourceFileKey] = llvmModCtx.DBuilder->createCompileUnit(
720 llvm::dwarf::DW_LANG_C,
721 llvmModCtx.DBuilder->createFile(sourceFile.filename().string(), sourceFile.parent_path().string()),
722 "hoshi-lang",
723 false,
724 "",
725 0
726 );
727 }
728 auto diFile = llvmModCtx.compileUnits[sourceFileKey];
729
730 llvm::SmallVector<llvm::Metadata *, 8> argsDIInfo;
731 if (funcDef.returnType->type == IRValueType::valueType::none) {
732 argsDIInfo.push_back(nullptr);
733 } else {
734 argsDIInfo.push_back(getDIType(llvmModCtx, funcDef.returnType));
735 }
736
737 for (const auto& argType : funcDef.argumentTypes) {
738 argsDIInfo.push_back(getDIType(llvmModCtx, argType));
739 }
740
741 auto *subroutineType = llvmModCtx.DBuilder->createSubroutineType(llvmModCtx.DBuilder->getOrCreateTypeArray(argsDIInfo));
742
743 auto *sp = llvmModCtx.DBuilder->createFunction(
744 (llvm::DIScope*) diFile->getFile(),
745 yoi::wstring2string(funcDef.name),
746 "",
747 diFile->getFile(),
748 funcDef.debugInfo.line + 1,
749 subroutineType,
750 funcDef.debugInfo.line + 1,
751 llvm::DINode::FlagPrototyped,
752 llvm::DISubprogram::SPFlagDefinition
753 );
754 llvmModCtx.currentFunction->setSubprogram(sp);
755 }
756
758 llvmModCtx.valueStackPhi.clear();
759 llvmModCtx.basicBlockMap.clear();
760 llvmModCtx.basicBlockVisited.clear();
761
762 llvmModCtx.basicBlockMap[0] = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", llvmModCtx.currentFunction);
763 auto* entryBlock = llvmModCtx.basicBlockMap[0];
764 llvmModCtx.Builder->SetInsertPoint(entryBlock);
765
766 // invoke runtime_debug_report_current_function
768 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug){
769 llvmModCtx.Builder->SetCurrentDebugLocation({llvm::DILocation::get(*llvmModCtx.TheContext, funcDef.debugInfo.line + 1, funcDef.debugInfo.column + 1, llvmModCtx.currentFunction->getSubprogram())});
770 std::string funcName = wstring2string(funcDef.name);
771 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
772 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
773 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
774 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_current_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
775 }
776
777
778 llvmModCtx.namedValues.clear();
779 auto& varTableRef = funcDef.getVariableTable();
780
781 // Allocate space for all local variables (args + locals) and init to null
782 auto& vars = varTableRef.getVariables();
783 auto& names = varTableRef.getReversedVariableNameMap();
784 for (yoi::indexT i = 0; i < vars.size(); ++i) {
785 vars[i] = managedPtr(IRValueType{*(vars[i])}.addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope));
786 auto* llvmType = yoiTypeToLLVMType(llvmModCtx, vars[i], vars[i]->isBasicRawType() || vars[i]->hasAttribute(IRValueType::ValueAttr::Raw));
787 auto* alloca = llvmModCtx.Builder->CreateAlloca(llvmType, nullptr, wstring2string(names.at(i)));
788 llvmModCtx.Builder->CreateStore(llvm::Constant::getNullValue(llvmType), alloca);
789 llvmModCtx.namedValues[i] = alloca;
790
791 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
792 std::filesystem::path sourcePath(funcDef.debugInfo.sourceFile);
793 auto* DILocalVar = llvmModCtx.DBuilder->createAutoVariable(
794 llvmModCtx.currentFunction->getSubprogram(),
795 wstring2string(names.at(i)),
796 llvmModCtx.DBuilder->createFile(sourcePath.filename().string(), sourcePath.parent_path().string()),
797 funcDef.debugInfo.line + 1,
798 getDIType(llvmModCtx, vars[i])
799 );
800
801 llvmModCtx.DBuilder->insertDeclare(
802 alloca, // The memory location of the variable
803 DILocalVar, // The debug info for the variable
804 llvmModCtx.DBuilder->createExpression(), // An empty expression
805 llvm::DILocation::get(*llvmModCtx.TheContext, funcDef.debugInfo.line, 1, llvmModCtx.currentFunction->getSubprogram()),
806 llvmModCtx.Builder->GetInsertBlock()
807 );
808 }
809 }
810
811 // Store incoming arguments into their allocas, handling reference counts
812 auto arg_it = llvmModCtx.currentFunction->arg_begin();
813 for (yoi::indexT i = 0; i < funcDef.argumentTypes.size(); ++i, ++arg_it) {
814 auto* alloca = llvmModCtx.namedValues.at(i);
815 // Arguments are considered "retained" by the callee
816 llvmModCtx.Builder->CreateStore(arg_it, alloca);
817 }
818
819 generateCodeBlock(llvmModCtx, *funcDef.codeBlock[0], 0, 0);
820
821 if (!llvmModCtx.Builder->GetInsertBlock()->getTerminator()) {
822 // A. Create the Final Suspend Block
823 auto finalSuspendBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "final_suspend", llvmModCtx.currentFunction);
824
825 // B. Jump from the last 'resumeBB' to here
826 llvmModCtx.Builder->CreateBr(finalSuspendBB);
827 llvmModCtx.Builder->SetInsertPoint(finalSuspendBB);
828
829 // C. Emit llvm.coro.suspend(token, TRUE)
830 // TRUE means "Final Suspend" (The coroutine is Done)
831 auto coro_suspend = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_suspend);
832 auto suspend_result =
833 llvmModCtx.Builder->CreateCall(coro_suspend,
834 {
835 llvm::ConstantTokenNone::get(*llvmModCtx.TheContext),
836 llvm::ConstantInt::get(llvm::Type::getInt1Ty(*llvmModCtx.TheContext), 1) // <--- TRUE HERE
837 });
838
839 // D. Create the Trap Block (Resuming a finished coroutine is illegal)
840 auto trapBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "trap", llvmModCtx.currentFunction);
841
842 // E. Create the Switch
843 // 0 (Resume) -> Trap (Cannot resume a finished coroutine)
844 // 1 (Destroy) -> Cleanup (Standard cleanup path)
845 // Default -> Suspend (Return control to caller one last time)
846 auto *switch_inst = llvmModCtx.Builder->CreateSwitch(suspend_result, llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB, 2);
847
848 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 0), trapBB);
849 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 1),
851
852 // F. Implement Trap
853 llvmModCtx.Builder->SetInsertPoint(trapBB);
854 llvmModCtx.Builder->CreateUnreachable();
855 }
856
857 llvmModCtx.DBuilder->finalize();
858 // if (llvmModCtx.currentFunction->getName() == "0_int_gen#") {
859 // llvmModCtx.TheModule->print(llvm::errs(), nullptr);
860 // }
861 if (llvm::verifyFunction(*llvmModCtx.currentFunction, &llvm::errs())) {
862 llvmModCtx.TheModule->print(llvm::errs(), nullptr);
863 panic(funcDef.debugInfo.line, funcDef.debugInfo.column, "LLVM function verification failed for: " + wstring2string(funcDef.name));
864 }
865 }
866
867 void LLVMCodegen::generateFunctionExitCleanup(LLVMModuleContext &llvmModCtx) {
868 for (const auto& pair : llvmModCtx.namedValues) {
869 auto varIndex = pair.first;
870 auto* alloca = pair.second;
871 auto varYoiType = llvmModCtx.currentFunctionDef->variableTable.get(varIndex);
872
873 if (varYoiType->isBasicRawType() || varYoiType->hasAttribute(IRValueType::ValueAttr::Raw))
874 continue;
875
876 // Load the final pointer value from the local variable
877 auto* objPtr = llvmModCtx.Builder->CreateLoad(alloca->getAllocatedType(), alloca, "cleanup_load");
878
879 // Decrease its reference count
880 if (varYoiType->hasAttribute(IRValueType::ValueAttr::Nullable))
881 callGcFunction(llvmModCtx, objPtr, varYoiType, false, true);
882 else
883 generateIfTargetNotNull(llvmModCtx, objPtr, varYoiType, [&] () {
884 callGcFunction(llvmModCtx, objPtr, varYoiType, false, true);
885 }, true);
886 }
887 }
888
889 void LLVMCodegen::generateCodeBlock(LLVMModuleContext &llvmModCtx, IRCodeBlock& block, yoi::indexT fromBlock, yoi::indexT toBlock, llvm::BasicBlock *actualFromBlock) {
890 // check whether generated
891 if (llvmModCtx.basicBlockVisited.contains(toBlock) && toBlock != 0) {
892 // merge stack values
893 llvmModCtx.valueStackPhi.enterNode(toBlock, fromBlock, llvmModCtx.basicBlockMap.at(toBlock), actualFromBlock ? actualFromBlock : llvmModCtx.basicBlockMap.at(fromBlock));
894 llvmModCtx.valueStackPhi.finalizeNode();
895 return;
896 }
897 llvmModCtx.basicBlockVisited[toBlock] = true;
898
899 llvmModCtx.Builder->SetInsertPoint(llvmModCtx.basicBlockMap.at(toBlock));
900 if (llvmModCtx.Builder->GetInsertBlock()->getTerminator()) return;
901
902 for (const auto& succ : llvmModCtx.controlFlowAnalysis.G[toBlock]) {
903 if (!llvmModCtx.basicBlockMap.contains(succ)) {
904 llvmModCtx.basicBlockMap[succ] = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "block_" + std::to_string(succ), llvmModCtx.currentFunction);
905 }
906 }
907
908 llvmModCtx.valueStackPhi.enterNode(toBlock, fromBlock, llvmModCtx.basicBlockMap.at(toBlock), actualFromBlock ? actualFromBlock : llvmModCtx.basicBlockMap.at(fromBlock));
909
910 if (fromBlock == toBlock && toBlock == 0 && llvmModCtx.currentFunctionDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::Generator)) {
911 generateGeneratorContextInitialization(llvmModCtx);
912 }
913
914 llvm::BasicBlock *actual_from_block_for_next = llvmModCtx.basicBlockMap.at(toBlock);
915
916 for (const auto& instr : block.getIRArray()) {
917 generateInstruction(llvmModCtx, instr, fromBlock, toBlock);
918 if (llvmModCtx.Builder->GetInsertBlock()->getTerminator()) {
919 actual_from_block_for_next = llvmModCtx.Builder->GetInsertBlock();
920 break;
921 }
922 }
923
924 llvmModCtx.valueStackPhi.finalizeNode();
925
926 for (const auto& succ : llvmModCtx.controlFlowAnalysis.G[toBlock]) {
927 // prepare the value stack for the next block
928 generateCodeBlock(llvmModCtx, *llvmModCtx.currentFunctionDef->codeBlock[succ], toBlock, succ, actual_from_block_for_next);
929 }
930 }
931
932 void LLVMCodegen::generateInstruction(LLVMModuleContext &llvmModCtx, const IR& instr, yoi::indexT fromBlock, yoi::indexT toBlock) {
933 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
934 auto scope = llvmModCtx.currentFunction->getSubprogram();
936 llvmModCtx.Builder->SetCurrentDebugLocation(llvm::DILocation::get(*llvmModCtx.TheContext, instr.debugInfo.line + 1, instr.debugInfo.column + 1, scope));
937 // insert call to runtime_debug_print extern func
938 std::string debugStr = "Performing: " + yoi::wstring2string(instr.to_string());
939 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, debugStr, true);
940 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
941 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
942 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print"), llvm::ArrayRef<llvm::Value*>(debugArgs));
943 }
944 switch(instr.opcode) {
945 case IR::Opcode::push_integer: {
946 auto val = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), instr.operands[0].value.integer, true);
947 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getIntObjectType()->getBasicRawType())});
948 break;
949 }
950 case IR::Opcode::push_decimal: {
951 auto val = llvm::ConstantFP::get(llvmModCtx.Builder->getDoubleTy(), instr.operands[0].value.decimal);
952 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getDeciObjectType()->getBasicRawType())});
953 break;
954 }
955 case IR::Opcode::push_boolean: {
956 auto val = llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), instr.operands[0].value.boolean);
957 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
958 break;
959 }
960 case IR::Opcode::push_short: {
961 auto val = llvm::ConstantInt::get(llvmModCtx.Builder->getInt16Ty(), instr.operands[0].value.shortV);
962 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getShortObjectType()->getBasicRawType())});
963 break;
964 }
965 case IR::Opcode::push_unsigned: {
966 auto val = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), instr.operands[0].value.unsignedV, false);
967 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getUnsignedObjectType()->getBasicRawType())});
968 break;
969 }
970 case IR::Opcode::push_string: {
971 auto& str = yoiModule->stringLiteralPool.getStringLiteral(instr.operands[1].value.stringLiteralIndex);
972 // Create a global string literal for this string
973 auto *literal = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, yoi::wstring2string(str), true);
974 auto *globalStr = llvmModCtx.Builder->CreateGlobalString(wstring2string(str), "global_string_literal");
975 llvmModCtx.valueStackPhi.push_back({globalStr, managedPtr(compilerCtx->getStrObjectType()->getBasicRawType())});
976 break;
977 }
978 case IR::Opcode::push_character: {
979 auto val = llvm::ConstantInt::get(llvmModCtx.Builder->getInt8Ty(), instr.operands[0].value.character);
980 llvmModCtx.valueStackPhi.push_back({val, managedPtr(compilerCtx->getCharObjectType()->getBasicRawType())});
981 break;
982 }
983 // Basic Type Casting
984 case IR::Opcode::basic_cast_char: {
985 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
986 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
987 llvm::Value* castedVal = nullptr;
988
989 if (rawVal->getType()->isDoubleTy()) {
990 castedVal = llvmModCtx.Builder->CreateFPToSI(rawVal, llvmModCtx.Builder->getInt8Ty(), "deci_to_char_cast");
991 } else if (rawVal->getType()->isIntegerTy(1)) { // bool
992 castedVal = llvmModCtx.Builder->CreateTrunc(rawVal, llvmModCtx.Builder->getInt8Ty(), "bool_to_char_cast");
993 } else if (rawVal->getType()->isIntegerTy(64)) { // int (no-op)
994 castedVal = llvmModCtx.Builder->CreateTrunc(rawVal, llvmModCtx.Builder->getInt8Ty(), "int_to_char_cast");
995 } else if (rawVal->getType()->isIntegerTy(8)) { // char (no-op)
996 castedVal = rawVal;
997 } else {
998 panic(instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: Unsupported type for basic_cast_char");
999 }
1000
1001 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getCharObjectType()->getBasicRawType())});
1002 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1003 break;
1004 }
1005 case IR::Opcode::basic_cast_int: {
1006 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1007 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1008 llvm::Value* castedVal = nullptr;
1009
1010 if (rawVal->getType()->isDoubleTy()) {
1011 castedVal = llvmModCtx.Builder->CreateFPToSI(rawVal, llvmModCtx.Builder->getInt64Ty(), "deci_to_int_cast");
1012 } else if (rawVal->getType()->isIntegerTy(1)) { // bool
1013 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "bool_to_int_cast");
1014 } else if (rawVal->getType()->isIntegerTy(8)) { // char
1015 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "char_to_int_cast");
1016 } else if (rawVal->getType()->isIntegerTy(16)) { // short
1017 castedVal = llvmModCtx.Builder->CreateSExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "short_to_int_cast");
1018 } else if (rawVal->getType()->isIntegerTy(64)) { // int (no-op)
1019 castedVal = rawVal;
1020 } else {
1021 panic(0, 0, "LLVM Codegen: Unsupported type for basic_cast_int");
1022 }
1023
1024 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getIntObjectType()->getBasicRawType())});
1025 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1026 break;
1027 }
1028 case IR::Opcode::basic_cast_deci: {
1029 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1030 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1031 llvm::Value* castedVal = nullptr;
1032
1033 if (rawVal->getType()->isIntegerTy(64)) { // int
1034 castedVal = llvmModCtx.Builder->CreateSIToFP(rawVal, llvmModCtx.Builder->getDoubleTy(), "int_to_deci_cast");
1035 } else if (rawVal->getType()->isIntegerTy(1)) { // bool
1036 castedVal = llvmModCtx.Builder->CreateUIToFP(rawVal, llvmModCtx.Builder->getDoubleTy(), "bool_to_deci_cast");
1037 } else if (rawVal->getType()->isIntegerTy(8)) { // char
1038 castedVal = llvmModCtx.Builder->CreateUIToFP(rawVal, llvmModCtx.Builder->getDoubleTy(), "char_to_deci_cast");
1039 } else if (rawVal->getType()->isIntegerTy(16)) { // short
1040 castedVal = llvmModCtx.Builder->CreateSIToFP(rawVal, llvmModCtx.Builder->getDoubleTy(), "short_to_deci_cast");
1041 } else if (rawVal->getType()->isDoubleTy()) { // deci (no-op)
1042 castedVal = rawVal;
1043 } else {
1044 panic(0, 0, "LLVM Codegen: Unsupported type for basic_cast_deci");
1045 }
1046
1047 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getDeciObjectType()->getBasicRawType())});
1048 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1049 break;
1050 }
1051 case IR::Opcode::basic_cast_unsigned: {
1052 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1053 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1054 llvm::Value* castedVal = nullptr;
1055
1056 if (rawVal->getType()->isIntegerTy(64)) { // int
1057 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "int_to_unsigned_cast");
1058 } else if (rawVal->getType()->isDoubleTy()) { // deci
1059 castedVal = llvmModCtx.Builder->CreateFPToUI(rawVal, llvmModCtx.Builder->getInt64Ty(), "deci_to_unsigned_cast");
1060 } else if (rawVal->getType()->isIntegerTy(16)) { // short
1061 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "short_to_unsigned_cast");
1062 } else if (rawVal->getType()->isIntegerTy(8)) { // char
1063 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "char_to_unsigned_cast");
1064 } else if (rawVal->getType()->isIntegerTy(1)) { // bool
1065 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt64Ty(), "bool_to_unsigned_cast");
1066 } else {
1067 panic(0, 0, "LLVM Codegen: Unsupported type for basic_cast_unsigned");
1068 }
1069
1070 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getUnsignedObjectType()->getBasicRawType())});
1071 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1072 break;
1073 }
1074 case IR::Opcode::basic_cast_short: {
1075 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1076 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1077 llvm::Value* castedVal = nullptr;
1078
1079 if (rawVal->getType()->isIntegerTy(64)) { // int
1080 castedVal = llvmModCtx.Builder->CreateTrunc(rawVal, llvmModCtx.Builder->getInt16Ty(), "int_to_short_cast");
1081 } else if (rawVal->getType()->isDoubleTy()) { // deci
1082 castedVal = llvmModCtx.Builder->CreateFPToSI(rawVal, llvmModCtx.Builder->getInt16Ty(), "deci_to_short_cast");
1083 } else if (rawVal->getType()->isIntegerTy(8)) { // char
1084 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt16Ty(), "char_to_short_cast");
1085 } else if (rawVal->getType()->isIntegerTy(1)) { // bool
1086 castedVal = llvmModCtx.Builder->CreateZExt(rawVal, llvmModCtx.Builder->getInt16Ty(), "bool_to_short_cast");
1087 } else {
1088 panic(0, 0, "LLVM Codegen: Unsupported type for basic_cast_short");
1089 }
1090
1091 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getShortObjectType()->getBasicRawType())});
1092 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1093 break;
1094 }
1095 case IR::Opcode::basic_cast_bool: {
1096 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1097 llvm::Value* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1098 llvm::Value* castedVal = nullptr;
1099
1100 if (rawVal->getType()->isIntegerTy(64)) { // int
1101 castedVal = llvmModCtx.Builder->CreateICmpNE(rawVal, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 0), "int_to_bool_cast");
1102 } else if (rawVal->getType()->isDoubleTy()) { // deci
1103 castedVal = llvmModCtx.Builder->CreateFCmpONE(rawVal, llvm::ConstantFP::get(llvmModCtx.Builder->getDoubleTy(), 0.0), "deci_to_bool_cast");
1104 } else if (rawVal->getType()->isIntegerTy(16)) { // short
1105 castedVal = llvmModCtx.Builder->CreateICmpNE(rawVal, llvm::ConstantInt::get(llvmModCtx.Builder->getInt8Ty(), 0), "short_to_bool_cast");
1106 } else if (rawVal->getType()->isIntegerTy(8)) { // char
1107 castedVal = llvmModCtx.Builder->CreateICmpNE(rawVal, llvm::ConstantInt::get(llvmModCtx.Builder->getInt8Ty(), 0), "char_to_bool_cast");
1108 } else if (rawVal->getType()->isIntegerTy(1)) { // bool (no-op)
1109 castedVal = rawVal;
1110 } else {
1111 panic(0, 0, "LLVM Codegen: Unsupported type for basic_cast_bool");
1112 }
1113
1114 llvmModCtx.valueStackPhi.push_back({castedVal, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
1115 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1116 break;
1117 }
1118 // Arithmetic
1119 case IR::Opcode::add: handleBinaryOp(llvmModCtx, llvm::Instruction::Add, false, fromBlock, toBlock); break;
1120 case IR::Opcode::sub: handleBinaryOp(llvmModCtx, llvm::Instruction::Sub, false, fromBlock, toBlock); break;
1121 case IR::Opcode::mul: handleBinaryOp(llvmModCtx, llvm::Instruction::Mul, false, fromBlock, toBlock); break;
1122 case IR::Opcode::div: handleBinaryOp(llvmModCtx, llvm::Instruction::SDiv, false, fromBlock, toBlock); break;
1123 case IR::Opcode::mod: handleBinaryOp(llvmModCtx, llvm::Instruction::SRem, false, fromBlock, toBlock); break;
1124 case IR::Opcode::bitwise_and: handleBinaryOp(llvmModCtx, llvm::Instruction::And, false, fromBlock, toBlock); break;
1125 case IR::Opcode::bitwise_or: handleBinaryOp(llvmModCtx, llvm::Instruction::Or, false, fromBlock, toBlock); break;
1126 case IR::Opcode::bitwise_xor: handleBinaryOp(llvmModCtx, llvm::Instruction::Xor, false, fromBlock, toBlock); break;
1127 case IR::Opcode::left_shift: handleBinaryOp(llvmModCtx, llvm::Instruction::Shl, false, fromBlock, toBlock); break;
1128 case IR::Opcode::right_shift: handleBinaryOp(llvmModCtx, llvm::Instruction::LShr, false, fromBlock, toBlock); break;
1129 // Unary
1130 case IR::Opcode::negate: {
1131 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1132 auto* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1133 auto* negatedRaw = rawVal->getType()->isDoubleTy() ? llvmModCtx.Builder->CreateFNeg(rawVal, "negtmp") : llvmModCtx.Builder->CreateNeg(rawVal, "negtmp");
1134 // llvmModCtx.valueStackPhi.push_back({resultObj, val.yoiType});
1135 llvmModCtx.valueStackPhi.push_back({negatedRaw, managedPtr(val.yoiType->getBasicRawType())});
1136 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1137 break;
1138 }
1139 case IR::Opcode::bitwise_not: {
1140 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1141 auto* rawVal = unboxValue(llvmModCtx, val.llvmValue, val.yoiType);
1142 auto* notRaw = llvmModCtx.Builder->CreateNot(rawVal, "nottmp");
1143 // auto* resultObj = createBasicObject(llvmModCtx, val.yoiType, notRaw);
1144 llvmModCtx.valueStackPhi.push_back({notRaw, managedPtr(val.yoiType->getBasicRawType())});
1145 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false); // Consume operand
1146 break;
1147 }
1148
1149 // Comparison
1150 case IR::Opcode::equal: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_EQ, false, fromBlock, toBlock); break;
1151 case IR::Opcode::not_equal: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_NE, false, fromBlock, toBlock); break;
1152 case IR::Opcode::less_than: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_SLT, false, fromBlock, toBlock); break;
1153 case IR::Opcode::less_equal: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_SLE, false, fromBlock, toBlock); break;
1154 case IR::Opcode::greater_than: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_SGT, false, fromBlock, toBlock); break;
1155 case IR::Opcode::greater_equal: handleComparison(llvmModCtx, llvm::CmpInst::ICMP_SGE, false, fromBlock, toBlock); break;
1156
1157 // Memory
1158 case IR::Opcode::load_local: {
1159 auto varIndex = instr.operands[0].value.symbolIndex;
1160 auto* alloca = llvmModCtx.namedValues.at(varIndex);
1161 auto yoiType = llvmModCtx.currentFunctionDef->variableTable.get(varIndex);
1162 if (yoiType->type == IRValueType::valueType::datastructObject && yoiType->hasAttribute(IRValueType::ValueAttr::Raw)) {
1163 llvmModCtx.valueStackPhi.push_back({alloca, yoiType});
1164 } else {
1165 auto loadedPtr = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, yoiType, yoiType->isBasicRawType() || yoiType->hasAttribute(IRValueType::ValueAttr::Raw)), alloca, "loadtmp");
1166 callGcFunction(llvmModCtx, loadedPtr, yoiType, true);
1167 llvmModCtx.valueStackPhi.push_back({loadedPtr, yoiType});
1168 }
1169 break;
1170 }
1171 case IR::Opcode::store_local: {
1172 auto varIndex = instr.operands[0].value.symbolIndex;
1173 auto* alloca = llvmModCtx.namedValues.at(varIndex);
1174 auto yoiType = llvmModCtx.currentFunctionDef->variableTable.get(varIndex);
1175 auto valToStore = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1176
1177 valToStore = yoiType->metadata.hasMetadata(L"regressed_interface_impl") ? valToStore : promiseInterfaceObjectIfInterface(llvmModCtx, valToStore);
1178
1179 // Release old value
1180 auto* oldPtr = llvmModCtx.Builder->CreateLoad(alloca->getAllocatedType(), alloca, "old_ptr_for_store");
1181 if (yoiType->hasAttribute(IRValueType::ValueAttr::Nullable))
1182 callGcFunction(llvmModCtx, oldPtr, yoiType, false, true);
1183 else
1184 generateIfTargetNotNull(llvmModCtx, oldPtr, yoiType, [&] () {
1185 callGcFunction(llvmModCtx, oldPtr, yoiType, false, true);
1186 }, !yoiType->hasAttribute(IRValueType::ValueAttr::Raw));
1187 // Store new value
1188 if (llvmModCtx.currentFunctionDef->variableTable.get(varIndex)->hasAttribute(IRValueType::ValueAttr::Raw)) {
1189 auto unboxedVal = unboxValue(llvmModCtx, valToStore.llvmValue, valToStore.yoiType);
1190 if (yoiType->type == IRValueType::valueType::datastructObject && yoiType->hasAttribute(IRValueType::ValueAttr::Raw)) {
1191 // create MemCpy
1192 auto fieldType = yoiTypeToLLVMType(llvmModCtx, yoiType, true);
1193 auto fieldSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(fieldType);
1194 llvmModCtx.Builder->CreateMemCpy(alloca, llvm::MaybeAlign(8), unboxedVal, llvm::MaybeAlign(8), fieldSize);
1195 } else {
1196 llvmModCtx.Builder->CreateStore(unboxedVal, alloca);
1197 }
1198 } else {
1199 auto object = ensureObject(llvmModCtx, valToStore.yoiType, valToStore.llvmValue);
1200 if (object.first->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1201 callGcFunction(llvmModCtx, object.second, valToStore.yoiType, true, true, true);
1202 llvmModCtx.Builder->CreateStore(object.second, alloca);
1203 }
1204
1205 break;
1206 }
1207 case IR::Opcode::load_global: {
1208 auto varIndex = instr.operands[1].value.symbolIndex;
1209 auto* global = llvmModCtx.globalValues.at(varIndex);
1210 auto yoiType = yoiModule ->globalVariables[varIndex];
1211 yoiType->addAttribute(IRValueType::ValueAttr::Nullable).addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope);
1212 auto loadedPtr = llvmModCtx.Builder->CreateLoad(global->getValueType(), global, "loadglobaltmp");
1213 llvmModCtx.valueStackPhi.push_back({loadedPtr, yoiType});
1214 break;
1215 }
1216 case IR::Opcode::store_global: {
1217 auto varIndex = instr.operands[1].value.symbolIndex;
1218 auto* global = llvmModCtx.globalValues.at(varIndex);
1219 auto yoiType = yoiModule->globalVariables[varIndex];
1220 auto valToStore = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1221
1222 valToStore = promiseInterfaceObjectIfInterface(llvmModCtx, valToStore);
1223
1224 yoiType->addAttribute(IRValueType::ValueAttr::Nullable);
1225
1226 auto* oldPtr = llvmModCtx.Builder->CreateLoad(global->getValueType(), global, "old_global_ptr");
1227 callGcFunction(llvmModCtx, oldPtr, yoiType, false, true);
1228
1229 auto object = ensureObject(llvmModCtx, valToStore.yoiType, valToStore.llvmValue);
1230 if (object.first->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1231 callGcFunction(llvmModCtx, object.second, valToStore.yoiType, true, true, true);
1232
1233 llvmModCtx.Builder->CreateStore(object.second, global);
1234 break;
1235 }
1236 case IR::Opcode::load_member: {
1237 auto structVal = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1238 auto memberIndex = instr.operands[0].value.symbolIndex;
1239 auto llvmMemberIndex = memberIndex + 2; // +2 to skip gc_refcount header and type index
1240
1241 auto key = std::make_tuple(IRValueType::valueType::structObject, structVal.yoiType->typeAffiliateModule, structVal.yoiType->typeIndex);
1242 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
1243 auto* gep = llvmModCtx.Builder->CreateStructGEP(llvmStructType, structVal.llvmValue, llvmMemberIndex, "memberptr");
1244
1245 auto yoiStructDef = compilerCtx->getIRObjectFile()->compiledModule->structTable[std::get<2>(key)];
1246 auto memberYoiType = yoiStructDef->fieldTypes[memberIndex];
1247 if (structVal.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1248 memberYoiType = managedPtr(IRValueType{*memberYoiType}.addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope));
1249
1250 // WeakRef path: load through WeakSlot, check if target is alive
1251 if (memberYoiType->hasAttribute(IRValueType::ValueAttr::WeakRef)) {
1252 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
1253 auto* slot = llvmModCtx.Builder->CreateLoad(i8PtrTy, gep, "weak_slot");
1254 auto* hasSlot = llvmModCtx.Builder->CreateIsNotNull(slot, "has_weak_slot");
1255 auto* loadTargetBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "load_weak_target", llvmModCtx.currentFunction);
1256 auto* pushNullBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "push_null_weak", llvmModCtx.currentFunction);
1257 auto* doneWeakLoadBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "done_weak_load", llvmModCtx.currentFunction);
1258 llvmModCtx.Builder->CreateCondBr(hasSlot, loadTargetBB, pushNullBB);
1259
1260 llvmModCtx.Builder->SetInsertPoint(loadTargetBB);
1261 auto* targetPtr = llvmModCtx.Builder->CreateLoad(i8PtrTy, slot, "weak_target_ptr");
1262 auto* hasTarget = llvmModCtx.Builder->CreateIsNotNull(targetPtr, "has_weak_target");
1263 auto* retainTargetBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "retain_weak_target", llvmModCtx.currentFunction);
1264 llvmModCtx.Builder->CreateCondBr(hasTarget, retainTargetBB, pushNullBB);
1265
1266 llvmModCtx.Builder->SetInsertPoint(retainTargetBB);
1267 auto loadedType = managedPtr(IRValueType{*memberYoiType}.removeAttribute(IRValueType::ValueAttr::WeakRef));
1268 callGcFunction(llvmModCtx, targetPtr, loadedType, true);
1269 llvmModCtx.Builder->CreateBr(doneWeakLoadBB);
1270
1271 llvmModCtx.Builder->SetInsertPoint(pushNullBB);
1272 llvmModCtx.Builder->CreateBr(doneWeakLoadBB);
1273
1274 llvmModCtx.Builder->SetInsertPoint(doneWeakLoadBB);
1275 auto nullType = managedPtr(IRValueType{*memberYoiType}.removeAttribute(IRValueType::ValueAttr::WeakRef));
1276 auto* phi = llvmModCtx.Builder->CreatePHI(i8PtrTy, 2, "weak_load_phi");
1277 phi->addIncoming(targetPtr, retainTargetBB);
1278 phi->addIncoming(llvm::ConstantPointerNull::get(i8PtrTy), pushNullBB);
1279 llvmModCtx.valueStackPhi.push_back({phi, nullType});
1280 callGcFunction(llvmModCtx, structVal.llvmValue, structVal.yoiType, false);
1281 break;
1282 }
1283
1284 llvm::Type* loadedType = yoiTypeToLLVMType(llvmModCtx, memberYoiType, memberYoiType->isBasicType() && memberYoiType->hasAttribute(IRValueType::ValueAttr::Raw));
1285 auto* loadedMember =
1286 memberYoiType->type == IRValueType::valueType::datastructObject
1287 ? gep
1288 : llvmModCtx.Builder->CreateLoad(loadedType, gep, "loadmember");
1289 callGcFunction(llvmModCtx, loadedMember, memberYoiType, true); // Create new reference for the loaded member
1290 llvmModCtx.valueStackPhi.push_back({loadedMember, memberYoiType});
1291
1292 callGcFunction(llvmModCtx, structVal.llvmValue, structVal.yoiType, false); // Consume the struct reference from the stack
1293 break;
1294 }
1295 case IR::Opcode::store_member: {
1296 auto structVal = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1297 auto valueToStore = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1298 valueToStore = promiseInterfaceObjectIfInterface(llvmModCtx, valueToStore);
1299
1300 auto memberIndex = instr.operands[0].value.symbolIndex;
1301 storeMember(llvmModCtx, valueToStore, structVal, memberIndex);
1302
1303 callGcFunction(llvmModCtx, structVal.llvmValue, structVal.yoiType, false);
1304 break;
1305 }
1306 // Control Flow
1307 case IR::Opcode::jump: {
1308 llvmModCtx.Builder->CreateBr(llvmModCtx.basicBlockMap.at(instr.operands[0].value.codeBlockIndex));
1309 break;
1310 }
1311 case IR::Opcode::jump_if_true:
1312 case IR::Opcode::jump_if_false: {
1313 auto condObj = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1314 auto* condRaw = unboxValue(llvmModCtx, condObj.llvmValue, condObj.yoiType);
1315 callGcFunction(llvmModCtx, condObj.llvmValue, condObj.yoiType, false);
1316
1317 auto* destBlock = llvmModCtx.basicBlockMap.at(instr.operands[0].value.codeBlockIndex);
1318 auto* nextBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "fallthrough", llvmModCtx.currentFunction);
1319
1320 if (instr.opcode == IR::Opcode::jump_if_true) {
1321 llvmModCtx.Builder->CreateCondBr(condRaw, destBlock, nextBlock);
1322 } else { // jump_if_false
1323 llvmModCtx.Builder->CreateCondBr(condRaw, nextBlock, destBlock);
1324 }
1325 llvmModCtx.Builder->SetInsertPoint(nextBlock);
1326 break;
1327 }
1328
1329 case IR::Opcode::ret: {
1330 auto retVal = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1331
1332 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
1333 std::string funcName = wstring2string(llvmModCtx.currentFunctionDef->name);
1334 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
1335 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
1336 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
1337 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_leave_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
1338 }
1339
1340 retVal = promiseInterfaceObjectIfInterface(llvmModCtx, retVal);
1341
1342 // The caller receives ownership, so we don't decrease the ref count here.
1343 if (llvmModCtx.currentFunctionDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw)) {
1344 auto res = unboxValue(llvmModCtx, retVal.llvmValue, retVal.yoiType);
1345 // call gc function for the return value
1346 callGcFunction(llvmModCtx, retVal.llvmValue, retVal.yoiType, false);
1347 generateFunctionExitCleanup(llvmModCtx);
1348 llvmModCtx.Builder->CreateRet(res);
1349 } else {
1350 auto object = ensureObject(llvmModCtx, retVal.yoiType, retVal.llvmValue);
1351 if (object.first->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1352 callGcFunction(llvmModCtx, object.second, retVal.yoiType, true, true, true);
1353 generateFunctionExitCleanup(llvmModCtx);
1354 llvmModCtx.Builder->CreateRet(object.second);
1355 }
1356 break;
1357 }
1358 case IR::Opcode::ret_none: {
1359 generateFunctionExitCleanup(llvmModCtx);
1360
1361 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
1362 std::string funcName = wstring2string(llvmModCtx.currentFunctionDef->name);
1363 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
1364 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
1365 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
1366 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_leave_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
1367 }
1368
1369 llvmModCtx.Builder->CreateRetVoid();
1370 break;
1371 }
1372 // Functions
1373 case IR::Opcode::invoke: {
1374 auto moduleIndex = instr.operands[0].value.symbolIndex;
1375 auto funcIndex = instr.operands[1].value.symbolIndex;
1376 auto argCount = instr.operands[2].value.symbolIndex;
1377
1378 auto funcDef = yoiModule->functionTable[funcIndex];
1379 auto* function = llvmModCtx.functionMap.at(funcDef->name);
1380
1381 std::vector<llvm::Value*> args;
1382 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Value*>> postCleanup;
1383
1384 for(size_t i = 0; i < argCount; ++i) {
1385 auto arg = llvmModCtx.valueStackPhi.back();
1386 llvmModCtx.valueStackPhi.pop_back();
1387
1388 arg = promiseInterfaceObjectIfInterface(llvmModCtx, arg);
1389
1390 if (funcDef->argumentTypes[argCount - i - 1]->hasAttribute(IRValueType::ValueAttr::Raw)) {
1391 args.push_back(
1392 loadIfDataStructObject(llvmModCtx, funcDef->argumentTypes[argCount - i - 1],
1393 unboxValue(llvmModCtx, arg.llvmValue, arg.yoiType)));
1394 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, false);
1395 } else if (funcDef->argumentTypes[argCount - i - 1]->hasAttribute(IRValueType::ValueAttr::Borrow)) {
1396 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1397 args.push_back(object.second);
1398 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw));
1399 else postCleanup.push_back(object);
1400 } else {
1401 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1402 args.push_back(object.second);
1403 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw))
1404 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, true, true);
1405 else;
1406 }
1407 }
1408 std::reverse(args.begin(), args.end());
1409
1410 if (funcDef->returnType->type == IRValueType::valueType::none) {
1411 llvmModCtx.Builder->CreateCall(function, args);
1412 } else {
1413 auto* call = llvmModCtx.Builder->CreateCall(function, args, "calltmp");
1414 // The returned value comes with a reference count for us to own.
1415 llvmModCtx.valueStackPhi.push_back({call, funcDef->returnType});
1416 }
1417
1418 for (auto &i : postCleanup) {
1419 callGcFunction(llvmModCtx, i.second, i.first, false);
1420 }
1421 break;
1422 }
1423 case IR::Opcode::invoke_dangling: {
1424 auto moduleIndex = instr.operands[0].value.symbolIndex;
1425 auto funcIndex = instr.operands[1].value.symbolIndex;
1426 auto argCount = instr.operands[2].value.symbolIndex;
1427
1428 yoi_assert(argCount, instr.debugInfo.line, instr.debugInfo.column, "invoke_dangling with no arguments");
1429
1430 auto funcDef = yoiModule->functionTable[funcIndex];
1431 auto* function = llvmModCtx.functionMap.at(funcDef->name);
1432
1433 std::vector<llvm::Value*> args;
1434 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Value*>> postCleanup;
1435
1436 llvm::Value *postponed = nullptr;
1437 {
1438 auto arg = llvmModCtx.valueStackPhi.back();
1439 llvmModCtx.valueStackPhi.pop_back();
1440
1441 arg = promiseInterfaceObjectIfInterface(llvmModCtx, arg);
1442
1443 if (funcDef->argumentTypes[0]->hasAttribute(IRValueType::ValueAttr::Raw)) {
1444 postponed = loadIfDataStructObject(llvmModCtx, funcDef->argumentTypes[0], unboxValue(llvmModCtx, arg.llvmValue, arg.yoiType));
1445 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, false);
1446 } else if (funcDef->argumentTypes[0]->hasAttribute(IRValueType::ValueAttr::Borrow)) {
1447 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1448 postponed = object.second;
1449 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw));
1450 else postCleanup.push_back(object);
1451 } else {
1452 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1453 postponed = object.second;
1454 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw))
1455 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, true, true);
1456 else;
1457 }
1458 }
1459
1460 for(size_t i = 1; i < argCount; ++i) {
1461 auto arg = llvmModCtx.valueStackPhi.back();
1462 llvmModCtx.valueStackPhi.pop_back();
1463
1464 arg = promiseInterfaceObjectIfInterface(llvmModCtx, arg);
1465
1466 if (funcDef->argumentTypes[argCount - i]->hasAttribute(IRValueType::ValueAttr::Raw)) {
1467 args.push_back(loadIfDataStructObject(llvmModCtx, funcDef->argumentTypes[argCount - i], unboxValue(llvmModCtx, arg.llvmValue, arg.yoiType)));
1468 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, false);
1469 } else if (funcDef->argumentTypes[argCount - i]->hasAttribute(IRValueType::ValueAttr::Borrow)) {
1470 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1471 args.push_back(object.second);
1472 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw));
1473 else postCleanup.push_back(object);
1474 } else {
1475 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1476 args.push_back(object.second);
1477 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !arg.yoiType->hasAttribute(IRValueType::ValueAttr::Raw))
1478 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, true, true);
1479 else;
1480 }
1481 }
1482
1483 args.push_back(postponed);
1484
1485 std::reverse(args.begin(), args.end());
1486
1487 if (funcDef->returnType->type == IRValueType::valueType::none) {
1488 llvmModCtx.Builder->CreateCall(function, args);
1489 } else {
1490 auto* call = llvmModCtx.Builder->CreateCall(function, args, "calltmp");
1491 // The returned value comes with a reference count for us to own.
1492 llvmModCtx.valueStackPhi.push_back({call, funcDef->returnType});
1493 }
1494
1495 for (auto &i : postCleanup) {
1496 callGcFunction(llvmModCtx, i.second, i.first, false);
1497 }
1498 break;
1499 }
1500 case IR::Opcode::invoke_imported: {
1501 auto libIndex = instr.operands[0].value.symbolIndex;
1502 auto funcIndex = instr.operands[1].value.symbolIndex;
1503 auto argCount = instr.operands[2].value.symbolIndex;
1504
1505 auto funcDef = compilerCtx->getIRFFITable()->importedLibraries[libIndex].importedFunctionTable[funcIndex];
1506
1507 if (funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic)) {
1508 handleIntrinsicCall(llvmModCtx, instr);
1509 break;
1510 }
1511
1512 bool noffi = funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI);
1513
1514 auto rawFuncName = compilerCtx->getIRFFITable()->importedLibraries[libIndex].importedFunctionTable.getKey(funcIndex);
1515 auto mangledFuncName = L"imported#" + std::to_wstring(libIndex) + L"#" + rawFuncName;
1516 if (!noffi) mangledFuncName += L"#wrapper";
1517
1518 auto* function = llvmModCtx.functionMap.at(mangledFuncName);
1519
1520 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Value*>> postCleanup;
1521 std::vector<llvm::Value*> args;
1522
1523 for(size_t i = 0; i < argCount; ++i) {
1524 auto arg = llvmModCtx.valueStackPhi.back();
1525 llvmModCtx.valueStackPhi.pop_back();
1526 arg = promiseInterfaceObjectIfInterface(llvmModCtx, arg);
1527
1528 if ((arg.yoiType->isBasicType() || arg.yoiType->isBasicRawType()) && !noffi) {
1529 auto *param = unboxValue(llvmModCtx, arg.llvmValue, arg.yoiType);
1530 if (arg.yoiType->type == IRValueType::valueType::stringObject || arg.yoiType->type == IRValueType::valueType::stringLiteral) {
1531 // the only fucking pointer that needs special handling here
1532 // we convert it to a int64 while passing it to the imported function
1533 param = llvmModCtx.Builder->CreatePtrToInt(param, llvm::Type::getInt64Ty(*llvmModCtx.TheContext), "string_to_int");
1534 }
1535 // clean up the mess immediately
1536 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, false);
1537 args.push_back(param);
1538 } else {
1539 auto object = ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue);
1540 postCleanup.push_back(object);
1541 if (arg.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && noffi) // retain the value for no ffi calls to prevent being destoryed
1542 callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, true, true, true);
1543 args.push_back(postCleanup.back().second);
1544 }
1545 // Callee will retain, so we release the stack's reference
1546 // callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, false);
1547 }
1548 std::reverse(args.begin(), args.end());
1549
1550 if (funcDef->returnType->type == IRValueType::valueType::none) {
1551 llvmModCtx.Builder->CreateCall(function, args);
1552 } else {
1553 auto* call = llvmModCtx.Builder->CreateCall(function, args, "calltmp");
1554 // The returned value comes with a reference count for us to own.
1555 // llvmModCtx.valueStackPhi.push_back({call, funcDef->returnType});
1556 auto onstackType = noffi ? *funcDef->returnType : compilerCtx->normalizeForeignBasicType(funcDef->returnType);
1557
1558 if (noffi && (funcDef->returnType->type == IRValueType::valueType::structObject ||
1559 funcDef->returnType->type == IRValueType::valueType::interfaceObject)) {
1560 // audit the type id before pushing to stack
1561 auto notNullBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "audit_typeid_not_null", llvmModCtx.currentFunction);
1562 auto continueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "audit_typeid_continue", llvmModCtx.currentFunction);
1563 llvmModCtx.Builder->CreateCondBr(llvmModCtx.Builder->CreateIsNotNull(call, "audit_typeid_isnotnull"), notNullBlock, continueBlock);
1564 llvmModCtx.Builder->SetInsertPoint(notNullBlock);
1565 auto typeIdKey = std::make_tuple(IRValueType::valueType::structObject,
1567 funcDef->returnType->typeIndex,
1568 0);
1569 auto structKey = std::make_tuple(IRValueType::valueType::structObject,
1571 funcDef->returnType->typeIndex);
1572 auto typeId = llvmModCtx.typeIDMap[typeIdKey];
1573 auto llvmStruct = llvmModCtx.structTypeMap.at(structKey);
1574 auto *typeIdPtr = llvmModCtx.Builder->CreateStructGEP(llvmStruct, call, 1, "typeid_ptr");
1575 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), typeId), typeIdPtr);
1576 llvmModCtx.Builder->CreateBr(continueBlock);
1577 llvmModCtx.Builder->SetInsertPoint(continueBlock);
1578 }
1579
1580 llvmModCtx.valueStackPhi.push_back({call, managedPtr(onstackType.isBasicType() && !noffi ? onstackType.getBasicRawType() : onstackType)});
1581 }
1582
1583 if (!noffi) {
1584 for (auto &i : postCleanup) {
1585 callGcFunction(llvmModCtx, i.second, i.first, false);
1586 }
1587 }
1588 break;
1589 }
1590 case IR::Opcode::new_struct: {
1591 auto moduleIndex = instr.operands[0].value.symbolIndex;
1592 auto structIndex = instr.operands[1].value.symbolIndex;
1593
1594 auto bitcast = createStructObject(llvmModCtx, moduleIndex, structIndex);
1595
1596 auto yoiType = std::make_shared<IRValueType>(IRValueType::valueType::structObject, yoiModule->identifier, structIndex);
1597 llvmModCtx.valueStackPhi.push_back({bitcast, yoiType});
1598 break;
1599 }
1600 case IR::Opcode::new_datastruct: {
1601 // default to Raw when initializing
1602 auto moduleIndex = instr.operands[0].value.symbolIndex;
1603 auto dataStructIndex = instr.operands[1].value.symbolIndex;
1604 auto key = std::make_tuple(IRValueType::valueType::datastructObject, yoiModule->identifier, dataStructIndex);
1605
1606 auto dataRegionType = llvmModCtx.dataStructDataRegionMap.at(dataStructIndex);
1607 // alloca
1608 auto* allocCall = llvmModCtx.Builder->CreateAlloca(dataRegionType, nullptr, "datastruct_alloc");
1609
1610 auto yoiType = std::make_shared<IRValueType>(IRValueType::valueType::datastructObject, yoiModule->identifier, dataStructIndex);
1611 yoiType->addAttribute(IRValueType::ValueAttr::Raw);
1612 llvmModCtx.valueStackPhi.push_back({allocCall, yoiType});
1613 break;
1614 }
1615 case IR::Opcode::initialize_field: {
1616 yoi::vec<StackValue> values(instr.operands[0].value.symbolIndex);
1617 for (yoi::indexT i = instr.operands[0].value.symbolIndex; i > 0; i--) {
1618 values[i - 1] = llvmModCtx.valueStackPhi.back();
1619 llvmModCtx.valueStackPhi.pop_back();
1620 }
1621 auto top = llvmModCtx.valueStackPhi.back();
1622 auto dataRegionType = llvmModCtx.dataStructDataRegionMap.at(top.yoiType->typeIndex);
1623 llvm::Value *dataRegionPtr = top.llvmValue;
1624 if (top.yoiType->hasAttribute(IRValueType::ValueAttr::Raw)) {
1625 dataRegionPtr = top.llvmValue;
1626 } else {
1627 auto objectTypeKey = std::make_tuple(IRValueType::valueType::datastructObject, yoiModule->identifier, top.yoiType->typeIndex);
1628 auto *objectType = llvmModCtx.structTypeMap.at(objectTypeKey);
1629 dataRegionPtr = llvmModCtx.Builder->CreateStructGEP(objectType, top.llvmValue, 2, "datastruct_ptr");
1630 }
1631 for (yoi::indexT i = 0; i < instr.operands[0].value.symbolIndex; i++) {
1632 auto value = unboxValue(llvmModCtx, values[i].llvmValue, values[i].yoiType);
1633 auto fieldPtr = llvmModCtx.Builder->CreateStructGEP(dataRegionType, dataRegionPtr, i, "field_ptr");
1634 auto fieldType = dataRegionType->getStructElementType(i);
1635 if (fieldType->isStructTy()) {
1636 // create MemCpy
1637 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(fieldType);
1638 auto *sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
1639 auto *memCpy = llvmModCtx.Builder->CreateMemCpy(fieldPtr, llvm::MaybeAlign(8), value, llvm::MaybeAlign(8), sizeVal);
1640 } else {
1641 llvmModCtx.Builder->CreateStore(value, fieldPtr);
1642 }
1643 callGcFunction(llvmModCtx, values[i].llvmValue, values[i].yoiType, false);
1644 }
1645 // no stack operation required
1646 break;
1647 }
1648 case IR::Opcode::store_field: {
1649 auto destValue = llvmModCtx.valueStackPhi.back();
1650 llvmModCtx.valueStackPhi.pop_back();
1651
1652 auto srcValue = llvmModCtx.valueStackPhi.back();
1653 llvmModCtx.valueStackPhi.pop_back();
1654
1655 auto currentType = srcValue.yoiType;
1656 auto val = unboxValue(llvmModCtx, srcValue.llvmValue, srcValue.yoiType);
1657 auto llvmType = yoiTypeToLLVMType(llvmModCtx, currentType, true);
1658
1659 auto destType = destValue.yoiType;
1660 auto destVal = unboxValue(llvmModCtx, destValue.llvmValue, destValue.yoiType);
1661 llvm::Value *fieldPtr = nullptr;
1662
1663 for (auto &operand : instr.operands) {
1664 auto def = yoiModule->dataStructTable[destType->typeIndex];
1665 auto nextType = def->fieldTypes[operand.value.symbolIndex];
1666 auto llvmType = llvmModCtx.dataStructDataRegionMap.at(destType->typeIndex);
1667 fieldPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, destVal, operand.value.symbolIndex, "field_ptr");
1668 destVal = fieldPtr;
1669 destType = nextType;
1670 }
1671 // check if assigning to datastruct
1672 if (destType->type == IRValueType::valueType::datastructObject) {
1673 // create MemCpy
1674 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(llvmType);
1675 auto *sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
1676 auto *memCpy = llvmModCtx.Builder->CreateMemCpy(fieldPtr, llvm::MaybeAlign(8), val, llvm::MaybeAlign(8), sizeVal);
1677 } else {
1678 llvmModCtx.Builder->CreateStore(val, fieldPtr);
1679 }
1680
1681 callGcFunction(llvmModCtx, srcValue.llvmValue, srcValue.yoiType, false);
1682 break;
1683 }
1684 case IR::Opcode::load_field: {
1685 auto top = llvmModCtx.valueStackPhi.back();
1686 llvmModCtx.valueStackPhi.pop_back();
1687 auto currentType = top.yoiType;
1688 auto val = unboxValue(llvmModCtx, top.llvmValue, top.yoiType);
1689 for (auto &operand : instr.operands) {
1690 auto def = yoiModule->dataStructTable[currentType->typeIndex];
1691 auto nextType = def->fieldTypes[operand.value.symbolIndex];
1692 auto llvmType = llvmModCtx.dataStructDataRegionMap.at(currentType->typeIndex);
1693 val = llvmModCtx.Builder->CreateStructGEP(llvmType, val, operand.value.symbolIndex, "field_ptr");
1694 currentType = nextType;
1695 }
1696 if (currentType->isArrayType()) {
1697 auto arrayVal = createArrayObject(llvmModCtx, managedPtr(currentType->getElementType()), {});
1698 auto llvmType = getArrayLLVMType(llvmModCtx, currentType);
1699 // offset to data region
1700 // 0 - refCount
1701 // 1 - type id
1702 // 2 - length
1703 // 3 - data
1704 auto dataRegionPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, val, 3, "data_region_ptr");
1705 auto dataSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(llvmType->getStructElementType(3));
1706 // create MemCpy
1707 llvmModCtx.Builder->CreateMemCpy(val, llvm::MaybeAlign(8), dataRegionPtr, llvm::MaybeAlign(8), dataSize);
1708 val = arrayVal;
1709 currentType = managedPtr(*currentType);
1710 } else if (currentType->isBasicType()) {
1711 val = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, currentType, true), val, "field_val");
1712 currentType = managedPtr(*currentType);
1713 currentType->addAttribute(IRValueType::ValueAttr::Raw);
1714 } else if (currentType->type == IRValueType::valueType::datastructObject) {
1715 // otherwise this is a data struct perform nothing
1716 currentType = managedPtr(*currentType);
1717 currentType->addAttribute(IRValueType::ValueAttr::Raw);
1718 } else {
1719 panic(instr.debugInfo.line, instr.debugInfo.column, "unreachable");
1720 }
1721 llvmModCtx.valueStackPhi.push_back({val, currentType});
1722 break;
1723 }
1724 case IR::Opcode::construct_interface_impl: {
1725 auto interfaceImplIndex = instr.operands[1].value.symbolIndex;
1726 auto interfaceImplDef = yoiModule->interfaceImplementationTable[interfaceImplIndex];
1727 auto &top = llvmModCtx.valueStackPhi.back();
1728 top = promiseInterfaceObjectIfInterface(llvmModCtx, top);
1729 auto object = ensureObject(llvmModCtx, top.yoiType, top.llvmValue);
1730 top.yoiType = object.first;
1731 top.llvmValue = object.second;
1732
1733 // prevent bugs for reusing the IRValueType
1734 top.yoiType = managedPtr(*top.yoiType);
1735 top.yoiType->type = IRValueType::valueType::interfaceObject;
1736 top.yoiType->typeAffiliateModule = ENTRY_MODULE_ID_CONST;
1737 top.yoiType->typeIndex = interfaceImplDef->implInterfaceIndex.second;
1738 top.yoiType->metadata.setMetadata(L"regressed_interface_impl", std::pair<yoi::indexT, yoi::indexT>{ENTRY_MODULE_ID_CONST, interfaceImplIndex});
1739 break;
1740 }
1741 case IR::Opcode::bind_elements_post:
1742 case IR::Opcode::bind_elements_pred: {
1743 auto array = llvmModCtx.valueStackPhi.back();
1744 llvmModCtx.valueStackPhi.pop_back();
1745
1746 yoi_assert(array.yoiType->isArrayType() || array.yoiType->isDynamicArrayType(), instr.debugInfo.line, instr.debugInfo.column, "Expected array type for bind_elements");
1747
1748 auto arrayLLVMType = getArrayLLVMType(llvmModCtx, array.yoiType);
1749 // gep index 2
1750 auto *arrayLen = llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, array.llvmValue, 2, "array_len");
1751 auto *loadedArrayLen = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), arrayLen, "loaded_array_len");
1752
1753 auto startPos = instr.opcode == IR::Opcode::bind_elements_post ? llvmModCtx.Builder->getInt64(0) : llvmModCtx.Builder->CreateSub(loadedArrayLen, llvmModCtx.Builder->getInt64(instr.operands[0].value.symbolIndex), "start_pos");
1754
1755 for (yoi::indexT i = 0;i < instr.operands[0].value.symbolIndex;i++) {
1756 auto currentPos = llvmModCtx.Builder->CreateAdd(startPos, llvmModCtx.Builder->getInt64(i), "current_pos");
1757 auto currentValue = loadArrayElement(llvmModCtx, array.yoiType, array.llvmValue, currentPos);
1758 std::shared_ptr<IRValueType> elementType;
1759 if (array.yoiType->isBasicType()) {
1760 elementType = managedPtr(array.yoiType->getElementType().getBasicRawType());
1761 } else if (array.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope)) {
1762 elementType = managedPtr(array.yoiType->getElementType().addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope).addAttribute(IRValueType::ValueAttr::Nullable));
1763 } else {
1764 elementType = managedPtr(array.yoiType->getElementType().addAttribute(IRValueType::ValueAttr::Nullable));
1765 }
1766 llvmModCtx.valueStackPhi.push_back({currentValue, elementType});
1767 }
1768
1769 callGcFunction(llvmModCtx, array.llvmValue, array.yoiType, false); // Release the reference to the array
1770 break;
1771 }
1772 case IR::Opcode::bind_fields_post:
1773 case IR::Opcode::bind_fields_pred: {
1774 auto structVal = llvmModCtx.valueStackPhi.back();
1775 llvmModCtx.valueStackPhi.pop_back();
1776
1777 yoi_assert(structVal.yoiType->type == IRValueType::valueType::structObject, instr.debugInfo.line, instr.debugInfo.column, "Expected struct type for bind_values");
1778 auto structDef = yoiModule->structTable[structVal.yoiType->typeIndex];
1779
1780 auto startPos = instr.opcode == IR::Opcode::bind_fields_post ? 0 : structDef->fieldTypes.size() - instr.operands[0].value.symbolIndex;
1781 for (yoi::indexT memberIndex = startPos; memberIndex < startPos + instr.operands[0].value.symbolIndex; memberIndex++) {
1782 auto llvmMemberIndex = memberIndex + 2; // +2 to skip gc_refcount header and type index
1783
1784 auto key = std::make_tuple(IRValueType::valueType::structObject, structVal.yoiType->typeAffiliateModule, structVal.yoiType->typeIndex);
1785 auto* llvmStructType = llvmModCtx.structTypeMap.at(key);
1786 auto* gep = llvmModCtx.Builder->CreateStructGEP(llvmStructType, structVal.llvmValue, llvmMemberIndex, "memberptr");
1787
1788 auto yoiStructDef = compilerCtx->getIRObjectFile()->compiledModule->structTable[std::get<2>(key)];
1789 auto memberYoiType = yoiStructDef->fieldTypes[memberIndex];
1790 if (structVal.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1791 memberYoiType = managedPtr(IRValueType{*memberYoiType}.addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope));
1792 memberYoiType->addAttribute(IRValueType::ValueAttr::Nullable);
1793 memberYoiType->removeAttribute(IRValueType::ValueAttr::Raw); // workaround for incorrect optimization labelling
1794
1795 llvm::Type* loadedType = yoiTypeToLLVMType(llvmModCtx, memberYoiType);
1796 auto* loadedMember = llvmModCtx.Builder->CreateLoad(loadedType, gep, "loadmember");
1797 callGcFunction(llvmModCtx, loadedMember, memberYoiType, true); // Create new reference for the loaded member
1798 llvmModCtx.valueStackPhi.push_back({loadedMember, memberYoiType});
1799 }
1800
1801 callGcFunction(llvmModCtx, structVal.llvmValue, structVal.yoiType, false); // Release the reference to the struct
1802 break;
1803 }
1804 case IR::Opcode::invoke_virtual: {
1805 auto methodVTableIndex = instr.operands[2].value.symbolIndex;
1806 auto userArgCount = instr.operands[3].value.symbolIndex;
1807
1808 std::vector<StackValue> userArgs;
1809 std::vector<std::pair<std::shared_ptr<IRValueType>, llvm::Value*>> postCleanup;
1810
1811 for (size_t i = 0; i < userArgCount - 1; ++i) { // userArgCount includes 'this'
1812 userArgs.push_back(promiseInterfaceObjectIfInterface(llvmModCtx, llvmModCtx.valueStackPhi.back()));
1813 llvmModCtx.valueStackPhi.pop_back();
1814 }
1815 std::reverse(userArgs.begin(), userArgs.end());
1816
1817 auto interfaceShellVal = llvmModCtx.valueStackPhi.back();
1818 llvmModCtx.valueStackPhi.pop_back();
1819
1820 auto interfaceKey = std::make_tuple(IRValueType::valueType::interfaceObject, interfaceShellVal.yoiType->typeAffiliateModule, interfaceShellVal.yoiType->typeIndex);
1821 auto* interfaceLLVMType = llvmModCtx.structTypeMap.at(interfaceKey);
1822 auto interfaceDef = yoiModule->interfaceTable[std::get<2>(interfaceKey)];
1823
1824 // Load the concrete `this` pointer from index 2
1825 auto concreteThisPtrRaw = unwrapInterfaceObject(llvmModCtx, interfaceShellVal);
1826 auto* bitcastedPointer = llvmModCtx.Builder->CreateBitCast(concreteThisPtrRaw, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "casted_this");
1827 // increase the reference count of this pointer, so that when leaving the function, it won't be collected
1828 // llvmModCtx.Builder->CreateStore(llvmModCtx.Builder->CreateAdd(oldRefcount, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1)), bitcastedPointer);
1829 // llvmModCtx.Builder->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, bitcastedPointer, llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), llvm::MaybeAlign(8), llvm::AtomicOrdering::Monotonic);
1830 // for now, we pass the value by borrow, this stmt is no longer needed.
1831
1832 // btw, we have increased the refcount of the interface as well before, so when we finish the invoking, we need to decrease it.
1833
1834 // Load the function pointer to call from the v-table. User methods start at index 5.
1835
1836 std::vector<llvm::Value*> finalArgs;
1837 finalArgs.push_back(concreteThisPtrRaw);
1838 for(yoi::indexT paramIndex = 0; paramIndex < userArgs.size(); ++paramIndex) {
1839 const auto& arg = userArgs[paramIndex];
1840 auto paramDef = interfaceDef->methodMap[methodVTableIndex]->argumentTypes[paramIndex];
1841 auto object = (paramDef->hasAttribute(IRValueType::ValueAttr::Nullable) || (!paramDef->isBasicType() && !paramDef->isBasicRawType()) || !paramDef->dimensions.empty())
1842 ? ensureObject(llvmModCtx, arg.yoiType, arg.llvmValue)
1843 : std::pair{managedPtr(arg.yoiType->getBasicRawType()), loadIfDataStructObject(llvmModCtx, paramDef, unboxValue(llvmModCtx, arg.llvmValue, arg.yoiType))};
1844 finalArgs.push_back(object.second);
1845 // default to borrow
1846 if (object.first->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !object.first->hasAttribute(IRValueType::ValueAttr::Raw));
1847 // callGcFunction(llvmModCtx, arg.llvmValue, arg.yoiType, true, true);
1848 else postCleanup.emplace_back(object);
1849 }
1850
1851 std::shared_ptr<IRFunctionDefinition> methodDef;
1852 llvm::Value *funcPtrToCall = nullptr;
1853 llvm::FunctionType *virtualFuncType = nullptr;
1854 if (interfaceShellVal.yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
1855 auto interfaceImplIndex = interfaceShellVal.yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
1856 auto interfaceImplDef = yoiModule->interfaceImplementationTable[interfaceImplIndex.second];
1857 methodDef = yoiModule->functionTable[interfaceImplDef->virtualMethods[methodVTableIndex]->typeIndex];
1858 funcPtrToCall = llvmModCtx.functionMap[methodDef->name];
1859 virtualFuncType = llvmModCtx.functionMap[methodDef->name]->getFunctionType();
1860 } else {
1861 auto vtableSlotIndex = methodVTableIndex + 5;
1862 auto* vtableSlotPtr = llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, interfaceShellVal.llvmValue, vtableSlotIndex, "vtable_slot_ptr");
1863
1864 auto interfaceDef = compilerCtx->getIRObjectFile()->compiledModule->interfaceTable[std::get<2>(interfaceKey)];
1865 methodDef = interfaceDef->methodMap[methodVTableIndex];
1866 auto* funcType = getFunctionType(llvmModCtx, methodDef);
1867
1868 std::vector<llvm::Type*> virtualArgTypes;
1869 virtualArgTypes.push_back(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
1870 for (size_t i = 0; i < funcType->getNumParams(); ++i) {
1871 virtualArgTypes.push_back(funcType->getParamType(i));
1872 }
1873 virtualFuncType = llvm::FunctionType::get(funcType->getReturnType(), virtualArgTypes, false);
1874 auto* virtualFuncPtrType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
1875 funcPtrToCall = llvmModCtx.Builder->CreateLoad(virtualFuncPtrType, vtableSlotPtr, "func_ptr");
1876 }
1877
1878 if (methodDef->returnType->type == IRValueType::valueType::none) {
1879 llvmModCtx.Builder->CreateCall(virtualFuncType, funcPtrToCall, finalArgs);
1880 } else {
1881 llvm::CallInst* call = llvmModCtx.Builder->CreateCall(virtualFuncType, funcPtrToCall, finalArgs, "virtcall");
1882 llvmModCtx.valueStackPhi.push_back({call, methodDef->returnType});
1883 }
1884
1885 for (auto &i : postCleanup) {
1886 callGcFunction(llvmModCtx, i.second, i.first, false);
1887 }
1888
1889 callGcFunction(llvmModCtx, interfaceShellVal.llvmValue, interfaceShellVal.yoiType, false);
1890 break;
1891 }
1892 case IR::Opcode::new_array_int:
1893 case IR::Opcode::new_array_bool:
1894 case IR::Opcode::new_array_char:
1895 case IR::Opcode::new_array_deci:
1896 case IR::Opcode::new_array_unsigned:
1897 case IR::Opcode::new_array_short:
1898 case IR::Opcode::new_array_str: {
1899 yoi::indexT size = 1;
1900 yoi::vec<StackValue> dimensionsVal;
1901 yoi::vec<yoi::indexT> dimensions;
1902
1903 std::shared_ptr<yoi::IRValueType> elementType;
1904 for (yoi::indexT i = 1; i < instr.operands.size(); ++i) {
1905 size *= instr.operands[i].value.symbolIndex;
1906 dimensions.push_back(instr.operands[i].value.symbolIndex);
1907 }
1908 for (yoi::indexT i = 0; i < instr.operands[0].value.symbolIndex; ++i) {
1909 // for basic types, receiving value is not owning the value, so we don't need to increase the refcount.
1910 dimensionsVal.push_back(llvmModCtx.valueStackPhi[llvmModCtx.valueStackPhi.size() - instr.operands[0].value.symbolIndex + i]);
1911 }
1912
1913 switch (instr.opcode) {
1914 case IR::Opcode::new_array_int:
1915 elementType = compilerCtx->getIntObjectType();
1916 break;
1917 case IR::Opcode::new_array_bool:
1918 elementType = compilerCtx->getBoolObjectType();
1919 break;
1920 case IR::Opcode::new_array_char:
1921 elementType = compilerCtx->getCharObjectType();
1922 break;
1923 case IR::Opcode::new_array_deci:
1924 elementType = compilerCtx->getDeciObjectType();
1925 break;
1926 case IR::Opcode::new_array_str:
1927 elementType = compilerCtx->getStrObjectType();
1928 break;
1929 case IR::Opcode::new_array_unsigned:
1930 elementType = compilerCtx->getUnsignedObjectType();
1931 break;
1932 case IR::Opcode::new_array_short:
1933 elementType = compilerCtx->getShortObjectType();
1934 break;
1935 default:
1936 break;
1937 }
1938
1939 // Create the array object
1940 auto arrayType = managedPtr(elementType->getArrayType(dimensions));
1941 auto val = createArrayObject(llvmModCtx, arrayType, dimensionsVal);
1942
1943 for (yoi::indexT i = 0; i < instr.operands[0].value.symbolIndex; ++i) {
1944 callGcFunction(llvmModCtx, llvmModCtx.valueStackPhi.back().llvmValue, llvmModCtx.valueStackPhi.back().yoiType, false);
1945 llvmModCtx.valueStackPhi.pop_back();
1946 }
1947
1948 llvmModCtx.valueStackPhi.push_back({val, arrayType});
1949 break;
1950 }
1951 case IR::Opcode::new_array_struct:
1952 case IR::Opcode::new_array_interface: {
1953 yoi::indexT size = 1;
1954 yoi::vec<StackValue> dimensionsVal;
1955 yoi::vec<yoi::indexT> dimensions;
1956
1957 std::shared_ptr<yoi::IRValueType> elementType;
1958 for (yoi::indexT i = 3; i < instr.operands.size(); ++i) {
1959 size *= instr.operands[i].value.symbolIndex;
1960 dimensions.push_back(instr.operands[i].value.symbolIndex);
1961 }
1962 for (yoi::indexT i = 0; i < instr.operands[2].value.symbolIndex; ++i) {
1963 auto value = promiseInterfaceObjectIfInterface(llvmModCtx, llvmModCtx.valueStackPhi[llvmModCtx.valueStackPhi.size() - size + i]);
1964 if (value.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
1965 callGcFunction(llvmModCtx, value.llvmValue, value.yoiType, true, true, true);
1966 dimensionsVal.push_back(value);
1967 }
1968
1969 elementType = managedPtr(IRValueType{instr.opcode == IR::Opcode::new_array_struct ? IRValueType::valueType::structObject : IRValueType::valueType::interfaceObject, yoiModule->identifier, instr.operands[1].value.symbolIndex});
1970
1971 // Create the array object
1972 auto arrayType = managedPtr(elementType->getArrayType(dimensions));
1973 auto val = createArrayObject(llvmModCtx, arrayType, dimensionsVal);
1974 for (yoi::indexT i = 0; i < size; ++i) {
1975 // pop the values from the stack
1976 llvmModCtx.valueStackPhi.pop_back();
1977 }
1978
1979 llvmModCtx.valueStackPhi.push_back({val, arrayType});
1980 break;
1981 }
1982 case IR::Opcode::new_dynamic_array_int:
1983 case IR::Opcode::new_dynamic_array_bool:
1984 case IR::Opcode::new_dynamic_array_char:
1985 case IR::Opcode::new_dynamic_array_deci:
1986 case IR::Opcode::new_dynamic_array_unsigned:
1987 case IR::Opcode::new_dynamic_array_short:
1988 case IR::Opcode::new_dynamic_array_str: {
1989 yoi::indexT size = instr.operands.back().value.symbolIndex;
1990
1991 auto llvmSize = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
1992 auto unboxedSize = unboxValue(llvmModCtx, llvmSize.llvmValue, llvmSize.yoiType);
1993
1994 yoi::vec<StackValue> valuesToStore;
1995 std::shared_ptr<yoi::IRValueType> elementType;
1996 for (yoi::indexT i = 0; i < size; ++i) {
1997 auto value = llvmModCtx.valueStackPhi[llvmModCtx.valueStackPhi.size() - size + i];
1998 valuesToStore.push_back(value);
1999 }
2000
2001 switch (instr.opcode) {
2002 case IR::Opcode::new_dynamic_array_int:
2003 elementType = compilerCtx->getIntObjectType();
2004 break;
2005 case IR::Opcode::new_dynamic_array_bool:
2006 elementType = compilerCtx->getBoolObjectType();
2007 break;
2008 case IR::Opcode::new_dynamic_array_char:
2009 elementType = compilerCtx->getCharObjectType();
2010 break;
2011 case IR::Opcode::new_dynamic_array_deci:
2012 elementType = compilerCtx->getDeciObjectType();
2013 break;
2014 case IR::Opcode::new_dynamic_array_str:
2015 elementType = compilerCtx->getStrObjectType();
2016 break;
2017 case IR::Opcode::new_dynamic_array_short:
2018 elementType = compilerCtx->getShortObjectType();
2019 break;
2020 case IR::Opcode::new_dynamic_array_unsigned:
2021 elementType = compilerCtx->getUnsignedObjectType();
2022 break;
2023 default:
2024 break;
2025 }
2026
2027 // Create the array object
2028 auto arrayType = managedPtr(elementType->getDynamicArrayType());
2029 auto val = createDynamicArrayObject(llvmModCtx, arrayType, valuesToStore, unboxedSize);
2030
2031 for (yoi::indexT i = 0; i < size; ++i) {
2032 callGcFunction(llvmModCtx, llvmModCtx.valueStackPhi.back().llvmValue, llvmModCtx.valueStackPhi.back().yoiType, false);
2033 llvmModCtx.valueStackPhi.pop_back();
2034 }
2035
2036 llvmModCtx.valueStackPhi.push_back({val, arrayType});
2037
2038 // release index
2039 callGcFunction(llvmModCtx, llvmSize.llvmValue, llvmSize.yoiType, false);
2040 break;
2041 }
2042 case IR::Opcode::new_dynamic_array_struct:
2043 case IR::Opcode::new_dynamic_array_interface: {
2044 yoi::indexT size = instr.operands.back().value.symbolIndex;
2045
2046 auto llvmSize = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2047 auto unboxedSize = unboxValue(llvmModCtx, llvmSize.llvmValue, llvmSize.yoiType);
2048
2049 yoi::vec<StackValue> valuesToStore;
2050 std::shared_ptr<yoi::IRValueType> elementType;
2051 for (yoi::indexT i = 0; i < size; ++i) {
2052 auto value = promiseInterfaceObjectIfInterface(llvmModCtx, llvmModCtx.valueStackPhi[llvmModCtx.valueStackPhi.size() - size + i]);
2053 if (value.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
2054 callGcFunction(llvmModCtx, value.llvmValue, value.yoiType, true, true, true);
2055 valuesToStore.push_back(value);
2056 }
2057
2058 elementType = managedPtr(IRValueType{instr.opcode == IR::Opcode::new_dynamic_array_struct ? IRValueType::valueType::structObject : IRValueType::valueType::interfaceObject, yoiModule->identifier, instr.operands[1].value.symbolIndex});
2059
2060 // Create the array object
2061 auto arrayType = managedPtr(elementType->getDynamicArrayType());
2062 auto val = createDynamicArrayObject(llvmModCtx, arrayType, valuesToStore, unboxedSize);
2063
2064 for (yoi::indexT i = 0; i < size; ++i) {
2065 llvmModCtx.valueStackPhi.pop_back();
2066 }
2067
2068 llvmModCtx.valueStackPhi.push_back({val, arrayType});
2069 callGcFunction(llvmModCtx, llvmSize.llvmValue, llvmSize.yoiType, false);
2070 break;
2071 }
2072 case IR::Opcode::load_element: {
2073 auto indexVal = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2074 auto arrayVal = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2075
2076 auto arrayType = arrayVal.yoiType;
2077 std::shared_ptr<yoi::IRValueType> elementType;
2078 if (arrayType->isBasicType()) {
2079 elementType = managedPtr(arrayType->getElementType().getBasicRawType());
2080 } else if (arrayType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope)) {
2081 elementType = managedPtr(arrayType->getElementType().addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope).addAttribute(IRValueType::ValueAttr::Nullable));
2082 } else {
2083 elementType = managedPtr(arrayType->getElementType().addAttribute(IRValueType::ValueAttr::Nullable));
2084 }
2085 auto unboxedIndexVal = unboxValue(llvmModCtx, indexVal.llvmValue, indexVal.yoiType);
2086 auto result = loadArrayElement(llvmModCtx, arrayType, arrayVal.llvmValue, unboxedIndexVal);
2087
2088 llvmModCtx.valueStackPhi.push_back({result, elementType});
2089 // resource releasing
2090 callGcFunction(llvmModCtx, indexVal.llvmValue, indexVal.yoiType, false);
2091 callGcFunction(llvmModCtx, arrayVal.llvmValue, arrayVal.yoiType, false);
2092 break;
2093 }
2094 case IR::Opcode::pop: {
2095 if (llvmModCtx.valueStackPhi.empty())
2096 break;
2097 auto val = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2098 callGcFunction(llvmModCtx, val.llvmValue, val.yoiType, false);
2099 break;
2100 }
2101 case IR::Opcode::direct_assign: {
2102 auto rhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2103 auto lhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2104
2105 rhs = promiseInterfaceObjectIfInterface(llvmModCtx, rhs);
2106
2107 auto object = ensureObject(llvmModCtx, rhs.yoiType, rhs.llvmValue);
2108 rhs = {object.second, object.first};
2109
2110 auto lhsType = lhs.yoiType;
2111 auto rhsType = rhs.yoiType;
2112 auto lhsLLVMType = llvmModCtx.structTypeMap.at(std::make_tuple(lhsType->type, lhsType->typeAffiliateModule, lhsType->typeIndex));
2113 auto rhsLLVMType = llvmModCtx.structTypeMap.at(std::make_tuple(rhsType->type, rhsType->typeAffiliateModule, rhsType->typeIndex));
2114
2115 yoi_assert(!lhsType->metadata.hasMetadata(L"STRUCT_DATAFIELD"), instr.debugInfo.line, instr.debugInfo.column, "direct assignment to a data field in legacy struct is prohibited");
2116
2117 if (lhsType->type == IRValueType::valueType::structObject) {
2118 // reduce refcount of object inside the lhs
2119 yoi::indexT fieldIndex = 2;
2120 for (const auto& field : yoiModule->structTable[lhsType->typeIndex]->fieldTypes) {
2121 auto fieldPtr = llvmModCtx.Builder->CreateStructGEP(lhsLLVMType, lhs.llvmValue, fieldIndex, "field_ptr");
2122 auto loadedFieldPtr = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), fieldPtr, "loaded_field_ptr");
2123 callGcFunction(llvmModCtx, loadedFieldPtr, field, false);
2124 fieldIndex++;
2125 }
2126 fieldIndex = 2;
2127 for (const auto& field : yoiModule->structTable[rhsType->typeIndex]->fieldTypes) {
2128 auto fieldPtr = llvmModCtx.Builder->CreateStructGEP(rhsLLVMType, rhs.llvmValue, fieldIndex, "field_ptr");
2129 auto loadedFieldPtr = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), fieldPtr, "loaded_field_ptr");
2130 callGcFunction(llvmModCtx, loadedFieldPtr, field, true);
2131 fieldIndex++;
2132 }
2133 } else if (lhsType->type == IRValueType::valueType::interfaceObject) {
2134 // reduce refcount of object inside the lhs
2135 // this time, we use implementation-specific vtable slots to reduce refcount
2136 auto thisPtr = llvmModCtx.Builder->CreateStructGEP(lhsLLVMType, lhs.llvmValue, 1, "this_ptr_field");
2137 auto* concreteThisPtrRaw = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), thisPtr, "concrete_this_raw");
2138 auto* implGcDecSlot = llvmModCtx.Builder->CreateStructGEP(lhsLLVMType, lhs.llvmValue, 3, "impl_gc_dec_slot");
2139 auto* implGcDecFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
2140 llvmModCtx.Builder->CreateCall(implGcDecFuncType, implGcDecSlot, {concreteThisPtrRaw});
2141 auto rhsThisPtr = llvmModCtx.Builder->CreateStructGEP(rhsLLVMType, rhs.llvmValue, 1, "this_ptr_field");
2142 auto* rhsConcreteThisPtrRaw = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), rhsThisPtr, "rhs_concrete_this_raw");
2143 auto* implGcIncSlot = llvmModCtx.Builder->CreateStructGEP(rhsLLVMType, rhs.llvmValue, 2, "impl_gc_inc_slot");
2144 auto* implGcIncFuncType = llvm::FunctionType::get(llvmModCtx.Builder->getVoidTy(), {llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
2145 llvmModCtx.Builder->CreateCall(implGcIncFuncType, implGcIncSlot, {rhsConcreteThisPtrRaw});
2146 }
2147
2148 auto structTypeSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(lhsLLVMType);
2149 // offset from 16 bytes to skip the refcount, and memcpy the rhs value to lhs
2150 auto* lhsPtr = llvmModCtx.Builder->CreateBitCast(lhs.llvmValue, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "lhs_ptr");
2151 auto* rhsPtr = llvmModCtx.Builder->CreateBitCast(rhs.llvmValue, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "rhs_ptr");
2152 auto* offsettedLhsPtr = llvmModCtx.Builder->CreateGEP(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), lhsPtr, {llvm::ConstantInt::get(llvmModCtx.Builder->getInt32Ty(), 16, true)});
2153 auto* offsettedRhsPtr = llvmModCtx.Builder->CreateGEP(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), rhsPtr, {llvm::ConstantInt::get(llvmModCtx.Builder->getInt32Ty(), 16, true)});
2154 llvmModCtx.Builder->CreateMemCpy(offsettedLhsPtr, llvm::MaybeAlign(8), offsettedRhsPtr, llvm::MaybeAlign(8), structTypeSize - 16);
2155 callGcFunction(llvmModCtx, rhs.llvmValue, rhs.yoiType, false);
2156 llvmModCtx.valueStackPhi.push_back(lhs);
2157 break;
2158 }
2159 case IR::Opcode::dyn_cast_any: {
2160 std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT> structTypeKey;
2161 std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT> structTypeIDKey;
2162
2163 auto interfaceRhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2164
2165 auto structTypeIndex = instr.operands[1].value.symbolIndex;
2166 std::shared_ptr<IRValueType> structYoiType;
2167
2168 structTypeIDKey = std::make_tuple(static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex), instr.operands[1].value.symbolIndex, instr.operands[2].value.symbolIndex, instr.operands[3].value.symbolIndex);
2169 if (instr.operands[3].value.symbolIndex) {
2170 structYoiType = managedPtr(IRValueType{static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex), instr.operands[1].value.symbolIndex, instr.operands[2].value.symbolIndex, yoi::vec<yoi::indexT>{instr.operands[3].value.symbolIndex}});
2171 } else {
2172 structYoiType = managedPtr(IRValueType{static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex), instr.operands[1].value.symbolIndex, instr.operands[2].value.symbolIndex});
2173 structTypeKey = std::make_tuple(static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex), instr.operands[1].value.symbolIndex, instr.operands[2].value.symbolIndex);
2174 }
2175
2176 if (interfaceRhs.yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
2177 auto regressedImpl = interfaceRhs.yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
2178 auto implDef = yoiModule->interfaceImplementationTable[regressedImpl.second];
2179 if (implDef->implStructIndex == structTypeKey) {
2180 llvmModCtx.valueStackPhi.push_back({unwrapInterfaceObject(llvmModCtx, interfaceRhs), managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex)})});
2181 } else {
2182 auto nullValue = llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2183 llvmModCtx.valueStackPhi.push_back({nullValue, managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex)})});
2184 }
2185
2186 if (interfaceRhs.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
2187 callGcFunction(llvmModCtx, interfaceRhs.llvmValue, interfaceRhs.yoiType, true, true);
2188
2189 // no decrement here
2190 // for which it is a reuse
2191
2192 break;
2193 }
2194
2195 auto structTypeId = llvmModCtx.typeIDMap.at(structTypeIDKey);
2196 auto structTypeLLVMType = structYoiType->isArrayType() || structYoiType->isDynamicArrayType()
2197 ? llvmModCtx.arrayTypeMap.at(structTypeIDKey)
2198 : llvmModCtx.structTypeMap.at(structTypeKey);
2199
2200 // offset by 16 bytes to skip the refcount and typeid
2201 auto* interfacePtr = llvmModCtx.Builder->CreateBitCast(interfaceRhs.llvmValue, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "interface_ptr");
2202 auto* offsettedInterfacePtr = llvmModCtx.Builder->CreateGEP(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), interfacePtr, {llvm::ConstantInt::get(llvmModCtx.Builder->getInt32Ty(), 16, true)});
2203 auto* structPtrPtr = llvmModCtx.Builder->CreateBitCast(offsettedInterfacePtr, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "struct_ptr");
2204 auto* loadedStructPtr = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), structPtrPtr, "loaded_struct_ptr");
2205 // offset by 8 bytes and check typeid
2206 auto* typeIdPtr = llvmModCtx.Builder->CreateStructGEP(structTypeLLVMType, loadedStructPtr, 1, "typeid_ptr");
2207 auto* loadedTypeId = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), typeIdPtr, "loaded_typeid");
2208 auto* expectedTypeId = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), structTypeId, true);
2209 auto* typeIdMatch = llvmModCtx.Builder->CreateICmpEQ(loadedTypeId, expectedTypeId, "typeid_match");
2210
2211 auto* failedMatchBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "failed_match_bb", llvmModCtx.currentFunction);
2212 auto* successBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "success_bb", llvmModCtx.currentFunction);
2213 auto* continueBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue_bb", llvmModCtx.currentFunction);
2214
2215 llvmModCtx.Builder->CreateCondBr(typeIdMatch, successBB, failedMatchBB);
2216 // failed match
2217 llvmModCtx.Builder->SetInsertPoint(failedMatchBB);
2218 auto* nullValue = llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2219 llvmModCtx.Builder->CreateBr(continueBB);
2220 // success match
2221 llvmModCtx.Builder->SetInsertPoint(successBB);
2222 auto* resultObject = loadedStructPtr;
2223 callGcFunction(llvmModCtx, resultObject, structYoiType, true);
2224 llvmModCtx.Builder->CreateBr(continueBB);
2225 // in continue block, decrement the interface refcount
2226 llvmModCtx.Builder->SetInsertPoint(continueBB);
2227 auto *finalValue = llvmModCtx.Builder->CreatePHI(llvm::PointerType::get(*llvmModCtx.TheContext, 0), 2, "final_value");
2228 finalValue->addIncoming(resultObject, successBB);
2229 finalValue->addIncoming(nullValue, failedMatchBB);
2230 callGcFunction(llvmModCtx, interfaceRhs.llvmValue, interfaceRhs.yoiType, false);
2231
2232 llvmModCtx.valueStackPhi.push_back({finalValue, structYoiType});
2233 break;
2234 }
2235 case IR::Opcode::push_null: {
2236 auto nullValue = llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2237 llvmModCtx.valueStackPhi.push_back({nullValue, managedPtr(IRValueType{IRValueType::valueType::pointerObject})});
2238 break;
2239 }
2240 case IR::Opcode::pointer_cast: {
2241 auto rhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2242 auto value = llvmModCtx.Builder->CreateBitCast(rhs.llvmValue, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "pointer_cast");
2243 llvmModCtx.valueStackPhi.push_back({value, managedPtr(IRValueType{IRValueType::valueType::pointerObject})});
2244 callGcFunction(llvmModCtx, rhs.llvmValue, rhs.yoiType, false);
2245 break;
2246 }
2247 case IR::Opcode::store_element: {
2248 auto index = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2249 auto lhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2250 auto rhs = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2251
2252 yoi_assert(lhs.yoiType->isArrayType() || lhs.yoiType->isDynamicArrayType(), instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: store element on non-array type.");
2253 yoi_assert(index.yoiType->type == IRValueType::valueType::unsignedObject || index.yoiType->type == IRValueType::valueType::unsignedRaw, instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: store element with non-integer index.");
2254
2255 rhs = promiseInterfaceObjectIfInterface(llvmModCtx, rhs);
2256
2257 // unbox index
2258 auto* indexValue = unboxValue(llvmModCtx, index.llvmValue, index.yoiType);
2259 if (rhs.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !lhs.yoiType->isBasicType())
2260 callGcFunction(llvmModCtx, rhs.llvmValue, rhs.yoiType, true, true, true);
2261 storeArrayElement(llvmModCtx, lhs.yoiType, rhs.yoiType, lhs.llvmValue, indexValue, rhs.llvmValue);
2262
2263 // release resource
2264 callGcFunction(llvmModCtx, index.llvmValue, index.yoiType, false);
2265 callGcFunction(llvmModCtx, rhs.llvmValue, rhs.yoiType, false);
2266 callGcFunction(llvmModCtx, lhs.llvmValue, lhs.yoiType, false);
2267 break;
2268 }
2269 case IR::Opcode::array_length: {
2270 auto array = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2271 yoi_assert(array.yoiType->isArrayType() || array.yoiType->isDynamicArrayType(), instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: array length on non-array type.");
2272 auto arrayLLVMType = getArrayLLVMType(llvmModCtx, array.yoiType);
2273 // gep index 2
2274 auto *arrayLen = llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, array.llvmValue, 2, "array_len");
2275 auto *loadedArrayLen = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), arrayLen, "loaded_array_len");
2276 llvmModCtx.valueStackPhi.push_back({loadedArrayLen, managedPtr(compilerCtx->getIntObjectType()->getBasicRawType())});
2277 callGcFunction(llvmModCtx, array.llvmValue, array.yoiType, false);
2278 break;
2279 }
2280 case IR::Opcode::interfaceof: {
2281 // get the typeid off the stack
2282 auto typeidValue = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2283 yoi_assert(typeidValue.yoiType->type == IRValueType::valueType::integerObject || typeidValue.yoiType->type == IRValueType::valueType::integerRaw, instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: interfaceof with non-integer typeid.");
2284 // get the interface object off the stack
2285 auto interfaceValue = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2286
2287
2288 if (interfaceValue.yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
2289 auto regressedImpl = interfaceValue.yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
2290 auto implDef = yoiModule->interfaceImplementationTable[regressedImpl.second];
2291 if (llvmModCtx.typeIDMap.contains({std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex), 0})) {
2292 auto *trueBoolean = llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), 1, true);
2293 llvmModCtx.valueStackPhi.push_back({trueBoolean, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
2294 } else {
2295 auto *falseBoolean = llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), 0, true);
2296 llvmModCtx.valueStackPhi.push_back({falseBoolean, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
2297 }
2298 callGcFunction(llvmModCtx, interfaceValue.llvmValue, interfaceValue.yoiType, false);
2299 callGcFunction(llvmModCtx, typeidValue.llvmValue, typeidValue.yoiType, false);
2300 break;
2301 }
2302
2303 // evaluate the interface this
2304 auto interfaceKey = std::make_tuple(interfaceValue.yoiType->type, interfaceValue.yoiType->typeAffiliateModule, interfaceValue.yoiType->typeIndex);
2305 // auto thisPtrToStruct = llvmModCtx.Builder->CreateStructGEP(llvmModCtx.structTypeMap.at(interfaceKey), interfaceValue.llvmValue, 2, "this_ptr_to_struct");
2306 // auto loadedThisPtr = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(llvmModCtx.Builder->getInt64Ty(), 0), thisPtrToStruct, "loaded_this_ptr");
2307 auto loadedThisPtr = unwrapInterfaceObject(llvmModCtx, interfaceValue);
2308 // offset by 8 bytes and check typeid
2309 auto* typeIdPtr = llvmModCtx.Builder->CreateGEP(llvmModCtx.Builder->getInt64Ty(), loadedThisPtr, {llvm::ConstantInt::get(llvmModCtx.Builder->getInt32Ty(), 1, true)});
2310 auto* loadedTypeId = llvmModCtx.Builder->CreateLoad(llvmModCtx.Builder->getInt64Ty(), typeIdPtr, "loaded_typeid");
2311 auto* expectedTypeId = unboxValue(llvmModCtx, typeidValue.llvmValue, typeidValue.yoiType);
2312 auto* typeIdMatch = llvmModCtx.Builder->CreateICmpEQ(loadedTypeId, expectedTypeId, "typeid_match");
2313
2314 auto* failedMatchBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "failed_match_bb", llvmModCtx.currentFunction);
2315 auto* successBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "success_bb", llvmModCtx.currentFunction);
2316 auto* continueBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue_bb", llvmModCtx.currentFunction);
2317
2318 llvmModCtx.Builder->CreateCondBr(typeIdMatch, successBB, failedMatchBB);
2319 // failed match
2320 llvmModCtx.Builder->SetInsertPoint(failedMatchBB);
2321 auto *falseBoolean = llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), 0, true);
2322 llvmModCtx.Builder->CreateBr(continueBB);
2323 // success match
2324 llvmModCtx.Builder->SetInsertPoint(successBB);
2325 auto *trueBoolean = llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), 1, true);
2326 llvmModCtx.Builder->CreateBr(continueBB);
2327 // in continue block, decrement the interface refcount
2328 // but phi first
2329 llvmModCtx.Builder->SetInsertPoint(continueBB);
2330 auto phiNode = llvmModCtx.Builder->CreatePHI(llvm::Type::getInt1Ty(*llvmModCtx.TheContext), 2, "phi_node");
2331 phiNode->addIncoming(trueBoolean, successBB);
2332 phiNode->addIncoming(falseBoolean, failedMatchBB);
2333
2334 callGcFunction(llvmModCtx, interfaceValue.llvmValue, interfaceValue.yoiType, false);
2335 callGcFunction(llvmModCtx, typeidValue.llvmValue, typeidValue.yoiType, false);
2336 llvmModCtx.valueStackPhi.push_back({phiNode, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
2337 break;
2338 }
2339 case IR::Opcode::typeid_object_non_stack: {
2340 auto key = std::make_tuple(static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex), instr.operands[1].value.symbolIndex, instr.operands[2].value.symbolIndex, instr.operands[3].value.symbolIndex);
2341 auto typeId = llvmModCtx.typeIDMap.at(key);
2342 llvmModCtx.valueStackPhi.push_back({llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), typeId, true), managedPtr(compilerCtx->getIntObjectType()->getBasicRawType())});
2343 break;
2344 }
2345 case IR::Opcode::yield: {
2346 // stack: [value, raw_ctx, ...]
2347 auto item = llvmModCtx.valueStackPhi.back();
2348 llvmModCtx.valueStackPhi.pop_back();
2349 auto raw_ctx = llvmModCtx.valueStackPhi.back();
2350 llvmModCtx.valueStackPhi.pop_back();
2351 auto raw_ctx_ptr = llvmModCtx.Builder->CreateIntToPtr(unboxValue(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType), llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2352 storeYieldValue(llvmModCtx, item.llvmValue, item.yoiType);
2353 auto coro_suspend = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_suspend);
2354 auto suspend_result = llvmModCtx.Builder->CreateCall(coro_suspend, {
2355 llvm::ConstantTokenNone::get(*llvmModCtx.TheContext),
2356 llvm::ConstantInt::get(llvm::Type::getInt1Ty(*llvmModCtx.TheContext), 0)});
2357 auto *resumeBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "resume_bb", llvmModCtx.currentFunction);
2358 auto *switch_inst = llvmModCtx.Builder->CreateSwitch(suspend_result, llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB, 2);
2359 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 0), resumeBB);
2360 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 1), llvmModCtx.currentGeneratorContextBasicBlocks.cleanupBB);
2361 callGcFunction(llvmModCtx, item.llvmValue, item.yoiType, false);
2362 callGcFunction(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType, false);
2363 llvmModCtx.Builder->SetInsertPoint(resumeBB);
2364 break;
2365 }
2366 case IR::Opcode::yield_none: {
2367 // stack: [raw_ctx, ...]
2368 auto raw_ctx = llvmModCtx.valueStackPhi.back();
2369 llvmModCtx.valueStackPhi.pop_back();
2370 auto raw_ctx_ptr = llvmModCtx.Builder->CreateIntToPtr(unboxValue(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType), llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2371 auto coro_suspend = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_suspend);
2372 auto suspend_result = llvmModCtx.Builder->CreateCall(coro_suspend, {
2373 llvm::ConstantTokenNone::get(*llvmModCtx.TheContext),
2374 llvm::ConstantInt::get(llvm::Type::getInt1Ty(*llvmModCtx.TheContext), 0)});
2375 auto *resumeBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "resume_bb", llvmModCtx.currentFunction);
2376 auto *switch_inst = llvmModCtx.Builder->CreateSwitch(suspend_result, llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB, 2);
2377 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 0), resumeBB);
2378 switch_inst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 1), llvmModCtx.currentGeneratorContextBasicBlocks.cleanupBB);
2379 callGcFunction(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType, false);
2380 llvmModCtx.Builder->SetInsertPoint(resumeBB);
2381 break;
2382 }
2383 case IR::Opcode::resume: {
2384 // stack: [raw_ctx, ...]
2385 auto raw_ctx = llvmModCtx.valueStackPhi.back();
2386 llvmModCtx.valueStackPhi.pop_back();
2387 auto raw_ctx_ptr = llvmModCtx.Builder->CreateIntToPtr(unboxValue(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType), llvm::PointerType::get(*llvmModCtx.TheContext, 0));
2388 auto coroResume = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_resume);
2389 auto resume_result = llvmModCtx.Builder->CreateCall(coroResume, {
2390 raw_ctx_ptr,
2391 });
2392 callGcFunction(llvmModCtx, raw_ctx.llvmValue, raw_ctx.yoiType, false);
2393 break;
2394 }
2395 case IR::Opcode::nop:
2396 break;
2397 default:
2398 panic(instr.debugInfo.line, instr.debugInfo.column, "LLVM Codegen: Unhandled yoi::IR opcode: " + std::string(magic_enum::enum_name(instr.opcode)));
2399 }
2400 }
2401
2402 llvm::Type* LLVMCodegen::yoiTypeToLLVMType(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType>& type, bool enforceForeignType) {
2403 if (enforceForeignType) {
2404 if (type->isArrayType()) {
2405 yoi::indexT count = 1;
2406 for (auto dim : type->dimensions) {
2407 count *= dim;
2408 }
2409 return llvm::ArrayType::get(yoiTypeToLLVMType(llvmModCtx, managedPtr(type->getElementType()), true), count);
2410 }
2411 auto [typeEnum, typeModule, typeIndex, dim, attr, _a, _b] = type->isBasicRawType() ? type->getBasicObjectType() : *type;
2412 auto key = std::make_tuple(typeEnum, typeModule, typeIndex);
2413 if (llvmModCtx.foreignTypeMap.count(key)) {
2414 return llvmModCtx.foreignTypeMap.at(key);
2415 }
2416 if (type->type == IRValueType::valueType::datastructObject) {
2417 return llvmModCtx.dataStructDataRegionMap.at(type->typeIndex);
2418 }
2419
2420 // yoi_assert(type->isForeignBasicType(), 0, 0, "LLVM Codegen: Enforcing foreign type, but type is not a exported type or basic type.");
2421 } else {
2422 auto key = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex);
2423 if (llvmModCtx.structTypeMap.count(key)) {
2424 if (type->isArrayType() || type->isDynamicArrayType()) {
2425 getArrayLLVMType(llvmModCtx, type);
2426 return llvm::PointerType::get(*llvmModCtx.TheContext, 0);
2427 } else {
2428 return llvm::PointerType::get(*llvmModCtx.TheContext, 0);
2429 }
2430 }
2431 }
2432
2433 // Fallback for non-object types or errors
2434 switch (type->type) {
2435 case IRValueType::valueType::integerRaw:
2436 return llvmModCtx.Builder->getInt64Ty();
2437 case IRValueType::valueType::decimalRaw:
2438 return llvmModCtx.Builder->getDoubleTy();
2439 case IRValueType::valueType::booleanRaw:
2440 return llvmModCtx.Builder->getInt1Ty();
2441 case IRValueType::valueType::shortRaw:
2442 return llvmModCtx.Builder->getInt16Ty();
2443 case IRValueType::valueType::unsignedRaw:
2444 return llvmModCtx.Builder->getInt64Ty();
2445 case IRValueType::valueType::charRaw:
2446 return llvmModCtx.Builder->getInt8Ty();
2447 case IRValueType::valueType::pointer:
2448 case IRValueType::valueType::pointerObject: // generic pointer
2449 return llvm::PointerType::get(*llvmModCtx.TheContext, 0);
2451 return llvmModCtx.Builder->getFloatTy();
2452 case IRValueType::valueType::foreignInt32Type:
2453 return llvmModCtx.Builder->getInt32Ty();
2454 case IRValueType::valueType::none:
2455 return llvmModCtx.Builder->getVoidTy();
2456 case IRValueType::valueType::stringLiteral:
2457 return llvm::PointerType::get(*llvmModCtx.TheContext, 0);
2458 default:
2459 panic(0, 0, "LLVM Codegen: Unhandled or unmapped yoi::IRValueType: " + std::string(magic_enum::enum_name(type->type)));
2460 return nullptr;
2461 }
2462 panic(0, 0, "No LLVM type available for yoiTypeToLLVMType yet: " + yoi::wstring2string(type->to_string()));
2463 }
2464
2465 llvm::FunctionType* LLVMCodegen::getFunctionType(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRFunctionDefinition>& funcDef) {
2466 auto* returnType = yoiTypeToLLVMType(llvmModCtx, funcDef->returnType, funcDef->returnType->hasAttribute(IRValueType::ValueAttr::Raw));
2467
2468 std::vector<llvm::Type*> argTypes;
2469 for (const auto& argType : funcDef->argumentTypes) {
2470 argTypes.push_back(yoiTypeToLLVMType(llvmModCtx, argType, argType->hasAttribute(IRValueType::ValueAttr::Raw)));
2471 }
2472 return llvm::FunctionType::get(returnType, argTypes, false);
2473 }
2474
2475 llvm::Constant* LLVMCodegen::getGlobalInitializer(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType>& type) {
2476 auto* llvmType = yoiTypeToLLVMType(llvmModCtx, type);
2477 return llvm::Constant::getNullValue(llvmType);
2478 }
2479
2480 void LLVMCodegen::handleBinaryOp(LLVMModuleContext &llvmModCtx, llvm::Instruction::BinaryOps op, bool isFloat, yoi::indexT fromBlock, yoi::indexT toBlock) {
2481 auto R = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2482 auto L = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2483 bool isUnsigned = L.yoiType->type == IRValueType::valueType::unsignedObject || L.yoiType->type == IRValueType::valueType::unsignedRaw;
2484
2485 llvm::Value* lValRaw = unboxValue(llvmModCtx, L.llvmValue, L.yoiType);
2486 llvm::Value* rValRaw = unboxValue(llvmModCtx, R.llvmValue, R.yoiType);
2487
2488 bool typesAreFloats = lValRaw->getType()->isDoubleTy() || rValRaw->getType()->isDoubleTy();
2489 auto resultYoiType = L.yoiType->getBasicRawType();
2490
2491 llvm::Value* resultRaw;
2492
2493 if (typesAreFloats) {
2494 if (lValRaw->getType()->isIntegerTy()) lValRaw = llvmModCtx.Builder->CreateSIToFP(lValRaw, llvmModCtx.Builder->getDoubleTy(), "inttofp");
2495 if (rValRaw->getType()->isIntegerTy()) rValRaw = llvmModCtx.Builder->CreateSIToFP(rValRaw, llvmModCtx.Builder->getDoubleTy(), "inttofp");
2496 auto fop = op;
2497 switch(op) {
2498 case llvm::Instruction::Add: fop = llvm::Instruction::FAdd; break;
2499 case llvm::Instruction::Sub: fop = llvm::Instruction::FSub; break;
2500 case llvm::Instruction::Mul: fop = llvm::Instruction::FMul; break;
2501 case llvm::Instruction::SDiv: fop = llvm::Instruction::FDiv; break;
2502 case llvm::Instruction::SRem: fop = llvm::Instruction::FRem; break;
2503 default: panic(0,0, "Unsupported float binary op");
2504 }
2505 resultRaw = llvmModCtx.Builder->CreateBinOp(fop, lValRaw, rValRaw, "fbinop");
2506 } else if (isUnsigned) {
2507 auto newOp = op;
2508 switch (op) {
2509 case llvm::Instruction::SDiv: newOp = llvm::Instruction::UDiv; break;
2510 case llvm::Instruction::SRem: newOp = llvm::Instruction::URem; break;
2511 default: break;
2512 }
2513 resultRaw = llvmModCtx.Builder->CreateBinOp(newOp, lValRaw, rValRaw, "ubinop");
2514 } else {
2515 resultRaw = llvmModCtx.Builder->CreateBinOp(op, lValRaw, rValRaw, "ibinop");
2516 }
2517
2518 llvmModCtx.valueStackPhi.push_back({resultRaw, managedPtr(resultYoiType)});
2519
2520 // Consume operands
2521 callGcFunction(llvmModCtx, L.llvmValue, L.yoiType, false);
2522 callGcFunction(llvmModCtx, R.llvmValue, R.yoiType, false);
2523 }
2524
2525 void LLVMCodegen::handleComparison(LLVMModuleContext &llvmModCtx, llvm::CmpInst::Predicate pred, bool isFloat, yoi::indexT fromBlock, yoi::indexT toBlock) {
2526 auto R = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2527 auto L = llvmModCtx.valueStackPhi.back(); llvmModCtx.valueStackPhi.pop_back();
2528
2529 llvm::Value* lValRaw = L.yoiType->type == IRValueType::valueType::pointerObject ? L.llvmValue : unboxValue(llvmModCtx, L.llvmValue, L.yoiType);
2530 llvm::Value* rValRaw = R.yoiType->type == IRValueType::valueType::pointerObject ? R.llvmValue : unboxValue(llvmModCtx, R.llvmValue, R.yoiType);
2531
2532 bool isUnsigned = L.yoiType->type == IRValueType::valueType::unsignedObject || L.yoiType->type == IRValueType::valueType::unsignedRaw;
2533 bool typesAreFloats = lValRaw->getType()->isDoubleTy() || rValRaw->getType()->isDoubleTy();
2534
2535 llvm::Value* resultRaw;
2536 if (typesAreFloats) {
2537 if (lValRaw->getType()->isIntegerTy()) lValRaw = llvmModCtx.Builder->CreateSIToFP(lValRaw, llvmModCtx.Builder->getDoubleTy(), "inttofp");
2538 if (rValRaw->getType()->isIntegerTy()) rValRaw = llvmModCtx.Builder->CreateSIToFP(rValRaw, llvmModCtx.Builder->getDoubleTy(), "inttofp");
2539
2540 auto fpred = llvm::CmpInst::FCMP_OEQ;
2541 switch(pred) {
2542 case llvm::CmpInst::ICMP_EQ: fpred = llvm::CmpInst::FCMP_OEQ; break;
2543 case llvm::CmpInst::ICMP_NE: fpred = llvm::CmpInst::FCMP_ONE; break;
2544 case llvm::CmpInst::ICMP_SLT: fpred = llvm::CmpInst::FCMP_OLT; break;
2545 case llvm::CmpInst::ICMP_SLE: fpred = llvm::CmpInst::FCMP_OLE; break;
2546 case llvm::CmpInst::ICMP_SGT: fpred = llvm::CmpInst::FCMP_OGT; break;
2547 case llvm::CmpInst::ICMP_SGE: fpred = llvm::CmpInst::FCMP_OGE; break;
2548 default: panic(0,0, "Unsupported float comparison op");
2549 }
2550 resultRaw = llvmModCtx.Builder->CreateFCmp(fpred, lValRaw, rValRaw, "fcmp");
2551 } else if (isUnsigned) {
2552 auto newPred = pred;
2553 switch (pred) {
2554 case llvm::CmpInst::ICMP_SLT: newPred = llvm::CmpInst::ICMP_ULT; break;
2555 case llvm::CmpInst::ICMP_SLE: newPred = llvm::CmpInst::ICMP_ULE; break;
2556 case llvm::CmpInst::ICMP_SGT: newPred = llvm::CmpInst::ICMP_UGT; break;
2557 case llvm::CmpInst::ICMP_SGE: newPred = llvm::CmpInst::ICMP_UGE; break;
2558 default: break;
2559 }
2560 resultRaw = llvmModCtx.Builder->CreateICmp(newPred, lValRaw, rValRaw, "ucmp");
2561 } else {
2562 resultRaw = llvmModCtx.Builder->CreateICmp(pred, lValRaw, rValRaw, "icmp");
2563 }
2564
2565 llvmModCtx.valueStackPhi.push_back({resultRaw, managedPtr(compilerCtx->getBoolObjectType()->getBasicRawType())});
2566
2567 // Consume operands
2568 callGcFunction(llvmModCtx, L.llvmValue, L.yoiType, false);
2569 callGcFunction(llvmModCtx, R.llvmValue, R.yoiType, false);
2570 }
2571
2572 llvm::Value* LLVMCodegen::createBasicObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType>& yoiType, llvm::Value* rawValue) {
2573 if (yoiType->isBasicRawType() || yoiType->hasAttribute(IRValueType::ValueAttr::Raw)) {
2574 auto bitCastedValue = llvmModCtx.Builder->CreateBitCast(rawValue, yoiTypeToLLVMType(llvmModCtx, yoiType, true), "bitcast_val");
2575 return bitCastedValue;
2576 }
2577
2578 auto key = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex);
2579 auto typeIdKey = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex, 0);
2580 auto* objType = llvmModCtx.structTypeMap.at(key);
2581 auto typeId = llvmModCtx.typeIDMap.at(typeIdKey);
2582
2583 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(objType);
2584 auto* sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
2585
2586 auto* allocCall = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"object_alloc"), sizeVal, "new_obj_alloc");
2587 auto* newObjPtr = llvmModCtx.Builder->CreateBitCast(allocCall, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "new_obj_ptr");
2588
2589 auto* refCountPtr = llvmModCtx.Builder->CreateStructGEP(objType, newObjPtr, 0, "refcount_ptr");
2590 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), refCountPtr);
2591
2592 auto typeIdPtr = llvmModCtx.Builder->CreateStructGEP(objType, newObjPtr, 1, "typeid_ptr");
2593 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), typeId), typeIdPtr);
2594
2595 auto* valuePtr = llvmModCtx.Builder->CreateStructGEP(objType, newObjPtr, 2, "value_ptr");
2596 if (yoiType->type == IRValueType::valueType::datastructObject) {
2597 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(objType->getStructElementType(2));
2598 auto *sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
2599 llvmModCtx.Builder->CreateMemCpy(valuePtr, llvm::MaybeAlign(8), rawValue, llvm::MaybeAlign(8), sizeVal);
2600 } else {
2601 llvmModCtx.Builder->CreateStore(rawValue, valuePtr);
2602 }
2603
2604 return newObjPtr;
2605 }
2606
2607 llvm::Value* LLVMCodegen::unboxValue(LLVMModuleContext &llvmModCtx, llvm::Value* objectPtr, const std::shared_ptr<IRValueType>& yoiType) {
2608 if (yoiType->isBasicRawType() || yoiType->hasAttribute(IRValueType::ValueAttr::Raw)) {
2609 if (yoiType->type != IRValueType::valueType::datastructObject) {
2610 auto bitCastedValue = llvmModCtx.Builder->CreateBitCast(objectPtr, yoiTypeToLLVMType(llvmModCtx, yoiType, true), "bitcast_val");
2611 return bitCastedValue;
2612 } else {
2613 return objectPtr;
2614 }
2615 }
2616
2617 auto key = std::make_tuple(yoiType->type, yoiType->typeAffiliateModule, yoiType->typeIndex);
2618 auto* objType = llvmModCtx.structTypeMap.at(key);
2619 auto* valuePtr = llvmModCtx.Builder->CreateStructGEP(objType, objectPtr, 2, "value_ptr");
2620 if (yoiType->type == IRValueType::valueType::datastructObject) {
2621 // extract the pointer would suffice
2622 return valuePtr;
2623 } else {
2624 return llvmModCtx.Builder->CreateLoad(objType->getElementType(2), valuePtr, "unboxed_val");
2625 }
2626 }
2627
2628 llvm::Function *LLVMCodegen::getGcFunction(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &yoiType, bool isIncrease) {
2629 auto finalType = managedPtr(*yoiType);
2630
2631 if (yoiType->type == IRValueType::valueType::interfaceObject && yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
2632 auto impl = yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
2633 if (impl.first != -1) {
2634 auto implDef = yoiModule->interfaceImplementationTable[impl.second];
2635 finalType->type = std::get<0>(implDef->implStructIndex);
2636 finalType->typeAffiliateModule = std::get<1>(implDef->implStructIndex);
2637 finalType->typeIndex = std::get<2>(implDef->implStructIndex);
2638 }
2639 }
2640
2641 std::string funcNameBase;
2642 if (finalType->isArrayType() || finalType->isDynamicArrayType()) {
2643 funcNameBase = "array_" + yoi::wstring2string(finalType->to_string());
2644 } else {
2645 switch(finalType->type) {
2646 case IRValueType::valueType::foreignInt32Type:
2647 case IRValueType::valueType::integerObject: funcNameBase = "basic_int"; break;
2648 case IRValueType::valueType::foreignFloatType:
2649 case IRValueType::valueType::decimalObject: funcNameBase = "basic_decimal"; break;
2650 case IRValueType::valueType::booleanObject: funcNameBase = "basic_bool"; break;
2651 case IRValueType::valueType::stringObject: funcNameBase = "basic_string"; break;
2652 case IRValueType::valueType::characterObject: funcNameBase = "basic_char"; break;
2653 case IRValueType::valueType::shortObject: funcNameBase = "basic_short"; break;
2654 case IRValueType::valueType::unsignedObject: funcNameBase = "basic_unsigned"; break;
2655 case IRValueType::valueType::structObject:
2656 funcNameBase = "struct_" + std::to_string(finalType->typeAffiliateModule) + "_" + std::to_string(finalType->typeIndex);
2657 break;
2658 case IRValueType::valueType::interfaceObject:
2659 funcNameBase = "interface_" + std::to_string(finalType->typeAffiliateModule) + "_" + std::to_string(finalType->typeIndex);
2660 break;
2661 default: return nullptr; // No GC needed for raw types or unhandled types
2662 }
2663 }
2664
2665 auto funcName = funcNameBase + (isIncrease ? "_gc_refcount_increase" : "_gc_refcount_decrease");
2666 auto* gcFunc = llvmModCtx.functionMap.at(string2wstring(funcName));
2667 return gcFunc;
2668 }
2669
2670 void LLVMCodegen::generateDescription(LLVMModuleContext &llvmModCtx) {
2671 auto* descStr = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext,
2672 std::string("hoshi-lang-")
2673 + yoi::wstring2string(compilerCtx->getBuildConfig()->buildPlatform)
2674 + "-"
2675 + yoi::wstring2string(compilerCtx->getBuildConfig()->buildArch),
2676 true);
2677 auto* descGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, descStr->getType(), true, llvm::GlobalValue::LinkageTypes::ExternalLinkage, descStr, "yoi_desc");
2678
2679 auto* buildTypeStr = llvm::ConstantDataArray::getIntegerValue(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), llvm::APInt(64, static_cast<uint64_t>(compilerCtx->getBuildConfig()->buildType)));
2680 auto* buildTypeGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, buildTypeStr->getType(), true, llvm::GlobalValue::LinkageTypes::ExternalLinkage, buildTypeStr, "yoi_build_type");
2681 }
2682
2683 void LLVMCodegen::generateTargetObjectCode(LLVMModuleContext &llvmModCtx, const yoi::wstr &pathToOutput) {
2684 auto TargetTriple = llvm::Triple(llvm::sys::getDefaultTargetTriple());
2685
2686 if (!compilerCtx->getBuildConfig()->targetTriple.empty()) {
2687 TargetTriple = llvm::Triple(yoi::wstring2string(compilerCtx->getBuildConfig()->targetTriple));
2688 } else {
2689 #if defined(BUILD_VARIANT_MSVC)
2690 TargetTriple.setEnvironment(llvm::Triple::MSVC);
2691 #elif defined(BUILD_VARIANT_MINGW)
2692 TargetTriple.setEnvironment(llvm::Triple::GNU);
2693 #else
2694 // leave it to system default
2695 #endif
2696 }
2697
2698 llvmModCtx.TheModule->setTargetTriple(TargetTriple);
2699 std::string Error;
2700 auto Target = llvm::TargetRegistry::lookupTarget(TargetTriple, Error);
2701 if (!Target) {
2702 panic(0, 0, "Could not create target for " + TargetTriple.str() + " (" + Error + ")");
2703 }
2704
2705 auto CPU = llvm::sys::getHostCPUName();
2706
2707 // Automatically detect the features of the host CPU
2708 llvm::SubtargetFeatures SubFeatures;
2709 llvm::StringMap<bool> HostFeatures = llvm::sys::getHostCPUFeatures();
2710 for (auto &F : HostFeatures) {
2711 SubFeatures.AddFeature(F.first(), F.second);
2712 }
2713 auto Features = SubFeatures.getString();
2714 // printf("Target triple %s, using CPU %s with features %s\n", TargetTriple.c_str(), CPU.str().c_str(), !Features.empty() ? Features.c_str() : "N/A");
2715
2716 llvm::TargetOptions Opt;
2717 auto RM = std::optional<llvm::Reloc::Model>(llvm::Reloc::PIC_);
2718 llvm::CodeGenOptLevel OptLevel = compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::release ? llvm::CodeGenOptLevel::Aggressive : llvm::CodeGenOptLevel::None;
2719 llvm::OptimizationLevel OptLevelPB = compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::release ? llvm::OptimizationLevel::O3 : llvm::OptimizationLevel::O0;
2720
2721 std::unique_ptr<llvm::TargetMachine> TM(
2722 Target->createTargetMachine(llvm::Triple(TargetTriple), CPU, Features, Opt, RM, std::optional<llvm::CodeModel::Model>(), OptLevel));
2723
2724 if (!TM) {
2725 panic(0, 0, "Could not create TargetMachine for " + TargetTriple.str());
2726 }
2727
2728 llvmModCtx.TheModule->setDataLayout(TM->createDataLayout());
2729
2730 std::error_code EC;
2731 llvm::raw_fd_ostream Dest(yoi::wstring2string(pathToOutput), EC, llvm::sys::fs::OF_None);
2732 if (EC) {
2733 panic(0, 0, "Could not open file for writing: " + yoi::wstring2string(pathToOutput) + " (" + EC.message() + ")");
2734 }
2735
2736 llvm::PassBuilder PB;
2737 llvm::LoopAnalysisManager LAM;
2738 llvm::FunctionAnalysisManager FAM;
2739 llvm::CGSCCAnalysisManager CGAM;
2740 llvm::ModuleAnalysisManager MAM;
2741
2742 // Register all the analysis passes with the managers.
2743 PB.registerModuleAnalyses(MAM);
2744 PB.registerCGSCCAnalyses(CGAM);
2745 PB.registerFunctionAnalyses(FAM);
2746 PB.registerLoopAnalyses(LAM);
2747 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
2748
2749 // Create the optimization pipeline for the module
2750 llvm::ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OptLevelPB);
2751
2752 // Optional: Add a verifier pass to check IR correctness after optimizations
2753 // This is good for debugging but can be removed for release builds.
2754 MPM.addPass(llvm::VerifierPass());
2755
2756 // Run the optimization pipeline on the module
2757 MPM.run(*llvmModCtx.TheModule, MAM);
2758
2759 llvm::legacy::PassManager CodeGenPasses;
2760 llvm::CodeGenFileType FileType = llvm::CodeGenFileType::ObjectFile; // To emit a .o file
2761
2762 if (TM->addPassesToEmitFile(CodeGenPasses, Dest, nullptr, FileType)) {
2763 panic(0, 0, "TargetMachine can't emit a file of this type");
2764 }
2765
2766 CodeGenPasses.run(*llvmModCtx.TheModule);
2767 Dest.flush();
2768 }
2769
2770 void LLVMCodegen::generateForeignStructTypes(LLVMModuleContext &llvmModCtx) {
2771 for (auto &foreignTypePair : compilerCtx->getIRFFITable()->foreignTypeTable) {
2772 auto &typeName = foreignTypePair.first;
2773 auto typeId = std::make_tuple(IRValueType::valueType::structObject, foreignTypePair.second->typeAffiliateModule, foreignTypePair.second->typeIndex);
2774 auto structType = yoiModule->structTable[foreignTypePair.second->typeIndex];
2775 yoi::vec<std::string> fieldNames;
2776 yoi::vec<llvm::Type*> fieldTypes;
2777 for (auto &name : structType->nameIndexMap) {
2778 if (name.second.type != IRStructDefinition::nameInfo::nameType::field) continue;
2779
2780 auto fieldType = yoiTypeToLLVMType(llvmModCtx, structType->fieldTypes[name.second.index], true);
2781 fieldNames.push_back(yoi::wstring2string(name.first));
2782 }
2783 auto llvmStructType = llvm::StructType::create(*llvmModCtx.TheContext, fieldTypes);
2784 // add to foreign type map
2785 llvmModCtx.foreignTypeMap[typeId] = llvmStructType;
2786 }
2787 }
2788
2789 void LLVMCodegen::generateExportFunctionDecls(LLVMModuleContext &llvmModCtx) {
2790 for (auto &exportedFunction : compilerCtx->getIRFFITable()->exportedFunctionTable) {
2791 auto &funcName = exportedFunction.first;
2792 auto &mangledName = yoiModule->functionTable.getKey(std::get<1>(exportedFunction.second));
2793 auto &funcDecl = yoiModule->functionTable[std::get<1>(exportedFunction.second)];
2794 auto &attrs = std::get<2>(exportedFunction.second);
2795 bool noffi = std::find(attrs.begin(), attrs.end(), IRFunctionDefinition::FunctionAttrs::NoFFI) != attrs.end();
2796
2797 if (noffi) {
2798 llvm::Type *returnType = yoiTypeToLLVMType(llvmModCtx, funcDecl->returnType, false);
2799 yoi::vec<llvm::Type*> argTypes;
2800 for (auto &argType : funcDecl->argumentTypes) {
2801 argTypes.push_back(yoiTypeToLLVMType(llvmModCtx, argType, false)); // make sure all types converted
2802 }
2803 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, argTypes, false);
2804 llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, yoi::wstring2string(funcName), llvmModCtx.TheModule.get());
2805 llvmModCtx.functionMap[funcName] = func;
2806
2807 llvm::BasicBlock *BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", func);
2808 llvmModCtx.Builder->SetInsertPoint(BB);
2809 // load arguments
2811 auto it = func->arg_begin();
2812 for (auto &arg : funcDecl->argumentTypes) {
2813 args.push_back(it++);
2814 }
2815 // call function
2816 auto *mangledFunction = llvmModCtx.functionMap.at(mangledName);
2817 auto *result = llvmModCtx.Builder->CreateCall(mangledFunction, args, "result");
2818 // return with result
2819 llvmModCtx.Builder->CreateRet(result);
2820 } else {
2821 llvm::Type *returnType = yoiTypeToLLVMType(llvmModCtx, funcDecl->returnType, true);
2822 yoi::vec<llvm::Type*> argTypes;
2823 for (auto &argType : funcDecl->argumentTypes) {
2824 argTypes.push_back(yoiTypeToLLVMType(llvmModCtx, argType, true)); // make sure all types converted
2825 }
2826 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, argTypes, false);
2827 llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, yoi::wstring2string(funcName), llvmModCtx.TheModule.get());
2828 llvmModCtx.functionMap[funcName] = func;
2829
2830 // add basic block
2831 llvm::BasicBlock *BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", func);
2832 llvmModCtx.Builder->SetInsertPoint(BB);
2833
2834 // load arguments
2836 auto it = func->arg_begin();
2837 for (auto &arg : funcDecl->argumentTypes) {
2838 yoi_assert(!arg->isArrayType() && !arg->isDynamicArrayType(), funcDecl->debugInfo.line, funcDecl->debugInfo.column, "Array return type not supported for foreign functions");
2839 if (arg->isBasicType()) {
2840 auto *argVal = createBasicObject(llvmModCtx, arg, it);
2841 args.push_back(argVal);
2842 } else {
2843 auto handledLLVMType = handleForeignTypeConv(llvmModCtx, it, arg->typeIndex, 0, false); // convert to yoi type
2844 args.push_back(handledLLVMType);
2845 }
2846 it ++;
2847 }
2848 // call function
2849 auto *mangledFunction = llvmModCtx.functionMap.at(mangledName);
2850 auto *result = llvmModCtx.Builder->CreateCall(mangledFunction, args, "result");
2851 llvm::Value *actualResultVal = nullptr;
2852 // convert result to foreign type
2853 yoi_assert(!funcDecl->returnType->isArrayType() && !funcDecl->returnType->isDynamicArrayType(), funcDecl->debugInfo.line, funcDecl->debugInfo.column, "Array return type not supported for foreign functions");
2854 if (funcDecl->returnType->isBasicType()) {
2855 actualResultVal = unboxValue(llvmModCtx, result, funcDecl->returnType);
2856 } else {
2857 actualResultVal = handleForeignTypeConv(llvmModCtx, result, funcDecl->returnType->typeIndex, 0, true); // convert back to foreign type
2858 }
2859 // resource releasing
2860 callGcFunction(llvmModCtx, result, funcDecl->returnType, false);
2861 for (auto &arg : funcDecl->argumentTypes) {
2862 callGcFunction(llvmModCtx, args.back(), arg, false);
2863 args.pop_back();
2864 }
2865
2866 // return with actual result
2867 llvmModCtx.Builder->CreateRet(actualResultVal);
2868 }
2869 }
2870
2871
2872 }
2873
2874 llvm::Value *LLVMCodegen::handleForeignTypeConv(LLVMModuleContext &llvmModCtx, llvm::Value *val, yoi::indexT foreignTypeIndex, yoi::indexT isArray, bool convertToForeign) {
2875 // get the foreign type
2876 auto &foreignType = compilerCtx->getIRFFITable()->foreignTypeTable[foreignTypeIndex];
2877 auto &originalType = yoiModule->structTable[foreignType->typeIndex];
2878 // get the llvm type
2879 auto llvmType = llvmModCtx.foreignTypeMap.at(std::make_tuple(IRValueType::valueType::structObject, foreignType->typeAffiliateModule, foreignType->typeIndex));
2880 auto objectLLVMType = llvmModCtx.structTypeMap.at(std::make_tuple(IRValueType::valueType::structObject, foreignType->typeAffiliateModule, foreignType->typeIndex));
2881
2882 auto copyToOne = [&](llvm::Value *src, llvm::Value *dest) {
2883 // convert yoi type to foreign type
2884 for (yoi::indexT i = 0; i < originalType->fieldTypes.size(); i++) {
2885 // get the field value
2886 auto *fieldPtr = llvmModCtx.Builder->CreateStructGEP(objectLLVMType, val, i + 2, "field_ptr");
2887 auto *destFieldPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, dest, i, "dest_field_ptr");
2888 auto &fieldType = originalType->fieldTypes[i];
2889 llvm::Value *fieldVal = nullptr;
2890 if (fieldType->isBasicType()) {
2891 fieldVal = unboxValue(llvmModCtx, fieldPtr, fieldType);
2892 } else if (fieldType->isForeignBasicType()) {
2893 fieldVal = handleForeignTypeConv(llvmModCtx, fieldPtr, fieldType, true);
2894 } else {
2895 fieldVal = handleForeignTypeConv(llvmModCtx, fieldPtr, fieldType->typeIndex, 0, true);
2896 }
2897 // count field size
2898 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(yoiTypeToLLVMType(llvmModCtx, fieldType, true));
2899 // populate memory
2900 llvmModCtx.Builder->CreateMemCpy(destFieldPtr, llvm::MaybeAlign(8), fieldVal, llvm::MaybeAlign(8), size);
2901 }
2902 };
2903
2904 if (convertToForeign) {
2905 llvm::Value *srcObjectToCopy = nullptr;
2906 llvm::Value *rawMemory = nullptr;
2907
2908 if (isArray != 0) {
2909 // load value
2910 auto arrayLLVMType = llvmModCtx.arrayTypeMap.at(std::make_tuple(IRValueType::valueType::structObject, foreignType->typeAffiliateModule, foreignType->typeIndex, isArray));
2911 auto *loadedVal = llvmModCtx.Builder->CreateLoad(arrayLLVMType, val, "loaded_val");
2912 // offset to 2
2913 auto *arrayLength = llvmModCtx.Builder->CreateLoad(
2914 llvmModCtx.Builder->getInt64Ty(),
2915 llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, loadedVal, 2, "array_length"),
2916 "array_length_val"
2917 );
2918
2919 rawMemory = llvmModCtx.Builder->CreateAlloca(llvmType, arrayLength, "yoi_to_foreign_alloca");
2920
2921 for (yoi::indexT i = 0; i < isArray; i++) {
2922 // get the array element
2923 auto *element = loadArrayElement(llvmModCtx, managedPtr(IRValueType{
2924 IRValueType::valueType::structObject,
2925 foreignType->typeAffiliateModule,
2926 foreignType->typeIndex,
2927 {isArray}
2928 }), val, llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), i));
2929 // copy to foreign type
2930 auto *dest = llvmModCtx.Builder->CreateGEP(llvmType, rawMemory, {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), i)});
2931 copyToOne(element, dest);
2932 }
2933 } else {
2934 srcObjectToCopy = val;
2935 rawMemory = llvmModCtx.Builder->CreateAlloca(llvmType, nullptr, "yoi_to_foreign_alloca");
2936 copyToOne(srcObjectToCopy, rawMemory);
2937 }
2938 return rawMemory;
2939 } else {
2940 llvm::Value *rawMemory = llvmModCtx.Builder->CreateAlloca(objectLLVMType, nullptr, "foreign_to_yoi_alloca");
2941 // convert foreign type to yoi type
2942 for (yoi::indexT i = 0; i < originalType->fieldTypes.size(); i++) {
2943 // get the field value
2944 auto *fieldPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, rawMemory, i + 2, "field_ptr");
2945 auto &fieldType = originalType->fieldTypes[i];
2946 llvm::Value *fieldVal = nullptr;
2947 if (fieldType->isBasicType()) {
2948 auto *loadedFieldValue = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, fieldType, true), fieldPtr, "loaded_field_val");
2949 fieldVal = createBasicObject(llvmModCtx, fieldType, fieldPtr);
2950 } else if (fieldType->isForeignBasicType()) {
2951 fieldVal = handleForeignTypeConv(llvmModCtx, fieldPtr, fieldType, false);
2952 } else {
2953 fieldVal = handleForeignTypeConv(llvmModCtx, fieldPtr, fieldType->typeIndex, 0, false);
2954 }
2955 // populate memory using store
2956 llvmModCtx.Builder->CreateStore(fieldVal, fieldPtr);
2957 }
2958 return rawMemory;
2959 }
2960 }
2961
2962 void LLVMCodegen::generateMainFunction(LLVMModuleContext &llvmModCtx) {
2963 if (compilerCtx->getBuildConfig()->buildType == IRBuildConfig::BuildType::executable) {
2964 yoi::vec<llvm::Type*> argTypes = {
2965 llvm::Type::getInt32Ty(*llvmModCtx.TheContext),
2966 llvm::PointerType::get(*llvmModCtx.TheContext, 0)
2967 };
2968 llvm::FunctionType *funcType = llvm::FunctionType::get(llvm::Type::getInt32Ty(*llvmModCtx.TheContext), argTypes, false);
2969 llvm::Function *elysiaMain = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "elysia_main", llvmModCtx.TheModule.get());
2970 llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", llvmModCtx.TheModule.get());
2971 llvmModCtx.functionMap[L"main"] = mainFunc;
2972
2973 // add basic block
2974 llvm::BasicBlock *BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", mainFunc);
2975 llvmModCtx.Builder->SetInsertPoint(BB);
2976
2977 auto it = mainFunc->arg_begin();
2978 auto argc = it++;
2979 auto argv = it++;
2980
2981 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
2982 // print starting message
2983 std::string startMsg = "Starting hoshi-lang program...\n";
2984 auto* startStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, startMsg, true);
2985 auto* startStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, startStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, startStrConst, "start_str");
2986 auto startArgs = std::array<llvm::Value*, 1>{ startStrGlobal };
2987 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print"), llvm::ArrayRef<llvm::Value*>(startArgs));
2988 // print argc and argv by runtime_print_int and runtime_print_address
2989 // i32 to i64
2990 auto argc_i64 = llvmModCtx.Builder->CreateSExt(argc, llvmModCtx.Builder->getInt64Ty(), "argc_i64");
2991 // print argc
2992 auto argcArgs = std::array<llvm::Value*, 1>{ argc_i64 };
2993 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print_int"), llvm::ArrayRef<llvm::Value*>(argcArgs));
2994 auto argvArgs = std::array<llvm::Value*, 1>{ argv };
2995 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_print_address"), llvm::ArrayRef<llvm::Value*>(argvArgs));
2996 }
2997
2998 // invoke elysia_main
2999 auto res = llvmModCtx.Builder->CreateCall(elysiaMain, {argc, argv}, "result");
3000
3001 // return with result
3002 llvmModCtx.Builder->CreateRet(res);
3003 }
3004 }
3005
3006 void LLVMCodegen::generateImportFunctionImplementations(LLVMModuleContext &llvmModCtx) {
3007 yoi::indexT moduleIndex = 0;
3008 for (auto &libraryPair : compilerCtx->getIRFFITable()->importedLibraries) {
3009 auto &libraryName = libraryPair.first;
3010 for (auto &functionPair : libraryPair.second.importedFunctionTable) {
3011 auto &funcName = functionPair.first;
3012 auto wrapperMangledName = L"imported#" + std::to_wstring(moduleIndex) + L"#" + funcName + L"#wrapper";
3013 auto mangledName = L"imported#" + std::to_wstring(moduleIndex) + L"#" + funcName;
3014 auto &funcDef = functionPair.second;
3015 bool noffi = funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::NoFFI);
3016 auto &wrapperFuncDecl = llvmModCtx.functionMap[wrapperMangledName];
3017 auto &externFuncDecl = llvmModCtx.functionMap[mangledName];
3018
3019 // generate wrapper function
3020 // create basic block
3021 if (!noffi) {
3022 llvm::BasicBlock *BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", wrapperFuncDecl);
3023 llvmModCtx.Builder->SetInsertPoint(BB);
3024
3025 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
3026 // print function name
3027 std::string funcName = wstring2string(funcDef->name);
3028 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
3029 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
3030 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
3031 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_current_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
3032 }
3033
3034 yoi_assert(!funcDef->returnType->isArrayType() && !funcDef->returnType->isDynamicArrayType(), funcDef->debugInfo.line, funcDef->debugInfo.column, "Array return type not supported for foreign functions");
3035
3037 auto it = wrapperFuncDecl->arg_begin();
3038 for (auto &arg : funcDef->argumentTypes) {
3039 if (arg->isForeignBasicType()) {
3040 auto *argVal = handleForeignTypeConv(llvmModCtx, it, arg, true);
3041 // callGcFunction(llvmModCtx, it, arg, false); // no gc now, cause all raw value
3042 args.push_back(argVal);
3043 } else if (arg->isBasicType()) {
3044 if (arg->isArrayType() || arg->isDynamicArrayType()) {
3045 auto arrayLLVMType = getArrayLLVMType(llvmModCtx, arg);
3046 auto *object = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), it, "loaded_arg");
3047 // struct gep to array data
3048 auto *arrayData = llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, object, 3, "array_data");
3049 // bitcast to pointer type
3050 auto *arrayDataPtr = llvmModCtx.Builder->CreateBitCast(arrayData, llvm::PointerType::get(*llvmModCtx.TheContext, 0));
3051 args.push_back(arrayDataPtr);
3052 } else {
3053 // auto *argVal = unboxValue(llvmModCtx, it, managedPtr(arg->getBasicRawType()));
3054 // auto *argVal = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, arg, true), it, "loaded_arg");
3055 args.push_back(it);
3056 }
3057 } else {
3058 auto handledLLVMType = handleForeignTypeConv(llvmModCtx, it, arg->typeIndex, 0, true);
3059 callGcFunction(llvmModCtx, it, arg, false);
3060 args.push_back(handledLLVMType);
3061 }
3062 it++;
3063 }
3064
3065 if (funcDef->returnType->type == IRValueType::valueType::none) {
3066 llvmModCtx.Builder->CreateCall(externFuncDecl, args);
3067 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
3068 // print function name
3069 std::string funcName = wstring2string(funcDef->name);
3070 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
3071 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
3072 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
3073 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_leave_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
3074 }
3075 llvmModCtx.Builder->CreateRetVoid();
3076 } else {
3077 auto result = llvmModCtx.Builder->CreateCall(externFuncDecl, args, "result");
3078
3079 llvm::Value *actualResultVal = nullptr;
3080 if (funcDef->returnType->isForeignBasicType()) {
3081 actualResultVal = handleForeignTypeConv(llvmModCtx, result, funcDef->returnType, false);
3082 } else if (funcDef->returnType->isBasicType()) {
3083 actualResultVal = result;
3084 } else {
3085 actualResultVal = handleForeignTypeConv(llvmModCtx, result, funcDef->returnType->typeIndex, 0, false); //convert back to yoi type
3086 }
3087
3088 if (compilerCtx->getBuildConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
3089 // print function name
3090 std::string funcName = wstring2string(funcDef->name);
3091 auto* debugStrConst = llvm::ConstantDataArray::getString(*llvmModCtx.TheContext, funcName, true);
3092 auto* debugStrGlobal = new llvm::GlobalVariable(*llvmModCtx.TheModule, debugStrConst->getType(), true, llvm::GlobalVariable::PrivateLinkage, debugStrConst, "debug_str");
3093 auto debugArgs = std::array<llvm::Value*, 1>{ debugStrGlobal };
3094 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_debug_report_leave_function"), llvm::ArrayRef<llvm::Value*>(debugArgs));
3095 }
3096
3097 // return with actual result
3098 llvmModCtx.Builder->CreateRet(actualResultVal);
3099 }
3100 }
3101 }
3102 moduleIndex ++;
3103 }
3104 }
3105
3106 void LLVMCodegen::generateImportFunctionDeclarations(LLVMModuleContext &llvmModCtx) {
3107 yoi::indexT moduleIndex = 0;
3108 for (auto &libraryPair : compilerCtx->getIRFFITable()->importedLibraries) {
3109 auto &libraryName = libraryPair.first;
3110 if (libraryName != L"builtin")
3111 compilerCtx->getBuildConfig()->additionalLinkingFiles.push_back(libraryName);
3112 for (auto &functionPair : libraryPair.second.importedFunctionTable) {
3113 auto &funcName = functionPair.first;
3114 bool noffi = std::find(functionPair.second->attrs.begin(), functionPair.second->attrs.end(), IRFunctionDefinition::FunctionAttrs::NoFFI) != functionPair.second->attrs.end();
3115
3116 // generate extern function first
3117 llvm::Type *returnType = yoiTypeToLLVMType(llvmModCtx, functionPair.second->returnType, !noffi);
3118 yoi::vec<llvm::Type*> argTypes;
3119 for (auto &argType : functionPair.second->argumentTypes) {
3120 argTypes.push_back(yoiTypeToLLVMType(llvmModCtx, argType, !noffi)); // make sure all types converted
3121 }
3122 llvm::FunctionType *funcType = llvm::FunctionType::get(returnType, argTypes, false);
3123 llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, yoi::wstring2string(funcName), llvmModCtx.TheModule.get());
3124
3125 // add to function map
3126 auto mangledName = L"imported#" + std::to_wstring(moduleIndex) + L"#" + funcName;
3127 llvmModCtx.functionMap[mangledName] = func;
3128
3129 // then generate wrapper function decl
3130 if (!noffi) {
3131 auto wrapperReturnYoiType = normalizeForeignType(llvmModCtx, functionPair.second->returnType);
3132 llvm::Type *wrapperReturnType = yoiTypeToLLVMType(llvmModCtx, wrapperReturnYoiType, wrapperReturnYoiType->isBasicType());
3133 yoi::vec<llvm::Type*> wrapperArgTypes;
3134 for (auto &argType : functionPair.second->argumentTypes) {
3135 auto paramYoiType = normalizeForeignType(llvmModCtx, argType); // normalize foreign int32 type to integerObject
3136 if (paramYoiType->isBasicType()) {
3137 // if parameter is a basic type, we pass it as raw value, so as reduce the FFI cost
3138 wrapperArgTypes.push_back(yoiTypeToLLVMType(llvmModCtx, paramYoiType, true));
3139 } else {
3140 wrapperArgTypes.push_back(yoiTypeToLLVMType(llvmModCtx, paramYoiType, false)); // otherwise, object
3141 }
3142 }
3143 llvm::FunctionType *wrapperFuncType = llvm::FunctionType::get(wrapperReturnType, wrapperArgTypes, false);
3144 llvm::Function *wrapperFunc = llvm::Function::Create(wrapperFuncType, llvm::Function::ExternalLinkage, yoi::wstring2string(mangledName), llvmModCtx.TheModule.get());
3145
3146 // add to function map
3147 auto wrapperMangledName = L"imported#" + std::to_wstring(moduleIndex) + L"#" + funcName + L"#wrapper";
3148 llvmModCtx.functionMap[wrapperMangledName] = wrapperFunc;
3149 }
3150 }
3151 moduleIndex ++;
3152 }
3153 }
3154
3155 std::shared_ptr<IRValueType>
3156 LLVMCodegen::normalizeForeignType(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type) {
3157 switch (type->type) {
3158 case IRValueType::valueType::foreignFloatType: {
3159 return compilerCtx->getDeciObjectType();
3160 }
3161 case IRValueType::valueType::foreignInt32Type: {
3162 return compilerCtx->getIntObjectType();
3163 }
3164 case IRValueType::valueType::pointer:
3165 case IRValueType::valueType::pointerObject: {
3166 return compilerCtx->getUnsignedObjectType();
3167 }
3168 default: {
3169 return type;
3170 }
3171 }
3172 }
3173
3174 llvm::Value *LLVMCodegen::handleForeignTypeConv(LLVMModuleContext &llvmModCtx, llvm::Value *val,
3175 const std::shared_ptr<IRValueType> &foreignType,
3176 bool convertToForeign) {
3177 yoi_assert(foreignType->isForeignBasicType(), 0, 0, "foreign type must be a basic type");
3178 switch (foreignType->type) {
3179 case IRValueType::valueType::foreignFloatType: {
3180 if (convertToForeign) {
3181 // unbox double type and convert to float type
3182 // since the default behaviour is changed, we now unbox raw value
3183 // auto *doubleVal = unboxValue(llvmModCtx, val, managedPtr(compilerCtx->getDeciObjectType()->getBasicRawType()));
3184 // auto *doubleVal = llvmModCtx.Builder->CreateLoad(llvm::Type::getDoubleTy(*llvmModCtx.TheContext), val, "double_val");
3185 auto *floatVal = llvmModCtx.Builder->CreateFPTrunc(val, llvm::Type::getFloatTy(*llvmModCtx.TheContext), "float_val");
3186 return floatVal;
3187 } else {
3188 // convert float type to double type
3189 auto *floatVal = llvmModCtx.Builder->CreateFPExt(val, llvm::Type::getDoubleTy(*llvmModCtx.TheContext), "float_val");
3190 return floatVal;
3191 }
3192 }
3193 case IRValueType::valueType::foreignInt32Type: {
3194 if (convertToForeign) {
3195 // unbox integer type and convert to int32 type
3196 // auto *intVal = unboxValue(llvmModCtx, val, managedPtr(compilerCtx->getIntObjectType()->getBasicRawType()));
3197 // auto *intVal = llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), val, "int_val");
3198 auto *int32Val = llvmModCtx.Builder->CreateTrunc(val, llvm::Type::getInt32Ty(*llvmModCtx.TheContext), "int32_val");
3199 return int32Val;
3200 } else {
3201 // convert int32 type to integer type
3202 auto *int32Val = llvmModCtx.Builder->CreateSExt(val, llvm::Type::getInt64Ty(*llvmModCtx.TheContext), "int32_val");
3203 // create new object
3204 // auto *newObj = createBasicObject(llvmModCtx, compilerCtx->getIntObjectType(), int32Val);
3205 return int32Val;
3206 }
3207 }
3208 case IRValueType::valueType::pointer: {
3209 if (convertToForeign) {
3210 // auto *ptrVal = unboxValue(llvmModCtx, val, managedPtr(compilerCtx->getUnsignedObjectType()->getBasicRawType()));
3211 // auto *ptrVal = llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), val, "ptr_val");
3212 // bit cast void*
3213 auto *voidPtr = llvmModCtx.Builder->CreateIntToPtr(val, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "void_ptr");
3214 return voidPtr;
3215 } else {
3216 // bitcast to i64
3217 auto *voidPtr = llvmModCtx.Builder->CreateBitCast(val, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "void_ptr");
3218 auto *int64Val = llvmModCtx.Builder->CreatePtrToInt(voidPtr, llvm::Type::getInt64Ty(*llvmModCtx.TheContext), "int64_val");
3219 // create new object
3220 // auto *newObj = createBasicObject(llvmModCtx, compilerCtx->getIntObjectType(), int64Val);
3221 return int64Val;
3222 }
3223 }
3224 default: {
3225 yoi_assert(false, 0, 0, "unsupported foreign type");
3226 return nullptr;
3227 }
3228 }
3229 }
3230
3231 llvm::Type *LLVMCodegen::getArrayLLVMType(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type, bool enforceForeignType) {
3232 if (enforceForeignType) {
3233 return llvm::PointerType::get(*llvmModCtx.TheContext, 0);
3234 } else {
3235 yoi::indexT size = 1;
3236 if (type->isArrayType()) {
3237 for (auto &i : type->dimensions) {
3238 size *= i;
3239 }
3240 } else if (type->isDynamicArrayType()) {
3241 size = static_cast<yoi::indexT>(-1);
3242 }
3243
3244 std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT> arrayKey = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex, size);
3245 if (auto it = llvmModCtx.arrayTypeMap.find(arrayKey); it!= llvmModCtx.arrayTypeMap.end()) {
3246 return it->second;
3247 }
3248 yoi::indexT arrayTypeId = -1;
3249 if (auto it = llvmModCtx.typeIDMap.find(arrayKey); it != llvmModCtx.typeIDMap.end()) {
3250 arrayTypeId = it->second;
3251 } else {
3252 arrayTypeId = llvmModCtx.nextTypeId++;
3253 llvmModCtx.typeIDMap[arrayKey] = arrayTypeId;
3254 }
3255
3256 std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT> structKey = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex);
3257
3258 llvm::Type *baseType = nullptr;
3259 switch (type->type) {
3260 case IRValueType::valueType::integerObject:
3261 baseType = llvm::Type::getInt64Ty(*llvmModCtx.TheContext);
3262 break;
3263 case IRValueType::valueType::decimalObject:
3264 baseType = llvm::Type::getDoubleTy(*llvmModCtx.TheContext);
3265 break;
3266 case IRValueType::valueType::unsignedObject:
3267 baseType = llvm::Type::getInt64Ty(*llvmModCtx.TheContext);
3268 break;
3269 case IRValueType::valueType::shortObject:
3270 baseType = llvm::Type::getInt16Ty(*llvmModCtx.TheContext);
3271 break;
3272 case IRValueType::valueType::booleanObject:
3273 baseType = llvm::Type::getInt1Ty(*llvmModCtx.TheContext);
3274 break;
3275 case IRValueType::valueType::characterObject:
3276 baseType = llvm::Type::getInt8Ty(*llvmModCtx.TheContext);
3277 break;
3278 case IRValueType::valueType::stringObject:
3279 baseType = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
3280 break;
3281 case IRValueType::valueType::structObject:
3282 case IRValueType::valueType::interfaceObject:
3283 baseType = llvm::PointerType::get(*llvmModCtx.TheContext, 0); // only this is a object
3284 break;
3285 default:
3286 panic(0, 0, "LLVM Codegen: Unhandled or unmapped array type: " + std::string(magic_enum::enum_name(type->type)));
3287 return nullptr;
3288 }
3289 auto arrayType = llvm::ArrayType::get(baseType, type->isArrayType() ? size : 1);
3290 // build struct with ref counter
3291 auto structType = llvm::StructType::create(*llvmModCtx.TheContext, yoi::vec<llvm::Type*>{
3292 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // ref counter
3293 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // type id
3294 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // array length
3295 arrayType // array
3296 });
3297 auto fullStructName = "array_" + yoi::wstring2string(type->to_string()) + "_" + (type->isArrayType() ? std::to_string(size) : "dynamic");
3298 generateArrayGCFunctionDeclarations(llvmModCtx, type, structType, baseType);
3299 llvmModCtx.arrayToGenerateImplementations.emplace_back(type, structType, baseType);
3300 // add struct to struct map
3301 llvmModCtx.arrayTypeMap[arrayKey] = structType;
3302 // clean up the mess, reset the insert point
3303 return structType;
3304 }
3305 }
3306
3307 llvm::Value *LLVMCodegen::createArrayObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type,
3308 const yoi::vec<StackValue> &elements) {
3309 yoi_assert(type->isArrayType(), 0, 0, "type must be an array type");
3310 llvm::Type *llvmType = getArrayLLVMType(llvmModCtx, type, false);
3311 // initialize the llvm struct, allocate memory and store the array
3312 auto memSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(llvmType);
3313 auto *memoryPointer = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"object_alloc"), {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), memSize, true)});
3314
3315 // increase the refcount to 1
3316 auto *refCounter = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 0, "ref_counter");
3317 auto *refCounterVal = llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 1, true);
3318 llvmModCtx.Builder->CreateStore(refCounterVal, refCounter);
3319
3320 yoi::indexT size = 1;
3321 for (auto &i : type->dimensions) {
3322 size *= i;
3323 }
3324 std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT> arrayKey = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex, size);
3325 auto typeId = llvmModCtx.typeIDMap.at(arrayKey);
3326 auto *typeIdPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 1, "type_id_ptr");
3327 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), typeId, true), typeIdPtr);
3328
3329 // store the array length
3330 auto arrayLengthPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 2, "array_length_ptr");
3331 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), size, true), arrayLengthPtr);
3332
3333 // store the array
3334 yoi::indexT index = 0;
3335 auto arrayBasePointer = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 3, "array_ptr");
3336 for (auto &i : elements) {
3337 // if basic type, unbox it first
3338 if (type->isBasicType()) {
3339 auto elementLLVMType = yoiTypeToLLVMType(llvmModCtx, managedPtr(type->getElementType()), true);
3340 auto arrayPointer = llvmModCtx.Builder->CreateGEP(elementLLVMType, arrayBasePointer, {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), index)}, "array_element_ptr");
3341 auto val = unboxValue(llvmModCtx, i.llvmValue, i.yoiType);
3342 llvmModCtx.Builder->CreateStore(val, arrayPointer);
3343 } else {
3344 // otherwise, store the pointer directly
3345 auto arrayPointer = llvmModCtx.Builder->CreateGEP(llvm::PointerType::get(*llvmModCtx.TheContext, 0), arrayBasePointer, {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), index)}, "array_element_ptr");
3346 llvmModCtx.Builder->CreateStore(i.llvmValue, arrayPointer);
3347 }
3348 index ++;
3349 }
3350 return memoryPointer;
3351 }
3352
3353 llvm::Value *
3354 LLVMCodegen::loadArrayElement(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type, llvm::Value *arrayPtr, llvm::Value *index) {
3355 yoi_assert(type->isArrayType() || type->isDynamicArrayType(), 0, 0, "type must be an array type");
3356 llvm::Type *llvmType = getArrayLLVMType(llvmModCtx, type, false);
3357
3358 auto *arrayPointer = llvmModCtx.Builder->CreateStructGEP(getArrayLLVMType(llvmModCtx, type), arrayPtr, 3, "array_ptr");
3359 switch (type->type) {
3360 case IRValueType::valueType::integerObject:
3361 case IRValueType::valueType::decimalObject:
3362 case IRValueType::valueType::booleanObject:
3363 case IRValueType::valueType::stringObject:
3364 case IRValueType::valueType::shortObject:
3365 case IRValueType::valueType::unsignedObject:
3366 case IRValueType::valueType::characterObject: {
3367 auto elementType = managedPtr(type->getElementType());
3368 auto elementLLVMType = yoiTypeToLLVMType(llvmModCtx, elementType, true);
3369 auto pointerToElement = llvmModCtx.Builder->CreateGEP(elementLLVMType, arrayPointer, {
3370 index
3371 }, "element_ptr");
3372 auto loadedVal = llvmModCtx.Builder->CreateLoad(elementLLVMType, pointerToElement, "loaded_val"); // get unboxed value, so ffi type
3373 return loadedVal;
3374 }
3375 case IRValueType::valueType::structObject:
3376 case IRValueType::valueType::interfaceObject: {
3377 auto elementType = managedPtr(type->getElementType());
3378 if (type->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope))
3379 elementType->addAttribute(IRValueType::ValueAttr::PermanentInCurrentScope);
3380 elementType->addAttribute(IRValueType::ValueAttr::Nullable);
3381 auto elementLLVMType = yoiTypeToLLVMType(llvmModCtx, elementType);
3382 auto pointerToElement = llvmModCtx.Builder->CreateGEP(elementLLVMType, arrayPointer, index, "element_ptr");
3383 auto *loadedVal = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, elementType), pointerToElement, "array_element_loaded_val");
3384 callGcFunction(llvmModCtx, loadedVal, elementType, true); // increase ref count
3385 return loadedVal;
3386 }
3387 default: {
3388 panic(0, 0, "LLVM Codegen: Unhandled or unmapped array type: " + std::string(magic_enum::enum_name(type->type)));
3389 return nullptr;
3390 }
3391 }
3392
3393 }
3394 LLVMCodegen::ControlFlowAnalysis::ControlFlowAnalysis(const std::vector<std::shared_ptr<IRCodeBlock>> &blocks) {
3395 for (yoi::indexT i = 0; i < blocks.size(); i++) {
3396 for (auto &ins : blocks[i]->getIRArray()) {
3397 switch (ins.opcode) {
3398 case IR::Opcode::jump:
3399 case IR::Opcode::jump_if_true:
3400 case IR::Opcode::jump_if_false: {
3401 G[i].push_back(ins.operands[0].value.symbolIndex);
3402 reverseG[ins.operands[0].value.symbolIndex].push_back(i);
3403 break;
3404 }
3405 default: {
3406 break;
3407 }
3408 }
3409 }
3410 }
3411 }
3412
3413 llvm::DIType *LLVMCodegen::getDIType(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type) {
3414 if (llvmModCtx.basicDITypeMap.count(L"di_i8_ptr")) {
3415 // assume if one is there, all are (at least the ones we need at the start)
3416 } else {
3417 auto* di_i8 = llvmModCtx.DBuilder->createBasicType("char", 8, llvm::dwarf::DW_ATE_signed_char);
3418 auto* di_i64 = llvmModCtx.DBuilder->createBasicType("long long", 64, llvm::dwarf::DW_ATE_signed);
3419
3420 llvmModCtx.basicDITypeMap[L"di_i8"] = di_i8;
3421 llvmModCtx.basicDITypeMap[L"di_i16"] = llvmModCtx.DBuilder->createBasicType("short", 16, llvm::dwarf::DW_ATE_signed);
3422 llvmModCtx.basicDITypeMap[L"di_i64"] = di_i64;
3423 llvmModCtx.basicDITypeMap[L"di_i64_u"] = llvmModCtx.DBuilder->createBasicType("unsigned long long", 64, llvm::dwarf::DW_ATE_unsigned);
3424 llvmModCtx.basicDITypeMap[L"di_double"] = llvmModCtx.DBuilder->createBasicType("double", 64, llvm::dwarf::DW_ATE_float);
3425 llvmModCtx.basicDITypeMap[L"di_i1"] = llvmModCtx.DBuilder->createBasicType("bool", 8, llvm::dwarf::DW_ATE_boolean);
3426 llvmModCtx.basicDITypeMap[L"di_i8_ptr"] = llvmModCtx.DBuilder->createPointerType(di_i8, 64);
3427
3428 auto* unknown_object_struct = llvmModCtx.DBuilder->createStructType(
3429 llvmModCtx.compileUnits[L"builtin"],
3430 "unknown_object",
3431 llvmModCtx.compileUnits[L"builtin"]->getFile(),
3432 1,
3433 64 + 64,
3434 64,
3435 llvm::DINode::FlagZero,
3436 nullptr,
3437 llvmModCtx.DBuilder->getOrCreateArray({
3438 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "refcount", nullptr, 0, 64, 64, 0, llvm::DINode::FlagZero, di_i64),
3439 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "typeid", nullptr, 0, 64, 64, 64, llvm::DINode::FlagZero, di_i64),
3440 })
3441 );
3442 llvmModCtx.basicDITypeMap[L"di_unknown_object_ptr"] = llvmModCtx.DBuilder->createPointerType(unknown_object_struct, 64);
3443 }
3444
3445 auto* di_i64_u = llvmModCtx.basicDITypeMap[L"di_i64_u"];
3446 auto* di_i64 = llvmModCtx.basicDITypeMap[L"di_i64"];
3447 auto* di_double = llvmModCtx.basicDITypeMap[L"di_double"];
3448 auto* di_i1 = llvmModCtx.basicDITypeMap[L"di_i1"];
3449 auto* di_i16 = llvmModCtx.basicDITypeMap[L"di_i16"];
3450 auto* di_i8 = llvmModCtx.basicDITypeMap[L"di_i8"];
3451 auto* di_i8_ptr = llvmModCtx.basicDITypeMap[L"di_i8_ptr"];
3452 auto* di_unknown_object_ptr = llvmModCtx.basicDITypeMap[L"di_unknown_object_ptr"];
3453
3454 if (type->isArrayType() || type->isDynamicArrayType()) {
3455 yoi::indexT size = 1;
3456 llvm::SmallVector<llvm::Metadata*, 8> dimensions;
3457 if (type->isArrayType()) {
3458 for (yoi::indexT i : type->dimensions) {
3459 size *= i;
3460 dimensions.push_back(llvmModCtx.DBuilder->getOrCreateSubrange(0, static_cast<int64_t>(i)));
3461 }
3462 } else {
3463 dimensions.push_back(llvmModCtx.DBuilder->getOrCreateSubrange(0, static_cast<int64_t>(0)));
3464 }
3465
3466 auto arrayKey = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex, type->isArrayType() ? size : static_cast<yoi::indexT>(-1));
3467 if (llvmModCtx.arrayDataRegionDITypeMap.count(arrayKey) && type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3468 return llvmModCtx.arrayDataRegionDITypeMap[arrayKey];
3469 } if (llvmModCtx.arrayTypeDIMap.count(arrayKey)) {
3470 return llvmModCtx.arrayTypeDIMap[arrayKey];
3471 }
3472
3473 llvm::DIType *elementDIType = nullptr;
3474 if (type->isBasicType()) {
3475 switch (type->type) {
3476 case IRValueType::valueType::integerObject:
3477 elementDIType = di_i64;
3478 break;
3479 case IRValueType::valueType::decimalObject:
3480 elementDIType = di_double;
3481 break;
3482 case IRValueType::valueType::booleanObject:
3483 elementDIType = di_i1;
3484 break;
3485 case IRValueType::valueType::characterObject:
3486 elementDIType = di_i8;
3487 break;
3488 case IRValueType::valueType::stringObject:
3489 elementDIType = di_i8_ptr;
3490 break;
3491 case IRValueType::valueType::unsignedObject:
3492 elementDIType = di_i64_u;
3493 break;
3494 case IRValueType::valueType::shortObject:
3495 elementDIType = di_i16;
3496 break;
3497 default:
3498 panic(0, 0, "LLVM Codegen: Unhandled or unmapped array element type: " + std::string(magic_enum::enum_name(type->type)));
3499 break;
3500 }
3501 } else {
3502 elementDIType = getDIType(llvmModCtx, managedPtr(type->getElementType()));
3503 }
3504
3505 auto *llvmElementRawType = yoiTypeToLLVMType(llvmModCtx, managedPtr(type->getElementType()), type->hasAttribute(IRValueType::ValueAttr::Raw));
3506 auto arraySizeInBits = size * llvmModCtx.TheModule->getDataLayout().getTypeSizeInBits(llvmElementRawType);
3507 auto* diArray = llvmModCtx.DBuilder->createArrayType(
3508 arraySizeInBits, llvmModCtx.TheModule->getDataLayout().getABITypeAlign(llvmElementRawType).value() * 8, elementDIType, {llvmModCtx.DBuilder->getOrCreateArray(dimensions)});
3509
3510 llvmModCtx.arrayDataRegionDITypeMap[arrayKey] = diArray;
3511
3512 auto *diArrayStruct = llvmModCtx.DBuilder->createStructType(
3513 llvmModCtx.compileUnits[L"builtin"],
3514 "array_" + wstring2string(type->to_string()),
3515 llvmModCtx.compileUnits[L"builtin"]->getFile(),
3516 1,
3517 64 + 64 + 64 + arraySizeInBits,
3518 64,
3519 llvm::DINode::FlagZero,
3520 nullptr,
3521 llvmModCtx.DBuilder->getOrCreateArray({
3522 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "refcount", nullptr, 0, 64, 64, 0, llvm::DINode::FlagZero, di_i64),
3523 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "typeid", nullptr, 0, 64, 64, 64, llvm::DINode::FlagZero, di_i64),
3524 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "array_length", nullptr, 0, 64, 64, 128, llvm::DINode::FlagZero, di_i64),
3525 llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "array", nullptr, 0, arraySizeInBits, 64, 192, llvm::DINode::FlagZero, diArray)
3526 })
3527 );
3528 auto *resultDIType = llvmModCtx.DBuilder->createPointerType(diArrayStruct, 64);
3529 llvmModCtx.arrayTypeDIMap[arrayKey] = resultDIType;
3530 if (type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3531 return diArray;
3532 } else {
3533 return resultDIType;
3534 }
3535 } else {
3536 auto key = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex);
3537
3538 if (type->isBasicType() && type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3539 switch (type->type) {
3540 case IRValueType::valueType::integerObject:
3541 return di_i64;
3542 case IRValueType::valueType::decimalObject:
3543 return di_double;
3544 case IRValueType::valueType::booleanObject:
3545 return di_i1;
3546 case IRValueType::valueType::characterObject:
3547 return di_i8;
3548 case IRValueType::valueType::stringObject:
3549 return di_i8_ptr;
3550 case IRValueType::valueType::unsignedObject:
3551 return di_i64_u;
3552 case IRValueType::valueType::shortObject:
3553 return di_i16;
3554 case IRValueType::valueType::datastructObject: {
3555 // it doesn't follow the general rule, so we need to handle it separately.
3556 if (llvmModCtx.dataStructDataRegionTypeDIMap.count(type->typeIndex) && type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3557 return llvmModCtx.DBuilder->createPointerType(llvmModCtx.dataStructDataRegionTypeDIMap[type->typeIndex], 64);
3558 } else if (llvmModCtx.dataStructDataRegionTypeDIMap.count(type->typeIndex) && !type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3559 return llvmModCtx.structTypeDIMap[key];
3560 }
3561
3562 auto dataStructDef = yoiModule->dataStructTable[type->typeIndex];
3563 auto* diDataRegionType = llvmModCtx.dataStructDataRegionTypeDIMap[type->typeIndex];
3564
3565 // generate the data region if not exists
3566 if (!diDataRegionType) {
3567 std::string dataRegionTypeName = "yoi.data_region." + wstring2string(type->to_string());
3568 llvm::SmallVector<llvm::Metadata *, 8> dataRegionMemberTypes;
3569
3570 auto *llvmDataRegionType = llvmModCtx.dataStructDataRegionMap.at(type->typeIndex);
3571 auto *layout = &llvmModCtx.TheModule->getDataLayout();
3572 auto *structLayout = layout->getStructLayout(llvmDataRegionType);
3573
3574 for (yoi::indexT i = 0; i < dataStructDef->fieldTypes.size(); i++) {
3575 auto memberType = managedPtr(*dataStructDef->fieldTypes[i]);
3576 memberType->addAttribute(IRValueType::ValueAttr::Raw);
3577
3578 auto memberTypeDI = getDIType(llvmModCtx, memberType);
3579 uint64_t fieldSize = memberTypeDI->getSizeInBits();
3580 uint64_t fieldOffset = structLayout->getElementOffsetInBits(i);
3581
3582 // find the field name
3583 std::string fieldName = "field" + std::to_string(i);
3584 for (auto &fieldPair : dataStructDef->fields) {
3585 if (fieldPair.second == i) {
3586 fieldName = wstring2string(fieldPair.first);
3587 break;
3588 }
3589 }
3590
3591 auto memberDIType = llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"],
3592 fieldName,
3593 nullptr,
3594 0,
3595 fieldSize,
3596 memberTypeDI->getAlignInBits(), // alignment
3597 fieldOffset,
3598 llvm::DINode::FlagZero,
3599 memberTypeDI);
3600 dataRegionMemberTypes.push_back(memberDIType);
3601 }
3602
3603 diDataRegionType = llvmModCtx.DBuilder->createStructType(llvmModCtx.compileUnits[L"builtin"],
3604 dataRegionTypeName,
3605 llvmModCtx.compileUnits[L"builtin"]->getFile(),
3606 1, // Line number
3607 structLayout->getSizeInBits(),
3608 structLayout->getAlignment().value() * 8, // Use ABI alignment
3609 llvm::DINode::FlagZero,
3610 nullptr,
3611 llvmModCtx.DBuilder->getOrCreateArray(dataRegionMemberTypes));
3612 llvmModCtx.dataStructDataRegionTypeDIMap[type->typeIndex] = diDataRegionType;
3613 }
3614
3615 if (type->hasAttribute(IRValueType::ValueAttr::Raw)) {
3616 return diDataRegionType;
3617 }
3618
3619 // generate the struct type
3620 std::string structTypeName = "yoi." + wstring2string(type->to_string());
3621 llvm::SmallVector<llvm::Metadata *, 8> objectMemberTypes;
3622
3623 // All our objects start with a refcount.
3624 objectMemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(
3625 llvmModCtx.compileUnits[L"builtin"], "refcount", nullptr, 0, 64, 64, 0, llvm::DINode::FlagZero, di_i64));
3626 objectMemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(
3627 llvmModCtx.compileUnits[L"builtin"], "typeid", nullptr, 0, 64, 64, 64, llvm::DINode::FlagZero, di_i64));
3628 uint64_t objectCurrentSize = 128; // Keep track of struct size
3629
3630 objectMemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"],
3631 "value",
3632 nullptr,
3633 0,
3634 diDataRegionType->getSizeInBits(),
3635 diDataRegionType->getAlignInBits(),
3636 objectCurrentSize,
3637 llvm::DINode::FlagZero,
3638 diDataRegionType));
3639 objectCurrentSize += diDataRegionType->getSizeInBits();
3640
3641 auto diStructType = llvmModCtx.DBuilder->createStructType(llvmModCtx.compileUnits[L"builtin"],
3642 structTypeName,
3643 llvmModCtx.compileUnits[L"builtin"]->getFile(),
3644 1, // Line number
3645 objectCurrentSize, // Size in bits
3646 64, // Alignment in bits
3647 llvm::DINode::FlagZero,
3648 nullptr,
3649 llvmModCtx.DBuilder->getOrCreateArray(objectMemberTypes));
3650
3651 auto diStructTypePtr = llvmModCtx.DBuilder->createPointerType(diStructType, 64);
3652 llvmModCtx.structTypeDIMap[key] = diStructTypePtr;
3653 return diStructTypePtr;
3654 }
3655 default:
3656 panic(0, 0, "LLVM Codegen: Unhandled or unmapped raw type: " + std::string(magic_enum::enum_name(type->type)));
3657 break;
3658 }
3659 }
3660
3661 if (llvmModCtx.structTypeDIMap.count(key)) {
3662 // printf("Existing Type Identifier: %lld %lld %lld, leave.\n", type->type, type->typeAffiliateModule, type->typeIndex);
3663 return llvmModCtx.structTypeDIMap[key];
3664 }
3665 // printf("Current Type Identifier: %lld %lld %lld\n", type->type, type->typeAffiliateModule, type->typeIndex);
3666
3667 std::string typeName = "yoi." + wstring2string(type->to_string());
3668
3669 llvm::DICompositeType *diFwdDecl = llvmModCtx.DBuilder->createReplaceableCompositeType(
3670 llvm::dwarf::DW_TAG_structure_type,
3671 typeName,
3672 llvmModCtx.compileUnits[L"builtin"],
3673 llvmModCtx.compileUnits[L"builtin"]->getFile(),
3674 1 // Line number
3675 );
3676
3677 auto* resultDIType = llvmModCtx.DBuilder->createPointerType(diFwdDecl, 64);
3678 llvmModCtx.structTypeDIMap[key] = resultDIType;
3679
3680 // An array to hold the DITypes of the struct members.
3681 llvm::SmallVector<llvm::Metadata*, 8> MemberTypes;
3682
3683 // All our objects start with a refcount.
3684 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(
3685 llvmModCtx.compileUnits[L"builtin"],
3686 "refcount",
3687 nullptr,
3688 0,
3689 64,
3690 64,
3691 0,
3692 llvm::DINode::FlagZero,
3693 di_i64
3694 ));
3695 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(
3696 llvmModCtx.compileUnits[L"builtin"],
3697 "typeid",
3698 nullptr,
3699 0,
3700 64,
3701 64,
3702 64,
3703 llvm::DINode::FlagZero,
3704 di_i64
3705 ));
3706 uint64_t currentSize = 128; // Keep track of struct size
3707
3708 // 2. Generate the body of the type based on the Yoi type.
3709 switch (type->type) {
3710 case IRValueType::valueType::integerObject: {
3711 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 64, 64, currentSize, llvm::DINode::FlagZero, di_i64));
3712 currentSize += 64;
3713 break;
3714 }
3715 case IRValueType::valueType::stringObject: {
3716 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 64, 64, currentSize, llvm::DINode::FlagZero, di_i8_ptr));
3717 currentSize += 64;
3718 break;
3719 }
3720 case IRValueType::valueType::decimalObject: {
3721 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 64, 64, currentSize, llvm::DINode::FlagZero, di_double));
3722 currentSize += 64;
3723 break;
3724 }
3725 case IRValueType::valueType::booleanObject: {
3726 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 8, 8, currentSize, llvm::DINode::FlagZero, di_i1));
3727 currentSize += 8;
3728 break;
3729 }
3730 case IRValueType::valueType::characterObject: {
3731 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 8, 8, currentSize, llvm::DINode::FlagZero, di_i8));
3732 currentSize += 8;
3733 break;
3734 }
3735 case IRValueType::valueType::unsignedObject: {
3736 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 64, 64, currentSize, llvm::DINode::FlagZero, di_i64_u));
3737 currentSize += 64;
3738 break;
3739 }
3740 case IRValueType::valueType::shortObject: {
3741 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 16, 16, currentSize, llvm::DINode::FlagZero, di_i16));
3742 currentSize += 16;
3743 break;
3744 }
3745 case IRValueType::valueType::structObject: {
3746 auto structDef = yoiModule->structTable[type->typeIndex];
3747 yoi::vec<std::string> fieldNames(structDef->fieldTypes.size());
3748 for (auto it = structDef->nameIndexMap.begin(); it!= structDef->nameIndexMap.end(); ++it) {
3749 if (it->second.type == IRStructDefinition::nameInfo::nameType::method)
3750 continue;
3751 auto fieldName = wstring2string(it->first);
3752 fieldNames[it->second.index] = fieldName;
3753 }
3754 for (yoi::indexT i = 0; i < fieldNames.size(); i++) {
3755 auto fieldYoiType = structDef->fieldTypes[i];
3756 // Recursively get the DIType for the field.
3757 auto* fieldDIType = getDIType(llvmModCtx, fieldYoiType);
3758 uint64_t fieldSize = llvmModCtx.TheModule->getDataLayout().getTypeSizeInBits(yoiTypeToLLVMType(llvmModCtx, fieldYoiType));
3759
3760 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(
3761 llvmModCtx.compileUnits[L"builtin"], fieldNames[i], nullptr, 0,
3762 fieldSize, fieldSize, currentSize,
3763 llvm::DINode::FlagZero, fieldDIType
3764 ));
3765 currentSize += fieldSize;
3766 }
3767 break;
3768 }
3769 case IRValueType::valueType::interfaceObject: {
3770 MemberTypes.push_back(llvmModCtx.DBuilder->createMemberType(llvmModCtx.compileUnits[L"builtin"], "value", nullptr, 0, 64, 64, currentSize, llvm::DINode::FlagZero, di_unknown_object_ptr));
3771 currentSize += 64;
3772 break;
3773 }
3774 case IRValueType::valueType::none:
3775 default: {
3776 // 'none' object only has a refcount.
3777 break;
3778 }
3779 }
3780
3781 // 3. Create the DIStructType for the object itself.
3782 auto* diStruct = llvmModCtx.DBuilder->createStructType(
3783 llvmModCtx.compileUnits[L"builtin"], // Scope
3784 typeName,
3785 llvmModCtx.compileUnits[L"builtin"]->getFile(), // File
3786 1, // Line number (can be 0)
3787 currentSize, // Size in bits
3788 64, // Alignment in bits
3789 llvm::DINode::FlagZero,
3790 nullptr, // Derived from
3791 llvmModCtx.DBuilder->getOrCreateArray(MemberTypes)
3792 );
3793
3794 auto node = llvm::TempMDNode(diFwdDecl);
3795 llvmModCtx.DBuilder->replaceTemporary(std::move(node), diStruct);
3796 // diFwdDecl->replaceAllUsesWith(diStruct);
3797 // llvm::errs() << " [" << typeName << "] replaceTemporary returned FinalNode: " << finalNode << "\n";
3798
3799 return resultDIType;
3800 }
3801 }
3802
3803 void LLVMCodegen::generateRTTIImplmentation(LLVMModuleContext &llvmModCtx) {
3804 // Generate the RTTI for the Yoi types.
3805 yoi::vec<llvm::Constant *> rttiFields(llvmModCtx.typeIDMap.size());
3806 auto RTTITableType = llvm::ArrayType::get(llvmModCtx.RTTIEntryType, llvmModCtx.typeIDMap.size());
3807 for (auto &typeIndexPair : llvmModCtx.typeIDMap) {
3808 auto typeId = typeIndexPair.second;
3809 std::string typenameString;
3810 auto yoiType = yoi::IRValueType{std::get<0>(typeIndexPair.first), std::get<1>(typeIndexPair.first), std::get<2>(typeIndexPair.first)};
3811 if (yoiType.isBasicType()) {
3812 typenameString = yoi::wstring2string(yoiType.to_string());
3813 } else if (yoiType.type == IRValueType::valueType::structObject) {
3814 typenameString = yoi::wstring2string(yoiModule->structTable[std::get<2>(typeIndexPair.first)]->name);
3815 } else if (yoiType.type == IRValueType::valueType::interfaceObject) {
3816 typenameString = yoi::wstring2string(yoiModule->interfaceTable[std::get<2>(typeIndexPair.first)]->name);
3817 }
3818 if (std::get<3>(typeIndexPair.first) != 0) {
3819 if (std::get<3>(typeIndexPair.first) == static_cast<yoi::indexT>(-1)) {
3820 typenameString += "[]";
3821 } else {
3822 typenameString += "[" + std::to_string(std::get<3>(typeIndexPair.first)) + "]";
3823 }
3824 }
3825
3826 std::array<llvm::Constant *, 6> rtti_entry_field{
3827 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), typeId),
3828 llvmModCtx.Builder->CreateGlobalString(typenameString, "rtti_type_name"),
3829 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), static_cast<yoi::indexT>(std::get<0>(typeIndexPair.first))),
3830 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), std::get<1>(typeIndexPair.first)),
3831 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), std::get<2>(typeIndexPair.first)),
3832 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), std::get<3>(typeIndexPair.first)),
3833 };
3834 rttiFields[typeId] = llvm::ConstantStruct::get(llvmModCtx.RTTIEntryType, rtti_entry_field);
3835 }
3836 auto RTTIConstantDataArray = llvm::ConstantArray::get(RTTITableType, rttiFields);
3837 llvmModCtx.RTTITable->setInitializer(RTTIConstantDataArray);
3838 }
3839
3840 void LLVMCodegen::generateRTTIDeclaration(LLVMModuleContext &llvmModCtx) {
3841 for (auto &funcPair : yoiModule->functionTable) {
3842 auto funcDef = funcPair.second;
3843 for (auto &blocks : funcDef->codeBlock) {
3844 for (auto &ins : blocks->getIRArray()) {
3845 switch (ins.opcode) {
3846 case IR::Opcode::new_array_bool:
3847 case IR::Opcode::new_array_int:
3848 case IR::Opcode::new_array_deci:
3849 case IR::Opcode::new_array_str:
3850 case IR::Opcode::new_array_unsigned:
3851 case IR::Opcode::new_array_short:
3852 case IR::Opcode::new_array_char: {
3853 std::shared_ptr<IRValueType> elementType;
3854 switch (ins.opcode) {
3855 case IR::Opcode::new_array_int:
3856 elementType = compilerCtx->getIntObjectType();
3857 break;
3858 case IR::Opcode::new_array_deci:
3859 elementType = compilerCtx->getDeciObjectType();
3860 break;
3861 case IR::Opcode::new_array_bool:
3862 elementType = compilerCtx->getBoolObjectType();
3863 break;
3864 case IR::Opcode::new_array_str:
3865 elementType = compilerCtx->getStrObjectType();
3866 break;
3867 case IR::Opcode::new_array_char:
3868 elementType = compilerCtx->getCharObjectType();
3869 break;
3870 case IR::Opcode::new_array_short:
3871 elementType = compilerCtx->getShortObjectType();
3872 break;
3873 case IR::Opcode::new_array_unsigned:
3874 elementType = compilerCtx->getUnsignedObjectType();
3875 break;
3876 default:
3877 break;
3878 }
3880 for (yoi::indexT i = 1;i < ins.operands.size(); i++) {
3881 dims.push_back(ins.operands[i].value.symbolIndex);
3882 }
3883 getArrayLLVMType(llvmModCtx, managedPtr(elementType->getArrayType(dims)));
3884 break;
3885 }
3886 case IR::Opcode::new_array_interface:
3887 case IR::Opcode::new_array_struct: {
3889 for (yoi::indexT i = 3;i < ins.operands.size(); i++) {
3890 dims.push_back(ins.operands[i].value.symbolIndex);
3891 }
3892 auto arrayType = managedPtr(IRValueType{
3893 ins.opcode == IR::Opcode::new_array_struct ? IRValueType::valueType::structObject : IRValueType::valueType::interfaceObject,
3894 ins.operands[0].value.symbolIndex,
3895 ins.operands[1].value.symbolIndex,
3896 dims
3897 });
3898 getArrayLLVMType(llvmModCtx, arrayType);
3899 break;
3900 }
3901 case IR::Opcode::new_dynamic_array_bool:
3902 case IR::Opcode::new_dynamic_array_int:
3903 case IR::Opcode::new_dynamic_array_deci:
3904 case IR::Opcode::new_dynamic_array_str:
3905 case IR::Opcode::new_dynamic_array_unsigned:
3906 case IR::Opcode::new_dynamic_array_short:
3907 case IR::Opcode::new_dynamic_array_char: {
3908 std::shared_ptr<IRValueType> elementType;
3909 switch (ins.opcode) {
3910 case IR::Opcode::new_dynamic_array_int:
3911 elementType = compilerCtx->getIntObjectType();
3912 break;
3913 case IR::Opcode::new_dynamic_array_deci:
3914 elementType = compilerCtx->getDeciObjectType();
3915 break;
3916 case IR::Opcode::new_dynamic_array_bool:
3917 elementType = compilerCtx->getBoolObjectType();
3918 break;
3919 case IR::Opcode::new_dynamic_array_str:
3920 elementType = compilerCtx->getStrObjectType();
3921 break;
3922 case IR::Opcode::new_dynamic_array_char:
3923 elementType = compilerCtx->getCharObjectType();
3924 break;
3925 case IR::Opcode::new_dynamic_array_unsigned:
3926 elementType = compilerCtx->getUnsignedObjectType();
3927 break;
3928 case IR::Opcode::new_dynamic_array_short:
3929 elementType = compilerCtx->getShortObjectType();
3930 break;
3931 default:
3932 break;
3933 }
3934 getArrayLLVMType(llvmModCtx, managedPtr(elementType->getDynamicArrayType()));
3935 break;
3936 }
3937 case IR::Opcode::new_dynamic_array_interface:
3938 case IR::Opcode::new_dynamic_array_struct: {
3939 auto arrayType = managedPtr(IRValueType{
3940 ins.opcode == IR::Opcode::new_dynamic_array_struct ? IRValueType::valueType::structObject : IRValueType::valueType::interfaceObject,
3941 ins.operands[0].value.symbolIndex,
3942 ins.operands[1].value.symbolIndex,
3943 {static_cast<yoi::indexT>(-1)}
3944 });
3945 getArrayLLVMType(llvmModCtx, arrayType);
3946 break;
3947 }
3948 default: break;
3949 }
3950 }
3951 }
3952 }
3953
3954 llvmModCtx.RTTIEntryType = llvm::StructType::get(*llvmModCtx.TheContext, {
3955 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // type id
3956 llvm::PointerType::get(*llvmModCtx.TheContext, 0), // type name
3957 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // type enum
3958 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // type affiliate module
3959 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // type index
3960 llvm::Type::getInt64Ty(*llvmModCtx.TheContext), // array size if provided, otherwise 0
3961 });
3962 auto RTTITableType = llvm::ArrayType::get(llvmModCtx.RTTIEntryType, llvmModCtx.typeIDMap.size());
3963 llvmModCtx.RTTITable = new llvm::GlobalVariable(*llvmModCtx.TheModule, RTTITableType, true, llvm::GlobalValue::LinkageTypes::ExternalLinkage, nullptr, "rtti_table");
3964 is_rtti_table_frozen = true;
3965 }
3966
3967 llvm::Value *LLVMCodegen::createDynamicArrayObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type,
3968 const yoi::vec<StackValue> &elements,
3969 llvm::Value *size) {
3970 yoi_assert(type->isDynamicArrayType(), 0, 0, "type must be an dynamic array type");
3971 llvm::Type *llvmType = getArrayLLVMType(llvmModCtx, type);
3972 auto key = std::make_tuple(type->type, type->typeAffiliateModule, type->typeIndex, type->dimensions.back()); // dims back should always be -1
3973 auto memSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(llvmType);
3974 auto elementSize = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(llvmModCtx.arrayTypeMap[key]->getElementType(3));
3975 memSize -= elementSize; // pure header length
3976
3977 // now calculate the total size of the array
3978 llvm::Value *totalSize = llvmModCtx.Builder->CreateAdd(
3979 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), memSize),
3980 llvmModCtx.Builder->CreateMul(size, llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), elementSize), "array_size"),
3981 "total_dyn_array_size"
3982 );
3983 // allocate memory
3984 auto *memoryPointer = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"object_alloc"), {totalSize});
3985 // increase the refcount to 1
3986 auto *refCounter = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 0, "ref_counter");
3987 auto *refCounterVal = llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 1, true);
3988 llvmModCtx.Builder->CreateStore(refCounterVal, refCounter);
3989 // store the type id
3990 auto typeId = llvmModCtx.typeIDMap.at(key);
3991 auto *typeIdPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 1, "type_id_ptr");
3992 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), typeId, true), typeIdPtr);
3993 // store array length
3994 auto arrayLengthPtr = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 2, "array_length_ptr");
3995 llvmModCtx.Builder->CreateStore(size, arrayLengthPtr);
3996 // store array elements
3997 auto arrayBasePointer = llvmModCtx.Builder->CreateStructGEP(llvmType, memoryPointer, 3, "array_ptr");
3998 auto index = 0;
3999 for (auto &element : elements) {
4000 if (type->isBasicType()) {
4001 auto elementLLVMType = yoiTypeToLLVMType(llvmModCtx, managedPtr(type->getElementType()), true);
4002 auto arrayPointer = llvmModCtx.Builder->CreateGEP(elementLLVMType, arrayBasePointer, {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), index)}, "array_element_ptr");
4003 auto val = unboxValue(llvmModCtx, element.llvmValue, element.yoiType);
4004 llvmModCtx.Builder->CreateStore(val, arrayPointer);
4005 } else {
4006 // otherwise, store the pointer directly
4007 auto arrayPointer = llvmModCtx.Builder->CreateGEP(llvm::PointerType::get(*llvmModCtx.TheContext, 0), arrayBasePointer, {llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), index)}, "array_element_ptr");
4008 llvmModCtx.Builder->CreateStore(element.llvmValue, arrayPointer);
4009 }
4010 index ++;
4011 }
4012 return memoryPointer;
4013 }
4014
4015 void LLVMCodegen::generateArrayGCFunctionDeclarations(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type, llvm::StructType *structType, llvm::Type *baseType) {
4016 // create gc function
4017 auto incFuncName = "array_" + yoi::wstring2string(type->to_string()) + "_gc_refcount_increase";
4018 auto decFuncName = "array_" + yoi::wstring2string(type->to_string()) + "_gc_refcount_decrease";
4019
4020 auto currentInsertPoint = llvmModCtx.Builder->GetInsertBlock();
4021
4022 if (auto it = llvmModCtx.functionMap.find(yoi::string2wstring(incFuncName)) == llvmModCtx.functionMap.end()) {
4023 auto gcIncFuncType = llvm::FunctionType::get(
4024 llvm::Type::getVoidTy(*llvmModCtx.TheContext), {llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
4025 auto gcIncFunc =
4026 llvm::Function::Create(gcIncFuncType, llvm::Function::ExternalLinkage, incFuncName, llvmModCtx.TheModule.get());
4027 llvmModCtx.functionMap[yoi::string2wstring(incFuncName)] = gcIncFunc;
4028 }
4029 if (auto it = llvmModCtx.functionMap.find(yoi::string2wstring(decFuncName)) == llvmModCtx.functionMap.end()) {
4030 auto gcDecFuncType = llvm::FunctionType::get(
4031 llvm::Type::getVoidTy(*llvmModCtx.TheContext), {llvm::PointerType::get(*llvmModCtx.TheContext, 0)}, false);
4032 auto gcDecFunc =
4033 llvm::Function::Create(gcDecFuncType, llvm::Function::ExternalLinkage, decFuncName, llvmModCtx.TheModule.get());
4034 llvmModCtx.functionMap[yoi::string2wstring(decFuncName)] = gcDecFunc;
4035 }
4036 llvmModCtx.Builder->SetInsertPoint(currentInsertPoint);
4037 }
4038
4039 void LLVMCodegen::storeArrayElement(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type,
4040 const std::shared_ptr<IRValueType> &valueToStoreType,
4041 llvm::Value *arrayPtr,
4042 llvm::Value *index,
4043 llvm::Value *value) {
4044 auto arrayLLVMType = getArrayLLVMType(llvmModCtx, type);
4045 if (type->isBasicType()) {
4046 auto elementLLVMType = yoiTypeToLLVMType(llvmModCtx, managedPtr(type->getElementType()), true);
4047 auto basePointer = llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, arrayPtr, 3, "array_ptr");
4048 auto elementPointer = llvmModCtx.Builder->CreateGEP(elementLLVMType, basePointer, {index}, "array_element_ptr");
4049 auto val = unboxValue(llvmModCtx, value, valueToStoreType);
4050 llvmModCtx.Builder->CreateStore(val, elementPointer);
4051 } else {
4052 // otherwise, store the pointer directly
4053 auto basePointer = llvmModCtx.Builder->CreateStructGEP(arrayLLVMType, arrayPtr, 3, "array_ptr");
4054 auto elementPointer = llvmModCtx.Builder->CreateGEP(llvm::PointerType::get(*llvmModCtx.TheContext, 0), basePointer, {index}, "array_element_ptr");
4055 auto loadedPointer = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), elementPointer, "loaded_pointer");
4056 callGcFunction(llvmModCtx, loadedPointer, managedPtr(type->getElementType().addAttribute(IRValueType::ValueAttr::Nullable)), false);
4057
4058 llvmModCtx.Builder->CreateStore(value, elementPointer);
4059 // increase the ref count of the object
4060 callGcFunction(llvmModCtx, value, valueToStoreType, true);
4061 }
4062 }
4063
4064 void LLVMCodegen::generateArrayGCFunctionImplementations(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type,
4065 llvm::StructType *structType,
4066 llvm::Type *baseType) {
4067 // create gc function
4068 auto incFuncName = "array_" + yoi::wstring2string(type->to_string()) + "_gc_refcount_increase";
4069 auto decFuncName = "array_" + yoi::wstring2string(type->to_string()) + "_gc_refcount_decrease";
4070
4071 auto currentInsertPoint = llvmModCtx.Builder->GetInsertBlock();
4072
4073 {
4074 auto gcIncFunc = llvmModCtx.functionMap[yoi::string2wstring(incFuncName)];
4075 gcIncFunc->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);
4076 // add basic block
4077 llvm::BasicBlock *BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", gcIncFunc);
4078 llvmModCtx.Builder->SetInsertPoint(BB);
4079 auto *objPtr = gcIncFunc->arg_begin();
4080 auto *refCounter = llvmModCtx.Builder->CreateStructGEP(structType, objPtr, 0, "ref_counter");
4081 auto *newRefCounter =
4082 llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), refCounter, "new_ref_counter");
4083 auto *newRefCounterVal =
4084 llvmModCtx.Builder->CreateAdd(newRefCounter,
4085 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 1, true),
4086 "new_ref_counter_val");
4087 llvmModCtx.Builder->CreateStore(newRefCounterVal, refCounter);
4088 llvmModCtx.Builder->CreateRetVoid();
4089 }
4090 {
4091 auto gcDecFunc = llvmModCtx.functionMap[yoi::string2wstring(decFuncName)];
4092 gcDecFunc->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);
4093 // add basic block
4094 auto BB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "entry", gcDecFunc);
4095 auto nullFailedBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "null_failed", gcDecFunc);
4096 auto continueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "continue", gcDecFunc);
4097 auto finalizeBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "finalize", gcDecFunc);
4098 auto retBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "ret", gcDecFunc);
4099
4100 llvmModCtx.Builder->SetInsertPoint(BB);
4101
4102 auto objPtr = gcDecFunc->arg_begin();
4103 auto refCounter = llvmModCtx.Builder->CreateStructGEP(structType, objPtr, 0, "ref_counter");
4104 // check whether object is null
4105 auto *isObjNull = llvmModCtx.Builder->CreateIsNull(objPtr, "is_obj_null");
4106 llvmModCtx.Builder->CreateCondBr(isObjNull, nullFailedBlock, continueBlock);
4107
4108 llvmModCtx.Builder->SetInsertPoint(continueBlock);
4109 auto newRefCounter =
4110 llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), refCounter, "new_ref_counter");
4111 auto newRefCounterVal =
4112 llvmModCtx.Builder->CreateSub(newRefCounter,
4113 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 1, true),
4114 "new_ref_counter_val");
4115 llvmModCtx.Builder->CreateStore(newRefCounterVal, refCounter);
4116
4117 auto icmpRes = llvmModCtx.Builder->CreateICmpEQ(newRefCounterVal,
4118 llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 0, true),
4119 "ref_counter_zero");
4120 llvmModCtx.Builder->CreateCondBr(icmpRes, finalizeBlock, retBlock);
4121 // ret block
4122 llvmModCtx.Builder->SetInsertPoint(retBlock);
4123 llvmModCtx.Builder->CreateRetVoid();
4124 // null failed block
4125 llvmModCtx.Builder->SetInsertPoint(nullFailedBlock);
4126 llvmModCtx.Builder->CreateRetVoid();
4127 // finalize block
4128 llvmModCtx.Builder->SetInsertPoint(finalizeBlock);
4129 // free memory
4130 if (type->type == IRValueType::valueType::structObject ||
4131 type->type == IRValueType::valueType::interfaceObject) {
4132 // decrease the ref count of array elements inside
4133 auto arrayPointer = llvmModCtx.Builder->CreateStructGEP(structType, objPtr, 3, "array_ptr");
4134 auto arrayLengthPtr = llvmModCtx.Builder->CreateStructGEP(structType, objPtr, 2, "array_length_ptr");
4135 auto arrayLength =
4136 llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), arrayLengthPtr, "array_length");
4137 auto currentIndex =
4138 llvmModCtx.Builder->CreateAlloca(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), nullptr, "current_index");
4139
4140 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 0, true),
4141 currentIndex);
4142 auto loopBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "loop", gcDecFunc);
4143 auto exitBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "exit", gcDecFunc);
4144 auto condBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "cond", gcDecFunc);
4145 llvmModCtx.Builder->CreateBr(loopBlock);
4146 llvmModCtx.Builder->SetInsertPoint(condBlock);
4147 auto *loopCond = llvmModCtx.Builder->CreateICmpSLT(
4148 llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), currentIndex), arrayLength, "loop_cond");
4149 llvmModCtx.Builder->CreateCondBr(loopCond, loopBlock, exitBlock);
4150 // loop block
4151 llvmModCtx.Builder->SetInsertPoint(loopBlock);
4152 auto elementPointer =
4153 llvmModCtx.Builder->CreateGEP(llvm::PointerType::get(*llvmModCtx.TheContext, 0),
4154 arrayPointer,
4155 {llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), currentIndex)},
4156 "element_ptr");
4157 auto elementPointerVal =
4158 llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0),
4159 elementPointer,
4160 "element_ptr_val"); // just too lazy, so I use int64*
4161 callGcFunction(llvmModCtx, elementPointerVal, managedPtr(type->getElementType().addAttribute(IRValueType::ValueAttr::Nullable)), false);
4162 auto nextIndex = llvmModCtx.Builder->CreateLoad(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), currentIndex, "next_index");
4163 auto nextIndexVal = llvmModCtx.Builder->CreateAdd(
4164 nextIndex, llvm::ConstantInt::get(llvm::Type::getInt64Ty(*llvmModCtx.TheContext), 1, true), "next_index_val");
4165 llvmModCtx.Builder->CreateStore(nextIndexVal, currentIndex);
4166 llvmModCtx.Builder->CreateBr(condBlock);
4167 // exit block
4168 llvmModCtx.Builder->SetInsertPoint(exitBlock);
4169 }
4170 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"finalize_object"), objPtr);
4171 llvmModCtx.Builder->CreateRetVoid();
4172 }
4173 llvmModCtx.Builder->SetInsertPoint(currentInsertPoint);
4174 }
4175
4176 std::pair<std::shared_ptr<IRValueType>, llvm::Value *>
4177 LLVMCodegen::ensureObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type, llvm::Value *val) {
4178 if (type->hasAttribute(IRValueType::ValueAttr::Raw) || type->isBasicRawType()) {
4179 auto unboxedValue = unboxValue(llvmModCtx, val, type);
4180 auto boxedValue = createBasicObject(llvmModCtx, managedPtr(type->getBasicObjectType()), unboxedValue);
4181 return {managedPtr(type->getBasicObjectType()), boxedValue};
4182 } else {
4183 return {type, val};
4184 }
4185 }
4186
4187 void LLVMCodegen::generateIfTargetNotNull(LLVMModuleContext &llvmModCtx, llvm::Value *objectPtr,
4188 const std::shared_ptr<IRValueType> &yoiType,
4189 const std::function<void()> &func, bool enforced) {
4190 if (!yoiType->hasAttribute(IRValueType::ValueAttr::Nullable) && !enforced) {
4191 func();
4192 return;
4193 }
4194 auto f = llvmModCtx.Builder->GetInsertBlock()->getParent();
4195 auto continueBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "if_continue", f);
4196 auto notNullBlock = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "if_not_null", f);
4197 auto comparsion = llvmModCtx.Builder->CreateIsNotNull(objectPtr);
4198 llvmModCtx.Builder->CreateCondBr(comparsion, notNullBlock, continueBlock);
4199 llvmModCtx.Builder->SetInsertPoint(notNullBlock);
4200 func();
4201 llvmModCtx.Builder->CreateBr(continueBlock);
4202 llvmModCtx.Builder->SetInsertPoint(continueBlock);
4203 }
4204
4205 LLVMCodegen::ValueStackWithPhi::ValueStackWithPhi(const ControlFlowAnalysis &cfa, llvm::IRBuilder<> *builder, const std::shared_ptr<IRModule> &yoiModule)
4206 : cfa(cfa), stackState(StackState::Finalized), currentState(0), builder(builder), yoiModule(yoiModule) {}
4207
4209 yoi::indexT fromState,
4210 llvm::BasicBlock *currentBlock,
4211 llvm::BasicBlock *fromBlock) {
4212 yoi_assert(
4213 stackState == StackState::Finalized, 0, 0, "llvmCodegen: invoking enterNode on an unfinalized stack");
4214 stackState = StackState::InEvaluation;
4215 this->currentState = currentState;
4216
4217 // check whether the first time to evaluate this block, if so, inherit the stack base from stack top of previous
4218 // block.
4219 if (auto it = valueStackStateIn.find(currentState) == valueStackStateIn.end()) {
4220 valueStackStateIn[currentState] = valueStackStateOut[fromState];
4221 phiNodes[currentState] = valueStackStateOut[fromState].empty() ? yoi::vec<llvm::PHINode *>{} : phiNodes[fromState];
4222 // also, for those which is not a phi node but exists in the previous block, create a new phi node for them.
4223 auto begin = phiNodes[currentState].size();
4224 for (yoi::indexT begins = phiNodes[currentState].size(); begins < valueStackStateIn[currentState].size(); begins++) {
4225 auto phiNode = builder->CreatePHI(valueStackStateIn[currentState][begins].llvmValue->getType(), cfa.reverseG.at(currentState).size(), "phi_node");
4226 phiNode->addIncoming(valueStackStateOut[fromState][begins].llvmValue, fromBlock); // definitely from the previous block.
4227 phiNodes[currentState].push_back(phiNode);
4228 valueStackStateIn[currentState][begins].llvmValue = phiNode;
4229 }
4230 valueStackStateOut[currentState] = valueStackStateIn[currentState];
4231 } else {
4232 // now is the second time to evaluate this block, merge all existing phi nodes from previous block into this
4233 // block. there would be a chance that the control path of two block, not only differs in the last frame,
4234 // but also in the middle of the frames, thus we need to iterate from the start. if the onward value collide
4235 // with the existing phi node, phi the phi node. also check whether the stack depth is the same, if not,
4236 // panic.
4237 yoi_assert(valueStackStateIn[currentState].size() == valueStackStateOut[fromState].size(),
4238 0,
4239 0,
4240 "llvmCodegen: incompatiable control flow");
4241 for (yoi::indexT i = 0; i < phiNodes[currentState].size(); i++) {
4242 if (valueStackStateOut[fromState][i].llvmValue != phiNodes[currentState][i]) {
4243 // merge phi nodes
4244 phiNodes[currentState][i]->addIncoming(valueStackStateOut[fromState][i].llvmValue, fromBlock);
4245
4246 // merge variable metadata
4247 if (valueStackStateIn[currentState][i].yoiType->metadata.hasMetadata(L"regressed_interface_impl") && valueStackStateOut[fromState][i].yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
4248 auto implIndex1 = valueStackStateIn[currentState][i].yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4249 auto implIndex2 = valueStackStateOut[fromState][i].yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4250 auto implDef1 = yoiModule->interfaceImplementationTable[implIndex1.second];
4251 auto implDef2 = yoiModule->interfaceImplementationTable[implIndex2.second];
4252 if (implIndex1 != implIndex2) {
4253 // conflict, remove metadata, and actualize the interface object.
4254 // while both sides are fucked, we take the phi node as input node.
4255 panic(0, 0, "llvmCodegen: interface implementation conflict");
4256 // valueStackStateIn[currentState][i] = actualizeFunc(managedPtr(IRValueType{std::get<0>(implDef1->implStructIndex), std::get<1>(implDef1->implStructIndex), std::get<2>(implDef1->implStructIndex)}), valueStackStateIn[currentState][i].llvmValue, implIndex1.second);
4257 }
4258 } else if (valueStackStateIn[currentState][i].yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
4259 auto implIndex1 = valueStackStateIn[currentState][i].yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4260 auto implDef = yoiModule->interfaceImplementationTable[implIndex1.second];
4261 // right side is plain, so we normalize the left side.
4262 // valueStackStateIn[currentState][i] = actualizeFunc(managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex)}), valueStackStateIn[currentState][i].llvmValue, implIndex1.second);
4263 panic(0, 0, "llvmCodegen: interface implementation conflict");
4264 } else if (valueStackStateOut[fromState][i].yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
4265 auto implIndex2 = valueStackStateOut[fromState][i].yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4266 auto implDef = yoiModule->interfaceImplementationTable[implIndex2.second];
4267 // left side is plain, so we normalize the right side.
4268 // valueStackStateOut[fromState][i] = actualizeFunc(managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex)}), valueStackStateOut[fromState][i].llvmValue, implIndex2.second);
4269 panic(0, 0, "llvmCodegen: interface implementation conflict");
4270 } else {
4271 // both side is plain, we do nothing.
4272 }
4273 }
4274 }
4275 valueStackStateOut[currentState] = valueStackStateIn[currentState];
4276 }
4277 }
4278
4280 yoi_assert(
4281 stackState == StackState::InEvaluation, 0, 0, "llvmCodegen: invoking finalizeNode on a finalized stack");
4282 stackState = StackState::Finalized;
4283 }
4284
4286 valueStackStateOut[currentState].push_back(value);
4287 }
4288
4290 return valueStackStateOut[currentState].back();
4291 }
4292
4294 valueStackStateOut[currentState].pop_back();
4295 }
4296
4298 valueStackStateOut.clear();
4299 valueStackStateIn.clear();
4300 phiNodes.clear();
4301 stackState = StackState::Finalized;
4302 currentState = 0;
4303 }
4304
4305 void LLVMCodegen::ValueStackWithPhi::enterNode(yoi::indexT currentState, llvm::BasicBlock *currentBlock) {
4306 yoi_assert(
4307 stackState == StackState::Finalized, 0, 0, "llvmCodegen: invoking enterNode on an unfinalized stack");
4308 stackState = StackState::InEvaluation;
4309 this->currentState = currentState;
4310
4311 // check whether the first time to evaluate this block, if so, inherit the stack base from stack top of previous
4312 // block.
4313 if (auto it = valueStackStateIn.find(currentState) == valueStackStateIn.end()) {
4314 valueStackStateIn[currentState] = {};
4315 phiNodes[currentState] = {};
4316 } else {
4317 panic(0, 0, "llvmCodegen: jumped at entry block");
4318 }
4319 }
4320
4322 return valueStackStateOut[currentState][index];
4323 }
4324
4326 return valueStackStateOut.at(currentState).size();
4327 }
4328
4330 return valueStackStateOut.at(currentState).empty();
4331 }
4332
4333 LLVMCodegen::StackValue LLVMCodegen::actualizeInterfaceObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type,
4334 llvm::Value *objectPtr,
4335 yoi::indexT implIndex) {
4336
4337 auto implDef = yoiModule->interfaceImplementationTable[implIndex];
4338 auto structYoiType = managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex)});
4339
4340 auto structGcFunc = getGcFunction(llvmModCtx, structYoiType, false);
4341 yoi_assert(structGcFunc != nullptr, 0, 0, "llvmCodegen: expected gc function for struct but received nullptr");
4342
4343 auto interfaceKey = std::make_tuple(IRValueType::valueType::interfaceObject,
4344 implDef->implInterfaceIndex.first,
4345 implDef->implInterfaceIndex.second);
4346
4347 auto key = std::make_tuple(
4348 IRValueType::valueType::interfaceObject, yoiModule->identifier, implDef->implInterfaceIndex.second);
4349 auto *interfaceLLVMType = llvmModCtx.structTypeMap.at(key);
4350
4351 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(interfaceLLVMType);
4352 auto *sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
4353
4354 auto *allocCall = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"object_alloc"), sizeVal, "newinterface_alloc");
4355 auto *bitcast = llvmModCtx.Builder->CreateBitCast(allocCall, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "casttmp");
4356
4357 auto *refCountPtr = llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, bitcast, 0, "refcount_ptr");
4358 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), refCountPtr);
4359
4360 auto *typeIdPtr = llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, bitcast, 1, "typeid_ptr");
4361 auto typeIdKey = std::make_tuple(
4362 IRValueType::valueType::interfaceObject, yoiModule->identifier, implDef->implInterfaceIndex.second, 0);
4363 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), llvmModCtx.typeIDMap[typeIdKey]), typeIdPtr);
4364
4365 auto yoiType = std::make_shared<IRValueType>(IRValueType::valueType::interfaceObject,
4366 implDef->implInterfaceIndex.first,
4367 implDef->implInterfaceIndex.second);
4368
4369 auto interfaceShellVal = StackValue{bitcast, yoiType};
4370
4371 /*auto structInstanceVal = llvmModCtx.valueStackPhi.back();
4372 llvmModCtx.valueStackPhi.pop_back();*/
4373 auto structInstanceVal = StackValue{objectPtr, type};
4374
4375 if (structInstanceVal.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope)) {
4376 callGcFunction(llvmModCtx, structInstanceVal.llvmValue, structInstanceVal.yoiType, true, true, true);
4377 }
4378
4379 // Store `this` pointer at index 1
4380 auto *thisPtrField =
4381 llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, interfaceShellVal.llvmValue, 2, "this_ptr_field");
4382 auto [objectType, objectValue] = ensureObject(llvmModCtx, structInstanceVal.yoiType, structInstanceVal.llvmValue);
4383 auto *castedStructPtr =
4384 llvmModCtx.Builder->CreateBitCast(objectValue, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "casted_this");
4385 llvmModCtx.Builder->CreateStore(castedStructPtr, thisPtrField);
4386
4387 // Populate GC function pointers at indices 3 and 4 with pointers to the interfaceImpl wrappers
4388 auto *decVTableSlot =
4389 llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, interfaceShellVal.llvmValue, 4, "gc_dec_slot");
4390 llvmModCtx.Builder->CreateStore(structGcFunc, decVTableSlot);
4391
4392 // Populate user method pointers starting at index 5
4393 for (size_t i = 0; i < implDef->virtualMethods.size(); ++i) {
4394 auto &methodYoiType = implDef->virtualMethods[i];
4396 0,
4397 0,
4398 "Expected virtual method type in impl definition");
4399 auto funcIndex = methodYoiType->typeIndex;
4400 auto funcDef = yoiModule->functionTable[funcIndex];
4401 auto *llvmFunction = llvmModCtx.functionMap.at(funcDef->name);
4402
4403 auto *vtableSlotPtr =
4404 llvmModCtx.Builder->CreateStructGEP(interfaceLLVMType, interfaceShellVal.llvmValue, i + 5, "vtable_slot");
4405 llvmModCtx.Builder->CreateStore(llvmFunction, vtableSlotPtr);
4406 }
4407
4408 return interfaceShellVal;
4409 }
4410
4412 if (objectVal.yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
4413 auto impl = objectVal.yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4414 auto implDef = yoiModule->interfaceImplementationTable[impl.second];
4415 if (impl.first != -1) {
4416 return actualizeInterfaceObject(llvmModCtx, managedPtr(IRValueType{std::get<0>(implDef->implStructIndex), std::get<1>(implDef->implStructIndex), std::get<2>(implDef->implStructIndex), objectVal.yoiType->attributes}), objectVal.llvmValue, impl.second);
4417 }
4418 }
4419 return objectVal;
4420 }
4421
4422 llvm::Value * LLVMCodegen::unwrapInterfaceObject(LLVMModuleContext &llvmModCtx, const StackValue &objectVal) {
4423 yoi_assert(objectVal.yoiType->type == IRValueType::valueType::interfaceObject, 0, 0, "unwrapInterfaceObject(llvmModCtx, ...): Except interface object");
4424 if (objectVal.yoiType->metadata.hasMetadata(L"regressed_interface_impl")) {
4425 auto impl = objectVal.yoiType->metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
4426 auto implDef = yoiModule->interfaceImplementationTable[impl.second];
4427
4428 if (impl.first != -1) {
4429 return objectVal.llvmValue;
4430 }
4431 }
4432 auto interfaceType = llvmModCtx.structTypeMap.at({objectVal.yoiType->type, objectVal.yoiType->typeAffiliateModule, objectVal.yoiType->typeIndex});
4433 auto *thisPtrField = llvmModCtx.Builder->CreateStructGEP(interfaceType, objectVal.llvmValue, 2);
4434 auto *thisPtr = llvmModCtx.Builder->CreateLoad(llvm::PointerType::get(*llvmModCtx.TheContext, 0), thisPtrField);
4435 return thisPtr;
4436 }
4438 return objectVal.yoiType->type == IRValueType::valueType::interfaceObject
4439 ? wrapInterfaceObjectIfRegressed(llvmModCtx, objectVal)
4440 : objectVal;
4441 }
4442
4444 switch (instr.opcode) {
4446 auto libIndex = instr.operands[0].value.symbolIndex;
4447 auto funcIndex = instr.operands[1].value.symbolIndex;
4448 auto argCount = instr.operands[2].value.symbolIndex;
4449
4450 auto funcDef = compilerCtx->getIRFFITable()->importedLibraries[libIndex].importedFunctionTable[funcIndex];
4451 yoi_assert(funcDef->hasAttribute(IRFunctionDefinition::FunctionAttrs::Intrinsic), instr.debugInfo.line, instr.debugInfo.column, "llvmCodegen: expected intrinsic function");
4452
4453 if (funcDef->name == L"runtime_get_string_array_data_pointer") {
4454 // logic of intrinsic, offset to the value address which is the forth member of an array struct definition
4455 auto object = llvmModCtx.valueStackPhi.back();
4456 llvmModCtx.valueStackPhi.pop_back();
4457 yoi_assert(object.yoiType->isArrayType() || object.yoiType->isDynamicArrayType(), instr.debugInfo.line, instr.debugInfo.column, "llvmCodegen: expected array type");
4458 auto pointer = llvmModCtx.Builder->CreateStructGEP(getArrayLLVMType(llvmModCtx, object.yoiType), object.llvmValue, 3, "array_ptr");
4459 auto toInt = llvmModCtx.Builder->CreatePtrToInt(pointer, llvmModCtx.Builder->getInt64Ty(), "array_ptr_ptrtoint");
4460 llvmModCtx.valueStackPhi.push_back(StackValue{toInt, managedPtr(compilerCtx->getUnsignedObjectType()->getBasicRawType())});
4461 } else {
4462 panic(instr.debugInfo.line, instr.debugInfo.column, "llvmCodegen: unsupported intrinsic function");
4463 }
4464 break;
4465 }
4466 default:
4467 panic(instr.debugInfo.line, instr.debugInfo.column, "llvmCodegen: unsupported intrinsic call");
4468 break;
4469 }
4470 }
4471
4472 void LLVMCodegen::generateWrapperForForeignCallablesIfNotExists(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type) {
4473 // further implementation details are under discussion, leave this function empty temporarily
4474
4475
4476 // std::tuple<IRValueType::valueType, yoi::indexT, yoi::indexT> key = {type->type, type->typeAffiliateModule, type->typeIndex};
4477
4478 // if (foreignTypeMap.find(key) != foreignTypeMap.end()) {
4479 // // we have already created the wrapper, just return
4480 // return;
4481 // }
4482
4483 // yoi_assert(type->type == IRValueType::valueType::interfaceObject, llvmModCtx.currentFunctionDef->debugInfo.line, llvmModCtx.currentFunctionDef->debugInfo.column, "llvmCodegen: expected callable interface type for generateWrapperForForeignCallablesIfNotExists");
4484
4485 // auto interfaceDef = yoiModule->interfaceTable[type->typeIndex];
4486 // yoi_assert(interfaceDef->functionOverloadIndexies.contains(L"operator()") && interfaceDef->functionOverloadIndexies.at(L"operator()").size() == 1, llvmModCtx.currentFunctionDef->debugInfo.line, llvmModCtx.currentFunctionDef->debugInfo.column, "llvmCodegen: expected operator() in callable interface");
4487
4488 // auto funcIndex = interfaceDef->functionOverloadIndexies.at(L"operator()")[0];
4489 // auto funcDef = yoiModule->functionTable[funcIndex];
4490 // // determine the LLVM type of the foreign type, first
4491 // yoi::vec<llvm::Type *> argTypes;
4492
4493 // for (auto &param : funcDef->argumentTypes) {
4494 // argTypes.push_back(yoiTypeToLLVMType(llvmModCtx, param, true));
4495 // }
4496
4497 // //
4498 }
4499
4501 auto it = llvmModuleContext.find(absolutePath);
4502 if (it == llvmModuleContext.end()) {
4503 throw std::runtime_error("Module not found");
4504 }
4505 return *it->second;
4506 }
4507
4509 yoi::indexT hash,
4510 const yoi::wstr &absolute_path)
4511 : TheContext(std::make_unique<llvm::LLVMContext>()),
4512 Builder(std::unique_ptr<llvm::IRBuilder<>>(new llvm::IRBuilder<>(*TheContext))),
4513 nextTypeId(0),
4514 controlFlowAnalysis({}),
4515 valueStackPhi(controlFlowAnalysis, Builder.get(), yoiModule),
4516 absolute_path(absolute_path) {
4517 TheModule = std::make_unique<llvm::Module>("yoi.module." + std::to_string(hash), *TheContext);
4518 TheModule->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION);
4519 TheModule->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 4);
4520 DBuilder = std::make_unique<llvm::DIBuilder>(*TheModule);
4521 }
4522
4524 llvm::InitializeNativeTarget();
4525 llvm::InitializeNativeTargetAsmParser();
4526 llvm::InitializeNativeTargetDisassembler();
4527 llvm::InitializeNativeTargetAsmPrinter();
4528
4529 // first, we create each modules for different source files
4530 for (auto &module : compilerCtx->getCompiledModules()) {
4531 llvmModuleContext[module.second->modulePath] = std::make_unique<LLVMModuleContext>(module.second, module.first, module.second->modulePath);
4532 generateDeclarations(*llvmModuleContext[module.second->modulePath]);
4533 }
4534
4535 std::set<yoi::indexT> dirtyModules;
4536 auto &allModules = compilerCtx->getCompiledModules();
4537
4538 // Identify initially dirty modules (disk changes)
4539 for (auto const &[id, module] : allModules) {
4540 if (module->modulePath == L"builtin") continue;
4541 yoi::indexT last_write_time = std::filesystem::last_write_time(module->modulePath).time_since_epoch().count();
4542 if (last_write_time != codegenObjectCache.get_entry(module->modulePath).getLastModification()) {
4543 dirtyModules.insert(id);
4544 }
4545 }
4546
4547 // Bidirectional transitive invalidation
4548 std::queue<yoi::indexT> q;
4549
4550 for (auto id : dirtyModules) q.push(id);
4551 while (!q.empty()) {
4552 yoi::indexT u = q.front();
4553 q.pop();
4554
4555 auto const &uMod = allModules.at(u);
4556
4557 for (auto v : uMod->dependentModules) {
4558 if (dirtyModules.find(v) == dirtyModules.end()) {
4559 dirtyModules.insert(v);
4560 q.push(v);
4561 }
4562 }
4563 }
4564
4565 for (auto id : dirtyModules) q.push(id);
4566 while (!q.empty()) {
4567 yoi::indexT u = q.front();
4568 q.pop();
4569
4570 auto const &uMod = allModules.at(u);
4571
4572 for (auto v : uMod->moduleImports) {
4573 if (dirtyModules.find(v.second) == dirtyModules.end()) {
4574 dirtyModules.insert(v.second);
4575 q.push(v.second);
4576 }
4577 }
4578 }
4579
4580 for (auto &pair : allModules) {
4581 if (pair.second->modulePath == L"builtin") {
4582 continue;
4583 }
4584 auto id = pair.first;
4585 auto module = pair.second;
4586 codegenTaskDispatcher.dispatch([this, id, module, &dirtyModules]() {
4587 set_current_file_path(module->modulePath);
4588 if (dirtyModules.find(id) == dirtyModules.end()) {
4589 warning(0, 0, "llvmCodegen: skipping module " + wstring2string(module->modulePath), "MODULE_NOT_MODIFIED");
4590 return;
4591 }
4592
4593 auto cache_entry = codegenObjectCache.get_entry(module->modulePath);
4594
4595 generateImplementations(*llvmModuleContext[module->modulePath]);
4596 llvmModuleContext[module->modulePath]->DBuilder->finalize();
4597 generateTargetObjectCode(*llvmModuleContext[module->modulePath], cache_entry.getObjectFilename());
4598
4599 yoi::indexT last_write_time = std::filesystem::last_write_time(module->modulePath).time_since_epoch().count();
4600 codegenObjectCache.update_last_modification(module->modulePath, last_write_time);
4601 });
4602 }
4603
4605
4614
4615 for (auto &arr : llvmModuleContext[L"builtin"]->arrayToGenerateImplementations) {
4616 generateArrayGCFunctionImplementations(*llvmModuleContext[L"builtin"], std::get<0>(arr), std::get<1>(arr), std::get<2>(arr));
4617 }
4618
4619 llvmModuleContext[L"builtin"]->DBuilder->finalize();
4621
4622 auto cache_path = std::filesystem::path(compilerCtx->getBuildConfig()->buildCachePath);
4623 if (!compilerCtx->getBuildConfig()->immediatelyClearupCache) {
4624 auto cache_file = fopen((cache_path / "hoshi.cache.tsuki").string().c_str(), "wb+");
4625 yoi_assert(cache_file, 0, 0, "llvmCodegen: failed to open cache file");
4627 fclose(cache_file);
4628 // save ir
4629 }
4630
4631 yoi::vec<yoi::wstr> objectFileNames;
4632 for (auto &module : compilerCtx->getCompiledModules()) {
4633 objectFileNames.push_back(codegenObjectCache.get_entry(module.second->modulePath).getObjectFilename());
4634 dumpIR(module.second->modulePath, (cache_path / (std::to_string(module.second->identifier) + ".ll")).string());
4635 }
4636 return objectFileNames;
4637 }
4638
4639 void LLVMCodegen::dumpIR(const yoi::wstr& modulePath, const std::string& filename) {
4640 if (llvmModuleContext.count(modulePath)) {
4641 std::error_code ec;
4642 llvm::raw_fd_ostream os(filename, ec);
4643 if (!ec) {
4644 llvmModuleContext[modulePath]->TheModule->print(os, nullptr);
4645 }
4646 }
4647 }
4648
4649
4651 llvm::Value *objectPtr,
4652 const std::shared_ptr<IRValueType> &yoiType,
4653 bool isIncrease,
4654 bool forceForPermanent,
4655 bool forceForBorrow) {
4656 if (yoiType->type == IRValueType::valueType::none || yoiType->hasAttribute(IRValueType::ValueAttr::Raw) || yoiType->isBasicRawType()) {
4657 return;
4658 }
4659 if (yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) && !forceForPermanent) {
4660 return;
4661 }
4662 if (yoiType->hasAttribute(IRValueType::ValueAttr::Borrow) && !forceForBorrow)
4663 return;
4664 if (yoiType->hasAttribute(IRValueType::ValueAttr::WeakRef))
4665 return;
4666
4667 auto gcFunc = getGcFunction(llvmModCtx, yoiType, isIncrease);
4668 if (gcFunc == nullptr) return;
4669
4670 auto f = [&]() {
4671 auto* ptrArg = llvmModCtx.Builder->CreateBitCast(objectPtr, gcFunc->getFunctionType()->getParamType(0));
4672 llvmModCtx.Builder->CreateCall(gcFunc, ptrArg);
4673 };
4674 generateIfTargetNotNull(llvmModCtx, objectPtr, yoiType, f);
4675 }
4676
4678 for (auto& datastructDefPair : yoiModule->dataStructTable) {
4679 auto dataStructDef = datastructDefPair.second;
4680 auto key = std::make_tuple(IRValueType::valueType::datastructObject, yoiModule->identifier, yoiModule->dataStructTable.getIndex(dataStructDef->name));
4681 auto dataRegionStructName = "datastruct.data." + std::to_string(yoiModule->identifier) + "." + wstring2string(dataStructDef->name);
4682 auto objectStructName = "datastruct." + std::to_string(yoiModule->identifier) + "." + wstring2string(dataStructDef->name);
4683 llvmModCtx.structTypeMap[key] = llvm::StructType::create(*llvmModCtx.TheContext, objectStructName);
4684 llvmModCtx.dataStructDataRegionMap[yoiModule->dataStructTable.getIndex(datastructDefPair.first)] = llvm::StructType::create(*llvmModCtx.TheContext, dataRegionStructName);
4685 auto typeIdKey = std::make_tuple(IRValueType::valueType::datastructObject, yoiModule->identifier, yoiModule->dataStructTable.getIndex(dataStructDef->name), 0);
4686 llvmModCtx.typeIDMap[typeIdKey] = llvmModCtx.nextTypeId++;
4687 }
4688 }
4689
4691 for (auto &datastructDefPair : yoiModule->dataStructTable) {
4692 auto structDef = datastructDefPair.second;
4693 auto key = std::make_tuple(IRValueType::valueType::datastructObject, yoiModule->identifier, yoiModule->dataStructTable.getIndex(structDef->name));
4694 auto *llvmDataRegionStructType = llvmModCtx.dataStructDataRegionMap.at(yoiModule->dataStructTable.getIndex(datastructDefPair.first));
4695 auto *llvmObjectType = llvmModCtx.structTypeMap.at(key);
4696
4697 std::vector<llvm::Type*> fieldTypes;
4698 fieldTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // gc_refcount
4699 fieldTypes.push_back(llvmModCtx.Builder->getInt64Ty()); // typeid
4700 fieldTypes.push_back(llvmDataRegionStructType);
4701 if (llvmObjectType->isOpaque()) {
4702 llvmObjectType->setBody(fieldTypes);
4703 }
4704
4705 // setup data region
4706 std::vector<llvm::Type*> dataRegionFieldTypes;
4707 for (const auto& fieldType : structDef->fieldTypes) {
4708 dataRegionFieldTypes.push_back(yoiTypeToLLVMType(llvmModCtx, fieldType, true));
4709 }
4710 if (llvmDataRegionStructType->isOpaque()) {
4711 llvmDataRegionStructType->setBody(dataRegionFieldTypes);
4712 }
4713 }
4714 }
4715 llvm::Value *LLVMCodegen::loadIfDataStructObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr<IRValueType> &type, llvm::Value *value) {
4716 if (type->type == IRValueType::valueType::datastructObject) {
4717 // data region def
4718 auto dataRegionDef = llvmModCtx.dataStructDataRegionMap[type->typeIndex];
4719 return llvmModCtx.Builder->CreateLoad(dataRegionDef, value, "datastruct_load");
4720 }
4721 return value;
4722 }
4723
4725 yoi_assert(
4727 llvmModCtx.currentFunctionDef->debugInfo.line,
4728 llvmModCtx.currentFunctionDef->debugInfo.column,
4729 "Function is not a generator"
4730 );
4731
4732 // generate the id of coroutine
4733 llvm::Function *coroId = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_id, {});
4734 auto coro_id = llvmModCtx.Builder->CreateCall(coroId, {
4735 llvm::ConstantInt::getIntegerValue(llvm::IntegerType::getInt32Ty(*llvmModCtx.TheContext), llvm::APInt(32, 0)),
4736 llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0)),
4737 llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0)),
4738 llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0))
4739 });
4740
4741 auto previousBlock = llvmModCtx.Builder->GetInsertBlock();
4742
4743 llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "suspend", llvmModCtx.currentFunction);
4744 llvmModCtx.currentGeneratorContextBasicBlocks.cleanupBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "cleanup", llvmModCtx.currentFunction);
4745
4746 auto afterSuspendBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "after_suspend", llvmModCtx.currentFunction);
4747
4748 llvm::Function *coroSize = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_size, {llvmModCtx.Builder->getInt64Ty()});
4749 llvm::Value *coro_size = llvmModCtx.Builder->CreateCall(coroSize, {}, "size");
4750
4751 llvm::Value *coro_allocated_mem = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions[L"mi_calloc"], {llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), coro_size});
4752 llvm::Function *coroBegin = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_begin);
4753 llvm::Value *coro_handle = llvmModCtx.Builder->CreateCall(coroBegin, {coro_id, coro_allocated_mem});
4754
4755 llvm::Value *allocated_ctx = createGeneratorContext(llvmModCtx, coro_handle);
4756 llvmModCtx.currentGeneratorContextValue = allocated_ctx;
4757 auto ctxIndex = llvmModCtx.currentFunctionDef->getVariableTable().lookup(L"__context__");
4758 auto* alloca = llvmModCtx.namedValues.at(ctxIndex);
4759 llvmModCtx.Builder->CreateStore(allocated_ctx, alloca);
4760 // callGcFunction(llvmModCtx, allocated_ctx, llvmModCtx.currentFunctionDef->returnType, true, true);
4761
4762 llvm::Function *coroSuspend = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_suspend);
4763 llvm::Value *coro_suspend = llvmModCtx.Builder->CreateCall(coroSuspend, {
4764 llvm::ConstantTokenNone::get(*llvmModCtx.TheContext),
4765 llvm::ConstantInt::get(llvm::Type::getInt1Ty(*llvmModCtx.TheContext), 0)});
4766 // switch!
4767 auto switchInst = llvmModCtx.Builder->CreateSwitch(coro_suspend, llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB);
4768 switchInst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 0), afterSuspendBB);
4769 switchInst->addCase(llvm::ConstantInt::get(llvm::Type::getInt8Ty(*llvmModCtx.TheContext), 1), llvmModCtx.currentGeneratorContextBasicBlocks.cleanupBB);
4770
4771 llvmModCtx.Builder->SetInsertPoint(llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB);
4772 // coro_end and return our allocated ctx
4773 llvm::Function *coroEnd = getLLVMCoroIntrinsic(llvmModCtx, llvm::Intrinsic::coro_end);
4774 llvmModCtx.Builder->CreateCall(coroEnd, {coro_handle, llvm::ConstantInt::get(llvmModCtx.Builder->getInt1Ty(), false), llvm::ConstantTokenNone::get(*llvmModCtx.TheContext)});
4775 llvmModCtx.Builder->CreateRet(allocated_ctx);
4776
4777 llvmModCtx.Builder->SetInsertPoint(llvmModCtx.currentGeneratorContextBasicBlocks.cleanupBB);
4778 // proceed with our own cleanup logic
4779 generateFunctionExitCleanup(llvmModCtx);
4780 llvmModCtx.Builder->CreateBr(llvmModCtx.currentGeneratorContextBasicBlocks.suspendBB);
4781
4782 // resume logic, which is the program logic
4783 llvmModCtx.Builder->SetInsertPoint(afterSuspendBB);
4784 }
4785
4786 llvm::Function *LLVMCodegen::getLLVMCoroIntrinsic(LLVMModuleContext &llvmModCtx, llvm::Intrinsic::ID id, llvm::ArrayRef<llvm::Type *> types) {
4787 return llvm::Intrinsic::getOrInsertDeclaration(llvmModCtx.TheModule.get(), id, types);
4788 }
4789
4790 llvm::Value* LLVMCodegen::createGeneratorContext(LLVMModuleContext &llvmModCtx, llvm::Value *coro_handle) {
4791 auto ctxType = llvmModCtx.currentFunctionDef->returnType;
4792 auto key = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, ctxType->typeIndex);
4793 auto *structType = llvmModCtx.structTypeMap.at(key);
4794
4795 auto *ctxValue = createStructObject(llvmModCtx, yoiModule->identifier, ctxType->typeIndex);
4796 // auto *ctxPtr = llvmModCtx.Builder->CreateStructGEP(structType, ctxValue, 2, "ctx_ptr");
4797 // llvmModCtx.Builder->CreateStore(coro_handle, ctxPtr);
4798 auto unsignedRawType = managedPtr(*compilerCtx->getUnsignedObjectType());
4799 unsignedRawType->addAttribute(IRValueType::ValueAttr::Raw);
4800 storeMember(llvmModCtx,
4801 {
4802 llvmModCtx.Builder->CreatePtrToInt(coro_handle, llvm::IntegerType::getInt64Ty(*llvmModCtx.TheContext), "wdnmdnmslwqnmgbd"),
4803 unsignedRawType
4804 },
4805 {
4806 ctxValue,
4807 ctxType
4808 }, 0);
4809 return ctxValue;
4810 }
4811
4812 llvm::Value *LLVMCodegen::createStructObject(LLVMModuleContext &llvmModCtx, yoi::indexT moduleIndex, yoi::indexT structIndex) {
4813 auto key = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, structIndex);
4814 auto *structType = llvmModCtx.structTypeMap.at(key);
4815
4816 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(structType);
4817 auto *sizeVal = llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), size);
4818
4819 auto *allocCall = llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"object_alloc"), sizeVal, "newtmp_alloc");
4820 auto *bitcast = llvmModCtx.Builder->CreateBitCast(allocCall, llvm::PointerType::get(*llvmModCtx.TheContext, 0), "casttmp");
4821
4822 auto *refCountPtr = llvmModCtx.Builder->CreateStructGEP(structType, bitcast, 0, "refcount_ptr");
4823 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), 1), refCountPtr);
4824
4825 auto *typeIdPtr = llvmModCtx.Builder->CreateStructGEP(structType, bitcast, 1, "typeid_ptr");
4826 auto typeIdKey = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, structIndex, 0);
4827 llvmModCtx.Builder->CreateStore(llvm::ConstantInt::get(llvmModCtx.Builder->getInt64Ty(), llvmModCtx.typeIDMap[typeIdKey]), typeIdPtr);
4828
4829 return bitcast;
4830 }
4831
4832 void LLVMCodegen::storeYieldValue(LLVMModuleContext &llvmModCtx, llvm::Value *value, const std::shared_ptr<IRValueType> &yoiType) {
4833 auto key = std::make_tuple(IRValueType::valueType::structObject, yoiModule->identifier, llvmModCtx.currentFunctionDef->returnType->typeIndex);
4834 auto *structType = llvmModCtx.structTypeMap.at(key);
4835
4836 storeMember(llvmModCtx, {value, yoiType}, {llvmModCtx.currentGeneratorContextValue, llvmModCtx.currentFunctionDef->returnType}, 1);
4837 }
4838
4839 llvm::StructType *LLVMCodegen::findStructLLVMType(const std::shared_ptr<IRValueType> &targetYoiType) {
4840 auto targetKey = std::make_tuple(targetYoiType->type, targetYoiType->typeAffiliateModule, targetYoiType->typeIndex);
4841 for (auto &[_, ctx] : llvmModuleContext) {
4842 if (ctx->structTypeMap.count(targetKey))
4843 return ctx->structTypeMap.at(targetKey);
4844 }
4845 return nullptr;
4846 }
4847
4848 llvm::Value *LLVMCodegen::emitWeakSlotAlloc(LLVMModuleContext &llvmModCtx, llvm::Value *targetPtr, const std::shared_ptr<IRValueType> &targetYoiType) {
4849 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
4850 auto* targetLLVMType = findStructLLVMType(targetYoiType);
4851 if (targetLLVMType) {
4852 auto targetWeakSlotsIdx = targetLLVMType->getNumElements() - 1;
4853 auto* targetWeakSlotsPtr = llvmModCtx.Builder->CreateStructGEP(targetLLVMType, targetPtr, targetWeakSlotsIdx, "target_weak_slots");
4854 return llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_weak_slot_alloc"), {targetPtr, targetWeakSlotsPtr}, "new_weak_slot");
4855 }
4856 return llvm::ConstantPointerNull::get(i8PtrTy);
4857 }
4858
4859 void LLVMCodegen::emitWeakSlotFree(LLVMModuleContext &llvmModCtx, llvm::Value *slotPtr, const std::shared_ptr<IRValueType> &targetYoiType) {
4860 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
4861 auto* targetPtr = llvmModCtx.Builder->CreateLoad(i8PtrTy, slotPtr, "slot_target_for_free");
4862 auto* targetLLVMType = findStructLLVMType(targetYoiType);
4863 if (targetLLVMType) {
4864 auto targetWeakSlotsIdx = targetLLVMType->getNumElements() - 1;
4865 auto* targetWeakSlotsPtr = llvmModCtx.Builder->CreateStructGEP(targetLLVMType, targetPtr, targetWeakSlotsIdx, "target_weak_slots_for_free");
4866 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_weak_slot_free"), {slotPtr, targetWeakSlotsPtr});
4867 return;
4868 }
4869 llvmModCtx.Builder->CreateCall(llvmModCtx.runtimeFunctions.at(L"runtime_weak_slot_free"), {slotPtr, llvm::ConstantPointerNull::get(llvm::PointerType::get(*llvmModCtx.TheContext, 0))});
4870 }
4871
4872 void LLVMCodegen::storeMember(LLVMModuleContext &llvmModCtx, const StackValue &storeValue, const StackValue &structVal, yoi::indexT memberIndex) {
4873 auto valueToStore = promiseInterfaceObjectIfInterface(llvmModCtx, storeValue);
4874
4875 auto llvmMemberIndex = memberIndex + 2; // +2 to skip gc_refcount header and type index
4876
4877 auto key = std::make_tuple(IRValueType::valueType::structObject, structVal.yoiType->typeAffiliateModule, structVal.yoiType->typeIndex);
4878 auto *llvmStructType = llvmModCtx.structTypeMap.at(key);
4879 auto *gep = llvmModCtx.Builder->CreateStructGEP(llvmStructType, structVal.llvmValue, llvmMemberIndex, "store_member_memberptr");
4880
4881 auto yoiStructDef = compilerCtx->getIRObjectFile()->compiledModule->structTable[std::get<2>(key)];
4882 auto memberYoiType = yoiStructDef->fieldTypes[memberIndex];
4883
4884 // WeakRef path: use WeakSlot indirection instead of direct pointer + ARC
4885 if (memberYoiType->hasAttribute(IRValueType::ValueAttr::WeakRef)) {
4886 auto* i8PtrTy = llvm::PointerType::get(*llvmModCtx.TheContext, 0);
4887 auto* oldSlot = llvmModCtx.Builder->CreateLoad(i8PtrTy, gep, "old_weak_slot");
4888 auto* hasOldSlot = llvmModCtx.Builder->CreateIsNotNull(oldSlot, "has_old_weak_slot");
4889 auto* freeOldBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "free_old_weak_slot", llvmModCtx.currentFunction);
4890 auto* storeNewBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "store_new_weak_slot", llvmModCtx.currentFunction);
4891 llvmModCtx.Builder->CreateCondBr(hasOldSlot, freeOldBB, storeNewBB);
4892
4893 llvmModCtx.Builder->SetInsertPoint(freeOldBB);
4894 auto targetYoiType = managedPtr(IRValueType{*memberYoiType}.removeAttribute(IRValueType::ValueAttr::WeakRef));
4895 emitWeakSlotFree(llvmModCtx, oldSlot, targetYoiType);
4896 llvmModCtx.Builder->CreateBr(storeNewBB);
4897
4898 llvmModCtx.Builder->SetInsertPoint(storeNewBB);
4899 auto* isNewNull = llvmModCtx.Builder->CreateIsNull(valueToStore.llvmValue, "new_value_is_null");
4900 auto* allocSlotBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "alloc_weak_slot", llvmModCtx.currentFunction);
4901 auto* storeNullBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "store_null_weak", llvmModCtx.currentFunction);
4902 auto* doneWeakBB = llvm::BasicBlock::Create(*llvmModCtx.TheContext, "done_weak_store", llvmModCtx.currentFunction);
4903 llvmModCtx.Builder->CreateCondBr(isNewNull, storeNullBB, allocSlotBB);
4904
4905 llvmModCtx.Builder->SetInsertPoint(allocSlotBB);
4906 auto* newSlot = emitWeakSlotAlloc(llvmModCtx, valueToStore.llvmValue, targetYoiType);
4907 llvmModCtx.Builder->CreateStore(newSlot, gep);
4908 llvmModCtx.Builder->CreateBr(doneWeakBB);
4909
4910 llvmModCtx.Builder->SetInsertPoint(storeNullBB);
4911 llvmModCtx.Builder->CreateStore(llvm::ConstantPointerNull::get(i8PtrTy), gep);
4912 llvmModCtx.Builder->CreateBr(doneWeakBB);
4913
4914 llvmModCtx.Builder->SetInsertPoint(doneWeakBB);
4915 return;
4916 }
4917
4918 auto *oldMemberPtr = llvmModCtx.Builder->CreateLoad(yoiTypeToLLVMType(llvmModCtx, memberYoiType), gep, "old_member_ptr");
4919 callGcFunction(llvmModCtx, oldMemberPtr, memberYoiType, false, true);
4920
4921 if (memberYoiType->metadata.hasMetadata(L"STRUCT_DATAFIELD")) {
4922 auto value = unboxValue(llvmModCtx, valueToStore.llvmValue, valueToStore.yoiType);
4923
4924 if (valueToStore.yoiType->type == IRValueType::valueType::datastructObject) {
4925 // create MemCpy
4926 auto datastructDef = llvmModCtx.dataStructDataRegionMap[valueToStore.yoiType->typeIndex];
4927 auto size = llvmModCtx.TheModule->getDataLayout().getTypeAllocSize(datastructDef);
4928
4929 llvmModCtx.Builder->CreateMemCpy(gep, llvm::MaybeAlign(8), value, llvm::MaybeAlign(8), size);
4930 } else {
4931 llvmModCtx.Builder->CreateStore(value, gep);
4932 }
4933 } else {
4934 auto object = ensureObject(llvmModCtx, valueToStore.yoiType, valueToStore.llvmValue);
4935 llvmModCtx.Builder->CreateStore(object.second, gep);
4936 }
4937
4938 if (valueToStore.yoiType->hasAttribute(IRValueType::ValueAttr::PermanentInCurrentScope) &&
4939 !valueToStore.yoiType->hasAttribute(IRValueType::ValueAttr::Raw))
4940 callGcFunction(llvmModCtx, valueToStore.llvmValue, valueToStore.yoiType, true, true, true);
4941 }
4942} // namespace yoi
#define ENTRY_MODULE_ID_CONST
Definition IRLinker.hpp:12
void purge_and_update(const yoi::vec< yoi::wstr > &source_files)
purge the cache, add the entries that previously not in the cache, remove the entries that are not in...
CodegenObjectCacheEntry get_entry(const yoi::wstr &abs_path_on_disk)
get the entry from the cache
CodegenObjectCache & setCompilerCtx(const std::shared_ptr< compilerContext > &compilerCtx)
set the build config
void update_last_modification(const yoi::wstr &abs_path_on_disk, yoi::indexT last_modification)
update the last modification time of an entry
void dispatch(std::function< void()> task)
Dispatch a task to the thread pool.
void wait()
Wait for all dispatched tasks to complete.
yoi::vec< IR > & getIRArray()
Definition IR.cpp:518
yoi::vec< std::shared_ptr< IRValueType > > argumentTypes
Definition IR.h:488
std::shared_ptr< IRValueType > returnType
Definition IR.h:489
IRDebugInfo debugInfo
Definition IR.h:493
yoi::vec< std::shared_ptr< IRCodeBlock > > codeBlock
Definition IR.h:490
IRVariableTable & getVariableTable()
Definition IR.cpp:619
IRValueType & removeAttribute(ValueAttr attr)
Definition IR.cpp:1358
IRValueType & addAttribute(ValueAttr attr)
Definition IR.cpp:1363
yoi::indexT typeAffiliateModule
Definition IR.h:146
yoi::vec< std::shared_ptr< IRValueType > > & getVariables()
Definition IR.cpp:1026
Definition IR.h:272
IRDebugInfo debugInfo
Definition IR.h:377
yoi::vec< IROperand > operands
Definition IR.h:375
enum yoi::IR::Opcode opcode
yoi::wstr to_string() const
Definition IR.cpp:74
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT >, yoi::indexT > typeIDMap
std::map< yoi::indexT, llvm::AllocaInst * > namedValues
LLVMModuleContext(const std::shared_ptr< IRModule > &yoiModule, yoi::indexT hash, const yoi::wstr &absolute_path)
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT >, llvm::Type * > foreignTypeMap
std::shared_ptr< yoi::IRFunctionDefinition > currentFunctionDef
std::map< yoi::wstr, llvm::DIType * > basicDITypeMap
yoi::vec< std::tuple< std::shared_ptr< IRValueType >, llvm::StructType *, llvm::Type * > > arrayToGenerateImplementations
std::map< yoi::indexT, llvm::DIType * > dataStructDataRegionTypeDIMap
std::map< yoi::wstr, llvm::Function * > functionMap
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT >, llvm::DIType * > arrayDataRegionDITypeMap
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT >, llvm::DIType * > structTypeDIMap
std::unique_ptr< llvm::LLVMContext > TheContext
std::map< yoi::wstr, llvm::DICompileUnit * > compileUnits
std::map< yoi::indexT, llvm::BasicBlock * > basicBlockMap
std::map< yoi::indexT, llvm::GlobalVariable * > globalValues
std::unique_ptr< llvm::IRBuilder<> > Builder
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT >, llvm::StructType * > structTypeMap
std::unique_ptr< llvm::Module > TheModule
std::map< yoi::indexT, bool > basicBlockVisited
std::unique_ptr< llvm::DIBuilder > DBuilder
struct yoi::LLVMCodegen::LLVMModuleContext::@0 currentGeneratorContextBasicBlocks
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT >, llvm::DIType * > arrayTypeDIMap
std::map< yoi::indexT, llvm::StructType * > dataStructDataRegionMap
std::map< yoi::wstr, llvm::Function * > runtimeFunctions
std::map< std::tuple< yoi::IRValueType::valueType, yoi::indexT, yoi::indexT, yoi::indexT >, llvm::StructType * > arrayTypeMap
void emitWeakSlotFree(LLVMModuleContext &llvmModCtx, llvm::Value *slotPtr, const std::shared_ptr< IRValueType > &targetYoiType)
llvm::Function * getGcFunction(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &yoiType, bool isIncrease)
void generateRuntimeFunctionImplementations(LLVMModuleContext &llvmModCtx)
StackValue promiseInterfaceObjectIfInterface(LLVMModuleContext &llvmModCtx, const StackValue &objectVal)
void generateInterfaceObjectGCFunctionImplementations(LLVMModuleContext &llvmModCtx)
yoi::vec< yoi::wstr > generate()
void generateImplementations(LLVMModuleContext &llvmModCtx)
void generateExportFunctionDecls(LLVMModuleContext &llvmModCtx)
CodegenTaskDispatcher codegenTaskDispatcher
void generateImportFunctionDeclarations(LLVMModuleContext &llvmModCtx)
llvm::Value * createGeneratorContext(LLVMModuleContext &llvmModCtx, llvm::Value *coro_handle)
StackValue wrapInterfaceObjectIfRegressed(LLVMModuleContext &llvmModCtx, const StackValue &objectVal)
llvm::Value * loadIfDataStructObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, llvm::Value *value)
void generateGlobalDeclarations(LLVMModuleContext &llvmModCtx)
void generateDeclarations(LLVMModuleContext &llvmModCtx)
llvm::Value * emitWeakSlotAlloc(LLVMModuleContext &llvmModCtx, llvm::Value *targetPtr, const std::shared_ptr< IRValueType > &targetYoiType)
void generateMainFunction(LLVMModuleContext &llvmModCtx)
void callGcFunction(LLVMModuleContext &llvmModCtx, llvm::Value *objectPtr, const std::shared_ptr< IRValueType > &yoiType, bool isIncrease, bool forceForPermanent=false, bool forceForBorrow=false)
void generateStructDeclarations(LLVMModuleContext &llvmModCtx)
llvm::Module * getModule(LLVMModuleContext &llvmModCtx)
std::pair< std::shared_ptr< IRValueType >, llvm::Value * > ensureObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, llvm::Value *val)
void generateForeignStructTypes(LLVMModuleContext &llvmModCtx)
void generateBasicTypeDeclarations(LLVMModuleContext &llvmModCtx)
void generateWrapperForForeignCallablesIfNotExists(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type)
void generateDataStructDeclarations(LLVMModuleContext &llvmModCtx)
void generateFunctionDeclarations(LLVMModuleContext &llvmModCtx)
void declareRuntimeFunctions(LLVMModuleContext &llvmModCtx)
void generateStructGCFunctionImplementations(LLVMModuleContext &llvmModCtx)
void dumpIR(const yoi::wstr &modulePath, const std::string &filename)
void generateDataStructShallowDeclarations(LLVMModuleContext &llvmModCtx)
void generateDescription(LLVMModuleContext &llvmModCtx)
llvm::Type * getArrayLLVMType(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, bool enforceForeignType=false)
void generateRTTIImplmentation(LLVMModuleContext &llvmModCtx)
void generateArrayGCFunctionImplementations(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, llvm::StructType *structType, llvm::Type *baseType)
llvm::Value * createStructObject(LLVMModuleContext &llvmModCtx, yoi::indexT moduleIndex, yoi::indexT structIndex)
std::map< yoi::wstr, std::unique_ptr< LLVMModuleContext > > llvmModuleContext
StackValue actualizeInterfaceObject(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, llvm::Value *objectPtr, yoi::indexT implIndex)
llvm::Type * yoiTypeToLLVMType(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRValueType > &type, bool enforceForeignType=false)
void generateFunctionImplementations(LLVMModuleContext &llvmModCtx)
void generateGeneratorContextInitialization(LLVMModuleContext &llvmModCtx)
void generateIfTargetNotNull(LLVMModuleContext &llvmModCtx, llvm::Value *objectPtr, const std::shared_ptr< IRValueType > &yoiType, const std::function< void()> &func, bool enforced=false)
llvm::Value * unwrapInterfaceObject(LLVMModuleContext &llvmModCtx, const StackValue &objectVal)
void generateFunctionExitCleanup(LLVMModuleContext &llvmModCtx)
void storeYieldValue(LLVMModuleContext &llvmModCtx, llvm::Value *value, const std::shared_ptr< IRValueType > &yoiType)
void storeMember(LLVMModuleContext &llvmModCtx, const StackValue &storeValue, const StackValue &structVal, yoi::indexT memberIndex)
void generateInterfaceObjectGCFunctionDeclarations(LLVMModuleContext &llvmModCtx)
void generateBasicTypeImplementations(LLVMModuleContext &llvmModCtx)
LLVMCodegen(std::shared_ptr< compilerContext > compilerCtx, const std::shared_ptr< IRModule > &yoiModule)
void generateTargetObjectCode(LLVMModuleContext &llvmModCtx, const yoi::wstr &pathToOutput)
void handleIntrinsicCall(LLVMModuleContext &llvmModCtx, const IR &instr)
void generateStructGCFunctionDeclarations(LLVMModuleContext &llvmModCtx)
llvm::Value * unboxValue(LLVMModuleContext &llvmModCtx, llvm::Value *objectPtr, const std::shared_ptr< IRValueType > &yoiType)
void generateImportFunctionImplementations(LLVMModuleContext &llvmModCtx)
std::shared_ptr< compilerContext > compilerCtx
LLVMModuleContext & getLLVMModuleContext(const yoi::wstr &absolutePath)
std::shared_ptr< IRModule > yoiModule
llvm::StructType * findStructLLVMType(const std::shared_ptr< IRValueType > &type)
CodegenObjectCache codegenObjectCache
void generateRTTIDeclaration(LLVMModuleContext &llvmModCtx)
llvm::Function * getLLVMCoroIntrinsic(LLVMModuleContext &llvmModCtx, llvm::Intrinsic::ID Id, llvm::ArrayRef< llvm::Type * > Types={})
llvm::FunctionType * getFunctionType(LLVMModuleContext &llvmModCtx, const std::shared_ptr< IRFunctionDefinition > &funcDef)
void generateGlobalInitializers(LLVMModuleContext &llvmModCtx)
void generateStructShallowDeclarations(LLVMModuleContext &llvmModCtx)
#define TIMER(X, Y)
constexpr auto enum_name() noexcept -> detail::enable_if_t< decltype(V), string_view >
Definition json.hpp:5639
void write(FILE *fp, const yoi::wstr &value)
void read(FILE *fp, yoi::wstr &value)
std::string wstring2string(const std::wstring &v)
Definition def.cpp:230
std::shared_ptr< T > managedPtr(const T &v)
Definition def.hpp:335
void warning(yoi::indexT line, yoi::indexT col, const std::string &msg, const std::string &label)
Definition def.cpp:164
std::vector< t > vec
Definition def.hpp:56
std::wstring string2wstring(const std::string &v)
Definition def.cpp:224
void yoi_assert(bool condition, yoi::indexT line, yoi::indexT col, const std::string &msg)
Asserts a condition that would be true and throws a runtime_error if it is false.
Definition def.cpp:217
void set_current_file_path(const std::wstring &path)
Definition def.cpp:123
std::wstring wstr
Definition def.hpp:51
uint64_t indexT
Definition def.hpp:54
void panic(yoi::indexT line, yoi::indexT col, const std::string &msg)
Definition def.cpp:141
const yoi::wstr & getObjectFilename() const
yoi::indexT getLastModification() const
yoi::indexT column
Definition IR.h:89
yoi::indexT line
Definition IR.h:88
yoi::wstr sourceFile
Definition IR.h:87
std::map< yoi::indexT, std::vector< indexT > > G
Stack Value struct exposed to original code base for compatibility.
std::shared_ptr< IRValueType > yoiType
StackValue & operator[](yoi::indexT index)
void push_back(const StackValue &value)
void enterNode(yoi::indexT currentState, yoi::indexT fromState, llvm::BasicBlock *currentBlock, llvm::BasicBlock *fromBlock)