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)
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")
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)
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)
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))
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)
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))
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)
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)
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)
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)
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)
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)
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)
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)
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)
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])
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))
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)
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))
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')))
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()))
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)
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)))
def fcopysign_op(config: Configuration) -> None: """ Common logic function for the float COPYSIGN opcodes """ b, a = config.pop2_f64() if config.enable_logic_fn_logging: logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a, b) a_is_negative = _is_negative(a) b_is_negative = _is_negative(b) if a_is_negative is b_is_negative: config.push_operand(a) else: config.push_operand(_negate_float(a))
def fXX_convert_usX_iXX_op(config: Configuration) -> None: """ Common logic function for the CONVERT opcodes """ instruction = cast(Convert, config.current_instruction) base_value = config.pop_u64() if instruction.signed: value = instruction.from_valtype.to_signed(base_value) else: value = base_value if config.enable_logic_fn_logging: logger.debug("%s(%s)", instruction.opcode.text, value) config.push_operand(instruction.valtype.to_float(value))
def iXX_rotr_op(config: Configuration) -> None: """ Common logic function for the integer ROTR 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) bit_size = instruction.valtype.bit_size.value shift_size = int(b % bit_size) lower = int(a) >> shift_size upper = int(a) << int(bit_size - shift_size) result = (upper | lower) % instruction.valtype.mod config.push_operand(instruction.valtype.value(result))
def memory_grow_op(config: Configuration) -> None: """ Logic function for the MEMORY_GROW 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] current_num_pages = mem.num_pages num_pages = config.pop_u32() try: mem.grow(num_pages) except ValidationError: config.push_operand(constants.INT32_NEGATIVE_ONE) else: config.push_operand(current_num_pages)
def i64extend_usX_op(config: Configuration) -> None: """ Common logic function for the EXTEND opcodes """ instruction = cast(Extend, config.current_instruction) value = config.pop_u32() if config.enable_logic_fn_logging: logger.debug("%s(%s)", instruction.opcode.text, value) if instruction.signed: signed_value = instruction.from_valtype.to_signed(value) result = instruction.valtype.from_signed(signed_value) else: result = instruction.valtype.value(value) config.push_operand(result)
def iXX_rems_op(config: Configuration) -> None: """ Common logic function for the integer REMS 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) if b == 0: raise Trap('DIVISION BY ZERO') b_s = instruction.valtype.to_signed(b) a_s = instruction.valtype.to_signed(a) raw_result = numpy.abs(a_s) % numpy.abs(b_s) result = -1 * raw_result if a_s < 0 else raw_result config.push_operand(instruction.valtype.value(result))