/* * Copyright (c) 2007, 2012, 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 com.sun.max.vm.thread; import static com.sun.max.vm.intrinsics.Infopoints.*; import static com.sun.max.vm.runtime.VMRegister.*; import static com.sun.max.vm.stack.StackReferenceMapPreparer.*; import static com.sun.max.vm.thread.VmThread.*; import java.lang.reflect.*; import java.util.*; import com.sun.cri.ci.CiKind.*; import com.sun.max.annotate.*; import com.sun.max.config.*; import com.sun.max.program.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.Log.LogPrintStream; import com.sun.max.vm.heap.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.jni.*; import com.sun.max.vm.log.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.stack.*; /** * VM thread local variables and mechanisms for accessing them. The majority of thread locals * are defined as static field of this class itself. However, thread locals can also be defined * in other {@linkplain VMScheme scheme}-specific classes. * <p> * All {@link VmThreadLocal} objects must be instantiated before * {@link #tlaSize()} or {@link #values()} is called. The recommended way to ensure this is * to explicitly reference these instances from the constructor of a {@link BootImagePackage} * subclass. A set of {@code registerThreadLocal()} methods are provided in * {@link BootImagePackage} to support this. * <p> * All thread local variables occupy one word and have a constant {@linkplain Nature nature}. * <p> * All thread locals are in a contiguous block of memory called a thread locals area (TLA) and there * are three TLAs per thread, one for each of the {@linkplain SafepointPoll safepoint} states: * <dl> * <dt>Enabled</dt> * <dd>Safepoints for the thread are {@linkplain SafepointPoll#enable() enabled}. The base address of this TLA is * obtained by reading the {@link #ETLA} variable from any TLA.</dd> * <dt>Disabled</dt> * <dd>Safepoints for the thread are {@linkplain SafepointPoll#disable() disabled}. The base address of this TLA is * obtained by reading the {@link #DTLA} variable from any TLA.</dd> * <dt>Triggered</dt> * <dd>Safepoints for the thread are {@linkplain SafepointPoll#isTriggered() triggered}. The base address of * this TLA is obtained by reading the {@link #TTLA} variable from any TLA.</dd> * </dl> * * The memory for each TLA is within a thread locals block allocated by the native code that starts a thread * (see the function 'thread_run' in Native/substrate/threads.c). A thread locals block contains not only * the three TLAs but other thread local data such as the stack * reference map. The format of the thread locals block is: * * <pre> * (low addresses) * * page aligned --> +---------------------------------------------+ * | X X X unmapped page X X X | * | X X X X X X | * page aligned --> +---------------------------------------------+ * | TLA (triggered) | * +---------------------------------------------+ * | TLA (enabled) | * +---------------------------------------------+ * | TLA (disabled) | * +---------------------------------------------+ * | NativeThreadLocalsStruct | * +---------------------------------------------+ * | | * | reference map | * | | * +---------------------------------------------+ * * (high addresses) * </pre> * * The thread local block layout for each thread is traced when a thread starts up if * the {@link VmThread#TraceThreads -XX:+TraceThreads} VM option is used. */ public class VmThreadLocal implements FormatWithToString { /** * Constants describing the read and write protocol for a thread local. * */ public enum Nature { /** * Denotes a thread local that has it's value maintained only in the {@linkplain VmThreadLocal#ETLA safepoints-enabled} TLA. * This should be used for thread locals that need to be written atomically (e.g. {@link VmThreadLocal#MUTATOR_STATE}) and/or in a fast path * (e.g. {@link VmThreadLocal#LAST_JAVA_FRAME_ANCHOR} which is written in JNI stubs). * * Note that the TLA variable used to access this variable must be the ETLA value. This * invariant is tested in a {@linkplain MaxineVM#isDebug() debug} VM. */ Single, /** * Denotes a thread local that has it's value maintained in all three TLAs for a thread. * This is used for thread locals that are initialized only once or whose update is not performance critical. * These local variables must be updated using one of the {@code store3(...)} methods in {@link VmThreadLocal}. */ Triple; } @INSPECTED private static final List<VmThreadLocal> VALUES = new ArrayList<VmThreadLocal>(); /** * Must be first. */ public static final VmThreadLocal SAFEPOINT_LATCH = new VmThreadLocal("SAFEPOINT_LATCH", false, "memory location loaded by safepoint instruction", Nature.Single); /** * The {@linkplain VmThread#currentTLA() current} thread local storage when safepoints for the thread are * {@linkplain SafepointPoll#enable() enabled}. */ public static final VmThreadLocal ETLA = new VmThreadLocal("ETLA", false, "points to TLA used when safepoints enabled"); /** * The {@linkplain VmThread#currentTLA() current} thread local storage when safepoints for the thread are * {@linkplain SafepointPoll#disable() disabled}. */ public static final VmThreadLocal DTLA = new VmThreadLocal("DTLA", false, "points to TLA used when safepoints disabled"); /** * The {@linkplain VmThread#currentTLA() current} thread local storage when safepoints for the thread are * triggered. */ public static final VmThreadLocal TTLA = new VmThreadLocal("TTLA", false, "points to TLA used when safepoints triggered"); public static final VmThreadLocal NATIVE_THREAD_LOCALS = new VmThreadLocal("NATIVE_THREAD_LOCALS", false, "pointer to a NativeThreadLocalsStruct"); /** * Next link for a doubly-linked list of all thread locals for active threads. */ public static final VmThreadLocal FORWARD_LINK = new VmThreadLocal("FORWARD_LINK", false, "points to next thread locals in list of all active"); /** * Previous link for a doubly-linked list of all thread locals for active threads. */ public static final VmThreadLocal BACKWARD_LINK = new VmThreadLocal("BACKWARD_LINK", false, "points to previous thread locals in list of all active"); /** * The {@link VmOperation} whose {@link VmOperation#doAtSafepoint(Pointer)} is invoked on a thread when it traps at a {@linkplain SafepointPoll safepoint}. */ public static final VmThreadLocal VM_OPERATION = new VmThreadLocal("VM_OPERATION", true, "Procedure to run when a safepoint is triggered", Nature.Single); /** * The identifier used to identify the thread in the {@linkplain VmThreadMap thread map}. * * 0: denotes the primordial thread * >0: denotes a VmThread * <0: denotes a native thread * * @see VmThread#id() */ public static final VmThreadLocal ID = new VmThreadLocal("ID", false, "Native ID of VM thread holding these locals"); /** * Reference to the {@link VmThread} associated with a set of thread locals. */ public static final VmThreadLocal VM_THREAD = new VmThreadLocal("VM_THREAD", true, "VM thread holding these locals") { @Override public void log(LogPrintStream out, Pointer tla, boolean prefixName) { super.log(out, tla, prefixName); final VmThread vmThread = VmThread.fromTLA(tla); if (vmThread != null) { out.print(' '); out.printThread(vmThread, false); } } }; /** * The address of the table of {@linkplain NativeInterfaces#jniEnv() JNI functions}. */ public static final VmThreadLocal JNI_ENV = new VmThreadLocal("JNI_ENV", false, "points to table of JNI functions"); /** * The address of the current {@linkplain JavaFrameAnchor Java frame anchor list} for a thread. */ public static final VmThreadLocal LAST_JAVA_FRAME_ANCHOR = new VmThreadLocal("LAST_JAVA_FRAME_ANCHOR", false, "", Nature.Single); /** * The state of this thread with respect to {@linkplain VmOperation freezing}. * This will be one of the {@code THREAD_IN_...} constants defined in {@link VmOperation}. */ public static final VmThreadLocal MUTATOR_STATE = new VmThreadLocal("MUTATOR_STATE", false, "Thread state wrt freezing", Nature.Single); /** * A boolean denoting whether this thread has been {@linkplain VmOperation frozen}. * A non-zero value means true, a zero value means false. * This variable is only used when {@link VmOperation#UseCASBasedThreadFreezing} is {@code false}. */ public static final VmThreadLocal FROZEN = new VmThreadLocal("FROZEN", false, "Non-zero if frozen", Nature.Single); /** * The number of the trap (i.e. signal) that occurred. */ public static final VmThreadLocal TRAP_NUMBER = new VmThreadLocal("TRAP_NUMBER", false, "Number of the trap (signal) that occurred"); /** * The value of the instruction pointer when the last trap occurred. * NOTE: like other trap information, this is ONLY VALID for a short time and should only be * used by the C trap handler and the prologue of the trap stub to pass information. */ public static final VmThreadLocal TRAP_INSTRUCTION_POINTER = new VmThreadLocal("TRAP_INSTRUCTION_POINTER", false, "IP when last trap occurred; short-lived"); /** * The fault address causing the trap. */ public static final VmThreadLocal TRAP_FAULT_ADDRESS = new VmThreadLocal("TRAP_FAULT_ADDRESS", false, "Fault address causing last trap; short-lived"); /** * The value of the latch register when the last trap occurred. */ public static final VmThreadLocal TRAP_LATCH_REGISTER = new VmThreadLocal("TRAP_LATCH_REGISTER", false, "Value of latch register at last trap; short-lived"); /** * The address of the stack slot with the highest address that is covered by the {@linkplain #STACK_REFERENCE_MAP * stack reference map}. This value is set so that it covers all thread locals, and the thread's stack. * Once initialized, this value does not change for the lifetime of the thread. */ public static final VmThreadLocal HIGHEST_STACK_SLOT_ADDRESS = new VmThreadLocal("HIGHEST_STACK_SLOT_ADDRESS", false, "points to stack slot with highest address covered by stack reference map"); /** * The address of the stack slot with the lowest address that is covered by the {@linkplain #STACK_REFERENCE_MAP * stack reference map}. This value is set so that it covers all thread locals. Once initialized, this value does * not change for the lifetime of the thread. */ public static final VmThreadLocal LOWEST_STACK_SLOT_ADDRESS = new VmThreadLocal("LOWEST_STACK_SLOT_ADDRESS", false, "points to stack slot with lowest address covered by stack reference map"); /** * The address of the active stack slot with the lowest address that is covered by the * {@linkplain #STACK_REFERENCE_MAP stack reference map}. This value is set during stack reference map preparation * and then used by stack reference map scanning. That is, this value indicates how much of the stack represents * live data at any given garbage collection point. The value is only non-zero for a Java thread that was stopped * for GC <b>and</b> has partially prepared its stack reference map. That is, if the GC sees a zero value for a thread * that has been stopped (with respect to object graph mutation), then it infers said thread is in native code and * needs to have its <b>complete</b> stack reference map prepared on its behalf. */ public static final VmThreadLocal LOWEST_ACTIVE_STACK_SLOT_ADDRESS = new VmThreadLocal("LOWEST_ACTIVE_STACK_SLOT_ADDRESS", false, "points to active stack slot with lowest address covered by stack reference map"); /** * The address of the stack reference map. This reference map has sufficient capacity to store a bit for each * word-aligned address in the (inclusive) range {@code [LOWEST_STACK_SLOT_ADDRESS .. HIGHEST_STACK_SLOT_ADDRESS]}.. */ public static final VmThreadLocal STACK_REFERENCE_MAP = new VmThreadLocal("STACK_REFERENCE_MAP", false, "points to stack reference map"); public static final VmThreadLocal STACK_REFERENCE_MAP_SIZE = new VmThreadLocal("STACK_REFERENCE_SIZE", false, "size of stack reference map"); /** * Threads allocate primarily via a TLAB, which is refilled by default from a default heap. * Occasionally, a thread may need to allocate outside of this allocator. * This thread local is used to indicate that a custom allocator is to be used on next TLAB overflow * events. These can be provoked artificially to force bypassing TLABs and route execution to the custom allocator. * The value stored in the thread local is specific to a heap scheme. */ public static final VmThreadLocal CUSTOM_ALLOCATION_ENABLED = new VmThreadLocal("CUSTOM_ALLOCATION_ENABLED", false, "Non-zero to bypass TLAB allocation and use an alternate allocator", Nature.Single); /** * Set by {@link VmOperation} when a thread is being forced to suspend. This is a bit mask, bit 0 * is the request to suspend and bit 1 determines whether thread was in Java or native when the * request was made. */ public static final VmThreadLocal SUSPEND = new VmThreadLocal("SUSPEND", false, "Bitset for thread suspension", Nature.Single); private static VmThreadLocal[] valuesNeedingInitialization; /** * Specifies if this variable is a reference. */ public final boolean isReference; /** * The name of this variable. */ public final String name; /** * The index of this variable in the array returned by {@link #values()}. */ public final int index; /** * The offset of this variable in a TLA. */ public final int offset; /** * The nature of this variable. */ public final Nature nature; /** * A very short string describing the variable, useful for debugging. */ public final String description; /** * The stack trace element describing where this thread local is declared. */ public final StackTraceElement declaration; static { ProgramError.check(SAFEPOINT_LATCH.index == 0); // The C code in trap.c relies on the following relationships: ProgramError.check(TRAP_NUMBER.index + 1 == TRAP_INSTRUCTION_POINTER.index); ProgramError.check(TRAP_NUMBER.index + 2 == TRAP_FAULT_ADDRESS.index); ProgramError.check(TRAP_NUMBER.index + 3 == TRAP_LATCH_REGISTER.index); } /** * Gets the complete set of declared VM thread locals. */ public static List<VmThreadLocal> values() { assert valuesNeedingInitialization != null : "Need to call completeInitialization() first"; return VALUES; } /** * Gets the array of VM thread locals whose class overrides {@link #initialize()}. */ static VmThreadLocal[] valuesNeedingInitialization() { return valuesNeedingInitialization; } /** * A bit map denoting the thread locals that are GC roots. Bit {@code n} is set in * this map if the thread local with {@link #index} {@code n} is a reference. * * If there is ever a reference-type thread local with an index > 63, then an * encoding larger than a long will be required. */ private static long REFERENCE_MAP; /** * Performs various initialization that can only be done once all the VM thread locals have * been created and registered with this class. * * N.B. javac will not compile the following without explicit package names */ @com.sun.max.annotate.HOSTED_ONLY static class InitializationCompleteCallback implements com.sun.max.vm.hosted.JavaPrototype.InitializationCompleteCallback { public void initializationComplete() { assert valuesNeedingInitialization == null : "Cannot call completeInitialization() more than once"; try { final List<VmThreadLocal> valuesNeedingInitialization = new ArrayList<VmThreadLocal>(); final Method emptyInitializeMethod = VmThreadLocal.class.getMethod("initialize"); for (VmThreadLocal value : VALUES) { if (!emptyInitializeMethod.equals(value.getClass().getMethod("initialize"))) { valuesNeedingInitialization.add(value); } if (value.isReference) { assert value.index <= 63 : "Need larger reference map for thread locals"; REFERENCE_MAP |= 1L << value.index; } } VmThreadLocal.valuesNeedingInitialization = valuesNeedingInitialization.toArray(new VmThreadLocal[valuesNeedingInitialization.size()]); } catch (NoSuchMethodException e) { throw ProgramError.unexpected(e); } } } static { JavaPrototype.registerInitializationCompleteCallback(new InitializationCompleteCallback()); } @HOSTED_ONLY private static StackTraceElement findDeclaration(String name, StackTraceElement[] stackTraceElements) { for (StackTraceElement stackTraceElement : stackTraceElements) { if (!stackTraceElement.getMethodName().equals("<init>")) { ProgramWarning.check(stackTraceElement.getMethodName().equals("<clinit>"), "VM thread local " + name + " should be created in a static field initializer, not at " + stackTraceElement); return stackTraceElement; } } throw ProgramError.unexpected("Could not find non-constructor call in stack trace of a call to VmThreadLocal constructor"); } /** * Gets the size of a {@linkplain VmThreadLocal thread locals area}. * This value is guaranteed to be word-aligned */ public static Size tlaSize() { assert valuesNeedingInitialization != null : "Need to call completeInitialization() first"; return Size.fromInt(VALUES.size() * Word.size()); } public static boolean inJava(Pointer tla) { return JavaFrameAnchor.inJava(JavaFrameAnchor.from(tla)); } // GC support: /** * Prepares a reference map for the stack of a VM thread executing or blocked in native code. * * @param tla a pointer to the VM thread locals denoting the thread stack whose reference map is to be prepared * @return the amount of time taken to prepare the reference map */ public static long prepareStackReferenceMap(Pointer tla) { final VmThread vmThread = VmThread.fromTLA(tla); final StackReferenceMapPreparer stackReferenceMapPreparer = vmThread.stackReferenceMapPreparer(); stackReferenceMapPreparer.prepareStackReferenceMap(tla); return stackReferenceMapPreparer.preparationTime(); } /** * Prepares a reference map for the entire stack of a VM thread starting from a trap. * * @param tla a pointer to the VM thread locals denoting the thread stack whose reference map is to be prepared * @param trapFrame a pointer to the trap frame */ public static void prepareStackReferenceMapFromTrap(Pointer tla, Pointer trapFrame) { VmThread.current().stackReferenceMapPreparer().prepareStackReferenceMapFromTrap(tla, trapFrame); } /** * Prepares the stack reference map for the current thread. The prepared map ignores the frame of this * particular method as it will not be at a stop from the stack reference map preparer's perspective. * Due to the {@link NEVER_INLINE} annotation, this frame will be dead when the caller (i.e. * the GC thread) calls the scheme-specific collector. * * @return the amount of time taken to prepare the reference map */ @NEVER_INLINE public static long prepareCurrentStackReferenceMap() { return VmThread.current().stackReferenceMapPreparer().prepareStackReferenceMap(VmThread.currentTLA(), CodePointer.from(here()), getAbiStackPointer(), getAbiFramePointer(), true); } /** * Scan all references in the VM thread locals. * * @param tla * @param wordPointerIndexVisitor */ private static void scanThreadLocals(Pointer tla, PointerIndexVisitor wordPointerIndexVisitor) { Pointer etla = ETLA.load(tla); Pointer dtla = DTLA.load(tla); Pointer ttla = TTLA.load(tla); if (logStackRootScanning()) { StackReferenceMapPreparer.stackRootScanLogger.logStartThreadLocals(); } int index = 0; long map = REFERENCE_MAP; while (map != 0) { if ((map & 1) != 0) { wordPointerIndexVisitor.visit(etla, index); wordPointerIndexVisitor.visit(dtla, index); wordPointerIndexVisitor.visit(ttla, index); if (logStackRootScanning()) { traceReferenceThreadLocal(etla, index, " (enabled)"); traceReferenceThreadLocal(dtla, index, " (disabled)"); traceReferenceThreadLocal(ttla, index, " (triggered)"); } } index++; map = map >>> 1; } } private static void traceReferenceThreadLocal(Pointer tla, int index, String categorySuffix) { Pointer address = tla.plus(index * Word.size()); StackReferenceMapPreparer.stackRootScanLogger.logReferenceThreadLocal(index, address, address.readWord(0), VALUES.get(index).name, categorySuffix); } /** * Scan all references on the stack, including the VM thread locals, including stored register values. * * This assumes that prepareStackReferenceMap() has been run for the same stack and that no mutator execution * affecting this stack has occurred in between. */ public static void scanReferences(Pointer tla, PointerIndexVisitor wordPointerIndexVisitor) { final VmThread thread = VmThread.fromTLA(tla); boolean isVmOperationThread = thread.isVmOperationThread(); // Note: as a side effect, this lock serializes stack reference map scanning boolean tracing = logStackRootScanning(); boolean lockDisabledSafepoints = tracing && stackRootScanLogger.lock(); if (tracing) { StackReferenceMapPreparer.stackRootScanLogger.logScanThread(thread); } // After this call, the thread object may have been forwarded which means // that vtable dispatch will no longer work for the object scanThreadLocals(tla, wordPointerIndexVisitor); VMLog.scanLogs(tla, wordPointerIndexVisitor); Pointer anchor = JavaFrameAnchor.from(tla); if (!anchor.isZero()) { final Pointer lastJavaCallerStackPointer = JavaFrameAnchor.SP.get(anchor); final Pointer lowestActiveSlot = LOWEST_ACTIVE_STACK_SLOT_ADDRESS.load(tla); final Pointer highestSlot = HIGHEST_STACK_SLOT_ADDRESS.load(tla); final Pointer lowestSlot = LOWEST_STACK_SLOT_ADDRESS.load(tla); if (!isVmOperationThread && lastJavaCallerStackPointer.lessThan(lowestActiveSlot)) { Log.print("The stack has slots between "); Log.print(lastJavaCallerStackPointer); Log.print(" and "); Log.print(lowestActiveSlot); Log.println(" are not covered by the reference map."); Throw.stackDump("Stack trace for thread:", JavaFrameAnchor.PC.get(anchor), lastJavaCallerStackPointer, JavaFrameAnchor.FP.get(anchor)); FatalError.unexpected("Stack reference map does not cover all active slots"); } if (tracing) { StackReferenceMapPreparer.stackRootScanLogger.logThreadSlotRange(highestSlot, lowestActiveSlot, lowestSlot); } StackReferenceMapPreparer.scanReferenceMapRange(tla, lowestActiveSlot, highestSlot, wordPointerIndexVisitor); } else { if (tracing) { StackReferenceMapPreparer.stackRootScanLogger.logThreadSlotRange(Pointer.zero(), Pointer.zero(), Pointer.zero()); } } if (tracing) { Log.unlock(lockDisabledSafepoints); } } @HOSTED_ONLY public VmThreadLocal(String name, boolean isReference, String description) { this(name, isReference, description, Nature.Triple); } /** * Creates a new thread local variable and adds a slot for it to the map. * * @param name name of the variable * @param isReference specifies if this variable is a reference * @param description a very short textual description, useful for debugging */ @HOSTED_ONLY public VmThreadLocal(String name, boolean isReference, String description, Nature nature) { this.isReference = isReference; this.name = name; this.nature = nature; this.index = VALUES.size(); this.offset = index * Word.size(); VALUES.add(this); this.description = description; this.declaration = findDeclaration(name, new Throwable().getStackTrace()); if (valuesNeedingInitialization != null) { FatalError.unexpected("Cannot register " + name + " after " + VmThreadLocal.class.getSimpleName() + ".completeInitialization() was called.\n" + "Make sure " + BootImagePackage.class.getSimpleName() + ".registerThreadLocal() is called for this thread local in the constructor of the " + "Package class responsible for including " + declaration.getClassName() + " in the image."); } } /** * Gets the address of this thread local variable in a given version of thread local variables. * * @param tla the base address of the thread local variables */ @INLINE public final Pointer addressIn(Pointer tla) { return tla.plus(offset); } /** * Prints the value of this thread local to a given log stream. * * @param out the log stream to which the value will be printed * @param tla the TLA from which to read the value of this thread local * @param prefixName if true, then the name of this thread local plus ": " will be printed before the value */ public void log(LogPrintStream out, Pointer tla, boolean prefixName) { if (prefixName) { out.print(name); out.print(": "); } if (this == SAFEPOINT_LATCH && TTLA.load(tla).equals(tla)) { out.print("<trigger latch>"); } else { out.print(tla.getWord(index)); } } /** * Performs any initialization required for this thread local at thread startup time. * This is called before any heap allocation or object stores are executed on the thread. * * The initialization logic should not perform any synchronization or heap allocation. * * The set of VM thread locals that override this method can be obtained via {@link #valuesNeedingInitialization()}. */ public void initialize() { } /** * Stores the value of this {@linkplain Nature#Single single} variable. * * This operation is a single store. * * @param etla this must be the value of the {@link #ETLA} thread local * @param value the value to store */ @INLINE public final void store(Pointer etla, Reference value) { if (MaxineVM.isDebug()) { if (nature != Nature.Single || !etla.readWord(ETLA.offset).equals(etla)) { Log.print("Triple thread local being stored to as a single: "); Log.println(name); FatalError.unexpected("Triple thread local updated as a single"); } } etla.setReference(index, value); } /** * Stores the value of this {@linkplain Nature#Single single} variable. * * This operation is a single store. * * @param etla this must be the value of the {@link #ETLA} thread local * @param value the value to store */ @INLINE public final void store(Pointer etla, Word value) { if (MaxineVM.isDebug()) { if (nature != Nature.Single || !etla.readWord(ETLA.offset).equals(etla)) { Log.print("Triple thread local being stored to as a single: "); Log.println(name); FatalError.unexpected("Triple thread local updated as a single"); } } etla.setWord(index, value); } @FOLD private static int tlaOffset(int n) { return tlaSize().toInt() * n; } /** * Stores the value of this variable in the TLAs denoted by a given TLA. * * This operation is composed of 1 load and 3 stores. */ @INLINE public final void store3(Pointer tla, Word value) { Pointer ttla = TTLA.load(tla); ttla.writeWord(offset, value); ttla.writeWord(offset + tlaOffset(1), value); ttla.writeWord(offset + tlaOffset(2), value); } /** * Stores the value of this variable in the TLAs denoted by the {@linkplain VmThread#currentTLA() current TLA}. * * This operation is composed of 3 loads and 3 stores. */ @INLINE public final void store3(Word value) { store3(currentTLA(), value); } /** * Stores the value of this variable in the TLAs denoted by a given TLA. * * This operation is composed of 1 load and 3 stores. */ @INLINE public final void store3(Pointer tla, Reference value) { Pointer ttla = TTLA.load(tla); ttla.writeReference(offset, value); ttla.writeReference(offset + tlaOffset(1), value); ttla.writeReference(offset + tlaOffset(2), value); } /** * Stores the value of this variable in the TLAs denoted by the {@linkplain VmThread#currentTLA() current TLA}. * * This operation is composed of 3 loads and 3 stores. */ @INLINE public final void store3(Reference value) { store3(currentTLA(), value); } private void checkLoad(Pointer tla) { if (nature == Nature.Single) { if (!tla.readWord(ETLA.offset).equals(tla)) { Log.print("Single thread local must be loaded from ETLA: "); Log.println(name); FatalError.unexpected("Single thread local must be loaded from ETLA", false, null, Pointer.zero()); } } } /** * Loads the value of this variable from a given TLA. * * This operation is a single load. * * @param tla this must be the value of the {@link #ETLA} thread local if this is a {@linkplain Nature#Single single} thread local * @return the value of this variable in {@code tla} as a pointer */ @INLINE public final Pointer load(Pointer tla) { /* if (MaxineVM.isDebug()) { checkLoad(tla); }*/ return tla.readWord(offset).asPointer(); } /** * Loads the value of this variable from a given TLA. * * This operation is a single load. * * @param tla this must be the value of the {@link #ETLA} thread local if this is a {@linkplain Nature#Single single} thread local * @return the value of this variable in {@code tla} as a reference */ @INLINE public final Reference loadRef(Pointer tla) { if (MaxineVM.isDebug()) { checkLoad(tla); } return tla.readReference(offset); } @Override public String toString() { return name; } }