Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Ciure Andrei 2019-04-24 01:22:25 +03:00
commit 1e7be41ddd
13 changed files with 318 additions and 70 deletions

View File

@ -0,0 +1,123 @@
// Checks that optimised handling, via Ion, of select-of-I32-values as driven
// by comparison-of-I32-values, works correctly. See bug 1532286 comment 7.
function moduleText(cmp_kind /* "eq", "ne", "le_u" etc */)
{
return `(module
(func (export "cmpI32_selI32")
(result i32)
(param i32) (param i32) (param i32) (param i32)
(select (get_local 2)
(get_local 3)
(i32.${cmp_kind} (get_local 0) (get_local 1))
)
))`;
}
// Make sure both endpoints and the middlepoint are covered, with one value
// either side, so as to expose both signedness and off-by-one failures.
// Ditto quarter points for good measure.
const vals = [0x0, 0x1,
0x3fffffff, 0x40000000, 0x40000001,
0x7fffffff, 0x80000000, 0x80000001,
0xbfffffff, 0xC0000000, 0xC0000001,
0xfffffffe, 0xffffffff];
const testNames
= ["eq", "ne", "lt_s", "lt_u", "gt_s", "gt_u",
"le_s", "le_u", "ge_s", "ge_u"];
const expected_eq
= [0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0];
const expected_ne
= [1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
const expected_lt_s
= [1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,
1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1];
const expected_lt_u
= [1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1];
const expected_gt_s
= [1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1];
const expected_gt_u
= [1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1];
const expected_le_s
= [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0];
const expected_le_u
= [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,
0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0];
const expected_ge_s
= [0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,
0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0];
const expected_ge_u
= [0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,
1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,
0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,
0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,
0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0];
const testExpVecs
= [expected_eq, expected_ne,
expected_lt_s, expected_lt_u, expected_gt_s, expected_gt_u,
expected_le_s, expected_le_u, expected_ge_s, expected_ge_u]
assertEq(testNames.length, testExpVecs.length);
for (i in testNames) {
const testExp = testExpVecs[i];
assertEq(testExp.length, vals.length * vals.length);
const inst = wasmEvalText(moduleText(testNames[i]));
let ctr = 0;
for (let a of vals) {
for (let b of vals) {
const actual = inst.exports.cmpI32_selI32(a, b, 42, 1337);
const expected = testExp[ctr] == 0 ? 42 : 1337;
assertEq(expected, actual);
ctr++;
}
}
}

View File

@ -6216,7 +6216,7 @@ bool CodeGenerator::generateBody() {
}
#ifdef JS_JITSPEW
JitSpewStart(JitSpew_Codegen, "instruction %s", iter->opName());
JitSpewStart(JitSpew_Codegen, "# instruction %s", iter->opName());
if (const char* extra = iter->getExtraName()) {
JitSpewCont(JitSpew_Codegen, ":%s", extra);
}
@ -14010,6 +14010,45 @@ void CodeGenerator::visitIsNullPointer(LIsNullPointer* lir) {
ToRegister(lir->output()));
}
void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) {
bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 ||
ins->compareType() == MCompare::Compare_UInt32;
bool selIs32bit = ins->mir()->type() == MIRType::Int32;
if (cmpIs32bit && selIs32bit) {
Register out = ToRegister(ins->output());
MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == out,
"true expr input is reused for output");
Assembler::Condition cond = Assembler::InvertCondition(JSOpToCondition(
ins->compareType(), ins->jsop()));
const LAllocation* rhs = ins->rightExpr();
const LAllocation* falseExpr = ins->ifFalseExpr();
Register lhs = ToRegister(ins->leftExpr());
if (rhs->isRegister()) {
if (falseExpr->isRegister()) {
// On arm32, this is the only one of the four cases that can actually
// happen, since |rhs| and |falseExpr| are marked useAny() by
// LIRGenerator::visitWasmSelect, and useAny() means "register only"
// on arm32.
masm.cmp32Move32(cond, lhs, ToRegister(rhs), ToRegister(falseExpr), out);
} else {
masm.cmp32Load32(cond, lhs, ToRegister(rhs), ToAddress(falseExpr), out);
}
} else {
if (falseExpr->isRegister()) {
masm.cmp32Move32(cond, lhs, ToAddress(rhs), ToRegister(falseExpr), out);
} else {
masm.cmp32Load32(cond, lhs, ToAddress(rhs), ToAddress(falseExpr), out);
}
}
return;
}
MOZ_CRASH("in CodeGenerator::visitWasmCompareAndSelect: unexpected types");
}
static_assert(!std::is_polymorphic<CodeGenerator>::value,
"CodeGenerator should not have any virtual methods");

View File

@ -904,21 +904,28 @@ static inline bool CanEmitCompareAtUses(MInstruction* ins) {
return false;
}
bool foundTest = false;
for (MUseIterator iter(ins->usesBegin()); iter != ins->usesEnd(); iter++) {
MNode* node = iter->consumer();
if (!node->isDefinition()) {
return false;
}
if (!node->toDefinition()->isTest()) {
return false;
}
if (foundTest) {
return false;
}
foundTest = true;
// If the result is never used, we can usefully defer emission to the use
// point, since that will never happen.
MUseIterator iter(ins->usesBegin());
if (iter == ins->usesEnd()) {
return true;
}
return true;
// If the first use isn't of the expected form, the answer is No.
MNode* node = iter->consumer();
if (!node->isDefinition()) {
return false;
}
MDefinition* use = node->toDefinition();
if (!use->isTest() && !use->isWasmSelect()) {
return false;
}
// Emission can be deferred to the first use point, but only if there are no
// other use points.
iter++;
return iter == ins->usesEnd();
}
void LIRGenerator::visitCompare(MCompare* comp) {
@ -5049,5 +5056,51 @@ void LIRGenerator::visitIonToWasmCall(MIonToWasmCall* ins) {
assignSafepoint(lir, ins);
}
void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
MDefinition* condExpr = ins->condExpr();
// Pick off specific cases that we can do with LWasmCompareAndSelect.
// Currently only{U,}Int32 selection driven by a comparison of {U,}Int32
// values.
if (condExpr->isCompare() && condExpr->isEmittedAtUses() &&
ins->type() == MIRType::Int32) {
MCompare* comp = condExpr->toCompare();
JSOp jsop = comp->jsop();
MCompare::CompareType compTy = comp->compareType();
// We don't currently generate any other JSOPs for the comparison, and if
// that changes, we want to know about it. Hence this assertion.
MOZ_ASSERT(jsop == JSOP_EQ || jsop == JSOP_NE || jsop == JSOP_LT ||
jsop == JSOP_GT || jsop == JSOP_LE || jsop == JSOP_GE);
if (compTy == MCompare::Compare_Int32 ||
compTy == MCompare::Compare_UInt32) {
auto* lir = new (alloc())
LWasmCompareAndSelect(useRegister(comp->lhs()), useAny(comp->rhs()),
compTy, jsop,
useRegisterAtStart(ins->trueExpr()),
useAny(ins->falseExpr()));
defineReuseInput(lir, ins, LWasmCompareAndSelect::IfTrueExprIndex);
return;
}
// Otherwise fall through to normal handling, which appears to emit the
// condexpr itself anyway.
}
if (ins->type() == MIRType::Int64) {
auto* lir = new (alloc()) LWasmSelectI64(
useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
useRegister(ins->condExpr()));
defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
return;
}
auto* lir = new (alloc())
LWasmSelect(useRegisterAtStart(ins->trueExpr()), useAny(ins->falseExpr()),
useRegister(ins->condExpr()));
defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
}
static_assert(!std::is_polymorphic<LIRGenerator>::value,
"LIRGenerator should not have any virtual methods");

View File

@ -1632,6 +1632,14 @@ class MacroAssembler : public MacroAssemblerSpecific {
Register src, Register dest)
DEFINED_ON(arm, arm64, mips_shared, x86_shared);
inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs,
const Address& src, Register dest)
DEFINED_ON(arm, arm64, x86_shared);
inline void cmp32Load32(Condition cond, Register lhs, Register rhs,
const Address& src, Register dest)
DEFINED_ON(arm, arm64, x86_shared);
inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
Register src, Register dest)
DEFINED_ON(arm, arm64, mips_shared, x86, x64);

View File

@ -471,23 +471,6 @@ void LIRGeneratorARM::lowerUrshD(MUrsh* mir) {
define(lir, mir);
}
void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
if (ins->type() == MIRType::Int64) {
auto* lir = new (alloc()) LWasmSelectI64(
useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
useRegister(ins->condExpr()));
defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
return;
}
auto* lir = new (alloc())
LWasmSelect(useRegisterAtStart(ins->trueExpr()),
useRegister(ins->falseExpr()), useRegister(ins->condExpr()));
defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
}
void LIRGenerator::visitWasmNeg(MWasmNeg* ins) {
if (ins->type() == MIRType::Int32) {
define(new (alloc()) LNegI(useRegisterAtStart(ins->input())), ins);

View File

@ -1899,6 +1899,20 @@ void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
cmp32Move32(cond, lhs, scratch, src, dest);
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
const Address& rhs, const Address& src,
Register dest) {
// This is never used, but must be present to facilitate linking on arm.
MOZ_CRASH("No known use cases");
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
Register rhs, const Address& src,
Register dest) {
// This is never used, but must be present to facilitate linking on arm.
MOZ_CRASH("No known use cases");
}
void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr,
Imm32 mask, const Address& src,
Register dest) {

View File

@ -308,10 +308,6 @@ void LIRGenerator::visitWasmNeg(MWasmNeg* ins) {
}
}
void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
MOZ_CRASH("visitWasmSelect");
}
void LIRGeneratorARM64::lowerUDiv(MDiv* div) {
LAllocation lhs = useRegister(div->lhs());
// TODO (Bug 1523568): Implement the division-avoidance paths when rhs is

View File

@ -1589,6 +1589,18 @@ void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
cond);
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
const Address& rhs, const Address& src,
Register dest) {
MOZ_CRASH("NYI");
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
Register rhs, const Address& src,
Register dest) {
MOZ_CRASH("NYI");
}
void MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
Register src, Register dest) {
cmp32(lhs, rhs);

View File

@ -6198,6 +6198,39 @@ class LWasmSelectI64
const LAllocation* condExpr() { return getOperand(CondIndex); }
};
class LWasmCompareAndSelect : public LWasmSelectBase<1, 4> {
MCompare::CompareType compareType_;
JSOp jsop_;
public:
LIR_HEADER(WasmCompareAndSelect);
static const size_t LeftExprIndex = 0;
static const size_t RightExprIndex = 1;
static const size_t IfTrueExprIndex = 2;
static const size_t IfFalseExprIndex = 3;
LWasmCompareAndSelect(const LAllocation& leftExpr,
const LAllocation& rightExpr,
MCompare::CompareType compareType, JSOp jsop,
const LAllocation& ifTrueExpr,
const LAllocation& ifFalseExpr)
: LWasmSelectBase(classOpcode), compareType_(compareType), jsop_(jsop) {
setOperand(LeftExprIndex, leftExpr);
setOperand(RightExprIndex, rightExpr);
setOperand(IfTrueExprIndex, ifTrueExpr);
setOperand(IfFalseExprIndex, ifFalseExpr);
}
const LAllocation* leftExpr() { return getOperand(LeftExprIndex); }
const LAllocation* rightExpr() { return getOperand(RightExprIndex); }
const LAllocation* ifTrueExpr() { return getOperand(IfTrueExprIndex); }
const LAllocation* ifFalseExpr() { return getOperand(IfFalseExprIndex); }
MCompare::CompareType compareType() { return compareType_; }
JSOp jsop() { return jsop_; }
};
class LWasmAddOffset : public LInstructionHelper<1, 1, 0> {
public:
LIR_HEADER(WasmAddOffset);

View File

@ -246,23 +246,6 @@ void LIRGeneratorX86Shared::lowerModI(MMod* mod) {
defineFixed(lir, mod, LAllocation(AnyRegister(edx)));
}
void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
if (ins->type() == MIRType::Int64) {
auto* lir = new (alloc()) LWasmSelectI64(
useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
useRegister(ins->condExpr()));
defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
return;
}
auto* lir = new (alloc())
LWasmSelect(useRegisterAtStart(ins->trueExpr()), use(ins->falseExpr()),
useRegister(ins->condExpr()));
defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
}
void LIRGenerator::visitWasmNeg(MWasmNeg* ins) {
switch (ins->type()) {
case MIRType::Int32:

View File

@ -992,6 +992,20 @@ void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
cmovCCl(cond, src, dest);
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
const Address& rhs, const Address& src,
Register dest) {
cmp32(lhs, Operand(rhs));
cmovCCl(cond, Operand(src), dest);
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
Register rhs, const Address& src,
Register dest) {
cmp32(lhs, rhs);
cmovCCl(cond, Operand(src), dest);
}
void MacroAssembler::spectreZeroRegister(Condition cond, Register scratch,
Register dest) {
// Note: use movl instead of move32/xorl to ensure flags are not clobbered.

View File

@ -32,26 +32,16 @@ class ArchlinuxBootstrapper(NodeInstall, StyloInstall,
BROWSER_PACKAGES = [
'alsa-lib',
'dbus-glib',
'desktop-file-utils',
'gconf',
'gtk2',
'gtk3',
'hicolor-icon-theme',
'hunspell',
'icu',
'libevent',
'libvpx',
'libxt',
'mime-types',
'mozilla-common',
'nasm',
'nss',
'sqlite',
'startup-notification',
'diffutils',
'gst-plugins-base-libs',
'imake',
'inetutils',
'libpulse',
'xorg-server-xvfb',
'yasm',

View File

@ -50,12 +50,6 @@ namespace mozilla {
using Compression::LZ4;
using dom::ipc::StructuredCloneData;
#ifdef XP_WIN
# define READ_BINARYMODE "rb"
#else
# define READ_BINARYMODE "r"
#endif
AddonManagerStartup& AddonManagerStartup::GetSingleton() {
static RefPtr<AddonManagerStartup> singleton;
if (!singleton) {
@ -116,12 +110,18 @@ static Result<nsCString, nsresult> DecodeLZ4(const nsACString& lz4,
auto size = LittleEndian::readUint32(data);
data += 4;
size_t dataLen = lz4.EndReading() - data;
size_t outputSize;
nsCString result;
if (!result.SetLength(size, fallible) ||
!LZ4::decompress(data, result.BeginWriting(), size)) {
!LZ4::decompress(data, dataLen, result.BeginWriting(), size,
&outputSize)) {
return Err(NS_ERROR_UNEXPECTED);
}
MOZ_DIAGNOSTIC_ASSERT(size == outputSize);
return result;
}