/* This file is part of solidity. solidity 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. solidity 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 solidity. If not, see . */ /** * Dialects for Wasm. */ #include using namespace std; using namespace solidity::yul; WasmDialect::WasmDialect() { defaultType = "i64"_yulstring; boolType = "i64"_yulstring; types = {"i64"_yulstring, "i32"_yulstring}; for (auto const& name: { "i64.add", "i64.sub", "i64.mul", "i64.div_u", "i64.rem_u", "i64.and", "i64.or", "i64.xor", "i64.shl", "i64.shr_u", "i64.eq", "i64.ne", "i64.lt_u", "i64.gt_u", "i64.le_u", "i64.ge_u" }) addFunction(name, 2, 1); m_functions["i64.lt_u"_yulstring].returns.front() = "i32"_yulstring; m_functions["i64.gt_u"_yulstring].returns.front() = "i32"_yulstring; m_functions["i64.le_u"_yulstring].returns.front() = "i32"_yulstring; m_functions["i64.ge_u"_yulstring].returns.front() = "i32"_yulstring; m_functions["i64.eq"_yulstring].returns.front() = "i32"_yulstring; m_functions["i64.ne"_yulstring].returns.front() = "i32"_yulstring; addFunction("i64.eqz", 1, 1); m_functions["i64.eqz"_yulstring].returns.front() = "i32"_yulstring; addFunction("i64.clz", 1, 1); addFunction("i64.store", 2, 0, false); m_functions["i64.store"_yulstring].parameters.front() = "i32"_yulstring; m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; addFunction("i64.store8", 2, 0, false); m_functions["i64.store8"_yulstring].parameters.front() = "i32"_yulstring; m_functions["i64.store8"_yulstring].sideEffects.invalidatesStorage = false; addFunction("i64.load", 1, 1, false); m_functions["i64.load"_yulstring].parameters.front() = "i32"_yulstring; m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; addFunction("drop", 1, 0); addFunction("unreachable", 0, 0, false); m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; addFunction("datasize", 1, 1, true, true); addFunction("dataoffset", 1, 1, true, true); addEthereumExternals(); } BuiltinFunction const* WasmDialect::builtin(YulString _name) const { auto it = m_functions.find(_name); if (it != m_functions.end()) return &it->second; else return nullptr; } WasmDialect const& WasmDialect::instance() { static std::unique_ptr dialect; static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }}; if (!dialect) dialect = make_unique(); return *dialect; } void WasmDialect::addEthereumExternals() { // These are not YulStrings because that would be too complicated with regards // to the YulStringRepository reset. static string const i64{"i64"}; static string const i32{"i32"}; static string const i32ptr{"i32"}; // Uses "i32" on purpose. struct External { string name; vector parameters; vector returns; }; static vector externals{ {"getAddress", {i32ptr}, {}}, {"getExternalBalance", {i32ptr, i32ptr}, {}}, {"getBlockHash", {i64, i32ptr}, {i32}}, {"call", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}}, {"callDataCopy", {i32ptr, i32, i32}, {}}, {"getCallDataSize", {}, {i32}}, {"callCode", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}}, {"callDelegate", {i64, i32ptr, i32ptr, i32}, {i32}}, {"callStatic", {i64, i32ptr, i32ptr, i32}, {i32}}, {"storageStore", {i32ptr, i32ptr}, {}}, {"storageLoad", {i32ptr, i32ptr}, {}}, {"getCaller", {i32ptr}, {}}, {"getCallValue", {i32ptr}, {}}, {"codeCopy", {i32ptr, i32, i32}, {}}, {"getCodeSize", {}, {i32}}, {"getBlockCoinbase", {i32ptr}, {}}, {"create", {i32ptr, i32ptr, i32, i32ptr}, {i32}}, {"getBlockDifficulty", {i32ptr}, {}}, {"externalCodeCopy", {i32ptr, i32ptr, i32, i32}, {}}, {"getExternalCodeSize", {i32ptr}, {i32}}, {"getGasLeft", {}, {i64}}, {"getBlockGasLimit", {}, {i64}}, {"getTxGasPrice", {i32ptr}, {}}, {"log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}}, {"getBlockNumber", {}, {i64}}, {"getTxOrigin", {i32ptr}, {}}, {"finish", {i32ptr, i32}, {}}, {"revert", {i32ptr, i32}, {}}, {"getReturnDataSize", {}, {i32}}, {"returnDataCopy", {i32ptr, i32, i32}, {}}, {"selfDestruct", {i32ptr}, {}}, {"getBlockTimestamp", {}, {i64}} }; for (External const& ext: externals) { YulString name{"eth." + ext.name}; BuiltinFunction& f = m_functions[name]; f.name = name; for (string const& p: ext.parameters) f.parameters.emplace_back(YulString(p)); for (string const& p: ext.returns) f.returns.emplace_back(YulString(p)); // TODO some of them are side effect free. f.sideEffects = SideEffects::worst(); f.isMSize = false; f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); f.literalArguments = false; } } void WasmDialect::addFunction( string _name, size_t _params, size_t _returns, bool _movable, bool _literalArguments ) { YulString name{move(_name)}; BuiltinFunction& f = m_functions[name]; f.name = name; f.parameters.resize(_params); f.returns.resize(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); f.isMSize = false; f.literalArguments = _literalArguments; }