summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp')
-rw-r--r--Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp250
1 files changed, 250 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp b/Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp
new file mode 100644
index 000000000..e0018734b
--- /dev/null
+++ b/Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2016 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 "AirLowerAfterRegAlloc.h"
+
+#if ENABLE(B3_JIT)
+
+#include "AirArgInlines.h"
+#include "AirCCallingConvention.h"
+#include "AirCode.h"
+#include "AirEmitShuffle.h"
+#include "AirInsertionSet.h"
+#include "AirInstInlines.h"
+#include "AirLiveness.h"
+#include "AirPhaseScope.h"
+#include "B3CCallValue.h"
+#include "B3ValueInlines.h"
+#include "RegisterSet.h"
+#include <wtf/HashMap.h>
+
+namespace JSC { namespace B3 { namespace Air {
+
+namespace {
+
+bool verbose = false;
+
+} // anonymous namespace
+
+void lowerAfterRegAlloc(Code& code)
+{
+ PhaseScope phaseScope(code, "lowerAfterRegAlloc");
+
+ if (verbose)
+ dataLog("Code before lowerAfterRegAlloc:\n", code);
+
+ HashMap<Inst*, RegisterSet> usedRegisters;
+
+ RegLiveness liveness(code);
+ for (BasicBlock* block : code) {
+ RegLiveness::LocalCalc localCalc(liveness, block);
+
+ for (unsigned instIndex = block->size(); instIndex--;) {
+ Inst& inst = block->at(instIndex);
+
+ RegisterSet set;
+
+ bool isRelevant = inst.kind.opcode == Shuffle || inst.kind.opcode == ColdCCall;
+
+ if (isRelevant) {
+ for (Reg reg : localCalc.live())
+ set.set(reg);
+ }
+
+ localCalc.execute(instIndex);
+
+ if (isRelevant)
+ usedRegisters.add(&inst, set);
+ }
+ }
+
+ auto getScratches = [&] (RegisterSet set, Arg::Type type) -> std::array<Arg, 2> {
+ std::array<Arg, 2> result;
+ for (unsigned i = 0; i < 2; ++i) {
+ bool found = false;
+ for (Reg reg : code.regsInPriorityOrder(type)) {
+ if (!set.get(reg)) {
+ result[i] = Tmp(reg);
+ set.set(reg);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ result[i] = Arg::stack(
+ code.addStackSlot(
+ Arg::bytes(Arg::conservativeWidth(type)),
+ StackSlotKind::Spill));
+ }
+ }
+ return result;
+ };
+
+ // Now transform the code.
+ InsertionSet insertionSet(code);
+ for (BasicBlock* block : code) {
+ for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
+ Inst& inst = block->at(instIndex);
+
+ switch (inst.kind.opcode) {
+ case Shuffle: {
+ RegisterSet set = usedRegisters.get(&inst);
+ Vector<ShufflePair> pairs;
+ for (unsigned i = 0; i < inst.args.size(); i += 3) {
+ Arg src = inst.args[i + 0];
+ Arg dst = inst.args[i + 1];
+ Arg::Width width = inst.args[i + 2].width();
+
+ // The used register set contains things live after the shuffle. But
+ // emitShuffle() wants a scratch register that is not just dead but also does not
+ // interfere with either sources or destinations.
+ auto excludeRegisters = [&] (Tmp tmp) {
+ if (tmp.isReg())
+ set.set(tmp.reg());
+ };
+ src.forEachTmpFast(excludeRegisters);
+ dst.forEachTmpFast(excludeRegisters);
+
+ pairs.append(ShufflePair(src, dst, width));
+ }
+ std::array<Arg, 2> gpScratch = getScratches(set, Arg::GP);
+ std::array<Arg, 2> fpScratch = getScratches(set, Arg::FP);
+ insertionSet.insertInsts(
+ instIndex, emitShuffle(code, pairs, gpScratch, fpScratch, inst.origin));
+ inst = Inst();
+ break;
+ }
+
+ case ColdCCall: {
+ CCallValue* value = inst.origin->as<CCallValue>();
+ Kind oldKind = inst.kind;
+
+ RegisterSet liveRegs = usedRegisters.get(&inst);
+ RegisterSet regsToSave = liveRegs;
+ regsToSave.exclude(RegisterSet::calleeSaveRegisters());
+ regsToSave.exclude(RegisterSet::stackRegisters());
+ regsToSave.exclude(RegisterSet::reservedHardwareRegisters());
+
+ RegisterSet preUsed = regsToSave;
+ Vector<Arg> destinations = computeCCallingConvention(code, value);
+ Tmp result = cCallResult(value->type());
+ Arg originalResult = result ? inst.args[1] : Arg();
+
+ Vector<ShufflePair> pairs;
+ for (unsigned i = 0; i < destinations.size(); ++i) {
+ Value* child = value->child(i);
+ Arg src = inst.args[result ? (i >= 1 ? i + 1 : i) : i ];
+ Arg dst = destinations[i];
+ Arg::Width width = Arg::widthForB3Type(child->type());
+ pairs.append(ShufflePair(src, dst, width));
+
+ auto excludeRegisters = [&] (Tmp tmp) {
+ if (tmp.isReg())
+ preUsed.set(tmp.reg());
+ };
+ src.forEachTmpFast(excludeRegisters);
+ dst.forEachTmpFast(excludeRegisters);
+ }
+
+ std::array<Arg, 2> gpScratch = getScratches(preUsed, Arg::GP);
+ std::array<Arg, 2> fpScratch = getScratches(preUsed, Arg::FP);
+
+ // Also need to save all live registers. Don't need to worry about the result
+ // register.
+ if (originalResult.isReg())
+ regsToSave.clear(originalResult.reg());
+ Vector<StackSlot*> stackSlots;
+ regsToSave.forEach(
+ [&] (Reg reg) {
+ Tmp tmp(reg);
+ Arg arg(tmp);
+ Arg::Width width = Arg::conservativeWidth(arg.type());
+ StackSlot* stackSlot =
+ code.addStackSlot(Arg::bytes(width), StackSlotKind::Spill);
+ pairs.append(ShufflePair(arg, Arg::stack(stackSlot), width));
+ stackSlots.append(stackSlot);
+ });
+
+ if (verbose)
+ dataLog("Pre-call pairs for ", inst, ": ", listDump(pairs), "\n");
+
+ insertionSet.insertInsts(
+ instIndex, emitShuffle(code, pairs, gpScratch, fpScratch, inst.origin));
+
+ inst = buildCCall(code, inst.origin, destinations);
+ if (oldKind.traps)
+ inst.kind.traps = true;
+
+ // Now we need to emit code to restore registers.
+ pairs.resize(0);
+ unsigned stackSlotIndex = 0;
+ regsToSave.forEach(
+ [&] (Reg reg) {
+ Tmp tmp(reg);
+ Arg arg(tmp);
+ Arg::Width width = Arg::conservativeWidth(arg.type());
+ StackSlot* stackSlot = stackSlots[stackSlotIndex++];
+ pairs.append(ShufflePair(Arg::stack(stackSlot), arg, width));
+ });
+ if (result) {
+ ShufflePair pair(result, originalResult, Arg::widthForB3Type(value->type()));
+ pairs.append(pair);
+ }
+
+ // For finding scratch registers, we need to account for the possibility that
+ // the result is dead.
+ if (originalResult.isReg())
+ liveRegs.set(originalResult.reg());
+
+ gpScratch = getScratches(liveRegs, Arg::GP);
+ fpScratch = getScratches(liveRegs, Arg::FP);
+
+ insertionSet.insertInsts(
+ instIndex + 1, emitShuffle(code, pairs, gpScratch, fpScratch, inst.origin));
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ insertionSet.execute(block);
+
+ block->insts().removeAllMatching(
+ [&] (Inst& inst) -> bool {
+ return !inst;
+ });
+ }
+
+ if (verbose)
+ dataLog("Code after lowerAfterRegAlloc:\n", code);
+}
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
+