xref: /llvm-project/flang/tools/tco/tco.cpp (revision e7e5541616435b62da56e0a1fcc587c10b25321c)
1 //===- tco.cpp - Tilikum Crossing Opt ---------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This is to be like LLVM's opt program, only for FIR.  Such a program is
10 // required for roundtrip testing, etc.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "flang/Optimizer/CodeGen/CodeGen.h"
15 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
16 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
17 #include "flang/Optimizer/Support/DataLayout.h"
18 #include "flang/Optimizer/Support/InitFIR.h"
19 #include "flang/Optimizer/Support/InternalNames.h"
20 #include "flang/Optimizer/Transforms/Passes.h"
21 #include "flang/Tools/CrossToolHelpers.h"
22 #include "mlir/IR/AsmState.h"
23 #include "mlir/IR/BuiltinOps.h"
24 #include "mlir/IR/MLIRContext.h"
25 #include "mlir/Parser/Parser.h"
26 #include "mlir/Pass/Pass.h"
27 #include "mlir/Pass/PassManager.h"
28 #include "mlir/Transforms/Passes.h"
29 #include "llvm/Passes/OptimizationLevel.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/ErrorOr.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/InitLLVM.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/SourceMgr.h"
36 #include "llvm/Support/TargetSelect.h"
37 #include "llvm/Support/ToolOutputFile.h"
38 #include "llvm/Support/raw_ostream.h"
39 
40 using namespace llvm;
41 
42 static cl::opt<std::string>
43     inputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
44 
45 static cl::opt<std::string> outputFilename("o",
46                                            cl::desc("Specify output filename"),
47                                            cl::value_desc("filename"),
48                                            cl::init("-"));
49 
50 static cl::opt<bool> emitFir("emit-fir",
51                              cl::desc("Parse and pretty-print the input"),
52                              cl::init(false));
53 
54 static cl::opt<std::string> targetTriple("target",
55                                          cl::desc("specify a target triple"),
56                                          cl::init("native"));
57 
58 static cl::opt<std::string>
59     targetCPU("target-cpu", cl::desc("specify a target CPU"), cl::init(""));
60 
61 static cl::opt<std::string> tuneCPU("tune-cpu", cl::desc("specify a tune CPU"),
62                                     cl::init(""));
63 
64 static cl::opt<std::string>
65     targetFeatures("target-features", cl::desc("specify the target features"),
66                    cl::init(""));
67 
68 static cl::opt<bool> codeGenLLVM(
69     "code-gen-llvm",
70     cl::desc("Run only CodeGen passes and translate FIR to LLVM IR"),
71     cl::init(false));
72 
73 #include "flang/Optimizer/Passes/CommandLineOpts.h"
74 #include "flang/Optimizer/Passes/Pipelines.h"
75 
76 static void printModule(mlir::ModuleOp mod, raw_ostream &output) {
77   output << mod << '\n';
78 }
79 
80 // compile a .fir file
81 static llvm::LogicalResult
82 compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
83   // check that there is a file to load
84   ErrorOr<std::unique_ptr<MemoryBuffer>> fileOrErr =
85       MemoryBuffer::getFileOrSTDIN(inputFilename);
86 
87   if (std::error_code EC = fileOrErr.getError()) {
88     errs() << "Could not open file: " << EC.message() << '\n';
89     return mlir::failure();
90   }
91 
92   // load the file into a module
93   SourceMgr sourceMgr;
94   sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc());
95   mlir::DialectRegistry registry;
96   fir::support::registerDialects(registry);
97   fir::support::addFIRExtensions(registry);
98   mlir::MLIRContext context(registry);
99   fir::support::loadDialects(context);
100   fir::support::registerLLVMTranslation(context);
101   auto owningRef = mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, &context);
102 
103   if (!owningRef) {
104     errs() << "Error can't load file " << inputFilename << '\n';
105     return mlir::failure();
106   }
107   if (mlir::failed(owningRef->verifyInvariants())) {
108     errs() << "Error verifying FIR module\n";
109     return mlir::failure();
110   }
111 
112   std::error_code ec;
113   ToolOutputFile out(outputFilename, ec, sys::fs::OF_None);
114 
115   // run passes
116   fir::KindMapping kindMap{&context};
117   fir::setTargetTriple(*owningRef, targetTriple);
118   fir::setKindMapping(*owningRef, kindMap);
119   fir::setTargetCPU(*owningRef, targetCPU);
120   fir::setTuneCPU(*owningRef, tuneCPU);
121   fir::setTargetFeatures(*owningRef, targetFeatures);
122   // tco is a testing tool, so it will happily use the target independent
123   // data layout if none is on the module.
124   fir::support::setMLIRDataLayoutFromAttributes(*owningRef,
125                                                 /*allowDefaultLayout=*/true);
126   mlir::PassManager pm((*owningRef)->getName(),
127                        mlir::OpPassManager::Nesting::Implicit);
128   pm.enableVerifier(/*verifyPasses=*/true);
129   (void)mlir::applyPassManagerCLOptions(pm);
130   if (emitFir) {
131     // parse the input and pretty-print it back out
132     // -emit-fir intentionally disables all the passes
133   } else if (passPipeline.hasAnyOccurrences()) {
134     auto errorHandler = [&](const Twine &msg) {
135       mlir::emitError(mlir::UnknownLoc::get(pm.getContext())) << msg;
136       return mlir::failure();
137     };
138     if (mlir::failed(passPipeline.addToPipeline(pm, errorHandler)))
139       return mlir::failure();
140   } else {
141     MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2);
142     config.EnableOpenMP = true;  // assume the input contains OpenMP
143     config.AliasAnalysis = true; // enabled when optimizing for speed
144     if (codeGenLLVM) {
145       // Run only CodeGen passes.
146       fir::createDefaultFIRCodeGenPassPipeline(pm, config);
147     } else {
148       // Run tco with O2 by default.
149       fir::registerDefaultInlinerPass(config);
150       fir::createMLIRToLLVMPassPipeline(pm, config);
151     }
152     fir::addLLVMDialectToLLVMPass(pm, out.os());
153   }
154 
155   // run the pass manager
156   if (mlir::succeeded(pm.run(*owningRef))) {
157     // passes ran successfully, so keep the output
158     if ((emitFir || passPipeline.hasAnyOccurrences()) && !codeGenLLVM)
159       printModule(*owningRef, out.os());
160     out.keep();
161     return mlir::success();
162   }
163 
164   // pass manager failed
165   printModule(*owningRef, errs());
166   errs() << "\n\nFAILED: " << inputFilename << '\n';
167   return mlir::failure();
168 }
169 
170 int main(int argc, char **argv) {
171   // Disable the ExternalNameConversion pass by default until all the tests have
172   // been updated to pass with it enabled.
173   disableExternalNameConversion = true;
174 
175   [[maybe_unused]] InitLLVM y(argc, argv);
176   fir::support::registerMLIRPassesForFortranTools();
177   fir::registerOptCodeGenPasses();
178   fir::registerOptTransformPasses();
179   mlir::registerMLIRContextCLOptions();
180   mlir::registerPassManagerCLOptions();
181   mlir::PassPipelineCLParser passPipe("", "Compiler passes to run");
182   cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n");
183   return mlir::failed(compileFIR(passPipe));
184 }
185