diff --git a/examples/capi.c b/examples/capi.c index 74cd6ac3..893a94a5 100644 --- a/examples/capi.c +++ b/examples/capi.c @@ -4,7 +4,8 @@ #include "evm.h" -struct evm_uint256be balance(struct evm_env* env, struct evm_uint160be address) +struct evm_uint256be balance(struct evm_env* env, + const struct evm_uint160be* address) { struct evm_uint256be ret = {.bytes = {1, 2, 3, 4}}; return ret; @@ -19,19 +20,21 @@ struct evm_uint160be address(struct evm_env* env) static void query(union evm_variant* result, struct evm_env* env, enum evm_query_key key, - const union evm_variant* arg) { + const struct evm_uint160be* address, + const struct evm_uint256be* storage_key) { printf("EVM-C: QUERY %d\n", key); switch (key) { - case EVM_GAS_LIMIT: - result->int64 = 314; + case EVM_CODE_BY_ADDRESS: + result->data = NULL; + result->data_size = 0; break; case EVM_BALANCE: - result->uint256be = balance(env, arg->address); + result->uint256be = balance(env, address); break; - case EVM_ADDRESS: - result->address = address(env); + case EVM_ACCOUNT_EXISTS: + result->int64 = 0; break; default: @@ -41,6 +44,7 @@ static void query(union evm_variant* result, static void update(struct evm_env* env, enum evm_update_key key, + const struct evm_uint160be* addr, const union evm_variant* arg1, const union evm_variant* arg2) { @@ -49,38 +53,48 @@ static void update(struct evm_env* env, static int64_t call( struct evm_env* _opaqueEnv, - enum evm_call_kind _kind, - int64_t _gas, - const struct evm_uint160be* _address, - const struct evm_uint256be* _value, - uint8_t const* _inputData, - size_t _inputSize, + const struct evm_message* _msg, uint8_t* _outputData, size_t _outputSize ) { - printf("EVM-C: CALL %d\n", _kind); + printf("EVM-C: CALL (depth: %d)\n", _msg->depth); return EVM_CALL_FAILURE; } +static void get_tx_context(struct evm_tx_context* result, struct evm_env* env) +{ + +} + +static void get_block_hash(struct evm_uint256be* result, struct evm_env* env, + int64_t number) +{ + +} + /// Example how the API is supposed to be used. int main(int argc, char *argv[]) { struct evm_factory factory = examplevm_get_factory(); if (factory.abi_version != EVM_ABI_VERSION) return 1; // Incompatible ABI version. - struct evm_instance* jit = factory.create(query, update, call); + struct evm_instance* jit = factory.create(query, update, call, + get_tx_context, get_block_hash); uint8_t const code[] = "Place some EVM bytecode here"; const size_t code_size = sizeof(code); struct evm_uint256be code_hash = {.bytes = {1, 2, 3,}}; uint8_t const input[] = "Hello World!"; - struct evm_uint256be value = {{1, 0, 0, 0}}; - + struct evm_uint256be value = {{1, 0,}}; + struct evm_uint160be addr = {{0, 1, 2,}}; int64_t gas = 200000; + + struct evm_message msg = {addr, addr, value, input, sizeof(input), + code_hash, gas, 0}; + struct evm_result result = - jit->execute(jit, NULL, EVM_HOMESTEAD, code_hash, code, code_size, gas, - input, sizeof(input), value); + jit->execute(jit, NULL, EVM_HOMESTEAD, &msg, code, code_size); printf("Execution result:\n"); if (result.code != EVM_SUCCESS) { diff --git a/examples/examplevm.c b/examples/examplevm.c index f934072d..ae390ff5 100644 --- a/examples/examplevm.c +++ b/examples/examplevm.c @@ -1,14 +1,19 @@ #include #include +#include #include "evm.h" struct examplevm { struct evm_instance instance; - evm_query_fn query_fn; - evm_update_fn update_fn; + evm_query_state_fn query_fn; + evm_update_state_fn update_fn; evm_call_fn call_fn; + evm_get_tx_context_fn get_tx_context_fn; + evm_get_block_hash_fn get_block_hash_fn; + + int example_option; }; static void evm_destroy(struct evm_instance* evm) @@ -19,12 +24,19 @@ static void evm_destroy(struct evm_instance* evm) /// Example options. /// /// VMs are allowed to omit this function implementation. -int evm_set_option(struct evm_instance* evm, +int evm_set_option(struct evm_instance* instance, char const* name, char const* value) { - if (strcmp(name, "example-option") == 0) + struct examplevm* vm = (struct examplevm*)instance; + if (strcmp(name, "example-option") == 0) { + long int v = strtol(value, NULL, 0); + if (v > INT_MAX || v < INT_MIN) + return 0; + vm->example_option = (int)v; return 1; + } + return 0; } @@ -37,24 +49,20 @@ static void free_result_output_data(struct evm_result const* result) free((uint8_t*)result->output_data); } -static struct evm_result evm_execute(struct evm_instance* instance, - struct evm_env* env, - enum evm_mode mode, - struct evm_uint256be code_hash, - uint8_t const* code, - size_t code_size, - int64_t gas, - uint8_t const* input, - size_t input_size, - struct evm_uint256be value) +static struct evm_result execute(struct evm_instance* instance, + struct evm_env* env, + enum evm_mode mode, + const struct evm_message* msg, + const uint8_t* code, + size_t code_size) { struct evm_result ret = {}; if (code_size == 0) { // In case of empty code return a fancy error message. - const char* msg = mode == EVM_METROPOLIS ? - "Welcome to Metropolis!" : "Hello Ethereum!"; - ret.output_data = (const uint8_t*)msg; - ret.output_size = strlen(msg); + const char* error = mode == EVM_METROPOLIS ? + "Welcome to Metropolis!" : "Hello Ethereum!"; + ret.output_data = (const uint8_t*)error; + ret.output_size = strlen(error); ret.code = EVM_FAILURE; ret.release = NULL; // We don't need to release the constant messages. return ret; @@ -66,19 +74,21 @@ static struct evm_result evm_execute(struct evm_instance* instance, // Solidity inline assembly is used in the examples instead of EVM bytecode. // Assembly: `{ mstore(0, address()) return(0, msize()) }`. - const char return_by_address[] = "30600052596000f3"; - if (code_size == strlen(return_by_address) && - strncmp((const char*)code, return_by_address, code_size)) { - union evm_variant query_result; - vm->query_fn(&query_result, env, EVM_ADDRESS, NULL); - static const size_t address_size = sizeof(query_result.address); + const char return_address[] = "30600052596000f3"; + + // Assembly: `{ sstore(0, add(sload(0), 1)) }` + const char counter[] = "600160005401600055"; + + if (code_size == strlen(return_address) && + strncmp((const char*)code, return_address, code_size)) { + static const size_t address_size = sizeof(msg->address); uint8_t* output_data = (uint8_t*)malloc(address_size); if (!output_data) { // malloc failed, report internal error. ret.code = EVM_INTERNAL_ERROR; return ret; } - memcpy(output_data, &query_result.address, address_size); + memcpy(output_data, &msg->address, address_size); ret.code = EVM_SUCCESS; ret.output_data = output_data; ret.output_size = address_size; @@ -86,6 +96,18 @@ static struct evm_result evm_execute(struct evm_instance* instance, ret.context = NULL; // We don't need another pointer. return ret; } + else if (code_size == strlen(counter) && + strncmp((const char*)code, counter, code_size)) { + union evm_variant value; + const struct evm_uint256be index = {{0,}}; + vm->query_fn(&value, env, EVM_SLOAD, &msg->address, &index); + value.uint256be.bytes[31] += 1; + union evm_variant arg; + arg.uint256be = index; + vm->update_fn(env, EVM_SSTORE, &msg->address, &arg, &value); + ret.code = EVM_SUCCESS; + return ret; + } ret.release = evm_release_result; ret.code = EVM_FAILURE; @@ -94,18 +116,22 @@ static struct evm_result evm_execute(struct evm_instance* instance, return ret; } -static struct evm_instance* evm_create(evm_query_fn query_fn, - evm_update_fn update_fn, - evm_call_fn call_fn) +static struct evm_instance* evm_create(evm_query_state_fn query_fn, + evm_update_state_fn update_fn, + evm_call_fn call_fn, + evm_get_tx_context_fn get_tx_context_fn, + evm_get_block_hash_fn get_block_hash_fn) { struct examplevm* vm = calloc(1, sizeof(struct examplevm)); struct evm_instance* interface = &vm->instance; interface->destroy = evm_destroy; - interface->execute = evm_execute; + interface->execute = execute; interface->set_option = evm_set_option; vm->query_fn = query_fn; vm->update_fn = update_fn; vm->call_fn = call_fn; + vm->get_tx_context_fn = get_tx_context_fn; + vm->get_block_hash_fn = get_block_hash_fn; return interface; } diff --git a/include/evm.h b/include/evm.h index abef3f99..52cb91af 100644 --- a/include/evm.h +++ b/include/evm.h @@ -29,6 +29,10 @@ enum { EVM_ABI_VERSION = 0 }; +/// Opaque struct representing execution environment managed by the host +/// application. +struct evm_env; + /// Big-endian 256-bit integer. /// /// 32 bytes of data representing big-endian 256-bit integer. I.e. bytes[0] is @@ -41,11 +45,49 @@ struct evm_uint256be { }; /// Big-endian 160-bit hash suitable for keeping an Ethereum address. +/// TODO: Rename to "address". struct evm_uint160be { /// The 20 bytes of the hash. uint8_t bytes[20]; }; +/// The kind of call-like instruction. +enum evm_call_kind { + EVM_CALL = 0, ///< Request CALL. + EVM_DELEGATECALL = 1, ///< Request DELEGATECALL. The value param ignored. + EVM_CALLCODE = 2, ///< Request CALLCODE. + EVM_CREATE = 3 ///< Request CREATE. Semantic of some params changes. +}; + +struct evm_message { + struct evm_uint160be address; + struct evm_uint160be sender; + struct evm_uint256be value; + const uint8_t* input; + size_t input_size; + struct evm_uint256be code_hash; + int64_t gas; + int32_t depth; + enum evm_call_kind kind; +}; + +struct evm_tx_context { + struct evm_uint256be tx_gas_price; + struct evm_uint160be tx_origin; + struct evm_uint160be block_coinbase; + int64_t block_number; + int64_t block_timestamp; + int64_t block_gas_limit; + struct evm_uint256be block_difficulty; +}; + +typedef void (*evm_get_tx_context_fn)(struct evm_tx_context* result, + struct evm_env* env); + +typedef void (*evm_get_block_hash_fn)(struct evm_uint256be* result, + struct evm_env* env, + int64_t number); + /// The execution result code. enum evm_result_code { EVM_SUCCESS = 0, ///< Execution finished with success. @@ -138,28 +180,13 @@ struct evm_result { /// The query callback key. enum evm_query_key { EVM_SLOAD = 0, ///< Storage value of a given key for SLOAD. - EVM_ADDRESS = 1, ///< Address of the contract for ADDRESS. - EVM_CALLER = 2, ///< Message sender address for CALLER. - EVM_ORIGIN = 3, ///< Transaction origin address for ORIGIN. - EVM_GAS_PRICE = 4, ///< Transaction gas price for GASPRICE. - EVM_COINBASE = 5, ///< Current block miner address for COINBASE. - EVM_DIFFICULTY = 6, ///< Current block difficulty for DIFFICULTY. - EVM_GAS_LIMIT = 7, ///< Current block gas limit for GASLIMIT. - EVM_NUMBER = 8, ///< Current block number for NUMBER. - EVM_TIMESTAMP = 9, ///< Current block timestamp for TIMESTAMP. EVM_CODE_BY_ADDRESS = 10, ///< Code by an address for EXTCODECOPY. EVM_CODE_SIZE = 11, ///< Code size by an address for EXTCODESIZE. EVM_BALANCE = 12, ///< Balance of a given address for BALANCE. - EVM_BLOCKHASH = 13, ///< Block hash of by block number for BLOCKHASH. EVM_ACCOUNT_EXISTS = 14, ///< Check if an account exists. - EVM_CALL_DEPTH = 15, ///< Current call depth. }; -/// Opaque struct representing execution enviroment managed by the host -/// application. -struct evm_env; - /// Variant type to represent possible types of values used in EVM. /// /// Type-safety is lost around the code that uses this type. We should have @@ -194,75 +221,46 @@ union evm_variant { /// Query callback function. /// /// This callback function is used by the EVM to query the host application -/// about additional data required to execute EVM code. -/// @param env Pointer to execution environment managed by the host -/// application. -/// @param key The kind of the query. See evm_query_key and details below. -/// @param arg Additional argument to the query. It has defined value only for -/// the subset of query keys. +/// about additional information about accounts in the state required to +/// execute EVM code. +/// @param[out] result The result of the query. +/// @param env Pointer to execution environment managed by the host +/// application. +/// @param key The kind of the query. See evm_query_key +/// and details below. +/// @param address The address of the account the query is about. +/// @param storage_key Optional argument to provide storage key. Used +/// only in ::EVM_SLOAD queries. /// /// ## Types of queries /// -/// - ::EVM_GAS_PRICE -/// @param arg n/a -/// @result evm_variant::uint256be The transaction gas price. -/// -/// - ::EVM_ADDRESS -/// @param arg n/a -/// @result evm_variant::address The address of the current contract. -/// -/// - ::EVM_CALLER -/// @param arg n/a -/// @result evm_variant::address The address of the caller. -/// -/// - ::EVM_ORIGIN -/// @param arg n/a -/// @result evm_variant::address The address of the transaction initiator. -/// -/// - ::EVM_COINBASE -/// @param arg n/a -/// @result evm_variant::address The address of the beneficiary of the block fees. -/// -/// - ::EVM_DIFFICULTY -/// @param arg n/a -/// @result evm_variant::uint256be The block difficulty. -/// -/// - ::EVM_GAS_LIMIT -/// @param arg n/a -/// @result evm_variant::uint256be The block gas limit. -/// -/// - ::EVM_NUMBER -/// @param arg n/a -/// @result evm_variant::int64 The block number. -/// -/// - ::EVM_TIMESTAMP -/// @param arg n/a -/// @result evm_variant::int64 The block timestamp. +/// - ::EVM_SLOAD +/// @param storage_key The index of the storage entry. +/// @result evm_variant::uint256be The current value of the storage entry. /// /// - ::EVM_CODE_BY_ADDRESS -/// @param arg evm_variant::address The address to look up. -/// @result evm_variant::data The appropriate code for the given address or NULL if not found. +/// @result evm_variant::data The appropriate code for the given address or NULL if not found. /// /// - ::EVM_CODE_SIZE -/// @param arg evm_variant::address The address to look up. -/// @result evm_variant::data The appropriate code size for the given address or 0 if not found. +/// @result evm_variant::data The appropriate code size for the given address or 0 if not found. /// /// - ::EVM_BALANCE -/// @param arg evm_variant::address The address to look up. -/// @result evm_variant::data The appropriate balance for the given address or 0 if not found. +/// @result evm_variant::uint256be The appropriate balance for the given address or 0 if not found. /// -/// - ::EVM_BLOCKHASH -/// @param arg evm_variant::int64 The block number to look up. +/// - ::EVM_ACCOUNT_EXISTS /// @result evm_variant::uint256be The hash of the requested block or 0 if not found. /// -/// - ::EVM_SLOAD -/// @param arg evm_variant::uint256be The index of the storage entry. -/// @result evm_variant::uint256be The current value of the storage entry. /// -typedef void (*evm_query_fn)(union evm_variant* result, - struct evm_env* env, - enum evm_query_key key, - const union evm_variant* arg); +/// @todo +/// - Consider swapping key and address arguments, +/// e.g. `query(result, env, addr, EVM_SLOAD, k)`. +/// - Consider renaming key argument to something else. Key is confusing +/// especially for SSTORE and SLOAD. Maybe "kind"? +typedef void (*evm_query_state_fn)(union evm_variant* result, + struct evm_env* env, + enum evm_query_key key, + const struct evm_uint160be* address, + const struct evm_uint256be* storage_key); /// The update callback key. enum evm_update_key { @@ -298,18 +296,14 @@ enum evm_update_key { /// - ::EVM_SELFDESTRUCT /// @param arg1 evm_variant::address The beneficiary address. /// @param arg2 n/a -typedef void (*evm_update_fn)(struct evm_env* env, - enum evm_update_key key, - const union evm_variant* arg1, - const union evm_variant* arg2); - -/// The kind of call-like instruction. -enum evm_call_kind { - EVM_CALL = 0, ///< Request CALL. - EVM_DELEGATECALL = 1, ///< Request DELEGATECALL. The value param ignored. - EVM_CALLCODE = 2, ///< Request CALLCODE. - EVM_CREATE = 3 ///< Request CREATE. Semantic of some params changes. -}; +/// +/// @todo +/// - Move LOG to separated callback function. +typedef void (*evm_update_state_fn)(struct evm_env* env, + enum evm_update_key key, + const struct evm_uint160be* address, + const union evm_variant* arg1, + const union evm_variant* arg2); /// The flag indicating call failure in evm_call_fn() -- highest bit set. static const int64_t EVM_CALL_FAILURE = 0x8000000000000000; @@ -318,14 +312,7 @@ static const int64_t EVM_CALL_FAILURE = 0x8000000000000000; /// /// @param env Pointer to execution environment managed by the host /// application. -/// @param kind The kind of call-like opcode requested. -/// @param gas The amount of gas for the call. -/// @param address The address of a contract to be called. Ignored in case -/// of CREATE. -/// @param value The value sent to the callee. The endowment in case of -/// CREATE. -/// @param input The call input data or the CREATE init code. -/// @param input_size The size of the input data. +/// @param msg Call parameters. /// @param output The reference to the memory where the call output is to /// be copied. In case of CREATE, the memory is guaranteed /// to be at least 20 bytes to hold the address of the @@ -337,12 +324,7 @@ static const int64_t EVM_CALL_FAILURE = 0x8000000000000000; /// There is no need to set 0 address in the output in this case. typedef int64_t (*evm_call_fn)( struct evm_env* env, - enum evm_call_kind kind, - int64_t gas, - const struct evm_uint160be* address, - const struct evm_uint256be* value, - uint8_t const* input, - size_t input_size, + const struct evm_message* msg, uint8_t* output, size_t output_size); @@ -357,10 +339,14 @@ struct evm_instance; ///< Forward declaration. /// @param query_fn Pointer to query callback function. Nonnull. /// @param update_fn Pointer to update callback function. Nonnull. /// @param call_fn Pointer to call callback function. Nonnull. +/// @param get_tx_context_fn Pointer to get tx context function. Nonnull. +/// @param get_block_hash_fn Pointer to get block hash function. Nonnull. /// @return Pointer to the created EVM instance. -typedef struct evm_instance* (*evm_create_fn)(evm_query_fn query_fn, - evm_update_fn update_fn, - evm_call_fn call_fn); +typedef struct evm_instance* (*evm_create_fn)(evm_query_state_fn query_fn, + evm_update_state_fn update_fn, + evm_call_fn call_fn, + evm_get_tx_context_fn get_tx_context_fn, + evm_get_block_hash_fn get_block_hash_fn); /// Destroys the EVM instance. /// @@ -417,13 +403,9 @@ enum evm_mode { typedef struct evm_result (*evm_execute_fn)(struct evm_instance* instance, struct evm_env* env, enum evm_mode mode, - struct evm_uint256be code_hash, + const struct evm_message* msg, uint8_t const* code, - size_t code_size, - int64_t gas, - uint8_t const* input, - size_t input_size, - struct evm_uint256be value); + size_t code_size); /// Status of a code in VM. Useful for JIT-like implementations. diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index ba19f6cf..9f301c0f 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -620,36 +620,36 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti } case Instruction::ADDRESS: - stack.push(_ext.query(EVM_ADDRESS)); + stack.push(Endianness::toNative(m_builder, _runtimeManager.getAddress())); break; case Instruction::CALLER: - stack.push(_ext.query(EVM_CALLER)); + stack.push(Endianness::toNative(m_builder, _runtimeManager.getSender())); break; case Instruction::ORIGIN: - stack.push(_ext.query(EVM_ORIGIN)); + stack.push(m_builder.CreateZExt(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(1)), Type::Word)); break; case Instruction::COINBASE: - stack.push(_ext.query(EVM_COINBASE)); + stack.push(m_builder.CreateZExt(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(2)), Type::Word)); break; case Instruction::GASPRICE: - stack.push(_ext.query(EVM_GAS_PRICE)); + stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(0))); break; case Instruction::DIFFICULTY: - stack.push(_ext.query(EVM_DIFFICULTY)); + stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(6))); break; case Instruction::GASLIMIT: - stack.push(_ext.query(EVM_GAS_LIMIT)); + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(5), Type::Word)); break; case Instruction::NUMBER: - stack.push(_ext.query(EVM_NUMBER)); + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(3), Type::Word)); break; case Instruction::TIMESTAMP: - stack.push(_ext.query(EVM_TIMESTAMP)); + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(4), Type::Word)); break; case Instruction::CALLVALUE: @@ -889,7 +889,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto noPenaltyCond = destExists; if (m_mode >= EVM_CLEARING) { - auto addr = _ext.query(EVM_ADDRESS); + auto addr = Endianness::toNative(m_builder, _runtimeManager.getAddress()); auto balance = _ext.balance(addr); auto noTransfer = m_builder.CreateICmpEQ(balance, Constant::get(0)); diff --git a/libevmjit/Ext.cpp b/libevmjit/Ext.cpp index b4791bdd..66ea6ced 100644 --- a/libevmjit/Ext.cpp +++ b/libevmjit/Ext.cpp @@ -67,13 +67,18 @@ llvm::Function* getQueryFunc(llvm::Module* _module) { // TODO: Mark the function as pure to eliminate multiple calls. auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); - auto fty = llvm::FunctionType::get(Type::Void, {Type::WordPtr, Type::EnvPtr, i32, Type::WordPtr}, false); + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = llvm::FunctionType::get( + Type::Void, {Type::WordPtr, Type::EnvPtr, i32, addrTy->getPointerTo(), Type::WordPtr}, false); func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); func->addAttribute(1, llvm::Attribute::NoAlias); func->addAttribute(1, llvm::Attribute::NoCapture); func->addAttribute(4, llvm::Attribute::ReadOnly); func->addAttribute(4, llvm::Attribute::NoAlias); func->addAttribute(4, llvm::Attribute::NoCapture); + func->addAttribute(5, llvm::Attribute::ReadOnly); + func->addAttribute(5, llvm::Attribute::NoAlias); + func->addAttribute(5, llvm::Attribute::NoCapture); } return func; } @@ -84,8 +89,9 @@ llvm::Function* getUpdateFunc(llvm::Module* _module) auto func = _module->getFunction(funcName); if (!func) { - auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); - auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, i32, Type::WordPtr, Type::WordPtr}, false); + auto i32 = llvm::Type::getInt32Ty(_module->getContext()); + auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, i32, addrPtrTy, Type::WordPtr, Type::WordPtr}, false); func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); func->addAttribute(3, llvm::Attribute::ReadOnly); func->addAttribute(3, llvm::Attribute::NoAlias); @@ -113,10 +119,11 @@ llvm::Function* getCallFunc(llvm::Module* _module) if (!func) { auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); - auto hash160Ty = llvm::IntegerType::getIntNTy(_module->getContext(), 160); + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto addrPtrTy = addrTy->getPointerTo(); auto fty = llvm::FunctionType::get( Type::Gas, - {Type::EnvPtr, i32, Type::Gas, hash160Ty->getPointerTo(), Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size}, + {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size}, false); func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, "evm.call", _module); func->addAttribute(4, llvm::Attribute::ReadOnly); @@ -131,6 +138,11 @@ llvm::Function* getCallFunc(llvm::Module* _module) auto callFunc = func; // Create a call wrapper to handle additional checks. + fty = llvm::FunctionType::get( + Type::Gas, + {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, addrTy, Type::Size}, + false + ); func = llvm::Function::Create(fty, llvm::Function::PrivateLinkage, funcName, _module); func->addAttribute(4, llvm::Attribute::ReadOnly); func->addAttribute(4, llvm::Attribute::NoAlias); @@ -150,6 +162,11 @@ llvm::Function* getCallFunc(llvm::Module* _module) auto& gas = *iter; std::advance(iter, 2); auto& valuePtr = *iter; + std::advance(iter, 5); + auto& addr = *iter; + std::advance(iter, 1); + auto& depth = *iter; + auto& ctx = _module->getContext(); llvm::IRBuilder<> builder(ctx); @@ -161,13 +178,9 @@ llvm::Function* getCallFunc(llvm::Module* _module) builder.SetInsertPoint(entryBB); auto v = builder.CreateAlloca(Type::Word); - auto addr = builder.CreateAlloca(Type::Word); + auto addrAlloca = builder.CreateBitCast(builder.CreateAlloca(Type::Word), addrPtrTy); auto queryFn = getQueryFunc(_module); - auto undef = llvm::UndefValue::get(Type::WordPtr); - builder.CreateCall(queryFn, {v, &env, builder.getInt32(EVM_CALL_DEPTH), undef}); - auto depthPtr = builder.CreateBitCast(v, builder.getInt64Ty()->getPointerTo()); - auto depth = builder.CreateLoad(depthPtr); - auto depthOk = builder.CreateICmpSLT(depth, builder.getInt64(1024)); + auto depthOk = builder.CreateICmpSLT(&depth, builder.getInt64(1024)); builder.CreateCondBr(depthOk, checkTransferBB, failBB); builder.SetInsertPoint(checkTransferBB); @@ -178,8 +191,9 @@ llvm::Function* getCallFunc(llvm::Module* _module) builder.CreateCondBr(transfer, checkBalanceBB, callBB); builder.SetInsertPoint(checkBalanceBB); - builder.CreateCall(queryFn, {addr, &env, builder.getInt32(EVM_ADDRESS), undef}); - builder.CreateCall(queryFn, {v, &env, builder.getInt32(EVM_BALANCE), addr}); + builder.CreateStore(&addr, addrAlloca); + auto null = llvm::ConstantPointerNull::get(Type::WordPtr); + builder.CreateCall(queryFn, {v, &env, builder.getInt32(EVM_BALANCE), addrAlloca, null}); llvm::Value* balance = builder.CreateLoad(v); balance = Endianness::toNative(builder, balance); value = Endianness::toNative(builder, value); @@ -187,9 +201,10 @@ llvm::Function* getCallFunc(llvm::Module* _module) builder.CreateCondBr(balanceOk, callBB, failBB); builder.SetInsertPoint(callBB); + // Pass the first 9 args to the external call. llvm::Value* args[9]; - auto outIt = std::begin(args); - for (auto it = func->arg_begin(); it != func->arg_end(); ++it, ++outIt) + auto it = func->arg_begin(); + for (auto outIt = std::begin(args); outIt != std::end(args); ++it, ++outIt) *outIt = &*it; auto ret = builder.CreateCall(callFunc, args); builder.CreateRet(ret); @@ -201,6 +216,22 @@ llvm::Function* getCallFunc(llvm::Module* _module) return func; } +llvm::Function* getBlockHashFunc(llvm::Module* _module) +{ + static const auto funcName = "evm.blockhash"; + auto func = _module->getFunction(funcName); + if (!func) + { + // TODO: Mark the function as pure to eliminate multiple calls. + auto i64 = llvm::IntegerType::getInt64Ty(_module->getContext()); + auto fty = llvm::FunctionType::get(Type::Void, {Type::WordPtr, Type::EnvPtr, i64}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(1, llvm::Attribute::NoAlias); + func->addAttribute(1, llvm::Attribute::NoCapture); + } + return func; +} + } @@ -221,13 +252,6 @@ llvm::Value* Ext::getArgAlloca() return a; } -llvm::Value* Ext::byPtr(llvm::Value* _value) -{ - auto a = getArgAlloca(); - m_builder.CreateStore(_value, a); - return a; -} - llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) { auto& func = m_funcs[static_cast(_funcId)]; @@ -265,26 +289,34 @@ llvm::Value* Ext::createCABICall(llvm::Function* _func, std::initializer_listgetPointerTo()); + m_builder.CreateStore(myAddr, pAddr); auto func = getQueryFunc(getModule()); auto pValue = getArgAlloca(); - createCABICall(func, {pValue, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SLOAD), index}); + createCABICall(func, {pValue, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SLOAD), pAddr, index}); return Endianness::toNative(m_builder, m_builder.CreateLoad(pValue)); } void Ext::sstore(llvm::Value* _index, llvm::Value* _value) { + auto addrTy = m_builder.getIntNTy(160); auto index = Endianness::toBE(m_builder, _index); auto value = Endianness::toBE(m_builder, _value); + auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); auto func = getUpdateFunc(getModule()); - createCABICall(func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SSTORE), index, value}); + createCABICall(func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SSTORE), myAddr, index, value}); } void Ext::selfdestruct(llvm::Value* _beneficiary) { + auto addrTy = m_builder.getIntNTy(160); auto func = getUpdateFunc(getModule()); auto b = Endianness::toBE(m_builder, _beneficiary); auto undef = llvm::UndefValue::get(Type::WordPtr); - createCABICall(func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SELFDESTRUCT), b, undef}); + auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + createCABICall(func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_SELFDESTRUCT), myAddr, b, undef}); } llvm::Value* Ext::calldataload(llvm::Value* _idx) @@ -310,73 +342,38 @@ llvm::Value* Ext::calldataload(llvm::Value* _idx) return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); } -llvm::Value* Ext::query(evm_query_key _key) -{ - auto func = getQueryFunc(getModule()); - auto undef = llvm::UndefValue::get(Type::WordPtr); - auto pResult = getArgAlloca(); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(_key), undef}); - llvm::Value* v = m_builder.CreateLoad(pResult); - - switch (_key) - { - case EVM_GAS_PRICE: - case EVM_DIFFICULTY: - v = Endianness::toNative(m_builder, v); - break; - case EVM_ADDRESS: - case EVM_CALLER: - case EVM_ORIGIN: - case EVM_COINBASE: - { - auto mask160 = llvm::APInt(160, -1, true).zext(256); - v = Endianness::toNative(m_builder, v); - v = m_builder.CreateAnd(v, mask160); - break; - } - case EVM_GAS_LIMIT: - case EVM_NUMBER: - case EVM_TIMESTAMP: - { - // Use only 64-bit -- single word. The rest is uninitialized. - // We could have mask 63 bits, but there is very little to gain in cost - // of additional and operation. - auto mask64 = llvm::APInt(256, std::numeric_limits::max()); - v = m_builder.CreateAnd(v, mask64); - break; - } - default: - break; - } - - return v; -} - llvm::Value* Ext::balance(llvm::Value* _address) { auto func = getQueryFunc(getModule()); - auto address = Endianness::toBE(m_builder, _address); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); auto pResult = getArgAlloca(); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_BALANCE), address}); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto null = llvm::ConstantPointerNull::get(Type::WordPtr); + createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_BALANCE), pAddr, null}); return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); } llvm::Value* Ext::exists(llvm::Value* _address) { auto func = getQueryFunc(getModule()); - auto address = Endianness::toBE(m_builder, _address); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); auto pResult = getArgAlloca(); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_ACCOUNT_EXISTS), address}); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto null = llvm::ConstantPointerNull::get(Type::WordPtr); + createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_ACCOUNT_EXISTS), pAddr, null}); return m_builder.CreateTrunc(m_builder.CreateLoad(pResult), m_builder.getInt1Ty()); } llvm::Value* Ext::blockHash(llvm::Value* _number) { - auto func = getQueryFunc(getModule()); - // TODO: We can explicitly trunc the number to i64. The optimizer will know - // that we care only about these 64 bit, not all 256. + auto func = getBlockHashFunc(getModule()); + auto number = m_builder.CreateTrunc(_number, m_builder.getInt64Ty()); auto pResult = getArgAlloca(); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_BLOCKHASH), _number}); + createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), number}); return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); } @@ -390,13 +387,16 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) return Endianness::toNative(m_builder, hash); } -MemoryRef Ext::extcode(llvm::Value* _addr) +MemoryRef Ext::extcode(llvm::Value* _address) { auto func = getQueryFunc(getModule()); - // TODO: We care only about 20 bytes here. Can we do it better? - auto address = Endianness::toBE(m_builder, _addr); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto null = llvm::ConstantPointerNull::get(Type::WordPtr); auto vPtr = getArgAlloca(); - createCABICall(func, {vPtr, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_CODE_BY_ADDRESS), address}); + createCABICall(func, {vPtr, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_CODE_BY_ADDRESS), pAddr, null}); auto memRefTy = getMemRefTy(getModule()); auto memRefPtr = m_builder.CreateBitCast(vPtr, memRefTy->getPointerTo()); auto memRef = m_builder.CreateLoad(memRefTy, memRefPtr, "memref"); @@ -406,13 +406,16 @@ MemoryRef Ext::extcode(llvm::Value* _addr) return {code, size256}; } -llvm::Value* Ext::extcodesize(llvm::Value* _addr) +llvm::Value* Ext::extcodesize(llvm::Value* _address) { auto func = getQueryFunc(getModule()); - // TODO: We care only about 20 bytes here. Can we do it better? - auto address = Endianness::toBE(m_builder, _addr); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto null = llvm::ConstantPointerNull::get(Type::WordPtr); auto vPtr = getArgAlloca(); - createCABICall(func, {vPtr, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_CODE_SIZE), address}); + createCABICall(func, {vPtr, getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_CODE_SIZE), pAddr, null}); auto int64ty = m_builder.getInt64Ty(); auto sizePtr = m_builder.CreateBitCast(vPtr, int64ty->getPointerTo()); auto size = m_builder.CreateLoad(int64ty, sizePtr, "codesize"); @@ -439,6 +442,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef Ext::create(llvm::Value* _gas, @@ -498,10 +506,11 @@ std::tuple Ext::create(llvm::Value* _gas, m_builder.CreateBitCast(getArgAlloca(), m_builder.getInt8PtrTy()); auto func = getCallFunc(getModule()); + auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); auto ret = createCABICall( func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVM_CREATE), _gas, llvm::UndefValue::get(addrTy), value, inData, inSize, pAddr, - m_builder.getInt64(20)}); + m_builder.getInt64(20), myAddr, getRuntimeManager().getDepth()}); pAddr = m_builder.CreateBitCast(pAddr, addrTy->getPointerTo()); return std::tuple{ret, pAddr}; diff --git a/libevmjit/Ext.h b/libevmjit/Ext.h index d9605133..bbf3b15a 100644 --- a/libevmjit/Ext.h +++ b/libevmjit/Ext.h @@ -48,7 +48,6 @@ class Ext : public RuntimeHelper llvm::Value* sload(llvm::Value* _index); void sstore(llvm::Value* _index, llvm::Value* _value); - llvm::Value* query(evm_query_key _key); llvm::Value* balance(llvm::Value* _address); llvm::Value* exists(llvm::Value* _address); llvm::Value* calldataload(llvm::Value* _index); @@ -86,7 +85,6 @@ class Ext : public RuntimeHelper llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); llvm::Value* getArgAlloca(); - llvm::Value* byPtr(llvm::Value* _value); llvm::Value* createCABICall(llvm::Function* _func, std::initializer_list const& _args); diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 414f049a..e865c883 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -1,5 +1,6 @@ #include "JIT.h" +#include #include #include "preprocessor/llvm_includes_start.h" @@ -9,9 +10,6 @@ #include #include #include -#include -#include -#include #include "preprocessor/llvm_includes_end.h" #include "Compiler.h" @@ -25,6 +23,8 @@ static_assert(sizeof(evm_uint256be) == 32, "evm_uint256be is too big"); static_assert(sizeof(evm_uint160be) == 20, "evm_uint160be is too big"); static_assert(sizeof(evm_result) <= 64, "evm_result does not fit cache line"); +static_assert(sizeof(evm_message) <= 17*8, "evm_message not optimally packed"); +static_assert(offsetof(evm_message, code_hash) % 8 == 0, "evm_message.code_hash not aligned"); // Check enums match int size. // On GCC/clang the underlying type should be unsigned int, on MSVC int @@ -138,11 +138,41 @@ class JITImpl: public evm_instance ExecFunc compile(evm_mode _mode, byte const* _code, uint64_t _codeSize, std::string const& _codeIdentifier); - evm_query_fn queryFn = nullptr; - evm_update_fn updateFn = nullptr; + evm_query_state_fn queryFn = nullptr; + evm_update_state_fn updateFn = nullptr; evm_call_fn callFn = nullptr; + evm_get_tx_context_fn getTxContextFn = nullptr; + evm_get_block_hash_fn getBlockHashFn = nullptr; + + evm_message const* currentMsg = nullptr; }; +static int64_t call_v2( + evm_env* _opaqueEnv, + evm_call_kind _kind, + int64_t _gas, + evm_uint160be const* _address, + evm_uint256be const* _value, + uint8_t const* _inputData, + size_t _inputSize, + uint8_t* _outputData, + size_t _outputSize +) noexcept +{ + auto& jit = JITImpl::instance(); + evm_message msg; + msg.kind = _kind; + msg.address = *_address; + msg.sender = _kind != EVM_DELEGATECALL ? jit.currentMsg->address : jit.currentMsg->sender; + msg.value = _kind != EVM_DELEGATECALL ? *_value : jit.currentMsg->value; + msg.input = _inputData; + msg.input_size = _inputSize; + msg.gas = _gas; + msg.depth = jit.currentMsg->depth + 1; + // FIXME: Handle code hash. + return jit.callFn(_opaqueEnv, &msg, _outputData, _outputSize); +} + class SymbolResolver : public llvm::SectionMemoryManager { @@ -153,7 +183,9 @@ class SymbolResolver : public llvm::SectionMemoryManager .Case("env_sha3", reinterpret_cast(&keccak)) .Case("evm.query", reinterpret_cast(jit.queryFn)) .Case("evm.update", reinterpret_cast(jit.updateFn)) - .Case("evm.call", reinterpret_cast(jit.callFn)) + .Case("evm.call", reinterpret_cast(call_v2)) + .Case("evm.get_tx_context", reinterpret_cast(jit.getTxContextFn)) + .Case("evm.blockhash", reinterpret_cast(jit.getBlockHashFn)) .Default(0); if (addr) return {addr, llvm::JITSymbolFlags::Exported}; @@ -272,8 +304,9 @@ bytes_ref ExecutionContext::getReturnData() const extern "C" { -static evm_instance* create(evm_query_fn queryFn, evm_update_fn updateFn, - evm_call_fn callFn) +static evm_instance* create(evm_query_state_fn queryFn, evm_update_state_fn updateFn, + evm_call_fn callFn, evm_get_tx_context_fn getTxContextFn, + evm_get_block_hash_fn getBlockHashFn) { // Let's always return the same instance. It's a bit of faking, but actually // this might be a compliant implementation. @@ -281,6 +314,8 @@ static evm_instance* create(evm_query_fn queryFn, evm_update_fn updateFn, jit.queryFn = queryFn; jit.updateFn = updateFn; jit.callFn = callFn; + jit.getTxContextFn = getTxContextFn; + jit.getBlockHashFn = getBlockHashFn; return &jit; } @@ -299,18 +334,26 @@ static void releaseResult(evm_result const* result) } static evm_result execute(evm_instance* instance, evm_env* env, evm_mode mode, - evm_uint256be code_hash, uint8_t const* code, size_t code_size, - int64_t gas, uint8_t const* input, size_t input_size, evm_uint256be value) + evm_message const* msg, uint8_t const* code, size_t code_size) { auto& jit = *reinterpret_cast(instance); + // TODO: Temporary keep track of the current message. + evm_message const* prevMsg = jit.currentMsg; + jit.currentMsg = msg; + RuntimeData rt; rt.code = code; rt.codeSize = code_size; - rt.gas = gas; - rt.callData = input; - rt.callDataSize = input_size; - std::memcpy(&rt.apparentValue, &value, sizeof(value)); + rt.gas = msg->gas; + rt.callData = msg->input; + rt.callDataSize = msg->input_size; + std::memcpy(&rt.apparentValue, &msg->value, sizeof(msg->value)); + std::memset(&rt.address, 0, 12); + std::memcpy(&rt.address[12], &msg->address, sizeof(msg->address)); + std::memset(&rt.caller, 0, 12); + std::memcpy(&rt.caller[12], &msg->sender, sizeof(msg->sender)); + rt.depth = msg->depth; ExecutionContext ctx{rt, env}; @@ -322,7 +365,7 @@ static evm_result execute(evm_instance* instance, evm_env* env, evm_mode mode, result.context = nullptr; result.release = releaseResult; - auto codeIdentifier = makeCodeId(code_hash, mode); + auto codeIdentifier = makeCodeId(msg->code_hash, mode); auto execFunc = jit.getExecFunc(codeIdentifier); if (!execFunc) { @@ -362,6 +405,7 @@ static evm_result execute(evm_instance* instance, evm_env* env, evm_mode mode, result.context = ctx.m_memData; ctx.m_memData = nullptr; + jit.currentMsg = prevMsg; return result; } diff --git a/libevmjit/JIT.h b/libevmjit/JIT.h index 9f030f4f..50a38e63 100644 --- a/libevmjit/JIT.h +++ b/libevmjit/JIT.h @@ -32,15 +32,18 @@ struct RuntimeData GasPrice, CallData, CallDataSize, - ApparentCallValue, // value of msg.value - different during DELEGATECALL + Value, // Value of msg.value - different during DELEGATECALL. Code, CodeSize, + Address, + Sender, + Depth, 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; + static size_t const numElements = Depth + 1; int64_t gas = 0; int64_t gasPrice = 0; @@ -49,6 +52,9 @@ struct RuntimeData i256 apparentValue; byte const* code = nullptr; uint64_t codeSize = 0; + byte address[32]; + byte caller[32]; + int64_t depth; }; struct JITSchedule diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 1d34df3d..b3d67738 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -29,6 +29,9 @@ llvm::StructType* RuntimeManager::getRuntimeDataType() Type::Word, // apparentValue Type::BytePtr, // code Type::Size, // codeSize + Type::Word, // adddress + Type::Word, // caller + Type::Size, // depth }; type = llvm::StructType::create(elems, "RuntimeData"); } @@ -51,6 +54,34 @@ llvm::StructType* RuntimeManager::getRuntimeType() return type; } +llvm::StructType* RuntimeManager::getTxContextType() +{ + auto name = "evm.txctx"; + auto type = getModule()->getTypeByName(name); + if (type) + return type; + + auto& ctx = getModule()->getContext(); + auto i256 = llvm::IntegerType::get(ctx, 256); + auto h160 = llvm::ArrayType::get(llvm::IntegerType::get(ctx, 8), 20); + auto i64 = llvm::IntegerType::get(ctx, 64); + return llvm::StructType::create({i256, h160, h160, i64, i64, i64, i256}, name); +} + +llvm::Value* RuntimeManager::getTxContextItem(unsigned _index) +{ + auto call = m_builder.CreateCall(m_loadTxCtxFn, {m_txCtxLoaded, m_txCtx, m_envPtr}); + call->setCallingConv(llvm::CallingConv::Fast); + auto ptr = m_builder.CreateStructGEP(getTxContextType(), m_txCtx, _index); + if (_index == 1 || _index == 2) + { + // In struct addresses are represented as char[20] to fix alignment + // issues (i160 has alignment of 8). Here we convert them back to i160. + ptr = m_builder.CreateBitCast(ptr, m_builder.getIntNTy(160)->getPointerTo()); + } + return m_builder.CreateLoad(ptr); +} + namespace { llvm::Twine getName(RuntimeData::Index _index) @@ -62,9 +93,12 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::GasPrice: return "tx.gasprice"; case RuntimeData::CallData: return "msg.data.ptr"; case RuntimeData::CallDataSize: return "msg.data.size"; - case RuntimeData::ApparentCallValue: return "msg.value"; + case RuntimeData::Value: return "msg.value"; case RuntimeData::Code: return "code.ptr"; case RuntimeData::CodeSize: return "code.size"; + case RuntimeData::Address: return "msg.address"; + case RuntimeData::Sender: return "msg.sender"; + case RuntimeData::Depth: return "msg.depth"; } } } @@ -74,6 +108,37 @@ RuntimeManager::RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, co m_codeBegin(_codeBegin), m_codeEnd(_codeEnd) { + m_txCtxLoaded = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, "txctx.loaded"); + m_builder.CreateStore(m_builder.getInt1(false), m_txCtxLoaded); + m_txCtx = m_builder.CreateAlloca(getTxContextType(), nullptr, "txctx"); + + auto getTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtx->getType(), Type::EnvPtr}, false); + auto getTxCtxFn = llvm::Function::Create(getTxCtxFnTy, llvm::Function::ExternalLinkage, "evm.get_tx_context", getModule()); + auto loadTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtxLoaded->getType(), m_txCtx->getType(), Type::EnvPtr}, false); + m_loadTxCtxFn = llvm::Function::Create(loadTxCtxFnTy, llvm::Function::PrivateLinkage, "loadTxCtx", getModule()); + m_loadTxCtxFn->setCallingConv(llvm::CallingConv::Fast); + auto checkBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Check", m_loadTxCtxFn); + auto loadBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Load", m_loadTxCtxFn); + auto exitBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Exit", m_loadTxCtxFn); + + auto iter = m_loadTxCtxFn->arg_begin(); + llvm::Argument* flag = &(*iter++); + flag->setName("flag"); + llvm::Argument* txCtx = &(*iter++); + txCtx->setName("txctx"); + llvm::Argument* env = &(*iter); + env->setName("env"); + + auto b = IRBuilder{checkBB}; + auto f = b.CreateLoad(flag); + b.CreateCondBr(f, exitBB, loadBB); + b.SetInsertPoint(loadBB); + b.CreateStore(b.getInt1(true), flag); + b.CreateCall(getTxCtxFn, {txCtx, env}); + b.CreateBr(exitBB); + b.SetInsertPoint(exitBB); + b.CreateRetVoid(); + // Unpack data auto rtPtr = getRuntimePtr(); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); @@ -148,9 +213,24 @@ llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) return ptr; } +llvm::Value* RuntimeManager::getAddress() +{ + return m_dataElts[RuntimeData::Address]; +} + +llvm::Value* RuntimeManager::getSender() +{ + return m_dataElts[RuntimeData::Sender]; +} + llvm::Value* RuntimeManager::getValue() { - return m_dataElts[RuntimeData::ApparentCallValue]; + return m_dataElts[RuntimeData::Value]; +} + +llvm::Value* RuntimeManager::getDepth() +{ + return m_dataElts[RuntimeData::Depth]; } void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 84ee0b96..96174256 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -23,6 +23,8 @@ class RuntimeManager: public CompilerHelper llvm::Value* getDataPtr(); llvm::Value* getEnvPtr(); + llvm::Value* getAddress(); + llvm::Value* getSender(); llvm::Value* getValue(); llvm::Value* getGas(); llvm::Value* getGasPtr(); @@ -30,6 +32,7 @@ class RuntimeManager: public CompilerHelper llvm::Value* getCode(); llvm::Value* getCodeSize(); llvm::Value* getCallDataSize(); + llvm::Value* getDepth(); llvm::Value* getJmpBuf() { return m_jmpBuf; } void setGas(llvm::Value* _gas); @@ -49,6 +52,9 @@ class RuntimeManager: public CompilerHelper static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeDataType(); + llvm::StructType* getTxContextType(); + + llvm::Value* getTxContextItem(unsigned _index); //TODO Move to schedule static const size_t stackSizeLimit = 1024; @@ -63,6 +69,10 @@ class RuntimeManager: public CompilerHelper llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + llvm::Value* m_txCtxLoaded = nullptr; + llvm::Value* m_txCtx = nullptr; + llvm::Function* m_loadTxCtxFn = nullptr; + std::array m_dataElts; llvm::Value* m_stackBase = nullptr; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 45588308..3be4de78 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,7 +11,7 @@ if (MSVC) elseif (APPLE) set(SYSTEM_LIBS stdc++ pthread) else() - set(SYSTEM_LIBS stdc++ pthread dl m) + set(SYSTEM_LIBS stdc++ pthread dl m z) endif() target_link_libraries(test-evmjit-standalone PRIVATE ${EVMJIT_STANDALONE_LIB} ${SYSTEM_LIBS}) add_dependencies(test-evmjit-standalone evmjit-standalone)