Closed Bug 496693 Opened 15 years ago Closed 15 years ago

nanojit: comment and refactor DeadCodeFilter vs Assembler::gen interactions

Categories

(Tamarin Graveyard :: Baseline JIT (CodegenLIR), defect)

defect
Not set
normal

Tracking

(Not tracked)

VERIFIED FIXED

People

(Reporter: jseward, Unassigned)

References

Details

(Whiteboard: fixed-in-tracemonkey)

Attachments

(1 file, 2 obsolete files)

User-Agent:       Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.10) Gecko/2009042700 SUSE/3.0.10-1.1 Firefox/3.0.10
Build Identifier: 

Summary
~~~~~~~

DeadCodeFilter and Assembler::gen interact in a subtle and
undocumented way.  This bug report supplies a patch whose purpose is
primarily to document the connection, to the extent that I understand
it.  The patch also deletes DeadCodeFilter and inserts equivalent
logic at the head of the LIR-scanning loop in Assembler::gen.

The patch does not change the generated code, nor should it increase
the costs of running the JIT.

Rationale & Background
~~~~~~~~~~~~~~~~~~~~~~

DeadCodeFilter is not a standalone LIR-to-LIR transformation.  It only
works if Assembler::gen is somewhere downstream, because it needs
Assembler::gen to poke the Reservation fields on value-producing
nodes, so DeadCodeFilter knows which of such nodes are actually
necessary.

This is confusing for newcomers to the code.  What it means is,
although Assembler::gen is reading LIRs from the end of the reader
pipeline, it is modifying what goes in the start of the pipeline, by
poking their Reservation fields.

Assembler::gen also appears to depend on DeadCodeFilter for correct
operation.  In particular, in some circumstances it needs
DeadCodeFilter upstream in order to stop it visiting value producing
nodes more than once.  I don't understand this part of the mechanism
yet.

Overall, the code generator would be easier to follow if:

* Assembler::gen was standalone, so that it did not require an
  upstream DeadCodeFilter for correct operation.  We should be able to
  chuck any old LIR into Assembler::gen (providing it follows type
  rules, and doesn't have a cyclic graph) without it breaking.

* DeadCodeFilter worked as a standalone LIR-to-LIR transformation, so
  as to facilitate experimentation with different orderings of LIR
  transformations.

In effect, Assembler::gen is at the moment tightly coupled to
preceding transformation pipeline.  Removing DeadCodeFilter and
placing equivalent logic at the start of Assembler::gen is a good
first step to decoupling the two.

Finally, w.r.t. definition of "bool required = " in the patch, this
change makes it clear which kinds of LIR nodes Assembler::gen
considers must always be retained, and which need to be retained only
if their output is used.  I think this provides a defendable basis on
which to formally split the LIR definition into LIR statements and
LIR values.  (Future work).


Reproducible: Always
Blocks: 495569
Depends on: 494864
is this ready for review?
Attachment #381915 - Flags: review?(edwsmith)
yes; request is now filed.
I agree in general with the idea of cleaning up the DeadCodeFilter logic and decoupling it, but I'd rather not see it land in gen().  Could we make it a separate callable routine?  

> Assembler::gen also appears to depend on DeadCodeFilter 
> for correct operation.

I'd be very surprised if this was the case, as DeadCodeFilter was added much later to the back-end.   But I can only assume that an unintended dependency has crept in.
(in reply to Comment #4)

> Could we make it a separate callable routine?

Do you mean: change the start of the gen() loop to be like this:

  bool required = ins->hasEffects() || ins->resv()->used
  if (!required)
      continue;

and then put this somewhere else:

  bool hasEffects ( LIns* ins ) {
     return
        ins->isGuard()
        || ins->isBranch()
        || (ins->isCall() && !ins->isCse(functions))
        || ins->isStore()
        || op == LIR_loop || op == LIR_label || op == LIR_live
        || ins->isRet();
  }

or something else?
Ed, any chance you could have a look at this?
re #4; sorry my bad, i was looking at the wrong diff.  The change looks fine to me.
Hi,

sorry for the slow review

This might be an okay patch but i'm concerned about the motivating assumption that each LirReader filter needs to work standalone;  that's not at all the intention.    This is also why Assembler.assembler() creates the DeadCodeFilter instance -- it's a required piece of the pipeline if you're using Assembler.  it's not a standalone component.  making it standalone is a big change, and we'd want to move it out of the Assembler impl to make it standalone too.

Before DeadCodeFilter was added, the dead code skipping logic actually was in gen() and we factored it out to clean up the code (separation of concerns).  Yes, DCF and Assembler must run synchronously, but that alone shouldn't justify putting the code back inline in gen().

so, R+ on the cleanup, and R+ on adding the clarifying comments, but I think we need a stronger reason to un-factor the code.  Performance?  

We know from profiling that read() is hot, I dont know if dropping one filter stage moves the needle significantly, however.
Attachment #381915 - Flags: review?(edwsmith) → review-
(In reply to comment #8)

> This might be an okay patch but i'm concerned about the motivating assumption
> that each LirReader filter needs to work standalone;  that's not at all the
> intention.

You've correctly identified this concern; but it seems rather like DCF is the only one for which this inseparability is true. The other read-side filters can be rearranged willy-nilly and have this nice "modular" independent feel to them. As far as I can tell anyway. I think the motivation of the patch is aesthetic: to avoid misleading readers by supporting this separability/pipeline notion everywhere-except-here.

Would comment #5 -- moving the DCF logic from a tightly-coupled "filter" to a helper function -- satisfy everyone? You'll avoid making an object that appears separable but isn't, yet also avoid cluttering up ::gen() with the logic in-line.
i'd be satisfied with that.

I've written LirWriter filters that are coupled (analyzer feeding information to a later filter that requires the information).  We shouldn't try hard to make every filter work standalone, but we absolutely should be clear about where the inter-filter dependencies are.
* splits keep/no-keep logic into a helper function LIns::isStmt

* shorten up & rewrite comments in Assembler::gen

Generated code is unaffected.  Both before and after the patch, a
non-debug build using gcc-4.3.1 with flags -g -O running trace-tests.js
requires 57.3 billion instructions (x86-linux).
Attachment #381915 - Attachment is obsolete: true
Attachment #384787 - Flags: review?(edwsmith)
Just to note:  I was just looking at DeadCodeFilter, trying to understand what it does.  I remembered Julian talking about this stuff and so talked to him and he pointed me at this bug.  Without that help I would have never worked out the coupling between DeadCodeFilter and Assembler.  So +1 for the patch.
Attachment #384787 - Flags: review?(edwsmith) → review+
Attachment #384787 - Attachment is obsolete: true
Keywords: checkin-needed
I still wonder whether a bitmap or bytemap (derived from the tbl) mapping opcode to stmt yes/no would be faster than the fairly hairy sequence of jumps we do right now in isStmt(). In the alternative we could always set ->used for statements when we construct them.
(In reply to comment #14)
> I still wonder whether a bitmap or bytemap (derived from the tbl) mapping
> opcode to stmt yes/no would be faster than the fairly hairy sequence of jumps
> we do right now in isStmt(). In the alternative we could always set ->used for
> statements when we construct them.

I have a patch doing the former, it didn't make any difference on SunSpider, but the code is a bit nicer so I'll put it in a separate bug report.  The latter idea sounds error-prone to me.
(In reply to comment #14)
> I still wonder whether a bitmap or bytemap (derived from the tbl) mapping
> opcode to stmt yes/no would be faster than the fairly hairy sequence of jumps
> we do right now in isStmt().

Bug 503990 has the details of this.
http://hg.mozilla.org/mozilla-central/rev/38fb8ab98e59
Status: UNCONFIRMED → RESOLVED
Closed: 15 years ago
Resolution: --- → FIXED
Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: