mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-20 11:46:44 +00:00
Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc
Conflicts: evmcc/test/jump/loop1.evm evmcc/test/jump/loop1.lll evmcc/test/jump/loop2.evm evmcc/test/jump/loop2.lll
This commit is contained in:
commit
07131e4e18
13 changed files with 126 additions and 85 deletions
|
|
@ -190,10 +190,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
IRBuilder<> builder(context);
|
||||
|
||||
// Create main function
|
||||
const auto i32Ty = builder.getInt32Ty();
|
||||
//Type* retTypeElems[] = {i32Ty, i32Ty};
|
||||
//auto retType = StructType::create(retTypeElems, "MemRef", true);
|
||||
m_mainFunc = Function::Create(FunctionType::get(builder.getInt64Ty(), false), Function::ExternalLinkage, "main", module.get());
|
||||
m_mainFunc = Function::Create(FunctionType::get(Type::MainReturn, false), Function::ExternalLinkage, "main", module.get());
|
||||
|
||||
// Create the basic blocks.
|
||||
auto entryBlock = llvm::BasicBlock::Create(context, "entry", m_mainFunc);
|
||||
|
|
@ -423,12 +420,12 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
|
||||
// TODO: Shifting by 0 gives wrong results as of this bug http://llvm.org/bugs/show_bug.cgi?id=16439
|
||||
|
||||
auto shbits = builder.CreateShl(byteNum, builder.getIntN(256, 3));
|
||||
auto shbits = builder.CreateShl(byteNum, Constant::get(3));
|
||||
value = builder.CreateShl(value, shbits);
|
||||
value = builder.CreateLShr(value, builder.getIntN(256, 31 * 8));
|
||||
value = builder.CreateLShr(value, Constant::get(31 * 8));
|
||||
|
||||
auto byteNumValid = builder.CreateICmpULT(byteNum, builder.getIntN(256, 32));
|
||||
value = builder.CreateSelect(byteNumValid, value, builder.getIntN(256, 0));
|
||||
auto byteNumValid = builder.CreateICmpULT(byteNum, Constant::get(32));
|
||||
value = builder.CreateSelect(byteNumValid, value, Constant::get(0));
|
||||
stack.push(value);
|
||||
|
||||
break;
|
||||
|
|
@ -684,7 +681,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
|
||||
case Instruction::PC:
|
||||
{
|
||||
auto value = builder.getIntN(256, currentPC);
|
||||
auto value = Constant::get(currentPC);
|
||||
stack.push(value);
|
||||
break;
|
||||
}
|
||||
|
|
@ -756,7 +753,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
|
||||
case Instruction::CODESIZE:
|
||||
{
|
||||
auto value = builder.getIntN(256, bytecode.size());
|
||||
auto value = Constant::get(bytecode.size());
|
||||
stack.push(value);
|
||||
break;
|
||||
}
|
||||
|
|
@ -834,13 +831,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
auto index = stack.pop();
|
||||
auto size = stack.pop();
|
||||
|
||||
auto ret = builder.CreateTrunc(index, builder.getInt64Ty());
|
||||
ret = builder.CreateShl(ret, 32);
|
||||
size = builder.CreateTrunc(size, i32Ty);
|
||||
size = builder.CreateZExt(size, builder.getInt64Ty());
|
||||
ret = builder.CreateOr(ret, size);
|
||||
memory.registerReturnData(index, size);
|
||||
|
||||
builder.CreateRet(ret);
|
||||
builder.CreateRet(Constant::get(ReturnCode::Return));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -852,7 +845,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
}
|
||||
case Instruction::STOP:
|
||||
{
|
||||
builder.CreateRet(builder.getInt64(0));
|
||||
builder.CreateRet(Constant::get(ReturnCode::Stop));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -884,11 +877,11 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
// Note: Right now the codegen for special blocks depends only on createBasicBlock(),
|
||||
// not on the codegen for 'regular' blocks. But it has to be done before linkBasicBlocks().
|
||||
builder.SetInsertPoint(m_finalBlock->llvm());
|
||||
builder.CreateRet(builder.getInt64(0));
|
||||
builder.CreateRet(Constant::get(ReturnCode::Stop));
|
||||
|
||||
// TODO: throw an exception or something
|
||||
builder.SetInsertPoint(m_badJumpBlock->llvm());
|
||||
builder.CreateRet(builder.getInt64(0));
|
||||
builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
|
||||
|
||||
builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
||||
if (m_indirectJumpTargets.size() > 0)
|
||||
|
|
@ -919,17 +912,22 @@ std::unique_ptr<llvm::Module> Compiler::compile(const dev::bytes& bytecode)
|
|||
void Compiler::linkBasicBlocks()
|
||||
{
|
||||
/// Helper function that finds basic block given LLVM basic block pointer
|
||||
auto findBasicBlock = [this](llvm::BasicBlock* _llbb) -> BasicBlock&
|
||||
auto findBasicBlock = [this](llvm::BasicBlock* _llbb) -> BasicBlock*
|
||||
{
|
||||
// TODO: Fix for finding jumpTableBlock
|
||||
if (_llbb == this->m_jumpTableBlock->llvm())
|
||||
return *this->m_jumpTableBlock;
|
||||
return this->m_jumpTableBlock.get();
|
||||
|
||||
for (auto&& bb : this->basicBlocks)
|
||||
if (_llbb == bb.second.llvm())
|
||||
return &bb.second;
|
||||
return nullptr;
|
||||
|
||||
// Name is used to get basic block index (index of first instruction)
|
||||
// TODO: If basicBlocs are still a map - multikey map can be used
|
||||
auto&& idxStr = _llbb->getName().substr(sizeof(BasicBlock::NamePrefix) - 2);
|
||||
auto idx = std::stoul(idxStr);
|
||||
return basicBlocks.find(idx)->second;
|
||||
//auto&& idxStr = _llbb->getName().substr(sizeof(BasicBlock::NamePrefix) - 2);
|
||||
//auto idx = std::stoul(idxStr);
|
||||
//return basicBlocks.find(idx)->second;
|
||||
};
|
||||
|
||||
auto completePhiNodes = [findBasicBlock](llvm::BasicBlock* _llbb) -> void
|
||||
|
|
@ -942,9 +940,14 @@ void Compiler::linkBasicBlocks()
|
|||
for (auto predIt = llvm::pred_begin(_llbb); predIt != llvm::pred_end(_llbb); ++predIt)
|
||||
{
|
||||
// TODO: In case entry block is reached - report error
|
||||
auto& predBB = findBasicBlock(*predIt);
|
||||
auto value = predBB.getStack().get(valueIdx);
|
||||
phi->addIncoming(value, predBB);
|
||||
auto predBB = findBasicBlock(*predIt);
|
||||
if (!predBB)
|
||||
{
|
||||
std::cerr << "Stack too small in " << _llbb->getName().str() << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
auto value = predBB->getStack().get(valueIdx);
|
||||
phi->addIncoming(value, predBB->llvm());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#include <llvm/Support/Host.h>
|
||||
|
||||
#include "Runtime.h"
|
||||
#include "Memory.h"
|
||||
#include "Type.h"
|
||||
|
||||
namespace evmcc
|
||||
{
|
||||
|
|
@ -97,13 +99,13 @@ int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module)
|
|||
|
||||
auto result = exec->runFunction(entryFunc, {});
|
||||
gas = static_cast<decltype(gas)>(Runtime::getGas());
|
||||
if (auto intResult = result.IntVal.getZExtValue())
|
||||
auto returnCode = static_cast<ReturnCode>(result.IntVal.getZExtValue());
|
||||
if (returnCode == ReturnCode::Return)
|
||||
{
|
||||
auto index = intResult >> 32;
|
||||
auto size = 0xFFFFFFFF & intResult;
|
||||
auto&& returnData = Memory::getReturnData(); // TODO: It might be better to place is in Runtime interface
|
||||
|
||||
std::cout << "RETURN [ ";
|
||||
for (dev::bytes::const_iterator it = Runtime::getMemory().cbegin() + index, end = it + size; it != end; ++it)
|
||||
for (auto it = returnData.begin(), end = returnData.end(); it != end; ++it)
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)*it << " ";
|
||||
std::cout << "]\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ uint64_t getStepCost(dev::eth::Instruction inst) // TODO: Add this function to F
|
|||
return static_cast<uint64_t>(c_createGas);
|
||||
|
||||
default: // Assumes instruction code is valid
|
||||
return static_cast<uint64_t>(c_stepGas);;
|
||||
return static_cast<uint64_t>(c_stepGas);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ void GasMeter::commitCostBlock()
|
|||
if (m_checkCall)
|
||||
{
|
||||
if (m_blockCost > 0) // If any cost
|
||||
m_checkCall->setArgOperand(0, m_builder.getIntN(256, m_blockCost)); // Update block cost in gas check call
|
||||
m_checkCall->setArgOperand(0, Constant::get(m_blockCost)); // Update block cost in gas check call
|
||||
else
|
||||
m_checkCall->eraseFromParent(); // Remove the gas check call
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ void GasMeter::commitCostBlock()
|
|||
void GasMeter::checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilder<>& _builder)
|
||||
{
|
||||
// Memory uses other builder, but that can be changes later
|
||||
auto cost = _builder.CreateMul(_additionalMemoryInWords, _builder.getIntN(256, static_cast<uint64_t>(c_memoryGas)), "memcost");
|
||||
auto cost = _builder.CreateMul(_additionalMemoryInWords, Constant::get(static_cast<uint64_t>(c_memoryGas)), "memcost");
|
||||
_builder.CreateCall(m_gasCheckFunc, cost);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,20 +21,8 @@ namespace evmcc
|
|||
Memory::Memory(llvm::IRBuilder<>& _builder, llvm::Module* _module, GasMeter& _gasMeter):
|
||||
m_builder(_builder)
|
||||
{
|
||||
auto voidTy = m_builder.getVoidTy();
|
||||
auto i64Ty = m_builder.getInt64Ty();
|
||||
|
||||
auto memRequireTy = llvm::FunctionType::get(m_builder.getInt8PtrTy(), i64Ty, false);
|
||||
m_memRequire = llvm::Function::Create(memRequireTy,
|
||||
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
|
||||
"evmccrt_memory_require", _module);
|
||||
|
||||
auto memSizeTy = llvm::FunctionType::get(i64Ty, false);
|
||||
m_memSize = llvm::Function::Create(memSizeTy,
|
||||
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
|
||||
"evmccrt_memory_size", _module);
|
||||
|
||||
std::vector<llvm::Type*> argTypes = {i64Ty, i64Ty};
|
||||
llvm::Type* argTypes[] = {i64Ty, i64Ty};
|
||||
auto dumpTy = llvm::FunctionType::get(m_builder.getVoidTy(), llvm::ArrayRef<llvm::Type*>(argTypes), false);
|
||||
m_memDump = llvm::Function::Create(dumpTy, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
|
||||
"evmccrt_memory_dump", _module);
|
||||
|
|
@ -42,9 +30,15 @@ Memory::Memory(llvm::IRBuilder<>& _builder, llvm::Module* _module, GasMeter& _ga
|
|||
m_data = new llvm::GlobalVariable(*_module, Type::BytePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::BytePtr), "mem.data");
|
||||
m_data->setUnnamedAddr(true); // Address is not important
|
||||
|
||||
m_size = new llvm::GlobalVariable(*_module, Type::i256, false, llvm::GlobalVariable::PrivateLinkage, m_builder.getIntN(256, 0), "mem.size");
|
||||
m_size = new llvm::GlobalVariable(*_module, Type::i256, false, llvm::GlobalVariable::PrivateLinkage, Constant::get(0), "mem.size");
|
||||
m_size->setUnnamedAddr(true); // Address is not important
|
||||
|
||||
m_returnDataOffset = new llvm::GlobalVariable(*_module, Type::i256, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "mem_returnDataOffset");
|
||||
m_returnDataOffset->setUnnamedAddr(true); // Address is not important
|
||||
|
||||
m_returnDataSize = new llvm::GlobalVariable(*_module, Type::i256, false, llvm::GlobalVariable::ExternalLinkage, nullptr, "mem_returnDataSize");
|
||||
m_returnDataSize->setUnnamedAddr(true); // Address is not important
|
||||
|
||||
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, Type::WordPtr, false), llvm::Function::ExternalLinkage, "mem_resize", _module);
|
||||
m_loadWord = createFunc(false, Type::i256, _module, _gasMeter);
|
||||
m_storeWord = createFunc(true, Type::i256, _module, _gasMeter);
|
||||
|
|
@ -69,7 +63,7 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, llvm::
|
|||
llvm::Value* index = func->arg_begin();
|
||||
index->setName("index");
|
||||
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
|
||||
auto sizeRequired = builder.CreateAdd(index, builder.getIntN(256, valueSize), "sizeRequired");
|
||||
auto sizeRequired = builder.CreateAdd(index, Constant::get(valueSize), "sizeRequired");
|
||||
auto size = builder.CreateLoad(m_size, "size");
|
||||
auto resizeNeeded = builder.CreateICmpULE(size, sizeRequired, "resizeNeeded");
|
||||
builder.CreateCondBr(resizeNeeded, resizeBB, accessBB); // OPT branch weights?
|
||||
|
|
@ -77,8 +71,8 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, llvm::
|
|||
// BB "resize"
|
||||
builder.SetInsertPoint(resizeBB);
|
||||
// Check gas first
|
||||
auto wordsRequired = builder.CreateUDiv(builder.CreateAdd(sizeRequired, builder.getIntN(256, 31)), builder.getIntN(256, 32), "wordsRequired");
|
||||
auto words = builder.CreateUDiv(builder.CreateAdd(size, builder.getIntN(256, 31)), builder.getIntN(256, 32), "words");
|
||||
auto wordsRequired = builder.CreateUDiv(builder.CreateAdd(sizeRequired, Constant::get(31)), Constant::get(32), "wordsRequired");
|
||||
auto words = builder.CreateUDiv(builder.CreateAdd(size, Constant::get(31)), Constant::get(32), "words");
|
||||
auto newWords = builder.CreateSub(wordsRequired, words, "addtionalWords");
|
||||
_gasMeter.checkMemory(newWords, builder);
|
||||
// Resize
|
||||
|
|
@ -137,6 +131,15 @@ llvm::Value* Memory::getSize()
|
|||
return m_builder.CreateLoad(m_size);
|
||||
}
|
||||
|
||||
void Memory::registerReturnData(llvm::Value* _index, llvm::Value* _size)
|
||||
{
|
||||
auto lastWord = m_builder.CreateAdd(_index, m_builder.CreateSub(_size, Constant::get(32)), "lastWord");
|
||||
loadWord(lastWord); // Make sure that memory is allocated and count gas
|
||||
|
||||
m_builder.CreateStore(_index, m_returnDataOffset);
|
||||
m_builder.CreateStore(_size, m_returnDataSize);
|
||||
}
|
||||
|
||||
void Memory::dump(uint64_t _begin, uint64_t _end)
|
||||
{
|
||||
if (getenv("EVMCC_DEBUG_MEMORY") == nullptr)
|
||||
|
|
@ -155,6 +158,9 @@ extern "C"
|
|||
{
|
||||
using namespace evmcc;
|
||||
|
||||
EXPORT i256 mem_returnDataOffset;
|
||||
EXPORT i256 mem_returnDataSize;
|
||||
|
||||
EXPORT uint8_t* mem_resize(i256* _size)
|
||||
{
|
||||
auto size = _size->a; // Trunc to 64-bit
|
||||
|
|
@ -163,37 +169,13 @@ EXPORT uint8_t* mem_resize(i256* _size)
|
|||
return memory.data();
|
||||
}
|
||||
|
||||
// Resizes memory to contain at least _index + 1 bytes and returns the base address.
|
||||
EXPORT uint8_t* evmccrt_memory_require(uint64_t _index)
|
||||
{
|
||||
uint64_t requiredSize = (_index / 32 + 1) * 32;
|
||||
auto&& memory = Runtime::getMemory();
|
||||
|
||||
if (memory.size() < requiredSize)
|
||||
{
|
||||
std::cerr << "MEMORY: current size: " << std::dec
|
||||
<< memory.size() << " bytes, required size: "
|
||||
<< requiredSize << " bytes"
|
||||
<< std::endl;
|
||||
|
||||
memory.resize(requiredSize);
|
||||
}
|
||||
|
||||
return memory.data();
|
||||
}
|
||||
|
||||
EXPORT uint64_t evmccrt_memory_size()
|
||||
{
|
||||
return Runtime::getMemory().size() / 32;
|
||||
}
|
||||
|
||||
EXPORT void evmccrt_memory_dump(uint64_t _begin, uint64_t _end)
|
||||
{
|
||||
if (_end == 0)
|
||||
_end = Runtime::getMemory().size();
|
||||
|
||||
std::cerr << "MEMORY: active size: " << std::dec
|
||||
<< evmccrt_memory_size() << " words\n";
|
||||
<< Runtime::getMemory().size() / 32 << " words\n";
|
||||
std::cerr << "MEMORY: dump from " << std::dec
|
||||
<< _begin << " to " << _end << ":";
|
||||
if (_end <= _begin)
|
||||
|
|
@ -212,3 +194,12 @@ EXPORT void evmccrt_memory_dump(uint64_t _begin, uint64_t _end)
|
|||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
dev::bytesConstRef evmcc::Memory::getReturnData()
|
||||
{
|
||||
// TODO: Handle large indexes
|
||||
auto offset = static_cast<size_t>(llvm2eth(mem_returnDataOffset));
|
||||
auto size = static_cast<size_t>(llvm2eth(mem_returnDataSize));
|
||||
auto& memory = Runtime::getMemory();
|
||||
return {memory.data() + offset, size};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ public:
|
|||
void storeByte(llvm::Value* _addr, llvm::Value* _byte);
|
||||
llvm::Value* getSize();
|
||||
|
||||
void registerReturnData(llvm::Value* _index, llvm::Value* _size);
|
||||
static dev::bytesConstRef getReturnData();
|
||||
|
||||
void dump(uint64_t _begin, uint64_t _end = 0);
|
||||
|
||||
private:
|
||||
|
|
@ -31,14 +34,16 @@ private:
|
|||
llvm::GlobalVariable* m_data;
|
||||
llvm::GlobalVariable* m_size;
|
||||
|
||||
/// @TODO: m_data and m_size could be used
|
||||
llvm::GlobalVariable* m_returnDataOffset;
|
||||
llvm::GlobalVariable* m_returnDataSize;
|
||||
|
||||
llvm::Function* m_loadWord;
|
||||
llvm::Function* m_storeWord;
|
||||
llvm::Function* m_storeByte;
|
||||
llvm::Function* m_resize;
|
||||
|
||||
llvm::Function* m_memRequire;
|
||||
llvm::Function* m_memDump;
|
||||
llvm::Function* m_memSize;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ llvm::IntegerType* Type::lowPrecision;
|
|||
llvm::IntegerType* Type::Byte;
|
||||
llvm::PointerType* Type::BytePtr;
|
||||
llvm::Type* Type::Void;
|
||||
llvm::Type* Type::MainReturn;
|
||||
|
||||
void Type::init(llvm::LLVMContext& _context)
|
||||
{
|
||||
|
|
@ -21,6 +22,17 @@ void Type::init(llvm::LLVMContext& _context)
|
|||
Byte = llvm::Type::getInt8Ty(_context);
|
||||
BytePtr = Byte->getPointerTo();
|
||||
Void = llvm::Type::getVoidTy(_context);
|
||||
MainReturn = llvm::Type::getInt32Ty(_context);
|
||||
}
|
||||
|
||||
llvm::Constant* Constant::get(uint64_t _n)
|
||||
{
|
||||
return llvm::ConstantInt::get(Type::i256, _n);
|
||||
}
|
||||
|
||||
llvm::Constant* Constant::get(ReturnCode _returnCode)
|
||||
{
|
||||
return llvm::ConstantInt::get(Type::MainReturn, static_cast<uint64_t>(_returnCode));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
21
evmcc/Type.h
21
evmcc/Type.h
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <llvm/IR/Type.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
|
||||
namespace evmcc
|
||||
{
|
||||
|
|
@ -20,7 +21,27 @@ struct Type
|
|||
|
||||
static llvm::Type* Void;
|
||||
|
||||
/// Main function return type
|
||||
static llvm::Type* MainReturn;
|
||||
|
||||
static void init(llvm::LLVMContext& _context);
|
||||
};
|
||||
|
||||
enum class ReturnCode
|
||||
{
|
||||
Stop = 0,
|
||||
Return = 1,
|
||||
Suicide = 2,
|
||||
|
||||
BadJumpDestination = 101,
|
||||
};
|
||||
|
||||
struct Constant
|
||||
{
|
||||
/// Returns word-size constant
|
||||
static llvm::Constant* get(uint64_t _n);
|
||||
|
||||
static llvm::Constant* get(ReturnCode _returnCode);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
1
evmcc/bytecode/return2.evm
Normal file
1
evmcc/bytecode/return2.evm
Normal file
|
|
@ -0,0 +1 @@
|
|||
6001620f4240f2
|
||||
6
evmcc/lll/return2.lll
Normal file
6
evmcc/lll/return2.lll
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
(asm
|
||||
1
|
||||
1000000
|
||||
RETURN ;; return 1 byte from index 1M
|
||||
)
|
||||
|
|
@ -1 +1 @@
|
|||
600a600181038060025960005460015460025460035400
|
||||
600a600181038060025960005460015460025400
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ MSTORE
|
|||
MSTORE
|
||||
2
|
||||
MSTORE
|
||||
3
|
||||
MSTORE
|
||||
;;3
|
||||
;;MSTORE
|
||||
|
||||
STOP
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
600a80600190038060025960005460015460025460035400
|
||||
600a80600190038060025960005460015460025400
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
;; 2
|
||||
DUP1
|
||||
1
|
||||
1
|
||||
SWAP1
|
||||
SUB
|
||||
DUP1
|
||||
|
|
@ -19,8 +19,8 @@ MSTORE
|
|||
MSTORE
|
||||
2
|
||||
MSTORE
|
||||
3
|
||||
MSTORE
|
||||
;;3
|
||||
;;MSTORE
|
||||
|
||||
STOP
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue