| /* |
| * Copyright (c) 2024, 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 |
| * 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. |
| * |
| */ |
| |
| #include "opto/mempointer.hpp" |
| #include "opto/addnode.hpp" |
| #include "utilities/resourceHash.hpp" |
| #include "classfile/vmSymbols.hpp" |
| |
| MemPointerParserCallback MemPointerParserCallback::_empty; |
| |
| MemPointer::MemPointer(const MemNode* mem, |
| MemPointerParserCallback& callback |
| NOT_PRODUCT(COMMA const TraceMemPointer& trace)) : |
| MemPointer(MemPointerParser::parse(mem, |
| callback |
| NOT_PRODUCT(COMMA trace))) {} |
| |
| // Recursively parse the pointer expression with a DFS all-path traversal |
| // (i.e. with node repetitions), starting at the pointer. |
| MemPointer MemPointerParser::parse(MemPointerParserCallback& callback |
| NOT_PRODUCT(COMMA const TraceMemPointer& trace)) { |
| assert(_worklist.is_empty(), "no prior parsing"); |
| assert(_summands.is_empty(), "no prior parsing"); |
| |
| Node* pointer = _mem->in(MemNode::Address); |
| const jint size = _mem->memory_size(); |
| |
| // Start with the trivial summand. |
| _worklist.push(MemPointerSummand(pointer, NoOverflowInt(1))); |
| |
| // Decompose the summands until only terminal summands remain. This effectively |
| // parses the pointer expression recursively. |
| int traversal_count = 0; |
| while (_worklist.is_nonempty()) { |
| // Bail out if the graph is too complex. |
| if (traversal_count++ > 1000) { |
| return MemPointer::make_trivial(pointer, size NOT_PRODUCT(COMMA trace)); |
| } |
| parse_sub_expression(_worklist.pop(), callback); |
| } |
| |
| // Bail out if there is a constant overflow. |
| if (_con.is_NaN()) { |
| return MemPointer::make_trivial(pointer, size NOT_PRODUCT(COMMA trace)); |
| } |
| |
| // Sorting by variable idx means that all summands with the same variable are consecutive. |
| // This simplifies the combining of summands with the same variable below. |
| _summands.sort(MemPointerSummand::cmp_by_variable_idx); |
| |
| // Combine summands for the same variable, adding up the scales. |
| int pos_put = 0; |
| int pos_get = 0; |
| while (pos_get < _summands.length()) { |
| const MemPointerSummand& summand = _summands.at(pos_get++); |
| Node* variable = summand.variable(); |
| NoOverflowInt scale = summand.scale(); |
| // Add up scale of all summands with the same variable. |
| while (pos_get < _summands.length() && _summands.at(pos_get).variable() == variable) { |
| MemPointerSummand s = _summands.at(pos_get++); |
| scale = scale + s.scale(); |
| } |
| // Bail out if scale is NaN. |
| if (scale.is_NaN()) { |
| return MemPointer::make_trivial(pointer, size NOT_PRODUCT(COMMA trace)); |
| } |
| // Keep summands with non-zero scale. |
| if (!scale.is_zero()) { |
| _summands.at_put(pos_put++, MemPointerSummand(variable, scale)); |
| } |
| } |
| _summands.trunc_to(pos_put); |
| |
| return MemPointer::make(pointer, _summands, _con, size NOT_PRODUCT(COMMA trace)); |
| } |
| |
| // Parse a sub-expression of the pointer, starting at the current summand. We parse the |
| // current node, and see if it can be decomposed into further summands, or if the current |
| // summand is terminal. |
| void MemPointerParser::parse_sub_expression(const MemPointerSummand& summand, MemPointerParserCallback& callback) { |
| Node* n = summand.variable(); |
| const NoOverflowInt scale = summand.scale(); |
| const NoOverflowInt one(1); |
| |
| int opc = n->Opcode(); |
| if (is_safe_to_decompose_op(opc, scale)) { |
| switch (opc) { |
| case Op_ConI: |
| case Op_ConL: |
| { |
| // Terminal: add to constant. |
| NoOverflowInt con = (opc == Op_ConI) ? NoOverflowInt(n->get_int()) |
| : NoOverflowInt(n->get_long()); |
| _con = _con + scale * con; |
| return; |
| } |
| case Op_AddP: |
| case Op_AddL: |
| case Op_AddI: |
| { |
| // Decompose addition. |
| Node* a = n->in((opc == Op_AddP) ? 2 : 1); |
| Node* b = n->in((opc == Op_AddP) ? 3 : 2); |
| _worklist.push(MemPointerSummand(a, scale)); |
| _worklist.push(MemPointerSummand(b, scale)); |
| callback.callback(n); |
| return; |
| } |
| case Op_SubL: |
| case Op_SubI: |
| { |
| // Decompose subtraction. |
| Node* a = n->in(1); |
| Node* b = n->in(2); |
| |
| NoOverflowInt sub_scale = NoOverflowInt(-1) * scale; |
| |
| _worklist.push(MemPointerSummand(a, scale)); |
| _worklist.push(MemPointerSummand(b, sub_scale)); |
| callback.callback(n); |
| return; |
| } |
| case Op_MulL: |
| case Op_MulI: |
| case Op_LShiftL: |
| case Op_LShiftI: |
| { |
| // Only multiplication with constants is allowed: factor * variable |
| // IGVN already folds constants to in(2). If we find a variable there |
| // instead, we cannot further decompose this summand, and have to add |
| // it to the terminal summands. |
| Node* variable = n->in(1); |
| Node* con = n->in(2); |
| if (!con->is_Con()) { break; } |
| NoOverflowInt factor; |
| switch (opc) { |
| case Op_MulL: // variable * con |
| factor = NoOverflowInt(con->get_long()); |
| break; |
| case Op_MulI: // variable * con |
| factor = NoOverflowInt(con->get_int()); |
| break; |
| case Op_LShiftL: // variable << con = variable * (1 << con) |
| factor = one << NoOverflowInt(con->get_int()); |
| break; |
| case Op_LShiftI: // variable << con = variable * (1 << con) |
| factor = one << NoOverflowInt(con->get_int()); |
| break; |
| } |
| |
| // Accumulate scale. |
| NoOverflowInt new_scale = scale * factor; |
| |
| _worklist.push(MemPointerSummand(variable, new_scale)); |
| callback.callback(n); |
| return; |
| } |
| case Op_CastX2P: |
| // A CastX2P indicates that we are pointing to native memory, where some long is cast to |
| // a pointer. In general, we have no guarantees about this long, and just take it as a |
| // terminal summand. A CastX2P can also be a good candidate for a native-memory "base". |
| if (!sub_expression_has_native_base_candidate(n->in(1))) { |
| // General case: take CastX2P as a terminal summand, it is a candidate for the "base". |
| break; |
| } |
| // Fall-through: we can find a more precise native-memory "base". We further decompose |
| // the CastX2P to find this "base" and any other offsets from it. |
| case Op_CastII: |
| case Op_CastLL: |
| case Op_ConvI2L: |
| // On 32bit systems we can also look through ConvL2I, since the final result will always |
| // be truncated back with ConvL2I. On 64bit systems we cannot decompose ConvL2I because |
| // such int values will eventually be expanded to long with a ConvI2L: |
| // |
| // valL = max_jint + 1 |
| // ConvI2L(ConvL2I(valL)) = ConvI2L(min_jint) = min_jint != max_jint + 1 = valL |
| // |
| NOT_LP64( case Op_ConvL2I: ) |
| { |
| // Decompose: look through. |
| Node* a = n->in(1); |
| _worklist.push(MemPointerSummand(a, scale)); |
| callback.callback(n); |
| return; |
| } |
| default: |
| // All other operations cannot be further decomposed. We just add them to the |
| // terminal summands below. |
| break; |
| } |
| } |
| |
| // Default: we could not parse the "summand" further, i.e. it is terminal. |
| _summands.push(summand); |
| } |
| |
| bool MemPointerParser::sub_expression_has_native_base_candidate(Node* start) { |
| // BFS over the expression. |
| // Allocate sufficient space in worklist for 100 limit below. |
| ResourceMark rm; |
| GrowableArray<Node*> worklist(102); |
| worklist.append(start); |
| for (int i = 0; i < worklist.length(); i++) { |
| Node* n = worklist.at(i); |
| switch (n->Opcode()) { |
| case Op_AddL: |
| // Traverse to both inputs. |
| worklist.append(n->in(1)); |
| worklist.append(n->in(2)); |
| break; |
| case Op_SubL: |
| case Op_CastLL: |
| // Traverse to the first input. The base cannot be on the rhs of a sub. |
| worklist.append(n->in(1)); |
| break; |
| default: |
| if (is_native_memory_base_candidate(n)) { return true; } |
| break; |
| } |
| // This is a heuristic, so we are allowed to bail out early if the graph |
| // is too deep. The constant is chosen arbitrarily, not too large but big |
| // enough for all normal cases. |
| if (worklist.length() > 100) { return false; } |
| } |
| // Parsed over the whole expression, nothing found. |
| return false; |
| } |
| |
| // Check if the node is a candidate to be a memory segment "base". |
| // (1) CastX2P: some arbitrary long that is cast to a pointer. |
| // (2) LoadL from field jdk.internal.foreign.NativeMemorySegmentImpl.min |
| // Holds the address() of a native memory segment. |
| bool MemPointerParser::is_native_memory_base_candidate(Node* n) { |
| // (1) CastX2P |
| if (n->Opcode() == Op_CastX2P) { return true; } |
| |
| // (2) LoadL from field jdk.internal.foreign.NativeMemorySegmentImpl.min |
| if (n->Opcode() != Op_LoadL) { return false; } |
| LoadNode* load = n->as_Load(); |
| |
| const TypeInstPtr* inst_ptr = load->adr_type()->isa_instptr(); |
| if (inst_ptr == nullptr) { return false; } |
| |
| ciInstanceKlass* klass = inst_ptr->instance_klass(); |
| int offset = inst_ptr->offset(); |
| ciField* field = klass->get_field_by_offset(offset, false); |
| if (field == nullptr) { return false; } |
| |
| Symbol* field_symbol = field->name()->get_symbol(); |
| Symbol* holder_symbol = field->holder()->name()->get_symbol(); |
| return holder_symbol == vmSymbols::jdk_internal_foreign_NativeMemorySegmentImpl() && |
| field_symbol == vmSymbols::min_name(); |
| } |
| |
| // Check if the decomposition of operation opc is guaranteed to be safe. |
| // Please refer to the definition of "safe decomposition" in mempointer.hpp |
| bool MemPointerParser::is_safe_to_decompose_op(const int opc, const NoOverflowInt& scale) const { |
| #ifndef _LP64 |
| // On 32-bit platforms, the pointer has 32bits, and thus any higher bits will always |
| // be truncated. Thus, it does not matter if we have int or long overflows. |
| // Simply put: all decompositions are (SAFE1). |
| return true; |
| #else |
| |
| switch (opc) { |
| // These operations are always safe to decompose, i.e. (SAFE1): |
| case Op_ConI: |
| case Op_ConL: |
| case Op_AddP: |
| case Op_AddL: |
| case Op_SubL: |
| case Op_MulL: |
| case Op_LShiftL: |
| case Op_CastII: |
| case Op_CastLL: |
| case Op_CastX2P: |
| case Op_CastPP: |
| case Op_ConvI2L: |
| return true; |
| |
| // But on 64-bit platforms, these operations are not trivially safe to decompose: |
| case Op_AddI: // ConvI2L(a + b) != ConvI2L(a) + ConvI2L(b) |
| case Op_SubI: // ConvI2L(a - b) != ConvI2L(a) - ConvI2L(b) |
| case Op_MulI: // ConvI2L(a * conI) != ConvI2L(a) * ConvI2L(conI) |
| case Op_LShiftI: // ConvI2L(a << conI) != ConvI2L(a) << ConvI2L(conI) |
| break; // Analysis below. |
| |
| // All other operations are assumed not safe to decompose, or simply cannot be decomposed |
| default: |
| return false; |
| } |
| |
| const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr(); |
| if (ary_ptr_t != nullptr) { |
| // Array accesses that are not Unsafe always have a RangeCheck which ensures |
| // that there is no int overflow. And without overflows, all decompositions |
| // are (SAFE1). |
| if (!_mem->is_unsafe_access()) { |
| return true; |
| } |
| |
| // Intuition: In general, the decomposition of AddI, SubI, MulI or LShiftI is not safe, |
| // because of overflows. But under some conditions, we can prove that such a |
| // decomposition is (SAFE2). Intuitively, we want to prove that an overflow |
| // would mean that the pointers have such a large distance, that at least one |
| // must lie out of bounds. In the proof of the "MemPointer Lemma", we thus |
| // get a contradiction with the condition that both pointers are in bounds. |
| // |
| // We prove that the decomposition of AddI, SubI, MulI (with constant) and ShiftI (with |
| // constant) is (SAFE2), under the condition: |
| // |
| // abs(scale) % array_element_size_in_bytes = 0 |
| // |
| // First, we describe how the decomposition works: |
| // |
| // mp_i = con + sum(other_summands) + summand |
| // ------------------------- ------- |
| // rest scale * ConvI2L(op) |
| // |
| // We decompose the summand depending on the op, where we know that there is some |
| // integer y, such that: |
| // |
| // scale * ConvI2L(a + b) = scale * ConvI2L(a) + scale * ConvI2L(b) + scale * y * 2^32 |
| // scale * ConvI2L(a - b) = scale * ConvI2L(a) - scale * ConvI2L(b) + scale * y * 2^32 |
| // scale * ConvI2L(a * con) = scale * con * ConvI2L(a) + scale * y * 2^32 |
| // scale * ConvI2L(a << con) = scale * (1 << con) * ConvI2L(a) + scale * y * 2^32 |
| // \_______________________/ \_____________________________________/ \______________/ |
| // before decomposition after decomposition ("new_summands") overflow correction |
| // |
| // Thus, for AddI and SubI, we get: |
| // summand = new_summand1 + new_summand2 + scale * y * 2^32 |
| // |
| // mp_{i+1} = con + sum(other_summands) + new_summand1 + new_summand2 |
| // = con + sum(other_summands) + summand - scale * y * 2^32 |
| // = mp_i - scale * y * 2^32 |
| // |
| // And for MulI and ShiftI we get: |
| // summand = new_summand + scale * y * 2^32 |
| // |
| // mp_{i+1} = con + sum(other_summands) + new_summand |
| // = con + sum(other_summands) + summand - scale * y * 2^32 |
| // = mp_i - scale * y * 2^32 |
| // |
| // Further: |
| // abs(scale) % array_element_size_in_bytes = 0 |
| // implies that there is some integer z, such that: |
| // z * array_element_size_in_bytes = scale |
| // |
| // And hence, with "x = y * z", the decomposition is (SAFE2) under the assumed condition: |
| // mp_i = mp_{i+1} + scale * y * 2^32 |
| // = mp_{i+1} + z * array_element_size_in_bytes * y * 2^32 |
| // = mp_{i+1} + x * array_element_size_in_bytes * 2^32 |
| // |
| BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type(); |
| if (is_java_primitive(array_element_bt)) { |
| NoOverflowInt array_element_size_in_bytes = NoOverflowInt(type2aelembytes(array_element_bt)); |
| if (scale.is_multiple_of(array_element_size_in_bytes)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| #endif |
| } |
| |
| MemPointer::Base MemPointer::Base::make(Node* pointer, const GrowableArray<MemPointerSummand>& summands) { |
| // Bad form -> unknown. |
| AddPNode* adr = pointer->isa_AddP(); |
| if (adr == nullptr) { return Base(); } |
| |
| // Non-TOP base -> object. |
| Node* maybe_object_base = adr->in(AddPNode::Base); |
| bool is_object_base = !maybe_object_base->is_top(); |
| |
| Node* base = find_base(is_object_base ? maybe_object_base : nullptr, summands); |
| |
| if (base == nullptr) { |
| // Not found -> unknown. |
| return Base(); |
| } else if (is_object_base) { |
| assert(base == maybe_object_base, "we confirmed that it is in summands"); |
| return Base(Object, base); |
| } else { |
| return Base(Native, base); |
| } |
| } |
| |
| Node* MemPointer::Base::find_base(Node* object_base, const GrowableArray<MemPointerSummand>& summands) { |
| for (int i = 0; i < summands.length(); i++) { |
| const MemPointerSummand& s = summands.at(i); |
| assert(s.variable() != nullptr, "no empty summands"); |
| // Object base. |
| if (object_base != nullptr && s.variable() == object_base && s.scale().is_one()) { |
| return object_base; |
| } |
| // Native base. |
| if (object_base == nullptr && |
| s.scale().is_one() && |
| MemPointerParser::is_native_memory_base_candidate(s.variable())) { |
| return s.variable(); |
| } |
| } |
| return nullptr; |
| } |
| |
| // Compute the aliasing between two MemPointer. We use the "MemPointer Lemma" to prove that the |
| // computed aliasing also applies for the underlying pointers. Note that the condition (S0) is |
| // already given, because the MemPointer is always constructed using only safe decompositions. |
| // |
| // Pre-Condition: |
| // We assume that both pointers are in-bounds of their respective memory object. If this does |
| // not hold, for example, with the use of Unsafe, then we would already have undefined behavior, |
| // and we are allowed to do anything. |
| MemPointerAliasing MemPointer::get_aliasing_with(const MemPointer& other |
| NOT_PRODUCT( COMMA const TraceMemPointer& trace) ) const { |
| #ifndef PRODUCT |
| if (trace.is_trace_aliasing()) { |
| tty->print_cr("MemPointer::get_aliasing_with:"); |
| print_on(tty); |
| other.print_on(tty); |
| } |
| #endif |
| |
| // "MemPointer Lemma" condition (S2): check if all summands are the same: |
| bool has_same_base = false; |
| if (has_different_object_base_but_otherwise_same_summands_as(other)) { |
| // At runtime, the two object bases can be: |
| // (1) different: we have no aliasing, pointers point to different memory objects. |
| // (2) the same: implies that all summands are the same, (S2) holds. |
| has_same_base = false; |
| } else if (has_same_summands_as(other)) { |
| // (S2) holds. If all summands are the same, also the base must be the same. |
| has_same_base = true; |
| } else { |
| #ifndef PRODUCT |
| if (trace.is_trace_aliasing()) { |
| tty->print_cr(" -> Aliasing unknown, summands are not the same."); |
| } |
| #endif |
| return MemPointerAliasing::make_unknown(); |
| } |
| |
| // "MemPointer Lemma" condition (S3): check that the constants do not differ too much: |
| const NoOverflowInt distance = other.con() - con(); |
| // We must check that: abs(distance) < 2^32 |
| // However, this is only false if: distance = min_jint |
| if (distance.is_NaN() || distance.value() == min_jint) { |
| #ifndef PRODUCT |
| if (trace.is_trace_aliasing()) { |
| tty->print(" -> Aliasing unknown, bad distance: "); |
| distance.print_on(tty); |
| tty->cr(); |
| } |
| #endif |
| return MemPointerAliasing::make_unknown(); |
| } |
| |
| if (has_same_base) { |
| // "MemPointer Lemma" condition (S1): |
| // Given that all summands are the same, we know that both pointers point into the |
| // same memory object. With the Pre-Condition, we know that both pointers are in |
| // bounds of that same memory object. |
| // |
| // Hence, all 4 conditions of the "MemPointer Lemma" are established, and hence |
| // we know that the distance between the underlying pointers is equal to the distance |
| // we computed for the MemPointers: |
| // p_other - p_this = distance = other.con - this.con |
| #ifndef PRODUCT |
| if (trace.is_trace_aliasing()) { |
| tty->print_cr(" -> Aliasing always at distance = %d.", distance.value()); |
| } |
| #endif |
| return MemPointerAliasing::make_always_at_distance(distance.value()); |
| } else { |
| // At runtime, the two object bases can be: |
| // (1) different: pointers do not alias. |
| // (2) the same: implies that (S2) holds. The summands are all the same, and with |
| // the Pre-Condition, we know that both pointers are in bounds of the |
| // same memory object, i.e. (S1) holds. We have already proven (S0) |
| // and (S3), so all 4 conditions for "MemPointer Lemma" are given. |
| #ifndef PRODUCT |
| if (trace.is_trace_aliasing()) { |
| tty->print_cr(" -> Aliasing not or at distance = %d.", distance.value()); |
| } |
| #endif |
| return MemPointerAliasing::make_not_or_at_distance(distance.value()); |
| } |
| } |
| |
| bool MemPointer::has_same_summands_as(const MemPointer& other, uint start) const { |
| for (uint i = start; i < SUMMANDS_SIZE; i++) { |
| if (summands_at(i) != other.summands_at(i)) { return false; } |
| } |
| return true; |
| } |
| |
| bool MemPointer::has_different_object_base_but_otherwise_same_summands_as(const MemPointer& other) const { |
| if (!base().is_object() || |
| !other.base().is_object() || |
| base().object() == other.base().object()) { |
| // The base is the same, or we do not know if the base is different. |
| return false; |
| } |
| |
| #ifdef ASSERT |
| const MemPointerSummand base1(base().object(), NoOverflowInt(1)); |
| const MemPointerSummand base2(other.base().object(), NoOverflowInt(1)); |
| assert(summands_at(0) == base1 && other.summands_at(0) == base2, "bases in 0th element"); |
| #endif |
| |
| // Check if all other summands are the same. |
| return has_same_summands_as(other, 1); |
| } |
| |
| // Examples: |
| // p1 = MemPointer[size=1, base + i + 16] |
| // p2 = MemPointer[size=1, base + i + 17] |
| // -> Always at distance 1 |
| // -> p1 always adjacent and before p2 -> return true |
| // |
| // p1 = MemPointer[size=4, x + y + z + 4L * i + 16] |
| // p2 = MemPointer[size=4, x + y + z + 4L * i + 20] |
| // -> Always at distance 4 |
| // -> p1 always adjacent and before p2 -> return true |
| // |
| // p1 = MemPointer[size=4, base1 + 4L * i1 + 16] |
| // p2 = MemPointer[size=4, base2 + 4L * i2 + 20] |
| // -> Have differing summands, distance is unknown |
| // -> Unknown if adjacent at runtime -> return false |
| bool MemPointer::is_adjacent_to_and_before(const MemPointer& other) const { |
| const MemPointerAliasing aliasing = get_aliasing_with(other NOT_PRODUCT( COMMA _trace )); |
| const bool is_adjacent = aliasing.is_always_at_distance(_size); |
| |
| #ifndef PRODUCT |
| if (_trace.is_trace_adjacency()) { |
| tty->print("Adjacent: %s, because size = %d and aliasing = ", |
| is_adjacent ? "true" : "false", _size); |
| aliasing.print_on(tty); |
| tty->cr(); |
| } |
| #endif |
| |
| return is_adjacent; |
| } |
| |
| // Examples: |
| // p1 = MemPointer[size=1, base + i + 16] |
| // p2 = MemPointer[size=1, base + i + 17] |
| // -> Always at distance 1 |
| // -> Can never overlap -> return true |
| // |
| // p1 = MemPointer[size=1, base + i + 16] |
| // p2 = MemPointer[size=1, base + i + 16] |
| // -> Always at distance 0 |
| // -> Always have exact overlap -> return false |
| // |
| // p1 = MemPointer[size=4, x + y + z + 4L * i + 16] |
| // p2 = MemPointer[size=4, x + y + z + 4L * i + 56] |
| // -> Always at distance 40 |
| // -> Can never overlap -> return true |
| // |
| // p1 = MemPointer[size=8, x + y + z + 4L * i + 16] |
| // p2 = MemPointer[size=8, x + y + z + 4L * i + 20] |
| // -> Always at distance 4 |
| // -> Always have partial overlap -> return false |
| // |
| // p1 = MemPointer[size=4, base1 + 4L * i1 + 16] |
| // p2 = MemPointer[size=4, base2 + 4L * i2 + 20] |
| // -> Have differing summands, distance is unknown |
| // -> Unknown if overlap at runtime -> return false |
| bool MemPointer::never_overlaps_with(const MemPointer& other) const { |
| const MemPointerAliasing aliasing = get_aliasing_with(other NOT_PRODUCT( COMMA _trace )); |
| |
| // The aliasing tries to compute: |
| // distance = other - this |
| // |
| // We know that we have no overlap if we can prove: |
| // this >= other + other.size || this + this.size <= other |
| // |
| // Which we can restate as: |
| // distance <= -other.size || this.size <= distance |
| // |
| const jint distance_lo = -other.size(); |
| const jint distance_hi = size(); |
| bool is_never_overlap = aliasing.is_never_in_distance_range(distance_lo, distance_hi); |
| |
| #ifndef PRODUCT |
| if (_trace.is_trace_overlap()) { |
| tty->print("Never Overlap: %s, distance_lo: %d, distance_hi: %d, aliasing: ", |
| is_never_overlap ? "true" : "false", distance_lo, distance_hi); |
| aliasing.print_on(tty); |
| tty->cr(); |
| } |
| #endif |
| |
| return is_never_overlap; |
| } |
| |