blob: b37431faaa7d0c714c341f0f14ab8fb01211e240 [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;
InsertionSet insertionSet { proc };
BlockInsertionSet blockInsertionSet { proc };
unsigned index { 0 };
Dominators& dominators = proc.dominators();
BackwardsDominators& backwardsDominators = proc.backwardsDominators();
HashMap<ValueKey, Vector<Value*>> baseOffsetToAddresses;
HashMap<MemoryValue*, Value*> preIndexCandidates;
HashMap<Value*, Vector<MemoryValue*>> baseToMemories;
HashMap<MemoryValue*, Value*> postIndexCandidates;
HashMap<Value*, unsigned> valueToIndex;
IndexSet<Value*> ignoredValues;
// Pre-Index Pattern:
// address = Add(base, offset)
// ...
// memory = Load(base, offset)
// Post-Index Pattern:
// memory = Load(base, 0)
// ...
// address = Add(base, offset)
auto tryPrePostIndex = [&] (Value* value) {
switch (value->opcode()) {
case Load: {
MemoryValue* memory = value->as<MemoryValue>();
auto tryPrePostIndexMemory = [&] () {
if (memory->type() != Int32 && memory->type() != Int64)
return;
if (memory->offset()) {
if (!Arg::isValidPreIndexForm(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))
preIndexCandidates.add(memory, address);
} else
baseToMemories.add(memory->child(0), Vector<MemoryValue*>()).iterator->value.append(memory);
valueToIndex.add(value, index);
};
tryPrePostIndexMemory();
break;
}
case Add: {
Value* left = value->child(0);
Value* right = value->child(1);
auto tryPotentialPostIndexAddress = [&] () {
if (!right->hasIntPtr() || value->type() != Int64)
return;
intptr_t offset = right->asIntPtr();
Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
if (smallOffset != offset || !Arg::isValidPreIndexForm(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);
valueToIndex.add(value, index);
if (!baseToMemories.contains(left))
return;
for (MemoryValue* memory : baseToMemories.get(left))
postIndexCandidates.add(memory, value);
};
tryPotentialPostIndexAddress();
break;
}
default:
break;
}
};
for (BasicBlock* basicBlock : proc.blocksInPreOrder()) {
for (index = 0; index < basicBlock->size(); ++index) {
Value* value = basicBlock->at(index);
tryPrePostIndex(value);
}
}
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));
};
auto detect = [&] (HashMap<MemoryValue*, Value*> candidates) {
for (auto itr = candidates.begin(); itr != candidates.end(); ++itr) {
MemoryValue* memory = itr->key;
Value* address = itr->value;
if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
continue;
if (candidates == preIndexCandidates) {
// address = Add(base, offset) address = Add(base, offset)
// --> newMemory = Load(base, offset)
// ... ...
// memory = Load(base, offset) memory = Identity(newMemory)
MemoryValue* newMemory = insertionSet.insert<MemoryValue>(valueToIndex.get(address) + 1, Load, memory->type(), address->origin(), memory->lastChild());
newMemory->setOffset(memory->offset());
memory->replaceWithIdentity(newMemory);
insertionSet.execute(address->owner);
} else {
// newOffset = Constant
// newAddress = Add(base, newOffset)
// memory = Load(base, 0) memory = Load(base, 0)
// ... --> ...
// offset = Constant offset = Identity(newOffset)
// address = Add(base, offset) address = Identity(newAddress)
unsigned memoryIndex = valueToIndex.get(memory);
Value* newOffset = insertionSet.insert<Const64Value>(memoryIndex, memory->origin(), address->child(1)->asInt());
Value* newAddress = insertionSet.insert<Value>(memoryIndex, Add, memory->origin(), address->child(0), newOffset);
address->child(1)->replaceWithIdentity(newOffset);
address->replaceWithIdentity(newAddress);
insertionSet.execute(memory->owner);
}
ignoredValues.add(address);
ignoredValues.add(memory);
}
};
detect(preIndexCandidates);
detect(postIndexCandidates);
return true;
}
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)