зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1445272: Implement basic anyref support in the baseline compiler; r=lth
--HG-- extra : rebase_source : b445cf2adf366f32022892d21241b27b8404eb91
This commit is contained in:
Родитель
a45951f2cd
Коммит
43a88914d0
|
@ -0,0 +1,383 @@
|
|||
// Ensure that if gc types aren't enabled, test cases properly fail.
|
||||
|
||||
if (!wasmGcEnabled()) {
|
||||
quit(0);
|
||||
}
|
||||
|
||||
// Dummy constructor.
|
||||
function Baguette(calories) {
|
||||
this.calories = calories;
|
||||
}
|
||||
|
||||
// Type checking.
|
||||
|
||||
const { validate, CompileError } = WebAssembly;
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module
|
||||
(func (result anyref)
|
||||
i32.const 42
|
||||
)
|
||||
)`), CompileError, mismatchError('i32', 'anyref'));
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module
|
||||
(func (result anyref)
|
||||
i32.const 0
|
||||
ref.null anyref
|
||||
i32.const 42
|
||||
select
|
||||
)
|
||||
)`), CompileError, /select operand types/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module
|
||||
(func (result i32)
|
||||
ref.null anyref
|
||||
if
|
||||
i32.const 42
|
||||
end
|
||||
)
|
||||
)`), CompileError, mismatchError('anyref', 'i32'));
|
||||
|
||||
|
||||
// Basic compilation tests.
|
||||
|
||||
let simpleTests = [
|
||||
"(module (func (drop (ref.null anyref))))",
|
||||
"(module (func $test (local anyref)))",
|
||||
"(module (func $test (param anyref)))",
|
||||
"(module (func $test (result anyref) (ref.null anyref)))",
|
||||
"(module (func $test (block anyref (unreachable)) unreachable))",
|
||||
"(module (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
|
||||
`(module (import "a" "b" (param anyref)))`,
|
||||
`(module (import "a" "b" (result anyref)))`,
|
||||
];
|
||||
|
||||
for (let src of simpleTests) {
|
||||
wasmEvalText(src, {a:{b(){}}});
|
||||
assertEq(validate(wasmTextToBinary(src)), true);
|
||||
}
|
||||
|
||||
// Basic behavioral tests.
|
||||
|
||||
let { exports } = wasmEvalText(`(module
|
||||
(func (export "is_null") (result i32)
|
||||
ref.null anyref
|
||||
ref.is_null
|
||||
)
|
||||
|
||||
(func $sum (result i32) (param i32)
|
||||
get_local 0
|
||||
i32.const 42
|
||||
i32.add
|
||||
)
|
||||
|
||||
(func (export "is_null_spill") (result i32)
|
||||
ref.null anyref
|
||||
i32.const 58
|
||||
call $sum
|
||||
drop
|
||||
ref.is_null
|
||||
)
|
||||
|
||||
(func (export "is_null_local") (result i32) (local anyref)
|
||||
ref.null anyref
|
||||
set_local 0
|
||||
i32.const 58
|
||||
call $sum
|
||||
drop
|
||||
get_local 0
|
||||
ref.is_null
|
||||
)
|
||||
)`);
|
||||
|
||||
assertEq(exports.is_null(), 1);
|
||||
assertEq(exports.is_null_spill(), 1);
|
||||
assertEq(exports.is_null_local(), 1);
|
||||
|
||||
// Anyref param and result in wasm functions.
|
||||
|
||||
exports = wasmEvalText(`(module
|
||||
(func (export "is_null") (result i32) (param $ref anyref)
|
||||
get_local $ref
|
||||
ref.is_null
|
||||
)
|
||||
|
||||
(func (export "ref_or_null") (result anyref) (param $ref anyref) (param $selector i32)
|
||||
get_local $ref
|
||||
ref.null anyref
|
||||
get_local $selector
|
||||
select
|
||||
)
|
||||
|
||||
(func $recursive (export "nested") (result anyref) (param $ref anyref) (param $i i32)
|
||||
;; i == 10 => ret $ref
|
||||
get_local $i
|
||||
i32.const 10
|
||||
i32.eq
|
||||
if
|
||||
get_local $ref
|
||||
return
|
||||
end
|
||||
|
||||
get_local $ref
|
||||
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
|
||||
call $recursive
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
assertErrorMessage(() => exports.is_null(undefined), TypeError, "can't convert undefined to object");
|
||||
assertEq(exports.is_null(null), 1);
|
||||
assertEq(exports.is_null({}), 0);
|
||||
assertEq(exports.is_null("hi"), 0);
|
||||
assertEq(exports.is_null(3), 0);
|
||||
assertEq(exports.is_null(3.5), 0);
|
||||
assertEq(exports.is_null(true), 0);
|
||||
assertEq(exports.is_null(Symbol("croissant")), 0);
|
||||
assertEq(exports.is_null(new Baguette(100)), 0);
|
||||
|
||||
let baguette = new Baguette(42);
|
||||
assertEq(exports.ref_or_null(null, 0), null);
|
||||
assertEq(exports.ref_or_null(baguette, 0), null);
|
||||
|
||||
let ref = exports.ref_or_null(baguette, 1);
|
||||
assertEq(ref, baguette);
|
||||
assertEq(ref.calories, baguette.calories);
|
||||
|
||||
ref = exports.nested(baguette, 0);
|
||||
assertEq(ref, baguette);
|
||||
assertEq(ref.calories, baguette.calories);
|
||||
|
||||
// More interesting use cases about control flow joins.
|
||||
|
||||
function assertJoin(body) {
|
||||
let val = { i: -1 };
|
||||
assertEq(wasmEvalText(`(module
|
||||
(func (export "test") (param $ref anyref) (param $i i32) (result anyref)
|
||||
${body}
|
||||
)
|
||||
)`).exports.test(val), val);
|
||||
assertEq(val.i, -1);
|
||||
}
|
||||
|
||||
assertJoin("(block anyref get_local $ref)");
|
||||
assertJoin("(block $out anyref get_local $ref br $out)");
|
||||
assertJoin("(loop anyref get_local $ref)");
|
||||
|
||||
assertJoin(`(block $out anyref (loop $top anyref
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $i
|
||||
i32.const 10
|
||||
i32.eq
|
||||
if
|
||||
get_local $ref
|
||||
return
|
||||
end
|
||||
br $top))
|
||||
`);
|
||||
|
||||
assertJoin(`(block $out (loop $top
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $i
|
||||
i32.const 10
|
||||
i32.le_s
|
||||
if
|
||||
br $top
|
||||
else
|
||||
get_local $ref
|
||||
return
|
||||
end
|
||||
)) unreachable
|
||||
`);
|
||||
|
||||
assertJoin(`(block $out anyref (loop $top
|
||||
get_local $ref
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $i
|
||||
i32.const 10
|
||||
i32.eq
|
||||
br_if $out
|
||||
br $top
|
||||
) unreachable)
|
||||
`);
|
||||
|
||||
assertJoin(`(block $out anyref (block $unreachable anyref (loop $top
|
||||
get_local $ref
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $i
|
||||
br_table $unreachable $out
|
||||
) unreachable))
|
||||
`);
|
||||
|
||||
let x = { i: 42 }, y = { f: 53 };
|
||||
exports = wasmEvalText(`(module
|
||||
(func (export "test") (param $lhs anyref) (param $rhs anyref) (param $i i32) (result anyref)
|
||||
get_local $lhs
|
||||
get_local $rhs
|
||||
get_local $i
|
||||
select
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
let result = exports.test(x, y, 0);
|
||||
assertEq(result, y);
|
||||
assertEq(result.i, undefined);
|
||||
assertEq(result.f, 53);
|
||||
assertEq(x.i, 42);
|
||||
|
||||
result = exports.test(x, y, 1);
|
||||
assertEq(result, x);
|
||||
assertEq(result.i, 42);
|
||||
assertEq(result.f, undefined);
|
||||
assertEq(y.f, 53);
|
||||
|
||||
// Anyref in params/result of imported functions.
|
||||
|
||||
let firstBaguette = new Baguette(13),
|
||||
secondBaguette = new Baguette(37);
|
||||
|
||||
let imports = {
|
||||
i: 0,
|
||||
myBaguette: null,
|
||||
funcs: {
|
||||
param(x) {
|
||||
if (this.i === 0) {
|
||||
assertEq(x, firstBaguette);
|
||||
assertEq(x.calories, 13);
|
||||
assertEq(secondBaguette !== null, true);
|
||||
} else if (this.i === 1 || this.i === 2) {
|
||||
assertEq(x, secondBaguette);
|
||||
assertEq(x.calories, 37);
|
||||
assertEq(firstBaguette !== null, true);
|
||||
} else if (this.i === 3) {
|
||||
assertEq(x, null);
|
||||
} else {
|
||||
firstBaguette = null;
|
||||
secondBaguette = null;
|
||||
gc(); // evil mode
|
||||
}
|
||||
this.i++;
|
||||
},
|
||||
ret() {
|
||||
return imports.myBaguette;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports = wasmEvalText(`(module
|
||||
(import $ret "funcs" "ret" (result anyref))
|
||||
(import $param "funcs" "param" (param anyref))
|
||||
|
||||
(func (export "param") (param $x anyref) (param $y anyref)
|
||||
get_local $y
|
||||
get_local $x
|
||||
call $param
|
||||
call $param
|
||||
)
|
||||
|
||||
(func (export "ret") (result anyref)
|
||||
call $ret
|
||||
)
|
||||
)`, imports).exports;
|
||||
|
||||
exports.param(firstBaguette, secondBaguette);
|
||||
exports.param(secondBaguette, null);
|
||||
exports.param(firstBaguette, secondBaguette);
|
||||
|
||||
imports.myBaguette = null;
|
||||
assertEq(exports.ret(), null);
|
||||
|
||||
imports.myBaguette = new Baguette(1337);
|
||||
assertEq(exports.ret(), imports.myBaguette);
|
||||
|
||||
// Check lazy stubs generation.
|
||||
|
||||
exports = wasmEvalText(`(module
|
||||
(import $mirror "funcs" "mirror" (param anyref) (result anyref))
|
||||
(import $augment "funcs" "augment" (param anyref) (result anyref))
|
||||
|
||||
(global $count_f (mut i32) (i32.const 0))
|
||||
(global $count_g (mut i32) (i32.const 0))
|
||||
|
||||
(func $f (param $param anyref) (result anyref)
|
||||
i32.const 1
|
||||
get_global $count_f
|
||||
i32.add
|
||||
set_global $count_f
|
||||
|
||||
get_local $param
|
||||
call $augment
|
||||
)
|
||||
|
||||
(func $g (param $param anyref) (result anyref)
|
||||
i32.const 1
|
||||
get_global $count_g
|
||||
i32.add
|
||||
set_global $count_g
|
||||
|
||||
get_local $param
|
||||
call $mirror
|
||||
)
|
||||
|
||||
(table (export "table") 10 anyfunc)
|
||||
(elem (i32.const 0) $f $g $mirror $augment)
|
||||
(type $table_type (func (param anyref) (result anyref)))
|
||||
|
||||
(func (export "call_indirect") (param $i i32) (param $ref anyref) (result anyref)
|
||||
get_local $ref
|
||||
get_local $i
|
||||
call_indirect $table_type
|
||||
)
|
||||
|
||||
(func (export "count_f") (result i32) get_global $count_f)
|
||||
(func (export "count_g") (result i32) get_global $count_g)
|
||||
)`, {
|
||||
funcs: {
|
||||
mirror(x) {
|
||||
return x;
|
||||
},
|
||||
augment(x) {
|
||||
x.i++;
|
||||
x.newProp = "hello";
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}).exports;
|
||||
|
||||
x = { i: 19 };
|
||||
assertEq(exports.table.get(0)(x), x);
|
||||
assertEq(x.i, 20);
|
||||
assertEq(x.newProp, "hello");
|
||||
assertEq(exports.count_f(), 1);
|
||||
assertEq(exports.count_g(), 0);
|
||||
|
||||
x = { i: 21 };
|
||||
assertEq(exports.table.get(1)(x), x);
|
||||
assertEq(x.i, 21);
|
||||
assertEq(typeof x.newProp, "undefined");
|
||||
assertEq(exports.count_f(), 1);
|
||||
assertEq(exports.count_g(), 1);
|
||||
|
||||
x = { i: 22 };
|
||||
assertEq(exports.table.get(2)(x), x);
|
||||
assertEq(x.i, 22);
|
||||
assertEq(typeof x.newProp, "undefined");
|
||||
assertEq(exports.count_f(), 1);
|
||||
assertEq(exports.count_g(), 1);
|
||||
|
||||
x = { i: 23 };
|
||||
assertEq(exports.table.get(3)(x), x);
|
||||
assertEq(x.i, 24);
|
||||
assertEq(x.newProp, "hello");
|
||||
assertEq(exports.count_f(), 1);
|
||||
assertEq(exports.count_g(), 1);
|
|
@ -0,0 +1 @@
|
|||
|jit-test| test-also=--wasm-gc; include:wasm.js
|
|
@ -0,0 +1,28 @@
|
|||
if (wasmGcEnabled()) {
|
||||
quit();
|
||||
}
|
||||
|
||||
const { CompileError, validate } = WebAssembly;
|
||||
|
||||
const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /(unrecognized opcode|bad type|invalid inline block type)/;
|
||||
|
||||
function assertValidateError(text) {
|
||||
assertEq(validate(wasmTextToBinary(text)), false);
|
||||
}
|
||||
|
||||
let simpleTests = [
|
||||
"(module (func (drop (ref.null anyref))))",
|
||||
"(module (func $test (local anyref)))",
|
||||
"(module (func $test (param anyref)))",
|
||||
"(module (func $test (result anyref) (ref.null anyref)))",
|
||||
"(module (func $test (block anyref (unreachable)) unreachable))",
|
||||
"(module (func $test (local anyref) (result i32) (ref.is_null (get_local 0))))",
|
||||
`(module (import "a" "b" (param anyref)))`,
|
||||
`(module (import "a" "b" (result anyref)))`,
|
||||
];
|
||||
|
||||
for (let src of simpleTests) {
|
||||
print(src)
|
||||
assertErrorMessage(() => wasmEvalText(src), CompileError, UNRECOGNIZED_OPCODE_OR_BAD_TYPE);
|
||||
assertValidateError(src);
|
||||
}
|
|
@ -296,6 +296,15 @@ struct RegI64 : public Register64
|
|||
static RegI64 Invalid() { return RegI64(Register64::Invalid()); }
|
||||
};
|
||||
|
||||
struct RegPtr : public Register
|
||||
{
|
||||
RegPtr() : Register(Register::Invalid()) {}
|
||||
explicit RegPtr(Register reg) : Register(reg) {}
|
||||
bool isValid() const { return *this != Invalid(); }
|
||||
bool isInvalid() const { return !isValid(); }
|
||||
static RegPtr Invalid() { return RegPtr(Register::Invalid()); }
|
||||
};
|
||||
|
||||
struct RegF32 : public FloatRegister
|
||||
{
|
||||
RegF32() : FloatRegister() {}
|
||||
|
@ -316,10 +325,21 @@ struct RegF64 : public FloatRegister
|
|||
|
||||
struct AnyReg
|
||||
{
|
||||
union {
|
||||
RegI32 i32_;
|
||||
RegI64 i64_;
|
||||
RegPtr ref_;
|
||||
RegF32 f32_;
|
||||
RegF64 f64_;
|
||||
};
|
||||
|
||||
enum { I32, I64, REF, F32, F64 } tag;
|
||||
|
||||
explicit AnyReg(RegI32 r) { tag = I32; i32_ = r; }
|
||||
explicit AnyReg(RegI64 r) { tag = I64; i64_ = r; }
|
||||
explicit AnyReg(RegF32 r) { tag = F32; f32_ = r; }
|
||||
explicit AnyReg(RegF64 r) { tag = F64; f64_ = r; }
|
||||
explicit AnyReg(RegPtr r) { tag = REF; ref_ = r; }
|
||||
|
||||
RegI32 i32() const {
|
||||
MOZ_ASSERT(tag == I32);
|
||||
|
@ -337,6 +357,11 @@ struct AnyReg
|
|||
MOZ_ASSERT(tag == F64);
|
||||
return f64_;
|
||||
}
|
||||
RegPtr ref() const {
|
||||
MOZ_ASSERT(tag == REF);
|
||||
return ref_;
|
||||
}
|
||||
|
||||
AnyRegister any() const {
|
||||
switch (tag) {
|
||||
case F32: return AnyRegister(f32_);
|
||||
|
@ -352,20 +377,14 @@ struct AnyReg
|
|||
// only on 64-bit platforms.
|
||||
MOZ_CRASH("AnyReg::any() on 32-bit platform");
|
||||
#endif
|
||||
case REF:
|
||||
MOZ_CRASH("AnyReg::any() not implemented for ref types");
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
// Work around GCC 5 analysis/warning bug.
|
||||
MOZ_CRASH("AnyReg::any(): impossible case");
|
||||
}
|
||||
|
||||
union {
|
||||
RegI32 i32_;
|
||||
RegI64 i64_;
|
||||
RegF32 f32_;
|
||||
RegF64 f64_;
|
||||
};
|
||||
enum { I32, I64, F32, F64 } tag;
|
||||
};
|
||||
|
||||
// Platform-specific registers.
|
||||
|
@ -691,6 +710,10 @@ class BaseRegAlloc
|
|||
#endif
|
||||
}
|
||||
|
||||
bool isAvailablePtr(RegPtr r) {
|
||||
return isAvailableGPR(r);
|
||||
}
|
||||
|
||||
bool isAvailableF32(RegF32 r) {
|
||||
return isAvailableFPU(r);
|
||||
}
|
||||
|
@ -726,6 +749,18 @@ class BaseRegAlloc
|
|||
allocInt64(specific);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE RegPtr needPtr() {
|
||||
if (!hasGPR())
|
||||
bc.sync();
|
||||
return RegPtr(allocGPR());
|
||||
}
|
||||
|
||||
void needPtr(RegPtr specific) {
|
||||
if (!isAvailablePtr(specific))
|
||||
bc.sync();
|
||||
allocGPR(specific);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE RegF32 needF32() {
|
||||
if (!hasFPU<MIRType::Float32>())
|
||||
bc.sync();
|
||||
|
@ -758,6 +793,10 @@ class BaseRegAlloc
|
|||
freeInt64(r);
|
||||
}
|
||||
|
||||
void freePtr(RegPtr r) {
|
||||
freeGPR(r);
|
||||
}
|
||||
|
||||
void freeF64(RegF64 r) {
|
||||
freeFPU(r);
|
||||
}
|
||||
|
@ -817,6 +856,10 @@ class BaseRegAlloc
|
|||
void addKnownF64(RegF64 r) {
|
||||
knownFPU_.add(r);
|
||||
}
|
||||
|
||||
void addKnownRef(RegPtr r) {
|
||||
knownGPR_.add(r);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
@ -888,23 +931,28 @@ class ScratchF32 : public ScratchFloat32Scope
|
|||
#endif
|
||||
|
||||
#ifdef RABALDR_SCRATCH_I32
|
||||
class ScratchI32 : public BaseScratchRegister
|
||||
template<class RegType>
|
||||
class ScratchGPR : public BaseScratchRegister
|
||||
{
|
||||
public:
|
||||
explicit ScratchI32(BaseRegAlloc& ra)
|
||||
explicit ScratchGPR(BaseRegAlloc& ra)
|
||||
: BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::I32)
|
||||
{}
|
||||
operator RegI32() const { return RegI32(RabaldrScratchI32); }
|
||||
operator RegType() const { return RegType(RabaldrScratchI32); }
|
||||
};
|
||||
#else
|
||||
class ScratchI32 : public ScratchRegisterScope
|
||||
template<class RegType>
|
||||
class ScratchGPR : public ScratchRegisterScope
|
||||
{
|
||||
public:
|
||||
explicit ScratchI32(MacroAssembler& m) : ScratchRegisterScope(m) {}
|
||||
operator RegI32() const { return RegI32(Register(*this)); }
|
||||
explicit ScratchGPR(MacroAssembler& m) : ScratchRegisterScope(m) {}
|
||||
operator RegType() const { return RegType(Register(*this)); }
|
||||
};
|
||||
#endif
|
||||
|
||||
using ScratchI32 = ScratchGPR<RegI32>;
|
||||
using ScratchPtr = ScratchGPR<RegPtr>;
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
// ScratchEBX is a mnemonic device: For some atomic ops we really need EBX,
|
||||
// no other register will do. And we would normally have to allocate that
|
||||
|
@ -985,6 +1033,7 @@ BaseLocalIter::settle()
|
|||
case MIRType::Int64:
|
||||
case MIRType::Double:
|
||||
case MIRType::Float32:
|
||||
case MIRType::Pointer:
|
||||
if (argsIter_->argInRegister())
|
||||
frameOffset_ = pushLocal(MIRTypeToSize(mirType_));
|
||||
else
|
||||
|
@ -1003,6 +1052,7 @@ BaseLocalIter::settle()
|
|||
case ValType::I64:
|
||||
case ValType::F32:
|
||||
case ValType::F64:
|
||||
case ValType::AnyRef:
|
||||
mirType_ = ToMIRType(locals_[index_]);
|
||||
frameOffset_ = pushLocal(MIRTypeToSize(mirType_));
|
||||
break;
|
||||
|
@ -1242,7 +1292,7 @@ class BaseStackFrame
|
|||
masm.load64(Address(sp_, localOffset(src)), dest);
|
||||
}
|
||||
|
||||
void loadLocalPtr(const Local& src, Register dest) {
|
||||
void loadLocalPtr(const Local& src, RegPtr dest) {
|
||||
masm.loadPtr(Address(sp_, localOffset(src)), dest);
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1601,7 @@ class BaseStackFrame
|
|||
// Disambiguation: this loads a "Ptr" value from the stack, it does not load
|
||||
// the "StackPtr".
|
||||
|
||||
void loadStackPtr(int32_t offset, Register dest) {
|
||||
void loadStackPtr(int32_t offset, RegPtr dest) {
|
||||
masm.loadPtr(Address(sp_, stackOffset(offset)), dest);
|
||||
}
|
||||
|
||||
|
@ -1862,6 +1912,7 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
|
||||
RegI32 joinRegI32_;
|
||||
RegI64 joinRegI64_;
|
||||
RegPtr joinRegPtr_;
|
||||
RegF32 joinRegF32_;
|
||||
RegF64 joinRegF64_;
|
||||
|
||||
|
@ -1924,16 +1975,19 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
|
||||
bool isAvailableI32(RegI32 r) { return ra.isAvailableI32(r); }
|
||||
bool isAvailableI64(RegI64 r) { return ra.isAvailableI64(r); }
|
||||
bool isAvailableRef(RegPtr r) { return ra.isAvailablePtr(r); }
|
||||
bool isAvailableF32(RegF32 r) { return ra.isAvailableF32(r); }
|
||||
bool isAvailableF64(RegF64 r) { return ra.isAvailableF64(r); }
|
||||
|
||||
MOZ_MUST_USE RegI32 needI32() { return ra.needI32(); }
|
||||
MOZ_MUST_USE RegI64 needI64() { return ra.needI64(); }
|
||||
MOZ_MUST_USE RegPtr needRef() { return ra.needPtr(); }
|
||||
MOZ_MUST_USE RegF32 needF32() { return ra.needF32(); }
|
||||
MOZ_MUST_USE RegF64 needF64() { return ra.needF64(); }
|
||||
|
||||
void needI32(RegI32 specific) { ra.needI32(specific); }
|
||||
void needI64(RegI64 specific) { ra.needI64(specific); }
|
||||
void needRef(RegPtr specific) { ra.needPtr(specific); }
|
||||
void needF32(RegF32 specific) { ra.needF32(specific); }
|
||||
void needF64(RegF64 specific) { ra.needF64(specific); }
|
||||
|
||||
|
@ -1943,6 +1997,7 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
|
||||
void freeI32(RegI32 r) { ra.freeI32(r); }
|
||||
void freeI64(RegI64 r) { ra.freeI64(r); }
|
||||
void freeRef(RegPtr r) { ra.freePtr(r); }
|
||||
void freeF32(RegF32 r) { ra.freeF32(r); }
|
||||
void freeF64(RegF64 r) { ra.freeF64(r); }
|
||||
|
||||
|
@ -2018,6 +2073,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
#endif
|
||||
}
|
||||
|
||||
RegI32 narrowPtr(RegPtr r) {
|
||||
return RegI32(r);
|
||||
}
|
||||
|
||||
RegI32 lowPart(RegI64 r) {
|
||||
#ifdef JS_PUNBOX64
|
||||
return RegI32(r.reg);
|
||||
|
@ -2050,6 +2109,11 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
masm.move64(src, dest);
|
||||
}
|
||||
|
||||
void moveRef(RegPtr src, RegPtr dest) {
|
||||
if (src != dest)
|
||||
masm.movePtr(src, dest);
|
||||
}
|
||||
|
||||
void moveF64(RegF64 src, RegF64 dest) {
|
||||
if (src != dest)
|
||||
masm.moveDouble(src, dest);
|
||||
|
@ -2065,6 +2129,8 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
needI32(joinRegI32_);
|
||||
else if (type == ExprType::I64)
|
||||
needI64(joinRegI64_);
|
||||
else if (type == ExprType::AnyRef)
|
||||
needRef(joinRegPtr_);
|
||||
}
|
||||
|
||||
void maybeUnreserveJoinRegI(ExprType type) {
|
||||
|
@ -2072,6 +2138,8 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
freeI32(joinRegI32_);
|
||||
else if (type == ExprType::I64)
|
||||
freeI64(joinRegI64_);
|
||||
else if (type == ExprType::AnyRef)
|
||||
freeRef(joinRegPtr_);
|
||||
}
|
||||
|
||||
void maybeReserveJoinReg(ExprType type) {
|
||||
|
@ -2088,6 +2156,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case ExprType::F64:
|
||||
needF64(joinRegF64_);
|
||||
break;
|
||||
case ExprType::AnyRef:
|
||||
needRef(joinRegPtr_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2107,6 +2178,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case ExprType::F64:
|
||||
freeF64(joinRegF64_);
|
||||
break;
|
||||
case ExprType::AnyRef:
|
||||
freeRef(joinRegPtr_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2127,6 +2201,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
|
||||
struct Stk
|
||||
{
|
||||
private:
|
||||
Stk() : kind_(Unknown), i64val_(0) {}
|
||||
|
||||
public:
|
||||
enum Kind
|
||||
{
|
||||
// The Mem opcodes are all clustered at the beginning to
|
||||
|
@ -2135,6 +2213,7 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
MemI64, // 64-bit integer stack value ("offs")
|
||||
MemF32, // 32-bit floating stack value ("offs")
|
||||
MemF64, // 64-bit floating stack value ("offs")
|
||||
MemRef, // reftype (pointer wide) stack value ("offs")
|
||||
|
||||
// The Local opcodes follow the Mem opcodes for a similar
|
||||
// quick test within hasLocal().
|
||||
|
@ -2142,47 +2221,61 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
LocalI64, // Local int64 var ("slot")
|
||||
LocalF32, // Local float32 var ("slot")
|
||||
LocalF64, // Local double var ("slot")
|
||||
LocalRef, // Local reftype (pointer wide) var ("slot")
|
||||
|
||||
RegisterI32, // 32-bit integer register ("i32reg")
|
||||
RegisterI64, // 64-bit integer register ("i64reg")
|
||||
RegisterF32, // 32-bit floating register ("f32reg")
|
||||
RegisterF64, // 64-bit floating register ("f64reg")
|
||||
RegisterRef, // reftype (pointer wide) register ("refReg")
|
||||
|
||||
ConstI32, // 32-bit integer constant ("i32val")
|
||||
ConstI64, // 64-bit integer constant ("i64val")
|
||||
ConstF32, // 32-bit floating constant ("f32val")
|
||||
ConstF64, // 64-bit floating constant ("f64val")
|
||||
ConstRef, // reftype (pointer wide) constant ("refval")
|
||||
|
||||
Unknown,
|
||||
};
|
||||
|
||||
Kind kind_;
|
||||
|
||||
static const Kind MemLast = MemF64;
|
||||
static const Kind LocalLast = LocalF64;
|
||||
static const Kind MemLast = MemRef;
|
||||
static const Kind LocalLast = LocalRef;
|
||||
|
||||
union {
|
||||
RegI32 i32reg_;
|
||||
RegI64 i64reg_;
|
||||
RegPtr refReg_;
|
||||
RegF32 f32reg_;
|
||||
RegF64 f64reg_;
|
||||
int32_t i32val_;
|
||||
int64_t i64val_;
|
||||
intptr_t refval_;
|
||||
float f32val_;
|
||||
double f64val_;
|
||||
uint32_t slot_;
|
||||
uint32_t offs_;
|
||||
};
|
||||
|
||||
explicit Stk(RegI32 r) : kind_(RegisterI32), i32reg_(r) {}
|
||||
explicit Stk(RegI64 r) : kind_(RegisterI64), i64reg_(r) {}
|
||||
explicit Stk(RegF32 r) : kind_(RegisterF32), f32reg_(r) {}
|
||||
explicit Stk(RegF64 r) : kind_(RegisterF64), f64reg_(r) {}
|
||||
explicit Stk(int32_t v) : kind_(ConstI32), i32val_(v) {}
|
||||
explicit Stk(int64_t v) : kind_(ConstI64), i64val_(v) {}
|
||||
explicit Stk(float v) : kind_(ConstF32), f32val_(v) {}
|
||||
explicit Stk(double v) : kind_(ConstF64), f64val_(v) {}
|
||||
explicit Stk(RegI32 r) : kind_(RegisterI32), i32reg_(r) {}
|
||||
explicit Stk(RegI64 r) : kind_(RegisterI64), i64reg_(r) {}
|
||||
explicit Stk(RegPtr r) : kind_(RegisterRef), refReg_(r) {}
|
||||
explicit Stk(RegF32 r) : kind_(RegisterF32), f32reg_(r) {}
|
||||
explicit Stk(RegF64 r) : kind_(RegisterF64), f64reg_(r) {}
|
||||
explicit Stk(int32_t v) : kind_(ConstI32), i32val_(v) {}
|
||||
explicit Stk(int64_t v) : kind_(ConstI64), i64val_(v) {}
|
||||
explicit Stk(float v) : kind_(ConstF32), f32val_(v) {}
|
||||
explicit Stk(double v) : kind_(ConstF64), f64val_(v) {}
|
||||
explicit Stk(Kind k, uint32_t v) : kind_(k), slot_(v) {
|
||||
MOZ_ASSERT(k > MemLast && k <= LocalLast);
|
||||
}
|
||||
static Stk StkRef(intptr_t v) {
|
||||
Stk s;
|
||||
s.kind_ = ConstRef;
|
||||
s.refval_ = v;
|
||||
return s;
|
||||
}
|
||||
|
||||
void setOffs(Kind k, uint32_t v) { MOZ_ASSERT(k <= MemLast); kind_ = k; offs_ = v; }
|
||||
|
||||
|
@ -2191,11 +2284,13 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
|
||||
RegI32 i32reg() const { MOZ_ASSERT(kind_ == RegisterI32); return i32reg_; }
|
||||
RegI64 i64reg() const { MOZ_ASSERT(kind_ == RegisterI64); return i64reg_; }
|
||||
RegPtr refReg() const { MOZ_ASSERT(kind_ == RegisterRef); return refReg_; }
|
||||
RegF32 f32reg() const { MOZ_ASSERT(kind_ == RegisterF32); return f32reg_; }
|
||||
RegF64 f64reg() const { MOZ_ASSERT(kind_ == RegisterF64); return f64reg_; }
|
||||
|
||||
int32_t i32val() const { MOZ_ASSERT(kind_ == ConstI32); return i32val_; }
|
||||
int64_t i64val() const { MOZ_ASSERT(kind_ == ConstI64); return i64val_; }
|
||||
intptr_t refval() const { MOZ_ASSERT(kind_ == ConstRef); return refval_; }
|
||||
|
||||
// For these two, use an out-param instead of simply returning, to
|
||||
// use the normal stack and not the x87 FP stack (which has effect on
|
||||
|
@ -2215,6 +2310,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
stk_.infallibleEmplaceBack(Stk(Forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void pushConstRef(intptr_t v) {
|
||||
stk_.infallibleEmplaceBack(Stk::StkRef(v));
|
||||
}
|
||||
|
||||
void loadConstI32(const Stk& src, RegI32 dest) {
|
||||
moveImm32(src.i32val(), dest);
|
||||
}
|
||||
|
@ -2247,6 +2346,22 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
moveI64(src.i64reg(), dest);
|
||||
}
|
||||
|
||||
void loadConstRef(const Stk& src, RegPtr dest) {
|
||||
moveImmRef(src.refval(), dest);
|
||||
}
|
||||
|
||||
void loadMemRef(const Stk& src, RegPtr dest) {
|
||||
fr.loadStackPtr(src.offs(), dest);
|
||||
}
|
||||
|
||||
void loadLocalRef(const Stk& src, RegPtr dest) {
|
||||
fr.loadLocalPtr(localFromSlot(src.slot(), MIRType::Pointer), dest);
|
||||
}
|
||||
|
||||
void loadRegisterRef(const Stk& src, RegPtr dest) {
|
||||
moveRef(src.refReg(), dest);
|
||||
}
|
||||
|
||||
void loadConstF64(const Stk& src, RegF64 dest) {
|
||||
double d;
|
||||
src.f64val(&d);
|
||||
|
@ -2399,6 +2514,25 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
}
|
||||
}
|
||||
|
||||
void loadRef(const Stk& src, RegPtr dest) {
|
||||
switch (src.kind()) {
|
||||
case Stk::ConstRef:
|
||||
loadConstRef(src, dest);
|
||||
break;
|
||||
case Stk::MemRef:
|
||||
loadMemRef(src, dest);
|
||||
break;
|
||||
case Stk::LocalRef:
|
||||
loadLocalRef(src, dest);
|
||||
break;
|
||||
case Stk::RegisterRef:
|
||||
loadRegisterRef(src, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Compiler bug: expected ref on stack");
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all local and register value stack elements to memory.
|
||||
//
|
||||
// TODO / OPTIMIZE: As this is fairly expensive and causes worse
|
||||
|
@ -2501,6 +2635,19 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
v.setOffs(Stk::MemF32, offs);
|
||||
break;
|
||||
}
|
||||
case Stk::LocalRef: {
|
||||
ScratchPtr scratch(*this);
|
||||
loadLocalRef(v, scratch);
|
||||
uint32_t offs = fr.pushPtr(scratch);
|
||||
v.setOffs(Stk::MemRef, offs);
|
||||
break;
|
||||
}
|
||||
case Stk::RegisterRef: {
|
||||
uint32_t offs = fr.pushPtr(v.refReg());
|
||||
freeRef(v.refReg());
|
||||
v.setOffs(Stk::MemRef, offs);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
@ -2544,6 +2691,11 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
push(r);
|
||||
}
|
||||
|
||||
void pushRef(RegPtr r) {
|
||||
MOZ_ASSERT(!isAvailableRef(r));
|
||||
push(r);
|
||||
}
|
||||
|
||||
void pushF64(RegF64 r) {
|
||||
MOZ_ASSERT(!isAvailableF64(r));
|
||||
push(r);
|
||||
|
@ -2564,6 +2716,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
push(v);
|
||||
}
|
||||
|
||||
void pushRef(intptr_t v) {
|
||||
pushConstRef(v);
|
||||
}
|
||||
|
||||
void pushF64(double v) {
|
||||
push(v);
|
||||
}
|
||||
|
@ -2584,6 +2740,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
push(Stk::LocalI64, slot);
|
||||
}
|
||||
|
||||
void pushLocalRef(uint32_t slot) {
|
||||
push(Stk::LocalRef, slot);
|
||||
}
|
||||
|
||||
void pushLocalF64(uint32_t slot) {
|
||||
push(Stk::LocalF64, slot);
|
||||
}
|
||||
|
@ -2698,6 +2858,54 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
return specific;
|
||||
}
|
||||
|
||||
// Call only from other popRef() variants.
|
||||
// v must be the stack top. May pop the CPU stack.
|
||||
|
||||
void popRef(const Stk& v, RegPtr dest) {
|
||||
MOZ_ASSERT(&v == &stk_.back());
|
||||
switch (v.kind()) {
|
||||
case Stk::ConstRef:
|
||||
loadConstRef(v, dest);
|
||||
break;
|
||||
case Stk::LocalRef:
|
||||
loadLocalRef(v, dest);
|
||||
break;
|
||||
case Stk::MemRef:
|
||||
fr.popPtr(dest);
|
||||
break;
|
||||
case Stk::RegisterRef:
|
||||
loadRegisterRef(v, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Compiler bug: expected ref on stack");
|
||||
}
|
||||
}
|
||||
|
||||
RegPtr popRef(RegPtr specific) {
|
||||
Stk& v = stk_.back();
|
||||
|
||||
if (!(v.kind() == Stk::RegisterRef && v.refReg() == specific)) {
|
||||
needRef(specific);
|
||||
popRef(v, specific);
|
||||
if (v.kind() == Stk::RegisterRef)
|
||||
freeRef(v.refReg());
|
||||
}
|
||||
|
||||
stk_.popBack();
|
||||
return specific;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE RegPtr popRef() {
|
||||
Stk& v = stk_.back();
|
||||
RegPtr r;
|
||||
if (v.kind() == Stk::RegisterRef)
|
||||
r = v.refReg();
|
||||
else
|
||||
popRef(v, (r = needRef()));
|
||||
stk_.popBack();
|
||||
return r;
|
||||
}
|
||||
|
||||
// Call only from other popF64() variants.
|
||||
// v must be the stack top. May pop the CPU stack.
|
||||
|
||||
|
@ -2914,6 +3122,12 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
k == Stk::LocalF32);
|
||||
return Some(AnyReg(popF32(joinRegF32_)));
|
||||
}
|
||||
case ExprType::AnyRef: {
|
||||
DebugOnly<Stk::Kind> k(stk_.back().kind());
|
||||
MOZ_ASSERT(k == Stk::RegisterRef || k == Stk::ConstRef || k == Stk::MemRef ||
|
||||
k == Stk::LocalRef);
|
||||
return Some(AnyReg(popRef(joinRegPtr_)));
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH("Compiler bug: unexpected expression type");
|
||||
}
|
||||
|
@ -2944,6 +3158,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
MOZ_ASSERT(isAvailableF64(joinRegF64_));
|
||||
needF64(joinRegF64_);
|
||||
return Some(AnyReg(joinRegF64_));
|
||||
case ExprType::AnyRef:
|
||||
MOZ_ASSERT(isAvailableRef(joinRegPtr_));
|
||||
needRef(joinRegPtr_);
|
||||
return Some(AnyReg(joinRegPtr_));
|
||||
case ExprType::Void:
|
||||
return Nothing();
|
||||
default:
|
||||
|
@ -2967,6 +3185,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case AnyReg::F32:
|
||||
pushF32(r->f32());
|
||||
break;
|
||||
case AnyReg::REF:
|
||||
pushRef(r->ref());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2986,6 +3207,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case AnyReg::F32:
|
||||
freeF32(r->f32());
|
||||
break;
|
||||
case AnyReg::REF:
|
||||
freeRef(r->ref());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2998,6 +3222,7 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
for (uint32_t i = stk_.length() - 1; numval > 0; numval--, i--) {
|
||||
Stk& v = stk_[i];
|
||||
switch (v.kind()) {
|
||||
case Stk::MemRef: size += BaseStackFrame::StackSizeOfPtr; break;
|
||||
case Stk::MemI32: size += BaseStackFrame::StackSizeOfPtr; break;
|
||||
case Stk::MemI64: size += BaseStackFrame::StackSizeOfInt64; break;
|
||||
case Stk::MemF64: size += BaseStackFrame::StackSizeOfDouble; break;
|
||||
|
@ -3024,6 +3249,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case Stk::RegisterF32:
|
||||
freeF32(v.f32reg());
|
||||
break;
|
||||
case Stk::RegisterRef:
|
||||
freeRef(v.refReg());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -3070,6 +3298,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case Stk::RegisterF64:
|
||||
check.addKnownF64(item.f64reg());
|
||||
break;
|
||||
case Stk::RegisterRef:
|
||||
check.addKnownRef(item.refReg());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -3159,6 +3390,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
case MIRType::Int64:
|
||||
fr.storeLocalI64(RegI64(i->gpr64()), l);
|
||||
break;
|
||||
case MIRType::Pointer:
|
||||
fr.storeLocalPtr(RegPtr(i->gpr()), l);
|
||||
break;
|
||||
case MIRType::Double:
|
||||
fr.storeLocalF64(RegF64(i->fpu()), l);
|
||||
break;
|
||||
|
@ -3488,6 +3722,17 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ValType::AnyRef: {
|
||||
ABIArg argLoc = call->abi.next(MIRType::Pointer);
|
||||
if (argLoc.kind() == ABIArg::Stack) {
|
||||
ScratchPtr scratch(*this);
|
||||
loadRef(arg, scratch);
|
||||
masm.storePtr(scratch, Address(masm.getStackPointer(), argLoc.offsetFromArgBase()));
|
||||
} else {
|
||||
loadRef(arg, RegPtr(argLoc.gpr()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Function argument type");
|
||||
}
|
||||
|
@ -3561,6 +3806,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
masm.move64(Imm64(v), dest);
|
||||
}
|
||||
|
||||
void moveImmRef(intptr_t v, RegPtr dest) {
|
||||
masm.movePtr(ImmWord(v), dest);
|
||||
}
|
||||
|
||||
void moveImmF32(float f, RegF32 dest) {
|
||||
masm.loadConstantFloat32(f, dest);
|
||||
}
|
||||
|
@ -3691,6 +3940,13 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
return r;
|
||||
}
|
||||
|
||||
RegPtr captureReturnedRef() {
|
||||
RegPtr r = RegPtr(ReturnReg);
|
||||
MOZ_ASSERT(isAvailableRef(r));
|
||||
needRef(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
void returnCleanup(bool popStack) {
|
||||
if (popStack)
|
||||
fr.popStackBeforeBranch(controlOutermost().stackHeight);
|
||||
|
@ -5275,6 +5531,11 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
*r0 = popF64();
|
||||
}
|
||||
|
||||
void pop2xRef(RegPtr* r0, RegPtr* r1) {
|
||||
*r1 = popRef();
|
||||
*r0 = popRef();
|
||||
}
|
||||
|
||||
RegI32 popI64ToI32() {
|
||||
RegI64 r = popI64();
|
||||
return narrowI64(r);
|
||||
|
@ -5614,6 +5875,9 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
MOZ_MUST_USE bool emitGrowMemory();
|
||||
MOZ_MUST_USE bool emitCurrentMemory();
|
||||
|
||||
MOZ_MUST_USE bool emitRefNull();
|
||||
void emitRefIsNull();
|
||||
|
||||
MOZ_MUST_USE bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
|
||||
MOZ_MUST_USE bool emitAtomicLoad(ValType type, Scalar::Type viewType);
|
||||
MOZ_MUST_USE bool emitAtomicRMW(ValType type, Scalar::Type viewType, AtomicOp op);
|
||||
|
@ -6851,10 +7115,9 @@ BaseCompiler::sniffConditionalControlCmp(Cond compareOp, ValType operandType)
|
|||
OpBytes op;
|
||||
iter_.peekOp(&op);
|
||||
switch (op.b0) {
|
||||
case uint16_t(Op::Select):
|
||||
MOZ_FALLTHROUGH;
|
||||
case uint16_t(Op::BrIf):
|
||||
case uint16_t(Op::If):
|
||||
case uint16_t(Op::Select):
|
||||
setLatentCompare(compareOp, operandType);
|
||||
return true;
|
||||
default:
|
||||
|
@ -7436,6 +7699,12 @@ BaseCompiler::doReturn(ExprType type, bool popStack)
|
|||
freeF32(rv);
|
||||
break;
|
||||
}
|
||||
case ExprType::AnyRef: {
|
||||
RegPtr rv = popRef(RegPtr(ReturnReg));
|
||||
returnCleanup(popStack);
|
||||
freeRef(rv);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH("Function return type");
|
||||
}
|
||||
|
@ -7500,6 +7769,11 @@ BaseCompiler::pushReturned(const FunctionCall& call, ExprType type)
|
|||
pushF64(rv);
|
||||
break;
|
||||
}
|
||||
case ExprType::AnyRef: {
|
||||
RegPtr rv = captureReturnedRef();
|
||||
pushRef(rv);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Function return type");
|
||||
}
|
||||
|
@ -7827,6 +8101,9 @@ BaseCompiler::emitGetLocal()
|
|||
case ValType::F32:
|
||||
pushLocalF32(slot);
|
||||
break;
|
||||
case ValType::AnyRef:
|
||||
pushLocalRef(slot);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Local variable type");
|
||||
}
|
||||
|
@ -7883,6 +8160,16 @@ BaseCompiler::emitSetOrTeeLocal(uint32_t slot)
|
|||
pushF32(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::AnyRef: {
|
||||
RegPtr rv = popRef();
|
||||
syncLocal(slot);
|
||||
fr.storeLocalPtr(rv, localFromSlot(slot, MIRType::Pointer));
|
||||
if (isSetLocal)
|
||||
freeRef(rv);
|
||||
else
|
||||
pushRef(rv);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Local variable type");
|
||||
}
|
||||
|
@ -8403,6 +8690,16 @@ BaseCompiler::emitSelect()
|
|||
pushF64(r);
|
||||
break;
|
||||
}
|
||||
case ValType::AnyRef: {
|
||||
RegPtr r, rs;
|
||||
pop2xRef(&r, &rs);
|
||||
emitBranchPerform(&b);
|
||||
moveRef(rs, r);
|
||||
masm.bind(&done);
|
||||
freeRef(rs);
|
||||
pushRef(r);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH("select type");
|
||||
}
|
||||
|
@ -8557,6 +8854,29 @@ BaseCompiler::emitCurrentMemory()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseCompiler::emitRefNull()
|
||||
{
|
||||
if (!iter_.readRefNull())
|
||||
return false;
|
||||
|
||||
if (deadCode_)
|
||||
return true;
|
||||
|
||||
pushRef(NULLREF_VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitRefIsNull()
|
||||
{
|
||||
RegPtr r = popRef();
|
||||
RegI32 rd = narrowPtr(r);
|
||||
|
||||
masm.cmpPtrSet(Assembler::Equal, r, ImmWord(NULLREF_VALUE), rd);
|
||||
pushI32(rd);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseCompiler::emitAtomicCmpXchg(ValType type, Scalar::Type viewType)
|
||||
{
|
||||
|
@ -9424,6 +9744,19 @@ BaseCompiler::emitBody()
|
|||
case uint16_t(Op::CurrentMemory):
|
||||
CHECK_NEXT(emitCurrentMemory());
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
case uint16_t(Op::RefNull):
|
||||
if (env_.gcTypesEnabled == HasGcTypes::False)
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
CHECK_NEXT(emitRefNull());
|
||||
break;
|
||||
case uint16_t(Op::RefIsNull):
|
||||
if (env_.gcTypesEnabled == HasGcTypes::False)
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
CHECK_NEXT(emitConversion(emitRefIsNull, ValType::AnyRef, ValType::I32));
|
||||
break;
|
||||
#endif
|
||||
|
||||
// Numeric operations
|
||||
case uint16_t(Op::NumericPrefix): {
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
|
@ -9702,6 +10035,7 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
|
|||
fr(*masm),
|
||||
joinRegI32_(RegI32(ReturnReg)),
|
||||
joinRegI64_(RegI64(ReturnReg64)),
|
||||
joinRegPtr_(RegPtr(ReturnReg)),
|
||||
joinRegF32_(RegF32(ReturnFloat32Reg)),
|
||||
joinRegF64_(RegF64(ReturnDoubleReg))
|
||||
{
|
||||
|
|
|
@ -98,6 +98,10 @@ enum class ValType
|
|||
|
||||
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
|
||||
|
||||
// The representation of a null reference value throughout the compiler.
|
||||
|
||||
static const intptr_t NULLREF_VALUE = intptr_t((void*)nullptr);
|
||||
|
||||
enum class DefinitionKind
|
||||
{
|
||||
Function = 0x00,
|
||||
|
|
Загрузка…
Ссылка в новой задаче