Skip to content

Commit

Permalink
[wasm][turboshaft] Int64Lowering: Support conversions from float to i64
Browse files Browse the repository at this point in the history
- i64.reinterpret_f64
- i64.trunc_f32_s / i64.trunc_f32_u
- i64.trunc_f64_s / i64.trunc_f64_u
- i64.trunc_sat_f32_s / i64.trunc_sat_f32_u
- i64.trunc_sat_f64_s / i64.trunc_sat_f64_u

Bug: v8:14108
Change-Id: I3032d58322b544e58f037ca31e13e0c6795e5bdd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4684070
Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Darius Mercadier <dmercadier@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#89003}
  • Loading branch information
Liedtke authored and V8 LUCI CQ committed Jul 18, 2023
1 parent 98000e1 commit 4fe718d
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 14 deletions.
60 changes: 55 additions & 5 deletions src/compiler/turboshaft/int64-lowering-reducer.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,16 +271,48 @@ class Int64LoweringReducer : public Next {
ChangeOp::Assumption assumption,
RegisterRepresentation from,
RegisterRepresentation to) {
auto word32 = RegisterRepresentation::Word32();
auto word64 = RegisterRepresentation::Word64();
auto float64 = RegisterRepresentation::Float64();
using Kind = ChangeOp::Kind;
// TODO(mliedtke): Also support other conversions.
if (from == RegisterRepresentation::Word32() &&
to == RegisterRepresentation::Word64() &&
kind == ChangeOp::Kind::kZeroExtend) {
// Convert uint32 to uint64.
return __ Tuple(input, __ Word32Constant(0));
if (from == word32 && to == word64) {
if (kind == Kind::kZeroExtend) {
return __ Tuple(input, __ Word32Constant(0));
}
if (kind == Kind::kSignExtend) {
// We use SAR to preserve the sign in the high word.
return __ Tuple(input, __ Word32ShiftRightArithmetic(input, 31));
}
UNIMPLEMENTED();
}
if (from == float64 && to == word64) {
if (kind == Kind::kBitcast) {
return __ Tuple(__ Float64ExtractLowWord32(input),
__ Float64ExtractHighWord32(input));
}
UNIMPLEMENTED();
}
return Next::ReduceChange(input, kind, assumption, from, to);
}

OpIndex REDUCE(Load)(OpIndex base_idx, OpIndex index, LoadOp::Kind kind,
MemoryRepresentation loaded_rep,
RegisterRepresentation result_rep, int32_t offset,
uint8_t element_scale) {
if (loaded_rep == MemoryRepresentation::Int64()) {
return __ Tuple(
Next::ReduceLoad(base_idx, index, kind, MemoryRepresentation::Int32(),
RegisterRepresentation::Word32(), offset,
element_scale),
Next::ReduceLoad(base_idx, index, kind, MemoryRepresentation::Int32(),
RegisterRepresentation::Word32(),
offset + sizeof(int32_t), element_scale));
}
return Next::ReduceLoad(base_idx, index, kind, loaded_rep, result_rep,
offset, element_scale);
}

OpIndex REDUCE(Store)(OpIndex base, OpIndex index, OpIndex value,
StoreOp::Kind kind, MemoryRepresentation stored_rep,
WriteBarrierKind write_barrier, int32_t offset,
Expand All @@ -303,6 +335,24 @@ class Int64LoweringReducer : public Next {
maybe_initializing_or_transitioning);
}

OpIndex REDUCE(Phi)(base::Vector<const OpIndex> inputs,
RegisterRepresentation rep) {
if (rep == RegisterRepresentation::Word64()) {
base::SmallVector<OpIndex, 8> inputs_low;
base::SmallVector<OpIndex, 8> inputs_high;
auto word32 = RegisterRepresentation::Word32();
inputs_low.reserve_no_init(inputs.size());
inputs_high.reserve_no_init(inputs.size());
for (OpIndex input : inputs) {
inputs_low.push_back(__ Projection(input, 0, word32));
inputs_high.push_back(__ Projection(input, 1, word32));
}
return __ Tuple(Next::ReducePhi(base::VectorOf(inputs_low), word32),
Next::ReducePhi(base::VectorOf(inputs_high), word32));
}
return Next::ReducePhi(inputs, rep);
}

private:
bool CheckPairOrPairOp(OpIndex input) {
if (const TupleOp* tuple = Asm().template TryCast<TupleOp>(input)) {
Expand Down
1 change: 1 addition & 0 deletions src/wasm/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ specific_include_rules = {
"+src/compiler/turboshaft/define-assembler-macros.inc",
"+src/compiler/turboshaft/graph.h",
"+src/compiler/turboshaft/undef-assembler-macros.inc",
"+src/compiler/turboshaft/variable-reducer.h",
],
"wasm-engine\.h": [
# The WasmEngine may cache common call descriptors.
Expand Down
124 changes: 115 additions & 9 deletions src/wasm/turboshaft-graph-interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "src/compiler/turboshaft/assembler.h"
#include "src/compiler/turboshaft/graph.h"
#include "src/compiler/turboshaft/variable-reducer.h"
#include "src/compiler/wasm-compiler-definitions.h"
#include "src/wasm/compilation-environment.h"
#include "src/wasm/function-body-decoder-impl.h"
Expand All @@ -21,7 +22,9 @@ namespace v8::internal::wasm {
#include "src/compiler/turboshaft/define-assembler-macros.inc"

using Assembler =
compiler::turboshaft::Assembler<compiler::turboshaft::reducer_list<>>;
compiler::turboshaft::Assembler<compiler::turboshaft::reducer_list<
compiler::turboshaft::VariableReducer,
compiler::turboshaft::RequiredOptimizationReducer>>;
using compiler::CallDescriptor;
using compiler::LinkageLocation;
using compiler::LocationSignature;
Expand Down Expand Up @@ -1404,6 +1407,73 @@ class TurboshaftGraphBuildingInterface {
return result;
}

std::pair<OpIndex, OpIndex> BuildCCallForFloatConversion(
OpIndex arg, MemoryRepresentation float_type,
ExternalReference ccall_ref) {
uint8_t slot_size = MemoryRepresentation::Int64().SizeInBytes();
OpIndex stack_slot = __ StackSlot(slot_size, slot_size);
__ Store(stack_slot, arg, StoreOp::Kind::RawAligned(), float_type,
compiler::WriteBarrierKind::kNoWriteBarrier);
MachineType reps[]{MachineType::Int32(), MachineType::Pointer()};
MachineSignature sig(1, 1, reps);
OpIndex overflow = CallC(&sig, ccall_ref, &stack_slot);
return {stack_slot, overflow};
}

OpIndex BuildCcallConvertFloat(OpIndex arg, MemoryRepresentation float_type,
ExternalReference ccall_ref) {
auto [stack_slot, overflow] =
BuildCCallForFloatConversion(arg, float_type, ccall_ref);
__ TrapIf(__ Word32Equal(overflow, 0), OpIndex::Invalid(),
compiler::TrapId::kTrapFloatUnrepresentable);
MemoryRepresentation int64 = MemoryRepresentation::Int64();
return __ Load(stack_slot, LoadOp::Kind::RawAligned(), int64);
}

OpIndex BuildCcallConvertFloatSat(OpIndex arg,
MemoryRepresentation float_type,
ExternalReference ccall_ref,
bool is_signed) {
auto [stack_slot, overflow] =
BuildCCallForFloatConversion(arg, float_type, ccall_ref);
Assembler::ScopedVar<Word64> result(Asm());
// TODO(mliedtke): This is quite complicated code for handling exceptional
// cases. Wouldn't it be better to call the corresponding [...]_sat C
// function and let it be handled there?
IF (UNLIKELY(__ Word32Equal(overflow, 0))) {
OpIndex is_not_nan = float_type == MemoryRepresentation::Float32()
? __ Float32Equal(arg, arg)
: __ Float64Equal(arg, arg);
OpIndex is_nan = __ Word32Equal(is_not_nan, 0);
IF (UNLIKELY(is_nan)) {
result = __ Word64Constant(uint64_t{0});
}
ELSE {
OpIndex less_than_zero = float_type == MemoryRepresentation::Float32()
? __ Float32LessThan(arg, 0)
: __ Float64LessThan(arg, 0);
IF (less_than_zero) {
result = __ Word64Constant(
is_signed ? std::numeric_limits<int64_t>::min()
: std::numeric_limits<uint64_t>::min());
}
ELSE {
result = __ Word64Constant(
is_signed ? std::numeric_limits<int64_t>::max()
: std::numeric_limits<uint64_t>::max());
}
END_IF
}
END_IF
}
ELSE {
MemoryRepresentation int64 = MemoryRepresentation::Int64();
result = __ Load(stack_slot, LoadOp::Kind::RawAligned(), int64);
}
END_IF
return *result;
}

// TODO(14108): Remove the decoder argument once we have no bailouts.
OpIndex UnOpImpl(FullDecoder* decoder, WasmOpcode opcode, OpIndex arg,
ValueType input_type /* for ref.is_null only*/) {
Expand Down Expand Up @@ -1460,17 +1530,29 @@ class TurboshaftGraphBuildingInterface {
return result;
}
case kExprI64SConvertF32:
return ExtractTruncationProjections(
asm_.TryTruncateFloat32ToInt64(arg));
return Is64() ? ExtractTruncationProjections(
asm_.TryTruncateFloat32ToInt64(arg))
: BuildCcallConvertFloat(
arg, MemoryRepresentation::Float32(),
ExternalReference::wasm_float32_to_int64());
case kExprI64UConvertF32:
return ExtractTruncationProjections(
asm_.TryTruncateFloat32ToUint64(arg));
return Is64() ? ExtractTruncationProjections(
asm_.TryTruncateFloat32ToUint64(arg))
: BuildCcallConvertFloat(
arg, MemoryRepresentation::Float32(),
ExternalReference::wasm_float32_to_uint64());
case kExprI64SConvertF64:
return ExtractTruncationProjections(
asm_.TryTruncateFloat64ToInt64(arg));
return Is64() ? ExtractTruncationProjections(
asm_.TryTruncateFloat64ToInt64(arg))
: BuildCcallConvertFloat(
arg, MemoryRepresentation::Float64(),
ExternalReference::wasm_float64_to_int64());
case kExprI64UConvertF64:
return ExtractTruncationProjections(
asm_.TryTruncateFloat64ToUint64(arg));
return Is64() ? ExtractTruncationProjections(
asm_.TryTruncateFloat64ToUint64(arg))
: BuildCcallConvertFloat(
arg, MemoryRepresentation::Float64(),
ExternalReference::wasm_float64_to_uint64());
case kExprF64SConvertI32:
return asm_.ChangeInt32ToFloat64(arg);
case kExprF64UConvertI32:
Expand Down Expand Up @@ -1630,6 +1712,12 @@ class TurboshaftGraphBuildingInterface {
return result;
}
case kExprI64SConvertSatF32: {
if (!Is64()) {
bool is_signed = true;
return BuildCcallConvertFloatSat(
arg, MemoryRepresentation::Float32(),
ExternalReference::wasm_float32_to_int64(), is_signed);
}
OpIndex converted = asm_.TryTruncateFloat32ToInt64(arg);
Label<compiler::turboshaft::Word64> done(&asm_);

Expand Down Expand Up @@ -1670,6 +1758,12 @@ class TurboshaftGraphBuildingInterface {
return result;
}
case kExprI64UConvertSatF32: {
if (!Is64()) {
bool is_signed = false;
return BuildCcallConvertFloatSat(
arg, MemoryRepresentation::Float32(),
ExternalReference::wasm_float32_to_uint64(), is_signed);
}
OpIndex converted = asm_.TryTruncateFloat32ToUint64(arg);
Label<compiler::turboshaft::Word64> done(&asm_);

Expand Down Expand Up @@ -1710,6 +1804,12 @@ class TurboshaftGraphBuildingInterface {
return result;
}
case kExprI64SConvertSatF64: {
if (!Is64()) {
bool is_signed = true;
return BuildCcallConvertFloatSat(
arg, MemoryRepresentation::Float64(),
ExternalReference::wasm_float64_to_int64(), is_signed);
}
OpIndex converted = asm_.TryTruncateFloat64ToInt64(arg);
Label<compiler::turboshaft::Word64> done(&asm_);

Expand Down Expand Up @@ -1751,6 +1851,12 @@ class TurboshaftGraphBuildingInterface {
return result;
}
case kExprI64UConvertSatF64: {
if (!Is64()) {
bool is_signed = false;
return BuildCcallConvertFloatSat(
arg, MemoryRepresentation::Float64(),
ExternalReference::wasm_float64_to_uint64(), is_signed);
}
OpIndex converted = asm_.TryTruncateFloat64ToUint64(arg);
Label<compiler::turboshaft::Word64> done(&asm_);

Expand Down
Loading

0 comments on commit 4fe718d

Please sign in to comment.