blob: e82582bb5ed0222ff7443eea26238341598a55e8 [file] [log] [blame]
/*
* Copyright (C) 2015-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.
*/
#pragma once
#if ENABLE(B3_JIT)
#include "AirArg.h"
#include "AirKind.h"
#include "MacroAssembler.h"
#include <wtf/ScopedLambda.h>
namespace JSC {
class CCallHelpers;
class RegisterSet;
namespace B3 {
class Value;
namespace Air {
struct GenerationContext;
struct Inst {
typedef Vector<Arg, 3> ArgList;
Inst()
: origin(nullptr)
{
}
Inst(Kind kind, Value* origin)
: origin(origin)
, kind(kind)
{
}
template<typename... Arguments>
Inst(Kind kind, Value* origin, Arg arg, Arguments... arguments)
: args{ arg, arguments... }
, origin(origin)
, kind(kind)
{
}
Inst(Kind kind, Value* origin, const ArgList& arguments)
: args(arguments)
, origin(origin)
, kind(kind)
{
}
Inst(Kind kind, Value* origin, ArgList&& arguments)
: args(WTFMove(arguments))
, origin(origin)
, kind(kind)
{
}
explicit operator bool() const { return origin || kind || args.size(); }
void append() { }
template<typename... Arguments>
void append(Arg arg, Arguments... arguments)
{
args.append(arg);
append(arguments...);
}
// Note that these functors all avoid using "const" because we want to use them for things that
// edit IR. IR is meant to be edited; if you're carrying around a "const Inst&" then you're
// probably doing it wrong.
// This only walks those Tmps that are explicitly mentioned, and it doesn't tell you their role
// or type.
template<typename Functor>
void forEachTmpFast(const Functor& functor)
{
for (Arg& arg : args)
arg.forEachTmpFast(functor);
}
typedef void EachArgCallback(Arg&, Arg::Role, Bank, Width);
// Calls the functor with (arg, role, type, width). This function is auto-generated by
// opcode_generator.rb.
template<typename Functor>
void forEachArg(const Functor&);
// Calls the functor with (tmp, role, type, width).
template<typename Functor>
void forEachTmp(const Functor& functor)
{
forEachArg(
[&] (Arg& arg, Arg::Role role, Bank bank, Width width) {
arg.forEachTmp(role, bank, width, functor);
});
}
// Thing can be either Arg, Tmp, or StackSlot*.
template<typename Thing, typename Functor>
void forEach(const Functor&);
// Reports any additional registers clobbered by this operation. Note that for efficiency,
// extraClobberedRegs() only works for the Patch opcode.
RegisterSet extraClobberedRegs();
RegisterSet extraEarlyClobberedRegs();
// Iterate over all Def's that happen at the end of an instruction. You supply a pair
// instructions. The instructions must appear next to each other, in that order, in some basic
// block. You can pass null for the first instruction when analyzing what happens at the top of
// a basic block. You can pass null for the second instruction when analyzing what happens at the
// bottom of a basic block.
template<typename Thing, typename Functor>
static void forEachDef(Inst* prevInst, Inst* nextInst, const Functor&);
// Iterate over all Def's that happen at the end of this instruction, including extra clobbered
// registers. Note that Thing can only be Arg or Tmp when you use this functor.
template<typename Thing, typename Functor>
static void forEachDefWithExtraClobberedRegs(Inst* prevInst, Inst* nextInst, const Functor&);
// Some summaries about all arguments. These are useful for needsPadding().
bool hasEarlyDef();
bool hasLateUseOrDef();
// Check if there needs to be a padding Nop between these two instructions.
static bool needsPadding(Inst* prevInst, Inst* nextInst);
// Use this to report which registers are live. This should be done just before codegen. Note
// that for efficiency, reportUsedRegisters() only works for the Patch opcode.
void reportUsedRegisters(const RegisterSet&);
// Is this instruction in one of the valid forms right now? This function is auto-generated by
// opcode_generator.rb.
bool isValidForm();
// Assuming this instruction is in a valid form right now, will it still be in one of the valid
// forms if we put an Addr referencing the stack (or a StackSlot or CallArg, of course) in the
// given index? Spilling uses this: it walks the args by index to find Tmps that need spilling;
// if it finds one, it calls this to see if it can replace the Arg::Tmp with an Arg::Addr. If it
// finds a non-Tmp Arg, then it calls that Arg's forEachTmp to do a replacement that way.
//
// This function is auto-generated by opcode_generator.rb.
bool admitsStack(unsigned argIndex);
bool admitsStack(Arg&);
bool admitsExtendedOffsetAddr(unsigned argIndex);
bool admitsExtendedOffsetAddr(Arg&);
// Defined by opcode_generator.rb.
bool isTerminal();
// Returns true if this instruction can have any effects other than control flow or arguments.
bool hasNonArgNonControlEffects();
// Returns true if this instruction can have any effects other than what is implied by arguments.
// For example, "Move $42, (%rax)" will return false because the effect of storing to (%rax) is
// implied by the second argument.
bool hasNonArgEffects();
// Tells you if this operation has arg effects.
bool hasArgEffects();
// Tells you if this operation has non-control effects.
bool hasNonControlEffects() { return hasNonArgNonControlEffects() || hasArgEffects(); }
// Generate some code for this instruction. This is, like, literally our backend. If this is the
// terminal, it returns the jump that needs to be linked for the "then" case, with the "else"
// case being fall-through. This function is auto-generated by opcode_generator.rb.
MacroAssembler::Jump generate(CCallHelpers&, GenerationContext&);
// If source arguments benefits from being aliased to a destination argument,
// this return the index of the destination argument.
// The source are assumed to be at (index - 1) and (index - 2)
// For example,
// Add Tmp1, Tmp2, Tmp3
// returns 2 if 0 and 1 benefit from aliasing to Tmp3.
std::optional<unsigned> shouldTryAliasingDef();
// This computes a hash for comparing this to JSAir's Inst.
unsigned jsHash() const;
void dump(PrintStream&) const;
ArgList args;
Value* origin; // The B3::Value that this originated from.
Kind kind;
private:
template<typename Func>
void forEachArgSimple(const Func&);
void forEachArgCustom(ScopedLambda<EachArgCallback>);
};
} } } // namespace JSC::B3::Air
#endif // ENABLE(B3_JIT)