Closed Bug 1505902 Opened 6 years ago Closed 5 years ago

jsapi-tests.exe fails with Assertion failure: (uintptr_t(ptr) & 1) == 0

Categories

(Firefox Build System :: General: Unsupported Platforms, enhancement, P5)

enhancement

Tracking

(firefox70 fixed)

RESOLVED FIXED
mozilla70
Tracking Status
firefox70 --- fixed

People

(Reporter: tjr, Assigned: iain)

References

(Blocks 1 open bug)

Details

Attachments

(1 file)

Depends on: 1505936
Okay, after developing patches for two sub-bugs, I have a stack: > 0019fb80 00b28c9d jsapi_tests!JS::Value::setPrivate+0x71 [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/obj-firefox/dist/include/js/Value.h @ 868] > 0019fba0 00cacf71 jsapi_tests!JS::PrivateValue+0x2d [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/obj-firefox/dist/include/js/Value.h @ 1212] > 0019fbec 00cabe33 jsapi_tests!js::ArrayBufferObject::setDataPointer+0x41 [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/js/src/vm/ArrayBufferObject.cpp @ 1026] > 0019fc40 00caee56 jsapi_tests!js::ArrayBufferObject::initialize+0xc3 [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/js/src/vm/ArrayBufferObject.h @ 446] > 0019fd50 0064c798 jsapi_tests!js::ArrayBufferObject::create+0x776 [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/js/src/vm/ArrayBufferObject.cpp @ 1281] > 0019fdc0 00a62bfd jsapi_tests!JS_NewArrayBufferWithExternalContents+0x1d8 [/builds/worker/workspace/build/src/obj-firefox/js/src//builds/worker/workspace/build/src/js/src/vm/ArrayBufferObject.cpp @ 1774] > 0019fe54 01db8e6b jsapi_tests!cls_testExternalArrayBuffer::run+0x4d [/builds/worker/workspace/build/src/obj-firefox/js/src/jsapi-tests//builds/worker/workspace/build/src/js/src/jsapi-tests/testExternalArrayBuffer.cpp @ 21] > 0019fefc 00401385 jsapi_tests!main+0x1eb [/builds/worker/workspace/build/src/obj-firefox/js/src/jsapi-tests//builds/worker/workspace/build/src/js/src/jsapi-tests/tests.cpp @ 134] > 0019ff80 748c8674 jsapi_tests!WinMainCRTStartup+0x275 > 0019ff94 77455d87 KERNEL32!BaseThreadInitThunk+0x24 > 0019ffdc 77455d57 ntdll!__RtlUserThreadStart+0x2f > 0019ffec 00000000 ntdll!_RtlUserThreadStart+0x1b
Okay, this isn't a bug seemingly. The buffer we are passing all the way down the stack is actually at an address with a 1 in its lowest bit. mingw-clang just did that. That causes the assert. https://searchfox.org/mozilla-central/rev/a3894a4dcfb5d42f2e6eee6cb9faf7141879ef1a/js/public/Value.h#868 > 0:000> dt -v test_data > Got address 020d95cb for symbol > 0:000> dt -v ptr > Local var [AddrFlags d0 AddrOff 00000008 Reg/Val ebp (8)] @ 0x19fb88 Type void* > 0x020d95cb Void I'm going to start the process of asking JIT devs why this assert is here, and if we can remove it in general, or if I can/should just remove it for MinGW-clang. Feel free to pass ni? to whomever is best qualified to weigh in!
Flags: needinfo?(arai.unmht)
tracked the history, and looks like luke added the shift operation (>> 1) on the pointer, for 64-bit. https://hg.mozilla.org/mozilla-central/rev/0d767e15745b#l5.447 ni?-ing luke for "why" part. (I guess it's related to isDouble requirement for private value, but not sure the original reason) then, if we drop the "64-bit payload" support for private value, but add 63-bit with the highest bit == 1 requirement (arbitrary pointer on 64-bit arch should fit this), the assertion and shift operation could just be removed. (actually, the 64-bit payload" support comment isn't already correct (bug 1506502)) for JIT part, what I can find is only MacroAssembler::branchPrivatePtr and MacroAssembler*Compat::loadPrivate (arm, mips), which can be simply fixed by removing shift.
Flags: needinfo?(arai.unmht) → needinfo?(luke)
Private Values are basically arbitrary non-GC pointers masquerading as doubles. That is, PrivateValue(rawPtr).isDouble()==true. Being a double means the GC won't attempt to mark the pointer. To make the pointer look like a double, the high bit must be set, which is the reason we assert the low-bit is unset and shift right to set the high bit. This assumes private pointers are aligned which is usually the case for the main use case of private pointers, viz., hanging something malloc()d off a GC object (using the GC object's finalizer to free it). In retrospect, we could've probably instead assumed the high bit was unset (usually this memory is reserved for the kernel), asserted that instead, and avoided the rshift altogether. It might be worth making this change now, although we should definitely avoid conflicting with bug 1401624 (which might've already changed this up anyway). Kannan?
Flags: needinfo?(luke) → needinfo?(kvijayan)
Bug 1401624 (object-biased nan-boxing) should land very shortly, but the interaction between this choice and that patch is minimal - the main change being that we will want the high bit to bet set to indicate double value rather than the high bit clear. Howerver, changing it to not assume the private-ptr is 2-byte aligned, and simply assert that ptr's high bit is clear instead of shifting out a clear low-bit, is reasonably independent of that landing. I can look to make this a follow-up of the nanboxing patch.
Flags: needinfo?(kvijayan)
Thanks Kannan! Sounds like there's no real downside to making this change. Just in case this provides us with additional motivation, Jan: are you aware of any hot paths that (un)box private values (that would benefit from avoiding the shift)?
Flags: needinfo?(jdemooij)
(In reply to Luke Wagner [:luke] from comment #6) > Just in case this provides us with additional motivation, Jan: are you aware > of any hot paths that (un)box private values (that would benefit from > avoiding the shift)? The interesting places in JIT code where we unbox private values: * Wasm's JIT entry stub: https://searchfox.org/mozilla-central/rev/d850d799a0009f851b5535580e0a8b4bb2c591d7/js/src/wasm/WasmStubs.cpp#495 * ArgumentsObject's data slot. * DOM objects store the C++ object as private value; see LoadDOMPrivate for instance. I'm not sure any of these are hot enough for the shift to have measurable overhead though.
Flags: needinfo?(jdemooij)
Priority: -- → P5

Now that Bug 1401624 has landed; can I ask folks to look at this again and give some guidance on whether triggering this assertion is worrying and needs investigation?

Flags: needinfo?(iireland)

Our previous representation of private values assumed that the private pointer was aligned, and did some bit twiddling to try to disguise it as a double. Since bug 1293313, it has been unnecessary to set the top bit for a double, so that bit twiddling is unnecessary. There are actual use cases where private values are unaligned, so we should fix this.

While cleaning this up, I also removed unboxPrivateValue, because its only use could be better written using loadPrivateValue directly.

Justification for the above patch: I've run myself around in circles a couple of times, but now I think I'm convinced that we were already implicitly relying on the top bit to be clear under the existing implementation, so it should not be a problem to make it explicit.

Specifically, because we set the sign bit in isDouble before doing the comparison, the value of the high bit is irrelevant when it comes to disguising a pointer as a double. All that matters is that the value of the pointer (after the high bit is set) must be less than JSVAL_SHIFTED_TAG_MAX_DOUBLE, which is 0xfff8_0000_ffff_ffff.

The bottom half of the standard actually-only-48-bits address space runs from 0x0 to 0x0000_7fff_ffff_ffff, which trivially satisfies the requirement. The top half of the address space (typically reserved for the kernel) runs from 0xffff_8000_0000_0000 to 0xffff_ffff_ffff_ffff. Shifting the low end of that range by 1 gives 0x7fff_c000_0000_0000, which (with the top bit set) is still greater than JSVAL_SHIFTED_TAG_MAX_DOUBLE.

So there's actually no need to do any shifting or bit-twiddling. Kernel pointers don't work either way. Any pointer that we could store under the old system can just be stored as a raw pointer, and everything will work out fine.

Assignee: tom → iireland
Flags: needinfo?(iireland)
Pushed by iireland@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/fbb42a9d132e Clean up private Value representation r=djvj
Status: NEW → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla70

Is this backportable to ESR68 and ESR60 without also backporting the object-biased boxing patch? In bug 1566883 I described a use case which produces some fairly surprising behaviour for embedders due to this bug.

Flags: needinfo?(iireland)

The patch would look different, because object-biased NaN-boxing did a lot of tidying, but this change can be made independently.

That said, I don't think uplifting the entire patch is realistic. The Value representation is a pretty fundamental part of the engine. What we might be able to do is uplift a minimal alternate API to ESR68: something along the lines of "getUnalignedPrivate/toUnalignedPrivate", using the new representation. There would be no in-tree uses of the API, but embedders could use it to store unaligned pointers.

The API would probably get deprecated in the next ESR release.

Would that be worthwhile for you?

Flags: needinfo?(iireland) → needinfo?(philip.chimento)

To be worthwhile for my use case, I would need ArrayBuffer to use that unaligned private API: https://searchfox.org/mozilla-esr68/source/js/src/vm/ArrayBufferObject.cpp#983

Would that be a possibility?

Flags: needinfo?(philip.chimento) → needinfo?(iireland)

I would like whatever is necessary to remove that assertion from esr68; for the MinGW build to work.

This assert should probably be a MOZ_RELEASE_ASSERT. The current 68 code has a hard requirement on the alignment (since it drops the low bit internally).

One option may be a compile-time #ifdef for 68 so that we can safely uplift and only things like mingw builds would opt in. Having release asserts in the key choke-points like NewArrayBuffer are probably worthwhile for all users and is a low risk change as well (since if the assert trips, the browser is about to experience far worse problems).

After talking it over with RyanVM, the plan is to uplift the core of the patch (minus some incidental cleanups) to ESR68, hidden behind an #ifdef. See bug 1568564.

Flags: needinfo?(iireland)

Appreciated, thanks!

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: