Skip to content

Commit ab5d6bf

Browse files
committed
feat(sema): support importing LLVM files using IRDec
1 parent 78b7619 commit ab5d6bf

5 files changed

Lines changed: 126 additions & 13 deletions

File tree

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/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)

test/functional/IRDec/import-c.glu

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// RUN: mkdir -p %t
3+
// RUN: clang -g -c -emit-llvm %S/simple.c -o %t/example.bc
4+
// RUN: gluc %s -I %t -o %t/import-c && %t/import-c | FileCheck -v %s
5+
//
6+
7+
import example;
8+
9+
@no_mangling @c_variadic func printf(s: *Char) -> Int;
10+
11+
func main() {
12+
// CHECK: Pizza count is 42, compiler is Clang
13+
printf("Pizza count is %d, compiler is %s\n", example::getPizzaCount(), example::getCC());
14+
}

tools/gluc/sources/CompilerDriver.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ std::vector<std::string> CompilerDriver::findImportedObjectFiles()
6161
<< "Object file not found for imported module: " << objPath
6262
<< " (from " << filePath << ")\n";
6363
}
64+
} else {
65+
// Direct LLVM IR or bitcode import: use the same file path
66+
importedFiles.push_back(filePath.str());
6467
}
6568
}
6669

0 commit comments

Comments
 (0)