Skip to content

Commit

Permalink
Merge pull request #92 from Shopify/improve-truffle-language-detection
Browse files Browse the repository at this point in the history
Look deeper in a node's source position to detect the Truffle language being used.
  • Loading branch information
nirvdrum authored Mar 25, 2024
2 parents 1b726bd + 9523129 commit a94c82d
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 91 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Bug Fixes:

Changes:

* Improve Truffle language detection by looking deeper into a node's source position (@nirvdrum).

# 0.16

Expand Down
10 changes: 10 additions & 0 deletions lib/seafoam/passes/truffle_translators/translators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ def get_translator(node)
subpackage = declaring_class[:declaring_class].match(/^(\w+\.\w+)\./)[1]
translator = TRUFFLE_LANGUAGES[subpackage]

unless translator
Seafoam::Graal::Source.walk(entry.props.dig("nodeSourcePosition")) do |method|
declaring_class = method[:declaring_class]
subpackage = declaring_class.match(/^(\w+\.\w+)\./)[1]
translator = TRUFFLE_LANGUAGES[subpackage]

break if translator
end
end

break const_get(translator).new if translator
end
end
Expand Down
194 changes: 103 additions & 91 deletions spec/seafoam/passes/truffle_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,111 +21,123 @@

describe "when run" do
Seafoam::SpecHelpers::GRAALVM_VERSIONS.each do |graalvm_version|
describe "with :simplify_alloc" do
before :all do
@graph = Seafoam::SpecHelpers.example_graph(
File.expand_path(
"../../../examples/#{graalvm_version}/ruby/example_object_allocation.bgv.gz",
__dir__,
),
"After TruffleTier",
)
pass = Seafoam::Passes::TrufflePass.new(simplify_alloc: true, hide_null_fields: true)
pass.apply(@graph)
end
describe "on GraalVM #{graalvm_version}" do
describe "with :simplify_alloc" do
before :all do
@graph = Seafoam::SpecHelpers.example_graph(
File.expand_path(
"../../../examples/#{graalvm_version}/ruby/example_object_allocation.bgv.gz",
__dir__,
),
"After TruffleTier",
)
pass = Seafoam::Passes::TrufflePass.new(simplify_alloc: true, hide_null_fields: true)
pass.apply(@graph)
end

it "hides all simple constant inputs to allocation nodes" do
@graph.nodes.each_value do |node|
next unless node.node_class.end_with?(".compiler.nodes.virtual.CommitAllocationNode")
it "hides all simple constant inputs to allocation nodes" do
@graph.nodes.each_value do |node|
next unless node.node_class.end_with?(".compiler.nodes.virtual.CommitAllocationNode")

node.inputs.each do |input|
next unless input.from.node_class.end_with?(".compiler.nodes.ConstantNode")
node.inputs.each do |input|
next unless input.from.node_class.end_with?(".compiler.nodes.ConstantNode")

if ["Object[null]", "0"].include?(input.from.props["rawvalue"])
expect(input.from.props[:hidden]).to(be_truthy)
if ["Object[null]", "0"].include?(input.from.props["rawvalue"])
expect(input.from.props[:hidden]).to(be_truthy)
end
end
end
end
end
end

describe "with :simplify_truffle_args" do
before :each do
@filename = File.expand_path(
"../../../examples/#{graalvm_version}/ruby/example_polymorphic_receiver.bgv.gz",
__dir__,
)
@phase_index = "After TruffleTier"
@graph = Seafoam::SpecHelpers.example_graph(@filename, @phase_index)
end

it "substitutes TruffleArgument nodes for LoadIndexed nodes" do
before_load_indexed_nodes = @graph.nodes.values.select do |n|
n.node_class.end_with?(".compiler.nodes.java.LoadIndexedNode")
end.map(&:dup)
before_truffle_argument_nodes = @graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end

# Verify initial graph state.
expect(before_load_indexed_nodes).not_to(be_empty)
expect(before_truffle_argument_nodes).to(be_empty)

# Run the Truffle argument simplification pass.
graph = Seafoam::SpecHelpers.example_graph(@filename, @phase_index)
pass = Seafoam::Passes::TrufflePass.new(simplify_truffle_args: true, hide_null_fields: true)
pass.apply(graph)

# Verify graph state after the pass has run.
after_load_indexed_nodes = graph.nodes.values.select do |n|
n.node_class.end_with?(".compiler.nodes.java.LoadIndexedNode")
end
after_truffle_argument_nodes = graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end

# Verify state after the pass has run. This test only makes sense if the TruffleArgument node list is
# non-empty.
expect(after_load_indexed_nodes.size).to(eq(before_load_indexed_nodes.size))
expect(after_truffle_argument_nodes).not_to(be_empty)

after_truffle_argument_nodes.each_with_index do |arg_node, index|
# The node_class should be the synthetic class value.
expect(arg_node.node_class).to(eq("TruffleArgument"))
describe "with :simplify_truffle_args" do
["example_polymorphic_receiver", "example_arith_operator"].each do |example|
describe "on #{example}" do
before :each do
@filename = File.expand_path(
"../../../examples/#{graalvm_version}/ruby/#{example}.bgv.gz",
__dir__,
)

@phase_index = "After TruffleTier"
@graph = Seafoam::SpecHelpers.example_graph(@filename, @phase_index)
end

# The "next" edges are never connected to the TruffleArgument node.
rewritten_edges = before_load_indexed_nodes[index].outputs.select { |edge| edge.props[:name] != "next" }
it "substitutes TruffleArgument nodes for LoadIndexed nodes" do
before_load_indexed_nodes = @graph.nodes.values.select do |n|
n.node_class.end_with?(".compiler.nodes.java.LoadIndexedNode")
end.map(&:dup)

before_truffle_argument_nodes = @graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end

# Verify initial graph state.
expect(before_load_indexed_nodes).not_to(be_empty)
expect(before_truffle_argument_nodes).to(be_empty)

# Run the Truffle argument simplification pass.
graph = Seafoam::SpecHelpers.example_graph(@filename, @phase_index)
pass = Seafoam::Passes::TrufflePass.new(simplify_truffle_args: true, hide_null_fields: true)
pass.apply(graph)

# Verify graph state after the pass has run.
after_load_indexed_nodes = graph.nodes.values.select do |n|
n.node_class.end_with?(".compiler.nodes.java.LoadIndexedNode")
end

after_truffle_argument_nodes = graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end

# Verify state after the pass has run. This test only makes sense if the TruffleArgument node list is
# non-empty.
expect(after_load_indexed_nodes.size).to(eq(before_load_indexed_nodes.size))
expect(after_truffle_argument_nodes).not_to(be_empty)

after_truffle_argument_nodes.each_with_index do |arg_node, index|
# The node_class should be the synthetic class value.
expect(arg_node.node_class).to(eq("TruffleArgument"))

# The "next" edges are never connected to the TruffleArgument node.
rewritten_edges = before_load_indexed_nodes[index].outputs.select do |edge|
edge.props[:name] != "next"
end

# The original LoadIndexed nodes should have no outgoing edges.
expect(after_load_indexed_nodes[index].outputs).to(be_empty)

# The new TruffleArgument nodes should have no incoming edges.
expect(arg_node.inputs).to(be_empty)

# The new TruffleArgument nodes should have edges pointing to the same edges the original LoadIndexed
# nodes did, minus the "next" nodes.
expect(arg_node.outputs.map(&:to).map(&:id)).to(eq(rewritten_edges.map(&:to).map(&:id)))
end
end

# The original LoadIndexed nodes should have no outgoing edges.
expect(after_load_indexed_nodes[index].outputs).to(be_empty)
it "uses symbolic Truffle argument names for TruffleRuby" do
# Run the Truffle argument simplification pass.
pass = Seafoam::Passes::TrufflePass.new(simplify_truffle_args: true, hide_null_fields: true)
pass.apply(@graph)

# The new TruffleArgument nodes should have no incoming edges.
expect(arg_node.inputs).to(be_empty)
# Verify graph state after the pass has run.
after_truffle_argument_nodes = @graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end

# The new TruffleArgument nodes should have edges pointing to the same edges the original LoadIndexed nodes
# did, minus the "next" nodes.
expect(arg_node.outputs.map(&:to).map(&:id)).to(eq(rewritten_edges.map(&:to).map(&:id)))
end
end
expect(after_truffle_argument_nodes).not_to(be_empty)

it "uses symbolic Truffle argument names for TruffleRuby" do
# Run the Truffle argument simplification pass.
pass = Seafoam::Passes::TrufflePass.new(simplify_truffle_args: true, hide_null_fields: true)
pass.apply(@graph)
truffleruby_arg_pattern = Regexp.union(
*Seafoam::Passes::TruffleTranslators::TruffleRuby::TRUFFLERUBY_ARGS,
/args\[\d+\]/,
)

# Verify graph state after the pass has run.
after_truffle_argument_nodes = @graph.nodes.values.select do |n|
n.props[:synthetic_class] == "TruffleArgument"
end
expect(after_truffle_argument_nodes).not_to(be_empty)

truffleruby_arg_pattern = Regexp.union(
*Seafoam::Passes::TruffleTranslators::TruffleRuby::TRUFFLERUBY_ARGS,
/args\[\d+\]/,
)

after_truffle_argument_nodes.each do |node|
expect(node.props[:label]).to(match(truffleruby_arg_pattern))
after_truffle_argument_nodes.each do |node|
expect(node.props[:label]).to(match(truffleruby_arg_pattern))
end
end
end
end
end
end
Expand Down

0 comments on commit a94c82d

Please sign in to comment.