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) {
switch (node->opcode()) {
case IrOpcode::kLoadFromObject:
return ReduceLoadFromObject(node, ObjectAccessOf(node->op()));
case IrOpcode::kStoreToObject:
return ReduceStoreToObject(node, ObjectAccessOf(node->op()));
case IrOpcode::kDebugBreak:
case IrOpcode::kDebugAbort:
// Avoid changing optimizations in the presence of debug instructions
......@@ -61,15 +63,51 @@ Reduction CsaLoadElimination::Reduce(Node* node) {
return NoChange();
}
namespace {
namespace CsaLoadEliminationHelpers {
bool CsaLoadEliminationIsCompatible(MachineRepresentation r1,
MachineRepresentation r2) {
bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
if (r1 == r2) return true;
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,
Zone* zone) {
......@@ -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::AddField(Node* object, Node* offset,
CsaLoadElimination::FieldInfo info,
......@@ -125,8 +182,7 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
// 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) &&
if (Helpers::IsCompatible(representation, lookup_result.representation) &&
!replacement->IsDead()) {
ReplaceWithValue(node, replacement, effect);
return Replace(replacement);
......@@ -138,6 +194,22 @@ Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
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) {
Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
Node* const control = NodeProperties::GetControlInput(node);
......
......@@ -68,9 +68,11 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final
}
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,
Zone* zone) const;
AbstractState const* KillAll(Zone* zone) const;
FieldInfo Lookup(Node* object, Node* offset) const;
void Print() const;
......@@ -82,6 +84,7 @@ class V8_EXPORT_PRIVATE CsaLoadElimination final
};
Reduction ReduceLoadFromObject(Node* node, ObjectAccess const& access);
Reduction ReduceStoreToObject(Node* node, ObjectAccess const& access);
Reduction ReduceEffectPhi(Node* node);
Reduction ReduceStart(Node* node);
Reduction ReduceCall(Node* node);
......
......@@ -498,7 +498,7 @@ TEST(TestStaticAssert) {
ft.Call();
}
TEST(TestLoadEliminationFixedNoWrite) {
TEST(TestLoadEliminationFixed) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
......@@ -507,14 +507,14 @@ TEST(TestLoadEliminationFixedNoWrite) {
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestLoadEliminationFixedNoWrite(
m.TestLoadEliminationFixed(
m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}
asm_tester.GenerateCode();
}
TEST(TestLoadEliminationVariableNoWrite) {
TEST(TestLoadEliminationVariable) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
......@@ -523,7 +523,7 @@ TEST(TestLoadEliminationVariableNoWrite) {
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestLoadEliminationVariableNoWrite(
m.TestLoadEliminationVariable(
m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}
......
......@@ -918,15 +918,21 @@ namespace test {
}
@export
macro TestLoadEliminationFixedNoWrite(implicit context: Context)() {
macro TestLoadEliminationFixed(implicit context: Context)() {
const box = NewSmiBox(123);
const v1 = box.value;
box.unrelated = 999;
const v2 = (box.unrelated == 0) ? box.value : box.value;
StaticAssert(WordEqual(v1, v2));
box.value = 11;
const v3 = box.value;
const eleven: Smi = 11;
StaticAssert(WordEqual(v3, eleven));
}
@export
macro TestLoadEliminationVariableNoWrite(implicit context: Context)() {
macro TestLoadEliminationVariable(implicit context: Context)() {
const a = UnsafeCast<FixedArray>(kEmptyFixedArray);
const box = NewSmiBox(1);
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