#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap) { 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") ("dont-optimize", "turn off optimizations") ("optimize-stack", "optimize stack use between basic blocks (default: on)") ("rewrite-switch", "rewrite LLVM switch to branches (default: on)") ("output-ll", opt::value(), "dump generated LLVM IR to file") ("output-bc", opt::value(), "dump generated LLVM bitcode to file") ("show-logs", "output LOG statements to stderr") ("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"; if (_varMap.count("disassemble") == 0 && _varMap.count("compile") == 0 && _varMap.count("interpret") == 0) { errorMsg = "at least one of -c, -i, -d is required"; } if (errorMsg || _varMap.count("help")) { 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) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc, argv); boost::program_options::variables_map options; parseProgramOptions(argc, argv, options); auto inputFile = options["input-file"].as(); std::ifstream ifs(inputFile); if (!ifs.is_open()) { std::cerr << "cannot open input file " << inputFile << std::endl; exit(1); } std::string src((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); boost::algorithm::trim(src); using namespace dev; bytes bytecode = fromHex(src); if (options.count("disassemble")) { std::string assembly = eth::disassemble(bytecode); std::cout << assembly << std::endl; } 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 compilerOptions; compilerOptions.dumpCFG = options.count("dump-cfg") > 0; bool optimize = options.count("dont-optimize") == 0; compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0; compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0; auto compiler = eth::jit::Compiler(compilerOptions); auto module = compiler.compile(bytecode, "main"); auto compilationEndTime = std::chrono::high_resolution_clock::now(); module->dump(); 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 (options.count("interpret")) { using namespace eth::jit; ExecutionEngine engine; eth::jit::u256 gas = initialGas; // Create random runtime data RuntimeData data; data.set(RuntimeData::Gas, gas); data.set(RuntimeData::Address, (u160)Address(1122334455667788)); data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface)); data.set(RuntimeData::Origin, (u160)Address(101010101010101010)); data.set(RuntimeData::CallValue, 0xabcd); data.set(RuntimeData::CallDataSize, 3); data.set(RuntimeData::GasPrice, 1003); data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); data.set(RuntimeData::TimeStamp, 1005); data.set(RuntimeData::Number, 1006); data.set(RuntimeData::Difficulty, 16); data.set(RuntimeData::GasLimit, 1008); data.set(RuntimeData::CodeSize, bytecode.size()); data.callData = (uint8_t*)"abc"; data.code = bytecode.data(); // BROKEN: env_* functions must be implemented & RuntimeData struct created // TODO: Do not compile module again auto result = engine.run(bytecode, &data, nullptr); return static_cast(result); } } return 0; }