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