[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:
parent
98ba192d12
commit
45bf9d8fa9
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user