Commit ef98172d authored by Karl Schimpf's avatar Karl Schimpf Committed by Commit Bot

[wasm] Implement i64.trunc_s:sat/f32

Implements the saturating opcode i64.trunc_s:sat/f32.

Also does some refactoring of the i32 saturating opcodes use a simplier
solution (calling a single method to handle all i32 values).

Also refactors code so that the remaining i64 saturating conversions
should be easy to add to the wasm compiler.

Bug: v8:7226
Change-Id: I031aca1e059b4baa989a56ecbc16941f591ff9b3
Reviewed-on: https://chromium-review.googlesource.com/887333
Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51001}
parent fcb5b4a5
......@@ -796,7 +796,10 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
op = m->SignExtendWord32ToInt64();
break;
case wasm::kExprI64SConvertF32:
return BuildI64SConvertF32(input, position);
return BuildI64SConvertF32(input, position, NumericImplementation::kTrap);
case wasm::kExprI64SConvertSatF32:
return BuildI64SConvertF32(input, position,
NumericImplementation::kSaturate);
case wasm::kExprI64SConvertF64:
return BuildI64SConvertF64(input, position);
case wasm::kExprI64UConvertF32:
......@@ -1388,167 +1391,166 @@ Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) {
#endif
}
// Helper classes for float to int conversions.
struct WasmGraphBuilder::IntConvertOps {
MachineRepresentation word_rep() const {
return MachineRepresentation::kWord32;
}
Node* zero() const { return builder_->Int32Constant(0); }
virtual Node* min() const = 0;
virtual Node* max() const = 0;
virtual ~IntConvertOps() = default;
protected:
explicit IntConvertOps(WasmGraphBuilder* builder) : builder_(builder) {}
WasmGraphBuilder* builder_;
DISALLOW_IMPLICIT_CONSTRUCTORS(IntConvertOps);
};
struct I32SConvertOps final : public WasmGraphBuilder::IntConvertOps {
explicit I32SConvertOps(WasmGraphBuilder* builder)
: WasmGraphBuilder::IntConvertOps(builder) {}
~I32SConvertOps() = default;
Node* min() const {
return builder_->Int32Constant(std::numeric_limits<int32_t>::min());
}
Node* max() const {
return builder_->Int32Constant(std::numeric_limits<int32_t>::max());
}
DISALLOW_IMPLICIT_CONSTRUCTORS(I32SConvertOps);
};
struct I32UConvertOps final : public WasmGraphBuilder::IntConvertOps {
explicit I32UConvertOps(WasmGraphBuilder* builder)
: WasmGraphBuilder::IntConvertOps(builder) {}
~I32UConvertOps() = default;
Node* min() const {
return builder_->Int32Constant(std::numeric_limits<uint32_t>::min());
}
Node* max() const {
return builder_->Int32Constant(std::numeric_limits<uint32_t>::max());
}
DISALLOW_IMPLICIT_CONSTRUCTORS(I32UConvertOps);
};
struct WasmGraphBuilder::FloatConvertOps {
virtual Node* zero() const = 0;
virtual wasm::WasmOpcode trunc_op() const = 0;
virtual wasm::WasmOpcode ne_op() const = 0;
virtual wasm::WasmOpcode lt_op() const = 0;
virtual ~FloatConvertOps() = default;
protected:
explicit FloatConvertOps(WasmGraphBuilder* builder) : builder_(builder) {}
WasmGraphBuilder* builder_;
DISALLOW_IMPLICIT_CONSTRUCTORS(FloatConvertOps);
};
struct F32ConvertOps final : public WasmGraphBuilder::FloatConvertOps {
explicit F32ConvertOps(WasmGraphBuilder* builder)
: WasmGraphBuilder::FloatConvertOps(builder) {}
~F32ConvertOps() = default;
Node* zero() const { return builder_->Float32Constant(0.0); }
wasm::WasmOpcode trunc_op() const { return wasm::kExprF32Trunc; }
wasm::WasmOpcode ne_op() const { return wasm::kExprF32Ne; }
wasm::WasmOpcode lt_op() const { return wasm::kExprF32Lt; }
DISALLOW_IMPLICIT_CONSTRUCTORS(F32ConvertOps);
};
struct F64ConvertOps final : public WasmGraphBuilder::FloatConvertOps {
explicit F64ConvertOps(WasmGraphBuilder* builder)
: WasmGraphBuilder::FloatConvertOps(builder) {}
~F64ConvertOps() = default;
Node* zero() const { return builder_->Float64Constant(0.0); }
wasm::WasmOpcode trunc_op() const { return wasm::kExprF64Trunc; }
wasm::WasmOpcode ne_op() const { return wasm::kExprF64Ne; }
wasm::WasmOpcode lt_op() const { return wasm::kExprF64Lt; }
DISALLOW_IMPLICIT_CONSTRUCTORS(F64ConvertOps);
};
Node* WasmGraphBuilder::BuildConvertCheck(Node* test, Node* result, Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl,
const IntConvertOps* int_ops,
const FloatConvertOps* float_ops) {
namespace {
MachineRepresentation word_rep(WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kInt32s:
case WasmGraphBuilder::Type::kInt32u:
return MachineRepresentation::kWord32;
case WasmGraphBuilder::Type::kInt64s:
return MachineRepresentation::kWord64;
default:
UNREACHABLE();
}
}
Node* zero(WasmGraphBuilder* builder, WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kInt32s:
case WasmGraphBuilder::Type::kInt32u:
return builder->Int32Constant(0);
case WasmGraphBuilder::Type::kInt64s:
return builder->Int64Constant(0);
case WasmGraphBuilder::Type::kFloat32:
return builder->Float32Constant(0.0);
case WasmGraphBuilder::Type::kFloat64:
return builder->Float64Constant(0.0);
default:
UNREACHABLE();
}
}
Node* min(WasmGraphBuilder* builder, WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kInt32s:
return builder->Int32Constant(std::numeric_limits<int32_t>::min());
case WasmGraphBuilder::Type::kInt32u:
return builder->Int32Constant(std::numeric_limits<uint32_t>::min());
case WasmGraphBuilder::Type::kInt64s:
return builder->Int64Constant(std::numeric_limits<int64_t>::min());
default:
UNREACHABLE();
}
}
Node* max(WasmGraphBuilder* builder, WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kInt32s:
return builder->Int32Constant(std::numeric_limits<int32_t>::max());
case WasmGraphBuilder::Type::kInt32u:
return builder->Int32Constant(std::numeric_limits<uint32_t>::max());
case WasmGraphBuilder::Type::kInt64s:
return builder->Int64Constant(std::numeric_limits<int64_t>::max());
default:
UNREACHABLE();
}
}
wasm::WasmOpcode trunc_op(WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kFloat32:
return wasm::kExprF32Trunc;
case WasmGraphBuilder::Type::kFloat64:
return wasm::kExprF64Trunc;
default:
UNREACHABLE();
}
}
wasm::WasmOpcode ne_op(WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kFloat32:
return wasm::kExprF32Ne;
case WasmGraphBuilder::Type::kFloat64:
return wasm::kExprF64Ne;
default:
UNREACHABLE();
}
}
wasm::WasmOpcode lt_op(WasmGraphBuilder::Type type) {
switch (type) {
case WasmGraphBuilder::Type::kFloat32:
return wasm::kExprF32Lt;
case WasmGraphBuilder::Type::kFloat64:
return wasm::kExprF64Lt;
default:
UNREACHABLE();
}
}
} // namespace
Node* WasmGraphBuilder::BuildI32ConvertFloat(Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl,
Type int_ty, Type float_ty,
const Operator* conv_op,
const wasm::WasmOpcode check_op) {
Node* trunc = Unop(trunc_op(float_ty), input);
Node* converted_value = graph()->NewNode(conv_op, trunc);
// Convert the result back to a float. If we end up at a different value
// than the truncated input value, then there has been an overflow and we
// trap/saturate.
Node* check = Unop(check_op, converted_value);
Node* test = Binop(ne_op(float_ty), trunc, check);
switch (impl) {
case NumericImplementation::kTrap:
TrapIfTrue(wasm::kTrapFloatUnrepresentable, test, position);
return result;
return converted_value;
case NumericImplementation::kSaturate: {
Diamond tl_d(graph(), jsgraph()->common(), test, BranchHint::kFalse);
tl_d.Chain(*control_);
Diamond nan_d(graph(), jsgraph()->common(),
Binop(float_ops->ne_op(), input, input), // Checks if NaN.
BranchHint::kFalse);
tl_d.Chain(Control());
Node* nan_test = Binop(ne_op(float_ty), input, input);
Diamond nan_d(graph(), jsgraph()->common(), nan_test, BranchHint::kFalse);
nan_d.Nest(tl_d, true);
Diamond sat_d(graph(), jsgraph()->common(),
Binop(float_ops->lt_op(), input, float_ops->zero()),
BranchHint::kNone);
Node* neg_test = Binop(lt_op(float_ty), input, zero(this, float_ty));
Diamond sat_d(graph(), jsgraph()->common(), neg_test, BranchHint::kNone);
sat_d.Nest(nan_d, false);
Node* sat_val =
sat_d.Phi(int_ops->word_rep(), int_ops->min(), int_ops->max());
Node* nan_val = nan_d.Phi(int_ops->word_rep(), int_ops->zero(), sat_val);
return tl_d.Phi(int_ops->word_rep(), nan_val, result);
sat_d.Phi(word_rep(int_ty), min(this, int_ty), max(this, int_ty));
Node* nan_val = nan_d.Phi(word_rep(int_ty), zero(this, int_ty), sat_val);
return tl_d.Phi(word_rep(int_ty), nan_val, converted_value);
}
}
UNREACHABLE();
}
Node* WasmGraphBuilder::BuildI32ConvertOp(
Node* input, wasm::WasmCodePosition position, NumericImplementation impl,
const Operator* op, wasm::WasmOpcode check_op, const IntConvertOps* int_ops,
const FloatConvertOps* float_ops) {
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(float_ops->trunc_op(), input);
Node* result = graph()->NewNode(op, trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we
// trap/saturate.
Node* check = Unop(check_op, result);
Node* overflow = Binop(float_ops->ne_op(), trunc, check);
return BuildConvertCheck(overflow, result, input, position, impl, int_ops,
float_ops);
}
Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl) {
I32SConvertOps int_ops(this);
F32ConvertOps float_ops(this);
return BuildI32ConvertOp(input, position, impl,
jsgraph()->machine()->TruncateFloat32ToInt32(),
wasm::kExprF32SConvertI32, &int_ops, &float_ops);
return BuildI32ConvertFloat(input, position, impl, Type::kInt32s,
Type::kFloat32,
jsgraph()->machine()->TruncateFloat32ToInt32(),
wasm::kExprF32SConvertI32);
}
Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl) {
I32SConvertOps int_ops(this);
F64ConvertOps float_ops(this);
return BuildI32ConvertOp(input, position, impl,
jsgraph()->machine()->ChangeFloat64ToInt32(),
wasm::kExprF64SConvertI32, &int_ops, &float_ops);
return BuildI32ConvertFloat(
input, position, impl, Type::kInt32s, Type::kFloat64,
jsgraph()->machine()->ChangeFloat64ToInt32(), wasm::kExprF64SConvertI32);
}
Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl) {
I32UConvertOps int_ops(this);
F32ConvertOps float_ops(this);
return BuildI32ConvertOp(input, position, impl,
jsgraph()->machine()->TruncateFloat32ToUint32(),
wasm::kExprF32UConvertI32, &int_ops, &float_ops);
return BuildI32ConvertFloat(input, position, impl, Type::kInt32u,
Type::kFloat32,
jsgraph()->machine()->TruncateFloat32ToUint32(),
wasm::kExprF32UConvertI32);
}
Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl) {
I32UConvertOps int_ops(this);
F64ConvertOps float_ops(this);
return BuildI32ConvertOp(input, position, impl,
jsgraph()->machine()->TruncateFloat64ToUint32(),
wasm::kExprF64UConvertI32, &int_ops, &float_ops);
return BuildI32ConvertFloat(input, position, impl, Type::kInt32u,
Type::kFloat64,
jsgraph()->machine()->TruncateFloat64ToUint32(),
wasm::kExprF64UConvertI32);
}
Node* WasmGraphBuilder::BuildI32AsmjsSConvertF32(Node* input) {
......@@ -1812,21 +1814,48 @@ Node* WasmGraphBuilder::BuildIntToFloatConversionInstruction(
return load;
}
Node* WasmGraphBuilder::BuildI64TruncConvertFloat(
Node* input, wasm::WasmCodePosition position, NumericImplementation impl,
Type int_ty, Type float_ty, const Operator* trunc_op) {
Node* trunc = graph()->NewNode(trunc_op, input);
Node* converted_value = graph()->NewNode(jsgraph()->common()->Projection(0),
trunc, graph()->start());
Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc,
graph()->start());
switch (impl) {
case NumericImplementation::kTrap:
ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position);
return converted_value;
case NumericImplementation::kSaturate: {
Node* test = Binop(wasm::kExprI64Eq, overflow, Int64Constant(0));
Diamond tl_d(graph(), jsgraph()->common(), test, BranchHint::kFalse);
tl_d.Chain(Control());
Node* nan_test = Binop(ne_op(float_ty), input, input);
Diamond nan_d(graph(), jsgraph()->common(), nan_test, BranchHint::kFalse);
nan_d.Nest(tl_d, true);
Node* neg_test = Binop(lt_op(float_ty), input, zero(this, float_ty));
Diamond sat_d(graph(), jsgraph()->common(), neg_test, BranchHint::kNone);
sat_d.Nest(nan_d, false);
Node* sat_val =
sat_d.Phi(word_rep(int_ty), min(this, int_ty), max(this, int_ty));
Node* nan_val = nan_d.Phi(word_rep(int_ty), zero(this, int_ty), sat_val);
return tl_d.Phi(word_rep(int_ty), nan_val, converted_value);
}
}
UNREACHABLE();
}
Node* WasmGraphBuilder::BuildI64SConvertF32(Node* input,
wasm::WasmCodePosition position) {
wasm::WasmCodePosition position,
NumericImplementation impl) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float32_to_int64(jsgraph()->isolate()),
MachineRepresentation::kFloat32, MachineType::Int64(), position);
return BuildI64CcallConvertFloat(
input, position, impl, Type::kInt64s, Type::kFloat32,
ExternalReference::wasm_float32_to_int64(jsgraph()->isolate()));
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat32ToInt64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc,
graph()->start());
Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc,
graph()->start());
ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position);
return result;
return BuildI64TruncConvertFloat(
input, position, impl, Type::kInt64s, Type::kFloat32,
jsgraph()->machine()->TryTruncateFloat32ToInt64());
}
}
......@@ -1884,6 +1913,9 @@ Node* WasmGraphBuilder::BuildI64UConvertF64(Node* input,
}
}
// TODO(kschimpf) Remove this method once BuildI64UConvertF32,
// BuildI64SConvertF64, and BuildI64UConvertF64 have been moved over to use
// method BuildI64CcallConvertFloat() below.
Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
......@@ -1914,6 +1946,63 @@ Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction(
return load;
}
Node* WasmGraphBuilder::BuildI64CcallConvertFloat(
Node* input, wasm::WasmCodePosition position, NumericImplementation impl,
Type int_ty, Type float_ty, ExternalReference call_ref) {
const MachineType result_type = MachineType::Int64();
MachineRepresentation parameter_representation = // make local
MachineRepresentation::kFloat32;
Node* stack_slot_param = graph()->NewNode(
jsgraph()->machine()->StackSlot(parameter_representation));
Node* stack_slot_result = graph()->NewNode(
jsgraph()->machine()->StackSlot(result_type.representation()));
const Operator* store_op = jsgraph()->machine()->Store(
StoreRepresentation(parameter_representation, kNoWriteBarrier));
*effect_ =
graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0),
input, *effect_, *control_);
MachineSignature::Builder sig_builder(jsgraph()->zone(), 1, 2);
sig_builder.AddReturn(MachineType::Int32());
sig_builder.AddParam(MachineType::Pointer());
sig_builder.AddParam(MachineType::Pointer());
Node* function =
graph()->NewNode(jsgraph()->common()->ExternalConstant(call_ref));
Node* overflow = BuildCCall(sig_builder.Build(), function, stack_slot_param,
stack_slot_result);
switch (impl) {
case NumericImplementation::kTrap: {
ZeroCheck32(wasm::kTrapFloatUnrepresentable, overflow, position);
const Operator* load_op = jsgraph()->machine()->Load(result_type);
Node* load =
graph()->NewNode(load_op, stack_slot_result,
jsgraph()->Int32Constant(0), *effect_, *control_);
*effect_ = load;
return load;
}
case NumericImplementation::kSaturate: {
Node* test =
Binop(wasm::kExprI32Eq, overflow, Int32Constant(0), position);
Diamond tl_d(graph(), jsgraph()->common(), test, BranchHint::kFalse);
tl_d.Chain(Control());
Node* nan_test = Binop(ne_op(float_ty), input, input);
Diamond nan_d(graph(), jsgraph()->common(), nan_test, BranchHint::kFalse);
nan_d.Nest(tl_d, true);
Node* neg_test = Binop(lt_op(float_ty), input, zero(this, float_ty));
Diamond sat_d(graph(), jsgraph()->common(), neg_test, BranchHint::kNone);
sat_d.Nest(nan_d, false);
Node* sat_val =
sat_d.Phi(word_rep(int_ty), min(this, int_ty), max(this, int_ty));
const Operator* load_op = jsgraph()->machine()->Load(result_type);
Node* load =
graph()->NewNode(load_op, stack_slot_result,
jsgraph()->Int32Constant(0), *effect_, *control_);
Node* nan_val = nan_d.Phi(word_rep(int_ty), zero(this, int_ty), sat_val);
return tl_d.Phi(word_rep(int_ty), nan_val, load);
}
}
UNREACHABLE();
}
Node* WasmGraphBuilder::GrowMemory(Node* input) {
SetNeedsStackCheck();
Diamond check_input_range(
......
......@@ -255,8 +255,16 @@ typedef ZoneVector<Node*> NodeVector;
class WasmGraphBuilder {
public:
enum EnforceBoundsCheck : bool { kNeedsBoundsCheck, kCanOmitBoundsCheck };
struct IntConvertOps;
struct FloatConvertOps;
enum class Type {
kInt8s,
kInt8u,
kInt32s,
kInt32u,
kInt64s,
kInt64u,
kFloat32,
kFloat64
};
WasmGraphBuilder(ModuleEnv* env, Zone* zone, JSGraph* graph,
Handle<Code> centry_stub, wasm::FunctionSig* sig,
......@@ -453,6 +461,7 @@ class WasmGraphBuilder {
private:
enum class NumericImplementation : uint8_t { kTrap, kSaturate };
static const int kDefaultBufferSize = 16;
Zone* const zone_;
......@@ -515,16 +524,10 @@ class WasmGraphBuilder {
Node* BuildF32CopySign(Node* left, Node* right);
Node* BuildF64CopySign(Node* left, Node* right);
Node* BuildI32ConvertOp(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl, const Operator* op,
wasm::WasmOpcode check_op,
const IntConvertOps* int_ops,
const FloatConvertOps* float_ops);
Node* BuildConvertCheck(Node* test, Node* result, Node* input,
wasm::WasmCodePosition position,
NumericImplementation impl,
const IntConvertOps* int_ops,
const FloatConvertOps* float_ops);
Node* BuildI32ConvertFloat(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl, Type int_type,
Type float_type, const Operator* conv_op,
const wasm::WasmOpcode check_op);
Node* BuildI32SConvertF32(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl);
Node* BuildI32SConvertF64(Node* input, wasm::WasmCodePosition position,
......@@ -571,7 +574,15 @@ class WasmGraphBuilder {
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type, wasm::WasmCodePosition position);
Node* BuildI64SConvertF32(Node* input, wasm::WasmCodePosition position);
Node* BuildI64CcallConvertFloat(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl, Type int_type,
Type float_type, ExternalReference call_ref);
Node* BuildI64TruncConvertFloat(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl, Type int_type,
Type float_type, const Operator* trunc_op);
Node* BuildI64SConvertF32(Node* input, wasm::WasmCodePosition position,
NumericImplementation impl);
Node* BuildI64UConvertF32(Node* input, wasm::WasmCodePosition position);
Node* BuildI64SConvertF64(Node* input, wasm::WasmCodePosition position);
Node* BuildI64UConvertF64(Node* input, wasm::WasmCodePosition position);
......
......@@ -477,6 +477,17 @@ int64_t ExecuteI64SConvertF32(float a, TrapReason* trap) {
return output;
}
int64_t ExecuteI64SConvertSatF32(float a) {
TrapReason base_trap = kTrapCount;
int64_t val = ExecuteI64SConvertF32(a, &base_trap);
if (base_trap == kTrapCount) {
return val;
}
return std::isnan(a) ? 0
: (a < 0.0 ? std::numeric_limits<int64_t>::min()
: std::numeric_limits<int64_t>::max());
}
int64_t ExecuteI64SConvertF64(double a, TrapReason* trap) {
int64_t output;
if (!float64_to_int64_wrapper(&a, &output)) {
......@@ -1565,6 +1576,10 @@ class ThreadImpl {
case kExprI32UConvertSatF64:
Push(WasmValue(ExecuteConvertSaturate<uint32_t>(Pop().to<double>())));
return true;
case kExprI64SConvertSatF32: {
Push(WasmValue(ExecuteI64SConvertSatF32(Pop().to<float>())));
return true;
}
default:
V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
code->start[pc], OpcodeName(code->start[pc]));
......
......@@ -101,9 +101,10 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_I32_OP(ConvertI64, "wrap/i64")
CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
// TODO(kschimpf): Add I64 versions of saturating conversions.
// TODO(kschimpf): Simplify after filling in other saturating operations.
CASE_CONVERT_SAT_OP(Convert, I32, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I32, F64, "f64", "trunc")
CASE_I64_OP(SConvertSatF32, "trunc_s::sat/f32")
CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend")
CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert")
......
......@@ -408,8 +408,9 @@ using WasmName = Vector<const char>;
V(I32SConvertSatF32, 0xfc00, i_f) \
V(I32UConvertSatF32, 0xfc01, i_f) \
V(I32SConvertSatF64, 0xfc02, i_d) \
V(I32UConvertSatF64, 0xfc03, i_d)
// TODO(kschimpf): Add remaining i64 numeric opcodes.
V(I32UConvertSatF64, 0xfc03, i_d) \
V(I64SConvertSatF32, 0xfc04, l_f)
// TODO(kschimpf): Add remaining numeric opcodes.
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicLoad, 0xfe10, i_i) \
......
......@@ -73,6 +73,7 @@ namespace test_run_wasm_64 {
V(I64SConvertF64, true) \
V(I64UConvertF32, true) \
V(I64UConvertF64, true) \
V(I64SConvertSatF32, true) \
V(I64SConvertI32, true) \
V(I64UConvertI32, true) \
V(F32SConvertI64, true) \
......@@ -848,6 +849,27 @@ WASM_EXEC_TEST(I64SConvertF32a) {
}
}
WASM_EXEC_TEST(I64SConvertSatF32a) {
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
WasmRunner<int64_t, float> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_SAT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
int64_t expected;
if (*i < static_cast<float>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<float>(std::numeric_limits<int64_t>::min())) {
expected = static_cast<int64_t>(*i);
} else if (std::isnan(*i)) {
expected = static_cast<int64_t>(0);
} else if (*i < 0.0) {
expected = std::numeric_limits<int64_t>::min();
} else {
expected = std::numeric_limits<int64_t>::max();
}
int64_t found = r.Call(*i);
CHECK_EQ(expected, found);
}
}
WASM_EXEC_TEST(I64SConvertF64a) {
WasmRunner<int64_t, double> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0)));
......@@ -1280,6 +1302,29 @@ WASM_EXEC_TEST(I64SConvertF32b) {
}
}
WASM_EXEC_TEST(I64SConvertSatF32b) {
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
REQUIRE(I64SConvertSatF32);
WasmRunner<int64_t, float> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_SAT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
int64_t expected;
if (*i < static_cast<float>(INT64_MAX) &&
*i >= static_cast<float>(INT64_MIN)) {
expected = static_cast<int64_t>(*i);
} else if (std::isnan(*i)) {
expected = static_cast<int64_t>(0);
} else if (*i < 0.0) {
expected = INT64_MIN;
} else {
expected = INT64_MAX;
}
int64_t found = r.Call(*i);
CHECK_EQ(expected, found);
}
}
WASM_EXEC_TEST(I64SConvertF64b) {
REQUIRE(I64SConvertF64);
WasmRunner<int64_t, double> r(execution_mode);
......
......@@ -564,6 +564,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_I32_UCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI32UConvertSatF32)
#define WASM_I32_SCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32SConvertSatF64)
#define WASM_I32_UCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32UConvertSatF64)
#define WASM_I64_SCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI64SConvertSatF32)
//------------------------------------------------------------------------------
// Memory Operations.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment