aboutsummaryrefslogtreecommitdiff
path: root/tools/driver
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-16 08:06:09 +0000
committer_ov4 <ov4@ov4.org>2026-10-16 08:06:09 +0000
commitd74e6f5278e67f42e9a020f81c9eb0272f624618 (patch)
tree935eaa12e239bc0b4409fdd948d16ab52feec9e3 /tools/driver
parentc9f3a6a0b7d781fe73b48a5765fa8b6e7ca3c2f7 (diff)
feat(driver)!: select LLVM IR output with -S
Save native executables by default and LLVM IR with -S, using source-adjacent .out and .ll names. Keep -r independent of output selection and update the manuals and three-platform nightly smoke checks. BREAKING CHANGE: plain compilation and -o now save native executables. Use -S to save textual LLVM IR. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'tools/driver')
-rw-r--r--tools/driver/NativeCompiler.cc10
-rw-r--r--tools/driver/NativeCompiler.h2
-rw-r--r--tools/driver/main.cc34
-rw-r--r--tools/driver/prepare-native-runtime.py2
4 files changed, 30 insertions, 18 deletions
diff --git a/tools/driver/NativeCompiler.cc b/tools/driver/NativeCompiler.cc
index f2ea3dc..8d798d7 100644
--- a/tools/driver/NativeCompiler.cc
+++ b/tools/driver/NativeCompiler.cc
@@ -60,7 +60,7 @@ class DriverDiagnostics : public clang::DiagnosticConsumer {
} // namespace
-bool compileNative(const cps::CodeGen &CG, llvm::StringRef OutputPath) {
+bool compileNative(const cps::CodeGen &CG, llvm::StringRef OutputPath, const char *Argv0) {
TemporaryDirectory Runtime;
if (auto EC = llvm::sys::fs::createUniqueDirectory("cpsc-native", Runtime.Path))
return fail("cannot create native compilation directory: " + EC.message());
@@ -157,10 +157,12 @@ bool compileNative(const cps::CodeGen &CG, llvm::StringRef OutputPath) {
DriverDiagnostics Printer;
clang::DiagnosticsEngine Diagnostics(new clang::DiagnosticIDs, Options, &Printer, false);
clang::driver::Driver Driver("cpsc", Triple.str(), Diagnostics);
- // The standard linker flavor constructs a job without checking for an
- // installed linker. Its platform arguments are passed to embedded LLD.
+ // Clang validates the linker executable even for an unlaunched job. Use
+ // cpsc's own path; only the job's arguments are passed to embedded LLD.
std::vector<std::string> Args = {"cpsc", "--driver-mode=g++", "-nostdlib", "-nostdlib++",
- "-fuse-ld=ld", "-o", std::string(StagedPath), std::string(ObjectPath)};
+ "-fuse-ld=lld", "-o", std::string(StagedPath), std::string(ObjectPath)};
+ Args.push_back("--ld-path=" + llvm::sys::fs::getMainExecutable(
+ Argv0, reinterpret_cast<void *>(&compileNative)));
for (const auto &Arg : *Metadata.getArray("arguments")) {
Args.push_back("-Xlinker");
Args.push_back(relocate(*Arg.getAsString()));
diff --git a/tools/driver/NativeCompiler.h b/tools/driver/NativeCompiler.h
index 17933e9..5e13c4d 100644
--- a/tools/driver/NativeCompiler.h
+++ b/tools/driver/NativeCompiler.h
@@ -6,4 +6,4 @@ namespace cps { class CodeGen; }
// Generate a host executable without an external compiler, linker or SDK.
// Failure leaves any existing output intact and reports a diagnostic.
-bool compileNative(const cps::CodeGen &CG, llvm::StringRef OutputPath);
+bool compileNative(const cps::CodeGen &CG, llvm::StringRef OutputPath, const char *Argv0);
diff --git a/tools/driver/main.cc b/tools/driver/main.cc
index 56986ec..a81fb30 100644
--- a/tools/driver/main.cc
+++ b/tools/driver/main.cc
@@ -1,6 +1,7 @@
#include "cps/Lexer.h"
#include "cps/Parser.h"
#include "cps/CodeGen.h"
+#include "NativeCompiler.h"
#ifdef CPSC_RUN_EMBEDDED
#include "EmbeddedRunner.h"
#endif
@@ -87,10 +88,13 @@ int main(int argc, char **argv) {
std::string InputPath;
std::string OutputPath;
bool Run = false;
+ bool EmitIR = false;
for (int I = 1; I < argc; ++I) {
std::string Arg = argv[I];
if (Arg == "-r") {
Run = true;
+ } else if (Arg == "-S") {
+ EmitIR = true;
} else if (Arg == "-o") {
if (!OutputPath.empty()) {
llvm::errs() << "cpsc: error: -o may only be specified once\n";
@@ -133,9 +137,10 @@ int main(int argc, char **argv) {
return compilationTerminated();
#endif
}
- if (OutputPath.empty() && !Run) {
+ const bool Save = !Run || EmitIR || !OutputPath.empty();
+ if (OutputPath.empty() && Save) {
llvm::SmallString<256> DefaultPath(InputPath);
- llvm::sys::path::replace_extension(DefaultPath, ".ir");
+ llvm::sys::path::replace_extension(DefaultPath, EmitIR ? ".ll" : ".out");
OutputPath = std::string(DefaultPath);
}
@@ -144,7 +149,7 @@ int main(int argc, char **argv) {
llvm::errs() << "cpsc: fatal error: cannot open source file '" << InputPath << "'\n";
return compilationTerminated();
}
- if (llvm::sys::fs::equivalent(InputPath, OutputPath)) {
+ if (Save && llvm::sys::fs::equivalent(InputPath, OutputPath)) {
llvm::errs() << "cpsc: error: output file must not be the source file\n";
return compilationTerminated();
}
@@ -164,24 +169,29 @@ int main(int argc, char **argv) {
if (CG.hadError()) return compilationTerminated();
+ if (Save && !EmitIR) {
+ if (!compileNative(CG, OutputPath, argv[0])) return compilationTerminated();
+ if (!Run) return 0;
+ }
+ std::string IRPath = Save && EmitIR ? OutputPath : std::string();
#ifdef CPSC_RUN_EMBEDDED
- if (Run && OutputPath.empty()) return runEmbedded(CG);
+ if (Run && IRPath.empty()) return runEmbedded(CG);
#endif
std::error_code EC;
int OutputFD;
llvm::FileRemover TemporaryOutput;
- if (OutputPath.empty()) {
+ if (IRPath.empty()) {
llvm::SmallString<256> TemporaryPath;
- EC = llvm::sys::fs::createTemporaryFile("cpsc", "ir", OutputFD, TemporaryPath);
+ EC = llvm::sys::fs::createTemporaryFile("cpsc", "ll", OutputFD, TemporaryPath);
if (!EC) {
- OutputPath = std::string(TemporaryPath);
- TemporaryOutput.setFile(OutputPath);
+ IRPath = std::string(TemporaryPath);
+ TemporaryOutput.setFile(IRPath);
}
} else {
- EC = llvm::sys::fs::openFileForWrite(OutputPath, OutputFD);
+ EC = llvm::sys::fs::openFileForWrite(IRPath, OutputFD);
}
if (EC) {
- llvm::errs() << "cpsc: fatal error: cannot open output file '" << OutputPath
+ llvm::errs() << "cpsc: fatal error: cannot open output file '" << IRPath
<< "': " << EC.message() << "\n";
return compilationTerminated();
}
@@ -189,7 +199,7 @@ int main(int argc, char **argv) {
CG.print(Output);
Output.close();
if (Output.has_error()) {
- llvm::errs() << "cpsc: fatal error: cannot write output file '" << OutputPath
+ llvm::errs() << "cpsc: fatal error: cannot write output file '" << IRPath
<< "': " << Output.error().message() << "\n";
Output.clear_error();
return compilationTerminated();
@@ -197,7 +207,7 @@ int main(int argc, char **argv) {
if (!Run) return 0;
#ifdef CPSC_RUN_PATH
- llvm::SmallString<256> RunPath(OutputPath);
+ llvm::SmallString<256> RunPath(IRPath);
if (auto PathError = llvm::sys::fs::make_absolute(RunPath)) {
llvm::errs() << "cpsc: error: cannot resolve IR path: " << PathError.message() << "\n";
return 1;
diff --git a/tools/driver/prepare-native-runtime.py b/tools/driver/prepare-native-runtime.py
index bd1c8d7..10dfb7b 100644
--- a/tools/driver/prepare-native-runtime.py
+++ b/tools/driver/prepare-native-runtime.py
@@ -40,7 +40,7 @@ def main():
raise RuntimeError("Expected one native linker command: " + trace.stderr)
command = commands[0][1:]
directories = [Path(arg[2:]) for arg in command if arg.startswith("-L")]
- target = compiler("-dumpmachine").stdout.strip()
+ target = compiler("-print-effective-triple").stdout.strip()
files = {}
scripts = set()
arguments = []