From fe303b4013790e65f9618b406a3b877cdccddd08 Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Mon, 3 Nov 2014 16:04:31 +0000 Subject: [PATCH] (1) Handling evmcc options with boost::program_options. (2) Writing out .ll and .bc files --- evmcc/CMakeLists.txt | 5 +- evmcc/evmcc.cpp | 166 +++++++++++++++++++++++++++---------------- 2 files changed, 109 insertions(+), 62 deletions(-) diff --git a/evmcc/CMakeLists.txt b/evmcc/CMakeLists.txt index 230013aef..488893509 100644 --- a/evmcc/CMakeLists.txt +++ b/evmcc/CMakeLists.txt @@ -8,6 +8,7 @@ set(EXECUTABLE evmcc) add_executable(${EXECUTABLE} ${SRC_LIST}) +target_link_libraries(${EXECUTABLE} boost_program_options) target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethereum) @@ -40,8 +41,8 @@ message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) -# llvm_map_components_to_libnames(llvm_libs core support mcjit x86asmparser x86codegen) -# target_link_libraries(evmcc ${llvm_libs}) +llvm_map_components_to_libnames(llvm_libs bitwriter) +target_link_libraries(evmcc ${llvm_libs}) # end of LLVM specific commands diff --git a/evmcc/evmcc.cpp b/evmcc/evmcc.cpp index 191b87dcf..535a9aed9 100644 --- a/evmcc/evmcc.cpp +++ b/evmcc/evmcc.cpp @@ -7,6 +7,10 @@ #include #include +#include + +#include +#include #include #include @@ -15,67 +19,76 @@ #include -void show_usage() +void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap) { - // FIXME: Use arg[0] as program name? - std::cerr << "usage: evmcc (-b|-c|-d)+ \n"; -} + namespace opt = boost::program_options; + + opt::options_description explicitOpts("Allowed options"); + explicitOpts.add_options() + ("help,h", "show usage information") + ("compile,c", "compile the code to LLVM IR") + ("interpret,i", "compile the code to LLVM IR and execute") + ("gas,g", opt::value(), "set initial gas for execution") + ("disassemble,d", "dissassemble the code") + ("dump-cfg", "dump control flow graph to graphviz file") + ("optimize-stack,os", "optimize stack use between basic blocks") + ("output-ll", opt::value(), "dump generated LLVM IR to file") + ("output-bc", opt::value(), "dump generated LLVM bitcode to file") + ("verbose,V", "enable verbose output"); + + opt::options_description implicitOpts("Input files"); + implicitOpts.add_options() + ("input-file", opt::value(), "input file"); + + opt::options_description allOpts(""); + allOpts.add(explicitOpts).add(implicitOpts); + + opt::positional_options_description inputOpts; + inputOpts.add("input-file", 1); + + const char* errorMsg = nullptr; + try + { + auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts); + opt::store(parser.run(), _varMap); + opt::notify(_varMap); + } + catch (boost::program_options::error& err) + { + errorMsg = err.what(); + } + if (!errorMsg && _varMap.count("input-file") == 0) + errorMsg = "missing input file name"; -int main(int argc, char** argv) -{ - std::string input_file; - bool opt_dissassemble = false; - bool opt_show_bytes = false; - bool opt_compile = false; - bool opt_interpret = false; - bool opt_dump_graph = false; - bool opt_unknown = false; - bool opt_verbose = false; - size_t initialGas = 10000; - - for (int i = 1; i < argc; i++) + if (_varMap.count("disassemble") == 0 + && _varMap.count("compile") == 0 + && _varMap.count("interpret") == 0) { - std::string option = argv[i]; - if (option == "-b") - opt_show_bytes = true; - else if (option == "-c") - opt_compile = true; - else if (option == "-d") - opt_dissassemble = true; - else if (option == "-i") - opt_interpret = true; - else if (option == "--dump-cfg") - opt_dump_graph = true; - else if (option == "-g" && i + 1 < argc) - { - std::string gasValue = argv[++i]; - initialGas = boost::lexical_cast(gasValue); - std::cerr << "Initial gas set to " << initialGas << "\n"; - } - else if (option == "-v") - opt_verbose = true; - else if (option[0] != '-' && input_file.empty()) - input_file = option; - else - { - opt_unknown = true; - break; - } + errorMsg = "at least one of -c, -i, -d is required"; } - if (opt_unknown || - input_file.empty() || - (!opt_show_bytes && !opt_compile && !opt_dissassemble && !opt_interpret)) + if (errorMsg || _varMap.count("help")) { - show_usage(); - exit(1); + if (errorMsg) + std::cerr << "Error: " << errorMsg << std::endl; + + std::cout << "Usage: " << _argv[0] << " input-file " << std::endl + << explicitOpts << std::endl; + std::exit(errorMsg ? 1 : 0); } +} + +int main(int argc, char** argv) +{ + boost::program_options::variables_map options; + parseProgramOptions(argc, argv, options); - std::ifstream ifs(input_file); + auto inputFile = options["input-file"].as(); + std::ifstream ifs(inputFile); if (!ifs.is_open()) { - std::cerr << "cannot open file " << input_file << std::endl; + std::cerr << "cannot open input file " << inputFile << std::endl; exit(1); } @@ -88,37 +101,70 @@ int main(int argc, char** argv) bytes bytecode = fromHex(src); - if (opt_show_bytes) - std::cout << memDump(bytecode) << std::endl; - - if (opt_dissassemble) + if (options.count("disassemble")) { std::string assembly = eth::disassemble(bytecode); std::cout << assembly << std::endl; } - if (opt_compile || opt_interpret) + if (options.count("compile") || options.count("interpret")) { + size_t initialGas = 10000; + + if (options.count("gas")) + initialGas = options["gas"].as(); + auto compilationStartTime = std::chrono::high_resolution_clock::now(); - eth::jit::Compiler::Options options; - options.dumpCFG = opt_dump_graph; + eth::jit::Compiler::Options compilerOptions; + compilerOptions.dumpCFG = options.count("dump-cfg") > 0; + compilerOptions.optimizeStack = options.count("optimize-stack") > 0; - auto compiler = eth::jit::Compiler(options); + auto compiler = eth::jit::Compiler(compilerOptions); auto module = compiler.compile({bytecode.data(), bytecode.size()}); auto compilationEndTime = std::chrono::high_resolution_clock::now(); module->dump(); - if (opt_verbose) + if (options.count("output-ll")) + { + auto outputFile = options["output-ll"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + module->print(ros, nullptr); + ofs.close(); + } + + if (options.count("output-bc")) + { + auto outputFile = options["output-bc"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + llvm::WriteBitcodeToFile(module.get(), ros); + ros.flush(); + ofs.close(); + } + + + if (options.count("verbose")) { std::cerr << "*** Compilation time: " << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count() << std::endl; } - if (opt_interpret) + if (options.count("interpret")) { auto engine = eth::jit::ExecutionEngine(); u256 gas = initialGas;