From 95231290383aeca3ff34764c3d410706e6bb58c3 Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Fri, 22 Mar 2024 23:48:16 -0400 Subject: [PATCH] Look deeper in a node's source position to detect the Truffle language being used. --- CHANGELOG.md | 1 + .../passes/truffle_translators/translators.rb | 10 + spec/seafoam/passes/truffle_spec.rb | 194 ++++++++++-------- 3 files changed, 114 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df28ccd..ad88906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug Fixes: Changes: +* Improve Truffle language detection by looking deeper into a node's source position (@nirvdrum). # 0.16 diff --git a/lib/seafoam/passes/truffle_translators/translators.rb b/lib/seafoam/passes/truffle_translators/translators.rb index f59eff8..c8473d1 100644 --- a/lib/seafoam/passes/truffle_translators/translators.rb +++ b/lib/seafoam/passes/truffle_translators/translators.rb @@ -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 diff --git a/spec/seafoam/passes/truffle_spec.rb b/spec/seafoam/passes/truffle_spec.rb index fd91f8f..1198582 100644 --- a/spec/seafoam/passes/truffle_spec.rb +++ b/spec/seafoam/passes/truffle_spec.rb @@ -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