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