#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;
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 (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;
HashSet<Arg> dsts;
for (unsigned i = 0; i < inst.args.size(); ++i) {
Arg arg = inst.args[i];
unsigned mode = i % 3;
if (mode == 2) {
if (!arg.isWidthArg())
return false;
continue;
}
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;
}
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;
}
}));
return MacroAssembler::Jump();
}
} } }
#endif // ENABLE(B3_JIT)