jdk
1/*
2* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3* Copyright (c) 2024, Red Hat Inc.
4* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5*
6* This code is free software; you can redistribute it and/or modify it
7* under the terms of the GNU General Public License version 2 only, as
8* published by the Free Software Foundation.
9*
10* This code is distributed in the hope that it will be useful, but WITHOUT
11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13* version 2 for more details (a copy is included in the LICENSE file that
14* accompanied this code).
15*
16* You should have received a copy of the GNU General Public License version
17* 2 along with this work; if not, write to the Free Software Foundation,
18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19*
20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21* or visit www.oracle.com if you need additional information or have any
22* questions.
23*
24*/
25
26#include "precompiled.hpp"27#include "nmt/vmatree.hpp"28#include "utilities/growableArray.hpp"29
30const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::StackIndex{}, mtNone};31
32const char* VMATree::statetype_strings[3] = {33"reserved", "committed", "released",34};35
36VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state,37const RegionData& metadata) {38if (A == B) {39// A 0-sized mapping isn't worth recording.40return SummaryDiff();41}42
43IntervalChange stA{44IntervalState{StateType::Released, empty_regiondata},45IntervalState{ state, metadata}46};47IntervalChange stB{48IntervalState{ state, metadata},49IntervalState{StateType::Released, empty_regiondata}50};51
52// First handle A.53// Find closest node that is LEQ A54bool LEQ_A_found = false;55AddressState LEQ_A;56TreapNode* leqA_n = _tree.closest_leq(A);57if (leqA_n == nullptr) {58// No match. We add the A node directly, unless it would have no effect.59if (!stA.is_noop()) {60_tree.upsert(A, stA);61}62} else {63LEQ_A_found = true;64LEQ_A = AddressState{leqA_n->key(), leqA_n->val()};65// Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A.66// Consider the case where the found node is the start of a region enclosing [A,B)67stB.out = leqA_n->val().out;68
69// Direct address match.70if (leqA_n->key() == A) {71// Take over in state from old address.72stA.in = leqA_n->val().in;73
74// We may now be able to merge two regions:75// If the node's old state matches the new, it becomes a noop. That happens, for example,76// when expanding a committed area: commit [x1, A); ... commit [A, x3)77// and the result should be a larger area, [x1, x3). In that case, the middle node (A and le_n)78// is not needed anymore. So we just remove the old node.79stB.in = stA.out;80if (stA.is_noop()) {81// invalidates leqA_n82_tree.remove(leqA_n->key());83} else {84// If the state is not matching then we have different operations, such as:85// reserve [x1, A); ... commit [A, x2); or86// reserve [x1, A), flag1; ... reserve [A, x2), flag2; or87// reserve [A, x1), flag1; ... reserve [A, x2), flag2;88// then we re-use the existing out node, overwriting its old metadata.89leqA_n->val() = stA;90}91} else {92// The address must be smaller.93assert(A > leqA_n->key(), "must be");94
95// We add a new node, but only if there would be a state change. If there would not be a96// state change, we just omit the node.97// That happens, for example, when reserving within an already reserved region with identical metadata.98stA.in = leqA_n->val().out; // .. and the region's prior state is the incoming state99if (stA.is_noop()) {100// Nothing to do.101} else {102// Add new node.103_tree.upsert(A, stA);104}105}106}107
108// Now we handle B.109// We first search all nodes that are (A, B]. All of these nodes110// need to be deleted and summary accounted for. The last node before B determines B's outgoing state.111// If there is no node between A and B, its A's incoming state.112GrowableArrayCHeap<AddressState, mtNMT> to_be_deleted_inbetween_a_b;113bool B_needs_insert = true;114
115// Find all nodes between (A, B] and record their addresses and values. Also update B's116// outgoing state.117_tree.visit_range_in_order(A + 1, B + 1, [&](TreapNode* head) {118int cmp_B = PositionComparator::cmp(head->key(), B);119stB.out = head->val().out;120if (cmp_B < 0) {121// Record all nodes preceding B.122to_be_deleted_inbetween_a_b.push({head->key(), head->val()});123} else if (cmp_B == 0) {124// Re-purpose B node, unless it would result in a noop node, in125// which case record old node at B for deletion and summary accounting.126if (stB.is_noop()) {127to_be_deleted_inbetween_a_b.push(AddressState{B, head->val()});128} else {129head->val() = stB;130}131B_needs_insert = false;132}133});134
135// Insert B node if needed136if (B_needs_insert && // Was not already inserted137!stB.is_noop()) // The operation is differing138{139_tree.upsert(B, stB);140}141
142// We now need to:143// a) Delete all nodes between (A, B]. Including B in the case of a noop.144// b) Perform summary accounting145SummaryDiff diff;146
147if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) {148// We must have smashed a hole in an existing region (or replaced it entirely).149// LEQ_A < A < B <= C150SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(LEQ_A.out().flag())];151if (LEQ_A.out().type() == StateType::Reserved) {152rescom.reserve -= B - A;153} else if (LEQ_A.out().type() == StateType::Committed) {154rescom.commit -= B - A;155rescom.reserve -= B - A;156}157}158
159// Track the previous node.160AddressState prev{A, stA};161for (int i = 0; i < to_be_deleted_inbetween_a_b.length(); i++) {162const AddressState delete_me = to_be_deleted_inbetween_a_b.at(i);163_tree.remove(delete_me.address);164
165// Perform summary accounting166SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(delete_me.in().flag())];167if (delete_me.in().type() == StateType::Reserved) {168rescom.reserve -= delete_me.address - prev.address;169} else if (delete_me.in().type() == StateType::Committed) {170rescom.commit -= delete_me.address - prev.address;171rescom.reserve -= delete_me.address - prev.address;172}173prev = delete_me;174}175
176if (prev.address != A && prev.out().type() != StateType::Released) {177// The last node wasn't released, so it must be connected to a node outside of (A, B)178// A - prev - B - (some node >= B)179// It might be that prev.address == B == (some node >= B), this is fine.180if (prev.out().type() == StateType::Reserved) {181SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(prev.out().flag())];182rescom.reserve -= B - prev.address;183} else if (prev.out().type() == StateType::Committed) {184SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(prev.out().flag())];185rescom.commit -= B - prev.address;186rescom.reserve -= B - prev.address;187}188}189
190// Finally, we can register the new region [A, B)'s summary data.191SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(metadata.flag)];192if (state == StateType::Reserved) {193rescom.reserve += B - A;194} else if (state == StateType::Committed) {195rescom.commit += B - A;196rescom.reserve += B - A;197}198return diff;199}
200