blob: cedcbf70e70f84c800fa89c7dd81a43f8927a2e1 [file] [log] [blame]
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "B3CanonicalizePrePostIncrements.h"
#include "B3BackwardsDominators.h"
#include "B3BasicBlockInlines.h"
#include "B3BlockInsertionSet.h"
#include "B3Dominators.h"
#include "B3InsertionSetInlines.h"
#include "B3PhaseScope.h"
#include "B3ProcedureInlines.h"
#include "B3ValueInlines.h"
#include "B3ValueKeyInlines.h"
#include <wtf/HashMap.h>
#include <wtf/IndexSet.h>
#include <wtf/StdLibExtras.h>
#if ENABLE(B3_JIT)
namespace JSC { namespace B3 {
bool canonicalizePrePostIncrements(Procedure& proc)
{
if (!isARM64())
return false;
PhaseScope phaseScope(proc, "canonicalizePrePostIncrements");
using Arg = Air::Arg;
unsigned index { 0 };
InsertionSet insertionSet { proc };
BlockInsertionSet blockInsertionSet { proc };
Dominators& dominators = proc.dominators();
BackwardsDominators& backwardsDominators = proc.backwardsDominators();
IndexSet<Value*> ignoredValues;
HashMap<Value*, Vector<MemoryValue*>> baseToLoads;
HashMap<MemoryValue*, Value*> preIndexLoadCandidates;
HashMap<MemoryValue*, Value*> postIndexLoadCandidates;
HashMap<ValueKey, Vector<Value*>> baseOffsetToAddresses;
HashMap<Value*, Vector<MemoryValue*>> baseToStores;
HashMap<MemoryValue*, Value*> postIndexStoreCandidates;
auto tryAddPrePostIndexCandidate = [&] (Value* value) {
switch (value->opcode()) {
case Load: {
// Pre-Index Pattern:
// address = Add(base, offset)
// ...
// memory = Load(base, offset)
// Post-Index Pattern:
// memory = Load(base, 0)
// ...
// address = Add(base, offset)
auto tryAddpreIndexLoadCandidates = [&] () {
MemoryValue* memory = value->as<MemoryValue>();
if (memory->type() != Int32 && memory->type() != Int64)
return;
if (memory->offset()) {
if (!Arg::isValidIncrementIndexForm(memory->offset()))
return;
ValueKey baseOffsetkey = ValueKey(memory->child(0), static_cast<int64_t>(memory->offset()));
if (!baseOffsetToAddresses.contains(baseOffsetkey))
return;
for (Value* address : baseOffsetToAddresses.get(baseOffsetkey))
preIndexLoadCandidates.add(memory, address);
} else
baseToLoads.add(memory->child(0), Vector<MemoryValue*>()).iterator->value.append(memory);
};
tryAddpreIndexLoadCandidates();
break;
}
case Store: {
// Pre-Index Pattern:
// address = Add(base, offset)
// memory = Store(value, base, offset)
// Post-Index Pattern:
// memory = Store(value, base, 0)
// ...
// address = Add(base, offset)
auto tryUpdateBaseToStores = [&] () {
MemoryValue* memory = value->as<MemoryValue>();
if (memory->child(0)->type() != Int32 && memory->child(0)->type() != Int64)
return;
if (memory->child(0)->hasInt() || memory->offset())
return;
baseToStores.add(memory->child(1), Vector<MemoryValue*>()).iterator->value.append(memory);
};
tryUpdateBaseToStores();
break;
}
case Add: {
Value* left = value->child(0);
Value* right = value->child(1);
auto tryAddpostIndexCandidates = [&] () {
if (!right->hasIntPtr() || value->type() != Int64)
return;
intptr_t offset = right->asIntPtr();
Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
if (smallOffset != offset || !Arg::isValidIncrementIndexForm(smallOffset))
return;
// so far this Add value is a valid address candidate for both prefix and postfix pattern
ValueKey baseOffsetkey = ValueKey(left, static_cast<int64_t>(smallOffset));
baseOffsetToAddresses.add(baseOffsetkey, Vector<Value*>()).iterator->value.append(value);
if (baseToLoads.contains(left)) {
for (MemoryValue* memory : baseToLoads.get(left))
postIndexLoadCandidates.add(memory, value);
}
if (baseToStores.contains(left)) {
for (MemoryValue* memory : baseToStores.get(left))
postIndexStoreCandidates.add(memory, value);
}
};
tryAddpostIndexCandidates();
break;
}
default:
break;
}
};
for (BasicBlock* basicBlock : proc.blocksInPreOrder()) {
for (index = 0; index < basicBlock->size(); ++index)
tryAddPrePostIndexCandidate(basicBlock->at(index));
}
auto controlEquivalent = [&] (Value* v1, Value* v2) -> bool {
return (dominators.dominates(v1->owner, v2->owner) && backwardsDominators.dominates(v2->owner, v1->owner))
|| (dominators.dominates(v2->owner, v1->owner) && backwardsDominators.dominates(v1->owner, v2->owner));
};
// This search is expensive. However, due to the greedy pattern
// matching, no better method can be proposed at present.
auto valueIndexInBasicBlock = [&] (Value* value) -> unsigned {
unsigned index = 0;
BasicBlock* block = value->owner;
for (index = 0; index < block->size(); ++index) {
if (block->at(index) == value)
return index;
}
return index;
};
for (auto pair : preIndexLoadCandidates) {
MemoryValue* memory = pair.key;
Value* address = pair.value;
if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
continue;
// address = Add(base, offset) address = Add(base, offset)
// ... --> newMemory = Load(base, offset)
// ... ...
// memory = Load(base, offset) memory = Identity(newMemory)
unsigned insertionIndex = valueIndexInBasicBlock(address) + 1;
MemoryValue* newMemory = insertionSet.insert<MemoryValue>(insertionIndex, Load, memory->type(), address->origin(), memory->lastChild());
newMemory->setOffset(memory->offset());
memory->replaceWithIdentity(newMemory);
insertionSet.execute(address->owner);
ignoredValues.add(memory);
ignoredValues.add(address);
}
auto detectPostIndex = [&] (const HashMap<MemoryValue*, Value*>& candidates) {
for (auto pair : candidates) {
MemoryValue* memory = pair.key;
Value* address = pair.value;
if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
continue;
unsigned insertionIndex = valueIndexInBasicBlock(memory);
Value* newOffset = insertionSet.insert<Const64Value>(insertionIndex, memory->origin(), address->child(1)->asInt());
Value* newAddress = insertionSet.insert<Value>(insertionIndex, Add, memory->origin(), address->child(0), newOffset);
address->replaceWithIdentity(newAddress);
insertionSet.execute(memory->owner);
ignoredValues.add(memory);
ignoredValues.add(address);
}
};
// ... newOffset = Constant
// ... newAddress = Add(base, newOffset)
// memory = Load(base, 0) memory = Load(base, 0)
// ... --> ...
// address = Add(base, offset) address = Identity(newAddress)
detectPostIndex(postIndexLoadCandidates);
// ... newOffset = Constant
// ... newAddress = Add(base, newOffset)
// memory = Store(value, base, 0) memory = Store(value, base, 0)
// ... --> ...
// address = Add(base, offset) address = Identity(newAddress)
detectPostIndex(postIndexStoreCandidates);
return true;
}
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)