Skip to content

Commit bc4bfd5

Browse files
committed
Add -emit-llvm to CLI and emit LLVM IR
1 parent 1d20255 commit bc4bfd5

File tree

7 files changed

+188
-97
lines changed

7 files changed

+188
-97
lines changed

src/frontend/cxx/frontend.cc

Lines changed: 158 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include <cxx/mlir/codegen.h>
4343
#include <cxx/mlir/cxx_dialect.h>
4444
#include <cxx/mlir/cxx_dialect_conversions.h>
45+
#include <llvm/IR/LLVMContext.h>
46+
#include <llvm/IR/Module.h>
4547
#endif
4648

4749
#include <format>
@@ -55,54 +57,123 @@
5557

5658
namespace cxx {
5759

58-
Frontend::Frontend(const CLI& cli, std::string fileName)
59-
: cli(cli), fileName_(std::move(fileName)) {
60-
diagnosticsClient_ = std::make_unique<VerifyDiagnosticsClient>();
61-
unit_ = std::make_unique<TranslationUnit>(diagnosticsClient_.get());
60+
struct Frontend::Private {
61+
Frontend& frontend;
62+
const CLI& cli;
63+
std::string fileName_;
64+
std::unique_ptr<TranslationUnit> unit_;
65+
std::unique_ptr<VerifyDiagnosticsClient> diagnosticsClient_;
66+
std::unique_ptr<Toolchain> toolchain_;
67+
std::vector<std::function<void()>> actions_;
68+
#ifdef CXX_WITH_MLIR
69+
std::unique_ptr<mlir::MLIRContext> context_;
70+
mlir::ModuleOp module_;
71+
std::unique_ptr<llvm::LLVMContext> llvmContext_;
72+
std::unique_ptr<llvm::Module> llvmModule_;
73+
#endif
74+
bool shouldExit_ = false;
75+
int exitStatus_ = 0;
6276

63-
actions_.emplace_back([this]() { showSearchPaths(std::cerr); });
64-
actions_.emplace_back([this]() { preprocess(); });
65-
actions_.emplace_back([this]() { printPreprocessedText(); });
66-
actions_.emplace_back([this]() { dumpMacros(std::cout); });
67-
actions_.emplace_back([this]() { dumpTokens(std::cout); });
68-
actions_.emplace_back([this]() { unit_->preprocessor()->squeeze(); });
69-
actions_.emplace_back([this]() { parse(); });
70-
actions_.emplace_back([this]() { dumpSymbols(std::cout); });
71-
actions_.emplace_back([this]() { dumpAst(); });
72-
actions_.emplace_back([this]() { printAstIfNeeded(); });
73-
actions_.emplace_back([this]() { serializeAst(); });
74-
actions_.emplace_back([this]() { emitIR(); });
77+
Private(Frontend& frontend, const CLI& cli, std::string fileName);
78+
~Private();
79+
80+
[[nodiscard]] auto needsIR() const -> bool {
81+
return cli.opt_emit_ir || cli.opt_emit_llvm || cli.opt_S || cli.opt_c;
82+
}
83+
84+
void prepare();
85+
void preparePreprocessor();
86+
void preprocess();
87+
void parse();
88+
void showSearchPaths(std::ostream& out);
89+
void dumpTokens(std::ostream& out);
90+
void dumpSymbols(std::ostream& out);
91+
void serializeAst();
92+
void dumpAst();
93+
void printAstIfNeeded();
94+
void generateIR();
95+
void emitIR();
96+
void emitLLVMIR();
97+
void printPreprocessedText();
98+
void dumpMacros(std::ostream& out);
99+
100+
[[nodiscard]] auto readAll(const std::string& fileName, std::istream& in)
101+
-> std::optional<std::string>;
102+
103+
[[nodiscard]] auto readAll(const std::string& fileName)
104+
-> std::optional<std::string>;
105+
106+
void withOutputStream(const std::optional<std::string>& extension,
107+
const std::function<void(std::ostream&)>& action);
108+
109+
#ifdef CXX_WITH_MLIR
110+
void withRawOutputStream(
111+
const std::optional<std::string>& extension,
112+
const std::function<void(llvm::raw_ostream&)>& action);
113+
#endif
114+
};
115+
116+
Frontend::Frontend(const CLI& cli, std::string fileName) {
117+
priv = std::make_unique<Private>(*this, cli, std::move(fileName));
75118
}
76119

77120
Frontend::~Frontend() {}
78121

79122
auto Frontend::translationUnit() const -> TranslationUnit* {
80-
return unit_.get();
123+
return priv->unit_.get();
81124
}
82125

83-
auto Frontend::toolchain() const -> Toolchain* { return toolchain_.get(); }
126+
auto Frontend::toolchain() const -> Toolchain* {
127+
return priv->toolchain_.get();
128+
}
84129

85-
auto Frontend::fileName() const -> const std::string& { return fileName_; }
130+
auto Frontend::fileName() const -> const std::string& {
131+
return priv->fileName_;
132+
}
86133

87134
void Frontend::addAction(std::function<void()> action) {
88-
actions_.emplace_back(std::move(action));
135+
priv->actions_.emplace_back(std::move(action));
89136
}
90137

91138
auto Frontend::operator()() -> bool {
92-
prepare();
93-
preparePreprocessor();
139+
priv->prepare();
140+
priv->preparePreprocessor();
94141

95-
for (const auto& action : actions_) {
96-
if (shouldExit_) break;
142+
for (const auto& action : priv->actions_) {
143+
if (priv->shouldExit_) break;
97144
action();
98145
}
99146

100-
diagnosticsClient_->verifyExpectedDiagnostics();
147+
priv->diagnosticsClient_->verifyExpectedDiagnostics();
101148

102-
return !diagnosticsClient_->hasErrors();
149+
return !priv->diagnosticsClient_->hasErrors();
103150
}
104151

105-
void Frontend::withOutputStream(
152+
Frontend::Private::Private(Frontend& frontend, const CLI& cli,
153+
std::string fileName)
154+
: frontend(frontend), cli(cli), fileName_(std::move(fileName)) {
155+
diagnosticsClient_ = std::make_unique<VerifyDiagnosticsClient>();
156+
unit_ = std::make_unique<TranslationUnit>(diagnosticsClient_.get());
157+
158+
actions_.emplace_back([this]() { showSearchPaths(std::cerr); });
159+
actions_.emplace_back([this]() { preprocess(); });
160+
actions_.emplace_back([this]() { printPreprocessedText(); });
161+
actions_.emplace_back([this]() { dumpMacros(std::cout); });
162+
actions_.emplace_back([this]() { dumpTokens(std::cout); });
163+
actions_.emplace_back([this]() { unit_->preprocessor()->squeeze(); });
164+
actions_.emplace_back([this]() { parse(); });
165+
actions_.emplace_back([this]() { dumpSymbols(std::cout); });
166+
actions_.emplace_back([this]() { dumpAst(); });
167+
actions_.emplace_back([this]() { printAstIfNeeded(); });
168+
actions_.emplace_back([this]() { serializeAst(); });
169+
actions_.emplace_back([this]() { generateIR(); });
170+
actions_.emplace_back([this]() { emitIR(); });
171+
actions_.emplace_back([this]() { emitLLVMIR(); });
172+
}
173+
174+
Frontend::Private::~Private() {}
175+
176+
void Frontend::Private::withOutputStream(
106177
const std::optional<std::string>& extension,
107178
const std::function<void(std::ostream&)>& action) {
108179
auto explicitOutput = cli.getSingle("-o");
@@ -123,7 +194,7 @@ void Frontend::withOutputStream(
123194
}
124195

125196
#ifdef CXX_WITH_MLIR
126-
void Frontend::withRawOutputStream(
197+
void Frontend::Private::withRawOutputStream(
127198
const std::optional<std::string>& extension,
128199
const std::function<void(llvm::raw_ostream&)>& action) {
129200
auto explicitOutput = cli.getSingle("-o");
@@ -145,7 +216,7 @@ void Frontend::withRawOutputStream(
145216
}
146217
#endif
147218

148-
void Frontend::printPreprocessedText() {
219+
void Frontend::Private::printPreprocessedText() {
149220
if (!cli.opt_E && !cli.opt_Eonly) {
150221
return;
151222
}
@@ -168,7 +239,7 @@ void Frontend::printPreprocessedText() {
168239
});
169240
}
170241

171-
void Frontend::preprocess() {
242+
void Frontend::Private::preprocess() {
172243
auto source = readAll(fileName_);
173244

174245
if (!source.has_value()) {
@@ -182,15 +253,15 @@ void Frontend::preprocess() {
182253
unit_->setSource(std::move(*source), fileName_);
183254
}
184255

185-
void Frontend::dumpMacros(std::ostream& out) {
256+
void Frontend::Private::dumpMacros(std::ostream& out) {
186257
if (!cli.opt_E && !cli.opt_dM) return;
187258

188259
unit_->preprocessor()->printMacros(out);
189260

190261
shouldExit_ = true;
191262
}
192263

193-
void Frontend::prepare() {
264+
void Frontend::Private::prepare() {
194265
auto preprocessor = unit_->preprocessor();
195266

196267
const auto lang = cli.getSingle("-x");
@@ -287,7 +358,7 @@ void Frontend::prepare() {
287358
unit_->control()->setMemoryLayout(toolchain_->memoryLayout());
288359
}
289360

290-
void Frontend::preparePreprocessor() {
361+
void Frontend::Private::preparePreprocessor() {
291362
auto preprocessor = unit_->preprocessor();
292363

293364
if (cli.opt_P) {
@@ -331,7 +402,7 @@ void Frontend::preparePreprocessor() {
331402
}
332403
}
333404

334-
void Frontend::parse() {
405+
void Frontend::Private::parse() {
335406
unit_->parse(ParserConfiguration{
336407
.checkTypes = cli.opt_fcheck || unit_->language() == LanguageKind::kC,
337408
.fuzzyTemplateResolution = true,
@@ -343,7 +414,7 @@ void Frontend::parse() {
343414
}
344415
}
345416

346-
void Frontend::dumpTokens(std::ostream& out) {
417+
void Frontend::Private::dumpTokens(std::ostream& out) {
347418
if (!cli.opt_dump_tokens) return;
348419

349420
auto dumpTokens = DumpTokens{cli};
@@ -352,33 +423,33 @@ void Frontend::dumpTokens(std::ostream& out) {
352423
shouldExit_ = true;
353424
}
354425

355-
void Frontend::dumpSymbols(std::ostream& out) {
426+
void Frontend::Private::dumpSymbols(std::ostream& out) {
356427
if (!cli.opt_dump_symbols) return;
357428
auto globalScope = unit_->globalScope();
358429
auto globalNamespace = globalScope->owner();
359430
cxx::dump(out, globalNamespace);
360431
}
361432

362-
void Frontend::dumpAst() {
433+
void Frontend::Private::dumpAst() {
363434
if (!cli.opt_ast_dump) return;
364435
auto printAST = ASTPrinter{unit_.get(), std::cout};
365436
printAST(unit_->ast());
366437
}
367438

368-
void Frontend::printAstIfNeeded() {
439+
void Frontend::Private::printAstIfNeeded() {
369440
if (!cli.opt_ast_print) return;
370441
auto prettyPrinter = ASTPrettyPrinter{unit_.get(), std::cout};
371442
prettyPrinter(unit_->ast());
372443
}
373444

374-
void Frontend::serializeAst() {
445+
void Frontend::Private::serializeAst() {
375446
if (!cli.opt_emit_ast) return;
376447
auto outputFile = fs::path{fileName_}.filename().replace_extension(".ast");
377448
std::ofstream out(outputFile.string(), std::ios::binary);
378449
(void)unit_->serialize(out);
379450
}
380451

381-
void Frontend::showSearchPaths(std::ostream& out) {
452+
void Frontend::Private::showSearchPaths(std::ostream& out) {
382453
if (!cli.opt_v) return;
383454

384455
out << std::format("#include <...> search starts here:\n");
@@ -392,37 +463,72 @@ void Frontend::showSearchPaths(std::ostream& out) {
392463
out << std::format("End of search list.\n");
393464
}
394465

395-
void Frontend::emitIR() {
396-
if (!cli.opt_emit_ir) return;
466+
void Frontend::Private::generateIR() {
467+
if (cli.opt_fsyntax_only) return;
468+
if (!needsIR()) return;
397469

398470
#ifdef CXX_WITH_MLIR
399-
mlir::MLIRContext context;
400-
context.loadDialect<mlir::cxx::CxxDialect>();
471+
context_ = std::make_unique<mlir::MLIRContext>();
472+
context_->loadDialect<mlir::cxx::CxxDialect>();
401473

402-
auto codegen = cxx::Codegen{context, unit_.get()};
474+
auto codegen = cxx::Codegen{*context_, unit_.get()};
403475

404476
auto ir = codegen(unit_->ast());
405477

406-
if (failed(lowerToMLIR(ir.module))) {
407-
std::cerr << "cxx: failed to lower C++ AST to MLIR" << std::endl;
408-
shouldExit_ = true;
409-
exitStatus_ = EXIT_FAILURE;
478+
if (succeeded(lowerToMLIR(ir.module))) {
479+
module_ = ir.module;
410480
return;
411481
}
412482

483+
std::cerr << "cxx: failed to lower C++ AST to MLIR" << std::endl;
484+
shouldExit_ = true;
485+
exitStatus_ = EXIT_FAILURE;
486+
#endif
487+
}
488+
489+
void Frontend::Private::emitIR() {
490+
if (!cli.opt_emit_ir) return;
491+
492+
#ifdef CXX_WITH_MLIR
493+
if (!module_) return;
494+
495+
shouldExit_ = true;
496+
413497
mlir::OpPrintingFlags flags;
414498
if (cli.opt_g) {
415499
flags.enableDebugInfo(true, false);
416500
}
417501

418502
withRawOutputStream(std::nullopt, [&](llvm::raw_ostream& out) {
419-
ir.module->print(out, flags);
503+
module_->print(out, flags);
420504
});
421505

422506
#endif
423507
}
424508

425-
auto Frontend::readAll(const std::string& fileName, std::istream& in)
509+
void Frontend::Private::emitLLVMIR() {
510+
if (!cli.opt_emit_llvm) return;
511+
512+
#ifdef CXX_WITH_MLIR
513+
if (!module_) return;
514+
515+
llvmContext_ = std::make_unique<llvm::LLVMContext>();
516+
llvmModule_ = exportToLLVMIR(module_, *llvmContext_);
517+
518+
if (!llvmModule_) {
519+
std::cerr << "cxx: failed to lower MLIR module to LLVM IR" << std::endl;
520+
shouldExit_ = true;
521+
exitStatus_ = EXIT_FAILURE;
522+
return;
523+
}
524+
525+
shouldExit_ = true;
526+
withRawOutputStream(
527+
".ll", [&](llvm::raw_ostream& out) { llvmModule_->print(out, nullptr); });
528+
#endif
529+
}
530+
531+
auto Frontend::Private::readAll(const std::string& fileName, std::istream& in)
426532
-> std::optional<std::string> {
427533
std::string code;
428534
char buffer[4 * 1024];
@@ -433,7 +539,7 @@ auto Frontend::readAll(const std::string& fileName, std::istream& in)
433539
return code;
434540
}
435541

436-
auto Frontend::readAll(const std::string& fileName)
542+
auto Frontend::Private::readAll(const std::string& fileName)
437543
-> std::optional<std::string> {
438544
if (fileName == "-" || fileName.empty()) return readAll("<stdin>", std::cin);
439545
if (std::ifstream stream(fileName); stream) return readAll(fileName, stream);

0 commit comments

Comments
 (0)