diff options
Diffstat (limited to 'Source/JavaScriptCore/b3/B3CheckSpecial.cpp')
-rw-r--r-- | Source/JavaScriptCore/b3/B3CheckSpecial.cpp | 247 |
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) |