From d74e6f5278e67f42e9a020f81c9eb0272f624618 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Fri, 16 Oct 2026 08:06:09 +0000 Subject: 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 --- tools/driver/NativeCompiler.cc | 10 ++++++---- tools/driver/NativeCompiler.h | 2 +- tools/driver/main.cc | 34 ++++++++++++++++++++++------------ tools/driver/prepare-native-runtime.py | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) (limited to 'tools/driver') 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 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(&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 = [] -- cgit v1.3.1-4-g156e