Genoil
9 years ago
67 changed files with 7 additions and 8513 deletions
@ -1,19 +0,0 @@ |
|||||
cmake_policy(SET CMP0015 NEW) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
aux_source_directory(. SRC_LIST) |
|
||||
|
|
||||
include_directories(BEFORE ..) |
|
||||
include_directories(${DB_INCLUDE_DIRS}) |
|
||||
|
|
||||
set(EXECUTABLE abi) |
|
||||
|
|
||||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} ethereum) |
|
||||
|
|
||||
if (APPLE) |
|
||||
install(TARGETS ${EXECUTABLE} DESTINATION bin) |
|
||||
else() |
|
||||
eth_install_executable(${EXECUTABLE}) |
|
||||
endif() |
|
@ -1,769 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file main.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* RLP tool. |
|
||||
*/ |
|
||||
#include <fstream> |
|
||||
#include <iostream> |
|
||||
#include <boost/regex.hpp> |
|
||||
#include <boost/algorithm/string.hpp> |
|
||||
#include "../test/JsonSpiritHeaders.h" |
|
||||
#include <libdevcore/CommonIO.h> |
|
||||
#include <libdevcore/RLP.h> |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
#include <libethereum/Client.h> |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
namespace js = json_spirit; |
|
||||
|
|
||||
void help() |
|
||||
{ |
|
||||
cout |
|
||||
<< "Usage abi enc <method_name> (<arg1>, (<arg2>, ... ))" << endl |
|
||||
<< " abi enc -a <abi.json> <method_name> (<arg1>, (<arg2>, ... ))" << endl |
|
||||
<< " abi dec -a <abi.json> [ <signature> | <unique_method_name> ]" << endl |
|
||||
<< "Options:" << endl |
|
||||
<< " -a,--abi-file <filename> Specify the JSON ABI file." << endl |
|
||||
<< " -h,--help Print this help message and exit." << endl |
|
||||
<< " -V,--version Show the version and exit." << endl |
|
||||
<< "Input options:" << endl |
|
||||
<< " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, + for decimal, ' for binary." << endl |
|
||||
<< " -F,--no-format-prefix Require no input format to be prefixed." << endl |
|
||||
<< " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl |
|
||||
<< " -T,--no-typing Require no arguments to be typed." << endl |
|
||||
<< "Output options:" << endl |
|
||||
<< " -i,--index <n> Output only the nth (counting from 0) return value." << endl |
|
||||
<< " -d,--decimal All data should be displayed as decimal." << endl |
|
||||
<< " -x,--hex Display all data as hex." << endl |
|
||||
<< " -b,--binary Display all data as binary." << endl |
|
||||
<< " -p,--prefix Prefix by a base identifier." << endl |
|
||||
; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
void version() |
|
||||
{ |
|
||||
cout << "abi version " << dev::Version << endl; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
enum class Mode |
|
||||
{ |
|
||||
Encode, |
|
||||
Decode |
|
||||
}; |
|
||||
|
|
||||
enum class Encoding |
|
||||
{ |
|
||||
Auto, |
|
||||
Decimal, |
|
||||
Hex, |
|
||||
Binary, |
|
||||
}; |
|
||||
|
|
||||
enum class Tristate |
|
||||
{ |
|
||||
False = false, |
|
||||
True = true, |
|
||||
Mu |
|
||||
}; |
|
||||
|
|
||||
enum class Format |
|
||||
{ |
|
||||
Binary, |
|
||||
Hex, |
|
||||
Decimal, |
|
||||
Open, |
|
||||
Close |
|
||||
}; |
|
||||
|
|
||||
struct InvalidUserString: public Exception {}; |
|
||||
struct InvalidFormat: public Exception {}; |
|
||||
|
|
||||
enum class Base |
|
||||
{ |
|
||||
Unknown, |
|
||||
Bytes, |
|
||||
Address, |
|
||||
Int, |
|
||||
Uint, |
|
||||
Fixed |
|
||||
}; |
|
||||
|
|
||||
static const map<Base, string> s_bases = |
|
||||
{ |
|
||||
{ Base::Bytes, "bytes" }, |
|
||||
{ Base::Address, "address" }, |
|
||||
{ Base::Int, "int" }, |
|
||||
{ Base::Uint, "uint" }, |
|
||||
{ Base::Fixed, "fixed" } |
|
||||
}; |
|
||||
|
|
||||
struct EncodingPrefs |
|
||||
{ |
|
||||
Encoding e = Encoding::Auto; |
|
||||
bool prefix = true; |
|
||||
}; |
|
||||
|
|
||||
struct ABIType |
|
||||
{ |
|
||||
Base base = Base::Unknown; |
|
||||
unsigned size = 32; |
|
||||
unsigned ssize = 0; |
|
||||
vector<int> dims; |
|
||||
string name; |
|
||||
ABIType() = default; |
|
||||
ABIType(std::string const& _type, std::string const& _name): |
|
||||
name(_name) |
|
||||
{ |
|
||||
string rest; |
|
||||
for (auto const& i: s_bases) |
|
||||
if (boost::algorithm::starts_with(_type, i.second)) |
|
||||
{ |
|
||||
base = i.first; |
|
||||
rest = _type.substr(i.second.size()); |
|
||||
} |
|
||||
if (base == Base::Unknown) |
|
||||
throw InvalidFormat(); |
|
||||
boost::regex r("(\\d*)(x(\\d+))?((\\[\\d*\\])*)"); |
|
||||
boost::smatch res; |
|
||||
boost::regex_match(rest, res, r); |
|
||||
size = res[1].length() > 0 ? stoi(res[1]) : 0; |
|
||||
ssize = res[3].length() > 0 ? stoi(res[3]) : 0; |
|
||||
boost::regex r2("\\[(\\d*)\\](.*)"); |
|
||||
for (rest = res[4]; boost::regex_match(rest, res, r2); rest = res[2]) |
|
||||
dims.push_back(!res[1].length() ? -1 : stoi(res[1])); |
|
||||
} |
|
||||
|
|
||||
ABIType(std::string const& _s) |
|
||||
{ |
|
||||
if (_s.size() < 1) |
|
||||
return; |
|
||||
switch (_s[0]) |
|
||||
{ |
|
||||
case 'b': base = Base::Bytes; break; |
|
||||
case 'a': base = Base::Address; break; |
|
||||
case 'i': base = Base::Int; break; |
|
||||
case 'u': base = Base::Uint; break; |
|
||||
case 'f': base = Base::Fixed; break; |
|
||||
default: throw InvalidFormat(); |
|
||||
} |
|
||||
if (_s.size() < 2) |
|
||||
{ |
|
||||
if (base == Base::Fixed) |
|
||||
size = ssize = 16; |
|
||||
else if (base == Base::Address || base == Base::Bytes) |
|
||||
size = 0; |
|
||||
else |
|
||||
size = 32; |
|
||||
return; |
|
||||
} |
|
||||
strings d; |
|
||||
boost::algorithm::split(d, _s, boost::is_any_of("*")); |
|
||||
string s = d[0]; |
|
||||
if (s.find_first_of('x') == string::npos) |
|
||||
size = stoi(s.substr(1)); |
|
||||
else |
|
||||
{ |
|
||||
size = stoi(s.substr(1, s.find_first_of('x') - 1)); |
|
||||
ssize = stoi(s.substr(s.find_first_of('x') + 1)); |
|
||||
} |
|
||||
for (unsigned i = 1; i < d.size(); ++i) |
|
||||
if (d[i].empty()) |
|
||||
dims.push_back(-1); |
|
||||
else |
|
||||
dims.push_back(stoi(d[i])); |
|
||||
} |
|
||||
|
|
||||
string canon() const |
|
||||
{ |
|
||||
string ret; |
|
||||
switch (base) |
|
||||
{ |
|
||||
case Base::Bytes: ret = "bytes" + (size > 0 ? toString(size) : ""); break; |
|
||||
case Base::Address: ret = "address"; break; |
|
||||
case Base::Int: ret = "int" + toString(size); break; |
|
||||
case Base::Uint: ret = "uint" + toString(size); break; |
|
||||
case Base::Fixed: ret = "fixed" + toString(size) + "x" + toString(ssize); break; |
|
||||
default: throw InvalidFormat(); |
|
||||
} |
|
||||
for (int i: dims) |
|
||||
ret += "[" + ((i > -1) ? toString(i) : "") + "]"; |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
bool isBytes() const { return base == Base::Bytes && !size; } |
|
||||
|
|
||||
string render(bytes const& _data, EncodingPrefs _e) const |
|
||||
{ |
|
||||
if (base == Base::Uint || base == Base::Int) |
|
||||
{ |
|
||||
if (_e.e == Encoding::Hex) |
|
||||
return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8)))); |
|
||||
else |
|
||||
{ |
|
||||
bigint i = fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8)); |
|
||||
if (base == Base::Int && i > (bigint(1) << (size - 1))) |
|
||||
i -= (bigint(1) << size); |
|
||||
return toString(i); |
|
||||
} |
|
||||
} |
|
||||
else if (base == Base::Address) |
|
||||
{ |
|
||||
Address a = Address(h256(_data), Address::AlignRight); |
|
||||
return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a)); |
|
||||
} |
|
||||
else if (isBytes()) |
|
||||
{ |
|
||||
return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data)); |
|
||||
} |
|
||||
else if (base == Base::Bytes) |
|
||||
{ |
|
||||
bytesConstRef b(&_data); |
|
||||
b = b.cropped(0, size); |
|
||||
return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b)); |
|
||||
} |
|
||||
else |
|
||||
throw InvalidFormat(); |
|
||||
} |
|
||||
|
|
||||
bytes unrender(bytes const& _data, Format _f) const |
|
||||
{ |
|
||||
if (isBytes()) |
|
||||
{ |
|
||||
auto ret = _data; |
|
||||
while (ret.size() % 32 != 0) |
|
||||
ret.push_back(0); |
|
||||
return ret; |
|
||||
} |
|
||||
else |
|
||||
return aligned(_data, _f, 32); |
|
||||
} |
|
||||
|
|
||||
void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } } |
|
||||
void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } } |
|
||||
void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } |
|
||||
|
|
||||
bytes aligned(bytes const& _b, Format _f, unsigned _length) const |
|
||||
{ |
|
||||
bytes ret = _b; |
|
||||
while (ret.size() < _length) |
|
||||
if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary)) |
|
||||
ret.push_back(0); |
|
||||
else |
|
||||
ret.insert(ret.begin(), 0); |
|
||||
while (ret.size() > _length) |
|
||||
if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary)) |
|
||||
ret.pop_back(); |
|
||||
else |
|
||||
ret.erase(ret.begin()); |
|
||||
return ret; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) |
|
||||
{ |
|
||||
ABIType type; |
|
||||
string val; |
|
||||
if (_typing == Tristate::True || (_typing == Tristate::Mu && _arg.find(':') != string::npos)) |
|
||||
{ |
|
||||
if (_arg.find(':') == string::npos) |
|
||||
throw InvalidUserString(); |
|
||||
type = ABIType(_arg.substr(0, _arg.find(':'))); |
|
||||
val = _arg.substr(_arg.find(':') + 1); |
|
||||
} |
|
||||
else |
|
||||
val = _arg; |
|
||||
|
|
||||
if (_prefix != Tristate::False) |
|
||||
{ |
|
||||
if (val.substr(0, 2) == "0x") |
|
||||
{ |
|
||||
type.noteHexInput(val.size() - 2); |
|
||||
return make_tuple(fromHex(val), type, Format::Hex); |
|
||||
} |
|
||||
if (val.substr(0, 1) == "+") |
|
||||
{ |
|
||||
type.noteDecimalInput(); |
|
||||
return make_tuple(toCompactBigEndian(bigint(val.substr(1))), type, Format::Decimal); |
|
||||
} |
|
||||
if (val.substr(0, 1) == "'") |
|
||||
{ |
|
||||
type.noteBinaryInput(); |
|
||||
return make_tuple(asBytes(val.substr(1)), type, Format::Binary); |
|
||||
} |
|
||||
if (val == "[") |
|
||||
return make_tuple(bytes(), type, Format::Open); |
|
||||
if (val == "]") |
|
||||
return make_tuple(bytes(), type, Format::Close); |
|
||||
} |
|
||||
if (_prefix != Tristate::True) |
|
||||
{ |
|
||||
if (val.find_first_not_of("0123456789") == string::npos) |
|
||||
{ |
|
||||
type.noteDecimalInput(); |
|
||||
return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal); |
|
||||
} |
|
||||
if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos) |
|
||||
{ |
|
||||
type.noteHexInput(val.size()); |
|
||||
return make_tuple(fromHex(val), type, Format::Hex); |
|
||||
} |
|
||||
if (val == "[") |
|
||||
return make_tuple(bytes(), type, Format::Open); |
|
||||
if (val == "]") |
|
||||
return make_tuple(bytes(), type, Format::Close); |
|
||||
type.noteBinaryInput(); |
|
||||
return make_tuple(asBytes(val), type, Format::Binary); |
|
||||
} |
|
||||
throw InvalidUserString(); |
|
||||
} |
|
||||
|
|
||||
struct ExpectedAdditionalParameter: public Exception {}; |
|
||||
struct ExpectedOpen: public Exception {}; |
|
||||
struct ExpectedClose: public Exception {}; |
|
||||
|
|
||||
struct ABIMethod |
|
||||
{ |
|
||||
string name; |
|
||||
vector<ABIType> ins; |
|
||||
vector<ABIType> outs; |
|
||||
bool isConstant = false; |
|
||||
|
|
||||
// isolation *IS* documentation.
|
|
||||
|
|
||||
ABIMethod() = default; |
|
||||
|
|
||||
ABIMethod(js::mObject _o) |
|
||||
{ |
|
||||
name = _o["name"].get_str(); |
|
||||
isConstant = _o["constant"].get_bool(); |
|
||||
if (_o.count("inputs")) |
|
||||
for (auto const& i: _o["inputs"].get_array()) |
|
||||
{ |
|
||||
js::mObject a = i.get_obj(); |
|
||||
ins.push_back(ABIType(a["type"].get_str(), a["name"].get_str())); |
|
||||
} |
|
||||
if (_o.count("outputs")) |
|
||||
for (auto const& i: _o["outputs"].get_array()) |
|
||||
{ |
|
||||
js::mObject a = i.get_obj(); |
|
||||
outs.push_back(ABIType(a["type"].get_str(), a["name"].get_str())); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ABIMethod(string const& _name, vector<ABIType> const& _args) |
|
||||
{ |
|
||||
name = _name; |
|
||||
ins = _args; |
|
||||
} |
|
||||
|
|
||||
string sig() const |
|
||||
{ |
|
||||
string methodArgs; |
|
||||
for (auto const& arg: ins) |
|
||||
methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); |
|
||||
return name + "(" + methodArgs + ")"; |
|
||||
} |
|
||||
FixedHash<4> id() const { return FixedHash<4>(sha3(sig())); } |
|
||||
|
|
||||
std::string solidityDeclaration() const |
|
||||
{ |
|
||||
ostringstream ss; |
|
||||
ss << "function " << name << "("; |
|
||||
int f = 0; |
|
||||
for (ABIType const& i: ins) |
|
||||
ss << (f++ ? ", " : "") << i.canon() << " " << i.name; |
|
||||
ss << ") "; |
|
||||
if (isConstant) |
|
||||
ss << "constant "; |
|
||||
if (!outs.empty()) |
|
||||
{ |
|
||||
ss << "returns("; |
|
||||
f = 0; |
|
||||
for (ABIType const& i: outs) |
|
||||
ss << (f++ ? ", " : "") << i.canon() << " " << i.name; |
|
||||
ss << ")"; |
|
||||
} |
|
||||
return ss.str(); |
|
||||
} |
|
||||
|
|
||||
bytes encode(vector<pair<bytes, Format>> const& _params) const |
|
||||
{ |
|
||||
bytes ret = name.empty() ? bytes() : id().asBytes(); |
|
||||
bytes suffix; |
|
||||
|
|
||||
// int int[] int
|
|
||||
// example: 42 [ 1 2 3 ] 69
|
|
||||
// int[2][][3]
|
|
||||
// example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ]
|
|
||||
|
|
||||
unsigned pi = 0; |
|
||||
|
|
||||
for (ABIType const& a: ins) |
|
||||
{ |
|
||||
if (pi >= _params.size()) |
|
||||
throw ExpectedAdditionalParameter(); |
|
||||
auto put = [&]() { |
|
||||
if (a.isBytes()) |
|
||||
ret += h256(u256(_params[pi].first.size())).asBytes(); |
|
||||
suffix += a.unrender(_params[pi].first, _params[pi].second); |
|
||||
pi++; |
|
||||
if (pi >= _params.size()) |
|
||||
throw ExpectedAdditionalParameter(); |
|
||||
}; |
|
||||
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) { |
|
||||
if (addr.size() == a.dims.size()) |
|
||||
put(); |
|
||||
else |
|
||||
{ |
|
||||
if (_params[pi].second != Format::Open) |
|
||||
throw ExpectedOpen(); |
|
||||
++pi; |
|
||||
int l = a.dims[a.dims.size() - 1 - addr.size()]; |
|
||||
if (l == -1) |
|
||||
{ |
|
||||
// read ahead in params and discover the arity.
|
|
||||
unsigned depth = 0; |
|
||||
l = 0; |
|
||||
for (unsigned pi2 = pi; depth || _params[pi2].second != Format::Close;) |
|
||||
{ |
|
||||
if (_params[pi2].second == Format::Open) |
|
||||
++depth; |
|
||||
if (_params[pi2].second == Format::Close) |
|
||||
--depth; |
|
||||
if (!depth) |
|
||||
++l; |
|
||||
if (++pi2 == _params.size()) |
|
||||
throw ExpectedClose(); |
|
||||
} |
|
||||
ret += h256(u256(l)).asBytes(); |
|
||||
} |
|
||||
q *= l; |
|
||||
for (addr.push_back(0); addr.back() < l; ++addr.back()) |
|
||||
putDim(addr, q); |
|
||||
if (_params[pi].second != Format::Close) |
|
||||
throw ExpectedClose(); |
|
||||
++pi; |
|
||||
} |
|
||||
}; |
|
||||
putDim(vector<int>(), 1); |
|
||||
} |
|
||||
return ret + suffix; |
|
||||
} |
|
||||
string decode(bytes const& _data, int _index, EncodingPrefs _ep) |
|
||||
{ |
|
||||
stringstream out; |
|
||||
unsigned di = 0; |
|
||||
vector<unsigned> catDims; |
|
||||
for (ABIType const& a: outs) |
|
||||
{ |
|
||||
auto put = [&]() { |
|
||||
if (a.isBytes()) |
|
||||
{ |
|
||||
catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32))); |
|
||||
di += 32; |
|
||||
} |
|
||||
}; |
|
||||
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) { |
|
||||
if (addr.size() == a.dims.size()) |
|
||||
put(); |
|
||||
else |
|
||||
{ |
|
||||
int l = a.dims[a.dims.size() - 1 - addr.size()]; |
|
||||
if (l == -1) |
|
||||
{ |
|
||||
l = fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32)); |
|
||||
catDims.push_back(l); |
|
||||
di += 32; |
|
||||
} |
|
||||
q *= l; |
|
||||
for (addr.push_back(0); addr.back() < l; ++addr.back()) |
|
||||
putDim(addr, q); |
|
||||
} |
|
||||
}; |
|
||||
putDim(vector<int>(), 1); |
|
||||
} |
|
||||
unsigned d = 0; |
|
||||
for (ABIType const& a: outs) |
|
||||
{ |
|
||||
if (_index == -1 && out.tellp() > 0) |
|
||||
out << ", "; |
|
||||
auto put = [&]() { |
|
||||
if (a.isBytes()) |
|
||||
{ |
|
||||
out << a.render(bytesConstRef(&_data).cropped(di, catDims[d]).toBytes(), _ep); |
|
||||
di += ((catDims[d] + 31) / 32) * 32; |
|
||||
d++; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes(), _ep); |
|
||||
di += 32; |
|
||||
} |
|
||||
}; |
|
||||
function<void(vector<int>)> putDim = [&](vector<int> addr) { |
|
||||
if (addr.size() == a.dims.size()) |
|
||||
put(); |
|
||||
else |
|
||||
{ |
|
||||
out << "["; |
|
||||
addr.push_back(0); |
|
||||
int l = a.dims[a.dims.size() - 1 - (addr.size() - 1)]; |
|
||||
if (l == -1) |
|
||||
l = catDims[d++]; |
|
||||
for (addr.back() = 0; addr.back() < l; ++addr.back()) |
|
||||
{ |
|
||||
if (addr.back()) |
|
||||
out << ", "; |
|
||||
putDim(addr); |
|
||||
} |
|
||||
out << "]"; |
|
||||
} |
|
||||
}; |
|
||||
putDim(vector<int>()); |
|
||||
} |
|
||||
return out.str(); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
string canonSig(string const& _name, vector<ABIType> const& _args) |
|
||||
{ |
|
||||
try { |
|
||||
string methodArgs; |
|
||||
for (auto const& arg: _args) |
|
||||
methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); |
|
||||
return _name + "(" + methodArgs + ")"; |
|
||||
} |
|
||||
catch (...) { |
|
||||
return string(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
struct UnknownMethod: public Exception {}; |
|
||||
struct OverloadedMethod: public Exception {}; |
|
||||
|
|
||||
class ABI |
|
||||
{ |
|
||||
public: |
|
||||
ABI() = default; |
|
||||
ABI(std::string const& _json) |
|
||||
{ |
|
||||
js::mValue v; |
|
||||
js::read_string(_json, v); |
|
||||
for (auto const& i: v.get_array()) |
|
||||
{ |
|
||||
js::mObject o = i.get_obj(); |
|
||||
if (o["type"].get_str() != "function") |
|
||||
continue; |
|
||||
ABIMethod m(o); |
|
||||
m_methods[m.id()] = m; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ABIMethod method(string _nameOrSig, vector<ABIType> const& _args) const |
|
||||
{ |
|
||||
auto id = FixedHash<4>(sha3(_nameOrSig)); |
|
||||
if (!m_methods.count(id)) |
|
||||
id = FixedHash<4>(sha3(canonSig(_nameOrSig, _args))); |
|
||||
if (!m_methods.count(id)) |
|
||||
for (auto const& m: m_methods) |
|
||||
if (m.second.name == _nameOrSig) |
|
||||
{ |
|
||||
if (m_methods.count(id)) |
|
||||
throw OverloadedMethod(); |
|
||||
id = m.first; |
|
||||
} |
|
||||
if (m_methods.count(id)) |
|
||||
return m_methods.at(id); |
|
||||
throw UnknownMethod(); |
|
||||
} |
|
||||
|
|
||||
friend ostream& operator<<(ostream& _out, ABI const& _abi); |
|
||||
|
|
||||
private: |
|
||||
map<FixedHash<4>, ABIMethod> m_methods; |
|
||||
}; |
|
||||
|
|
||||
ostream& operator<<(ostream& _out, ABI const& _abi) |
|
||||
{ |
|
||||
_out << "contract {" << endl; |
|
||||
for (auto const& i: _abi.m_methods) |
|
||||
_out << " " << i.second.solidityDeclaration() << "; // " << i.first.abridged() << endl; |
|
||||
_out << "}" << endl; |
|
||||
return _out; |
|
||||
} |
|
||||
|
|
||||
void userOutput(ostream& _out, bytes const& _data, Encoding _e) |
|
||||
{ |
|
||||
switch (_e) |
|
||||
{ |
|
||||
case Encoding::Binary: |
|
||||
_out.write((char const*)_data.data(), _data.size()); |
|
||||
break; |
|
||||
default: |
|
||||
_out << toHex(_data) << endl; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template <unsigned n, class T> vector<typename std::remove_reference<decltype(get<n>(T()))>::type> retrieve(vector<T> const& _t) |
|
||||
{ |
|
||||
vector<typename std::remove_reference<decltype(get<n>(T()))>::type> ret; |
|
||||
for (T const& i: _t) |
|
||||
ret.push_back(get<n>(i)); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
int main(int argc, char** argv) |
|
||||
{ |
|
||||
Encoding encoding = Encoding::Auto; |
|
||||
Mode mode = Mode::Encode; |
|
||||
string abiFile; |
|
||||
string method; |
|
||||
Tristate formatPrefix = Tristate::Mu; |
|
||||
Tristate typePrefix = Tristate::Mu; |
|
||||
EncodingPrefs prefs; |
|
||||
bool verbose = false; |
|
||||
int outputIndex = -1; |
|
||||
vector<pair<bytes, Format>> params; |
|
||||
vector<ABIType> args; |
|
||||
string incoming; |
|
||||
|
|
||||
for (int i = 1; i < argc; ++i) |
|
||||
{ |
|
||||
string arg = argv[i]; |
|
||||
if (arg == "-h" || arg == "--help") |
|
||||
help(); |
|
||||
else if (arg == "enc" && i == 1) |
|
||||
mode = Mode::Encode; |
|
||||
else if (arg == "dec" && i == 1) |
|
||||
mode = Mode::Decode; |
|
||||
else if ((arg == "-a" || arg == "--abi") && argc > i) |
|
||||
abiFile = argv[++i]; |
|
||||
else if ((arg == "-i" || arg == "--index") && argc > i) |
|
||||
outputIndex = atoi(argv[++i]); |
|
||||
else if (arg == "-p" || arg == "--prefix") |
|
||||
prefs.prefix = true; |
|
||||
else if (arg == "-f" || arg == "--format-prefix") |
|
||||
formatPrefix = Tristate::True; |
|
||||
else if (arg == "-F" || arg == "--no-format-prefix") |
|
||||
formatPrefix = Tristate::False; |
|
||||
else if (arg == "-t" || arg == "--typing") |
|
||||
typePrefix = Tristate::True; |
|
||||
else if (arg == "-T" || arg == "--no-typing") |
|
||||
typePrefix = Tristate::False; |
|
||||
else if (arg == "-x" || arg == "--hex") |
|
||||
prefs.e = Encoding::Hex; |
|
||||
else if (arg == "-d" || arg == "--decimal" || arg == "--dec") |
|
||||
prefs.e = Encoding::Decimal; |
|
||||
else if (arg == "-b" || arg == "--binary" || arg == "--bin") |
|
||||
prefs.e = Encoding::Binary; |
|
||||
else if (arg == "-v" || arg == "--verbose") |
|
||||
verbose = true; |
|
||||
else if (arg == "-V" || arg == "--version") |
|
||||
version(); |
|
||||
else if (method.empty()) |
|
||||
method = arg; |
|
||||
else if (mode == Mode::Encode) |
|
||||
{ |
|
||||
auto u = fromUser(arg, formatPrefix, typePrefix); |
|
||||
args.push_back(get<1>(u)); |
|
||||
params.push_back(make_pair(get<0>(u), get<2>(u))); |
|
||||
} |
|
||||
else if (mode == Mode::Decode) |
|
||||
incoming += arg; |
|
||||
} |
|
||||
|
|
||||
string abiData; |
|
||||
if (!abiFile.empty()) |
|
||||
abiData = contentsString(abiFile); |
|
||||
|
|
||||
if (mode == Mode::Encode) |
|
||||
{ |
|
||||
ABIMethod m; |
|
||||
if (abiData.empty()) |
|
||||
m = ABIMethod(method, args); |
|
||||
else |
|
||||
{ |
|
||||
ABI abi(abiData); |
|
||||
if (verbose) |
|
||||
cerr << "ABI:" << endl << abi; |
|
||||
try { |
|
||||
m = abi.method(method, args); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Unknown method in ABI." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
} |
|
||||
try { |
|
||||
userOutput(cout, m.encode(params), encoding); |
|
||||
} |
|
||||
catch (ExpectedAdditionalParameter const&) |
|
||||
{ |
|
||||
cerr << "Expected additional parameter in input." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
catch (ExpectedOpen const&) |
|
||||
{ |
|
||||
cerr << "Expected open-bracket '[' in input." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
catch (ExpectedClose const&) |
|
||||
{ |
|
||||
cerr << "Expected close-bracket ']' in input." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
} |
|
||||
else if (mode == Mode::Decode) |
|
||||
{ |
|
||||
if (abiData.empty()) |
|
||||
{ |
|
||||
cerr << "Please specify an ABI file." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
ABI abi(abiData); |
|
||||
ABIMethod m; |
|
||||
if (verbose) |
|
||||
cerr << "ABI:" << endl << abi; |
|
||||
try { |
|
||||
m = abi.method(method, args); |
|
||||
} |
|
||||
catch(...) |
|
||||
{ |
|
||||
cerr << "Unknown method in ABI." << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
string encoded; |
|
||||
if (incoming == "--" || incoming.empty()) |
|
||||
for (int i = cin.get(); i != -1; i = cin.get()) |
|
||||
encoded.push_back((char)i); |
|
||||
else |
|
||||
{ |
|
||||
encoded = contentsString(incoming); |
|
||||
} |
|
||||
cout << m.decode(fromHex(boost::trim_copy(encoded)), outputIndex, prefs) << endl; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
add_custom_target(alethzero) |
|
@ -1,33 +0,0 @@ |
|||||
cmake_policy(SET CMP0015 NEW) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
aux_source_directory(. SRC_LIST) |
|
||||
|
|
||||
include_directories(BEFORE ..) |
|
||||
include_directories(${Boost_INCLUDE_DIRS}) |
|
||||
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) |
|
||||
|
|
||||
if (JSCONSOLE) |
|
||||
include_directories(${V8_INCLUDE_DIRS}) |
|
||||
endif() |
|
||||
|
|
||||
set(EXECUTABLE ethkey) |
|
||||
|
|
||||
file(GLOB HEADERS "*.h") |
|
||||
|
|
||||
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) |
|
||||
|
|
||||
add_dependencies(${EXECUTABLE} BuildInfo.h) |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} devcrypto) |
|
||||
target_link_libraries(${EXECUTABLE} ethcore) |
|
||||
|
|
||||
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) |
|
||||
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) |
|
||||
endif() |
|
||||
|
|
||||
if (APPLE) |
|
||||
install(TARGETS ${EXECUTABLE} DESTINATION bin) |
|
||||
else() |
|
||||
eth_install_executable(${EXECUTABLE}) |
|
||||
endif() |
|
@ -1,769 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file KeyAux.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* CLI module for key management. |
|
||||
*/ |
|
||||
|
|
||||
#include <thread> |
|
||||
#include <chrono> |
|
||||
#include <fstream> |
|
||||
#include <iostream> |
|
||||
#include <boost/algorithm/string.hpp> |
|
||||
#include <boost/algorithm/string/trim_all.hpp> |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
#include <libdevcore/FileSystem.h> |
|
||||
#include <libethcore/KeyManager.h> |
|
||||
#include <libethcore/ICAP.h> |
|
||||
#include <libethcore/Transaction.h> |
|
||||
#include <libdevcrypto/WordList.h> |
|
||||
#include "BuildInfo.h" |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace dev::eth; |
|
||||
using namespace boost::algorithm; |
|
||||
|
|
||||
#undef RETURN |
|
||||
|
|
||||
class BadArgument: public Exception {}; |
|
||||
|
|
||||
string getAccountPassword(KeyManager& keyManager, Address const& a) |
|
||||
{ |
|
||||
return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); |
|
||||
} |
|
||||
|
|
||||
string createPassword(std::string const& _prompt) |
|
||||
{ |
|
||||
string ret; |
|
||||
while (true) |
|
||||
{ |
|
||||
ret = getPassword(_prompt); |
|
||||
string confirm = getPassword("Please confirm the password by entering it again: "); |
|
||||
if (ret == confirm) |
|
||||
break; |
|
||||
cout << "Passwords were different. Try again." << endl; |
|
||||
} |
|
||||
return ret; |
|
||||
// cout << "Enter a hint to help you remember this password: " << flush;
|
|
||||
// cin >> hint;
|
|
||||
// return make_pair(ret, hint);
|
|
||||
} |
|
||||
|
|
||||
pair<string, string> createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string()) |
|
||||
{ |
|
||||
string pass = _pass; |
|
||||
if (pass.empty()) |
|
||||
while (true) |
|
||||
{ |
|
||||
pass = getPassword(_prompt); |
|
||||
string confirm = getPassword("Please confirm the password by entering it again: "); |
|
||||
if (pass == confirm) |
|
||||
break; |
|
||||
cout << "Passwords were different. Try again." << endl; |
|
||||
} |
|
||||
string hint = _hint; |
|
||||
if (hint.empty() && !pass.empty() && !_keyManager.haveHint(pass)) |
|
||||
{ |
|
||||
cout << "Enter a hint to help you remember this password: " << flush; |
|
||||
getline(cin, hint); |
|
||||
} |
|
||||
return make_pair(pass, hint); |
|
||||
} |
|
||||
|
|
||||
class KeyCLI |
|
||||
{ |
|
||||
public: |
|
||||
enum class OperationMode |
|
||||
{ |
|
||||
None, |
|
||||
ListBare, |
|
||||
NewBare, |
|
||||
ImportBare, |
|
||||
ExportBare, |
|
||||
RecodeBare, |
|
||||
KillBare, |
|
||||
InspectBare, |
|
||||
CreateWallet, |
|
||||
List, |
|
||||
New, |
|
||||
Import, |
|
||||
ImportWithAddress, |
|
||||
ImportPresale, |
|
||||
Export, |
|
||||
Recode, |
|
||||
Kill, |
|
||||
NewBrain, |
|
||||
ImportBrain, |
|
||||
InspectBrain, |
|
||||
SignTx, |
|
||||
DecodeTx, |
|
||||
}; |
|
||||
|
|
||||
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} |
|
||||
|
|
||||
bool interpretOption(int& i, int argc, char** argv) |
|
||||
{ |
|
||||
string arg = argv[i]; |
|
||||
if (arg == "--wallet-path" && i + 1 < argc) |
|
||||
m_walletPath = argv[++i]; |
|
||||
else if (arg == "--secrets-path" && i + 1 < argc) |
|
||||
m_secretsPath = argv[++i]; |
|
||||
else if ((arg == "-m" || arg == "--master") && i + 1 < argc) |
|
||||
m_masterPassword = argv[++i]; |
|
||||
else if (arg == "--unlock" && i + 1 < argc) |
|
||||
m_unlocks.push_back(argv[++i]); |
|
||||
else if (arg == "--lock" && i + 1 < argc) |
|
||||
m_lock = argv[++i]; |
|
||||
else if (arg == "--kdf" && i + 1 < argc) |
|
||||
m_kdf = argv[++i]; |
|
||||
else if (arg == "--kdf-param" && i + 2 < argc) |
|
||||
{ |
|
||||
auto n = argv[++i]; |
|
||||
auto v = argv[++i]; |
|
||||
m_kdfParams[n] = v; |
|
||||
} |
|
||||
else if (arg == "--sign-tx" && i + 1 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::SignTx; |
|
||||
m_signKey = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--tx-data" && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.data = fromHex(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if (arg == "--tx-nonce" && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.nonce = u256(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if ((arg == "--tx-dest" || arg == "--tx-to" || arg == "--tx-destination") && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.creation = false; |
|
||||
m_toSign.to = toAddress(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if (arg == "--tx-gas" && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.gas = u256(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if (arg == "--tx-gasprice" && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.gasPrice = u256(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if (arg == "--tx-value" && i + 1 < argc) |
|
||||
try |
|
||||
{ |
|
||||
m_toSign.value = u256(argv[++i]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Invalid argument to " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
else if (arg == "--decode-tx") |
|
||||
m_mode = OperationMode::DecodeTx; |
|
||||
else if (arg == "--import-bare") |
|
||||
m_mode = OperationMode::ImportBare; |
|
||||
else if (arg == "--list-bare") |
|
||||
m_mode = OperationMode::ListBare; |
|
||||
else if (arg == "--export-bare") |
|
||||
m_mode = OperationMode::ExportBare; |
|
||||
else if (arg == "--inspect-bare") |
|
||||
m_mode = OperationMode::InspectBare; |
|
||||
else if (arg == "--recode-bare") |
|
||||
m_mode = OperationMode::RecodeBare; |
|
||||
else if (arg == "--kill-bare") |
|
||||
m_mode = OperationMode::KillBare; |
|
||||
else if (arg == "--create-wallet") |
|
||||
m_mode = OperationMode::CreateWallet; |
|
||||
else if (arg == "-l" || arg == "--list") |
|
||||
m_mode = OperationMode::List; |
|
||||
else if ((arg == "-n" || arg == "--new") && i + 1 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::New; |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if ((arg == "-i" || arg == "--import") && i + 2 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::Import; |
|
||||
m_inputs = strings(1, argv[++i]); |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--import-presale" && i + 2 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::ImportPresale; |
|
||||
m_inputs = strings(1, argv[++i]); |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--new-brain" && i + 1 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::NewBrain; |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--import-brain" && i + 1 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::ImportBrain; |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--inspect-brain") |
|
||||
m_mode = OperationMode::InspectBrain; |
|
||||
else if (arg == "--import-with-address" && i + 3 < argc) |
|
||||
{ |
|
||||
m_mode = OperationMode::ImportWithAddress; |
|
||||
m_inputs = strings(1, argv[++i]); |
|
||||
m_address = Address(argv[++i]); |
|
||||
m_name = argv[++i]; |
|
||||
} |
|
||||
else if (arg == "--export") |
|
||||
m_mode = OperationMode::Export; |
|
||||
else if (arg == "--recode") |
|
||||
m_mode = OperationMode::Recode; |
|
||||
else if (arg == "--no-icap") |
|
||||
m_icap = false; |
|
||||
else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) |
|
||||
m_inputs.push_back(arg); |
|
||||
else |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
KeyPair makeKey() const |
|
||||
{ |
|
||||
KeyPair k(Secret::random()); |
|
||||
while (m_icap && k.address()[0]) |
|
||||
k = KeyPair(Secret(sha3(k.secret().ref()))); |
|
||||
return k; |
|
||||
} |
|
||||
|
|
||||
Secret getSecret(std::string const& _signKey) |
|
||||
{ |
|
||||
string json = contentsString(_signKey); |
|
||||
if (!json.empty()) |
|
||||
return Secret(secretStore().secret(secretStore().readKeyContent(json), [&](){ return getPassword("Enter password for key: "); })); |
|
||||
else |
|
||||
{ |
|
||||
if (h128 u = fromUUID(_signKey)) |
|
||||
return Secret(secretStore().secret(u, [&](){ return getPassword("Enter password for key: "); })); |
|
||||
if (_signKey.substr(0, 6) == "brain#" && _signKey.find(":") != string::npos) |
|
||||
return KeyManager::subkey(KeyManager::brain(_signKey.substr(_signKey.find(":"))), stoul(_signKey.substr(6, _signKey.find(":") - 7))); |
|
||||
if (_signKey.substr(0, 6) == "brain:") |
|
||||
return KeyManager::brain(_signKey.substr(6)); |
|
||||
if (_signKey == "brain") |
|
||||
return KeyManager::brain(getPassword("Enter brain wallet phrase: ")); |
|
||||
Address a; |
|
||||
DEV_IGNORE_EXCEPTIONS(a = Address(_signKey)); |
|
||||
if (a) |
|
||||
return keyManager().secret(a, [&](){ return getPassword("Enter password for key (hint:" + keyManager().passwordHint(a) + "): "); }); |
|
||||
cerr << "Bad file, UUID and address: " << _signKey << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void execute() |
|
||||
{ |
|
||||
switch (m_mode) |
|
||||
{ |
|
||||
case OperationMode::CreateWallet: |
|
||||
{ |
|
||||
KeyManager wallet(m_walletPath, m_secretsPath); |
|
||||
if (m_masterPassword.empty()) |
|
||||
m_masterPassword = createPassword("Please enter a MASTER password to protect your key store (make it strong!): "); |
|
||||
if (m_masterPassword.empty()) |
|
||||
cerr << "Aborted (empty password not allowed)." << endl; |
|
||||
else |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
wallet.create(m_masterPassword); |
|
||||
} |
|
||||
catch (Exception const& _e) |
|
||||
{ |
|
||||
cerr << "unable to create wallet" << endl << boost::diagnostic_information(_e); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::DecodeTx: |
|
||||
{ |
|
||||
bytes b = inputData(m_inputs[0]); |
|
||||
if (b.empty()) |
|
||||
cerr << "Unknown file or bad hex: '" << m_inputs[0] << "'" << endl; |
|
||||
else |
|
||||
try |
|
||||
{ |
|
||||
TransactionBase t(b, CheckTransaction::None); |
|
||||
cout << "Transaction " << t.sha3().hex() << endl; |
|
||||
if (t.isCreation()) |
|
||||
{ |
|
||||
cout << " type: creation" << endl; |
|
||||
cout << " code: " << toHex(t.data()) << endl; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
cout << " type: message" << endl; |
|
||||
cout << " to: " << t.to().hex() << endl; |
|
||||
cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl; |
|
||||
} |
|
||||
try |
|
||||
{ |
|
||||
auto s = t.sender(); |
|
||||
if (t.isCreation()) |
|
||||
cout << " creates: " << toAddress(s, t.nonce()).hex() << endl; |
|
||||
cout << " from: " << s.hex() << endl; |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cout << " from: <unsigned>" << endl; |
|
||||
} |
|
||||
cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl; |
|
||||
cout << " nonce: " << t.nonce() << endl; |
|
||||
cout << " gas: " << t.gas() << endl; |
|
||||
cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl; |
|
||||
cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl; |
|
||||
cout << " v: " << (int)t.signature().v << endl; |
|
||||
cout << " r: " << t.signature().r << endl; |
|
||||
cout << " s: " << t.signature().s << endl; |
|
||||
} |
|
||||
catch (Exception& ex) |
|
||||
{ |
|
||||
cerr << "Invalid transaction: " << ex.what() << endl; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::SignTx: |
|
||||
{ |
|
||||
Secret s = getSecret(m_signKey); |
|
||||
if (m_inputs.empty()) |
|
||||
m_inputs.push_back(string()); |
|
||||
for (string const& i: m_inputs) |
|
||||
{ |
|
||||
bool isFile = false; |
|
||||
try |
|
||||
{ |
|
||||
TransactionBase t = i.empty() ? TransactionBase(m_toSign) : TransactionBase(inputData(i, &isFile), CheckTransaction::None); |
|
||||
t.sign(s); |
|
||||
cout << t.sha3() << ": "; |
|
||||
if (isFile) |
|
||||
{ |
|
||||
writeFile(i + ".signed", toHex(t.rlp())); |
|
||||
cout << i + ".signed" << endl; |
|
||||
} |
|
||||
else |
|
||||
cout << toHex(t.rlp()) << endl; |
|
||||
} |
|
||||
catch (Exception& ex) |
|
||||
{ |
|
||||
cerr << "Invalid transaction: " << ex.what() << endl; |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::NewBrain: |
|
||||
{ |
|
||||
if (m_name != "--") |
|
||||
keyManager(); |
|
||||
boost::random_device d; |
|
||||
boost::random::uniform_int_distribution<unsigned> pickWord(0, WordList.size() - 1); |
|
||||
string seed; |
|
||||
for (int i = 0; i < 13; ++i) |
|
||||
seed += (seed.size() ? " " : "") + WordList[pickWord(d)]; |
|
||||
cout << "Your brain key phrase: <<" << seed << ">>" << endl; |
|
||||
if (m_name != "--") |
|
||||
{ |
|
||||
std::string hint; |
|
||||
cout << "Enter a hint for the phrase if you want: " << flush; |
|
||||
getline(cin, hint); |
|
||||
Address a = keyManager().importBrain(seed, m_name, hint); |
|
||||
cout << a.abridged() << endl; |
|
||||
cout << " ICAP: " << ICAP(a).encoded() << endl; |
|
||||
cout << " Address: " << a.hex() << endl; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::InspectBrain: |
|
||||
{ |
|
||||
Address a = toAddress(KeyManager::brain(getPassword("Enter brain wallet key phrase: "))); |
|
||||
cout << a.abridged() << endl; |
|
||||
cout << " ICAP: " << ICAP(a).encoded() << endl; |
|
||||
cout << " Address: " << a.hex() << endl; |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::ListBare: |
|
||||
for (h128 const& u: std::set<h128>() + secretStore().keys()) |
|
||||
cout << toUUID(u) << endl; |
|
||||
break; |
|
||||
case OperationMode::NewBare: |
|
||||
{ |
|
||||
if (m_lock.empty()) |
|
||||
m_lock = createPassword("Enter a password with which to secure this account: "); |
|
||||
auto k = makeKey(); |
|
||||
h128 u = secretStore().importSecret(k.secret().ref(), m_lock); |
|
||||
cout << "Created key " << toUUID(u) << endl; |
|
||||
cout << " Address: " << k.address().hex() << endl; |
|
||||
cout << " ICAP: " << ICAP(k.address()).encoded() << endl; |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::ImportBare: |
|
||||
for (string const& input: m_inputs) |
|
||||
{ |
|
||||
h128 u; |
|
||||
bytesSec b; |
|
||||
b.writable() = fromHex(input); |
|
||||
if (b.size() != 32) |
|
||||
{ |
|
||||
std::string s = contentsString(input); |
|
||||
b.writable() = fromHex(s); |
|
||||
if (b.size() != 32) |
|
||||
u = secretStore().importKey(input); |
|
||||
} |
|
||||
if (!u && b.size() == 32) |
|
||||
u = secretStore().importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); |
|
||||
if (!u) |
|
||||
{ |
|
||||
cerr << "Cannot import " << input << " not a file or secret." << endl; |
|
||||
continue; |
|
||||
} |
|
||||
cout << "Successfully imported " << input << " as " << toUUID(u); |
|
||||
} |
|
||||
break; |
|
||||
case OperationMode::InspectBare: |
|
||||
for (auto const& i: m_inputs) |
|
||||
if (!contents(i).empty()) |
|
||||
{ |
|
||||
h128 u = secretStore().readKey(i, false); |
|
||||
bytesSec s = secretStore().secret(u, [&](){ return getPassword("Enter password for key " + i + ": "); }); |
|
||||
cout << "Key " << i << ":" << endl; |
|
||||
cout << " UUID: " << toUUID(u) << ":" << endl; |
|
||||
cout << " Address: " << toAddress(Secret(s)).hex() << endl; |
|
||||
cout << " Secret: " << toHex(s.ref().cropped(0, 8)) << "..." << endl; |
|
||||
} |
|
||||
else if (h128 u = fromUUID(i)) |
|
||||
{ |
|
||||
bytesSec s = secretStore().secret(u, [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }); |
|
||||
cout << "Key " << i << ":" << endl; |
|
||||
cout << " Address: " << toAddress(Secret(s)).hex() << endl; |
|
||||
cout << " Secret: " << toHex(s.ref().cropped(0, 8)) << "..." << endl; |
|
||||
} |
|
||||
else |
|
||||
cerr << "Couldn't inspect " << i << "; not found." << endl; |
|
||||
break; |
|
||||
case OperationMode::ExportBare: break; |
|
||||
case OperationMode::RecodeBare: |
|
||||
for (auto const& i: m_inputs) |
|
||||
if (h128 u = fromUUID(i)) |
|
||||
if (secretStore().recode(u, lockPassword(toUUID(u)), [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }, kdf())) |
|
||||
cerr << "Re-encoded " << toUUID(u) << endl; |
|
||||
else |
|
||||
cerr << "Couldn't re-encode " << toUUID(u) << "; key corrupt or incorrect password supplied." << endl; |
|
||||
else |
|
||||
cerr << "Couldn't re-encode " << i << "; not found." << endl; |
|
||||
break; |
|
||||
case OperationMode::KillBare: |
|
||||
for (auto const& i: m_inputs) |
|
||||
if (h128 u = fromUUID(i)) |
|
||||
secretStore().kill(u); |
|
||||
else |
|
||||
cerr << "Couldn't kill " << i << "; not found." << endl; |
|
||||
break; |
|
||||
case OperationMode::New: |
|
||||
{ |
|
||||
keyManager(); |
|
||||
tie(m_lock, m_lockHint) = createPassword(keyManager(), "Enter a password with which to secure this account (or nothing to use the master password): ", m_lock, m_lockHint); |
|
||||
auto k = makeKey(); |
|
||||
bool usesMaster = m_lock.empty(); |
|
||||
h128 u = usesMaster ? keyManager().import(k.secret(), m_name) : keyManager().import(k.secret(), m_name, m_lock, m_lockHint); |
|
||||
cout << "Created key " << toUUID(u) << endl; |
|
||||
cout << " Name: " << m_name << endl; |
|
||||
if (usesMaster) |
|
||||
cout << " Uses master password." << endl; |
|
||||
else |
|
||||
cout << " Password hint: " << m_lockHint << endl; |
|
||||
cout << " Address: " << k.address().hex() << endl; |
|
||||
cout << " ICAP: " << ICAP(k.address()).encoded() << endl; |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::ImportWithAddress: |
|
||||
{ |
|
||||
keyManager(); |
|
||||
string const& i = m_inputs[0]; |
|
||||
h128 u; |
|
||||
bytesSec b; |
|
||||
b.writable() = fromHex(i); |
|
||||
if (b.size() != 32) |
|
||||
{ |
|
||||
std::string s = contentsString(i); |
|
||||
b.writable() = fromHex(s); |
|
||||
if (b.size() != 32) |
|
||||
u = keyManager().store().importKey(i); |
|
||||
} |
|
||||
if (!u && b.size() == 32) |
|
||||
u = keyManager().store().importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); |
|
||||
if (!u) |
|
||||
{ |
|
||||
cerr << "Cannot import " << i << " not a file or secret." << endl; |
|
||||
break; |
|
||||
} |
|
||||
keyManager().importExisting(u, m_name, m_address); |
|
||||
cout << "Successfully imported " << i << ":" << endl; |
|
||||
cout << " Name: " << m_name << endl; |
|
||||
cout << " Address: " << m_address << endl; |
|
||||
cout << " UUID: " << toUUID(u) << endl; |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::ImportBrain: |
|
||||
{ |
|
||||
keyManager(); |
|
||||
std::string seed = getPassword("Enter brain wallet key phrase: "); |
|
||||
std::string hint; |
|
||||
cout << "Enter a hint for the phrase if you want: " << flush; |
|
||||
getline(cin, hint); |
|
||||
Address a = keyManager().importBrain(seed, m_name, hint); |
|
||||
cout << a << endl; |
|
||||
cout << " ICAP: " << ICAP(a).encoded() << endl; |
|
||||
cout << " Address: " << a.hex() << endl; |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::ImportPresale: |
|
||||
{ |
|
||||
keyManager(); |
|
||||
std::string pw; |
|
||||
KeyPair k = keyManager().presaleSecret(contentsString(m_inputs[0]), [&](bool){ return (pw = getPassword("Enter the password for the presale key: ")); }); |
|
||||
keyManager().import(k.secret(), m_name, pw, "Same password as used for presale key"); |
|
||||
break; |
|
||||
} |
|
||||
case OperationMode::List: |
|
||||
{ |
|
||||
vector<u128> bare; |
|
||||
AddressHash got; |
|
||||
|
|
||||
for (auto const& u: keyManager().store().keys()) |
|
||||
if (Address a = keyManager().address(u)) |
|
||||
{ |
|
||||
got.insert(a); |
|
||||
cout << toUUID(u) << " " << a.abridged(); |
|
||||
string s = ICAP(a).encoded(); |
|
||||
cout << " " << s << string(35 - s.size(), ' '); |
|
||||
cout << " " << keyManager().accountName(a) << endl; |
|
||||
} |
|
||||
else |
|
||||
bare.push_back(u); |
|
||||
for (auto const& a: keyManager().accounts()) |
|
||||
if (!got.count(a)) |
|
||||
{ |
|
||||
cout << " (Brain) " << a.abridged(); |
|
||||
cout << " " << ICAP(a).encoded() << " "; |
|
||||
cout << " " << keyManager().accountName(a) << endl; |
|
||||
} |
|
||||
for (auto const& u: bare) |
|
||||
cout << toUUID(u) << " (Bare)" << endl; |
|
||||
} |
|
||||
default: break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
std::string lockPassword(std::string const& _accountName) |
|
||||
{ |
|
||||
return m_lock.empty() ? createPassword("Enter a password with which to secure account " + _accountName + ": ") : m_lock; |
|
||||
} |
|
||||
|
|
||||
static void streamHelp(ostream& _out) |
|
||||
{ |
|
||||
_out |
|
||||
<< "Secret-store (\"bare\") operation modes:" << endl |
|
||||
<< " --list-bare List all secret available in secret-store." << endl |
|
||||
<< " --new-bare Generate and output a key without interacting with wallet and dump the JSON." << endl |
|
||||
<< " --import-bare [ <file>|<secret-hex> , ... ] Import keys from given sources." << endl |
|
||||
<< " --recode-bare [ <uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl |
|
||||
<< " --inspect-bare [ <uuid>|<file> , ... ] Output information on given keys." << endl |
|
||||
// << " --export-bare [ <uuid> , ... ] Export given keys." << endl
|
|
||||
<< " --kill-bare [ <uuid> , ... ] Delete given keys." << endl |
|
||||
<< "Secret-store configuration:" << endl |
|
||||
<< " --secrets-path <path> Specify Web3 secret-store path (default: " << SecretStore::defaultPath() << ")" << endl |
|
||||
<< endl |
|
||||
<< "Wallet operating modes:" << endl |
|
||||
<< " -l,--list List all keys available in wallet." << endl |
|
||||
<< " -n,--new <name> Create a new key with given name and add it in the wallet." << endl |
|
||||
<< " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl |
|
||||
<< " --import-presale <file> <name> Import a presale wallet into a key with the given name." << endl |
|
||||
<< " --import-with-address [<uuid>|<file>|<secret-hex>] <address> <name> Import keys from given source with given address and place in wallet." << endl |
|
||||
<< " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl |
|
||||
<< " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl |
|
||||
<< "Brain wallet operating modes:" << endl |
|
||||
<< "WARNING: Brain wallets with human-generated passphrasses are highly susceptible to attack. Don't use such a thing for" << endl |
|
||||
<< "anything important." << endl |
|
||||
<< " --new-brain [ <name>|-- ] Create a new 13-word brain wallet; argument is the name or if --, do not add to wallet."<< endl |
|
||||
<< " --import-brain <name> Import your own brain wallet." << endl |
|
||||
<< " --inspect-brain Check the address of a particular brain wallet." << endl |
|
||||
<< "Wallet configuration:" << endl |
|
||||
<< " --create-wallet Create an Ethereum master wallet." << endl |
|
||||
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl |
|
||||
<< " -m, --master <password> Specify wallet (master) password." << endl |
|
||||
<< endl |
|
||||
<< "Transaction operating modes:" << endl |
|
||||
<< " -d,--decode-tx [<hex>|<file>] Decode given transaction." << endl |
|
||||
<< " -s,--sign-tx [ <address>|<uuid>|<file>|brain((#<HD-index>):<brain-phrase>) ] [ <hex>|<file> , ... ] (Re-)Sign given transaction." << endl |
|
||||
<< endl |
|
||||
<< "Encryption configuration:" << endl |
|
||||
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl |
|
||||
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl |
|
||||
// << " --cipher <ciphername> Specify cipher to use when encrypting (default: aes-128-ctr)" << endl
|
|
||||
// << " --cipher-param <name> <value> Specify a parameter for the cipher." << endl
|
|
||||
<< " --lock <password> Specify password for when encrypting a (the) key." << endl |
|
||||
<< " --hint <hint> Specify hint for the --lock password." << endl |
|
||||
<< endl |
|
||||
<< "Decryption configuration:" << endl |
|
||||
<< " --unlock <password> Specify password for a (the) key." << endl |
|
||||
<< "Key generation configuration:" << endl |
|
||||
<< " --no-icap Don't bother to make a direct-ICAP capable key." << endl |
|
||||
; |
|
||||
} |
|
||||
|
|
||||
static bytes inputData(std::string const& _input, bool* _isFile = nullptr) |
|
||||
{ |
|
||||
bytes b = fromHex(_input); |
|
||||
if (_isFile) |
|
||||
*_isFile = false; |
|
||||
if (b.empty()) |
|
||||
{ |
|
||||
if (_isFile) |
|
||||
*_isFile = true; |
|
||||
std::string s = boost::trim_copy_if(contentsString(_input), is_any_of(" \t\n")); |
|
||||
b = fromHex(s); |
|
||||
if (b.empty()) |
|
||||
b = asBytes(s); |
|
||||
} |
|
||||
return b; |
|
||||
} |
|
||||
|
|
||||
static bool isTrue(std::string const& _m) |
|
||||
{ |
|
||||
return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; |
|
||||
} |
|
||||
|
|
||||
static bool isFalse(std::string const& _m) |
|
||||
{ |
|
||||
return _m == "off" || _m == "no" || _m == "false" || _m == "0"; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
void openWallet(KeyManager& _w) |
|
||||
{ |
|
||||
while (true) |
|
||||
{ |
|
||||
if (_w.load(m_masterPassword)) |
|
||||
break; |
|
||||
if (!m_masterPassword.empty()) |
|
||||
{ |
|
||||
cout << "Password invalid. Try again." << endl; |
|
||||
m_masterPassword.clear(); |
|
||||
} |
|
||||
m_masterPassword = getPassword("Please enter your MASTER password: "); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } |
|
||||
|
|
||||
KeyManager& keyManager() |
|
||||
{ |
|
||||
if (!m_keyManager) |
|
||||
{ |
|
||||
m_keyManager.reset(new KeyManager(m_walletPath, m_secretsPath)); |
|
||||
if (m_keyManager->exists()) |
|
||||
openWallet(*m_keyManager); |
|
||||
else |
|
||||
{ |
|
||||
cerr << "Couldn't open wallet. Does it exist?" << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
} |
|
||||
return *m_keyManager; |
|
||||
} |
|
||||
|
|
||||
SecretStore& secretStore() |
|
||||
{ |
|
||||
if (m_keyManager) |
|
||||
return m_keyManager->store(); |
|
||||
if (!m_secretStore) |
|
||||
m_secretStore.reset(new SecretStore(m_secretsPath)); |
|
||||
return *m_secretStore; |
|
||||
} |
|
||||
|
|
||||
/// Where the keys are.
|
|
||||
unique_ptr<SecretStore> m_secretStore; |
|
||||
unique_ptr<KeyManager> m_keyManager; |
|
||||
|
|
||||
/// Operating mode.
|
|
||||
OperationMode m_mode; |
|
||||
|
|
||||
/// Wallet stuff
|
|
||||
string m_secretsPath = SecretStore::defaultPath(); |
|
||||
string m_walletPath = KeyManager::defaultPath(); |
|
||||
|
|
||||
/// Wallet password stuff
|
|
||||
string m_masterPassword; |
|
||||
strings m_unlocks; |
|
||||
string m_lock; |
|
||||
string m_lockHint; |
|
||||
bool m_icap = true; |
|
||||
|
|
||||
/// Creating/importing
|
|
||||
string m_name; |
|
||||
Address m_address; |
|
||||
|
|
||||
/// Importing
|
|
||||
strings m_inputs; |
|
||||
|
|
||||
/// Signing
|
|
||||
string m_signKey; |
|
||||
TransactionSkeleton m_toSign; |
|
||||
|
|
||||
string m_kdf = "scrypt"; |
|
||||
map<string, string> m_kdfParams; |
|
||||
// string m_cipher;
|
|
||||
// map<string, string> m_cipherParams;
|
|
||||
}; |
|
@ -1,84 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file main.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* Ethereum client. |
|
||||
*/ |
|
||||
|
|
||||
#include <thread> |
|
||||
#include <chrono> |
|
||||
#include <fstream> |
|
||||
#include <iostream> |
|
||||
#include <libdevcore/FileSystem.h> |
|
||||
#include <libdevcore/Log.h> |
|
||||
#include <libethcore/KeyManager.h> |
|
||||
#include "BuildInfo.h" |
|
||||
#include "KeyAux.h" |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace dev::eth; |
|
||||
|
|
||||
void help() |
|
||||
{ |
|
||||
cout |
|
||||
<< "Usage ethkey [OPTIONS]" << endl |
|
||||
<< "Options:" << endl << endl; |
|
||||
KeyCLI::streamHelp(cout); |
|
||||
cout |
|
||||
<< "General Options:" << endl |
|
||||
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl |
|
||||
<< " -V,--version Show the version and exit." << endl |
|
||||
<< " -h,--help Show this help message and exit." << endl |
|
||||
; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
void version() |
|
||||
{ |
|
||||
cout << "ethkey version " << dev::Version << endl; |
|
||||
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
int main(int argc, char** argv) |
|
||||
{ |
|
||||
KeyCLI m(KeyCLI::OperationMode::ListBare); |
|
||||
g_logVerbosity = 0; |
|
||||
|
|
||||
for (int i = 1; i < argc; ++i) |
|
||||
{ |
|
||||
string arg = argv[i]; |
|
||||
if (m.interpretOption(i, argc, argv)) {} |
|
||||
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) |
|
||||
g_logVerbosity = atoi(argv[++i]); |
|
||||
else if (arg == "-h" || arg == "--help") |
|
||||
help(); |
|
||||
else if (arg == "-V" || arg == "--version") |
|
||||
version(); |
|
||||
else |
|
||||
{ |
|
||||
cerr << "Invalid argument: " << arg << endl; |
|
||||
exit(-1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
m.execute(); |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
@ -1,19 +0,0 @@ |
|||||
cmake_policy(SET CMP0015 NEW) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
aux_source_directory(. SRC_LIST) |
|
||||
|
|
||||
include_directories(BEFORE ..) |
|
||||
include_directories(${DB_INCLUDE_DIRS}) |
|
||||
|
|
||||
set(EXECUTABLE ethvm) |
|
||||
|
|
||||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} ethereum) |
|
||||
|
|
||||
if (APPLE) |
|
||||
install(TARGETS ${EXECUTABLE} DESTINATION bin) |
|
||||
else() |
|
||||
eth_install_executable(${EXECUTABLE}) |
|
||||
endif() |
|
@ -1,224 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file main.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* EVM Execution tool. |
|
||||
*/ |
|
||||
#include <fstream> |
|
||||
#include <iostream> |
|
||||
#include <ctime> |
|
||||
#include <boost/algorithm/string.hpp> |
|
||||
#include <libdevcore/CommonIO.h> |
|
||||
#include <libdevcore/RLP.h> |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
#include <libethereum/Block.h> |
|
||||
#include <libethereum/Executive.h> |
|
||||
#include <libevm/VM.h> |
|
||||
#include <libevm/VMFactory.h> |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace eth; |
|
||||
|
|
||||
void help() |
|
||||
{ |
|
||||
cout |
|
||||
<< "Usage ethvm <options> [trace|stats|output] (<file>|--)" << endl |
|
||||
<< "Transaction options:" << endl |
|
||||
<< " --value <n> Transaction should transfer the <n> wei (default: 0)." << endl |
|
||||
<< " --gas <n> Transaction should be given <n> gas (default: block gas limit)." << endl |
|
||||
<< " --gas-price <n> Transaction's gas price' should be <n> (default: 0)." << endl |
|
||||
<< " --sender <a> Transaction sender should be <a> (default: 0000...0069)." << endl |
|
||||
<< " --origin <a> Transaction origin should be <a> (default: 0000...0069)." << endl |
|
||||
#if ETH_EVMJIT || !ETH_TRUE |
|
||||
<< endl |
|
||||
<< "VM options:" << endl |
|
||||
<< " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl |
|
||||
#endif |
|
||||
<< endl |
|
||||
<< "Options for trace:" << endl |
|
||||
<< " --flat Minimal whitespace in the JSON." << endl |
|
||||
<< " --mnemonics Show instruction mnemonics in the trace (non-standard)." << endl |
|
||||
<< endl |
|
||||
<< "General options:" << endl |
|
||||
<< " -V,--version Show the version and exit." << endl |
|
||||
<< " -h,--help Show this help message and exit." << endl; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
void version() |
|
||||
{ |
|
||||
cout << "ethvm version " << dev::Version << endl; |
|
||||
cout << "By Gav Wood, 2015." << endl; |
|
||||
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
enum class Mode |
|
||||
{ |
|
||||
Trace, |
|
||||
Statistics, |
|
||||
OutputOnly |
|
||||
}; |
|
||||
|
|
||||
int main(int argc, char** argv) |
|
||||
{ |
|
||||
string incoming = "--"; |
|
||||
|
|
||||
Mode mode = Mode::Statistics; |
|
||||
State state; |
|
||||
Address sender = Address(69); |
|
||||
Address origin = Address(69); |
|
||||
u256 value = 0; |
|
||||
u256 gas = Block().gasLimitRemaining(); |
|
||||
u256 gasPrice = 0; |
|
||||
bool styledJson = true; |
|
||||
StandardTrace st; |
|
||||
EnvInfo envInfo; |
|
||||
|
|
||||
for (int i = 1; i < argc; ++i) |
|
||||
{ |
|
||||
string arg = argv[i]; |
|
||||
if (arg == "-h" || arg == "--help") |
|
||||
help(); |
|
||||
else if (arg == "-V" || arg == "--version") |
|
||||
version(); |
|
||||
#if ETH_EVMJIT |
|
||||
else if (arg == "--vm" && i + 1 < argc) |
|
||||
{ |
|
||||
string vmKind = argv[++i]; |
|
||||
if (vmKind == "interpreter") |
|
||||
VMFactory::setKind(VMKind::Interpreter); |
|
||||
else if (vmKind == "jit") |
|
||||
VMFactory::setKind(VMKind::JIT); |
|
||||
else if (vmKind == "smart") |
|
||||
VMFactory::setKind(VMKind::Smart); |
|
||||
else |
|
||||
{ |
|
||||
cerr << "Unknown VM kind: " << vmKind << endl; |
|
||||
return -1; |
|
||||
} |
|
||||
} |
|
||||
#endif |
|
||||
else if (arg == "--mnemonics") |
|
||||
st.setShowMnemonics(); |
|
||||
else if (arg == "--flat") |
|
||||
styledJson = false; |
|
||||
else if (arg == "--value" && i + 1 < argc) |
|
||||
value = u256(argv[++i]); |
|
||||
else if (arg == "--sender" && i + 1 < argc) |
|
||||
sender = Address(argv[++i]); |
|
||||
else if (arg == "--origin" && i + 1 < argc) |
|
||||
origin = Address(argv[++i]); |
|
||||
else if (arg == "--gas" && i + 1 < argc) |
|
||||
gas = u256(argv[++i]); |
|
||||
else if (arg == "--gas-price" && i + 1 < argc) |
|
||||
gasPrice = u256(argv[++i]); |
|
||||
else if (arg == "--value" && i + 1 < argc) |
|
||||
value = u256(argv[++i]); |
|
||||
else if (arg == "--value" && i + 1 < argc) |
|
||||
value = u256(argv[++i]); |
|
||||
else if (arg == "--beneficiary" && i + 1 < argc) |
|
||||
envInfo.setBeneficiary(Address(argv[++i])); |
|
||||
else if (arg == "--number" && i + 1 < argc) |
|
||||
envInfo.setNumber(u256(argv[++i])); |
|
||||
else if (arg == "--difficulty" && i + 1 < argc) |
|
||||
envInfo.setDifficulty(u256(argv[++i])); |
|
||||
else if (arg == "--timestamp" && i + 1 < argc) |
|
||||
envInfo.setTimestamp(u256(argv[++i])); |
|
||||
else if (arg == "--gas-limit" && i + 1 < argc) |
|
||||
envInfo.setGasLimit(u256(argv[++i])); |
|
||||
else if (arg == "--value" && i + 1 < argc) |
|
||||
value = u256(argv[++i]); |
|
||||
else if (arg == "stats") |
|
||||
mode = Mode::Statistics; |
|
||||
else if (arg == "output") |
|
||||
mode = Mode::OutputOnly; |
|
||||
else if (arg == "trace") |
|
||||
mode = Mode::Trace; |
|
||||
else |
|
||||
incoming = arg; |
|
||||
} |
|
||||
|
|
||||
bytes code; |
|
||||
if (incoming == "--" || incoming.empty()) |
|
||||
for (int i = cin.get(); i != -1; i = cin.get()) |
|
||||
code.push_back((char)i); |
|
||||
else |
|
||||
code = contents(incoming); |
|
||||
bytes data = fromHex(boost::trim_copy(asString(code))); |
|
||||
if (data.empty()) |
|
||||
data = code; |
|
||||
|
|
||||
state.addBalance(sender, value); |
|
||||
Executive executive(state, envInfo); |
|
||||
ExecutionResult res; |
|
||||
executive.setResultRecipient(res); |
|
||||
Transaction t = eth::Transaction(value, gasPrice, gas, data, 0); |
|
||||
t.forceSender(sender); |
|
||||
|
|
||||
unordered_map<byte, pair<unsigned, bigint>> counts; |
|
||||
unsigned total = 0; |
|
||||
bigint memTotal; |
|
||||
auto onOp = [&](uint64_t step, Instruction inst, bigint m, bigint gasCost, bigint gas, VM* vm, ExtVMFace const* extVM) { |
|
||||
if (mode == Mode::Statistics) |
|
||||
{ |
|
||||
counts[(byte)inst].first++; |
|
||||
counts[(byte)inst].second += gasCost; |
|
||||
total++; |
|
||||
if (m > 0) |
|
||||
memTotal = m; |
|
||||
} |
|
||||
else if (mode == Mode::Trace) |
|
||||
st(step, inst, m, gasCost, gas, vm, extVM); |
|
||||
}; |
|
||||
|
|
||||
executive.initialize(t); |
|
||||
executive.create(sender, value, gasPrice, gas, &data, origin); |
|
||||
Timer timer; |
|
||||
executive.go(onOp); |
|
||||
double execTime = timer.elapsed(); |
|
||||
executive.finalize(); |
|
||||
bytes output = std::move(res.output); |
|
||||
|
|
||||
if (mode == Mode::Statistics) |
|
||||
{ |
|
||||
cout << "Gas used: " << res.gasUsed << " (+" << t.gasRequired() << " for transaction, -" << res.gasRefunded << " refunded)" << endl; |
|
||||
cout << "Output: " << toHex(output) << endl; |
|
||||
LogEntries logs = executive.logs(); |
|
||||
cout << logs.size() << " logs" << (logs.empty() ? "." : ":") << endl; |
|
||||
for (LogEntry const& l: logs) |
|
||||
{ |
|
||||
cout << " " << l.address.hex() << ": " << toHex(t.data()) << endl; |
|
||||
for (h256 const& t: l.topics) |
|
||||
cout << " " << t.hex() << endl; |
|
||||
} |
|
||||
|
|
||||
cout << total << " operations in " << execTime << " seconds." << endl; |
|
||||
cout << "Maximum memory usage: " << memTotal * 32 << " bytes" << endl; |
|
||||
cout << "Expensive operations:" << endl; |
|
||||
for (auto const& c: {Instruction::SSTORE, Instruction::SLOAD, Instruction::CALL, Instruction::CREATE, Instruction::CALLCODE, Instruction::MSTORE8, Instruction::MSTORE, Instruction::MLOAD, Instruction::SHA3}) |
|
||||
if (!!counts[(byte)c].first) |
|
||||
cout << " " << instructionInfo(c).name << " x " << counts[(byte)c].first << " (" << counts[(byte)c].second << " gas)" << endl; |
|
||||
} |
|
||||
else if (mode == Mode::Trace) |
|
||||
cout << st.json(styledJson); |
|
||||
else if (mode == Mode::OutputOnly) |
|
||||
cout << toHex(output); |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
/build/ |
|
@ -1,65 +0,0 @@ |
|||||
cmake_minimum_required(VERSION 2.8.12) |
|
||||
|
|
||||
if (${CMAKE_VERSION} VERSION_GREATER 3.0) |
|
||||
cmake_policy(SET CMP0042 OLD) # fix MACOSX_RPATH |
|
||||
cmake_policy(SET CMP0048 NEW) # allow VERSION argument in project() |
|
||||
project(EVMJIT VERSION 0.9.0.1 LANGUAGES CXX) |
|
||||
else() |
|
||||
project(EVMJIT) |
|
||||
set(EVMJIT_VERSION "0.9.0.1") |
|
||||
set(EVMJIT_VERSION_MAJOR 0) |
|
||||
set(EVMJIT_VERSION_MINOR 9) |
|
||||
set(EVMJIT_VERSION_PATCH 0) |
|
||||
set(EVMJIT_VERSION_TWEAK 1) |
|
||||
endif() |
|
||||
|
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|
||||
else() |
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") |
|
||||
endif() |
|
||||
|
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan") |
|
||||
# Do not allow unresovled symbols in shared library (default on linux) |
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") |
|
||||
endif() |
|
||||
|
|
||||
# LLVM |
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT LLVM_DIR) |
|
||||
# Workaround for Ubuntu broken LLVM package |
|
||||
find_program(LLVM3_7_CONFIG llvm-config-3.7) |
|
||||
find_program(LLVM3_8_CONFIG llvm-config-3.8) |
|
||||
if (LLVM3_7_CONFIG) |
|
||||
message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") |
|
||||
set(LLVM_CONFIG_EXEC llvm-config-3.7) |
|
||||
set(LLVM_LIB_DIR /usr/lib/llvm-3.7/lib) |
|
||||
elseif(LLVM3_8_CONFIG) |
|
||||
message(STATUS "Using llvm-3.8-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") |
|
||||
set(LLVM_CONFIG_EXEC llvm-config-3.8) |
|
||||
set(LLVM_LIB_DIR /usr/lib/llvm-3.8/lib) |
|
||||
else() |
|
||||
message(FATAL_ERROR "No LLVM package found!") |
|
||||
endif() |
|
||||
|
|
||||
execute_process(COMMAND ${LLVM_CONFIG_EXEC} --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE) |
|
||||
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") |
|
||||
set(LLVM_LIBS "-lLLVMipo -lLLVMVectorize -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMMCDisassembler -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMProfileData -lLLVMInstCombine -lLLVMInstrumentation -lLLVMTransformUtils -lLLVMipa -lLLVMMCJIT -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") |
|
||||
set(LLVM_DEFINITIONS "-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS") |
|
||||
link_directories(${LLVM_LIB_DIR}) |
|
||||
else() |
|
||||
find_package(LLVM REQUIRED CONFIG) |
|
||||
if (${LLVM_VERSION} VERSION_LESS 3.7) |
|
||||
message(FATAL_ERROR "Incompatible LLVM version ${LLVM_VERSION}") |
|
||||
endif() |
|
||||
message(STATUS "Found LLVM ${LLVM_VERSION}") |
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") |
|
||||
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo) |
|
||||
endif() |
|
||||
|
|
||||
add_subdirectory(libevmjit) |
|
||||
|
|
||||
if(EVMJIT_CPP) |
|
||||
add_subdirectory(libevmjit-cpp) |
|
||||
endif() |
|
@ -1,21 +0,0 @@ |
|||||
The MIT License (MIT) |
|
||||
|
|
||||
Copyright (c) 2015 Paweł Bylica <chfast@gmail.com> |
|
||||
|
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
||||
of this software and associated documentation files (the "Software"), to deal |
|
||||
in the Software without restriction, including without limitation the rights |
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
||||
copies of the Software, and to permit persons to whom the Software is |
|
||||
furnished to do so, subject to the following conditions: |
|
||||
|
|
||||
The above copyright notice and this permission notice shall be included in all |
|
||||
copies or substantial portions of the Software. |
|
||||
|
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||
SOFTWARE. |
|
@ -1,36 +0,0 @@ |
|||||
# The Ethereum EVM JIT |
|
||||
|
|
||||
EVM JIT is a library for just-in-time compilation of Ethereum EVM code. |
|
||||
It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client. |
|
||||
|
|
||||
## Build |
|
||||
|
|
||||
### Linux / Ubuntu |
|
||||
|
|
||||
1. Install llvm-3.5-dev package |
|
||||
1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt |
|
||||
2. For Ubuntu 14.10 using Ubuntu packages |
|
||||
2. Build library with cmake |
|
||||
1. `mkdir build && cd $_` |
|
||||
2. `cmake .. && make` |
|
||||
3. Install library |
|
||||
1. `sudo make install` |
|
||||
2. `sudo ldconfig` |
|
||||
|
|
||||
### OSX |
|
||||
|
|
||||
1. Install llvm35 |
|
||||
1. `brew install llvm35 --disable-shared --HEAD` |
|
||||
2. Build library with cmake |
|
||||
1. `mkdir build && cd $_` |
|
||||
2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make` |
|
||||
3. Install library |
|
||||
1. `make install` (with admin rights?) |
|
||||
|
|
||||
### Windows |
|
||||
|
|
||||
Ask me. |
|
||||
|
|
||||
## Options |
|
||||
|
|
||||
Options to evmjit library can be passed by environmental variable, e.g. `EVMJIT="-help" testeth --jit`. |
|
@ -1,70 +0,0 @@ |
|||||
|
|
||||
#include "stdint.h" |
|
||||
|
|
||||
#ifdef _MSC_VER |
|
||||
#define EXPORT __declspec(dllexport) |
|
||||
#define _ALLOW_KEYWORD_MACROS |
|
||||
#define noexcept throw() |
|
||||
#else |
|
||||
#define EXPORT |
|
||||
#endif |
|
||||
|
|
||||
#ifdef __cplusplus |
|
||||
extern "C" { |
|
||||
#endif |
|
||||
|
|
||||
typedef struct evmjit_i256 |
|
||||
{ |
|
||||
uint64_t words[4]; |
|
||||
} evmjit_i256; |
|
||||
|
|
||||
typedef struct evmjit_runtime_data |
|
||||
{ |
|
||||
int64_t gas; |
|
||||
int64_t gasPrice; |
|
||||
char const* callData; |
|
||||
uint64_t callDataSize; |
|
||||
evmjit_i256 address; |
|
||||
evmjit_i256 caller; |
|
||||
evmjit_i256 origin; |
|
||||
evmjit_i256 callValue; |
|
||||
evmjit_i256 coinBase; |
|
||||
evmjit_i256 difficulty; |
|
||||
evmjit_i256 gasLimit; |
|
||||
uint64_t number; |
|
||||
int64_t timestamp; |
|
||||
char const* code; |
|
||||
uint64_t codeSize; |
|
||||
evmjit_i256 codeHash; |
|
||||
} evmjit_runtime_data; |
|
||||
|
|
||||
typedef enum evmjit_return_code |
|
||||
{ |
|
||||
// Success codes
|
|
||||
Stop = 0, |
|
||||
Return = 1, |
|
||||
Suicide = 2, |
|
||||
|
|
||||
// Standard error codes
|
|
||||
OutOfGas = -1, |
|
||||
|
|
||||
// Internal error codes
|
|
||||
LLVMError = -101, |
|
||||
UnexpectedException = -111 |
|
||||
} evmjit_return_code; |
|
||||
|
|
||||
typedef struct evmjit_context evmjit_context; |
|
||||
|
|
||||
EXPORT evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env); |
|
||||
|
|
||||
EXPORT evmjit_return_code evmjit_exec(evmjit_context* _context); |
|
||||
|
|
||||
EXPORT void evmjit_destroy(evmjit_context* _context); |
|
||||
|
|
||||
|
|
||||
inline char const* evmjit_get_output(evmjit_runtime_data* _data) { return _data->callData; } |
|
||||
inline uint64_t evmjit_get_output_size(evmjit_runtime_data* _data) { return _data->callDataSize; } |
|
||||
|
|
||||
#ifdef __cplusplus |
|
||||
} |
|
||||
#endif |
|
@ -1,172 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <cstdint> |
|
||||
#include <cstring> |
|
||||
#include <functional> |
|
||||
|
|
||||
#ifdef _MSC_VER |
|
||||
#define EXPORT __declspec(dllexport) |
|
||||
#define _ALLOW_KEYWORD_MACROS |
|
||||
#define noexcept throw() |
|
||||
#else |
|
||||
#define EXPORT |
|
||||
#endif |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
using byte = uint8_t; |
|
||||
using bytes_ref = std::tuple<byte const*, size_t>; |
|
||||
|
|
||||
/// Representation of 256-bit hash value
|
|
||||
struct h256 |
|
||||
{ |
|
||||
uint64_t words[4]; |
|
||||
}; |
|
||||
|
|
||||
inline bool operator==(h256 const& _h1, h256 const& _h2) |
|
||||
{ |
|
||||
return _h1.words[0] == _h2.words[0] && |
|
||||
_h1.words[1] == _h2.words[1] && |
|
||||
_h1.words[2] == _h2.words[2] && |
|
||||
_h1.words[3] == _h2.words[3]; |
|
||||
} |
|
||||
|
|
||||
/// Representation of 256-bit value binary compatible with LLVM i256
|
|
||||
struct i256 |
|
||||
{ |
|
||||
uint64_t words[4]; |
|
||||
|
|
||||
i256() = default; |
|
||||
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); } |
|
||||
}; |
|
||||
|
|
||||
// TODO: Merge with ExecutionContext
|
|
||||
struct RuntimeData |
|
||||
{ |
|
||||
enum Index |
|
||||
{ |
|
||||
Gas, |
|
||||
GasPrice, |
|
||||
CallData, |
|
||||
CallDataSize, |
|
||||
Address, |
|
||||
Caller, |
|
||||
Origin, |
|
||||
CallValue, |
|
||||
CoinBase, |
|
||||
Difficulty, |
|
||||
GasLimit, |
|
||||
Number, |
|
||||
Timestamp, |
|
||||
Code, |
|
||||
CodeSize, |
|
||||
|
|
||||
SuicideDestAddress = Address, ///< Suicide balance destination address
|
|
||||
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
|
|
||||
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
|
|
||||
}; |
|
||||
|
|
||||
static size_t const numElements = CodeSize + 1; |
|
||||
|
|
||||
int64_t gas = 0; |
|
||||
int64_t gasPrice = 0; |
|
||||
byte const* callData = nullptr; |
|
||||
uint64_t callDataSize = 0; |
|
||||
i256 address; |
|
||||
i256 caller; |
|
||||
i256 origin; |
|
||||
i256 callValue; |
|
||||
i256 coinBase; |
|
||||
i256 difficulty; |
|
||||
i256 gasLimit; |
|
||||
uint64_t number = 0; |
|
||||
int64_t timestamp = 0; |
|
||||
byte const* code = nullptr; |
|
||||
uint64_t codeSize = 0; |
|
||||
h256 codeHash; |
|
||||
}; |
|
||||
|
|
||||
/// VM Environment (ExtVM) opaque type
|
|
||||
struct Env; |
|
||||
|
|
||||
enum class ReturnCode |
|
||||
{ |
|
||||
// Success codes
|
|
||||
Stop = 0, |
|
||||
Return = 1, |
|
||||
Suicide = 2, |
|
||||
|
|
||||
// Standard error codes
|
|
||||
OutOfGas = -1, |
|
||||
|
|
||||
// Internal error codes
|
|
||||
LLVMError = -101, |
|
||||
|
|
||||
UnexpectedException = -111, |
|
||||
|
|
||||
LinkerWorkaround = -299, |
|
||||
}; |
|
||||
|
|
||||
class ExecutionContext |
|
||||
{ |
|
||||
public: |
|
||||
ExecutionContext() = default; |
|
||||
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); } |
|
||||
ExecutionContext(ExecutionContext const&) = delete; |
|
||||
ExecutionContext& operator=(ExecutionContext const&) = delete; |
|
||||
EXPORT ~ExecutionContext() noexcept; |
|
||||
|
|
||||
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; } |
|
||||
|
|
||||
byte const* code() const { return m_data->code; } |
|
||||
uint64_t codeSize() const { return m_data->codeSize; } |
|
||||
h256 const& codeHash() const { return m_data->codeHash; } |
|
||||
|
|
||||
bytes_ref getReturnData() const; |
|
||||
|
|
||||
protected: |
|
||||
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
|
|
||||
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
|
|
||||
byte* m_memData = nullptr; |
|
||||
uint64_t m_memSize = 0; |
|
||||
uint64_t m_memCap = 0; |
|
||||
|
|
||||
public: |
|
||||
/// Reference to returned data (RETURN opcode used)
|
|
||||
bytes_ref returnData; |
|
||||
}; |
|
||||
|
|
||||
class JIT |
|
||||
{ |
|
||||
public: |
|
||||
|
|
||||
/// Ask JIT if the EVM code is ready for execution.
|
|
||||
/// Returns `true` if the EVM code has been compiled and loaded into memory.
|
|
||||
/// In this case the code can be executed without overhead.
|
|
||||
/// \param _codeHash The Keccak hash of the EVM code.
|
|
||||
EXPORT static bool isCodeReady(h256 const& _codeHash); |
|
||||
|
|
||||
/// Compile the given EVM code to machine code and make available for execution.
|
|
||||
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); |
|
||||
|
|
||||
EXPORT static ReturnCode exec(ExecutionContext& _context); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
namespace std |
|
||||
{ |
|
||||
template<> struct hash<dev::evmjit::h256> |
|
||||
{ |
|
||||
size_t operator()(dev::evmjit::h256 const& _h) const |
|
||||
{ |
|
||||
/// This implementation expects the argument to be a full 256-bit Keccak hash.
|
|
||||
/// It does nothing more than returning a slice of the input hash.
|
|
||||
return static_cast<size_t>(_h.words[0]); |
|
||||
}; |
|
||||
}; |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
set(TARGET_NAME evmjit-cpp) |
|
||||
|
|
||||
# Boost |
|
||||
find_package(Boost REQUIRED) |
|
||||
|
|
||||
set(SOURCES |
|
||||
Env.cpp |
|
||||
JitVM.cpp JitVM.h |
|
||||
Utils.h |
|
||||
) |
|
||||
source_group("" FILES ${SOURCES}) |
|
||||
|
|
||||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|
||||
else() |
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive |
|
||||
endif() |
|
||||
|
|
||||
add_library(${TARGET_NAME} STATIC ${SOURCES}) |
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") |
|
||||
|
|
||||
include_directories(../..) |
|
||||
include_directories(${Boost_INCLUDE_DIRS}) |
|
||||
|
|
||||
target_link_libraries(${TARGET_NAME} evmjit) |
|
@ -1,135 +0,0 @@ |
|||||
|
|
||||
#pragma GCC diagnostic ignored "-Wconversion" |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
#include <libevmcore/Params.h> |
|
||||
#include <libevm/ExtVMFace.h> |
|
||||
|
|
||||
#include "Utils.h" |
|
||||
|
|
||||
extern "C" |
|
||||
{ |
|
||||
#ifdef _MSC_VER |
|
||||
#define EXPORT __declspec(dllexport) |
|
||||
#else |
|
||||
#define EXPORT |
|
||||
#endif |
|
||||
|
|
||||
using namespace dev; |
|
||||
using namespace dev::eth; |
|
||||
using evmjit::i256; |
|
||||
|
|
||||
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) |
|
||||
{ |
|
||||
auto index = jit2eth(*_index); |
|
||||
auto value = _env->store(index); // Interface uses native endianness
|
|
||||
*o_value = eth2jit(value); |
|
||||
} |
|
||||
|
|
||||
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) |
|
||||
{ |
|
||||
auto index = jit2eth(*_index); |
|
||||
auto value = jit2eth(*_value); |
|
||||
|
|
||||
if (value == 0 && _env->store(index) != 0) // If delete
|
|
||||
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
|
|
||||
|
|
||||
_env->setStore(index, value); // Interface uses native endianness
|
|
||||
} |
|
||||
|
|
||||
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) |
|
||||
{ |
|
||||
auto u = _env->balance(right160(*_address)); |
|
||||
*o_value = eth2jit(u); |
|
||||
} |
|
||||
|
|
||||
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) |
|
||||
{ |
|
||||
*o_hash = _env->blockHash(jit2eth(*_number)); |
|
||||
} |
|
||||
|
|
||||
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) |
|
||||
{ |
|
||||
auto endowment = jit2eth(*_endowment); |
|
||||
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) |
|
||||
{ |
|
||||
u256 gas = *io_gas; |
|
||||
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight); |
|
||||
*io_gas = static_cast<int64_t>(gas); |
|
||||
*o_address = address; |
|
||||
} |
|
||||
else |
|
||||
*o_address = {}; |
|
||||
} |
|
||||
|
|
||||
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) |
|
||||
{ |
|
||||
CallParameters params; |
|
||||
params.value = jit2eth(*_value); |
|
||||
params.senderAddress = _env->myAddress; |
|
||||
params.receiveAddress = right160(*_receiveAddress); |
|
||||
params.codeAddress = right160(*_codeAddress); |
|
||||
params.data = {_inBeg, (size_t)_inSize}; |
|
||||
params.out = {_outBeg, (size_t)_outSize}; |
|
||||
params.onOp = {}; |
|
||||
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
|
|
||||
|
|
||||
*io_gas -= _callGas; |
|
||||
if (*io_gas < 0) |
|
||||
return false; |
|
||||
|
|
||||
if (isCall && !_env->exists(params.receiveAddress)) |
|
||||
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
|
|
||||
|
|
||||
if (params.value > 0) // value transfer
|
|
||||
{ |
|
||||
/*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); |
|
||||
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
|
|
||||
_callGas += static_cast<int64_t>(c_callStipend); // overflow possibility, but in the same time *io_gas < 0
|
|
||||
} |
|
||||
|
|
||||
if (*io_gas < 0) |
|
||||
return false; |
|
||||
|
|
||||
auto ret = false; |
|
||||
params.gas = u256{_callGas}; |
|
||||
if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024) |
|
||||
ret = _env->call(params); |
|
||||
|
|
||||
*io_gas += static_cast<int64_t>(params.gas); // it is never more than initial _callGas
|
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) |
|
||||
{ |
|
||||
auto hash = sha3({_begin, (size_t)_size}); |
|
||||
*o_hash = hash; |
|
||||
} |
|
||||
|
|
||||
EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) |
|
||||
{ |
|
||||
auto addr = right160(*_addr256); |
|
||||
auto& code = _env->codeAt(addr); |
|
||||
*o_size = code.size(); |
|
||||
return code.data(); |
|
||||
} |
|
||||
|
|
||||
EXPORT void env_log(ExtVMFace* _env, byte* _beg, uint64_t _size, h256* _topic1, h256* _topic2, h256* _topic3, h256* _topic4) |
|
||||
{ |
|
||||
dev::h256s topics; |
|
||||
|
|
||||
if (_topic1) |
|
||||
topics.push_back(*_topic1); |
|
||||
|
|
||||
if (_topic2) |
|
||||
topics.push_back(*_topic2); |
|
||||
|
|
||||
if (_topic3) |
|
||||
topics.push_back(*_topic3); |
|
||||
|
|
||||
if (_topic4) |
|
||||
topics.push_back(*_topic4); |
|
||||
|
|
||||
_env->log(std::move(topics), {_beg, (size_t)_size}); |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,77 +0,0 @@ |
|||||
|
|
||||
#pragma GCC diagnostic ignored "-Wconversion" |
|
||||
|
|
||||
#include "JitVM.h" |
|
||||
|
|
||||
#include <libdevcore/Log.h> |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
#include <libevm/VM.h> |
|
||||
#include <libevm/VMFactory.h> |
|
||||
|
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
|
|
||||
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
|
|
||||
|
|
||||
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) |
|
||||
{ |
|
||||
auto rejected = false; |
|
||||
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
|
|
||||
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
|
|
||||
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max(); |
|
||||
rejected |= _ext.envInfo().number() > std::numeric_limits<decltype(m_data.number)>::max(); |
|
||||
rejected |= _ext.envInfo().timestamp() > std::numeric_limits<decltype(m_data.timestamp)>::max(); |
|
||||
|
|
||||
if (rejected) |
|
||||
{ |
|
||||
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; |
|
||||
m_fallbackVM = VMFactory::create(VMKind::Interpreter); |
|
||||
return m_fallbackVM->execImpl(io_gas, _ext, _onOp); |
|
||||
} |
|
||||
|
|
||||
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas); |
|
||||
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); |
|
||||
m_data.callData = _ext.data.data(); |
|
||||
m_data.callDataSize = _ext.data.size(); |
|
||||
m_data.address = eth2jit(fromAddress(_ext.myAddress)); |
|
||||
m_data.caller = eth2jit(fromAddress(_ext.caller)); |
|
||||
m_data.origin = eth2jit(fromAddress(_ext.origin)); |
|
||||
m_data.callValue = eth2jit(_ext.value); |
|
||||
m_data.coinBase = eth2jit(fromAddress(_ext.envInfo().beneficiary())); |
|
||||
m_data.difficulty = eth2jit(_ext.envInfo().difficulty()); |
|
||||
m_data.gasLimit = eth2jit(_ext.envInfo().gasLimit()); |
|
||||
m_data.number = static_cast<decltype(m_data.number)>(_ext.envInfo().number()); |
|
||||
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.envInfo().timestamp()); |
|
||||
m_data.code = _ext.code.data(); |
|
||||
m_data.codeSize = _ext.code.size(); |
|
||||
m_data.codeHash = eth2jit(_ext.codeHash); |
|
||||
|
|
||||
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
|
|
||||
// JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
|
|
||||
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext)); |
|
||||
auto exitCode = evmjit::JIT::exec(m_context); |
|
||||
switch (exitCode) |
|
||||
{ |
|
||||
case evmjit::ReturnCode::Suicide: |
|
||||
_ext.suicide(right160(jit2eth(m_data.address))); |
|
||||
break; |
|
||||
|
|
||||
case evmjit::ReturnCode::OutOfGas: |
|
||||
BOOST_THROW_EXCEPTION(OutOfGas()); |
|
||||
case evmjit::ReturnCode::LinkerWorkaround: // never happens
|
|
||||
env_sload(); // but forces linker to include env_* JIT callback functions
|
|
||||
break; |
|
||||
default: |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
io_gas = m_data.gas; |
|
||||
return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)}; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <libevm/VMFace.h> |
|
||||
#include <evmjit/JIT.h> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
|
|
||||
class JitVM: public VMFace |
|
||||
{ |
|
||||
public: |
|
||||
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; |
|
||||
|
|
||||
private: |
|
||||
evmjit::RuntimeData m_data; |
|
||||
evmjit::ExecutionContext m_context; |
|
||||
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
|
|
||||
}; |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <evmjit/JIT.h> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
|
|
||||
/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
|
|
||||
inline u256 jit2eth(evmjit::i256 _i) |
|
||||
{ |
|
||||
u256 u = _i.words[3]; |
|
||||
u <<= 64; |
|
||||
u |= _i.words[2]; |
|
||||
u <<= 64; |
|
||||
u |= _i.words[1]; |
|
||||
u <<= 64; |
|
||||
u |= _i.words[0]; |
|
||||
return u; |
|
||||
} |
|
||||
|
|
||||
/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
|
|
||||
inline evmjit::i256 eth2jit(u256 _u) |
|
||||
{ |
|
||||
evmjit::i256 i; |
|
||||
i.words[0] = static_cast<uint64_t>(_u); |
|
||||
_u >>= 64; |
|
||||
i.words[1] = static_cast<uint64_t>(_u); |
|
||||
_u >>= 64; |
|
||||
i.words[2] = static_cast<uint64_t>(_u); |
|
||||
_u >>= 64; |
|
||||
i.words[3] = static_cast<uint64_t>(_u); |
|
||||
return i; |
|
||||
} |
|
||||
|
|
||||
/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
|
|
||||
inline evmjit::h256 eth2jit(h256 _u) |
|
||||
{ |
|
||||
/// Just directly copies memory
|
|
||||
return *(evmjit::h256*)&_u; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,508 +0,0 @@ |
|||||
#include "Arith256.h" |
|
||||
|
|
||||
#include <iostream> |
|
||||
#include <iomanip> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Type.h" |
|
||||
#include "Endianness.h" |
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
Arith256::Arith256(llvm::IRBuilder<>& _builder) : |
|
||||
CompilerHelper(_builder) |
|
||||
{} |
|
||||
|
|
||||
void Arith256::debug(llvm::Value* _value, char _c) |
|
||||
{ |
|
||||
if (!m_debug) |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; |
|
||||
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); |
|
||||
} |
|
||||
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)}); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getMulFunc(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.mul.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
llvm::Type* argTypes[] = {Type::Word, Type::Word}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto i64 = Type::Size; |
|
||||
auto i128 = builder.getIntNTy(128); |
|
||||
auto i256 = Type::Word; |
|
||||
auto c64 = Constant::get(64); |
|
||||
auto c128 = Constant::get(128); |
|
||||
auto c192 = Constant::get(192); |
|
||||
|
|
||||
auto x_lo = builder.CreateTrunc(x, i64, "x.lo"); |
|
||||
auto y_lo = builder.CreateTrunc(y, i64, "y.lo"); |
|
||||
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64); |
|
||||
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64); |
|
||||
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128); |
|
||||
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128); |
|
||||
|
|
||||
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128)); |
|
||||
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128)); |
|
||||
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi); |
|
||||
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128)); |
|
||||
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128)); |
|
||||
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi); |
|
||||
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128)); |
|
||||
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128)); |
|
||||
|
|
||||
auto p = builder.CreateZExt(t1, i256); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128)); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192)); |
|
||||
builder.CreateRet(p); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getMul512Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.mul.i512"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512); |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
|
|
||||
auto i128 = builder.getIntNTy(128); |
|
||||
auto i256 = Type::Word; |
|
||||
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256); |
|
||||
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256); |
|
||||
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); |
|
||||
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); |
|
||||
|
|
||||
auto mul256Func = getMulFunc(_module); |
|
||||
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo}); |
|
||||
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi}); |
|
||||
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo}); |
|
||||
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi}); |
|
||||
|
|
||||
auto p = builder.CreateZExt(t1, i512Ty); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128))); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128))); |
|
||||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256))); |
|
||||
builder.CreateRet(p); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) |
|
||||
{ |
|
||||
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
|
|
||||
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
|
|
||||
|
|
||||
auto retType = llvm::VectorType::get(_type, 2); |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto zero = llvm::ConstantInt::get(_type, 0); |
|
||||
auto one = llvm::ConstantInt::get(_type, 1); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto yArg = x->getNextNode(); |
|
||||
yArg->setName("y"); |
|
||||
|
|
||||
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); |
|
||||
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); |
|
||||
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); |
|
||||
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); |
|
||||
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); |
|
||||
|
|
||||
auto builder = llvm::IRBuilder<>{entryBB}; |
|
||||
auto yLEx = builder.CreateICmpULE(yArg, x); |
|
||||
auto r0 = x; |
|
||||
builder.CreateCondBr(yLEx, mainBB, returnBB); |
|
||||
|
|
||||
builder.SetInsertPoint(mainBB); |
|
||||
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); |
|
||||
// both y and r are non-zero
|
|
||||
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz"); |
|
||||
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); |
|
||||
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); |
|
||||
auto y0 = builder.CreateShl(yArg, i0); |
|
||||
builder.CreateBr(loopBB); |
|
||||
|
|
||||
builder.SetInsertPoint(loopBB); |
|
||||
auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); |
|
||||
auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); |
|
||||
auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); |
|
||||
auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); |
|
||||
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); |
|
||||
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
|
|
||||
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); |
|
||||
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); |
|
||||
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); |
|
||||
auto iZero = builder.CreateICmpEQ(iPhi, zero); |
|
||||
builder.CreateCondBr(iZero, returnBB, continueBB); |
|
||||
|
|
||||
builder.SetInsertPoint(continueBB); |
|
||||
auto i2 = builder.CreateNUWSub(iPhi, one); |
|
||||
auto q2 = builder.CreateShl(q1, one); |
|
||||
auto y2 = builder.CreateLShr(yPhi, one); |
|
||||
builder.CreateBr(loopBB); |
|
||||
|
|
||||
yPhi->addIncoming(y0, mainBB); |
|
||||
yPhi->addIncoming(y2, continueBB); |
|
||||
rPhi->addIncoming(r0, mainBB); |
|
||||
rPhi->addIncoming(r1, continueBB); |
|
||||
iPhi->addIncoming(i0, mainBB); |
|
||||
iPhi->addIncoming(i2, continueBB); |
|
||||
qPhi->addIncoming(zero, mainBB); |
|
||||
qPhi->addIncoming(q2, continueBB); |
|
||||
|
|
||||
builder.SetInsertPoint(returnBB); |
|
||||
auto qRet = builder.CreatePHI(_type, 2, "q.ret"); |
|
||||
qRet->addIncoming(zero, entryBB); |
|
||||
qRet->addIncoming(q1, loopBB); |
|
||||
auto rRet = builder.CreatePHI(_type, 2, "r.ret"); |
|
||||
rRet->addIncoming(r0, entryBB); |
|
||||
rRet->addIncoming(r1, loopBB); |
|
||||
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); |
|
||||
ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); |
|
||||
builder.CreateRet(ret); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.udivrem.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
return createUDivRemFunc(Type::Word, _module, funcName); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.udivrem.i512"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.udiv.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
auto udivremFunc = getUDivRem256Func(_module); |
|
||||
|
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto udivrem = builder.CreateCall(udivremFunc, {x, y}); |
|
||||
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); |
|
||||
builder.CreateRet(udiv); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) |
|
||||
{ |
|
||||
auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); |
|
||||
|
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto udivrem = builder.CreateCall(udivremFunc, {x, y}); |
|
||||
auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); |
|
||||
builder.CreateRet(r); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getURem256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.urem.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
return createURemFunc(Type::Word, _module, funcName); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getURem512Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.urem.i512"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.sdivrem.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
auto udivremFunc = getUDivRem256Func(_module); |
|
||||
|
|
||||
auto retType = llvm::VectorType::get(Type::Word, 2); |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); |
|
||||
auto xNeg = builder.CreateSub(Constant::get(0), x); |
|
||||
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); |
|
||||
|
|
||||
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); |
|
||||
auto yNeg = builder.CreateSub(Constant::get(0), y); |
|
||||
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); |
|
||||
|
|
||||
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); |
|
||||
auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); |
|
||||
auto rAbs = builder.CreateExtractElement(res, 1); |
|
||||
|
|
||||
// the remainder has the same sign as dividend
|
|
||||
auto rNeg = builder.CreateSub(Constant::get(0), rAbs); |
|
||||
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); |
|
||||
|
|
||||
auto qNeg = builder.CreateSub(Constant::get(0), qAbs); |
|
||||
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); |
|
||||
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); |
|
||||
|
|
||||
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); |
|
||||
ret = builder.CreateInsertElement(ret, r, 1); |
|
||||
builder.CreateRet(ret); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.sdiv.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
auto sdivremFunc = getSDivRem256Func(_module); |
|
||||
|
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); |
|
||||
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); |
|
||||
builder.CreateRet(q); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) |
|
||||
{ |
|
||||
static const auto funcName = "evm.srem.i256"; |
|
||||
if (auto func = _module.getFunction(funcName)) |
|
||||
return func; |
|
||||
|
|
||||
auto sdivremFunc = getSDivRem256Func(_module); |
|
||||
|
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto x = &func->getArgumentList().front(); |
|
||||
x->setName("x"); |
|
||||
auto y = x->getNextNode(); |
|
||||
y->setName("y"); |
|
||||
|
|
||||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|
||||
auto builder = llvm::IRBuilder<>{bb}; |
|
||||
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); |
|
||||
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); |
|
||||
builder.CreateRet(r); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Arith256::getExpFunc() |
|
||||
{ |
|
||||
if (!m_exp) |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {Type::Word, Type::Word}; |
|
||||
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule()); |
|
||||
m_exp->setDoesNotThrow(); |
|
||||
m_exp->setDoesNotAccessMemory(); |
|
||||
|
|
||||
auto base = &m_exp->getArgumentList().front(); |
|
||||
base->setName("base"); |
|
||||
auto exponent = base->getNextNode(); |
|
||||
exponent->setName("exponent"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
|
|
||||
// while (e != 0) {
|
|
||||
// if (e % 2 == 1)
|
|
||||
// r *= b;
|
|
||||
// b *= b;
|
|
||||
// e /= 2;
|
|
||||
// }
|
|
||||
|
|
||||
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); |
|
||||
auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); |
|
||||
auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); |
|
||||
auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); |
|
||||
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); |
|
||||
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); |
|
||||
|
|
||||
m_builder.SetInsertPoint(entryBB); |
|
||||
m_builder.CreateBr(headerBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(headerBB); |
|
||||
auto r = m_builder.CreatePHI(Type::Word, 2, "r"); |
|
||||
auto b = m_builder.CreatePHI(Type::Word, 2, "b"); |
|
||||
auto e = m_builder.CreatePHI(Type::Word, 2, "e"); |
|
||||
auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); |
|
||||
m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(bodyBB); |
|
||||
auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); |
|
||||
m_builder.CreateCondBr(eOdd, updateBB, continueBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(updateBB); |
|
||||
auto mul256Func = getMulFunc(*getModule()); |
|
||||
auto r0 = createCall(mul256Func, {r, b}); |
|
||||
m_builder.CreateBr(continueBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(continueBB); |
|
||||
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); |
|
||||
r1->addIncoming(r, bodyBB); |
|
||||
r1->addIncoming(r0, updateBB); |
|
||||
auto b1 = createCall(mul256Func, {b, b}); |
|
||||
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); |
|
||||
m_builder.CreateBr(headerBB); |
|
||||
|
|
||||
r->addIncoming(Constant::get(1), entryBB); |
|
||||
r->addIncoming(r1, continueBB); |
|
||||
b->addIncoming(base, entryBB); |
|
||||
b->addIncoming(b1, continueBB); |
|
||||
e->addIncoming(exponent, entryBB); |
|
||||
e->addIncoming(e1, continueBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(returnBB); |
|
||||
m_builder.CreateRet(r); |
|
||||
} |
|
||||
return m_exp; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) |
|
||||
{ |
|
||||
// while (e != 0) {
|
|
||||
// if (e % 2 == 1)
|
|
||||
// r *= b;
|
|
||||
// b *= b;
|
|
||||
// e /= 2;
|
|
||||
// }
|
|
||||
|
|
||||
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1)) |
|
||||
{ |
|
||||
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2)) |
|
||||
{ |
|
||||
auto b = c1->getValue(); |
|
||||
auto e = c2->getValue(); |
|
||||
auto r = llvm::APInt{256, 1}; |
|
||||
while (e != 0) |
|
||||
{ |
|
||||
if (e[0]) |
|
||||
r *= b; |
|
||||
b *= b; |
|
||||
e = e.lshr(1); |
|
||||
} |
|
||||
return Constant::get(r); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return createCall(getExpFunc(), {_arg1, _arg2}); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
extern "C" |
|
||||
{ |
|
||||
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) |
|
||||
{ |
|
||||
DLOG(JIT) << "DEBUG " << std::dec << z << ": " //<< d << c << b << a
|
|
||||
<< " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n"; |
|
||||
} |
|
||||
} |
|
@ -1,42 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "CompilerHelper.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
class Arith256 : public CompilerHelper |
|
||||
{ |
|
||||
public: |
|
||||
Arith256(llvm::IRBuilder<>& _builder); |
|
||||
|
|
||||
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); |
|
||||
|
|
||||
void debug(llvm::Value* _value, char _c); |
|
||||
|
|
||||
static llvm::Function* getMulFunc(llvm::Module& _module); |
|
||||
static llvm::Function* getMul512Func(llvm::Module& _module); |
|
||||
static llvm::Function* getUDiv256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getURem256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getURem512Func(llvm::Module& _module); |
|
||||
static llvm::Function* getUDivRem256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getSDiv256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getSRem256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getSDivRem256Func(llvm::Module& _module); |
|
||||
static llvm::Function* getUDivRem512Func(llvm::Module& _module); |
|
||||
|
|
||||
private: |
|
||||
llvm::Function* getExpFunc(); |
|
||||
|
|
||||
llvm::Function* m_exp = nullptr; |
|
||||
llvm::Function* m_debug = nullptr; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,270 +0,0 @@ |
|||||
#include "Array.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include <llvm/IR/Function.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "RuntimeManager.h" |
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
static const auto c_reallocStep = 1; |
|
||||
|
|
||||
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name) |
|
||||
{ |
|
||||
if (!m_func) |
|
||||
m_func = m_creator(); |
|
||||
|
|
||||
return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createArrayPushFunc() |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
auto value = arrayPtr->getNextNode(); |
|
||||
value->setName("value"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); |
|
||||
auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); |
|
||||
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); |
|
||||
|
|
||||
m_builder.SetInsertPoint(entryBB); |
|
||||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|
||||
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); |
|
||||
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|
||||
auto cap = m_builder.CreateLoad(capPtr, "cap"); |
|
||||
auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); |
|
||||
m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(reallocBB); |
|
||||
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); |
|
||||
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
|
|
||||
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); |
|
||||
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); |
|
||||
auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); |
|
||||
m_builder.CreateStore(newData, dataPtr); |
|
||||
m_builder.CreateStore(newCap, capPtr); |
|
||||
m_builder.CreateBr(pushBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(pushBB); |
|
||||
auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); |
|
||||
dataPhi->addIncoming(data, entryBB); |
|
||||
dataPhi->addIncoming(newData, reallocBB); |
|
||||
auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); |
|
||||
m_builder.CreateStore(value, newElemPtr); |
|
||||
auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); |
|
||||
m_builder.CreateStore(newSize, sizePtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createArraySetFunc() |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
auto index = arrayPtr->getNextNode(); |
|
||||
index->setName("index"); |
|
||||
auto value = index->getNextNode(); |
|
||||
value->setName("value"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|
||||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); |
|
||||
m_builder.CreateStore(value, valuePtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createArrayGetFunc() |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
auto index = arrayPtr->getNextNode(); |
|
||||
index->setName("index"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|
||||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); |
|
||||
auto value = m_builder.CreateLoad(valuePtr, "value"); |
|
||||
m_builder.CreateRet(value); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createGetPtrFunc() |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
auto index = arrayPtr->getNextNode(); |
|
||||
index->setName("index"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|
||||
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr"); |
|
||||
auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr"); |
|
||||
m_builder.CreateRet(wordPtr); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createFreeFunc() |
|
||||
{ |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "ext_free", getModule()); |
|
||||
freeFunc->setDoesNotThrow(); |
|
||||
freeFunc->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|
||||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); |
|
||||
m_builder.CreateCall(freeFunc, mem); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::getReallocFunc() |
|
||||
{ |
|
||||
if (auto func = getModule()->getFunction("ext_realloc")) |
|
||||
return func; |
|
||||
|
|
||||
llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; |
|
||||
auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "ext_realloc", getModule()); |
|
||||
reallocFunc->setDoesNotThrow(); |
|
||||
reallocFunc->setDoesNotAlias(0); |
|
||||
reallocFunc->setDoesNotCapture(1); |
|
||||
return reallocFunc; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Array::createExtendFunc() |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|
||||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
func->setDoesNotCapture(1); |
|
||||
|
|
||||
auto arrayPtr = &func->getArgumentList().front(); |
|
||||
arrayPtr->setName("arrayPtr"); |
|
||||
auto newSize = arrayPtr->getNextNode(); |
|
||||
newSize->setName("newSize"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|
||||
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
|
|
||||
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); |
|
||||
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); |
|
||||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|
||||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|
||||
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); |
|
||||
auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null
|
|
||||
auto extPtr = m_builder.CreateGEP(newData, size, "extPtr"); |
|
||||
m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16); |
|
||||
m_builder.CreateStore(newData, dataPtr); |
|
||||
m_builder.CreateStore(newSize, sizePtr); |
|
||||
m_builder.CreateStore(newSize, capPtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Type* Array::getType() |
|
||||
{ |
|
||||
llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; |
|
||||
static auto arrayTy = llvm::StructType::create(elementTys, "Array"); |
|
||||
return arrayTy; |
|
||||
} |
|
||||
|
|
||||
Array::Array(llvm::IRBuilder<>& _builder, char const* _name) : |
|
||||
CompilerHelper(_builder) |
|
||||
{ |
|
||||
m_array = m_builder.CreateAlloca(getType(), nullptr, _name); |
|
||||
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); |
|
||||
} |
|
||||
|
|
||||
Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) : |
|
||||
CompilerHelper(_builder), |
|
||||
m_array(_array) |
|
||||
{ |
|
||||
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Array::pop(llvm::Value* _count) |
|
||||
{ |
|
||||
auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); |
|
||||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|
||||
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); |
|
||||
m_builder.CreateStore(newSize, sizePtr); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Array::size(llvm::Value* _array) |
|
||||
{ |
|
||||
auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); |
|
||||
return m_builder.CreateLoad(sizePtr, "array.size"); |
|
||||
} |
|
||||
|
|
||||
void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) |
|
||||
{ |
|
||||
assert(_arrayPtr->getType() == m_array->getType()); |
|
||||
assert(_size->getType() == Type::Size); |
|
||||
m_extendFunc.call(m_builder, {_arrayPtr, _size}); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
extern "C" |
|
||||
{ |
|
||||
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept |
|
||||
{ |
|
||||
return std::realloc(_data, _size); |
|
||||
} |
|
||||
|
|
||||
EXPORT void ext_free(void* _data) noexcept |
|
||||
{ |
|
||||
std::free(_data); |
|
||||
} |
|
||||
} |
|
@ -1,72 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <functional> |
|
||||
|
|
||||
#include "CompilerHelper.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
class LazyFunction |
|
||||
{ |
|
||||
public: |
|
||||
using Creator = std::function<llvm::Function*()>; |
|
||||
|
|
||||
LazyFunction(Creator _creator) : |
|
||||
m_creator(_creator) |
|
||||
{} |
|
||||
|
|
||||
llvm::Value* call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name = ""); |
|
||||
|
|
||||
private: |
|
||||
llvm::Function* m_func = nullptr; |
|
||||
Creator m_creator; |
|
||||
}; |
|
||||
|
|
||||
class Array : public CompilerHelper |
|
||||
{ |
|
||||
public: |
|
||||
Array(llvm::IRBuilder<>& _builder, char const* _name); |
|
||||
Array(llvm::IRBuilder<>& _builder, llvm::Value* _array); |
|
||||
|
|
||||
void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } |
|
||||
void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); } |
|
||||
llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } |
|
||||
void pop(llvm::Value* _count); |
|
||||
llvm::Value* size(llvm::Value* _array = nullptr); |
|
||||
void free() { m_freeFunc.call(m_builder, {m_array}); } |
|
||||
|
|
||||
void extend(llvm::Value* _arrayPtr, llvm::Value* _size); |
|
||||
llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) { return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); } |
|
||||
|
|
||||
llvm::Value* getPointerTo() const { return m_array; } |
|
||||
|
|
||||
static llvm::Type* getType(); |
|
||||
|
|
||||
private: |
|
||||
llvm::Value* m_array = nullptr; |
|
||||
|
|
||||
llvm::Function* createArrayPushFunc(); |
|
||||
llvm::Function* createArraySetFunc(); |
|
||||
llvm::Function* createArrayGetFunc(); |
|
||||
llvm::Function* createGetPtrFunc(); |
|
||||
llvm::Function* createFreeFunc(); |
|
||||
llvm::Function* createExtendFunc(); |
|
||||
llvm::Function* getReallocFunc(); |
|
||||
|
|
||||
LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; |
|
||||
LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }}; |
|
||||
LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }}; |
|
||||
LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }}; |
|
||||
LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }}; |
|
||||
LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }}; |
|
||||
LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }}; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,148 +0,0 @@ |
|||||
#include "BasicBlock.h" |
|
||||
|
|
||||
#include <iostream> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/CFG.h> |
|
||||
#include <llvm/IR/Function.h> |
|
||||
#include <llvm/IR/Instructions.h> |
|
||||
#include <llvm/IR/IRBuilder.h> |
|
||||
#include <llvm/Support/raw_os_ostream.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Type.h" |
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): |
|
||||
m_firstInstrIdx{_firstInstrIdx}, |
|
||||
m_begin(_begin), |
|
||||
m_end(_end), |
|
||||
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc)) |
|
||||
{} |
|
||||
|
|
||||
LocalStack::LocalStack(Stack& _globalStack): |
|
||||
m_global(_globalStack) |
|
||||
{} |
|
||||
|
|
||||
void LocalStack::push(llvm::Value* _value) |
|
||||
{ |
|
||||
assert(_value->getType() == Type::Word); |
|
||||
m_local.push_back(_value); |
|
||||
m_maxSize = std::max(m_maxSize, size()); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* LocalStack::pop() |
|
||||
{ |
|
||||
auto item = get(0); |
|
||||
assert(!m_local.empty() || !m_input.empty()); |
|
||||
|
|
||||
if (m_local.size() > 0) |
|
||||
m_local.pop_back(); |
|
||||
else |
|
||||
++m_globalPops; |
|
||||
|
|
||||
m_minSize = std::min(m_minSize, size()); |
|
||||
return item; |
|
||||
} |
|
||||
|
|
||||
/**
|
|
||||
* Pushes a copy of _index-th element (tos is 0-th elem). |
|
||||
*/ |
|
||||
void LocalStack::dup(size_t _index) |
|
||||
{ |
|
||||
auto val = get(_index); |
|
||||
push(val); |
|
||||
} |
|
||||
|
|
||||
/**
|
|
||||
* Swaps tos with _index-th element (tos is 0-th elem). |
|
||||
* _index must be > 0. |
|
||||
*/ |
|
||||
void LocalStack::swap(size_t _index) |
|
||||
{ |
|
||||
assert(_index > 0); |
|
||||
auto val = get(_index); |
|
||||
auto tos = get(0); |
|
||||
set(_index, tos); |
|
||||
set(0, val); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* LocalStack::get(size_t _index) |
|
||||
{ |
|
||||
if (_index < m_local.size()) |
|
||||
return *(m_local.rbegin() + _index); // count from back
|
|
||||
|
|
||||
auto idx = _index - m_local.size() + m_globalPops; |
|
||||
if (idx >= m_input.size()) |
|
||||
m_input.resize(idx + 1); |
|
||||
auto& item = m_input[idx]; |
|
||||
|
|
||||
if (!item) |
|
||||
{ |
|
||||
item = m_global.get(idx); // Reach an item from global stack
|
|
||||
m_minSize = std::min(m_minSize, -static_cast<ssize_t>(idx) - 1); // and remember required stack size
|
|
||||
} |
|
||||
|
|
||||
return item; |
|
||||
} |
|
||||
|
|
||||
void LocalStack::set(size_t _index, llvm::Value* _word) |
|
||||
{ |
|
||||
if (_index < m_local.size()) |
|
||||
{ |
|
||||
*(m_local.rbegin() + _index) = _word; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
auto idx = _index - m_local.size() + m_globalPops; |
|
||||
assert(idx < m_input.size()); |
|
||||
m_input[idx] = _word; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) |
|
||||
{ |
|
||||
auto blockTerminator = _bb.getTerminator(); |
|
||||
if (!blockTerminator || blockTerminator->getOpcode() != llvm::Instruction::Ret) |
|
||||
{ |
|
||||
// Not needed in case of ret instruction. Ret invalidates the stack.
|
|
||||
if (blockTerminator) |
|
||||
_builder.SetInsertPoint(blockTerminator); |
|
||||
else |
|
||||
_builder.SetInsertPoint(&_bb); |
|
||||
|
|
||||
// Update items fetched from global stack ignoring the poped ones
|
|
||||
assert(m_globalPops <= m_input.size()); // pop() always does get()
|
|
||||
for (auto i = m_globalPops; i < m_input.size(); ++i) |
|
||||
{ |
|
||||
if (m_input[i]) |
|
||||
m_global.set(i, m_input[i]); |
|
||||
} |
|
||||
|
|
||||
// Add new items
|
|
||||
auto pops = m_globalPops; // Copy pops counter to keep original value
|
|
||||
for (auto& item: m_local) |
|
||||
{ |
|
||||
if (pops) // Override poped global items
|
|
||||
m_global.set(--pops, item); // using pops counter as the index
|
|
||||
else |
|
||||
m_global.push(item); |
|
||||
} |
|
||||
|
|
||||
// Pop not overriden items
|
|
||||
if (pops) |
|
||||
m_global.pop(pops); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,86 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <vector> |
|
||||
|
|
||||
#include "Common.h" |
|
||||
#include "Stack.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
using namespace evmjit; |
|
||||
using instr_idx = uint64_t; |
|
||||
|
|
||||
class LocalStack |
|
||||
{ |
|
||||
public: |
|
||||
explicit LocalStack(Stack& _globalStack); |
|
||||
LocalStack(LocalStack const&) = delete; |
|
||||
void operator=(LocalStack const&) = delete; |
|
||||
|
|
||||
/// Pushes value on stack
|
|
||||
void push(llvm::Value* _value); |
|
||||
|
|
||||
/// Pops and returns top value
|
|
||||
llvm::Value* pop(); |
|
||||
|
|
||||
/// Duplicates _index'th value on stack
|
|
||||
void dup(size_t _index); |
|
||||
|
|
||||
/// Swaps _index'th value on stack with a value on stack top.
|
|
||||
/// @param _index Index of value to be swaped. Must be > 0.
|
|
||||
void swap(size_t _index); |
|
||||
|
|
||||
ssize_t size() const { return static_cast<ssize_t>(m_local.size()) - static_cast<ssize_t>(m_globalPops); } |
|
||||
ssize_t minSize() const { return m_minSize; } |
|
||||
ssize_t maxSize() const { return m_maxSize; } |
|
||||
|
|
||||
/// Finalize local stack: check the requirements and update of the global stack.
|
|
||||
void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb); |
|
||||
|
|
||||
private: |
|
||||
/// Gets _index'th value from top (counting from 0)
|
|
||||
llvm::Value* get(size_t _index); |
|
||||
|
|
||||
/// Sets _index'th value from top (counting from 0)
|
|
||||
void set(size_t _index, llvm::Value* _value); |
|
||||
|
|
||||
/// Items fetched from global stack. First element matches the top of the global stack.
|
|
||||
/// Can contain nulls if some items has been skipped.
|
|
||||
std::vector<llvm::Value*> m_input; |
|
||||
|
|
||||
/// Local stack items that has not been pushed to global stack. First item is just above global stack.
|
|
||||
std::vector<llvm::Value*> m_local; |
|
||||
|
|
||||
Stack& m_global; ///< Reference to global stack.
|
|
||||
|
|
||||
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
|
|
||||
ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative.
|
|
||||
ssize_t m_maxSize = 0; ///< Maximum reached local stack size.
|
|
||||
}; |
|
||||
|
|
||||
class BasicBlock |
|
||||
{ |
|
||||
public: |
|
||||
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); |
|
||||
|
|
||||
llvm::BasicBlock* llvm() { return m_llvmBB; } |
|
||||
|
|
||||
instr_idx firstInstrIdx() const { return m_firstInstrIdx; } |
|
||||
code_iterator begin() const { return m_begin; } |
|
||||
code_iterator end() const { return m_end; } |
|
||||
|
|
||||
private: |
|
||||
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
|
|
||||
code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
|
|
||||
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
|
|
||||
|
|
||||
llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock
|
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,9 +0,0 @@ |
|||||
|
|
||||
#define EVMJIT_VERSION "${EVMJIT_VERSION}" |
|
||||
#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR} |
|
||||
#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR} |
|
||||
#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH} |
|
||||
|
|
||||
#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}" |
|
||||
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}" |
|
||||
#define LLVM_DEBUG ${LLVM_DEBUG} |
|
@ -1,58 +0,0 @@ |
|||||
set(TARGET_NAME evmjit) |
|
||||
|
|
||||
get_filename_component(EVMJIT_INCLUDE_DIR ../include ABSOLUTE) |
|
||||
|
|
||||
set(SOURCES |
|
||||
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h |
|
||||
JIT-c.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT-c.h |
|
||||
Arith256.cpp Arith256.h |
|
||||
Array.cpp Array.h |
|
||||
BasicBlock.cpp BasicBlock.h |
|
||||
Cache.cpp Cache.h |
|
||||
Common.h |
|
||||
Compiler.cpp Compiler.h |
|
||||
CompilerHelper.cpp CompilerHelper.h |
|
||||
Endianness.cpp Endianness.h |
|
||||
ExecStats.cpp ExecStats.h |
|
||||
Ext.cpp Ext.h |
|
||||
GasMeter.cpp GasMeter.h |
|
||||
Instruction.cpp Instruction.h |
|
||||
Memory.cpp Memory.h |
|
||||
Optimizer.cpp Optimizer.h |
|
||||
RuntimeManager.cpp RuntimeManager.h |
|
||||
Stack.cpp Stack.h |
|
||||
Type.cpp Type.h |
|
||||
Utils.cpp Utils.h |
|
||||
) |
|
||||
source_group("" FILES ${SOURCES}) |
|
||||
|
|
||||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|
||||
else() |
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") |
|
||||
endif() |
|
||||
|
|
||||
if(${EVMJIT_VERSION_MAJOR} EQUAL 0) |
|
||||
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}") |
|
||||
else() |
|
||||
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR}) |
|
||||
endif() |
|
||||
|
|
||||
|
|
||||
string(COMPARE EQUAL "${LLVM_ENABLE_ASSERTIONS}" "ON" LLVM_DEBUG) |
|
||||
configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h) |
|
||||
|
|
||||
message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH}") |
|
||||
|
|
||||
add_library(${TARGET_NAME} SHARED ${SOURCES} gen/BuildInfo.gen.h) |
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES |
|
||||
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} |
|
||||
FOLDER "libs") |
|
||||
|
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${EVMJIT_INCLUDE_DIR}) |
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${LLVM_INCLUDE_DIRS}) |
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen) |
|
||||
target_compile_definitions(${TARGET_NAME} PRIVATE ${LLVM_DEFINITIONS}) |
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) |
|
||||
|
|
||||
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) |
|
||||
install(DIRECTORY ${EVMJIT_INCLUDE_DIR} DESTINATION include) |
|
@ -1,190 +0,0 @@ |
|||||
#include "Cache.h" |
|
||||
|
|
||||
#include <mutex> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include <llvm/IR/LLVMContext.h> |
|
||||
#include <llvm/IR/Instructions.h> |
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h> |
|
||||
#include <llvm/Support/Path.h> |
|
||||
#include <llvm/Support/FileSystem.h> |
|
||||
#include <llvm/Support/raw_os_ostream.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "ExecStats.h" |
|
||||
#include "Utils.h" |
|
||||
#include "BuildInfo.gen.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
using Guard = std::lock_guard<std::mutex>; |
|
||||
std::mutex x_cacheMutex; |
|
||||
CacheMode g_mode; |
|
||||
std::unique_ptr<llvm::MemoryBuffer> g_lastObject; |
|
||||
JITListener* g_listener; |
|
||||
|
|
||||
llvm::StringRef getLibVersionStamp() |
|
||||
{ |
|
||||
return EVMJIT_VERSION; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
g_mode = _mode; |
|
||||
g_listener = _listener; |
|
||||
|
|
||||
if (g_mode == CacheMode::clear) |
|
||||
{ |
|
||||
Cache::clear(); |
|
||||
g_mode = CacheMode::off; |
|
||||
} |
|
||||
|
|
||||
if (g_mode != CacheMode::off) |
|
||||
{ |
|
||||
static ObjectCache objectCache; |
|
||||
return &objectCache; |
|
||||
} |
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
void Cache::clear() |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
using namespace llvm::sys; |
|
||||
llvm::SmallString<256> cachePath; |
|
||||
path::system_temp_directory(false, cachePath); |
|
||||
path::append(cachePath, "evm_objs"); |
|
||||
|
|
||||
std::error_code err; |
|
||||
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) |
|
||||
fs::remove(it->path()); |
|
||||
} |
|
||||
|
|
||||
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache) |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
// TODO: Cache dir should be in one place
|
|
||||
using namespace llvm::sys; |
|
||||
llvm::SmallString<256> cachePath; |
|
||||
path::system_temp_directory(false, cachePath); |
|
||||
path::append(cachePath, "evm_objs"); |
|
||||
|
|
||||
// Disable listener
|
|
||||
auto listener = g_listener; |
|
||||
g_listener = nullptr; |
|
||||
|
|
||||
std::error_code err; |
|
||||
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) |
|
||||
{ |
|
||||
auto name = it->path().substr(cachePath.size() + 1); |
|
||||
if (auto module = getObject(name)) |
|
||||
{ |
|
||||
DLOG(cache) << "Preload: " << name << "\n"; |
|
||||
_ee.addModule(std::move(module)); |
|
||||
auto addr = _ee.getFunctionAddress(name); |
|
||||
assert(addr); |
|
||||
_funcCache[std::move(name)] = addr; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
g_listener = listener; |
|
||||
} |
|
||||
|
|
||||
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
if (g_mode != CacheMode::on && g_mode != CacheMode::read) |
|
||||
return nullptr; |
|
||||
|
|
||||
// TODO: Disabled because is not thread-safe.
|
|
||||
//if (g_listener)
|
|
||||
// g_listener->stateChanged(ExecState::CacheLoad);
|
|
||||
|
|
||||
DLOG(cache) << id << ": search\n"; |
|
||||
if (!CHECK(!g_lastObject)) |
|
||||
g_lastObject = nullptr; |
|
||||
|
|
||||
llvm::SmallString<256> cachePath; |
|
||||
llvm::sys::path::system_temp_directory(false, cachePath); |
|
||||
llvm::sys::path::append(cachePath, "evm_objs", id); |
|
||||
|
|
||||
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) |
|
||||
{ |
|
||||
auto& buf = r.get(); |
|
||||
auto expectedStamp = getLibVersionStamp(); |
|
||||
auto stampSize = expectedStamp.size(); |
|
||||
auto objStamp = buf->getBufferSize() >= stampSize ? llvm::StringRef{buf->getBufferEnd() - stampSize, stampSize} : llvm::StringRef{}; |
|
||||
if (objStamp == expectedStamp) |
|
||||
g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); |
|
||||
else |
|
||||
DLOG(cache) << "Unmatched version: " << objStamp.str() << ", expected " << expectedStamp.str() << "\n"; |
|
||||
} |
|
||||
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) |
|
||||
DLOG(cache) << r.getError().message(); // TODO: Add warning log
|
|
||||
|
|
||||
if (g_lastObject) // if object found create fake module
|
|
||||
{ |
|
||||
DLOG(cache) << id << ": found\n"; |
|
||||
auto&& context = llvm::getGlobalContext(); |
|
||||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context)); |
|
||||
auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false); |
|
||||
auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); |
|
||||
auto bb = llvm::BasicBlock::Create(context, {}, mainFunc); |
|
||||
bb->getInstList().push_back(new llvm::UnreachableInst{context}); |
|
||||
return module; |
|
||||
} |
|
||||
DLOG(cache) << id << ": not found\n"; |
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
// Only in "on" and "write" mode
|
|
||||
if (g_mode != CacheMode::on && g_mode != CacheMode::write) |
|
||||
return; |
|
||||
|
|
||||
// TODO: Disabled because is not thread-safe.
|
|
||||
// if (g_listener)
|
|
||||
// g_listener->stateChanged(ExecState::CacheWrite);
|
|
||||
|
|
||||
auto&& id = _module->getModuleIdentifier(); |
|
||||
llvm::SmallString<256> cachePath; |
|
||||
llvm::sys::path::system_temp_directory(false, cachePath); |
|
||||
llvm::sys::path::append(cachePath, "evm_objs"); |
|
||||
|
|
||||
if (llvm::sys::fs::create_directory(cachePath.str())) |
|
||||
DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << "\n"; |
|
||||
|
|
||||
llvm::sys::path::append(cachePath, id); |
|
||||
|
|
||||
DLOG(cache) << id << ": write\n"; |
|
||||
std::error_code error; |
|
||||
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); |
|
||||
cacheFile << _object.getBuffer() << getLibVersionStamp(); |
|
||||
} |
|
||||
|
|
||||
std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module) |
|
||||
{ |
|
||||
Guard g{x_cacheMutex}; |
|
||||
|
|
||||
DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; |
|
||||
return std::move(g_lastObject); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,59 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <memory> |
|
||||
#include <unordered_map> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/ExecutionEngine/ObjectCache.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
namespace llvm |
|
||||
{ |
|
||||
class ExecutionEngine; |
|
||||
} |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
class JITListener; |
|
||||
|
|
||||
enum class CacheMode |
|
||||
{ |
|
||||
on, |
|
||||
off, |
|
||||
read, |
|
||||
write, |
|
||||
clear, |
|
||||
preload |
|
||||
}; |
|
||||
|
|
||||
class ObjectCache : public llvm::ObjectCache |
|
||||
{ |
|
||||
public: |
|
||||
/// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
|
|
||||
virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; |
|
||||
|
|
||||
/// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
|
|
||||
/// contains the object which corresponds with Module M, or 0 if an object is
|
|
||||
/// not available. The caller owns both the MemoryBuffer returned by this
|
|
||||
/// and the memory it references.
|
|
||||
virtual std::unique_ptr<llvm::MemoryBuffer> getObject(llvm::Module const* _module) final override; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class Cache |
|
||||
{ |
|
||||
public: |
|
||||
static ObjectCache* init(CacheMode _mode, JITListener* _listener); |
|
||||
static std::unique_ptr<llvm::Module> getObject(std::string const& id); |
|
||||
|
|
||||
/// Clears cache storage
|
|
||||
static void clear(); |
|
||||
|
|
||||
/// Loads all available cached objects to ExecutionEngine
|
|
||||
static void preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,16 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <cstdint> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
using byte = uint8_t; |
|
||||
using code_iterator = byte const*; |
|
||||
|
|
||||
#define UNTESTED assert(false) |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,800 +0,0 @@ |
|||||
#include "Compiler.h" |
|
||||
|
|
||||
#include <functional> |
|
||||
#include <fstream> |
|
||||
#include <chrono> |
|
||||
#include <sstream> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/CFG.h> |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Instruction.h" |
|
||||
#include "Type.h" |
|
||||
#include "Memory.h" |
|
||||
#include "Stack.h" |
|
||||
#include "Ext.h" |
|
||||
#include "GasMeter.h" |
|
||||
#include "Utils.h" |
|
||||
#include "Endianness.h" |
|
||||
#include "Arith256.h" |
|
||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
static const auto c_destIdxLabel = "destIdx"; |
|
||||
|
|
||||
Compiler::Compiler(Options const& _options): |
|
||||
m_options(_options), |
|
||||
m_builder(llvm::getGlobalContext()) |
|
||||
{ |
|
||||
Type::init(m_builder.getContext()); |
|
||||
} |
|
||||
|
|
||||
std::vector<BasicBlock> Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd, llvm::SwitchInst& _jumpTable) |
|
||||
{ |
|
||||
/// Helper function that skips push data and finds next iterator (can be the end)
|
|
||||
auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) |
|
||||
{ |
|
||||
static const auto push1 = static_cast<size_t>(Instruction::PUSH1); |
|
||||
static const auto push32 = static_cast<size_t>(Instruction::PUSH32); |
|
||||
size_t offset = 1; |
|
||||
if (*_curr >= push1 && *_curr <= push32) |
|
||||
offset += std::min<size_t>(*_curr - push1 + 1, (_end - _curr) - 1); |
|
||||
return _curr + offset; |
|
||||
}; |
|
||||
|
|
||||
// Skip all STOPs in the end
|
|
||||
for (; _codeEnd != _codeBegin; --_codeEnd) |
|
||||
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP)) |
|
||||
break; |
|
||||
|
|
||||
std::vector<BasicBlock> blocks; |
|
||||
|
|
||||
auto begin = _codeBegin; // begin of current block
|
|
||||
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) |
|
||||
{ |
|
||||
next = skipPushDataAndGetNext(curr, _codeEnd); |
|
||||
|
|
||||
bool isEnd = false; |
|
||||
switch (Instruction(*curr)) |
|
||||
{ |
|
||||
case Instruction::JUMP: |
|
||||
case Instruction::JUMPI: |
|
||||
case Instruction::RETURN: |
|
||||
case Instruction::STOP: |
|
||||
case Instruction::SUICIDE: |
|
||||
isEnd = true; |
|
||||
break; |
|
||||
|
|
||||
default: |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
assert(next <= _codeEnd); |
|
||||
if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) |
|
||||
isEnd = true; |
|
||||
|
|
||||
if (isEnd) |
|
||||
{ |
|
||||
auto beginIdx = begin - _codeBegin; |
|
||||
blocks.emplace_back(beginIdx, begin, next, m_mainFunc); |
|
||||
if (Instruction(*begin) == Instruction::JUMPDEST) |
|
||||
_jumpTable.addCase(Constant::get(beginIdx), blocks.back().llvm()); |
|
||||
begin = next; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return blocks; |
|
||||
} |
|
||||
|
|
||||
void Compiler::resolveJumps() |
|
||||
{ |
|
||||
// Iterate through all EVM instructions blocks (skip first 4 - special blocks).
|
|
||||
for (auto it = std::next(m_mainFunc->begin(), 4); it != m_mainFunc->end(); ++it) |
|
||||
{ |
|
||||
auto jumpTable = llvm::cast<llvm::SwitchInst>(m_jumpTableBB->getTerminator()); |
|
||||
auto jumpTableInput = llvm::cast<llvm::PHINode>(m_jumpTableBB->begin()); |
|
||||
auto nextBlock = it->getNextNode() != m_mainFunc->end() ? it->getNextNode() : m_stopBB; |
|
||||
auto term = it->getTerminator(); |
|
||||
|
|
||||
if (!term) // Block may have no terminator if the next instruction is a jump destination.
|
|
||||
llvm::IRBuilder<>{it}.CreateBr(nextBlock); |
|
||||
else if (auto jump = llvm::dyn_cast<llvm::BranchInst>(term)) |
|
||||
if (jump->getSuccessor(0) == m_jumpTableBB) |
|
||||
{ |
|
||||
auto destIdx = llvm::cast<llvm::ValueAsMetadata>(jump->getMetadata(c_destIdxLabel)->getOperand(0))->getValue(); |
|
||||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(destIdx)) |
|
||||
{ |
|
||||
// If destination index is a constant do direct jump to the destination block.
|
|
||||
auto bb = jumpTable->findCaseValue(constant).getCaseSuccessor(); |
|
||||
jump->setSuccessor(0, bb); |
|
||||
} |
|
||||
else |
|
||||
jumpTableInput->addIncoming(destIdx, it); // Fill up PHI node
|
|
||||
|
|
||||
if (jump->isConditional()) |
|
||||
jump->setSuccessor(1, nextBlock); // Set next block for conditional jumps
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) |
|
||||
{ |
|
||||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext())); |
|
||||
|
|
||||
// Create main function
|
|
||||
auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); |
|
||||
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); |
|
||||
m_mainFunc->getArgumentList().front().setName("rt"); |
|
||||
|
|
||||
// Create entry basic block
|
|
||||
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc); |
|
||||
|
|
||||
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); |
|
||||
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); |
|
||||
m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); |
|
||||
m_builder.SetInsertPoint(m_jumpTableBB); |
|
||||
auto target = m_builder.CreatePHI(Type::Word, 16, "target"); |
|
||||
auto& jumpTable = *m_builder.CreateSwitch(target, m_abortBB); |
|
||||
|
|
||||
m_builder.SetInsertPoint(entryBlock); |
|
||||
|
|
||||
auto blocks = createBasicBlocks(_begin, _end, jumpTable); |
|
||||
|
|
||||
// Init runtime structures.
|
|
||||
RuntimeManager runtimeManager(m_builder, _begin, _end); |
|
||||
GasMeter gasMeter(m_builder, runtimeManager); |
|
||||
Memory memory(runtimeManager, gasMeter); |
|
||||
Ext ext(runtimeManager, memory); |
|
||||
Stack stack(m_builder); |
|
||||
runtimeManager.setStack(stack); // Runtime Manager will free stack memory
|
|
||||
Arith256 arith(m_builder); |
|
||||
|
|
||||
auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words"); |
|
||||
auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); |
|
||||
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); |
|
||||
m_builder.CreateStore(fp, jmpBufWords); |
|
||||
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); |
|
||||
auto sp = m_builder.CreateCall(stacksave, {}, "sp"); |
|
||||
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); |
|
||||
m_builder.CreateStore(sp, jmpBufSp); |
|
||||
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); |
|
||||
auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); |
|
||||
auto r = m_builder.CreateCall(setjmp, jmpBuf); |
|
||||
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); |
|
||||
runtimeManager.setJmpBuf(jmpBuf); |
|
||||
|
|
||||
auto firstBB = blocks.empty() ? m_stopBB : blocks.front().llvm(); |
|
||||
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); |
|
||||
|
|
||||
for (auto& block: blocks) |
|
||||
compileBasicBlock(block, runtimeManager, arith, memory, ext, gasMeter, stack); |
|
||||
|
|
||||
// Code for special blocks:
|
|
||||
m_builder.SetInsertPoint(m_stopBB); |
|
||||
runtimeManager.exit(ReturnCode::Stop); |
|
||||
|
|
||||
m_builder.SetInsertPoint(m_abortBB); |
|
||||
runtimeManager.exit(ReturnCode::OutOfGas); |
|
||||
|
|
||||
resolveJumps(); |
|
||||
|
|
||||
return module; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, |
|
||||
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, Stack& _globalStack) |
|
||||
{ |
|
||||
m_builder.SetInsertPoint(_basicBlock.llvm()); |
|
||||
LocalStack stack{_globalStack}; |
|
||||
|
|
||||
for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) |
|
||||
{ |
|
||||
auto inst = Instruction(*it); |
|
||||
|
|
||||
_gasMeter.count(inst); |
|
||||
|
|
||||
switch (inst) |
|
||||
{ |
|
||||
|
|
||||
case Instruction::ADD: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto result = m_builder.CreateAdd(lhs, rhs); |
|
||||
stack.push(result); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SUB: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto result = m_builder.CreateSub(lhs, rhs); |
|
||||
stack.push(result); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MUL: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res = m_builder.CreateMul(lhs, rhs); |
|
||||
stack.push(res); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::DIV: |
|
||||
{ |
|
||||
auto d = stack.pop(); |
|
||||
auto n = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|
||||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|
||||
auto r = m_builder.CreateUDiv(d, n); |
|
||||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|
||||
stack.push(r); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SDIV: |
|
||||
{ |
|
||||
auto d = stack.pop(); |
|
||||
auto n = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|
||||
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); |
|
||||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|
||||
auto r = m_builder.CreateSDiv(d, n); |
|
||||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|
||||
auto dNeg = m_builder.CreateSub(Constant::get(0), d); |
|
||||
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
|
|
||||
stack.push(r); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MOD: |
|
||||
{ |
|
||||
auto d = stack.pop(); |
|
||||
auto n = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|
||||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|
||||
auto r = m_builder.CreateURem(d, n); |
|
||||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|
||||
stack.push(r); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SMOD: |
|
||||
{ |
|
||||
auto d = stack.pop(); |
|
||||
auto n = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|
||||
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); |
|
||||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|
||||
auto r = m_builder.CreateSRem(d, n); |
|
||||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|
||||
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
|
|
||||
stack.push(r); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ADDMOD: |
|
||||
{ |
|
||||
auto i512Ty = m_builder.getIntNTy(512); |
|
||||
auto a = stack.pop(); |
|
||||
auto b = stack.pop(); |
|
||||
auto m = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); |
|
||||
a = m_builder.CreateZExt(a, i512Ty); |
|
||||
b = m_builder.CreateZExt(b, i512Ty); |
|
||||
m = m_builder.CreateZExt(m, i512Ty); |
|
||||
auto s = m_builder.CreateNUWAdd(a, b); |
|
||||
s = m_builder.CreateURem(s, m); |
|
||||
s = m_builder.CreateTrunc(s, Type::Word); |
|
||||
s = m_builder.CreateSelect(divByZero, Constant::get(0), s); |
|
||||
stack.push(s); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MULMOD: |
|
||||
{ |
|
||||
auto i512Ty = m_builder.getIntNTy(512); |
|
||||
auto a = stack.pop(); |
|
||||
auto b = stack.pop(); |
|
||||
auto m = stack.pop(); |
|
||||
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); |
|
||||
m = m_builder.CreateZExt(m, i512Ty); |
|
||||
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
|
|
||||
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b}); |
|
||||
p = m_builder.CreateURem(p, m); |
|
||||
p = m_builder.CreateTrunc(p, Type::Word); |
|
||||
p = m_builder.CreateSelect(divByZero, Constant::get(0), p); |
|
||||
stack.push(p); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::EXP: |
|
||||
{ |
|
||||
auto base = stack.pop(); |
|
||||
auto exponent = stack.pop(); |
|
||||
_gasMeter.countExp(exponent); |
|
||||
auto ret = _arith.exp(base, exponent); |
|
||||
stack.push(ret); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::NOT: |
|
||||
{ |
|
||||
auto value = stack.pop(); |
|
||||
auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); |
|
||||
stack.push(ret); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::LT: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res1 = m_builder.CreateICmpULT(lhs, rhs); |
|
||||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|
||||
stack.push(res256); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::GT: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res1 = m_builder.CreateICmpUGT(lhs, rhs); |
|
||||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|
||||
stack.push(res256); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SLT: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res1 = m_builder.CreateICmpSLT(lhs, rhs); |
|
||||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|
||||
stack.push(res256); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SGT: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res1 = m_builder.CreateICmpSGT(lhs, rhs); |
|
||||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|
||||
stack.push(res256); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::EQ: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res1 = m_builder.CreateICmpEQ(lhs, rhs); |
|
||||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|
||||
stack.push(res256); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ISZERO: |
|
||||
{ |
|
||||
auto top = stack.pop(); |
|
||||
auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); |
|
||||
auto result = m_builder.CreateZExt(iszero, Type::Word); |
|
||||
stack.push(result); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::AND: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res = m_builder.CreateAnd(lhs, rhs); |
|
||||
stack.push(res); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::OR: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res = m_builder.CreateOr(lhs, rhs); |
|
||||
stack.push(res); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::XOR: |
|
||||
{ |
|
||||
auto lhs = stack.pop(); |
|
||||
auto rhs = stack.pop(); |
|
||||
auto res = m_builder.CreateXor(lhs, rhs); |
|
||||
stack.push(res); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::BYTE: |
|
||||
{ |
|
||||
const auto idx = stack.pop(); |
|
||||
auto value = Endianness::toBE(m_builder, stack.pop()); |
|
||||
|
|
||||
auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); |
|
||||
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); |
|
||||
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
|
|
||||
auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); |
|
||||
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
|
|
||||
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size); |
|
||||
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); |
|
||||
value = m_builder.CreateZExt(byte, Type::Word); |
|
||||
value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SIGNEXTEND: |
|
||||
{ |
|
||||
auto idx = stack.pop(); |
|
||||
auto word = stack.pop(); |
|
||||
|
|
||||
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); |
|
||||
auto k32 = m_builder.CreateZExt(k32_, Type::Size); |
|
||||
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); |
|
||||
|
|
||||
// test for word >> (k * 8 + 7)
|
|
||||
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); |
|
||||
auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); |
|
||||
auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval"); |
|
||||
auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); |
|
||||
|
|
||||
auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); |
|
||||
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); |
|
||||
|
|
||||
auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); |
|
||||
auto val1 = m_builder.CreateOr(word, negmask); |
|
||||
auto val0 = m_builder.CreateAnd(word, mask); |
|
||||
|
|
||||
auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); |
|
||||
auto result = m_builder.CreateSelect(kInRange, |
|
||||
m_builder.CreateSelect(bittest, val1, val0), |
|
||||
word); |
|
||||
stack.push(result); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SHA3: |
|
||||
{ |
|
||||
auto inOff = stack.pop(); |
|
||||
auto inSize = stack.pop(); |
|
||||
_memory.require(inOff, inSize); |
|
||||
_gasMeter.countSha3Data(inSize); |
|
||||
auto hash = _ext.sha3(inOff, inSize); |
|
||||
stack.push(hash); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::POP: |
|
||||
{ |
|
||||
stack.pop(); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ANY_PUSH: |
|
||||
{ |
|
||||
auto value = readPushData(it, _basicBlock.end()); |
|
||||
stack.push(Constant::get(value)); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ANY_DUP: |
|
||||
{ |
|
||||
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::DUP1); |
|
||||
stack.dup(index); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ANY_SWAP: |
|
||||
{ |
|
||||
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::SWAP1) + 1; |
|
||||
stack.swap(index); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MLOAD: |
|
||||
{ |
|
||||
auto addr = stack.pop(); |
|
||||
auto word = _memory.loadWord(addr); |
|
||||
stack.push(word); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MSTORE: |
|
||||
{ |
|
||||
auto addr = stack.pop(); |
|
||||
auto word = stack.pop(); |
|
||||
_memory.storeWord(addr, word); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MSTORE8: |
|
||||
{ |
|
||||
auto addr = stack.pop(); |
|
||||
auto word = stack.pop(); |
|
||||
_memory.storeByte(addr, word); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::MSIZE: |
|
||||
{ |
|
||||
auto word = _memory.getSize(); |
|
||||
stack.push(word); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SLOAD: |
|
||||
{ |
|
||||
auto index = stack.pop(); |
|
||||
auto value = _ext.sload(index); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SSTORE: |
|
||||
{ |
|
||||
auto index = stack.pop(); |
|
||||
auto value = stack.pop(); |
|
||||
_gasMeter.countSStore(_ext, index, value); |
|
||||
_ext.sstore(index, value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::JUMP: |
|
||||
case Instruction::JUMPI: |
|
||||
{ |
|
||||
auto destIdx = llvm::MDNode::get(m_builder.getContext(), llvm::ValueAsMetadata::get(stack.pop())); |
|
||||
|
|
||||
// Create branch instruction, initially to jump table.
|
|
||||
// Destination will be optimized with direct jump during jump resolving if destination index is a constant.
|
|
||||
auto jumpInst = (inst == Instruction::JUMP) ? |
|
||||
m_builder.CreateBr(m_jumpTableBB) : |
|
||||
m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), m_jumpTableBB, nullptr); |
|
||||
|
|
||||
// Attach medatada to branch instruction with information about destination index.
|
|
||||
jumpInst->setMetadata(c_destIdxLabel, destIdx); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::JUMPDEST: |
|
||||
{ |
|
||||
// Nothing to do
|
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::PC: |
|
||||
{ |
|
||||
auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx()); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::GAS: |
|
||||
{ |
|
||||
_gasMeter.commitCostBlock(); |
|
||||
stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word)); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::ADDRESS: |
|
||||
case Instruction::CALLER: |
|
||||
case Instruction::ORIGIN: |
|
||||
case Instruction::CALLVALUE: |
|
||||
case Instruction::GASPRICE: |
|
||||
case Instruction::COINBASE: |
|
||||
case Instruction::DIFFICULTY: |
|
||||
case Instruction::GASLIMIT: |
|
||||
case Instruction::NUMBER: |
|
||||
case Instruction::TIMESTAMP: |
|
||||
{ |
|
||||
// Pushes an element of runtime data on stack
|
|
||||
auto value = _runtimeManager.get(inst); |
|
||||
value = m_builder.CreateZExt(value, Type::Word); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CODESIZE: |
|
||||
stack.push(_runtimeManager.getCodeSize()); |
|
||||
break; |
|
||||
|
|
||||
case Instruction::CALLDATASIZE: |
|
||||
stack.push(_runtimeManager.getCallDataSize()); |
|
||||
break; |
|
||||
|
|
||||
case Instruction::BLOCKHASH: |
|
||||
{ |
|
||||
auto number = stack.pop(); |
|
||||
auto hash = _ext.blockHash(number); |
|
||||
stack.push(hash); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::BALANCE: |
|
||||
{ |
|
||||
auto address = stack.pop(); |
|
||||
auto value = _ext.balance(address); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::EXTCODESIZE: |
|
||||
{ |
|
||||
auto addr = stack.pop(); |
|
||||
auto codeRef = _ext.extcode(addr); |
|
||||
stack.push(codeRef.size); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CALLDATACOPY: |
|
||||
{ |
|
||||
auto destMemIdx = stack.pop(); |
|
||||
auto srcIdx = stack.pop(); |
|
||||
auto reqBytes = stack.pop(); |
|
||||
|
|
||||
auto srcPtr = _runtimeManager.getCallData(); |
|
||||
auto srcSize = _runtimeManager.getCallDataSize(); |
|
||||
|
|
||||
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CODECOPY: |
|
||||
{ |
|
||||
auto destMemIdx = stack.pop(); |
|
||||
auto srcIdx = stack.pop(); |
|
||||
auto reqBytes = stack.pop(); |
|
||||
|
|
||||
auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234
|
|
||||
auto srcSize = _runtimeManager.getCodeSize(); |
|
||||
|
|
||||
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::EXTCODECOPY: |
|
||||
{ |
|
||||
auto addr = stack.pop(); |
|
||||
auto destMemIdx = stack.pop(); |
|
||||
auto srcIdx = stack.pop(); |
|
||||
auto reqBytes = stack.pop(); |
|
||||
|
|
||||
auto codeRef = _ext.extcode(addr); |
|
||||
|
|
||||
_memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CALLDATALOAD: |
|
||||
{ |
|
||||
auto idx = stack.pop(); |
|
||||
auto value = _ext.calldataload(idx); |
|
||||
stack.push(value); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CREATE: |
|
||||
{ |
|
||||
auto endowment = stack.pop(); |
|
||||
auto initOff = stack.pop(); |
|
||||
auto initSize = stack.pop(); |
|
||||
_memory.require(initOff, initSize); |
|
||||
|
|
||||
_gasMeter.commitCostBlock(); |
|
||||
auto address = _ext.create(endowment, initOff, initSize); |
|
||||
stack.push(address); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CALL: |
|
||||
case Instruction::CALLCODE: |
|
||||
{ |
|
||||
auto callGas = stack.pop(); |
|
||||
auto codeAddress = stack.pop(); |
|
||||
auto value = stack.pop(); |
|
||||
auto inOff = stack.pop(); |
|
||||
auto inSize = stack.pop(); |
|
||||
auto outOff = stack.pop(); |
|
||||
auto outSize = stack.pop(); |
|
||||
|
|
||||
_gasMeter.commitCostBlock(); |
|
||||
|
|
||||
// Require memory for in and out buffers
|
|
||||
_memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one
|
|
||||
_memory.require(inOff, inSize); |
|
||||
|
|
||||
auto receiveAddress = codeAddress; |
|
||||
if (inst == Instruction::CALLCODE) |
|
||||
receiveAddress = _runtimeManager.get(RuntimeData::Address); |
|
||||
|
|
||||
auto ret = _ext.call(callGas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); |
|
||||
_gasMeter.count(m_builder.getInt64(0), _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); |
|
||||
stack.push(ret); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::RETURN: |
|
||||
{ |
|
||||
auto index = stack.pop(); |
|
||||
auto size = stack.pop(); |
|
||||
|
|
||||
_memory.require(index, size); |
|
||||
_runtimeManager.registerReturnData(index, size); |
|
||||
|
|
||||
_runtimeManager.exit(ReturnCode::Return); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::SUICIDE: |
|
||||
{ |
|
||||
_runtimeManager.registerSuicide(stack.pop()); |
|
||||
_runtimeManager.exit(ReturnCode::Suicide); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
case Instruction::STOP: |
|
||||
{ |
|
||||
m_builder.CreateBr(m_stopBB); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
case Instruction::LOG0: |
|
||||
case Instruction::LOG1: |
|
||||
case Instruction::LOG2: |
|
||||
case Instruction::LOG3: |
|
||||
case Instruction::LOG4: |
|
||||
{ |
|
||||
auto beginIdx = stack.pop(); |
|
||||
auto numBytes = stack.pop(); |
|
||||
_memory.require(beginIdx, numBytes); |
|
||||
|
|
||||
// This will commit the current cost block
|
|
||||
_gasMeter.countLogData(numBytes); |
|
||||
|
|
||||
std::array<llvm::Value*, 4> topics{{}}; |
|
||||
auto numTopics = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::LOG0); |
|
||||
for (size_t i = 0; i < numTopics; ++i) |
|
||||
topics[i] = stack.pop(); |
|
||||
|
|
||||
_ext.log(beginIdx, numBytes, topics); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
default: // Invalid instruction - abort
|
|
||||
m_builder.CreateBr(m_abortBB); |
|
||||
it = _basicBlock.end() - 1; // finish block compilation
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_gasMeter.commitCostBlock(); |
|
||||
|
|
||||
stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references
|
|
||||
|
|
||||
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize
|
|
||||
_runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size()); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,59 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "BasicBlock.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
class Compiler |
|
||||
{ |
|
||||
public: |
|
||||
|
|
||||
struct Options |
|
||||
{ |
|
||||
/// Rewrite switch instructions to sequences of branches
|
|
||||
bool rewriteSwitchToBranches = true; |
|
||||
|
|
||||
/// Dump CFG as a .dot file for graphviz
|
|
||||
bool dumpCFG = false; |
|
||||
}; |
|
||||
|
|
||||
Compiler(Options const& _options); |
|
||||
|
|
||||
std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id); |
|
||||
|
|
||||
private: |
|
||||
|
|
||||
std::vector<BasicBlock> createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); |
|
||||
|
|
||||
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, |
|
||||
class Stack& _globalStack); |
|
||||
|
|
||||
void resolveJumps(); |
|
||||
|
|
||||
/// Compiler options
|
|
||||
Options const& m_options; |
|
||||
|
|
||||
/// Helper class for generating IR
|
|
||||
llvm::IRBuilder<> m_builder; |
|
||||
|
|
||||
/// Stop basic block - terminates execution with STOP code (0)
|
|
||||
llvm::BasicBlock* m_stopBB = nullptr; |
|
||||
|
|
||||
/// Abort basic block - terminates execution with OOG-like state
|
|
||||
llvm::BasicBlock* m_abortBB = nullptr; |
|
||||
|
|
||||
/// Block with a jump table.
|
|
||||
llvm::BasicBlock* m_jumpTableBB = nullptr; |
|
||||
|
|
||||
/// Main program function
|
|
||||
llvm::Function* m_mainFunc = nullptr; // TODO: Remove
|
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
#include "CompilerHelper.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
CompilerHelper::CompilerHelper(llvm::IRBuilder<>& _builder) : |
|
||||
m_builder(_builder) |
|
||||
{} |
|
||||
|
|
||||
llvm::Module* CompilerHelper::getModule() |
|
||||
{ |
|
||||
assert(m_builder.GetInsertBlock()); |
|
||||
assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function
|
|
||||
return m_builder.GetInsertBlock()->getParent()->getParent(); |
|
||||
} |
|
||||
|
|
||||
llvm::Function* CompilerHelper::getMainFunction() |
|
||||
{ |
|
||||
// TODO: Rename or change semantics of getMainFunction() function
|
|
||||
assert(m_builder.GetInsertBlock()); |
|
||||
auto mainFunc = m_builder.GetInsertBlock()->getParent(); |
|
||||
assert(mainFunc); |
|
||||
if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module
|
|
||||
return mainFunc; |
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args) |
|
||||
{ |
|
||||
return getBuilder().CreateCall(_func, {_args.begin(), _args.size()}); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): |
|
||||
CompilerHelper(_runtimeManager.getBuilder()), |
|
||||
m_runtimeManager(_runtimeManager) |
|
||||
{} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,64 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IRBuilder.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
class RuntimeManager; |
|
||||
|
|
||||
/// Base class for compiler helpers like Memory, GasMeter, etc.
|
|
||||
class CompilerHelper |
|
||||
{ |
|
||||
protected: |
|
||||
CompilerHelper(llvm::IRBuilder<>& _builder); |
|
||||
|
|
||||
CompilerHelper(const CompilerHelper&) = delete; |
|
||||
CompilerHelper& operator=(CompilerHelper) = delete; |
|
||||
|
|
||||
/// Reference to the IR module being compiled
|
|
||||
llvm::Module* getModule(); |
|
||||
|
|
||||
/// Reference to the main module function
|
|
||||
llvm::Function* getMainFunction(); |
|
||||
|
|
||||
/// Reference to parent compiler IR builder
|
|
||||
llvm::IRBuilder<>& m_builder; |
|
||||
llvm::IRBuilder<>& getBuilder() { return m_builder; } |
|
||||
|
|
||||
llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args); |
|
||||
|
|
||||
friend class RuntimeHelper; |
|
||||
}; |
|
||||
|
|
||||
/// Compiler helper that depends on runtime data
|
|
||||
class RuntimeHelper : public CompilerHelper |
|
||||
{ |
|
||||
protected: |
|
||||
RuntimeHelper(RuntimeManager& _runtimeManager); |
|
||||
|
|
||||
RuntimeManager& getRuntimeManager() { return m_runtimeManager; } |
|
||||
|
|
||||
private: |
|
||||
RuntimeManager& m_runtimeManager; |
|
||||
}; |
|
||||
|
|
||||
struct InsertPointGuard |
|
||||
{ |
|
||||
explicit InsertPointGuard(llvm::IRBuilderBase& _builder): m_builder(_builder), m_insertPoint(_builder.saveIP()) {} |
|
||||
~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); } |
|
||||
|
|
||||
private: |
|
||||
llvm::IRBuilderBase& m_builder; |
|
||||
llvm::IRBuilderBase::InsertPoint m_insertPoint; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,33 +0,0 @@ |
|||||
#include "Endianness.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include <llvm/Support/Host.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Type.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word) |
|
||||
{ |
|
||||
if (llvm::sys::IsLittleEndianHost) |
|
||||
{ |
|
||||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word)) |
|
||||
return _builder.getInt(constant->getValue().byteSwap()); |
|
||||
|
|
||||
// OPT: Cache func declaration?
|
|
||||
auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); |
|
||||
return _builder.CreateCall(bswapFunc, _word); |
|
||||
} |
|
||||
return _word; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,25 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IRBuilder.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
struct Endianness |
|
||||
{ |
|
||||
static llvm::Value* toBE(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } |
|
||||
static llvm::Value* toNative(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } |
|
||||
|
|
||||
private: |
|
||||
static llvm::Value* bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,95 +0,0 @@ |
|||||
#include "ExecStats.h" |
|
||||
|
|
||||
#include <iostream> |
|
||||
#include <iomanip> |
|
||||
#include <cassert> |
|
||||
|
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
void ExecStats::stateChanged(ExecState _state) |
|
||||
{ |
|
||||
if (!CHECK(m_state != ExecState::Finished)) |
|
||||
return; |
|
||||
|
|
||||
auto now = clock::now(); |
|
||||
if (_state != ExecState::Started) |
|
||||
{ |
|
||||
assert(time[(int)m_state] == ExecStats::duration::zero()); |
|
||||
time[(int)m_state] = now - m_tp; |
|
||||
} |
|
||||
m_state = _state; |
|
||||
m_tp = now; |
|
||||
} |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
struct StatsAgg |
|
||||
{ |
|
||||
using unit = std::chrono::microseconds; |
|
||||
ExecStats::duration tot = ExecStats::duration::zero(); |
|
||||
ExecStats::duration min = ExecStats::duration::max(); |
|
||||
ExecStats::duration max = ExecStats::duration::zero(); |
|
||||
size_t count = 0; |
|
||||
|
|
||||
void update(ExecStats::duration _d) |
|
||||
{ |
|
||||
++count; |
|
||||
tot += _d; |
|
||||
min = _d < min ? _d : min; |
|
||||
max = _d > max ? _d : max; |
|
||||
} |
|
||||
|
|
||||
void output(char const* _name, std::ostream& _os) |
|
||||
{ |
|
||||
auto avg = tot / count; |
|
||||
_os << std::setfill(' ') |
|
||||
<< std::setw(12) << std::left << _name |
|
||||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(tot).count() |
|
||||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(avg).count() |
|
||||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(min).count() |
|
||||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(max).count() |
|
||||
<< std::endl; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
char const* getExecStateName(ExecState _state) |
|
||||
{ |
|
||||
switch (_state) |
|
||||
{ |
|
||||
case ExecState::Started: return "Start"; |
|
||||
case ExecState::CacheLoad: return "CacheLoad"; |
|
||||
case ExecState::CacheWrite: return "CacheWrite"; |
|
||||
case ExecState::Compilation: return "Compilation"; |
|
||||
case ExecState::Optimization: return "Optimization"; |
|
||||
case ExecState::CodeGen: return "CodeGen"; |
|
||||
case ExecState::Execution: return "Execution"; |
|
||||
case ExecState::Return: return "Return"; |
|
||||
case ExecState::Finished: return "Finish"; |
|
||||
} |
|
||||
return nullptr; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
StatsCollector::~StatsCollector() |
|
||||
{ |
|
||||
if (stats.empty()) |
|
||||
return; |
|
||||
|
|
||||
std::cout << " [us] total avg min max\n"; |
|
||||
for (int i = 0; i < (int)ExecState::Finished; ++i) |
|
||||
{ |
|
||||
StatsAgg agg; |
|
||||
for (auto&& s : stats) |
|
||||
agg.update(s->time[i]); |
|
||||
|
|
||||
agg.output(getExecStateName(ExecState(i)), std::cout); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,68 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <memory> |
|
||||
#include <vector> |
|
||||
#include <string> |
|
||||
#include <chrono> |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
enum class ExecState |
|
||||
{ |
|
||||
Started, |
|
||||
CacheLoad, |
|
||||
CacheWrite, |
|
||||
Compilation, |
|
||||
Optimization, |
|
||||
CodeGen, |
|
||||
Execution, |
|
||||
Return, |
|
||||
Finished |
|
||||
}; |
|
||||
|
|
||||
class JITListener |
|
||||
{ |
|
||||
public: |
|
||||
JITListener() = default; |
|
||||
JITListener(JITListener const&) = delete; |
|
||||
JITListener& operator=(JITListener) = delete; |
|
||||
virtual ~JITListener() {} |
|
||||
|
|
||||
virtual void executionStarted() {} |
|
||||
virtual void executionEnded() {} |
|
||||
|
|
||||
virtual void stateChanged(ExecState) {} |
|
||||
}; |
|
||||
|
|
||||
class ExecStats : public JITListener |
|
||||
{ |
|
||||
public: |
|
||||
using clock = std::chrono::high_resolution_clock; |
|
||||
using duration = clock::duration; |
|
||||
using time_point = clock::time_point; |
|
||||
|
|
||||
std::string id; |
|
||||
duration time[(int)ExecState::Finished] = {}; |
|
||||
|
|
||||
void stateChanged(ExecState _state) override; |
|
||||
|
|
||||
private: |
|
||||
ExecState m_state = {}; |
|
||||
time_point m_tp = {}; |
|
||||
|
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class StatsCollector |
|
||||
{ |
|
||||
public: |
|
||||
std::vector<std::unique_ptr<ExecStats>> stats; |
|
||||
|
|
||||
~StatsCollector(); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,210 +0,0 @@ |
|||||
#include "Ext.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "RuntimeManager.h" |
|
||||
#include "Memory.h" |
|
||||
#include "Type.h" |
|
||||
#include "Endianness.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): |
|
||||
RuntimeHelper(_runtimeManager), |
|
||||
m_memoryMan(_memoryMan) |
|
||||
{ |
|
||||
m_funcs = decltype(m_funcs)(); |
|
||||
m_argAllocas = decltype(m_argAllocas)(); |
|
||||
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
using FuncDesc = std::tuple<char const*, llvm::FunctionType*>; |
|
||||
|
|
||||
llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes) |
|
||||
{ |
|
||||
return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false); |
|
||||
} |
|
||||
|
|
||||
std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs() |
|
||||
{ |
|
||||
static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{ |
|
||||
FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|
||||
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|
||||
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, |
|
||||
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|
||||
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, |
|
||||
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::Gas, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, |
|
||||
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, |
|
||||
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|
||||
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, |
|
||||
}}; |
|
||||
|
|
||||
return descs; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) |
|
||||
{ |
|
||||
auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)]; |
|
||||
return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::getArgAlloca() |
|
||||
{ |
|
||||
auto& a = m_argAllocas[m_argCounter]; |
|
||||
if (!a) |
|
||||
{ |
|
||||
InsertPointGuard g{getBuilder()}; |
|
||||
auto allocaIt = getMainFunction()->front().begin(); |
|
||||
std::advance(allocaIt, m_argCounter); // Skip already created allocas
|
|
||||
getBuilder().SetInsertPoint(allocaIt); |
|
||||
a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); |
|
||||
} |
|
||||
++m_argCounter; |
|
||||
return a; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::byPtr(llvm::Value* _value) |
|
||||
{ |
|
||||
auto a = getArgAlloca(); |
|
||||
getBuilder().CreateStore(_value, a); |
|
||||
return a; |
|
||||
} |
|
||||
|
|
||||
llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args) |
|
||||
{ |
|
||||
auto& func = m_funcs[static_cast<size_t>(_funcId)]; |
|
||||
if (!func) |
|
||||
func = createFunc(_funcId, getModule()); |
|
||||
|
|
||||
m_argCounter = 0; |
|
||||
return getBuilder().CreateCall(func, {_args.begin(), _args.size()}); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::sload(llvm::Value* _index) |
|
||||
{ |
|
||||
auto ret = getArgAlloca(); |
|
||||
createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
|
|
||||
return m_builder.CreateLoad(ret); |
|
||||
} |
|
||||
|
|
||||
void Ext::sstore(llvm::Value* _index, llvm::Value* _value) |
|
||||
{ |
|
||||
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
|
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::calldataload(llvm::Value* _idx) |
|
||||
{ |
|
||||
auto ret = getArgAlloca(); |
|
||||
auto result = m_builder.CreateBitCast(ret, Type::BytePtr); |
|
||||
|
|
||||
auto callDataSize = getRuntimeManager().getCallDataSize(); |
|
||||
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); |
|
||||
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); |
|
||||
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); |
|
||||
|
|
||||
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); |
|
||||
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); |
|
||||
auto copySize = m_builder.CreateNUWSub(end, idx); |
|
||||
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); |
|
||||
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); |
|
||||
m_builder.CreateMemCpy(result, dataBegin, copySize, 1); |
|
||||
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); |
|
||||
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); |
|
||||
|
|
||||
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
|
|
||||
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::balance(llvm::Value* _address) |
|
||||
{ |
|
||||
auto address = Endianness::toBE(m_builder, _address); |
|
||||
auto ret = getArgAlloca(); |
|
||||
createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret}); |
|
||||
return m_builder.CreateLoad(ret); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::blockHash(llvm::Value* _number) |
|
||||
{ |
|
||||
auto hash = getArgAlloca(); |
|
||||
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash}); |
|
||||
hash = m_builder.CreateLoad(hash); |
|
||||
return Endianness::toNative(getBuilder(), hash); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) |
|
||||
{ |
|
||||
auto ret = getArgAlloca(); |
|
||||
auto begin = m_memoryMan.getBytePtr(_initOff); |
|
||||
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); |
|
||||
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret}); |
|
||||
llvm::Value* address = m_builder.CreateLoad(ret); |
|
||||
address = Endianness::toNative(m_builder, address); |
|
||||
return address; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) |
|
||||
{ |
|
||||
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); |
|
||||
auto inBeg = m_memoryMan.getBytePtr(_inOff); |
|
||||
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); |
|
||||
auto outBeg = m_memoryMan.getBytePtr(_outOff); |
|
||||
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); |
|
||||
auto codeAddress = Endianness::toBE(m_builder, _codeAddress); |
|
||||
auto callGas = m_builder.CreateSelect( |
|
||||
m_builder.CreateICmpULE(_callGas, m_builder.CreateZExt(Constant::gasMax, Type::Word)), |
|
||||
m_builder.CreateTrunc(_callGas, Type::Gas), |
|
||||
Constant::gasMax); |
|
||||
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), callGas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); |
|
||||
return m_builder.CreateZExt(ret, Type::Word, "ret"); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) |
|
||||
{ |
|
||||
auto begin = m_memoryMan.getBytePtr(_inOff); |
|
||||
auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); |
|
||||
auto ret = getArgAlloca(); |
|
||||
createCall(EnvFunc::sha3, {begin, size, ret}); |
|
||||
llvm::Value* hash = m_builder.CreateLoad(ret); |
|
||||
hash = Endianness::toNative(m_builder, hash); |
|
||||
return hash; |
|
||||
} |
|
||||
|
|
||||
MemoryRef Ext::extcode(llvm::Value* _addr) |
|
||||
{ |
|
||||
auto addr = Endianness::toBE(m_builder, _addr); |
|
||||
auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); |
|
||||
auto codeSize = m_builder.CreateLoad(m_size); |
|
||||
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); |
|
||||
return {code, codeSize256}; |
|
||||
} |
|
||||
|
|
||||
void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics) |
|
||||
{ |
|
||||
auto begin = m_memoryMan.getBytePtr(_memIdx); |
|
||||
auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size"); |
|
||||
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()}; |
|
||||
|
|
||||
auto topicArgPtr = &args[3]; |
|
||||
for (auto&& topic : _topics) |
|
||||
{ |
|
||||
if (topic) |
|
||||
m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr); |
|
||||
else |
|
||||
*topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr); |
|
||||
++topicArgPtr; |
|
||||
} |
|
||||
|
|
||||
createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<>
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,79 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <array> |
|
||||
|
|
||||
#include "CompilerHelper.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
class Memory; |
|
||||
|
|
||||
struct MemoryRef |
|
||||
{ |
|
||||
llvm::Value* ptr; |
|
||||
llvm::Value* size; |
|
||||
}; |
|
||||
|
|
||||
template<typename _EnumT> |
|
||||
struct sizeOf |
|
||||
{ |
|
||||
static const size_t value = static_cast<size_t>(_EnumT::_size); |
|
||||
}; |
|
||||
|
|
||||
enum class EnvFunc |
|
||||
{ |
|
||||
sload, |
|
||||
sstore, |
|
||||
sha3, |
|
||||
balance, |
|
||||
create, |
|
||||
call, |
|
||||
log, |
|
||||
blockhash, |
|
||||
extcode, |
|
||||
|
|
||||
_size |
|
||||
}; |
|
||||
|
|
||||
class Ext : public RuntimeHelper |
|
||||
{ |
|
||||
public: |
|
||||
Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); |
|
||||
|
|
||||
llvm::Value* sload(llvm::Value* _index); |
|
||||
void sstore(llvm::Value* _index, llvm::Value* _value); |
|
||||
|
|
||||
llvm::Value* balance(llvm::Value* _address); |
|
||||
llvm::Value* calldataload(llvm::Value* _index); |
|
||||
llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); |
|
||||
llvm::Value* call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); |
|
||||
llvm::Value* blockHash(llvm::Value* _number); |
|
||||
|
|
||||
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); |
|
||||
MemoryRef extcode(llvm::Value* _addr); |
|
||||
|
|
||||
void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics); |
|
||||
|
|
||||
private: |
|
||||
Memory& m_memoryMan; |
|
||||
|
|
||||
llvm::Value* m_size; |
|
||||
|
|
||||
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs; |
|
||||
std::array<llvm::Value*, 8> m_argAllocas; |
|
||||
size_t m_argCounter = 0; |
|
||||
|
|
||||
llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args); |
|
||||
llvm::Value* getArgAlloca(); |
|
||||
llvm::Value* byPtr(llvm::Value* _value); |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,299 +0,0 @@ |
|||||
#include "GasMeter.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Ext.h" |
|
||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
namespace // Helper functions
|
|
||||
{ |
|
||||
|
|
||||
int64_t const c_stepGas[] = {0, 2, 3, 5, 8, 10, 20}; |
|
||||
int64_t const c_expByteGas = 10; |
|
||||
int64_t const c_sha3Gas = 30; |
|
||||
int64_t const c_sha3WordGas = 6; |
|
||||
int64_t const c_sloadGas = 50; |
|
||||
int64_t const c_sstoreSetGas = 20000; |
|
||||
int64_t const c_sstoreResetGas = 5000; |
|
||||
int64_t const c_sstoreClearGas = 5000; |
|
||||
int64_t const c_jumpdestGas = 1; |
|
||||
int64_t const c_logGas = 375; |
|
||||
int64_t const c_logTopicGas = 375; |
|
||||
int64_t const c_logDataGas = 8; |
|
||||
int64_t const c_callGas = 40; |
|
||||
int64_t const c_createGas = 32000; |
|
||||
int64_t const c_memoryGas = 3; |
|
||||
int64_t const c_copyGas = 3; |
|
||||
|
|
||||
int64_t getStepCost(Instruction inst) |
|
||||
{ |
|
||||
switch (inst) |
|
||||
{ |
|
||||
// Tier 0
|
|
||||
case Instruction::STOP: |
|
||||
case Instruction::RETURN: |
|
||||
case Instruction::SUICIDE: |
|
||||
case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore()
|
|
||||
return c_stepGas[0]; |
|
||||
|
|
||||
// Tier 1
|
|
||||
case Instruction::ADDRESS: |
|
||||
case Instruction::ORIGIN: |
|
||||
case Instruction::CALLER: |
|
||||
case Instruction::CALLVALUE: |
|
||||
case Instruction::CALLDATASIZE: |
|
||||
case Instruction::CODESIZE: |
|
||||
case Instruction::GASPRICE: |
|
||||
case Instruction::COINBASE: |
|
||||
case Instruction::TIMESTAMP: |
|
||||
case Instruction::NUMBER: |
|
||||
case Instruction::DIFFICULTY: |
|
||||
case Instruction::GASLIMIT: |
|
||||
case Instruction::POP: |
|
||||
case Instruction::PC: |
|
||||
case Instruction::MSIZE: |
|
||||
case Instruction::GAS: |
|
||||
return c_stepGas[1]; |
|
||||
|
|
||||
// Tier 2
|
|
||||
case Instruction::ADD: |
|
||||
case Instruction::SUB: |
|
||||
case Instruction::LT: |
|
||||
case Instruction::GT: |
|
||||
case Instruction::SLT: |
|
||||
case Instruction::SGT: |
|
||||
case Instruction::EQ: |
|
||||
case Instruction::ISZERO: |
|
||||
case Instruction::AND: |
|
||||
case Instruction::OR: |
|
||||
case Instruction::XOR: |
|
||||
case Instruction::NOT: |
|
||||
case Instruction::BYTE: |
|
||||
case Instruction::CALLDATALOAD: |
|
||||
case Instruction::CALLDATACOPY: |
|
||||
case Instruction::CODECOPY: |
|
||||
case Instruction::MLOAD: |
|
||||
case Instruction::MSTORE: |
|
||||
case Instruction::MSTORE8: |
|
||||
case Instruction::ANY_PUSH: |
|
||||
case Instruction::ANY_DUP: |
|
||||
case Instruction::ANY_SWAP: |
|
||||
return c_stepGas[2]; |
|
||||
|
|
||||
// Tier 3
|
|
||||
case Instruction::MUL: |
|
||||
case Instruction::DIV: |
|
||||
case Instruction::SDIV: |
|
||||
case Instruction::MOD: |
|
||||
case Instruction::SMOD: |
|
||||
case Instruction::SIGNEXTEND: |
|
||||
return c_stepGas[3]; |
|
||||
|
|
||||
// Tier 4
|
|
||||
case Instruction::ADDMOD: |
|
||||
case Instruction::MULMOD: |
|
||||
case Instruction::JUMP: |
|
||||
return c_stepGas[4]; |
|
||||
|
|
||||
// Tier 5
|
|
||||
case Instruction::EXP: |
|
||||
case Instruction::JUMPI: |
|
||||
return c_stepGas[5]; |
|
||||
|
|
||||
// Tier 6
|
|
||||
case Instruction::BALANCE: |
|
||||
case Instruction::EXTCODESIZE: |
|
||||
case Instruction::EXTCODECOPY: |
|
||||
case Instruction::BLOCKHASH: |
|
||||
return c_stepGas[6]; |
|
||||
|
|
||||
case Instruction::SHA3: |
|
||||
return c_sha3Gas; |
|
||||
|
|
||||
case Instruction::SLOAD: |
|
||||
return c_sloadGas; |
|
||||
|
|
||||
case Instruction::JUMPDEST: |
|
||||
return c_jumpdestGas; |
|
||||
|
|
||||
case Instruction::LOG0: |
|
||||
case Instruction::LOG1: |
|
||||
case Instruction::LOG2: |
|
||||
case Instruction::LOG3: |
|
||||
case Instruction::LOG4: |
|
||||
{ |
|
||||
auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0); |
|
||||
return c_logGas + numTopics * c_logTopicGas; |
|
||||
} |
|
||||
|
|
||||
case Instruction::CALL: |
|
||||
case Instruction::CALLCODE: |
|
||||
return c_callGas; |
|
||||
|
|
||||
case Instruction::CREATE: |
|
||||
return c_createGas; |
|
||||
} |
|
||||
|
|
||||
return 0; // TODO: Add UNREACHABLE macro
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager) : |
|
||||
CompilerHelper(_builder), |
|
||||
m_runtimeManager(_runtimeManager) |
|
||||
{ |
|
||||
llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr}; |
|
||||
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule()); |
|
||||
m_gasCheckFunc->setDoesNotThrow(); |
|
||||
m_gasCheckFunc->setDoesNotCapture(1); |
|
||||
|
|
||||
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); |
|
||||
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); |
|
||||
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); |
|
||||
|
|
||||
auto gasPtr = &m_gasCheckFunc->getArgumentList().front(); |
|
||||
gasPtr->setName("gasPtr"); |
|
||||
auto cost = gasPtr->getNextNode(); |
|
||||
cost->setName("cost"); |
|
||||
auto jmpBuf = cost->getNextNode(); |
|
||||
jmpBuf->setName("jmpBuf"); |
|
||||
|
|
||||
InsertPointGuard guard(m_builder); |
|
||||
m_builder.SetInsertPoint(checkBB); |
|
||||
auto gas = m_builder.CreateLoad(gasPtr, "gas"); |
|
||||
auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); |
|
||||
auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions
|
|
||||
m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue); |
|
||||
|
|
||||
m_builder.SetInsertPoint(updateBB); |
|
||||
m_builder.CreateStore(gasUpdated, gasPtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
|
|
||||
m_builder.SetInsertPoint(outOfGasBB); |
|
||||
m_runtimeManager.abort(jmpBuf); |
|
||||
m_builder.CreateUnreachable(); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::count(Instruction _inst) |
|
||||
{ |
|
||||
if (!m_checkCall) |
|
||||
{ |
|
||||
// Create gas check call with mocked block cost at begining of current cost-block
|
|
||||
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()}); |
|
||||
} |
|
||||
|
|
||||
m_blockCost += getStepCost(_inst); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) |
|
||||
{ |
|
||||
if (_cost->getType() == Type::Word) |
|
||||
{ |
|
||||
auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); |
|
||||
auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); |
|
||||
auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); |
|
||||
_cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); |
|
||||
} |
|
||||
|
|
||||
assert(_cost->getType() == Type::Gas); |
|
||||
createCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()}); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::countExp(llvm::Value* _exponent) |
|
||||
{ |
|
||||
// Additional cost is 1 per significant byte of exponent
|
|
||||
// lz - leading zeros
|
|
||||
// cost = ((256 - lz) + 7) / 8
|
|
||||
|
|
||||
// OPT: Can gas update be done in exp algorithm?
|
|
||||
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); |
|
||||
auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); |
|
||||
auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); |
|
||||
auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); |
|
||||
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); |
|
||||
count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas))); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) |
|
||||
{ |
|
||||
auto oldValue = _ext.sload(_index); |
|
||||
auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); |
|
||||
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); |
|
||||
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); |
|
||||
static_assert(c_sstoreResetGas == c_sstoreClearGas, "Update SSTORE gas cost"); |
|
||||
auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost"); |
|
||||
count(cost); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::countLogData(llvm::Value* _dataLength) |
|
||||
{ |
|
||||
assert(m_checkCall); |
|
||||
assert(m_blockCost > 0); // LOGn instruction is already counted
|
|
||||
static_assert(c_logDataGas != 1, "Log data gas cost has changed. Update GasMeter."); |
|
||||
count(m_builder.CreateNUWMul(_dataLength, Constant::get(c_logDataGas))); // TODO: Use i64
|
|
||||
} |
|
||||
|
|
||||
void GasMeter::countSha3Data(llvm::Value* _dataLength) |
|
||||
{ |
|
||||
assert(m_checkCall); |
|
||||
assert(m_blockCost > 0); // SHA3 instruction is already counted
|
|
||||
|
|
||||
// TODO: This round ups to 32 happens in many places
|
|
||||
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); |
|
||||
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas); |
|
||||
auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); |
|
||||
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); |
|
||||
count(cost64); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::giveBack(llvm::Value* _gas) |
|
||||
{ |
|
||||
assert(_gas->getType() == Type::Gas); |
|
||||
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::commitCostBlock() |
|
||||
{ |
|
||||
// If any uncommited block
|
|
||||
if (m_checkCall) |
|
||||
{ |
|
||||
if (m_blockCost == 0) // Do not check 0
|
|
||||
{ |
|
||||
m_checkCall->eraseFromParent(); // Remove the gas check call
|
|
||||
m_checkCall = nullptr; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
|
|
||||
m_checkCall = nullptr; // End cost-block
|
|
||||
m_blockCost = 0; |
|
||||
} |
|
||||
assert(m_blockCost == 0); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) |
|
||||
{ |
|
||||
static_assert(c_memoryGas != 1, "Memory gas cost has changed. Update GasMeter."); |
|
||||
count(_additionalMemoryInWords, _jmpBuf, _gasPtr); |
|
||||
} |
|
||||
|
|
||||
void GasMeter::countCopy(llvm::Value* _copyWords) |
|
||||
{ |
|
||||
static_assert(c_copyGas != 1, "Copy gas cost has changed. Update GasMeter."); |
|
||||
count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(c_copyGas))); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,64 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "CompilerHelper.h" |
|
||||
#include "Instruction.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
class RuntimeManager; |
|
||||
using namespace evmjit; |
|
||||
|
|
||||
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
|
|
||||
{ |
|
||||
public: |
|
||||
GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager); |
|
||||
|
|
||||
/// Count step cost of instruction
|
|
||||
void count(Instruction _inst); |
|
||||
|
|
||||
/// Count additional cost
|
|
||||
void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr); |
|
||||
|
|
||||
/// Calculate & count gas cost for SSTORE instruction
|
|
||||
void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); |
|
||||
|
|
||||
/// Calculate & count additional gas cost for EXP instruction
|
|
||||
void countExp(llvm::Value* _exponent); |
|
||||
|
|
||||
/// Count gas cost of LOG data
|
|
||||
void countLogData(llvm::Value* _dataLength); |
|
||||
|
|
||||
/// Count gas cost of SHA3 data
|
|
||||
void countSha3Data(llvm::Value* _dataLength); |
|
||||
|
|
||||
/// Finalize cost-block by checking gas needed for the block before the block
|
|
||||
void commitCostBlock(); |
|
||||
|
|
||||
/// Give back an amount of gas not used by a call
|
|
||||
void giveBack(llvm::Value* _gas); |
|
||||
|
|
||||
/// Generate code that checks the cost of additional memory used by program
|
|
||||
void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr); |
|
||||
|
|
||||
/// Count addional gas cost for memory copy
|
|
||||
void countCopy(llvm::Value* _copyWords); |
|
||||
|
|
||||
private: |
|
||||
/// Cumulative gas cost of a block of instructions
|
|
||||
/// @TODO Handle overflow
|
|
||||
int64_t m_blockCost = 0; |
|
||||
|
|
||||
llvm::CallInst* m_checkCall = nullptr; |
|
||||
llvm::Function* m_gasCheckFunc = nullptr; |
|
||||
|
|
||||
RuntimeManager& m_runtimeManager; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,39 +0,0 @@ |
|||||
#include "Instruction.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/ADT/APInt.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) |
|
||||
{ |
|
||||
auto pushInst = *_curr; |
|
||||
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); |
|
||||
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1; |
|
||||
llvm::APInt value(256, 0); |
|
||||
++_curr; // Point the data
|
|
||||
for (decltype(numBytes) i = 0; i < numBytes; ++i) |
|
||||
{ |
|
||||
byte b = (_curr != _end) ? *_curr++ : 0; |
|
||||
value <<= 8; |
|
||||
value |= b; |
|
||||
} |
|
||||
--_curr; // Point the last real byte read
|
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
void skipPushData(code_iterator& _curr, code_iterator _end) |
|
||||
{ |
|
||||
auto pushInst = *_curr; |
|
||||
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); |
|
||||
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1; |
|
||||
--_end; |
|
||||
for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,236 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "Common.h" |
|
||||
|
|
||||
namespace llvm |
|
||||
{ |
|
||||
class APInt; |
|
||||
} |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
/// Virtual machine bytecode instruction.
|
|
||||
enum class Instruction: uint8_t |
|
||||
{ |
|
||||
STOP = 0x00, ///< halts execution
|
|
||||
ADD, ///< addition operation
|
|
||||
MUL, ///< mulitplication operation
|
|
||||
SUB, ///< subtraction operation
|
|
||||
DIV, ///< integer division operation
|
|
||||
SDIV, ///< signed integer division operation
|
|
||||
MOD, ///< modulo remainder operation
|
|
||||
SMOD, ///< signed modulo remainder operation
|
|
||||
ADDMOD, ///< unsigned modular addition
|
|
||||
MULMOD, ///< unsigned modular multiplication
|
|
||||
EXP, ///< exponential operation
|
|
||||
SIGNEXTEND, ///< extend length of signed integer
|
|
||||
|
|
||||
LT = 0x10, ///< less-than comparision
|
|
||||
GT, ///< greater-than comparision
|
|
||||
SLT, ///< signed less-than comparision
|
|
||||
SGT, ///< signed greater-than comparision
|
|
||||
EQ, ///< equality comparision
|
|
||||
ISZERO, ///< simple not operator
|
|
||||
AND, ///< bitwise AND operation
|
|
||||
OR, ///< bitwise OR operation
|
|
||||
XOR, ///< bitwise XOR operation
|
|
||||
NOT, ///< bitwise NOT opertation
|
|
||||
BYTE, ///< retrieve single byte from word
|
|
||||
|
|
||||
SHA3 = 0x20, ///< compute SHA3-256 hash
|
|
||||
|
|
||||
ADDRESS = 0x30, ///< get address of currently executing account
|
|
||||
BALANCE, ///< get balance of the given account
|
|
||||
ORIGIN, ///< get execution origination address
|
|
||||
CALLER, ///< get caller address
|
|
||||
CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution
|
|
||||
CALLDATALOAD, ///< get input data of current environment
|
|
||||
CALLDATASIZE, ///< get size of input data in current environment
|
|
||||
CALLDATACOPY, ///< copy input data in current environment to memory
|
|
||||
CODESIZE, ///< get size of code running in current environment
|
|
||||
CODECOPY, ///< copy code running in current environment to memory
|
|
||||
GASPRICE, ///< get price of gas in current environment
|
|
||||
EXTCODESIZE, ///< get external code size (from another contract)
|
|
||||
EXTCODECOPY, ///< copy external code (from another contract)
|
|
||||
|
|
||||
BLOCKHASH = 0x40, ///< get hash of most recent complete block
|
|
||||
COINBASE, ///< get the block's coinbase address
|
|
||||
TIMESTAMP, ///< get the block's timestamp
|
|
||||
NUMBER, ///< get the block's number
|
|
||||
DIFFICULTY, ///< get the block's difficulty
|
|
||||
GASLIMIT, ///< get the block's gas limit
|
|
||||
|
|
||||
POP = 0x50, ///< remove item from stack
|
|
||||
MLOAD, ///< load word from memory
|
|
||||
MSTORE, ///< save word to memory
|
|
||||
MSTORE8, ///< save byte to memory
|
|
||||
SLOAD, ///< load word from storage
|
|
||||
SSTORE, ///< save word to storage
|
|
||||
JUMP, ///< alter the program counter
|
|
||||
JUMPI, ///< conditionally alter the program counter
|
|
||||
PC, ///< get the program counter
|
|
||||
MSIZE, ///< get the size of active memory
|
|
||||
GAS, ///< get the amount of available gas
|
|
||||
JUMPDEST, ///< set a potential jump destination
|
|
||||
|
|
||||
PUSH1 = 0x60, ///< place 1 byte item on stack
|
|
||||
PUSH2, ///< place 2 byte item on stack
|
|
||||
PUSH3, ///< place 3 byte item on stack
|
|
||||
PUSH4, ///< place 4 byte item on stack
|
|
||||
PUSH5, ///< place 5 byte item on stack
|
|
||||
PUSH6, ///< place 6 byte item on stack
|
|
||||
PUSH7, ///< place 7 byte item on stack
|
|
||||
PUSH8, ///< place 8 byte item on stack
|
|
||||
PUSH9, ///< place 9 byte item on stack
|
|
||||
PUSH10, ///< place 10 byte item on stack
|
|
||||
PUSH11, ///< place 11 byte item on stack
|
|
||||
PUSH12, ///< place 12 byte item on stack
|
|
||||
PUSH13, ///< place 13 byte item on stack
|
|
||||
PUSH14, ///< place 14 byte item on stack
|
|
||||
PUSH15, ///< place 15 byte item on stack
|
|
||||
PUSH16, ///< place 16 byte item on stack
|
|
||||
PUSH17, ///< place 17 byte item on stack
|
|
||||
PUSH18, ///< place 18 byte item on stack
|
|
||||
PUSH19, ///< place 19 byte item on stack
|
|
||||
PUSH20, ///< place 20 byte item on stack
|
|
||||
PUSH21, ///< place 21 byte item on stack
|
|
||||
PUSH22, ///< place 22 byte item on stack
|
|
||||
PUSH23, ///< place 23 byte item on stack
|
|
||||
PUSH24, ///< place 24 byte item on stack
|
|
||||
PUSH25, ///< place 25 byte item on stack
|
|
||||
PUSH26, ///< place 26 byte item on stack
|
|
||||
PUSH27, ///< place 27 byte item on stack
|
|
||||
PUSH28, ///< place 28 byte item on stack
|
|
||||
PUSH29, ///< place 29 byte item on stack
|
|
||||
PUSH30, ///< place 30 byte item on stack
|
|
||||
PUSH31, ///< place 31 byte item on stack
|
|
||||
PUSH32, ///< place 32 byte item on stack
|
|
||||
|
|
||||
DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack
|
|
||||
DUP2, ///< copies the second highest item in the stack to the top of the stack
|
|
||||
DUP3, ///< copies the third highest item in the stack to the top of the stack
|
|
||||
DUP4, ///< copies the 4th highest item in the stack to the top of the stack
|
|
||||
DUP5, ///< copies the 5th highest item in the stack to the top of the stack
|
|
||||
DUP6, ///< copies the 6th highest item in the stack to the top of the stack
|
|
||||
DUP7, ///< copies the 7th highest item in the stack to the top of the stack
|
|
||||
DUP8, ///< copies the 8th highest item in the stack to the top of the stack
|
|
||||
DUP9, ///< copies the 9th highest item in the stack to the top of the stack
|
|
||||
DUP10, ///< copies the 10th highest item in the stack to the top of the stack
|
|
||||
DUP11, ///< copies the 11th highest item in the stack to the top of the stack
|
|
||||
DUP12, ///< copies the 12th highest item in the stack to the top of the stack
|
|
||||
DUP13, ///< copies the 13th highest item in the stack to the top of the stack
|
|
||||
DUP14, ///< copies the 14th highest item in the stack to the top of the stack
|
|
||||
DUP15, ///< copies the 15th highest item in the stack to the top of the stack
|
|
||||
DUP16, ///< copies the 16th highest item in the stack to the top of the stack
|
|
||||
|
|
||||
SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack
|
|
||||
SWAP2, ///< swaps the highest and third highest value on the stack
|
|
||||
SWAP3, ///< swaps the highest and 4th highest value on the stack
|
|
||||
SWAP4, ///< swaps the highest and 5th highest value on the stack
|
|
||||
SWAP5, ///< swaps the highest and 6th highest value on the stack
|
|
||||
SWAP6, ///< swaps the highest and 7th highest value on the stack
|
|
||||
SWAP7, ///< swaps the highest and 8th highest value on the stack
|
|
||||
SWAP8, ///< swaps the highest and 9th highest value on the stack
|
|
||||
SWAP9, ///< swaps the highest and 10th highest value on the stack
|
|
||||
SWAP10, ///< swaps the highest and 11th highest value on the stack
|
|
||||
SWAP11, ///< swaps the highest and 12th highest value on the stack
|
|
||||
SWAP12, ///< swaps the highest and 13th highest value on the stack
|
|
||||
SWAP13, ///< swaps the highest and 14th highest value on the stack
|
|
||||
SWAP14, ///< swaps the highest and 15th highest value on the stack
|
|
||||
SWAP15, ///< swaps the highest and 16th highest value on the stack
|
|
||||
SWAP16, ///< swaps the highest and 17th highest value on the stack
|
|
||||
|
|
||||
LOG0 = 0xa0, ///< Makes a log entry; no topics.
|
|
||||
LOG1, ///< Makes a log entry; 1 topic.
|
|
||||
LOG2, ///< Makes a log entry; 2 topics.
|
|
||||
LOG3, ///< Makes a log entry; 3 topics.
|
|
||||
LOG4, ///< Makes a log entry; 4 topics.
|
|
||||
|
|
||||
CREATE = 0xf0, ///< create a new account with associated code
|
|
||||
CALL, ///< message-call into an account
|
|
||||
CALLCODE, ///< message-call with another account's code only
|
|
||||
RETURN, ///< halt execution returning output data
|
|
||||
SUICIDE = 0xff ///< halt execution and register account for later deletion
|
|
||||
}; |
|
||||
|
|
||||
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
|
|
||||
/// Reading out of bytecode means reading 0
|
|
||||
/// @param _curr is updated and points the last real byte read
|
|
||||
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end); |
|
||||
|
|
||||
/// Skips PUSH data in pointed fragment of bytecode.
|
|
||||
/// @param _curr is updated and points the last real byte skipped
|
|
||||
void skipPushData(code_iterator& _curr, code_iterator _end); |
|
||||
|
|
||||
#define ANY_PUSH PUSH1: \ |
|
||||
case Instruction::PUSH2: \ |
|
||||
case Instruction::PUSH3: \ |
|
||||
case Instruction::PUSH4: \ |
|
||||
case Instruction::PUSH5: \ |
|
||||
case Instruction::PUSH6: \ |
|
||||
case Instruction::PUSH7: \ |
|
||||
case Instruction::PUSH8: \ |
|
||||
case Instruction::PUSH9: \ |
|
||||
case Instruction::PUSH10: \ |
|
||||
case Instruction::PUSH11: \ |
|
||||
case Instruction::PUSH12: \ |
|
||||
case Instruction::PUSH13: \ |
|
||||
case Instruction::PUSH14: \ |
|
||||
case Instruction::PUSH15: \ |
|
||||
case Instruction::PUSH16: \ |
|
||||
case Instruction::PUSH17: \ |
|
||||
case Instruction::PUSH18: \ |
|
||||
case Instruction::PUSH19: \ |
|
||||
case Instruction::PUSH20: \ |
|
||||
case Instruction::PUSH21: \ |
|
||||
case Instruction::PUSH22: \ |
|
||||
case Instruction::PUSH23: \ |
|
||||
case Instruction::PUSH24: \ |
|
||||
case Instruction::PUSH25: \ |
|
||||
case Instruction::PUSH26: \ |
|
||||
case Instruction::PUSH27: \ |
|
||||
case Instruction::PUSH28: \ |
|
||||
case Instruction::PUSH29: \ |
|
||||
case Instruction::PUSH30: \ |
|
||||
case Instruction::PUSH31: \ |
|
||||
case Instruction::PUSH32 |
|
||||
|
|
||||
#define ANY_DUP DUP1: \ |
|
||||
case Instruction::DUP2: \ |
|
||||
case Instruction::DUP3: \ |
|
||||
case Instruction::DUP4: \ |
|
||||
case Instruction::DUP5: \ |
|
||||
case Instruction::DUP6: \ |
|
||||
case Instruction::DUP7: \ |
|
||||
case Instruction::DUP8: \ |
|
||||
case Instruction::DUP9: \ |
|
||||
case Instruction::DUP10: \ |
|
||||
case Instruction::DUP11: \ |
|
||||
case Instruction::DUP12: \ |
|
||||
case Instruction::DUP13: \ |
|
||||
case Instruction::DUP14: \ |
|
||||
case Instruction::DUP15: \ |
|
||||
case Instruction::DUP16 |
|
||||
|
|
||||
#define ANY_SWAP SWAP1: \ |
|
||||
case Instruction::SWAP2: \ |
|
||||
case Instruction::SWAP3: \ |
|
||||
case Instruction::SWAP4: \ |
|
||||
case Instruction::SWAP5: \ |
|
||||
case Instruction::SWAP6: \ |
|
||||
case Instruction::SWAP7: \ |
|
||||
case Instruction::SWAP8: \ |
|
||||
case Instruction::SWAP9: \ |
|
||||
case Instruction::SWAP10: \ |
|
||||
case Instruction::SWAP11: \ |
|
||||
case Instruction::SWAP12: \ |
|
||||
case Instruction::SWAP13: \ |
|
||||
case Instruction::SWAP14: \ |
|
||||
case Instruction::SWAP15: \ |
|
||||
case Instruction::SWAP16 |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,48 +0,0 @@ |
|||||
#include <evmjit/JIT-c.h> |
|
||||
#include <cassert> |
|
||||
#include <evmjit/JIT.h> |
|
||||
|
|
||||
extern "C" |
|
||||
{ |
|
||||
using namespace dev::evmjit; |
|
||||
|
|
||||
evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env) |
|
||||
{ |
|
||||
auto data = reinterpret_cast<RuntimeData*>(_data); |
|
||||
auto env = reinterpret_cast<Env*>(_env); |
|
||||
|
|
||||
assert(!data && "Pointer to runtime data must not be null"); |
|
||||
if (!data) |
|
||||
return nullptr; |
|
||||
|
|
||||
// TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow
|
|
||||
auto context = new(std::nothrow) ExecutionContext{*data, env}; |
|
||||
return reinterpret_cast<evmjit_context*>(context); |
|
||||
} |
|
||||
|
|
||||
void evmjit_destroy(evmjit_context* _context) |
|
||||
{ |
|
||||
auto context = reinterpret_cast<ExecutionContext*>(_context); |
|
||||
delete context; |
|
||||
} |
|
||||
|
|
||||
evmjit_return_code evmjit_exec(evmjit_context* _context) |
|
||||
{ |
|
||||
auto context = reinterpret_cast<ExecutionContext*>(_context); |
|
||||
|
|
||||
assert(!context && "Invalid context"); |
|
||||
if (!context) |
|
||||
return UnexpectedException; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
auto returnCode = JIT::exec(*context); |
|
||||
return static_cast<evmjit_return_code>(returnCode); |
|
||||
} |
|
||||
catch(...) |
|
||||
{ |
|
||||
return UnexpectedException; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,252 +0,0 @@ |
|||||
#include "evmjit/JIT.h" |
|
||||
|
|
||||
#include <array> |
|
||||
#include <mutex> |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Module.h> |
|
||||
#include <llvm/ADT/Triple.h> |
|
||||
#include <llvm/ExecutionEngine/MCJIT.h> |
|
||||
#include <llvm/Support/TargetSelect.h> |
|
||||
#include <llvm/Support/Host.h> |
|
||||
#include <llvm/Support/CommandLine.h> |
|
||||
#include <llvm/Support/ManagedStatic.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Compiler.h" |
|
||||
#include "Optimizer.h" |
|
||||
#include "Cache.h" |
|
||||
#include "ExecStats.h" |
|
||||
#include "Utils.h" |
|
||||
#include "BuildInfo.gen.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
using namespace eth::jit; |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
using ExecFunc = ReturnCode(*)(ExecutionContext*); |
|
||||
|
|
||||
std::string hash2str(i256 const& _hash) |
|
||||
{ |
|
||||
static const auto size = sizeof(_hash); |
|
||||
static const auto hexChars = "0123456789abcdef"; |
|
||||
std::string str; |
|
||||
str.resize(size * 2); |
|
||||
auto outIt = str.rbegin(); // reverse for BE
|
|
||||
auto& arr = *(std::array<byte, size>*)&_hash; |
|
||||
for (auto b : arr) |
|
||||
{ |
|
||||
*(outIt++) = hexChars[b & 0xf]; |
|
||||
*(outIt++) = hexChars[b >> 4]; |
|
||||
} |
|
||||
return str; |
|
||||
} |
|
||||
|
|
||||
void printVersion() |
|
||||
{ |
|
||||
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" |
|
||||
<< " EVMJIT version " << EVMJIT_VERSION << "\n" |
|
||||
#ifdef NDEBUG |
|
||||
<< " Optimized build, " |
|
||||
#else |
|
||||
<< " DEBUG build, " |
|
||||
#endif |
|
||||
<< __DATE__ << " (" << __TIME__ << ")\n" |
|
||||
<< std::endl; |
|
||||
} |
|
||||
|
|
||||
namespace cl = llvm::cl; |
|
||||
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}}; |
|
||||
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, |
|
||||
cl::values( |
|
||||
clEnumValN(CacheMode::on, "1", "Enabled"), |
|
||||
clEnumValN(CacheMode::off, "0", "Disabled"), |
|
||||
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), |
|
||||
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), |
|
||||
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), |
|
||||
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), |
|
||||
clEnumValEnd)}; |
|
||||
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}}; |
|
||||
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; |
|
||||
|
|
||||
void parseOptions() |
|
||||
{ |
|
||||
static llvm::llvm_shutdown_obj shutdownObj{}; |
|
||||
cl::AddExtraVersionPrinter(printVersion); |
|
||||
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); |
|
||||
} |
|
||||
|
|
||||
class JITImpl |
|
||||
{ |
|
||||
std::unique_ptr<llvm::ExecutionEngine> m_engine; |
|
||||
mutable std::mutex x_codeMap; |
|
||||
std::unordered_map<h256, ExecFunc> m_codeMap; |
|
||||
|
|
||||
public: |
|
||||
static JITImpl& instance() |
|
||||
{ |
|
||||
static JITImpl s_instance; |
|
||||
return s_instance; |
|
||||
} |
|
||||
|
|
||||
JITImpl(); |
|
||||
|
|
||||
llvm::ExecutionEngine& engine() { return *m_engine; } |
|
||||
|
|
||||
ExecFunc getExecFunc(h256 const& _codeHash) const; |
|
||||
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr); |
|
||||
|
|
||||
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); |
|
||||
}; |
|
||||
|
|
||||
JITImpl::JITImpl() |
|
||||
{ |
|
||||
parseOptions(); |
|
||||
|
|
||||
bool preloadCache = g_cache == CacheMode::preload; |
|
||||
if (preloadCache) |
|
||||
g_cache = CacheMode::on; |
|
||||
|
|
||||
llvm::InitializeNativeTarget(); |
|
||||
llvm::InitializeNativeTargetAsmPrinter(); |
|
||||
|
|
||||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext())); |
|
||||
|
|
||||
// FIXME: LLVM 3.7: test on Windows
|
|
||||
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); |
|
||||
if (triple.getOS() == llvm::Triple::OSType::Win32) |
|
||||
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
|
|
||||
module->setTargetTriple(triple.str()); |
|
||||
|
|
||||
llvm::EngineBuilder builder(std::move(module)); |
|
||||
builder.setEngineKind(llvm::EngineKind::JIT); |
|
||||
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); |
|
||||
|
|
||||
m_engine.reset(builder.create()); |
|
||||
|
|
||||
// TODO: Update cache listener
|
|
||||
m_engine->setObjectCache(Cache::init(g_cache, nullptr)); |
|
||||
|
|
||||
// FIXME: Disabled during API changes
|
|
||||
//if (preloadCache)
|
|
||||
// Cache::preload(*m_engine, funcCache);
|
|
||||
} |
|
||||
|
|
||||
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const |
|
||||
{ |
|
||||
std::lock_guard<std::mutex> lock{x_codeMap}; |
|
||||
auto it = m_codeMap.find(_codeHash); |
|
||||
if (it != m_codeMap.end()) |
|
||||
return it->second; |
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr) |
|
||||
{ |
|
||||
std::lock_guard<std::mutex> lock{x_codeMap}; |
|
||||
m_codeMap.emplace(std::move(_codeHash), _funcAddr); |
|
||||
} |
|
||||
|
|
||||
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) |
|
||||
{ |
|
||||
auto name = hash2str(_codeHash); |
|
||||
auto module = Cache::getObject(name); |
|
||||
if (!module) |
|
||||
{ |
|
||||
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
|
|
||||
//listener->stateChanged(ExecState::Compilation);
|
|
||||
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
|
|
||||
module = Compiler{{}}.compile(_code, _code + _codeSize, name); |
|
||||
|
|
||||
if (g_optimize) |
|
||||
{ |
|
||||
//listener->stateChanged(ExecState::Optimization);
|
|
||||
optimize(*module); |
|
||||
} |
|
||||
|
|
||||
prepare(*module); |
|
||||
} |
|
||||
if (g_dump) |
|
||||
module->dump(); |
|
||||
|
|
||||
m_engine->addModule(std::move(module)); |
|
||||
//listener->stateChanged(ExecState::CodeGen);
|
|
||||
return (ExecFunc)m_engine->getFunctionAddress(name); |
|
||||
} |
|
||||
|
|
||||
} // anonymous namespace
|
|
||||
|
|
||||
bool JIT::isCodeReady(h256 const& _codeHash) |
|
||||
{ |
|
||||
return JITImpl::instance().getExecFunc(_codeHash) != nullptr; |
|
||||
} |
|
||||
|
|
||||
void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) |
|
||||
{ |
|
||||
auto& jit = JITImpl::instance(); |
|
||||
auto execFunc = jit.compile(_code, _codeSize, _codeHash); |
|
||||
if (execFunc) // FIXME: What with error?
|
|
||||
jit.mapExecFunc(_codeHash, execFunc); |
|
||||
} |
|
||||
|
|
||||
ReturnCode JIT::exec(ExecutionContext& _context) |
|
||||
{ |
|
||||
//std::unique_ptr<ExecStats> listener{new ExecStats};
|
|
||||
//listener->stateChanged(ExecState::Started);
|
|
||||
//static StatsCollector statsCollector;
|
|
||||
|
|
||||
auto& jit = JITImpl::instance(); |
|
||||
auto codeHash = _context.codeHash(); |
|
||||
auto execFunc = jit.getExecFunc(codeHash); |
|
||||
if (!execFunc) |
|
||||
{ |
|
||||
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash); |
|
||||
if (!execFunc) |
|
||||
return ReturnCode::LLVMError; |
|
||||
jit.mapExecFunc(codeHash, execFunc); |
|
||||
} |
|
||||
|
|
||||
//listener->stateChanged(ExecState::Execution);
|
|
||||
auto returnCode = execFunc(&_context); |
|
||||
//listener->stateChanged(ExecState::Return);
|
|
||||
|
|
||||
if (returnCode == ReturnCode::Return) |
|
||||
_context.returnData = _context.getReturnData(); // Save reference to return data
|
|
||||
|
|
||||
//listener->stateChanged(ExecState::Finished);
|
|
||||
// if (g_stats)
|
|
||||
// statsCollector.stats.push_back(std::move(listener));
|
|
||||
|
|
||||
return returnCode; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
extern "C" void ext_free(void* _data) noexcept; |
|
||||
|
|
||||
ExecutionContext::~ExecutionContext() noexcept |
|
||||
{ |
|
||||
if (m_memData) |
|
||||
ext_free(m_memData); // Use helper free to check memory leaks
|
|
||||
} |
|
||||
|
|
||||
bytes_ref ExecutionContext::getReturnData() const |
|
||||
{ |
|
||||
auto data = m_data->callData; |
|
||||
auto size = static_cast<size_t>(m_data->callDataSize); |
|
||||
|
|
||||
if (data < m_memData || data >= m_memData + m_memSize || size == 0) |
|
||||
{ |
|
||||
assert(size == 0); // data can be an invalid pointer only if size is 0
|
|
||||
m_data->callData = nullptr; |
|
||||
return {}; |
|
||||
} |
|
||||
|
|
||||
return bytes_ref{data, size}; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -1,246 +0,0 @@ |
|||||
#include "Memory.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Type.h" |
|
||||
#include "GasMeter.h" |
|
||||
#include "Endianness.h" |
|
||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
|
||||
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
|
||||
m_memory{getBuilder(), _runtimeManager.getMem()}, |
|
||||
m_gasMeter(_gasMeter) |
|
||||
{} |
|
||||
|
|
||||
llvm::Function* Memory::getRequireFunc() |
|
||||
{ |
|
||||
auto& func = m_require; |
|
||||
if (!func) |
|
||||
{ |
|
||||
llvm::Type* argTypes[] = {Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr}; |
|
||||
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); |
|
||||
func->setDoesNotThrow(); |
|
||||
|
|
||||
auto mem = &func->getArgumentList().front(); |
|
||||
mem->setName("mem"); |
|
||||
auto blkOffset = mem->getNextNode(); |
|
||||
blkOffset->setName("blkOffset"); |
|
||||
auto blkSize = blkOffset->getNextNode(); |
|
||||
blkSize->setName("blkSize"); |
|
||||
auto jmpBuf = blkSize->getNextNode(); |
|
||||
jmpBuf->setName("jmpBuf"); |
|
||||
auto gas = jmpBuf->getNextNode(); |
|
||||
gas->setName("gas"); |
|
||||
|
|
||||
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
|
||||
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
|
||||
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
|
||||
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
|
||||
|
|
||||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|
||||
|
|
||||
// BB "Pre": Ignore checks with size 0
|
|
||||
m_builder.SetInsertPoint(preBB); |
|
||||
m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue); |
|
||||
|
|
||||
// BB "Check"
|
|
||||
m_builder.SetInsertPoint(checkBB); |
|
||||
static const auto c_inputMax = uint64_t(1) << 33; // max value of blkSize and blkOffset that will not result in integer overflow in calculations below
|
|
||||
auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk"); |
|
||||
auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO"); |
|
||||
auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk"); |
|
||||
auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS"); |
|
||||
|
|
||||
auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0"); |
|
||||
auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32
|
|
||||
auto sizeCur = m_memory.size(mem); |
|
||||
auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk"); |
|
||||
|
|
||||
m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue); |
|
||||
|
|
||||
// BB "Resize"
|
|
||||
m_builder.SetInsertPoint(resizeBB); |
|
||||
// Check gas first
|
|
||||
auto w1 = m_builder.CreateLShr(sizeReq, 5); |
|
||||
auto w1s = m_builder.CreateNUWMul(w1, w1); |
|
||||
auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9)); |
|
||||
auto w0 = m_builder.CreateLShr(sizeCur, 5); |
|
||||
auto w0s = m_builder.CreateNUWMul(w0, w0); |
|
||||
auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9)); |
|
||||
auto cc = m_builder.CreateNUWSub(c1, c0); |
|
||||
auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk"); |
|
||||
auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits<int64_t>::max()), "c"); |
|
||||
m_gasMeter.count(c, jmpBuf, gas); |
|
||||
// Resize
|
|
||||
m_memory.extend(mem, sizeReq); |
|
||||
m_builder.CreateBr(returnBB); |
|
||||
|
|
||||
// BB "Return"
|
|
||||
m_builder.SetInsertPoint(returnBB); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
} |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType) |
|
||||
{ |
|
||||
auto isWord = _valueType == Type::Word; |
|
||||
|
|
||||
llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType}; |
|
||||
llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word}; |
|
||||
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; |
|
||||
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); |
|
||||
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); |
|
||||
|
|
||||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|
||||
|
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
|
||||
auto mem = &func->getArgumentList().front(); |
|
||||
mem->setName("mem"); |
|
||||
auto index = mem->getNextNode(); |
|
||||
index->setName("index"); |
|
||||
|
|
||||
if (_isStore) |
|
||||
{ |
|
||||
auto valueArg = index->getNextNode(); |
|
||||
valueArg->setName("value"); |
|
||||
auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg; |
|
||||
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); |
|
||||
auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr"); |
|
||||
m_builder.CreateStore(value, valuePtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); |
|
||||
llvm::Value* ret = m_builder.CreateLoad(memPtr); |
|
||||
ret = Endianness::toNative(m_builder, ret); |
|
||||
m_builder.CreateRet(ret); |
|
||||
} |
|
||||
|
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Memory::getLoadWordFunc() |
|
||||
{ |
|
||||
auto& func = m_loadWord; |
|
||||
if (!func) |
|
||||
func = createFunc(false, Type::Word); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Memory::getStoreWordFunc() |
|
||||
{ |
|
||||
auto& func = m_storeWord; |
|
||||
if (!func) |
|
||||
func = createFunc(true, Type::Word); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
llvm::Function* Memory::getStoreByteFunc() |
|
||||
{ |
|
||||
auto& func = m_storeByte; |
|
||||
if (!func) |
|
||||
func = createFunc(true, Type::Byte); |
|
||||
return func; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
|
||||
{ |
|
||||
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); |
|
||||
return createCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr}); |
|
||||
} |
|
||||
|
|
||||
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
|
||||
{ |
|
||||
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); |
|
||||
createCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word}); |
|
||||
} |
|
||||
|
|
||||
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
|
||||
{ |
|
||||
require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8)); |
|
||||
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
|
||||
createCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte}); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Memory::getData() |
|
||||
{ |
|
||||
auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo()); |
|
||||
auto data = m_builder.CreateLoad(memPtr, "data"); |
|
||||
assert(data->getType() == Type::BytePtr); |
|
||||
return data; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* Memory::getSize() |
|
||||
{ |
|
||||
return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); // TODO: Allow placing i64 on stack
|
|
||||
} |
|
||||
|
|
||||
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
|
||||
{ |
|
||||
return m_builder.CreateGEP(getData(), _index, "ptr"); |
|
||||
} |
|
||||
|
|
||||
void Memory::require(llvm::Value* _offset, llvm::Value* _size) |
|
||||
{ |
|
||||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size)) |
|
||||
{ |
|
||||
if (!constant->getValue()) |
|
||||
return; |
|
||||
} |
|
||||
createCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()}); |
|
||||
} |
|
||||
|
|
||||
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, |
|
||||
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) |
|
||||
{ |
|
||||
require(_destMemIdx, _reqBytes); |
|
||||
|
|
||||
// Additional copy cost
|
|
||||
// TODO: This round ups to 32 happens in many places
|
|
||||
auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); |
|
||||
auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); |
|
||||
m_gasMeter.countCopy(copyWords); |
|
||||
|
|
||||
// Algorithm:
|
|
||||
// isOutsideData = idx256 >= size256
|
|
||||
// idx64 = trunc idx256
|
|
||||
// size64 = trunc size256
|
|
||||
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
|
||||
// reqBytes64 = trunc _reqBytes // require() handles large values
|
|
||||
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
|
|
||||
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
|
|
||||
|
|
||||
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); |
|
||||
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); |
|
||||
auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); |
|
||||
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
|
||||
auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); |
|
||||
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); |
|
||||
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy"); |
|
||||
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); |
|
||||
|
|
||||
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); |
|
||||
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); |
|
||||
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); |
|
||||
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); |
|
||||
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); |
|
||||
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); |
|
||||
m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "Array.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
class GasMeter; |
|
||||
|
|
||||
class Memory : public RuntimeHelper |
|
||||
{ |
|
||||
public: |
|
||||
Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); |
|
||||
|
|
||||
llvm::Value* loadWord(llvm::Value* _addr); |
|
||||
void storeWord(llvm::Value* _addr, llvm::Value* _word); |
|
||||
void storeByte(llvm::Value* _addr, llvm::Value* _byte); |
|
||||
llvm::Value* getData(); |
|
||||
llvm::Value* getSize(); |
|
||||
llvm::Value* getBytePtr(llvm::Value* _index); |
|
||||
void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, |
|
||||
llvm::Value* _destMemIdx, llvm::Value* _byteCount); |
|
||||
|
|
||||
/// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory.
|
|
||||
void require(llvm::Value* _offset, llvm::Value* _size); |
|
||||
|
|
||||
private: |
|
||||
Array m_memory; |
|
||||
|
|
||||
GasMeter& m_gasMeter; |
|
||||
|
|
||||
llvm::Function* createFunc(bool _isStore, llvm::Type* _type); |
|
||||
|
|
||||
llvm::Function* getRequireFunc(); |
|
||||
llvm::Function* getLoadWordFunc(); |
|
||||
llvm::Function* getStoreWordFunc(); |
|
||||
llvm::Function* getStoreByteFunc(); |
|
||||
|
|
||||
llvm::Function* m_require = nullptr; |
|
||||
llvm::Function* m_loadWord = nullptr; |
|
||||
llvm::Function* m_storeWord = nullptr; |
|
||||
llvm::Function* m_storeByte = nullptr; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,125 +0,0 @@ |
|||||
#include "Optimizer.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/BasicBlock.h> |
|
||||
#include <llvm/IR/Function.h> |
|
||||
#include <llvm/IR/LegacyPassManager.h> |
|
||||
#include <llvm/Transforms/Scalar.h> |
|
||||
#include <llvm/Transforms/IPO.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Arith256.h" |
|
||||
#include "Type.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
bool optimize(llvm::Module& _module) |
|
||||
{ |
|
||||
auto pm = llvm::legacy::PassManager{}; |
|
||||
pm.add(llvm::createFunctionInliningPass(2, 2)); |
|
||||
pm.add(llvm::createCFGSimplificationPass()); |
|
||||
pm.add(llvm::createInstructionCombiningPass()); |
|
||||
pm.add(llvm::createAggressiveDCEPass()); |
|
||||
pm.add(llvm::createLowerSwitchPass()); |
|
||||
return pm.run(_module); |
|
||||
} |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
|
|
||||
class LowerEVMPass: public llvm::BasicBlockPass |
|
||||
{ |
|
||||
static char ID; |
|
||||
|
|
||||
public: |
|
||||
LowerEVMPass(): |
|
||||
llvm::BasicBlockPass(ID) |
|
||||
{} |
|
||||
|
|
||||
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; |
|
||||
|
|
||||
using llvm::BasicBlockPass::doFinalization; |
|
||||
virtual bool doFinalization(llvm::Module& _module) override; |
|
||||
}; |
|
||||
|
|
||||
char LowerEVMPass::ID = 0; |
|
||||
|
|
||||
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) |
|
||||
{ |
|
||||
auto modified = false; |
|
||||
auto module = _bb.getParent()->getParent(); |
|
||||
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); |
|
||||
for (auto it = _bb.begin(); it != _bb.end(); ) |
|
||||
{ |
|
||||
auto& inst = *it++; |
|
||||
llvm::Function* func = nullptr; |
|
||||
if (inst.getType() == Type::Word) |
|
||||
{ |
|
||||
switch (inst.getOpcode()) |
|
||||
{ |
|
||||
case llvm::Instruction::Mul: |
|
||||
func = Arith256::getMulFunc(*module); |
|
||||
break; |
|
||||
|
|
||||
case llvm::Instruction::UDiv: |
|
||||
func = Arith256::getUDiv256Func(*module); |
|
||||
break; |
|
||||
|
|
||||
case llvm::Instruction::URem: |
|
||||
func = Arith256::getURem256Func(*module); |
|
||||
break; |
|
||||
|
|
||||
case llvm::Instruction::SDiv: |
|
||||
func = Arith256::getSDiv256Func(*module); |
|
||||
break; |
|
||||
|
|
||||
case llvm::Instruction::SRem: |
|
||||
func = Arith256::getSRem256Func(*module); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
else if (inst.getType() == i512Ty) |
|
||||
{ |
|
||||
switch (inst.getOpcode()) |
|
||||
{ |
|
||||
case llvm::Instruction::URem: |
|
||||
func = Arith256::getURem512Func(*module); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (func) |
|
||||
{ |
|
||||
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst); |
|
||||
inst.replaceAllUsesWith(call); |
|
||||
inst.eraseFromParent(); |
|
||||
modified = true; |
|
||||
} |
|
||||
} |
|
||||
return modified; |
|
||||
} |
|
||||
|
|
||||
bool LowerEVMPass::doFinalization(llvm::Module&) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
bool prepare(llvm::Module& _module) |
|
||||
{ |
|
||||
auto pm = llvm::legacy::PassManager{}; |
|
||||
pm.add(llvm::createCFGSimplificationPass()); |
|
||||
pm.add(llvm::createDeadCodeEliminationPass()); |
|
||||
pm.add(new LowerEVMPass{}); |
|
||||
return pm.run(_module); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,21 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
namespace llvm |
|
||||
{ |
|
||||
class Module; |
|
||||
} |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
bool optimize(llvm::Module& _module); |
|
||||
|
|
||||
bool prepare(llvm::Module& _module); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,302 +0,0 @@ |
|||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/IntrinsicInst.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "Stack.h" |
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
llvm::StructType* RuntimeManager::getRuntimeDataType() |
|
||||
{ |
|
||||
static llvm::StructType* type = nullptr; |
|
||||
if (!type) |
|
||||
{ |
|
||||
llvm::Type* elems[] = |
|
||||
{ |
|
||||
Type::Size, // gas
|
|
||||
Type::Size, // gasPrice
|
|
||||
Type::BytePtr, // callData
|
|
||||
Type::Size, // callDataSize
|
|
||||
Type::Word, // address
|
|
||||
Type::Word, // caller
|
|
||||
Type::Word, // origin
|
|
||||
Type::Word, // callValue
|
|
||||
Type::Word, // coinBase
|
|
||||
Type::Word, // difficulty
|
|
||||
Type::Word, // gasLimit
|
|
||||
Type::Size, // blockNumber
|
|
||||
Type::Size, // blockTimestamp
|
|
||||
Type::BytePtr, // code
|
|
||||
Type::Size, // codeSize
|
|
||||
}; |
|
||||
type = llvm::StructType::create(elems, "RuntimeData"); |
|
||||
} |
|
||||
return type; |
|
||||
} |
|
||||
|
|
||||
llvm::StructType* RuntimeManager::getRuntimeType() |
|
||||
{ |
|
||||
static llvm::StructType* type = nullptr; |
|
||||
if (!type) |
|
||||
{ |
|
||||
llvm::Type* elems[] = |
|
||||
{ |
|
||||
Type::RuntimeDataPtr, // data
|
|
||||
Type::EnvPtr, // Env*
|
|
||||
Array::getType() // memory
|
|
||||
}; |
|
||||
type = llvm::StructType::create(elems, "Runtime"); |
|
||||
} |
|
||||
return type; |
|
||||
} |
|
||||
|
|
||||
namespace |
|
||||
{ |
|
||||
llvm::Twine getName(RuntimeData::Index _index) |
|
||||
{ |
|
||||
switch (_index) |
|
||||
{ |
|
||||
default: return ""; |
|
||||
case RuntimeData::Gas: return "msg.gas"; |
|
||||
case RuntimeData::GasPrice: return "tx.gasprice"; |
|
||||
case RuntimeData::CallData: return "msg.data.ptr"; |
|
||||
case RuntimeData::CallDataSize: return "msg.data.size"; |
|
||||
case RuntimeData::Address: return "this.address"; |
|
||||
case RuntimeData::Caller: return "msg.caller"; |
|
||||
case RuntimeData::Origin: return "tx.origin"; |
|
||||
case RuntimeData::CallValue: return "msg.value"; |
|
||||
case RuntimeData::CoinBase: return "block.coinbase"; |
|
||||
case RuntimeData::Difficulty: return "block.difficulty"; |
|
||||
case RuntimeData::GasLimit: return "block.gaslimit"; |
|
||||
case RuntimeData::Number: return "block.number"; |
|
||||
case RuntimeData::Timestamp: return "block.timestamp"; |
|
||||
case RuntimeData::Code: return "code.ptr"; |
|
||||
case RuntimeData::CodeSize: return "code.size"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd): |
|
||||
CompilerHelper(_builder), |
|
||||
m_codeBegin(_codeBegin), |
|
||||
m_codeEnd(_codeEnd) |
|
||||
{ |
|
||||
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); |
|
||||
|
|
||||
// Unpack data
|
|
||||
auto rtPtr = getRuntimePtr(); |
|
||||
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); |
|
||||
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); |
|
||||
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); |
|
||||
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); |
|
||||
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); |
|
||||
assert(m_envPtr->getType() == Type::EnvPtr); |
|
||||
|
|
||||
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); |
|
||||
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); |
|
||||
|
|
||||
auto data = m_builder.CreateLoad(m_dataPtr, "data"); |
|
||||
for (unsigned i = 0; i < m_dataElts.size(); ++i) |
|
||||
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); |
|
||||
|
|
||||
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); |
|
||||
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); |
|
||||
|
|
||||
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; |
|
||||
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "evm.stack.require", getModule()); |
|
||||
m_checkStackLimit->setDoesNotThrow(); |
|
||||
m_checkStackLimit->setDoesNotCapture(1); |
|
||||
|
|
||||
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit); |
|
||||
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit); |
|
||||
auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit); |
|
||||
|
|
||||
auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); |
|
||||
currSizePtr->setName("currSize"); |
|
||||
auto min = currSizePtr->getNextNode(); |
|
||||
min->setName("min"); |
|
||||
auto max = min->getNextNode(); |
|
||||
max->setName("max"); |
|
||||
auto diff = max->getNextNode(); |
|
||||
diff->setName("diff"); |
|
||||
auto jmpBuf = diff->getNextNode(); |
|
||||
jmpBuf->setName("jmpBuf"); |
|
||||
|
|
||||
InsertPointGuard guard{m_builder}; |
|
||||
m_builder.SetInsertPoint(checkBB); |
|
||||
auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); |
|
||||
auto minSize = m_builder.CreateAdd(currSize, min, "minSize", false, true); |
|
||||
auto maxSize = m_builder.CreateAdd(currSize, max, "maxSize", true, true); |
|
||||
auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "min.ok"); |
|
||||
auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "max.ok"); |
|
||||
auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); |
|
||||
m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); |
|
||||
|
|
||||
m_builder.SetInsertPoint(updateBB); |
|
||||
auto newSize = m_builder.CreateNSWAdd(currSize, diff); |
|
||||
m_builder.CreateStore(newSize, currSizePtr); |
|
||||
m_builder.CreateRetVoid(); |
|
||||
|
|
||||
m_builder.SetInsertPoint(outOfStackBB); |
|
||||
abort(jmpBuf); |
|
||||
m_builder.CreateUnreachable(); |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff) |
|
||||
{ |
|
||||
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_min), m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getRuntimePtr() |
|
||||
{ |
|
||||
// Expect first argument of a function to be a pointer to Runtime
|
|
||||
auto func = m_builder.GetInsertBlock()->getParent(); |
|
||||
auto rtPtr = &func->getArgumentList().front(); |
|
||||
assert(rtPtr->getType() == Type::RuntimePtr); |
|
||||
return rtPtr; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getDataPtr() |
|
||||
{ |
|
||||
if (getMainFunction()) |
|
||||
return m_dataPtr; |
|
||||
|
|
||||
auto rtPtr = getRuntimePtr(); |
|
||||
auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); |
|
||||
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); |
|
||||
return dataPtr; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getEnvPtr() |
|
||||
{ |
|
||||
assert(getMainFunction()); // Available only in main function
|
|
||||
return m_envPtr; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) |
|
||||
{ |
|
||||
auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); |
|
||||
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); |
|
||||
return ptr; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::get(RuntimeData::Index _index) |
|
||||
{ |
|
||||
return m_dataElts[_index]; |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) |
|
||||
{ |
|
||||
auto ptr = getPtr(_index); |
|
||||
assert(ptr->getType() == _value->getType()->getPointerTo()); |
|
||||
getBuilder().CreateStore(_value, ptr); |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) |
|
||||
{ |
|
||||
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); |
|
||||
auto mem = getBuilder().CreateLoad(memPtr, "memory"); |
|
||||
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset); |
|
||||
set(RuntimeData::ReturnData, returnDataPtr); |
|
||||
|
|
||||
auto size64 = getBuilder().CreateTrunc(_size, Type::Size); |
|
||||
set(RuntimeData::ReturnDataSize, size64); |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) |
|
||||
{ |
|
||||
set(RuntimeData::SuicideDestAddress, _balanceAddress); |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::exit(ReturnCode _returnCode) |
|
||||
{ |
|
||||
if (m_stack) |
|
||||
m_stack->free(); |
|
||||
|
|
||||
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); |
|
||||
m_builder.CreateStore(getGas(), extGasPtr); |
|
||||
m_builder.CreateRet(Constant::get(_returnCode)); |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::abort(llvm::Value* _jmpBuf) |
|
||||
{ |
|
||||
auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); |
|
||||
createCall(longjmp, {_jmpBuf}); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::get(Instruction _inst) |
|
||||
{ |
|
||||
switch (_inst) |
|
||||
{ |
|
||||
default: assert(false); return nullptr; |
|
||||
case Instruction::ADDRESS: return get(RuntimeData::Address); |
|
||||
case Instruction::CALLER: return get(RuntimeData::Caller); |
|
||||
case Instruction::ORIGIN: return get(RuntimeData::Origin); |
|
||||
case Instruction::CALLVALUE: return get(RuntimeData::CallValue); |
|
||||
case Instruction::GASPRICE: return get(RuntimeData::GasPrice); |
|
||||
case Instruction::COINBASE: return get(RuntimeData::CoinBase); |
|
||||
case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); |
|
||||
case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); |
|
||||
case Instruction::NUMBER: return get(RuntimeData::Number); |
|
||||
case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getCallData() |
|
||||
{ |
|
||||
return get(RuntimeData::CallData); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getCode() |
|
||||
{ |
|
||||
// OPT Check what is faster
|
|
||||
//return get(RuntimeData::Code);
|
|
||||
return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code"); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getCodeSize() |
|
||||
{ |
|
||||
return Constant::get(m_codeEnd - m_codeBegin); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getCallDataSize() |
|
||||
{ |
|
||||
auto value = get(RuntimeData::CallDataSize); |
|
||||
assert(value->getType() == Type::Size); |
|
||||
return getBuilder().CreateZExt(value, Type::Word); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getGas() |
|
||||
{ |
|
||||
return getBuilder().CreateLoad(getGasPtr(), "gas"); |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getGasPtr() |
|
||||
{ |
|
||||
assert(getMainFunction()); |
|
||||
return m_gasPtr; |
|
||||
} |
|
||||
|
|
||||
llvm::Value* RuntimeManager::getMem() |
|
||||
{ |
|
||||
assert(getMainFunction()); |
|
||||
return m_memPtr; |
|
||||
} |
|
||||
|
|
||||
void RuntimeManager::setGas(llvm::Value* _gas) |
|
||||
{ |
|
||||
assert(_gas->getType() == Type::Gas); |
|
||||
getBuilder().CreateStore(_gas, getGasPtr()); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,79 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <array> |
|
||||
|
|
||||
#include "CompilerHelper.h" |
|
||||
#include "Type.h" |
|
||||
#include "Instruction.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
using namespace evmjit; |
|
||||
class Stack; |
|
||||
|
|
||||
class RuntimeManager: public CompilerHelper |
|
||||
{ |
|
||||
public: |
|
||||
RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd); |
|
||||
|
|
||||
llvm::Value* getRuntimePtr(); |
|
||||
llvm::Value* getDataPtr(); |
|
||||
llvm::Value* getEnvPtr(); |
|
||||
|
|
||||
llvm::Value* get(RuntimeData::Index _index); |
|
||||
llvm::Value* get(Instruction _inst); |
|
||||
llvm::Value* getGas(); |
|
||||
llvm::Value* getGasPtr(); |
|
||||
llvm::Value* getCallData(); |
|
||||
llvm::Value* getCode(); |
|
||||
llvm::Value* getCodeSize(); |
|
||||
llvm::Value* getCallDataSize(); |
|
||||
llvm::Value* getJmpBuf() { return m_jmpBuf; } |
|
||||
void setGas(llvm::Value* _gas); |
|
||||
|
|
||||
llvm::Value* getMem(); |
|
||||
|
|
||||
void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory.
|
|
||||
void registerSuicide(llvm::Value* _balanceAddress); |
|
||||
|
|
||||
void exit(ReturnCode _returnCode); |
|
||||
|
|
||||
void abort(llvm::Value* _jmpBuf); |
|
||||
|
|
||||
void setStack(Stack& _stack) { m_stack = &_stack; } |
|
||||
void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; } |
|
||||
|
|
||||
static llvm::StructType* getRuntimeType(); |
|
||||
static llvm::StructType* getRuntimeDataType(); |
|
||||
|
|
||||
void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff); |
|
||||
|
|
||||
private: |
|
||||
llvm::Value* getPtr(RuntimeData::Index _index); |
|
||||
void set(RuntimeData::Index _index, llvm::Value* _value); |
|
||||
|
|
||||
llvm::Function* m_longjmp = nullptr; |
|
||||
llvm::Value* m_jmpBuf = nullptr; |
|
||||
llvm::Value* m_dataPtr = nullptr; |
|
||||
llvm::Value* m_gasPtr = nullptr; |
|
||||
llvm::Value* m_memPtr = nullptr; |
|
||||
llvm::Value* m_envPtr = nullptr; |
|
||||
|
|
||||
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts; |
|
||||
|
|
||||
llvm::Value* m_stackSize = nullptr; |
|
||||
llvm::Function* m_checkStackLimit = nullptr; |
|
||||
|
|
||||
code_iterator m_codeBegin = {}; |
|
||||
code_iterator m_codeEnd = {}; |
|
||||
|
|
||||
Stack* m_stack = nullptr; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,44 +0,0 @@ |
|||||
#include "Stack.h" |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Function.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "RuntimeManager.h" |
|
||||
#include "Utils.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
Stack::Stack(llvm::IRBuilder<>& _builder): |
|
||||
CompilerHelper(_builder), |
|
||||
m_stack(_builder, "stack") |
|
||||
{} |
|
||||
|
|
||||
llvm::Value* Stack::get(size_t _index) |
|
||||
{ |
|
||||
return m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1))); |
|
||||
} |
|
||||
|
|
||||
void Stack::set(size_t _index, llvm::Value* _value) |
|
||||
{ |
|
||||
m_stack.set(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)), _value); |
|
||||
} |
|
||||
|
|
||||
void Stack::pop(size_t _count) |
|
||||
{ |
|
||||
m_stack.pop(m_builder.getInt64(_count)); |
|
||||
} |
|
||||
|
|
||||
void Stack::push(llvm::Value* _value) |
|
||||
{ |
|
||||
m_stack.push(_value); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,30 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "Array.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
class Stack: public CompilerHelper |
|
||||
{ |
|
||||
public: |
|
||||
Stack(llvm::IRBuilder<>& builder); |
|
||||
|
|
||||
llvm::Value* get(size_t _index); |
|
||||
void set(size_t _index, llvm::Value* _value); |
|
||||
void pop(size_t _count); |
|
||||
void push(llvm::Value* _value); |
|
||||
void free() { m_stack.free(); } |
|
||||
|
|
||||
private: |
|
||||
Array m_stack; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,73 +0,0 @@ |
|||||
#include "Type.h" |
|
||||
|
|
||||
#include <llvm/IR/MDBuilder.h> |
|
||||
|
|
||||
#include "RuntimeManager.h" |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
|
|
||||
llvm::IntegerType* Type::Word; |
|
||||
llvm::PointerType* Type::WordPtr; |
|
||||
llvm::IntegerType* Type::Bool; |
|
||||
llvm::IntegerType* Type::Size; |
|
||||
llvm::IntegerType* Type::Gas; |
|
||||
llvm::PointerType* Type::GasPtr; |
|
||||
llvm::IntegerType* Type::Byte; |
|
||||
llvm::PointerType* Type::BytePtr; |
|
||||
llvm::Type* Type::Void; |
|
||||
llvm::IntegerType* Type::MainReturn; |
|
||||
llvm::PointerType* Type::EnvPtr; |
|
||||
llvm::PointerType* Type::RuntimeDataPtr; |
|
||||
llvm::PointerType* Type::RuntimePtr; |
|
||||
llvm::ConstantInt* Constant::gasMax; |
|
||||
llvm::MDNode* Type::expectTrue; |
|
||||
|
|
||||
void Type::init(llvm::LLVMContext& _context) |
|
||||
{ |
|
||||
if (!Word) // Do init only once
|
|
||||
{ |
|
||||
Word = llvm::Type::getIntNTy(_context, 256); |
|
||||
WordPtr = Word->getPointerTo(); |
|
||||
Bool = llvm::Type::getInt1Ty(_context); |
|
||||
Size = llvm::Type::getInt64Ty(_context); |
|
||||
Gas = Size; |
|
||||
GasPtr = Gas->getPointerTo(); |
|
||||
Byte = llvm::Type::getInt8Ty(_context); |
|
||||
BytePtr = Byte->getPointerTo(); |
|
||||
Void = llvm::Type::getVoidTy(_context); |
|
||||
MainReturn = llvm::Type::getInt32Ty(_context); |
|
||||
|
|
||||
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); |
|
||||
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); |
|
||||
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); |
|
||||
|
|
||||
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max()); |
|
||||
|
|
||||
expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
llvm::ConstantInt* Constant::get(int64_t _n) |
|
||||
{ |
|
||||
return llvm::ConstantInt::getSigned(Type::Word, _n); |
|
||||
} |
|
||||
|
|
||||
llvm::ConstantInt* Constant::get(llvm::APInt const& _n) |
|
||||
{ |
|
||||
return llvm::ConstantInt::get(Type::Word->getContext(), _n); |
|
||||
} |
|
||||
|
|
||||
llvm::ConstantInt* Constant::get(ReturnCode _returnCode) |
|
||||
{ |
|
||||
return llvm::ConstantInt::get(Type::MainReturn, static_cast<uint64_t>(_returnCode)); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,61 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "preprocessor/llvm_includes_start.h" |
|
||||
#include <llvm/IR/Type.h> |
|
||||
#include <llvm/IR/Constants.h> |
|
||||
#include <llvm/IR/Metadata.h> |
|
||||
#include "preprocessor/llvm_includes_end.h" |
|
||||
|
|
||||
#include "evmjit/JIT.h" // ReturnCode |
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace eth |
|
||||
{ |
|
||||
namespace jit |
|
||||
{ |
|
||||
using namespace evmjit; |
|
||||
|
|
||||
struct Type |
|
||||
{ |
|
||||
static llvm::IntegerType* Word; |
|
||||
static llvm::PointerType* WordPtr; |
|
||||
|
|
||||
static llvm::IntegerType* Bool; |
|
||||
static llvm::IntegerType* Size; |
|
||||
static llvm::IntegerType* Gas; |
|
||||
static llvm::PointerType* GasPtr; |
|
||||
|
|
||||
static llvm::IntegerType* Byte; |
|
||||
static llvm::PointerType* BytePtr; |
|
||||
|
|
||||
static llvm::Type* Void; |
|
||||
|
|
||||
/// Main function return type
|
|
||||
static llvm::IntegerType* MainReturn; |
|
||||
|
|
||||
static llvm::PointerType* EnvPtr; |
|
||||
static llvm::PointerType* RuntimeDataPtr; |
|
||||
static llvm::PointerType* RuntimePtr; |
|
||||
|
|
||||
// TODO: Redesign static LLVM objects
|
|
||||
static llvm::MDNode* expectTrue; |
|
||||
|
|
||||
static void init(llvm::LLVMContext& _context); |
|
||||
}; |
|
||||
|
|
||||
struct Constant |
|
||||
{ |
|
||||
static llvm::ConstantInt* gasMax; |
|
||||
|
|
||||
/// Returns word-size constant
|
|
||||
static llvm::ConstantInt* get(int64_t _n); |
|
||||
static llvm::ConstantInt* get(llvm::APInt const& _n); |
|
||||
|
|
||||
static llvm::ConstantInt* get(ReturnCode _returnCode); |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,27 +0,0 @@ |
|||||
#include "Utils.h" |
|
||||
|
|
||||
#include <llvm/Support/Debug.h> |
|
||||
|
|
||||
#include "BuildInfo.gen.h" |
|
||||
|
|
||||
#if !defined(NDEBUG) // Debug
|
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
std::ostream& getLogStream(char const* _channel) |
|
||||
{ |
|
||||
static std::ostream nullStream{nullptr}; |
|
||||
#if LLVM_DEBUG |
|
||||
return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream; |
|
||||
#else |
|
||||
return (void)_channel, nullStream; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif |
|
@ -1,40 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <iostream> |
|
||||
|
|
||||
// The same as assert, but expression is always evaluated and result returned
|
|
||||
#define CHECK(expr) (assert(expr), expr) |
|
||||
|
|
||||
#if !defined(NDEBUG) // Debug
|
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
std::ostream& getLogStream(char const* _channel); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL) |
|
||||
|
|
||||
#else // Release
|
|
||||
|
|
||||
namespace dev |
|
||||
{ |
|
||||
namespace evmjit |
|
||||
{ |
|
||||
|
|
||||
struct Voider |
|
||||
{ |
|
||||
void operator=(std::ostream const&) {} |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#define DLOG(CHANNEL) true ? (void)0 : ::dev::evmjit::Voider{} = std::cerr |
|
||||
|
|
||||
#endif |
|
@ -1,7 +0,0 @@ |
|||||
#if defined(_MSC_VER) |
|
||||
#pragma warning(pop) |
|
||||
#elif defined(__clang__) |
|
||||
#pragma clang diagnostic pop |
|
||||
#else |
|
||||
#pragma GCC diagnostic pop |
|
||||
#endif |
|
@ -1,12 +0,0 @@ |
|||||
#if defined(_MSC_VER) |
|
||||
#pragma warning(push) |
|
||||
#pragma warning(disable: 4267 4244 4800 4624) |
|
||||
#elif defined(__clang__) |
|
||||
#pragma clang diagnostic push |
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter" |
|
||||
#pragma clang diagnostic ignored "-Wconversion" |
|
||||
#else |
|
||||
#pragma GCC diagnostic push |
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|
||||
#pragma GCC diagnostic ignored "-Wconversion" |
|
||||
#endif |
|
@ -1,38 +0,0 @@ |
|||||
cmake_policy(SET CMP0015 NEW) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
aux_source_directory(. SRC_LIST) |
|
||||
|
|
||||
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) |
|
||||
include_directories(BEFORE ..) |
|
||||
include_directories(${DB_INCLUDE_DIRS}) |
|
||||
|
|
||||
set(EXECUTABLE exp) |
|
||||
|
|
||||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) |
|
||||
|
|
||||
if (READLINE_FOUND) |
|
||||
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES}) |
|
||||
endif() |
|
||||
|
|
||||
if (JSONRPC) |
|
||||
target_link_libraries(${EXECUTABLE} web3jsonrpc) |
|
||||
endif() |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} webthree) |
|
||||
target_link_libraries(${EXECUTABLE} ethereum) |
|
||||
target_link_libraries(${EXECUTABLE} p2p) |
|
||||
if (ETHASHCL) |
|
||||
# target_link_libraries(${EXECUTABLE} ethash-cl) |
|
||||
# target_link_libraries(${EXECUTABLE} ethash) |
|
||||
# target_link_libraries(${EXECUTABLE} OpenCL) |
|
||||
endif() |
|
||||
target_link_libraries(${EXECUTABLE} ethcore) |
|
||||
install( TARGETS ${EXECUTABLE} DESTINATION bin) |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -1,402 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file main.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* Ethereum client. |
|
||||
*/ |
|
||||
#if ETH_ETHASHCL |
|
||||
#define __CL_ENABLE_EXCEPTIONS |
|
||||
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS |
|
||||
#if defined(__clang__) |
|
||||
#pragma clang diagnostic push |
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter" |
|
||||
#include <libethash-cl/cl.hpp> |
|
||||
#pragma clang diagnostic pop |
|
||||
#else |
|
||||
#include <libethash-cl/cl.hpp> |
|
||||
#endif |
|
||||
#endif |
|
||||
#include <functional> |
|
||||
#include <boost/filesystem.hpp> |
|
||||
#include <boost/algorithm/string.hpp> |
|
||||
#if 0 |
|
||||
#include <libdevcore/TrieDB.h> |
|
||||
#include <libdevcore/TrieHash.h> |
|
||||
#include <libdevcore/RangeMask.h> |
|
||||
#include <libdevcore/Log.h> |
|
||||
#include <libdevcore/Common.h> |
|
||||
#include <libdevcore/CommonData.h> |
|
||||
#include <libdevcore/RLP.h> |
|
||||
#include <libdevcore/TransientDirectory.h> |
|
||||
#include <libdevcore/CommonIO.h> |
|
||||
#include <libdevcrypto/SecretStore.h> |
|
||||
#include <libp2p/All.h> |
|
||||
#include <libethcore/Farm.h> |
|
||||
#include <libdevcore/FileSystem.h> |
|
||||
#include <libethereum/All.h> |
|
||||
#include <libethcore/KeyManager.h> |
|
||||
#include <libethereum/AccountDiff.h> |
|
||||
#include <libethereum/DownloadMan.h> |
|
||||
#include <libethereum/Client.h> |
|
||||
#include <liblll/All.h> |
|
||||
#include <libwhisper/WhisperPeer.h> |
|
||||
#include <libwhisper/WhisperHost.h> |
|
||||
#include <test/JsonSpiritHeaders.h> |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace dev::eth; |
|
||||
using namespace dev::p2p; |
|
||||
using namespace dev::shh; |
|
||||
namespace js = json_spirit; |
|
||||
namespace fs = boost::filesystem; |
|
||||
#else |
|
||||
#include <libethcore/Sealer.h> |
|
||||
#include <libethcore/BasicAuthority.h> |
|
||||
#include <libethcore/BlockInfo.h> |
|
||||
#include <libethcore/Ethash.h> |
|
||||
#include <libethcore/Params.h> |
|
||||
#include <libethereum/All.h> |
|
||||
#include <libethereum/AccountDiff.h> |
|
||||
#include <libethereum/DownloadMan.h> |
|
||||
#include <libethereum/Client.h> |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
using namespace eth; |
|
||||
#endif |
|
||||
|
|
||||
#if 0 |
|
||||
int main() |
|
||||
{ |
|
||||
BlockInfo bi; |
|
||||
bi.difficulty = c_genesisDifficulty; |
|
||||
bi.gasLimit = c_genesisGasLimit; |
|
||||
bi.number() = 1; |
|
||||
bi.parentHash() = sha3("parentHash"); |
|
||||
|
|
||||
bytes sealedData; |
|
||||
|
|
||||
{ |
|
||||
KeyPair kp(sha3("test")); |
|
||||
SealEngineFace* se = BasicAuthority::createSealEngine(); |
|
||||
se->setOption("authority", rlp(kp.secret())); |
|
||||
se->setOption("authorities", rlpList(kp.address())); |
|
||||
cdebug << se->sealers(); |
|
||||
bool done = false; |
|
||||
se->onSealGenerated([&](SealFace const* seal){ |
|
||||
sealedData = seal->sealedHeader(bi); |
|
||||
done = true; |
|
||||
}); |
|
||||
se->generateSeal(bi); |
|
||||
while (!done) |
|
||||
this_thread::sleep_for(chrono::milliseconds(50)); |
|
||||
BasicAuthority::BlockHeader sealed = BasicAuthority::BlockHeader::fromHeader(sealedData, CheckEverything); |
|
||||
cdebug << sealed.sig(); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
SealEngineFace* se = Ethash::createSealEngine(); |
|
||||
cdebug << se->sealers(); |
|
||||
bool done = false; |
|
||||
se->setSealer("cpu"); |
|
||||
se->onSealGenerated([&](SealFace const* seal){ |
|
||||
sealedData = seal->sealedHeader(bi); |
|
||||
done = true; |
|
||||
}); |
|
||||
se->generateSeal(bi); |
|
||||
while (!done) |
|
||||
this_thread::sleep_for(chrono::milliseconds(50)); |
|
||||
Ethash::BlockHeader sealed = Ethash::BlockHeader::fromHeader(sealedData, CheckEverything); |
|
||||
cdebug << sealed.nonce(); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
cdebug << pbkdf2("password", asBytes("salt"), 1, 32); |
|
||||
cdebug << pbkdf2("password", asBytes("salt"), 1, 16); |
|
||||
cdebug << pbkdf2("password", asBytes("salt"), 2, 16); |
|
||||
cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16); |
|
||||
return 0; |
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
cdebug << "EXP"; |
|
||||
vector<bytes> data; |
|
||||
for (unsigned i = 0; i < 10000; ++i) |
|
||||
data.push_back(rlp(i)); |
|
||||
|
|
||||
h256 ret; |
|
||||
DEV_TIMED("triedb") |
|
||||
{ |
|
||||
MemoryDB mdb; |
|
||||
GenericTrieDB<MemoryDB> t(&mdb); |
|
||||
t.init(); |
|
||||
unsigned i = 0; |
|
||||
for (auto const& d: data) |
|
||||
t.insert(rlp(i++), d); |
|
||||
ret = t.root(); |
|
||||
} |
|
||||
cdebug << ret; |
|
||||
DEV_TIMED("hash256") |
|
||||
ret = orderedTrieRoot(data); |
|
||||
cdebug << ret; |
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
KeyManager keyman; |
|
||||
if (keyman.exists()) |
|
||||
keyman.load("foo"); |
|
||||
else |
|
||||
keyman.create("foo"); |
|
||||
|
|
||||
Address a("9cab1cc4e8fe528267c6c3af664a1adbce810b5f"); |
|
||||
|
|
||||
// keyman.importExisting(fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0"), "{\"name\":\"Gavin Wood - Main identity\"}", "bar", "{\"hint\":\"Not foo.\"}");
|
|
||||
// Address a2 = keyman.address(keyman.import(Secret::random(), "Key with no additional security."));
|
|
||||
// cdebug << toString(a2);
|
|
||||
Address a2("19c486071651b2650449ba3c6a807f316a73e8fe"); |
|
||||
|
|
||||
cdebug << keyman.accountDetails(); |
|
||||
|
|
||||
cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; }); |
|
||||
cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2); |
|
||||
|
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
DownloadMan man; |
|
||||
DownloadSub s0(man); |
|
||||
DownloadSub s1(man); |
|
||||
DownloadSub s2(man); |
|
||||
man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)}), 0); |
|
||||
assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8})); |
|
||||
assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6})); |
|
||||
assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4})); |
|
||||
s0.noteBlock(u256(8)); |
|
||||
s0.doneFetch(); |
|
||||
assert((s0.nextFetch(2) == h256Set{(u256)2, (u256)7})); |
|
||||
s1.noteBlock(u256(6)); |
|
||||
s1.noteBlock(u256(5)); |
|
||||
s1.doneFetch(); |
|
||||
assert((s1.nextFetch(2) == h256Set{(u256)0, (u256)1})); |
|
||||
s0.doneFetch(); // TODO: check exact semantics of doneFetch & nextFetch. Not sure if they're right -> doneFetch calls resetFetch which kills all the info of past fetches.
|
|
||||
cdebug << s0.nextFetch(2); |
|
||||
assert((s0.nextFetch(2) == h256Set{(u256)3, (u256)4})); |
|
||||
|
|
||||
/* RangeMask<unsigned> m(0, 100);
|
|
||||
cnote << m; |
|
||||
m += UnsignedRange(3, 10); |
|
||||
cnote << m; |
|
||||
m += UnsignedRange(11, 16); |
|
||||
cnote << m; |
|
||||
m += UnsignedRange(10, 11); |
|
||||
cnote << m; |
|
||||
cnote << ~m; |
|
||||
cnote << (~m).lowest(10); |
|
||||
for (auto i: (~m).lowest(10)) |
|
||||
cnote << i;*/ |
|
||||
return 0; |
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
KeyPair u = KeyPair::create(); |
|
||||
KeyPair cb = KeyPair::create(); |
|
||||
OverlayDB db; |
|
||||
State s(cb.address(), db, BaseState::Empty); |
|
||||
cnote << s.rootHash(); |
|
||||
s.addBalance(u.address(), 1 * ether); |
|
||||
Address c = s.newContract(1000 * ether, compileLLL("(suicide (caller))")); |
|
||||
s.commit(); |
|
||||
State before = s; |
|
||||
cnote << "State before transaction: " << before; |
|
||||
Transaction t(0, 10000, 10000, c, bytes(), 0, u.secret()); |
|
||||
cnote << "Transaction: " << t; |
|
||||
cnote << s.balance(c); |
|
||||
s.execute(LastHashes(), t.rlp()); |
|
||||
cnote << "State after transaction: " << s; |
|
||||
cnote << before.diff(s); |
|
||||
} |
|
||||
#elif 0 |
|
||||
int main() |
|
||||
{ |
|
||||
GenericFarm<EthashProofOfWork> f; |
|
||||
BlockInfo genesis = CanonBlockChain::genesis(); |
|
||||
genesis.difficulty = 1 << 18; |
|
||||
cdebug << genesis.boundary(); |
|
||||
|
|
||||
auto mine = [](GenericFarm<EthashProofOfWork>& f, BlockInfo const& g, unsigned timeout) { |
|
||||
BlockInfo bi = g; |
|
||||
bool completed = false; |
|
||||
f.onSolutionFound([&](EthashProofOfWork::Solution sol) |
|
||||
{ |
|
||||
bi.proof = sol; |
|
||||
return completed = true; |
|
||||
}); |
|
||||
f.setWork(bi); |
|
||||
for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush) |
|
||||
this_thread::sleep_for(chrono::milliseconds(100)); |
|
||||
cout << endl << flush; |
|
||||
cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad"); |
|
||||
}; |
|
||||
|
|
||||
Ethash::prep(genesis); |
|
||||
|
|
||||
genesis.difficulty = u256(1) << 40; |
|
||||
genesis.noteDirty(); |
|
||||
f.startCPU(); |
|
||||
mine(f, genesis, 10); |
|
||||
|
|
||||
f.startGPU(); |
|
||||
|
|
||||
cdebug << "Good:"; |
|
||||
genesis.difficulty = 1 << 18; |
|
||||
genesis.noteDirty(); |
|
||||
mine(f, genesis, 30); |
|
||||
|
|
||||
cdebug << "Bad:"; |
|
||||
genesis.difficulty = (u256(1) << 40); |
|
||||
genesis.noteDirty(); |
|
||||
mine(f, genesis, 30); |
|
||||
|
|
||||
f.stop(); |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
#elif 1 |
|
||||
int main() |
|
||||
{ |
|
||||
bytes tx = fromHex("f84c01028332dcd58004801ba024843272ee176277535489859cbd275686023fe64aabd158b6fcdf2ae6a1ab6ba02f252a5016a48e5ec8d17aefaf4324d29b9e123fa623dc5a60539b3ad3610c95"); |
|
||||
Transaction t(tx, CheckTransaction::None); |
|
||||
Public p = recover(t.signature(), t.sha3(WithoutSignature)); |
|
||||
cnote << t.signature().r; |
|
||||
cnote << t.signature().s; |
|
||||
cnote << t.signature().v; |
|
||||
cnote << p; |
|
||||
cnote << toAddress(p); |
|
||||
cnote << t.sender(); |
|
||||
} |
|
||||
#elif 0 |
|
||||
void mine(State& s, BlockChain const& _bc, SealEngineFace* _se) |
|
||||
{ |
|
||||
s.commitToSeal(_bc); |
|
||||
Notified<bytes> sealed; |
|
||||
_se->onSealGenerated([&](bytes const& sealedHeader){ sealed = sealedHeader; }); |
|
||||
_se->generateSeal(s.info()); |
|
||||
sealed.waitNot({}); |
|
||||
s.sealBlock(sealed); |
|
||||
} |
|
||||
int main() |
|
||||
{ |
|
||||
cnote << "Testing State..."; |
|
||||
|
|
||||
KeyPair me = sha3("Gav Wood"); |
|
||||
KeyPair myMiner = sha3("Gav's Miner"); |
|
||||
// KeyPair you = sha3("123");
|
|
||||
|
|
||||
Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count())); |
|
||||
|
|
||||
using Sealer = Ethash; |
|
||||
CanonBlockChain<Sealer> bc; |
|
||||
auto gbb = bc.headerData(bc.genesisHash()); |
|
||||
assert(Sealer::BlockHeader(bc.headerData(bc.genesisHash()), IgnoreSeal, bc.genesisHash(), HeaderData)); |
|
||||
|
|
||||
SealEngineFace* se = Sealer::createSealEngine(); |
|
||||
KeyPair kp(sha3("test")); |
|
||||
se->setOption("authority", rlp(kp.secret())); |
|
||||
se->setOption("authorities", rlpList(kp.address())); |
|
||||
|
|
||||
OverlayDB stateDB = State::openDB(bc.genesisHash()); |
|
||||
cnote << bc; |
|
||||
|
|
||||
Block s = bc.genesisBlock(stateDB); |
|
||||
s.setBeneficiary(myMiner.address()); |
|
||||
cnote << s; |
|
||||
|
|
||||
// Sync up - this won't do much until we use the last state.
|
|
||||
s.sync(bc); |
|
||||
|
|
||||
cnote << s; |
|
||||
|
|
||||
// Mine to get some ether!
|
|
||||
mine(s, bc, se); |
|
||||
|
|
||||
bytes minedBlock = s.blockData(); |
|
||||
cnote << "Mined block is" << BlockInfo(minedBlock).stateRoot(); |
|
||||
bc.import(minedBlock, stateDB); |
|
||||
|
|
||||
cnote << bc; |
|
||||
|
|
||||
s.sync(bc); |
|
||||
|
|
||||
cnote << s; |
|
||||
cnote << "Miner now has" << s.balance(myMiner.address()); |
|
||||
s.resetCurrent(); |
|
||||
cnote << "Miner now has" << s.balance(myMiner.address()); |
|
||||
|
|
||||
// Inject a transaction to transfer funds from miner to me.
|
|
||||
Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); |
|
||||
assert(t.sender() == myMiner.address()); |
|
||||
s.execute(bc.lastHashes(), t); |
|
||||
|
|
||||
cnote << s; |
|
||||
|
|
||||
// Mine to get some ether and set in stone.
|
|
||||
s.commitToSeal(bc); |
|
||||
s.commitToSeal(bc); |
|
||||
mine(s, bc, se); |
|
||||
bc.attemptImport(s.blockData(), stateDB); |
|
||||
|
|
||||
cnote << bc; |
|
||||
|
|
||||
s.sync(bc); |
|
||||
|
|
||||
cnote << s; |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
#else |
|
||||
int main() |
|
||||
{ |
|
||||
string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()); |
|
||||
|
|
||||
KeyPair myMiner = sha3("Gav's Miner"); |
|
||||
|
|
||||
p2p::Host net("Test"); |
|
||||
cdebug << "Path:" << tempDir; |
|
||||
Client c(&net, tempDir); |
|
||||
|
|
||||
c.setBeneficiary(myMiner.address()); |
|
||||
|
|
||||
this_thread::sleep_for(chrono::milliseconds(1000)); |
|
||||
|
|
||||
c.startMining(); |
|
||||
|
|
||||
this_thread::sleep_for(chrono::milliseconds(6000)); |
|
||||
|
|
||||
c.stopMining(); |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
#endif |
|
||||
|
|
@ -1 +0,0 @@ |
|||||
add_custom_target(mix) |
|
@ -1,21 +0,0 @@ |
|||||
cmake_policy(SET CMP0015 NEW) |
|
||||
set(CMAKE_AUTOMOC OFF) |
|
||||
|
|
||||
aux_source_directory(. SRC_LIST) |
|
||||
|
|
||||
include_directories(BEFORE ..) |
|
||||
include_directories(${DB_INCLUDE_DIRS}) |
|
||||
|
|
||||
set(EXECUTABLE rlp) |
|
||||
|
|
||||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|
||||
|
|
||||
target_link_libraries(${EXECUTABLE} devcrypto) |
|
||||
target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES}) |
|
||||
|
|
||||
if (APPLE) |
|
||||
install(TARGETS ${EXECUTABLE} DESTINATION bin) |
|
||||
else() |
|
||||
eth_install_executable(${EXECUTABLE}) |
|
||||
endif() |
|
||||
|
|
@ -1,394 +0,0 @@ |
|||||
/*
|
|
||||
This file is part of cpp-ethereum. |
|
||||
|
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify |
|
||||
it under the terms of the GNU General Public License as published by |
|
||||
the Free Software Foundation, either version 3 of the License, or |
|
||||
(at your option) any later version. |
|
||||
|
|
||||
cpp-ethereum is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
GNU General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU General Public License |
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file main.cpp
|
|
||||
* @author Gav Wood <i@gavwood.com> |
|
||||
* @date 2014 |
|
||||
* RLP tool. |
|
||||
*/ |
|
||||
#include <fstream> |
|
||||
#include <iostream> |
|
||||
#include <boost/algorithm/string.hpp> |
|
||||
#include "../test/JsonSpiritHeaders.h" |
|
||||
#include <libdevcore/CommonIO.h> |
|
||||
#include <libdevcore/RLP.h> |
|
||||
#include <libdevcore/SHA3.h> |
|
||||
using namespace std; |
|
||||
using namespace dev; |
|
||||
namespace js = json_spirit; |
|
||||
|
|
||||
void help() |
|
||||
{ |
|
||||
cout |
|
||||
<< "Usage rlp [OPTIONS] [ <file> | -- ]" << endl |
|
||||
<< "Options:" << endl |
|
||||
<< " -r,--render Render the given RLP. Options:" << endl |
|
||||
<< " --indent <string> Use string as the level indentation (default ' ')." << endl |
|
||||
<< " --hex-ints Render integers in hex." << endl |
|
||||
<< " --string-ints Render integers in the same way as strings." << endl |
|
||||
<< " --ascii-strings Render data as C-style strings or hex depending on content being ASCII." << endl |
|
||||
<< " --force-string Force all data to be rendered as C-style strings." << endl |
|
||||
<< " --force-escape When rendering as C-style strings, force all characters to be escaped." << endl |
|
||||
<< " --force-hex Force all data to be rendered as raw hex." << endl |
|
||||
<< " -l,--list-archive List the items in the RLP list by hash and size." << endl |
|
||||
<< " -e,--extract-archive Extract all items in the RLP list, named by hash." << endl |
|
||||
<< " -c,--create Given a simplified JSON string, output the RLP." << endl |
|
||||
<< "General options:" << endl |
|
||||
<< " -L,--lenience Try not to bomb out early if possible." << endl |
|
||||
<< " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl |
|
||||
<< " -k,--keccak Output Keccak-256 hash only." << endl |
|
||||
<< " --64,--base-64 Treat input RLP as base-64 encoded data." << endl |
|
||||
<< " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl |
|
||||
<< " -h,--help Print this help message and exit." << endl |
|
||||
<< " -V,--version Show the version and exit." << endl |
|
||||
; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
void version() |
|
||||
{ |
|
||||
cout << "rlp version " << dev::Version << endl; |
|
||||
exit(0); |
|
||||
} |
|
||||
|
|
||||
enum class Mode { |
|
||||
ListArchive, |
|
||||
ExtractArchive, |
|
||||
Render, |
|
||||
Create |
|
||||
}; |
|
||||
|
|
||||
enum class Encoding { |
|
||||
Auto, |
|
||||
Hex, |
|
||||
Base64, |
|
||||
Binary, |
|
||||
Keccak, |
|
||||
}; |
|
||||
|
|
||||
bool isAscii(string const& _s) |
|
||||
{ |
|
||||
// Always hex-encode anything beginning with 0x to avoid ambiguity.
|
|
||||
if (_s.size() >= 2 && _s.substr(0, 2) == "0x") |
|
||||
return false; |
|
||||
|
|
||||
for (char c: _s) |
|
||||
if (c < 32) |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
class RLPStreamer |
|
||||
{ |
|
||||
public: |
|
||||
struct Prefs |
|
||||
{ |
|
||||
string indent; |
|
||||
bool hexInts = false; |
|
||||
bool stringInts = true; |
|
||||
bool hexPrefix = true; |
|
||||
bool forceString = false; |
|
||||
bool escapeAll = false; |
|
||||
bool forceHex = true; |
|
||||
}; |
|
||||
|
|
||||
RLPStreamer(ostream& _out, Prefs _p): m_out(_out), m_prefs(_p) {} |
|
||||
|
|
||||
void output(RLP const& _d, unsigned _level = 0) |
|
||||
{ |
|
||||
if (_d.isNull()) |
|
||||
m_out << "null"; |
|
||||
else if (_d.isInt() && !m_prefs.stringInts) |
|
||||
if (m_prefs.hexInts) |
|
||||
m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt<bigint>(RLP::LaissezFaire), 1), 1); |
|
||||
else |
|
||||
m_out << _d.toInt<bigint>(RLP::LaissezFaire); |
|
||||
else if (_d.isData() || (_d.isInt() && m_prefs.stringInts)) |
|
||||
if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString()))) |
|
||||
m_out << escaped(_d.toString(), m_prefs.escapeAll); |
|
||||
else |
|
||||
m_out << "\"" << (m_prefs.hexPrefix ? "0x" : "") << toHex(_d.toBytes()) << "\""; |
|
||||
else if (_d.isList()) |
|
||||
{ |
|
||||
m_out << "["; |
|
||||
string newline = "\n"; |
|
||||
for (unsigned i = 0; i < _level + 1; ++i) |
|
||||
newline += m_prefs.indent; |
|
||||
int j = 0; |
|
||||
for (auto i: _d) |
|
||||
{ |
|
||||
m_out << (j++ ? |
|
||||
(m_prefs.indent.empty() ? ", " : ("," + newline)) : |
|
||||
(m_prefs.indent.empty() ? " " : newline)); |
|
||||
output(i, _level + 1); |
|
||||
} |
|
||||
newline = newline.substr(0, newline.size() - m_prefs.indent.size()); |
|
||||
m_out << (m_prefs.indent.empty() ? (j ? " ]" : "]") : (j ? newline + "]" : "]")); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
std::ostream& m_out; |
|
||||
Prefs m_prefs; |
|
||||
}; |
|
||||
|
|
||||
int main(int argc, char** argv) |
|
||||
{ |
|
||||
Encoding encoding = Encoding::Auto; |
|
||||
Mode mode = Mode::Render; |
|
||||
string inputFile = "--"; |
|
||||
bool lenience = false; |
|
||||
RLPStreamer::Prefs prefs; |
|
||||
|
|
||||
for (int i = 1; i < argc; ++i) |
|
||||
{ |
|
||||
string arg = argv[i]; |
|
||||
if (arg == "-h" || arg == "--help") |
|
||||
help(); |
|
||||
else if (arg == "-r" || arg == "--render") |
|
||||
mode = Mode::Render; |
|
||||
else if (arg == "-c" || arg == "--create") |
|
||||
mode = Mode::Create; |
|
||||
else if ((arg == "-i" || arg == "--indent") && argc > i) |
|
||||
prefs.indent = argv[++i]; |
|
||||
else if (arg == "--hex-ints") |
|
||||
prefs.hexInts = true; |
|
||||
else if (arg == "--string-ints") |
|
||||
prefs.stringInts = true; |
|
||||
else if (arg == "--ascii-strings") |
|
||||
prefs.forceString = prefs.forceHex = false; |
|
||||
else if (arg == "--force-string") |
|
||||
prefs.forceString = true; |
|
||||
else if (arg == "--force-hex") |
|
||||
prefs.forceHex = true, prefs.forceString = false; |
|
||||
else if (arg == "--force-escape") |
|
||||
prefs.escapeAll = true; |
|
||||
else if (arg == "-n" || arg == "--nice") |
|
||||
prefs.forceString = true, prefs.stringInts = false, prefs.forceHex = false, prefs.indent = " "; |
|
||||
else if (arg == "-l" || arg == "--list-archive") |
|
||||
mode = Mode::ListArchive; |
|
||||
else if (arg == "-e" || arg == "--extract-archive") |
|
||||
mode = Mode::ExtractArchive; |
|
||||
else if (arg == "-L" || arg == "--lenience") |
|
||||
lenience = true; |
|
||||
else if (arg == "-V" || arg == "--version") |
|
||||
version(); |
|
||||
else if (arg == "-x" || arg == "--hex" || arg == "--base-16") |
|
||||
encoding = Encoding::Hex; |
|
||||
else if (arg == "-k" || arg == "--keccak") |
|
||||
encoding = Encoding::Keccak; |
|
||||
else if (arg == "--64" || arg == "--base-64") |
|
||||
encoding = Encoding::Base64; |
|
||||
else if (arg == "-b" || arg == "--bin" || arg == "--base-256") |
|
||||
encoding = Encoding::Binary; |
|
||||
else |
|
||||
inputFile = arg; |
|
||||
} |
|
||||
|
|
||||
bytes in; |
|
||||
if (inputFile == "--") |
|
||||
for (int i = cin.get(); i != -1; i = cin.get()) |
|
||||
in.push_back((byte)i); |
|
||||
else |
|
||||
in = contents(inputFile); |
|
||||
|
|
||||
bytes b; |
|
||||
|
|
||||
if (mode != Mode::Create) |
|
||||
{ |
|
||||
if (encoding == Encoding::Auto) |
|
||||
{ |
|
||||
encoding = Encoding::Hex; |
|
||||
for (char b: in) |
|
||||
if (b != '\n' && b != ' ' && b != '\t') |
|
||||
{ |
|
||||
if (encoding == Encoding::Hex && (b < '0' || b > '9' ) && (b < 'a' || b > 'f' ) && (b < 'A' || b > 'F' )) |
|
||||
{ |
|
||||
cerr << "'" << b << "':" << (int)b << endl; |
|
||||
encoding = Encoding::Base64; |
|
||||
} |
|
||||
if (encoding == Encoding::Base64 && (b < '0' || b > '9' ) && (b < 'a' || b > 'z' ) && (b < 'A' || b > 'Z' ) && b != '+' && b != '/') |
|
||||
{ |
|
||||
encoding = Encoding::Binary; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
switch (encoding) |
|
||||
{ |
|
||||
case Encoding::Hex: |
|
||||
{ |
|
||||
string s = asString(in); |
|
||||
boost::algorithm::replace_all(s, " ", ""); |
|
||||
boost::algorithm::replace_all(s, "\n", ""); |
|
||||
boost::algorithm::replace_all(s, "\t", ""); |
|
||||
b = fromHex(s); |
|
||||
break; |
|
||||
} |
|
||||
case Encoding::Base64: |
|
||||
{ |
|
||||
string s = asString(in); |
|
||||
boost::algorithm::replace_all(s, " ", ""); |
|
||||
boost::algorithm::replace_all(s, "\n", ""); |
|
||||
boost::algorithm::replace_all(s, "\t", ""); |
|
||||
b = fromBase64(s); |
|
||||
break; |
|
||||
} |
|
||||
default: |
|
||||
swap(b, in); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
RLP rlp(b); |
|
||||
switch (mode) |
|
||||
{ |
|
||||
case Mode::ListArchive: |
|
||||
{ |
|
||||
if (!rlp.isList()) |
|
||||
{ |
|
||||
cout << "Error: Invalid format; RLP data is not a list." << endl; |
|
||||
exit(1); |
|
||||
} |
|
||||
cout << rlp.itemCount() << " items:" << endl; |
|
||||
for (auto i: rlp) |
|
||||
{ |
|
||||
if (!i.isData()) |
|
||||
{ |
|
||||
cout << "Error: Invalid format; RLP list item is not data." << endl; |
|
||||
if (!lenience) |
|
||||
exit(1); |
|
||||
} |
|
||||
cout << " " << i.size() << " bytes: " << sha3(i.data()) << endl; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case Mode::ExtractArchive: |
|
||||
{ |
|
||||
if (!rlp.isList()) |
|
||||
{ |
|
||||
cout << "Error: Invalid format; RLP data is not a list." << endl; |
|
||||
exit(1); |
|
||||
} |
|
||||
cout << rlp.itemCount() << " items:" << endl; |
|
||||
for (auto i: rlp) |
|
||||
{ |
|
||||
if (!i.isData()) |
|
||||
{ |
|
||||
cout << "Error: Invalid format; RLP list item is not data." << endl; |
|
||||
if (!lenience) |
|
||||
exit(1); |
|
||||
} |
|
||||
ofstream fout; |
|
||||
fout.open(toString(sha3(i.data()))); |
|
||||
fout.write(reinterpret_cast<char const*>(i.data().data()), i.data().size()); |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case Mode::Render: |
|
||||
{ |
|
||||
RLPStreamer s(cout, prefs); |
|
||||
s.output(rlp); |
|
||||
cout << endl; |
|
||||
break; |
|
||||
} |
|
||||
case Mode::Create: |
|
||||
{ |
|
||||
vector<js::mValue> v(1); |
|
||||
try { |
|
||||
js::read_string(asString(in), v[0]); |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Error: Invalid format; bad JSON." << endl; |
|
||||
exit(1); |
|
||||
} |
|
||||
RLPStream out; |
|
||||
while (!v.empty()) |
|
||||
{ |
|
||||
auto vb = v.back(); |
|
||||
v.pop_back(); |
|
||||
switch (vb.type()) |
|
||||
{ |
|
||||
case js::array_type: |
|
||||
{ |
|
||||
js::mArray a = vb.get_array(); |
|
||||
out.appendList(a.size()); |
|
||||
for (int i = a.size() - 1; i >= 0; --i) |
|
||||
v.push_back(a[i]); |
|
||||
break; |
|
||||
} |
|
||||
case js::str_type: |
|
||||
{ |
|
||||
string const& s = vb.get_str(); |
|
||||
if (s.size() >= 2 && s.substr(0, 2) == "0x") |
|
||||
out << fromHex(s); |
|
||||
else |
|
||||
{ |
|
||||
// assume it's a normal JS escaped string.
|
|
||||
bytes ss; |
|
||||
ss.reserve(s.size()); |
|
||||
for (unsigned i = 0; i < s.size(); ++i) |
|
||||
if (s[i] == '\\' && i + 1 < s.size()) |
|
||||
{ |
|
||||
if (s[++i] == 'x' && i + 2 < s.size()) |
|
||||
ss.push_back(fromHex(s.substr(i, 2))[0]); |
|
||||
} |
|
||||
else if (s[i] != '\\') |
|
||||
ss.push_back((byte)s[i]); |
|
||||
out << ss; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
case js::int_type: |
|
||||
out << vb.get_int(); |
|
||||
break; |
|
||||
default: |
|
||||
cerr << "ERROR: Unsupported type in JSON." << endl; |
|
||||
if (!lenience) |
|
||||
exit(1); |
|
||||
} |
|
||||
} |
|
||||
switch (encoding) |
|
||||
{ |
|
||||
case Encoding::Hex: case Encoding::Auto: |
|
||||
cout << toHex(out.out()) << endl; |
|
||||
break; |
|
||||
case Encoding::Base64: |
|
||||
cout << toBase64(&out.out()) << endl; |
|
||||
break; |
|
||||
case Encoding::Binary: |
|
||||
cout.write((char const*)out.out().data(), out.out().size()); |
|
||||
break; |
|
||||
case Encoding::Keccak: |
|
||||
cout << sha3(out.out()).hex() << endl; |
|
||||
break; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
default:; |
|
||||
} |
|
||||
} |
|
||||
catch (...) |
|
||||
{ |
|
||||
cerr << "Error: Invalid format; bad RLP." << endl; |
|
||||
exit(1); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
Loading…
Reference in new issue