Bug 1289054 - Part 18: Implement the 64bit variant of WasmTruncate on arm, r=bbouvier

This commit is contained in:
Hannes Verschore 2016-07-29 16:53:49 +02:00
Родитель 08728c529c
Коммит db11d480ae
9 изменённых файлов: 144 добавлений и 22 удалений

Просмотреть файл

@ -200,6 +200,30 @@ UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
return x % y; return x % y;
} }
static int64_t
TruncateDoubleToInt64(double input)
{
// Note: INT64_MAX is not representable in double. It is actually INT64_MAX + 1.
// Therefore also sending the failure value.
if (input >= double(INT64_MAX))
return 0x8000000000000000;
if (input < double(INT64_MIN))
return 0x8000000000000000;
return int64_t(input);
}
static uint64_t
TruncateDoubleToUint64(double input)
{
// Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
// Therefore also sending the failure value.
if (input >= double(UINT64_MAX))
return 0x8000000000000000;
if (input <= -1.0)
return 0x8000000000000000;
return uint64_t(input);
}
template <class F> template <class F>
static inline void* static inline void*
FuncCast(F* pf, ABIFunctionType type) FuncCast(F* pf, ABIFunctionType type)
@ -247,6 +271,10 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
return FuncCast(ModI64, Args_General4); return FuncCast(ModI64, Args_General4);
case SymbolicAddress::UModI64: case SymbolicAddress::UModI64:
return FuncCast(UModI64, Args_General4); return FuncCast(UModI64, Args_General4);
case SymbolicAddress::TruncateDoubleToUint64:
return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
case SymbolicAddress::TruncateDoubleToInt64:
return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
#if defined(JS_CODEGEN_ARM) #if defined(JS_CODEGEN_ARM)
case SymbolicAddress::aeabi_idivmod: case SymbolicAddress::aeabi_idivmod:
return FuncCast(__aeabi_idivmod, Args_General2); return FuncCast(__aeabi_idivmod, Args_General2);

Просмотреть файл

@ -901,6 +901,8 @@ enum class SymbolicAddress
UDivI64, UDivI64,
ModI64, ModI64,
UModI64, UModI64,
TruncateDoubleToInt64,
TruncateDoubleToUint64,
Limit Limit
}; };

Просмотреть файл

@ -762,10 +762,11 @@ enum {
ArgType_General = 0x1, ArgType_General = 0x1,
ArgType_Double = 0x2, ArgType_Double = 0x2,
ArgType_Float32 = 0x3, ArgType_Float32 = 0x3,
ArgType_Int64 = 0x4,
RetType_Shift = 0x0, RetType_Shift = 0x0,
ArgType_Shift = 0x2, ArgType_Shift = 0x3,
ArgType_Mask = 0x3 ArgType_Mask = 0x7
}; };
enum ABIFunctionType enum ABIFunctionType
@ -782,6 +783,9 @@ enum ABIFunctionType
Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)), Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)),
Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)), Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)),
// int64 f(double)
Args_Int64_Double = (ArgType_Int64 << RetType_Shift) | (ArgType_Double << ArgType_Shift),
// double f() // double f()
Args_Double_None = ArgType_Double << RetType_Shift, Args_Double_None = ArgType_Double << RetType_Shift,

Просмотреть файл

@ -2968,6 +2968,45 @@ CodeGeneratorARM::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
masm.bind(ool->rejoin()); masm.bind(ool->rejoin());
} }
void
CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
FloatRegister inputDouble = input;
Register64 output = ToOutRegister64(lir);
MWasmTruncateToInt64* mir = lir->mir();
MIRType fromType = mir->input()->type();
auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
addOutOfLineCode(ool, mir);
ScratchDoubleScope scratchScope(masm);
if (fromType == MIRType::Float32) {
inputDouble = ScratchDoubleReg;
masm.convertFloat32ToDouble(input, inputDouble);
}
masm.Push(input);
masm.setupUnalignedABICall(output.high);
masm.passABIArg(inputDouble, MoveOp::DOUBLE);
if (lir->mir()->isUnsigned())
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
else
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
masm.Pop(input);
masm.ma_cmp(output.high, Imm32(0x80000000));
masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal);
masm.ma_b(ool->entry(), Assembler::Equal);
masm.bind(ool->rejoin());
MOZ_ASSERT(ReturnReg64 == output);
}
void void
CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool) CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
{ {
@ -2989,39 +3028,57 @@ CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* oo
// Handle special values. // Handle special values.
Label fail; Label fail;
// By default test for the following inputs and bail:
// signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
// unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
// Note: we cannot always represent those exact values. As a result
// this changes the actual comparison a bit.
double minValue, maxValue; double minValue, maxValue;
if (ool->isUnsigned()) { Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
minValue = -1; Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
maxValue = double(UINT32_MAX) + 1.0; if (ool->toType() == MIRType::Int64) {
if (ool->isUnsigned()) {
minValue = -1;
maxValue = double(UINT64_MAX) + 1.0;
} else {
// In the float32/double range there exists no value between
// INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
minValue = double(INT64_MIN);
minCond = Assembler::DoubleLessThan;
maxValue = double(INT64_MAX) + 1.0;
}
} else { } else {
minValue = double(INT32_MIN) - 1.0; if (ool->isUnsigned()) {
maxValue = double(INT32_MAX) + 1.0; minValue = -1;
maxValue = double(UINT32_MAX) + 1.0;
} else {
if (fromType == MIRType::Float32) {
// In the float32 range there exists no value between
// INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
minValue = double(INT32_MIN);
minCond = Assembler::DoubleLessThan;
} else {
minValue = double(INT32_MIN) - 1.0;
}
maxValue = double(INT32_MAX) + 1.0;
}
} }
if (fromType == MIRType::Double) { if (fromType == MIRType::Double) {
scratch = scratchScope.doubleOverlay(); scratch = scratchScope.doubleOverlay();
masm.loadConstantDouble(minValue, scratch); masm.loadConstantDouble(minValue, scratch);
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &fail); masm.branchDouble(minCond, input, scratch, &fail);
masm.loadConstantDouble(maxValue, scratch); masm.loadConstantDouble(maxValue, scratch);
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail); masm.branchDouble(maxCond, input, scratch, &fail);
} else { } else {
MOZ_ASSERT(fromType == MIRType::Float32); MOZ_ASSERT(fromType == MIRType::Float32);
scratch = scratchScope.singleOverlay(); scratch = scratchScope.singleOverlay();
// For int32, float(minValue) rounds to INT32_MIN, we want to fail when
// input < float(minValue).
// For uint32, float(minValue) == -1, we want to fail when input <= -1.
auto condition = minValue == -1.0
? Assembler::DoubleLessThanOrEqual
: Assembler::DoubleLessThan;
masm.loadConstantFloat32(float(minValue), scratch); masm.loadConstantFloat32(float(minValue), scratch);
masm.branchFloat(condition, input, scratch, &fail); masm.branchFloat(minCond, input, scratch, &fail);
// maxValue is exactly represented in both cases.
masm.loadConstantFloat32(float(maxValue), scratch); masm.loadConstantFloat32(float(maxValue), scratch);
masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail); masm.branchFloat(maxCond, input, scratch, &fail);
} }
// We had an actual correct value, get back to where we were. // We had an actual correct value, get back to where we were.

Просмотреть файл

@ -177,6 +177,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
virtual void visitClzI64(LClzI64* ins); virtual void visitClzI64(LClzI64* ins);
virtual void visitCtzI64(LCtzI64* ins); virtual void visitCtzI64(LCtzI64* ins);
virtual void visitNotI64(LNotI64* ins); virtual void visitNotI64(LNotI64* ins);
virtual void visitWasmTruncateToInt64(LWasmTruncateToInt64* ins);
// Out of line visitors. // Out of line visitors.
void visitOutOfLineBailout(OutOfLineBailout* ool); void visitOutOfLineBailout(OutOfLineBailout* ool);

Просмотреть файл

@ -541,6 +541,21 @@ class LAsmJSAtomicBinopCallout : public LCallInstructionHelper<1, 2, 0>
} }
}; };
class LWasmTruncateToInt64 : public LCallInstructionHelper<INT64_PIECES, 1, 0>
{
public:
LIR_HEADER(WasmTruncateToInt64);
LWasmTruncateToInt64(const LAllocation& in)
{
setOperand(0, in);
}
MWasmTruncateToInt64* mir() const {
return mir_->toWasmTruncateToInt64();
}
};
} // namespace jit } // namespace jit
} // namespace js } // namespace js

Просмотреть файл

@ -21,6 +21,7 @@
_(AsmJSAtomicExchangeCallout) \ _(AsmJSAtomicExchangeCallout) \
_(AsmJSAtomicBinopCallout) \ _(AsmJSAtomicBinopCallout) \
_(DivOrModI64) \ _(DivOrModI64) \
_(UDivOrModI64) _(UDivOrModI64) \
_(WasmTruncateToInt64)
#endif /* jit_arm_LOpcodes_arm_h */ #endif /* jit_arm_LOpcodes_arm_h */

Просмотреть файл

@ -915,7 +915,10 @@ LIRGeneratorARM::visitRandom(MRandom* ins)
void void
LIRGeneratorARM::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins) LIRGeneratorARM::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
{ {
MOZ_CRASH("NY"); MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
defineReturn(new(alloc()) LWasmTruncateToInt64(useRegisterAtStart(opd)), ins);
} }
void void

Просмотреть файл

@ -2287,6 +2287,7 @@ typedef double (*Prototype_Double_None)();
typedef double (*Prototype_Double_Double)(double arg0); typedef double (*Prototype_Double_Double)(double arg0);
typedef double (*Prototype_Double_Int)(int32_t arg0); typedef double (*Prototype_Double_Int)(int32_t arg0);
typedef int32_t (*Prototype_Int_Double)(double arg0); typedef int32_t (*Prototype_Int_Double)(double arg0);
typedef int64_t (*Prototype_Int64_Double)(double arg0);
typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2); 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, typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
int32_t arg3); int32_t arg3);
@ -2429,6 +2430,16 @@ Simulator::softwareInterrupt(SimInstruction* instr)
setCallResult(result); setCallResult(result);
break; break;
} }
case Args_Int64_Double: {
double dval0, dval1;
int32_t ival;
getFpArgs(&dval0, &dval1, &ival);
Prototype_Int64_Double target = reinterpret_cast<Prototype_Int64_Double>(external);
int64_t result = target(dval0);
scratchVolatileRegisters(/* scratchFloat = true */);
setCallResult(result);
break;
}
case Args_Double_None: { case Args_Double_None: {
Prototype_Double_None target = reinterpret_cast<Prototype_Double_None>(external); Prototype_Double_None target = reinterpret_cast<Prototype_Double_None>(external);
double dresult = target(); double dresult = target();