summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/b3/B3CheckSpecial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/b3/B3CheckSpecial.cpp')
-rw-r--r--Source/JavaScriptCore/b3/B3CheckSpecial.cpp247
1 files changed, 247 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/b3/B3CheckSpecial.cpp b/Source/JavaScriptCore/b3/B3CheckSpecial.cpp
new file mode 100644
index 000000000..2d48d4386
--- /dev/null
+++ b/Source/JavaScriptCore/b3/B3CheckSpecial.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015-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 "B3CheckSpecial.h"
+
+#if ENABLE(B3_JIT)
+
+#include "AirCode.h"
+#include "AirGenerationContext.h"
+#include "AirInstInlines.h"
+#include "B3StackmapGenerationParams.h"
+#include "B3ValueInlines.h"
+
+namespace JSC { namespace B3 {
+
+using namespace Air;
+
+namespace {
+
+unsigned numB3Args(B3::Opcode opcode)
+{
+ switch (opcode) {
+ case CheckAdd:
+ case CheckSub:
+ case CheckMul:
+ return 2;
+ case Check:
+ return 1;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+unsigned numB3Args(Value* value)
+{
+ return numB3Args(value->opcode());
+}
+
+unsigned numB3Args(Inst& inst)
+{
+ return numB3Args(inst.origin);
+}
+
+} // anonymous namespace
+
+CheckSpecial::Key::Key(const Inst& inst)
+{
+ m_opcode = inst.opcode;
+ m_numArgs = inst.args.size();
+ m_stackmapRole = SameAsRep;
+}
+
+void CheckSpecial::Key::dump(PrintStream& out) const
+{
+ out.print(m_opcode, "(", m_numArgs, ",", m_stackmapRole, ")");
+}
+
+CheckSpecial::CheckSpecial(Air::Opcode opcode, unsigned numArgs, RoleMode stackmapRole)
+ : m_checkOpcode(opcode)
+ , m_stackmapRole(stackmapRole)
+ , m_numCheckArgs(numArgs)
+{
+ ASSERT(isTerminal(opcode));
+}
+
+CheckSpecial::CheckSpecial(const CheckSpecial::Key& key)
+ : CheckSpecial(key.opcode(), key.numArgs(), key.stackmapRole())
+{
+}
+
+CheckSpecial::~CheckSpecial()
+{
+}
+
+Inst CheckSpecial::hiddenBranch(const Inst& inst) const
+{
+ Inst hiddenBranch(m_checkOpcode, inst.origin);
+ hiddenBranch.args.reserveInitialCapacity(m_numCheckArgs);
+ for (unsigned i = 0; i < m_numCheckArgs; ++i)
+ hiddenBranch.args.append(inst.args[i + 1]);
+ return hiddenBranch;
+}
+
+void CheckSpecial::forEachArg(Inst& inst, const ScopedLambda<Inst::EachArgCallback>& callback)
+{
+ Inst hidden = hiddenBranch(inst);
+ hidden.forEachArg(
+ [&] (Arg& arg, Arg::Role role, Arg::Type type, Arg::Width width) {
+ unsigned index = &arg - &hidden.args[0];
+ callback(inst.args[1 + index], role, type, width);
+ });
+
+ Optional<unsigned> firstRecoverableIndex;
+ if (m_checkOpcode == BranchAdd32 || m_checkOpcode == BranchAdd64)
+ firstRecoverableIndex = 1;
+ forEachArgImpl(numB3Args(inst), m_numCheckArgs + 1, inst, m_stackmapRole, firstRecoverableIndex, callback);
+}
+
+bool CheckSpecial::isValid(Inst& inst)
+{
+ return hiddenBranch(inst).isValidForm()
+ && isValidImpl(numB3Args(inst), m_numCheckArgs + 1, inst)
+ && inst.args.size() - m_numCheckArgs - 1 == inst.origin->numChildren() - numB3Args(inst);
+}
+
+bool CheckSpecial::admitsStack(Inst& inst, unsigned argIndex)
+{
+ if (argIndex >= 1 && argIndex < 1 + m_numCheckArgs)
+ return hiddenBranch(inst).admitsStack(argIndex - 1);
+ return admitsStackImpl(numB3Args(inst), m_numCheckArgs + 1, inst, argIndex);
+}
+
+Optional<unsigned> CheckSpecial::shouldTryAliasingDef(Inst& inst)
+{
+ if (Optional<unsigned> branchDef = hiddenBranch(inst).shouldTryAliasingDef())
+ return *branchDef + 1;
+ return Nullopt;
+}
+
+CCallHelpers::Jump CheckSpecial::generate(Inst& inst, CCallHelpers& jit, GenerationContext& context)
+{
+ CCallHelpers::Jump fail = hiddenBranch(inst).generate(jit, context);
+ ASSERT(fail.isSet());
+
+ StackmapValue* value = inst.origin->as<StackmapValue>();
+ ASSERT(value);
+
+ Vector<ValueRep> reps = repsImpl(context, numB3Args(inst), m_numCheckArgs + 1, inst);
+
+ // Set aside the args that are relevant to undoing the operation. This is because we don't want to
+ // capture all of inst in the closure below.
+ Vector<Arg, 3> args;
+ for (unsigned i = 0; i < m_numCheckArgs; ++i)
+ args.append(inst.args[1 + i]);
+
+ context.latePaths.append(
+ createSharedTask<GenerationContext::LatePathFunction>(
+ [=] (CCallHelpers& jit, GenerationContext& context) {
+ fail.link(&jit);
+
+ // If necessary, undo the operation.
+ switch (m_checkOpcode) {
+ case BranchAdd32:
+ if ((m_numCheckArgs == 4 && args[1] == args[2] && args[2] == args[3])
+ || (m_numCheckArgs == 3 && args[1] == args[2])) {
+ // This is ugly, but that's fine - we won't have to do this very often.
+ ASSERT(args[1].isGPR());
+ GPRReg valueGPR = args[1].gpr();
+ GPRReg scratchGPR = CCallHelpers::selectScratchGPR(valueGPR);
+ jit.pushToSave(scratchGPR);
+ jit.setCarry(scratchGPR);
+ jit.lshift32(CCallHelpers::TrustedImm32(31), scratchGPR);
+ jit.urshift32(CCallHelpers::TrustedImm32(1), valueGPR);
+ jit.or32(scratchGPR, valueGPR);
+ jit.popToRestore(scratchGPR);
+ break;
+ }
+ if (m_numCheckArgs == 4) {
+ if (args[1] == args[3])
+ Inst(Sub32, nullptr, args[2], args[3]).generate(jit, context);
+ else if (args[2] == args[3])
+ Inst(Sub32, nullptr, args[1], args[3]).generate(jit, context);
+ } else if (m_numCheckArgs == 3)
+ Inst(Sub32, nullptr, args[1], args[2]).generate(jit, context);
+ break;
+ case BranchAdd64:
+ if ((m_numCheckArgs == 4 && args[1] == args[2] && args[2] == args[3])
+ || (m_numCheckArgs == 3 && args[1] == args[2])) {
+ // This is ugly, but that's fine - we won't have to do this very often.
+ ASSERT(args[1].isGPR());
+ GPRReg valueGPR = args[1].gpr();
+ GPRReg scratchGPR = CCallHelpers::selectScratchGPR(valueGPR);
+ jit.pushToSave(scratchGPR);
+ jit.setCarry(scratchGPR);
+ jit.lshift64(CCallHelpers::TrustedImm32(63), scratchGPR);
+ jit.urshift64(CCallHelpers::TrustedImm32(1), valueGPR);
+ jit.or64(scratchGPR, valueGPR);
+ jit.popToRestore(scratchGPR);
+ break;
+ }
+ if (m_numCheckArgs == 4) {
+ if (args[1] == args[3])
+ Inst(Sub64, nullptr, args[2], args[3]).generate(jit, context);
+ else if (args[2] == args[3])
+ Inst(Sub64, nullptr, args[1], args[3]).generate(jit, context);
+ } else if (m_numCheckArgs == 3)
+ Inst(Sub64, nullptr, args[1], args[2]).generate(jit, context);
+ break;
+ case BranchSub32:
+ Inst(Add32, nullptr, args[1], args[2]).generate(jit, context);
+ break;
+ case BranchSub64:
+ Inst(Add64, nullptr, args[1], args[2]).generate(jit, context);
+ break;
+ case BranchNeg32:
+ Inst(Neg32, nullptr, args[1]).generate(jit, context);
+ break;
+ case BranchNeg64:
+ Inst(Neg64, nullptr, args[1]).generate(jit, context);
+ break;
+ default:
+ break;
+ }
+
+ value->m_generator->run(jit, StackmapGenerationParams(value, reps, context));
+ }));
+
+ return CCallHelpers::Jump(); // As far as Air thinks, we are not a terminal.
+}
+
+void CheckSpecial::dumpImpl(PrintStream& out) const
+{
+ out.print(m_checkOpcode, "(", m_numCheckArgs, ",", m_stackmapRole, ")");
+}
+
+void CheckSpecial::deepDumpImpl(PrintStream& out) const
+{
+ out.print("B3::CheckValue lowered to ", m_checkOpcode, " with ", m_numCheckArgs, " args.");
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)