Skip to content

Commit 35ca6ef

Browse files
authored
Merge pull request #7079 from ethereum/moreWorkOnEWasmExternals
Add eWasm externals.
2 parents 147f736 + 6c31a5f commit 35ca6ef

File tree

9 files changed

+440
-76
lines changed

9 files changed

+440
-76
lines changed

libyul/backends/wasm/EVMToEWasmTranslator.cpp

Lines changed: 153 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -268,61 +268,96 @@ function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
268268
// TODO implement
269269
unreachable()
270270
}
271+
272+
function u256_to_i64(x1, x2, x3, x4) -> v {
273+
if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() }
274+
v := x4
275+
}
276+
277+
function u256_to_i32(x1, x2, x3, x4) -> v {
278+
if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() }
279+
if i64.ne(0, i64.shr_u(x4, 32)) { invalid() }
280+
v := x4
281+
}
282+
283+
function u256_to_i32ptr(x1, x2, x3, x4) -> v {
284+
v := u256_to_i32(x1, x2, x3, x4)
285+
}
286+
271287
function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 {
272288
// TODO implement
273289
unreachable()
274290
}
275291
276292
function address() -> z1, z2, z3, z4 {
277-
// TODO implement
278-
unreachable()
293+
let t1, t2, t3, t4 := save_temp_mem_32()
294+
eth.getAddress(0)
295+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
296+
restore_temp_mem_32(t1, t2, t3, t4)
279297
}
280298
function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 {
281299
// TODO implement
282300
unreachable()
283301
}
284302
function origin() -> z1, z2, z3, z4 {
285-
// TODO implement
286-
unreachable()
303+
let t1, t2, t3, t4 := save_temp_mem_32()
304+
eth.getTxOrigin(0)
305+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
306+
restore_temp_mem_32(t1, t2, t3, t4)
287307
}
288308
function caller() -> z1, z2, z3, z4 {
289-
// TODO implement
290-
unreachable()
309+
let t1, t2, t3, t4 := save_temp_mem_32()
310+
eth.getCaller(0)
311+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
312+
restore_temp_mem_32(t1, t2, t3, t4)
291313
}
292314
function callvalue() -> z1, z2, z3, z4 {
293-
// TODO implement
294-
unreachable()
315+
let t1, t2, t3, t4 := save_temp_mem_32()
316+
eth.getCallValue(0)
317+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
318+
restore_temp_mem_32(t1, t2, t3, t4)
295319
}
296320
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
297-
// TODO implement
298-
unreachable()
321+
let t1, t2, t3, t4 := save_temp_mem_32()
322+
eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32)
323+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
324+
restore_temp_mem_32(t1, t2, t3, t4)
299325
}
300326
function calldatasize() -> z1, z2, z3, z4 {
301-
// TODO implement
302-
unreachable()
327+
z4 := eth.getCallDataSize()
303328
}
304329
function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
305-
// TODO implement
306-
unreachable()
330+
eth.callDataCopy(
331+
u256_to_i32ptr(x1, x2, x3, x4),
332+
u256_to_i32(y1, y2, y3, y4),
333+
u256_to_i32(z1, z2, z3, z4)
334+
)
307335
}
308336
309337
// Needed?
310338
function codesize() -> z1, z2, z3, z4 {
311-
// TODO implement
312-
unreachable()
339+
let t1, t2, t3, t4 := save_temp_mem_32()
340+
eth.getCodeSize(0)
341+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
342+
restore_temp_mem_32(t1, t2, t3, t4)
313343
}
314344
function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
315-
// TODO implement
316-
unreachable()
345+
eth.codeCopy(
346+
u256_to_i32ptr(x1, x2, x3, x4),
347+
u256_to_i32(y1, y2, y3, y4),
348+
u256_to_i32(z1, z2, z3, z4)
349+
)
317350
}
318351
function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
319-
// TODO implement
320-
unreachable()
352+
// TODO correct?
353+
codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4)
321354
}
322355
323356
function gasprice() -> z1, z2, z3, z4 {
324-
// TODO implement
325-
unreachable()
357+
let t1, t2, t3, t4 := save_temp_mem_32()
358+
eth.getTxGasPrice(0)
359+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
360+
restore_temp_mem_32(t1, t2, t3, t4)
326361
}
327362
function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 {
328363
// TODO implement
@@ -338,12 +373,14 @@ function extcodecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
338373
}
339374
340375
function returndatasize() -> z1, z2, z3, z4 {
341-
// TODO implement
342-
unreachable()
376+
z4 := eth.getReturnDataSize()
343377
}
344378
function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) {
345-
// TODO implement
346-
unreachable()
379+
eth.returnDataCopy(
380+
u256_to_i32ptr(x1, x2, x3, x4),
381+
u256_to_i32(y1, y2, y3, y4),
382+
u256_to_i32(z1, z2, z3, z4)
383+
)
347384
}
348385
349386
function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 {
@@ -355,34 +392,87 @@ function coinbase() -> z1, z2, z3, z4 {
355392
unreachable()
356393
}
357394
function timestamp() -> z1, z2, z3, z4 {
358-
// TODO implement
359-
unreachable()
395+
z4 := eth.getBlockTimestamp()
360396
}
361397
function number() -> z1, z2, z3, z4 {
362-
// TODO implement
363-
unreachable()
398+
z4 := eth.getBlockNumber()
364399
}
365400
function difficulty() -> z1, z2, z3, z4 {
366-
// TODO implement
367-
unreachable()
401+
let t1, t2, t3, t4 := save_temp_mem_32()
402+
eth.getBlockDifficulty(0)
403+
z1, z2, z3, z4 := mload(0, 0, 0, 0)
404+
restore_temp_mem_32(t1, t2, t3, t4)
368405
}
369406
function gaslimit() -> z1, z2, z3, z4 {
370-
// TODO implement
371-
unreachable()
407+
z4 := eth.getBlockGasLimit()
372408
}
373409
374410
function pop(x1, x2, x3, x4) {
375-
// TODO implement
376-
unreachable()
377411
}
378412
413+
414+
function endian_swap_16(x) -> y {
415+
let hi := i64.and(i64.shl(x, 8), 0xff00)
416+
let lo := i64.and(i64.shr_u(x, 8), 0xff)
417+
y := i64.or(hi, lo)
418+
}
419+
420+
function endian_swap_32(x) -> y {
421+
let hi := i64.shl(endian_swap_16(x), 16)
422+
let lo := endian_swap_16(i64.shr_u(x, 16))
423+
y := i64.or(hi, lo)
424+
}
425+
426+
function endian_swap(x) -> y {
427+
let hi := i64.shl(endian_swap_32(x), 32)
428+
let lo := endian_swap_32(i64.shr_u(x, 32))
429+
y := i64.or(hi, lo)
430+
}
431+
function save_temp_mem_32() -> t1, t2, t3, t4 {
432+
t1 := i64.load(0)
433+
t2 := i64.load(8)
434+
t3 := i64.load(16)
435+
t4 := i64.load(24)
436+
}
437+
function restore_temp_mem_32(t1, t2, t3, t4) {
438+
i64.store(0, t1)
439+
i64.store(8, t2)
440+
i64.store(16, t3)
441+
i64.store(24, t4)
442+
}
443+
function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 {
444+
t1 := i64.load(0)
445+
t2 := i64.load(8)
446+
t3 := i64.load(16)
447+
t4 := i64.load(24)
448+
t5 := i64.load(32)
449+
t6 := i64.load(40)
450+
t7 := i64.load(48)
451+
t8 := i64.load(54)
452+
}
453+
function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) {
454+
i64.store(0, t1)
455+
i64.store(8, t2)
456+
i64.store(16, t3)
457+
i64.store(24, t4)
458+
i64.store(32, t5)
459+
i64.store(40, t6)
460+
i64.store(48, t7)
461+
i64.store(54, t8)
462+
}
379463
function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
380-
// TODO implement
381-
unreachable()
464+
let pos := u256_to_i32ptr(x1, x2, x3, x4)
465+
z1 := endian_swap(i64.load(pos))
466+
z2 := endian_swap(i64.load(i64.add(pos, 8)))
467+
z3 := endian_swap(i64.load(i64.add(pos, 16)))
468+
z4 := endian_swap(i64.load(i64.add(pos, 24)))
382469
}
383470
function mstore(x1, x2, x3, x4, y1, y2, y3, y4) {
384-
// TODO implement
385-
unreachable()
471+
let pos := u256_to_i32ptr(x1, x2, x3, x4)
472+
i64.store(pos, endian_swap(x1))
473+
i64.store(i64.add(pos, 8), endian_swap(x2))
474+
i64.store(i64.add(pos, 16), endian_swap(x3))
475+
i64.store(i64.add(pos, 24), endian_swap(x4))
386476
}
387477
function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) {
388478
// TODO implement
@@ -394,12 +484,19 @@ function msize() -> z1, z2, z3, z4 {
394484
unreachable()
395485
}
396486
function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
397-
// TODO implement
398-
unreachable()
487+
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
488+
mstore(0, 0, 0, 0, x1, x2, x3, x4)
489+
eth.storageLoad(0, 16)
490+
z1, z2, z3, z4 := mload(0, 0, 0, 16)
491+
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
399492
}
493+
400494
function sstore(x1, x2, x3, x4, y1, y2, y3, y4) {
401-
// TODO implement
402-
unreachable()
495+
let t1, t2, t3, t4, t5, t6, t7, t8 := save_temp_mem_64()
496+
mstore(0, 0, 0, 0, x1, x2, x3, x4)
497+
mstore(0, 0, 0, 32, y1, y2, y3, y4)
498+
eth.storageStore(0, 32)
499+
restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8)
403500
}
404501
405502
// Needed?
@@ -408,8 +505,7 @@ function pc() -> z1, z2, z3, z4 {
408505
unreachable()
409506
}
410507
function gas() -> z1, z2, z3, z4 {
411-
// TODO implement
412-
unreachable()
508+
z4 := eth.getGasLeft()
413509
}
414510
415511
function log0(p1, p2, p3, p4, s1, s2, s3, s4) {
@@ -511,17 +607,22 @@ function create2(
511607
unreachable()
512608
}
513609
function selfdestruct(a1, a2, a3, a4) {
514-
// TODO implement
515-
unreachable()
610+
mstore(0, 0, 0, 0, a1, a2, a3, a4)
611+
// In EVM, addresses are padded to 32 bytes, so discard the first 12.
612+
eth.selfDestruct(12)
516613
}
517614
518615
function return(x1, x2, x3, x4, y1, y2, y3, y4) {
519-
// TODO implement
520-
unreachable()
616+
eth.finish(
617+
u256_to_i32ptr(x1, x2, x3, x4),
618+
u256_to_i32(y1, y2, y3, y4)
619+
)
521620
}
522621
function revert(x1, x2, x3, x4, y1, y2, y3, y4) {
523-
// TODO implement
524-
unreachable()
622+
eth.revert(
623+
u256_to_i32ptr(x1, x2, x3, x4),
624+
u256_to_i32(y1, y2, y3, y4)
625+
)
525626
}
526627
function invalid() {
527628
unreachable()

libyul/backends/wasm/EWasmAST.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ struct Continue { Label label; };
7070

7171
struct VariableDeclaration { std::string variableName; };
7272
struct GlobalVariableDeclaration { std::string variableName; };
73+
struct FunctionImport {
74+
std::string module;
75+
std::string externalName;
76+
std::string internalName;
77+
std::vector<std::string> paramTypes;
78+
std::unique_ptr<std::string> returnType;
79+
};
7380

7481
struct FunctionDefinition
7582
{

libyul/backends/wasm/EWasmCodeTransform.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
5252
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
5353
}
5454

55-
return EWasmToText{}.run(transform.m_globalVariables, functions);
55+
std::vector<wasm::FunctionImport> imports;
56+
for (auto& imp: transform.m_functionsToImport)
57+
imports.emplace_back(std::move(imp.second));
58+
return EWasmToText{}.run(
59+
transform.m_globalVariables,
60+
imports,
61+
functions
62+
);
5663
}
5764

5865
wasm::Expression EWasmCodeTransform::generateMultiAssignment(
@@ -128,7 +135,25 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
128135
{
129136
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
130137
{
131-
if (builtin->literalArguments)
138+
if (_call.functionName.name.str().substr(0, 4) == "eth.")
139+
{
140+
yulAssert(builtin->returns.size() <= 1, "");
141+
// Imported function, use regular call, but mark for import.
142+
if (!m_functionsToImport.count(builtin->name))
143+
{
144+
wasm::FunctionImport imp{
145+
"ethereum",
146+
builtin->name.str().substr(4),
147+
builtin->name.str(),
148+
{},
149+
builtin->returns.empty() ? nullptr : make_unique<string>(builtin->returns.front().str())
150+
};
151+
for (auto const& param: builtin->parameters)
152+
imp.paramTypes.emplace_back(param.str());
153+
m_functionsToImport[builtin->name] = std::move(imp);
154+
}
155+
}
156+
else if (builtin->literalArguments)
132157
{
133158
vector<wasm::Expression> literals;
134159
for (auto const& arg: _call.arguments)
@@ -138,12 +163,12 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
138163
else
139164
return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)};
140165
}
141-
else
142-
// If this function returns multiple values, then the first one will
143-
// be returned in the expression itself and the others in global variables.
144-
// The values have to be used right away in an assignment or variable declaration,
145-
// so it is handled there.
146-
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
166+
167+
// If this function returns multiple values, then the first one will
168+
// be returned in the expression itself and the others in global variables.
169+
// The values have to be used right away in an assignment or variable declaration,
170+
// so it is handled there.
171+
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
147172
}
148173

149174
wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier)

libyul/backends/wasm/EWasmCodeTransform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <libyul/optimiser/NameDispenser.h>
2727

2828
#include <stack>
29+
#include <map>
2930

3031
namespace yul
3132
{
@@ -90,6 +91,7 @@ class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
9091

9192
std::vector<wasm::VariableDeclaration> m_localVariables;
9293
std::vector<wasm::GlobalVariableDeclaration> m_globalVariables;
94+
std::map<YulString, wasm::FunctionImport> m_functionsToImport;
9395
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
9496
};
9597

0 commit comments

Comments
 (0)