#include
#include
#include
#include
#define CLI11_HAS_FILESYSTEM 0
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace {
using LFortran::endswith;
using LFortran::CompilerOptions;
using LFortran::Python::parse_python_file;
enum Backend {
llvm, cpp, x86
};
enum ASRPass {
do_loops, global_stmts, implied_do_loops, array_op,
arr_slice, print_arr, class_constructor, unused_functions,
};
std::string remove_extension(const std::string& filename) {
size_t lastdot = filename.find_last_of(".");
if (lastdot == std::string::npos) return filename;
return filename.substr(0, lastdot);
}
std::string remove_path(const std::string& filename) {
size_t lastslash = filename.find_last_of("/");
if (lastslash == std::string::npos) return filename;
return filename.substr(lastslash+1);
}
std::string read_file(const std::string &filename)
{
std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary
| std::ios::ate);
std::ifstream::pos_type filesize = ifs.tellg();
if (filesize < 0) return std::string();
ifs.seekg(0, std::ios::beg);
std::vector bytes(filesize);
ifs.read(&bytes[0], filesize);
return std::string(&bytes[0], filesize);
}
std::string get_kokkos_dir()
{
char *env_p = std::getenv("LFORTRAN_KOKKOS_DIR");
if (env_p) return env_p;
std::cerr << "The code C++ generated by the C++ LFortran backend uses the Kokkos library" << std::endl;
std::cerr << "(https://github.com/kokkos/kokkos). Please define the LFORTRAN_KOKKOS_DIR" << std::endl;
std::cerr << "environment variable to point to the Kokkos installation." << std::endl;
throw LFortran::LFortranException("LFORTRAN_KOKKOS_DIR is not defined");
}
#ifdef HAVE_LFORTRAN_LLVM
void section(const std::string &s)
{
std::cout << color(LFortran::style::bold) << color(LFortran::fg::blue) << s << color(LFortran::style::reset) << color(LFortran::fg::reset) << std::endl;
}
int emit_tokens(const std::string &input, std::vector<:string>
&tok_strings, std::vector &toks, std::vector<:yystype>
&stypes)
{
// Overload for the case where we want all the token information to use
// elsewhere
// Src -> Tokens
Allocator al(64*1024*1024);
//std::vector toks;
//std::vector<:yystype> stypes;
LFortran::diag::Diagnostics diagnostics;
auto res = LFortran::tokens(al, input, diagnostics, &stypes);
LFortran::LocationManager lm;
lm.in_filename = "input";
lm.init_simple(input);
LFortran::CompilerOptions cu;
std::cerr << diagnostics.render(input, lm, cu);
if (res.ok) {
toks = res.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
for (size_t i=0; i < toks.size(); i++) {
tok_strings.push_back(LFortran::pickle(toks[i], stypes[i]));
//std::cout << LFortran::pickle(toks[i], stypes[i]) << std::endl;
}
return 0;
}
bool determine_completeness(std::string command)
{
// Determine if the statement is complete
// Get the tokens
bool complete;
std::vector toks;
std::vector<:yystype> stypes;
std::vector<:string> token_strings;
int tok_ret = emit_tokens(command, token_strings, toks, stypes);
// The token enumerators are in parser.tab.hh
int do_blnc = 0;
if (std::find(toks.begin(), toks.end(), KW_DO)!=toks.end()
|| std::find(toks.begin(), toks.end(), KW_DOWHILE)!=toks.end()) {
// Statement contains do loop
for (size_t i = 0; i < toks.size(); i++) {
if (toks[i] == KW_DO || toks[i] == KW_DOWHILE) {
do_blnc++;
} else if (toks[i] == KW_END_DO || toks[i] == KW_ENDDO) {
do_blnc--;
}
}
}
int sr_blnc = 0;
if (std::find(toks.begin(), toks.end(), KW_SUBROUTINE)!=toks.end()) {
// Statement contains subroutine
for (size_t i = 0; i < toks.size(); i++) {
if (toks[i] == KW_SUBROUTINE) {
sr_blnc++;
} else if (toks[i] == KW_END_SUBROUTINE
|| toks[i] == KW_ENDSUBROUTINE) {
sr_blnc--;
}
}
}
int fn_blnc = 0;
if (std::find(toks.begin(), toks.end(), KW_FUNCTION)!=toks.end()) {
// Statement contains function
for (size_t i = 0; i < toks.size(); i++) {
if (toks[i] == KW_FUNCTION) {
fn_blnc++;
} else if (toks[i] == KW_END_FUNCTION
|| toks[i] == KW_ENDFUNCTION) {
fn_blnc--;
}
}
}
int if_blnc = 0;
size_t endif_loc = toks.size() - 1;
// If statements need more involved checks due to the potential for deep
// nesting of block and logical if's.
if (std::find(toks.begin(), toks.end(), KW_IF)!=toks.end()) {
// Statement contains if
for (size_t i = 0; i < toks.size(); i++) {
if (toks[i] == KW_IF) {
if_blnc++;
// Determine if this is a logical or block if by checking for
// then token.
// Block if balance is decremented on an end if token.
// An apparent logical if is decremented arbitrarily since we
// aren't checking syntax here.
if (std::find(toks.begin() + i, toks.begin() + endif_loc,
KW_THEN)!=toks.begin() + endif_loc) {
// The statement contains block ifs, check for end if
for (size_t j = i+1; j < endif_loc; j++) {
if (toks[j] == KW_THEN) {
bool then_found = true;
for (size_t k = endif_loc; k > j; k--) {
if (toks[k] == KW_ENDIF || toks[k] ==
KW_END_IF) {
if_blnc--;
// Make sure to not double count this end
// if by not looping to this far again
endif_loc = k - 1;
}
}
if (then_found) {
// We hit a then and no end if so this must be
// incomplete
break;
}
} else if (toks[j] == KW_IF) {
// We've encountered another if before a then, so
// this appears to be a logical if in a statement
// with other block ifs.
if_blnc--;
break;
}
}
} else {
// No associated then, assume logical if
if_blnc--;
}
}
}
}
if (do_blnc > 0 || sr_blnc > 0 || fn_blnc > 0 || if_blnc > 0) {
complete = false;
} else {
// If there are excess end statements just return error eventually
complete = true;
}
if (tok_ret == 1) {
// Tokenizer error, assume complete and return error eventually
complete = true;
}
return complete;
}
int prompt(bool verbose)
{
Terminal term(true, false);
std::cout << "Interactive Fortran. Experimental prototype, not ready for end users." << std::endl;
std::cout << " * Use Ctrl-D to exit" << std::endl;
std::cout << " * Use Enter to submit" << std::endl;
std::cout << " * Use Alt-Enter or Ctrl-N to make a new line" << std::endl;
std::cout << " - Editing (Keys: Left, Right, Home, End, Backspace, Delete)" << std::endl;
std::cout << " - History (Keys: Up, Down)" << std::endl;
Allocator al(64*1024*1024);
CompilerOptions cu;
LFortran::FortranEvaluator e(cu);
std::vector<:string> history;
std::function iscomplete = determine_completeness;
while (true) {
std::string input = prompt0(term, ">>> ", history, iscomplete);
if (input.size() == 1 && input[0] == CTRL_KEY('d')) {
std::cout << std::endl;
std::cout << "Exiting." << std::endl;
return 0;
}
if (verbose) {
section("Input:");
std::cout << input << std::endl;
}
LFortran::FortranEvaluator::EvalResult r;
LFortran::diag::Diagnostics diagnostics;
try {
LFortran::LocationManager lm;
lm.in_filename = "input";
LFortran::Result<:fortranevaluator::evalresult>
res = e.evaluate(input, verbose, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, cu);
if (res.ok) {
r = res.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
continue;
}
} catch (const LFortran::LFortranException &e) {
std::cout << "Other LFortran exception: " << e.msg() << std::endl;
continue;
}
if (verbose) {
section("AST:");
std::cout << r.ast << std::endl;
section("ASR:");
std::cout << r.asr << std::endl;
section("LLVM IR:");
std::cout << r.llvm_ir << std::endl;
}
switch (r.type) {
case (LFortran::FortranEvaluator::EvalResult::integer4) : {
if (verbose) std::cout << "Return type: integer" << std::endl;
if (verbose) section("Result:");
std::cout << r.i32 << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::integer8) : {
if (verbose) std::cout << "Return type: integer(8)" << std::endl;
if (verbose) section("Result:");
std::cout << r.i64 << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::real4) : {
if (verbose) std::cout << "Return type: real" << std::endl;
if (verbose) section("Result:");
std::cout << std::setprecision(8) << r.f32 << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::real8) : {
if (verbose) std::cout << "Return type: real(8)" << std::endl;
if (verbose) section("Result:");
std::cout << std::setprecision(17) << r.f64 << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::complex4) : {
if (verbose) std::cout << "Return type: complex" << std::endl;
if (verbose) section("Result:");
std::cout << std::setprecision(8) << "(" << r.c32.re << ", " << r.c32.im << ")" << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::complex8) : {
if (verbose) std::cout << "Return type: complex(8)" << std::endl;
if (verbose) section("Result:");
std::cout << std::setprecision(17) << "(" << r.c64.re << ", " << r.c64.im << ")" << std::endl;
break;
}
case (LFortran::FortranEvaluator::EvalResult::statement) : {
if (verbose) {
std::cout << "Return type: none" << std::endl;
section("Result:");
std::cout << "(statement)" << std::endl;
}
break;
}
case (LFortran::FortranEvaluator::EvalResult::none) : {
if (verbose) {
std::cout << "Return type: none" << std::endl;
section("Result:");
std::cout << "(nothing to execute)" << std::endl;
}
break;
}
default : throw LFortran::LFortranException("Return type not supported");
}
}
return 0;
}
#endif
int emit_prescan(const std::string &infile, CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
LFortran::LocationManager lm;
lm.in_filename = infile;
std::string prescan = LFortran::fix_continuation(input, lm,
compiler_options.fixed_form);
std::cout << prescan << std::endl;
return 0;
}
int emit_tokens(const std::string &infile, bool line_numbers, const CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
// Src -> Tokens
Allocator al(64*1024*1024);
std::vector toks;
std::vector<:yystype> stypes;
std::vector<:location> locations;
LFortran::diag::Diagnostics diagnostics;
auto res = LFortran::tokens(al, input, diagnostics, &stypes, &locations);
LFortran::LocationManager lm;
lm.in_filename = infile;
lm.init_simple(input);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (res.ok) {
toks = res.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
for (size_t i=0; i < toks.size(); i++) {
std::cout << LFortran::pickle(toks[i], stypes[i]);
if (line_numbers) {
std::cout << " " << locations[i].first << ":" << locations[i].last;
}
std::cout << std::endl;
}
return 0;
}
int emit_ast_f90(const std::string &infile, CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
LFortran::FortranEvaluator fe(compiler_options);
LFortran::LocationManager lm;
LFortran::diag::Diagnostics diagnostics;
lm.in_filename = infile;
LFortran::Result<:ast::translationunit_t> r
= fe.get_ast2(input, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (r.ok) {
std::cout << LFortran::ast_to_src(*r.result,
compiler_options.use_colors);
return 0;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
}
// int format(const std::string &infile, bool inplace, bool color, int indent,
// bool indent_unit, CompilerOptions &compiler_options)
// {
// std::string input = read_file(infile);
//
// LFortran::FortranEvaluator fe(compiler_options);
// LFortran::LocationManager lm;
// LFortran::diag::Diagnostics diagnostics;
// lm.in_filename = infile;
// LFortran::Result<:ast::translationunit_t>
// r = fe.get_ast2(input, lm, diagnostics);
// std::cerr << diagnostics.render(input, lm, compiler_options);
// if (!r.ok) {
// LFORTRAN_ASSERT(diagnostics.has_error())
// return 2;
// }
// LFortran::AST::TranslationUnit_t* ast = r.result;
//
// // AST -> Source
// if (inplace) color = false;
// std::string source = LFortran::ast_to_src(*ast, color,
// indent, indent_unit);
//
// if (inplace) {
// std::ofstream out;
// out.open(infile);
// out << source;
// } else {
// std::cout << source;
// }
//
// return 0;
// }
int python_wrapper(const std::string &infile, std::string array_order,
CompilerOptions &compiler_options)
{
bool c_order = (0==array_order.compare("c"));
std::string input = read_file(infile);
LFortran::FortranEvaluator fe(compiler_options);
LFortran::ASR::TranslationUnit_t* asr;
// Src -> AST -> ASR
LFortran::LocationManager lm;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
result = fe.get_asr2(input, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
asr = result.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
// figure out pyx and pxd filenames
auto prefix = infile.substr(0,infile.rfind('.'));
auto chdr_fname = prefix + ".h";
auto pxd_fname = prefix + "_pxd.pxd"; // the "_pxd" is an ugly hack, see comment in asr_to_py.cpp
auto pyx_fname = prefix + ".pyx";
// The ASR to Python converter needs to know the name of the .h file that will be written,
// but needs all path information stripped off - just the filename.
auto chdr_fname_forcodegen = chdr_fname;
{
// Find last ocurrence of \ or /, and delete everything up to that point.
auto pos_windows = chdr_fname_forcodegen.rfind('\\');
auto pos_other = chdr_fname_forcodegen.rfind('/');
auto lastpos = std::max( (pos_windows == std::string::npos ? 0 : pos_windows) ,
(pos_other == std::string::npos ? 0 : pos_other) );
if (lastpos > 0UL) chdr_fname_forcodegen.erase(0,lastpos+1);
}
// ASR -> (C header file, Cython pxd file, Cython pyx file)
std::string c_h, pxd, pyx;
std::tie(c_h, pxd, pyx) = LFortran::asr_to_py(*asr, c_order, chdr_fname_forcodegen);
// save generated outputs to files.
std::ofstream(chdr_fname) << c_h;
std::ofstream(pxd_fname) << pxd;
std::ofstream(pyx_fname) << pyx;
return 0;
}
int emit_ast(const std::string &infile,
const std::string &runtime_library_dir,
CompilerOptions &compiler_options)
{
Allocator al(4*1024);
LFortran::Result<:python::ast::ast_t> r = parse_python_file(
al, runtime_library_dir, infile);
if (!r.ok) {
return 1;
}
LFortran::Python::AST::ast_t* ast = r.result;
std::cout << LFortran::Python::pickle_python(*ast,
compiler_options.use_colors, compiler_options.indent) << std::endl;
return 0;
}
int emit_asr(const std::string &infile,
const std::string &runtime_library_dir,
bool with_intrinsic_modules, CompilerOptions &compiler_options)
{
Allocator al(4*1024);
LFortran::Result<:python::ast::ast_t> r1 = parse_python_file(
al, runtime_library_dir, infile);
if (!r1.ok) {
return 1;
}
LFortran::Python::AST::ast_t* ast = r1.result;
LFortran::LocationManager lm;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
r = LFortran::Python::python_ast_to_asr(al, *ast, diagnostics, true);
std::string input = read_file(infile);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!r.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
LFortran::ASR::TranslationUnit_t* asr = r.result;
std::cout << LFortran::pickle(*asr, compiler_options.use_colors, compiler_options.indent,
with_intrinsic_modules) << std::endl;
return 0;
}
int emit_cpp(const std::string &infile,
const std::string &runtime_library_dir,
CompilerOptions &compiler_options)
{
Allocator al(4*1024);
LFortran::Result<:python::ast::ast_t> r = parse_python_file(
al, runtime_library_dir, infile);
if (!r.ok) {
return 1;
}
LFortran::Python::AST::ast_t* ast = r.result;
LFortran::LocationManager lm;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
r1 = LFortran::Python::python_ast_to_asr(al, *ast, diagnostics, true);
std::string input = read_file(infile);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!r1.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
LFortran::ASR::TranslationUnit_t* asr = r1.result;
diagnostics.diagnostics.clear();
auto res = LFortran::asr_to_cpp(al, *asr, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!res.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 3;
}
std::cout << res.result;
return 0;
}
int save_mod_files(const LFortran::ASR::TranslationUnit_t &u)
{
for (auto &item : u.m_global_scope->scope) {
if (LFortran::ASR::is_a<:asr::module_t>(*item.second)) {
LFortran::ASR::Module_t *m = LFortran::ASR::down_cast<:asr::module_t>(item.second);
// Do not save modfiles for modules that were already loaded
// from modfiles (as full ASR)
if (m->m_loaded_from_mod) continue;
Allocator al(4*1024);
LFortran::SymbolTable *symtab =
al.make_new<:symboltable>(nullptr);
symtab->scope[std::string(m->m_name)] = item.second;
LFortran::SymbolTable *orig_symtab = m->m_symtab->parent;
m->m_symtab->parent = symtab;
LFortran::Location loc;
LFortran::ASR::asr_t *asr = LFortran::ASR::make_TranslationUnit_t(al, loc,
symtab, nullptr, 0);
LFortran::ASR::TranslationUnit_t *tu =
LFortran::ASR::down_cast2<:asr::translationunit_t>(asr);
LFORTRAN_ASSERT(LFortran::asr_verify(*tu));
std::string modfile_binary = LFortran::save_modfile(*tu);
m->m_symtab->parent = orig_symtab;
LFORTRAN_ASSERT(LFortran::asr_verify(u));
std::string modfile = std::string(m->m_name) + ".mod";
{
std::ofstream out;
out.open(modfile, std::ofstream::out | std::ofstream::binary);
out << modfile_binary;
}
}
}
return 0;
}
#ifdef HAVE_LFORTRAN_LLVM
int emit_llvm(const std::string &infile,
const std::string &runtime_library_dir,
CompilerOptions &compiler_options)
{
Allocator al(4*1024);
LFortran::Result<:python::ast::ast_t> r = parse_python_file(
al, runtime_library_dir, infile);
if (!r.ok) {
return 1;
}
LFortran::LocationManager lm;
// Src -> AST -> ASR
LFortran::Python::AST::ast_t* ast = r.result;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
r1 = LFortran::Python::python_ast_to_asr(al, *ast, diagnostics, true);
std::string input = read_file(infile);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!r1.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
LFortran::ASR::TranslationUnit_t* asr = r1.result;
diagnostics.diagnostics.clear();
// ASR -> LLVM
LFortran::FortranEvaluator fe(compiler_options);
LFortran::Result<:unique_ptr>>
res = fe.get_llvm3(*asr, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!res.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 3;
}
std::cout << (res.result)->str();
return 0;
}
int emit_asm(const std::string &infile, CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
LFortran::FortranEvaluator fe(compiler_options);
LFortran::LocationManager lm;
LFortran::diag::Diagnostics diagnostics;
lm.in_filename = infile;
LFortran::Result<:string> r = fe.get_asm(input, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (r.ok) {
std::cout << r.result;
return 0;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
}
int compile_python_to_object_file(
const std::string &infile,
const std::string &outfile,
const std::string &runtime_library_dir,
CompilerOptions &compiler_options)
{
Allocator al(4*1024);
LFortran::Result<:python::ast::ast_t> r = parse_python_file(
al, runtime_library_dir, infile);
if (!r.ok) {
return 1;
}
LFortran::LocationManager lm;
// Src -> AST -> ASR
LFortran::Python::AST::ast_t* ast = r.result;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
r1 = LFortran::Python::python_ast_to_asr(al, *ast, diagnostics, true);
std::string input = read_file(infile);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!r1.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
LFortran::ASR::TranslationUnit_t* asr = r1.result;
diagnostics.diagnostics.clear();
// ASR -> LLVM
LFortran::FortranEvaluator fe(compiler_options);
LFortran::LLVMEvaluator e(compiler_options.target);
std::unique_ptr<:llvmmodule> m;
LFortran::Result<:unique_ptr>>
res = fe.get_llvm3(*asr, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (!res.ok) {
LFORTRAN_ASSERT(diagnostics.has_error())
return 3;
}
m = std::move(res.result);
e.save_object_file(*(m->m_m), outfile);
return 0;
}
int compile_to_object_file(const std::string &infile,
const std::string &outfile,
bool assembly,
CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
LFortran::FortranEvaluator fe(compiler_options);
LFortran::ASR::TranslationUnit_t* asr;
// Src -> AST -> ASR
LFortran::LocationManager lm;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
result = fe.get_asr2(input, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
asr = result.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
// Save .mod files
{
int err = save_mod_files(*asr);
if (err) return err;
}
// ASR -> LLVM
LFortran::LLVMEvaluator e(compiler_options.target);
if (!LFortran::ASRUtils::main_program_present(*asr)) {
// Create an empty object file (things will be actually
// compiled and linked when the main program is present):
{
e.create_empty_object_file(outfile);
}
return 0;
}
std::unique_ptr<:llvmmodule> m;
diagnostics.diagnostics.clear();
LFortran::Result<:unique_ptr>>
res = fe.get_llvm3(*asr, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (res.ok) {
m = std::move(res.result);
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 5;
}
if (compiler_options.fast) {
e.opt(*m->m_m);
}
// LLVM -> Machine code (saves to an object file)
if (assembly) {
e.save_asm_file(*(m->m_m), outfile);
} else {
e.save_object_file(*(m->m_m), outfile);
}
return 0;
}
int compile_to_assembly_file(const std::string &infile,
const std::string &outfile, CompilerOptions &compiler_options)
{
return compile_to_object_file(infile, outfile, true, compiler_options);
}
#endif
int compile_to_binary_x86(const std::string &infile, const std::string &outfile,
bool time_report,
CompilerOptions &compiler_options)
{
int time_file_read=0;
int time_src_to_ast=0;
int time_ast_to_asr=0;
int time_asr_to_x86=0;
std::string input;
LFortran::diag::Diagnostics diagnostics;
LFortran::FortranEvaluator fe(compiler_options);
Allocator al(64*1024*1024); // Allocate 64 MB
LFortran::AST::TranslationUnit_t* ast;
LFortran::ASR::TranslationUnit_t* asr;
{
auto t1 = std::chrono::high_resolution_clock::now();
input = read_file(infile);
auto t2 = std::chrono::high_resolution_clock::now();
time_file_read = std::chrono::duration_cast<:chrono::milliseconds>(t2 - t1).count();
}
// Src -> AST
LFortran::LocationManager lm;
lm.in_filename = infile;
{
auto t1 = std::chrono::high_resolution_clock::now();
LFortran::Result<:ast::translationunit_t>
result = fe.get_ast2(input, lm, diagnostics);
auto t2 = std::chrono::high_resolution_clock::now();
time_src_to_ast = std::chrono::duration_cast<:chrono::milliseconds>(t2 - t1).count();
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
ast = result.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
}
// AST -> ASR
{
diagnostics.diagnostics.clear();
auto t1 = std::chrono::high_resolution_clock::now();
LFortran::Result<:asr::translationunit_t>
result = fe.get_asr3(*ast, diagnostics);
auto t2 = std::chrono::high_resolution_clock::now();
time_ast_to_asr = std::chrono::duration_cast<:chrono::milliseconds>(t2 - t1).count();
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
asr = result.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 2;
}
}
// ASR -> x86 machine code
{
diagnostics.diagnostics.clear();
auto t1 = std::chrono::high_resolution_clock::now();
LFortran::Result
result = LFortran::asr_to_x86(*asr, al, outfile, time_report);
auto t2 = std::chrono::high_resolution_clock::now();
time_asr_to_x86 = std::chrono::duration_cast<:chrono::milliseconds>(t2 - t1).count();
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
// pass
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 3;
}
}
if (time_report) {
std::cout << "Allocator usage of last chunk (MB): "
<< al.size_current() / (1024. * 1024) << std::endl;
std::cout << "Allocator chunks: " << al.num_chunks() << std::endl;
std::cout << std::endl;
std::cout << "Time report:" << std::endl;
std::cout << "File reading:" << std::setw(5) << time_file_read << std::endl;
std::cout << "Src -> AST: " << std::setw(5) << time_src_to_ast << std::endl;
std::cout << "AST -> ASR: " << std::setw(5) << time_ast_to_asr << std::endl;
std::cout << "ASR -> x86: " << std::setw(5) << time_asr_to_x86 << std::endl;
int total = time_file_read + time_src_to_ast + time_ast_to_asr
+ time_asr_to_x86;
std::cout << "Total: " << std::setw(5) << total << std::endl;
}
return 0;
}
int compile_to_object_file_cpp(const std::string &infile,
const std::string &outfile,
bool assembly, bool kokkos, const std::string &rtlib_header_dir,
CompilerOptions &compiler_options)
{
std::string input = read_file(infile);
LFortran::FortranEvaluator fe(compiler_options);
LFortran::ASR::TranslationUnit_t* asr;
// Src -> AST -> ASR
LFortran::LocationManager lm;
lm.in_filename = infile;
LFortran::diag::Diagnostics diagnostics;
LFortran::Result<:asr::translationunit_t>
result = fe.get_asr2(input, lm, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (result.ok) {
asr = result.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 1;
}
// Save .mod files
{
int err = save_mod_files(*asr);
if (err) return err;
}
if (!LFortran::ASRUtils::main_program_present(*asr)) {
// Create an empty object file (things will be actually
// compiled and linked when the main program is present):
if (compiler_options.platform == LFortran::Platform::Windows) {
{
std::ofstream out;
out.open(outfile);
out << " ";
}
} else {
std::string outfile_empty = outfile + ".empty.c";
{
std::ofstream out;
out.open(outfile_empty);
out << " ";
}
std::string CC = "cc";
char *env_CC = std::getenv("LFORTRAN_CC");
if (env_CC) CC = env_CC;
std::string cmd = CC + " -c " + outfile_empty + " -o " + outfile;
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 11;
}
}
return 0;
}
// ASR -> C++
std::string src;
diagnostics.diagnostics.clear();
LFortran::Result<:string> res
= fe.get_cpp2(*asr, diagnostics);
std::cerr << diagnostics.render(input, lm, compiler_options);
if (res.ok) {
src = res.result;
} else {
LFORTRAN_ASSERT(diagnostics.has_error())
return 5;
}
// C++ -> Machine code (saves to an object file)
if (assembly) {
throw LFortran::LFortranException("Not implemented");
} else {
std::string cppfile = outfile + ".tmp.cpp";
{
std::ofstream out;
out.open(cppfile);
out << src;
}
std::string CXX = "g++";
std::string options;
if (compiler_options.openmp) {
options += "-fopenmp ";
}
if (kokkos) {
std::string kokkos_dir = get_kokkos_dir();
options += "-std=c++17 -I" + kokkos_dir + "/include";
}
options += " -I" + rtlib_header_dir;
std::string cmd = CXX + " " + options + " -o " + outfile + " -c " + cppfile;
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 11;
}
}
return 0;
}
// infile is an object file
// outfile will become the executable
int link_executable(const std::vector<:string> &infiles,
const std::string &outfile,
const std::string &runtime_library_dir, Backend backend,
bool static_executable, bool kokkos,
CompilerOptions &compiler_options)
{
/*
The `gcc` line for dynamic linking that is constructed below:
gcc -o $outfile $infile \
-Lsrc/runtime -Wl,-rpath=src/runtime -llfortran_runtime
is equivalent to the following:
ld -o $outfile $infile \
-Lsrc/runtime -rpath=src/runtime -llfortran_runtime \
-dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/Scrt1.o /usr/lib/x86_64-linux-gnu/libc.so
and this for static linking:
gcc -static -o $outfile $infile \
-Lsrc/runtime -Wl,-rpath=src/runtime -llfortran_runtime_static
is equivalent to:
ld -o $outfile $infile \
-Lsrc/runtime -rpath=src/runtime -llfortran_runtime_static \
/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o \
/usr/lib/x86_64-linux-gnu/libc.a \
/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_eh.a \
/usr/lib/x86_64-linux-gnu/libc.a \
/usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a \
/usr/lib/x86_64-linux-gnu/crtn.o
This was tested on Ubuntu 18.04.
The `gcc` and `ld` approaches are equivalent except:
1. The `gcc` command knows how to find and link the `libc` library,
while in `ld` we must do that manually
2. For dynamic linking, we must also specify the dynamic linker for `ld`
Notes:
* We can use `lld` to do the linking via the `ld` approach, so `ld` is
preferable if we can mitigate the issues 1. and 2.
* If we ship our own libc (such as musl), then we know how to find it
and link it, which mitigates the issue 1.
* If we link `musl` statically, then issue 2. does not apply.
* If we link `musl` dynamically, then we have to find the dynamic
linker (doable), which mitigates the issue 2.
One way to find the default dynamic linker is by:
$ readelf -e /bin/bash | grep ld-linux
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
There are probably simpler ways.
*/
#ifdef HAVE_LFORTRAN_LLVM
std::string t = (compiler_options.target == "") ? LFortran::LLVMEvaluator::get_default_target_triple() : compiler_options.target;
#else
std::string t = (compiler_options.platform == LFortran::Platform::Windows) ? "x86_64-pc-windows-msvc" : compiler_options.target;
#endif
if (backend == Backend::llvm) {
if (t == "x86_64-pc-windows-msvc") {
std::string cmd = "link /NOLOGO /OUT:" + outfile + " ";
for (auto &s : infiles) {
cmd += s + " ";
}
cmd += runtime_library_dir + "\\lfortran_runtime_static.lib";
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 10;
}
} else {
std::string CC = "cc";
char *env_CC = std::getenv("LFORTRAN_CC");
if (env_CC) CC = env_CC;
std::string base_path = "\"" + runtime_library_dir + "\"";
std::string options;
std::string runtime_lib = "lfortran_runtime";
if (static_executable) {
if (compiler_options.platform != LFortran::Platform::macOS_Intel
&& compiler_options.platform != LFortran::Platform::macOS_ARM) {
options += " -static ";
}
runtime_lib = "lfortran_runtime_static";
}
std::string cmd = CC + options + " -o " + outfile + " ";
for (auto &s : infiles) {
cmd += s + " ";
}
cmd += + " -L"
+ base_path + " -Wl,-rpath," + base_path + " -l" + runtime_lib + " -lm";
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 10;
}
}
return 0;
} else if (backend == Backend::cpp) {
std::string CXX = "g++";
std::string options, post_options;
if (static_executable) {
options += " -static ";
}
if (compiler_options.openmp) {
options += " -fopenmp ";
}
if (kokkos) {
std::string kokkos_dir = get_kokkos_dir();
post_options += kokkos_dir + "/lib/libkokkoscontainers.a "
+ kokkos_dir + "/lib/libkokkoscore.a -ldl";
}
std::string cmd = CXX + options + " -o " + outfile + " ";
for (auto &s : infiles) {
cmd += s + " ";
}
cmd += + " -L";
cmd += " " + post_options + " -lm";
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 10;
}
return 0;
} else if (backend == Backend::x86) {
std::string cmd = "cp " + infiles[0] + " " + outfile;
int err = system(cmd.c_str());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 10;
}
return 0;
} else {
LFORTRAN_ASSERT(false);
return 1;
}
}
// int emit_c_preprocessor(const std::string &infile, CompilerOptions &compiler_options)
// {
// std::string input = read_file(infile);
//
// LFortran::CPreprocessor cpp(compiler_options);
// LFortran::LocationManager lm;
// lm.in_filename = infile;
// std::string s = cpp.run(input, lm, cpp.macro_definitions);
// std::cout << s;
// return 0;
// }
} // anonymous namespace
int main(int argc, char *argv[])
{
LFortran::initialize();
#if defined(HAVE_LFORTRAN_STACKTRACE)
LFortran::print_stack_on_segfault();
#endif
try {
int dirname_length;
LFortran::get_executable_path(LFortran::binary_executable_path, dirname_length);
std::string runtime_library_dir = LFortran::get_runtime_library_dir();
std::string rtlib_header_dir = LFortran::get_runtime_library_header_dir();
Backend backend;
bool arg_S = false;
bool arg_c = false;
bool arg_v = false;
// bool arg_E = false;
// bool arg_g = false;
// std::string arg_J;
// std::vector<:string> arg_I;
// std::vector<:string> arg_l;
// std::vector<:string> arg_L;
std::string arg_o;
std::vector<:string> arg_files;
bool arg_version = false;
bool show_prescan = false;
bool show_tokens = false;
bool show_ast = false;
bool show_asr = false;
bool show_cpp = false;
bool with_intrinsic_modules = false;
bool show_ast_f90 = false;
std::string arg_pass;
bool arg_no_color = false;
bool show_llvm = false;
bool show_asm = false;
bool time_report = false;
bool static_link = false;
std::string arg_backend = "llvm";
std::string arg_kernel_f;
bool print_targets = false;
std::string arg_fmt_file;
// int arg_fmt_indent = 4;
// bool arg_fmt_indent_unit = false;
// bool arg_fmt_inplace = false;
// bool arg_fmt_no_color = false;
std::string arg_mod_file;
// bool arg_mod_show_asr = false;
// bool arg_mod_no_color = false;
std::string arg_pywrap_file;
std::string arg_pywrap_array_order="f";
CompilerOptions compiler_options;
CLI::App app{"LPython: modern interactive LLVM-based Python compiler"};
// Standard options compatible with gfortran, gcc or clang
// We follow the established conventions
app.add_option("files", arg_files, "Source files");
// Should the following Options required for LPython??
// Instead we need support all the options from Python 3
app.add_flag("-S", arg_S, "Emit assembly, do not assemble or link");
app.add_flag("-c", arg_c, "Compile and assemble, do not link");
app.add_option("-o", arg_o, "Specify the file to place the output into");
app.add_flag("-v", arg_v, "Be more verbose");
// app.add_flag("-E", arg_E, "Preprocess only; do not compile, assemble or link");
// app.add_option("-l", arg_l, "Link library option");
// app.add_option("-L", arg_L, "Library path option");
// app.add_option("-I", arg_I, "Include path")->allow_extra_args(false);
// app.add_option("-J", arg_J, "Where to save mod files");
// app.add_flag("-g", arg_g, "Compile with debugging information");
// app.add_option("-D", compiler_options.c_preprocessor_defines, "Define = (or 1 if omitted)")->allow_extra_args(false);
app.add_flag("--version", arg_version, "Display compiler version information");
// LPython specific options
app.add_flag("--cpp", compiler_options.c_preprocessor, "Enable C preprocessing");
app.add_flag("--show-prescan", show_prescan, "Show tokens for the given file and exit");
app.add_flag("--show-tokens", show_tokens, "Show tokens for the given file and exit");
app.add_flag("--show-ast", show_ast, "Show AST for the given python file and exit");
app.add_flag("--show-asr", show_asr, "Show ASR for the given python file and exit");
app.add_flag("--show-ast-f90", show_ast_f90, "Show Fortran from AST for the given file and exit");
app.add_flag("--show-llvm", show_llvm, "Show LLVM IR for the given file and exit");
app.add_flag("--show-cpp", show_cpp, "Show C++ translation source for the given python file and exit");
app.add_flag("--show-asm", show_asm, "Show assembly for the given file and exit");
app.add_flag("--show-stacktrace", compiler_options.show_stacktrace, "Show internal stacktrace on compiler errors");
app.add_flag("--with-intrinsic-mods", with_intrinsic_modules, "Show intrinsic modules in ASR");
app.add_flag("--no-color", arg_no_color, "Turn off colored AST/ASR");
app.add_flag("--indent", compiler_options.indent, "Indented print ASR/AST");
app.add_option("--pass", arg_pass, "Apply the ASR pass and show ASR (implies --show-asr)");
app.add_flag("--symtab-only", compiler_options.symtab_only, "Only create symbol tables in ASR (skip executable stmt)");
app.add_flag("--time-report", time_report, "Show compilation time report");
app.add_flag("--static", static_link, "Create a static executable");
app.add_flag("--no-warnings", compiler_options.no_warnings, "Turn off all warnings");
app.add_flag("--no-error-banner", compiler_options.no_error_banner, "Turn off error banner");
app.add_option("--backend", arg_backend, "Select a backend (llvm, cpp, x86)")->capture_default_str();
app.add_flag("--openmp", compiler_options.openmp, "Enable openmp");
app.add_flag("--fast", compiler_options.fast, "Best performance (disable strict standard compliance)");
app.add_option("--target", compiler_options.target, "Generate code for the given target")->capture_default_str();
app.add_flag("--print-targets", print_targets, "Print the registered targets");
/*
* Subcommands:
*/
// fmt: Should LPython support `fmt` subcommand??
// CLI::App &fmt = *app.add_subcommand("fmt", "Format Fortran source files.");
// fmt.add_option("file", arg_fmt_file, "Fortran source file to format")->required();
// fmt.add_flag("-i", arg_fmt_inplace, "Modify in-place (instead of writing to stdout)");
// fmt.add_option("--spaces", arg_fmt_indent, "Number of spaces to use for indentation")->capture_default_str();
// fmt.add_flag("--indent-unit", arg_fmt_indent_unit, "Indent contents of sub / fn / prog / mod");
// fmt.add_flag("--no-color", arg_fmt_no_color, "Turn off color when writing to stdout");
// kernel
CLI::App &kernel = *app.add_subcommand("kernel", "Run in Jupyter kernel mode.");
kernel.add_option("-f", arg_kernel_f, "The kernel connection file")->required();
// mod
// CLI::App &mod = *app.add_subcommand("mod", "Fortran mod file utilities.");
// mod.add_option("file", arg_mod_file, "Mod file (*.mod)")->required();
// mod.add_flag("--show-asr", arg_mod_show_asr, "Show ASR for the module");
// mod.add_flag("--no-color", arg_mod_no_color, "Turn off colored ASR");
// pywrap
CLI::App &pywrap = *app.add_subcommand("pywrap", "Python wrapper generator");
pywrap.add_option("file", arg_pywrap_file, "Fortran source file (*.f90)")->required();
pywrap.add_option("--array-order", arg_pywrap_array_order,
"Select array order (c, f)")->capture_default_str();
app.get_formatter()->column_width(25);
app.require_subcommand(0, 1);
CLI11_PARSE(app, argc, argv);
if (arg_version) {
std::string version = LFORTRAN_VERSION;
std::cout << "LFortran version: " << version << std::endl;
std::cout << "Platform: ";
switch (compiler_options.platform) {
case (LFortran::Platform::Linux) : std::cout << "Linux"; break;
case (LFortran::Platform::macOS_Intel) : std::cout << "macOS Intel"; break;
case (LFortran::Platform::macOS_ARM) : std::cout << "macOS ARM"; break;
case (LFortran::Platform::Windows) : std::cout << "Windows"; break;
case (LFortran::Platform::FreeBSD) : std::cout << "FreeBSD"; break;
}
std::cout << std::endl;
#ifdef HAVE_LFORTRAN_LLVM
std::cout << "Default target: " << LFortran::LLVMEvaluator::get_default_target_triple() << std::endl;
#endif
return 0;
}
if (print_targets) {
#ifdef HAVE_LFORTRAN_LLVM
LFortran::LLVMEvaluator::print_targets();
return 0;
#else
std::cerr << "The --print-targets option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
}
compiler_options.use_colors = !arg_no_color;
// if (fmt) {
// return format(arg_fmt_file, arg_fmt_inplace, !arg_fmt_no_color,
// arg_fmt_indent, arg_fmt_indent_unit, compiler_options);
// }
if (kernel) {
#ifdef HAVE_LFORTRAN_XEUS
return LFortran::run_kernel(arg_kernel_f);
#else
std::cerr << "The kernel subcommand requires LFortran to be compiled with XEUS support. Recompile with `WITH_XEUS=yes`." << std::endl;
return 1;
#endif
}
// if (mod) {
// if (arg_mod_show_asr) {
// Allocator al(1024*1024);
// LFortran::ASR::TranslationUnit_t *asr;
// asr = LFortran::mod_to_asr(al, arg_mod_file);
// std::cout << LFortran::pickle(*asr, !arg_mod_no_color) << std::endl;
// return 0;
// }
// return 0;
// }
if (pywrap) {
return python_wrapper(arg_pywrap_file, arg_pywrap_array_order,
compiler_options);
}
if (arg_backend == "llvm") {
backend = Backend::llvm;
} else if (arg_backend == "cpp") {
backend = Backend::cpp;
} else if (arg_backend == "x86") {
backend = Backend::x86;
} else {
std::cerr << "The backend must be one of: llvm, cpp, x86." << std::endl;
return 1;
}
if (arg_files.size() == 0) {
#ifdef HAVE_LFORTRAN_LLVM
return prompt(arg_v);
#else
std::cerr << "Interactive prompt requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
}
// TODO: for now we ignore the other filenames, only handle
// the first:
std::string arg_file = arg_files[0];
std::string outfile;
std::string basename;
basename = remove_extension(arg_file);
basename = remove_path(basename);
if (arg_o.size() > 0) {
outfile = arg_o;
} else if (arg_S) {
outfile = basename + ".s";
} else if (arg_c) {
outfile = basename + ".o";
} else if (show_prescan) {
outfile = basename + ".prescan";
} else if (show_tokens) {
outfile = basename + ".tokens";
} else if (show_ast) {
outfile = basename + ".ast";
} else if (show_asr) {
outfile = basename + ".asr";
} else if (show_llvm) {
outfile = basename + ".ll";
} else {
outfile = "a.out";
}
// if (arg_E) {
// return emit_c_preprocessor(arg_file, compiler_options);
// }
if (show_prescan) {
return emit_prescan(arg_file, compiler_options);
}
if (show_tokens) {
return emit_tokens(arg_file, false, compiler_options);
}
if (show_ast_f90) {
return emit_ast_f90(arg_file, compiler_options);
}
std::vector passes;
if (arg_pass != "") {
if (arg_pass == "do_loops") {
passes.push_back(ASRPass::do_loops);
} else if (arg_pass == "global_stmts") {
passes.push_back(ASRPass::global_stmts);
} else if (arg_pass == "implied_do_loops") {
passes.push_back(ASRPass::implied_do_loops);
} else if (arg_pass == "array_op") {
passes.push_back(ASRPass::array_op);
} else if (arg_pass == "class_constructor") {
passes.push_back(ASRPass::class_constructor);
} else if (arg_pass == "print_arr") {
passes.push_back(ASRPass::print_arr);
} else if (arg_pass == "arr_slice") {
passes.push_back(ASRPass::arr_slice);
} else if (arg_pass == "unused_functions") {
passes.push_back(ASRPass::unused_functions);
} else {
std::cerr << "Pass must be one of: do_loops, global_stmts, implied_do_loops, array_op, class_constructor, print_arr, arr_slice, unused_functions" << std::endl;
return 1;
}
show_asr = true;
}
if (show_ast) {
return emit_ast(arg_file, runtime_library_dir, compiler_options);
}
if (show_asr) {
return emit_asr(arg_file, runtime_library_dir,
with_intrinsic_modules, compiler_options);
}
if (show_cpp) {
return emit_cpp(arg_file, runtime_library_dir, compiler_options);
}
if (show_llvm) {
#ifdef HAVE_LFORTRAN_LLVM
return emit_llvm(arg_file, runtime_library_dir, compiler_options);
#else
std::cerr << "The --show-llvm option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
}
if (show_asm) {
#ifdef HAVE_LFORTRAN_LLVM
return emit_asm(arg_file, compiler_options);
#else
std::cerr << "The --show-asm option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
}
if (arg_S) {
if (backend == Backend::llvm) {
#ifdef HAVE_LFORTRAN_LLVM
return compile_to_assembly_file(arg_file, outfile, compiler_options);
#else
std::cerr << "The -S option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
} else if (backend == Backend::cpp) {
std::cerr << "The C++ backend does not work with the -S option yet." << std::endl;
return 1;
} else {
LFORTRAN_ASSERT(false);
}
}
if (arg_c) {
if (backend == Backend::llvm) {
#ifdef HAVE_LFORTRAN_LLVM
return compile_python_to_object_file(arg_file, outfile, runtime_library_dir, compiler_options);
#else
std::cerr << "The -c option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
} else if (backend == Backend::cpp) {
return compile_to_object_file_cpp(arg_file, outfile, false,
true, rtlib_header_dir, compiler_options);
} else if (backend == Backend::x86) {
return compile_to_binary_x86(arg_file, outfile, time_report, compiler_options);
} else {
throw LFortran::LFortranException("Unsupported backend.");
}
}
if (endswith(arg_file, ".py"))
{
std::string tmp_o = outfile + ".tmp.o";
int err;
if (backend == Backend::llvm) {
#ifdef HAVE_LFORTRAN_LLVM
err = compile_python_to_object_file(arg_file, tmp_o, runtime_library_dir, compiler_options);
#else
std::cerr << "Compiling Python files to object files requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
return 1;
#endif
} else {
throw LFortran::LFortranException("Unsupported backend.");
}
if (err) return err;
return link_executable({tmp_o}, outfile, runtime_library_dir,
backend, static_link, true, compiler_options);
} else {
return link_executable(arg_files, outfile, runtime_library_dir,
backend, static_link, true, compiler_options);
}
} catch(const LFortran::LFortranException &e) {
std::cerr << "Internal Compiler Error: Unhandled exception" << std::endl;
std::vector<:stacktraceitem> d = e.stacktrace_addresses();
get_local_addresses(d);
get_local_info(d);
std::cerr << stacktrace2str(d, LFortran::stacktrace_depth);
std::cerr << e.name() + ": " << e.msg() << std::endl;
return 1;
} catch(const std::runtime_error &e) {
std::cerr << "runtime_error: " << e.what() << std::endl;
return 1;
} catch(const std::exception &e) {
std::cerr << "std::exception: " << e.what() << std::endl;
return 1;
} catch(...) {
std::cerr << "Unknown Exception" << std::endl;
return 1;
}
return 0;
}