Commit 16893bda authored by Georg Schmid's avatar Georg Schmid Committed by Commit Bot

[csa] Track stores and perform simple alias analysis in CSA Load Elimination

This CL allows CsaLoadElimination to retain some information in the presence of StoreToObject nodes. Two stores to an object don't alias if either the objects or the offsets don't alias. The analysis approximates either of these two conditions conservatively as follows:
- Freshly allocated, distinct objects cannot alias.
- Two objects cannot alias if one of is freshly allocated and the other was passed as a parameter or is a heap constant.
- Two offsets cannot alias if they are both constant and distinct from each other.

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

Change-Id: Ibec81913b413f81a3f7cbd40544a22d3711e6e5a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1660626
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62232}
parent 597d048b
...@@ -43,6 +43,8 @@ Reduction CsaLoadElimination::Reduce(Node* node) { ...@@ -43,6 +43,8 @@ Reduction CsaLoadElimination::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kLoadFromObject: case IrOpcode::kLoadFromObject:
return ReduceLoadFromObject(node, ObjectAccessOf(node->op())); return ReduceLoadFromObject(node, ObjectAccessOf(node->op()));
case IrOpcode::kStoreToObject:
return ReduceStoreToObject(node, ObjectAccessOf(node->op()));
case IrOpcode::kDebugBreak: case IrOpcode::kDebugBreak:
case IrOpcode::kDebugAbort: case IrOpcode::kDebugAbort:
// Avoid changing optimizations in the presence of debug instructions // Avoid changing optimizations in the presence of debug instructions
...@@ -61,15 +63,51 @@ Reduction CsaLoadElimination::Reduce(Node* node) { ...@@ -61,15 +63,51 @@ Reduction CsaLoadElimination::Reduce(Node* node) {
return NoChange(); return NoChange();
} }
namespace { namespace CsaLoadEliminationHelpers {
bool CsaLoadEliminationIsCompatible(MachineRepresentation r1, bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
MachineRepresentation r2) {
if (r1 == r2) return true; if (r1 == r2) return true;
return IsAnyCompressedTagged(r1) && IsAnyCompressedTagged(r2); return IsAnyCompressedTagged(r1) && IsAnyCompressedTagged(r2);
} }
} // namespace bool ObjectMayAlias(Node* a, Node* b) {
if (a != b) {
if (b->opcode() == IrOpcode::kAllocate) {
std::swap(a, b);
}
if (a->opcode() == IrOpcode::kAllocate) {
switch (b->opcode()) {
case IrOpcode::kAllocate:
case IrOpcode::kHeapConstant:
case IrOpcode::kParameter:
return false;
default:
break;
}
}
}
return true;
}
bool OffsetMayAlias(Node* offset1, MachineRepresentation repr1, Node* offset2,
MachineRepresentation repr2) {
IntPtrMatcher matcher1(offset1);
IntPtrMatcher matcher2(offset2);
// If either of the offsets is variable, accesses may alias
if (!matcher1.HasValue() || !matcher2.HasValue()) {
return true;
}
// Otherwise, we return whether accesses overlap
intptr_t start1 = matcher1.Value();
intptr_t end1 = start1 + ElementSizeInBytes(repr1);
intptr_t start2 = matcher2.Value();
intptr_t end2 = start2 + ElementSizeInBytes(repr2);
return !(end1 <= start2 || end2 <= start1);
}
} // namespace CsaLoadEliminationHelpers
namespace Helpers = CsaLoadEliminationHelpers;
void CsaLoadElimination::AbstractState::Merge(AbstractState const* that, void CsaLoadElimination::AbstractState::Merge(AbstractState const* that,
Zone* zone) { Zone* zone) {
...@@ -81,6 +119,25 @@ void CsaLoadElimination::AbstractState::Merge(AbstractState const* that, ...@@ -81,6 +119,25 @@ void CsaLoadElimination::AbstractState::Merge(AbstractState const* that,
} }
} }
CsaLoadElimination::AbstractState const*
CsaLoadElimination::AbstractState::KillField(Node* kill_object,
Node* kill_offset,
MachineRepresentation kill_repr,
Zone* zone) const {
FieldInfo empty_info;
AbstractState* that = new (zone) AbstractState(*this);
for (std::pair<Field, FieldInfo> entry : that->field_infos_) {
Field field = entry.first;
MachineRepresentation field_repr = entry.second.representation;
if (Helpers::OffsetMayAlias(kill_offset, kill_repr, field.second,
field_repr) &&
Helpers::ObjectMayAlias(kill_object, field.first)) {
that->field_infos_.Set(field, empty_info);
}
}
return that;
}
CsaLoadElimination::AbstractState const* CsaLoadElimination::AbstractState const*
CsaLoadElimination::AbstractState::AddField(Node* object, Node* offset, CsaLoadElimination::AbstractState::AddField(Node* object, Node* offset,
CsaLoadElimination::FieldInfo info, CsaLoadElimination::FieldInfo info,
...@@ -125,8 +182,7 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node, ...@@ -125,8 +182,7 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
// Make sure we don't reuse values that were recorded with a different // Make sure we don't reuse values that were recorded with a different
// representation or resurrect dead {replacement} nodes. // representation or resurrect dead {replacement} nodes.
Node* replacement = lookup_result.value; Node* replacement = lookup_result.value;
if (CsaLoadEliminationIsCompatible(representation, if (Helpers::IsCompatible(representation, lookup_result.representation) &&
lookup_result.representation) &&
!replacement->IsDead()) { !replacement->IsDead()) {
ReplaceWithValue(node, replacement, effect); ReplaceWithValue(node, replacement, effect);
return Replace(replacement); return Replace(replacement);
...@@ -138,6 +194,22 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node, ...@@ -138,6 +194,22 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
return UpdateState(node, state); return UpdateState(node, state);
} }
Reduction CsaLoadElimination::ReduceStoreToObject(Node* node,
ObjectAccess const& access) {
Node* object = NodeProperties::GetValueInput(node, 0);
Node* offset = NodeProperties::GetValueInput(node, 1);
Node* value = NodeProperties::GetValueInput(node, 2);
Node* effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
FieldInfo info(value, access.machine_type.representation());
state = state->KillField(object, offset, info.representation, zone());
state = state->AddField(object, offset, info, zone());
return UpdateState(node, state);
}
Reduction CsaLoadElimination::ReduceEffectPhi(Node* node) { Reduction CsaLoadElimination::ReduceEffectPhi(Node* node) {
Node* const effect0 = NodeProperties::GetEffectInput(node, 0); Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
Node* const control = NodeProperties::GetControlInput(node); Node* const control = NodeProperties::GetControlInput(node);
......
...@@ -68,9 +68,11 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final ...@@ -68,9 +68,11 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final
} }
void Merge(AbstractState const* that, Zone* zone); void Merge(AbstractState const* that, Zone* zone);
AbstractState const* KillField(Node* object, Node* offset,
MachineRepresentation repr,
Zone* zone) const;
AbstractState const* AddField(Node* object, Node* offset, FieldInfo info, AbstractState const* AddField(Node* object, Node* offset, FieldInfo info,
Zone* zone) const; Zone* zone) const;
AbstractState const* KillAll(Zone* zone) const;
FieldInfo Lookup(Node* object, Node* offset) const; FieldInfo Lookup(Node* object, Node* offset) const;
void Print() const; void Print() const;
...@@ -82,6 +84,7 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final ...@@ -82,6 +84,7 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final
}; };
Reduction ReduceLoadFromObject(Node* node, ObjectAccess const& access); Reduction ReduceLoadFromObject(Node* node, ObjectAccess const& access);
Reduction ReduceStoreToObject(Node* node, ObjectAccess const& access);
Reduction ReduceEffectPhi(Node* node); Reduction ReduceEffectPhi(Node* node);
Reduction ReduceStart(Node* node); Reduction ReduceStart(Node* node);
Reduction ReduceCall(Node* node); Reduction ReduceCall(Node* node);
......
...@@ -498,7 +498,7 @@ TEST(TestStaticAssert) { ...@@ -498,7 +498,7 @@ TEST(TestStaticAssert) {
ft.Call(); ft.Call();
} }
TEST(TestLoadEliminationFixedNoWrite) { TEST(TestLoadEliminationFixed) {
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate()); Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
...@@ -507,14 +507,14 @@ TEST(TestLoadEliminationFixedNoWrite) { ...@@ -507,14 +507,14 @@ TEST(TestLoadEliminationFixedNoWrite) {
CodeAssemblerTester asm_tester(isolate); CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state()); TestTorqueAssembler m(asm_tester.state());
{ {
m.TestLoadEliminationFixedNoWrite( m.TestLoadEliminationFixed(
m.UncheckedCast<Context>(m.HeapConstant(context))); m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant()); m.Return(m.UndefinedConstant());
} }
asm_tester.GenerateCode(); asm_tester.GenerateCode();
} }
TEST(TestLoadEliminationVariableNoWrite) { TEST(TestLoadEliminationVariable) {
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate()); Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
...@@ -523,7 +523,7 @@ TEST(TestLoadEliminationVariableNoWrite) { ...@@ -523,7 +523,7 @@ TEST(TestLoadEliminationVariableNoWrite) {
CodeAssemblerTester asm_tester(isolate); CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state()); TestTorqueAssembler m(asm_tester.state());
{ {
m.TestLoadEliminationVariableNoWrite( m.TestLoadEliminationVariable(
m.UncheckedCast<Context>(m.HeapConstant(context))); m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant()); m.Return(m.UndefinedConstant());
} }
......
...@@ -918,15 +918,21 @@ namespace test { ...@@ -918,15 +918,21 @@ namespace test {
} }
@export @export
macro TestLoadEliminationFixedNoWrite(implicit context: Context)() { macro TestLoadEliminationFixed(implicit context: Context)() {
const box = NewSmiBox(123); const box = NewSmiBox(123);
const v1 = box.value; const v1 = box.value;
box.unrelated = 999;
const v2 = (box.unrelated == 0) ? box.value : box.value; const v2 = (box.unrelated == 0) ? box.value : box.value;
StaticAssert(WordEqual(v1, v2)); StaticAssert(WordEqual(v1, v2));
box.value = 11;
const v3 = box.value;
const eleven: Smi = 11;
StaticAssert(WordEqual(v3, eleven));
} }
@export @export
macro TestLoadEliminationVariableNoWrite(implicit context: Context)() { macro TestLoadEliminationVariable(implicit context: Context)() {
const a = UnsafeCast<FixedArray>(kEmptyFixedArray); const a = UnsafeCast<FixedArray>(kEmptyFixedArray);
const box = NewSmiBox(1); const box = NewSmiBox(1);
const v1 = a.objects[box.value]; const v1 = a.objects[box.value];
......
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