Skip to content

Commit b1839bf

Browse files
authored
Merge pull request #690 from glu-lang/feature/import-llvm
Feature: Import LLVM files 🎉
2 parents 55f41b7 + 409cf1c commit b1839bf

16 files changed

Lines changed: 264 additions & 25 deletions

include/Basic/SourceManager.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,6 @@ class SourceManager {
139139

140140
~SourceManager() = default;
141141

142-
///
143-
/// TODO: This function must be part of the FileManager class.
144-
///
145142
/// Load a file from the file system. The file is loaded using the virtual
146143
/// file system. The content of the file is stored in a ContentCache object
147144
/// and the file is assigned a FileID.
@@ -150,6 +147,7 @@ class SourceManager {
150147
/// @return A FileID object that represents the file that has been loaded.
151148
///
152149
llvm::ErrorOr<FileID> loadFile(llvm::StringRef filePath);
150+
llvm::ErrorOr<FileID> loadIRFile(llvm::StringRef filePath);
153151
llvm::MemoryBuffer *getBuffer(FileID fileId) const;
154152

155153
void setMainFileID(FileID fid) { _mainFile = fid; }

include/IRDec/ModuleLifter.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#define GLU_IRDEC_MODULE_LIFTER_HPP
33

44
#include "AST/Decls.hpp"
5-
#include "GIL/Module.hpp"
5+
66
#include <llvm/IR/Module.h>
77

88
namespace glu::irdec {

include/Sema/ImportManager.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,31 @@ class ImportManager {
186186
LocalImportResult tryLoadingFile(
187187
SourceLocation importLoc, FileID fid, llvm::StringRef selector
188188
);
189+
/// @brief Tries to select a module to import from a given LLVM IR file. The
190+
/// file should contain LLVM bitcode or human-readable LLVM IR representing
191+
/// a previously compiled module.
192+
/// @param importLoc The source location of the import declaration, used for
193+
/// diagnostics.
194+
/// @param fid The file ID to load the module from.
195+
/// @param selector The selector to import (or empty to import the namespace
196+
/// itself, or "@all" to import all content).
197+
/// @return Returns an import result if it was successfully loaded, or
198+
/// std::nullopt if an error occurred.
199+
LocalImportResult tryLoadingIRFile(
200+
SourceLocation importLoc, FileID fid, llvm::StringRef selector
201+
);
189202
/// @brief Loads a module from a file ID.
190203
/// @param fid The FileID of the module to load.
191204
/// @return Returns true if the module was loaded successfully, false
192205
/// otherwise.
193206
bool loadModuleFromFileID(FileID fid);
207+
/// @brief Loads a module from an LLVM IR file.
208+
/// @param importLoc The source location of the import declaration, used for
209+
/// diagnostics.
210+
/// @param fid The FileID of the module to load.
211+
/// @return Returns true if the module was loaded successfully, false
212+
/// otherwise.
213+
bool loadModuleFromIRFile(SourceLocation importLoc, FileID fid);
194214
/// @brief Imports a module into a given scope.
195215
/// @param importedModule The module to import.
196216
/// @param selector The selector to import (or empty to import the namespace

lib/Basic/SourceManager.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,33 @@ glu::SourceManager::loadFile(llvm::StringRef filePath)
4141
return llvm::ErrorOr<glu::FileID>(glu::FileID(_fileLocEntries.size() - 1));
4242
}
4343

44+
llvm::ErrorOr<glu::FileID>
45+
glu::SourceManager::loadIRFile(llvm::StringRef filePath)
46+
{
47+
llvm::SmallString<256> absPath(filePath);
48+
_vfs->makeAbsolute(absPath);
49+
50+
// First check if the file is already loaded.
51+
for (unsigned i = 0; i < _fileLocEntries.size(); ++i) {
52+
if (_fileLocEntries[i]._fileName == absPath.str()) {
53+
return glu::FileID(i);
54+
}
55+
}
56+
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> file
57+
= _vfs->openFileForRead(filePath);
58+
if (!file) {
59+
return file.getError();
60+
}
61+
62+
// Don't load it in a buffer
63+
64+
_fileLocEntries.emplace_back(
65+
_nextOffset, nullptr, SourceLocation::invalid, absPath.str().str()
66+
);
67+
68+
return llvm::ErrorOr<glu::FileID>(glu::FileID(_fileLocEntries.size() - 1));
69+
}
70+
4471
llvm::MemoryBuffer *glu::SourceManager::getBuffer(FileID fileId) const
4572
{
4673
if (static_cast<size_t>(fileId._id) >= _fileLocEntries.size()) {

lib/IRDec/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ target_include_directories(IRDec
77

88
target_link_libraries(IRDec
99
PUBLIC
10-
GIL
11-
GILGen
10+
AST
11+
LLVM
1212
)
1313

1414
target_sources(IRDec

lib/IRDec/DITypeLifter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "DITypeLifter.hpp"
2+
23
#include <llvm/BinaryFormat/Dwarf.h>
34

45
namespace glu::irdec {

lib/IRDec/ModuleLifter.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#include "ModuleLifter.hpp"
22
#include "AST/Exprs.hpp"
33
#include "DITypeLifter.hpp"
4-
#include "GILGen/GILGen.hpp"
54
#include "TypeLifter.hpp"
6-
#include "llvm/IR/Function.h"
5+
6+
#include <llvm/IR/Function.h>
77

88
namespace glu::irdec {
99

@@ -53,7 +53,10 @@ class ModuleLifter {
5353
llvm::StringRef funcName = func.getName();
5454
if (auto subprogram = func.getSubprogram()) {
5555
type = diTypeLifter.lift(subprogram->getType());
56-
funcName = subprogram->getName();
56+
funcName = copyString(
57+
subprogram->getName(),
58+
_astContext.getASTMemoryArena().getAllocator()
59+
);
5760
} else {
5861
type = typeLifter.lift(func.getFunctionType());
5962
}
@@ -63,11 +66,15 @@ class ModuleLifter {
6366
continue;
6467
llvm::SmallVector<ast::Attribute *, 4> attrs;
6568
if (funcName != func.getName()) {
69+
auto linkageName = copyString(
70+
func.getName(),
71+
_astContext.getASTMemoryArena().getAllocator()
72+
);
6673
auto *attr = astArena.create<ast::Attribute>(
6774
ast::AttributeKind::LinkageNameKind,
6875
SourceLocation::invalid,
6976
astArena.create<ast::LiteralExpr>(
70-
func.getName(), nullptr, SourceLocation::invalid
77+
linkageName, nullptr, SourceLocation::invalid
7178
)
7279
);
7380
attrs.push_back(attr);

lib/IRDec/TypeLifter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "TypeLifter.hpp"
22
#include "AST/Decls.hpp"
3+
34
#include <llvm/IR/DerivedTypes.h>
45

56
namespace glu::irdec {

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ target_link_libraries(Sema
1212
LLVM
1313
gluBasic
1414
Parser
15+
IRDec
1516
)
1617

1718
target_sources(Sema

lib/Sema/ImportManager.cpp

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,39 @@
44
#include "Lexer/Scanner.hpp"
55
#include "Parser/Parser.hpp"
66

7+
#include "IRDec/ModuleLifter.hpp"
8+
9+
#include <llvm/IR/LLVMContext.h>
10+
#include <llvm/IRReader/IRReader.h>
11+
712
namespace glu::sema {
813

914
// Import Search Path Priority:
1015
// Example: import foo::bar::baz;
11-
// 1. ./foo/bar/baz.glu (no selector)
12-
// 2. ./foo/bar.glu (with selector baz)
13-
// 3. <import paths>/foo/bar/baz.glu
14-
// 4. <import paths>/foo/bar.glu (with selector baz)
15-
// 5. <system import paths>/foo/bar/baz.glu
16-
// 6. <system import paths>/foo/bar.glu (with selector baz)
17-
// Note that the system import paths are added to the import paths by the
18-
// compiler driver, so they are not explicitly handled here.
19-
// With wildcards, multiple files are not searched:
16+
// 1. ./foo/bar/baz.glu (no selector) (Glu source file)
17+
// 2. ./foo/bar/baz.bc (no selector) (decompiling LLVM bitcode)
18+
// 3. ./foo/bar/baz.ll (no selector) (human-readable LLVM IR)
19+
// 4. ./foo/bar.glu (with selector baz) (Glu source file)
20+
// 5. ./foo/bar.bc (with selector baz) (decompiling LLVM bitcode)
21+
// 6. ./foo/bar.ll (with selector baz) (human-readable LLVM IR)
22+
// 7. <import paths>/foo/bar/baz.glu (no selector) (Glu source file)
23+
// 8. <import paths>/foo/bar/baz.bc (no selector) (decompiling LLVM bitcode)
24+
// 9. <import paths>/foo/bar/baz.ll (no selector) (human-readable LLVM IR)
25+
// 10. <import paths>/foo/bar.glu (with selector baz) (Glu source file)
26+
// 11. <import paths>/foo/bar.bc (with selector baz) (decompiling LLVM bitcode)
27+
// 12. <import paths>/foo/bar.ll (with selector baz) (human-readable LLVM IR)
28+
// Note that the system import paths are added at the end of the import paths by
29+
// the compiler driver, so they are not explicitly handled here. With wildcards,
30+
// files are not wildcard-expanded, the wildcard is treated as a selector.
2031
// Example: import foo::bar::*;
2132
// 1. ./foo/bar.glu (with selector *)
2233
// 2. <import paths>/foo/bar.glu (with selector *)
2334
// 3. <system import paths>/foo/bar.glu (with selector *)
2435
// This may be changed in the future to support wildcard imports of multiple
25-
// files.
36+
// files. Decompiling bitcode or LLVM IR with wildcards is also supported with
37+
// wildcards, but not shown here for brevity.
38+
39+
// MARK: - Import Resolution
2640

2741
ImportManager::LocalImportResult ImportManager::findImport(
2842
SourceLocation importLoc, llvm::ArrayRef<llvm::StringRef> components,
@@ -99,13 +113,29 @@ bool ImportManager::trySelectPath(
99113
}
100114

101115
// Try with .bc extension (binary LLVM bitcode).
102-
// fullPath = path;
103-
// fullPath.append(".bc");
116+
fullPath = path;
117+
fullPath.append(".bc");
118+
if (auto fileOrErr = sm->loadIRFile(fullPath)) {
119+
// Found
120+
result = tryLoadingIRFile(importLoc, *fileOrErr, selector);
121+
return true;
122+
}
123+
124+
// Try with .ll extension (human-readable LLVM IR).
125+
fullPath = path;
126+
fullPath.append(".ll");
127+
if (auto fileOrErr = sm->loadIRFile(fullPath)) {
128+
// Found
129+
result = tryLoadingIRFile(importLoc, *fileOrErr, selector);
130+
return true;
131+
}
104132

105133
// No file found.
106134
return false;
107135
}
108136

137+
// MARK: - Import File Loading
138+
109139
ImportManager::LocalImportResult ImportManager::tryLoadingFile(
110140
SourceLocation importLoc, FileID fid, llvm::StringRef selector
111141
)
@@ -134,9 +164,27 @@ ImportManager::LocalImportResult ImportManager::tryLoadingFile(
134164
return std::tuple(_importedFiles[fid], selector);
135165
}
136166

167+
ImportManager::LocalImportResult ImportManager::tryLoadingIRFile(
168+
SourceLocation importLoc, FileID fid, llvm::StringRef selector
169+
)
170+
{
171+
if (_failedImports.contains(fid)) {
172+
// Previous import failed, do not try again. Do not generate new errors.
173+
return std::nullopt;
174+
}
175+
if (!_importedFiles[fid]) {
176+
// File has not been imported yet.
177+
if (!loadModuleFromIRFile(importLoc, fid)) {
178+
_failedImports.insert(fid);
179+
return std::nullopt; // Import failed.
180+
}
181+
}
182+
// File has already been imported.
183+
return std::tuple(_importedFiles[fid], selector);
184+
}
185+
137186
bool ImportManager::loadModuleFromFileID(FileID fid)
138187
{
139-
_importStack.push_back(fid);
140188
auto *sm = _context.getSourceManager();
141189
glu::Scanner scanner(sm->getBuffer(fid));
142190
glu::Parser parser(scanner, _context, *sm, _diagManager);
@@ -147,12 +195,39 @@ bool ImportManager::loadModuleFromFileID(FileID fid)
147195
if (!ast) {
148196
return false;
149197
}
198+
_importStack.push_back(fid);
150199
_importedFiles[fid] = sema::fastConstrainAST(ast, _diagManager, this);
151200
assert(_importStack.back() == fid);
152201
_importStack.pop_back();
153202
return _importedFiles[fid] != nullptr;
154203
}
155204

205+
bool ImportManager::loadModuleFromIRFile(SourceLocation importLoc, FileID fid)
206+
{
207+
llvm::SMDiagnostic err;
208+
llvm::LLVMContext localContext;
209+
auto *sm = _context.getSourceManager();
210+
auto llvmModule
211+
= llvm::parseIRFile(sm->getBufferName(fid), err, localContext);
212+
213+
if (!llvmModule) {
214+
_diagManager.error(
215+
importLoc, "Failed to parse LLVM module: " + err.getMessage()
216+
);
217+
return false;
218+
}
219+
220+
auto *ast = glu::irdec::liftModule(_context, llvmModule.get());
221+
222+
_importStack.push_back(fid);
223+
_importedFiles[fid] = sema::fastConstrainAST(ast, _diagManager, this);
224+
assert(_importStack.back() == fid);
225+
_importStack.pop_back();
226+
return _importedFiles[fid] != nullptr;
227+
}
228+
229+
// MARK: - Module Copying
230+
156231
static llvm::StringRef isOperatorOverload(llvm::StringRef name)
157232
{
158233
return llvm::StringSwitch<bool>(name)

0 commit comments

Comments
 (0)