1 /**
2 * Copyright (c) 2021-2022 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 "utils/expected.h"
35 #include "utils/logger.h"
36 #include "utils/pandargs.h"
37 #include "pandasm.h"
38
39 namespace panda::pandasm {
40
PrintError(const panda::pandasm::Error & e,const std::string & msg)41 void PrintError(const panda::pandasm::Error &e, const std::string &msg)
42 {
43 std::stringstream sos;
44 std::cerr << msg << ": " << e.message << std::endl;
45 sos << " Line " << e.line_number << ", Column " << e.pos + 1 << ": ";
46 std::cerr << sos.str() << e.whole_line << std::endl;
47 std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl;
48 }
49
PrintErrors(const panda::pandasm::ErrorList & warnings,const std::string & msg)50 void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &msg)
51 {
52 for (const auto &iter : warnings) {
53 PrintError(iter, msg);
54 }
55 }
56
PrintHelp(const panda::PandArgParser & pa_parser)57 void PrintHelp(const panda::PandArgParser &pa_parser)
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 << pa_parser.GetHelpString() << std::endl;
63 }
64
PrepareArgs(panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & input_file,const panda::PandArg<std::string> & output_file,const panda::PandArg<std::string> & log_file,const panda::PandArg<bool> & help,const panda::PandArg<bool> & verbose,const panda::PandArg<bool> & version,std::ifstream & inputfile,int argc,const char ** argv)65 bool PrepareArgs(panda::PandArgParser &pa_parser, const panda::PandArg<std::string> &input_file,
66 const panda::PandArg<std::string> &output_file, const panda::PandArg<std::string> &log_file,
67 const panda::PandArg<bool> &help, const panda::PandArg<bool> &verbose,
68 const panda::PandArg<bool> &version, std::ifstream &inputfile, int argc, const char **argv)
69 {
70 if (!pa_parser.Parse(argc, argv)) {
71 PrintHelp(pa_parser);
72 return false;
73 }
74
75 if (version.GetValue()) {
76 panda::PrintPandaVersion();
77 panda_file::PrintBytecodeVersion();
78 return false;
79 }
80
81 if (input_file.GetValue().empty() || output_file.GetValue().empty() || help.GetValue()) {
82 PrintHelp(pa_parser);
83 return false;
84 }
85
86 if (verbose.GetValue()) {
87 if (log_file.GetValue().empty()) {
88 panda::Logger::ComponentMask component_mask;
89 component_mask.set(panda::Logger::Component::ASSEMBLER);
90 component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER);
91 panda::Logger::InitializeStdLogging(panda::Logger::Level::DEBUG, component_mask);
92 } else {
93 panda::Logger::ComponentMask component_mask;
94 component_mask.set(panda::Logger::Component::ASSEMBLER);
95 component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER);
96 panda::Logger::InitializeFileLogging(log_file.GetValue(), panda::Logger::Level::DEBUG, component_mask);
97 }
98 }
99
100 inputfile.open(input_file.GetValue(), std::ifstream::in);
101
102 if (!inputfile) {
103 std::cerr << "The input file does not exist." << std::endl;
104 return false;
105 }
106
107 return true;
108 }
109
Tokenize(panda::pandasm::Lexer & lexer,std::vector<std::vector<panda::pandasm::Token>> & tokens,std::ifstream & inputfile)110 bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::pandasm::Token>> &tokens,
111 std::ifstream &inputfile)
112 {
113 std::string s;
114
115 while (getline(inputfile, s)) {
116 panda::pandasm::Tokens q = lexer.TokenizeString(s);
117
118 auto e = q.second;
119
120 if (e.err != panda::pandasm::Error::ErrorType::ERR_NONE) {
121 e.line_number = tokens.size() + 1;
122 PrintError(e, "ERROR");
123 return false;
124 }
125
126 tokens.push_back(q.first);
127 }
128
129 return true;
130 }
131
ParseProgram(panda::pandasm::Parser & parser,std::vector<std::vector<panda::pandasm::Token>> & tokens,const panda::PandArg<std::string> & input_file,panda::Expected<panda::pandasm::Program,panda::pandasm::Error> & res)132 bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda::pandasm::Token>> &tokens,
133 const panda::PandArg<std::string> &input_file,
134 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res)
135 {
136 res = parser.Parse(tokens, input_file.GetValue());
137
138 if (!res) {
139 PrintError(res.Error(), "ERROR");
140 return false;
141 }
142
143 return true;
144 }
145
DumpProgramInJson(panda::pandasm::Program & program,const panda::PandArg<std::string> & scopes_file)146 bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<std::string> &scopes_file)
147 {
148 if (!scopes_file.GetValue().empty()) {
149 std::ofstream dump_file;
150 dump_file.open(scopes_file.GetValue());
151
152 if (!dump_file) {
153 std::cerr << "Cannot write scopes into the given file." << std::endl;
154 return false;
155 }
156 dump_file << program.JsonDump();
157 }
158
159 return true;
160 }
161
EmitProgramInBinary(panda::pandasm::Program & program,panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & output_file,panda::PandArg<bool> & optimize,panda::PandArg<bool> & size_stat)162 bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
163 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
164 panda::PandArg<bool> &size_stat)
165 {
166 auto emit_debug_info = !optimize.GetValue();
167 std::map<std::string, size_t> stat;
168 std::map<std::string, size_t> *statp = size_stat.GetValue() ? &stat : nullptr;
169 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
170 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr;
171
172 if (!panda::pandasm::AsmEmitter::Emit(output_file.GetValue(), program, statp, mapsp, emit_debug_info)) {
173 std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl;
174 return false;
175 }
176
177 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
178 if (optimize.GetValue()) {
179 bool is_optimized = panda::bytecodeopt::OptimizeBytecode(&program, mapsp, output_file.GetValue());
180 if (!panda::pandasm::AsmEmitter::Emit(output_file.GetValue(), program, statp, mapsp, emit_debug_info)) {
181 std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl;
182 return false;
183 }
184 if (!is_optimized) {
185 std::cerr << "Bytecode optimizer reported internal errors" << std::endl;
186 return false;
187 }
188 }
189 #endif
190
191 if (size_stat.GetValue()) {
192 size_t total_size = 0;
193 std::cout << "Panda file size statistic:" << std::endl;
194
195 for (auto [name, size] : stat) {
196 std::cout << name << " section: " << size << std::endl;
197 total_size += size;
198 }
199
200 std::cout << "total: " << total_size << std::endl;
201 }
202
203 pa_parser.DisableTail();
204
205 return true;
206 }
207
BuildFiles(panda::pandasm::Program & program,panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & output_file,panda::PandArg<bool> & optimize,panda::PandArg<bool> & size_stat,panda::PandArg<std::string> & scopes_file)208 bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
209 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
210 panda::PandArg<bool> &size_stat, panda::PandArg<std::string> &scopes_file)
211 {
212 if (!DumpProgramInJson(program, scopes_file)) {
213 return false;
214 }
215
216 if (!EmitProgramInBinary(program, pa_parser, output_file, optimize, size_stat)) {
217 return false;
218 }
219
220 return true;
221 }
222
223 } // namespace panda::pandasm
224
main(int argc,const char * argv[])225 int main(int argc, const char *argv[])
226 {
227 panda::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)");
228 panda::PandArg<std::string> log_file("log-file", "", "(--log-file FILENAME) Set log file name");
229 panda::PandArg<std::string> scopes_file("dump-scopes", "",
230 "(--dump-scopes FILENAME) Enable dump of scopes to file");
231 panda::PandArg<bool> help("help", false, "Print this message and exit");
232 panda::PandArg<bool> size_stat("size-stat", false, "Print panda file size statistic");
233 panda::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization");
234 panda::PandArg<bool> version {"version", false,
235 "Ark version, file format version and minimum supported file format version"};
236 // tail arguments
237 panda::PandArg<std::string> input_file("INPUT_FILE", "", "Path to the source assembly code");
238 panda::PandArg<std::string> output_file("OUTPUT_FILE", "", "Path to the generated binary code");
239 panda::PandArgParser pa_parser;
240 pa_parser.Add(&verbose);
241 pa_parser.Add(&help);
242 pa_parser.Add(&log_file);
243 pa_parser.Add(&scopes_file);
244 pa_parser.Add(&size_stat);
245 pa_parser.Add(&optimize);
246 pa_parser.Add(&version);
247 pa_parser.PushBackTail(&input_file);
248 pa_parser.PushBackTail(&output_file);
249 pa_parser.EnableTail();
250
251 std::ifstream inputfile;
252
253 if (!panda::pandasm::PrepareArgs(pa_parser, input_file, output_file, log_file, help, verbose, version, inputfile,
254 argc, argv)) {
255 return 1;
256 }
257
258 LOG(DEBUG, ASSEMBLER) << "Lexical analysis:";
259
260 panda::pandasm::Lexer lexer;
261
262 std::vector<std::vector<panda::pandasm::Token>> tokens;
263
264 if (!Tokenize(lexer, tokens, inputfile)) {
265 return 1;
266 }
267
268 LOG(DEBUG, ASSEMBLER) << "parsing:";
269
270 panda::pandasm::Parser parser;
271
272 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> res;
273 if (!panda::pandasm::ParseProgram(parser, tokens, input_file, res)) {
274 return 1;
275 }
276
277 auto &program = res.Value();
278
279 auto w = parser.ShowWarnings();
280 if (!w.empty()) {
281 panda::pandasm::PrintErrors(w, "WARNING");
282 }
283
284 if (!panda::pandasm::BuildFiles(program, pa_parser, output_file, optimize, size_stat, scopes_file)) {
285 return 1;
286 }
287
288 return 0;
289 }
290