Skip to content

Commit

Permalink
Implement float->int conversion instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jul 31, 2020
1 parent 0728606 commit c71fe26
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 9 deletions.
54 changes: 46 additions & 8 deletions lib/fizzy/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ inline DstT extend(SrcT in) noexcept
return DstT{in};
}

template <typename SrcT, typename DstT>
inline void trunc(OperandStack& stack) noexcept
{
stack.top() = static_cast<DstT>(stack.top().as<SrcT>());
}

template <typename DstT, typename SrcT = DstT>
inline bool load_from_memory(
bytes_view memory, OperandStack& stack, const uint8_t*& immediates) noexcept
Expand Down Expand Up @@ -1375,6 +1381,26 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, span<const Value>
stack.push(static_cast<uint32_t>(stack.pop()));
break;
}
case Instr::i32_trunc_f32_s:
{
trunc<float, int32_t>(stack);
break;
}
case Instr::i32_trunc_f32_u:
{
trunc<float, uint32_t>(stack);
break;
}
case Instr::i32_trunc_f64_s:
{
trunc<double, int32_t>(stack);
break;
}
case Instr::i32_trunc_f64_u:
{
trunc<double, uint32_t>(stack);
break;
}
case Instr::i64_extend_i32_s:
{
const auto value = static_cast<int32_t>(stack.pop());
Expand All @@ -1386,6 +1412,26 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, span<const Value>
// effectively no-op
break;
}
case Instr::i64_trunc_f32_s:
{
trunc<float, int64_t>(stack);
break;
}
case Instr::i64_trunc_f32_u:
{
trunc<float, uint64_t>(stack);
break;
}
case Instr::i64_trunc_f64_s:
{
trunc<double, int64_t>(stack);
break;
}
case Instr::i64_trunc_f64_u:
{
trunc<double, uint64_t>(stack);
break;
}
case Instr::f32_add:
{
binary_op(stack, std::plus<float>{});
Expand Down Expand Up @@ -1439,14 +1485,6 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, span<const Value>
case Instr::f64_min:
case Instr::f64_max:
case Instr::f64_copysign:
case Instr::i32_trunc_f32_s:
case Instr::i32_trunc_f32_u:
case Instr::i32_trunc_f64_s:
case Instr::i32_trunc_f64_u:
case Instr::i64_trunc_f32_s:
case Instr::i64_trunc_f32_u:
case Instr::i64_trunc_f64_s:
case Instr::i64_trunc_f64_u:
case Instr::f32_convert_i32_s:
case Instr::f32_convert_i32_u:
case Instr::f32_convert_i64_s:
Expand Down
178 changes: 177 additions & 1 deletion test/unittests/execute_floating_point_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "parser.hpp"
#include <gtest/gtest.h>
#include <test/utils/hex.hpp>
#include <cmath>

using namespace fizzy;
using namespace fizzy::test;
Expand Down Expand Up @@ -69,4 +70,179 @@ TEST(execute_floating_point, f64_add)
auto instance = instantiate(parse(wasm));
const auto result = execute(*instance, 0, {1.0011, 6.0066});
EXPECT_EQ(result.value.f64, 7.0077);
}
}


template <typename SrcT, typename DstT>
struct ConversionPairWasmTraits;

template <>
struct ConversionPairWasmTraits<float, int32_t>
{
static constexpr auto opcode_name = "i32_trunc_f32_s";
static constexpr auto opcode = Instr::i32_trunc_f32_s;
static constexpr auto src_valtype = ValType::f32;
static constexpr auto dst_valtype = ValType::i32;
};
template <>
struct ConversionPairWasmTraits<float, uint32_t>
{
static constexpr auto opcode_name = "i32_trunc_f32_u";
static constexpr auto opcode = Instr::i32_trunc_f32_u;
static constexpr auto src_valtype = ValType::f32;
static constexpr auto dst_valtype = ValType::i32;
};
template <>
struct ConversionPairWasmTraits<double, int32_t>
{
static constexpr auto opcode_name = "i32_trunc_f64_s";
static constexpr auto opcode = Instr::i32_trunc_f64_s;
static constexpr auto src_valtype = ValType::f64;
static constexpr auto dst_valtype = ValType::i32;
};
template <>
struct ConversionPairWasmTraits<double, uint32_t>
{
static constexpr auto opcode_name = "i32_trunc_f64_u";
static constexpr auto opcode = Instr::i32_trunc_f64_u;
static constexpr auto src_valtype = ValType::f64;
static constexpr auto dst_valtype = ValType::i32;
};
template <>
struct ConversionPairWasmTraits<float, int64_t>
{
static constexpr auto opcode_name = "i64_trunc_f32_s";
static constexpr auto opcode = Instr::i64_trunc_f32_s;
static constexpr auto src_valtype = ValType::f32;
static constexpr auto dst_valtype = ValType::i64;
};
template <>
struct ConversionPairWasmTraits<float, uint64_t>
{
static constexpr auto opcode_name = "i64_trunc_f32_u";
static constexpr auto opcode = Instr::i64_trunc_f32_u;
static constexpr auto src_valtype = ValType::f32;
static constexpr auto dst_valtype = ValType::i64;
};
template <>
struct ConversionPairWasmTraits<double, int64_t>
{
static constexpr auto opcode_name = "i64_trunc_f64_s";
static constexpr auto opcode = Instr::i64_trunc_f64_s;
static constexpr auto src_valtype = ValType::f64;
static constexpr auto dst_valtype = ValType::i64;
};
template <>
struct ConversionPairWasmTraits<double, uint64_t>
{
static constexpr auto opcode_name = "i64_trunc_f64_u";
static constexpr auto opcode = Instr::i64_trunc_f64_u;
static constexpr auto src_valtype = ValType::f64;
static constexpr auto dst_valtype = ValType::i64;
};

template <typename SrcT, typename DstT>
struct ConversionPair : ConversionPairWasmTraits<SrcT, DstT>
{
using src_type = SrcT;
using dst_type = DstT;
};

template <typename T>
class execute_floating_point_conversions : public testing::Test
{
};

struct ConversionName
{
template <typename T>
static std::string GetName(int /*unused*/)
{
return T::opcode_name;
}
};

using pairs = testing::Types<ConversionPair<float, int32_t>, ConversionPair<float, uint32_t>,
ConversionPair<double, int32_t>, ConversionPair<double, uint32_t>,
ConversionPair<float, int64_t>, ConversionPair<float, uint64_t>,
ConversionPair<double, int64_t>, ConversionPair<double, uint64_t>>;
TYPED_TEST_SUITE(execute_floating_point_conversions, pairs, ConversionName);

TYPED_TEST(execute_floating_point_conversions, trunc)
{
using src_type = typename TypeParam::src_type;
using dst_type = typename TypeParam::dst_type;
using src_limits = std::numeric_limits<src_type>;
using dst_limits = std::numeric_limits<dst_type>;

/* wat2wasm
(func (param f32) (result i32)
local.get 0
i32.trunc_f32_s
)
*/
auto wasm = from_hex("0061736d0100000001060160017d017f030201000a070105002000a80b");

// Find and replace changeable values: types and the conversion instruction.
auto& param_type = wasm[wasm.find(static_cast<uint8_t>(ValType::f32))];
auto& result_type = wasm[wasm.find(static_cast<uint8_t>(ValType::i32))];
auto& opcode = wasm[wasm.find(static_cast<uint8_t>(Instr::i32_trunc_f32_s))];
param_type = static_cast<uint8_t>(TypeParam::src_valtype);
result_type = static_cast<uint8_t>(TypeParam::dst_valtype);
opcode = static_cast<uint8_t>(TypeParam::opcode);

auto instance = instantiate(parse(wasm));

{ // Zero.
const auto result = execute(*instance, 0, {src_type{0}});
EXPECT_EQ(result.value.template as<dst_type>(), 0);
}

{ // Max defined: something little smaller than 2^N.
const auto arg =
std::nextafter(std::pow(src_type{2}, src_type{dst_limits::digits}), src_type{0});
const auto expected = static_cast<dst_type>(arg);
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), expected);
}

{ // Something slightly bigger than 0.0.
const auto arg = src_limits::denorm_min();
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), 0);
}

{ // Something slightly smaller than 0.0.
const auto arg = -src_limits::denorm_min();
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), 0);
}

{ // Something smaller than 2.0.
const auto arg = std::nextafter(src_type{2}, src_type{0});
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), src_type{1});
}

{ // Something bigger than -1.0.
const auto arg = std::nextafter(src_type{-1}, src_type{0});
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), 0);
}

if (dst_limits::is_signed)
{
{ // Min defined: -2^N.
const auto arg = -std::pow(src_type{2}, src_type{dst_limits::digits});
const auto expected = static_cast<dst_type>(arg);
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), expected);
}

{ // Something bigger than -2.0.
const auto arg = std::nextafter(src_type{-2}, src_type{0});
const auto result = execute(*instance, 0, {arg});
EXPECT_EQ(result.value.template as<dst_type>(), src_type{-1});
}
}
}

0 comments on commit c71fe26

Please sign in to comment.