Make code poisoning in JitCode::finalize faster with W^X

RESOLVED FIXED in Firefox 46

Status

()

Core
JavaScript Engine: JIT
RESOLVED FIXED
a year ago
a year ago

People

(Reporter: jandem, Assigned: jandem)

Tracking

unspecified
mozilla46
Points:
---

Firefox Tracking Flags

(firefox46 fixed)

Details

Attachments

(1 attachment)

(Assignee)

Description

a year ago
Created attachment 8701814 [details] [diff] [review]
Patch

We poison JIT code (in JitCode::finalize) in all builds. With W^X enabled that can be pretty slow: we often mprotect the exact same pages while we finalize different JitCodes.

The attached patch optimizes this: JitCode::finalize adds a JitPoisonRange entry to a Vector stored in FreeOp (similar to the freeLater Vector there). Then in ~FreeOp, we pass this Vector to ExecutableAllocator::poisonCode, where we can do the poisoning much more efficiently by ensuring each ExecutablePool is made writable only once.

On a pretty worst-case benchmark, this reduces the time spent poisoning from 57 ms to 2 ms or so.
Attachment #8701814 - Flags: review?(bhackett1024)
(Assignee)

Comment 1

a year ago
Comment on attachment 8701814 [details] [diff] [review]
Patch

Review of attachment 8701814 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/vm/Runtime.h
@@ +1600,5 @@
> +inline bool
> +FreeOp::appendJitPoisonRange(const jit::JitPoisonRange& range)
> +{
> +    // FreeOps other than the defaultFreeOp() are constructed on the stack,
> +    // and won't hold onto the pointers to free indefinitely.

Ugh, copy/paste typo. s/pointers to free/ranges to poison/
Comment on attachment 8701814 [details] [diff] [review]
Patch

Review of attachment 8701814 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/jit/ExecutableAllocator.cpp
@@ +370,5 @@
> +
> +    // Don't race with reprotectAll called from the signal handler.
> +    JitRuntime::AutoPreventBackedgePatching apbp(rt);
> +
> +    for (size_t i = 0; i < ranges.length(); i++) {

Maybe have an #ifdef DEBUG loop before this one that asserts the mark bits are clear on all the pools?

::: js/src/jit/Ion.cpp
@@ +873,5 @@
> +    MOZ_ASSERT(pool_);
> +
> +    // With W^X JIT code, reprotecting memory for each JitCode instance is
> +    // slow, so we just record the ranges and poison them later all at once.
> +    if (fop->appendJitPoisonRange(JitPoisonRange(pool_, code_, bufferSize_)))

A note about why it's ok to ignore append failure would be good here.
Attachment #8701814 - Flags: review?(bhackett1024) → review+

Comment 3

a year ago
https://hg.mozilla.org/integration/mozilla-inbound/rev/dd9728713ebb
(Assignee)

Comment 4

a year ago
(In reply to Brian Hackett (:bhackett) from comment #2)
> Maybe have an #ifdef DEBUG loop before this one that asserts the mark bits
> are clear on all the pools?

Good idea, done.

Comment 5

a year ago
bugherder
https://hg.mozilla.org/mozilla-central/rev/dd9728713ebb
Status: ASSIGNED → RESOLVED
Last Resolved: a year ago
status-firefox46: --- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla46
You need to log in before you can comment on or make changes to this bug.