8276455: C2: iterative EA

Reviewed-by: iveresov, neliasso, roland
This commit is contained in:
Vladimir Kozlov 2021-12-14 19:24:13 +00:00
parent de65230d71
commit a1dfe57249
10 changed files with 561 additions and 34 deletions
src/hotspot/share/opto
test
hotspot/jtreg/compiler
micro/org/openjdk/bench/vm/compiler

@ -48,6 +48,9 @@ const char* C2Compiler::retry_no_escape_analysis() {
const char* C2Compiler::retry_no_locks_coarsening() {
return "retry without locks coarsening";
}
const char* C2Compiler::retry_no_iterative_escape_analysis() {
return "retry without iterative escape analysis";
}
const char* C2Compiler::retry_class_loading_during_parsing() {
return "retry class loading during parsing";
}
@ -99,12 +102,13 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo
bool subsume_loads = SubsumeLoads;
bool do_escape_analysis = DoEscapeAnalysis;
bool do_iterative_escape_analysis = DoEscapeAnalysis;
bool eliminate_boxing = EliminateAutoBox;
bool do_locks_coarsening = EliminateLocks;
while (!env->failing()) {
// Attempt to compile while subsuming loads into machine instructions.
Options options(subsume_loads, do_escape_analysis, eliminate_boxing, do_locks_coarsening, install_code);
Options options(subsume_loads, do_escape_analysis, do_iterative_escape_analysis, eliminate_boxing, do_locks_coarsening, install_code);
Compile C(env, target, entry_bci, options, directive);
// Check result and retry if appropriate.
@ -125,6 +129,12 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo
env->report_failure(C.failure_reason());
continue; // retry
}
if (C.failure_reason_is(retry_no_iterative_escape_analysis())) {
assert(do_iterative_escape_analysis, "must make progress");
do_iterative_escape_analysis = false;
env->report_failure(C.failure_reason());
continue; // retry
}
if (C.failure_reason_is(retry_no_locks_coarsening())) {
assert(do_locks_coarsening, "must make progress");
do_locks_coarsening = false;

@ -49,6 +49,7 @@ public:
// sentinel value used to trigger backtracking in compile_method().
static const char* retry_no_subsuming_loads();
static const char* retry_no_escape_analysis();
static const char* retry_no_iterative_escape_analysis();
static const char* retry_no_locks_coarsening();
static const char* retry_class_loading_during_parsing();

@ -499,6 +499,12 @@ void Compile::print_compile_messages() {
tty->print_cr("** Bailout: Recompile without escape analysis **");
tty->print_cr("*********************************************************");
}
if (do_iterative_escape_analysis() != DoEscapeAnalysis && PrintOpto) {
// Recompiling without iterative escape analysis
tty->print_cr("*********************************************************");
tty->print_cr("** Bailout: Recompile without iterative escape analysis**");
tty->print_cr("*********************************************************");
}
if ((eliminate_boxing() != EliminateAutoBox) && PrintOpto) {
// Recompiling without boxing elimination
tty->print_cr("*********************************************************");
@ -2161,27 +2167,37 @@ void Compile::Optimize() {
if (major_progress()) print_method(PHASE_PHASEIDEAL_BEFORE_EA, 2);
if (failing()) return;
}
ConnectionGraph::do_analysis(this, &igvn);
if (failing()) return;
// Optimize out fields loads from scalar replaceable allocations.
igvn.optimize();
print_method(PHASE_ITER_GVN_AFTER_EA, 2);
if (failing()) return;
if (congraph() != NULL && macro_count() > 0) {
TracePhase tp("macroEliminate", &timers[_t_macroEliminate]);
PhaseMacroExpand mexp(igvn);
mexp.eliminate_macro_nodes();
igvn.set_delay_transform(false);
igvn.optimize();
print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2);
bool progress;
do {
ConnectionGraph::do_analysis(this, &igvn);
if (failing()) return;
}
int mcount = macro_count(); // Record number of allocations and locks before IGVN
// Optimize out fields loads from scalar replaceable allocations.
igvn.optimize();
print_method(PHASE_ITER_GVN_AFTER_EA, 2);
if (failing()) return;
if (congraph() != NULL && macro_count() > 0) {
TracePhase tp("macroEliminate", &timers[_t_macroEliminate]);
PhaseMacroExpand mexp(igvn);
mexp.eliminate_macro_nodes();
igvn.set_delay_transform(false);
igvn.optimize();
print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2);
if (failing()) return;
}
progress = do_iterative_escape_analysis() &&
(macro_count() < mcount) &&
ConnectionGraph::has_candidates(this);
// Try again if candidates exist and made progress
// by removing some allocations and/or locks.
} while (progress);
}
// Loop transforms on the ideal graph. Range Check Elimination,

@ -168,15 +168,18 @@ class Options {
private:
const bool _subsume_loads; // Load can be matched as part of a larger op.
const bool _do_escape_analysis; // Do escape analysis.
const bool _do_iterative_escape_analysis; // Do iterative escape analysis.
const bool _eliminate_boxing; // Do boxing elimination.
const bool _do_locks_coarsening; // Do locks coarsening
const bool _install_code; // Install the code that was compiled
public:
Options(bool subsume_loads, bool do_escape_analysis,
bool do_iterative_escape_analysis,
bool eliminate_boxing, bool do_locks_coarsening,
bool install_code) :
_subsume_loads(subsume_loads),
_do_escape_analysis(do_escape_analysis),
_do_iterative_escape_analysis(do_iterative_escape_analysis),
_eliminate_boxing(eliminate_boxing),
_do_locks_coarsening(do_locks_coarsening),
_install_code(install_code) {
@ -186,6 +189,7 @@ class Options {
return Options(
/* subsume_loads = */ true,
/* do_escape_analysis = */ false,
/* do_iterative_escape_analysis = */ false,
/* eliminate_boxing = */ false,
/* do_lock_coarsening = */ false,
/* install_code = */ true
@ -534,6 +538,7 @@ class Compile : public Phase {
bool subsume_loads() const { return _options._subsume_loads; }
/** Do escape analysis. */
bool do_escape_analysis() const { return _options._do_escape_analysis; }
bool do_iterative_escape_analysis() const { return _options._do_iterative_escape_analysis; }
/** Do boxing elimination. */
bool eliminate_boxing() const { return _options._eliminate_boxing; }
/** Do aggressive boxing elimination. */

@ -41,7 +41,7 @@
#include "opto/rootnode.hpp"
#include "utilities/macros.hpp"
ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) :
ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn, int invocation) :
_nodes(C->comp_arena(), C->unique(), C->unique(), NULL),
_in_worklist(C->comp_arena()),
_next_pidx(0),
@ -49,6 +49,9 @@ ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) :
_verify(false),
_compile(C),
_igvn(igvn),
_invocation(invocation),
_build_iterations(0),
_build_time(0.),
_node_map(C->comp_arena()) {
// Add unknown java object.
add_java_object(C->top(), PointsToNode::GlobalEscape);
@ -96,7 +99,11 @@ void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) {
// to create space for them in ConnectionGraph::_nodes[].
Node* oop_null = igvn->zerocon(T_OBJECT);
Node* noop_null = igvn->zerocon(T_NARROWOOP);
ConnectionGraph* congraph = new(C->comp_arena()) ConnectionGraph(C, igvn);
int invocation = 0;
if (C->congraph() != NULL) {
invocation = C->congraph()->_invocation + 1;
}
ConnectionGraph* congraph = new(C->comp_arena()) ConnectionGraph(C, igvn, invocation);
// Perform escape analysis
if (congraph->compute_escape()) {
// There are non escaping objects.
@ -1326,18 +1333,12 @@ bool ConnectionGraph::complete_connection_graph(
C->log()->text("%s", timeout ? "time" : "iterations");
C->log()->end_elem(" limit'");
}
assert(ExitEscapeAnalysisOnTimeout, "infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d",
_build_time, _build_iterations, nodes_size(), ptnodes_worklist.length());
assert(ExitEscapeAnalysisOnTimeout, "infinite EA connection graph build during invocation %d (%f sec, %d iterations) with %d nodes and worklist size %d",
_invocation, _build_time, _build_iterations, nodes_size(), ptnodes_worklist.length());
// Possible infinite build_connection_graph loop,
// bailout (no changes to ideal graph were made).
return false;
}
#ifdef ASSERT
if (Verbose && PrintEscapeAnalysis) {
tty->print_cr("EA: %d iterations and %f sec to build connection graph with %d nodes and worklist size %d",
_build_iterations, _build_time, nodes_size(), ptnodes_worklist.length());
}
#endif
#undef GRAPH_BUILD_ITER_LIMIT
@ -2645,7 +2646,7 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro
// Retry compilation without escape analysis.
// If this is the first failure, the sentinel string will "stick"
// to the Compile object, and the C2Compiler will see it and retry.
C->record_failure(C2Compiler::retry_no_escape_analysis());
C->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis());
}
return NULL;
}
@ -3216,7 +3217,7 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
ptnode_adr(n->_idx)->dump();
assert(jobj != NULL && jobj != phantom_obj, "escaped allocation");
#endif
_compile->record_failure(C2Compiler::retry_no_escape_analysis());
_compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis());
return;
}
Node *base = get_map(jobj->idx()); // CheckCastPP node
@ -3236,7 +3237,7 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
ptnode_adr(n->_idx)->dump();
assert(jobj != NULL && jobj != phantom_obj, "escaped allocation");
#endif
_compile->record_failure(C2Compiler::retry_no_escape_analysis());
_compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis());
return;
} else {
Node *val = get_map(jobj->idx()); // CheckCastPP node
@ -3692,6 +3693,9 @@ void ConnectionGraph::dump(GrowableArray<PointsToNode*>& ptnodes_worklist) {
tty->print("======== Connection graph for ");
_compile->method()->print_short_name();
tty->cr();
tty->print_cr("invocation #%d: %d iterations and %f sec to build connection graph with %d nodes and worklist size %d",
_invocation, _build_iterations, _build_time, nodes_size(), ptnodes_worklist.length());
tty->cr();
first = false;
}
ptn->dump();

@ -340,6 +340,7 @@ private:
Unique_Node_List ideal_nodes; // Used by CG construction and types splitting.
int _invocation; // Current number of analysis invocation
int _build_iterations; // Number of iterations took to build graph
double _build_time; // Time (sec) took to build graph
@ -569,7 +570,7 @@ private:
bool compute_escape();
public:
ConnectionGraph(Compile *C, PhaseIterGVN *igvn);
ConnectionGraph(Compile *C, PhaseIterGVN *igvn, int iteration);
// Check for non-escaping candidates
static bool has_candidates(Compile *C);

@ -0,0 +1,128 @@
/*
* Copyright (c) 2021, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.c2.irTests;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.test.lib.Asserts;
import compiler.lib.ir_framework.*;
/*
* @test
* @bug 8276455
* @summary Test C2 iterative Escape Analysis to remove all allocations in test
* @library /test/lib /
* @run driver compiler.c2.irTests.TestIterativeEA
*/
public class TestIterativeEA {
public static void main(String[] args) {
TestFramework.run();
}
static class MyClass {
int val;
public MyClass(int val) {
this.val = val;
}
}
static class AbstractClass {
final int unused;
public AbstractClass() {
unused = 42;
}
}
static class HolderWithSuper extends AbstractClass {
final MyClass obj;
public HolderWithSuper(MyClass obj) {
this.obj = obj;
}
}
static class Holder {
final MyClass obj;
public Holder(MyClass obj) {
this.obj = obj;
}
}
static class GenericHolder {
final Object obj;
public GenericHolder(Object obj) {
this.obj = obj;
}
}
@Test
@Arguments({ Argument.RANDOM_EACH })
@IR(failOn = { IRNode.ALLOC })
public static int testSlow(int val) {
MyClass obj = new MyClass(val);
HolderWithSuper h1 = new HolderWithSuper(obj);
GenericHolder h2 = new GenericHolder(h1);
return ((HolderWithSuper)h2.obj).obj.val;
}
@Test
@Arguments({ Argument.RANDOM_EACH })
@IR(failOn = { IRNode.ALLOC })
public static int testFast(int val) {
MyClass obj = new MyClass(val);
Holder h1 = new Holder(obj);
GenericHolder h2 = new GenericHolder(h1);
return ((Holder)h2.obj).obj.val;
}
static class A {
int i;
public A(int i) {
this.i = i;
}
}
static class B {
A a;
public B(A a) {
this.a = a;
}
}
static class C {
B b;
public C(B b) {
this.b = b;
}
}
@Test
@Arguments({ Argument.RANDOM_EACH })
@IR(failOn = { IRNode.ALLOC })
static int testNested(int i) {
C c = new C(new B(new A(i)));
return c.b.a.i;
}
}

@ -0,0 +1,93 @@
/*
* Copyright (c) 2021, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8276455
* @summary Test C2 iterative Escape Analysis
* @library /test/lib /
*
* @requires vm.flagless
* @requires vm.compiler2.enabled & vm.debug == true
*
* @run driver TestIterativeEA
*/
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class TestIterativeEA {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-server", "-XX:-TieredCompilation", "-Xbatch", "-XX:+PrintEliminateAllocations",
Launcher.class.getName());
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
System.out.println(analyzer.getOutput());
analyzer.shouldHaveExitValue(0);
analyzer.shouldContain("++++ Eliminated: 26 Allocate");
analyzer.shouldContain("++++ Eliminated: 48 Allocate");
analyzer.shouldContain("++++ Eliminated: 78 Allocate");
}
static class A {
int i;
public A(int i) {
this.i = i;
}
}
static class B {
A a;
public B(A a) {
this.a = a;
}
}
static class C {
B b;
public C(B b) {
this.b = b;
}
}
static int test(int i) {
C c = new C(new B(new A(i)));
return c.b.a.i;
}
static class Launcher {
public static void main(String[] args) {
for (int i = 0; i < 12000; ++i) {
int j = test(i);
}
}
}
}

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.vm.compiler;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(1)
public class IterativeEA {
public static int ii = 1;
static class A {
int i;
public A(int i) {
this.i = i;
}
}
static class B {
A a;
public B(A a) {
this.a = a;
}
}
static class C {
B b;
public C(B b) {
this.b = b;
}
}
@Benchmark
public int test1() {
C c = new C(new B(new A(ii)));
return c.b.a.i;
}
static class Point {
int x;
int y;
int ax[];
int ay[];
}
@Benchmark
public int test2() {
Point p = new Point();
p.ax = new int[2];
p.ay = new int[2];
int x = 3;
p.ax[0] = x;
p.ay[1] = 3 * x + ii;
return p.ax[0] * p.ay[1];
}
public static final Double dbc = Double.valueOf(1.);
@Benchmark
public double test3() {
Double j1 = Double.valueOf(1.);
Double j2 = Double.valueOf(1.);
for (int i = 0; i< 1000; i++) {
j1 = j1 + 1.;
j2 = j2 + 2.;
}
return j1 + j2;
}
}

@ -0,0 +1,164 @@
/*
* Copyright (c) 2021, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.vm.compiler;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import java.util.concurrent.TimeUnit;
@Fork(value = 3)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class PointerBenchmarkFlat {
static final int ELEM_SIZE = 1_000_000;
PointerImpl ptr_ptr;
PointerImplFlat ptr_ptr_flat;
@Setup
public void setup() {
ptr_ptr = new PointerImpl(new FakeSegment(MemoryAddress.NULL, Long.MAX_VALUE));
ptr_ptr_flat = new PointerImplFlat(new FakeSegmentFlat(MemoryAddress.NULL, Long.MAX_VALUE));
}
static class MemoryAddress {
private long addr;
public MemoryAddress(long addr) {
this.addr = addr;
}
long toRawLongValue() {
return addr;
}
private static final MemoryAddress NULL = new MemoryAddress(0);
static MemoryAddress ofLong(long val) {
return new MemoryAddress(val);
}
}
static class PointerImpl {
final FakeSegment segment;
public PointerImpl(FakeSegment segment) {
this.segment = segment;
}
MemoryAddress address() {
return segment.address();
}
PointerImpl get(long index) {
MemoryAddress address = MemoryAddress.ofLong(index);
FakeSegment holder = new FakeSegment(address, Long.MAX_VALUE);
return new PointerImpl(holder);
}
}
static class PointerImplFlat {
final FakeSegmentFlat segment;
public PointerImplFlat(FakeSegmentFlat segment) {
this.segment = segment;
}
MemoryAddress address() {
return segment.address();
}
PointerImplFlat get(long index) {
MemoryAddress address = MemoryAddress.ofLong(index);
FakeSegmentFlat holder = new FakeSegmentFlat(address, Long.MAX_VALUE);
return new PointerImplFlat(holder);
}
}
static class AbstractFakeSegment {
final long size;
public AbstractFakeSegment(long size) {
this.size = size;
}
}
static class FakeSegment extends AbstractFakeSegment {
final MemoryAddress address;
public FakeSegment(MemoryAddress address, long size) {
super(size);
this.address = address;
}
MemoryAddress address() {
return address;
}
}
static class FakeSegmentFlat {
final MemoryAddress address;
final long size;
public FakeSegmentFlat(MemoryAddress address, long size) {
this.size = size;
this.address = address;
}
MemoryAddress address() {
return address;
}
}
@Benchmark
public int test() {
int sum = 0;
for (int i = 0 ; i < ELEM_SIZE ; i++) {
sum += ptr_ptr.get(i).address().toRawLongValue();
}
return sum;
}
@Benchmark
public int testFlat() {
int sum = 0;
for (int i = 0 ; i < ELEM_SIZE ; i++) {
sum += ptr_ptr_flat.get(i).address().toRawLongValue();
}
return sum;
}
}