[torque] Add support for variable offset loads to CSA Load Elimination

R=jarin@chromium.org, tebbi@chromium.org

Change-Id: Iec887aec4ae0fc477176a7431a1bd0de0775c060
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1645325
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62121}
This commit is contained in:
Georg Schmid 2019-06-12 13:30:05 +02:00 committed by Commit Bot
parent 98ba192d12
commit 45bf9d8fa9
7 changed files with 81 additions and 58 deletions

View File

@ -1952,8 +1952,14 @@ TNode<MaybeObject> CodeStubAssembler::LoadArrayElement(
parameter_mode, header_size);
CSA_ASSERT(this, IsOffsetInBounds(offset, LoadArrayLength(array),
array_header_size));
return UncheckedCast<MaybeObject>(
Load(MachineType::AnyTagged(), array, offset, needs_poisoning));
// TODO(gsps): Remove the Load case once LoadFromObject supports poisoning
if (needs_poisoning == LoadSensitivity::kSafe) {
return UncheckedCast<MaybeObject>(
LoadFromObject(MachineType::AnyTagged(), array, offset));
} else {
return UncheckedCast<MaybeObject>(
Load(MachineType::AnyTagged(), array, offset, needs_poisoning));
}
}
template TNode<MaybeObject>

View File

@ -889,15 +889,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
std::is_convertible<TNode<T>, TNode<Object>>::value,
int>::type = 0>
TNode<T> LoadReference(Reference reference) {
return CAST(LoadFromObject(MachineTypeOf<T>::value, reference.object,
reference.offset));
TNode<IntPtrT> offset =
IntPtrSub(reference.offset, IntPtrConstant(kHeapObjectTag));
return CAST(
LoadFromObject(MachineTypeOf<T>::value, reference.object, offset));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value,
int>::type = 0>
TNode<T> LoadReference(Reference reference) {
return UncheckedCast<T>(LoadFromObject(MachineTypeOf<T>::value,
reference.object, reference.offset));
TNode<IntPtrT> offset =
IntPtrSub(reference.offset, IntPtrConstant(kHeapObjectTag));
return UncheckedCast<T>(
LoadFromObject(MachineTypeOf<T>::value, reference.object, offset));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<Object>>::value,
@ -910,15 +914,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
} else if (std::is_same<T, Map>::value) {
write_barrier = StoreToObjectWriteBarrier::kMap;
}
StoreToObject(rep, reference.object, reference.offset, value,
write_barrier);
TNode<IntPtrT> offset =
IntPtrSub(reference.offset, IntPtrConstant(kHeapObjectTag));
StoreToObject(rep, reference.object, offset, value, write_barrier);
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value,
int>::type = 0>
void StoreReference(Reference reference, TNode<T> value) {
StoreToObject(MachineRepresentationOf<T>::value, reference.object,
reference.offset, value, StoreToObjectWriteBarrier::kNone);
TNode<IntPtrT> offset =
IntPtrSub(reference.offset, IntPtrConstant(kHeapObjectTag));
StoreToObject(MachineRepresentationOf<T>::value, reference.object, offset,
value, StoreToObjectWriteBarrier::kNone);
}
// Tag a smi and store it.

View File

@ -13,20 +13,6 @@ namespace v8 {
namespace internal {
namespace compiler {
namespace {
base::Optional<size_t> ConstantOffsetOf(Node* node) {
DCHECK(node->opcode() == IrOpcode::kLoadFromObject ||
node->opcode() == IrOpcode::kStoreToObject);
IntPtrMatcher m(NodeProperties::GetValueInput(node, 1));
if (m.HasValue()) {
return m.Value();
}
return {};
}
} // namespace
Reduction CsaLoadElimination::Reduce(Node* node) {
if (FLAG_trace_turbo_load_elimination) {
if (node->op()->EffectInputCount() > 0) {
@ -96,30 +82,30 @@ void CsaLoadElimination::AbstractState::Merge(AbstractState const* that,
}
CsaLoadElimination::AbstractState const*
CsaLoadElimination::AbstractState::AddField(Node* object, size_t offset,
CsaLoadElimination::AbstractState::AddField(Node* object, Node* offset,
CsaLoadElimination::FieldInfo info,
Zone* zone) const {
AbstractState* that = new (zone) AbstractState(*this);
that->field_infos_.Set({offset, object}, info);
that->field_infos_.Set({object, offset}, info);
return that;
}
CsaLoadElimination::FieldInfo CsaLoadElimination::AbstractState::Lookup(
Node* object, size_t offset) const {
Node* object, Node* offset) const {
if (object->IsDead()) {
return {};
}
return field_infos_.Get({offset, object});
return field_infos_.Get({object, offset});
}
void CsaLoadElimination::AbstractState::Print() const {
for (std::pair<Field, FieldInfo> entry : field_infos_) {
Field field = entry.first;
size_t offset = field.first;
Node* node = field.second;
Node* object = field.first;
Node* offset = field.second;
FieldInfo info = entry.second;
PrintF(" #%d+%zu:%s -> #%d:%s [repr=%s]\n", node->id(), offset,
node->op()->mnemonic(), info.value->id(),
PrintF(" #%d+#%d:%s -> #%d:%s [repr=%s]\n", object->id(), offset->id(),
object->op()->mnemonic(), info.value->id(),
info.value->op()->mnemonic(),
MachineReprToString(info.representation));
}
@ -128,27 +114,26 @@ void CsaLoadElimination::AbstractState::Print() const {
Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
ObjectAccess const& access) {
Node* object = NodeProperties::GetValueInput(node, 0);
Node* offset = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
if (base::Optional<size_t> offset = ConstantOffsetOf(node)) {
MachineRepresentation representation = access.machine_type.representation();
FieldInfo lookup_result = state->Lookup(object, offset.value());
if (!lookup_result.IsEmpty()) {
// Make sure we don't reuse values that were recorded with a different
// representation or resurrect dead {replacement} nodes.
Node* replacement = lookup_result.value;
if (CsaLoadEliminationIsCompatible(representation,
lookup_result.representation) &&
!replacement->IsDead()) {
ReplaceWithValue(node, replacement, effect);
return Replace(replacement);
}
MachineRepresentation representation = access.machine_type.representation();
FieldInfo lookup_result = state->Lookup(object, offset);
if (!lookup_result.IsEmpty()) {
// Make sure we don't reuse values that were recorded with a different
// representation or resurrect dead {replacement} nodes.
Node* replacement = lookup_result.value;
if (CsaLoadEliminationIsCompatible(representation,
lookup_result.representation) &&
!replacement->IsDead()) {
ReplaceWithValue(node, replacement, effect);
return Replace(replacement);
}
FieldInfo info(node, representation);
state = state->AddField(object, offset.value(), info, zone());
}
FieldInfo info(node, representation);
state = state->AddField(object, offset, info, zone());
return UpdateState(node, state);
}

View File

@ -68,15 +68,15 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final
}
void Merge(AbstractState const* that, Zone* zone);
AbstractState const* AddField(Node* object, size_t offset, FieldInfo info,
AbstractState const* AddField(Node* object, Node* offset, FieldInfo info,
Zone* zone) const;
AbstractState const* KillAll(Zone* zone) const;
FieldInfo Lookup(Node* object, size_t offset) const;
FieldInfo Lookup(Node* object, Node* offset) const;
void Print() const;
private:
using Field = std::pair<size_t, Node*>;
using Field = std::pair<Node*, Node*>;
using FieldInfos = PersistentMap<Field, FieldInfo>;
FieldInfos field_infos_;
};

View File

@ -483,8 +483,6 @@ void MemoryOptimizer::VisitLoadFromObject(Node* node,
AllocationState const* state) {
DCHECK_EQ(IrOpcode::kLoadFromObject, node->opcode());
ObjectAccess const& access = ObjectAccessOf(node->op());
Node* offset = node->InputAt(1);
node->ReplaceInput(1, __ IntSub(offset, __ IntPtrConstant(kHeapObjectTag)));
NodeProperties::ChangeOp(node, machine()->Load(access.machine_type));
EnqueueUses(node, state);
}
@ -494,9 +492,7 @@ void MemoryOptimizer::VisitStoreToObject(Node* node,
DCHECK_EQ(IrOpcode::kStoreToObject, node->opcode());
ObjectAccess const& access = ObjectAccessOf(node->op());
Node* object = node->InputAt(0);
Node* offset = node->InputAt(1);
Node* value = node->InputAt(2);
node->ReplaceInput(1, __ IntSub(offset, __ IntPtrConstant(kHeapObjectTag)));
WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
node, object, value, state, access.write_barrier_kind);
NodeProperties::ChangeOp(

View File

@ -498,7 +498,7 @@ TEST(TestStaticAssert) {
ft.Call();
}
TEST(TestLoadEliminationNoWrite) {
TEST(TestLoadEliminationFixedNoWrite) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
@ -507,7 +507,23 @@ TEST(TestLoadEliminationNoWrite) {
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestLoadEliminationNoWrite(
m.TestLoadEliminationFixedNoWrite(
m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}
asm_tester.GenerateCode();
}
TEST(TestLoadEliminationVariableNoWrite) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestLoadEliminationVariableNoWrite(
m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}

View File

@ -913,15 +913,28 @@ namespace test {
unrelated: Smi;
}
builtin NewSmiBox(implicit c: Context)(value: Smi): SmiBox {
builtin NewSmiBox(implicit context: Context)(value: Smi): SmiBox {
return new SmiBox{value, unrelated: 0};
}
@export
macro TestLoadEliminationNoWrite(implicit c: Context)() {
macro TestLoadEliminationFixedNoWrite(implicit context: Context)() {
const box = NewSmiBox(123);
const v1 = box.value;
const v2 = (box.unrelated == 0) ? box.value : box.value;
StaticAssert(WordEqual(v1, v2));
}
@export
macro TestLoadEliminationVariableNoWrite(implicit context: Context)() {
const a = UnsafeCast<FixedArray>(kEmptyFixedArray);
const box = NewSmiBox(1);
const v1 = a.objects[box.value];
const u1 = a.objects[box.value + 2];
const v2 = a.objects[box.value];
const u2 = a.objects[box.value + 2];
StaticAssert(WordEqual(v1, v2));
StaticAssert(WordEqual(u1, u2));
}
}