Bug 1523993: Spew debug information for wasm calls; r=luke

Differential Revision: https://phabricator.services.mozilla.com/D18342

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Benjamin Bouvier 2019-02-06 16:38:24 +00:00
parent 2ce2c5e407
commit a371e5fe19
18 changed files with 401 additions and 10 deletions

View File

@ -770,6 +770,9 @@ enum ABIFunctionType {
// int f(double)
Args_Int_Double = Args_General0 | (ArgType_Double << ArgType_Shift),
// int f(float32)
Args_Int_Float32 = Args_General0 | (ArgType_Float32 << ArgType_Shift),
// float f(float)
Args_Float32_Float32 =
(ArgType_Float32 << RetType_Shift) | (ArgType_Float32 << ArgType_Shift),

View File

@ -278,6 +278,8 @@ DefaultJitOptions::DefaultJitOptions() {
SET_DEFAULT(enableWasmJitExit, true);
SET_DEFAULT(enableWasmJitEntry, true);
SET_DEFAULT(enableWasmIonFastCalls, true);
SET_DEFAULT(enableWasmImportCallSpew, false);
SET_DEFAULT(enableWasmFuncCallSpew, false);
#endif
}

View File

@ -79,6 +79,8 @@ struct DefaultJitOptions {
bool enableWasmJitExit;
bool enableWasmJitEntry;
bool enableWasmIonFastCalls;
bool enableWasmImportCallSpew;
bool enableWasmFuncCallSpew;
#endif
uint32_t baselineWarmUpThreshold;
uint32_t exceptionBailoutThreshold;

View File

@ -3208,6 +3208,15 @@ CodeOffset MacroAssembler::callWithABI(wasm::BytecodeOffset bytecode,
return raOffset;
}
void MacroAssembler::callDebugWithABI(wasm::SymbolicAddress imm,
MoveOp::Type result) {
MOZ_ASSERT(!wasm::NeedsBuiltinThunk(imm));
uint32_t stackAdjust;
callWithABIPre(&stackAdjust, /* callFromWasm = */ false);
call(imm);
callWithABIPost(stackAdjust, result, /* callFromWasm = */ false);
}
// ===============================================================
// Exit frame footer.

View File

@ -589,6 +589,7 @@ class MacroAssembler : public MacroAssemblerSpecific {
CodeOffset callWithABI(wasm::BytecodeOffset offset, wasm::SymbolicAddress fun,
MoveOp::Type result = MoveOp::GENERAL);
void callDebugWithABI(wasm::SymbolicAddress fun, MoveOp::Type result = MoveOp::GENERAL);
private:
// Reinitialize the variables which have to be cleared before making a call

View File

@ -4329,7 +4329,6 @@ void MacroAssembler::popReturnAddress() { pop(lr); }
// ABI function calls.
void MacroAssembler::setupUnalignedABICall(Register scratch) {
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
setupABICall();
dynamicAlignment_ = true;

View File

@ -2332,6 +2332,8 @@ typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1,
int32_t arg2);
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1,
int32_t arg2, int32_t arg3);
typedef int32_t (*Prototype_Int_Float32)(float arg0);
typedef float (*Prototype_Float32_Float32)(float arg0);
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
typedef float (*Prototype_Float32_IntInt)(int arg0, int arg1);
@ -2541,6 +2543,19 @@ void Simulator::softwareInterrupt(SimInstruction* instr) {
set_register(r0, res);
break;
}
case Args_Int_Float32: {
float fval0;
if (UseHardFpABI()) {
get_float_from_s_register(0, &fval0);
} else {
fval0 = mozilla::BitwiseCast<float>(arg0);
}
auto target = reinterpret_cast<Prototype_Int_Float32>(external);
int32_t res = target(fval0);
scratchVolatileRegisters(/* scratchFloat = true */);
set_register(r0, res);
break;
}
case Args_Double_Double: {
double dval0, dval1;
int32_t ival;

View File

@ -302,7 +302,6 @@ void MacroAssembler::subFromStackPtr(Imm32 imm32) {
// ABI function calls.
void MacroAssembler::setupUnalignedABICall(Register scratch) {
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
setupABICall();
dynamicAlignment_ = true;

View File

@ -300,7 +300,6 @@ void MacroAssembler::subFromStackPtr(Imm32 imm32) {
// ABI function calls.
void MacroAssembler::setupUnalignedABICall(Register scratch) {
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
setupABICall();
dynamicAlignment_ = true;

View File

@ -467,6 +467,33 @@ static inline void* FuncCast(F* funcPtr, ABIFunctionType abiType) {
return pf;
}
#ifdef WASM_CODEGEN_DEBUG
void wasm::PrintI32(int32_t val)
{
fprintf(stderr, "i32(%d) ", val);
}
void wasm::PrintPtr(uint8_t* val)
{
fprintf(stderr, "ptr(%p) ", val);
}
void wasm::PrintF32(float val)
{
fprintf(stderr, "f32(%f) ", val);
}
void wasm::PrintF64(double val)
{
fprintf(stderr, "f64(%lf) ", val);
}
void wasm::PrintText(const char* out)
{
fprintf(stderr, "%s", out);
}
#endif
static void* AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
switch (imm) {
case SymbolicAddress::HandleDebugTrap:
@ -669,6 +696,23 @@ static void* AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
#if defined(JS_CODEGEN_MIPS32)
case SymbolicAddress::js_jit_gAtomic64Lock:
return &js::jit::gAtomic64Lock;
#endif
#ifdef WASM_CODEGEN_DEBUG
case SymbolicAddress::PrintI32:
*abiType = Args_General1;
return FuncCast(PrintI32, *abiType);
case SymbolicAddress::PrintPtr:
*abiType = Args_General1;
return FuncCast(PrintPtr, *abiType);
case SymbolicAddress::PrintF32:
*abiType = Args_Int_Float32;
return FuncCast(PrintF32, *abiType);
case SymbolicAddress::PrintF64:
*abiType = Args_Int_Double;
return FuncCast(PrintF64, *abiType);
case SymbolicAddress::PrintText:
*abiType = Args_General1;
return FuncCast(PrintText, *abiType);
#endif
case SymbolicAddress::Limit:
break;
@ -693,6 +737,13 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
case SymbolicAddress::CoerceInPlace_ToNumber:
#if defined(JS_CODEGEN_MIPS32)
case SymbolicAddress::js_jit_gAtomic64Lock:
#endif
#ifdef WASM_CODEGEN_DEBUG
case SymbolicAddress::PrintI32:
case SymbolicAddress::PrintPtr:
case SymbolicAddress::PrintF32:
case SymbolicAddress::PrintF64:
case SymbolicAddress::PrintText: // Used only in stubs
#endif
return false;
case SymbolicAddress::ToInt32:
@ -993,13 +1044,13 @@ bool wasm::EnsureBuiltinThunksInitialized() {
allocSize - masm.bytesNeeded());
masm.processCodeLabels(thunks->codeBase);
PatchDebugSymbolicAccesses(thunks->codeBase, masm);
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.symbolicAccesses().empty());
ExecutableAllocator::cacheFlush(thunks->codeBase, thunks->codeSize);
if (!ExecutableAllocator::makeExecutable(thunks->codeBase,

View File

@ -55,6 +55,14 @@ void* MaybeGetBuiltinThunk(HandleFunction f, const FuncType& funcType);
void ReleaseBuiltinThunks();
#ifdef WASM_CODEGEN_DEBUG
void PrintI32(int32_t val);
void PrintF32(float val);
void PrintF64(double val);
void PrintPtr(uint8_t* val);
void PrintText(const char* out);
#endif
} // namespace wasm
} // namespace js

View File

@ -686,7 +686,6 @@ bool LazyStubTier::createMany(const Uint32Vector& funcExportIndices,
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.symbolicAccesses().empty());
if (masm.oom()) {
return false;
@ -718,6 +717,7 @@ bool LazyStubTier::createMany(const Uint32Vector& funcExportIndices,
return false;
masm.executableCopy(codePtr, /* flushICache = */ false);
PatchDebugSymbolicAccesses(codePtr, masm);
memset(codePtr + masm.bytesNeeded(), 0, codeLength - masm.bytesNeeded());
for (const CodeLabel& label : masm.codeLabels()) {
@ -1448,3 +1448,29 @@ uint8_t* Code::serialize(uint8_t* cursor, const LinkData& linkData) const {
*out = code;
return cursor;
}
void wasm::PatchDebugSymbolicAccesses(uint8_t* codeBase, MacroAssembler& masm)
{
#ifdef WASM_CODEGEN_DEBUG
for (auto& access : masm.symbolicAccesses()) {
switch (access.target) {
case SymbolicAddress::PrintI32:
case SymbolicAddress::PrintPtr:
case SymbolicAddress::PrintF32:
case SymbolicAddress::PrintF64:
case SymbolicAddress::PrintText:
break;
default:
MOZ_CRASH("unexpected symbol in PatchDebugSymbolicAccesses");
}
ABIFunctionType abiType;
void* target = AddressOf(access.target, &abiType);
uint8_t* patchAt = codeBase + access.patchAt.offset();
Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
PatchedImmPtr(target),
PatchedImmPtr((void*)-1));
}
#else
MOZ_ASSERT(masm.symbolicAccesses().empty());
#endif
}

View File

@ -724,6 +724,8 @@ class Code : public ShareableBase<Code> {
Metadata& metadata, SharedCode* code);
};
void PatchDebugSymbolicAccesses(uint8_t* codeBase, jit::MacroAssembler& masm);
} // namespace wasm
} // namespace js

View File

@ -1376,6 +1376,13 @@ static const char* ThunkedNativeToDescription(SymbolicAddress func) {
#if defined(JS_CODEGEN_MIPS32)
case SymbolicAddress::js_jit_gAtomic64Lock:
MOZ_CRASH();
#endif
#ifdef WASM_CODEGEN_DEBUG
case SymbolicAddress::PrintI32:
case SymbolicAddress::PrintPtr:
case SymbolicAddress::PrintF32:
case SymbolicAddress::PrintF64:
case SymbolicAddress::PrintText:
#endif
case SymbolicAddress::Limit:
break;

View File

@ -1348,35 +1348,44 @@ bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) {
return false;
}
DebugCodegen(DebugChannel::Function, "wasm-function[%d]; arguments ", funcIndex);
RootedValue v(cx);
for (unsigned i = 0; i < func.funcType().args().length(); ++i) {
v = i < args.length() ? args[i] : UndefinedValue();
switch (func.funcType().arg(i).code()) {
case ValType::I32:
if (!ToInt32(cx, v, (int32_t*)&exportArgs[i])) {
DebugCodegen(DebugChannel::Function, "call to ToInt32 failed!\n");
return false;
}
DebugCodegen(DebugChannel::Function, "i32(%d) ", *(int32_t*)&exportArgs[i]);
break;
case ValType::I64:
MOZ_CRASH("unexpected i64 flowing into callExport");
case ValType::F32:
if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) {
DebugCodegen(DebugChannel::Function, "call to RoundFloat32 failed!\n");
return false;
}
DebugCodegen(DebugChannel::Function, "f32(%f) ", *(float*)&exportArgs[i]);
break;
case ValType::F64:
if (!ToNumber(cx, v, (double*)&exportArgs[i])) {
DebugCodegen(DebugChannel::Function, "call to ToNumber failed!\n");
return false;
}
DebugCodegen(DebugChannel::Function, "f64(%lf) ", *(double*)&exportArgs[i]);
break;
case ValType::Ref:
MOZ_CRASH("temporarily unsupported Ref type in callExport");
case ValType::AnyRef: {
RootedAnyRef ar(cx, AnyRef::null());
if (!BoxAnyRef(cx, v, &ar)) {
DebugCodegen(DebugChannel::Function, "call to BoxAnyRef failed!\n");
return false;
}
*(void**)&exportArgs[i] = ar.get().forCompiledCode();
DebugCodegen(DebugChannel::Function, "ptr(%p) ", *(void**)&exportArgs[i]);
break;
}
case ValType::NullRef: {
@ -1385,6 +1394,8 @@ bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) {
}
}
DebugCodegen(DebugChannel::Function, "\n");
{
JitActivation activation(cx);
@ -1417,31 +1428,38 @@ bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) {
void* retAddr = &exportArgs[0];
DebugCodegen(DebugChannel::Function, "wasm-function[%d]; returns ", funcIndex);
switch (func.funcType().ret().code()) {
case ExprType::Void:
args.rval().set(UndefinedValue());
DebugCodegen(DebugChannel::Function, "void");
break;
case ExprType::I32:
args.rval().set(Int32Value(*(int32_t*)retAddr));
DebugCodegen(DebugChannel::Function, "i32(%d)", *(int32_t*)retAddr);
break;
case ExprType::I64:
MOZ_CRASH("unexpected i64 flowing from callExport");
case ExprType::F32:
args.rval().set(NumberValue(*(float*)retAddr));
DebugCodegen(DebugChannel::Function, "f32(%f)", *(float*)retAddr);
break;
case ExprType::F64:
args.rval().set(NumberValue(*(double*)retAddr));
DebugCodegen(DebugChannel::Function, "f64(%lf)", *(double*)retAddr);
break;
case ExprType::Ref:
MOZ_CRASH("temporarily unsupported Ref type in callExport");
case ExprType::AnyRef:
args.rval().set(UnboxAnyRef(AnyRef::fromCompiledCode(*(void**)retAddr)));
DebugCodegen(DebugChannel::Function, "ptr(%p)", *(void**)retAddr);
break;
case ExprType::NullRef:
MOZ_CRASH("NullRef not expressible");
case ExprType::Limit:
MOZ_CRASH("Limit");
}
DebugCodegen(DebugChannel::Function, "\n");
return true;
}

View File

@ -37,6 +37,126 @@ typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
#ifdef WASM_CODEGEN_DEBUG
template<class Closure>
static void GenPrint(DebugChannel channel, MacroAssembler& masm, Maybe<Register> taken,
Closure passArgAndCall)
{
if (!IsCodegenDebugEnabled(channel)){
return;
}
AllocatableRegisterSet regs(RegisterSet::All());
LiveRegisterSet save(regs.asLiveSet());
masm.PushRegsInMask(save);
if (taken) {
regs.take(taken.value());
}
Register temp = regs.takeAnyGeneral();
{
MOZ_ASSERT(MaybeGetJitContext(), "codegen debug checks require a jit context");
masm.setupUnalignedABICall(temp);
passArgAndCall(IsCompilingWasm(), temp);
}
masm.PopRegsInMask(save);
}
static void GenPrintf(DebugChannel channel, MacroAssembler& masm, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
UniqueChars str = JS_vsmprintf(fmt, ap);
va_end(ap);
// We leak the strings! This is done only for debugging purposes, and doing
// the right thing is cumbersome (in Ion, it'd mean add a vec of strings to
// the IonScript; in wasm, it'd mean add it to the current Module and
// serialize it properly).
const char* text = str.release();
GenPrint(channel, masm, Nothing(), [&](bool inWasm, Register temp) {
masm.movePtr(ImmPtr((void*)text, ImmPtr::NoCheckToken()), temp);
masm.passABIArg(temp);
if (inWasm) {
masm.callDebugWithABI(SymbolicAddress::PrintText);
} else {
masm.callWithABI((void*)PrintText, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
}
});
}
static void GenPrintIsize(DebugChannel channel, MacroAssembler& masm, const Register& src)
{
GenPrint(channel, masm, Some(src), [&](bool inWasm, Register _temp) {
masm.passABIArg(src);
if (inWasm) {
masm.callDebugWithABI(SymbolicAddress::PrintI32);
} else {
masm.callWithABI((void*)PrintI32, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
}
});
}
static void GenPrintPtr(DebugChannel channel, MacroAssembler& masm, const Register& src)
{
GenPrint(channel, masm, Some(src), [&](bool inWasm, Register _temp) {
masm.passABIArg(src);
if (inWasm) {
masm.callDebugWithABI(SymbolicAddress::PrintPtr);
} else {
masm.callWithABI((void*)PrintPtr, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
}
});
}
static void GenPrintI64(DebugChannel channel, MacroAssembler& masm, const Register64& src)
{
# if JS_BITS_PER_WORD == 64
GenPrintf(channel, masm, "i64 ");
GenPrintIsize(channel, masm, src.reg);
# else
GenPrintf(channel, masm, "i64(");
GenPrintIsize(channel, masm, src.low);
GenPrintIsize(channel, masm, src.high);
GenPrintf(channel, masm, ") ");
# endif
}
static void GenPrintF32(DebugChannel channel, MacroAssembler& masm, const FloatRegister& src)
{
GenPrint(channel, masm, Nothing(), [&](bool inWasm, Register temp) {
masm.passABIArg(src, MoveOp::FLOAT32);
if (inWasm) {
masm.callDebugWithABI(SymbolicAddress::PrintF32);
} else {
masm.callWithABI((void*)PrintF32, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
}
});
}
static void GenPrintF64(DebugChannel channel, MacroAssembler& masm, const FloatRegister& src)
{
GenPrint(channel, masm, Nothing(), [&](bool inWasm, Register temp) {
masm.passABIArg(src, MoveOp::DOUBLE);
if (inWasm) {
masm.callDebugWithABI(SymbolicAddress::PrintF64);
} else {
masm.callWithABI((void*)PrintF64, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
}
});
}
#else
static void GenPrintf(DebugChannel channel, MacroAssembler& masm, const char* fmt, ...) {}
static void GenPrintIsize(DebugChannel channel, MacroAssembler& masm, const Register& src) {}
static void GenPrintPtr(DebugChannel channel, MacroAssembler& masm, const Register& src) {}
static void GenPrintI64(DebugChannel channel, MacroAssembler& masm, const Register64& src) {}
static void GenPrintF32(DebugChannel channel, MacroAssembler& masm, const FloatRegister& src) {}
static void GenPrintF64(DebugChannel channel, MacroAssembler& masm, const FloatRegister& src) {}
#endif
static bool FinishOffsets(MacroAssembler& masm, Offsets* offsets) {
// On old ARM hardware, constant pools could be inserted and they need to
// be flushed before considering the size of the masm.
@ -556,6 +676,8 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
Register scratchG = ScratchIonEntry;
ValueOperand scratchV = ScratchValIonEntry;
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; arguments ", fe.funcIndex());
// We do two loops:
// - one loop up-front will make sure that all the Value tags fit the
// expected signature argument types. If at least one inline conversion
@ -685,6 +807,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
case MIRType::Int32: {
Register target = isStackArg ? ScratchIonEntry : iter->gpr();
masm.unboxInt32(argv, target);
GenPrintIsize(DebugChannel::Function, masm, target);
if (isStackArg) {
masm.storePtr(target, Address(sp, iter->offsetFromArgBase()));
}
@ -694,6 +817,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
FloatRegister target = isStackArg ? ABINonArgDoubleReg : iter->fpu();
masm.unboxDouble(argv, ABINonArgDoubleReg);
masm.convertDoubleToFloat32(ABINonArgDoubleReg, target);
GenPrintF32(DebugChannel::Function, masm, target.asSingle());
if (isStackArg) {
masm.storeFloat32(target, Address(sp, iter->offsetFromArgBase()));
}
@ -702,6 +826,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
case MIRType::Double: {
FloatRegister target = isStackArg ? ABINonArgDoubleReg : iter->fpu();
masm.unboxDouble(argv, target);
GenPrintF64(DebugChannel::Function, masm, target);
if (isStackArg) {
masm.storeDouble(target, Address(sp, iter->offsetFromArgBase()));
}
@ -711,6 +836,8 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
}
}
GenPrintf(DebugChannel::Function, masm, "\n");
// Setup wasm register state.
masm.loadWasmPinnedRegsFromTls();
@ -729,23 +856,29 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
// Pop arguments.
masm.freeStack(frameSize);
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; returns ", fe.funcIndex());
// Store the return value in the JSReturnOperand.
switch (fe.funcType().ret().code()) {
case ExprType::Void:
GenPrintf(DebugChannel::Function, masm, "void");
masm.moveValue(UndefinedValue(), JSReturnOperand);
break;
case ExprType::I32:
GenPrintIsize(DebugChannel::Function, masm, ReturnReg);
masm.boxNonDouble(JSVAL_TYPE_INT32, ReturnReg, JSReturnOperand);
break;
case ExprType::F32: {
masm.canonicalizeFloat(ReturnFloat32Reg);
masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg);
GenPrintF64(DebugChannel::Function, masm, ReturnDoubleReg);
ScratchDoubleScope fpscratch(masm);
masm.boxDouble(ReturnDoubleReg, JSReturnOperand, fpscratch);
break;
}
case ExprType::F64: {
masm.canonicalizeDouble(ReturnDoubleReg);
GenPrintF64(DebugChannel::Function, masm, ReturnDoubleReg);
ScratchDoubleScope fpscratch(masm);
masm.boxDouble(ReturnDoubleReg, JSReturnOperand, fpscratch);
break;
@ -764,6 +897,8 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
MOZ_CRASH("Limit");
}
GenPrintf(DebugChannel::Function, masm, "\n");
MOZ_ASSERT(masm.framePushed() == 0);
#ifdef JS_CODEGEN_ARM64
masm.loadPtr(Address(sp, 0), lr);
@ -868,10 +1003,18 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
masm.reserveStack(bytesNeeded);
}
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; arguments ", fe.funcIndex());
for (ABIArgValTypeIter iter(fe.funcType().args()); !iter.done(); iter++) {
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != scratch);
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != FramePointer);
if (iter->kind() != ABIArg::Stack) {
switch (iter.mirType()) {
case MIRType::Int32: GenPrintIsize(DebugChannel::Function, masm, iter->gpr()); break;
case MIRType::Float32: GenPrintF32(DebugChannel::Function, masm, iter->fpu()); break;
case MIRType::Double: GenPrintF64(DebugChannel::Function, masm, iter->fpu()); break;
default: MOZ_CRASH("ion to wasm fast path can only handle i32/f32/f64");
}
continue;
}
@ -880,19 +1023,23 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
const JitCallStackArg& stackArg = stackArgs[iter.index()];
switch (stackArg.tag()) {
case JitCallStackArg::Tag::Imm32:
GenPrintf(DebugChannel::Function, masm, "%d ", stackArg.imm32());
masm.storePtr(ImmWord(stackArg.imm32()), dst);
break;
case JitCallStackArg::Tag::GPR:
MOZ_ASSERT(stackArg.gpr() != scratch);
MOZ_ASSERT(stackArg.gpr() != FramePointer);
GenPrintIsize(DebugChannel::Function, masm, stackArg.gpr());
masm.storePtr(stackArg.gpr(), dst);
break;
case JitCallStackArg::Tag::FPU:
switch (iter.mirType()) {
case MIRType::Double:
GenPrintF64(DebugChannel::Function, masm, stackArg.fpu());
masm.storeDouble(stackArg.fpu(), dst);
break;
case MIRType::Float32:
GenPrintF32(DebugChannel::Function, masm, stackArg.fpu());
masm.storeFloat32(stackArg.fpu(), dst);
break;
default:
@ -907,6 +1054,7 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
switch (iter.mirType()) {
case MIRType::Double: {
ScratchDoubleScope fpscratch(masm);
GenPrintF64(DebugChannel::Function, masm, fpscratch);
masm.loadDouble(src, fpscratch);
masm.storeDouble(fpscratch, dst);
break;
@ -914,11 +1062,13 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
case MIRType::Float32: {
ScratchFloat32Scope fpscratch(masm);
masm.loadFloat32(src, fpscratch);
GenPrintF32(DebugChannel::Function, masm, fpscratch);
masm.storeFloat32(fpscratch, dst);
break;
}
case MIRType::Int32:
masm.loadPtr(src, scratch);
GenPrintIsize(DebugChannel::Function, masm, scratch);
masm.storePtr(scratch, dst);
break;
default:
@ -932,6 +1082,8 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
}
}
GenPrintf(DebugChannel::Function, masm, "\n");
// Load tls; from now on, WasmTlsReg is live.
masm.movePtr(ImmPtr(inst.tlsData()), WasmTlsReg);
masm.loadWasmPinnedRegsFromTls();
@ -950,17 +1102,23 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
masm.exceptionLabel());
// Store the return value in the appropriate place.
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; returns ", fe.funcIndex());
switch (fe.funcType().ret().code()) {
case wasm::ExprType::Void:
masm.moveValue(UndefinedValue(), JSReturnOperand);
GenPrintf(DebugChannel::Function, masm, "void");
break;
case wasm::ExprType::I32:
// The return value is in ReturnReg, which is what Ion expects.
GenPrintIsize(DebugChannel::Function, masm, ReturnReg);
break;
case wasm::ExprType::F32:
masm.canonicalizeFloat(ReturnFloat32Reg);
GenPrintF32(DebugChannel::Function, masm, ReturnFloat32Reg);
break;
case wasm::ExprType::F64:
masm.canonicalizeDouble(ReturnDoubleReg);
GenPrintF64(DebugChannel::Function, masm, ReturnDoubleReg);
break;
case wasm::ExprType::Ref:
case wasm::ExprType::AnyRef:
@ -972,6 +1130,8 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
MOZ_CRASH("Limit");
}
GenPrintf(DebugChannel::Function, masm, "\n");
// Free args + frame descriptor.
masm.leaveExitFrame(bytesNeeded + ExitFrameLayout::Size());
@ -987,39 +1147,50 @@ static void StackCopy(MacroAssembler& masm, MIRType type, Register scratch,
Address src, Address dst) {
if (type == MIRType::Int32) {
masm.load32(src, scratch);
GenPrintIsize(DebugChannel::Import, masm, scratch);
masm.store32(scratch, dst);
} else if (type == MIRType::Int64) {
#if JS_BITS_PER_WORD == 32
GenPrintf(DebugChannel::Import, masm, "i64(");
masm.load32(LowWord(src), scratch);
GenPrintIsize(DebugChannel::Import, masm, scratch);
masm.store32(scratch, LowWord(dst));
masm.load32(HighWord(src), scratch);
GenPrintIsize(DebugChannel::Import, masm, scratch);
masm.store32(scratch, HighWord(dst));
GenPrintf(DebugChannel::Import, masm, ") ");
#else
Register64 scratch64(scratch);
masm.load64(src, scratch64);
GenPrintIsize(DebugChannel::Import, masm, scratch);
masm.store64(scratch64, dst);
#endif
} else if (type == MIRType::Pointer) {
masm.loadPtr(src, scratch);
GenPrintPtr(DebugChannel::Import, masm, scratch);
masm.storePtr(scratch, dst);
} else if (type == MIRType::Float32) {
ScratchFloat32Scope fpscratch(masm);
masm.loadFloat32(src, fpscratch);
GenPrintF32(DebugChannel::Import, masm, fpscratch);
masm.storeFloat32(fpscratch, dst);
} else {
MOZ_ASSERT(type == MIRType::Double);
ScratchDoubleScope fpscratch(masm);
masm.loadDouble(src, fpscratch);
GenPrintF64(DebugChannel::Import, masm, fpscratch);
masm.storeDouble(fpscratch, dst);
}
}
typedef bool ToValue;
static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
static void FillArgumentArray(MacroAssembler& masm, unsigned funcImportIndex,
const ValTypeVector& args,
unsigned argOffset,
unsigned offsetToCallerStackArgs,
Register scratch, ToValue toValue) {
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; arguments ", funcImportIndex);
for (ABIArgValTypeIter i(args); !i.done(); i++) {
Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
@ -1027,6 +1198,7 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
switch (i->kind()) {
case ABIArg::GPR:
if (type == MIRType::Int32) {
GenPrintIsize(DebugChannel::Import, masm, i->gpr());
if (toValue) {
masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dst);
} else {
@ -1037,18 +1209,21 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
if (toValue) {
masm.breakpoint();
} else {
GenPrintI64(DebugChannel::Import, masm, i->gpr64());
masm.store64(i->gpr64(), dst);
}
} else if (type == MIRType::Pointer) {
if (toValue) {
MOZ_CRASH("generating a jit exit for anyref NYI");
}
GenPrintPtr(DebugChannel::Import, masm, i->gpr());
masm.storePtr(i->gpr(), dst);
}
break;
#ifdef JS_CODEGEN_REGISTER_PAIR
case ABIArg::GPR_PAIR:
if (type == MIRType::Int64) {
GenPrintI64(DebugChannel::Import, masm, i->gpr64());
masm.store64(i->gpr64(), dst);
} else {
MOZ_CRASH("wasm uses hardfp for function calls.");
@ -1064,8 +1239,10 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
ScratchDoubleScope fpscratch(masm);
masm.moveDouble(srcReg, fpscratch);
masm.canonicalizeDouble(fpscratch);
GenPrintF64(DebugChannel::Import, masm, fpscratch);
masm.storeDouble(fpscratch, dst);
} else {
GenPrintF64(DebugChannel::Import, masm, srcReg);
masm.storeDouble(srcReg, dst);
}
} else {
@ -1075,12 +1252,14 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
ScratchDoubleScope fpscratch(masm);
masm.convertFloat32ToDouble(srcReg, fpscratch);
masm.canonicalizeDouble(fpscratch);
GenPrintF64(DebugChannel::Import, masm, fpscratch);
masm.storeDouble(fpscratch, dst);
} else {
// Preserve the NaN pattern in the input.
ScratchFloat32Scope fpscratch(masm);
masm.moveFloat32(srcReg, fpscratch);
masm.canonicalizeFloat(fpscratch);
GenPrintF32(DebugChannel::Import, masm, fpscratch);
masm.storeFloat32(fpscratch, dst);
}
}
@ -1092,6 +1271,7 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
if (toValue) {
if (type == MIRType::Int32) {
masm.load32(src, scratch);
GenPrintIsize(DebugChannel::Import, masm, scratch);
masm.storeValue(JSVAL_TYPE_INT32, scratch, dst);
} else if (type == MIRType::Int64) {
// We can't box int64 into Values (yet).
@ -1109,6 +1289,7 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
masm.loadDouble(src, dscratch);
}
masm.canonicalizeDouble(dscratch);
GenPrintF64(DebugChannel::Import, masm, dscratch);
masm.storeDouble(dscratch, dst);
}
} else {
@ -1120,6 +1301,7 @@ static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args,
MOZ_CRASH("Uninitialized ABIArg kind");
}
}
GenPrintf(DebugChannel::Import, masm, "\n");
}
// Generate a wrapper function with the standard intra-wasm call ABI which
@ -1160,7 +1342,9 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm,
Address src(masm.getStackPointer(),
offsetToCallerStackArgs + i->offsetFromArgBase());
Address dst(masm.getStackPointer(), i->offsetFromArgBase());
GenPrintf(DebugChannel::Import, masm, "calling exotic import function with arguments: ");
StackCopy(masm, i.mirType(), scratch, src, dst);
GenPrintf(DebugChannel::Import, masm, "\n");
}
// Call the import exit stub.
@ -1247,7 +1431,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
// Fill the argument array.
unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
Register scratch = ABINonArgReturnReg0;
FillArgumentArray(masm, fi.funcType().args(), argOffset,
FillArgumentArray(masm, funcImportIndex, fi.funcType().args(), argOffset,
offsetToCallerStackArgs, scratch, ToValue(false));
// Prepare the arguments for the call to Instance::callImport_*.
@ -1301,11 +1485,15 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
case ExprType::Void:
masm.call(SymbolicAddress::CallImport_Void);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
GenPrintf(DebugChannel::Import, masm, "void");
break;
case ExprType::I32:
masm.call(SymbolicAddress::CallImport_I32);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.load32(argv, ReturnReg);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
GenPrintIsize(DebugChannel::Import, masm, ReturnReg);
break;
case ExprType::I64:
masm.call(SymbolicAddress::CallImport_I64);
@ -1316,11 +1504,15 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnDoubleReg);
masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
GenPrintF32(DebugChannel::Import, masm, ReturnFloat32Reg);
break;
case ExprType::F64:
masm.call(SymbolicAddress::CallImport_F64);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnDoubleReg);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
GenPrintF64(DebugChannel::Import, masm, ReturnDoubleReg);
break;
case ExprType::Ref:
MOZ_CRASH("No Ref support here yet");
@ -1328,6 +1520,8 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
masm.call(SymbolicAddress::CallImport_AnyRef);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadPtr(argv, ReturnReg);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
GenPrintPtr(DebugChannel::Import, masm, ReturnReg);
break;
case ExprType::NullRef:
MOZ_CRASH("NullRef not expressible");
@ -1335,6 +1529,8 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
MOZ_CRASH("Limit");
}
GenPrintf(DebugChannel::Import, masm, "\n");
// The native ABI preserves the TLS, heap and global registers since they
// are non-volatile.
MOZ_ASSERT(NonVolatileRegs.has(WasmTlsReg));
@ -1353,7 +1549,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
// Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout.
static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, unsigned funcImportIndex,
Label* throwLabel, JitExitOffsets* offsets) {
AssertExpectedSP(masm);
masm.setFramePushed(0);
@ -1420,7 +1616,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
// 5. Fill the arguments
unsigned offsetToCallerStackArgs = jitFramePushed + sizeof(Frame);
FillArgumentArray(masm, fi.funcType().args(), argOffset,
FillArgumentArray(masm, funcImportIndex, fi.funcType().args(), argOffset,
offsetToCallerStackArgs, scratch, ToValue(true));
argOffset += fi.funcType().args().length() * sizeof(Value);
MOZ_ASSERT(argOffset == sizeOfThisAndArgs + sizeOfPreFrame + frameAlignExtra);
@ -1493,22 +1689,28 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
}
#endif
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ", funcImportIndex);
Label oolConvert;
switch (fi.funcType().ret().code()) {
case ExprType::Void:
GenPrintf(DebugChannel::Import, masm, "void");
break;
case ExprType::I32:
masm.truncateValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg,
&oolConvert);
GenPrintIsize(DebugChannel::Import, masm, ReturnReg);
break;
case ExprType::I64:
masm.breakpoint();
break;
case ExprType::F32:
masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert);
GenPrintF32(DebugChannel::Import, masm, ReturnFloat32Reg);
break;
case ExprType::F64:
masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
GenPrintF64(DebugChannel::Import, masm, ReturnDoubleReg);
break;
case ExprType::Ref:
MOZ_CRASH("ref returned by import (jit exit) NYI");
@ -1522,6 +1724,8 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
MOZ_CRASH("Limit");
}
GenPrintf(DebugChannel::Import, masm, "\n");
Label done;
masm.bind(&done);
@ -1971,7 +2175,7 @@ bool wasm::GenerateStubs(const ModuleEnvironment& env,
}
JitExitOffsets jitOffsets;
if (!GenerateImportJitExit(masm, fi, &throwLabel, &jitOffsets)) {
if (!GenerateImportJitExit(masm, fi, funcIndex, &throwLabel, &jitOffsets)) {
return false;
}
if (!code->codeRanges.emplaceBack(funcIndex, jitOffsets)) {

View File

@ -960,3 +960,28 @@ void wasm::Log(JSContext* cx, const char* fmt, ...) {
va_end(args);
}
#ifdef WASM_CODEGEN_DEBUG
bool wasm::IsCodegenDebugEnabled(DebugChannel channel)
{
switch (channel) {
case DebugChannel::Function:
return JitOptions.enableWasmFuncCallSpew;
case DebugChannel::Import:
return JitOptions.enableWasmImportCallSpew;
}
return false;
}
#endif
void wasm::DebugCodegen(DebugChannel channel, const char* fmt, ...) {
#ifdef WASM_CODEGEN_DEBUG
if (!IsCodegenDebugEnabled(channel)) {
return;
}
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
#endif
}

View File

@ -2082,6 +2082,13 @@ enum class SymbolicAddress {
StructNarrow,
#if defined(JS_CODEGEN_MIPS32)
js_jit_gAtomic64Lock,
#endif
#ifdef WASM_CODEGEN_DEBUG
PrintI32,
PrintPtr,
PrintF32,
PrintF64,
PrintText,
#endif
Limit
};
@ -2614,6 +2621,20 @@ class DebugFrame {
extern void Log(JSContext* cx, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
// Codegen debug support.
enum class DebugChannel {
Function,
Import,
};
#ifdef WASM_CODEGEN_DEBUG
bool IsCodegenDebugEnabled(DebugChannel channel);
#endif
void DebugCodegen(DebugChannel channel, const char* fmt, ...)
MOZ_FORMAT_PRINTF(2, 3);
} // namespace wasm
} // namespace js