Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
159721f
Dump live range info from HotSpot
robcasloz Jan 26, 2025
1efce3d
Parse, compute, and represent liveness information at the data level
robcasloz Jan 26, 2025
e40342d
Add button and define action to enable live range visualization
robcasloz Jan 27, 2025
7a3814d
Generalize clearSelectedNodes to different elements (also live ranges)
robcasloz Jan 27, 2025
322c97d
Represent live ranges at the diagram graph and abstract layout level
robcasloz Jan 27, 2025
33db9a4
Compute live range segment subsets and their layout
robcasloz Jan 27, 2025
4aca9b5
Compute live range segments to be laid out
robcasloz Jan 27, 2025
f43dbf6
Show live range ids on top of basic blocks
robcasloz Jan 27, 2025
3d25cd4
Add live range widgets and logic for selecting and centering them
robcasloz Jan 27, 2025
990c54c
Actually draw the live ranges
robcasloz Jan 28, 2025
b4fada7
Add quick live range search
robcasloz Jan 28, 2025
39aab9c
Factor out block and live range search functionality
robcasloz Jan 28, 2025
ce8b3cb
Fix crashes when disabling live ranges or switching mode
robcasloz Jan 28, 2025
f551f49
Enable/disable live range widgets similarly to block widgets
robcasloz Jan 29, 2025
6480444
Fix crashes when filtering out live range-related nodes
robcasloz Jan 29, 2025
05f3a30
Position live ranges in empty blocks properly
robcasloz Jan 30, 2025
a04fc9b
Enable predecessor and successor expansion only if some node is selected
robcasloz Jan 30, 2025
cad7908
Enable extraction of live ranges via related nodes
robcasloz Jan 30, 2025
8b619bd
Preserve combine node and live range selection across relayouts
robcasloz Jan 30, 2025
e25b13d
Make it possible to hide live ranges
robcasloz Jan 30, 2025
5937be6
Do not show duplicate live ranges in properties, deselect live ranges…
robcasloz Jan 30, 2025
add4fb8
Extract on live range double-click
robcasloz Jan 30, 2025
b1c2a94
Add pop-up menu to live ranges
robcasloz Jan 30, 2025
8a76fac
Add pop-up option to select definers and users
robcasloz Jan 30, 2025
1ce3552
Add actions to select live ranges for a node
robcasloz Jan 31, 2025
4043b61
Add pretty icons
robcasloz Jan 31, 2025
77bdc77
Restart filter defaults
robcasloz Jan 31, 2025
0a321e9
Fix live range selection/deselection inconsistencies
robcasloz Feb 5, 2025
9ea960e
Simplify live range highlighting
robcasloz Feb 6, 2025
7992c04
Add basic filter support for live ranges
robcasloz Feb 7, 2025
4f89c6e
Remove unused definition
robcasloz Feb 7, 2025
4d36f22
Close live ranges only on first definitions and kills
robcasloz Feb 7, 2025
7a3e992
Complete dump of live range properties
robcasloz Feb 7, 2025
66073d3
Draw segments in empty blocks
robcasloz Feb 8, 2025
a30893b
Let phi-defined live ranges start at the top of the basic block
robcasloz Feb 8, 2025
2647f1c
Update copyright headers
robcasloz Feb 10, 2025
8336f67
Remove unused option to use definer node ids instead of live range ids
robcasloz Feb 10, 2025
1cd7a4e
Remove comments that are no longer needed
robcasloz Feb 10, 2025
cf2f945
Select all live range sements and not just a representative one
robcasloz Feb 10, 2025
0978eee
Ensure live ranges to go to from pop-up menu become visible
robcasloz Feb 10, 2025
153e01a
Ensure basic blocks with live ranges are always visible
robcasloz Feb 10, 2025
c5e48e4
Fix def live range printing in 'Show liveness information' filter
robcasloz Feb 11, 2025
0016922
Check if live range widgets actually exist for computing block visibi…
robcasloz Feb 17, 2025
08ee449
Remove unnecessary whitespace
robcasloz Feb 18, 2025
87b31e9
Export liveness information when saving a graph from IGV
robcasloz Feb 20, 2025
31e4510
Open and close live ranges joined by phis in their respective blocks
robcasloz Feb 20, 2025
51718b9
Handle single-block CFGs
robcasloz Feb 20, 2025
efbde14
Show liveness info extra line only when liveness information is avail…
robcasloz Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions src/hotspot/share/opto/idealGraphPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ const char *IdealGraphPrinter::BLOCK_ELEMENT = "block";
const char *IdealGraphPrinter::SUCCESSORS_ELEMENT = "successors";
const char *IdealGraphPrinter::SUCCESSOR_ELEMENT = "successor";
const char *IdealGraphPrinter::ASSEMBLY_ELEMENT = "assembly";
const char *IdealGraphPrinter::LIVEOUT_ELEMENT = "liveOut";
const char *IdealGraphPrinter::LIVE_RANGE_ELEMENT = "lrg";
const char *IdealGraphPrinter::LIVE_RANGE_ID_PROPERTY = "id";
const char *IdealGraphPrinter::LIVE_RANGES_ELEMENT = "liveRanges";

int IdealGraphPrinter::_file_count = 0;

Expand Down Expand Up @@ -781,6 +785,12 @@ Node* IdealGraphPrinter::get_load_node(const Node* node) {
return load;
}

bool IdealGraphPrinter::has_liveness_info() const {
return _chaitin &&
_chaitin != (PhaseChaitin *)((intptr_t)0xdeadbeef) &&
_chaitin->get_live() != nullptr;
}

void IdealGraphPrinter::walk_nodes(Node* start, bool edges) {
VectorSet visited;
GrowableArray<Node *> nodeStack(Thread::current()->resource_area(), 0, 0, nullptr);
Expand Down Expand Up @@ -877,6 +887,19 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const
}
tail(NODES_ELEMENT);

if (has_liveness_info()) {
head(LIVEOUT_ELEMENT);
const IndexSet* liveout = _chaitin->get_live()->live(block);
IndexSetIterator lrgs(liveout);
uint lrg;
while ((lrg = lrgs.next()) != 0) {
begin_elem(LIVE_RANGE_ELEMENT);
print_attr(LIVE_RANGE_ID_PROPERTY, lrg);
end_elem();
}
tail(LIVEOUT_ELEMENT);
}

tail(BLOCK_ELEMENT);
}
tail(CONTROL_FLOW_ELEMENT);
Expand All @@ -900,6 +923,91 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const
tail(STATE_ELEMENT);
tail(GRAPH_STATES_ELEMENT);
}

if (has_liveness_info()) {
head(LIVE_RANGES_ELEMENT);
for (uint i = 1; i < _chaitin->_lrg_map.max_lrg_id(); i++) {
begin_head(LIVE_RANGE_ELEMENT);
print_attr(LIVE_RANGE_ID_PROPERTY, i);
end_head();
head(PROPERTIES_ELEMENT);
const LRG& lrg = _chaitin->lrgs(i);
buffer[0] = 0;
stringStream lrg_mask_stream(buffer, sizeof(buffer) - 1);
lrg.mask().dump(&lrg_mask_stream);
print_prop("mask", buffer);
print_prop("mask_size", lrg.mask_size());
if (lrg._degree_valid) {
print_prop("degree", lrg.degree());
}
print_prop("num_regs", lrg.num_regs());
print_prop("reg_pressure", lrg.reg_pressure());
print_prop("cost", lrg._cost);
print_prop("area", lrg._area);
print_prop("score", lrg.score());
if (lrg._risk_bias != 0) {
print_prop("risk_bias", lrg._risk_bias);
}
if (lrg._copy_bias != 0) {
print_prop("copy_bias", lrg._copy_bias);
}
if (lrg.is_singledef()) {
print_prop("is_singledef", TRUE_VALUE);
}
if (lrg.is_multidef()) {
print_prop("is_multidef", TRUE_VALUE);
}
if (lrg._is_oop) {
print_prop("is_oop", TRUE_VALUE);
}
if (lrg._is_float) {
print_prop("is_float", TRUE_VALUE);
}
if (lrg._is_vector) {
print_prop("is_vector", TRUE_VALUE);
}
if (lrg._is_predicate) {
print_prop("is_predicate", TRUE_VALUE);
}
if (lrg._is_scalable) {
print_prop("is_scalable", TRUE_VALUE);
}
if (lrg._was_spilled1) {
print_prop("was_spilled1", TRUE_VALUE);
}
if (lrg._was_spilled2) {
print_prop("was_spilled2", TRUE_VALUE);
}
if (lrg._direct_conflict) {
print_prop("direct_conflict", TRUE_VALUE);
}
if (lrg._fat_proj) {
print_prop("fat_proj", TRUE_VALUE);
}
if (lrg._was_lo) {
print_prop("_was_lo", TRUE_VALUE);
}
if (lrg._has_copy) {
print_prop("has_copy", TRUE_VALUE);
}
if (lrg._at_risk) {
print_prop("at_risk", TRUE_VALUE);
}
if (lrg._must_spill) {
print_prop("must_spill", TRUE_VALUE);
}
if (lrg._is_bound) {
print_prop("is_bound", TRUE_VALUE);
}
if (lrg._msize_valid && lrg._degree_valid && lrg.lo_degree()) {
print_prop("trivial", TRUE_VALUE);
}
Comment on lines +938 to +1004
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Roberto,

Ihis is just a drive-by comment and I know that this style is standard in the IGP source code. However, have you considered re-writing this in the style of setting up the data and then looping over the data in order to print it?

Here's an example transformation I did from some other code in IGP:

// Before
    if (flags & Node::Flag_is_Copy) {
      print_prop("is_copy", "true");
    }
    if (flags & Node::Flag_rematerialize) {
      print_prop("rematerialize", "true");
    }
    if (flags & Node::Flag_needs_anti_dependence_check) {
      print_prop("needs_anti_dependence_check", "true");
    }
    if (flags & Node::Flag_is_macro) {
      print_prop("is_macro", "true");
    }
    if (flags & Node::Flag_is_Con) {
      print_prop("is_con", "true");
    }
    if (flags & Node::Flag_is_cisc_alternate) {
      print_prop("is_cisc_alternate", "true");
    }
    if (flags & Node::Flag_is_dead_loop_safe) {
      print_prop("is_dead_loop_safe", "true");
    }
    if (flags & Node::Flag_may_be_short_branch) {
      print_prop("may_be_short_branch", "true");
    }
    if (flags & Node::Flag_has_call) {
      print_prop("has_call", "true");
    }
    if (flags & Node::Flag_has_swapped_edges) {
      print_prop("has_swapped_edges", "true");
    }

```c++
// After
    struct BKV { int r; const char *name, *v; };
    const BKV r[] = {
        {                    flags & Node::Flag_is_Copy,                     "is_copy", "true"},
        {              flags & Node::Flag_rematerialize,               "rematerialize", "true"},
        {flags & Node::Flag_needs_anti_dependence_check, "needs_anti_dependence_check", "true"},
        {                   flags & Node::Flag_is_macro,                    "is_macro", "true"},
        {                     flags & Node::Flag_is_Con,                      "is_con", "true"},
        {          flags & Node::Flag_is_cisc_alternate,           "is_cisc_alternate", "true"},
        {          flags & Node::Flag_is_dead_loop_safe,           "is_dead_loop_safe", "true"},
        {        flags & Node::Flag_may_be_short_branch,         "may_be_short_branch", "true"},
        {                   flags & Node::Flag_has_call,                    "has_call", "true"},
        {          flags & Node::Flag_has_swapped_edges,           "has_swapped_edges", "true"}
    };
    for (size_t i = 0; i < sizeof(r) / sizeof(BKV); i++) {
      if (r[i].r != 0) {
        print_prop(r[i].name, r[i].v);
      }
    }

You save a lot of lines of code :-).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, Johan! I like the proposal but I think it is best left out as a separate RFE, to make sure it is applied consistently to the entire IGV graph printing code. I created JDK-8349835 for that.

tail(PROPERTIES_ELEMENT);
tail(LIVE_RANGE_ELEMENT);
}
tail(LIVE_RANGES_ELEMENT);
}

tail(GRAPH_ELEMENT);
_xml->flush();
}
Expand Down
7 changes: 6 additions & 1 deletion src/hotspot/share/opto/idealGraphPrinter.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -91,6 +91,10 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
static const char *METHOD_BCI_PROPERTY;
static const char *METHOD_SHORT_NAME_PROPERTY;
static const char *ASSEMBLY_ELEMENT;
static const char *LIVEOUT_ELEMENT;
static const char *LIVE_RANGE_ELEMENT;
static const char *LIVE_RANGE_ID_PROPERTY;
static const char *LIVE_RANGES_ELEMENT;

static int _file_count;
networkStream *_network_stream;
Expand All @@ -114,6 +118,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
ciField* get_field(const Node* node);
ciField* find_source_field_of_array_access(const Node* node, uint& depth);
static Node* get_load_node(const Node* node);
bool has_liveness_info() const;
void walk_nodes(Node* start, bool edges);
void begin_elem(const char *s);
void end_elem();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -41,7 +41,7 @@ protected void performAction(Node[] activatedNodes) {
SelectBytecodesCookie c = activatedNodes[0].getCookie(SelectBytecodesCookie.class);
InputGraphProvider p = LookupHistory.getLast(InputGraphProvider.class);
if (p != null) {
p.clearSelectedNodes();
p.clearSelectedElements();
p.addSelectedNodes(c.getNodes(), true);
p.centerSelectedNodes();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -35,6 +35,7 @@ public class InputBlock {
private final String name;
private final InputGraph graph;
private final Set<InputBlock> successors;
private Set<Integer> liveOut;
private boolean artificial;

@Override
Expand Down Expand Up @@ -70,6 +71,15 @@ public boolean equals(Object o) {
}
}

if (this.liveOut.size() != b.liveOut.size()) {
return false;
}
for (int liveRangeId : this.liveOut) {
if (!b.liveOut.contains(liveRangeId)) {
return false;
}
}

return true;
}

Expand All @@ -78,6 +88,7 @@ public boolean equals(Object o) {
this.name = name;
nodes = new ArrayList<>();
successors = new LinkedHashSet<>(2);
liveOut = new HashSet<Integer>(0);
artificial = false;
}

Expand All @@ -99,6 +110,14 @@ public void addNode(int id) {
nodes.add(node);
}

public void addLiveOut(int liveRangeId) {
liveOut.add(liveRangeId);
}

public Set<Integer> getLiveOut() {
return Collections.unmodifiableSet(liveOut);
}

public Set<InputBlock> getSuccessors() {
return Collections.unmodifiableSet(successors);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -38,6 +38,11 @@ public class InputGraph extends Properties.Entity implements FolderElement {
private final Map<String, InputBlock> blocks;
private final List<InputBlockEdge> blockEdges;
private final Map<Integer, InputBlock> nodeToBlock;
private final Map<Integer, InputLiveRange> liveRanges;
private Map<Integer, LivenessInfo> livenessInfo;
private Map<Integer, Set<InputNode>> relatedNodes;
private Map<Integer, Set<InputNode>> defNodes;
private Map<Integer, Set<InputNode>> useNodes;
private final boolean isDiffGraph;
private final InputGraph firstGraph;
private final InputGraph secondGraph;
Expand All @@ -58,6 +63,11 @@ private InputGraph(String name, InputGraph firstGraph, InputGraph secondGraph) {
nodes = new LinkedHashMap<>();
edges = new ArrayList<>();
blocks = new LinkedHashMap<>();
liveRanges = new LinkedHashMap<>();
livenessInfo = new LinkedHashMap<>();
relatedNodes = new LinkedHashMap<>();
defNodes = new LinkedHashMap<>();
useNodes = new LinkedHashMap<>();
blockEdges = new ArrayList<>();
nodeToBlock = new LinkedHashMap<>();
isDiffGraph = firstGraph != null && secondGraph != null;
Expand Down Expand Up @@ -304,12 +314,66 @@ public Group getGroup() {
return parentGroup;
}

public void addLiveRange(InputLiveRange lrg) {
liveRanges.put(lrg.getId(), lrg);
relatedNodes.put(lrg.getId(), new HashSet<>());
defNodes.put(lrg.getId(), new HashSet<>());
useNodes.put(lrg.getId(), new HashSet<>());
}

public InputLiveRange getLiveRange(int liveRangeId) {
return liveRanges.get(liveRangeId);
}

public Collection<InputLiveRange> getLiveRanges() {
return Collections.unmodifiableCollection(liveRanges.values());
}

public void addLivenessInfo(InputNode node, LivenessInfo info) {
livenessInfo.put(node.getId(), info);
if (info.def != null) {
relatedNodes.get(info.def).add(node);
defNodes.get(info.def).add(node);
}
if (info.use != null) {
for (int lrg : info.use) {
relatedNodes.get(lrg).add(node);
useNodes.get(lrg).add(node);
}
}
if (info.join != null) {
for (int lrg : info.join) {
relatedNodes.get(lrg).add(node);
useNodes.get(lrg).add(node);
}
}
}

public LivenessInfo getLivenessInfoForNode(InputNode node) {
return livenessInfo.get(node.getId());
}

public Set<InputNode> getRelatedNodes(int liveRangeId) {
return relatedNodes.get(liveRangeId);
}

public Set<InputNode> getDefNodes(int liveRangeId) {
return defNodes.get(liveRangeId);
}

public Set<InputNode> getUseNodes(int liveRangeId) {
return useNodes.get(liveRangeId);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Graph ").append(getName()).append(" ").append(getProperties().toString()).append("\n");
for (InputNode n : nodes.values()) {
sb.append(n.toString());
if (livenessInfo.containsKey(n.getId())) {
sb.append(" " + livenessInfo.get(n.getId()).toString());
}
sb.append("\n");
}

Expand All @@ -323,6 +387,11 @@ public String toString() {
sb.append("\n");
}

for (InputLiveRange l : liveRanges.values()) {
sb.append(l.toString());
sb.append("\n");
}

return sb.toString();
}

Expand Down
Loading