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