
jdk.graal.compiler.lir.gen.LIRGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
The GraalVM compiler and the Graal-truffle optimizer.
The newest version!
/*
* Copyright (c) 2009, 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.graal.compiler.lir.gen;
import static jdk.graal.compiler.core.common.GraalOptions.LoopHeaderAlignment;
import static jdk.vm.ci.code.ValueUtil.asAllocatableValue;
import static jdk.vm.ci.code.ValueUtil.asStackSlot;
import static jdk.vm.ci.code.ValueUtil.isAllocatableValue;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isLegal;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static jdk.vm.ci.code.ValueUtil.isStackSlot;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import jdk.graal.compiler.asm.Label;
import jdk.graal.compiler.core.common.LIRKind;
import jdk.graal.compiler.core.common.calc.Condition;
import jdk.graal.compiler.core.common.cfg.BasicBlock;
import jdk.graal.compiler.core.common.spi.ForeignCallLinkage;
import jdk.graal.compiler.core.common.spi.LIRKindTool;
import jdk.graal.compiler.core.common.type.Stamp;
import jdk.graal.compiler.debug.DebugCloseable;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.debug.TTY;
import jdk.graal.compiler.graph.NodeSourcePosition;
import jdk.graal.compiler.lir.ConstantValue;
import jdk.graal.compiler.lir.LIR;
import jdk.graal.compiler.lir.LIRFrameState;
import jdk.graal.compiler.lir.LIRInstruction;
import jdk.graal.compiler.lir.LIRValueUtil;
import jdk.graal.compiler.lir.LabelRef;
import jdk.graal.compiler.lir.StandardOp;
import jdk.graal.compiler.lir.SwitchStrategy;
import jdk.graal.compiler.lir.Variable;
import jdk.graal.compiler.lir.hashing.IntHasher;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.nodes.spi.CoreProvidersDelegate;
import jdk.graal.compiler.options.Option;
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionType;
import jdk.graal.compiler.options.OptionValues;
import jdk.vm.ci.code.CallingConvention;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.RegisterAttributes;
import jdk.vm.ci.code.RegisterConfig;
import jdk.vm.ci.code.StackSlot;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.PlatformKind;
import jdk.vm.ci.meta.Value;
import jdk.vm.ci.meta.ValueKind;
/**
* This class traverses the HIR instructions and generates LIR instructions from them.
*/
public abstract class LIRGenerator extends CoreProvidersDelegate implements LIRGeneratorTool {
private final int loopHeaderAlignment;
public static class Options {
// @formatter:off
@Option(help = "Print HIR along side LIR as the latter is generated", type = OptionType.Debug)
public static final OptionKey PrintIRWithLIR = new OptionKey<>(false);
@Option(help = "The trace level for the LIR generator", type = OptionType.Debug)
public static final OptionKey TraceLIRGeneratorLevel = new OptionKey<>(0);
// @formatter:on
}
private final LIRKindTool lirKindTool;
private BasicBlock> currentBlock;
private LIRGenerationResult res;
protected final ArithmeticLIRGenerator arithmeticLIRGen;
protected final BarrierSetLIRGeneratorTool barrierSetLIRGen;
private final MoveFactory moveFactory;
private final boolean printIrWithLir;
private final int traceLIRGeneratorLevel;
@SuppressWarnings("this-escape")
public LIRGenerator(LIRKindTool lirKindTool, ArithmeticLIRGenerator arithmeticLIRGen, BarrierSetLIRGeneratorTool barrierSetLIRGen, MoveFactory moveFactory, CoreProviders providers,
LIRGenerationResult res) {
super(providers);
this.lirKindTool = lirKindTool;
this.arithmeticLIRGen = arithmeticLIRGen;
this.barrierSetLIRGen = barrierSetLIRGen;
this.res = res;
OptionValues options = res.getLIR().getOptions();
this.printIrWithLir = !TTY.isSuppressed() && Options.PrintIRWithLIR.getValue(options);
this.traceLIRGeneratorLevel = TTY.isSuppressed() ? 0 : Options.TraceLIRGeneratorLevel.getValue(options);
this.loopHeaderAlignment = LoopHeaderAlignment.getValue(options);
this.moveFactory = moveFactory;
arithmeticLIRGen.setLirGen(this);
}
@Override
public ArithmeticLIRGeneratorTool getArithmetic() {
return arithmeticLIRGen;
}
@Override
public BarrierSetLIRGeneratorTool getBarrierSet() {
return barrierSetLIRGen;
}
@Override
public MoveFactory getMoveFactory() {
return moveFactory;
}
private MoveFactory spillMoveFactory;
@Override
public MoveFactory getSpillMoveFactory() {
if (spillMoveFactory == null) {
boolean verify = false;
assert (verify = true) == true;
if (verify) {
spillMoveFactory = new VerifyingMoveFactory(moveFactory);
} else {
spillMoveFactory = moveFactory;
}
}
return spillMoveFactory;
}
@Override
public LIRKind getValueKind(JavaKind javaKind) {
return LIRKind.fromJavaKind(target().arch, javaKind);
}
@Override
public TargetDescription target() {
return getCodeCache().getTarget();
}
public LIRKindTool getLIRKindTool() {
return lirKindTool;
}
/**
* Hide {@link #nextVariable()} from other users.
*/
public abstract static class VariableProvider {
private int numVariables;
public int numVariables() {
return numVariables;
}
private int nextVariable() {
return numVariables++;
}
}
@Override
public Variable newVariable(ValueKind> valueKind) {
return new Variable(valueKind, ((VariableProvider) res.getLIR()).nextVariable());
}
@Override
public RegisterConfig getRegisterConfig() {
return res.getRegisterConfig();
}
public RegisterAttributes attributes(Register register) {
return getRegisterConfig().getAttributesMap()[register.number];
}
@Override
public Variable emitMove(Value input) {
assert !LIRValueUtil.isVariable(input) : "Creating a copy of a variable via this method is not supported (and potentially a bug): " + input;
return emitMoveHelper(input.getValueKind(), input);
}
@Override
public Variable emitMove(ValueKind> dst, Value src) {
return emitMoveHelper(dst, src);
}
private Variable emitMoveHelper(ValueKind> dst, Value input) {
Variable result = newVariable(dst);
emitMove(result, input);
return result;
}
@Override
public void emitMove(AllocatableValue dst, Value src) {
append(moveFactory.createMove(dst, src));
}
@Override
public Variable emitReadRegister(Register register, ValueKind> kind) {
return emitMove(register.asValue(kind));
}
@Override
public void emitWriteRegister(Register dst, Value src, ValueKind> kind) {
emitMove(dst.asValue(kind), src);
}
@Override
public void emitMoveConstant(AllocatableValue dst, Constant src) {
append(moveFactory.createLoad(dst, src));
}
@Override
public boolean canInlineConstant(Constant constant) {
return moveFactory.canInlineConstant(constant);
}
@Override
public boolean mayEmbedConstantLoad(Constant constant) {
return moveFactory.mayEmbedConstantLoad(constant);
}
@Override
public Value emitConstant(LIRKind kind, Constant constant) {
if (moveFactory.canInlineConstant(constant)) {
return new ConstantValue(toRegisterKind(kind), constant);
} else {
return emitLoadConstant(toRegisterKind(kind), constant);
}
}
@Override
public Value emitJavaConstant(JavaConstant constant) {
return emitConstant(getValueKind(constant.getJavaKind()), constant);
}
@Override
public AllocatableValue emitLoadConstant(ValueKind> kind, Constant constant) {
Variable result = newVariable(kind);
emitMoveConstant(result, constant);
return result;
}
@Override
public AllocatableValue asAllocatable(Value value) {
if (isAllocatableValue(value)) {
return asAllocatableValue(value);
} else if (LIRValueUtil.isConstantValue(value)) {
return emitLoadConstant(value.getValueKind(), LIRValueUtil.asConstant(value));
} else {
return emitMove(value);
}
}
/**
* Determines if only oop maps are required for the code generated from the LIR.
*/
public boolean needOnlyOopMaps() {
return false;
}
/**
* Gets the ABI specific operand used to return a value of a given kind from a method.
*
* @param javaKind the kind of value being returned
* @param valueKind the backend type of the value being returned
* @return the operand representing the ABI defined location used return a value of kind
* {@code kind}
*/
public AllocatableValue resultOperandFor(JavaKind javaKind, ValueKind> valueKind) {
Register reg = getRegisterConfig().getReturnRegister(javaKind);
assert target().arch.canStoreValue(reg.getRegisterCategory(), valueKind.getPlatformKind()) : reg.getRegisterCategory() + " " + valueKind.getPlatformKind();
return reg.asValue(valueKind);
}
NodeSourcePosition currentPosition;
public void setSourcePosition(NodeSourcePosition position) {
currentPosition = position;
}
private static boolean verify(final LIRInstruction op) {
op.visitEachInput(LIRGenerator::allowed);
op.visitEachAlive(LIRGenerator::allowed);
op.visitEachState(LIRGenerator::allowed);
op.visitEachTemp(LIRGenerator::allowed);
op.visitEachOutput(LIRGenerator::allowed);
op.verify();
return true;
}
// @formatter:off
private static void allowed(Object op, Value val, LIRInstruction.OperandMode mode, EnumSet flags) {
Value value = LIRValueUtil.stripCast(val);
if ((LIRValueUtil.isVariable(value) && flags.contains(LIRInstruction.OperandFlag.REG)) ||
(isRegister(value) && flags.contains(LIRInstruction.OperandFlag.REG)) ||
(LIRValueUtil.isStackSlotValue(value) && flags.contains(LIRInstruction.OperandFlag.STACK)) ||
(LIRValueUtil.isConstantValue(value) && flags.contains(LIRInstruction.OperandFlag.CONST) && mode != LIRInstruction.OperandMode.DEF) ||
(isIllegal(value) && flags.contains(LIRInstruction.OperandFlag.ILLEGAL))) {
return;
}
throw new GraalError("Invalid LIR%n Instruction: %s%n Mode: %s%n Flags: %s%n Unexpected value: %s %s",
op, mode, flags, value.getClass().getSimpleName(), value);
}
// @formatter:on
@Override
public I append(I op) {
LIR lir = res.getLIR();
if (printIrWithLir) {
TTY.println(op.toStringWithIdPrefix());
TTY.println();
}
assert verify(op);
ArrayList lirForBlock = lir.getLIRforBlock(getCurrentBlock());
op.setPosition(currentPosition);
assert !lirForBlock.contains(op) : "added " + op + " twice";
lirForBlock.add(op);
return op;
}
public boolean hasBlockEnd(BasicBlock> block) {
ArrayList ops = getResult().getLIR().getLIRforBlock(block);
if (ops.size() == 0) {
return false;
}
return ops.get(ops.size() - 1) instanceof StandardOp.BlockEndOp;
}
private final class BlockScopeImpl extends BlockScope {
private BlockScopeImpl(BasicBlock> block) {
currentBlock = block;
}
private void doBlockStart() {
if (printIrWithLir) {
TTY.print(currentBlock.toString());
}
// set up the list of LIR instructions
assert res.getLIR().getLIRforBlock(currentBlock) == null : "LIR list already computed for this block";
res.getLIR().setLIRforBlock(currentBlock, new ArrayList<>());
append(new StandardOp.LabelOp(new Label(currentBlock.getId()), currentBlock.isAligned() ? loopHeaderAlignment : 0));
if (traceLIRGeneratorLevel >= 1) {
TTY.println("BEGIN Generating LIR for block B" + currentBlock.getId());
}
}
private void doBlockEnd() {
if (traceLIRGeneratorLevel >= 1) {
TTY.println("END Generating LIR for block B" + currentBlock.getId());
}
if (printIrWithLir) {
TTY.println();
}
currentBlock = null;
}
@Override
public BasicBlock> getCurrentBlock() {
return currentBlock;
}
@Override
public void close() {
doBlockEnd();
}
}
public final BlockScope getBlockScope(BasicBlock> block) {
BlockScopeImpl blockScope = new BlockScopeImpl(block);
blockScope.doBlockStart();
return blockScope;
}
private final class MatchScope implements DebugCloseable {
private MatchScope(BasicBlock> block) {
currentBlock = block;
}
@Override
public void close() {
currentBlock = null;
}
}
public final DebugCloseable getMatchScope(BasicBlock> block) {
MatchScope matchScope = new MatchScope(block);
return matchScope;
}
public void emitIncomingValues(Value[] params) {
((StandardOp.LabelOp) res.getLIR().getLIRforBlock(getCurrentBlock()).get(0)).setIncomingValues(params);
}
@Override
public abstract void emitJump(LabelRef label);
public abstract void emitCompareBranch(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination,
double trueDestinationProbability);
public abstract void emitOverflowCheckBranch(LabelRef overflow, LabelRef noOverflow, LIRKind cmpKind, double overflowProbability);
public abstract void emitIntegerTestBranch(Value left, Value right, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability);
public abstract void emitOpMaskTestBranch(Value left, boolean negateLeft, Value right, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability);
public abstract void emitOpMaskOrTestBranch(Value left, Value right, boolean allZeros, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability);
@Override
public abstract Variable emitConditionalMove(PlatformKind cmpKind, Value leftVal, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue);
@Override
public abstract Variable emitIntegerTestMove(Value leftVal, Value right, Value trueValue, Value falseValue);
public abstract Variable emitOpMaskTestMove(Value leftVal, boolean negateLeft, Value right, Value trueValue, Value falseValue);
public abstract Variable emitOpMaskOrTestMove(Value leftVal, Value right, boolean allZeros, Value trueValue, Value falseValue);
/** Loads the target address for indirect {@linkplain #emitForeignCall foreign calls}. */
protected Value emitIndirectForeignCallAddress(@SuppressWarnings("unused") ForeignCallLinkage linkage) {
return null;
}
/**
* Emits the single call operation at the heart of generating LIR for a
* {@linkplain #emitForeignCall foreign call}.
*/
protected abstract void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress, Value result, Value[] arguments, Value[] temps, LIRFrameState info);
@Override
public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState frameState, Value... args) {
LIRFrameState state = null;
if (linkage.needsDebugInfo()) {
if (frameState != null) {
state = frameState;
} else {
assert needOnlyOopMaps();
state = new LIRFrameState(null, null, null, false);
}
}
Value targetAddress = emitIndirectForeignCallAddress(linkage);
// move the arguments into the correct location
CallingConvention linkageCc = linkage.getOutgoingCallingConvention();
res.getFrameMapBuilder().callsMethod(linkageCc);
assert linkageCc.getArgumentCount() == args.length : "argument count mismatch";
Value[] argLocations = new Value[args.length];
for (int i = 0; i < args.length; i++) {
Value arg = args[i];
AllocatableValue loc = linkageCc.getArgument(i);
emitMove(loc, arg);
argLocations[i] = loc;
}
res.setForeignCall(true);
emitForeignCallOp(linkage, targetAddress, linkageCc.getReturn(), argLocations, linkage.getTemporaries(), state);
if (isLegal(linkageCc.getReturn())) {
return emitMove(linkageCc.getReturn());
} else {
return null;
}
}
public void emitStrategySwitch(JavaConstant[] keyConstants, double[] keyProbabilities, LabelRef[] keyTargets, LabelRef defaultTarget, AllocatableValue value) {
SwitchStrategy strategy = SwitchStrategy.getBestStrategy(keyProbabilities, keyConstants, keyTargets);
int keyCount = keyConstants.length;
Optional hasher = hasherFor(keyConstants);
double hashTableSwitchDensity = hasher.map(h -> (double) keyCount / h.cardinality).orElse(0d);
// The value range computation below may overflow, so compute it as a long.
long valueRange = (long) keyConstants[keyCount - 1].asInt() - (long) keyConstants[0].asInt() + 1;
double tableSwitchDensity = keyCount / (double) valueRange;
/*
* This heuristic tries to find a compromise between the effort for the best switch strategy
* and the density of a tableswitch. If the effort for the strategy is at least 4, then a
* tableswitch is preferred if better than a certain value that starts at 0.5 and lowers
* gradually with additional effort.
*/
double minDensity = 1 / Math.sqrt(strategy.getAverageEffort());
if (strategy.getAverageEffort() < 4d || (tableSwitchDensity < minDensity && hashTableSwitchDensity < minDensity)) {
emitStrategySwitch(strategy, value, keyTargets, defaultTarget);
} else {
if (hashTableSwitchDensity > tableSwitchDensity) {
IntHasher h = hasher.get();
LabelRef[] targets = new LabelRef[h.cardinality];
JavaConstant[] keys = new JavaConstant[h.cardinality];
for (int i = 0; i < h.cardinality; i++) {
keys[i] = JavaConstant.INT_0;
targets[i] = defaultTarget;
}
for (int i = 0; i < keyCount; i++) {
int idx = h.hash(keyConstants[i].asInt());
keys[idx] = keyConstants[i];
targets[idx] = keyTargets[i];
}
emitHashTableSwitch(h, keys, defaultTarget, targets, value);
} else {
int minValue = keyConstants[0].asInt();
assert valueRange < Integer.MAX_VALUE : valueRange;
LabelRef[] targets = new LabelRef[(int) valueRange];
for (int i = 0; i < valueRange; i++) {
targets[i] = defaultTarget;
}
for (int i = 0; i < keyCount; i++) {
targets[keyConstants[i].asInt() - minValue] = keyTargets[i];
}
emitRangeTableSwitch(minValue, defaultTarget, targets, value);
}
}
}
public abstract void emitStrategySwitch(SwitchStrategy strategy, AllocatableValue key, LabelRef[] keyTargets, LabelRef defaultTarget);
protected abstract void emitRangeTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, AllocatableValue key);
protected abstract void emitHashTableSwitch(JavaConstant[] keys, LabelRef defaultTarget, LabelRef[] targets, AllocatableValue value, Value hash);
private static Optional hasherFor(JavaConstant[] keyConstants) {
int[] keys = new int[keyConstants.length];
for (int i = 0; i < keyConstants.length; i++) {
keys[i] = keyConstants[i].asInt();
}
return IntHasher.forKeys(keys);
}
private void emitHashTableSwitch(IntHasher hasher, JavaConstant[] keys, LabelRef defaultTarget, LabelRef[] targets, AllocatableValue value) {
Value hash = value;
if (hasher.factor > 1) {
Value factor = emitJavaConstant(JavaConstant.forShort(hasher.factor));
hash = arithmeticLIRGen.emitMul(hash, factor, false);
}
if (hasher.shift > 0) {
Value shift = emitJavaConstant(JavaConstant.forByte(hasher.shift));
hash = arithmeticLIRGen.emitShr(hash, shift);
}
Value cardinalityAnd = emitJavaConstant(JavaConstant.forInt(hasher.cardinality - 1));
hash = arithmeticLIRGen.emitAnd(hash, cardinalityAnd);
emitHashTableSwitch(keys, defaultTarget, targets, value, hash);
}
/**
* Called just before register allocation is performed on the LIR owned by this generator.
* Overriding implementations of this method must call the overridden method.
*/
public void beforeRegisterAllocation() {
}
/**
* Gets a garbage value for a given kind.
*/
protected abstract JavaConstant zapValueForKind(PlatformKind kind);
@Override
public LIRKind getLIRKind(Stamp stamp) {
return stamp.getLIRKind(lirKindTool);
}
@Override
public BasicBlock> getCurrentBlock() {
return currentBlock;
}
@Override
public LIRGenerationResult getResult() {
return res;
}
@Override
public void emitBlackhole(Value operand) {
append(new StandardOp.BlackholeOp(operand));
}
@Override
public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
throw GraalError.unimplementedOverride(); // ExcludeFromJacocoGeneratedReport
}
@Override
public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
throw GraalError.unimplementedOverride(); // ExcludeFromJacocoGeneratedReport
}
@Override
public abstract LIRInstruction createZapRegisters(Register[] zappedRegisters, JavaConstant[] zapValues);
@Override
public LIRInstruction createZapRegisters() {
Register[] zappedRegisters = getResult().getFrameMap().getRegisterConfig().getAllocatableRegisters().toArray();
return createZapRegisters(zappedRegisters);
}
@Override
public LIRInstruction createZapRegisters(Register[] zappedRegisters) {
JavaConstant[] zapValues = new JavaConstant[zappedRegisters.length];
for (int i = 0; i < zappedRegisters.length; i++) {
PlatformKind kind = target().arch.getLargestStorableKind(zappedRegisters[i].getRegisterCategory());
zapValues[i] = zapValueForKind(kind);
}
return createZapRegisters(zappedRegisters, zapValues);
}
@Override
public abstract LIRInstruction createZapArgumentSpace(StackSlot[] zappedStack, JavaConstant[] zapValues);
@Override
public LIRInstruction zapArgumentSpace() {
List slots = null;
CallingConvention cc = res.getCallingConvention();
for (AllocatableValue arg : cc.getArguments()) {
if (isStackSlot(arg)) {
if (slots == null) {
slots = new ArrayList<>();
}
slots.add((StackSlot) arg);
} else {
assert !LIRValueUtil.isVirtualStackSlot(arg);
}
}
if (slots != null && isStackSlot(cc.getReturn())) {
// Some calling conventions pass their return value through the stack so make sure not
// to kill the return value.
slots.remove(asStackSlot(cc.getReturn()));
}
if (slots == null || slots.size() == 0) {
return null;
}
StackSlot[] zappedStack = slots.toArray(new StackSlot[slots.size()]);
JavaConstant[] zapValues = new JavaConstant[zappedStack.length];
for (int i = 0; i < zappedStack.length; i++) {
PlatformKind kind = zappedStack[i].getPlatformKind();
zapValues[i] = zapValueForKind(kind);
}
return createZapArgumentSpace(zappedStack, zapValues);
}
/**
* Returns the offset of the array length word in an array object's header.
*/
public abstract int getArrayLengthOffset();
/**
* Returns the offset of the first array element.
*/
public int getArrayBaseOffset(JavaKind elementKind) {
return getMetaAccess().getArrayBaseOffset(elementKind);
}
/**
* Returns the register holding the heap base address for compressed pointer.
*/
public abstract Register getHeapBaseRegister();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy