| /* |
| * Copyright (C) 2016-2017 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 "AirCustom.h" |
| |
| #if ENABLE(B3_JIT) |
| |
| #include "AirInstInlines.h" |
| #include "B3CCallValue.h" |
| #include "B3ValueInlines.h" |
| #include "CCallHelpers.h" |
| |
| namespace JSC { namespace B3 { namespace Air { |
| |
| bool PatchCustom::isValidForm(Inst& inst) |
| { |
| if (inst.args.size() < 1) |
| return false; |
| if (!inst.args[0].isSpecial()) |
| return false; |
| if (!inst.args[0].special()->isValid(inst)) |
| return false; |
| RegisterSet clobberedEarly = inst.extraEarlyClobberedRegs(); |
| RegisterSet clobberedLate = inst.extraClobberedRegs(); |
| bool ok = true; |
| inst.forEachTmp( |
| [&] (Tmp& tmp, Arg::Role role, Bank, Width) { |
| if (!tmp.isReg()) |
| return; |
| if (Arg::isLateDef(role) || Arg::isLateUse(role)) |
| ok &= !clobberedLate.get(tmp.reg()); |
| else |
| ok &= !clobberedEarly.get(tmp.reg()); |
| }); |
| return ok; |
| } |
| |
| bool CCallCustom::isValidForm(Inst& inst) |
| { |
| CCallValue* value = inst.origin->as<CCallValue>(); |
| if (!value) |
| return false; |
| |
| if (inst.args.size() != (value->type() == Void ? 0 : 1) + value->numChildren()) |
| return false; |
| |
| // The arguments can only refer to the stack, tmps, or immediates. |
| for (Arg& arg : inst.args) { |
| if (!arg.isTmp() && !arg.isStackMemory() && !arg.isSomeImm()) |
| return false; |
| } |
| |
| unsigned offset = 0; |
| |
| if (!inst.args[0].isGP()) |
| return false; |
| |
| // If there is a result then it cannot be an immediate. |
| if (value->type() != Void) { |
| if (inst.args[1].isSomeImm()) |
| return false; |
| if (!inst.args[1].canRepresent(value)) |
| return false; |
| offset++; |
| } |
| |
| for (unsigned i = value->numChildren(); i-- > 1;) { |
| Value* child = value->child(i); |
| Arg arg = inst.args[offset + i]; |
| if (!arg.canRepresent(child)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| MacroAssembler::Jump CCallCustom::generate(Inst& inst, CCallHelpers&, GenerationContext&) |
| { |
| dataLog("FATAL: Unlowered C call: ", inst, "\n"); |
| UNREACHABLE_FOR_PLATFORM(); |
| return MacroAssembler::Jump(); |
| } |
| |
| bool ShuffleCustom::isValidForm(Inst& inst) |
| { |
| if (inst.args.size() % 3) |
| return false; |
| |
| // A destination may only appear once. This requirement allows us to avoid the undefined behavior |
| // of having a destination that is supposed to get multiple inputs simultaneously. It also |
| // imposes some interesting constraints on the "shape" of the shuffle. If we treat a shuffle pair |
| // as an edge and the Args as nodes, then the single-destination requirement means that the |
| // shuffle graph consists of two kinds of subgraphs: |
| // |
| // - Spanning trees. We call these shifts. They can be executed as a sequence of Move |
| // instructions and don't usually require scratch registers. |
| // |
| // - Closed loops. These loops consist of nodes that have one successor and one predecessor, so |
| // there is no way to "get into" the loop from outside of it. These can be executed using swaps |
| // or by saving one of the Args to a scratch register and executing it as a shift. |
| HashSet<Arg> dsts; |
| |
| for (unsigned i = 0; i < inst.args.size(); ++i) { |
| Arg arg = inst.args[i]; |
| unsigned mode = i % 3; |
| |
| if (mode == 2) { |
| // It's the width. |
| if (!arg.isWidthArg()) |
| return false; |
| continue; |
| } |
| |
| // The source can be an immediate. |
| if (!mode) { |
| if (arg.isSomeImm()) |
| continue; |
| |
| if (!arg.isCompatibleBank(inst.args[i + 1])) |
| return false; |
| } else { |
| ASSERT(mode == 1); |
| if (!dsts.add(arg).isNewEntry) |
| return false; |
| } |
| |
| if (arg.isTmp() || arg.isMemory()) |
| continue; |
| |
| return false; |
| } |
| |
| // No destination register may appear in any address expressions. The lowering can't handle it |
| // and it's not useful for the way we end up using Shuffles. Normally, Shuffles only used for |
| // stack addresses and non-stack registers. |
| for (Arg& arg : inst.args) { |
| if (!arg.isMemory()) |
| continue; |
| bool ok = true; |
| arg.forEachTmpFast( |
| [&] (Tmp tmp) { |
| if (dsts.contains(tmp)) |
| ok = false; |
| }); |
| if (!ok) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| MacroAssembler::Jump ShuffleCustom::generate(Inst& inst, CCallHelpers&, GenerationContext&) |
| { |
| dataLog("FATAL: Unlowered shuffle: ", inst, "\n"); |
| UNREACHABLE_FOR_PLATFORM(); |
| return MacroAssembler::Jump(); |
| } |
| |
| bool WasmBoundsCheckCustom::isValidForm(Inst& inst) |
| { |
| if (inst.args.size() != 2) |
| return false; |
| if (!inst.args[0].isTmp() && !inst.args[0].isSomeImm()) |
| return false; |
| |
| return inst.args[1].isReg() || inst.args[1].isTmp() || inst.args[1].isSomeImm(); |
| } |
| |
| MacroAssembler::Jump WasmBoundsCheckCustom::generate(Inst& inst, CCallHelpers& jit, GenerationContext& context) |
| { |
| WasmBoundsCheckValue* value = inst.origin->as<WasmBoundsCheckValue>(); |
| MacroAssembler::Jump outOfBounds = Inst(Air::Branch64, value, Arg::relCond(MacroAssembler::AboveOrEqual), inst.args[0], inst.args[1]).generate(jit, context); |
| |
| context.latePaths.append(createSharedTask<GenerationContext::LatePathFunction>( |
| [outOfBounds, value] (CCallHelpers& jit, Air::GenerationContext& context) { |
| outOfBounds.link(&jit); |
| switch (value->boundsType()) { |
| case WasmBoundsCheckValue::Type::Pinned: |
| context.code->wasmBoundsCheckGenerator()->run(jit, value->bounds().pinnedSize); |
| break; |
| |
| case WasmBoundsCheckValue::Type::Maximum: |
| context.code->wasmBoundsCheckGenerator()->run(jit, InvalidGPRReg); |
| break; |
| } |
| })); |
| |
| // We said we were not a terminal. |
| return MacroAssembler::Jump(); |
| } |
| |
| } } } // namespace JSC::B3::Air |
| |
| #endif // ENABLE(B3_JIT) |
| |