Beispiel #1
0
def iXX_divs_op(config: Configuration) -> None:
    """
    Common logic function for the integer DIVS opcodes
    """
    instruction = cast(BinOp, config.current_instruction)
    b, a = config.pop2_u32()

    b_s = instruction.valtype.to_signed(b)
    a_s = instruction.valtype.to_signed(a)
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", instruction.opcode.text, a_s, b_s)

    if b == 0:
        raise Trap('DIVISION BY ZERO')

    raw_result = abs(int(a_s)) // abs(int(b_s))
    _, upper_bound = instruction.valtype.signed_bounds

    if raw_result > upper_bound:
        raise Trap('UNDEFINED')

    if (a_s < 0) is not (b_s < 0):
        signed_result = instruction.valtype.signed_type(-1 * raw_result)
    else:
        signed_result = instruction.valtype.signed_type(raw_result)

    result = instruction.valtype.from_signed(signed_result)

    config.push_operand(result)
Beispiel #2
0
def if_op(config: Configuration) -> None:
    """
    Logic function for the IF opcode
    """
    instruction = cast(If, config.current_instruction)

    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    value = config.pop_operand()
    arity = len(instruction.result_type)

    if value:
        label = Label(
            arity=arity,
            instructions=instruction.instructions,
            is_loop=False,
        )
    else:
        label = Label(
            arity=arity,
            instructions=instruction.else_instructions,
            is_loop=False,
        )

    config.push_label(label)
Beispiel #3
0
def _setup_function_invocation(config: Configuration,
                               function_address: FunctionAddress,
                               function_args: Tuple[TValue, ...]) -> None:
    """
    Helper function for invoking a function by the function's address.
    """
    function = config.store.funcs[function_address]

    if len(function_args) != len(function.type.params):
        raise TypeError(
            f"Wrong number of arguments. Expected {len(function.type.params)} "
            f"Got {len(function_args)}")

    if isinstance(function, FunctionInstance):
        locals = [valtype.zero for valtype in function.code.locals]
        frame = Frame(
            module=function.module,
            locals=list(function_args) + locals,
            # TODO: do we need this wrapping anymore?
            instructions=Block.wrap_with_end(function.type.results,
                                             function.code.body),
            arity=len(function.type.results),
        )
        config.push_frame(frame)
    elif isinstance(function, HostFunction):
        ret = function.hostcode(config, function_args)
        if len(ret) > 1:
            raise Exception("Invariant")
        elif ret:
            config.push_operand(ret[0])
    else:
        raise Exception("Invariant: unreachable code path")
Beispiel #4
0
def iXX_trunc_usX_fXX_op(config: Configuration) -> None:
    """
    Common logic function for the TRUNC opcodes which convert a float to an
    integer
    """
    instruction = cast(Truncate, config.current_instruction)

    value = config.pop_f32()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    if numpy.isnan(value) or numpy.isinf(value):
        raise Trap(f"Truncation is undefined for {value}")
    else:
        trunc_value = int(numpy.trunc(value))

        if instruction.signed:
            s_lower_bound, s_upper_bound = instruction.valtype.signed_bounds
            if trunc_value < s_lower_bound or trunc_value > s_upper_bound:
                raise Trap(
                    f"Truncation is undefined for {value}. Result outside of s32 "
                    "range.")

            result = instruction.valtype.from_signed(
                instruction.valtype.signed_type(trunc_value))
        else:
            u_lower_bound, u_upper_bound = instruction.valtype.bounds
            if trunc_value < u_lower_bound or trunc_value > u_upper_bound:
                raise Trap(
                    f"Truncation is undefined for {value}. Result outside of s32 "
                    "range.")
            result = instruction.valtype.value(trunc_value)

        config.push_operand(result)
Beispiel #5
0
def store_op(config: Configuration) -> None:
    """
    Logic function for the various *STORE* memory opcodes.
    """
    instruction = cast(MemoryOp, config.current_instruction)
    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    memarg = instruction.memarg

    memory_address = config.frame_module.memory_addrs[0]
    mem = config.store.mems[memory_address]

    value = config.pop_operand()

    base_offset = config.pop_u32()
    memory_location = numpy.uint32(base_offset + memarg.offset)

    # TODO: update this section to use the `ValType.pack_bytes` API
    if instruction.valtype.is_integer_type:
        wrapped_value = instruction.memory_bit_size.wrap_type(value)
        encoded_value = wrapped_value.tobytes()
    elif instruction.valtype.is_float_type:
        encoded_value = value.tobytes()
    else:
        raise Exception("Invariant")

    assert len(encoded_value) == instruction.memory_bit_size.value // 8
    mem.write(memory_location, encoded_value)
Beispiel #6
0
def drop_op(config: Configuration) -> None:
    """
    Logic functin for the DROP opcode.
    """
    if config.enable_logic_fn_logging:
        logger.debug("%s()", config.current_instruction.opcode.text)

    config.pop_operand()
Beispiel #7
0
def const_op(config: Configuration) -> None:
    """
    Common logic function for the various CONST opcodes.
    """
    instruction = cast(TConst, config.current_instruction)
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, instruction)

    config.push_operand(instruction.value)
Beispiel #8
0
def ixor_op(config: Configuration) -> None:
    """
    Common logic function for the integer XOR opcodes
    """
    b, a = config.pop2_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    config.push_operand(a ^ b)
Beispiel #9
0
def f32demote_op(config: Configuration) -> None:
    """
    Logic function for the F32_DEMOTE_F64 opcode
    """
    value = config.pop_f64()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", config.current_instruction.opcode.text, value)

    config.push_operand(numpy.float32(value))
Beispiel #10
0
def iwrap64_op(config: Configuration) -> None:
    """
    Logic function for the I32_WRAP_I64 opcode
    """
    value = config.pop_u64()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", config.current_instruction.opcode.text, value)

    config.push_operand(numpy.uint32(value))
Beispiel #11
0
def get_local_op(config: Configuration) -> None:
    """
    Logic functin for the GET_LOCAL opcode.
    """
    instruction = cast(LocalOp, config.current_instruction)
    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    value = config.frame_locals[instruction.local_idx]
    config.push_operand(value)
Beispiel #12
0
def fsqrt_op(config: Configuration) -> None:
    """
    Common logic function for the float SQRT opcodes
    """
    instruction = cast(UnOp, config.current_instruction)

    value = config.pop_f64()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    if numpy.isnan(value):
        with allow_invalid():
            config.push_operand(numpy.sqrt(value))
    elif value == 0:
        # TODO: this block and the subsequent _is_negative block are in
        # inverted order in the spec, indicating that the proper response here
        # should potentially be `nan` for the case of `sqrt(-0.0)` but the spec
        # tests assert that is not correct.
        # f32.wasm: line 2419
        config.push_operand(value)
    elif _is_negative(value):
        config.push_operand(instruction.valtype.nan)
    else:
        config.push_operand(numpy.sqrt(value))
Beispiel #13
0
def iXX_mul_op(config: Configuration) -> None:
    """
    Common logic function for the integer MUL opcodes
    """
    b, a = config.pop2_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    with allow_overflow():
        config.push_operand(a * b)
Beispiel #14
0
def get_global_op(config: Configuration) -> None:
    """
    Logic functin for the GET_GLOBAL opcode.
    """
    instruction = cast(GlobalOp, config.current_instruction)
    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    global_address = config.frame_module.global_addrs[instruction.global_idx]
    global_ = config.store.globals[global_address]
    config.push_operand(global_.value)
Beispiel #15
0
def memory_size_op(config: Configuration) -> None:
    """
    Logic function for the MEMORY_SIZE opcode
    """
    if config.enable_logic_fn_logging:
        logger.debug("%s()", config.current_instruction.opcode.text)

    memory_address = config.frame_module.memory_addrs[0]
    mem = config.store.mems[memory_address]
    size = numpy.uint32(len(mem.data) // constants.PAGE_SIZE_64K)
    config.push_operand(size)
Beispiel #16
0
def ieqz_op(config: Configuration) -> None:
    """
    Common logic function for the integer EQZ opcodes
    """
    value = config.pop_operand()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", config.current_instruction.opcode.text, value)

    if value == 0:
        config.push_operand(constants.U32_ONE)
    else:
        config.push_operand(constants.U32_ZERO)
Beispiel #17
0
def fadd_op(config: Configuration) -> None:
    """
    Common logic function for the float ADD opcodes
    """
    b, a = config.pop2_f64()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    with allow_multiple(over=True, invalid=True):
        config.push_operand(a + b)
Beispiel #18
0
def idivu_op(config: Configuration) -> None:
    """
    Common logic function for the integer DIVU opcodes
    """
    b, a = config.pop2_u32()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    if b == 0:
        raise Trap('DIVISION BY ZERO')
    config.push_operand(a // b)
Beispiel #19
0
def select_op(config: Configuration) -> None:
    """
    Logic functin for the SELECT opcode.
    """
    if config.enable_logic_fn_logging:
        logger.debug("%s()", config.current_instruction.opcode.text)

    a, b, c = config.pop3_operands()

    if a:
        config.push_operand(c)
    else:
        config.push_operand(b)
Beispiel #20
0
def XXX_reinterpret_XXX_op(config: Configuration) -> None:
    """
    Common logic function for the REINTERPRET opcodes
    """
    instruction = cast(Convert, config.current_instruction)

    value = config.pop_f32()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    config.push_operand(
        numpy.frombuffer(value.data, instruction.valtype.value)[0])
Beispiel #21
0
def ne_op(config: Configuration) -> None:
    """
    Common logic function for all NE opcodes
    """
    b, a = config.pop2_operands()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    if a == b:
        config.push_operand(constants.U32_ZERO)
    else:
        config.push_operand(constants.U32_ONE)
Beispiel #22
0
def iXX_shl_op(config: Configuration) -> None:
    """
    Common logic function for the integer SHL opcodes
    """
    instruction = cast(BinOp, config.current_instruction)
    b, a = config.pop2_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", instruction.opcode.text, a, b)

    shift_amount = int(b % instruction.valtype.bit_size.value)
    raw_result = int(a) << shift_amount
    mod = instruction.valtype.mod
    config.push_operand(instruction.valtype.value(raw_result % mod))
Beispiel #23
0
def fneg_op(config: Configuration) -> None:
    """
    Common logic function for the float NEG opcodes
    """
    instruction = cast(UnOp, config.current_instruction)

    value = config.pop_f64()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    negated_value = _negate_float(value)

    config.push_operand(negated_value)
Beispiel #24
0
def ipopcnt_op(config: Configuration) -> None:
    """
    Common logic function for the integer POPCNT opcodes
    """
    instruction = cast(UnOp, config.current_instruction)
    value = config.pop_operand()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    if value == 0:
        config.push_operand(instruction.valtype.zero)
    else:
        config.push_operand(
            instruction.valtype.value(bin(int(value)).count('1')))
Beispiel #25
0
def loop_op(config: Configuration) -> None:
    """
    Logic function for the LOOP opcode
    """
    instruction = cast(Loop, config.current_instruction)

    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    label = Label(
        arity=0,
        instructions=instruction.instructions,
        is_loop=True,
    )
    config.push_label(label)
Beispiel #26
0
def block_op(config: Configuration) -> None:
    """
    Logic function for the BLOCK opcode
    """
    block = cast(Block, config.current_instruction)

    if config.enable_logic_fn_logging:
        logger.debug("%s()", block.opcode.text)

    label = Label(
        arity=len(block.result_type),
        instructions=block.instructions,
        is_loop=False,
    )
    config.push_label(label)
Beispiel #27
0
def iXX_ges_op(config: Configuration) -> None:
    """
    Common logic function for the integer GES opcodes
    """
    instruction = cast(RelOp, config.current_instruction)
    b, a = config.pop2_u64()
    b_s = instruction.valtype.to_signed(b)
    a_s = instruction.valtype.to_signed(a)
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", instruction.opcode.text, a_s, b_s)

    if a_s >= b_s:
        config.push_operand(constants.U32_ONE)
    else:
        config.push_operand(constants.U32_ZERO)
Beispiel #28
0
def iXX_clz_op(config: Configuration) -> None:
    """
    Common logic function for the integer CLZ opcodes
    """
    instruction = cast(TestOp, config.current_instruction)
    value = config.pop_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    bit_size = instruction.valtype.bit_size.value
    if value == 0:
        config.push_operand(instruction.valtype.value(bit_size))
    else:
        config.push_operand(
            instruction.valtype.value(bit_size - int(value).bit_length()))
Beispiel #29
0
def call_indirect_op(config: Configuration) -> None:
    """
    Logic function for the CALL_INDIRECT opcode
    """
    instruction = cast(CallIndirect, config.current_instruction)

    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    table_address = config.frame_module.table_addrs[0]
    table = config.store.tables[table_address]
    function_type = config.frame_module.types[instruction.type_idx]

    element_idx = config.pop_u32()

    if len(table.elem) <= element_idx:
        raise Trap(
            "Element index out of table range: {element_idx} > {len(table.elem)}"
        )

    function_address = table.elem[element_idx]

    if function_address is None:
        raise Trap("Table element at index {element_idx} is empty")

    function = config.store.funcs[int(function_address)]

    if function.type != function_type:
        raise Trap(
            "Function type mismatch.  Expected {function_type}.  Got {function.type}"
        )

    _setup_call(config, function_address)
Beispiel #30
0
def iXX_ctz_op(config: Configuration) -> None:
    """
    Common logic function for the integer CTZ opcodes
    """
    instruction = cast(TestOp, config.current_instruction)
    value = config.pop_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    if value == 0:
        config.push_operand(
            instruction.valtype.value(instruction.valtype.bit_size.value))
    else:
        as_bin = bin(int(value))
        _, _, zeros = as_bin.rpartition('1')
        config.push_operand(instruction.valtype.value(len(zeros)))