hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
IRLinker.cpp
Go to the documentation of this file.
1//
2// Created by XIaokang00010 on 2024/10/12.
3//
4
5#include "IRLinker.hpp"
6#include "IR.h"
9#include "compiler/ir/IR.h"
10#include "share/def.hpp"
11#include <ranges>
12#include <utility>
13
14namespace yoi {
15
16 IRLinker::IRLinker() = default;
17
18 std::shared_ptr<IRObjectFile> IRLinker::link(const std::shared_ptr<compilerContext>& context, indexT entryId) {
19 this->compilerCtx = context;
20 this->entryModuleId = entryId;
21 this->finalModule = std::make_shared<IRModule>();
22 finalModule->identifier = ENTRY_MODULE_ID_CONST; // Unified module has no specific ID, elysia here
23
24 auto objectFile = std::make_shared<IRObjectFile>();
25 objectFile->compiledModule = this->finalModule;
26 objectFile->entryModule = this->entryModuleId;
27
28 // The order is important:
29 // 1. Strings, Structs, Interfaces: Define types first.
30 // 2. Globals: Depend on types.
31 // 3. Functions: Depend on everything.
41
42 return objectFile;
43 }
44
45 wstr IRLinker::mangleName(indexT moduleId, const wstr& originalName) {
46 // only when it's not a main function, we need to mangle the name
47 if (moduleId == entryModuleId && originalName == L"main#") {
48 return L"yoi_main";
49 }
50 std::wstringstream ss;
51 ss << std::hex << moduleId;
52 return ss.str() + L"_" + originalName;
53 }
54
56 for (const auto& modPair : compilerCtx->getCompiledModules()) {
57 indexT modId = modPair.first;
58 const auto& srcModule = modPair.second;
59 for (indexT oldIdx = 0; oldIdx < srcModule->stringLiteralPool.pool.size(); ++oldIdx) {
60 const auto& str = srcModule->stringLiteralPool.pool[oldIdx];
61 indexT newIdx = finalModule->stringLiteralPool.addStringLiteral(str);
62 stringRemapping[modId][oldIdx] = newIdx;
63 }
64 }
65 }
66
68 for (const auto& modPair : compilerCtx->getCompiledModules()) {
69 indexT modId = modPair.first;
70 const auto& srcModule = modPair.second;
71
72 // Link Structs
73 for (const auto& structPair : srcModule->structTable) {
74 indexT oldIdx = srcModule->structTable.getIndex(structPair.first);
75 auto newName = mangleName(modId, structPair.second->name);
76 // Just copy the definition, field types will be patched later if needed
77 auto newIndex = finalModule->structTable.put_create(newName, structPair.second);
78 structRemapping[modId][oldIdx] = newIndex;
79 finalModule->structTable[newIndex]->linkedModuleId = modId;
80 }
81
82 for (const auto& ifacePair : srcModule->interfaceTable) {
83 indexT oldIdx = srcModule->interfaceTable.getIndex(ifacePair.first);
84 auto newName = mangleName(modId, ifacePair.second->name);
85 // Just copy the definition, method types will be patched later if needed
86 finalModule->interfaceTable.put_create(newName, ifacePair.second);
87 auto newIndex = finalModule->interfaceTable.getIndex(newName);
88 interfaceRemapping[modId][oldIdx] = newIndex;
89 finalModule->interfaceTable[newIndex]->linkedModuleId = modId;
90 }
91 }
92
93 for (const auto& modPair : compilerCtx->getCompiledModules()) {
94 indexT modId = modPair.first;
95 const auto& srcModule = modPair.second;
96
97 for (const auto& structPair : srcModule->structTable) {
98 indexT oldIdx = srcModule->structTable.getIndex(structPair.first);
99 auto newName = mangleName(modId, structPair.second->name);
100 indexT newIdx = finalModule->structTable.getIndex(newName);
101 finalModule->structTable[newIdx]->name = newName;
102 for (auto &type : finalModule->structTable[newIdx]->fieldTypes) {
103 *type = *patchType(type);
104 }
105 }
106
107 // Link Interfaces
108 for (const auto& ifacePair : srcModule->interfaceTable) {
109 indexT oldIdx = srcModule->interfaceTable.getIndex(ifacePair.first);
110 auto newName = mangleName(modId, ifacePair.second->name);
111 indexT newIdx = finalModule->interfaceTable.getIndex(newName);
112 finalModule->interfaceTable[newIdx]->name = newName;
113 finalModule->interfaceTable[newIdx]->linkedModuleId = modId;
114
115 for (auto &method : finalModule->interfaceTable[newIdx]->methodMap) {
116 for (auto &param : method.second->argumentTypes) {
117 *param = *patchType(param);
118 }
119 for (auto &ret : method.second->variableTable.getVariables()) {
120 *ret = *patchType(ret);
121 }
122 method.second->returnType = patchType(method.second->returnType);
123 }
124
125 interfaceRemapping[modId][oldIdx] = newIdx;
126 }
127
128 // Link Interface Implementations
129 for (const auto& implPair : srcModule->interfaceImplementationTable) {
130 indexT oldIdx = srcModule->interfaceImplementationTable.getIndex(implPair.first);
131 auto newName = mangleName(modId, implPair.second->name);
132 indexT newIdx = finalModule->interfaceImplementationTable.put_create(newName, implPair.second);
133 // printf("remapping interface %lld %lld to %lld %lld\n", implPair.second->implInterfaceIndex.first, implPair.second->implInterfaceIndex.second, ENTRY_MODULE_ID_CONST, interfaceRemapping[implPair.second->implInterfaceIndex.first][implPair.second->implInterfaceIndex.second]);
134 finalModule->interfaceImplementationTable[newIdx]->implInterfaceIndex = {ENTRY_MODULE_ID_CONST, interfaceRemapping[implPair.second->implInterfaceIndex.first][implPair.second->implInterfaceIndex.second]};
135 finalModule->interfaceImplementationTable[newIdx]->name = newName;
136 finalModule->interfaceImplementationTable[newIdx]->linkedModuleId = modId;
137 interfaceImplRemapping[modId][oldIdx] = newIdx;
138 }
139 }
140 }
141
143 for (const auto& modPair : compilerCtx->getCompiledModules()) {
144 indexT modId = modPair.first;
145 const auto& srcModule = modPair.second;
146 for (const auto& globalPair : srcModule->globalVariables) {
147 indexT oldIdx = srcModule->globalVariables.getIndex(globalPair.first);
148 auto newName = mangleName(modId, globalPair.first);
149 // Type might need patching later, but for now just copy
150 indexT newIdx = finalModule->globalVariables.put_create(newName, patchType(globalPair.second));
151 globalRemapping[modId][oldIdx] = newIdx;
152 }
153 }
154 }
155
157 // Pass 1: Create declarations for all functions to handle forward calls
158 for (const auto& modPair : compilerCtx->getCompiledModules()) {
159 indexT modId = modPair.first;
160 const auto& srcModule = modPair.second;
161 for (const auto& funcPair : srcModule->functionTable) {
162 indexT oldIdx = srcModule->functionTable.getIndex(funcPair.first);
163 auto newName = mangleName(modId, funcPair.second->name);
164
165 // Create a shell definition. The body will be filled in Pass 2.
166 auto newFuncDef = std::make_shared<IRFunctionDefinition>(*funcPair.second);
167 newFuncDef->name = newName;
168 newFuncDef->codeBlock.clear();
169
170 for (auto &var : newFuncDef->variableTable.getVariables()) {
171 *var = *patchType(var);
172 }
173 for (auto &param : newFuncDef->argumentTypes) {
174 *param = *patchType(param);
175 }
176
177 indexT newIdx = finalModule->functionTable.put_create(newName, newFuncDef);
178 functionRemapping[modId][oldIdx] = newIdx;
179 finalModule->functionTable[newIdx]->linkedModuleId = modId;
180
181 if (funcPair.first == L"yoimiya_glob_initializer") {
182 globInitializerIndexes.emplace_back(newIdx);
183 }
184 }
185 }
186
187 // Pass 2: Link bodies and patch instructions
188 for (const auto& modPair : compilerCtx->getCompiledModules()) {
189 indexT modId = modPair.first;
190 const auto& srcModule = modPair.second;
191 for (const auto& funcPair : srcModule->functionTable) {
192 indexT oldIdx = srcModule->functionTable.getIndex(funcPair.first);
193 indexT newIdx = functionRemapping.at(modId).at(oldIdx);
194
195 const auto& srcFunc = funcPair.second;
196 auto& destFunc = finalModule->functionTable[newIdx];
197
198 // Copy variable table, local vars don't need remapping
199 destFunc->returnType = patchType(srcFunc->returnType);
200 destFunc->variableTable = srcFunc->variableTable;
201
202 // Copy and patch code blocks
203 for (const auto& srcBlock : srcFunc->codeBlock) {
204 auto newBlock = std::make_shared<IRCodeBlock>();
205 for (const auto& srcInstr : srcBlock->getIRArray()) {
206 newBlock->insert(patchInstruction(srcInstr, modId));
207 }
208 destFunc->codeBlock.push_back(newBlock);
209 }
210 }
211 }
212 }
213
214 IR IRLinker::patchInstruction(const IR& instr, indexT currentModuleId) {
215 IR newInstr = instr;
216 switch (instr.opcode) {
218 auto moduleId = instr.operands[0].value.symbolIndex;
219 auto stringIndex = instr.operands[1].value.symbolIndex;
220 auto newStringIndex = stringRemapping.at(moduleId).at(stringIndex);
221 newInstr.operands[1].value.symbolIndex = newStringIndex;
222 break;
223 }
236 auto moduleId = instr.operands[0].value.symbolIndex;
237 auto symbolIndex = instr.operands[1].value.symbolIndex;
238 newInstr.operands[0].value.symbolIndex = ENTRY_MODULE_ID_CONST;
239 switch (instr.opcode) {
242 newInstr.operands[1].value.symbolIndex = functionRemapping.at(moduleId).at(symbolIndex);
243 break;
246 newInstr.operands[1].value.symbolIndex = globalRemapping.at(moduleId).at(symbolIndex);
247 break;
251 newInstr.operands[1].value.symbolIndex = structRemapping.at(moduleId).at(symbolIndex);
252 break;
254 newInstr.operands[1].value.symbolIndex = datastructRemapping.at(moduleId).at(symbolIndex);
255 break;
259 newInstr.operands[1].value.symbolIndex = interfaceRemapping.at(moduleId).at(symbolIndex);
260 break;
262 newInstr.operands[1].value.symbolIndex = interfaceImplRemapping.at(moduleId).at(symbolIndex);
263 break;
264 default: break;
265 }
266 break;
267 }
270 auto valueType = static_cast<IRValueType::valueType>(instr.operands[0].value.symbolIndex);
271 switch (valueType) {
273 newInstr.operands[1].value.symbolIndex = ENTRY_MODULE_ID_CONST;
274 newInstr.operands[2].value.symbolIndex = structRemapping.at(instr.operands[1].value.symbolIndex).at(instr.operands[2].value.symbolIndex);
275 break;
277 newInstr.operands[1].value.symbolIndex = ENTRY_MODULE_ID_CONST;
278 newInstr.operands[2].value.symbolIndex = interfaceRemapping.at(instr.operands[1].value.symbolIndex).at(instr.operands[2].value.symbolIndex);
279 break;
280 default:
281 break;
282 }
283 break;
284 }
285 default:
286 break;
287 }
288
289 return newInstr;
290 }
291
292 std::shared_ptr<IRValueType> IRLinker::patchType(const std::shared_ptr<IRValueType> &oldType) {
293 // map the old type to the new type
294 if (oldType->typeAffiliateModule == ENTRY_MODULE_ID_CONST) {
295 return oldType;
296 }
297 std::shared_ptr<IRValueType> newType = managedPtr(*oldType);
298 switch (oldType->type) {
300 auto newIndex = structRemapping[oldType->typeAffiliateModule][oldType->typeIndex];
301 newType->typeAffiliateModule = ENTRY_MODULE_ID_CONST;
302 newType->typeIndex = newIndex;
303 break;
304 }
306 auto newIndex = datastructRemapping[oldType->typeAffiliateModule][oldType->typeIndex];
307 newType->typeAffiliateModule = ENTRY_MODULE_ID_CONST;
308 newType->typeIndex = newIndex;
309 break;
310 }
312 auto newIndex = interfaceRemapping[oldType->typeAffiliateModule][oldType->typeIndex];
313 newType->typeAffiliateModule = ENTRY_MODULE_ID_CONST;
314 newType->typeIndex = newIndex;
315 break;
316 }
318 auto newIndex = functionRemapping[oldType->typeAffiliateModule][oldType->typeIndex];
319 newType->typeAffiliateModule = ENTRY_MODULE_ID_CONST;
320 newType->typeIndex = newIndex;
321 break;
322 }
323 default: {
324 // Other types don't need patching
325 break;
326 }
327 }
328 // record the origin index into IRMetadata
329 newType->metadata.setMetadata(L"linkMetadata", std::make_pair(oldType->typeAffiliateModule, oldType->typeIndex));
330 return newType;
331 }
334 auto entry = entryBuilder.setName(L"yoimiya_entry").setReturnType(compilerCtx->getIntObjectType()).setDebugInfo({L"<entry>", 0, 0}).yield();
335 entry->linkedModuleId = HOSHI_COMPILER_CTX_GLOB_ID_CONST;
336 auto entryIndex = this->finalModule->functionTable.put_create(L"yoimiya_entry", entry);
337 IRBuilder builder(compilerCtx, finalModule, entry);
338 builder.switchCodeBlock(builder.createCodeBlock());
339 for (auto &globInitializerIndex : std::ranges::reverse_view(globInitializerIndexes)) {
340 if (finalModule->functionTable[globInitializerIndex]->hasAttribute(IRFunctionDefinition::FunctionAttrs::Unreachable))
341 continue;
342 builder.invokeOp(globInitializerIndex, 0, compilerCtx->getIntObjectType());
343 }
344 if (compilerCtx->getBuildConfig()->buildType == IRBuildConfig::BuildType::executable) {
345 try {
346 builder.invokeOp(finalModule->functionTable.getIndex(L"yoi_main"), 0, compilerCtx->getIntObjectType());
347 builder.retOp();
348 } catch (const std::out_of_range&) {
349 panic(0, 0, "Entry function not found for executable build!");
350 return;
351 }
352 } else {
354 builder.retOp();
355 }
356 builder.yield();
357 }
359 for (auto &implPair : finalModule->interfaceImplementationTable) {
360 implPair.second->implStructIndex = patchUniqueKey(implPair.second->implStructIndex);
361 for (auto &virtualMethod : implPair.second->virtualMethods) {
363 }
364 }
365 }
367 for (auto &funcPair : compilerCtx->getIRFFITable()->exportedFunctionTable) {
368 funcPair.second = {ENTRY_MODULE_ID_CONST, functionRemapping.at(std::get<0>(funcPair.second)).at(std::get<1>(funcPair.second)), std::get<2>(funcPair.second)};
369 }
370 for (auto &libPair : compilerCtx->getIRFFITable()->importedLibraries) {
371 for (auto &funcPair : libPair.second.importedFunctionTable) {
372 funcPair.second->returnType = patchType(funcPair.second->returnType);
373 for (auto &param : funcPair.second->argumentTypes) {
374 *param = *patchType(param);
375 }
376 for (auto &ret : funcPair.second->variableTable.getVariables()) {
377 *ret = *patchType(ret);
378 }
379 }
380 }
381 for (auto &foreignTypePair : compilerCtx->getIRFFITable()->foreignTypeTable) {
382 foreignTypePair.second = patchType(foreignTypePair.second);
383 }
384 }
385
386 std::tuple<IRValueType::valueType, indexT, indexT>
387 IRLinker::patchUniqueKey(const std::tuple<IRValueType::valueType, indexT, indexT> &key) {
388 switch (std::get<0>(key)) {
390 auto newIndex = structRemapping[std::get<1>(key)][std::get<2>(key)];
391 return std::make_tuple(IRValueType::valueType::structObject, ENTRY_MODULE_ID_CONST, newIndex);
392 }
394 auto newIndex = datastructRemapping[std::get<1>(key)][std::get<2>(key)];
395 return std::make_tuple(IRValueType::valueType::datastructObject, ENTRY_MODULE_ID_CONST, newIndex);
396 }
398 auto newIndex = interfaceRemapping[std::get<1>(key)][std::get<2>(key)];
399 return std::make_tuple(IRValueType::valueType::interfaceObject, ENTRY_MODULE_ID_CONST, newIndex);
400 }
402 auto newIndex = functionRemapping[std::get<1>(key)][std::get<2>(key)];
403 return std::make_tuple(IRValueType::valueType::virtualMethod, ENTRY_MODULE_ID_CONST, newIndex);
404 }
405 default: {
406 // Other types don't need patching
407 return key;
408 }
409 }
410 }
411
413 auto link = [&](IRMetadata &metadata) {
414 if (metadata.hasMetadata(L"regressed_interface_impl")) {
415 auto &impl = metadata.getMetadata<std::pair<yoi::indexT, yoi::indexT>>(L"regressed_interface_impl");
416 impl = impl.first != -1 ? std::pair<yoi::indexT, yoi::indexT>(ENTRY_MODULE_ID_CONST, interfaceImplRemapping.at(impl.first).at(impl.second)) : impl;
417 }
418 };
419 for (auto &func : finalModule->functionTable) {
420 for (auto &var : func.second->variableTable.getVariables()) {
421 link(var->metadata);
422 }
423 }
424 }
425
427 for (const auto& modPair : compilerCtx->getCompiledModules()) {
428 indexT modId = modPair.first;
429 const auto& srcModule = modPair.second;
430 for (const auto& dataStructPair : srcModule->dataStructTable) {
431 auto &dataStruct = dataStructPair.second;
432 dataStruct->linkedModuleId = modId;
433 datastructRemapping[modId][srcModule->dataStructTable.getIndex(dataStructPair.first)] = finalModule->dataStructTable.put_create(dataStructPair.first, managedPtr(* dataStruct));
434 }
435 }
436
437 for (auto &dataStructPair : finalModule->dataStructTable) {
438 for (auto &field : dataStructPair.second->fieldTypes) {
439 *field = *patchType(field);
440 }
441 }
442 }
443} // namespace yoi
#define ENTRY_MODULE_ID_CONST
Definition IRLinker.hpp:12
#define HOSHI_COMPILER_CTX_GLOB_ID_CONST
void invokeOp(yoi::indexT funcIndex, yoi::indexT funcArgsCount, const std::shared_ptr< IRValueType > &returnType, bool externalInvocation=false, yoi::indexT moduleIndex=-1)
Invoke a function with the given arguments.
Definition IR.cpp:382
void pushOp(IR::Opcode op, const yoi::IROperand &constV)
Definition IR.cpp:303
yoi::indexT createCodeBlock()
Definition IR.cpp:123
void yield()
Definition IR.cpp:132
yoi::indexT switchCodeBlock(yoi::indexT index)
Definition IR.cpp:492
void retOp(bool returnWithNone=false)
Definition IR.cpp:438
std::map< indexT, std::map< indexT, indexT > > datastructRemapping
Definition IRLinker.hpp:35
void patchIRFFITable()
Definition IRLinker.cpp:366
yoi::vec< yoi::indexT > globInitializerIndexes
Definition IRLinker.hpp:41
indexT entryModuleId
Definition IRLinker.hpp:31
void linkInterfaceImplementations()
Definition IRLinker.cpp:358
void linkGlobals()
Definition IRLinker.cpp:142
void linkStringLiterals()
Definition IRLinker.cpp:55
void linkStructsAndInterfaces()
Definition IRLinker.cpp:67
wstr mangleName(indexT moduleId, const wstr &originalName)
Mangles a symbol name with its module ID, unless it's the main function in the entry module.
Definition IRLinker.cpp:45
std::map< indexT, std::map< indexT, indexT > > structRemapping
Definition IRLinker.hpp:34
std::map< indexT, std::map< indexT, indexT > > globalRemapping
Definition IRLinker.hpp:38
void linkDataStructs()
Definition IRLinker.cpp:426
std::shared_ptr< IRObjectFile > link(const std::shared_ptr< compilerContext > &context, indexT entryModuleId)
Links all compiled modules from the context into a single IRObjectFile.
Definition IRLinker.cpp:18
std::map< indexT, std::map< indexT, indexT > > interfaceImplRemapping
Definition IRLinker.hpp:37
std::tuple< IRValueType::valueType, indexT, indexT > patchUniqueKey(const std::tuple< IRValueType::valueType, indexT, indexT > &key)
Definition IRLinker.cpp:387
std::map< indexT, std::map< indexT, indexT > > interfaceRemapping
Definition IRLinker.hpp:36
void createEntryFunction()
Definition IRLinker.cpp:332
void linkMetadata()
Definition IRLinker.cpp:412
std::shared_ptr< IRModule > finalModule
Definition IRLinker.hpp:30
IR patchInstruction(const IR &instr, indexT currentModuleId)
Definition IRLinker.cpp:214
std::shared_ptr< compilerContext > compilerCtx
Definition IRLinker.hpp:29
std::map< indexT, std::map< indexT, indexT > > functionRemapping
Definition IRLinker.hpp:39
void linkFunctions()
Definition IRLinker.cpp:156
std::shared_ptr< IRValueType > patchType(const std::shared_ptr< IRValueType > &oldType)
Definition IRLinker.cpp:292
std::map< indexT, std::map< indexT, indexT > > stringRemapping
Definition IRLinker.hpp:40
Definition IR.h:264
yoi::vec< IROperand > operands
Definition IR.h:367
enum yoi::IR::Opcode opcode
std::shared_ptr< T > managedPtr(const T &v)
Definition def.hpp:324
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
valueType
Definition rtti.h:11
Builder & setDebugInfo(const IRDebugInfo &debugInfo)
Definition IR.cpp:1211
Builder & setName(const yoi::wstr &name)
Definition IR.cpp:541
Builder & setReturnType(const std::shared_ptr< IRValueType > &returnType)
Definition IR.cpp:596