v8/test/unittests/compiler/machine-operator-unittest.cc
Manos Koukoutos 02ac71e256 [turbofan] Disallow floating control in wasm
Loop unrolling did not work properly with floating control. Seeing as
very few spots in the wasm compiler introduced floating control, we
decided to disallow it altogether.
Changes:
- When lowering 64-bit rol/ror/clz/ctz in 32-bit platforms, we use a
  diamond operator, which used to introduce floating control. This CL
  adds a control edge to these operators so that the diamond can be
  chained to that control instead.
- During loop analysis, as an additional safety check, we check that the
  explored loop does not have floating control. Exceptionally, floating
  control pointing directly do start() is allowed.
- Change wasm-compiler so that generated floating projections point to
  start() even after stack check patch-in.

Bug: chromium:1184929, v8:11298
Change-Id: I1ee063f5250037ae6c84d2f16b0bd8fff3923117
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2876851
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74527}
2021-05-12 15:26:33 +00:00

400 lines
16 KiB
C++

// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/machine-operator.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/compiler/operator-properties.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace machine_operator_unittest {
template <typename T>
class MachineOperatorTestWithParam
: public TestWithZone,
public ::testing::WithParamInterface<
::testing::tuple<MachineRepresentation, T> > {
protected:
MachineRepresentation representation() const {
return ::testing::get<0>(B::GetParam());
}
const T& GetParam() const { return ::testing::get<1>(B::GetParam()); }
private:
using B = ::testing::WithParamInterface<
::testing::tuple<MachineRepresentation, T> >;
};
const MachineRepresentation kMachineReps[] = {MachineRepresentation::kWord32,
MachineRepresentation::kWord64};
const MachineType kMachineTypesForAccess[] = {
MachineType::Float32(), MachineType::Float64(), MachineType::Int8(),
MachineType::Uint8(), MachineType::Int16(), MachineType::Uint16(),
MachineType::Int32(), MachineType::Uint32(), MachineType::Int64(),
MachineType::Uint64(), MachineType::AnyTagged()};
const MachineRepresentation kRepresentationsForStore[] = {
MachineRepresentation::kFloat32, MachineRepresentation::kFloat64,
MachineRepresentation::kWord8, MachineRepresentation::kWord16,
MachineRepresentation::kWord32, MachineRepresentation::kWord64,
MachineRepresentation::kTagged};
// -----------------------------------------------------------------------------
// Load operator.
using MachineLoadOperatorTest =
MachineOperatorTestWithParam<LoadRepresentation>;
TEST_P(MachineLoadOperatorTest, InstancesAreGloballyShared) {
MachineOperatorBuilder machine1(zone(), representation());
MachineOperatorBuilder machine2(zone(), representation());
EXPECT_EQ(machine1.Load(GetParam()), machine2.Load(GetParam()));
}
TEST_P(MachineLoadOperatorTest, NumberOfInputsAndOutputs) {
MachineOperatorBuilder machine(zone(), representation());
const Operator* op = machine.Load(GetParam());
EXPECT_EQ(2, op->ValueInputCount());
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount());
EXPECT_EQ(4, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(1, op->ValueOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
}
TEST_P(MachineLoadOperatorTest, OpcodeIsCorrect) {
MachineOperatorBuilder machine(zone(), representation());
EXPECT_EQ(IrOpcode::kLoad, machine.Load(GetParam())->opcode());
}
TEST_P(MachineLoadOperatorTest, ParameterIsCorrect) {
MachineOperatorBuilder machine(zone(), representation());
EXPECT_EQ(GetParam(), LoadRepresentationOf(machine.Load(GetParam())));
}
INSTANTIATE_TEST_SUITE_P(
MachineOperatorTest, MachineLoadOperatorTest,
::testing::Combine(::testing::ValuesIn(kMachineReps),
::testing::ValuesIn(kMachineTypesForAccess)));
// -----------------------------------------------------------------------------
// Store operator.
class MachineStoreOperatorTest
: public MachineOperatorTestWithParam<
::testing::tuple<MachineRepresentation, WriteBarrierKind> > {
protected:
StoreRepresentation GetParam() const {
return StoreRepresentation(
::testing::get<0>(
MachineOperatorTestWithParam< ::testing::tuple<
MachineRepresentation, WriteBarrierKind> >::GetParam()),
::testing::get<1>(
MachineOperatorTestWithParam< ::testing::tuple<
MachineRepresentation, WriteBarrierKind> >::GetParam()));
}
};
TEST_P(MachineStoreOperatorTest, InstancesAreGloballyShared) {
MachineOperatorBuilder machine1(zone(), representation());
MachineOperatorBuilder machine2(zone(), representation());
EXPECT_EQ(machine1.Store(GetParam()), machine2.Store(GetParam()));
}
TEST_P(MachineStoreOperatorTest, NumberOfInputsAndOutputs) {
MachineOperatorBuilder machine(zone(), representation());
const Operator* op = machine.Store(GetParam());
EXPECT_EQ(3, op->ValueInputCount());
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount());
EXPECT_EQ(5, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ValueOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
}
TEST_P(MachineStoreOperatorTest, OpcodeIsCorrect) {
MachineOperatorBuilder machine(zone(), representation());
EXPECT_EQ(IrOpcode::kStore, machine.Store(GetParam())->opcode());
}
TEST_P(MachineStoreOperatorTest, ParameterIsCorrect) {
MachineOperatorBuilder machine(zone(), representation());
EXPECT_EQ(GetParam(), StoreRepresentationOf(machine.Store(GetParam())));
}
INSTANTIATE_TEST_SUITE_P(
MachineOperatorTest, MachineStoreOperatorTest,
::testing::Combine(
::testing::ValuesIn(kMachineReps),
::testing::Combine(::testing::ValuesIn(kRepresentationsForStore),
::testing::Values(kNoWriteBarrier,
kFullWriteBarrier))));
// -----------------------------------------------------------------------------
// Pure operators.
struct PureOperator {
const Operator* (MachineOperatorBuilder::*constructor)();
char const* const constructor_name;
int value_input_count;
int control_input_count;
int value_output_count;
};
std::ostream& operator<<(std::ostream& os, PureOperator const& pop) {
return os << pop.constructor_name;
}
const PureOperator kPureOperators[] = {
#define PURE(Name, value_input_count, control_input_count, value_output_count) \
{ \
&MachineOperatorBuilder::Name, #Name, value_input_count, \
control_input_count, value_output_count \
}
PURE(Word32And, 2, 0, 1), // --
PURE(Word32Or, 2, 0, 1), // --
PURE(Word32Xor, 2, 0, 1), // --
PURE(Word32Shl, 2, 0, 1), // --
PURE(Word32Shr, 2, 0, 1), // --
PURE(Word32Sar, 2, 0, 1), // --
PURE(Word32Ror, 2, 0, 1), // --
PURE(Word32Equal, 2, 0, 1), // --
PURE(Word32Clz, 1, 0, 1), // --
PURE(Word64And, 2, 0, 1), // --
PURE(Word64Or, 2, 0, 1), // --
PURE(Word64Xor, 2, 0, 1), // --
PURE(Word64Shl, 2, 0, 1), // --
PURE(Word64Shr, 2, 0, 1), // --
PURE(Word64Sar, 2, 0, 1), // --
PURE(Word64Ror, 2, 0, 1), // --
PURE(Word64RorLowerable, 2, 1, 1), // --
PURE(Word64Equal, 2, 0, 1), // --
PURE(Int32Add, 2, 0, 1), // --
PURE(Int32Sub, 2, 0, 1), // --
PURE(Int32Mul, 2, 0, 1), // --
PURE(Int32MulHigh, 2, 0, 1), // --
PURE(Int32Div, 2, 1, 1), // --
PURE(Uint32Div, 2, 1, 1), // --
PURE(Int32Mod, 2, 1, 1), // --
PURE(Uint32Mod, 2, 1, 1), // --
PURE(Int32LessThan, 2, 0, 1), // --
PURE(Int32LessThanOrEqual, 2, 0, 1), // --
PURE(Uint32LessThan, 2, 0, 1), // --
PURE(Uint32LessThanOrEqual, 2, 0, 1), // --
PURE(Int64Add, 2, 0, 1), // --
PURE(Int64Sub, 2, 0, 1), // --
PURE(Int64Mul, 2, 0, 1), // --
PURE(Int64Div, 2, 1, 1), // --
PURE(Uint64Div, 2, 1, 1), // --
PURE(Int64Mod, 2, 1, 1), // --
PURE(Uint64Mod, 2, 1, 1), // --
PURE(Int64LessThan, 2, 0, 1), // --
PURE(Int64LessThanOrEqual, 2, 0, 1), // --
PURE(Uint64LessThan, 2, 0, 1), // --
PURE(Uint64LessThanOrEqual, 2, 0, 1), // --
PURE(ChangeFloat32ToFloat64, 1, 0, 1), // --
PURE(ChangeFloat64ToInt32, 1, 0, 1), // --
PURE(ChangeFloat64ToUint32, 1, 0, 1), // --
PURE(ChangeInt32ToInt64, 1, 0, 1), // --
PURE(ChangeUint32ToFloat64, 1, 0, 1), // --
PURE(ChangeUint32ToUint64, 1, 0, 1), // --
PURE(TruncateFloat64ToFloat32, 1, 0, 1), // --
PURE(TruncateInt64ToInt32, 1, 0, 1), // --
PURE(Float32Abs, 1, 0, 1), // --
PURE(Float32Add, 2, 0, 1), // --
PURE(Float32Sub, 2, 0, 1), // --
PURE(Float32Mul, 2, 0, 1), // --
PURE(Float32Div, 2, 0, 1), // --
PURE(Float32Sqrt, 1, 0, 1), // --
PURE(Float32Equal, 2, 0, 1), // --
PURE(Float32LessThan, 2, 0, 1), // --
PURE(Float32LessThanOrEqual, 2, 0, 1), // --
PURE(Float32Neg, 1, 0, 1), // --
PURE(Float64Abs, 1, 0, 1), // --
PURE(Float64Add, 2, 0, 1), // --
PURE(Float64Sub, 2, 0, 1), // --
PURE(Float64Mul, 2, 0, 1), // --
PURE(Float64Div, 2, 0, 1), // --
PURE(Float64Mod, 2, 0, 1), // --
PURE(Float64Sqrt, 1, 0, 1), // --
PURE(Float64Max, 2, 0, 1), // --
PURE(Float64Min, 2, 0, 1), // --
PURE(Float64Equal, 2, 0, 1), // --
PURE(Float64LessThan, 2, 0, 1), // --
PURE(Float64LessThanOrEqual, 2, 0, 1), // --
PURE(Float64ExtractLowWord32, 1, 0, 1), // --
PURE(Float64ExtractHighWord32, 1, 0, 1), // --
PURE(Float64InsertLowWord32, 2, 0, 1), // --
PURE(Float64InsertHighWord32, 2, 0, 1), // --
PURE(Float64Neg, 1, 0, 1), // --
#undef PURE
};
class MachinePureOperatorTest : public TestWithZone {
protected:
MachineRepresentation word_type() {
return MachineType::PointerRepresentation();
}
};
TEST_F(MachinePureOperatorTest, PureOperators) {
TRACED_FOREACH(MachineRepresentation, machine_rep1, kMachineReps) {
MachineOperatorBuilder machine1(zone(), machine_rep1);
TRACED_FOREACH(MachineRepresentation, machine_rep2, kMachineReps) {
MachineOperatorBuilder machine2(zone(), machine_rep2);
TRACED_FOREACH(PureOperator, pop, kPureOperators) {
const Operator* op1 = (machine1.*pop.constructor)();
const Operator* op2 = (machine2.*pop.constructor)();
EXPECT_EQ(op1, op2);
EXPECT_EQ(pop.value_input_count, op1->ValueInputCount());
EXPECT_EQ(pop.control_input_count, op1->ControlInputCount());
EXPECT_EQ(pop.value_output_count, op1->ValueOutputCount());
}
}
}
}
// Optional operators.
struct OptionalOperatorEntry {
const OptionalOperator (MachineOperatorBuilder::*constructor)();
MachineOperatorBuilder::Flag enabling_flag;
char const* const constructor_name;
int value_input_count;
int control_input_count;
int value_output_count;
};
std::ostream& operator<<(std::ostream& os, OptionalOperatorEntry const& pop) {
return os << pop.constructor_name;
}
const OptionalOperatorEntry kOptionalOperators[] = {
#define OPTIONAL_ENTRY(Name, value_input_count, control_input_count, \
value_output_count) \
{ \
&MachineOperatorBuilder::Name, MachineOperatorBuilder::k##Name, #Name, \
value_input_count, control_input_count, value_output_count \
}
OPTIONAL_ENTRY(Float64RoundDown, 1, 0, 1), // --
OPTIONAL_ENTRY(Float64RoundTruncate, 1, 0, 1), // --
OPTIONAL_ENTRY(Float64RoundTiesAway, 1, 0, 1), // --
OPTIONAL_ENTRY(Float64Select, 3, 0, 1), // --
OPTIONAL_ENTRY(Float32Select, 3, 0, 1), // --
OPTIONAL_ENTRY(Word32Select, 3, 0, 1), // --
OPTIONAL_ENTRY(Word64Select, 3, 0, 1), // --
#undef OPTIONAL_ENTRY
};
class MachineOptionalOperatorTest : public TestWithZone {
protected:
MachineRepresentation word_rep() {
return MachineType::PointerRepresentation();
}
};
TEST_F(MachineOptionalOperatorTest, OptionalOperators) {
TRACED_FOREACH(OptionalOperatorEntry, pop, kOptionalOperators) {
TRACED_FOREACH(MachineRepresentation, machine_rep1, kMachineReps) {
MachineOperatorBuilder machine1(zone(), machine_rep1, pop.enabling_flag);
TRACED_FOREACH(MachineRepresentation, machine_rep2, kMachineReps) {
MachineOperatorBuilder machine2(zone(), machine_rep2,
pop.enabling_flag);
const Operator* op1 = (machine1.*pop.constructor)().op();
const Operator* op2 = (machine2.*pop.constructor)().op();
EXPECT_EQ(op1, op2);
EXPECT_EQ(pop.value_input_count, op1->ValueInputCount());
EXPECT_EQ(pop.control_input_count, op1->ControlInputCount());
EXPECT_EQ(pop.value_output_count, op1->ValueOutputCount());
MachineOperatorBuilder machine3(zone(), word_rep());
EXPECT_TRUE((machine1.*pop.constructor)().IsSupported());
EXPECT_FALSE((machine3.*pop.constructor)().IsSupported());
}
}
}
}
// -----------------------------------------------------------------------------
// Pseudo operators.
using MachineOperatorTest = TestWithZone;
TEST_F(MachineOperatorTest, PseudoOperatorsWhenWordSizeIs32Bit) {
MachineOperatorBuilder machine(zone(), MachineRepresentation::kWord32);
EXPECT_EQ(machine.Word32And(), machine.WordAnd());
EXPECT_EQ(machine.Word32Or(), machine.WordOr());
EXPECT_EQ(machine.Word32Xor(), machine.WordXor());
EXPECT_EQ(machine.Word32Shl(), machine.WordShl());
EXPECT_EQ(machine.Word32Shr(), machine.WordShr());
EXPECT_EQ(machine.Word32Sar(), machine.WordSar());
EXPECT_EQ(machine.Word32Ror(), machine.WordRor());
EXPECT_EQ(machine.Word32Equal(), machine.WordEqual());
EXPECT_EQ(machine.Int32Add(), machine.IntAdd());
EXPECT_EQ(machine.Int32Sub(), machine.IntSub());
EXPECT_EQ(machine.Int32Mul(), machine.IntMul());
EXPECT_EQ(machine.Int32Div(), machine.IntDiv());
EXPECT_EQ(machine.Uint32Div(), machine.UintDiv());
EXPECT_EQ(machine.Int32Mod(), machine.IntMod());
EXPECT_EQ(machine.Uint32Mod(), machine.UintMod());
EXPECT_EQ(machine.Int32LessThan(), machine.IntLessThan());
EXPECT_EQ(machine.Int32LessThanOrEqual(), machine.IntLessThanOrEqual());
}
TEST_F(MachineOperatorTest, PseudoOperatorsWhenWordSizeIs64Bit) {
MachineOperatorBuilder machine(zone(), MachineRepresentation::kWord64);
EXPECT_EQ(machine.Word64And(), machine.WordAnd());
EXPECT_EQ(machine.Word64Or(), machine.WordOr());
EXPECT_EQ(machine.Word64Xor(), machine.WordXor());
EXPECT_EQ(machine.Word64Shl(), machine.WordShl());
EXPECT_EQ(machine.Word64Shr(), machine.WordShr());
EXPECT_EQ(machine.Word64Sar(), machine.WordSar());
EXPECT_EQ(machine.Word64Ror(), machine.WordRor());
EXPECT_EQ(machine.Word64Equal(), machine.WordEqual());
EXPECT_EQ(machine.Int64Add(), machine.IntAdd());
EXPECT_EQ(machine.Int64Sub(), machine.IntSub());
EXPECT_EQ(machine.Int64Mul(), machine.IntMul());
EXPECT_EQ(machine.Int64Div(), machine.IntDiv());
EXPECT_EQ(machine.Uint64Div(), machine.UintDiv());
EXPECT_EQ(machine.Int64Mod(), machine.IntMod());
EXPECT_EQ(machine.Uint64Mod(), machine.UintMod());
EXPECT_EQ(machine.Int64LessThan(), machine.IntLessThan());
EXPECT_EQ(machine.Int64LessThanOrEqual(), machine.IntLessThanOrEqual());
}
} // namespace machine_operator_unittest
} // namespace compiler
} // namespace internal
} // namespace v8