• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <fstream>
17 #include <iomanip>
18 #include <iostream>
19 #include <map>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 
24 #include "ark_version.h"
25 
26 #include "assembly-emitter.h"
27 #include "assembly-parser.h"
28 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
29 #include "bytecode_optimizer/optimize_bytecode.h"
30 #endif
31 #include "file_format_version.h"
32 #include "error.h"
33 #include "lexer.h"
34 #include "pandasm.h"
35 #include "utils/expected.h"
36 #include "utils/logger.h"
37 #include "utils/pandargs.h"
38 
39 namespace ark::pandasm {
40 
PrintError(const ark::pandasm::Error & e,const std::string & msg)41 void PrintError(const ark::pandasm::Error &e, const std::string &msg)
42 {
43     std::stringstream sos;
44     std::cerr << msg << ": " << e.message << std::endl;
45     sos << "      Line " << e.lineNumber << ", Column " << (e.pos + 1) << ": ";
46     std::cerr << sos.str() << e.wholeLine << std::endl;
47     std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl;
48 }
49 
PrintErrors(const ark::pandasm::ErrorList & warnings,const std::string & msg)50 void PrintErrors(const ark::pandasm::ErrorList &warnings, const std::string &msg)
51 {
52     for (const auto &iter : warnings) {
53         PrintError(iter, msg);
54     }
55 }
56 
PrintHelp(const ark::PandArgParser & paParser)57 void PrintHelp(const ark::PandArgParser &paParser)
58 {
59     std::cerr << "Usage:" << std::endl;
60     std::cerr << "pandasm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl;
61     std::cerr << "Supported options:" << std::endl << std::endl;
62     std::cerr << paParser.GetHelpString() << std::endl;
63 }
64 
PrepareArgs(ark::PandArgParser & paParser,const ark::PandArg<std::string> & inputFile,const ark::PandArg<std::string> & outputFile,const ark::PandArg<std::string> & logFile,const ark::PandArg<bool> & help,const ark::PandArg<bool> & verbose,const ark::PandArg<bool> & version,std::ifstream & inputfile,int argc,const char ** argv)65 bool PrepareArgs(ark::PandArgParser &paParser, const ark::PandArg<std::string> &inputFile,
66                  const ark::PandArg<std::string> &outputFile, const ark::PandArg<std::string> &logFile,
67                  const ark::PandArg<bool> &help, const ark::PandArg<bool> &verbose, const ark::PandArg<bool> &version,
68                  std::ifstream &inputfile, int argc, const char **argv)
69 {
70     if (!paParser.Parse(argc, argv)) {
71         PrintHelp(paParser);
72         return false;
73     }
74 
75     if (version.GetValue()) {
76         ark::PrintPandaVersion();
77         panda_file::PrintBytecodeVersion();
78         return false;
79     }
80 
81     if (inputFile.GetValue().empty() || outputFile.GetValue().empty() || help.GetValue()) {
82         PrintHelp(paParser);
83         return false;
84     }
85 
86     if (verbose.GetValue()) {
87         ark::Logger::ComponentMask componentMask;
88         componentMask.set(ark::Logger::Component::ASSEMBLER);
89         componentMask.set(ark::Logger::Component::BYTECODE_OPTIMIZER);
90         if (logFile.GetValue().empty()) {
91             ark::Logger::InitializeStdLogging(ark::Logger::Level::DEBUG, componentMask);
92         } else {
93             ark::Logger::InitializeFileLogging(logFile.GetValue(), ark::Logger::Level::DEBUG, componentMask);
94         }
95     }
96 
97     inputfile.open(inputFile.GetValue(), std::ifstream::in);
98 
99     if (!inputfile) {
100         std::cerr << "The input file does not exist." << std::endl;
101         return false;
102     }
103 
104     return true;
105 }
106 
Tokenize(ark::pandasm::Lexer & lexer,std::vector<std::vector<ark::pandasm::Token>> & tokens,std::ifstream & inputfile)107 bool Tokenize(ark::pandasm::Lexer &lexer, std::vector<std::vector<ark::pandasm::Token>> &tokens,
108               std::ifstream &inputfile)
109 {
110     std::string s;
111 
112     while (getline(inputfile, s)) {
113         ark::pandasm::Tokens q = lexer.TokenizeString(s);
114 
115         auto e = q.second;
116 
117         if (e.err != ark::pandasm::Error::ErrorType::ERR_NONE) {
118             e.lineNumber = tokens.size() + 1;
119             PrintError(e, "ERROR");
120             return false;
121         }
122 
123         tokens.push_back(q.first);
124     }
125 
126     return true;
127 }
128 
ParseProgram(ark::pandasm::Parser & parser,std::vector<std::vector<ark::pandasm::Token>> & tokens,const ark::PandArg<std::string> & inputFile,ark::Expected<ark::pandasm::Program,ark::pandasm::Error> & res)129 bool ParseProgram(ark::pandasm::Parser &parser, std::vector<std::vector<ark::pandasm::Token>> &tokens,
130                   const ark::PandArg<std::string> &inputFile,
131                   ark::Expected<ark::pandasm::Program, ark::pandasm::Error> &res)
132 {
133     res = parser.Parse(tokens, inputFile.GetValue());
134     if (!res) {
135         PrintError(res.Error(), "ERROR");
136         return false;
137     }
138 
139     return true;
140 }
141 
DumpProgramInJson(ark::pandasm::Program & program,const ark::PandArg<std::string> & scopesFile)142 bool DumpProgramInJson(ark::pandasm::Program &program, const ark::PandArg<std::string> &scopesFile)
143 {
144     if (!scopesFile.GetValue().empty()) {
145         std::ofstream dumpFile;
146         dumpFile.open(scopesFile.GetValue());
147 
148         if (!dumpFile) {
149             std::cerr << "Cannot write scopes into the given file." << std::endl;
150             return false;
151         }
152         dumpFile << program.JsonDump();
153     }
154 
155     return true;
156 }
157 
EmitProgramInBinary(ark::pandasm::Program & program,ark::PandArgParser & paParser,const ark::PandArg<std::string> & outputFile,ark::PandArg<bool> & optimize,ark::PandArg<bool> & sizeStat)158 bool EmitProgramInBinary(ark::pandasm::Program &program, ark::PandArgParser &paParser,
159                          const ark::PandArg<std::string> &outputFile, ark::PandArg<bool> &optimize,
160                          ark::PandArg<bool> &sizeStat)
161 {
162     auto emitDebugInfo = !optimize.GetValue();
163     std::map<std::string, size_t> stat;
164     std::map<std::string, size_t> *statp = sizeStat.GetValue() ? &stat : nullptr;
165     ark::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
166     ark::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr;
167 
168     if (!ark::pandasm::AsmEmitter::Emit(outputFile.GetValue(), program, statp, mapsp, emitDebugInfo)) {
169         std::cerr << "Failed to emit binary data: " << ark::pandasm::AsmEmitter::GetLastError() << std::endl;
170         return false;
171     }
172 
173 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
174     if (optimize.GetValue()) {
175         bool isOptimized = ark::bytecodeopt::OptimizeBytecode(&program, mapsp, outputFile.GetValue());
176         if (!ark::pandasm::AsmEmitter::Emit(outputFile.GetValue(), program, statp, mapsp, emitDebugInfo)) {
177             std::cerr << "Failed to emit binary data: " << ark::pandasm::AsmEmitter::GetLastError() << std::endl;
178             return false;
179         }
180         if (!isOptimized) {
181             std::cerr << "Bytecode optimizer reported internal errors" << std::endl;
182             return false;
183         }
184     }
185 #endif
186 
187     if (sizeStat.GetValue()) {
188         size_t totalSize = 0;
189         std::cout << "Panda file size statistic:" << std::endl;
190 
191         for (auto [name, size] : stat) {
192             std::cout << name << " section: " << size << std::endl;
193             totalSize += size;
194         }
195 
196         std::cout << "total: " << totalSize << std::endl;
197     }
198 
199     paParser.DisableTail();
200 
201     return true;
202 }
203 
BuildFiles(ark::pandasm::Program & program,ark::PandArgParser & paParser,const ark::PandArg<std::string> & outputFile,ark::PandArg<bool> & optimize,ark::PandArg<bool> & sizeStat,ark::PandArg<std::string> & scopesFile)204 bool BuildFiles(ark::pandasm::Program &program, ark::PandArgParser &paParser,
205                 const ark::PandArg<std::string> &outputFile, ark::PandArg<bool> &optimize, ark::PandArg<bool> &sizeStat,
206                 ark::PandArg<std::string> &scopesFile)
207 {
208     if (!DumpProgramInJson(program, scopesFile)) {
209         return false;
210     }
211 
212     if (!EmitProgramInBinary(program, paParser, outputFile, optimize, sizeStat)) {
213         return false;
214     }
215 
216     return true;
217 }
218 
219 }  // namespace ark::pandasm
220 
main(int argc,const char * argv[])221 int main(int argc, const char *argv[])
222 {
223     ark::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)");
224     ark::PandArg<std::string> logFile("log-file", "", "(--log-file FILENAME) Set log file name");
225     ark::PandArg<std::string> scopesFile("dump-scopes", "", "(--dump-scopes FILENAME) Enable dump of scopes to file");
226     ark::PandArg<bool> help("help", false, "Print this message and exit");
227     ark::PandArg<bool> sizeStat("size-stat", false, "Print panda file size statistic");
228     ark::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization");
229     ark::PandArg<bool> version {"version", false,
230                                 "Ark version, file format version and minimum supported file format version"};
231     // tail arguments
232     ark::PandArg<std::string> inputFile("INPUT_FILE", "", "Path to the source assembly code");
233     ark::PandArg<std::string> outputFile("OUTPUT_FILE", "", "Path to the generated binary code");
234     ark::PandArgParser paParser;
235     paParser.Add(&verbose);
236     paParser.Add(&help);
237     paParser.Add(&logFile);
238     paParser.Add(&scopesFile);
239     paParser.Add(&sizeStat);
240     paParser.Add(&optimize);
241     paParser.Add(&version);
242     paParser.PushBackTail(&inputFile);
243     paParser.PushBackTail(&outputFile);
244     paParser.EnableTail();
245 
246     std::ifstream inputfile;
247 
248     if (!ark::pandasm::PrepareArgs(paParser, inputFile, outputFile, logFile, help, verbose, version, inputfile, argc,
249                                    argv)) {
250         return 1;
251     }
252 
253     LOG(DEBUG, ASSEMBLER) << "Lexical analysis:";
254 
255     ark::pandasm::Lexer lexer;
256 
257     std::vector<std::vector<ark::pandasm::Token>> tokens;
258 
259     if (!Tokenize(lexer, tokens, inputfile)) {
260         return 1;
261     }
262 
263     LOG(DEBUG, ASSEMBLER) << "parsing:";
264 
265     ark::pandasm::Parser parser;
266 
267     ark::Expected<ark::pandasm::Program, ark::pandasm::Error> res;
268     if (!ark::pandasm::ParseProgram(parser, tokens, inputFile, res)) {
269         return 1;
270     }
271 
272     auto &program = res.Value();
273 
274     auto w = parser.ShowWarnings();
275     if (!w.empty()) {
276         ark::pandasm::PrintErrors(w, "WARNING");
277     }
278 
279     if (!ark::pandasm::BuildFiles(program, paParser, outputFile, optimize, sizeStat, scopesFile)) {
280         return 1;
281     }
282 
283     return 0;
284 }
285