Program Listing for File alu_operations.cuh#

Return to documentation for file (src/alu_operations.cuh)

// cuEVM: CUDA Ethereum Virtual Machine implementation
// Copyright 2023 Stefan-Dan Ciocirlan (SBIP - Singapore Blockchain Innovation Programme)
// Author: Stefan-Dan Ciocirlan
// Data: 2023-11-30
// SPDX-License-Identifier: MIT

#ifndef _ALU_H_
#define _ALU_H_

#include "utils.h"
#include "stack.cuh"

template <class params>
class arithmetic_operations
{
public:
    typedef arith_env_t<params> arith_t;
    typedef typename arith_t::bn_t bn_t;
    typedef typename arith_t::bn_wide_t bn_wide_t;
    typedef stack_t<params> stack_t;

    __host__ __device__ __forceinline__ static void operation_ADD(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            cgbn_add(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_MUL(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            cgbn_mul(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SUB(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            cgbn_sub(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_DIV(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            // division by zero no error
            if (cgbn_compare_ui32(arith._env, b, 0) == 0)
                cgbn_set_ui32(arith._env, r, 0);
            else
                cgbn_div(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SDIV(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            bn_t d;
            bn_t e;
            // d = -1
            cgbn_set_ui32(arith._env, d, 0);
            cgbn_sub_ui32(arith._env, d, d, 1);
            // e = -2^254
            cgbn_set_ui32(arith._env, e, 1);
            cgbn_shift_left(arith._env, e, e, arith_t::BITS - 1);
            uint32_t sign_a = cgbn_extract_bits_ui32(arith._env, a, arith_t::BITS - 1, 1);
            uint32_t sign_b = cgbn_extract_bits_ui32(arith._env, b, arith_t::BITS - 1, 1);
            uint32_t sign = sign_a ^ sign_b;
            // division by zero no error
            if (cgbn_compare_ui32(arith._env, b, 0) == 0)
                cgbn_set_ui32(arith._env, r, 0);
            else if ( // -2^254 / -1 = -2^254
                (cgbn_compare(arith._env, b, d) == 0) &&
                (cgbn_compare(arith._env, a, e) == 0))
            {
                cgbn_set(arith._env, r, e);
            }
            else
            {
                // div between absolute values
                if (sign_a == 1)
                {
                    cgbn_negate(arith._env, a, a);
                }
                if (sign_b == 1)
                {
                    cgbn_negate(arith._env, b, b);
                }
                cgbn_div(arith._env, r, a, b);
                if (sign)
                {
                    cgbn_negate(arith._env, r, r);
                }
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_MOD(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            // // rem by zero no error
            if (cgbn_compare_ui32(arith._env, b, 0) == 0)
                cgbn_set_ui32(arith._env, r, 0);
            else
                cgbn_rem(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SMOD(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);

            uint32_t sign_a = cgbn_extract_bits_ui32(arith._env, a, arith_t::BITS - 1, 1);
            uint32_t sign_b = cgbn_extract_bits_ui32(arith._env, b, arith_t::BITS - 1, 1);
            uint32_t sign = sign_a ^ sign_b;
            if (cgbn_compare_ui32(arith._env, b, 0) == 0)
                cgbn_set_ui32(arith._env, r, 0);
            else
            {
                // mod between absolute values
                if (sign_a == 1)
                {
                    cgbn_negate(arith._env, a, a);
                }
                if (sign_b == 1)
                {
                    cgbn_negate(arith._env, b, b);
                }
                cgbn_rem(arith._env, r, a, b);
                if (sign)
                {
                    cgbn_negate(arith._env, r, r);
                }
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_ADDMOD(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_MID);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, c, N, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);
            stack.pop(N, error_code);

            if (cgbn_compare_ui32(arith._env, N, 0) == 0)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else if (cgbn_compare_ui32(arith._env, N, 1) == 0)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else
            {
                int32_t carry = cgbn_add(arith._env, c, a, b);
                bn_wide_t d;
                if (carry == 1)
                {
                    cgbn_set_ui32(arith._env, d._high, 1);
                    cgbn_set(arith._env, d._low, c);
                    cgbn_rem_wide(arith._env, r, d, N);
                }
                else
                {
                    cgbn_rem(arith._env, r, c, N);
                }
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_MULMOD(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_MID);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b, c, N, r;
            stack.pop(a, error_code);
            stack.pop(b, error_code);
            stack.pop(N, error_code);

            if (cgbn_compare_ui32(arith._env, N, 0) == 0)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else
            {
                bn_wide_t d;
                cgbn_rem(arith._env, a, a, N);
                cgbn_rem(arith._env, b, b, N);
                cgbn_mul_wide(arith._env, d, a, b);
                cgbn_rem_wide(arith._env, r, d, N);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_EXP(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_EXP);
        bn_t a, exponent, r;
        stack.pop(a, error_code);
        stack.pop(exponent, error_code);

        int32_t last_bit;
        last_bit = arith_t::BITS - 1 - cgbn_clz(arith._env, exponent);
        uint32_t exponent_byte_size;
        if (last_bit == -1)
        {
            exponent_byte_size = 0;
        }
        else
        {
            exponent_byte_size = (last_bit) / 8 + 1;
        }
        bn_t dynamic_gas;
        cgbn_set_ui32(arith._env, dynamic_gas, exponent_byte_size);
        cgbn_mul_ui32(arith._env, dynamic_gas, dynamic_gas, GAS_EXP_BYTE);
        cgbn_add(arith._env, gas_used, gas_used, dynamic_gas);
        if (error_code == ERR_NONE)
        {
            if (arith.has_gas(gas_limit, gas_used, error_code))
            {
                //^0=1 even for 0^0
                if (last_bit == -1)
                {
                    cgbn_set_ui32(arith._env, r, 1);
                }
                else
                {
                    bn_t current, square;
                    cgbn_set_ui32(arith._env, current, 1); // r=1
                    cgbn_set(arith._env, square, a);       // square=a
                    for (int32_t bit = 0; bit <= last_bit; bit++)
                    {
                        if (cgbn_extract_bits_ui32(arith._env, exponent, bit, 1) == 1)
                        {
                            cgbn_mul(arith._env, current, current, square); // r=r*square
                        }
                        cgbn_mul(arith._env, square, square, square); // square=square*square
                    }
                    cgbn_set(arith._env, r, current);
                }

                stack.push(r, error_code);

                pc = pc + 1;
            }
        }
    }

    __host__ __device__ __forceinline__ static void operation_SIGNEXTEND(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        /*
        Even if x has more bytes than the value b, the operation consider only the first
        (b+1) bytes of x and the other are considered zero and they don't have any influence
        on the final result.
        Optimised: use cgbn_bitwise_mask_ior instead of cgbn_insert_bits_ui32
        */
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t b, x, r;
            stack.pop(b, error_code);
            stack.pop(x, error_code);

            if (cgbn_compare_ui32(arith._env, b, (arith_t::BYTES - 1) ) == 1)
            {
                cgbn_set(arith._env, r, x);
            }
            else
            {
                uint32_t c = cgbn_get_ui32(arith._env, b) + 1;
                uint32_t sign = cgbn_extract_bits_ui32(arith._env, x, c * 8 - 1, 1);
                int32_t numbits = int32_t(c);
                if (sign == 1)
                {
                    numbits = int32_t(arith_t::BITS) - 8 * numbits;
                    numbits = -numbits;
                    cgbn_bitwise_mask_ior(arith._env, r, x, numbits);
                }
                else
                {
                    cgbn_bitwise_mask_and(arith._env, r, x, 8 * numbits);
                }
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }
};

template <class params>
class stack_operations
{
public:
    typedef arith_env_t<params> arith_t;
    typedef typename arith_t::bn_t bn_t;
    typedef stack_t<params> stack_t;

    __host__ __device__ __forceinline__ static void operation_POP(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);

        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t y;

            stack.pop(y, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_PUSH0(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t r;
            cgbn_set_ui32(arith._env, r, 0);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_PUSHX(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack,
        uint8_t *bytecode,
        uint32_t &code_size,
        uint8_t &opcode)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            uint8_t push_size = (opcode & 0x1F) + 1;
            uint8_t *byte_data = &(bytecode[pc + 1]);
            uint8_t available_size = push_size;

            // if pushx is outside code size
            if ((pc + push_size) >= code_size)
            {
                available_size = code_size - pc - 1;
            }
            stack.pushx(push_size, error_code, byte_data, available_size);

            pc = pc + push_size + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_DUPX(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack,
        uint8_t &opcode)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            uint8_t dup_index = (opcode & 0x0F) + 1;

            stack.dupx(dup_index, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SWAPX(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack,
        uint8_t &opcode)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            uint8_t swap_index = (opcode & 0x0F) + 1;

            stack.swapx(swap_index, error_code);

            pc = pc + 1;
        }
    }
};

template <class params>
class comparison_operations
{
public:
    typedef arith_env_t<params> arith_t;
    typedef typename arith_t::bn_t bn_t;
    typedef stack_t<params> stack_t;

    __host__ __device__ __forceinline__ static int32_t compare(
        arith_t &arith,
        uint32_t &error_code,
        stack_t &stack)
    {
        bn_t a, b;
        stack.pop(a, error_code);
        stack.pop(b, error_code);

        return cgbn_compare(arith._env, a, b);
    }

    __host__ __device__ __forceinline__ static int32_t scompare(
        arith_t &arith,
        uint32_t &error_code,
        stack_t &stack)
    {
        bn_t a, b;
        stack.pop(a, error_code);
        stack.pop(b, error_code);

        uint32_t sign_a = cgbn_extract_bits_ui32(arith._env, a, arith_t::BITS - 1, 1);
        uint32_t sign_b = cgbn_extract_bits_ui32(arith._env, b, arith_t::BITS - 1, 1);
        if (sign_a == 0 && sign_b == 1)
        {
            return 1;
        }
        else if (sign_a == 1 && sign_b == 0)
        {
            return -1;
        }
        else
        {
            return cgbn_compare(arith._env, a, b);
        }
    }

    __host__ __device__ __forceinline__ static void operation_LT(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {

            int32_t int_result = compare(
                arith,
                error_code,
                stack);
            uint32_t result = (int_result < 0) ? 1 : 0;
            bn_t r;

            cgbn_set_ui32(arith._env, r, result);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_GT(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {

            int32_t int_result = compare(
                arith,
                error_code,
                stack);
            uint32_t result = (int_result > 0) ? 1 : 0;
            bn_t r;

            cgbn_set_ui32(arith._env, r, result);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SLT(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {

            int32_t int_result = scompare(
                arith,
                error_code,
                stack);
            uint32_t result = (int_result < 0) ? 1 : 0;
            bn_t r;

            cgbn_set_ui32(arith._env, r, result);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SGT(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {

            int32_t int_result = scompare(
                arith,
                error_code,
                stack);
            uint32_t result = (int_result > 0) ? 1 : 0;
            bn_t r;

            cgbn_set_ui32(arith._env, r, result);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_EQ(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {

            int32_t int_result = compare(
                arith,
                error_code,
                stack);
            uint32_t result = (int_result == 0) ? 1 : 0;
            bn_t r;

            cgbn_set_ui32(arith._env, r, result);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_ISZERO(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a;
            stack.pop(a, error_code);
            bn_t r;

            int32_t compare = cgbn_compare_ui32(arith._env, a, 0);
            if (compare == 0)
            {
                cgbn_set_ui32(arith._env, r, 1);
            }
            else
            {
                cgbn_set_ui32(arith._env, r, 0);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }
};

template <class params>
class bitwise_operations
{
public:
    typedef arith_env_t<params> arith_t;
    typedef typename arith_t::bn_t bn_t;
    typedef stack_t<params> stack_t;

    __host__ __device__ __forceinline__ static void operation_AND(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b;
            stack.pop(a, error_code);
            stack.pop(b, error_code);
            bn_t r;

            cgbn_bitwise_and(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_OR(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b;
            stack.pop(a, error_code);
            stack.pop(b, error_code);
            bn_t r;

            cgbn_bitwise_ior(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_XOR(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a, b;
            stack.pop(a, error_code);
            stack.pop(b, error_code);
            bn_t r;

            cgbn_bitwise_xor(arith._env, r, a, b);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_NOT(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t a;
            stack.pop(a, error_code);
            bn_t r;

            cgbn_bitwise_mask_xor(arith._env, r, a, arith_t::BITS);

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_BYTE(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t i, x;
            stack.pop(i, error_code);
            stack.pop(x, error_code);
            bn_t r;

            if (cgbn_compare_ui32(arith._env, i, (arith_t::BYTES-1)) == 1)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else
            {
                uint32_t index = cgbn_get_ui32(arith._env, i);
                uint32_t byte = cgbn_extract_bits_ui32(arith._env, x, 8 * ((arith_t::BYTES - 1) - index), 8);
                cgbn_set_ui32(arith._env, r, byte);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SHL(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t shift, value;
            stack.pop(shift, error_code);
            stack.pop(value, error_code);
            bn_t r;

            if (cgbn_compare_ui32(arith._env, shift, arith_t::BITS - 1) == 1)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else
            {
                uint32_t shift_left = cgbn_get_ui32(arith._env, shift);
                cgbn_shift_left(arith._env, r, value, shift_left);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SHR(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t shift, value;
            stack.pop(shift, error_code);
            stack.pop(value, error_code);
            bn_t r;

            if (cgbn_compare_ui32(arith._env, shift, arith_t::BITS - 1) == 1)
            {
                cgbn_set_ui32(arith._env, r, 0);
            }
            else
            {
                uint32_t shift_right = cgbn_get_ui32(arith._env, shift);
                cgbn_shift_right(arith._env, r, value, shift_right);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }

    __host__ __device__ __forceinline__ static void operation_SAR(
        arith_t &arith,
        bn_t &gas_limit,
        bn_t &gas_used,
        uint32_t &error_code,
        uint32_t &pc,
        stack_t &stack)
    {
        cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
        if (arith.has_gas(gas_limit, gas_used, error_code))
        {
            bn_t shift, value;
            stack.pop(shift, error_code);
            stack.pop(value, error_code);
            bn_t r;

            uint32_t sign_b = cgbn_extract_bits_ui32(arith._env, value, arith_t::BITS - 1, 1);
            uint32_t shift_right = cgbn_get_ui32(arith._env, shift);

            if (cgbn_compare_ui32(arith._env, shift, arith_t::BITS - 1) == 1)
                shift_right = arith_t::BITS;

            cgbn_shift_right(arith._env, r, value, shift_right);
            if (sign_b == 1)
            {
                cgbn_bitwise_mask_ior(arith._env, r, r, -shift_right);
            }

            stack.push(r, error_code);

            pc = pc + 1;
        }
    }
};

#endif